Private
Public Access
1
0

Merge branch 'release/v21.0.0'

This commit is contained in:
2024-05-04 16:18:52 +02:00
79 changed files with 3108 additions and 8662 deletions
+4 -2
View File
@@ -1,6 +1,6 @@
from rowers.models import Alert, Condition, User, Rower, Workout
from rowers.teams import coach_getcoachees
from rowers.dataprep import getsmallrowdata_db, getrowdata_db
from rowers.dataprep import getrowdata_db, read_data, remove_nulls_pl
import datetime
import numpy as np
import math
@@ -101,8 +101,10 @@ def alert_get_stats(alert, nperiod=0): # pragma: no cover
ids = [w.id for w in workouts]
try:
df = getsmallrowdata_db(columns, ids=ids, doclean=True,
df = getsmallrowdata_pd(columns, ids=ids, doclean=True,
workstrokesonly=workstrokesonly)
df.dropna(axis=1,how='all',inplace=True)
df.dropna(axis=0,how='all',inplace=True)
except:
return {
'workouts': workouts.count(),
-57
View File
@@ -1,5 +1,4 @@
from rowers.courseutils import coursetime_paths, coursetime_first, time_in_path
import pandas as pd
from rowers.models import (
Rower, Workout,
GeoPoint, GeoPolygon, GeoCourse,
@@ -420,62 +419,6 @@ def createcourse(
return c
def get_time_course(ws, course): # pragma: no cover
coursetimeseconds = 0.0
coursecompleted = False
w = ws[0]
columns = ['time', ' latitude', ' longitude', 'cum_dist']
rowdata = dataprep.getsmallrowdata_db(
columns,
ids=[w.id],
doclean=False,
workstrokesonly=False
)
rowdata.rename(columns={
' latitude': 'latitude',
' longitude': 'longitude',
}, inplace=True)
rowdata['time'] = rowdata['time']/1000.
rowdata.fillna(method='backfill', inplace=True)
rowdata['time'] = rowdata['time']-rowdata.ix[0, 'time']
# we may want to expand the time (interpolate)
rowdata['dt'] = rowdata['time'].apply(
lambda x: timedelta(seconds=x)
)
rowdata = rowdata.resample('100ms', on='dt').mean()
rowdata = rowdata.interpolate()
# create path
polygons = GeoPolygon.objects.filter(
course=course).order_by("order_in_course")
paths = []
for polygon in polygons:
path = polygon_to_path(polygon)
paths.append(path)
(
coursetimeseconds,
coursemeters,
coursecompleted,
) = coursetime_paths(rowdata, paths)
(
coursetimefirst,
coursemetersfirst,
firstcompleted
) = coursetime_first(
rowdata, paths)
coursetimeseconds = coursetimeseconds-coursetimefirst
coursemeters = coursemeters-coursemetersfirst
return coursetimeseconds, coursemeters, coursecompleted
def replacecourse(course1, course2):
ps = PlannedSession.objects.filter(course=course1)
+84 -108
View File
@@ -8,6 +8,7 @@ from rowers.datautils import p0
from scipy import optimize
from rowers.utils import calculate_age
import datetime
import gzip
from scipy.signal import savgol_filter
from rowers.opaque import encoder
from rowers.database import *
@@ -27,6 +28,8 @@ from fitparse import FitFile
import itertools
import numpy as np
import pandas as pd
import polars as pl
from polars.exceptions import ColumnNotFoundError
from zipfile import BadZipFile
import zipfile
import os
@@ -73,6 +76,7 @@ import pytz
import collections
import pendulum
from rowingdata import rowingdata as rrdata
from rowingdata import rowingdata_pl as rrdata_pl
from rowingdata import rower as rrower
@@ -88,10 +92,9 @@ from rowers.dataroutines import *
from rowers.tasks import (
handle_sendemail_newftp,
handle_sendemail_unrecognized, handle_setcp,
handle_sendemail_unrecognized,
handle_getagegrouprecords, handle_update_wps,
handle_request_post, handle_calctrimp,
handle_updatecp, handle_updateergcp,
handle_sendemail_breakthrough,
handle_sendemail_hard,
)
@@ -123,6 +126,10 @@ from rq import Queue
from rowers.datautils import rpetotss
def rscore_approx(row):
if isinstance(row, pl.DataFrame):
row = {'rscore': row['rscore'][0]}
if isinstance(row, pl.Series):
row = {'rscore': row['rscore'][0]}
if row['rscore'] > 0:
return row['rscore']
if row['rscore'] == 0: # pragma: no cover
@@ -215,18 +222,18 @@ def check_marker(workout):
ids.append(w.id)
gms.append(gmstandard)
df = pd.DataFrame({
df = pl.DataFrame({
'id': ids,
'gms': gms,
})
if df.empty: # pragma: no cover
if df.is_empty(): # pragma: no cover
workout.ranking = True
workout.save()
return workout
indexmax = df['gms'].idxmax()
theid = df.loc[indexmax, 'id']
theid = df.filter(pl.col("gms") == pl.col("gms").max())['id'][0]
wmax = Workout.objects.get(id=theid)
# gms_max = wmax.goldmedalstandard
@@ -322,7 +329,7 @@ def workout_summary_to_df(
goldstandarddurations.append(int(goldstandardduration))
rankingpieces.append(w.rankingpiece)
df = pd.DataFrame({
df = pl.DataFrame({
'ID': ids,
'date': startdatetimes,
'name': names,
@@ -392,7 +399,7 @@ def resample(id, r, parent, overwrite='copy'):
row.write_csv(parent.csvfilename, gzip=True)
_ = dataprep(row.df, id=parent.id, bands=True, barchart=True,
_ = dataplep(row.df, id=parent.id, bands=True, barchart=True,
otwpower=True, empower=True, inboard=parent.inboard)
isbreakthrough, ishard = checkbreakthrough(parent, r)
_ = check_marker(parent)
@@ -414,18 +421,23 @@ def resample(id, r, parent, overwrite='copy'):
def calculate_goldmedalstandard(rower, workout, recurrance=True):
cpfile = 'media/cpdata_{id}.parquet.gz'.format(id=workout.id)
try:
df = pd.read_parquet(cpfile)
df = pl.read_parquet(cpfile)
except:
df = read_data(['power'], ids=[workout.id])
df = remove_nulls_pl(df)
background = True
if settings.TESTING:
background = False
df, delta, cpvalues = setcp(workout, background=background)
if df.empty:
return 0, 0
if recurrance:
df, delta, cpvalues = setcp(workout, background=background)
if df.is_empty():
return 0, 0
else:
return 0,0
if df.empty and recurrance: # pragma: no cover
if df.is_empty() and recurrance: # pragma: no cover
df, delta, cpvalues = setcp(workout, recurrance=False, background=True)
if df.empty:
if df.is_empty():
return 0, 0
age = calculate_age(rower.birthdate, today=workout.date)
@@ -453,7 +465,7 @@ def calculate_goldmedalstandard(rower, workout, recurrance=True):
if getrecords: # pragma: no cover
durations = [1, 4, 30, 60]
distances = [100, 500, 1000, 2000, 5000, 6000, 10000, 21097, 42195]
df2 = pd.DataFrame(
df2 = pl.DataFrame(
list(
C2WorldClassAgePerformance.objects.filter(
sex=rower.sex,
@@ -461,7 +473,7 @@ def calculate_goldmedalstandard(rower, workout, recurrance=True):
).values()
)
)
jsondf = df2.to_json()
jsondf = df2.write_json()
_ = myqueue(queuelow, handle_getagegrouprecords,
jsondf, distances, durations, age, rower.sex, rower.weightcategory)
@@ -489,9 +501,10 @@ def calculate_goldmedalstandard(rower, workout, recurrance=True):
scores = 100.*powers/wcpowers
try:
indexmax = scores.idxmax()
delta = int(df.loc[indexmax, 'delta'])
maxvalue = scores.max()
df = pl.DataFrame({'times': times, 'scores': scores})
df = df.filter(pl.col("scores") == pl.col("scores").max())
delta = df[0, "times"]
maxvalue = df[0, "scores"]
except (ValueError, TypeError, KeyError): # pragma: no cover
indexmax = 0
delta = 0
@@ -504,21 +517,22 @@ def calculate_goldmedalstandard(rower, workout, recurrance=True):
def setcp(workout, background=False, recurrance=True):
try:
filename = 'media/cpdata_{id}.parquet.gz'.format(id=workout.id)
df = pd.read_parquet(filename)
df = pl.read_parquet(filename)
if not df.empty:
if not df.is_empty():
# check dts
tarr = datautils.getlogarr(4000)
if df['delta'][0] in tarr:
return(df, df['delta'], df['cp'])
except:
except Exception as e:
pass
strokesdf = getsmallrowdata_db(
strokesdf = read_data(
['power', 'workoutid', 'time'], ids=[workout.id])
strokesdf = remove_nulls_pl(strokesdf)
if strokesdf.empty:
return pd.DataFrame({'delta': [], 'cp': []}), pd.Series(dtype='float'), pd.Series(dtype='float')
if strokesdf.is_empty():
return pl.DataFrame({'delta': [], 'cp': []}), pl.Series(dtype=pl.Float64), pl.Series(dtype=pl.Float64)
totaltime = strokesdf['time'].max()
maxt = totaltime/1000.
@@ -533,7 +547,7 @@ def setcp(workout, background=False, recurrance=True):
elif os.path.exists(csvfilename+'.gz'): # pragma: no cover
csvfile = csvfilename+'.gz'
else: # pragma: no cover
return pd.DataFrame({'delta': [], 'cp': []}), pd.Series(dtype='float'), pd.Series(dtype='float')
return pl.DataFrame({'delta': [], 'cp': []}), pl.Series(dtype=pl.Float64), pl.Series(dtype=pl.Float64)
csvfile = os.path.abspath(csvfile)
@@ -547,7 +561,7 @@ def setcp(workout, background=False, recurrance=True):
grpc.channel_ready_future(channel).result(timeout=10)
except grpc.FutureTimeoutError: # pragma: no cover
dologging('metrics.log','grpc channel time out in setcp')
return pd.DataFrame({'delta': [], 'cp': []}), pd.Series(dtype='float'), pd.Series(dtype='float')
return pl.DataFrame({'delta': [], 'cp': []}), pl.Series(dtype=pl.Float64), pl.Series(dtype=pl.Float64)
stub = metrics_pb2_grpc.MetricsStub(channel)
req = metrics_pb2.CPRequest(filename = csvfile, filetype = "CSV", tarr = logarr)
@@ -556,25 +570,29 @@ def setcp(workout, background=False, recurrance=True):
response = stub.GetCP(req, timeout=60)
except Exception as e:
dologging('metrics.log', traceback.format_exc())
return pd.DataFrame({'delta': [], 'cp': []}), pd.Series(dtype='float'), pd.Series(dtype='float')
return pl.DataFrame({'delta': [], 'cp': []}), pl.Series(dtype=pl.Float64), pl.Series(dtype=pl.Float64)
delta = pd.Series(np.array(response.delta))
cpvalues = pd.Series(np.array(response.power))
delta = pl.Series(np.array(response.delta))
cpvalues = pl.Series(np.array(response.power))
powermean = response.avgpower
df = pd.DataFrame({
df = pl.DataFrame({
'delta': delta,
'cp': cpvalues,
'id': workout.id,
})
df.to_parquet(filename, engine='fastparquet', compression='GZIP')
df = df.drop_nulls()
with gzip.open(filename, 'w') as f:
df.write_parquet(f)
#df.to_parquet(filename, engine='fastparquet', compression='GZIP')
if recurrance:
goldmedalstandard, goldmedalduration = calculate_goldmedalstandard(
workout.user, workout)
workout.user, workout, recurrance=False)
workout.goldmedalstandard = goldmedalstandard
workout.goldmedalduration = goldmedalduration
workout.save()
@@ -602,14 +620,10 @@ def update_wps(r, types, mode='water', asynchron=True):
mode
)
df = getsmallrowdata_db(['time', 'driveenergy'], ids=ids)
df = read_data(['time', 'driveenergy'], ids=ids)
try:
mask = df['driveenergy'] > 100
except (KeyError, TypeError):
return False
try:
wps_median = int(df.loc[mask, 'driveenergy'].median())
wps_median = int(df.filter(pl.col("driveenergy")>100)["driveenergy"].median())
if mode == 'water':
r.median_wps = wps_median
else: # pragma: no cover
@@ -618,6 +632,10 @@ def update_wps(r, types, mode='water', asynchron=True):
r.save()
except ValueError: # pragma: no cover
pass
except OverflowError:
pass
except ColumnNotFoundError:
pass
return True
@@ -718,77 +736,28 @@ def fetchcp_new(rower, workouts):
data = []
for workout in workouts:
cpfile = 'media/cpdata_{id}.parquet.gz'.format(id=workout.id)
try:
df, delta, cpvalues = setcp(workout)
df = pd.read_parquet(cpfile)
df['workout'] = str(workout)
df['url'] = workout.url()
data.append(df)
except:
# CP data file doesn't exist yet. has to be created
df, delta, cpvalues = setcp(workout)
df['workout'] = str(workout)
df['url'] = workout.url()
df, delta, cpvalues = setcp(workout)
df = df.drop('id')
df = df.with_columns((pl.lit(str(workout))).alias("workout"))
df = df.with_columns((pl.lit(workout.url())).alias("url"))
if not df.is_empty():
data.append(df)
if len(data) == 0:
return pd.Series(dtype='float'), pd.Series(dtype='float'), 0, pd.Series(dtype='float'), pd.Series(dtype='float')
return pl.Series(dtype=pl.Float64), pl.Series(dtype=pl.Float64), 0, pl.Series(dtype=pl.Float64), pl.Series(dtype=pl.Float64)
if len(data) > 1:
df = pd.concat(data, axis=0)
df = pl.concat(data)
try:
df = df[df['cp'] == df.groupby(['delta'])['cp'].transform('max')]
except KeyError: # pragma: no cover
return pd.Series(dtype='float'), pd.Series(dtype='float'), 0, pd.Series(dtype='float'), pd.Series(dtype='float')
df = df.sort_values(['delta']).reset_index()
df = df[df['cp']>20]
df = df.group_by(pl.col("delta")).agg(pl.max("cp"), pl.max("workout"), pl.max("url")).sort("delta")
except (KeyError, ColumnNotFoundError): # pragma: no cover
return pl.Series(dtype=pl.Float64), pl.Series(dtype=pl.Float64), 0, pl.Series(dtype=pl.Float64), pl.Series(dtype=pl.Float64)
df = df.filter(pl.col("cp")>20)
return df['delta'], df['cp'], 0, df['workout'], df['url']
def fetchcp(rower, theworkouts, table='cpdata'): # pragma: no cover
# get all power data from database (plus workoutid)
theids = [int(w.id) for w in theworkouts]
columns = ['power', 'workoutid', 'time']
df = getsmallrowdata_db(columns, ids=theids)
df.dropna(inplace=True, axis=0)
if df.empty:
avgpower2 = {}
for id in theids:
avgpower2[id] = 0
return pd.Series([], dtype='float'), pd.Series([], dtype='float'), avgpower2
try:
dfgrouped = df.groupby(['workoutid'])
except KeyError:
avgpower2 = {}
return pd.Series([], dtype='float'), pd.Series([], dtype='float'), avgpower2
try:
avgpower2 = dict(dfgrouped.mean()['power'].astype(int))
except KeyError:
avgpower2 = {}
for id in theids:
avgpower2[id] = 0
return pd.Series([], dtype='float'), pd.Series([], dtype='float'), avgpower2
cpdf = getcpdata_sql(rower.id, table=table)
if not cpdf.empty:
return cpdf['delta'], cpdf['cp'], avgpower2
else:
_ = myqueue(queuelow,
handle_updatecp,
rower.id,
theids,
table=table)
return pd.Series([], dtype='float'), pd.Series([], dtype='float'), avgpower2
return pd.Series([], dtype='float'), pd.Series([], dtype='float'), avgpower2
def update_rolling_cp(r, types, mode='water', dosend=False):
@@ -801,16 +770,18 @@ def update_rolling_cp(r, types, mode='water', dosend=False):
delta, cp, avgpower, workoutnames, urls = fetchcp_new(r, workouts)
powerdf = pd.DataFrame({
powerdf = pl.DataFrame({
'Delta': delta,
'CP': cp,
})
powerdf = powerdf[powerdf['CP'] > 0]
powerdf.dropna(axis=0, inplace=True)
powerdf.sort_values(['Delta', 'CP'], ascending=[1, 0], inplace=True)
powerdf.drop_duplicates(subset='Delta', keep='first', inplace=True)
powerdf = powerdf.filter(pl.col("CP")>0)
powerdf = powerdf.fill_nan(None).drop_nulls().sort(["Delta", "CP"])
powerdf = powerdf.unique(subset=["Delta"], keep="first")
if powerdf.is_empty():
return False
res2 = datautils.cpfit(powerdf)
p1 = res2[0]
# calculate FTP
@@ -1051,7 +1022,7 @@ def checkbreakthrough(w, r):
workouttype = w.workouttype
if workouttype in rowtypes:
cpdf, delta, cpvalues = setcp(w)
if not cpdf.empty:
if not cpdf.is_empty():
if workouttype in otwtypes:
try:
res, btvalues, res2 = utils.isbreakthrough(
@@ -1397,7 +1368,12 @@ def save_workout_database(f2, r, dosmooth=True, workouttype='rower',
w.team.add(t)
# put stroke data in database
_ = dataprep(row.df, id=w.id, bands=True,
try:
row = rrdata_pl(df=pl.from_pandas(row.df))
except:
pass
_ = dataplep(row.df, id=w.id, bands=True,
barchart=True, otwpower=True, empower=True, inboard=inboard)
isbreakthrough, ishard = checkbreakthrough(w, r)
+836 -63
View File
File diff suppressed because it is too large Load Diff
+19 -32
View File
@@ -1,8 +1,10 @@
import pandas as pd
import polars as pl
import numpy as np
from scipy.interpolate import griddata
from scipy import optimize
from rowers.mytypes import otwtypes, otetypes, rowtypes
from rowers.models import Workout
@@ -81,8 +83,9 @@ def cpfit(powerdf, fraclimit=0.0001, nmax=1000):
p1 = p0
thesecs = powerdf['Delta']
theavpower = powerdf['CP']
thesecs = powerdf['Delta'].to_numpy()
theavpower = powerdf['CP'].to_numpy()
if len(thesecs) >= 4:
try:
@@ -95,6 +98,7 @@ def cpfit(powerdf, fraclimit=0.0001, nmax=1000):
else:
factor = fitfunc(p0, thesecs.mean())/theavpower.mean()
p1 = [p0[0]/factor, p0[1]/factor, p0[2], p0[3]]
p1 = [abs(p) for p in p1]
fitt = pd.Series(10**(4*np.arange(100)/100.))
@@ -102,7 +106,7 @@ def cpfit(powerdf, fraclimit=0.0001, nmax=1000):
fitpower = fitfunc(p1, fitt)
fitpoints = fitfunc(p1, thesecs)
fitpoints0 = fitpoints.copy()
fitpoints0 = fitpoints
dd = fitpoints-theavpower
ddmin = dd.min()
@@ -356,8 +360,8 @@ def getmaxwattinterval(tt, ww, i):
def getfastest(df, thevalue, mode='distance'):
tt = df['time'].copy()
dd = df['cumdist'].copy()
tt = df['time'].clone()
dd = df['cumdist'].clone()
tmax = tt.max()
if mode == 'distance': # pragma: no cover
@@ -368,40 +372,28 @@ def getfastest(df, thevalue, mode='distance'):
return 0
# if tmax > 500000:
# newlen=int(tmax/2000.)
# newt = np.arange(newlen)*tmax/float(newlen)
# deltat = newt[1]-newt[0]
# else:
# newt = np.arange(0,tmax,10.)
# deltat = 10.
newlen = 1000
newt = np.arange(newlen)*tmax/float(newlen)
deltat = newt[1]-newt[0]
dd = griddata(tt.values,
dd.values, newt, method='linear', rescale=True)
dd = griddata(tt.to_numpy(),
dd.to_numpy(), newt, method='linear', rescale=True)
tt = pd.Series(newt, dtype='float')
dd = pd.Series(dd, dtype='float')
tt = pl.Series(newt, dtype=pl.Float64)
dd = pl.Series(dd, dtype=pl.Float64)
G = pl.concat([pl.Series([0.0]), dd])
G = pd.concat([pd.Series([0]), dd])
# T = pd.concat([pd.Series([0]), dd])
# h = np.mgrid[0:len(tt)+1:1, 0:len(tt)+1:1]
# distances = pd.DataFrame(h[1]-h[0])
ones = 1+np.zeros(len(G))
Ghor = np.outer(ones, G)
# Thor = np.outer(ones, T)
# Tver = np.outer(T, ones)
Gver = np.outer(G, ones)
Gdif = Ghor-Gver
Gdif = np.tril(Gdif.T).T
Gdif = pd.DataFrame(Gdif)
Gdif = pl.DataFrame(Gdif)
F = Gdif
F.fillna(inplace=True, method='ffill', axis=1)
F.fillna(inplace=True, value=0)
F = F.fill_nan(0)
restime = []
distance = []
@@ -412,7 +404,7 @@ def getfastest(df, thevalue, mode='distance'):
restime.append(deltat*i)
cp = np.diag(F, i).max()
loc = np.argmax(np.diag(F, i))
thestarttime = tt[loc]
thestarttime = tt.to_numpy()[loc]
starttimes.append(thestarttime)
distance.append(cp)
@@ -422,10 +414,6 @@ def getfastest(df, thevalue, mode='distance'):
distance = np.array(distance)
starttimes = np.array(starttimes)
# for i in range(len(restime)):
# if restime[i]<thevalue*60*1000:
# print(i,restime[i],distance[i],60*1000*thevalue)
if mode == 'distance':
duration = griddata(distance, restime, [
thevalue], method='linear', rescale=True)
@@ -434,7 +422,6 @@ def getfastest(df, thevalue, mode='distance'):
distance = griddata(distance, distance, [
thevalue], method='linear', rescale=True)
endtime = starttime+duration
# print(duration,starttime,endtime,'aa')
return duration[0]/1000., starttime[0]/1000., endtime[0]/1000.
else: # pragma: no cover
distance = griddata(restime, distance, [
+10 -83
View File
@@ -74,27 +74,6 @@ class ResampleForm(forms.Form):
class TrainingZonesForm(forms.Form):
zoneschoices = (
('power', 'Power Zones'),
('hr', 'Heart Rate Zones')
)
datechoices = (
('month', 'By Month'),
('week', 'By Week'),
)
yaxischoices = (
('time', 'Time'),
('percentage', 'Percentage of Time')
)
zones = forms.ChoiceField(
initial='hr', label='Training Zones', choices=zoneschoices)
dates = forms.ChoiceField(
initial='week', label='Date Aggregation', choices=datechoices)
yaxis = forms.ChoiceField(initial='percentage',
label='Y axis', choices=yaxischoices)
startdate = forms.DateField(
initial=timezone.now()-datetime.timedelta(days=42),
widget=AdminDateWidget(), # format='%Y-%m-%d'),
@@ -778,59 +757,7 @@ class PredictedPieceFormNoDistance(forms.Form):
value = forms.FloatField(initial=10, label='Free ranking piece')
# On the Geeky side, to update stream information for river dwellers
class UpdateStreamForm(forms.Form):
unitchoices = (
('m', 'm/s'),
('f', 'foot/s'),
('k', 'knots'),
('p', 'pace difference (sec/500m)'),
)
dist1 = forms.FloatField(initial=0, label='Distance 1')
dist2 = forms.FloatField(initial=1000, label='Distance 2')
stream1 = forms.FloatField(initial=0, label='Stream velocity 1')
stream2 = forms.FloatField(initial=0, label='Stream velocity 2')
streamunit = forms.ChoiceField(required=True,
choices=unitchoices,
initial='m',
label='Unit')
class Meta:
fields = ['dist1', 'dist2', 'stream1', 'stream2', 'streamunit']
# add wind information to your workout
class UpdateWindForm(forms.Form):
unitchoices = (
('m', 'm/s'),
('k', 'knots'),
('b', 'beaufort'),
('kmh', 'km/h'),
('mph', 'miles/hour'),
)
dist1 = forms.FloatField(initial=0, label='Distance 1')
dist2 = forms.FloatField(initial=1000, label='Distance 2')
vwind1 = forms.FloatField(initial=0, required=False, label='Wind Speed 1')
vwind2 = forms.FloatField(initial=0, required=False, label='Wind Speed 2')
windunit = forms.ChoiceField(required=True,
choices=unitchoices,
initial='m',
label='Unit')
winddirection1 = forms.IntegerField(initial=0, required=False,
label='Wind Direction 1')
winddirection2 = forms.IntegerField(initial=0, required=False,
label='Wind Direction 2')
class Meta:
fields = ['dist1', 'dist2',
'vwind1', 'vwind2',
'windunit',
'winddirection1', 'winddirection2']
# Form to select a data range to show workouts from a certain time period
class DateRangeForm(forms.Form):
startdate = forms.DateField(
initial=timezone.now()-datetime.timedelta(days=365),
@@ -1859,27 +1786,27 @@ class FlexOptionsForm(forms.Form):
class ForceCurveOptionsForm(forms.Form):
includereststrokes = forms.BooleanField(initial=False, required=False,
label='Include Rest Strokes')
label='Include Rest Strokes',
widget=forms.CheckboxInput(attrs={'class':'hidden'}))
spm_min = forms.FloatField(initial=15.0,label='SPM Min',widget=HiddenInput,required=False)
spm_max = forms.FloatField(initial=55.0,label='SPM Max',widget=HiddenInput,required=False)
dist_min = forms.IntegerField(initial=0,label='Dist Min',widget=HiddenInput,required=False)
dist_max = forms.IntegerField(initial=0,label='Dist Max',widget=HiddenInput,required=False)
dist_max = forms.IntegerField(initial=150000,label='Dist Max',widget=HiddenInput,required=False)
work_min = forms.IntegerField(initial=0,label='Work Min',widget=HiddenInput,required=False)
work_max = forms.IntegerField(initial=1500,label='Work Max',widget=HiddenInput,required=False)
notes = forms.CharField(initial="", label='notes', widget=HiddenInput, required=False)
plotchoices = (
('line', 'Force Curve Collection Plot'),
('scatter', 'Peak Force Scatter Plot'),
('none', 'Only aggregrate data')
)
plottype = forms.ChoiceField(choices=plotchoices, initial='line',
label='Individual Stroke Chart Type')
name = forms.CharField(initial="", label='Name',required=False)
plotcircles = forms.BooleanField(initial=False,
widget=forms.CheckboxInput(attrs={'class':'hidden'}),
required=False)
plotlines = forms.BooleanField(initial=False,
widget=forms.CheckboxInput(attrs={'class':'hidden'}),
required=False)
axchoices = list(
(ax[0], ax[1]) for ax in axes if ax[0] not in ['cumdist', 'None']
+1 -1
View File
@@ -2,7 +2,7 @@ from .integrations import SyncIntegration, NoTokenError, create_or_update_syncre
from rowers.models import User, Rower, Workout, TombStone
from django.db.utils import IntegrityError
from rowingdata import rowingdata
from rowingdata import rowingdata, rowingdata_pl
import numpy as np
import datetime
import json
+954 -5809
View File
File diff suppressed because it is too large Load Diff
+1
View File
@@ -364,6 +364,7 @@ for key, dict in rowingmetrics:
metricsdicts[key] = dict
metricsgroups = list(set([d['group'] for n, d in rowingmetrics]))
dtypes = {}
+7 -2
View File
@@ -3833,9 +3833,12 @@ def auto_delete_file_on_delete(sender, instance, **kwargs):
# remove parquet file
try:
dirname = 'media/strokedata_{id}.parquet.gz'.format(id=instance.id)
shutil.rmtree(dirname)
os.remove(dirname)
except FileNotFoundError:
pass
try:
shutil.rmtree(dirname)
except:
pass
# remove parquet file
try:
@@ -5257,6 +5260,8 @@ class ForceCurveAnalysis(models.Model):
average_spm = models.FloatField(default=23)
average_boatspeed = models.FloatField(default=4.0)
include_rest_strokes = models.BooleanField(default=False)
plotcircles = models.BooleanField(default=False)
plotlines = models.BooleanField(default=False)
def __str__(self):
s = 'Force Curve Analysis {name} ({date})'.format(name = self.name,
+11 -17
View File
@@ -18,13 +18,12 @@ import rowers.dataprep as dataprep
import numpy as np
import rowers.metrics as metrics
import rowers.mytypes as mytypes
from rowers.courses import get_time_course
from rowers.utils import to_pace
from rowers.opaque import encoder
from rowingdata import rower as rrower
from rowingdata import rowingdata as rrdata
import arrow
import pandas as pd
import polars as pl
import json
# Python
@@ -203,16 +202,8 @@ def get_execution_report(rower, startdate, enddate, plan=None):
startdate__gte=enddate)
else: # pragma: no cover
plans = TrainingPlan.objects.filter(rowers__in=[rower])
#plans2 = TrainingPlan.objects.filter(
# enddate__lte=enddate, startdate__lte=enddate, rowers__in=[rower])
#plans = plans | plans2
#plans = plans.exclude(status=False).order_by("-enddate")
if not plans:
# make week cycles here
# get monday before startdate
micros = []
else:
sorted_plans = sorted(plans, key= lambda t: t.overlap(startdate,enddate))
@@ -290,7 +281,7 @@ def get_execution_report(rower, startdate, enddate, plan=None):
else:
plannedscore += 60
actualscore += 0
elif w.hrtss != 0:
elif w.hrtss >= 0:
if ratio > 0:
plannedscore += w.hrtss/ratio
actualscore += w.hrtss
@@ -313,7 +304,7 @@ def get_execution_report(rower, startdate, enddate, plan=None):
planned += [plannedscore]
executed += [actualscore]
data = pd.DataFrame({
data = pl.DataFrame({
'startdate': startdates,
'planned': planned,
'executed': executed,
@@ -444,8 +435,9 @@ def add_workouts_plannedsession(ws, ps, r):
for record in records:
record.delete()
df = dataprep.getsmallrowdata_db(
df = dataprep.read_data(
['time', 'cumdist'], ids=[w.id])
df = dataprep.remove_nulls_pl(df)
fastest_milliseconds, starttime, endtime = datautils.getfastest(
df, ps.sessionvalue, mode='distance')
@@ -475,11 +467,11 @@ def add_workouts_plannedsession(ws, ps, r):
for record in records:
record.delete()
df = dataprep.getsmallrowdata_db(
df = dataprep.read_data(
['time', 'cumdist'], ids=[w.id])
df = dataprep.remove_nulls_pl(df)
fastest_meters, starttime, endtime = datautils.getfastest(
df, ps.sessionvalue, mode='time')
if fastest_meters > 0:
w.plannedsession = ps
w.save()
@@ -1694,8 +1686,9 @@ def add_workout_fastestrace(ws, race, r, recordid=0, doregister=False):
record.coursecompleted = True
record.workoutid = ws[0].id
if race.sessiontype == 'fastest_distance':
df = dataprep.getsmallrowdata_db(
df = dataprep.read_data(
['time', 'cumdist'], ids=[ws[0].id])
df = dataprep.remove_nulls_pl(df)
fastest_milliseconds, startsecond, endsecond = datautils.getfastest(
df, race.sessionvalue, mode='distance')
velo = race.sessionvalue/fastest_milliseconds
@@ -1711,8 +1704,9 @@ def add_workout_fastestrace(ws, race, r, recordid=0, doregister=False):
record.endsecond = endsecond
record.save()
if race.sessiontype == 'fastest_time': # pragma: no cover
df = dataprep.getsmallrowdata_db(
df = dataprep.read_data(
['time', 'cumdist'], ids=[ws[0].id])
df = dataprep.remove_nulls_pl(df)
fastest_meters, startsecond, endsecond = datautils.getfastest(
df, race.sessionvalue, mode='time')
velo = fastest_meters/(60.*race.sessionvalue)
+17 -1
View File
@@ -6,7 +6,7 @@ from rest_framework import serializers
from rowers.models import (
Workout, Rower, FavoriteChart, VirtualRaceResult,
VirtualRace, GeoCourse, StandardCollection, CourseStandard,
GeoPolygon, GeoPoint, PlannedSession,
GeoPolygon, GeoPoint, PlannedSession, ForceCurveAnalysis
)
from django.core.exceptions import PermissionDenied
@@ -297,7 +297,23 @@ class GeoPointSerializer(serializers.ModelSerializer):
)
extra_kwargs = {'id': {'read_only': False, 'required': True}}
class ForceCurveAnalysisSerializer(serializers.ModelSerializer):
class Meta:
model = ForceCurveAnalysis
fields = (
'id',
'name',
'workout',
'dist_min',
'dist_max',
'spm_min',
'spm_max',
'work_min',
'work_max',
'include_rest_strokes'
)
class GeoPolygonSerializer(serializers.ModelSerializer):
points = GeoPointSerializer(many=True)
+44 -124
View File
@@ -56,7 +56,7 @@ from scipy.signal import savgol_filter
from scipy.interpolate import griddata
import rowingdata
from rowingdata import make_cumvalues
from rowingdata import make_cumvalues, make_cumvalues_array
from uuid import uuid4
from rowingdata import rowingdata as rdata
@@ -115,6 +115,9 @@ tpapilocation = TP_API_LOCATION
from requests_oauthlib import OAuth1, OAuth1Session
import pandas as pd
import polars as pl
from polars.exceptions import ColumnNotFoundError
from django_rq import job
from django.utils import timezone
@@ -127,7 +130,8 @@ from rowers import mytypes
from rowers.dataroutines import (
getsmallrowdata_db, updatecpdata_sql, update_c2id_sql,
getsmallrowdata_pd, updatecpdata_sql, update_c2id_sql,
read_data,
#update_workout_field_sql,
update_agegroup_db, update_strokedata,
add_c2_stroke_data_db, totaltime_sec_to_string,
@@ -559,7 +563,7 @@ def handle_sporttracks_workout_from_data(user, importid, source,
strokedata = pd.DataFrame.from_dict({
key: pd.Series(value, dtype='object') for key, value in data.items()
})
try:
workouttype = data['type']
except KeyError: # pragma: no cover
@@ -2549,9 +2553,10 @@ def handle_otwsetpower(self, f1, boattype, boatclass, coastalbrand, weightvalue,
totaltime = totaltime + rowdata.df.loc[0, ' ElapsedTime (sec)']
except KeyError: # pragma: no cover
pass
df = getsmallrowdata_db(
df = getsmallrowdata_pd(
['power', 'workoutid', 'time'], ids=[workoutid],
debug=debug)
thesecs = totaltime
maxt = 1.05 * thesecs
logarr = datautils.getlogarr(maxt)
@@ -2583,53 +2588,6 @@ def handle_otwsetpower(self, f1, boattype, boatclass, coastalbrand, weightvalue,
return 1
@app.task
def handle_updateergcp(rower_id, workoutfilenames, debug=False, **kwargs):
therows = []
for f1 in workoutfilenames:
try:
rowdata = rdata(csvfile=f1)
except IOError: # pragma: no cover
try:
rowdata = rdata(csvfile=f1 + '.csv')
except IOError:
try:
rowdata = rdata(csvfile=f1 + '.gz')
except IOError:
rowdata = 0
if rowdata != 0:
therows.append(rowdata)
cpdata = rowingdata.cumcpdata(therows)
cpdata.columns = cpdata.columns.str.lower()
updatecpdata_sql(rower_id, cpdata['delta'], cpdata['cp'],
table='ergcpdata', distance=cpdata['distance'],
debug=debug)
return 1
@app.task
def handle_updatecp(rower_id, workoutids, debug=False, table='cpdata', **kwargs):
columns = ['power', 'workoutid', 'time']
df = getsmallrowdata_db(columns, ids=workoutids, debug=debug)
if df.empty: # pragma: no cover
return 0
maxt = 1.05*df['time'].max()/1000.
logarr = datautils.getlogarr(maxt)
dfgrouped = df.groupby(['workoutid'])
delta, cpvalue, avgpower = datautils.getcp(dfgrouped, logarr)
updatecpdata_sql(rower_id, delta, cpvalue, debug=debug, table=table)
return 1
@app.task
def handle_makeplot(f1, f2, t, hrdata, plotnr, imagename,
@@ -3179,43 +3137,6 @@ def handle_sendemail_invite_reject(email, name, teamname, managername,
return 1
@app.task
def handle_setcp(strokesdf, filename, workoutid, debug=False, **kwargs):
try:
os.remove(filename)
except FileNotFoundError:
pass
if not strokesdf.empty:
try:
totaltime = strokesdf['time'].max()
except KeyError: # pragma: no cover
return 0
try:
powermean = strokesdf['power'].mean()
except KeyError: # pragma: no cover
powermean = 0
if powermean != 0:
thesecs = totaltime
maxt = 1.05 * thesecs
if maxt > 0:
logarr = datautils.getlogarr(maxt)
dfgrouped = strokesdf.groupby(['workoutid'])
delta, cpvalues, avgpower = datautils.getcp(dfgrouped, logarr)
df = pd.DataFrame({
'delta': delta,
'cp': cpvalues,
'id': workoutid,
})
df.to_parquet(filename, engine='fastparquet',
compression='GZIP')
return 1
return 1 # pragma: no cover
@app.task
def handle_sendemail_invite_accept(email, name, teamname, managername,
@@ -3251,23 +3172,22 @@ graphql_url = "https://rp3rowing-app.com/graphql"
@app.task
def handle_update_wps(rid, types, ids, mode, debug=False, **kwargs):
df = getsmallrowdata_db(['time', 'driveenergy'], ids=ids)
df = read_data(['time', 'driveenergy'], ids=ids)
try:
mask = df['driveenergy'] > 100
except (KeyError, TypeError): # pragma: no cover
return 0
try:
wps_median = int(df.loc[mask, 'driveenergy'].median())
wps_median = int(df.filter(pl.col("driveenergy")>100)["driveenergy"].median())
rower = Rower.objects.get(id=rid)
if mode == 'water':
rower.median_wps = wps_median
else: # pragma: no cover
rower.median_wps_erg = wps_median
rower.save()
except ValueError: # pragma: no cover
return 0
rower = Rower.objects.get(id=rid)
if mode == 'water':
rower.median_wps = wps_median
else:
rower.median_wps_erg = wps_median
rower.save()
wps_median = 0
except OverflowError:
wps_median = 0
except ColumnNotFoundError:
wps_median = 0
return wps_median
@@ -3647,7 +3567,7 @@ def handle_c2_async_workout(alldata, userid, c2token, c2id, delaysec,
loncoord = np.zeros(nr_rows)
try:
strokelength = strokedata.loc[:, 'strokelength']
strokelength = strokedata.loc[:,'strokelength']
except: # pragma: no cover
strokelength = np.zeros(nr_rows)
@@ -3901,7 +3821,7 @@ def fetch_strava_workout(stravatoken, oauth_data, stravaid, csvfilename, userid,
pace[np.isinf(pace)] = 0.0
try:
strokedata = pd.DataFrame({'t': 10*t,
strokedata = pl.DataFrame({'t': 10*t,
'd': 10*d,
'p': 10*pace,
'spm': spm,
@@ -3947,18 +3867,18 @@ def fetch_strava_workout(stravatoken, oauth_data, stravaid, csvfilename, userid,
starttimeunix = arrow.get(rowdatetime).timestamp()
res = make_cumvalues(0.1*strokedata['t'])
cum_time = res[0]
lapidx = res[1]
res = make_cumvalues_array(0.1*strokedata['t'].to_numpy())
cum_time = pl.Series(res[0])
lapidx = pl.Series(res[1])
unixtime = cum_time+starttimeunix
seconds = 0.1*strokedata.loc[:, 't']
seconds = 0.1*strokedata['t']
nr_rows = len(unixtime)
try:
latcoord = strokedata.loc[:, 'lat']
loncoord = strokedata.loc[:, 'lon']
latcoord = strokedata['lat']
loncoord = strokedata['lon']
if latcoord.std() == 0 and loncoord.std() == 0 and workouttype == 'water': # pragma: no cover
workouttype = 'rower'
except: # pragma: no cover
@@ -3968,29 +3888,29 @@ def fetch_strava_workout(stravatoken, oauth_data, stravaid, csvfilename, userid,
workouttype = 'rower'
try:
strokelength = strokedata.loc[:, 'strokelength']
strokelength = strokedata['strokelength']
except: # pragma: no cover
strokelength = np.zeros(nr_rows)
dist2 = 0.1*strokedata.loc[:, 'd']
dist2 = 0.1*strokedata['d']
try:
spm = strokedata.loc[:, 'spm']
except KeyError: # pragma: no cover
spm = strokedata['spm']
except (KeyError, ColumnNotFoundError): # pragma: no cover
spm = 0*dist2
try:
hr = strokedata.loc[:, 'hr']
except KeyError: # pragma: no cover
hr = strokedata['hr']
except (KeyError, ColumnNotFoundError): # pragma: no cover
hr = 0*spm
pace = strokedata.loc[:, 'p']/10.
pace = strokedata['p']/10.
pace = np.clip(pace, 0, 1e4)
pace = pace.replace(0, 300)
pace = pl.Series(pace).replace(0, 300)
velo = 500./pace
try:
power = strokedata.loc[:, 'power']
power = strokedata['power']
except KeyError: # pragma: no cover
power = 2.8*velo**3
@@ -3999,7 +3919,7 @@ def fetch_strava_workout(stravatoken, oauth_data, stravaid, csvfilename, userid,
# save csv
# Create data frame with all necessary data to write to csv
df = pd.DataFrame({'TimeStamp (sec)': unixtime,
df = pl.DataFrame({'TimeStamp (sec)': unixtime,
' Horizontal (meters)': dist2,
' Cadence (stokes/min)': spm,
' HRCur (bpm)': hr,
@@ -4019,10 +3939,10 @@ def fetch_strava_workout(stravatoken, oauth_data, stravaid, csvfilename, userid,
'cum_dist': dist2,
})
df.sort_values(by='TimeStamp (sec)', ascending=True)
df.sort('TimeStamp (sec)')
row = rowingdata.rowingdata(df=df)
row.write_csv(csvfilename, gzip=False)
row = rowingdata.rowingdata_pl(df=df)
row.write_csv(csvfilename, compressed=False)
# summary = row.allstats()
# maxdist = df['cum_dist'].max()
+1 -2
View File
@@ -32,8 +32,7 @@
<div class="mapdiv">
{{ mapdiv|safe }}
{{ mapscript|safe }}
{{ mapscript|safe }}
</div>
</li>
+3 -2
View File
@@ -37,8 +37,9 @@
<div class="mapdiv">
{{ mapdiv|safe }}
{{ mapscript|safe }}
<script>
{{ mapscript|safe }}
</script>
</div>
</li>
</ul>
+3 -1
View File
@@ -45,7 +45,9 @@
<li class="grid_4">
<div class="mapdiv">
{{ mapdiv|safe }}
{{ mapscript|safe }}
<script>
{{ mapscript|safe }}
</script>
</div>
</li>
</ul>
+1 -2
View File
@@ -54,8 +54,7 @@
<div class="mapdiv">
{{ mapdiv|safe }}
{{ mapscript|safe }}
{{ mapscript|safe }}
</div>
</li>
{% if records %}
+1 -1
View File
@@ -19,7 +19,7 @@
<h1>{{ course.name }}</h1>
<div class="mapdiv">
{{ mapdiv|safe }}
{{ mapscript|safe }}
{{ mapscript|safe }}
</div>
{% endblock %}
+3 -6
View File
@@ -102,14 +102,11 @@
</li>
{% endif %}
<li class="grid_2">
<script src="https://cdn.pydata.org/bokeh/release/bokeh-3.1.1.min.js"></script>
<script async="true" type="text/javascript">
Bokeh.set_log_level("info");
</script>
{{ interactiveplot |safe }}
<script src="https://d3js.org/d3.v6.js"></script>
{{ the_div|safe }}
{{ interactiveplot |safe }}
</li>
<li class="grid_4">
+4 -4
View File
@@ -116,7 +116,7 @@ function copyText() {
</li>
{% if user.is_authenticated and user == workout.user.user and not locked %}
<li class="grid_2">
<p>Paste link to you tube video below</p>
<p>Paste link to Youtube video below</p>
<p>Use the slider to locate start point for video on workout map</p>
<p>Playing the video will advance the data in synchonization. Use the regular YouTube
controls
@@ -169,9 +169,8 @@ function copyText() {
</li>
<li class="grid_2">
<div id="player"></div>
<script>
// 1. Code for the map
{{ mapscript | safe }}
<script>
// 2. This code loads the IFrame Player API code asynchronously.
@@ -188,7 +187,8 @@ function copyText() {
var dataplaying = false;
var videotime = 0;
var data = JSON.parse('{{ data|safe }}');
var data = JSON.parse('{{ data|safe }}');
{% for id, metric in metrics.items %}
var {{ id }}_values = data["{{ id }}"];
// console.log("{{ id }}_values",{{ id }}_values);
+71
View File
@@ -0,0 +1,71 @@
{% extends "newbase.html" %}
{% load static %}
{% load rowerfilters %}
{% block title %}Rowsandall {% endblock %}
{% block meta %}
<style type="text/css">
circle {
cursor: pointer;
}
.axis path,
.axis line {
fill: none;
stroke: black;
shape-rendering: crispEdges;
}
.axis text {
font-family: sans-serif;
font-size: 11px;
}
.tooltip {
padding: 1em;
width: 200px;
height: 200px;
background-color: #f00;
color: #fff;
position: absolute;
top: 0;
left: 0;
z-index: 999;
}
</style>
{% endblock %}
{% block main %}
<script src="https://d3js.org/d3.v3.min.js"></script>
<h1>Film Deaths</h1>
<ul class="main-content">
<li class="grid_4">
<div id="id_chart">
<script>
{{ the_script| safe }}
</script>
{{ the_div|safe }}
</div>
</li>
</ul>
{% endblock %}
{% block scripts %}
<script type='text/javascript'
src='https://ajax.googleapis.com/ajax/libs/jquery/2.1.4/jquery.min.js'>
</script>
{% endblock %}
{% block sidebar %}
{% include 'menu_analytics.html' %}
{% endblock %}
+6 -13
View File
@@ -8,16 +8,6 @@
{% localtime on %}
{% block main %}
{{ js_res | safe }}
{{ css_res| safe }}
<script src="https://cdn.bokeh.org/bokeh/release/bokeh-3.1.1.min.js"></script>
<script src="https://cdn.bokeh.org/bokeh/release/bokeh-widgets-3.1.1.min.js"></script>
<script async="true" type="text/javascript">
Bokeh.set_log_level("info");
</script>
{{ the_script |safe }}
<p>
{% if workout|previousworkout:rower.user %}
@@ -34,10 +24,9 @@
<h1>Flexible Chart</h1>
<ul class="main-content">
<canvas hidden id="canvas"></canvas>
<li class="grid_4">
<div id="theplot" class="flexplot">
{{ the_div|safe }}
</div>
{{ the_div|safe }}
</li>
<li class="grid_2">
<form enctype="multipart/form-data"
@@ -96,6 +85,10 @@
</li>
</ul>
<script src="https://d3js.org/d3.v6.js"></script>
{{ the_script |safe }}
{% endblock %}
{% endlocaltime %}
+3 -9
View File
@@ -8,16 +8,8 @@
{% localtime on %}
{% block main %}
{{ js_res | safe }}
{{ css_res| safe }}
<script src="https://d3js.org/d3.v6.js"></script>
<script src="https://cdn.pydata.org/bokeh/release/bokeh-3.1.1.min.js"></script>
<script src="https://cdn.pydata.org/bokeh/release/bokeh-widgets-3.1.1.min.js"></script>
<script async="true" type="text/javascript">
Bokeh.set_log_level("info");
</script>
{{ the_script |safe }}
<p>
{% if workout|previousworkout:rower.user %}
@@ -34,6 +26,7 @@
<h1>Chart Stack</h1>
<ul class="main-content">
<canvas hidden id="canvas"></canvas>
<li class="grid_4">
<div id="theplot">
{{ the_div|safe }}
@@ -57,6 +50,7 @@
</form>
</li>
</ul>
{{ the_script |safe }}
{% endblock %}
{% endlocaltime %}
+4 -9
View File
@@ -6,25 +6,20 @@
{% block main %}
{{ js_res | safe }}
{{ css_res| safe }}
<script src="https://code.jquery.com/jquery-3.4.1.min.js"></script>
<script src="https://d3js.org/d3.v6.js"></script>
<script src="https://cdn.pydata.org/bokeh/release/bokeh-3.1.1.min.js"></script>
<script src="https://cdn.pydata.org/bokeh/release/bokeh-widgets-3.1.1.min.js"></script>
<script async="true" type="text/javascript">
Bokeh.set_log_level("info");
</script>
{{ the_script |safe }}
<h1>Force Curve Analysis for {{ rower.user.first_name }} {{ rower.user.last_name }}</h1>
<ul class="main-content">
<canvas hidden id="canvas"></canvas>
{% if the_div %}
<li class="grid_4">
<div id="theplot" class="flexplot">
{{ the_div|safe }}
{{ the_script |safe }}
</div>
</li>
{% endif %}
+14 -34
View File
@@ -8,32 +8,26 @@
{% localtime on %}
{% block main %}
{{ js_res | safe }}
{{ css_res| safe }}
<script src="https://d3js.org/d3.v6.js"></script>
<script src="https://cdn.pydata.org/bokeh/release/bokeh-3.1.1.min.js"></script>
<script src="https://cdn.pydata.org/bokeh/release/bokeh-widgets-3.1.1.min.js"></script>
<script async="true" type="text/javascript">
Bokeh.set_log_level("info");
</script>
{{ the_script |safe }}
<h1>Empower Force Curve</h1>
<ul class="main-content">
<canvas hidden id="canvas"></canvas>
<li class="grid_4">
<div id="theplot" class="flexplot">
{{ the_div|safe }}
</div>
{{ the_script |safe }}
</div>
</li>
<li class="grid_4">
<form enctype="multipart/form-data" action="" method="post">
{% csrf_token %}
<table>
{{ form.as_table }}
</table>
{{ form.as_table }}
</table>
<div class="buttoncontainer">
<input name="chartform" type="submit" class="button"
value="Update Chart">
@@ -51,28 +45,14 @@
<script src="https://code.jquery.com/jquery-3.4.1.min.js"></script>
<script>
$(document).ready(function() {
var slider_min_spm = Bokeh.documents[0].get_model_by_name('min_spm_slider')
slider_min_spm.value = {{ spm_min }}
console.log('min spm value after:', slider_min_spm.value)
var slider_max_spm = Bokeh.documents[0].get_model_by_name('max_spm_slider')
slider_max_spm.value = {{ spm_max }}
console.log('max spm value after:', slider_max_spm.value)
var slider_min_work = Bokeh.documents[0].get_model_by_name('min_work_slider')
slider_min_work.value = {{ work_min }}
console.log('min work value after:', slider_min_work.value)
var slider_max_work = Bokeh.documents[0].get_model_by_name('max_work_slider')
slider_max_work.value = {{ work_max }}
console.log('max work value after:', slider_max_work.value)
var slider_min_dist = Bokeh.documents[0].get_model_by_name('min_dist_slider')
slider_min_dist.value = {{ dist_min }}
console.log('min dist value after:', slider_min_dist.value)
var slider_max_dist = Bokeh.documents[0].get_model_by_name('max_dist_slider')
slider_max_dist.value = {{ dist_max }}
console.log('max dist value after:', slider_max_dist.value)
var annotation = Bokeh.documents[0].get_model_by_name('annotation')
annotation.value = "{{ annotation }}"
console.log('Annotation set to ', annotation.value)
$(document).ready(function() {
var label = $('label[for="' + $("#id_plotcircles").attr('id') + '"]');
label.attr("class", "hidden")
var label2 = $('label[for="' + $("#id_plotlines").attr('id') + '"]');
label2.attr("class", "hidden")
var label3 = $('label[for="' + $("#id_includereststrokes").attr('id') + '"]');
label3.attr("class", "hidden")
});
</script>
+6 -11
View File
@@ -60,17 +60,6 @@
{% block main %}
<script src="https://cdn.pydata.org/bokeh/release/bokeh-3.1.1.min.js"></script>
<script async="true" type="text/javascript">
Bokeh.set_log_level("info");
</script>
<div id="id_script">
{{ chartscript |safe }}
</div>
{% if rower.user %}
<h1>Gold Medal Scores for {{ rower.user.first_name }} </h1>
{% else %}
@@ -79,6 +68,7 @@
<ul class="main-content">
<canvas hidden id="canvas"></canvas>
<li class="grid_4">
<p>Hover over the workout to see details, click on the workout to open in a
separate page.</p>
@@ -136,6 +126,11 @@
</li>
</ul>
<script src="https://d3js.org/d3.v7.min.js"></script>
<div id="id_script">
{{ chartscript |safe }}
</div>
{% endblock %}
+16 -15
View File
@@ -6,6 +6,7 @@
{% block main %}
<script src="https://code.jquery.com/jquery-1.9.1.min.js"></script>
<script src="https://d3js.org/d3/v6/js"></script>
<script>
$(function() {
@@ -86,7 +87,6 @@
<li class="grid_4">
<div id="id_chart">
{{ the_div|safe }}
</div>
</li>
@@ -123,22 +123,23 @@
<script>
$(function($) {
console.log('loading script');
$.getJSON(window.location.protocol + '//'+window.location.host + '/rowers/histodata/', function(json) {
console.log('got script');
var counter=0;
var script = json.script;
var div = json.div;
console.log('set vars');
$("#id_sitready").remove();
console.log('sitready removed');
$("#id_chart").append(div);
console.log('div appended');
$("#id_script").append("<script>"+script+"</s"+"cript>");
console.log('script changed');
console.log('loading script');
$.getJSON(window.location.protocol + '//'+window.location.host + '/rowers/histodata/', function(json) {
console.log('got script');
var counter=0;
var script = json.script;
var div = json.div;
console.log(div)
console.log('set vars');
$("#id_sitready").remove();
console.log('sitready removed');
$("#id_chart").append(div);
console.log('div appended');
$("#id_script").append("<script>"+script+"</s"+"cript>");
console.log('script changed');
});
});
});
</script>
+3 -5
View File
@@ -6,12 +6,9 @@
{% block main %}
<script src="https://cdn.pydata.org/bokeh/release/bokeh-3.1.1.min.js"></script>
<script async="true" type="text/javascript">
Bokeh.set_log_level("info");
</script>
<script src="https://d3js.org/d3.v6.js"></script>
{{ interactiveplot |safe }}
@@ -24,6 +21,7 @@
</li>
</ul>
{{ interactiveplot |safe }}
{% endif %}
{% endblock %}
+91 -97
View File
@@ -5,25 +5,14 @@
{% block title %}Rowsandall {% endblock %}
{% block main %}
<div id="id_css_res">
<link rel="stylesheet" href="https://cdn.pydata.org/bokeh/release/bokeh-3.1.1.min.css" type="text/css" />
<link rel="stylesheet" href="https://cdn.pydata.org/bokeh/release/bokeh-widgets-3.1.1.min.css" type="text/css" />
</div>
<div id="id_js_res">
<script src="https://cdn.pydata.org/bokeh/release/bokeh-3.1.1.min.js"></script>
<script src="https://cdn.pydata.org/bokeh/release/bokeh-widgets-3.1.1.min.js"></script>
<script src="https://d3js.org/d3.v7.min.js"></script>
</div>
<script async="true" type="text/javascript">
Bokeh.set_log_level("info");
</script>
<div id="id_script">
</div>
<div id="t_script">
{{ tscript|safe}}
</div>
<div id="activities_script">
</div>
@@ -52,35 +41,35 @@
<input type="submit" value="Submit"/>
</form>
</p>
</li>
<li class="grid_2">
</li>
<li class="grid_2">
<h2>All workouts</h2>
<p>
<table class="listtable shortpadded">
<tbody>
<tr>
<td>Total Distance</td><td>{{ totalsdict|lookup:"distance"}} meters</td>
</tr>
<tr>
<td>Total Duration</td><td>{{ totalsdict|lookup:"duration"}} </td>
</tr>
<tr>
<td>Number of workouts</td><td>{{ totalsdict|lookup:"nrworkouts"}}</td>
</tr>
<tr>
<td>Average heart rate</td><td><span id="total_hr"></span> bpm</td>
</tr>
<tr>
<td>Maximum heart rate</td><td><span id="total_maxhr"></span> bpm</td>
</tr>
<tr>
<td>Average power</td><td><span id="total_power"></span> W</td>
</tr>
<tr>
<td>Maximum power</td><td><span id="total_maxpower"></span> W</td>
</tr>
</tbody>
<tbody>
<tr>
<td>Total Distance</td><td>{{ totalsdict|lookup:"distance"}} meters</td>
</tr>
<tr>
<td>Total Duration</td><td>{{ totalsdict|lookup:"duration"}} </td>
</tr>
<tr>
<td>Number of workouts</td><td>{{ totalsdict|lookup:"nrworkouts"}}</td>
</tr>
<tr>
<td>Average heart rate</td><td><span id="total_hr"></span> bpm</td>
</tr>
<tr>
<td>Maximum heart rate</td><td><span id="total_maxhr"></span> bpm</td>
</tr>
<tr>
<td>Average power</td><td><span id="total_power"></span> W</td>
</tr>
<tr>
<td>Maximum power</td><td><span id="total_maxpower"></span> W</td>
</tr>
</tbody>
</table>
</p>
</li>
@@ -90,46 +79,50 @@
</li>
<li class="grid_2">
{{ tdiv|safe }}
<div id="t_script">
{{ tscript|safe}}
</div>
</li>
<li class="grid_2">
<div id="id_chart">
{{ totaldiv|safe }}
</div>
</li>
{% for ddict in typedicts %}
<li class="grid_1">
<h2>{{ ddict|lookup:"wtype"}}</h2>
<p>
<table class="listtable shortpadded">
<tbody>
<tr>
<td>Total Distance</td><td>{{ ddict|lookup:"distance"}} meters</td>
</tr>
<tr>
<td>Total Duration</td><td>{{ ddict|lookup:"duration"}} </td>
</tr>
<tr>
<td>Number of workouts</td><td>{{ ddict|lookup:"nrworkouts"}}</td>
</tr>
<tr>
<td>Average heart rate</td><td><span id="{{ ddict|lookup:'id'}}_hr"></span> bpm</td>
</tr>
<tr>
<td>Maximum heart rate</td><td><span id="{{ ddict|lookup:'id' }}_hrmax"></span> bpm</td>
</tr>
<tr>
<td>Average power</td><td><span id="{{ ddict|lookup:'id'}}_power"></span> W</td>
</tr>
<tr>
<td>Maximum power</td><td><span id="{{ ddict|lookup:'id'}}_powermax"></span> W</td>
</tr>
</tbody>
<tbody>
<tr>
<td>Total Distance</td><td>{{ ddict|lookup:"distance"}} meters</td>
</tr>
<tr>
<td>Total Duration</td><td>{{ ddict|lookup:"duration"}} </td>
</tr>
<tr>
<td>Number of workouts</td><td>{{ ddict|lookup:"nrworkouts"}}</td>
</tr>
<tr>
<td>Average heart rate</td><td><span id="{{ ddict|lookup:'id'}}_hr"></span> bpm</td>
</tr>
<tr>
<td>Maximum heart rate</td><td><span id="{{ ddict|lookup:'id' }}_hrmax"></span> bpm</td>
</tr>
<tr>
<td>Average power</td><td><span id="{{ ddict|lookup:'id'}}_power"></span> W</td>
</tr>
<tr>
<td>Maximum power</td><td><span id="{{ ddict|lookup:'id'}}_powermax"></span> W</td>
</tr>
</tbody>
</table>
</p>
</li>
{% endfor %}
</ul>
@@ -142,40 +135,41 @@
<script>
$(function($) {
console.log('loading script for chart');
var ed = '{{ senddate|date:"Y-m-d" }}'
console.log('End',ed);
console.log(window.location.protocol + '//'+window.location.host + '/rowers/history/user/{{ rower.user.id }}/data/?startdate={{ sstartdate|date:"Y-m-d" }}&enddate={{ senddate|date:"Y-m-d" }}&workouttype={{ workouttype }}&yaxis={{ yaxis }}')
$.getJSON(window.location.protocol + '//'+window.location.host + '/rowers/history/user/{{ rower.user.id }}/data/?startdate={{ sstartdate|date:"Y-m-d" }}&enddate={{ senddate|date:"Y-m-d" }}&workouttype={{ workouttype }}&yaxis={{ yaxis }}', function(json) {
console.log('loading script for chart');
var ed = '{{ senddate|date:"Y-m-d" }}'
console.log('End',ed);
console.log(window.location.protocol + '//'+window.location.host + '/rowers/history/user/{{ rower.user.id }}/data/?startdate={{ sstartdate|date:"Y-m-d" }}&enddate={{ senddate|date:"Y-m-d" }}&workouttype={{ workouttype }}&yaxis={{ yaxis }}')
$.getJSON(window.location.protocol + '//'+window.location.host + '/rowers/history/user/{{ rower.user.id }}/data/?startdate={{ sstartdate|date:"Y-m-d" }}&enddate={{ senddate|date:"Y-m-d" }}&workouttype={{ workouttype }}&yaxis={{ yaxis }}', function(json) {
var script = json.script;
var div = json.div;
var totalsdict = json.totalsdict
var listofdicts = json.listofdicts
var activities_script = json.activities_script
var activities_chart = json.activities_chart
$("#id_sitready").remove();
$("#id_chart").append(div);
$("#id_script").append(script);
$("#id_activities").append(activities_chart)
$("#activities_script").append(activities_script)
$("#total_hr").append(totalsdict.hrmean);
$("#total_maxhr").append(totalsdict.hrmax);
$("#total_power").append(totalsdict.powermean);
$("#total_maxpower").append(totalsdict.powermax);
$("#selectors").hide();
var script = json.script;
var div = json.div;
var totalsdict = json.totalsdict
var listofdicts = json.listofdicts
var activities_script = json.activities_script
var activities_chart = json.activities_chart
$("#id_sitready").remove();
$("#id_chart").append(div);
// $("#id_script").append("<s"+"cript>"+script+"</s"+"cript>");
$("#id_script").append(script);
$("#id_activities").append(activities_chart)
$("#activities_script").append(activities_script)
$("#total_hr").append(totalsdict.hrmean);
$("#total_maxhr").append(totalsdict.hrmax);
$("#total_power").append(totalsdict.powermean);
$("#total_maxpower").append(totalsdict.powermax);
listofdicts.forEach(function(item){
var id = "#"+item.id+"_hr";
$(id).append(item.hrmean);
id = "#"+item.id+"_hrmax";
$(id).append(item.hrmax);
id = "#"+item.id+"_power";
$(id).append(item.powermean);
id = "#"+item.id+"_powermax";
$(id).append(item.powermax);
})
});
listofdicts.forEach(function(item){
var id = "#"+item.id+"_hr";
$(id).append(item.hrmean);
id = "#"+item.id+"_hrmax";
$(id).append(item.hrmax);
id = "#"+item.id+"_power";
$(id).append(item.powermean);
id = "#"+item.id+"_powermax";
$(id).append(item.powermax);
})
});
});
</script>
{% endblock %}
+3 -7
View File
@@ -6,24 +6,20 @@
{% block main %}
{{ js_res | safe }}
{{ css_res| safe }}
<script src="https://cdn.pydata.org/bokeh/release/bokeh-3.1.1.min.js"></script>
<script src="https://cdn.pydata.org/bokeh/release/bokeh-widgets-3.1.1.min.js"></script>
<script src="https://d3js.org/d3.v6.js"></script>
<script async="true" type="text/javascript">
Bokeh.set_log_level("info");
</script>
{{ the_script |safe }}
<h1>In-Stroke Analysis for {{ rower.user.first_name }} {{ rower.user.last_name }}</h1>
<ul class="main-content">
<canvas hidden id="canvas"></canvas>
{% if the_div %}
<li class="grid_4">
<div id="theplot" class="flexplot">
{{ the_div|safe }}
{{ the_script |safe }}
</div>
</li>
{% endif %}
+23 -18
View File
@@ -11,10 +11,8 @@
<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");
@@ -30,11 +28,14 @@
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>');
$("#id_chart").replaceWith('<div id="id_chart">'+data.div+'</d'+'iv>');
$("#id_script").replaceWith('<div id="id_script">'+data.script+'</d'+'iv>');
$("#dd").empty();
$("#dd").html(data.dd);
$("#ds").empty();
$('#ds').html(data.ds)
}
});
}
@@ -119,15 +120,10 @@ $( function() {
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">
<canvas hidden id="canvas"></canvas>
<li class="grid_4" >
{% if user.rower|is_basic %}
@@ -144,7 +140,7 @@ $( function() {
</table>
<div id="slider-range"></div>
<p>
<label for="amountspm">Active Range:</label>
<label for="amountspm">Stroke Rate:</label>
<input type="text" id="amountspm" readonly style="border:0; color:#1c75bc; font-weight:bold;">
</p>
<div id="slider-timerange"></div>
@@ -168,15 +164,24 @@ $( function() {
</p>
</li>
<li class="grid_4">
<script src="https://d3js.org/d3.v6.js"></script>
<div id="id_chart" class="flexplot">
{{ the_div|safe }}
</div>
</li>
<li class="grid_4">
<div id="dd" class="flexplot">
{{ dd|safe }}
<div id="id_script">
{{ the_script |safe }}
</div>
</li>
<script src="https://d3js.org/d3.v6.js"></script>
<li class="grid_2" id="interactive">
<div id="dd" class="flexplot">
{{ dd|safe }}
</div>
<div id="ds">
{{ ds |safe }}
</div>
</li>
</ul>
+4 -15
View File
@@ -10,7 +10,7 @@
</script>
<script
type='text/javascript'
src='https://ajax.googleapis.com/ajax/libs/jquery/2.1.4/jquery.min.js'>
src='https://ajax.googleapis.com/ajax/libs/jquery/2.1.4/jquery.min.js'>
</script>
{% endblock %}
@@ -229,22 +229,11 @@
Total meters: {{ totalmeters }}. Total time {{ totalhours }}:{{ totalminutes }}h.
<a href="/rowers/history/">Dig deeper</a>.
</p>
<p>
Activity chart by
<a href="{{ request.get_path }}?yaxis=trimp">TRIMP</a>,
<a href="{{ request.get_path }}?yaxis=rscore">rScore</a>,
<a href="{{ request.get_path }}?yaxis=duration">Time</a>.
</p>
<link rel="stylesheet" href="https://cdn.pydata.org/bokeh/release/bokeh-3.1.1.min.css" type="text/css" />
<link rel="stylesheet" href="https://cdn.pydata.org/bokeh/release/bokeh-widgets-3.1.1.min.css" type="text/css" />
<script src="https://cdn.pydata.org/bokeh/release/bokeh-3.1.1.min.js"></script>
<script async="true" type="text/text/javascript">
Bokeh.set_log_level("info")
</script>
{{ interactiveplot |safe }}
<script src="https://d3js.org/d3.v7.min.js"></script>
{{ the_div |safe }}
{{ interactiveplot |safe }}
</li>
{% if announcements %}
<li class="grid_4">
+1 -2
View File
@@ -26,8 +26,7 @@
<div style="height:100%;" id="theplot" class="flexplot mapdiv">
{{ mapdiv|safe }}
{{ mapscript|safe }}
{{ mapscript|safe }}
</li>
</ul>
+3 -1
View File
@@ -25,7 +25,9 @@
<div class="mapdiv">
{{ mapdiv|safe }}
{{ mapscript|safe }}
<script>
{{ mapscript|safe }}
</script>
</div>
-25
View File
@@ -101,11 +101,6 @@
<i class="fas fa-dumbbell fa-fw"></i>&nbsp;Force Curve
</a>
</li>
<li id="chart-otwpower">
<a href="/rowers/workout/{{ workout.id|encode }}/interactiveotwplot/">
<i class="fal fa-calculator-alt fa-fw"></i>&nbsp;Corrected Pace Plot
</a>
</li>
{% endif %}
</ul>
</li>
@@ -294,26 +289,6 @@
<label for="group-advanced">Advanced</label>
<ul>
{% if workout|water %}
<li id="advanced-wind">
<a href="/rowers/workout/{{ workout.id|encode }}/wind/">
<i class="fas fa-pennant fa-fw"></i>&nbsp;Wind
</a>
</li>
<li id="advanced-stream">
<a href="/rowers/workout/{{ workout.id|encode }}/stream/">
<i class="fas fa-stream fa-fw"></i>&nbsp;Stream
</a>
</li>
<li id="advanced-otwpower">
<a href="/rowers/workout/{{ workout.id|encode }}/otwsetpower/">
<i class="fas fa-calculator-alt fa-fw"></i>&nbsp;OTW Power
</a>
</li>
<li id="advanced-otwpower">
<a href="/rowers/workout/{{ workout.id|encode }}/zeropower-confirm/">
<i class="fas fa-eraser fa-fw"></i>&nbsp;Remove Power Data
</a>
</li>
{% if 'speedcoach2' in workout.workoutsource or 'nklinklogbook' in workout.workoutsource %}
<li id="advanced-usegps">
<a href="/rowers/workout/{{ workout.id|encode }}/otwusegps/">
+3 -5
View File
@@ -6,13 +6,10 @@
{% block main %}
<script src="https://cdn.pydata.org/bokeh/release/bokeh-3.1.1.min.js"></script>
<script async="true" type="text/javascript">
Bokeh.set_log_level("info");
</script>
<script src="https://d3js.org/d3.v6.js"></script>
{{ interactiveplot |safe }}
<h1>Interactive Comparison</h1>
<form enctype="multipart/form-data" action="" method="post">
@@ -20,6 +17,7 @@
<li class="grid_4">
<div>
{{ the_div|safe }}
{{ interactiveplot |safe }}
</div>
</li>
-1
View File
@@ -3,7 +3,6 @@
<div class="mapdiv">
{{ mapdiv|safe }}
{{ mapscript|safe }}
</div>
</li>
+19 -15
View File
@@ -8,6 +8,15 @@
<script type='text/javascript'
src='https://ajax.googleapis.com/ajax/libs/jquery/2.1.4/jquery.min.js'>
</script>
{% endblock %}
{% block main %}
<script src="https://d3js.org/d3.v6.js">
</script>
<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.4.1/jquery.min.js"></script>
<script>
function submit_form() {
@@ -25,11 +34,14 @@
dataType: 'json',
success: function(data) {
console.log(data.div);
// var parsedJSON = $.parseJSON(data); //
$("#id_script").replaceWith('<div id="id_script">'+data.script+'</d'+'iv>');
// $('#id_script').remove()
$("#id_chart").replaceWith('<div id="id_chart">'+data.div+'</d'+'iv>');
$("#id_chart").append(data.script)
$("#endfitness").html(data.endfitness)
$("#endfatigue").html(data.endfatigue)
$("#endform").html(data.endform)
@@ -59,19 +71,6 @@
});
});
</script>
{% endblock %}
{% block main %}
<script src="https://cdn.pydata.org/bokeh/release/bokeh-3.1.1.min.js"></script>
<script async="true" type="text/javascript">
Bokeh.set_log_level("info");
</script>
<div id="id_script">
{{ chartscript |safe }}
</div>
@@ -83,10 +82,12 @@
<ul class="main-content">
<canvas hidden id="canvas"></canvas>
<li class="grid_4">
<div id="id_chart">
{{ the_div|safe }}
</div>
</div>
</li>
<li class="grid_1">
<form id="performanceform" enctype="multipart/form-data" method="post">
@@ -188,6 +189,9 @@
</li>
</ul>
<div id="id_script">
{{ chartscript |safe }}
</div>
{% endblock %}
+53
View File
@@ -0,0 +1,53 @@
{% extends "newbase.html" %}
{% load static %}
{% load rowerfilters %}
{% block title %}Rowsandall {% endblock %}
{% block main %}
<div id="id_css_res">
<link rel="stylesheet" href="https://cdn.pydata.org/bokeh/release/bokeh-3.1.1.min.css" type="text/css" />
<link rel="stylesheet" href="https://cdn.pydata.org/bokeh/release/bokeh-widgets-3.1.1.min.css" type="text/css" />
</div>
<div id="id_js_res">
<script type="text/javascript" src="https://cdn.bokeh.org/bokeh/release/bokeh-3.1.1.min.js"></script>
<script type="text/javascript" src="https://cdn.bokeh.org/bokeh/release/bokeh-gl-3.1.1.min.js"></script>
<script type="text/javascript" src="https://cdn.bokeh.org/bokeh/release/bokeh-widgets-3.1.1.min.js"></script>
<script type="text/javascript" src="https://cdn.bokeh.org/bokeh/release/bokeh-tables-3.1.1.min.js"></script>
<script type="text/javascript" src="https://cdn.bokeh.org/bokeh/release/bokeh-mathjax-3.1.1.min.js"></script>
<script type="text/javascript" src="https://cdn.bokeh.org/bokeh/release/bokeh-api-3.1.1.min.js"></script>
<script type="text/javascript" src="https://cdn.bokeh.org/bokeh/release/bokeh-api-3.1.1.min.js"></script>
</div>
<script src="https://code.jquery.com/jquery-1.9.1.min.js"></script>
<h1>Sleep</h1>
<ul class="main-content">
<li class="grid_4">
<div id="id_chart">
<script>
{{ the_script| safe }}
</script>
{{ the_div|safe }}
</div>
</li>
</ul>
{% endblock %}
{% block scripts %}
<script type='text/javascript'
src='https://ajax.googleapis.com/ajax/libs/jquery/2.1.4/jquery.min.js'>
</script>
{% endblock %}
{% block sidebar %}
{% include 'menu_analytics.html' %}
{% endblock %}
+1 -1
View File
@@ -23,7 +23,7 @@
</li>
<li class="grid_2">
<script src="https://cdn.pydata.org/bokeh/release/bokeh-3.1.1.min.js"></script>
<script src="https://d3js.org/d3.v6.js"></script>
<script async="true" type="text/javascript">
Bokeh.set_log_level("info");
</script>
+121 -124
View File
@@ -8,26 +8,26 @@
{% block scripts %}
{% include "monitorjobs.html" %}
<link rel="stylesheet" href="//code.jquery.com/ui/1.12.1/themes/base/jquery-ui.css">
<script src="https://code.jquery.com/jquery-1.12.4.js"></script>
<script src="https://code.jquery.com/ui/1.12.1/jquery-ui.js"></script>
<script>
<script src="https://code.jquery.com/jquery-1.12.4.js"></script>
<script src="https://code.jquery.com/ui/1.12.1/jquery-ui.js"></script>
<script>
$( function() {
console.log({{ activeminutesmin }}, {{ activeminutesmax}}, 'active range');
$( "#slider-range" ).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-range" ).slider( "values", 0 ) +
" min - " + $( "#slider-range" ).slider( "values", 1 ) + " min ");
console.log({{ activeminutesmin }}, {{ activeminutesmax}}, 'active range');
$( "#slider-range" ).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-range" ).slider( "values", 0 ) +
" min - " + $( "#slider-range" ).slider( "values", 1 ) + " min ");
} );
</script>
</script>
{% endblock %}
{% block main %}
@@ -42,78 +42,75 @@
{% endif %}
</p>
<p>
<table width=100%>
<tr>
<th>Name</th><td>{{ workout.name }}</td>
</tr><tr>
<th>Distance:</th><td>{{ workout.distance }}m</td>
</tr><tr>
<th>Duration:</th><td>{{ workout.duration |durationprint:"%H:%M:%S.%f" }}</td>
</tr><tr>
<th>Public link to this workout</th>
<td>
<table width=100%>
<tr>
<th>Name</th><td>{{ workout.name }}</td>
</tr><tr>
<th>Distance:</th><td>{{ workout.distance }}m</td>
</tr><tr>
<th>Duration:</th><td>{{ workout.duration |durationprint:"%H:%M:%S.%f" }}</td>
</tr><tr>
<th>Public link to this workout</th>
<td>
<a href="/rowers/workout/{{ workout.id|encode }}/">https://rowsandall.com/rowers/workout/{{ workout.id|encode }}</a>
<td>
</tr>
</table>
</p>
<td>
</tr>
</table>
</p>
<h1>Edit Workout Interval Data</h1>
<p>
Use "Interval Shorthand", "Feeling lucky?", or "Intervals by power/pace" methods below to edit work and rest
intervals. To save your work, press the Save button above the updated summary.
Use "Interval Shorthand", "Feeling lucky?", or "Intervals by power/pace" methods below to edit work and rest
intervals. To save your work, press the Save button above the updated summary.
</p>
<ul class="main-content">
<li class="grid_2">
<h1>Updated Summary</h1>
<form enctype="multipart/form-data" action="/rowers/workout/{{ workout.id|encode }}/editintervals/" method="post">
{% csrf_token %}
<input type="hidden" name="{{ savebutton }}" value="{{ intervalstring }}">
<input type="hidden" name="nrintervals" value={{ nrintervals }}>
{% for key,value in formvalues.items %}
<input type="hidden" name="{{ key }}" value="{{ value|safe }}">
{% endfor %}
<p>
<input type="submit" value="Save">
</p>
<span>
<a href="">Reset to last saved</a>
&nbsp;
<a href="/rowers/workout/{{ workout.id|encode }}/restore/">Restore Original data</a>
</span>
</form>
<p>
<pre>
{{ intervalstats }}
</pre>
</p>
</li>
<li class="grid_2">
<script src="https://cdn.pydata.org/bokeh/release/bokeh-3.1.1.min.js"></script>
<script async="true" type="text/javascript">
Bokeh.set_log_level("info");
</script>
{{ interactiveplot |safe }}
{{ the_div |safe }}
</li>
<li class="grid_2">
<h1>Feeling lucky?</h1>
<p>This new, experimental feature tries to find intervals by looking for patterns in the data. It does not
autodetect rest and work intervals yet. The resulting "interval string" is copied to the Interval Shorthand
section below, where you can edit it.
<form enctype="multipart/form-data" method="post">
<input type="hidden" name="ruptures" value="ruptures">
<table width=100%>
{{ ruptureform.as_table }}
</table>
{% csrf_token %}
<input class="button" type="submit" value="I'm feeling lucky">
</form>
</p>
</li>
<li class="grid_2">
<li class="grid_2">
<h1>Updated Summary</h1>
<form enctype="multipart/form-data" action="/rowers/workout/{{ workout.id|encode }}/editintervals/" method="post">
{% csrf_token %}
<input type="hidden" name="{{ savebutton }}" value="{{ intervalstring }}">
<input type="hidden" name="nrintervals" value={{ nrintervals }}>
{% for key,value in formvalues.items %}
<input type="hidden" name="{{ key }}" value="{{ value|safe }}">
{% endfor %}
<p>
<input type="submit" value="Save">
</p>
<span>
<a href="">Reset to last saved</a>
&nbsp;
<a href="/rowers/workout/{{ workout.id|encode }}/restore/">Restore Original data</a>
</span>
</form>
<p>
<pre>
{{ intervalstats }}
</pre>
</p>
</li>
<li class="grid_2">
<script src="https://d3js.org/d3.v6.js"></script>
{{ the_div |safe }}
{{ interactiveplot |safe }}
</li>
<li class="grid_2">
<h1>Feeling lucky?</h1>
<p>This new, experimental feature tries to find intervals by looking for patterns in the data. It does not
autodetect rest and work intervals yet. The resulting "interval string" is copied to the Interval Shorthand
section below, where you can edit it.
<form enctype="multipart/form-data" method="post">
<input type="hidden" name="ruptures" value="ruptures">
<table width=100%>
{{ ruptureform.as_table }}
</table>
{% csrf_token %}
<input class="button" type="submit" value="I'm feeling lucky">
</form>
</p>
</li>
<li class="grid_2">
<h1>Interval Shorthand</h1>
<p>
See the how-to <a href="#howto">at the bottom of this page</a> for details on how to use this form.
@@ -125,26 +122,26 @@
{% csrf_token %}
<input class="button" type="submit" value="Update">
</form>
</li>
<li class="grid_2">
</li>
<li class="grid_2">
<h1>Intervals by Power/Pace</h1>
<p>With this form, you can specify a power or pace level. Everything faster/harder than the
specified pace/power will become a work interval. Everything slower will become a rest
interval. Use the slider to limit the active range. Everything outside the active range will
become rest (warming up and cooling down).
become rest (warming up and cooling down).
</p>
<form ecntype="multipart/form-data" method="post">
<div id="slider-range"></div>
<p>
<label for="amount">Active Range:</label>
<input type="text" id="amount" readonly style="border:0; color:#1c75bc; font-weight:bold;">
</p>
<div id="slider-range"></div>
<p>
<label for="amount">Active Range:</label>
<input type="text" id="amount" readonly style="border:0; color:#1c75bc; font-weight:bold;">
</p>
<table>
{{ powerupdateform.as_table }}
</table>
{% csrf_token %}
<input class="button" type="submit" value="Submit">
</form>
@@ -153,11 +150,11 @@
<h1 id="howto">Interval Shorthand How-To</h1>
<p>This is a quick way to enter the intervals using a special mini-language.</p>
<p>You enter something like <em>8x500m/3min</em>, press "Update" and the site will interpret this for you and update the summary on the right. If you're happy with the result, press the green Save button to update the values. Nothing will be changed permanently until you hit Save.</p>
<p>Special characters are <em>x</em> (times), <em>+</em> and <em>/</em> (denotes a rest interval), as well as <em>(</em> and <em>)</em>. Units are <em>min</em> (minutes), <em>sec</em> (seconds), <em>m</em> (meters) and <em>km</em> (km). </p>
<p>A typical interval is described as "<b>10min/5min</b>", with the work part before the "<b>/</b>" and the rest part after it. A zero rest can be omitted, so a single 1000m piece could be described either as "<b>1km</b>" or "<b>1000m</b>". The basic units can be combined with "<b>+</b>" and "<b>Nx</b>". You can use parentheses as in the example below.</p>
<p>Here are a few examples</p>
<table class="listtable" width=100%>
<tr>
@@ -182,32 +179,32 @@
</tr>
</table>
</li>
{% if courses %}
<li>
<h1>Interval by Course</h1>
<p>
This functionality allows you to record a time on a set course that you've rowed during the workout.
The summary will be updated to show time on course, and you can compare this with other
attempts.
</p>
<p>
{% if rower.share_course_results %}
You are currently sharing your course results with all Rowsandall users.
Click <a href="/rowers/me/edit/?courseshare=false">here</a> to hide your course results.
{% else %}
You are currently hiding your course results (except for your participation in online challenges).
Click <a href="/rowers/me/edit/?courseshare=true">here</a> to hide your course results.
{% endif %}
</p>
<form ecntype="multipart/form-data" method="post">
<table>
{{ courseselectform.as_table }}
</table>
{% csrf_token %}
<input class="button" type="submit" value="Select Course">
</form>
</li>
{% endif %}
{% if courses %}
<li>
<h1>Interval by Course</h1>
<p>
This functionality allows you to record a time on a set course that you've rowed during the workout.
The summary will be updated to show time on course, and you can compare this with other
attempts.
</p>
<p>
{% if rower.share_course_results %}
You are currently sharing your course results with all Rowsandall users.
Click <a href="/rowers/me/edit/?courseshare=false">here</a> to hide your course results.
{% else %}
You are currently hiding your course results (except for your participation in online challenges).
Click <a href="/rowers/me/edit/?courseshare=true">here</a> to hide your course results.
{% endif %}
</p>
<form ecntype="multipart/form-data" method="post">
<table>
{{ courseselectform.as_table }}
</table>
{% csrf_token %}
<input class="button" type="submit" value="Select Course">
</form>
</li>
{% endif %}
</ul>
{% endblock %}
+6 -9
View File
@@ -6,25 +6,22 @@
{% block main %}
<script src="https://cdn.pydata.org/bokeh/release/bokeh-3.1.1.min.js"></script>
<script src="https://cdn.pydata.org/bokeh/release/bokeh-widgets-3.1.1.min.js"></script>
<script async="true" type="text/javascript">
Bokeh.set_log_level("info");
</script>
<script src="https://code.jquery.com/jquery-1.9.1.min.js"></script>
<h1>Training Plan - {{ plan.name }}</h1>
{{ the_script | safe }}
<ul class="main-content">
<li class="grid_4">
<div id="theplot" class="flexplot">
{{ the_div|safe }}
</div>
{{ the_div|safe }}
</li>
</ul>
<script src="https://d3js.org/d3.v6.js"></script>
{{ the_script | safe }}
{% endblock %}
{% block scripts %}
+3 -5
View File
@@ -11,17 +11,15 @@
<script async="true" type="text/javascript">
Bokeh.set_log_level("info");
</script>
<div id="id_js_res">
<script src="https://d3js.org/d3.v7.min.js"></script>
</div>
<h1>Training Zones</h1>
<div id="id_script">
</div>
<ul class="main-content">
<li class="grid_4">
<div id="id_chart">
+142 -159
View File
@@ -14,6 +14,7 @@
}
</script>
<script src="https://code.jquery.com/jquery-1.9.1.min.js"></script>
<script src="https://d3js.org/d3.v6.js"></script>
<script>
$(function() {
@@ -387,33 +388,15 @@
</script>
<div id="id_css_res">
</div>
<div id="id_js_res">
<script src="https://cdn.bokeh.org/bokeh/release/bokeh-3.1.1.min.js"
crossorigin="anonymous"></script>
<script src="https://cdn.bokeh.org/bokeh/release/bokeh-widgets-3.1.1.min.js"
crossorigin="anonymous"></script>
<script src="https://cdn.bokeh.org/bokeh/release/bokeh-tables-3.1.1.min.js"
crossorigin="anonymous"></script>
<script src="https://cdn.bokeh.org/bokeh/release/bokeh-gl-3.1.1.min.js"
crossorigin="anonymous"></script>
<script src="https://cdn.bokeh.org/bokeh/release/bokeh-mathjax-3.1.1.min.js"
crossorigin="anonymous"></script>
</div>
<script async="true" type="text/javascript">
Bokeh.set_log_level("info");
</script>
<div id="id_script">
</div>
<ul class="main-content">
<canvas hidden id="canvas"></canvas>
<li class="grid_4">
<div id="id_chart">
{{ the_div|safe }}
</div>
<div id="id_chart">
{{ the_div|safe }}
</div>
</li>
<li class="grid_4">
<p>You can use the date and search forms to search through all
@@ -438,144 +421,144 @@
{{ chartform.as_table }}
</table>
</li>
<li class="grid_2 maxheight">
<p>{{ searchform }}</p>
{% if workouts %}
<input type="checkbox" onClick="toggle(this)" /> Toggle All<br/>
<table width="100%" class="listtable">
{{ form.as_table }}
</table>
{% else %}
<p> No workouts found </p>
{% endif %}
</li>
<li class="grid_2">
{% csrf_token %}
<input name='optionsform' type="submit" value="Submit">
</form>
<li class="grid_2 maxheight">
<p>{{ searchform }}</p>
{% if workouts %}
<input type="checkbox" onClick="toggle(this)" /> Toggle All<br/>
<table width="100%" class="listtable">
{{ form.as_table }}
</table>
{% else %}
<p> No workouts found </p>
{% endif %}
</li>
{% if worldclass %}
{% if age and sex != 'not specified' %}
<li class="grid_4">
<h2>Gold Medal Standards</h2>
<p>The dashed lines are based on the
<a href="https://log.concept2.com/rankings">Concept2</a>
rankings for your age ({{ age }}), gender ({{ sex }})
and weight category ({{ weightcategory }}). The Gold Medal
Standard power is derived from the
<a href="http://www.concept2.com/indoor-rowers/racing/records/world">
World Record</a> for your category.
The percentile lines are estimates of where the percentiles
of the Concept2 rankings historically are for those of exactly
your age, gender and weight class.
</p>
<p>
For rowing on the water, the results are corrected for the expected
difference in power between the Concept2 indoor rower and power
values on the water.
</p>
</li>
<li>
<table width="100%" class="listtable">
<tbody>
<tr>
<td>
<a target="_"
href="/rowers/agegrouprecords/{{ sex }}/{{ weightcategory }}/100m/">100m
</a>
</td>
</tr>
<tr>
<td>
<a target="_"
href="/rowers/agegrouprecords/{{ sex }}/{{ weightcategory }}/500m/">500m
</a>
</td>
</tr>
<tr>
<td>
<a target="_"
href="/rowers/agegrouprecords/{{ sex }}/{{ weightcategory }}/1000m/">1000m
</a>
</td>
</tr>
<tr>
<td>
<a target="_"
href="/rowers/agegrouprecords/{{ sex }}/{{ weightcategory }}/2000m/">2000m
</a>
</td>
</tr>
<tr>
<td>
<a target="_"
href="/rowers/agegrouprecords/{{ sex }}/{{ weightcategory }}/5000m/">5000m
</a>
</td>
</tr>
<tr>
<td>
<a target="_"
href="/rowers/agegrouprecords/{{ sex }}/{{ weightcategory }}/6000m/">6000m
</a>
</td>
</tr>
<tr>
<td>
<a target="_"
href="/rowers/agegrouprecords/{{ sex }}/{{ weightcategory }}/10000m/">10000m
</a>
</td>
</tr>
<tr>
<td>
<a target="_"
href="/rowers/agegrouprecords/{{ sex }}/{{ weightcategory }}/21097m/">Half Marathon
</a>
</td>
</tr>
<tr>
<td>
<a
href="/rowers/agegrouprecords/{{ sex }}/{{ weightcategory }}/42195m/">Full Marathon
</a>
</td>
</tr>
<tr>
<td>
<a
href="/rowers/agegrouprecords/{{ sex }}/{{ weightcategory }}/1min/">1 minute
</a>
</td>
</tr>
<tr>
<td>
<a
href="/rowers/agegrouprecords/{{ sex }}/{{ weightcategory }}/4min/">4 minutes
</a>
</td>
</tr>
<tr>
<td>
<a
href="/rowers/agegrouprecords/{{ sex }}/{{ weightcategory }}/30min/">30 minutes
</a>
</td>
</tr>
<tr>
<td>
<a
href="/rowers/agegrouprecords/{{ sex }}/{{ weightcategory }}/60min/">1 hour
</a>
</td>
</tr>
</tbody>
</table>
</li>
{% endif %}
{% endif %}
<li class="grid_2">
{% csrf_token %}
<input name='optionsform' type="submit" value="Submit">
</form>
</li>
{% if worldclass %}
{% if age and sex != 'not specified' %}
<li class="grid_4">
<h2>Gold Medal Standards</h2>
<p>The dashed lines are based on the
<a href="https://log.concept2.com/rankings">Concept2</a>
rankings for your age ({{ age }}), gender ({{ sex }})
and weight category ({{ weightcategory }}). The Gold Medal
Standard power is derived from the
<a href="http://www.concept2.com/indoor-rowers/racing/records/world">
World Record</a> for your category.
The percentile lines are estimates of where the percentiles
of the Concept2 rankings historically are for those of exactly
your age, gender and weight class.
</p>
<p>
For rowing on the water, the results are corrected for the expected
difference in power between the Concept2 indoor rower and power
values on the water.
</p>
</li>
<li>
<table width="100%" class="listtable">
<tbody>
<tr>
<td>
<a target="_"
href="/rowers/agegrouprecords/{{ sex }}/{{ weightcategory }}/100m/">100m
</a>
</td>
</tr>
<tr>
<td>
<a target="_"
href="/rowers/agegrouprecords/{{ sex }}/{{ weightcategory }}/500m/">500m
</a>
</td>
</tr>
<tr>
<td>
<a target="_"
href="/rowers/agegrouprecords/{{ sex }}/{{ weightcategory }}/1000m/">1000m
</a>
</td>
</tr>
<tr>
<td>
<a target="_"
href="/rowers/agegrouprecords/{{ sex }}/{{ weightcategory }}/2000m/">2000m
</a>
</td>
</tr>
<tr>
<td>
<a target="_"
href="/rowers/agegrouprecords/{{ sex }}/{{ weightcategory }}/5000m/">5000m
</a>
</td>
</tr>
<tr>
<td>
<a target="_"
href="/rowers/agegrouprecords/{{ sex }}/{{ weightcategory }}/6000m/">6000m
</a>
</td>
</tr>
<tr>
<td>
<a target="_"
href="/rowers/agegrouprecords/{{ sex }}/{{ weightcategory }}/10000m/">10000m
</a>
</td>
</tr>
<tr>
<td>
<a target="_"
href="/rowers/agegrouprecords/{{ sex }}/{{ weightcategory }}/21097m/">Half Marathon
</a>
</td>
</tr>
<tr>
<td>
<a
href="/rowers/agegrouprecords/{{ sex }}/{{ weightcategory }}/42195m/">Full Marathon
</a>
</td>
</tr>
<tr>
<td>
<a
href="/rowers/agegrouprecords/{{ sex }}/{{ weightcategory }}/1min/">1 minute
</a>
</td>
</tr>
<tr>
<td>
<a
href="/rowers/agegrouprecords/{{ sex }}/{{ weightcategory }}/4min/">4 minutes
</a>
</td>
</tr>
<tr>
<td>
<a
href="/rowers/agegrouprecords/{{ sex }}/{{ weightcategory }}/30min/">30 minutes
</a>
</td>
</tr>
<tr>
<td>
<a
href="/rowers/agegrouprecords/{{ sex }}/{{ weightcategory }}/60min/">1 hour
</a>
</td>
</tr>
</tbody>
</table>
</li>
{% endif %}
{% endif %}
</ul>
+3 -1
View File
@@ -67,7 +67,9 @@
<div class="mapdiv">
{{ gmapdiv |safe }}
{{ gmap |safe }}
<script>
{{ gmap |safe }}
</script>
</div>
</li>
</ul>
+3 -6
View File
@@ -98,14 +98,11 @@
</li>
{% endif %}
<li class="grid_2">
<script src="https://cdn.pydata.org/bokeh/release/bokeh-3.1.1.min.js"></script>
<script async="true" type="text/javascript">
Bokeh.set_log_level("info");
</script>
{{ interactiveplot |safe }}
<script src="https://d3js.org/d3.v6.js"></script>
{{ the_div|safe }}
{{ interactiveplot |safe }}
</li>
<li class="grid_4">
+2 -5
View File
@@ -16,10 +16,6 @@
{% block meta %}
{% leaflet_js %}
{% leaflet_css %}
<script src="https://cdn.pydata.org/bokeh/release/bokeh-3.1.1.min.js"></script>
<script async="true" type="text/javascript">
Bokeh.set_log_level("info");
</script>
{% for chart in charts %}
{{ chart.script |safe }}
@@ -51,7 +47,7 @@
"<a href=\"/rowers/workout/"+workoutid+"/flexchart?favoritechart="+counter+"\">"+element.div+"</a>"+
"</li>");
$("#id_thumbscripts").append(element.script);
$("#id_thumbs").append(element.script);
counter += 1;
});
});
@@ -64,6 +60,7 @@
{% block main %}
<script src="https://d3js.org/d3.v6.js"></script>
<p>
{% if workout|previousworkout:rower.user %}
<a href="/rowers/workout/{{ workout|previousworkout:rower.user }}/workflow"
+2 -2
View File
@@ -217,9 +217,9 @@
<div class="mapdiv">
{{ mapdiv|safe }}
</div>
<script>
{{ mapscript|safe }}
</script>
</li>
{% endif %}
{% for graph in graphs %}
+41 -46
View File
@@ -104,58 +104,53 @@
</td>
</tr>
{% endif %}
{% for course in courses %}
<tr>
<th>
Timed Course:
</th>
<td>
<a href="/rowers/courses/{{ course.id }}"/>{{ course }}</a>
</td>
</tr>
{% endfor %}
</table>
</li>
<li class="grid_2">
<h1>Workout Summary</h1>
<p>
<pre>
{{ summary }}
</pre>
</p>
</li>
{% if mapdiv %}
<li class="grid_2">
<div class="mapdiv">
{% for course in courses %}
<tr>
<th>
Timed Course:
</th>
<td>
<a href="/rowers/courses/{{ course.id }}"/>{{ course }}</a>
</td>
</tr>
{% endfor %}
</table>
</li>
<li class="grid_2">
<h1>Workout Summary</h1>
<p>
<pre>
{{ summary }}
</pre>
</p>
</li>
{% if mapdiv %}
<li class="grid_2">
<div class="mapdiv">
{{ mapdiv|safe }}
{{ mapscript|safe }}
</div>
</li>
{% endif %}
<li class="grid_2">
<script src="https://cdn.pydata.org/bokeh/release/bokeh-3.1.1.min.js"></script>
<script async="true" type="text/javascript">
Bokeh.set_log_level("info");
<script>
{{ mapscript|safe }}
</script>
{{ interactiveplot |safe }}
{{ the_div|safe }}
</li>
{% for graph in graphs %}
<li>
<a href="/rowers/graph/{{ graph.id }}/">
</div>
</li>
{% endif %}
<li class="grid_2">
<script src="https://d3js.org/d3.v6.js"></script>
{{ the_div|safe }}
{{ interactiveplot |safe }}
</li>
{% for graph in graphs %}
<li>
<a href="/rowers/graph/{{ graph.id }}/">
<img src="/{{ graph.filename }}"
onerror="this.src='/static/img/rowingtimer.gif'"
alt="{{ graph.filename }}" width="120" height="100">
</a>
</a>
</li>
{% endfor %}
{% endfor %}
</ul>
{% endblock %}
+18 -6
View File
@@ -38,6 +38,7 @@ from nose.tools import assert_true
from mock import Mock, patch
#from minimocktest import MockTestCase
import pandas as pd
import polars as pl
import arrow
from django.http import HttpResponseRedirect
@@ -271,7 +272,7 @@ def mocked_fetchcperg(*args, **kwargs):
return df
import pandas as pd
def mocked_read_df_sql(id): # pragma: no cover
df = pd.read_csv('rowers/tests/testdata/fake_strokedata.csv')
@@ -300,7 +301,7 @@ def mocked_getrowdata_db(*args, **kwargs):
return df,row
def mocked_getrowdata_uh(*args, **kwargs): # pragma: no cover
df = pd.read_csv('rowers/tests/testdata/uhfull.csv')
df = pl.read_csv('rowers/tests/testdata/uhfull.csv')
id = kwargs['id']
@@ -308,13 +309,19 @@ def mocked_getrowdata_uh(*args, **kwargs): # pragma: no cover
return df, row
def mocked_getsmallrowdata_uh(*args, **kwargs): # pragma: no cover
df = pl.read_csv('rowers/tests/testdata/uhfull.csv')
return df
def mocked_getsmallrowdata_uh_pd(*args, **kwargs): # pragma: no cover
df = pd.read_csv('rowers/tests/testdata/uhfull.csv')
return df
def mocked_getsmallrowdata_forfusion(*args, **kwargs):
df = pd.read_csv('rowers/tests/testdata/getrowdata_mock.csv')
df = pl.read_csv('rowers/tests/testdata/getrowdata_mock.csv')
return df
@@ -335,6 +342,11 @@ def mocked_getsmallrowdata_db(*args, **kwargs):
return df
def mocked_read_data(*args, **kwargs):
df = pl.read_csv('rowers/tests/testdata/colsfromdb.csv')
return df
def mocked_getsmallrowdata_db_updatecp(*args, **kwargs): # pragma: no cover
df = pd.read_csv('rowers/tests/testdata/colsfromdb.csv')
@@ -351,8 +363,8 @@ def mocked_getsmallrowdata_db_water(*args, **kwargs): # pragma: no cover
return df
def mocked_getsmallrowdata_db_wps(*args, **kwargs): # pragma: no cover
df = pd.read_csv('rowers/tests/testdata/driveenergies.csv')
def mocked_read_data_wps(*args, **kwargs): # pragma: no cover
df = pl.read_csv('rowers/tests/testdata/driveenergies.csv')
return df
@@ -384,7 +396,7 @@ def mocked_read_cols_df_sql(*args, **kwargs):
def mock_workout_summaries(*args, **kwargs):
df = pd.read_csv('rowers/tests/testdata/workout_summaries.csv')
df = pl.read_csv('rowers/tests/testdata/workout_summaries.csv')
return df
def mocked_read_df_cols_sql_multi(ids, columns, convertnewtons=True): # pragma: no cover
+1
View File
@@ -65,6 +65,7 @@ from nose.tools import assert_true
from mock import Mock, patch
#from minimocktest import MockTestCase
import pandas as pd
import polars as pl
import rowers.c2stuff as c2stuff
import rowers.rojabo_stuff as rojabo_stuff
+46 -76
View File
@@ -43,10 +43,10 @@ class ListWorkoutTest(TestCase):
pass
@patch('rowers.dataprep.create_engine')
@patch('rowers.dataprep.getsmallrowdata_db')
@patch('rowers.dataprep.read_data')
@patch('rowers.dataprep.myqueue')
def test_list_workouts(self, mocked_sqlalchemy,
mocked_getsmallrowdata_db,
mocked_read_data,
mocked_myqueue):
login = self.c.login(username=self.u.username, password=self.password)
@@ -169,8 +169,8 @@ class ForcecurveTest(TestCase):
pass
@patch('rowers.dataprep.getsmallrowdata_db',side_effect = mocked_getempowerdata_db)
def test_forcecurve_plot(self, mocked_getsmallrowdata_db):
@patch('rowers.dataprep.read_data',side_effect = mocked_read_data)
def test_forcecurve_plot(self, mocked_read_data):
login = self.c.login(username=self.u.username, password = self.password)
self.assertTrue(login)
@@ -296,9 +296,9 @@ class WorkoutCompareTestNew(TestCase):
pass
@patch('rowers.dataprep.create_engine')
@patch('rowers.dataprep.getsmallrowdata_db')
@patch('rowers.dataprep.read_data')
def test_workouts_compare(self, mocked_sqlalchemy,
mocked_getsmallrowdata_db):
mocked_read_data):
login = self.c.login(username=self.u.username, password=self.password)
self.assertTrue(login)
@@ -309,9 +309,9 @@ class WorkoutCompareTestNew(TestCase):
self.assertEqual(response.status_code,200)
@patch('rowers.dataprep.create_engine')
@patch('rowers.dataprep.getsmallrowdata_db', side_effect=mocked_getsmallrowdata_db)
@patch('rowers.dataprep.read_data', side_effect=mocked_read_data)
def test_workouts_compare_submit(self, mocked_sqlalchemy,
mocked_getsmallrowdata_db):
mocked_read_data):
login = self.c.login(username=self.u.username,password=self.password)
self.assertTrue(login)
@@ -398,9 +398,9 @@ class WorkoutBoxPlotTestNew(TestCase):
pass
@patch('rowers.dataprep.create_engine')
@patch('rowers.dataprep.getsmallrowdata_db')
@patch('rowers.dataprep.read_data')
def test_workouts_boxplot(self, mocked_sqlalchemy,
mocked_getsmallrowdata_db):
mocked_read_data):
login = self.c.login(username=self.u.username, password=self.password)
self.assertTrue(login)
@@ -411,9 +411,9 @@ class WorkoutBoxPlotTestNew(TestCase):
self.assertEqual(response.status_code,200)
@patch('rowers.dataprep.create_engine')
@patch('rowers.dataprep.getsmallrowdata_db', side_effect=mocked_getsmallrowdata_db)
@patch('rowers.dataprep.read_data', side_effect=mocked_read_data)
def test_workouts_boxplot_submit(self, mocked_sqlalchemy,
mocked_getsmallrowdata_db):
mocked_read_data):
login = self.c.login(username=self.u.username,password=self.password)
self.assertTrue(login)
@@ -499,9 +499,9 @@ class WorkoutHistoTestNew(TestCase):
pass
@patch('rowers.dataprep.create_engine')
@patch('rowers.dataprep.getsmallrowdata_db')
@patch('rowers.dataprep.read_data')
def test_workouts_histo(self, mocked_sqlalchemy,
mocked_getsmallrowdata_db):
mocked_read_data):
login = self.c.login(username=self.u.username, password=self.password)
self.assertTrue(login)
@@ -512,9 +512,9 @@ class WorkoutHistoTestNew(TestCase):
self.assertEqual(response.status_code,200)
@patch('rowers.dataprep.create_engine')
@patch('rowers.dataprep.getsmallrowdata_db', side_effect=mocked_getsmallrowdata_db)
@patch('rowers.dataprep.read_data', side_effect=mocked_read_data)
def test_workouts_histo_submit(self, mocked_sqlalchemy,
mocked_getsmallrowdata_db):
mocked_read_data):
login = self.c.login(username=self.u.username,password=self.password)
self.assertTrue(login)
@@ -600,9 +600,9 @@ class History(TestCase):
pass
@patch('rowers.dataprep.create_engine')
@patch('rowers.dataprep.getsmallrowdata_db',side_effect=mocked_getsmallrowdata_db)
@patch('rowers.dataprep.read_data',side_effect=mocked_read_data)
def test_workouts_history(self, mocked_sqlalchemy,
mocked_getsmallrowdata_db):
mocked_read_data):
login = self.c.login(username=self.u.username, password=self.password)
self.assertTrue(login)
@@ -618,9 +618,9 @@ class History(TestCase):
@patch('rowers.dataprep.create_engine')
@patch('rowers.dataprep.getsmallrowdata_db', side_effect=mocked_getsmallrowdata_db)
@patch('rowers.dataprep.read_data', side_effect=mocked_read_data)
def test_workouts_history_submit(self, mocked_sqlalchemy,
mocked_getsmallrowdata_db):
mocked_read_data):
login = self.c.login(username=self.u.username,password=self.password)
self.assertTrue(login)
@@ -713,9 +713,9 @@ class GoldMedalScores(TestCase):
pass
@patch('rowers.dataprep.create_engine')
@patch('rowers.dataprep.getsmallrowdata_db')
@patch('rowers.dataprep.read_data')
def test_workouts_goldmedalscores(self, mocked_sqlalchemy,
mocked_getsmallrowdata_db):
mocked_read_data):
ws = Workout.objects.filter(rankingpiece=True)
self.assertEqual(ws.count(),2)
@@ -729,9 +729,9 @@ class GoldMedalScores(TestCase):
self.assertEqual(response.status_code,200)
@patch('rowers.dataprep.create_engine')
@patch('rowers.dataprep.getsmallrowdata_db', side_effect=mocked_getsmallrowdata_db)
@patch('rowers.dataprep.read_data', side_effect=mocked_read_data)
def test_workouts_goldmedalscores_submit(self, mocked_sqlalchemy,
mocked_getsmallrowdata_db):
mocked_read_data):
login = self.c.login(username=self.u.username,password=self.password)
self.assertTrue(login)
@@ -795,9 +795,9 @@ class WorkoutFlexallTestNew(TestCase):
pass
@patch('rowers.dataprep.create_engine')
@patch('rowers.dataprep.getsmallrowdata_db')
@patch('rowers.dataprep.read_data')
def test_workouts_flexall(self, mocked_sqlalchemy,
mocked_getsmallrowdata_db):
mocked_read_data):
login = self.c.login(username=self.u.username, password=self.password)
self.assertTrue(login)
@@ -808,9 +808,9 @@ class WorkoutFlexallTestNew(TestCase):
self.assertEqual(response.status_code,200)
@patch('rowers.dataprep.create_engine')
@patch('rowers.dataprep.getsmallrowdata_db', side_effect=mocked_getsmallrowdata_db)
@patch('rowers.dataprep.read_data', side_effect=mocked_read_data)
def test_workouts_flexall_submit(self, mocked_sqlalchemy,
mocked_getsmallrowdata_db):
mocked_read_data):
login = self.c.login(username=self.u.username,password=self.password)
self.assertTrue(login)
@@ -903,9 +903,9 @@ class WorkoutStatsTestNew(TestCase):
pass
@patch('rowers.dataprep.create_engine')
@patch('rowers.dataprep.getsmallrowdata_db')
@patch('rowers.dataprep.read_data')
def test_workouts_stats(self, mocked_sqlalchemy,
mocked_getsmallrowdata_db):
mocked_read_data):
login = self.c.login(username=self.u.username, password=self.password)
self.assertTrue(login)
@@ -916,11 +916,11 @@ class WorkoutStatsTestNew(TestCase):
self.assertEqual(response.status_code,200)
@patch('rowers.dataprep.create_engine')
@patch('rowers.dataprep.getsmallrowdata_db', side_effect=mocked_getsmallrowdata_db)
@patch('rowers.dataprep.read_data', side_effect=mocked_read_data)
@patch('rowers.dataprep.read_cols_df_sql', side_effect=mocked_read_cols_df_sql)
def test_analysis_data(self,
mocked_sqlalchemy,
mocked_getsmallrowdata_db,
mocked_read_data,
mocked_read_cols_df_sql,
):
@@ -979,11 +979,11 @@ class WorkoutStatsTestNew(TestCase):
script, div = comparisondata(workouts,options)
@patch('rowers.dataprep.create_engine')
@patch('rowers.dataprep.getsmallrowdata_db', side_effect=mocked_getsmallrowdata_db)
@patch('rowers.dataprep.read_data', side_effect=mocked_read_data)
@patch('rowers.dataprep.read_cols_df_sql', side_effect=mocked_read_cols_df_sql)
def test_analysis_data2(self,
mocked_sqlalchemy,
mocked_getsmallrowdata_db,
mocked_read_data,
mocked_read_cols_df_sql,
):
@@ -1042,11 +1042,11 @@ class WorkoutStatsTestNew(TestCase):
@patch('rowers.dataprep.create_engine')
@patch('rowers.dataprep.getsmallrowdata_db', side_effect=mocked_getsmallrowdata_db)
@patch('rowers.dataprep.read_data', side_effect=mocked_read_data)
@patch('rowers.dataprep.read_cols_df_sql', side_effect=mocked_read_cols_df_sql)
def test_analysis_data2(self,
mocked_sqlalchemy,
mocked_getsmallrowdata_db,
mocked_read_data,
mocked_read_cols_df_sql,
):
@@ -1120,9 +1120,9 @@ class WorkoutStatsTestNew(TestCase):
@patch('rowers.dataprep.create_engine')
@patch('rowers.dataprep.getsmallrowdata_db', side_effect=mocked_getsmallrowdata_db)
@patch('rowers.dataprep.read_data', side_effect=mocked_read_data)
def test_workouts_stats_submit(self, mocked_sqlalchemy,
mocked_getsmallrowdata_db):
mocked_read_data):
login = self.c.login(username=self.u.username,password=self.password)
self.assertTrue(login)
@@ -1241,9 +1241,9 @@ class MarkerPerformanceTest(TestCase):
pass
@patch('rowers.dataprep.create_engine')
@patch('rowers.dataprep.getsmallrowdata_db')
@patch('rowers.dataprep.read_data')
def test_create_marker_workouts(self, mocked_sqlalchemy,
mocked_getsmallrowdata_db):
mocked_read_data):
login = self.c.login(username=self.u.username,password=self.password)
self.assertTrue(login)
@@ -1257,8 +1257,8 @@ class MarkerPerformanceTest(TestCase):
self.assertRedirects(response, expected_url=expected_url, status_code=302,target_status_code=200)
@patch('rowers.dataprep.getsmallrowdata_db', side_effect=mocked_getsmallrowdata_uh)
def test_trainingzones_view(self,mocked_getsmallrowdata_db):
@patch('rowers.dataprep.read_data', side_effect=mocked_getsmallrowdata_uh)
def test_trainingzones_view(self,mocked_getsmallrowdata_uh):
login = self.c.login(username=self.u.username,password=self.password)
self.assertTrue(login)
@@ -1326,9 +1326,9 @@ class MarkerPerformanceTest(TestCase):
@patch('rowers.dataprep.create_engine')
@patch('rowers.dataprep.getsmallrowdata_db')
@patch('rowers.dataprep.read_data')
def test_performancemanager_view(self, mocked_sqlalchemy,
mocked_getsmallrowdata_db):
mocked_read_data):
login = self.c.login(username=self.u.username,password=self.password)
self.assertTrue(login)
@@ -1356,36 +1356,6 @@ class MarkerPerformanceTest(TestCase):
response = self.c.post(url,form_data)
self.assertEqual(response.status_code,200)
@patch('rowers.dataprep.create_engine')
@patch('rowers.dataprep.getsmallrowdata_db')
def test_ranking_view2(self, mocked_sqlalchemy,
mocked_getsmallrowdata_db):
login = self.c.login(username=self.u.username,password=self.password)
self.assertTrue(login)
startdate = (self.user_workouts[0].startdatetime-datetime.timedelta(days=3)).date()
enddate = (self.user_workouts[0].startdatetime+datetime.timedelta(days=3)).date()
url = reverse('rankings_view2')
response = self.c.get(url)
self.assertEqual(response.status_code,200)
form_data = {
'startdate':startdate.strftime("%Y-%m-%d"),
'enddate': enddate.strftime("%Y-%m-%d"),
'doform': True,
'dofatigue': True,
'metricchoice':'rscore',
'modelchoice': 'coggan',
'daterange': '',
}
form = DateRangeForm(form_data)
if not form.is_valid():
print(form.errors)
self.assertTrue(form.is_valid())
response = self.c.post(url,form_data)
self.assertEqual(response.status_code,200)
class AlertTest(TestCase):
def setUp(self):
@@ -1443,9 +1413,9 @@ class AlertTest(TestCase):
pass
@patch('rowers.dataprep.create_engine')
@patch('rowers.alerts.getsmallrowdata_db')
@patch('rowers.alerts.read_data')
def test_alerts(self, mocked_sqlalchemy,
mocked_getsmallrowdata_db):
mocked_read_data):
login = self.c.login(username=self.u.username,password=self.password)
self.assertTrue(login)
+22 -16
View File
@@ -58,19 +58,22 @@ class OwnApi(TestCase):
response = self.c.get(url)
self.assertEqual(response.status_code,200)
url = reverse('strokedatajson',kwargs={'id':w.id})
with patch('rowers.dataprep.read_data') as mock_read_data:
mock_read_data.return_value = pl.read_csv('rowers/tests/testdata/colsfromdb.csv')
url = reverse('strokedatajson',kwargs={'id':w.id})
request = self.factory.get(url)
request.user = self.u
force_authenticate(request, user=self.u)
response = strokedatajson(request,id=w.id)
self.assertEqual(response.status_code,200)
request = self.factory.get(url)
request.user = self.u
force_authenticate(request, user=self.u)
response = strokedatajson(request,id=w.id)
self.assertEqual(response.status_code,200)
# response must be json
strokedata = json.loads(response.content)
df = pd.DataFrame(strokedata)
# response must be json
strokedata = json.loads(response.content)
df = pl.from_dict(strokedata)
self.assertFalse(df.empty)
self.assertFalse(df.is_empty())
form_data = {
"distance": [23, 46, 48],
@@ -119,13 +122,16 @@ class OwnApi(TestCase):
request = self.factory.get(url)
request.user = self.u
force_authenticate(request, user=self.u)
response = strokedatajson_v2(request,id=w.id)
self.assertEqual(response.status_code,200)
with patch('rowers.dataprep.read_data') as mock_read_data:
mock_read_data.return_value = pl.read_csv('rowers/tests/testdata/colsfromdb.csv')
response = strokedatajson_v2(request,id=w.id)
self.assertEqual(response.status_code,200)
# response must be json
strokedata = json.loads(response.content)
df = pd.DataFrame(strokedata)
self.assertFalse(df.empty)
# response must be json
strokedata = json.loads(response.content)
df = pl.from_dicts(strokedata['data'])
self.assertFalse(df.is_empty())
form_data = {
+2 -46
View File
@@ -484,54 +484,10 @@ class AsyncTaskTests(TestCase):
res = tasks.handle_c2_import_stroke_data(c2token,c2id,workoutid,starttimeunix,csvfilename)
self.assertEqual(res,1)
@patch('rowers.tasks.grpc',side_effect=mocked_grpc)
@patch('rowers.tasks.send_template_email',side_effect=mocked_send_template_email)
def test_handle_otwsetpower(self,mocked_send_template_email,mocked_grpc):
f1 = get_random_file(filename='rowers/tests/testdata/sprintervals.csv')['filename']
boattype = '1x'
boatclass = 'water'
coastalbrand = 'other'
weightvalue = 80.
first_name = self.u.first_name
last_name = self.u.last_name
email = self.u.email
workoutid = self.wwater.id
job = fakerequest()
res = tasks.handle_otwsetpower(f1,boattype,boatclass,coastalbrand,
weightvalue,first_name,last_name,email,workoutid,
jobkey='23')
self.assertEqual(res,1)
@patch('rowers.dataprep.create_engine')
def test_handle_updateergcp(self,mocked_sqlalchemy):
f1 = get_random_file()['filename']
res = tasks.handle_updateergcp(1,[f1])
self.assertEqual(res,1)
@patch('rowers.dataprep.getsmallrowdata_db')
def test_handle_updatecp(self,mocked_getsmallrowdata_db_updatecp):
rower_id = 1
workoutids = [1]
res = tasks.handle_updatecp(rower_id,workoutids)
self.assertEqual(res,1)
@patch('rowers.dataprep.getsmallrowdata_db')
def test_handle_setcp(self,mocked_getsmallrowdata_db_setcp):
strokesdf = pd.read_csv('rowers/tests/testdata/uhfull.csv')
filename = 'rowers/tests/testdata/temp/pq.gz'
workoutids = 1
res = tasks.handle_setcp(strokesdf,filename,1)
self.assertEqual(res,1)
try:
os.remove(filename)
except FileNotFoundError:
pass
@patch('rowers.dataprep.getsmallrowdata_db')
def test_handle_update_wps(self,mocked_getsmallrowdata_db_wps):
@patch('rowers.dataprep.read_data')
def test_handle_update_wps(self,mocked_read_data_wps):
ids = [1,2,3]
result = tasks.handle_update_wps(self.r.id,['water'],ids,mode='water')
+33 -178
View File
@@ -62,10 +62,10 @@ class ListWorkoutTest(TestCase):
pass
@patch('rowers.dataprep.create_engine')
@patch('rowers.dataprep.getsmallrowdata_db')
@patch('rowers.dataprep.read_data')
@patch('rowers.dataprep.myqueue')
def test_list_workouts(self, mocked_sqlalchemy,
mocked_getsmallrowdata_db,
mocked_read_data,
mocked_myqueue):
login = self.c.login(username=self.u.username, password=self.password)
@@ -87,10 +87,10 @@ class ListWorkoutTest(TestCase):
self.assertEqual(response.status_code,200)
@patch('rowers.dataprep.create_engine')
@patch('rowers.dataprep.getsmallrowdata_db')
@patch('rowers.dataprep.read_data')
@patch('rowers.dataprep.myqueue')
def test_bulk_workouts(self, mocked_sqlalchemy,
mocked_getsmallrowdata_db,
mocked_read_data,
mocked_myqueue):
login = self.c.login(username=self.u.username, password=self.password)
@@ -234,9 +234,9 @@ class WorkoutViewTest(TestCase):
@patch('rowers.dataprep.create_engine')
@patch('rowers.dataprep.getsmallrowdata_db')
@patch('rowers.dataprep.read_data')
@patch('rowers.middleware.myqueue')
def test_forcecurve(self, mocked_sqlalchemy, mocked_getsmallrowdata_db,
def test_forcecurve(self, mocked_sqlalchemy, mocked_read_data,
mocked_myqueue):
login = self.c.login(username=self.u.username, password=self.password)
self.assertTrue(login)
@@ -255,9 +255,9 @@ class WorkoutViewTest(TestCase):
self.assertEqual(response.status_code,200)
@patch('rowers.dataprep.create_engine')
@patch('rowers.dataprep.getsmallrowdata_db')
@patch('rowers.dataprep.read_data')
@patch('rowers.middleware.myqueue')
def test_resample(self, mocked_sqlalchemy, mocked_getsmallrowdata_db,
def test_resample(self, mocked_sqlalchemy, mocked_read_data,
mocked_myqueue):
login = self.c.login(username=self.u.username, password=self.password)
self.assertTrue(login)
@@ -283,9 +283,9 @@ class WorkoutViewTest(TestCase):
@patch('rowers.dataprep.create_engine')
@patch('rowers.dataprep.getsmallrowdata_db')
@patch('rowers.dataprep.read_data')
@pytest.mark.django_db(transaction=True)
def test_joins(self, mocked_sqlalchemy, mocked_getsmallrowdata_db):
def test_joins(self, mocked_sqlalchemy, mocked_read_data):
with transaction.atomic():
login = self.c.login(username=self.u.username, password=self.password)
self.assertTrue(login)
@@ -350,10 +350,10 @@ class WorkoutViewTest(TestCase):
@patch('rowers.dataprep.create_engine')
@patch('rowers.dataprep.getsmallrowdata_db')
@patch('rowers.dataprep.read_data')
@patch('rowers.weather.requests.get', side_effect=mocked_requests)
def test_waterworkout_view(self,
mocked_sqlalchemy, mocked_getsmallrowdata_db, mocked_requests):
mocked_sqlalchemy, mocked_read_data, mocked_requests):
login = self.c.login(username=self.u.username, password=self.password)
self.assertTrue(login)
@@ -362,22 +362,6 @@ class WorkoutViewTest(TestCase):
response = self.c.get(url)
self.assertEqual(response.status_code,200)
url = reverse('workout_downloadmetar_view',kwargs={
'id': encoder.encode_hex(self.wwater.id),
'airportcode': 'LKHO'
}
)
response = self.c.get(url,follow=True)
self.assertEqual(response.status_code,200)
url = reverse('workout_downloadwind_view',kwargs={
'id': encoder.encode_hex(self.wwater.id),
}
)
response = self.c.get(url,follow=True)
self.assertEqual(response.status_code,200)
# Stacked Flex Chart
url = reverse('workout_flexchart_stacked_view',kwargs={
@@ -422,8 +406,8 @@ class WorkoutViewTest(TestCase):
self.assertEqual(response.status_code,200)
@patch('rowers.dataprep.create_engine')
@patch('rowers.dataprep.getsmallrowdata_db')
def test_smoothen(self, mocked_sqlalchemy, mocked_getsmallrowdata_db):
@patch('rowers.dataprep.read_data')
def test_smoothen(self, mocked_sqlalchemy, mocked_read_data):
login = self.c.login(username=self.u.username, password=self.password)
self.assertTrue(login)
@@ -459,125 +443,12 @@ class WorkoutViewTest(TestCase):
status_code=302,target_status_code=200)
@patch('rowers.dataprep.create_engine')
@patch('rowers.dataprep.getsmallrowdata_db')
def test_windform(self, mocked_sqlalchemy, mocked_getsmallrowdata_db):
login = self.c.login(username=self.u.username, password=self.password)
self.assertTrue(login)
url = reverse('workout_wind_view',kwargs={'id':encoder.encode_hex(self.wwater.id)})
response = self.c.get(url)
self.assertEqual(response.status_code,200)
form_data = {
'dist1':1000,
'dist2':2000,
'vwind1':2.0,
'vwind2':1.4,
'windunit':'m',
'winddirection1': 0,
'winddirection2': 90,
}
form = UpdateWindForm(form_data)
self.assertTrue(form.is_valid())
response = self.c.post(url,form_data)
self.assertEqual(response.status_code,200)
@patch('rowers.dataprep.create_engine')
@patch('rowers.dataprep.getsmallrowdata_db')
def test_streamform(self, mocked_sqlalchemy, mocked_getsmallrowdata_db):
login = self.c.login(username=self.u.username, password=self.password)
self.assertTrue(login)
url = reverse('workout_stream_view',kwargs={'id':encoder.encode_hex(self.wwater.id)})
response = self.c.get(url)
self.assertEqual(response.status_code,200)
form_data = {
'dist1':1000,
'dist2':2000,
'stream1':2.0,
'stream2':1.4,
'streamunit':'m',
}
form = UpdateStreamForm(form_data)
self.assertTrue(form.is_valid())
response = self.c.post(url,form_data)
self.assertEqual(response.status_code,200)
@patch('rowers.dataprep.create_engine')
@patch('rowers.dataprep.getsmallrowdata_db')
@patch('rowers.middleware.myqueue')
def test_setpowerform(self, mocked_sqlalchemy, mocked_getsmallrowdata_db,
mocked_myqueue):
login = self.c.login(username=self.u.username, password=self.password)
self.assertTrue(login)
url = reverse('instroke_view',kwargs={'id':encoder.encode_hex(self.winstroke.id)})
response = self.c.get(url)
self.assertEqual(response.status_code,200)
rowdata = rowingdata.rowingdata(csvfile=self.winstroke.csvfilename)
instrokemetrics = rowdata.get_instroke_columns()
self.assertTrue(len(instrokemetrics)>0)
url = reverse('instroke_chart',
kwargs={
'id':encoder.encode_hex(self.winstroke.id),
'metric':instrokemetrics[0],
})
url2 = reverse(self.r.defaultlandingpage,kwargs={'id':encoder.encode_hex(self.winstroke.id)})
response = self.c.get(url)
self.assertRedirects(response,
expected_url=url2,
status_code=302,target_status_code=200)
@patch('rowers.dataprep.create_engine')
@patch('rowers.dataprep.getsmallrowdata_db')
def test_setpowerform(self, mocked_sqlalchemy, mocked_getsmallrowdata_db):
login = self.c.login(username=self.u.username, password=self.password)
self.assertTrue(login)
url = reverse('workout_otwsetpower_view',kwargs={'id':encoder.encode_hex(self.wwater.id)})
response = self.c.get(url)
self.assertEqual(response.status_code,200)
form_data = {
'quick_calc':True,
'boattype': '1x',
'weightvalue': 75.0,
'boatbrand':'maas',
}
form = AdvancedWorkoutForm(form_data)
self.assertTrue(form.is_valid())
response = self.c.post(url,form_data,follow=True)
self.assertEqual(response.status_code,200)
expected_url = reverse('workout_edit_view',kwargs={'id':encoder.encode_hex(self.wwater.id)})
self.assertRedirects(response,
expected_url=expected_url,
status_code=302,target_status_code=200)
@patch('rowers.dataprep.create_engine')
@patch('rowers.dataprep.getsmallrowdata_db')
def test_commentview(self, mocked_sqlalchemy, mocked_getsmallrowdata_db):
@patch('rowers.dataprep.read_data')
def test_commentview(self, mocked_sqlalchemy, mocked_read_data):
login = self.c.login(username=self.u.username, password=self.password)
self.assertTrue(login)
@@ -602,8 +473,8 @@ class WorkoutViewTest(TestCase):
self.assertEqual(response.status_code,200)
@patch('rowers.dataprep.create_engine')
@patch('rowers.dataprep.getsmallrowdata_db')
def test_mapview(self, mocked_sqlalchemy, mocked_getsmallrowdata_db):
@patch('rowers.dataprep.read_data')
def test_mapview(self, mocked_sqlalchemy, mocked_read_data):
login = self.c.login(username=self.u.username, password=self.password)
self.assertTrue(login)
@@ -614,9 +485,9 @@ class WorkoutViewTest(TestCase):
self.assertEqual(response.status_code,200)
@patch('rowers.dataprep.create_engine')
@patch('rowers.dataprep.getsmallrowdata_db')
@patch('rowers.dataprep.read_data')
def notworking_test_workout_image(self, mocked_sqlalchemy,
mocked_getsmallrowdata_db):
mocked_read_data):
login = self.c.login(username=self.u.username, password=self.password)
self.assertTrue(login)
@@ -642,10 +513,10 @@ class WorkoutViewTest(TestCase):
status_code=302,target_status_code=200)
@patch('rowers.dataprep.create_engine')
@patch('rowers.dataprep.getsmallrowdata_db')
@patch('rowers.dataprep.read_data')
@patch('rowers.dataprep.getrowdata_db',side_effect=mocked_getrowdata_db)
@pytest.mark.django_db(transaction=True)
def test_workout_split(self, mocked_sqlalchemy, mocked_getsmallrowdata_db,
def test_workout_split(self, mocked_sqlalchemy, mocked_read_data,
mocked_getrowdata_db):
with transaction.atomic():
login = self.c.login(username=self.u.username, password=self.password)
@@ -670,9 +541,9 @@ class WorkoutViewTest(TestCase):
@patch('rowers.dataprep.create_engine')
@patch('rowers.dataprep.getrowdata_db',side_effect=mocked_getrowdata_db)
@patch('rowers.dataprep.getsmallrowdata_db',side_effect=mocked_getsmallrowdata_forfusion)
@patch('rowers.dataprep.read_data',side_effect=mocked_getsmallrowdata_forfusion)
def test_workout_fusion(self, mocked_sqlalchemy, mocked_getrowdata_db,
mocked_getsmallrowdata_db):
mocked_read_data):
login = self.c.login(username=self.u.username, password=self.password)
self.assertTrue(login)
@@ -699,27 +570,11 @@ class WorkoutViewTest(TestCase):
self.assertEqual(response.status_code,200)
def test_remove_power_view(self):
login = self.c.login(username=self.u.username, password=self.password)
self.assertTrue(login)
url = reverse('remove_power_view',kwargs={'id':encoder.encode_hex(self.wwater.id)})
url2 = reverse(self.r.defaultlandingpage,
kwargs={
'id':encoder.encode_hex(self.wwater.id)
}
)
response = self.c.get(url)
self.assertRedirects(response,
expected_url=url2,
status_code=302,
target_status_code=200
)
@patch('rowers.dataprep.create_engine')
@patch('rowers.dataprep.getsmallrowdata_db')
@patch('rowers.dataprep.read_data')
@patch('rowers.dataprep.get_video_data',side_effect=mocked_videodata)
def test_workout_video_view(self, mocked_sqlalchemy, mocked_getsmallrowdata_db,
def test_workout_video_view(self, mocked_sqlalchemy, mocked_read_data,
mocked_videodata):
login = self.c.login(username=self.u.username, password=self.password)
self.assertTrue(login)
@@ -779,9 +634,9 @@ class WorkoutViewTest(TestCase):
self.assertRedirects(response,expected_url=expected_url,status_code=302,target_status_code=200)
@patch('rowers.dataprep.create_engine')
@patch('rowers.dataprep.getsmallrowdata_db')
@patch('rowers.dataprep.read_data')
@patch('rowers.dataprep.get_video_data',side_effect=mocked_videodata)
def test_workout_video_view_erg(self, mocked_sqlalchemy, mocked_getsmallrowdata_db,
def test_workout_video_view_erg(self, mocked_sqlalchemy, mocked_read_data,
mocked_videodata):
login = self.c.login(username=self.u.username, password=self.password)
self.assertTrue(login)
@@ -843,7 +698,7 @@ class WorkoutViewTest(TestCase):
@patch('rowers.dataprep.create_engine')
@patch('rowers.dataprep.getrowdata_db',side_effect=mocked_getrowdata_db)
@patch('rowers.dataprep.get_video_data',side_effect=mocked_videodata)
def test_video_selectworkout(self, mocked_sqlalchemy, mocked_getsmallrowdata_db,
def test_video_selectworkout(self, mocked_sqlalchemy, mocked_read_data,
mocked_videodata):
login = self.c.login(username=self.u.username, password=self.password)
self.assertTrue(login)
@@ -874,8 +729,8 @@ class WorkoutViewTest(TestCase):
@patch('rowers.dataprep.create_engine')
@patch('rowers.dataprep.getsmallrowdata_db')
def test_editsummaryview(self, mocked_sqlalchemy, mocked_getsmallrowdata_db):
@patch('rowers.dataprep.read_data')
def test_editsummaryview(self, mocked_sqlalchemy, mocked_read_data):
login = self.c.login(username=self.u.username, password=self.password)
self.assertTrue(login)
@@ -940,8 +795,8 @@ class WorkoutViewTest(TestCase):
@patch('rowers.dataprep.create_engine')
@patch('rowers.dataprep.getsmallrowdata_db')
def test_workout_delete(self, mocked_sqlalchemy, mocked_getsmallrowdata_db):
@patch('rowers.dataprep.read_data')
def test_workout_delete(self, mocked_sqlalchemy, mocked_read_data):
login = self.c.login(username=self.u.username, password=self.password)
self.assertTrue(login)
-62
View File
@@ -166,65 +166,3 @@ class CPChartTest(TestCase):
self.assertEqual(response.status_code,200)
@patch('rowers.dataprep.fetchcperg', side_effect = mocked_fetchcperg)
@patch('rowers.dataprep.create_engine')
def test_rankingpieces(self, mocked_fetchcperg, mocked_sqlalchemy):
url = '/rowers/ote-bests2/'
login = self.c.login(username=self.u.username,password=self.password)
self.assertTrue(login)
# update_records(url='rowers/tests/c2worldrecords.html',verbose=False)
form_data = {
'name': faker.word(),
'date': nu.date(),
'timezone': 'UTC',
'duration': '00:02:00.0',
'starttime': '10:01:43',
'distance': 500,
'workouttype': 'rower',
'boattype': '1x',
'weightcategory': 'hwt',
'adaptiveclass': 'None',
'notes': faker.text(),
'rankingpiece': True,
'duplicate': False,
'avghr': '160',
'avgpwr': 0,
'avgspm': 40,
}
url2 = '/rowers/workout/addmanual/'
response = self.c.post(url2, form_data, follow=True)
self.assertEqual(response.status_code, 200)
form_data['distance'] = 100
form_data['duration'] = '00:00:18.5'
response = self.c.post(url2, form_data, follow=True)
self.assertEqual(response.status_code, 200)
form_data['distance'] = 2000
form_data['duration'] = '00:06:56.5'
response = self.c.post(url2, form_data, follow=True)
self.assertEqual(response.status_code, 200)
form_data['distance'] = 5000
form_data['duration'] = '00:20:48.0'
response = self.c.post(url2, form_data, follow=True)
self.assertEqual(response.status_code, 200)
form_data['distance'] = 6000
form_data['duration'] = '00:22:17.6'
response = self.c.post(url2, form_data, follow=True)
self.assertEqual(response.status_code, 200)
form_data['distance'] = 10000
form_data['duration'] = '00:38:16.5'
response = self.c.post(url2, form_data, follow=True)
self.assertEqual(response.status_code, 200)
response = self.c.get(url)
self.assertEqual(response.status_code, 200)
+8 -8
View File
@@ -55,8 +55,8 @@ class EmailUpload(TestCase):
@patch('rowers.dataprep.create_engine')
@patch('rowers.dataprep.getsmallrowdata_db',side_effect=mocked_getsmallrowdata_db)
def test_uploadapi(self,mocked_sqlalchemy,mocked_getsmallrowdata_db):
@patch('rowers.dataprep.read_data',side_effect=mocked_read_data)
def test_uploadapi(self,mocked_sqlalchemy,mocked_read_data):
form_data = {
'title': 'test',
'workouttype':'rower',
@@ -108,8 +108,8 @@ class EmailUpload(TestCase):
@patch('rowers.dataprep.create_engine')
@patch('rowers.dataprep.getsmallrowdata_db',side_effect=mocked_getsmallrowdata_db)
def test_uploadapi2(self,mocked_sqlalchemy,mocked_getsmallrowdata_db):
@patch('rowers.dataprep.read_data',side_effect=mocked_read_data)
def test_uploadapi2(self,mocked_sqlalchemy,mocked_read_data):
form_data = {"secret": settings.UPLOAD_SERVICE_SECRET,
"user": 1,
"file": 'media/mailbox_attachments/colin3.csv',
@@ -140,8 +140,8 @@ class EmailUpload(TestCase):
@patch('rowers.dataprep.create_engine')
@patch('rowers.dataprep.getsmallrowdata_db',side_effect=mocked_getsmallrowdata_db)
def test_uploadapi3(self,mocked_sqlalchemy,mocked_getsmallrowdata_db):
@patch('rowers.dataprep.read_data',side_effect=mocked_read_data)
def test_uploadapi3(self,mocked_sqlalchemy,mocked_read_data):
with transaction.atomic():
form_data = {
'title': 'test',
@@ -167,8 +167,8 @@ class EmailUpload(TestCase):
self.assertEqual(w.notes,'aap noot mies')
@patch('rowers.dataprep.create_engine')
@patch('rowers.dataprep.getsmallrowdata_db',side_effect=mocked_getsmallrowdata_db)
def test_uploadapi_credentials(self,mocked_sqlalchemy,mocked_getsmallrowdata_db):
@patch('rowers.dataprep.read_data',side_effect=mocked_read_data)
def test_uploadapi_credentials(self,mocked_sqlalchemy,mocked_read_data):
form_data = {
'title': 'test',
'workouttype':'rower',
+4 -4
View File
@@ -103,8 +103,8 @@ class WorkoutViewTest(TestCase):
pass
@patch('rowers.dataprep.create_engine')
@patch('rowers.dataprep.getsmallrowdata_db')
def test_flexchart_water(self, mocked_sqlalechemy, mocked_getsmallrowdata_db):
@patch('rowers.dataprep.read_data')
def test_flexchart_water(self, mocked_sqlalechemy, mocked_read_data):
login = self.c.login(username=self.u.username, password=self.password)
self.assertTrue(login)
@@ -151,8 +151,8 @@ class WorkoutViewTest(TestCase):
self.assertEqual(response.status_code,200)
@patch('rowers.dataprep.create_engine')
@patch('rowers.dataprep.getsmallrowdata_db')
def test_flexchart_erg(self, mocked_sqlalechemy, mocked_getsmallrowdata_db):
@patch('rowers.dataprep.read_data')
def test_flexchart_erg(self, mocked_sqlalechemy, mocked_read_data):
login = self.c.login(username=self.u.username, password=self.password)
self.assertTrue(login)
+13 -13
View File
@@ -720,8 +720,8 @@ class NKObjects(DjangoTestCase):
)
@patch('rowers.dataprep.create_engine')
@patch('rowers.dataprep.getsmallrowdata_db',side_effect=mocked_getsmallrowdata_db)
def test_nk_intervals(self,mocked_sqlalchemy,mocked_getsmallrowdata_db):
@patch('rowers.dataprep.read_data',side_effect=mocked_read_data)
def test_nk_intervals(self,mocked_sqlalchemy,mocked_read_data):
with open('rowers/tests/testdata/carlos_workout.json','r') as f:
workoutdata = json.load(f)
with open('rowers/tests/testdata/carlos_strokes.json','r') as f:
@@ -817,10 +817,10 @@ class NKObjects(DjangoTestCase):
@patch('rowers.integrations.nk.requests.get', side_effect=mocked_requests)
@patch('rowers.integrations.nk.requests.post', side_effect=mocked_requests)
@patch('rowers.nkimportutils.requests.session', side_effect=mocked_session)
@patch('rowers.dataprep.getsmallrowdata_db', side_effect=mocked_getsmallrowdata_db)
@patch('rowers.dataprep.read_data', side_effect=mocked_read_data)
def test_nk_import(self, mock_get, mock_post,
mocked_session,
mocked_getsmallrowdata_db):
mocked_read_data):
integration = NKIntegration(self.u)
result = integration.token_refresh()
@@ -854,10 +854,10 @@ class NKObjects(DjangoTestCase):
@patch('rowers.integrations.nk.requests.get', side_effect=mocked_requests)
@patch('rowers.integrations.nk.requests.post', side_effect=mocked_requests)
@patch('rowers.nkimportutils.requests.session', side_effect=mocked_session)
@patch('rowers.dataprep.getsmallrowdata_db', side_effect=mocked_getsmallrowdata_db)
@patch('rowers.dataprep.read_data', side_effect=mocked_read_data)
def test_nk_import_task(self, mock_get, mock_post,
mocked_session,
mocked_getsmallrowdata_db):
mocked_read_data):
alldata = {
469: {'elapsedTime': 3901900,
'totalDistanceImp': 0,
@@ -904,10 +904,10 @@ class NKObjects(DjangoTestCase):
@patch('rowers.integrations.nk.requests.get', side_effect=mocked_requests)
@patch('rowers.integrations.nk.requests.post', side_effect=mocked_requests)
@patch('rowers.nkimportutils.requests.session', side_effect=mocked_session)
@patch('rowers.dataprep.getsmallrowdata_db', side_effect=mocked_getsmallrowdata_db)
@patch('rowers.dataprep.read_data', side_effect=mocked_read_data)
def test_nk_import_impeller(self, mock_get, mock_post,
mocked_session,
mocked_getsmallrowdata_db):
mocked_read_data):
integration = NKIntegration(self.u)
result = integration.token_refresh()
@@ -1083,9 +1083,9 @@ class RP3Objects(DjangoTestCase):
@patch('rowers.integrations.rp3.requests.get', side_effect=mocked_requests)
@patch('rowers.integrations.rp3.requests.post', side_effect=mocked_requests)
@patch('rowers.dataprep.getsmallrowdata_db', side_effect=mocked_getsmallrowdata_db)
@patch('rowers.dataprep.read_data', side_effect=mocked_read_data)
def test_rp3_import(self, mock_get, mockpost,
mocked_getsmallrowdata_db):
mocked_read_data):
response = self.c.get('/rowers/workout/rp3import/591621',follow=True)
@@ -1268,9 +1268,9 @@ class StravaObjects(DjangoTestCase):
@patch('rowers.utils.requests.get', side_effect=mocked_requests)
@patch('rowers.integrations.strava.requests.post', side_effect=mocked_requests)
@patch('rowers.dataprep.getsmallrowdata_db')
@patch('rowers.dataprep.read_data')
def test_strava_import(self, mock_get, mock_post,
mocked_getsmallrowdata_db):
mocked_read_data):
response = self.c.get('/rowers/workout/stravaimport/12',follow=True)
expected_url = reverse('workout_import_view',kwargs={'source':'strava'})
@@ -1307,7 +1307,7 @@ class STObjects(DjangoTestCase):
self.r.sporttrackstoken = '12'
self.r.sporttracksrefreshtoken = '12'
self.r.sporttroacksrefreshtoken = '12'
self.r.sporttrackstokenexpirydate = arrow.get(datetime.datetime.now()+datetime.timedelta(days=1)).datetime
self.r.save()
+16 -16
View File
@@ -51,16 +51,16 @@ class InteractiveChartTest(TestCase):
@patch('rowers.dataprep.create_engine')
@patch('rowers.dataprep.read_df_sql')
@patch('rowers.dataprep.getsmallrowdata_db',side_effect=mocked_getsmallrowdata_db)
@patch('rowers.dataprep.read_data',side_effect=mocked_read_data)
def test_interactive_chart1(self, mocked_sqlalchemy,mocked_read_df_sql,
mocked_getsmallrowdata_db):
mocked_read_data):
res = iplots.interactive_chart(self.wote.id)
@patch('rowers.dataprep.create_engine')
@patch('rowers.dataprep.read_df_sql')
@patch('rowers.dataprep.getsmallrowdata_db',side_effect=mocked_getsmallrowdata_db)
@patch('rowers.dataprep.read_data',side_effect=mocked_read_data)
def test_interactive_chart2(self, mocked_sqlalchemy,mocked_read_df_sql,
mocked_getsmallrowdata_db):
mocked_read_data):
res = iplots.interactive_chart(self.wote.id,promember=1)
@@ -80,52 +80,52 @@ class InteractiveChartTest(TestCase):
@patch('rowers.dataprep.create_engine')
@patch('rowers.dataprep.read_df_sql')
@patch('rowers.dataprep.getsmallrowdata_db',side_effect=mocked_getsmallrowdata_db)
@patch('rowers.dataprep.read_data',side_effect=mocked_read_data)
def test_interactive_chart7(self, mocked_sqlalchemy,mocked_read_df_sql,
mocked_getsmallrowdata_db):
mocked_read_data):
res = iplots.interactive_flex_chart2(self.wote.id,self.r,promember=0,
xparam='time',
yparam1='pace',yparam2='spm',mode='water')
@patch('rowers.dataprep.create_engine')
@patch('rowers.dataprep.read_df_sql')
@patch('rowers.dataprep.getsmallrowdata_db',side_effect=mocked_getsmallrowdata_db)
@patch('rowers.dataprep.read_data',side_effect=mocked_read_data)
def test_interactive_chart8(self, mocked_sqlalchemy,mocked_read_df_sql,
mocked_getsmallrowdata_db):
mocked_read_data):
res = iplots.interactive_flex_chart2(self.wote.id,self.r,
promember=0,xparam='distance',
yparam1='pace',yparam2='spm',mode='water')
@patch('rowers.dataprep.create_engine')
@patch('rowers.dataprep.read_df_sql')
@patch('rowers.dataprep.getsmallrowdata_db',side_effect=mocked_getsmallrowdata_db)
@patch('rowers.dataprep.read_data',side_effect=mocked_read_data)
def test_interactive_chart9(self, mocked_sqlalchemy,mocked_read_df_sql,
mocked_getsmallrowdata_db):
mocked_read_data):
res = iplots.interactive_flex_chart2(self.wote.id,self.r,
promember=1,xparam='time',
yparam1='pace',yparam2='hr',mode='water')
@patch('rowers.dataprep.create_engine')
@patch('rowers.dataprep.read_df_sql')
@patch('rowers.dataprep.getsmallrowdata_db',side_effect=mocked_getsmallrowdata_db)
@patch('rowers.dataprep.read_data',side_effect=mocked_read_data)
def test_interactive_chart10(self, mocked_sqlalchemy,mocked_read_df_sql,
mocked_getsmallrowdata_db):
mocked_read_data):
res = iplots.interactive_flex_chart2(self.wote.id,self.r,
promember=1,xparam='distance',
yparam1='pace',yparam2='hr',mode='water')
@patch('rowers.dataprep.create_engine')
@patch('rowers.dataprep.read_df_sql')
@patch('rowers.dataprep.getsmallrowdata_db',side_effect=mocked_getsmallrowdata_db)
@patch('rowers.dataprep.read_data',side_effect=mocked_read_data)
def test_interactive_chart11(self, mocked_sqlalchemy,mocked_read_df_sql,
mocked_getsmallrowdata_db):
mocked_read_data):
res = iplots.interactive_flex_chart2(self.wote.id,self.r,
promember=1,xparam='time',
yparam1='pace',yparam2='spm',mode='water')
@patch('rowers.dataprep.create_engine')
@patch('rowers.dataprep.read_df_sql')
@patch('rowers.dataprep.getsmallrowdata_db',side_effect=mocked_getsmallrowdata_db)
@patch('rowers.dataprep.read_data',side_effect=mocked_read_data)
def test_interactive_chart12(self, mocked_sqlalchemy,mocked_read_df_sql,
mocked_getsmallrowdata_db):
mocked_read_data):
res = iplots.interactive_flex_chart2(self.wote.id,self.r,
promember=1,
xparam='distance',
+8 -8
View File
@@ -911,8 +911,8 @@ class PermissionsViewTests(TestCase):
## Coach can upload on behalf of athlete - if team allows
@patch('rowers.dataprep.create_engine')
@patch('rowers.dataprep.getsmallrowdata_db',side_effect=mocked_getsmallrowdata_db)
def test_coach_edit_athlete_upload(self,mocked_sqlalchemy,mocked_getsmallrowdata_db):
@patch('rowers.dataprep.read_data',side_effect=mocked_read_data)
def test_coach_edit_athlete_upload(self,mocked_sqlalchemy,mocked_read_data):
self.rbasic.team.add(self.teamcoach)
self.rbasic.coachinggroups.add(self.coachinggroup)
@@ -957,8 +957,8 @@ class PermissionsViewTests(TestCase):
## Coach can upload on behalf of athlete - if team allows
@patch('rowers.dataprep.create_engine')
@patch('rowers.dataprep.getsmallrowdata_db',side_effect=mocked_getsmallrowdata_db)
def test_coach_edit_athlete_uploadnot(self,mocked_sqlalchemy,mocked_getsmallrowdata_db):
@patch('rowers.dataprep.read_data',side_effect=mocked_read_data)
def test_coach_edit_athlete_uploadnot(self,mocked_sqlalchemy,mocked_read_data):
self.rbasic.team.add(self.teamcoach)
login = self.c.login(username=self.ucoach.username, password=self.ucoachpassword)
@@ -1168,8 +1168,8 @@ class PermissionsViewTests(TestCase):
## Self Coach cannot upload on behalf of athlete
@patch('rowers.dataprep.create_engine')
@patch('rowers.dataprep.getsmallrowdata_db',side_effect=mocked_getsmallrowdata_db)
def test_plan_edit_athlete_upload(self,mocked_sqlalchemy,mocked_getsmallrowdata_db):
@patch('rowers.dataprep.read_data',side_effect=mocked_read_data)
def test_plan_edit_athlete_upload(self,mocked_sqlalchemy,mocked_read_data):
self.rpro.team.add(self.teamplan)
login = self.c.login(username=self.uplan2.username, password=self.uplan2password)
@@ -1291,8 +1291,8 @@ class PermissionsViewTests(TestCase):
## Self Coach cannot upload on behalf of athlete
@patch('rowers.dataprep.create_engine')
@patch('rowers.dataprep.getsmallrowdata_db',side_effect=mocked_getsmallrowdata_db)
def test_plan_edit_athlete_upload(self,mocked_sqlalchemy,mocked_getsmallrowdata_db):
@patch('rowers.dataprep.read_data',side_effect=mocked_read_data)
def test_plan_edit_athlete_upload(self,mocked_sqlalchemy,mocked_read_data):
self.rpro.team.add(self.teamplan)
login = self.c.login(username=self.uplan2.username, password=self.uplan2password)
+8 -8
View File
@@ -218,7 +218,7 @@ class PermissionsViewTests(TestCase):
@patch('rowers.integrations.c2.C2Integration.open')
@patch('rowers.dataprep.create_engine')
@patch('rowers.dataprep.read_df_sql')
@patch('rowers.dataprep.getsmallrowdata_db')
@patch('rowers.dataprep.read_data')
@patch('requests.get',side_effect=mocked_requests)
@patch('requests.post',side_effect=mocked_requests)
@patch('rowers.dataprep.get_video_data',side_effect=mocked_get_video_data)
@@ -228,7 +228,7 @@ class PermissionsViewTests(TestCase):
mock_c2open,
mocked_sqlalchemy,
mocked_read_df_sql,
mocked_getsmallrowdata_db,
mocked_read_data,
mock_get,
mock_post,
mocked_get_video_data,
@@ -253,7 +253,7 @@ class PermissionsViewTests(TestCase):
@patch('rowers.integrations.c2.C2Integration.open')
@patch('rowers.dataprep.create_engine')
@patch('rowers.dataprep.read_df_sql')
@patch('rowers.dataprep.getsmallrowdata_db')
@patch('rowers.dataprep.read_data')
@patch('requests.get',side_effect=mocked_requests)
@patch('requests.post',side_effect=mocked_requests)
@patch('rowers.dataprep.get_video_data',side_effect=mocked_get_video_data)
@@ -263,7 +263,7 @@ class PermissionsViewTests(TestCase):
mock_c2open,
mocked_sqlalchemy,
mocked_read_df_sql,
mocked_getsmallrowdata_db,
mocked_read_data,
mock_get,
mock_post,
mocked_get_video_data,
@@ -335,7 +335,7 @@ class PermissionsViewTests(TestCase):
@patch('rowers.integrations.c2.C2Integration.open')
@patch('rowers.dataprep.create_engine')
@patch('rowers.dataprep.read_df_sql')
@patch('rowers.dataprep.getsmallrowdata_db')
@patch('rowers.dataprep.read_data')
@patch('requests.get',side_effect=mocked_requests)
@patch('requests.post',side_effect=mocked_requests)
@patch('rowers.dataprep.get_video_data',side_effect=mocked_get_video_data)
@@ -345,7 +345,7 @@ class PermissionsViewTests(TestCase):
mock_c2open,
mocked_sqlalchemy,
mocked_read_df_sql,
mocked_getsmallrowdata_db,
mocked_read_data,
mock_get,
mock_post,
mocked_get_video_data,
@@ -418,7 +418,7 @@ class PermissionsViewTests(TestCase):
@patch('rowers.integrations.c2.C2Integration.open')
@patch('rowers.dataprep.create_engine')
@patch('rowers.dataprep.read_df_sql')
@patch('rowers.dataprep.getsmallrowdata_db')
@patch('rowers.dataprep.read_data')
@patch('requests.get',side_effect=mocked_requests)
@patch('requests.post',side_effect=mocked_requests)
@patch('rowers.dataprep.get_video_data',side_effect=mocked_get_video_data)
@@ -428,7 +428,7 @@ class PermissionsViewTests(TestCase):
mock_c2open,
mocked_sqlalchemy,
mocked_read_df_sql,
mocked_getsmallrowdata_db,
mocked_read_data,
mock_get,
mock_post,
mocked_get_video_data,
+14 -12
View File
@@ -13,6 +13,7 @@ from django.db import transaction
nu = datetime.datetime.now()
import datetime
import pytz
import polars as pl
# interactive plots
from rowers import interactiveplots
@@ -581,7 +582,7 @@ class DataPrepTests(TestCase):
age = dataprep.calculate_age(born,today=today)
self.assertEqual(age,49)
@patch('rowers.dataprep.getsmallrowdata_db',side_effect=mocked_getsmallrowdata_uh)
@patch('rowers.dataprep.read_data',side_effect=mocked_getsmallrowdata_uh)
def test_goldmedalstandard(self,mocked_getsmallrowdata_uh):
maxvalue, delta = dataprep.calculate_goldmedalstandard(self.r,self.wuh_otw)
records = CalcAgePerformance.objects.filter(
@@ -596,8 +597,8 @@ class DataPrepTests(TestCase):
result = getagegrouprecord(25)
self.assertEqual(int(result),590)
@patch('rowers.dataprep.getsmallrowdata_db',side_effect=mocked_getsmallrowdata_uh)
def test_get_videodata(self,mocked_getsmallrowdata_uh):
@patch('rowers.dataprep.getsmallrowdata_pd',side_effect=mocked_getsmallrowdata_uh_pd)
def test_get_videodata(self,mocked_getsmallrowdata_uh_pd):
data, metrics, maxtime = dataprep.get_video_data(self.wuh_otw)
self.assertEqual(len(data),9)
@@ -659,7 +660,7 @@ class InteractivePlotTests(TestCase):
pass
def test_interactive_hr_piechart(self):
df = pd.read_csv('rowers/tests/testdata/getrowdata_mock.csv')
df = pl.read_csv('rowers/tests/testdata/getrowdata_mock.csv')
script, div = interactiveplots.interactive_hr_piechart(df, self.r,'')
self.assertFalse(len(script)==0)
@@ -672,7 +673,8 @@ class InteractivePlotTests(TestCase):
self.assertFalse(len(div)==0)
def test_interactive_boxchart(self):
df = pd.read_csv('rowers/tests/testdata/boxplotdata.csv')
df = pl.read_csv('rowers/tests/testdata/boxplotdata.csv')
df = df.with_columns(pl.col("date").str.to_datetime("%Y-%m-%d"))
script, div = interactiveplots.interactive_boxchart(df, 'spm')
@@ -685,12 +687,12 @@ class InteractivePlotTests(TestCase):
startdate = workouts[0].date-datetime.timedelta(days=5)
enddate = workouts2[0].date+datetime.timedelta(days=5)
script, div = interactiveplots.interactive_activitychart(workouts,startdate,enddate)
script, div = interactiveplots.interactive_activitychart2(workouts,startdate,enddate)
self.assertFalse(len(script)==0)
self.assertFalse(len(div)==0)
def test_interactive_otwcpchart(self):
df = pd.read_csv('rowers/tests/testdata/otwcp.csv')
df = pl.read_csv('rowers/tests/testdata/otwcp.csv')
script, div, p1, ratio, message = interactiveplots.interactive_otwcpchart(df,r=self.r,cpfit='data')
self.assertFalse(len(script)==0)
@@ -701,9 +703,9 @@ class InteractivePlotTests(TestCase):
self.assertFalse(len(div)==0)
@patch('rowers.dataprep.create_engine')
@patch('rowers.dataprep.getsmallrowdata_db', side_effect=mocked_getsmallrowdata_db)
@patch('rowers.dataprep.read_data', side_effect=mocked_read_data)
def test_interactive_chart(self, mocked_sqlalchemy,
mocked_getsmallrowdata_db):
mocked_read_data):
workout = Workout.objects.filter(user=self.r,workouttype__in=mytypes.rowtypes)[0]
id = workout.id
@@ -733,12 +735,12 @@ class InteractivePlotTests(TestCase):
self.assertFalse(len(div)==0)
@patch('rowers.dataprep.create_engine')
@patch('rowers.dataprep.getsmallrowdata_db', side_effect=mocked_getsmallrowdata_db)
@patch('rowers.dataprep.read_data', side_effect=mocked_read_data)
def test_interactive_flexchart_stacked(self, mocked_sqlalchemy,
mocked_getsmallrowdata_db):
mocked_read_data):
workout = Workout.objects.filter(user=self.r,workouttype__in=mytypes.rowtypes)[0]
id = workout.id
script, div, js_res, css_res, comment = interactiveplots.interactive_flexchart_stacked(id,self.r)
script, div = interactiveplots.interactive_flexchart_stacked(id,self.r)
self.assertFalse(len(script)==0)
self.assertFalse(len(div)==0)
+10 -7
View File
@@ -71,9 +71,10 @@ class ForceUnits(TestCase):
w = Workout.objects.get(id=1)
self.assertEqual(w.forceunit,'lbs')
df = dataprep.getsmallrowdata_db(['averageforce'],ids=[13])
df = dataprep.read_data(['averageforce'],ids=[13])
df = dataprep.remove_nulls_pl(df)
average_N = int(df['averageforce'].mean())
self.assertEqual(average_N,398)
self.assertEqual(average_N,400)
data = dataprep.read_df_sql(13)
average_N = int(data['averageforce'].mean())
@@ -119,9 +120,10 @@ class ForceUnits(TestCase):
w = Workout.objects.get(id=13)
self.assertEqual(w.forceunit,'N')
df = dataprep.getsmallrowdata_db(['averageforce'],ids=[13])
df = dataprep.read_data(['averageforce'],ids=[13])
df = dataprep.remove_nulls_pl(df)
average_N = int(df['averageforce'].mean())
self.assertEqual(average_N,263)
self.assertEqual(average_N,271)
def test_upload_speedcoach_colin(self):
login = self.c.login(username=self.u.username, password=self.password)
@@ -155,9 +157,10 @@ class ForceUnits(TestCase):
w = Workout.objects.get(id=13)
self.assertEqual(w.forceunit,'N')
df = dataprep.getsmallrowdata_db(['averageforce'],ids=[13])
df = dataprep.read_data(['averageforce'],ids=[13])
df = dataprep.remove_nulls_pl(df)
average_N = int(df['averageforce'].mean())
self.assertEqual(average_N,105)
self.assertEqual(average_N,122)
@override_settings(TESTING=True)
class TestForceUnit(TestCase):
@@ -206,7 +209,7 @@ class TestForceUnit(TestCase):
rowdata = dataprep.rdata('rowers/tests/testdata/PainsledForce.csv')
df = dataprep.dataprep(rowdata.df)
#df = dataprep.getsmallrowdata_db(['averageforce'],ids=[w[0].id],doclean=False,
#df = dataprep.read_data(['averageforce'],ids=[w[0].id],doclean=False,
# compute=False)
try:
average_N = int(df['averageforce'].mean())
+4 -8
View File
@@ -29,8 +29,8 @@ class ViewTest(TestCase):
self.nu = datetime.datetime.now()
@patch('rowers.dataprep.create_engine')
@patch('rowers.dataprep.getsmallrowdata_db',side_effect=mocked_getsmallrowdata_db)
def test_upload_view_sled(self, mocked_sqlalchemy,mocked_getsmallrowdata_db):
@patch('rowers.dataprep.read_data',side_effect=mocked_read_data)
def test_upload_view_sled(self, mocked_sqlalchemy,mocked_read_data):
login = self.c.login(username='john',password='koeinsloot')
self.assertTrue(login)
@@ -74,10 +74,6 @@ class ViewTest(TestCase):
self.assertEqual(response.status_code, 200)
response = self.c.get('/rowers/workout/'+encoded1+'/histo/', form_data, follow=True)
self.assertEqual(response.status_code, 200)
f.close()
@@ -124,8 +120,8 @@ class ViewTest(TestCase):
pass
@patch('rowers.dataprep.create_engine')
@patch('rowers.dataprep.getsmallrowdata_db',side_effect=mocked_getsmallrowdata_db)
def test_upload_view_sled_bg(self, mocked_sqlalchemy,mocked_getsmallrowdata_db):
@patch('rowers.dataprep.read_data',side_effect=mocked_read_data)
def test_upload_view_sled_bg(self, mocked_sqlalchemy,mocked_read_data):
login = self.c.login(username='john',password='koeinsloot')
self.assertTrue(login)
+3 -16
View File
@@ -79,11 +79,6 @@ class URLTests(TestCase):
'/rowers/502/',
'/rowers/about/',
'/rowers/workout/addmanual/',
'/rowers/agegroupcp/30/',
'/rowers/agegroupcp/30/1/',
'/rowers/agegrouprecords/male/hwt/',
'/rowers/agegrouprecords/male/hwt/2000m/',
'/rowers/agegrouprecords/male/hwt/30min/',
'/rowers/ajax_agegroup/45/hwt/male/1/',
'/rowers/analysis/',
'/rowers/analysis/user/1/',
@@ -122,8 +117,6 @@ class URLTests(TestCase):
'/rowers/me/workflowconfig2/',
'/rowers/me/workflowconfig2/user/1/',
'/rowers/me/workflowdefault/',
'/rowers/ote-bests2/',
'/rowers/ote-bests2/user/1/',
'/rowers/partners/',
'/rowers/physics/',
'/rowers/planrequired/',
@@ -148,29 +141,23 @@ class URLTests(TestCase):
'/rowers/workout/'+encoded1+'/addtimeplot/',
'/rowers/workout/'+encoded1+'/addtimeplot2/',
'/rowers/workout/'+encoded1+'/comment/',
'/rowers/workout/'+encoded1+'/darkskywind/',
'/rowers/workout/'+encoded1+'/data/',
'/rowers/workout/'+encoded1+'/edit/',
'/rowers/workout/'+encoded1+'/editintervals/',
'/rowers/workout/'+encoded1+'/flexchart/',
'/rowers/workout/'+encoded1+'/forcecurve/',
'/rowers/workout/'+encoded1+'/get-thumbnails/',
'/rowers/workout/'+encoded1+'/histo/',
'/rowers/workout/'+encoded1+'/image/',
'/rowers/workout/'+encoded1+'/instroke/',
'/rowers/workout/'+encoded1+'/interactiveotwplot/',
'/rowers/workout/'+encoded1+'/map/',
'/rowers/workout/'+encoded1+'/otwsetpower/',
'/rowers/workout/'+encoded1+'/recalcsummary/',
'/rowers/workout/'+encoded1+'/restore/',
'/rowers/workout/'+encoded1+'/smoothenpace/',
'/rowers/workout/'+encoded1+'/split/',
'/rowers/workout/'+encoded1+'/stats/',
'/rowers/workout/'+encoded1+'/stream/',
'/rowers/workout/'+encoded1+'/undosmoothenpace/',
'/rowers/workout/'+encoded1+'/unsubscribe/',
'/rowers/workout/'+encoded1+'/view/',
'/rowers/workout/'+encoded1+'/wind/',
'/rowers/workout/'+encoded1+'/workflow/',
'/rowers/workout/fusion/'+encoded1+'/',
'/rowers/workout/upload/',
@@ -206,7 +193,7 @@ class URLTests(TestCase):
@parameterized.expand(lijst)
@patch('rowers.dataprep.create_engine')
@patch('rowers.dataprep.read_df_sql')
@patch('rowers.dataprep.getsmallrowdata_db')
@patch('rowers.dataprep.read_data')
@patch('requests.get',side_effect=mocked_requests)
@patch('requests.post',side_effect=mocked_requests)
@patch('rowers.dataprep.get_video_data',side_effect=mocked_get_video_data)
@@ -214,7 +201,7 @@ class URLTests(TestCase):
def test_url_generator(self,url,expected,
mocked_sqlalchemy,
mocked_read_df_sql,
mocked_getsmallrowdata_db,
mocked_read_data,
mock_get,
mock_post,
mocked_get_video_data,
@@ -225,7 +212,7 @@ class URLTests(TestCase):
self.assertTrue(login)
response = self.c.get(url,follow=True)
if response.status_code != expected:
print(url )
print(url)
print(response.status_code)
self.assertEqual(response.status_code,
Binary file not shown.
-10
View File
@@ -1,9 +1,7 @@
,id,view,function,anonymous,anonymous_response,own,own_response,own_nonperm,member,member_response,member_nonperm,coachee,coachee_response,coachee_nonperm,is_staff,userid,workoutid,dotest,realtest,kwargs
0,0,workouts_summaries_email_view,sends summary excel with workouts list and links to data to user,TRUE,302,basic,200,302,FALSE,403,403,FALSE,403,403,FALSE,FALSE,FALSE,TRUE,TRUE,
1,1,rower_update_empower_view,updates old Empower Oarlock files (corrects Power bug),TRUE,302,basic,200,302,FALSE,200,302,FALSE,200,302,FALSE,FALSE,FALSE,TRUE,TRUE,
2,2,agegroupcpview,needs age,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE,FALSE,
3,4,ajax_agegrouprecords,gets age group records from C2 ,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE,FALSE,
5,6,agegrouprecordview,shows ergo age group records,TRUE,200,basic,200,302,FALSE,200,302,FALSE,200,302,FALSE,FALSE,FALSE,TRUE,TRUE,
6,7,workouts_view,workouts list,TRUE,302,basic,200,302,basic,200,403,coach,200,403,FALSE,TRUE,FALSE,TRUE,TRUE,
7,8,virtualevents_view,virtual races list,TRUE,200,basic,200,302,FALSE,200,302,FALSE,200,302,FALSE,FALSE,FALSE,TRUE,TRUE,
8,9,virtualevent_create_view,create virtual event,TRUE,302,basic,200,302,FALSE,200,302,FALSE,200,302,FALSE,FALSE,FALSE,TRUE,TRUE,
@@ -31,7 +29,6 @@
33,35,kill_async_job,kill job,TRUE,302,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE,FALSE,
34,36,post_progress,post progress,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE,FALSE,
35,37,graphs_view,view charts,TRUE,302,basic,200,302,basic,200,302,coach,200,302,FALSE,TRUE,FALSE,TRUE,TRUE,
38,40,rankings_view2,view ranking,TRUE,302,pro,200,302,pro,403,302,coach,200,302,FALSE,TRUE,FALSE,TRUE,TRUE,
41,43,cum_flex,flex all chart,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE,FALSE,
42,44,analysis_view_data,redirects to analysis direct,TRUE,302,pro,200,302,pro,200,302,coach,200,302,FALSE,FALSE,FALSE,TRUE,TRUE,
43,47,cum_flex_data,flex all chart data (json),TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE,FALSE,
@@ -43,7 +40,6 @@
52,58,workout_toggle_ranking,toggle ranking,TRUE,302,basic,302,302,basic,403,302,coach,302,302,FALSE,FALSE,TRUE,TRUE,TRUE,
53,59,team_workout_upload_view,upload workout for team member,TRUE,302,coach,200,302,FALSE,200,302,FALSE,200,302,FALSE,FALSE,FALSE,TRUE,TRUE,
54,60,workout_upload_view,upload a workout,TRUE,302,basic,200,302,FALSE,200,302,FALSE,200,302,FALSE,FALSE,FALSE,TRUE,TRUE,
55,61,workout_histo_view,histogram ,TRUE,302,basic,200,302,basic,403,403,coach,200,302,FALSE,FALSE,TRUE,TRUE,TRUE,
56,62,workout_forcecurve_view,force curve,TRUE,302,pro,200,302,pro,403,302,coach,200,302,FALSE,FALSE,TRUE,TRUE,TRUE,
57,63,workout_unsubscribe_view,unsubscribe from comments,TRUE,302,basic,200,302,basic,200,302,basic,200,302,FALSE,FALSE,TRUE,TRUE,TRUE,
58,64,workout_comment_view,comment on workout,TRUE,302,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,TRUE,TRUE,TRUE,
@@ -57,17 +53,11 @@
67,73,instroke_view,Create in stroke chart,TRUE,403,basic,200,302,basic,403,403,coach,200,302,FALSE,FALSE,TRUE,TRUE,TRUE,
68,74,workout_stats_view,View Workout Stats,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,TRUE,TRUE,TRUE,
69,75,workout_data_view,shows the detailed data for a workout,TRUE,403,basic,200,403,basic,403,403,coach,200,403,FALSE,FALSE,TRUE,TRUE,TRUE,
70,76,workout_otwsetpower_view,set weight and start offline OTW power calculations,TRUE,403,pro,200,302,pro,403,403,coach,200,403,FALSE,FALSE,TRUE,TRUE,TRUE,
71,77,workout_otwpowerplot_view,generates OTW power chart,TRUE,200,basic,200,200,basic,200,200,coach,200,302,FALSE,FALSE,TRUE,TRUE,TRUE,
72,78,workout_wind_view,set wind,TRUE,403,pro,200,302,pro,403,403,coach,200,403,FALSE,FALSE,TRUE,TRUE,TRUE,
73,79,workout_uploadimage_view,upload image,TRUE,403,basic,200,403,basic,403,403,coach,200,403,FALSE,FALSE,TRUE,TRUE,TRUE,
74,80,virtualevent_compare_view,compare workouts from a virtual event,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE,FALSE,
75,81,virtualevent_uploadimage_view,upload image to virtual event,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE,FALSE,
76,82,virtualevent_setlogo_view,set logo of virtual event,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE,FALSE,
77,83,logo_delete_view,delete logo,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE,FALSE,
78,84,workout_downloadwind_view,download Wind,TRUE,403,pro,302,302,pro,403,403,coach,302,403,FALSE,FALSE,TRUE,FALSE,FALSE,
79,85,workout_downloadmetar_view,download METAR,TRUE,403,pro,200,302,pro,403,403,coach,200,403,FALSE,FALSE,TRUE,FALSE,FALSE,
80,86,workout_stream_view,edit stream (redirects as no data in test suite),TRUE,403,pro,200,302,pro,403,403,coach,200,302,FALSE,FALSE,TRUE,TRUE,TRUE,
81,87,workout_summary_edit_view,edit workout summary,TRUE,302,basic,200,403,basic,403,403,coach,200,403,FALSE,FALSE,TRUE,TRUE,TRUE,
82,88,workout_summary_restore_view,restore workout summary,TRUE,403,basic,302,403,basic,403,403,coach,302,403,FALSE,FALSE,TRUE,TRUE,TRUE,
83,89,workout_split_view,split workout,TRUE,403,pro,200,302,pro,403,403,coach,200,302,FALSE,FALSE,TRUE,TRUE,TRUE,
1 id view function anonymous anonymous_response own own_response own_nonperm member member_response member_nonperm coachee coachee_response coachee_nonperm is_staff userid workoutid dotest realtest kwargs
2 0 0 workouts_summaries_email_view sends summary excel with workouts list and links to data to user TRUE 302 basic 200 302 FALSE 403 403 FALSE 403 403 FALSE FALSE FALSE TRUE TRUE
3 1 1 rower_update_empower_view updates old Empower Oarlock files (corrects Power bug) TRUE 302 basic 200 302 FALSE 200 302 FALSE 200 302 FALSE FALSE FALSE TRUE TRUE
2 2 agegroupcpview needs age TRUE 200 basic 200 302 basic 200 302 coach 200 302 FALSE FALSE FALSE FALSE FALSE
4 3 4 ajax_agegrouprecords gets age group records from C2 TRUE 200 basic 200 302 basic 200 302 coach 200 302 FALSE FALSE FALSE FALSE FALSE
5 6 agegrouprecordview shows ergo age group records TRUE 200 basic 200 302 FALSE 200 302 FALSE 200 302 FALSE FALSE FALSE TRUE TRUE
5 6 7 workouts_view workouts list TRUE 302 basic 200 302 basic 200 403 coach 200 403 FALSE TRUE FALSE TRUE TRUE
6 7 8 virtualevents_view virtual races list TRUE 200 basic 200 302 FALSE 200 302 FALSE 200 302 FALSE FALSE FALSE TRUE TRUE
7 8 9 virtualevent_create_view create virtual event TRUE 302 basic 200 302 FALSE 200 302 FALSE 200 302 FALSE FALSE FALSE TRUE TRUE
29 33 35 kill_async_job kill job TRUE 302 basic 200 302 basic 200 302 coach 200 302 FALSE FALSE FALSE FALSE FALSE
30 34 36 post_progress post progress TRUE 200 basic 200 302 basic 200 302 coach 200 302 FALSE FALSE FALSE FALSE FALSE
31 35 37 graphs_view view charts TRUE 302 basic 200 302 basic 200 302 coach 200 302 FALSE TRUE FALSE TRUE TRUE
38 40 rankings_view2 view ranking TRUE 302 pro 200 302 pro 403 302 coach 200 302 FALSE TRUE FALSE TRUE TRUE
32 41 43 cum_flex flex all chart TRUE 200 basic 200 302 basic 200 302 coach 200 302 FALSE FALSE FALSE FALSE FALSE
33 42 44 analysis_view_data redirects to analysis direct TRUE 302 pro 200 302 pro 200 302 coach 200 302 FALSE FALSE FALSE TRUE TRUE
34 43 47 cum_flex_data flex all chart data (json) TRUE 200 basic 200 302 basic 200 302 coach 200 302 FALSE FALSE FALSE FALSE FALSE
40 52 58 workout_toggle_ranking toggle ranking TRUE 302 basic 302 302 basic 403 302 coach 302 302 FALSE FALSE TRUE TRUE TRUE
41 53 59 team_workout_upload_view upload workout for team member TRUE 302 coach 200 302 FALSE 200 302 FALSE 200 302 FALSE FALSE FALSE TRUE TRUE
42 54 60 workout_upload_view upload a workout TRUE 302 basic 200 302 FALSE 200 302 FALSE 200 302 FALSE FALSE FALSE TRUE TRUE
55 61 workout_histo_view histogram TRUE 302 basic 200 302 basic 403 403 coach 200 302 FALSE FALSE TRUE TRUE TRUE
43 56 62 workout_forcecurve_view force curve TRUE 302 pro 200 302 pro 403 302 coach 200 302 FALSE FALSE TRUE TRUE TRUE
44 57 63 workout_unsubscribe_view unsubscribe from comments TRUE 302 basic 200 302 basic 200 302 basic 200 302 FALSE FALSE TRUE TRUE TRUE
45 58 64 workout_comment_view comment on workout TRUE 302 basic 200 302 basic 200 302 coach 200 302 FALSE FALSE TRUE TRUE TRUE
53 67 73 instroke_view Create in stroke chart TRUE 403 basic 200 302 basic 403 403 coach 200 302 FALSE FALSE TRUE TRUE TRUE
54 68 74 workout_stats_view View Workout Stats TRUE 200 basic 200 302 basic 200 302 coach 200 302 FALSE FALSE TRUE TRUE TRUE
55 69 75 workout_data_view shows the detailed data for a workout TRUE 403 basic 200 403 basic 403 403 coach 200 403 FALSE FALSE TRUE TRUE TRUE
70 76 workout_otwsetpower_view set weight and start offline OTW power calculations TRUE 403 pro 200 302 pro 403 403 coach 200 403 FALSE FALSE TRUE TRUE TRUE
71 77 workout_otwpowerplot_view generates OTW power chart TRUE 200 basic 200 200 basic 200 200 coach 200 302 FALSE FALSE TRUE TRUE TRUE
72 78 workout_wind_view set wind TRUE 403 pro 200 302 pro 403 403 coach 200 403 FALSE FALSE TRUE TRUE TRUE
56 73 79 workout_uploadimage_view upload image TRUE 403 basic 200 403 basic 403 403 coach 200 403 FALSE FALSE TRUE TRUE TRUE
57 74 80 virtualevent_compare_view compare workouts from a virtual event TRUE 200 basic 200 302 basic 200 302 coach 200 302 FALSE FALSE FALSE FALSE FALSE
58 75 81 virtualevent_uploadimage_view upload image to virtual event TRUE 200 basic 200 302 basic 200 302 coach 200 302 FALSE FALSE FALSE FALSE FALSE
59 76 82 virtualevent_setlogo_view set logo of virtual event TRUE 200 basic 200 302 basic 200 302 coach 200 302 FALSE FALSE FALSE FALSE FALSE
60 77 83 logo_delete_view delete logo TRUE 200 basic 200 302 basic 200 302 coach 200 302 FALSE FALSE FALSE FALSE FALSE
78 84 workout_downloadwind_view download Wind TRUE 403 pro 302 302 pro 403 403 coach 302 403 FALSE FALSE TRUE FALSE FALSE
79 85 workout_downloadmetar_view download METAR TRUE 403 pro 200 302 pro 403 403 coach 200 403 FALSE FALSE TRUE FALSE FALSE
80 86 workout_stream_view edit stream (redirects as no data in test suite) TRUE 403 pro 200 302 pro 403 403 coach 200 302 FALSE FALSE TRUE TRUE TRUE
61 81 87 workout_summary_edit_view edit workout summary TRUE 302 basic 200 403 basic 403 403 coach 200 403 FALSE FALSE TRUE TRUE TRUE
62 82 88 workout_summary_restore_view restore workout summary TRUE 403 basic 302 403 basic 403 403 coach 302 403 FALSE FALSE TRUE TRUE TRUE
63 83 89 workout_split_view split workout TRUE 403 pro 200 302 pro 403 403 coach 200 302 FALSE FALSE TRUE TRUE TRUE
+2 -23
View File
@@ -273,19 +273,13 @@ urlpatterns = [
re_path(r'^exportallworkouts/?/$', views.workouts_summaries_email_view,
name='workouts_summaries_email_view'),
path('failedjobs/', views.failed_queue_view, name='failed_queue_view'),
path('sleep/', views.sleep_view, name='sleep_view'),
path('filmdeaths/', views.filmdeaths_view, name='filmdeaths_view'),
path('failedjobs/empty/', views.failed_queue_empty, name='failed_queue_empty'),
re_path('^failedjobs/(?P<id>\w+.*)/$',
views.failed_job_view, name='failed_job_view'),
re_path(r'^update_empower/$', views.rower_update_empower_view,
name='rower_update_empower_view'),
re_path(r'^agegroupcp/(?P<age>\d+)/$',
views.agegroupcpview, name='agegroupcpview'),
re_path(r'^agegroupcp/(?P<age>\d+)/user/(?P<userid>\d+)/$',
views.agegroupcpview, name='agegroupcpview'),
re_path(r'^agegroupcp/(?P<age>\d+)/(?P<normalize>\d+)/$',
views.agegroupcpview, name='agegroupcpview'),
re_path(r'^agegroupcp/(?P<age>\d+)/(?P<normalize>\d+)/user/(?P<userid>\d+)/$',
views.agegroupcpview, name='agegroupcpview'),
re_path(r'^ajax_agegroup/(?P<age>\d+)/(?P<weightcategory>\w+.*)/(?P<sex>\w+.*)/(?P<userid>\d+)/$',
views.ajax_agegrouprecords, name='ajax_agegrouprecords'),
re_path(r'^agegrouprecords/(?P<sex>\w+.*)/(?P<weightcategory>\w+.*)/(?P<distance>\d+)m/$',
@@ -448,9 +442,6 @@ urlpatterns = [
views.trainingzones_view_data, name="trainingzones_view_data"),
re_path(r'^trainingzones/data/$', views.trainingzones_view_data,
name="trainingzones_view_data"),
re_path(r'^ote-bests2/user/(?P<userid>\d+)/$',
views.rankings_view2, name='rankings_view2'),
re_path(r'^ote-bests2/$', views.rankings_view2, name='rankings_view2'),
re_path(r'^analysisdata/user/(?P<userid>\d+)/$', views.analysis_view_data,
name='analysis_view_data'),
re_path(r'^analysisdata/$', views.analysis_view_data,
@@ -475,8 +466,6 @@ urlpatterns = [
views.workout_upload_view, name='workout_upload_view'),
re_path(r'^workout/upload/$', views.workout_upload_view,
name='workout_upload_view'),
re_path(r'^workout/(?P<id>\b[0-9A-Fa-f]+\b)/histo/$', views.workout_histo_view,
name='workout_histo_view'),
re_path(r'^workout/(?P<id>\b[0-9A-Fa-f]+\b)/forcecurve/$', views.workout_forcecurve_view,
name='workout_forcecurve_view'),
re_path(r'^workout/(?P<id>\b[0-9A-Fa-f]+\b)/forcecurve/(?P<analysis>\d+)/$', views.workout_forcecurve_view,
@@ -513,14 +502,6 @@ urlpatterns = [
name='workout_erase_column_view'),
re_path(r'^workout/(?P<id>\b[0-9A-Fa-f]+\b)/zeropower-confirm/$', views.remove_power_confirm_view,
name='remove_power_confirm_view'),
re_path(r'^workout/(?P<id>\b[0-9A-Fa-f]+\b)/zeropower/$', views.remove_power_view,
name='remove_power_view'),
re_path(r'^workout/(?P<id>\b[0-9A-Fa-f]+\b)/otwsetpower/$',
views.workout_otwsetpower_view, name='workout_otwsetpower_view'),
re_path(r'^workout/(?P<id>\b[0-9A-Fa-f]+\b)/interactiveotwplot/$',
views.workout_otwpowerplot_view, name='workout_otwpowerplot_view'),
re_path(r'^workout/(?P<id>\b[0-9A-Fa-f]+\b)/wind/$',
views.workout_wind_view, name='workout_wind_view'),
re_path(r'^workout/(?P<id>\b[0-9A-Fa-f]+\b)/image/$',
views.workout_uploadimage_view, name='workout_uploadimage_view'),
re_path(r'^virtualevent/(?P<id>\d+)/compare/$',
@@ -543,8 +524,6 @@ urlpatterns = [
views.workout_downloadwind_view, name='workout_downloadwind_view'),
re_path(r'^workout/(?P<id>\b[0-9A-Fa-f]+\b)/metar/(?P<airportcode>\w+)/$',
views.workout_downloadmetar_view, name='workout_downloadmetar_view'),
re_path(r'^workout/(?P<id>\b[0-9A-Fa-f]+\b)/stream/$',
views.workout_stream_view, name='workout_stream_view'),
re_path(r'^workout/(?P<id>\b[0-9A-Fa-f]+\b)/editintervals/$', views.workout_summary_edit_view,
name='workout_summary_edit_view'),
re_path(r'^workout/(?P<id>\b[0-9A-Fa-f]+\b)/restore/$',
+20 -21
View File
@@ -6,6 +6,8 @@ from django.utils import timezone
import math
import numpy as np
import pandas as pd
import polars as pl
from polars.exceptions import ColumnNotFoundError
import colorsys
from django.conf import settings
import collections
@@ -155,12 +157,7 @@ def get_call():
call1 = random.choice(coxes_calls)
call2 = random.choice(info_calls)
call = """<div id="id_sitready" class="successmessage">
<p>
%s (%s)
</p>
</div>
""" % (call1, call2)
call = """<div id="id_sitready" class="successmessage"><p>%s (%s)</p></div>""" % (call1, call2)
return call
@@ -347,22 +344,22 @@ def isbreakthrough(delta, cpvalues, p0, p1, p2, p3, ratio):
pwr *= ratio
delta = delta.astype(int, errors='ignore').values
cpvalues = cpvalues.astype(int, errors='ignore').values
pwr = pwr.astype(int, errors='ignore').values
delta = delta.cast(pl.Int32)
cpvalues = cpvalues.cast(pl.Int32)
pwr = pwr.cast(pl.Int32)
res = np.sum(cpvalues > pwr+1)
res2 = np.sum(cpvalues > pwr2+1)
btdf = pl.DataFrame({
'delta': delta,
'cpvalues': cpvalues,
'pwr': pwr,
'pwr2': pwr2
})
btdf = pd.DataFrame(
{
'delta': delta[cpvalues > pwr],
'cpvalues': cpvalues[cpvalues > pwr],
'pwr': pwr[cpvalues > pwr],
}
)
res = btdf.select(pl.col("cpvalues")>pl.col("pwr")+1)['cpvalues'].sum()
res2 = btdf.select(pl.col("cpvalues")> pl.col("pwr2")+1)['cpvalues'].sum()
btdf.sort_values('delta', axis=0, inplace=True)
btdf = btdf.filter(pl.col("cpvalues")>pl.col("pwr"))
btdf = btdf.sort('delta')
return res >= 1, btdf, res2 >= 1
@@ -444,17 +441,19 @@ def wavg(group, avg_name, weight_name):
"""
try:
d = group[avg_name]
except KeyError:
except (KeyError, ColumnNotFoundError):
return 0
try:
w = group[weight_name]
except KeyError:
except (KeyError, ColumnNotFoundError):
return d.mean()
try:
return (d * w).sum() / w.sum()
except ZeroDivisionError: # pragma: no cover
return d.mean()
return 0
from string import Formatter
def totaltime_sec_to_string(totaltime, shorten=False):
+71 -602
View File
@@ -296,7 +296,10 @@ def analysis_new(request,
df = cpdata(tw, options)
options['savedata'] = False
request.session['options'] = options
response = HttpResponse(df.to_csv())
try:
response = HttpResponse(df.to_csv())
except AttributeError:
response = HttpResponse(df.write_csv())
code = str(uuid4())
filename = code+'.csv'
chartform.fields['savedata'].initial = False
@@ -384,6 +387,9 @@ def trendflexdata(workouts, options, userid=0):
datadf = dataprep.filter_df(datadf, 'driveneergy', workmax,
largerthan=False)
if yparam == 'power':
datadf = dataprep.filter_df(datadf, 'power', 1, largerthan=True)
datadf.dropna(axis=0, how='any', inplace=True)
datemapping = {
@@ -472,8 +478,8 @@ def trendflexdata(workouts, options, userid=0):
'groupsize': groupsize,
})
if yparam == 'pace':
df['y'] = dataprep.paceformatsecs(df['y']/1.0e3)
#if yparam == 'pace':
# df['y'] = dataprep.paceformatsecs(df['y']/1.0e3)
aantal = len(df)
@@ -568,7 +574,7 @@ def flexalldata(workouts, options):
workstrokesonly = not includereststrokes
columns = [xparam, yparam1, yparam2, 'spm', 'driveenergy', 'distance']
ids = [int(w.id) for w in workouts]
df = dataprep.getsmallrowdata_db(columns, ids=ids,
df = dataprep.read_data(columns, ids=ids,
workstrokesonly=workstrokesonly,
doclean=True,
)
@@ -609,7 +615,7 @@ def histodata(workouts, options):
if savedata: # pragma: no cover
workstrokesonly = not includereststrokes
ids = [int(w.id) for w in workouts]
df = dataprep.getsmallrowdata_db([plotfield], ids=ids,
df = dataprep.read_data([plotfield], ids=ids,
workstrokesonly=workstrokesonly,
doclean=True,
)
@@ -625,6 +631,7 @@ def histodata(workouts, options):
def cpdata(workouts, options):
start = timezone.now()
userid = options['userid']
cpfit = options['cpfit']
cpoverlay = options['cpoverlay']
@@ -635,7 +642,7 @@ def cpdata(workouts, options):
delta, cpvalue, avgpower, workoutnames, urls = dataprep.fetchcp_new(
r, workouts)
powerdf = pd.DataFrame({
powerdf = pl.DataFrame({
'Delta': delta,
'CP': cpvalue,
'workout': workoutnames,
@@ -644,16 +651,17 @@ def cpdata(workouts, options):
savedata = options.get('savedata',False)
if savedata: # pragma: no cover
return powerdf
return powerdf.to_pandas()
if powerdf.empty: # pragma: no cover
if powerdf.is_empty(): # pragma: no cover
return('', '<p>No valid data found</p>')
powerdf = powerdf[powerdf['CP'] > 0]
powerdf.dropna(axis=0, inplace=True)
powerdf.sort_values(['Delta', 'CP'], ascending=[1, 0], inplace=True)
powerdf.drop_duplicates(subset='Delta', keep='first', inplace=True)
powerdf = powerdf.lazy().filter(pl.col("CP")>0)
powerdf = powerdf.sort(["Delta", "CP"], descending=[False, True])
powerdf = powerdf.unique(subset="Delta", keep="first")
powerdf = powerdf.fill_nan(None).drop_nulls()
powerdf = powerdf.collect()
rowername = r.user.first_name+" "+r.user.last_name
@@ -880,7 +888,7 @@ def comparisondata(workouts, options):
'time', 'pace', 'workoutstate',
'workoutid']
df = dataprep.getsmallrowdata_db(columns, ids=ids,
df = dataprep.read_data(columns, ids=ids,
workstrokesonly=workstrokesonly,
doclean=True,
)
@@ -923,28 +931,22 @@ def boxplotdata(workouts, options):
ids = [w.id for w in workouts]
# prepare data frame
datadf, extracols = dataprep.read_cols_df_sql(ids, fieldlist)
datadf = dataprep.read_data(fieldlist, ids)
datadf = dataprep.clean_df_stats(datadf, workstrokesonly=workstrokesonly)
datadf = dataprep.remove_nulls_pl(datadf)
datadf = dataprep.filter_df(datadf, 'spm', spmmin,
largerthan=True)
datadf = dataprep.filter_df(datadf, 'spm', spmmax,
largerthan=False)
datadf = dataprep.filter_df(datadf, 'driveenergy', workmin,
largerthan=True)
datadf = dataprep.filter_df(datadf, 'driveneergy', workmax,
largerthan=False)
try:
datadf = datadf.filter(
pl.col("spm")>spmmin,
pl.col("spm")<spmmax,
pl.col("driveenergy")>workmin,
pl.col("driveenergy")<workmax,
)
except ColumnNotFoundError:
pass
datadf.dropna(axis=0, how='any', inplace=True)
datadf = datadf[datadf['workoutid'].isin(ids) == True]
datadf['workoutid'].replace(datemapping, inplace=True)
datadf.rename(columns={"workoutid": "date"}, inplace=True)
datadf['date'] = pd.to_datetime(datadf['date'], errors='coerce')
datadf = datadf.dropna(subset=['date'])
datadf = datadf.sort_values(['date'])
datadf = datadf.with_columns((pl.col("workoutid").apply(lambda x: datemapping[x])).alias("date"))
if userid == 0: # pragma: no cover
extratitle = ''
@@ -954,7 +956,7 @@ def boxplotdata(workouts, options):
savedata = options.get('savedata',False)
if savedata: # pragma: no cover
return datadf
return datadf.to_pandas()
script, div = interactive_boxchart(datadf, plotfield,
extratitle=extratitle,
@@ -1149,9 +1151,6 @@ def trainingzones_view(request, userid=0):
form = TrainingZonesForm({
'startdate': startdate,
'enddate': enddate,
'zones': zones,
'dates': date_agg,
'yaxis': yaxis,
})
if request.method == 'POST': # pragma: no cover
@@ -1160,9 +1159,6 @@ def trainingzones_view(request, userid=0):
if form.is_valid():
startdate = form.cleaned_data['startdate']
enddate = form.cleaned_data['enddate']
zones = form.cleaned_data['zones']
date_agg = form.cleaned_data['dates']
yaxis = form.cleaned_data['yaxis']
if date_agg == 'week':
startdate = startdate - datetime.timedelta(days=startdate.weekday())
@@ -1234,12 +1230,16 @@ def trainingzones_view_data(request, userid=0):
request.GET.get('enddate'), "%Y-%m-%d")
enddate = arrow.get(enddate).datetime
data = get_zones_report(r, startdate, enddate,
trainingzones=zones, date_agg=date_agg, yaxis=yaxis)
if (enddate-startdate).days > 200:
date_agg = "month"
script, div = interactive_zoneschart(
data = get_zones_report_pl(r, startdate, enddate,
trainingzones=zones, date_agg=date_agg, yaxis=yaxis)
script, div = interactive_zoneschart2(
r, data, startdate, enddate, trainingzones=zones, date_agg=date_agg, yaxis=yaxis)
return JSONResponse({
'script': script,
'div': div,
@@ -1384,547 +1384,6 @@ def ajax_agegrouprecords(request,
)
# Show ranking distances including predicted paces
@login_required()
@permission_required('rower.is_coach', fn=get_user_by_userid, raise_exception=True)
def rankings_view2(request, userid=0,
startdate=timezone.now()-datetime.timedelta(days=365),
enddate=timezone.now(),
deltadays=-1,
startdatestring="",
enddatestring=""):
if deltadays > 0: # pragma: no cover
startdate = enddate-datetime.timedelta(days=int(deltadays))
if startdatestring != "": # pragma: no cover
startdate = iso8601.parse_date(startdatestring)
if enddatestring != "": # pragma: no cover
enddate = iso8601.parse_date(enddatestring)
if enddate < startdate: # pragma: no cover
s = enddate
enddate = startdate
startdate = s
if userid == 0:
userid = request.user.id
else:
lastupdated = "1900-01-01"
promember = 0
r = getrequestrower(request, userid=userid)
theuser = r.user
wcdurations = []
wcpower = []
lastupdated = "1900-01-01"
userid = 0
if 'options' in request.session:
options = request.session['options']
try:
wcdurations = options['wcdurations']
wcpower = options['wcpower']
lastupdated = options['lastupdated']
except KeyError: # pragma: no cover
pass
try:
userid = options['userid']
except KeyError: # pragma: no cover
userid = 0
else:
options = {}
lastupdatedtime = arrow.get(lastupdated).timestamp()
current_time = arrow.utcnow().timestamp()
deltatime_seconds = current_time - lastupdatedtime
recalc = False
if str(userid) != str(theuser) or deltatime_seconds > 3600:
recalc = True
options['lastupdated'] = arrow.utcnow().isoformat()
else: # pragma: no cover
recalc = False
options['userid'] = theuser.id
if r.birthdate:
age = calculate_age(r.birthdate)
else:
age = 0
agerecords = CalcAgePerformance.objects.filter(
age=age,
sex=r.sex,
weightcategory=r.weightcategory)
if len(agerecords) == 0:
recalc = True
wcpower = []
wcdurations = []
else:
wcdurations = []
wcpower = []
for record in agerecords:
wcdurations.append(record.duration)
wcpower.append(record.power)
options['wcpower'] = wcpower
options['wcdurations'] = wcdurations
if theuser:
options['userid'] = theuser.id
request.session['options'] = options
result = request.user.is_authenticated and ispromember(request.user)
if result:
promember = 1
# get all indoor rows in date range
# process form
if request.method == 'POST' and "daterange" in request.POST:
dateform = DateRangeForm(request.POST)
deltaform = DeltaDaysForm(request.POST)
if dateform.is_valid():
startdate = dateform.cleaned_data['startdate']
enddate = dateform.cleaned_data['enddate']
if startdate > enddate: # pragma: no cover
s = enddate
enddate = startdate
startdate = s
elif request.method == 'POST' and "datedelta" in request.POST: # pragma: no cover
deltaform = DeltaDaysForm(request.POST)
if deltaform.is_valid():
deltadays = deltaform.cleaned_data['deltadays']
if deltadays:
enddate = timezone.now()
startdate = enddate-datetime.timedelta(days=deltadays)
if startdate > enddate:
s = enddate
enddate = startdate
startdate = s
dateform = DateRangeForm(initial={
'startdate': startdate,
'enddate': enddate,
})
else:
dateform = DateRangeForm()
deltaform = DeltaDaysForm()
else:
dateform = DateRangeForm(initial={
'startdate': startdate,
'enddate': enddate,
})
deltaform = DeltaDaysForm()
# get all 2k (if any) - this rower, in date range
try:
r = getrower(theuser)
except Rower.DoesNotExist: # pragma: no cover
r = 0
uu = theuser
# test to fix bug
startdate = datetime.datetime.combine(startdate, datetime.time())
enddate = datetime.datetime.combine(enddate, datetime.time(23, 59, 59))
startdate = arrow.get(startdate).datetime
enddate = arrow.get(enddate).datetime
thedistances = []
theworkouts = []
thesecs = []
rankingdistances.sort()
rankingdurations.sort()
for rankingdistance in rankingdistances:
workouts = Workout.objects.filter(
user=r, distance=rankingdistance,
workouttype__in=['rower', 'dynamic', 'slides'],
rankingpiece=True,
startdatetime__gte=startdate,
startdatetime__lte=enddate).order_by('duration')
if workouts:
thedistances.append(rankingdistance)
theworkouts.append(workouts[0])
timesecs = 3600*workouts[0].duration.hour
timesecs += 60*workouts[0].duration.minute
timesecs += workouts[0].duration.second
timesecs += 1.e-6*workouts[0].duration.microsecond
thesecs.append(timesecs)
for rankingduration in rankingdurations:
workouts = Workout.objects.filter(
user=r, duration=rankingduration,
workouttype='rower',
rankingpiece=True,
startdatetime__gte=startdate,
startdatetime__lte=enddate).order_by('-distance')
if workouts:
thedistances.append(workouts[0].distance)
theworkouts.append(workouts[0])
timesecs = 3600*workouts[0].duration.hour
timesecs += 60*workouts[0].duration.minute
timesecs += workouts[0].duration.second
timesecs += 1.e-5*workouts[0].duration.microsecond
thesecs.append(timesecs)
thedistances = np.array(thedistances)
thesecs = np.array(thesecs)
thevelos = thedistances/thesecs
theavpower = 2.8*(thevelos**3)
# create interactive plot
if len(thedistances) != 0:
res = interactive_cpchart(
r, thedistances, thesecs, theavpower,
theworkouts, promember=promember,
wcdurations=wcdurations, wcpower=wcpower
)
script = res[0]
div = res[1]
paulslope = res[2]
paulintercept = res[3]
p1 = res[4]
message = res[5]
else:
script = ''
div = '<p>No ranking pieces found.</p>'
paulslope = 1
paulintercept = 1
p1 = [1, 1, 1, 1]
message = ""
if request.method == 'POST' and "piece" in request.POST: # pragma: no cover
form = PredictedPieceForm(request.POST)
if form.is_valid():
value = form.cleaned_data['value']
hourvalue, value = divmod(value, 60)
if hourvalue >= 24:
hourvalue = 23
pieceunit = form.cleaned_data['pieceunit']
if pieceunit == 'd':
rankingdistances.append(value)
else:
rankingdurations.append(datetime.time(
minute=int(value), hour=int(hourvalue)))
else:
form = PredictedPieceForm()
rankingdistances.sort()
rankingdurations.sort()
predictions = []
cpredictions = []
for rankingdistance in rankingdistances:
# Paul's model
p = paulslope*np.log10(rankingdistance)+paulintercept
velo = 500./p
t = rankingdistance/velo
pwr = 2.8*(velo**3)
try:
pwr = int(pwr)
except (ValueError, AttributeError): # pragma: no cover
pwr = 0
a = {'distance': rankingdistance,
'duration': timedeltaconv(t),
'pace': timedeltaconv(p),
'power': int(pwr)}
predictions.append(a)
# CP model -
pwr2 = p1[0]/(1+t/p1[2])
pwr2 += p1[1]/(1+t/p1[3])
if pwr2 <= 0: # pragma: no cover
pwr2 = 50.
velo2 = (pwr2/2.8)**(1./3.)
if np.isnan(velo2) or velo2 <= 0: # pragma: no cover
velo2 = 1.0
t2 = rankingdistance/velo2
pwr3 = p1[0]/(1+t2/p1[2])
pwr3 += p1[1]/(1+t2/p1[3])
if pwr3 <= 0: # pragma: no cover
pwr3 = 50.
velo3 = (pwr3/2.8)**(1./3.)
if np.isnan(velo3) or velo3 <= 0: # pragma: no cover
velo3 = 1.0
t3 = rankingdistance/velo3
p3 = 500./velo3
a = {'distance': rankingdistance,
'duration': timedeltaconv(t3),
'pace': timedeltaconv(p3),
'power': int(pwr3)}
cpredictions.append(a)
for rankingduration in rankingdurations:
t = 3600.*rankingduration.hour
t += 60.*rankingduration.minute
t += rankingduration.second
t += rankingduration.microsecond/1.e6
# Paul's model
ratio = paulintercept/paulslope
u = ((2**(2+ratio))*(5.**(3+ratio))*t*np.log(10))/paulslope
d = 500*t*np.log(10.)
d = d/(paulslope*lambertw(u))
d = d.real
velo = d/t
p = 500./velo
pwr = 2.8*(velo**3)
try:
a = {'distance': int(d),
'duration': timedeltaconv(t),
'pace': timedeltaconv(p),
'power': int(pwr)}
predictions.append(a)
except: # pragma: no cover
pass
# CP model
pwr = p1[0] / (1 + t / p1[2])
pwr += p1[1] / (1 + t / p1[3])
if pwr <= 0: # pragma: no cover
pwr = 50.
velo = (pwr / 2.8)**(1. / 3.)
if np.isnan(velo) or velo <= 0: # pragma: no cover
velo = 1.0
d = t * velo
p = 500. / velo
a = {'distance': int(d),
'duration': timedeltaconv(t),
'pace': timedeltaconv(p),
'power': int(pwr)}
cpredictions.append(a)
if recalc:
wcdurations = []
wcpower = []
durations = [1, 4, 30, 60]
distances = [100, 500, 1000, 2000, 5000, 6000, 10000, 21097, 42195]
df = pd.DataFrame(
list(
C2WorldClassAgePerformance.objects.filter(
sex=r.sex,
weightcategory=r.weightcategory
).values()
)
)
jsondf = df.to_json()
messages.error(request, message)
return render(request, 'rankings.html',
{'rankingworkouts': theworkouts,
'interactiveplot': script,
'the_div': div,
'predictions': predictions,
'cpredictions': cpredictions,
'nrdata': len(thedistances),
'form': form,
'dateform': dateform,
'deltaform': deltaform,
'id': theuser,
'theuser': uu,
'rower': r,
'active': 'nav-analysis',
'age': age,
'sex': r.sex,
'recalc': recalc,
'weightcategory': r.weightcategory,
'startdate': startdate,
'enddate': enddate,
'teams': get_my_teams(request.user),
})
@login_required()
def otecp_toadmin_view(request, theuser=0,
startdate=timezone.now() - datetime.timedelta(days=365),
enddate=timezone.now(),
startdatestring="",
enddatestring="",
): # pragma: no cover
if startdatestring != "": # pragma: no cover
try:
startdate = iso8601.parse_date(startdatestring)
except ParseError:
pass
if enddatestring != "": # pragma: no cover
try:
enddate = iso8601.parse_date(enddatestring)
except ParseError:
pass
if theuser == 0: # pragma: no cover
theuser = request.user.id
u = User.objects.get(id=theuser)
r = Rower.objects.get(user=u)
startdate = datetime.datetime.combine(startdate, datetime.time())
enddate = datetime.datetime.combine(enddate, datetime.time(23, 59, 59))
theworkouts = Workout.objects.filter(
user=r, rankingpiece=True,
workouttype__in=[
'rower',
'dynamic',
'slides'
],
startdatetime__gte=startdate,
startdatetime__lte=enddate
).order_by("-startdatetime")
delta, cpvalue, avgpower = dataprep.fetchcp(
r, theworkouts, table='cpergdata'
)
powerdf = pd.DataFrame({
'Delta': delta,
'CP': cpvalue,
})
csvfilename = 'CP_data_user_{id}.csv'.format(
id=theuser
)
powerdf = powerdf[powerdf['CP'] > 0]
powerdf.dropna(axis=0, inplace=True)
powerdf.sort_values(['Delta', 'CP'], ascending=[1, 0], inplace=True)
powerdf.drop_duplicates(subset='Delta', keep='first', inplace=True)
powerdf.to_csv(csvfilename)
_ = myqueue(queuehigh,
handle_sendemailfile,
'Sander',
'Roosendaal',
'roosendaalsander@gmail.com',
csvfilename,
delete=True)
successmessage = "The CSV file was sent to the site admin per email"
messages.info(request, successmessage)
response = HttpResponseRedirect('/rowers/list-workouts/')
return response
@login_required()
def otwcp_toadmin_view(request, theuser=0,
startdate=timezone.now() - datetime.timedelta(days=365),
enddate=timezone.now(),
startdatestring="",
enddatestring="",
): # pragma: no cover
if startdatestring != "":
try:
startdate = iso8601.parse_date(startdatestring)
except ParseError:
pass
if enddatestring != "":
try:
enddate = iso8601.parse_date(enddatestring)
except ParseError:
pass
if theuser == 0:
theuser = request.user.id
u = User.objects.get(id=theuser)
r = Rower.objects.get(user=u)
startdate = datetime.datetime.combine(startdate, datetime.time())
enddate = datetime.datetime.combine(enddate, datetime.time(23, 59, 59))
theworkouts = Workout.objects.filter(
user=r, rankingpiece=True,
workouttype='water',
startdatetime__gte=startdate,
startdatetime__lte=enddate
).order_by("-startdatetime")
delta, cpvalue, avgpower = dataprep.fetchcp(
r, theworkouts, table='cpdata'
)
powerdf = pd.DataFrame({
'Delta': delta,
'CP': cpvalue,
})
csvfilename = 'CP_data_user_{id}.csv'.format(
id=theuser
)
powerdf = powerdf[powerdf['CP'] > 0]
powerdf.dropna(axis=0, inplace=True)
powerdf.sort_values(['Delta', 'CP'], ascending=[1, 0], inplace=True)
powerdf.drop_duplicates(subset='Delta', keep='first', inplace=True)
powerdf.to_csv(csvfilename)
_ = myqueue(queuehigh,
handle_sendemailfile,
'Sander',
'Roosendaal',
'roosendaalsander@gmail.com',
csvfilename,
delete=True)
successmessage = "The CSV file was sent to the site admin per email"
messages.info(request, successmessage)
response = HttpResponseRedirect('/rowers/list-workouts/')
return response
def agegroupcpview(request, age, normalize=0, userid=0):
script, div = interactive_agegroupcpchart(age, normalized=normalize)
response = render(request, 'agegroupcp.html',
{
'active': 'nav-analysis',
'interactiveplot': script,
'the_div': div,
}
)
return response
def agegrouprecordview(request, sex='male', weightcategory='hwt',
distance=2000, duration=None):
@@ -2761,15 +2220,16 @@ def history_view_data(request, userid=0):
ids = [w.id for w in g_workouts]
columns = ['hr', 'power', 'time']
df = getsmallrowdata_db(columns, ids=ids)
columns = ['hr', 'power', 'time', 'workoutstate', 'workoutid']
df = dataprep.read_data(columns, ids=ids)
df = dataprep.remove_nulls_pl(df)
try:
df['deltat'] = df['time'].diff().clip(lower=0)
df = df.with_columns(pl.col('time').diff().clip(lower_bound=0).alias("deltat"))
except KeyError: # pragma: no cover
pass
df = dataprep.clean_df_stats(df, workstrokesonly=True,
ignoreadvanced=True, ignorehr=False)
totalmeters, totalhours, totalminutes, totalseconds = get_totals(
g_workouts)
@@ -2798,13 +2258,14 @@ def history_view_data(request, userid=0):
whours=whours,
wminutes=wminutes, wseconds=wseconds,
)
ddf = getsmallrowdata_db(columns, ids=[w.id for w in a_workouts])
ddf = dataprep.read_data(columns, ids=[w.id for w in a_workouts])
ddf = dataprep.remove_nulls_pl(ddf)
try:
ddf['deltat'] = ddf['time'].diff().clip(lower=0)
ddf = ddf.with_columns(pl.col("time").diff().clip(lower_bound=0).alias("deltat"))
except KeyError: # pragma: no cover
pass
ddf = dataprep.clean_df_stats(ddf, workstrokesonly=False,
ddf = dataprep.clean_df_stats_pl(ddf, workstrokesonly=False,
ignoreadvanced=True)
try:
@@ -2812,14 +2273,14 @@ def history_view_data(request, userid=0):
except (KeyError, ValueError, AttributeError): # pragma: no cover
ddict['hrmean'] = 0
try:
ddict['hrmax'] = ddf['hr'].max().astype(int)
ddict['hrmax'] = int(ddf['hr'].max())
except (KeyError, ValueError, AttributeError): # pragma: no cover
ddict['hrmax'] = 0
ddict['powermean'] = int(wavg(ddf, 'power', 'deltat'))
try:
ddict['powermax'] = ddf['power'].max().astype(int)
except KeyError: # pragma: no cover
ddict['powermax'] = int(ddf['power'].max())
except (KeyError, ColumnNotFoundError): # pragma: no cover
ddict['powermax'] = 0
ddict['nrworkouts'] = a_workouts.count()
listofdicts.append(ddict)
@@ -2833,14 +2294,14 @@ def history_view_data(request, userid=0):
totalsdict['distance'] = totalmeters
try:
totalsdict['powermean'] = int(wavg(df, 'power', 'deltat'))
totalsdict['powermax'] = df['power'].max().astype(int)
except KeyError: # pragma: no cover
totalsdict['powermax'] = int(df['power'].max())
except (KeyError, ColumnNotFoundError): # pragma: no cover
totalsdict['powermean'] = 0
totalsdict['powermax'] = 0
try:
totalsdict['hrmean'] = int(wavg(df, 'hr', 'deltat'))
totalsdict['hrmax'] = df['hr'].max().astype(int)
except KeyError: # pragma: no cover
totalsdict['hrmax'] = int(df['hr'].max())
except (KeyError, ColumnNotFoundError): # pragma: no cover
totalsdict['hrmean'] = 0
totalsdict['hrmax'] = 0
@@ -2859,14 +2320,22 @@ def history_view_data(request, userid=0):
a_workouts = g_workouts.filter(workouttype=typeselect)
meters, hours, minutes, seconds = get_totals(a_workouts)
totalseconds = 3600 * hours + 60 * minutes + seconds
ddf = getsmallrowdata_db(columns, ids=[w.id for w in a_workouts])
ddf = dataprep.read_data(columns, ids=[w.id for w in a_workouts])
ddf = dataprep.remove_nulls_pl(ddf)
if ddf.is_empty():
totalscript = ""
totaldiv = "No data"
try:
ddf['deltat'] = ddf['time'].diff().clip(lower=0)
ddf = ddf.with_columns(pl.col("time").diff().clip(lower_bound=0).alias("deltat"))
except KeyError:
pass
except ColumnNotFoundError:
totalscript = ""
totaldiv = "No data"
ddf = dataprep.clean_df_stats(ddf, workstrokesonly=True,
ddf = dataprep.clean_df_stats_pl(ddf, workstrokesonly=True,
ignoreadvanced=True)
totalscript, totaldiv = interactive_hr_piechart(
+64 -61
View File
@@ -69,102 +69,102 @@ def strokedataform(request, id=0):
def api_get_dataframe(startdatetime, df):
try:
time = df['time']/1.e3
except KeyError: # pragma: no cover
except (KeyError, ColumnNotFoundError): # pragma: no cover
try:
time = df['t']/10.
except KeyError:
return 400, "Missing time", pd.DataFrame()
except (KeyError, ColumnNotFoundError):
return 400, "Missing time", pl.DataFrame()
try:
spm = df['spm']
except KeyError: # pragma: no cover
return 400, "Missing spm", pd.DataFrame()
except (KeyError, ColumnNotFoundError): # pragma: no cover
return 400, "Missing spm", pl.DataFrame()
try:
distance = df['distance']
except KeyError: # pragma: no cover
except (KeyError, ColumnNotFoundError): # pragma: no cover
try:
distance = df['d']/10.
except KeyError:
return 400, "Missing distance", pd.DataFrame()
except (KeyError, ColumnNotFoundError):
return 400, "Missing distance", pl.DataFrame()
try:
pace = df['pace']/1.e3
except KeyError: # pragma: no cover
except (KeyError, ColumnNotFoundError): # pragma: no cover
try:
pace = df['p']/10.
except KeyError:
return 400, "Missing pace", pd.DataFrame()
except (KeyError, ColumnNotFoundError):
return 400, "Missing pace", pl.DataFrame()
try:
power = df['power']
except KeyError: # pragma: no cover
except (KeyError, ColumnNotFoundError): # pragma: no cover
power = 0*time
try:
drivelength = df['drivelength']
except KeyError:
except (KeyError, ColumnNotFoundError):
drivelength = 0*time
try:
dragfactor = df['dragfactor']
except KeyError:
except (KeyError, ColumnNotFoundError):
dragfactor = 0*time
try:
drivetime = df['drivetime']
except KeyError:
except (KeyError, ColumnNotFoundError):
drivetime = 0*time
try:
strokerecoverytime = df['strokerecoverytime']
except KeyError:
except (KeyError, ColumnNotFoundError):
strokerecoverytime = 0*time
try:
averagedriveforce = df['averagedriveforce']
except KeyError:
except (KeyError, ColumnNotFoundError):
averagedriveforce = 0*time
try:
peakdriveforce = df['peakdriveforce']
except KeyError:
except (KeyError, ColumnNotFoundError):
peakdriveforce = 0*time
try:
wash = df['wash']
except KeyError:
except (KeyError, ColumnNotFoundError):
wash = 0*time
try:
catch = df['catch']
except KeyError:
except (KeyError, ColumnNotFoundError):
catch = 0*time
try:
finish = df['finish']
except KeyError:
except (KeyError, ColumnNotFoundError):
finish = 0*time
try:
peakforceangle = df['peakforceangle']
except KeyError:
except (KeyError, ColumnNotFoundError):
peakforceangle = 0*time
try:
driveenergy = df['driveenergy']
except KeyError:
except (KeyError, ColumnNotFoundError):
driveenergy = 60.*power/spm
try:
slip = df['slip']
except KeyError:
except (KeyError, ColumnNotFoundError):
slip = 0*time
try:
lapidx = df['lapidx']
except KeyError:
except (KeyError, ColumnNotFoundError):
lapidx = 0*time
try:
hr = df['hr']
except KeyError: # pragma: no cover
except (KeyError, ColumnNotFoundError): # pragma: no cover
hr = 0*df['time']
try:
latitude = df['latitude']
except KeyError:
except (KeyError, ColumnNotFoundError):
latitude = 0*df['time']
try:
longitude = df['longitude']
except KeyError:
except (KeyError, ColumnNotFoundError):
longitude = 0*df['time']
starttime = totimestamp(startdatetime)+time[0]
@@ -172,7 +172,7 @@ def api_get_dataframe(startdatetime, df):
dologging('apilog.log',"(strokedatajson_v2/3 POST - data parsed)")
data = pd.DataFrame({'TimeStamp (sec)': unixtime,
data = pl.DataFrame({'TimeStamp (sec)': unixtime,
' Horizontal (meters)': distance,
' Cadence (stokes/min)': spm,
' HRCur (bpm)': hr,
@@ -526,7 +526,7 @@ def strokedatajson_v3(request):
title = request.data.get('name','')
try:
elapsedTime = request.data['elapsedTime']
except KeyError: # pragma: no cover
except (KeyError, ColumnNotFoundError): # pragma: no cover
try:
duration = request.data['duration']
try:
@@ -538,7 +538,7 @@ def strokedatajson_v3(request):
return HttpResponse("Missing Elapsed Time", status=400)
try:
totalDistance = request.data['distance']
except KeyError: # pragma: no cover
except (KeyError, ColumnNotFoundError): # pragma: no cover
return HttpResponse("Missing Total Distance", status=400)
timeZone = request.data.get('timezone','UTC')
workouttype = request.data.get('workouttype','rower')
@@ -556,23 +556,21 @@ def strokedatajson_v3(request):
dologging('apilog.log',totalDistance)
dologging('apilog.log',elapsedTime)
df = pd.DataFrame()
df = pl.DataFrame()
try:
strokes = request.data['strokes']
except KeyError: # pragma: no cover
except (KeyError, ColumnNotFoundError): # pragma: no cover
return HttpResponse("No Stroke Data in JSON", status=400)
try:
df = pd.DataFrame(strokes['data'])
except KeyError: # pragma: no cover
df = pl.DataFrame(strokes['data'])
except (KeyError, ColumnNotFoundError): # pragma: no cover
try:
df = pd.DataFrame(request.data['strokedata'])
df = pl.DataFrame(request.data['strokedata'])
except:
return HttpResponse("No JSON Object could be decoded", status=400)
df.index = df.index.astype(int)
df.sort_index(inplace=True)
df = df.sort("time")
status, comment, data = api_get_dataframe(startdatetime, df)
if status != 200: # pragma: no cover
@@ -580,7 +578,8 @@ def strokedatajson_v3(request):
csvfilename = 'media/{code}.csv.gz'.format(code=uuid4().hex[:16])
_ = data.to_csv(csvfilename, index_label='index', compression='gzip')
with gzip.open(csvfilename, 'w') as f:
_ = data.write_csv(f)
duration = datetime.time(0,0,1)
w = Workout(
@@ -672,10 +671,11 @@ def strokedatajson_v2(request, id):
if request.method == 'GET':
columns = ['spm', 'time', 'hr', 'pace', 'power', 'distance']
datadf = dataprep.getsmallrowdata_db(columns, ids=[id])
datadf = dataprep.read_data(columns, ids=[id])
datadf = dataprep.remove_nulls_pl(datadf)
dologging('apilog.log',request.user.username+"(strokedatajson_v2 GET)")
data = datadf.to_json(orient='records')
data = datadf.write_json(row_oriented=True)
data2 = json.loads(data)
data2 = {"data": data2}
@@ -686,28 +686,27 @@ def strokedatajson_v2(request, id):
try:
for d in request.data['data']:
dologging('apilog.log',json.dumps(d))
except KeyError: # pragma: no cover
except (KeyError, ColumnNotFoundError): # pragma: no cover
try:
for d in request.data['strokedata']:
dologging('apilog.log',json.dumps(d))
except KeyError:
except (KeyError, ColumnNotFoundError):
dologging('apilog.log','No data in request.data')
checkdata, r = dataprep.getrowdata_db(id=row.id)
if not checkdata.empty: # pragma: no cover
return HttpResponse("Duplicate Error", status=409)
df = pd.DataFrame()
df = pl.DataFrame()
try:
df = pd.DataFrame(request.data['data'])
except KeyError: # pragma: no cover
df = pl.DataFrame(request.data['data'])
except (KeyError, ColumnNotFoundError): # pragma: no cover
try:
df = pd.DataFrame(request.data['strokedata'])
df = pl.DataFrame(request.data['strokedata'])
except:
return HttpResponse("No JSON object could be decoded", status=400)
df.index = df.index.astype(int)
df.sort_index(inplace=True)
df = df.sort("time")
status, comment, data = api_get_dataframe(row.startdatetime, df)
if status != 200: # pragma: no cover
@@ -729,7 +728,8 @@ def strokedatajson_v2(request, id):
row.duplicate = True
row.save()
_ = data.to_csv(csvfilename+'.gz', index_label='index', compression='gzip')
with gzip.open(csvfilename+'.gz', 'w') as f:
_ = data.write_csv(f)
row.csvfilename = csvfilename
row.save()
@@ -748,7 +748,7 @@ def strokedatajson_v2(request, id):
powerperc=powerperc, powerzones=r.powerzones)
rowdata = rdata(csvfile=row.csvfilename, rower=rr).df
datadf = dataprep.dataprep(
datadf = dataprep.dataplep(
rowdata, id=row.id, bands=True, barchart=True, otwpower=True, empower=True)
_ = myqueue(queuehigh, handle_calctrimp, row.id,
@@ -802,7 +802,11 @@ def strokedatajson(request, id=0):
if request.method == 'GET':
# currently only returns a subset.
columns = ['spm', 'time', 'hr', 'pace', 'power', 'distance']
datadf = dataprep.getsmallrowdata_db(columns, ids=[id])
datadf = dataprep.read_data(columns, ids=[id])
datadf = dataprep.remove_nulls_pl(datadf)
datadf = datadf.to_pandas()
dologging("apilog.log",request.user.username+"(strokedatajson GET) ")
return JSONResponse(datadf)
@@ -823,14 +827,13 @@ def strokedatajson(request, id=0):
return HttpResponse("No JSON object could be decoded", status=400)
try:
df = pd.DataFrame(strokedata)
df = pl.DataFrame(strokedata)
except ValueError: # pragma: no cover
return HttpResponse("Arrays must all be same length", status=400)
df.index = df.index.astype(int)
df.sort_index(inplace=True)
df = df.sort("time")
try:
time = df['time']/1.e3
except KeyError: # pragma: no cover
except (KeyError, ColumnNotFoundError): # pragma: no cover
return HttpResponse("There must be time values", status=400)
aantal = len(time)
pace = df['pace']/1.e3
@@ -869,7 +872,7 @@ def strokedatajson(request, id=0):
dologging("apilog.log",request.user.username+"(POST)")
data = pd.DataFrame({'TimeStamp (sec)': unixtime,
data = pl.DataFrame({'TimeStamp (sec)': unixtime,
' Horizontal (meters)': distance,
' Cadence (stokes/min)': spm,
' HRCur (bpm)': hr,
@@ -896,8 +899,8 @@ def strokedatajson(request, id=0):
timestr = row.startdatetime.strftime("%Y%m%d-%H%M%S")
csvfilename = 'media/Import_'+timestr+'.csv'
res = data.to_csv(csvfilename+'.gz', index_label='index',
compression='gzip')
with gzip.open(csvfilename+'.gz','w') as f:
res = data.write_csv(f)
row.csvfilename = csvfilename
row.save()
@@ -916,7 +919,7 @@ def strokedatajson(request, id=0):
powerperc=powerperc, powerzones=r.powerzones)
rowdata = rdata(csvfile=row.csvfilename, rower=rr).df
datadf = dataprep.dataprep(
datadf = dataprep.dataplep(
rowdata, id=row.id, bands=True, barchart=True, otwpower=True, empower=True)
# mangling
+1 -1
View File
@@ -208,7 +208,7 @@ def workouts_summaries_email_view(request):
)
df = dataprep.workout_summary_to_df(
r, startdate=startdate, enddate=enddate)
df.to_csv(filename, encoding='utf-8')
df.write_csv(filename)
_ = myqueue(queuehigh, handle_sendemailsummary,
r.user.first_name,
r.user.last_name,
+23
View File
@@ -1,10 +1,33 @@
from rowers.views.statements import *
from rowers.interactiveplots import sleep
from rq import Queue
from redis import Redis
from rq.job import Job
@login_required()
def sleep_view(request):
script, div = sleep()
return render(request,
"sleep.html",
{
"the_div": div,
"the_script": script,
})
def filmdeaths_view(request):
script, div = filmdeaths()
return render(request,
"filmdeaths.html",
{
"the_div": div,
"the_script": script,
}
)
@login_required()
def download_fit(request, filename=''):
+1 -1
View File
@@ -3569,7 +3569,7 @@ def rower_trainingplan_execution_view(request,
else: # pragma: no cover
data, message = get_execution_report(r, startdate, enddate)
if not data.empty:
if not data.is_empty():
script, div = interactive_planchart(data, startdate, enddate)
else: # pragma: no cover
script = ''
+8 -6
View File
@@ -16,7 +16,7 @@ from rowers.utils import (
from rowers.celery import result as celery_result
from rowers.interactiveplots import *
from scipy.interpolate import griddata
from rowers.dataprep import getsmallrowdata_db
from rowers.dataprep import getsmallrowdata_pd, read_data
from rowers.dataprep import timedeltaconv
from scipy.special import lambertw
from io import BytesIO
@@ -35,6 +35,7 @@ import threading
import redis
import colorsys
import re
import gzip
import zipfile
import bleach
import arrow
@@ -136,9 +137,9 @@ from rowers.forms import (
FitnessMetricForm, PredictedPieceFormNoDistance,
EmailForm, RegistrationForm, RegistrationFormTermsOfService,
RegistrationFormUniqueEmail, RegistrationFormSex,
CNsummaryForm, UpdateWindForm,
CNsummaryForm,
StandardsForm,
UpdateStreamForm, WorkoutMultipleCompareForm, ChartParamChoiceForm,
WorkoutMultipleCompareForm, ChartParamChoiceForm,
FusionMetricChoiceForm, BoxPlotChoiceForm, MultiFlexChoiceForm,
TrendFlexModalForm, WorkoutSplitForm, WorkoutJoinParamForm,
AnalysisOptionsForm, AnalysisChoiceForm,
@@ -205,6 +206,7 @@ from rowers.rojabo_stuff import rojabo_open
from rowers.integrations import *
from polars.exceptions import ColumnNotFoundError
import rowers.ownapistuff as ownapistuff
from rowers.ownapistuff import TEST_CLIENT_ID, TEST_CLIENT_SECRET, TEST_REDIRECT_URI
@@ -251,7 +253,7 @@ from rowers.tasks import (
handle_sendemailfile,
handle_sendemailkml,
handle_sendemailnewresponse, handle_updatedps,
handle_updatecp, long_test_task, long_test_task2,
long_test_task, long_test_task2,
handle_zip_file, handle_getagegrouprecords,
handle_update_empower,
handle_sendemailics,
@@ -1344,9 +1346,9 @@ def trydf(df, aantal, column): # pragma: no cover
s = df[column]
if len(s) != aantal:
return np.zeros(aantal)
if not np.issubdtype(s, np.number):
if not s.dtype in pl.NUMERIC_DTYPES:
return np.zeros(aantal)
except KeyError:
except (KeyError, ColumnNotFoundError):
s = np.zeros(aantal)
return s
+40 -496
View File
@@ -126,8 +126,7 @@ def workout_video_view_mini(request, id=''):
hascoordinates = pd.Series(data['latitude']).std() > 0
# create map
if hascoordinates and mode == 'water':
mapscript, mapdiv = leaflet_chart_video(data['latitude'], data['longitude'],
w.name)
mapscript, mapdiv = leaflet_chart(data['latitude'], data['longitude'])
else:
mapscript, mapdiv = interactive_chart_video(data)
data['longitude'] = data['spm']
@@ -237,8 +236,7 @@ def workout_video_view(request, id=''):
hascoordinates = pd.Series(data['latitude']).std() > 0
# create map
if hascoordinates and mode == 'water':
mapscript, mapdiv = leaflet_chart_video(data['latitude'], data['longitude'],
w.name)
mapscript, mapdiv = leaflet_chart(data['latitude'], data['longitude'])
else:
mapscript, mapdiv = interactive_chart_video(data)
data['longitude'] = data['spm']
@@ -349,12 +347,12 @@ def workout_video_create_view(request, id=0):
# get data
data, metrics, maxtime = dataprep.get_video_data(
w, groups=metricsgroups, mode=mode)
hascoordinates = pd.Series(data['latitude']).std() > 0
# create map
if hascoordinates and mode == 'water':
mapscript, mapdiv = leaflet_chart_video(data['latitude'], data['longitude'],
w.name)
mapscript, mapdiv = leaflet_chart(data['latitude'], data['longitude'])
else:
mapscript, mapdiv = interactive_chart_video(data)
data['longitude'] = data['spm']
@@ -380,6 +378,7 @@ def workout_video_create_view(request, id=0):
template = 'embedded_video.html'
return render(request,
template,
{
@@ -429,12 +428,14 @@ def workout_forcecurve_view(request, id=0, analysis=0, userid=0, workstrokesonly
work_max = forceanalysis.work_max
notes = forceanalysis.notes
name = forceanalysis.name
plotcircles = forceanalysis.plotcircles
plotlines = forceanalysis.plotlines
includereststrokes = forceanalysis.include_rest_strokes
except (ForceCurveAnalysis.DoesNotExist, ValueError):
pass
else:
dist_min = 0
dist_max = 0
dist_max = row.distance
spm_min = 15
spm_max = 55
work_min = 0
@@ -442,6 +443,8 @@ def workout_forcecurve_view(request, id=0, analysis=0, userid=0, workstrokesonly
notes = ''
includereststrokes = False
name = ''
plotcircles = True
plotlines = False
form = ForceCurveOptionsForm(initial={
'spm_min': spm_min,
@@ -451,10 +454,10 @@ def workout_forcecurve_view(request, id=0, analysis=0, userid=0, workstrokesonly
'work_min': work_min,
'work_max': work_max,
'notes': notes,
'plottype': 'line',
'name': name,
'plotcircles': plotcircles,
'plotlines': plotlines,
})
plottype = 'line'
if request.method == 'POST':
@@ -468,10 +471,11 @@ def workout_forcecurve_view(request, id=0, analysis=0, userid=0, workstrokesonly
work_max = form.cleaned_data['work_max']
notes = form.cleaned_data['notes']
name = form.cleaned_data['name']
plotlines = form.cleaned_data['plotlines']
plotcircles = form.cleaned_data['plotcircles']
if not name:
name = row.name
includereststrokes = form.cleaned_data['includereststrokes']
plottype = form.cleaned_data['plottype']
workstrokesonly = not includereststrokes
if "_save" in request.POST and "new" not in request.POST: # pragma: no cover
@@ -488,6 +492,8 @@ def workout_forcecurve_view(request, id=0, analysis=0, userid=0, workstrokesonly
spm_min = spm_min,
spm_max = spm_max,
rower=row.user,
plotlines = plotlines,
plotcircles = plotcircles,
include_rest_strokes = includereststrokes,
)
else:
@@ -501,6 +507,8 @@ def workout_forcecurve_view(request, id=0, analysis=0, userid=0, workstrokesonly
forceanalysis.work_max = work_max
forceanalysis.spm_min = spm_min
forceanalysis.spm_max = spm_max
forceanalysis.plotcircles = plotcircles
forceanalysis.plotlines = plotlines
forceanalysis.include_rest_strokes = includereststrokes
forceanalysis.save()
dosave = True
@@ -526,20 +534,10 @@ def workout_forcecurve_view(request, id=0, analysis=0, userid=0, workstrokesonly
else: # pragma: no cover
workstrokesonly = True
plottype = 'line'
script, div, js_resources, css_resources = interactive_forcecurve(
[row],
workstrokesonly=workstrokesonly,
plottype=plottype,
dist_min = dist_min,
dist_max = dist_max,
spm_min = spm_min,
spm_max = spm_max,
work_min = work_min,
work_max = work_max,
notes=notes,
script, div = interactive_forcecurve(
[row]
)
breadcrumbs = [
@@ -580,8 +578,6 @@ def workout_forcecurve_view(request, id=0, analysis=0, userid=0, workstrokesonly
'work_max': work_max,
'annotation': notes,
'the_div': div,
'js_res': js_resources,
'css_res': css_resources,
'id': id,
'mayedit': mayedit,
'teams': get_my_teams(request.user),
@@ -633,50 +629,6 @@ def otw_use_gps(request, id=0):
return HttpResponseRedirect(url)
# Show Stroke power histogram for a workout
@login_required()
@permission_required('workout.change_workout', fn=get_workout_by_opaqueid, raise_exception=True)
def workout_histo_view(request, id=0):
w = get_workoutuser(id, request)
r = getrequestrower(request)
mayedit = 0
if w.user == r:
mayedit = 1
res = interactive_histoall([w], 'power', False)
script = res[0]
div = res[1]
breadcrumbs = [
{
'url': '/rowers/list-workouts/',
'name': 'Workouts'
},
{
'url': get_workout_default_page(request, id),
'name': w.name
},
{
'url': reverse('workout_histo_view', kwargs={'id': id}),
'name': 'Histogram'
}
]
return render(request,
'histo_single.html',
{'interactiveplot': script,
'breadcrumbs': breadcrumbs,
'active': 'nav-workouts',
'workout': w,
'rower': r,
'the_div': div,
'id': id,
'mayedit': mayedit,
'teams': get_my_teams(request.user),
})
# add a workout manually
@login_required()
@@ -1343,7 +1295,7 @@ def remove_power_view(request, id=0):
row = rdata(csvfile=f, rower=rr)
row.df[' Power (watts)'] = 0
row.write_csv(f)
_ = dataprep.dataprep(row.df, id=workout.id)
_ = dataprep.dataplep(row.df, id=workout.id)
cpdf, delta, cpvalues = dataprep.setcp(workout)
workout.normp = 0
@@ -1667,15 +1619,10 @@ def course_compare_view(request, id=0):
except: # pragma: no cover
labeldict = {}
res = interactive_multiple_compare_chart(workoutids, xparam, yparam,
script, div = interactive_multiple_compare_chart(workoutids, xparam, yparam,
promember=promember,
plottype=plottype,
labeldict=labeldict, startenddict=startenddict)
script = res[0]
div = res[1]
errormessage = res[3]
if errormessage != '': # pragma: no cover
messages.error(request, errormessage)
breadcrumbs = [
{
@@ -1849,15 +1796,10 @@ def virtualevent_compare_view(request, id=0):
except: # pragma: no cover
labeldict = {}
res = interactive_multiple_compare_chart(workoutids, xparam, yparam,
script, div = interactive_multiple_compare_chart(workoutids, xparam, yparam,
promember=promember,
plottype=plottype,
labeldict=labeldict, startenddict=startenddict)
script = res[0]
div = res[1]
errormessage = res[3]
if errormessage != '': # pragma: no cover
messages.error(request, errormessage)
breadcrumbs = [
{
@@ -2237,10 +2179,6 @@ def workouts_view(request, message='', successmessage='',
for w in workoutsnohr: # pragma: no cover
_ = dataprep.workout_trimp(w)
# ids = [w.id for w in workouts]
# df = dataprep.getsmallrowdata_db(['time','power'],ids=ids)
# polarization = dataprep.polarization_index(df,r)
if query: # pragma: no cover
query_list = query.split()
workouts = workouts.filter(
@@ -2278,7 +2216,7 @@ def workouts_view(request, message='', successmessage='',
if yaxis not in ['duration', 'trimp', 'rscore']: # pragma: no cover
yaxis = 'duration'
script, div = interactive_activitychart(g_workouts,
script, div = interactive_activitychart2(g_workouts,
g_startdate,
g_enddate,
stack=stack,
@@ -2823,348 +2761,6 @@ def workout_downloadmetar_view(request, id=0,
return response
# Show form to update wind data
@permission_required('workout.change_workout', fn=get_workout_by_opaqueid, raise_exception=True)
@user_passes_test(ispromember,
login_url="/rowers/paidplans",
message="This functionality requires a Pro plan or higher."
" If you are already a Pro user, please log in to access this functionality",
redirect_field_name=None)
def workout_wind_view(request, id=0, message="", successmessage=""):
row = get_workoutuser(id, request)
r = getrower(request.user)
breadcrumbs = [
{
'url': '/rowers/list-workouts/',
'name': 'Workouts'
},
{
'url': get_workout_default_page(request, id),
'name': row.name
},
{
'url': reverse('workout_wind_view', kwargs={'id': id}),
'name': 'Wind'
}
]
# get data
f1 = row.csvfilename
u = row.user.user
r = getrower(u)
# create bearing
rowdata = rdata(csvfile=f1)
if row == 0: # pragma: no cover
return HttpResponse("Error: CSV Data File Not Found")
hascoordinates = 1
try:
latitude = rowdata.df.loc[:, ' latitude']
except KeyError:
hascoordinates = 0
if hascoordinates and not latitude.std(): # pragma: no cover
hascoordinates = 0
try:
_ = rowdata.df.loc[:, 'bearing'].values
except KeyError:
rowdata.add_bearing()
rowdata.write_csv(f1, gzip=True)
if hascoordinates:
avglat = rowdata.df[' latitude'].mean()
avglon = rowdata.df[' longitude'].mean()
airportcode, newlat, newlon, airportdistance = get_airport_code(
avglat, avglon)
airportcode = airportcode.upper()
airportdistance = airportdistance[0]
else:
airportcode = 'UNKNOWN'
airportdistance = 0
if request.method == 'POST':
# process form
form = UpdateWindForm(request.POST)
if form.is_valid():
vwind1 = form.cleaned_data['vwind1']
vwind2 = form.cleaned_data['vwind2']
dist1 = form.cleaned_data['dist1']
dist2 = form.cleaned_data['dist2']
winddirection1 = form.cleaned_data['winddirection1']
winddirection2 = form.cleaned_data['winddirection2']
windunit = form.cleaned_data['windunit']
rowdata.update_wind(vwind1, vwind2,
winddirection1,
winddirection2,
dist1, dist2,
units=windunit)
rowdata.write_csv(f1, gzip=True)
else: # pragma: no cover
message = "Invalid Form"
messages.error(request, message)
kwargs = {
'id': id
}
url = reverse('workout_wind_view', kwargs=kwargs)
_ = HttpResponseRedirect(url)
else:
form = UpdateWindForm()
# create interactive plot
res = interactive_windchart(encoder.decode_hex(id), promember=1)
script = res[0]
div = res[1]
if hascoordinates:
gmscript, gmdiv = leaflet_chart(
rowdata.df[' latitude'],
rowdata.df[' longitude'],
row.name)
else:
gmscript = ""
gmdiv = "No GPS data available"
messages.info(request, successmessage)
messages.error(request, message)
return render(request,
'windedit.html',
{'workout': row,
'rower': r,
'breadcrumbs': breadcrumbs,
'active': 'nav-workouts',
'teams': get_my_teams(request.user),
'interactiveplot': script,
'form': form,
'airport': airportcode,
'airportdistance': airportdistance,
'the_div': div,
'gmap': gmscript,
'gmapdiv': gmdiv})
# Show form to update River stream data (for river dwellers)
@permission_required('workout.change_workout', fn=get_workout_by_opaqueid, raise_exception=True)
@user_passes_test(ispromember,
login_url="/rowers/paidplans",
message="This functionality requires a Pro plan or higher."
" If you are already a Pro user, please log in to access this functionality",
redirect_field_name=None)
def workout_stream_view(request, id=0, message="", successmessage=""):
row = get_workoutuser(id, request)
r = getrower(request.user)
# create interactive plot
f1 = row.csvfilename
u = row.user.user
r = getrower(u)
rowdata = rdata(csvfile=f1)
if rowdata == 0: # pragma: no cover
messages.info(request, "Error: CSV data file not found")
url = reverse('workout_edit_view', kwargs={
'id': encoder.encode_hex(row.id)})
return HttpResponseRedirect(url)
if request.method == 'POST':
# process form
form = UpdateStreamForm(request.POST)
if form.is_valid():
dist1 = form.cleaned_data['dist1']
dist2 = form.cleaned_data['dist2']
stream1 = form.cleaned_data['stream1']
stream2 = form.cleaned_data['stream2']
streamunit = form.cleaned_data['streamunit']
rowdata.update_stream(stream1, stream2, dist1, dist2,
units=streamunit)
rowdata.write_csv(f1, gzip=True)
else: # pragma: no cover
message = "Invalid Form"
messages.error(request, message)
kwargs = {
'id': id}
url = reverse('workout_wind_view', kwargs=kwargs)
_ = HttpResponseRedirect(url)
else:
form = UpdateStreamForm()
# create interactive plot
res = interactive_streamchart(encoder.decode_hex(id), promember=1)
script = res[0]
div = res[1]
breadcrumbs = [
{
'url': '/rowers/list-workouts/',
'name': 'Workouts'
},
{
'url': get_workout_default_page(request, id),
'name': row.name
},
{
'url': reverse('workout_stream_view', kwargs={'id': id}),
'name': 'Stream'
}
]
messages.info(request, successmessage)
messages.error(request, message)
return render(request,
'streamedit.html',
{'workout': row,
'rower': r,
'breadcrumbs': breadcrumbs,
'active': 'nav-workouts',
'teams': get_my_teams(request.user),
'interactiveplot': script,
'form': form,
'the_div': div})
# Form to set average crew weight and boat type, then run power calcs
@permission_required('workout.change_workout', fn=get_workout_by_opaqueid, raise_exception=True)
@user_passes_test(ispromember, login_url="/rowers/paidplans", redirect_field_name=None)
def workout_otwsetpower_view(request, id=0, message="", successmessage=""):
w = get_workoutuser(id, request)
r = getrower(request.user)
mayedit = 1
if request.method == 'POST':
# process form
form = AdvancedWorkoutForm(request.POST)
if form.is_valid():
boattype = form.cleaned_data['boattype']
weightvalue = form.cleaned_data['weightvalue']
coastalbrand = form.cleaned_data['boatbrand']
boatclass = w.workouttype
w.boattype = boattype
w.weightvalue = weightvalue
w.boatbrand = coastalbrand
w.save()
# load row data & create power/wind/bearing columns if not set
f1 = w.csvfilename
rowdata = rdata(csvfile=f1)
if rowdata == 0: # pragma: no cover
return HttpResponse("Error: CSV Data File Not Found")
try:
_ = rowdata.df['vstream']
except KeyError:
rowdata.add_stream(0)
rowdata.write_csv(f1, gzip=True)
try:
_ = rowdata.df['bearing']
except KeyError:
rowdata.add_bearing()
rowdata.write_csv(f1, gzip=True)
try:
_ = rowdata.df['vwind']
except KeyError:
rowdata.add_wind(0, 0)
rowdata.write_csv(f1, gzip=True)
# do power calculation (asynchronous)
r = w.user
u = r.user
first_name = u.first_name
last_name = u.last_name
emailaddress = u.email
job = myqueue(queue,
handle_otwsetpower, f1, boattype, boatclass, coastalbrand,
weightvalue,
first_name, last_name, emailaddress, encoder.decode_hex(
id),
ps=[r.p0, r.p1, r.p2, r.p3],
ratio=r.cpratio,
# quick_calc = quick_calc,
# go_service=go_service,
emailbounced=r.emailbounced
)
try:
request.session['async_tasks'] += [(job.id, 'otwsetpower')]
except KeyError:
request.session['async_tasks'] = [(job.id, 'otwsetpower')]
successmessage = 'Your calculations have been submitted." \
" You will receive an email when they are done." \
" You can check the status of your calculations" \
" <a href="/rowers/jobs-status/" target="_blank">here</a>'
messages.info(request, successmessage)
kwargs = {
'id': id}
try:
url = request.session['referer']
except KeyError:
url = reverse('workout_edit_view', kwargs=kwargs)
response = HttpResponseRedirect(url)
return response
else: # pragma: no cover
message = "Invalid Form"
messages.error(request, message)
kwargs = {
'id': id}
url = reverse('workout_otwsetpower_view', kwargs=kwargs)
response = HttpResponseRedirect(url)
else:
form = AdvancedWorkoutForm(instance=w)
breadcrumbs = [
{
'url': '/rowers/list-workouts/',
'name': 'Workouts'
},
{
'url': get_workout_default_page(request, id),
'name': w.name
},
{
'url': reverse('workout_otwsetpower_view', kwargs={'id': id}),
'name': 'OTW Power'
}
]
messages.error(request, message)
messages.info(request, successmessage)
return render(request,
'otwsetpower.html',
{'workout': w,
'rower': w,
'mayedit': mayedit,
'active': 'nav-workouts',
'breadcrumbs': breadcrumbs,
'teams': get_my_teams(request.user),
'form': form,
})
@permission_required('workout.change_workout', fn=get_workout_by_opaqueid, raise_exception=True)
@@ -3598,7 +3194,7 @@ def workout_erase_column_view(request, id=0, column=''):
return HttpResponseRedirect(url)
try:
data = dataprep.getsmallrowdata_db([column], ids=[w.id])
data = dataprep.read_data([column], ids=[w.id])
except: # pragma: no cover
messages.error(request, 'Invalid column')
url = reverse('workout_data_view', kwargs={
@@ -3654,12 +3250,18 @@ def workout_erase_column_view(request, id=0, column=''):
row, workout = dataprep.getrowdata(id=w.id)
row.df[columnl] = defaultvalue
os.remove(w.csvfilename+'.gz')
try:
os.remove(w.csvfilename+'.gz')
except FileNotFoundError:
try:
os.remove(w.csvfilename)
except FileNotFoundError:
pass
row.write_csv(w.csvfilename, gzip=True)
row, workout = dataprep.getrowdata(id=w.id)
_ = dataprep.dataprep(row.df, id=w.id)
_ = dataprep.dataplep(row.df, id=w.id)
if column == 'hr':
w.hrtss = 0
@@ -4141,7 +3743,7 @@ def workout_workflow_view(request, id):
if 'panel_map.html' in r.workflowmiddlepanel and rowhascoordinates(row):
rowdata = rdata(csvfile=row.csvfilename)
mapscript, mapdiv = leaflet_chart2(rowdata.df[' latitude'],
mapscript, mapdiv = leaflet_chart(rowdata.df[' latitude'],
rowdata.df[' longitude'],
row.name)
else:
@@ -4370,7 +3972,7 @@ def workout_flexchart3_view(request, *args, **kwargs):
# create interactive plot
(
script, div, js_resources, css_resources, workstrokesonly
script, div, workstrokesonly
) = interactive_flex_chart2(
encoder.decode_hex(id), request.user.rower,
xparam=xparam, yparam1=yparam1,
@@ -4456,8 +4058,6 @@ def workout_flexchart3_view(request, *args, **kwargs):
'workout': row,
'chartform': flexaxesform,
'optionsform': flexoptionsform,
'js_res': js_resources,
'css_res': css_resources,
'teams': get_my_teams(request.user),
'id': id,
'xparam': xparam,
@@ -4505,7 +4105,7 @@ def workout_flexchart_stacked_view(request, *args, **kwargs):
yparam4 = cd['yaxis4']
(
script, div, js_resources, css_resources, comment
script, div
) = interactive_flexchart_stacked(
encoder.decode_hex(id), r, xparam=xparam,
yparam1=yparam1,
@@ -4515,9 +4115,6 @@ def workout_flexchart_stacked_view(request, *args, **kwargs):
mode=workout.workouttype,
)
if comment is not None: # pragma: no cover
messages.error(request, comment)
initial = {
'xaxis': xparam,
'yaxis1': yparam1,
@@ -4554,8 +4151,6 @@ def workout_flexchart_stacked_view(request, *args, **kwargs):
'active': 'nav-workouts',
'workout': workout,
'chartform': flexaxesform,
'js_res': js_resources,
'css_res': css_resources,
'id': id,
'xparam': xparam,
'yparam1': yparam1,
@@ -4568,57 +4163,6 @@ def workout_flexchart_stacked_view(request, *args, **kwargs):
# The interactive plot with wind corrected pace for OTW outings
def workout_otwpowerplot_view(request, id=0, message="", successmessage=""):
w = get_workout(id)
r = getrower(request.user)
breadcrumbs = [
{
'url': '/rowers/list-workouts/',
'name': 'Workouts'
},
{
'url': get_workout_default_page(request, id),
'name': w.name
},
{
'url': reverse('workout_otwpowerplot_view', kwargs={'id': id}),
'name': 'Interactive OTW Power Plot'
}
]
# check if user is owner of this workout
# create interactive plot
promember = 0
mayedit = 0
result = request.user.is_authenticated and ispromember(request.user)
if result:
promember = 1
if request.user == w.user.user:
mayedit = 1
# create interactive plot
res = interactive_otw_advanced_pace_chart(
encoder.decode_hex(id), promember=promember)
script = res[0]
div = res[1]
messages.error(request, message)
messages.info(request, successmessage)
return render(request,
'otwinteractive.html',
{'workout': w,
'rower': r,
'active': 'nav-workouts',
'breadcrumbs': breadcrumbs,
'teams': get_my_teams(request.user),
'interactiveplot': script,
'the_div': div,
'mayedit': mayedit})
#
@@ -4780,7 +4324,7 @@ def workout_edit_view(request, id=0, message="", successmessage=""):
row = get_workoutuser(id, request)
if request.user.rower.rowerplan == 'basic' and 'speedcoach2' in row.workoutsource: # pragma: no cover
data = getsmallrowdata_db(['wash'], ids=[encoder.decode_hex(id)])
data = read_data(['wash'], ids=[encoder.decode_hex(id)])
try:
if data['wash'].std() != 0:
url = reverse('paidplans_view')
@@ -4793,7 +4337,7 @@ def workout_edit_view(request, id=0, message="", successmessage=""):
pass
if request.user.rower.rowerplan == 'basic' and 'nklinklogbook' in row.workoutsource: # pragma: no cover
data = getsmallrowdata_db(['wash'], ids=[encoder.decode_hex(id)])
data = read_data(['wash'], ids=[encoder.decode_hex(id)])
try:
if data['wash'].std() != 0:
url = reverse('paidplans_view')
@@ -5063,7 +4607,7 @@ def workout_map_view(request, id=0):
hascoordinates = 0
if hascoordinates:
mapscript, mapdiv = leaflet_chart2(rowdata.df[' latitude'],
mapscript, mapdiv = leaflet_chart(rowdata.df[' latitude'],
rowdata.df[' longitude'],
w.name)
else:
@@ -5188,7 +4732,7 @@ def workout_uploadimage_view(request, id): # pragma: no cover
# Generic chart creation
@permission_required('workout.change_workout', fn=get_workout_by_opaqueid, raise_exception=True)
def workout_add_chart_view(request, id, plotnr=1):
w = get_workoutuser(id, request)
r = getrower(request.user)
+7
View File
@@ -599,6 +599,13 @@ except KeyError: # pragma: no cover
WORKOUTS_FIT_TOKEN = 'aapnootmies'
WORKOUTS_FIT_URL = 'http://localhost:50053/tojson'
try:
ROWSANDALL_CHARTS_TOKEN = CFG['rowsandall_charts_token']
ROWSANDALL_CHARTS_URL = CFG['rowsandall_charts_url']
except KeyError:
ROWSANDALL_CHARTS_TOKEN = 'aapnootmies'
ROWSANDALL_CHARTS_URL = 'http://localhost:3000'
# Recaptcha
try:
+10
View File
@@ -32,6 +32,16 @@
color: red;
}
.chart {
background-image: url(/static/img/logo7.png);
background-size: 200px;
background-repeat: no-repeat;
background-position: top 50px right 100px;
}
.chartlabel {
font-size: 1.2em;
}
.watermark {
position: absolute;