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.models import Alert, Condition, User, Rower, Workout
from rowers.teams import coach_getcoachees 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 datetime
import numpy as np import numpy as np
import math import math
@@ -101,8 +101,10 @@ def alert_get_stats(alert, nperiod=0): # pragma: no cover
ids = [w.id for w in workouts] ids = [w.id for w in workouts]
try: try:
df = getsmallrowdata_db(columns, ids=ids, doclean=True, df = getsmallrowdata_pd(columns, ids=ids, doclean=True,
workstrokesonly=workstrokesonly) workstrokesonly=workstrokesonly)
df.dropna(axis=1,how='all',inplace=True)
df.dropna(axis=0,how='all',inplace=True)
except: except:
return { return {
'workouts': workouts.count(), 'workouts': workouts.count(),
-57
View File
@@ -1,5 +1,4 @@
from rowers.courseutils import coursetime_paths, coursetime_first, time_in_path from rowers.courseutils import coursetime_paths, coursetime_first, time_in_path
import pandas as pd
from rowers.models import ( from rowers.models import (
Rower, Workout, Rower, Workout,
GeoPoint, GeoPolygon, GeoCourse, GeoPoint, GeoPolygon, GeoCourse,
@@ -420,62 +419,6 @@ def createcourse(
return c 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): def replacecourse(course1, course2):
ps = PlannedSession.objects.filter(course=course1) ps = PlannedSession.objects.filter(course=course1)
+81 -105
View File
@@ -8,6 +8,7 @@ from rowers.datautils import p0
from scipy import optimize from scipy import optimize
from rowers.utils import calculate_age from rowers.utils import calculate_age
import datetime import datetime
import gzip
from scipy.signal import savgol_filter from scipy.signal import savgol_filter
from rowers.opaque import encoder from rowers.opaque import encoder
from rowers.database import * from rowers.database import *
@@ -27,6 +28,8 @@ from fitparse import FitFile
import itertools import itertools
import numpy as np import numpy as np
import pandas as pd import pandas as pd
import polars as pl
from polars.exceptions import ColumnNotFoundError
from zipfile import BadZipFile from zipfile import BadZipFile
import zipfile import zipfile
import os import os
@@ -73,6 +76,7 @@ import pytz
import collections import collections
import pendulum import pendulum
from rowingdata import rowingdata as rrdata from rowingdata import rowingdata as rrdata
from rowingdata import rowingdata_pl as rrdata_pl
from rowingdata import rower as rrower from rowingdata import rower as rrower
@@ -88,10 +92,9 @@ from rowers.dataroutines import *
from rowers.tasks import ( from rowers.tasks import (
handle_sendemail_newftp, handle_sendemail_newftp,
handle_sendemail_unrecognized, handle_setcp, handle_sendemail_unrecognized,
handle_getagegrouprecords, handle_update_wps, handle_getagegrouprecords, handle_update_wps,
handle_request_post, handle_calctrimp, handle_request_post, handle_calctrimp,
handle_updatecp, handle_updateergcp,
handle_sendemail_breakthrough, handle_sendemail_breakthrough,
handle_sendemail_hard, handle_sendemail_hard,
) )
@@ -123,6 +126,10 @@ from rq import Queue
from rowers.datautils import rpetotss from rowers.datautils import rpetotss
def rscore_approx(row): 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: if row['rscore'] > 0:
return row['rscore'] return row['rscore']
if row['rscore'] == 0: # pragma: no cover if row['rscore'] == 0: # pragma: no cover
@@ -215,18 +222,18 @@ def check_marker(workout):
ids.append(w.id) ids.append(w.id)
gms.append(gmstandard) gms.append(gmstandard)
df = pd.DataFrame({ df = pl.DataFrame({
'id': ids, 'id': ids,
'gms': gms, 'gms': gms,
}) })
if df.empty: # pragma: no cover if df.is_empty(): # pragma: no cover
workout.ranking = True workout.ranking = True
workout.save() workout.save()
return workout return workout
indexmax = df['gms'].idxmax() theid = df.filter(pl.col("gms") == pl.col("gms").max())['id'][0]
theid = df.loc[indexmax, 'id']
wmax = Workout.objects.get(id=theid) wmax = Workout.objects.get(id=theid)
# gms_max = wmax.goldmedalstandard # gms_max = wmax.goldmedalstandard
@@ -322,7 +329,7 @@ def workout_summary_to_df(
goldstandarddurations.append(int(goldstandardduration)) goldstandarddurations.append(int(goldstandardduration))
rankingpieces.append(w.rankingpiece) rankingpieces.append(w.rankingpiece)
df = pd.DataFrame({ df = pl.DataFrame({
'ID': ids, 'ID': ids,
'date': startdatetimes, 'date': startdatetimes,
'name': names, 'name': names,
@@ -392,7 +399,7 @@ def resample(id, r, parent, overwrite='copy'):
row.write_csv(parent.csvfilename, gzip=True) 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) otwpower=True, empower=True, inboard=parent.inboard)
isbreakthrough, ishard = checkbreakthrough(parent, r) isbreakthrough, ishard = checkbreakthrough(parent, r)
_ = check_marker(parent) _ = check_marker(parent)
@@ -414,18 +421,23 @@ def resample(id, r, parent, overwrite='copy'):
def calculate_goldmedalstandard(rower, workout, recurrance=True): def calculate_goldmedalstandard(rower, workout, recurrance=True):
cpfile = 'media/cpdata_{id}.parquet.gz'.format(id=workout.id) cpfile = 'media/cpdata_{id}.parquet.gz'.format(id=workout.id)
try: try:
df = pd.read_parquet(cpfile) df = pl.read_parquet(cpfile)
except: except:
df = read_data(['power'], ids=[workout.id])
df = remove_nulls_pl(df)
background = True background = True
if settings.TESTING: if settings.TESTING:
background = False background = False
if recurrance:
df, delta, cpvalues = setcp(workout, background=background) df, delta, cpvalues = setcp(workout, background=background)
if df.empty: if df.is_empty():
return 0, 0
else:
return 0,0 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) df, delta, cpvalues = setcp(workout, recurrance=False, background=True)
if df.empty: if df.is_empty():
return 0, 0 return 0, 0
age = calculate_age(rower.birthdate, today=workout.date) age = calculate_age(rower.birthdate, today=workout.date)
@@ -453,7 +465,7 @@ def calculate_goldmedalstandard(rower, workout, recurrance=True):
if getrecords: # pragma: no cover if getrecords: # pragma: no cover
durations = [1, 4, 30, 60] durations = [1, 4, 30, 60]
distances = [100, 500, 1000, 2000, 5000, 6000, 10000, 21097, 42195] distances = [100, 500, 1000, 2000, 5000, 6000, 10000, 21097, 42195]
df2 = pd.DataFrame( df2 = pl.DataFrame(
list( list(
C2WorldClassAgePerformance.objects.filter( C2WorldClassAgePerformance.objects.filter(
sex=rower.sex, sex=rower.sex,
@@ -461,7 +473,7 @@ def calculate_goldmedalstandard(rower, workout, recurrance=True):
).values() ).values()
) )
) )
jsondf = df2.to_json() jsondf = df2.write_json()
_ = myqueue(queuelow, handle_getagegrouprecords, _ = myqueue(queuelow, handle_getagegrouprecords,
jsondf, distances, durations, age, rower.sex, rower.weightcategory) jsondf, distances, durations, age, rower.sex, rower.weightcategory)
@@ -489,9 +501,10 @@ def calculate_goldmedalstandard(rower, workout, recurrance=True):
scores = 100.*powers/wcpowers scores = 100.*powers/wcpowers
try: try:
indexmax = scores.idxmax() df = pl.DataFrame({'times': times, 'scores': scores})
delta = int(df.loc[indexmax, 'delta']) df = df.filter(pl.col("scores") == pl.col("scores").max())
maxvalue = scores.max() delta = df[0, "times"]
maxvalue = df[0, "scores"]
except (ValueError, TypeError, KeyError): # pragma: no cover except (ValueError, TypeError, KeyError): # pragma: no cover
indexmax = 0 indexmax = 0
delta = 0 delta = 0
@@ -504,21 +517,22 @@ def calculate_goldmedalstandard(rower, workout, recurrance=True):
def setcp(workout, background=False, recurrance=True): def setcp(workout, background=False, recurrance=True):
try: try:
filename = 'media/cpdata_{id}.parquet.gz'.format(id=workout.id) 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 # check dts
tarr = datautils.getlogarr(4000) tarr = datautils.getlogarr(4000)
if df['delta'][0] in tarr: if df['delta'][0] in tarr:
return(df, df['delta'], df['cp']) return(df, df['delta'], df['cp'])
except: except Exception as e:
pass pass
strokesdf = getsmallrowdata_db( strokesdf = read_data(
['power', 'workoutid', 'time'], ids=[workout.id]) ['power', 'workoutid', 'time'], ids=[workout.id])
strokesdf = remove_nulls_pl(strokesdf)
if strokesdf.empty: if strokesdf.is_empty():
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)
totaltime = strokesdf['time'].max() totaltime = strokesdf['time'].max()
maxt = totaltime/1000. maxt = totaltime/1000.
@@ -533,7 +547,7 @@ def setcp(workout, background=False, recurrance=True):
elif os.path.exists(csvfilename+'.gz'): # pragma: no cover elif os.path.exists(csvfilename+'.gz'): # pragma: no cover
csvfile = csvfilename+'.gz' csvfile = csvfilename+'.gz'
else: # pragma: no cover 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) csvfile = os.path.abspath(csvfile)
@@ -547,7 +561,7 @@ def setcp(workout, background=False, recurrance=True):
grpc.channel_ready_future(channel).result(timeout=10) grpc.channel_ready_future(channel).result(timeout=10)
except grpc.FutureTimeoutError: # pragma: no cover except grpc.FutureTimeoutError: # pragma: no cover
dologging('metrics.log','grpc channel time out in setcp') 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) stub = metrics_pb2_grpc.MetricsStub(channel)
req = metrics_pb2.CPRequest(filename = csvfile, filetype = "CSV", tarr = logarr) 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) response = stub.GetCP(req, timeout=60)
except Exception as e: except Exception as e:
dologging('metrics.log', traceback.format_exc()) 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)) delta = pl.Series(np.array(response.delta))
cpvalues = pd.Series(np.array(response.power)) cpvalues = pl.Series(np.array(response.power))
powermean = response.avgpower powermean = response.avgpower
df = pl.DataFrame({
df = pd.DataFrame({
'delta': delta, 'delta': delta,
'cp': cpvalues, 'cp': cpvalues,
'id': workout.id, '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: if recurrance:
goldmedalstandard, goldmedalduration = calculate_goldmedalstandard( goldmedalstandard, goldmedalduration = calculate_goldmedalstandard(
workout.user, workout) workout.user, workout, recurrance=False)
workout.goldmedalstandard = goldmedalstandard workout.goldmedalstandard = goldmedalstandard
workout.goldmedalduration = goldmedalduration workout.goldmedalduration = goldmedalduration
workout.save() workout.save()
@@ -602,14 +620,10 @@ def update_wps(r, types, mode='water', asynchron=True):
mode mode
) )
df = getsmallrowdata_db(['time', 'driveenergy'], ids=ids) df = read_data(['time', 'driveenergy'], ids=ids)
try: try:
mask = df['driveenergy'] > 100 wps_median = int(df.filter(pl.col("driveenergy")>100)["driveenergy"].median())
except (KeyError, TypeError):
return False
try:
wps_median = int(df.loc[mask, 'driveenergy'].median())
if mode == 'water': if mode == 'water':
r.median_wps = wps_median r.median_wps = wps_median
else: # pragma: no cover else: # pragma: no cover
@@ -618,6 +632,10 @@ def update_wps(r, types, mode='water', asynchron=True):
r.save() r.save()
except ValueError: # pragma: no cover except ValueError: # pragma: no cover
pass pass
except OverflowError:
pass
except ColumnNotFoundError:
pass
return True return True
@@ -718,77 +736,28 @@ def fetchcp_new(rower, workouts):
data = [] data = []
for workout in workouts: for workout in workouts:
cpfile = 'media/cpdata_{id}.parquet.gz'.format(id=workout.id)
try:
df, delta, cpvalues = setcp(workout) df, delta, cpvalues = setcp(workout)
df = pd.read_parquet(cpfile) df = df.drop('id')
df['workout'] = str(workout) df = df.with_columns((pl.lit(str(workout))).alias("workout"))
df['url'] = workout.url() df = df.with_columns((pl.lit(workout.url())).alias("url"))
data.append(df) if not df.is_empty():
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()
data.append(df) data.append(df)
if len(data) == 0: 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: if len(data) > 1:
df = pd.concat(data, axis=0) df = pl.concat(data)
try: try:
df = df[df['cp'] == df.groupby(['delta'])['cp'].transform('max')] df = df.group_by(pl.col("delta")).agg(pl.max("cp"), pl.max("workout"), pl.max("url")).sort("delta")
except KeyError: # pragma: no cover except (KeyError, ColumnNotFoundError): # pragma: no cover
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)
df = df.sort_values(['delta']).reset_index()
df = df[df['cp']>20]
df = df.filter(pl.col("cp")>20)
return df['delta'], df['cp'], 0, df['workout'], df['url'] 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): def update_rolling_cp(r, types, mode='water', dosend=False):
@@ -801,15 +770,17 @@ def update_rolling_cp(r, types, mode='water', dosend=False):
delta, cp, avgpower, workoutnames, urls = fetchcp_new(r, workouts) delta, cp, avgpower, workoutnames, urls = fetchcp_new(r, workouts)
powerdf = pd.DataFrame({ powerdf = pl.DataFrame({
'Delta': delta, 'Delta': delta,
'CP': cp, 'CP': cp,
}) })
powerdf = powerdf[powerdf['CP'] > 0] powerdf = powerdf.filter(pl.col("CP")>0)
powerdf.dropna(axis=0, inplace=True) powerdf = powerdf.fill_nan(None).drop_nulls().sort(["Delta", "CP"])
powerdf.sort_values(['Delta', 'CP'], ascending=[1, 0], inplace=True) powerdf = powerdf.unique(subset=["Delta"], keep="first")
powerdf.drop_duplicates(subset='Delta', keep='first', inplace=True)
if powerdf.is_empty():
return False
res2 = datautils.cpfit(powerdf) res2 = datautils.cpfit(powerdf)
p1 = res2[0] p1 = res2[0]
@@ -1051,7 +1022,7 @@ def checkbreakthrough(w, r):
workouttype = w.workouttype workouttype = w.workouttype
if workouttype in rowtypes: if workouttype in rowtypes:
cpdf, delta, cpvalues = setcp(w) cpdf, delta, cpvalues = setcp(w)
if not cpdf.empty: if not cpdf.is_empty():
if workouttype in otwtypes: if workouttype in otwtypes:
try: try:
res, btvalues, res2 = utils.isbreakthrough( res, btvalues, res2 = utils.isbreakthrough(
@@ -1397,7 +1368,12 @@ def save_workout_database(f2, r, dosmooth=True, workouttype='rower',
w.team.add(t) w.team.add(t)
# put stroke data in database # 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) barchart=True, otwpower=True, empower=True, inboard=inboard)
isbreakthrough, ishard = checkbreakthrough(w, r) isbreakthrough, ishard = checkbreakthrough(w, r)
+827 -54
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 pandas as pd
import polars as pl
import numpy as np import numpy as np
from scipy.interpolate import griddata from scipy.interpolate import griddata
from scipy import optimize from scipy import optimize
from rowers.mytypes import otwtypes, otetypes, rowtypes from rowers.mytypes import otwtypes, otetypes, rowtypes
from rowers.models import Workout from rowers.models import Workout
@@ -81,8 +83,9 @@ def cpfit(powerdf, fraclimit=0.0001, nmax=1000):
p1 = p0 p1 = p0
thesecs = powerdf['Delta'] thesecs = powerdf['Delta'].to_numpy()
theavpower = powerdf['CP'] theavpower = powerdf['CP'].to_numpy()
if len(thesecs) >= 4: if len(thesecs) >= 4:
try: try:
@@ -96,13 +99,14 @@ def cpfit(powerdf, fraclimit=0.0001, nmax=1000):
factor = fitfunc(p0, thesecs.mean())/theavpower.mean() factor = fitfunc(p0, thesecs.mean())/theavpower.mean()
p1 = [p0[0]/factor, p0[1]/factor, p0[2], p0[3]] p1 = [p0[0]/factor, p0[1]/factor, p0[2], p0[3]]
p1 = [abs(p) for p in p1] p1 = [abs(p) for p in p1]
fitt = pd.Series(10**(4*np.arange(100)/100.)) fitt = pd.Series(10**(4*np.arange(100)/100.))
fitpower = fitfunc(p1, fitt) fitpower = fitfunc(p1, fitt)
fitpoints = fitfunc(p1, thesecs) fitpoints = fitfunc(p1, thesecs)
fitpoints0 = fitpoints.copy() fitpoints0 = fitpoints
dd = fitpoints-theavpower dd = fitpoints-theavpower
ddmin = dd.min() ddmin = dd.min()
@@ -356,8 +360,8 @@ def getmaxwattinterval(tt, ww, i):
def getfastest(df, thevalue, mode='distance'): def getfastest(df, thevalue, mode='distance'):
tt = df['time'].copy() tt = df['time'].clone()
dd = df['cumdist'].copy() dd = df['cumdist'].clone()
tmax = tt.max() tmax = tt.max()
if mode == 'distance': # pragma: no cover if mode == 'distance': # pragma: no cover
@@ -368,40 +372,28 @@ def getfastest(df, thevalue, mode='distance'):
return 0 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 newlen = 1000
newt = np.arange(newlen)*tmax/float(newlen) newt = np.arange(newlen)*tmax/float(newlen)
deltat = newt[1]-newt[0] deltat = newt[1]-newt[0]
dd = griddata(tt.values, dd = griddata(tt.to_numpy(),
dd.values, newt, method='linear', rescale=True) dd.to_numpy(), newt, method='linear', rescale=True)
tt = pd.Series(newt, dtype='float') tt = pl.Series(newt, dtype=pl.Float64)
dd = pd.Series(dd, dtype='float') 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)) ones = 1+np.zeros(len(G))
Ghor = np.outer(ones, G) Ghor = np.outer(ones, G)
# Thor = np.outer(ones, T)
# Tver = np.outer(T, ones)
Gver = np.outer(G, ones) Gver = np.outer(G, ones)
Gdif = Ghor-Gver Gdif = Ghor-Gver
Gdif = np.tril(Gdif.T).T Gdif = np.tril(Gdif.T).T
Gdif = pd.DataFrame(Gdif) Gdif = pl.DataFrame(Gdif)
F = Gdif F = Gdif
F.fillna(inplace=True, method='ffill', axis=1) F = F.fill_nan(0)
F.fillna(inplace=True, value=0)
restime = [] restime = []
distance = [] distance = []
@@ -412,7 +404,7 @@ def getfastest(df, thevalue, mode='distance'):
restime.append(deltat*i) restime.append(deltat*i)
cp = np.diag(F, i).max() cp = np.diag(F, i).max()
loc = np.argmax(np.diag(F, i)) loc = np.argmax(np.diag(F, i))
thestarttime = tt[loc] thestarttime = tt.to_numpy()[loc]
starttimes.append(thestarttime) starttimes.append(thestarttime)
distance.append(cp) distance.append(cp)
@@ -422,10 +414,6 @@ def getfastest(df, thevalue, mode='distance'):
distance = np.array(distance) distance = np.array(distance)
starttimes = np.array(starttimes) 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': if mode == 'distance':
duration = griddata(distance, restime, [ duration = griddata(distance, restime, [
thevalue], method='linear', rescale=True) thevalue], method='linear', rescale=True)
@@ -434,7 +422,6 @@ def getfastest(df, thevalue, mode='distance'):
distance = griddata(distance, distance, [ distance = griddata(distance, distance, [
thevalue], method='linear', rescale=True) thevalue], method='linear', rescale=True)
endtime = starttime+duration endtime = starttime+duration
# print(duration,starttime,endtime,'aa')
return duration[0]/1000., starttime[0]/1000., endtime[0]/1000. return duration[0]/1000., starttime[0]/1000., endtime[0]/1000.
else: # pragma: no cover else: # pragma: no cover
distance = griddata(restime, distance, [ distance = griddata(restime, distance, [
+10 -83
View File
@@ -74,27 +74,6 @@ class ResampleForm(forms.Form):
class TrainingZonesForm(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( startdate = forms.DateField(
initial=timezone.now()-datetime.timedelta(days=42), initial=timezone.now()-datetime.timedelta(days=42),
widget=AdminDateWidget(), # format='%Y-%m-%d'), widget=AdminDateWidget(), # format='%Y-%m-%d'),
@@ -778,59 +757,7 @@ class PredictedPieceFormNoDistance(forms.Form):
value = forms.FloatField(initial=10, label='Free ranking piece') 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 # Form to select a data range to show workouts from a certain time period
class DateRangeForm(forms.Form): class DateRangeForm(forms.Form):
startdate = forms.DateField( startdate = forms.DateField(
initial=timezone.now()-datetime.timedelta(days=365), initial=timezone.now()-datetime.timedelta(days=365),
@@ -1859,27 +1786,27 @@ class FlexOptionsForm(forms.Form):
class ForceCurveOptionsForm(forms.Form): class ForceCurveOptionsForm(forms.Form):
includereststrokes = forms.BooleanField(initial=False, required=False, 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_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) 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_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_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) work_max = forms.IntegerField(initial=1500,label='Work Max',widget=HiddenInput,required=False)
notes = forms.CharField(initial="", label='notes', 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) 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( axchoices = list(
(ax[0], ax[1]) for ax in axes if ax[0] not in ['cumdist', 'None'] (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 rowers.models import User, Rower, Workout, TombStone
from django.db.utils import IntegrityError from django.db.utils import IntegrityError
from rowingdata import rowingdata from rowingdata import rowingdata, rowingdata_pl
import numpy as np import numpy as np
import datetime import datetime
import json import json
+904 -5759
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 metricsdicts[key] = dict
metricsgroups = list(set([d['group'] for n, d in rowingmetrics])) metricsgroups = list(set([d['group'] for n, d in rowingmetrics]))
dtypes = {} dtypes = {}
+6 -1
View File
@@ -3833,8 +3833,11 @@ def auto_delete_file_on_delete(sender, instance, **kwargs):
# remove parquet file # remove parquet file
try: try:
dirname = 'media/strokedata_{id}.parquet.gz'.format(id=instance.id) dirname = 'media/strokedata_{id}.parquet.gz'.format(id=instance.id)
shutil.rmtree(dirname) os.remove(dirname)
except FileNotFoundError: except FileNotFoundError:
try:
shutil.rmtree(dirname)
except:
pass pass
# remove parquet file # remove parquet file
@@ -5257,6 +5260,8 @@ class ForceCurveAnalysis(models.Model):
average_spm = models.FloatField(default=23) average_spm = models.FloatField(default=23)
average_boatspeed = models.FloatField(default=4.0) average_boatspeed = models.FloatField(default=4.0)
include_rest_strokes = models.BooleanField(default=False) include_rest_strokes = models.BooleanField(default=False)
plotcircles = models.BooleanField(default=False)
plotlines = models.BooleanField(default=False)
def __str__(self): def __str__(self):
s = 'Force Curve Analysis {name} ({date})'.format(name = self.name, 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 numpy as np
import rowers.metrics as metrics import rowers.metrics as metrics
import rowers.mytypes as mytypes import rowers.mytypes as mytypes
from rowers.courses import get_time_course
from rowers.utils import to_pace from rowers.utils import to_pace
from rowers.opaque import encoder from rowers.opaque import encoder
from rowingdata import rower as rrower from rowingdata import rower as rrower
from rowingdata import rowingdata as rrdata from rowingdata import rowingdata as rrdata
import arrow import arrow
import pandas as pd import polars as pl
import json import json
# Python # Python
@@ -203,16 +202,8 @@ def get_execution_report(rower, startdate, enddate, plan=None):
startdate__gte=enddate) startdate__gte=enddate)
else: # pragma: no cover else: # pragma: no cover
plans = TrainingPlan.objects.filter(rowers__in=[rower]) 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: if not plans:
# make week cycles here
# get monday before startdate
micros = [] micros = []
else: else:
sorted_plans = sorted(plans, key= lambda t: t.overlap(startdate,enddate)) 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: else:
plannedscore += 60 plannedscore += 60
actualscore += 0 actualscore += 0
elif w.hrtss != 0: elif w.hrtss >= 0:
if ratio > 0: if ratio > 0:
plannedscore += w.hrtss/ratio plannedscore += w.hrtss/ratio
actualscore += w.hrtss actualscore += w.hrtss
@@ -313,7 +304,7 @@ def get_execution_report(rower, startdate, enddate, plan=None):
planned += [plannedscore] planned += [plannedscore]
executed += [actualscore] executed += [actualscore]
data = pd.DataFrame({ data = pl.DataFrame({
'startdate': startdates, 'startdate': startdates,
'planned': planned, 'planned': planned,
'executed': executed, 'executed': executed,
@@ -444,8 +435,9 @@ def add_workouts_plannedsession(ws, ps, r):
for record in records: for record in records:
record.delete() record.delete()
df = dataprep.getsmallrowdata_db( df = dataprep.read_data(
['time', 'cumdist'], ids=[w.id]) ['time', 'cumdist'], ids=[w.id])
df = dataprep.remove_nulls_pl(df)
fastest_milliseconds, starttime, endtime = datautils.getfastest( fastest_milliseconds, starttime, endtime = datautils.getfastest(
df, ps.sessionvalue, mode='distance') df, ps.sessionvalue, mode='distance')
@@ -475,11 +467,11 @@ def add_workouts_plannedsession(ws, ps, r):
for record in records: for record in records:
record.delete() record.delete()
df = dataprep.getsmallrowdata_db( df = dataprep.read_data(
['time', 'cumdist'], ids=[w.id]) ['time', 'cumdist'], ids=[w.id])
df = dataprep.remove_nulls_pl(df)
fastest_meters, starttime, endtime = datautils.getfastest( fastest_meters, starttime, endtime = datautils.getfastest(
df, ps.sessionvalue, mode='time') df, ps.sessionvalue, mode='time')
if fastest_meters > 0: if fastest_meters > 0:
w.plannedsession = ps w.plannedsession = ps
w.save() w.save()
@@ -1694,8 +1686,9 @@ def add_workout_fastestrace(ws, race, r, recordid=0, doregister=False):
record.coursecompleted = True record.coursecompleted = True
record.workoutid = ws[0].id record.workoutid = ws[0].id
if race.sessiontype == 'fastest_distance': if race.sessiontype == 'fastest_distance':
df = dataprep.getsmallrowdata_db( df = dataprep.read_data(
['time', 'cumdist'], ids=[ws[0].id]) ['time', 'cumdist'], ids=[ws[0].id])
df = dataprep.remove_nulls_pl(df)
fastest_milliseconds, startsecond, endsecond = datautils.getfastest( fastest_milliseconds, startsecond, endsecond = datautils.getfastest(
df, race.sessionvalue, mode='distance') df, race.sessionvalue, mode='distance')
velo = race.sessionvalue/fastest_milliseconds velo = race.sessionvalue/fastest_milliseconds
@@ -1711,8 +1704,9 @@ def add_workout_fastestrace(ws, race, r, recordid=0, doregister=False):
record.endsecond = endsecond record.endsecond = endsecond
record.save() record.save()
if race.sessiontype == 'fastest_time': # pragma: no cover if race.sessiontype == 'fastest_time': # pragma: no cover
df = dataprep.getsmallrowdata_db( df = dataprep.read_data(
['time', 'cumdist'], ids=[ws[0].id]) ['time', 'cumdist'], ids=[ws[0].id])
df = dataprep.remove_nulls_pl(df)
fastest_meters, startsecond, endsecond = datautils.getfastest( fastest_meters, startsecond, endsecond = datautils.getfastest(
df, race.sessionvalue, mode='time') df, race.sessionvalue, mode='time')
velo = fastest_meters/(60.*race.sessionvalue) velo = fastest_meters/(60.*race.sessionvalue)
+17 -1
View File
@@ -6,7 +6,7 @@ from rest_framework import serializers
from rowers.models import ( from rowers.models import (
Workout, Rower, FavoriteChart, VirtualRaceResult, Workout, Rower, FavoriteChart, VirtualRaceResult,
VirtualRace, GeoCourse, StandardCollection, CourseStandard, VirtualRace, GeoCourse, StandardCollection, CourseStandard,
GeoPolygon, GeoPoint, PlannedSession, GeoPolygon, GeoPoint, PlannedSession, ForceCurveAnalysis
) )
from django.core.exceptions import PermissionDenied from django.core.exceptions import PermissionDenied
@@ -297,6 +297,22 @@ class GeoPointSerializer(serializers.ModelSerializer):
) )
extra_kwargs = {'id': {'read_only': False, 'required': True}} 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): class GeoPolygonSerializer(serializers.ModelSerializer):
points = GeoPointSerializer(many=True) points = GeoPointSerializer(many=True)
+37 -117
View File
@@ -56,7 +56,7 @@ from scipy.signal import savgol_filter
from scipy.interpolate import griddata from scipy.interpolate import griddata
import rowingdata import rowingdata
from rowingdata import make_cumvalues from rowingdata import make_cumvalues, make_cumvalues_array
from uuid import uuid4 from uuid import uuid4
from rowingdata import rowingdata as rdata from rowingdata import rowingdata as rdata
@@ -115,6 +115,9 @@ tpapilocation = TP_API_LOCATION
from requests_oauthlib import OAuth1, OAuth1Session from requests_oauthlib import OAuth1, OAuth1Session
import pandas as pd import pandas as pd
import polars as pl
from polars.exceptions import ColumnNotFoundError
from django_rq import job from django_rq import job
from django.utils import timezone from django.utils import timezone
@@ -127,7 +130,8 @@ from rowers import mytypes
from rowers.dataroutines import ( 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_workout_field_sql,
update_agegroup_db, update_strokedata, update_agegroup_db, update_strokedata,
add_c2_stroke_data_db, totaltime_sec_to_string, add_c2_stroke_data_db, totaltime_sec_to_string,
@@ -2549,9 +2553,10 @@ def handle_otwsetpower(self, f1, boattype, boatclass, coastalbrand, weightvalue,
totaltime = totaltime + rowdata.df.loc[0, ' ElapsedTime (sec)'] totaltime = totaltime + rowdata.df.loc[0, ' ElapsedTime (sec)']
except KeyError: # pragma: no cover except KeyError: # pragma: no cover
pass pass
df = getsmallrowdata_db( df = getsmallrowdata_pd(
['power', 'workoutid', 'time'], ids=[workoutid], ['power', 'workoutid', 'time'], ids=[workoutid],
debug=debug) debug=debug)
thesecs = totaltime thesecs = totaltime
maxt = 1.05 * thesecs maxt = 1.05 * thesecs
logarr = datautils.getlogarr(maxt) logarr = datautils.getlogarr(maxt)
@@ -2583,53 +2588,6 @@ def handle_otwsetpower(self, f1, boattype, boatclass, coastalbrand, weightvalue,
return 1 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 @app.task
def handle_makeplot(f1, f2, t, hrdata, plotnr, imagename, def handle_makeplot(f1, f2, t, hrdata, plotnr, imagename,
@@ -3179,43 +3137,6 @@ def handle_sendemail_invite_reject(email, name, teamname, managername,
return 1 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 @app.task
def handle_sendemail_invite_accept(email, name, teamname, managername, def handle_sendemail_invite_accept(email, name, teamname, managername,
@@ -3251,23 +3172,22 @@ graphql_url = "https://rp3rowing-app.com/graphql"
@app.task @app.task
def handle_update_wps(rid, types, ids, mode, debug=False, **kwargs): 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: try:
mask = df['driveenergy'] > 100 wps_median = int(df.filter(pl.col("driveenergy")>100)["driveenergy"].median())
except (KeyError, TypeError): # pragma: no cover
return 0
try:
wps_median = int(df.loc[mask, 'driveenergy'].median())
except ValueError: # pragma: no cover
return 0
rower = Rower.objects.get(id=rid) rower = Rower.objects.get(id=rid)
if mode == 'water': if mode == 'water':
rower.median_wps = wps_median rower.median_wps = wps_median
else: else: # pragma: no cover
rower.median_wps_erg = wps_median rower.median_wps_erg = wps_median
rower.save() rower.save()
except ValueError: # pragma: no cover
wps_median = 0
except OverflowError:
wps_median = 0
except ColumnNotFoundError:
wps_median = 0
return wps_median return wps_median
@@ -3901,7 +3821,7 @@ def fetch_strava_workout(stravatoken, oauth_data, stravaid, csvfilename, userid,
pace[np.isinf(pace)] = 0.0 pace[np.isinf(pace)] = 0.0
try: try:
strokedata = pd.DataFrame({'t': 10*t, strokedata = pl.DataFrame({'t': 10*t,
'd': 10*d, 'd': 10*d,
'p': 10*pace, 'p': 10*pace,
'spm': spm, 'spm': spm,
@@ -3947,18 +3867,18 @@ def fetch_strava_workout(stravatoken, oauth_data, stravaid, csvfilename, userid,
starttimeunix = arrow.get(rowdatetime).timestamp() starttimeunix = arrow.get(rowdatetime).timestamp()
res = make_cumvalues(0.1*strokedata['t']) res = make_cumvalues_array(0.1*strokedata['t'].to_numpy())
cum_time = res[0] cum_time = pl.Series(res[0])
lapidx = res[1] lapidx = pl.Series(res[1])
unixtime = cum_time+starttimeunix unixtime = cum_time+starttimeunix
seconds = 0.1*strokedata.loc[:, 't'] seconds = 0.1*strokedata['t']
nr_rows = len(unixtime) nr_rows = len(unixtime)
try: try:
latcoord = strokedata.loc[:, 'lat'] latcoord = strokedata['lat']
loncoord = strokedata.loc[:, 'lon'] loncoord = strokedata['lon']
if latcoord.std() == 0 and loncoord.std() == 0 and workouttype == 'water': # pragma: no cover if latcoord.std() == 0 and loncoord.std() == 0 and workouttype == 'water': # pragma: no cover
workouttype = 'rower' workouttype = 'rower'
except: # pragma: no cover except: # pragma: no cover
@@ -3968,29 +3888,29 @@ def fetch_strava_workout(stravatoken, oauth_data, stravaid, csvfilename, userid,
workouttype = 'rower' workouttype = 'rower'
try: try:
strokelength = strokedata.loc[:, 'strokelength'] strokelength = strokedata['strokelength']
except: # pragma: no cover except: # pragma: no cover
strokelength = np.zeros(nr_rows) strokelength = np.zeros(nr_rows)
dist2 = 0.1*strokedata.loc[:, 'd'] dist2 = 0.1*strokedata['d']
try: try:
spm = strokedata.loc[:, 'spm'] spm = strokedata['spm']
except KeyError: # pragma: no cover except (KeyError, ColumnNotFoundError): # pragma: no cover
spm = 0*dist2 spm = 0*dist2
try: try:
hr = strokedata.loc[:, 'hr'] hr = strokedata['hr']
except KeyError: # pragma: no cover except (KeyError, ColumnNotFoundError): # pragma: no cover
hr = 0*spm hr = 0*spm
pace = strokedata.loc[:, 'p']/10. pace = strokedata['p']/10.
pace = np.clip(pace, 0, 1e4) pace = np.clip(pace, 0, 1e4)
pace = pace.replace(0, 300) pace = pl.Series(pace).replace(0, 300)
velo = 500./pace velo = 500./pace
try: try:
power = strokedata.loc[:, 'power'] power = strokedata['power']
except KeyError: # pragma: no cover except KeyError: # pragma: no cover
power = 2.8*velo**3 power = 2.8*velo**3
@@ -3999,7 +3919,7 @@ def fetch_strava_workout(stravatoken, oauth_data, stravaid, csvfilename, userid,
# save csv # save csv
# Create data frame with all necessary data to write to 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, ' Horizontal (meters)': dist2,
' Cadence (stokes/min)': spm, ' Cadence (stokes/min)': spm,
' HRCur (bpm)': hr, ' HRCur (bpm)': hr,
@@ -4019,10 +3939,10 @@ def fetch_strava_workout(stravatoken, oauth_data, stravaid, csvfilename, userid,
'cum_dist': dist2, 'cum_dist': dist2,
}) })
df.sort_values(by='TimeStamp (sec)', ascending=True) df.sort('TimeStamp (sec)')
row = rowingdata.rowingdata(df=df) row = rowingdata.rowingdata_pl(df=df)
row.write_csv(csvfilename, gzip=False) row.write_csv(csvfilename, compressed=False)
# summary = row.allstats() # summary = row.allstats()
# maxdist = df['cum_dist'].max() # maxdist = df['cum_dist'].max()
-1
View File
@@ -32,7 +32,6 @@
<div class="mapdiv"> <div class="mapdiv">
{{ mapdiv|safe }} {{ mapdiv|safe }}
{{ mapscript|safe }} {{ mapscript|safe }}
</div> </div>
</li> </li>
+2 -1
View File
@@ -37,8 +37,9 @@
<div class="mapdiv"> <div class="mapdiv">
{{ mapdiv|safe }} {{ mapdiv|safe }}
<script>
{{ mapscript|safe }} {{ mapscript|safe }}
</script>
</div> </div>
</li> </li>
</ul> </ul>
@@ -45,7 +45,9 @@
<li class="grid_4"> <li class="grid_4">
<div class="mapdiv"> <div class="mapdiv">
{{ mapdiv|safe }} {{ mapdiv|safe }}
<script>
{{ mapscript|safe }} {{ mapscript|safe }}
</script>
</div> </div>
</li> </li>
</ul> </ul>
-1
View File
@@ -54,7 +54,6 @@
<div class="mapdiv"> <div class="mapdiv">
{{ mapdiv|safe }} {{ mapdiv|safe }}
{{ mapscript|safe }} {{ mapscript|safe }}
</div> </div>
</li> </li>
+3 -6
View File
@@ -102,14 +102,11 @@
</li> </li>
{% endif %} {% endif %}
<li class="grid_2"> <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>
{{ interactiveplot |safe }}
{{ the_div|safe }} {{ the_div|safe }}
{{ interactiveplot |safe }}
</li> </li>
<li class="grid_4"> <li class="grid_4">
+3 -3
View File
@@ -116,7 +116,7 @@ function copyText() {
</li> </li>
{% if user.is_authenticated and user == workout.user.user and not locked %} {% if user.is_authenticated and user == workout.user.user and not locked %}
<li class="grid_2"> <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>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 <p>Playing the video will advance the data in synchonization. Use the regular YouTube
controls controls
@@ -169,9 +169,8 @@ function copyText() {
</li> </li>
<li class="grid_2"> <li class="grid_2">
<div id="player"></div> <div id="player"></div>
<script>
// 1. Code for the map
{{ mapscript | safe }} {{ mapscript | safe }}
<script>
// 2. This code loads the IFrame Player API code asynchronously. // 2. This code loads the IFrame Player API code asynchronously.
@@ -189,6 +188,7 @@ function copyText() {
var videotime = 0; var videotime = 0;
var data = JSON.parse('{{ data|safe }}'); var data = JSON.parse('{{ data|safe }}');
{% for id, metric in metrics.items %} {% for id, metric in metrics.items %}
var {{ id }}_values = data["{{ id }}"]; var {{ id }}_values = data["{{ id }}"];
// console.log("{{ id }}_values",{{ id }}_values); // 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 %}
+5 -12
View File
@@ -8,16 +8,6 @@
{% localtime on %} {% localtime on %}
{% block main %} {% 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> <p>
{% if workout|previousworkout:rower.user %} {% if workout|previousworkout:rower.user %}
@@ -34,10 +24,9 @@
<h1>Flexible Chart</h1> <h1>Flexible Chart</h1>
<ul class="main-content"> <ul class="main-content">
<canvas hidden id="canvas"></canvas>
<li class="grid_4"> <li class="grid_4">
<div id="theplot" class="flexplot">
{{ the_div|safe }} {{ the_div|safe }}
</div>
</li> </li>
<li class="grid_2"> <li class="grid_2">
<form enctype="multipart/form-data" <form enctype="multipart/form-data"
@@ -96,6 +85,10 @@
</li> </li>
</ul> </ul>
<script src="https://d3js.org/d3.v6.js"></script>
{{ the_script |safe }}
{% endblock %} {% endblock %}
{% endlocaltime %} {% endlocaltime %}
+3 -9
View File
@@ -8,16 +8,8 @@
{% localtime on %} {% localtime on %}
{% block main %} {% block main %}
{{ js_res | safe }} <script src="https://d3js.org/d3.v6.js"></script>
{{ 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 async="true" type="text/javascript">
Bokeh.set_log_level("info");
</script>
{{ the_script |safe }}
<p> <p>
{% if workout|previousworkout:rower.user %} {% if workout|previousworkout:rower.user %}
@@ -34,6 +26,7 @@
<h1>Chart Stack</h1> <h1>Chart Stack</h1>
<ul class="main-content"> <ul class="main-content">
<canvas hidden id="canvas"></canvas>
<li class="grid_4"> <li class="grid_4">
<div id="theplot"> <div id="theplot">
{{ the_div|safe }} {{ the_div|safe }}
@@ -57,6 +50,7 @@
</form> </form>
</li> </li>
</ul> </ul>
{{ the_script |safe }}
{% endblock %} {% endblock %}
{% endlocaltime %} {% endlocaltime %}
+4 -9
View File
@@ -6,25 +6,20 @@
{% block main %} {% block main %}
{{ js_res | safe }} <script src="https://code.jquery.com/jquery-3.4.1.min.js"></script>
{{ 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>Force Curve Analysis for {{ rower.user.first_name }} {{ rower.user.last_name }}</h1> <h1>Force Curve Analysis for {{ rower.user.first_name }} {{ rower.user.last_name }}</h1>
<ul class="main-content"> <ul class="main-content">
<canvas hidden id="canvas"></canvas>
{% if the_div %} {% if the_div %}
<li class="grid_4"> <li class="grid_4">
<div id="theplot" class="flexplot"> <div id="theplot" class="flexplot">
{{ the_div|safe }} {{ the_div|safe }}
{{ the_script |safe }}
</div> </div>
</li> </li>
{% endif %} {% endif %}
+10 -30
View File
@@ -8,23 +8,17 @@
{% localtime on %} {% localtime on %}
{% block main %} {% block main %}
{{ js_res | safe }} <script src="https://d3js.org/d3.v6.js"></script>
{{ 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 async="true" type="text/javascript">
Bokeh.set_log_level("info");
</script>
{{ the_script |safe }}
<h1>Empower Force Curve</h1> <h1>Empower Force Curve</h1>
<ul class="main-content"> <ul class="main-content">
<canvas hidden id="canvas"></canvas>
<li class="grid_4"> <li class="grid_4">
<div id="theplot" class="flexplot"> <div id="theplot" class="flexplot">
{{ the_div|safe }} {{ the_div|safe }}
{{ the_script |safe }}
</div> </div>
</li> </li>
@@ -52,27 +46,13 @@
<script src="https://code.jquery.com/jquery-3.4.1.min.js"></script> <script src="https://code.jquery.com/jquery-3.4.1.min.js"></script>
<script> <script>
$(document).ready(function() { $(document).ready(function() {
var slider_min_spm = Bokeh.documents[0].get_model_by_name('min_spm_slider') var label = $('label[for="' + $("#id_plotcircles").attr('id') + '"]');
slider_min_spm.value = {{ spm_min }} label.attr("class", "hidden")
console.log('min spm value after:', slider_min_spm.value) var label2 = $('label[for="' + $("#id_plotlines").attr('id') + '"]');
var slider_max_spm = Bokeh.documents[0].get_model_by_name('max_spm_slider') label2.attr("class", "hidden")
slider_max_spm.value = {{ spm_max }} var label3 = $('label[for="' + $("#id_includereststrokes").attr('id') + '"]');
console.log('max spm value after:', slider_max_spm.value) label3.attr("class", "hidden")
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)
}); });
</script> </script>
+6 -11
View File
@@ -60,17 +60,6 @@
{% block main %} {% 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 %} {% if rower.user %}
<h1>Gold Medal Scores for {{ rower.user.first_name }} </h1> <h1>Gold Medal Scores for {{ rower.user.first_name }} </h1>
{% else %} {% else %}
@@ -79,6 +68,7 @@
<ul class="main-content"> <ul class="main-content">
<canvas hidden id="canvas"></canvas>
<li class="grid_4"> <li class="grid_4">
<p>Hover over the workout to see details, click on the workout to open in a <p>Hover over the workout to see details, click on the workout to open in a
separate page.</p> separate page.</p>
@@ -136,6 +126,11 @@
</li> </li>
</ul> </ul>
<script src="https://d3js.org/d3.v7.min.js"></script>
<div id="id_script">
{{ chartscript |safe }}
</div>
{% endblock %} {% endblock %}
+2 -1
View File
@@ -6,6 +6,7 @@
{% block main %} {% block main %}
<script src="https://code.jquery.com/jquery-1.9.1.min.js"></script> <script src="https://code.jquery.com/jquery-1.9.1.min.js"></script>
<script src="https://d3js.org/d3/v6/js"></script>
<script> <script>
$(function() { $(function() {
@@ -86,7 +87,6 @@
<li class="grid_4"> <li class="grid_4">
<div id="id_chart"> <div id="id_chart">
{{ the_div|safe }} {{ the_div|safe }}
</div> </div>
</li> </li>
@@ -129,6 +129,7 @@
var counter=0; var counter=0;
var script = json.script; var script = json.script;
var div = json.div; var div = json.div;
console.log(div)
console.log('set vars'); console.log('set vars');
$("#id_sitready").remove(); $("#id_sitready").remove();
console.log('sitready removed'); console.log('sitready removed');
+3 -5
View File
@@ -6,12 +6,9 @@
{% block main %} {% block main %}
<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>
{{ interactiveplot |safe }}
@@ -24,6 +21,7 @@
</li> </li>
</ul> </ul>
{{ interactiveplot |safe }}
{% endif %} {% endif %}
{% endblock %} {% endblock %}
+7 -13
View File
@@ -5,25 +5,14 @@
{% block title %}Rowsandall {% endblock %} {% block title %}Rowsandall {% endblock %}
{% block main %} {% 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"> <div id="id_js_res">
<script src="https://cdn.pydata.org/bokeh/release/bokeh-3.1.1.min.js"></script> <script src="https://d3js.org/d3.v7.min.js"></script>
<script src="https://cdn.pydata.org/bokeh/release/bokeh-widgets-3.1.1.min.js"></script>
</div> </div>
<script async="true" type="text/javascript">
Bokeh.set_log_level("info");
</script>
<div id="id_script"> <div id="id_script">
</div> </div>
<div id="t_script">
{{ tscript|safe}}
</div>
<div id="activities_script"> <div id="activities_script">
</div> </div>
@@ -90,6 +79,10 @@
</li> </li>
<li class="grid_2"> <li class="grid_2">
{{ tdiv|safe }} {{ tdiv|safe }}
<div id="t_script">
{{ tscript|safe}}
</div>
</li> </li>
<li class="grid_2"> <li class="grid_2">
<div id="id_chart"> <div id="id_chart">
@@ -157,7 +150,6 @@
var activities_chart = json.activities_chart var activities_chart = json.activities_chart
$("#id_sitready").remove(); $("#id_sitready").remove();
$("#id_chart").append(div); $("#id_chart").append(div);
// $("#id_script").append("<s"+"cript>"+script+"</s"+"cript>");
$("#id_script").append(script); $("#id_script").append(script);
$("#id_activities").append(activities_chart) $("#id_activities").append(activities_chart)
$("#activities_script").append(activities_script) $("#activities_script").append(activities_script)
@@ -165,6 +157,8 @@
$("#total_maxhr").append(totalsdict.hrmax); $("#total_maxhr").append(totalsdict.hrmax);
$("#total_power").append(totalsdict.powermean); $("#total_power").append(totalsdict.powermean);
$("#total_maxpower").append(totalsdict.powermax); $("#total_maxpower").append(totalsdict.powermax);
$("#selectors").hide();
listofdicts.forEach(function(item){ listofdicts.forEach(function(item){
var id = "#"+item.id+"_hr"; var id = "#"+item.id+"_hr";
$(id).append(item.hrmean); $(id).append(item.hrmean);
+3 -7
View File
@@ -6,24 +6,20 @@
{% block main %} {% block main %}
{{ js_res | safe }} <script src="https://d3js.org/d3.v6.js"></script>
{{ 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 async="true" type="text/javascript"> <script async="true" type="text/javascript">
Bokeh.set_log_level("info"); Bokeh.set_log_level("info");
</script> </script>
{{ the_script |safe }}
<h1>In-Stroke Analysis for {{ rower.user.first_name }} {{ rower.user.last_name }}</h1> <h1>In-Stroke Analysis for {{ rower.user.first_name }} {{ rower.user.last_name }}</h1>
<ul class="main-content"> <ul class="main-content">
<canvas hidden id="canvas"></canvas>
{% if the_div %} {% if the_div %}
<li class="grid_4"> <li class="grid_4">
<div id="theplot" class="flexplot"> <div id="theplot" class="flexplot">
{{ the_div|safe }} {{ the_div|safe }}
{{ the_script |safe }}
</div> </div>
</li> </li>
{% endif %} {% endif %}
+19 -14
View File
@@ -11,10 +11,8 @@
<meta name="viewport" content="width=device-width, initial-scale=1"> <meta name="viewport" content="width=device-width, initial-scale=1">
<title>jQuery UI Slider - Range slider</title> <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="//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/jquery-3.6.0.js"></script>
<script src="https://code.jquery.com/ui/1.13.2/jquery-ui.js"></script> <script src="https://code.jquery.com/ui/1.13.2/jquery-ui.js"></script>
<script> <script>
function submit_form() { function submit_form() {
console.log("form changed"); console.log("form changed");
@@ -30,11 +28,14 @@
dataType: 'json', dataType: 'json',
success: function(data) { 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>'); $("#id_chart").replaceWith('<div id="id_chart">'+data.div+'</d'+'iv>');
$("#ds").replaceWith('<div id="ds">'+data.ds+'</d'+'iv>'); $("#id_script").replaceWith('<div id="id_script">'+data.script+'</d'+'iv>');
$("#dd").replaceWith('<div id="dd">'+data.dd+'</d'+'iv>');
$("#dd").empty();
$("#dd").html(data.dd);
$("#ds").empty();
$('#ds').html(data.ds)
} }
}); });
} }
@@ -119,15 +120,10 @@ $( function() {
Bokeh.set_log_level("info"); Bokeh.set_log_level("info");
</script> </script>
<div id="id_script">
{{ the_script |safe }}
</div>
<div id="ds">
{{ ds |safe }}
</div>
<h1>In Stroke Metrics</h1> <h1>In Stroke Metrics</h1>
<ul class="main-content"> <ul class="main-content">
<canvas hidden id="canvas"></canvas>
<li class="grid_4" > <li class="grid_4" >
{% if user.rower|is_basic %} {% if user.rower|is_basic %}
@@ -144,7 +140,7 @@ $( function() {
</table> </table>
<div id="slider-range"></div> <div id="slider-range"></div>
<p> <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;"> <input type="text" id="amountspm" readonly style="border:0; color:#1c75bc; font-weight:bold;">
</p> </p>
<div id="slider-timerange"></div> <div id="slider-timerange"></div>
@@ -168,14 +164,23 @@ $( function() {
</p> </p>
</li> </li>
<li class="grid_4"> <li class="grid_4">
<script src="https://d3js.org/d3.v6.js"></script>
<div id="id_chart" class="flexplot"> <div id="id_chart" class="flexplot">
{{ the_div|safe }} {{ the_div|safe }}
</div> </div>
<div id="id_script">
{{ the_script |safe }}
</div>
</li> </li>
<li class="grid_4"> <script src="https://d3js.org/d3.v6.js"></script>
<li class="grid_2" id="interactive">
<div id="dd" class="flexplot"> <div id="dd" class="flexplot">
{{ dd|safe }} {{ dd|safe }}
</div> </div>
<div id="ds">
{{ ds |safe }}
</div>
</li> </li>
</ul> </ul>
+3 -14
View File
@@ -229,22 +229,11 @@
Total meters: {{ totalmeters }}. Total time {{ totalhours }}:{{ totalminutes }}h. Total meters: {{ totalmeters }}. Total time {{ totalhours }}:{{ totalminutes }}h.
<a href="/rowers/history/">Dig deeper</a>. <a href="/rowers/history/">Dig deeper</a>.
</p> </p>
<p> <script src="https://d3js.org/d3.v7.min.js"></script>
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 }}
{{ the_div |safe }} {{ the_div |safe }}
{{ interactiveplot |safe }}
</li> </li>
{% if announcements %} {% if announcements %}
<li class="grid_4"> <li class="grid_4">
-1
View File
@@ -26,7 +26,6 @@
<div style="height:100%;" id="theplot" class="flexplot mapdiv"> <div style="height:100%;" id="theplot" class="flexplot mapdiv">
{{ mapdiv|safe }} {{ mapdiv|safe }}
{{ mapscript|safe }} {{ mapscript|safe }}
</li> </li>
</ul> </ul>
+2
View File
@@ -25,7 +25,9 @@
<div class="mapdiv"> <div class="mapdiv">
{{ mapdiv|safe }} {{ mapdiv|safe }}
<script>
{{ mapscript|safe }} {{ mapscript|safe }}
</script>
</div> </div>
-25
View File
@@ -101,11 +101,6 @@
<i class="fas fa-dumbbell fa-fw"></i>&nbsp;Force Curve <i class="fas fa-dumbbell fa-fw"></i>&nbsp;Force Curve
</a> </a>
</li> </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 %} {% endif %}
</ul> </ul>
</li> </li>
@@ -294,26 +289,6 @@
<label for="group-advanced">Advanced</label> <label for="group-advanced">Advanced</label>
<ul> <ul>
{% if workout|water %} {% 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 %} {% if 'speedcoach2' in workout.workoutsource or 'nklinklogbook' in workout.workoutsource %}
<li id="advanced-usegps"> <li id="advanced-usegps">
<a href="/rowers/workout/{{ workout.id|encode }}/otwusegps/"> <a href="/rowers/workout/{{ workout.id|encode }}/otwusegps/">
+3 -5
View File
@@ -6,12 +6,9 @@
{% block main %} {% block main %}
<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>
{{ interactiveplot |safe }}
<h1>Interactive Comparison</h1> <h1>Interactive Comparison</h1>
@@ -20,6 +17,7 @@
<li class="grid_4"> <li class="grid_4">
<div> <div>
{{ the_div|safe }} {{ the_div|safe }}
{{ interactiveplot |safe }}
</div> </div>
</li> </li>
-1
View File
@@ -3,7 +3,6 @@
<div class="mapdiv"> <div class="mapdiv">
{{ mapdiv|safe }} {{ mapdiv|safe }}
{{ mapscript|safe }} {{ mapscript|safe }}
</div> </div>
</li> </li>
+18 -14
View File
@@ -8,6 +8,15 @@
<script type='text/javascript' <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> </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> <script>
function submit_form() { function submit_form() {
@@ -25,11 +34,14 @@
dataType: 'json', dataType: 'json',
success: function(data) { success: function(data) {
console.log(data.div);
// var parsedJSON = $.parseJSON(data); // // var parsedJSON = $.parseJSON(data); //
$("#id_script").replaceWith('<div id="id_script">'+data.script+'</d'+'iv>'); $("#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").replaceWith('<div id="id_chart">'+data.div+'</d'+'iv>');
$("#id_chart").append(data.script)
$("#endfitness").html(data.endfitness) $("#endfitness").html(data.endfitness)
$("#endfatigue").html(data.endfatigue) $("#endfatigue").html(data.endfatigue)
$("#endform").html(data.endform) $("#endform").html(data.endform)
@@ -59,19 +71,6 @@
}); });
}); });
</script> </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"> <ul class="main-content">
<canvas hidden id="canvas"></canvas>
<li class="grid_4"> <li class="grid_4">
<div id="id_chart"> <div id="id_chart">
{{ the_div|safe }} {{ the_div|safe }}
</div> </div>
</li> </li>
<li class="grid_1"> <li class="grid_1">
<form id="performanceform" enctype="multipart/form-data" method="post"> <form id="performanceform" enctype="multipart/form-data" method="post">
@@ -188,6 +189,9 @@
</li> </li>
</ul> </ul>
<div id="id_script">
{{ chartscript |safe }}
</div>
{% endblock %} {% 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>
<li class="grid_2"> <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"> <script async="true" type="text/javascript">
Bokeh.set_log_level("info"); Bokeh.set_log_level("info");
</script> </script>
+3 -6
View File
@@ -89,14 +89,11 @@
</p> </p>
</li> </li>
<li class="grid_2"> <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>
{{ interactiveplot |safe }}
{{ the_div |safe }} {{ the_div |safe }}
{{ interactiveplot |safe }}
</li> </li>
<li class="grid_2"> <li class="grid_2">
<h1>Feeling lucky?</h1> <h1>Feeling lucky?</h1>
+5 -8
View File
@@ -6,25 +6,22 @@
{% block main %} {% block main %}
<script src="https://cdn.pydata.org/bokeh/release/bokeh-3.1.1.min.js"></script> <script src="https://code.jquery.com/jquery-1.9.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>
<h1>Training Plan - {{ plan.name }}</h1> <h1>Training Plan - {{ plan.name }}</h1>
{{ the_script | safe }}
<ul class="main-content"> <ul class="main-content">
<li class="grid_4"> <li class="grid_4">
<div id="theplot" class="flexplot">
{{ the_div|safe }} {{ the_div|safe }}
</div>
</li> </li>
</ul> </ul>
<script src="https://d3js.org/d3.v6.js"></script>
{{ the_script | safe }}
{% endblock %} {% endblock %}
{% block scripts %} {% block scripts %}
+3 -5
View File
@@ -11,17 +11,15 @@
<script async="true" type="text/javascript"> <script async="true" type="text/javascript">
Bokeh.set_log_level("info"); Bokeh.set_log_level("info");
</script> </script>
<div id="id_js_res">
<script src="https://d3js.org/d3.v7.min.js"></script>
</div>
<h1>Training Zones</h1> <h1>Training Zones</h1>
<div id="id_script"> <div id="id_script">
</div> </div>
<ul class="main-content"> <ul class="main-content">
<li class="grid_4"> <li class="grid_4">
<div id="id_chart"> <div id="id_chart">
+2 -19
View File
@@ -14,6 +14,7 @@
} }
</script> </script>
<script src="https://code.jquery.com/jquery-1.9.1.min.js"></script> <script src="https://code.jquery.com/jquery-1.9.1.min.js"></script>
<script src="https://d3js.org/d3.v6.js"></script>
<script> <script>
$(function() { $(function() {
@@ -387,29 +388,11 @@
</script> </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 id="id_script">
</div> </div>
<ul class="main-content"> <ul class="main-content">
<canvas hidden id="canvas"></canvas>
<li class="grid_4"> <li class="grid_4">
<div id="id_chart"> <div id="id_chart">
{{ the_div|safe }} {{ the_div|safe }}
+2
View File
@@ -67,7 +67,9 @@
<div class="mapdiv"> <div class="mapdiv">
{{ gmapdiv |safe }} {{ gmapdiv |safe }}
<script>
{{ gmap |safe }} {{ gmap |safe }}
</script>
</div> </div>
</li> </li>
</ul> </ul>
+3 -6
View File
@@ -98,14 +98,11 @@
</li> </li>
{% endif %} {% endif %}
<li class="grid_2"> <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>
{{ interactiveplot |safe }}
{{ the_div|safe }} {{ the_div|safe }}
{{ interactiveplot |safe }}
</li> </li>
<li class="grid_4"> <li class="grid_4">
+2 -5
View File
@@ -16,10 +16,6 @@
{% block meta %} {% block meta %}
{% leaflet_js %} {% leaflet_js %}
{% leaflet_css %} {% 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 %} {% for chart in charts %}
{{ chart.script |safe }} {{ chart.script |safe }}
@@ -51,7 +47,7 @@
"<a href=\"/rowers/workout/"+workoutid+"/flexchart?favoritechart="+counter+"\">"+element.div+"</a>"+ "<a href=\"/rowers/workout/"+workoutid+"/flexchart?favoritechart="+counter+"\">"+element.div+"</a>"+
"</li>"); "</li>");
$("#id_thumbscripts").append(element.script); $("#id_thumbs").append(element.script);
counter += 1; counter += 1;
}); });
}); });
@@ -64,6 +60,7 @@
{% block main %} {% block main %}
<script src="https://d3js.org/d3.v6.js"></script>
<p> <p>
{% if workout|previousworkout:rower.user %} {% if workout|previousworkout:rower.user %}
<a href="/rowers/workout/{{ workout|previousworkout:rower.user }}/workflow" <a href="/rowers/workout/{{ workout|previousworkout:rower.user }}/workflow"
+2 -2
View File
@@ -217,9 +217,9 @@
<div class="mapdiv"> <div class="mapdiv">
{{ mapdiv|safe }} {{ mapdiv|safe }}
</div> </div>
<script>
{{ mapscript|safe }} {{ mapscript|safe }}
</script>
</li> </li>
{% endif %} {% endif %}
{% for graph in graphs %} {% for graph in graphs %}
+4 -9
View File
@@ -131,21 +131,16 @@
{{ mapdiv|safe }} {{ mapdiv|safe }}
<script>
{{ mapscript|safe }} {{ mapscript|safe }}
</script>
</div> </div>
</li> </li>
{% endif %} {% endif %}
<li class="grid_2"> <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>
{{ interactiveplot |safe }}
{{ the_div|safe }} {{ the_div|safe }}
{{ interactiveplot |safe }}
</li> </li>
{% for graph in graphs %} {% for graph in graphs %}
<li> <li>
+18 -6
View File
@@ -38,6 +38,7 @@ from nose.tools import assert_true
from mock import Mock, patch from mock import Mock, patch
#from minimocktest import MockTestCase #from minimocktest import MockTestCase
import pandas as pd import pandas as pd
import polars as pl
import arrow import arrow
from django.http import HttpResponseRedirect from django.http import HttpResponseRedirect
@@ -271,7 +272,7 @@ def mocked_fetchcperg(*args, **kwargs):
return df return df
import pandas as pd
def mocked_read_df_sql(id): # pragma: no cover def mocked_read_df_sql(id): # pragma: no cover
df = pd.read_csv('rowers/tests/testdata/fake_strokedata.csv') df = pd.read_csv('rowers/tests/testdata/fake_strokedata.csv')
@@ -300,7 +301,7 @@ def mocked_getrowdata_db(*args, **kwargs):
return df,row return df,row
def mocked_getrowdata_uh(*args, **kwargs): # pragma: no cover 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'] id = kwargs['id']
@@ -308,13 +309,19 @@ def mocked_getrowdata_uh(*args, **kwargs): # pragma: no cover
return df, row return df, row
def mocked_getsmallrowdata_uh(*args, **kwargs): # pragma: no cover 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') df = pd.read_csv('rowers/tests/testdata/uhfull.csv')
return df return df
def mocked_getsmallrowdata_forfusion(*args, **kwargs): 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 return df
@@ -335,6 +342,11 @@ def mocked_getsmallrowdata_db(*args, **kwargs):
return df 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 def mocked_getsmallrowdata_db_updatecp(*args, **kwargs): # pragma: no cover
df = pd.read_csv('rowers/tests/testdata/colsfromdb.csv') 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 return df
def mocked_getsmallrowdata_db_wps(*args, **kwargs): # pragma: no cover def mocked_read_data_wps(*args, **kwargs): # pragma: no cover
df = pd.read_csv('rowers/tests/testdata/driveenergies.csv') df = pl.read_csv('rowers/tests/testdata/driveenergies.csv')
return df return df
@@ -384,7 +396,7 @@ def mocked_read_cols_df_sql(*args, **kwargs):
def mock_workout_summaries(*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 return df
def mocked_read_df_cols_sql_multi(ids, columns, convertnewtons=True): # pragma: no cover 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 mock import Mock, patch
#from minimocktest import MockTestCase #from minimocktest import MockTestCase
import pandas as pd import pandas as pd
import polars as pl
import rowers.c2stuff as c2stuff import rowers.c2stuff as c2stuff
import rowers.rojabo_stuff as rojabo_stuff import rowers.rojabo_stuff as rojabo_stuff
+46 -76
View File
@@ -43,10 +43,10 @@ class ListWorkoutTest(TestCase):
pass pass
@patch('rowers.dataprep.create_engine') @patch('rowers.dataprep.create_engine')
@patch('rowers.dataprep.getsmallrowdata_db') @patch('rowers.dataprep.read_data')
@patch('rowers.dataprep.myqueue') @patch('rowers.dataprep.myqueue')
def test_list_workouts(self, mocked_sqlalchemy, def test_list_workouts(self, mocked_sqlalchemy,
mocked_getsmallrowdata_db, mocked_read_data,
mocked_myqueue): mocked_myqueue):
login = self.c.login(username=self.u.username, password=self.password) login = self.c.login(username=self.u.username, password=self.password)
@@ -169,8 +169,8 @@ class ForcecurveTest(TestCase):
pass pass
@patch('rowers.dataprep.getsmallrowdata_db',side_effect = mocked_getempowerdata_db) @patch('rowers.dataprep.read_data',side_effect = mocked_read_data)
def test_forcecurve_plot(self, mocked_getsmallrowdata_db): def test_forcecurve_plot(self, mocked_read_data):
login = self.c.login(username=self.u.username, password = self.password) login = self.c.login(username=self.u.username, password = self.password)
self.assertTrue(login) self.assertTrue(login)
@@ -296,9 +296,9 @@ class WorkoutCompareTestNew(TestCase):
pass pass
@patch('rowers.dataprep.create_engine') @patch('rowers.dataprep.create_engine')
@patch('rowers.dataprep.getsmallrowdata_db') @patch('rowers.dataprep.read_data')
def test_workouts_compare(self, mocked_sqlalchemy, def test_workouts_compare(self, mocked_sqlalchemy,
mocked_getsmallrowdata_db): mocked_read_data):
login = self.c.login(username=self.u.username, password=self.password) login = self.c.login(username=self.u.username, password=self.password)
self.assertTrue(login) self.assertTrue(login)
@@ -309,9 +309,9 @@ class WorkoutCompareTestNew(TestCase):
self.assertEqual(response.status_code,200) self.assertEqual(response.status_code,200)
@patch('rowers.dataprep.create_engine') @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, 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) login = self.c.login(username=self.u.username,password=self.password)
self.assertTrue(login) self.assertTrue(login)
@@ -398,9 +398,9 @@ class WorkoutBoxPlotTestNew(TestCase):
pass pass
@patch('rowers.dataprep.create_engine') @patch('rowers.dataprep.create_engine')
@patch('rowers.dataprep.getsmallrowdata_db') @patch('rowers.dataprep.read_data')
def test_workouts_boxplot(self, mocked_sqlalchemy, def test_workouts_boxplot(self, mocked_sqlalchemy,
mocked_getsmallrowdata_db): mocked_read_data):
login = self.c.login(username=self.u.username, password=self.password) login = self.c.login(username=self.u.username, password=self.password)
self.assertTrue(login) self.assertTrue(login)
@@ -411,9 +411,9 @@ class WorkoutBoxPlotTestNew(TestCase):
self.assertEqual(response.status_code,200) self.assertEqual(response.status_code,200)
@patch('rowers.dataprep.create_engine') @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, 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) login = self.c.login(username=self.u.username,password=self.password)
self.assertTrue(login) self.assertTrue(login)
@@ -499,9 +499,9 @@ class WorkoutHistoTestNew(TestCase):
pass pass
@patch('rowers.dataprep.create_engine') @patch('rowers.dataprep.create_engine')
@patch('rowers.dataprep.getsmallrowdata_db') @patch('rowers.dataprep.read_data')
def test_workouts_histo(self, mocked_sqlalchemy, def test_workouts_histo(self, mocked_sqlalchemy,
mocked_getsmallrowdata_db): mocked_read_data):
login = self.c.login(username=self.u.username, password=self.password) login = self.c.login(username=self.u.username, password=self.password)
self.assertTrue(login) self.assertTrue(login)
@@ -512,9 +512,9 @@ class WorkoutHistoTestNew(TestCase):
self.assertEqual(response.status_code,200) self.assertEqual(response.status_code,200)
@patch('rowers.dataprep.create_engine') @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, 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) login = self.c.login(username=self.u.username,password=self.password)
self.assertTrue(login) self.assertTrue(login)
@@ -600,9 +600,9 @@ class History(TestCase):
pass pass
@patch('rowers.dataprep.create_engine') @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, def test_workouts_history(self, mocked_sqlalchemy,
mocked_getsmallrowdata_db): mocked_read_data):
login = self.c.login(username=self.u.username, password=self.password) login = self.c.login(username=self.u.username, password=self.password)
self.assertTrue(login) self.assertTrue(login)
@@ -618,9 +618,9 @@ class History(TestCase):
@patch('rowers.dataprep.create_engine') @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, 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) login = self.c.login(username=self.u.username,password=self.password)
self.assertTrue(login) self.assertTrue(login)
@@ -713,9 +713,9 @@ class GoldMedalScores(TestCase):
pass pass
@patch('rowers.dataprep.create_engine') @patch('rowers.dataprep.create_engine')
@patch('rowers.dataprep.getsmallrowdata_db') @patch('rowers.dataprep.read_data')
def test_workouts_goldmedalscores(self, mocked_sqlalchemy, def test_workouts_goldmedalscores(self, mocked_sqlalchemy,
mocked_getsmallrowdata_db): mocked_read_data):
ws = Workout.objects.filter(rankingpiece=True) ws = Workout.objects.filter(rankingpiece=True)
self.assertEqual(ws.count(),2) self.assertEqual(ws.count(),2)
@@ -729,9 +729,9 @@ class GoldMedalScores(TestCase):
self.assertEqual(response.status_code,200) self.assertEqual(response.status_code,200)
@patch('rowers.dataprep.create_engine') @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, 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) login = self.c.login(username=self.u.username,password=self.password)
self.assertTrue(login) self.assertTrue(login)
@@ -795,9 +795,9 @@ class WorkoutFlexallTestNew(TestCase):
pass pass
@patch('rowers.dataprep.create_engine') @patch('rowers.dataprep.create_engine')
@patch('rowers.dataprep.getsmallrowdata_db') @patch('rowers.dataprep.read_data')
def test_workouts_flexall(self, mocked_sqlalchemy, def test_workouts_flexall(self, mocked_sqlalchemy,
mocked_getsmallrowdata_db): mocked_read_data):
login = self.c.login(username=self.u.username, password=self.password) login = self.c.login(username=self.u.username, password=self.password)
self.assertTrue(login) self.assertTrue(login)
@@ -808,9 +808,9 @@ class WorkoutFlexallTestNew(TestCase):
self.assertEqual(response.status_code,200) self.assertEqual(response.status_code,200)
@patch('rowers.dataprep.create_engine') @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, 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) login = self.c.login(username=self.u.username,password=self.password)
self.assertTrue(login) self.assertTrue(login)
@@ -903,9 +903,9 @@ class WorkoutStatsTestNew(TestCase):
pass pass
@patch('rowers.dataprep.create_engine') @patch('rowers.dataprep.create_engine')
@patch('rowers.dataprep.getsmallrowdata_db') @patch('rowers.dataprep.read_data')
def test_workouts_stats(self, mocked_sqlalchemy, def test_workouts_stats(self, mocked_sqlalchemy,
mocked_getsmallrowdata_db): mocked_read_data):
login = self.c.login(username=self.u.username, password=self.password) login = self.c.login(username=self.u.username, password=self.password)
self.assertTrue(login) self.assertTrue(login)
@@ -916,11 +916,11 @@ class WorkoutStatsTestNew(TestCase):
self.assertEqual(response.status_code,200) self.assertEqual(response.status_code,200)
@patch('rowers.dataprep.create_engine') @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) @patch('rowers.dataprep.read_cols_df_sql', side_effect=mocked_read_cols_df_sql)
def test_analysis_data(self, def test_analysis_data(self,
mocked_sqlalchemy, mocked_sqlalchemy,
mocked_getsmallrowdata_db, mocked_read_data,
mocked_read_cols_df_sql, mocked_read_cols_df_sql,
): ):
@@ -979,11 +979,11 @@ class WorkoutStatsTestNew(TestCase):
script, div = comparisondata(workouts,options) script, div = comparisondata(workouts,options)
@patch('rowers.dataprep.create_engine') @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) @patch('rowers.dataprep.read_cols_df_sql', side_effect=mocked_read_cols_df_sql)
def test_analysis_data2(self, def test_analysis_data2(self,
mocked_sqlalchemy, mocked_sqlalchemy,
mocked_getsmallrowdata_db, mocked_read_data,
mocked_read_cols_df_sql, mocked_read_cols_df_sql,
): ):
@@ -1042,11 +1042,11 @@ class WorkoutStatsTestNew(TestCase):
@patch('rowers.dataprep.create_engine') @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) @patch('rowers.dataprep.read_cols_df_sql', side_effect=mocked_read_cols_df_sql)
def test_analysis_data2(self, def test_analysis_data2(self,
mocked_sqlalchemy, mocked_sqlalchemy,
mocked_getsmallrowdata_db, mocked_read_data,
mocked_read_cols_df_sql, mocked_read_cols_df_sql,
): ):
@@ -1120,9 +1120,9 @@ class WorkoutStatsTestNew(TestCase):
@patch('rowers.dataprep.create_engine') @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, 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) login = self.c.login(username=self.u.username,password=self.password)
self.assertTrue(login) self.assertTrue(login)
@@ -1241,9 +1241,9 @@ class MarkerPerformanceTest(TestCase):
pass pass
@patch('rowers.dataprep.create_engine') @patch('rowers.dataprep.create_engine')
@patch('rowers.dataprep.getsmallrowdata_db') @patch('rowers.dataprep.read_data')
def test_create_marker_workouts(self, mocked_sqlalchemy, 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) login = self.c.login(username=self.u.username,password=self.password)
self.assertTrue(login) self.assertTrue(login)
@@ -1257,8 +1257,8 @@ class MarkerPerformanceTest(TestCase):
self.assertRedirects(response, expected_url=expected_url, status_code=302,target_status_code=200) self.assertRedirects(response, expected_url=expected_url, status_code=302,target_status_code=200)
@patch('rowers.dataprep.getsmallrowdata_db', side_effect=mocked_getsmallrowdata_uh) @patch('rowers.dataprep.read_data', side_effect=mocked_getsmallrowdata_uh)
def test_trainingzones_view(self,mocked_getsmallrowdata_db): def test_trainingzones_view(self,mocked_getsmallrowdata_uh):
login = self.c.login(username=self.u.username,password=self.password) login = self.c.login(username=self.u.username,password=self.password)
self.assertTrue(login) self.assertTrue(login)
@@ -1326,9 +1326,9 @@ class MarkerPerformanceTest(TestCase):
@patch('rowers.dataprep.create_engine') @patch('rowers.dataprep.create_engine')
@patch('rowers.dataprep.getsmallrowdata_db') @patch('rowers.dataprep.read_data')
def test_performancemanager_view(self, mocked_sqlalchemy, def test_performancemanager_view(self, mocked_sqlalchemy,
mocked_getsmallrowdata_db): mocked_read_data):
login = self.c.login(username=self.u.username,password=self.password) login = self.c.login(username=self.u.username,password=self.password)
self.assertTrue(login) self.assertTrue(login)
@@ -1356,36 +1356,6 @@ class MarkerPerformanceTest(TestCase):
response = self.c.post(url,form_data) response = self.c.post(url,form_data)
self.assertEqual(response.status_code,200) 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): class AlertTest(TestCase):
def setUp(self): def setUp(self):
@@ -1443,9 +1413,9 @@ class AlertTest(TestCase):
pass pass
@patch('rowers.dataprep.create_engine') @patch('rowers.dataprep.create_engine')
@patch('rowers.alerts.getsmallrowdata_db') @patch('rowers.alerts.read_data')
def test_alerts(self, mocked_sqlalchemy, def test_alerts(self, mocked_sqlalchemy,
mocked_getsmallrowdata_db): mocked_read_data):
login = self.c.login(username=self.u.username,password=self.password) login = self.c.login(username=self.u.username,password=self.password)
self.assertTrue(login) self.assertTrue(login)
+11 -5
View File
@@ -58,6 +58,8 @@ class OwnApi(TestCase):
response = self.c.get(url) response = self.c.get(url)
self.assertEqual(response.status_code,200) 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')
url = reverse('strokedatajson',kwargs={'id':w.id}) url = reverse('strokedatajson',kwargs={'id':w.id})
request = self.factory.get(url) request = self.factory.get(url)
@@ -67,10 +69,11 @@ class OwnApi(TestCase):
self.assertEqual(response.status_code,200) self.assertEqual(response.status_code,200)
# response must be json # response must be json
strokedata = json.loads(response.content)
df = pd.DataFrame(strokedata)
self.assertFalse(df.empty) strokedata = json.loads(response.content)
df = pl.from_dict(strokedata)
self.assertFalse(df.is_empty())
form_data = { form_data = {
"distance": [23, 46, 48], "distance": [23, 46, 48],
@@ -119,13 +122,16 @@ class OwnApi(TestCase):
request = self.factory.get(url) request = self.factory.get(url)
request.user = self.u request.user = self.u
force_authenticate(request, user=self.u) force_authenticate(request, user=self.u)
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) response = strokedatajson_v2(request,id=w.id)
self.assertEqual(response.status_code,200) self.assertEqual(response.status_code,200)
# response must be json # response must be json
strokedata = json.loads(response.content) strokedata = json.loads(response.content)
df = pd.DataFrame(strokedata) df = pl.from_dicts(strokedata['data'])
self.assertFalse(df.empty)
self.assertFalse(df.is_empty())
form_data = { 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) res = tasks.handle_c2_import_stroke_data(c2token,c2id,workoutid,starttimeunix,csvfilename)
self.assertEqual(res,1) 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') @patch('rowers.dataprep.read_data')
def test_handle_updatecp(self,mocked_getsmallrowdata_db_updatecp): def test_handle_update_wps(self,mocked_read_data_wps):
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):
ids = [1,2,3] ids = [1,2,3]
result = tasks.handle_update_wps(self.r.id,['water'],ids,mode='water') result = tasks.handle_update_wps(self.r.id,['water'],ids,mode='water')
+33 -178
View File
@@ -62,10 +62,10 @@ class ListWorkoutTest(TestCase):
pass pass
@patch('rowers.dataprep.create_engine') @patch('rowers.dataprep.create_engine')
@patch('rowers.dataprep.getsmallrowdata_db') @patch('rowers.dataprep.read_data')
@patch('rowers.dataprep.myqueue') @patch('rowers.dataprep.myqueue')
def test_list_workouts(self, mocked_sqlalchemy, def test_list_workouts(self, mocked_sqlalchemy,
mocked_getsmallrowdata_db, mocked_read_data,
mocked_myqueue): mocked_myqueue):
login = self.c.login(username=self.u.username, password=self.password) login = self.c.login(username=self.u.username, password=self.password)
@@ -87,10 +87,10 @@ class ListWorkoutTest(TestCase):
self.assertEqual(response.status_code,200) self.assertEqual(response.status_code,200)
@patch('rowers.dataprep.create_engine') @patch('rowers.dataprep.create_engine')
@patch('rowers.dataprep.getsmallrowdata_db') @patch('rowers.dataprep.read_data')
@patch('rowers.dataprep.myqueue') @patch('rowers.dataprep.myqueue')
def test_bulk_workouts(self, mocked_sqlalchemy, def test_bulk_workouts(self, mocked_sqlalchemy,
mocked_getsmallrowdata_db, mocked_read_data,
mocked_myqueue): mocked_myqueue):
login = self.c.login(username=self.u.username, password=self.password) 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.create_engine')
@patch('rowers.dataprep.getsmallrowdata_db') @patch('rowers.dataprep.read_data')
@patch('rowers.middleware.myqueue') @patch('rowers.middleware.myqueue')
def test_forcecurve(self, mocked_sqlalchemy, mocked_getsmallrowdata_db, def test_forcecurve(self, mocked_sqlalchemy, mocked_read_data,
mocked_myqueue): mocked_myqueue):
login = self.c.login(username=self.u.username, password=self.password) login = self.c.login(username=self.u.username, password=self.password)
self.assertTrue(login) self.assertTrue(login)
@@ -255,9 +255,9 @@ class WorkoutViewTest(TestCase):
self.assertEqual(response.status_code,200) self.assertEqual(response.status_code,200)
@patch('rowers.dataprep.create_engine') @patch('rowers.dataprep.create_engine')
@patch('rowers.dataprep.getsmallrowdata_db') @patch('rowers.dataprep.read_data')
@patch('rowers.middleware.myqueue') @patch('rowers.middleware.myqueue')
def test_resample(self, mocked_sqlalchemy, mocked_getsmallrowdata_db, def test_resample(self, mocked_sqlalchemy, mocked_read_data,
mocked_myqueue): mocked_myqueue):
login = self.c.login(username=self.u.username, password=self.password) login = self.c.login(username=self.u.username, password=self.password)
self.assertTrue(login) self.assertTrue(login)
@@ -283,9 +283,9 @@ class WorkoutViewTest(TestCase):
@patch('rowers.dataprep.create_engine') @patch('rowers.dataprep.create_engine')
@patch('rowers.dataprep.getsmallrowdata_db') @patch('rowers.dataprep.read_data')
@pytest.mark.django_db(transaction=True) @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(): with transaction.atomic():
login = self.c.login(username=self.u.username, password=self.password) login = self.c.login(username=self.u.username, password=self.password)
self.assertTrue(login) self.assertTrue(login)
@@ -350,10 +350,10 @@ class WorkoutViewTest(TestCase):
@patch('rowers.dataprep.create_engine') @patch('rowers.dataprep.create_engine')
@patch('rowers.dataprep.getsmallrowdata_db') @patch('rowers.dataprep.read_data')
@patch('rowers.weather.requests.get', side_effect=mocked_requests) @patch('rowers.weather.requests.get', side_effect=mocked_requests)
def test_waterworkout_view(self, 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) login = self.c.login(username=self.u.username, password=self.password)
self.assertTrue(login) self.assertTrue(login)
@@ -362,22 +362,6 @@ class WorkoutViewTest(TestCase):
response = self.c.get(url) response = self.c.get(url)
self.assertEqual(response.status_code,200) 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 # Stacked Flex Chart
url = reverse('workout_flexchart_stacked_view',kwargs={ url = reverse('workout_flexchart_stacked_view',kwargs={
@@ -422,8 +406,8 @@ class WorkoutViewTest(TestCase):
self.assertEqual(response.status_code,200) self.assertEqual(response.status_code,200)
@patch('rowers.dataprep.create_engine') @patch('rowers.dataprep.create_engine')
@patch('rowers.dataprep.getsmallrowdata_db') @patch('rowers.dataprep.read_data')
def test_smoothen(self, mocked_sqlalchemy, mocked_getsmallrowdata_db): def test_smoothen(self, mocked_sqlalchemy, mocked_read_data):
login = self.c.login(username=self.u.username, password=self.password) login = self.c.login(username=self.u.username, password=self.password)
self.assertTrue(login) self.assertTrue(login)
@@ -459,125 +443,12 @@ class WorkoutViewTest(TestCase):
status_code=302,target_status_code=200) 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.create_engine')
@patch('rowers.dataprep.getsmallrowdata_db') @patch('rowers.dataprep.read_data')
@patch('rowers.middleware.myqueue') def test_commentview(self, mocked_sqlalchemy, mocked_read_data):
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):
login = self.c.login(username=self.u.username, password=self.password) login = self.c.login(username=self.u.username, password=self.password)
self.assertTrue(login) self.assertTrue(login)
@@ -602,8 +473,8 @@ class WorkoutViewTest(TestCase):
self.assertEqual(response.status_code,200) self.assertEqual(response.status_code,200)
@patch('rowers.dataprep.create_engine') @patch('rowers.dataprep.create_engine')
@patch('rowers.dataprep.getsmallrowdata_db') @patch('rowers.dataprep.read_data')
def test_mapview(self, mocked_sqlalchemy, mocked_getsmallrowdata_db): def test_mapview(self, mocked_sqlalchemy, mocked_read_data):
login = self.c.login(username=self.u.username, password=self.password) login = self.c.login(username=self.u.username, password=self.password)
self.assertTrue(login) self.assertTrue(login)
@@ -614,9 +485,9 @@ class WorkoutViewTest(TestCase):
self.assertEqual(response.status_code,200) self.assertEqual(response.status_code,200)
@patch('rowers.dataprep.create_engine') @patch('rowers.dataprep.create_engine')
@patch('rowers.dataprep.getsmallrowdata_db') @patch('rowers.dataprep.read_data')
def notworking_test_workout_image(self, mocked_sqlalchemy, 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) login = self.c.login(username=self.u.username, password=self.password)
self.assertTrue(login) self.assertTrue(login)
@@ -642,10 +513,10 @@ class WorkoutViewTest(TestCase):
status_code=302,target_status_code=200) status_code=302,target_status_code=200)
@patch('rowers.dataprep.create_engine') @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) @patch('rowers.dataprep.getrowdata_db',side_effect=mocked_getrowdata_db)
@pytest.mark.django_db(transaction=True) @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): mocked_getrowdata_db):
with transaction.atomic(): with transaction.atomic():
login = self.c.login(username=self.u.username, password=self.password) 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.create_engine')
@patch('rowers.dataprep.getrowdata_db',side_effect=mocked_getrowdata_db) @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, 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) login = self.c.login(username=self.u.username, password=self.password)
self.assertTrue(login) self.assertTrue(login)
@@ -699,27 +570,11 @@ class WorkoutViewTest(TestCase):
self.assertEqual(response.status_code,200) 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.create_engine')
@patch('rowers.dataprep.getsmallrowdata_db') @patch('rowers.dataprep.read_data')
@patch('rowers.dataprep.get_video_data',side_effect=mocked_videodata) @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): mocked_videodata):
login = self.c.login(username=self.u.username, password=self.password) login = self.c.login(username=self.u.username, password=self.password)
self.assertTrue(login) self.assertTrue(login)
@@ -779,9 +634,9 @@ class WorkoutViewTest(TestCase):
self.assertRedirects(response,expected_url=expected_url,status_code=302,target_status_code=200) self.assertRedirects(response,expected_url=expected_url,status_code=302,target_status_code=200)
@patch('rowers.dataprep.create_engine') @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) @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): mocked_videodata):
login = self.c.login(username=self.u.username, password=self.password) login = self.c.login(username=self.u.username, password=self.password)
self.assertTrue(login) self.assertTrue(login)
@@ -843,7 +698,7 @@ class WorkoutViewTest(TestCase):
@patch('rowers.dataprep.create_engine') @patch('rowers.dataprep.create_engine')
@patch('rowers.dataprep.getrowdata_db',side_effect=mocked_getrowdata_db) @patch('rowers.dataprep.getrowdata_db',side_effect=mocked_getrowdata_db)
@patch('rowers.dataprep.get_video_data',side_effect=mocked_videodata) @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): mocked_videodata):
login = self.c.login(username=self.u.username, password=self.password) login = self.c.login(username=self.u.username, password=self.password)
self.assertTrue(login) self.assertTrue(login)
@@ -874,8 +729,8 @@ class WorkoutViewTest(TestCase):
@patch('rowers.dataprep.create_engine') @patch('rowers.dataprep.create_engine')
@patch('rowers.dataprep.getsmallrowdata_db') @patch('rowers.dataprep.read_data')
def test_editsummaryview(self, mocked_sqlalchemy, mocked_getsmallrowdata_db): def test_editsummaryview(self, mocked_sqlalchemy, mocked_read_data):
login = self.c.login(username=self.u.username, password=self.password) login = self.c.login(username=self.u.username, password=self.password)
self.assertTrue(login) self.assertTrue(login)
@@ -940,8 +795,8 @@ class WorkoutViewTest(TestCase):
@patch('rowers.dataprep.create_engine') @patch('rowers.dataprep.create_engine')
@patch('rowers.dataprep.getsmallrowdata_db') @patch('rowers.dataprep.read_data')
def test_workout_delete(self, mocked_sqlalchemy, mocked_getsmallrowdata_db): def test_workout_delete(self, mocked_sqlalchemy, mocked_read_data):
login = self.c.login(username=self.u.username, password=self.password) login = self.c.login(username=self.u.username, password=self.password)
self.assertTrue(login) self.assertTrue(login)
-62
View File
@@ -166,65 +166,3 @@ class CPChartTest(TestCase):
self.assertEqual(response.status_code,200) 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.create_engine')
@patch('rowers.dataprep.getsmallrowdata_db',side_effect=mocked_getsmallrowdata_db) @patch('rowers.dataprep.read_data',side_effect=mocked_read_data)
def test_uploadapi(self,mocked_sqlalchemy,mocked_getsmallrowdata_db): def test_uploadapi(self,mocked_sqlalchemy,mocked_read_data):
form_data = { form_data = {
'title': 'test', 'title': 'test',
'workouttype':'rower', 'workouttype':'rower',
@@ -108,8 +108,8 @@ class EmailUpload(TestCase):
@patch('rowers.dataprep.create_engine') @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_uploadapi2(self,mocked_sqlalchemy,mocked_getsmallrowdata_db): def test_uploadapi2(self,mocked_sqlalchemy,mocked_read_data):
form_data = {"secret": settings.UPLOAD_SERVICE_SECRET, form_data = {"secret": settings.UPLOAD_SERVICE_SECRET,
"user": 1, "user": 1,
"file": 'media/mailbox_attachments/colin3.csv', "file": 'media/mailbox_attachments/colin3.csv',
@@ -140,8 +140,8 @@ class EmailUpload(TestCase):
@patch('rowers.dataprep.create_engine') @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_uploadapi3(self,mocked_sqlalchemy,mocked_getsmallrowdata_db): def test_uploadapi3(self,mocked_sqlalchemy,mocked_read_data):
with transaction.atomic(): with transaction.atomic():
form_data = { form_data = {
'title': 'test', 'title': 'test',
@@ -167,8 +167,8 @@ class EmailUpload(TestCase):
self.assertEqual(w.notes,'aap noot mies') self.assertEqual(w.notes,'aap noot mies')
@patch('rowers.dataprep.create_engine') @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_uploadapi_credentials(self,mocked_sqlalchemy,mocked_getsmallrowdata_db): def test_uploadapi_credentials(self,mocked_sqlalchemy,mocked_read_data):
form_data = { form_data = {
'title': 'test', 'title': 'test',
'workouttype':'rower', 'workouttype':'rower',
+4 -4
View File
@@ -103,8 +103,8 @@ class WorkoutViewTest(TestCase):
pass pass
@patch('rowers.dataprep.create_engine') @patch('rowers.dataprep.create_engine')
@patch('rowers.dataprep.getsmallrowdata_db') @patch('rowers.dataprep.read_data')
def test_flexchart_water(self, mocked_sqlalechemy, mocked_getsmallrowdata_db): def test_flexchart_water(self, mocked_sqlalechemy, mocked_read_data):
login = self.c.login(username=self.u.username, password=self.password) login = self.c.login(username=self.u.username, password=self.password)
self.assertTrue(login) self.assertTrue(login)
@@ -151,8 +151,8 @@ class WorkoutViewTest(TestCase):
self.assertEqual(response.status_code,200) self.assertEqual(response.status_code,200)
@patch('rowers.dataprep.create_engine') @patch('rowers.dataprep.create_engine')
@patch('rowers.dataprep.getsmallrowdata_db') @patch('rowers.dataprep.read_data')
def test_flexchart_erg(self, mocked_sqlalechemy, mocked_getsmallrowdata_db): def test_flexchart_erg(self, mocked_sqlalechemy, mocked_read_data):
login = self.c.login(username=self.u.username, password=self.password) login = self.c.login(username=self.u.username, password=self.password)
self.assertTrue(login) self.assertTrue(login)
+13 -13
View File
@@ -720,8 +720,8 @@ class NKObjects(DjangoTestCase):
) )
@patch('rowers.dataprep.create_engine') @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_nk_intervals(self,mocked_sqlalchemy,mocked_getsmallrowdata_db): def test_nk_intervals(self,mocked_sqlalchemy,mocked_read_data):
with open('rowers/tests/testdata/carlos_workout.json','r') as f: with open('rowers/tests/testdata/carlos_workout.json','r') as f:
workoutdata = json.load(f) workoutdata = json.load(f)
with open('rowers/tests/testdata/carlos_strokes.json','r') as 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.get', side_effect=mocked_requests)
@patch('rowers.integrations.nk.requests.post', 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.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, def test_nk_import(self, mock_get, mock_post,
mocked_session, mocked_session,
mocked_getsmallrowdata_db): mocked_read_data):
integration = NKIntegration(self.u) integration = NKIntegration(self.u)
result = integration.token_refresh() 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.get', side_effect=mocked_requests)
@patch('rowers.integrations.nk.requests.post', 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.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, def test_nk_import_task(self, mock_get, mock_post,
mocked_session, mocked_session,
mocked_getsmallrowdata_db): mocked_read_data):
alldata = { alldata = {
469: {'elapsedTime': 3901900, 469: {'elapsedTime': 3901900,
'totalDistanceImp': 0, 'totalDistanceImp': 0,
@@ -904,10 +904,10 @@ class NKObjects(DjangoTestCase):
@patch('rowers.integrations.nk.requests.get', side_effect=mocked_requests) @patch('rowers.integrations.nk.requests.get', side_effect=mocked_requests)
@patch('rowers.integrations.nk.requests.post', 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.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, def test_nk_import_impeller(self, mock_get, mock_post,
mocked_session, mocked_session,
mocked_getsmallrowdata_db): mocked_read_data):
integration = NKIntegration(self.u) integration = NKIntegration(self.u)
result = integration.token_refresh() 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.get', side_effect=mocked_requests)
@patch('rowers.integrations.rp3.requests.post', 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, def test_rp3_import(self, mock_get, mockpost,
mocked_getsmallrowdata_db): mocked_read_data):
response = self.c.get('/rowers/workout/rp3import/591621',follow=True) 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.utils.requests.get', side_effect=mocked_requests)
@patch('rowers.integrations.strava.requests.post', 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, 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) response = self.c.get('/rowers/workout/stravaimport/12',follow=True)
expected_url = reverse('workout_import_view',kwargs={'source':'strava'}) expected_url = reverse('workout_import_view',kwargs={'source':'strava'})
@@ -1307,7 +1307,7 @@ class STObjects(DjangoTestCase):
self.r.sporttrackstoken = '12' 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.sporttrackstokenexpirydate = arrow.get(datetime.datetime.now()+datetime.timedelta(days=1)).datetime
self.r.save() self.r.save()
+16 -16
View File
@@ -51,16 +51,16 @@ class InteractiveChartTest(TestCase):
@patch('rowers.dataprep.create_engine') @patch('rowers.dataprep.create_engine')
@patch('rowers.dataprep.read_df_sql') @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, def test_interactive_chart1(self, mocked_sqlalchemy,mocked_read_df_sql,
mocked_getsmallrowdata_db): mocked_read_data):
res = iplots.interactive_chart(self.wote.id) res = iplots.interactive_chart(self.wote.id)
@patch('rowers.dataprep.create_engine') @patch('rowers.dataprep.create_engine')
@patch('rowers.dataprep.read_df_sql') @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, 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) res = iplots.interactive_chart(self.wote.id,promember=1)
@@ -80,52 +80,52 @@ class InteractiveChartTest(TestCase):
@patch('rowers.dataprep.create_engine') @patch('rowers.dataprep.create_engine')
@patch('rowers.dataprep.read_df_sql') @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, 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, res = iplots.interactive_flex_chart2(self.wote.id,self.r,promember=0,
xparam='time', xparam='time',
yparam1='pace',yparam2='spm',mode='water') yparam1='pace',yparam2='spm',mode='water')
@patch('rowers.dataprep.create_engine') @patch('rowers.dataprep.create_engine')
@patch('rowers.dataprep.read_df_sql') @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, 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, res = iplots.interactive_flex_chart2(self.wote.id,self.r,
promember=0,xparam='distance', promember=0,xparam='distance',
yparam1='pace',yparam2='spm',mode='water') yparam1='pace',yparam2='spm',mode='water')
@patch('rowers.dataprep.create_engine') @patch('rowers.dataprep.create_engine')
@patch('rowers.dataprep.read_df_sql') @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, 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, res = iplots.interactive_flex_chart2(self.wote.id,self.r,
promember=1,xparam='time', promember=1,xparam='time',
yparam1='pace',yparam2='hr',mode='water') yparam1='pace',yparam2='hr',mode='water')
@patch('rowers.dataprep.create_engine') @patch('rowers.dataprep.create_engine')
@patch('rowers.dataprep.read_df_sql') @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, 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, res = iplots.interactive_flex_chart2(self.wote.id,self.r,
promember=1,xparam='distance', promember=1,xparam='distance',
yparam1='pace',yparam2='hr',mode='water') yparam1='pace',yparam2='hr',mode='water')
@patch('rowers.dataprep.create_engine') @patch('rowers.dataprep.create_engine')
@patch('rowers.dataprep.read_df_sql') @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, 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, res = iplots.interactive_flex_chart2(self.wote.id,self.r,
promember=1,xparam='time', promember=1,xparam='time',
yparam1='pace',yparam2='spm',mode='water') yparam1='pace',yparam2='spm',mode='water')
@patch('rowers.dataprep.create_engine') @patch('rowers.dataprep.create_engine')
@patch('rowers.dataprep.read_df_sql') @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, 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, res = iplots.interactive_flex_chart2(self.wote.id,self.r,
promember=1, promember=1,
xparam='distance', xparam='distance',
+8 -8
View File
@@ -911,8 +911,8 @@ class PermissionsViewTests(TestCase):
## Coach can upload on behalf of athlete - if team allows ## Coach can upload on behalf of athlete - if team allows
@patch('rowers.dataprep.create_engine') @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_coach_edit_athlete_upload(self,mocked_sqlalchemy,mocked_getsmallrowdata_db): def test_coach_edit_athlete_upload(self,mocked_sqlalchemy,mocked_read_data):
self.rbasic.team.add(self.teamcoach) self.rbasic.team.add(self.teamcoach)
self.rbasic.coachinggroups.add(self.coachinggroup) self.rbasic.coachinggroups.add(self.coachinggroup)
@@ -957,8 +957,8 @@ class PermissionsViewTests(TestCase):
## Coach can upload on behalf of athlete - if team allows ## Coach can upload on behalf of athlete - if team allows
@patch('rowers.dataprep.create_engine') @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_coach_edit_athlete_uploadnot(self,mocked_sqlalchemy,mocked_getsmallrowdata_db): def test_coach_edit_athlete_uploadnot(self,mocked_sqlalchemy,mocked_read_data):
self.rbasic.team.add(self.teamcoach) self.rbasic.team.add(self.teamcoach)
login = self.c.login(username=self.ucoach.username, password=self.ucoachpassword) 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 ## Self Coach cannot upload on behalf of athlete
@patch('rowers.dataprep.create_engine') @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_plan_edit_athlete_upload(self,mocked_sqlalchemy,mocked_getsmallrowdata_db): def test_plan_edit_athlete_upload(self,mocked_sqlalchemy,mocked_read_data):
self.rpro.team.add(self.teamplan) self.rpro.team.add(self.teamplan)
login = self.c.login(username=self.uplan2.username, password=self.uplan2password) 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 ## Self Coach cannot upload on behalf of athlete
@patch('rowers.dataprep.create_engine') @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_plan_edit_athlete_upload(self,mocked_sqlalchemy,mocked_getsmallrowdata_db): def test_plan_edit_athlete_upload(self,mocked_sqlalchemy,mocked_read_data):
self.rpro.team.add(self.teamplan) self.rpro.team.add(self.teamplan)
login = self.c.login(username=self.uplan2.username, password=self.uplan2password) 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.integrations.c2.C2Integration.open')
@patch('rowers.dataprep.create_engine') @patch('rowers.dataprep.create_engine')
@patch('rowers.dataprep.read_df_sql') @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.get',side_effect=mocked_requests)
@patch('requests.post',side_effect=mocked_requests) @patch('requests.post',side_effect=mocked_requests)
@patch('rowers.dataprep.get_video_data',side_effect=mocked_get_video_data) @patch('rowers.dataprep.get_video_data',side_effect=mocked_get_video_data)
@@ -228,7 +228,7 @@ class PermissionsViewTests(TestCase):
mock_c2open, mock_c2open,
mocked_sqlalchemy, mocked_sqlalchemy,
mocked_read_df_sql, mocked_read_df_sql,
mocked_getsmallrowdata_db, mocked_read_data,
mock_get, mock_get,
mock_post, mock_post,
mocked_get_video_data, mocked_get_video_data,
@@ -253,7 +253,7 @@ class PermissionsViewTests(TestCase):
@patch('rowers.integrations.c2.C2Integration.open') @patch('rowers.integrations.c2.C2Integration.open')
@patch('rowers.dataprep.create_engine') @patch('rowers.dataprep.create_engine')
@patch('rowers.dataprep.read_df_sql') @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.get',side_effect=mocked_requests)
@patch('requests.post',side_effect=mocked_requests) @patch('requests.post',side_effect=mocked_requests)
@patch('rowers.dataprep.get_video_data',side_effect=mocked_get_video_data) @patch('rowers.dataprep.get_video_data',side_effect=mocked_get_video_data)
@@ -263,7 +263,7 @@ class PermissionsViewTests(TestCase):
mock_c2open, mock_c2open,
mocked_sqlalchemy, mocked_sqlalchemy,
mocked_read_df_sql, mocked_read_df_sql,
mocked_getsmallrowdata_db, mocked_read_data,
mock_get, mock_get,
mock_post, mock_post,
mocked_get_video_data, mocked_get_video_data,
@@ -335,7 +335,7 @@ class PermissionsViewTests(TestCase):
@patch('rowers.integrations.c2.C2Integration.open') @patch('rowers.integrations.c2.C2Integration.open')
@patch('rowers.dataprep.create_engine') @patch('rowers.dataprep.create_engine')
@patch('rowers.dataprep.read_df_sql') @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.get',side_effect=mocked_requests)
@patch('requests.post',side_effect=mocked_requests) @patch('requests.post',side_effect=mocked_requests)
@patch('rowers.dataprep.get_video_data',side_effect=mocked_get_video_data) @patch('rowers.dataprep.get_video_data',side_effect=mocked_get_video_data)
@@ -345,7 +345,7 @@ class PermissionsViewTests(TestCase):
mock_c2open, mock_c2open,
mocked_sqlalchemy, mocked_sqlalchemy,
mocked_read_df_sql, mocked_read_df_sql,
mocked_getsmallrowdata_db, mocked_read_data,
mock_get, mock_get,
mock_post, mock_post,
mocked_get_video_data, mocked_get_video_data,
@@ -418,7 +418,7 @@ class PermissionsViewTests(TestCase):
@patch('rowers.integrations.c2.C2Integration.open') @patch('rowers.integrations.c2.C2Integration.open')
@patch('rowers.dataprep.create_engine') @patch('rowers.dataprep.create_engine')
@patch('rowers.dataprep.read_df_sql') @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.get',side_effect=mocked_requests)
@patch('requests.post',side_effect=mocked_requests) @patch('requests.post',side_effect=mocked_requests)
@patch('rowers.dataprep.get_video_data',side_effect=mocked_get_video_data) @patch('rowers.dataprep.get_video_data',side_effect=mocked_get_video_data)
@@ -428,7 +428,7 @@ class PermissionsViewTests(TestCase):
mock_c2open, mock_c2open,
mocked_sqlalchemy, mocked_sqlalchemy,
mocked_read_df_sql, mocked_read_df_sql,
mocked_getsmallrowdata_db, mocked_read_data,
mock_get, mock_get,
mock_post, mock_post,
mocked_get_video_data, mocked_get_video_data,
+14 -12
View File
@@ -13,6 +13,7 @@ from django.db import transaction
nu = datetime.datetime.now() nu = datetime.datetime.now()
import datetime import datetime
import pytz import pytz
import polars as pl
# interactive plots # interactive plots
from rowers import interactiveplots from rowers import interactiveplots
@@ -581,7 +582,7 @@ class DataPrepTests(TestCase):
age = dataprep.calculate_age(born,today=today) age = dataprep.calculate_age(born,today=today)
self.assertEqual(age,49) 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): def test_goldmedalstandard(self,mocked_getsmallrowdata_uh):
maxvalue, delta = dataprep.calculate_goldmedalstandard(self.r,self.wuh_otw) maxvalue, delta = dataprep.calculate_goldmedalstandard(self.r,self.wuh_otw)
records = CalcAgePerformance.objects.filter( records = CalcAgePerformance.objects.filter(
@@ -596,8 +597,8 @@ class DataPrepTests(TestCase):
result = getagegrouprecord(25) result = getagegrouprecord(25)
self.assertEqual(int(result),590) self.assertEqual(int(result),590)
@patch('rowers.dataprep.getsmallrowdata_db',side_effect=mocked_getsmallrowdata_uh) @patch('rowers.dataprep.getsmallrowdata_pd',side_effect=mocked_getsmallrowdata_uh_pd)
def test_get_videodata(self,mocked_getsmallrowdata_uh): def test_get_videodata(self,mocked_getsmallrowdata_uh_pd):
data, metrics, maxtime = dataprep.get_video_data(self.wuh_otw) data, metrics, maxtime = dataprep.get_video_data(self.wuh_otw)
self.assertEqual(len(data),9) self.assertEqual(len(data),9)
@@ -659,7 +660,7 @@ class InteractivePlotTests(TestCase):
pass pass
def test_interactive_hr_piechart(self): 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,'') script, div = interactiveplots.interactive_hr_piechart(df, self.r,'')
self.assertFalse(len(script)==0) self.assertFalse(len(script)==0)
@@ -672,7 +673,8 @@ class InteractivePlotTests(TestCase):
self.assertFalse(len(div)==0) self.assertFalse(len(div)==0)
def test_interactive_boxchart(self): 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') script, div = interactiveplots.interactive_boxchart(df, 'spm')
@@ -685,12 +687,12 @@ class InteractivePlotTests(TestCase):
startdate = workouts[0].date-datetime.timedelta(days=5) startdate = workouts[0].date-datetime.timedelta(days=5)
enddate = workouts2[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(script)==0)
self.assertFalse(len(div)==0) self.assertFalse(len(div)==0)
def test_interactive_otwcpchart(self): 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') script, div, p1, ratio, message = interactiveplots.interactive_otwcpchart(df,r=self.r,cpfit='data')
self.assertFalse(len(script)==0) self.assertFalse(len(script)==0)
@@ -701,9 +703,9 @@ class InteractivePlotTests(TestCase):
self.assertFalse(len(div)==0) self.assertFalse(len(div)==0)
@patch('rowers.dataprep.create_engine') @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, 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] workout = Workout.objects.filter(user=self.r,workouttype__in=mytypes.rowtypes)[0]
id = workout.id id = workout.id
@@ -733,12 +735,12 @@ class InteractivePlotTests(TestCase):
self.assertFalse(len(div)==0) self.assertFalse(len(div)==0)
@patch('rowers.dataprep.create_engine') @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, 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] workout = Workout.objects.filter(user=self.r,workouttype__in=mytypes.rowtypes)[0]
id = workout.id 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(script)==0)
self.assertFalse(len(div)==0) self.assertFalse(len(div)==0)
+10 -7
View File
@@ -71,9 +71,10 @@ class ForceUnits(TestCase):
w = Workout.objects.get(id=1) w = Workout.objects.get(id=1)
self.assertEqual(w.forceunit,'lbs') 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()) average_N = int(df['averageforce'].mean())
self.assertEqual(average_N,398) self.assertEqual(average_N,400)
data = dataprep.read_df_sql(13) data = dataprep.read_df_sql(13)
average_N = int(data['averageforce'].mean()) average_N = int(data['averageforce'].mean())
@@ -119,9 +120,10 @@ class ForceUnits(TestCase):
w = Workout.objects.get(id=13) w = Workout.objects.get(id=13)
self.assertEqual(w.forceunit,'N') 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()) average_N = int(df['averageforce'].mean())
self.assertEqual(average_N,263) self.assertEqual(average_N,271)
def test_upload_speedcoach_colin(self): def test_upload_speedcoach_colin(self):
login = self.c.login(username=self.u.username, password=self.password) login = self.c.login(username=self.u.username, password=self.password)
@@ -155,9 +157,10 @@ class ForceUnits(TestCase):
w = Workout.objects.get(id=13) w = Workout.objects.get(id=13)
self.assertEqual(w.forceunit,'N') 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()) average_N = int(df['averageforce'].mean())
self.assertEqual(average_N,105) self.assertEqual(average_N,122)
@override_settings(TESTING=True) @override_settings(TESTING=True)
class TestForceUnit(TestCase): class TestForceUnit(TestCase):
@@ -206,7 +209,7 @@ class TestForceUnit(TestCase):
rowdata = dataprep.rdata('rowers/tests/testdata/PainsledForce.csv') rowdata = dataprep.rdata('rowers/tests/testdata/PainsledForce.csv')
df = dataprep.dataprep(rowdata.df) 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) # compute=False)
try: try:
average_N = int(df['averageforce'].mean()) average_N = int(df['averageforce'].mean())
+4 -8
View File
@@ -29,8 +29,8 @@ class ViewTest(TestCase):
self.nu = datetime.datetime.now() self.nu = datetime.datetime.now()
@patch('rowers.dataprep.create_engine') @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_upload_view_sled(self, mocked_sqlalchemy,mocked_getsmallrowdata_db): def test_upload_view_sled(self, mocked_sqlalchemy,mocked_read_data):
login = self.c.login(username='john',password='koeinsloot') login = self.c.login(username='john',password='koeinsloot')
self.assertTrue(login) self.assertTrue(login)
@@ -74,10 +74,6 @@ class ViewTest(TestCase):
self.assertEqual(response.status_code, 200) 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() f.close()
@@ -124,8 +120,8 @@ class ViewTest(TestCase):
pass pass
@patch('rowers.dataprep.create_engine') @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_upload_view_sled_bg(self, mocked_sqlalchemy,mocked_getsmallrowdata_db): def test_upload_view_sled_bg(self, mocked_sqlalchemy,mocked_read_data):
login = self.c.login(username='john',password='koeinsloot') login = self.c.login(username='john',password='koeinsloot')
self.assertTrue(login) self.assertTrue(login)
+2 -15
View File
@@ -79,11 +79,6 @@ class URLTests(TestCase):
'/rowers/502/', '/rowers/502/',
'/rowers/about/', '/rowers/about/',
'/rowers/workout/addmanual/', '/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/ajax_agegroup/45/hwt/male/1/',
'/rowers/analysis/', '/rowers/analysis/',
'/rowers/analysis/user/1/', '/rowers/analysis/user/1/',
@@ -122,8 +117,6 @@ class URLTests(TestCase):
'/rowers/me/workflowconfig2/', '/rowers/me/workflowconfig2/',
'/rowers/me/workflowconfig2/user/1/', '/rowers/me/workflowconfig2/user/1/',
'/rowers/me/workflowdefault/', '/rowers/me/workflowdefault/',
'/rowers/ote-bests2/',
'/rowers/ote-bests2/user/1/',
'/rowers/partners/', '/rowers/partners/',
'/rowers/physics/', '/rowers/physics/',
'/rowers/planrequired/', '/rowers/planrequired/',
@@ -148,29 +141,23 @@ class URLTests(TestCase):
'/rowers/workout/'+encoded1+'/addtimeplot/', '/rowers/workout/'+encoded1+'/addtimeplot/',
'/rowers/workout/'+encoded1+'/addtimeplot2/', '/rowers/workout/'+encoded1+'/addtimeplot2/',
'/rowers/workout/'+encoded1+'/comment/', '/rowers/workout/'+encoded1+'/comment/',
'/rowers/workout/'+encoded1+'/darkskywind/',
'/rowers/workout/'+encoded1+'/data/', '/rowers/workout/'+encoded1+'/data/',
'/rowers/workout/'+encoded1+'/edit/', '/rowers/workout/'+encoded1+'/edit/',
'/rowers/workout/'+encoded1+'/editintervals/', '/rowers/workout/'+encoded1+'/editintervals/',
'/rowers/workout/'+encoded1+'/flexchart/', '/rowers/workout/'+encoded1+'/flexchart/',
'/rowers/workout/'+encoded1+'/forcecurve/', '/rowers/workout/'+encoded1+'/forcecurve/',
'/rowers/workout/'+encoded1+'/get-thumbnails/', '/rowers/workout/'+encoded1+'/get-thumbnails/',
'/rowers/workout/'+encoded1+'/histo/',
'/rowers/workout/'+encoded1+'/image/', '/rowers/workout/'+encoded1+'/image/',
'/rowers/workout/'+encoded1+'/instroke/', '/rowers/workout/'+encoded1+'/instroke/',
'/rowers/workout/'+encoded1+'/interactiveotwplot/',
'/rowers/workout/'+encoded1+'/map/', '/rowers/workout/'+encoded1+'/map/',
'/rowers/workout/'+encoded1+'/otwsetpower/',
'/rowers/workout/'+encoded1+'/recalcsummary/', '/rowers/workout/'+encoded1+'/recalcsummary/',
'/rowers/workout/'+encoded1+'/restore/', '/rowers/workout/'+encoded1+'/restore/',
'/rowers/workout/'+encoded1+'/smoothenpace/', '/rowers/workout/'+encoded1+'/smoothenpace/',
'/rowers/workout/'+encoded1+'/split/', '/rowers/workout/'+encoded1+'/split/',
'/rowers/workout/'+encoded1+'/stats/', '/rowers/workout/'+encoded1+'/stats/',
'/rowers/workout/'+encoded1+'/stream/',
'/rowers/workout/'+encoded1+'/undosmoothenpace/', '/rowers/workout/'+encoded1+'/undosmoothenpace/',
'/rowers/workout/'+encoded1+'/unsubscribe/', '/rowers/workout/'+encoded1+'/unsubscribe/',
'/rowers/workout/'+encoded1+'/view/', '/rowers/workout/'+encoded1+'/view/',
'/rowers/workout/'+encoded1+'/wind/',
'/rowers/workout/'+encoded1+'/workflow/', '/rowers/workout/'+encoded1+'/workflow/',
'/rowers/workout/fusion/'+encoded1+'/', '/rowers/workout/fusion/'+encoded1+'/',
'/rowers/workout/upload/', '/rowers/workout/upload/',
@@ -206,7 +193,7 @@ class URLTests(TestCase):
@parameterized.expand(lijst) @parameterized.expand(lijst)
@patch('rowers.dataprep.create_engine') @patch('rowers.dataprep.create_engine')
@patch('rowers.dataprep.read_df_sql') @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.get',side_effect=mocked_requests)
@patch('requests.post',side_effect=mocked_requests) @patch('requests.post',side_effect=mocked_requests)
@patch('rowers.dataprep.get_video_data',side_effect=mocked_get_video_data) @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, def test_url_generator(self,url,expected,
mocked_sqlalchemy, mocked_sqlalchemy,
mocked_read_df_sql, mocked_read_df_sql,
mocked_getsmallrowdata_db, mocked_read_data,
mock_get, mock_get,
mock_post, mock_post,
mocked_get_video_data, mocked_get_video_data,
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 ,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, 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, 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, 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, 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, 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, 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, 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, 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, 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, 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, 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,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, 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, 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, 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, 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, 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, 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, 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, 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, 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, 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, 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, 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, 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, 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, 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, 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, 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, re_path(r'^exportallworkouts/?/$', views.workouts_summaries_email_view,
name='workouts_summaries_email_view'), name='workouts_summaries_email_view'),
path('failedjobs/', views.failed_queue_view, name='failed_queue_view'), path('failedjobs/', views.failed_queue_view, name='failed_queue_view'),
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'), path('failedjobs/empty/', views.failed_queue_empty, name='failed_queue_empty'),
re_path('^failedjobs/(?P<id>\w+.*)/$', re_path('^failedjobs/(?P<id>\w+.*)/$',
views.failed_job_view, name='failed_job_view'), views.failed_job_view, name='failed_job_view'),
re_path(r'^update_empower/$', views.rower_update_empower_view, re_path(r'^update_empower/$', views.rower_update_empower_view,
name='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+)/$', re_path(r'^ajax_agegroup/(?P<age>\d+)/(?P<weightcategory>\w+.*)/(?P<sex>\w+.*)/(?P<userid>\d+)/$',
views.ajax_agegrouprecords, name='ajax_agegrouprecords'), views.ajax_agegrouprecords, name='ajax_agegrouprecords'),
re_path(r'^agegrouprecords/(?P<sex>\w+.*)/(?P<weightcategory>\w+.*)/(?P<distance>\d+)m/$', 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"), views.trainingzones_view_data, name="trainingzones_view_data"),
re_path(r'^trainingzones/data/$', views.trainingzones_view_data, re_path(r'^trainingzones/data/$', views.trainingzones_view_data,
name="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, re_path(r'^analysisdata/user/(?P<userid>\d+)/$', views.analysis_view_data,
name='analysis_view_data'), name='analysis_view_data'),
re_path(r'^analysisdata/$', views.analysis_view_data, re_path(r'^analysisdata/$', views.analysis_view_data,
@@ -475,8 +466,6 @@ urlpatterns = [
views.workout_upload_view, name='workout_upload_view'), views.workout_upload_view, name='workout_upload_view'),
re_path(r'^workout/upload/$', views.workout_upload_view, re_path(r'^workout/upload/$', views.workout_upload_view,
name='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, re_path(r'^workout/(?P<id>\b[0-9A-Fa-f]+\b)/forcecurve/$', views.workout_forcecurve_view,
name='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, 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'), name='workout_erase_column_view'),
re_path(r'^workout/(?P<id>\b[0-9A-Fa-f]+\b)/zeropower-confirm/$', views.remove_power_confirm_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'), 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/$', re_path(r'^workout/(?P<id>\b[0-9A-Fa-f]+\b)/image/$',
views.workout_uploadimage_view, name='workout_uploadimage_view'), views.workout_uploadimage_view, name='workout_uploadimage_view'),
re_path(r'^virtualevent/(?P<id>\d+)/compare/$', re_path(r'^virtualevent/(?P<id>\d+)/compare/$',
@@ -543,8 +524,6 @@ urlpatterns = [
views.workout_downloadwind_view, name='workout_downloadwind_view'), views.workout_downloadwind_view, name='workout_downloadwind_view'),
re_path(r'^workout/(?P<id>\b[0-9A-Fa-f]+\b)/metar/(?P<airportcode>\w+)/$', re_path(r'^workout/(?P<id>\b[0-9A-Fa-f]+\b)/metar/(?P<airportcode>\w+)/$',
views.workout_downloadmetar_view, name='workout_downloadmetar_view'), 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, re_path(r'^workout/(?P<id>\b[0-9A-Fa-f]+\b)/editintervals/$', views.workout_summary_edit_view,
name='workout_summary_edit_view'), name='workout_summary_edit_view'),
re_path(r'^workout/(?P<id>\b[0-9A-Fa-f]+\b)/restore/$', 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 math
import numpy as np import numpy as np
import pandas as pd import pandas as pd
import polars as pl
from polars.exceptions import ColumnNotFoundError
import colorsys import colorsys
from django.conf import settings from django.conf import settings
import collections import collections
@@ -155,12 +157,7 @@ def get_call():
call1 = random.choice(coxes_calls) call1 = random.choice(coxes_calls)
call2 = random.choice(info_calls) call2 = random.choice(info_calls)
call = """<div id="id_sitready" class="successmessage"> call = """<div id="id_sitready" class="successmessage"><p>%s (%s)</p></div>""" % (call1, call2)
<p>
%s (%s)
</p>
</div>
""" % (call1, call2)
return call return call
@@ -347,22 +344,22 @@ def isbreakthrough(delta, cpvalues, p0, p1, p2, p3, ratio):
pwr *= ratio pwr *= ratio
delta = delta.astype(int, errors='ignore').values delta = delta.cast(pl.Int32)
cpvalues = cpvalues.astype(int, errors='ignore').values cpvalues = cpvalues.cast(pl.Int32)
pwr = pwr.astype(int, errors='ignore').values pwr = pwr.cast(pl.Int32)
res = np.sum(cpvalues > pwr+1) btdf = pl.DataFrame({
res2 = np.sum(cpvalues > pwr2+1) 'delta': delta,
'cpvalues': cpvalues,
'pwr': pwr,
'pwr2': pwr2
})
btdf = pd.DataFrame( res = btdf.select(pl.col("cpvalues")>pl.col("pwr")+1)['cpvalues'].sum()
{ res2 = btdf.select(pl.col("cpvalues")> pl.col("pwr2")+1)['cpvalues'].sum()
'delta': delta[cpvalues > pwr],
'cpvalues': cpvalues[cpvalues > pwr],
'pwr': pwr[cpvalues > pwr],
}
)
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 return res >= 1, btdf, res2 >= 1
@@ -444,17 +441,19 @@ def wavg(group, avg_name, weight_name):
""" """
try: try:
d = group[avg_name] d = group[avg_name]
except KeyError: except (KeyError, ColumnNotFoundError):
return 0 return 0
try: try:
w = group[weight_name] w = group[weight_name]
except KeyError: except (KeyError, ColumnNotFoundError):
return d.mean() return d.mean()
try: try:
return (d * w).sum() / w.sum() return (d * w).sum() / w.sum()
except ZeroDivisionError: # pragma: no cover except ZeroDivisionError: # pragma: no cover
return d.mean() return d.mean()
return 0
from string import Formatter from string import Formatter
def totaltime_sec_to_string(totaltime, shorten=False): def totaltime_sec_to_string(totaltime, shorten=False):
+67 -598
View File
@@ -296,7 +296,10 @@ def analysis_new(request,
df = cpdata(tw, options) df = cpdata(tw, options)
options['savedata'] = False options['savedata'] = False
request.session['options'] = options request.session['options'] = options
try:
response = HttpResponse(df.to_csv()) response = HttpResponse(df.to_csv())
except AttributeError:
response = HttpResponse(df.write_csv())
code = str(uuid4()) code = str(uuid4())
filename = code+'.csv' filename = code+'.csv'
chartform.fields['savedata'].initial = False chartform.fields['savedata'].initial = False
@@ -384,6 +387,9 @@ def trendflexdata(workouts, options, userid=0):
datadf = dataprep.filter_df(datadf, 'driveneergy', workmax, datadf = dataprep.filter_df(datadf, 'driveneergy', workmax,
largerthan=False) largerthan=False)
if yparam == 'power':
datadf = dataprep.filter_df(datadf, 'power', 1, largerthan=True)
datadf.dropna(axis=0, how='any', inplace=True) datadf.dropna(axis=0, how='any', inplace=True)
datemapping = { datemapping = {
@@ -472,8 +478,8 @@ def trendflexdata(workouts, options, userid=0):
'groupsize': groupsize, 'groupsize': groupsize,
}) })
if yparam == 'pace': #if yparam == 'pace':
df['y'] = dataprep.paceformatsecs(df['y']/1.0e3) # df['y'] = dataprep.paceformatsecs(df['y']/1.0e3)
aantal = len(df) aantal = len(df)
@@ -568,7 +574,7 @@ def flexalldata(workouts, options):
workstrokesonly = not includereststrokes workstrokesonly = not includereststrokes
columns = [xparam, yparam1, yparam2, 'spm', 'driveenergy', 'distance'] columns = [xparam, yparam1, yparam2, 'spm', 'driveenergy', 'distance']
ids = [int(w.id) for w in workouts] ids = [int(w.id) for w in workouts]
df = dataprep.getsmallrowdata_db(columns, ids=ids, df = dataprep.read_data(columns, ids=ids,
workstrokesonly=workstrokesonly, workstrokesonly=workstrokesonly,
doclean=True, doclean=True,
) )
@@ -609,7 +615,7 @@ def histodata(workouts, options):
if savedata: # pragma: no cover if savedata: # pragma: no cover
workstrokesonly = not includereststrokes workstrokesonly = not includereststrokes
ids = [int(w.id) for w in workouts] ids = [int(w.id) for w in workouts]
df = dataprep.getsmallrowdata_db([plotfield], ids=ids, df = dataprep.read_data([plotfield], ids=ids,
workstrokesonly=workstrokesonly, workstrokesonly=workstrokesonly,
doclean=True, doclean=True,
) )
@@ -625,6 +631,7 @@ def histodata(workouts, options):
def cpdata(workouts, options): def cpdata(workouts, options):
start = timezone.now()
userid = options['userid'] userid = options['userid']
cpfit = options['cpfit'] cpfit = options['cpfit']
cpoverlay = options['cpoverlay'] cpoverlay = options['cpoverlay']
@@ -635,7 +642,7 @@ def cpdata(workouts, options):
delta, cpvalue, avgpower, workoutnames, urls = dataprep.fetchcp_new( delta, cpvalue, avgpower, workoutnames, urls = dataprep.fetchcp_new(
r, workouts) r, workouts)
powerdf = pd.DataFrame({ powerdf = pl.DataFrame({
'Delta': delta, 'Delta': delta,
'CP': cpvalue, 'CP': cpvalue,
'workout': workoutnames, 'workout': workoutnames,
@@ -644,16 +651,17 @@ def cpdata(workouts, options):
savedata = options.get('savedata',False) savedata = options.get('savedata',False)
if savedata: # pragma: no cover 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>') return('', '<p>No valid data found</p>')
powerdf = powerdf[powerdf['CP'] > 0] powerdf = powerdf.lazy().filter(pl.col("CP")>0)
powerdf.dropna(axis=0, inplace=True) powerdf = powerdf.sort(["Delta", "CP"], descending=[False, True])
powerdf.sort_values(['Delta', 'CP'], ascending=[1, 0], inplace=True) powerdf = powerdf.unique(subset="Delta", keep="first")
powerdf.drop_duplicates(subset='Delta', keep='first', inplace=True) powerdf = powerdf.fill_nan(None).drop_nulls()
powerdf = powerdf.collect()
rowername = r.user.first_name+" "+r.user.last_name rowername = r.user.first_name+" "+r.user.last_name
@@ -880,7 +888,7 @@ def comparisondata(workouts, options):
'time', 'pace', 'workoutstate', 'time', 'pace', 'workoutstate',
'workoutid'] 'workoutid']
df = dataprep.getsmallrowdata_db(columns, ids=ids, df = dataprep.read_data(columns, ids=ids,
workstrokesonly=workstrokesonly, workstrokesonly=workstrokesonly,
doclean=True, doclean=True,
) )
@@ -923,28 +931,22 @@ def boxplotdata(workouts, options):
ids = [w.id for w in workouts] ids = [w.id for w in workouts]
# prepare data frame # 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, try:
largerthan=True) datadf = datadf.filter(
datadf = dataprep.filter_df(datadf, 'spm', spmmax, pl.col("spm")>spmmin,
largerthan=False) pl.col("spm")<spmmax,
datadf = dataprep.filter_df(datadf, 'driveenergy', workmin, pl.col("driveenergy")>workmin,
largerthan=True) pl.col("driveenergy")<workmax,
datadf = dataprep.filter_df(datadf, 'driveneergy', workmax, )
largerthan=False) 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 = datadf.with_columns((pl.col("workoutid").apply(lambda x: datemapping[x])).alias("date"))
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'])
if userid == 0: # pragma: no cover if userid == 0: # pragma: no cover
extratitle = '' extratitle = ''
@@ -954,7 +956,7 @@ def boxplotdata(workouts, options):
savedata = options.get('savedata',False) savedata = options.get('savedata',False)
if savedata: # pragma: no cover if savedata: # pragma: no cover
return datadf return datadf.to_pandas()
script, div = interactive_boxchart(datadf, plotfield, script, div = interactive_boxchart(datadf, plotfield,
extratitle=extratitle, extratitle=extratitle,
@@ -1149,9 +1151,6 @@ def trainingzones_view(request, userid=0):
form = TrainingZonesForm({ form = TrainingZonesForm({
'startdate': startdate, 'startdate': startdate,
'enddate': enddate, 'enddate': enddate,
'zones': zones,
'dates': date_agg,
'yaxis': yaxis,
}) })
if request.method == 'POST': # pragma: no cover if request.method == 'POST': # pragma: no cover
@@ -1160,9 +1159,6 @@ def trainingzones_view(request, userid=0):
if form.is_valid(): if form.is_valid():
startdate = form.cleaned_data['startdate'] startdate = form.cleaned_data['startdate']
enddate = form.cleaned_data['enddate'] 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': if date_agg == 'week':
startdate = startdate - datetime.timedelta(days=startdate.weekday()) 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") request.GET.get('enddate'), "%Y-%m-%d")
enddate = arrow.get(enddate).datetime enddate = arrow.get(enddate).datetime
data = get_zones_report(r, startdate, enddate, if (enddate-startdate).days > 200:
date_agg = "month"
data = get_zones_report_pl(r, startdate, enddate,
trainingzones=zones, date_agg=date_agg, yaxis=yaxis) trainingzones=zones, date_agg=date_agg, yaxis=yaxis)
script, div = interactive_zoneschart( script, div = interactive_zoneschart2(
r, data, startdate, enddate, trainingzones=zones, date_agg=date_agg, yaxis=yaxis) r, data, startdate, enddate, trainingzones=zones, date_agg=date_agg, yaxis=yaxis)
return JSONResponse({ return JSONResponse({
'script': script, 'script': script,
'div': div, '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', def agegrouprecordview(request, sex='male', weightcategory='hwt',
distance=2000, duration=None): distance=2000, duration=None):
@@ -2761,15 +2220,16 @@ def history_view_data(request, userid=0):
ids = [w.id for w in g_workouts] ids = [w.id for w in g_workouts]
columns = ['hr', 'power', 'time'] columns = ['hr', 'power', 'time', 'workoutstate', 'workoutid']
df = dataprep.read_data(columns, ids=ids)
df = dataprep.remove_nulls_pl(df)
df = getsmallrowdata_db(columns, ids=ids)
try: 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 except KeyError: # pragma: no cover
pass pass
df = dataprep.clean_df_stats(df, workstrokesonly=True,
ignoreadvanced=True, ignorehr=False)
totalmeters, totalhours, totalminutes, totalseconds = get_totals( totalmeters, totalhours, totalminutes, totalseconds = get_totals(
g_workouts) g_workouts)
@@ -2798,13 +2258,14 @@ def history_view_data(request, userid=0):
whours=whours, whours=whours,
wminutes=wminutes, wseconds=wseconds, 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: 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 except KeyError: # pragma: no cover
pass pass
ddf = dataprep.clean_df_stats(ddf, workstrokesonly=False, ddf = dataprep.clean_df_stats_pl(ddf, workstrokesonly=False,
ignoreadvanced=True) ignoreadvanced=True)
try: try:
@@ -2812,14 +2273,14 @@ def history_view_data(request, userid=0):
except (KeyError, ValueError, AttributeError): # pragma: no cover except (KeyError, ValueError, AttributeError): # pragma: no cover
ddict['hrmean'] = 0 ddict['hrmean'] = 0
try: try:
ddict['hrmax'] = ddf['hr'].max().astype(int) ddict['hrmax'] = int(ddf['hr'].max())
except (KeyError, ValueError, AttributeError): # pragma: no cover except (KeyError, ValueError, AttributeError): # pragma: no cover
ddict['hrmax'] = 0 ddict['hrmax'] = 0
ddict['powermean'] = int(wavg(ddf, 'power', 'deltat')) ddict['powermean'] = int(wavg(ddf, 'power', 'deltat'))
try: try:
ddict['powermax'] = ddf['power'].max().astype(int) ddict['powermax'] = int(ddf['power'].max())
except KeyError: # pragma: no cover except (KeyError, ColumnNotFoundError): # pragma: no cover
ddict['powermax'] = 0 ddict['powermax'] = 0
ddict['nrworkouts'] = a_workouts.count() ddict['nrworkouts'] = a_workouts.count()
listofdicts.append(ddict) listofdicts.append(ddict)
@@ -2833,14 +2294,14 @@ def history_view_data(request, userid=0):
totalsdict['distance'] = totalmeters totalsdict['distance'] = totalmeters
try: try:
totalsdict['powermean'] = int(wavg(df, 'power', 'deltat')) totalsdict['powermean'] = int(wavg(df, 'power', 'deltat'))
totalsdict['powermax'] = df['power'].max().astype(int) totalsdict['powermax'] = int(df['power'].max())
except KeyError: # pragma: no cover except (KeyError, ColumnNotFoundError): # pragma: no cover
totalsdict['powermean'] = 0 totalsdict['powermean'] = 0
totalsdict['powermax'] = 0 totalsdict['powermax'] = 0
try: try:
totalsdict['hrmean'] = int(wavg(df, 'hr', 'deltat')) totalsdict['hrmean'] = int(wavg(df, 'hr', 'deltat'))
totalsdict['hrmax'] = df['hr'].max().astype(int) totalsdict['hrmax'] = int(df['hr'].max())
except KeyError: # pragma: no cover except (KeyError, ColumnNotFoundError): # pragma: no cover
totalsdict['hrmean'] = 0 totalsdict['hrmean'] = 0
totalsdict['hrmax'] = 0 totalsdict['hrmax'] = 0
@@ -2859,14 +2320,22 @@ def history_view_data(request, userid=0):
a_workouts = g_workouts.filter(workouttype=typeselect) a_workouts = g_workouts.filter(workouttype=typeselect)
meters, hours, minutes, seconds = get_totals(a_workouts) meters, hours, minutes, seconds = get_totals(a_workouts)
totalseconds = 3600 * hours + 60 * minutes + seconds 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: 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: except KeyError:
pass 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) ignoreadvanced=True)
totalscript, totaldiv = interactive_hr_piechart( totalscript, totaldiv = interactive_hr_piechart(
+64 -61
View File
@@ -69,102 +69,102 @@ def strokedataform(request, id=0):
def api_get_dataframe(startdatetime, df): def api_get_dataframe(startdatetime, df):
try: try:
time = df['time']/1.e3 time = df['time']/1.e3
except KeyError: # pragma: no cover except (KeyError, ColumnNotFoundError): # pragma: no cover
try: try:
time = df['t']/10. time = df['t']/10.
except KeyError: except (KeyError, ColumnNotFoundError):
return 400, "Missing time", pd.DataFrame() return 400, "Missing time", pl.DataFrame()
try: try:
spm = df['spm'] spm = df['spm']
except KeyError: # pragma: no cover except (KeyError, ColumnNotFoundError): # pragma: no cover
return 400, "Missing spm", pd.DataFrame() return 400, "Missing spm", pl.DataFrame()
try: try:
distance = df['distance'] distance = df['distance']
except KeyError: # pragma: no cover except (KeyError, ColumnNotFoundError): # pragma: no cover
try: try:
distance = df['d']/10. distance = df['d']/10.
except KeyError: except (KeyError, ColumnNotFoundError):
return 400, "Missing distance", pd.DataFrame() return 400, "Missing distance", pl.DataFrame()
try: try:
pace = df['pace']/1.e3 pace = df['pace']/1.e3
except KeyError: # pragma: no cover except (KeyError, ColumnNotFoundError): # pragma: no cover
try: try:
pace = df['p']/10. pace = df['p']/10.
except KeyError: except (KeyError, ColumnNotFoundError):
return 400, "Missing pace", pd.DataFrame() return 400, "Missing pace", pl.DataFrame()
try: try:
power = df['power'] power = df['power']
except KeyError: # pragma: no cover except (KeyError, ColumnNotFoundError): # pragma: no cover
power = 0*time power = 0*time
try: try:
drivelength = df['drivelength'] drivelength = df['drivelength']
except KeyError: except (KeyError, ColumnNotFoundError):
drivelength = 0*time drivelength = 0*time
try: try:
dragfactor = df['dragfactor'] dragfactor = df['dragfactor']
except KeyError: except (KeyError, ColumnNotFoundError):
dragfactor = 0*time dragfactor = 0*time
try: try:
drivetime = df['drivetime'] drivetime = df['drivetime']
except KeyError: except (KeyError, ColumnNotFoundError):
drivetime = 0*time drivetime = 0*time
try: try:
strokerecoverytime = df['strokerecoverytime'] strokerecoverytime = df['strokerecoverytime']
except KeyError: except (KeyError, ColumnNotFoundError):
strokerecoverytime = 0*time strokerecoverytime = 0*time
try: try:
averagedriveforce = df['averagedriveforce'] averagedriveforce = df['averagedriveforce']
except KeyError: except (KeyError, ColumnNotFoundError):
averagedriveforce = 0*time averagedriveforce = 0*time
try: try:
peakdriveforce = df['peakdriveforce'] peakdriveforce = df['peakdriveforce']
except KeyError: except (KeyError, ColumnNotFoundError):
peakdriveforce = 0*time peakdriveforce = 0*time
try: try:
wash = df['wash'] wash = df['wash']
except KeyError: except (KeyError, ColumnNotFoundError):
wash = 0*time wash = 0*time
try: try:
catch = df['catch'] catch = df['catch']
except KeyError: except (KeyError, ColumnNotFoundError):
catch = 0*time catch = 0*time
try: try:
finish = df['finish'] finish = df['finish']
except KeyError: except (KeyError, ColumnNotFoundError):
finish = 0*time finish = 0*time
try: try:
peakforceangle = df['peakforceangle'] peakforceangle = df['peakforceangle']
except KeyError: except (KeyError, ColumnNotFoundError):
peakforceangle = 0*time peakforceangle = 0*time
try: try:
driveenergy = df['driveenergy'] driveenergy = df['driveenergy']
except KeyError: except (KeyError, ColumnNotFoundError):
driveenergy = 60.*power/spm driveenergy = 60.*power/spm
try: try:
slip = df['slip'] slip = df['slip']
except KeyError: except (KeyError, ColumnNotFoundError):
slip = 0*time slip = 0*time
try: try:
lapidx = df['lapidx'] lapidx = df['lapidx']
except KeyError: except (KeyError, ColumnNotFoundError):
lapidx = 0*time lapidx = 0*time
try: try:
hr = df['hr'] hr = df['hr']
except KeyError: # pragma: no cover except (KeyError, ColumnNotFoundError): # pragma: no cover
hr = 0*df['time'] hr = 0*df['time']
try: try:
latitude = df['latitude'] latitude = df['latitude']
except KeyError: except (KeyError, ColumnNotFoundError):
latitude = 0*df['time'] latitude = 0*df['time']
try: try:
longitude = df['longitude'] longitude = df['longitude']
except KeyError: except (KeyError, ColumnNotFoundError):
longitude = 0*df['time'] longitude = 0*df['time']
starttime = totimestamp(startdatetime)+time[0] starttime = totimestamp(startdatetime)+time[0]
@@ -172,7 +172,7 @@ def api_get_dataframe(startdatetime, df):
dologging('apilog.log',"(strokedatajson_v2/3 POST - data parsed)") dologging('apilog.log',"(strokedatajson_v2/3 POST - data parsed)")
data = pd.DataFrame({'TimeStamp (sec)': unixtime, data = pl.DataFrame({'TimeStamp (sec)': unixtime,
' Horizontal (meters)': distance, ' Horizontal (meters)': distance,
' Cadence (stokes/min)': spm, ' Cadence (stokes/min)': spm,
' HRCur (bpm)': hr, ' HRCur (bpm)': hr,
@@ -526,7 +526,7 @@ def strokedatajson_v3(request):
title = request.data.get('name','') title = request.data.get('name','')
try: try:
elapsedTime = request.data['elapsedTime'] elapsedTime = request.data['elapsedTime']
except KeyError: # pragma: no cover except (KeyError, ColumnNotFoundError): # pragma: no cover
try: try:
duration = request.data['duration'] duration = request.data['duration']
try: try:
@@ -538,7 +538,7 @@ def strokedatajson_v3(request):
return HttpResponse("Missing Elapsed Time", status=400) return HttpResponse("Missing Elapsed Time", status=400)
try: try:
totalDistance = request.data['distance'] totalDistance = request.data['distance']
except KeyError: # pragma: no cover except (KeyError, ColumnNotFoundError): # pragma: no cover
return HttpResponse("Missing Total Distance", status=400) return HttpResponse("Missing Total Distance", status=400)
timeZone = request.data.get('timezone','UTC') timeZone = request.data.get('timezone','UTC')
workouttype = request.data.get('workouttype','rower') workouttype = request.data.get('workouttype','rower')
@@ -556,23 +556,21 @@ def strokedatajson_v3(request):
dologging('apilog.log',totalDistance) dologging('apilog.log',totalDistance)
dologging('apilog.log',elapsedTime) dologging('apilog.log',elapsedTime)
df = pd.DataFrame() df = pl.DataFrame()
try: try:
strokes = request.data['strokes'] strokes = request.data['strokes']
except KeyError: # pragma: no cover except (KeyError, ColumnNotFoundError): # pragma: no cover
return HttpResponse("No Stroke Data in JSON", status=400) return HttpResponse("No Stroke Data in JSON", status=400)
try: try:
df = pd.DataFrame(strokes['data']) df = pl.DataFrame(strokes['data'])
except KeyError: # pragma: no cover except (KeyError, ColumnNotFoundError): # pragma: no cover
try: try:
df = pd.DataFrame(request.data['strokedata']) df = pl.DataFrame(request.data['strokedata'])
except: except:
return HttpResponse("No JSON Object could be decoded", status=400) return HttpResponse("No JSON Object could be decoded", status=400)
df.index = df.index.astype(int) df = df.sort("time")
df.sort_index(inplace=True)
status, comment, data = api_get_dataframe(startdatetime, df) status, comment, data = api_get_dataframe(startdatetime, df)
if status != 200: # pragma: no cover if status != 200: # pragma: no cover
@@ -580,7 +578,8 @@ def strokedatajson_v3(request):
csvfilename = 'media/{code}.csv.gz'.format(code=uuid4().hex[:16]) 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) duration = datetime.time(0,0,1)
w = Workout( w = Workout(
@@ -672,10 +671,11 @@ def strokedatajson_v2(request, id):
if request.method == 'GET': if request.method == 'GET':
columns = ['spm', 'time', 'hr', 'pace', 'power', 'distance'] 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)") 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 = json.loads(data)
data2 = {"data": data2} data2 = {"data": data2}
@@ -686,28 +686,27 @@ def strokedatajson_v2(request, id):
try: try:
for d in request.data['data']: for d in request.data['data']:
dologging('apilog.log',json.dumps(d)) dologging('apilog.log',json.dumps(d))
except KeyError: # pragma: no cover except (KeyError, ColumnNotFoundError): # pragma: no cover
try: try:
for d in request.data['strokedata']: for d in request.data['strokedata']:
dologging('apilog.log',json.dumps(d)) dologging('apilog.log',json.dumps(d))
except KeyError: except (KeyError, ColumnNotFoundError):
dologging('apilog.log','No data in request.data') dologging('apilog.log','No data in request.data')
checkdata, r = dataprep.getrowdata_db(id=row.id) checkdata, r = dataprep.getrowdata_db(id=row.id)
if not checkdata.empty: # pragma: no cover if not checkdata.empty: # pragma: no cover
return HttpResponse("Duplicate Error", status=409) return HttpResponse("Duplicate Error", status=409)
df = pd.DataFrame() df = pl.DataFrame()
try: try:
df = pd.DataFrame(request.data['data']) df = pl.DataFrame(request.data['data'])
except KeyError: # pragma: no cover except (KeyError, ColumnNotFoundError): # pragma: no cover
try: try:
df = pd.DataFrame(request.data['strokedata']) df = pl.DataFrame(request.data['strokedata'])
except: except:
return HttpResponse("No JSON object could be decoded", status=400) return HttpResponse("No JSON object could be decoded", status=400)
df.index = df.index.astype(int) df = df.sort("time")
df.sort_index(inplace=True)
status, comment, data = api_get_dataframe(row.startdatetime, df) status, comment, data = api_get_dataframe(row.startdatetime, df)
if status != 200: # pragma: no cover if status != 200: # pragma: no cover
@@ -729,7 +728,8 @@ def strokedatajson_v2(request, id):
row.duplicate = True row.duplicate = True
row.save() 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.csvfilename = csvfilename
row.save() row.save()
@@ -748,7 +748,7 @@ def strokedatajson_v2(request, id):
powerperc=powerperc, powerzones=r.powerzones) powerperc=powerperc, powerzones=r.powerzones)
rowdata = rdata(csvfile=row.csvfilename, rower=rr).df 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) rowdata, id=row.id, bands=True, barchart=True, otwpower=True, empower=True)
_ = myqueue(queuehigh, handle_calctrimp, row.id, _ = myqueue(queuehigh, handle_calctrimp, row.id,
@@ -802,7 +802,11 @@ def strokedatajson(request, id=0):
if request.method == 'GET': if request.method == 'GET':
# currently only returns a subset. # currently only returns a subset.
columns = ['spm', 'time', 'hr', 'pace', 'power', 'distance'] 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) ") dologging("apilog.log",request.user.username+"(strokedatajson GET) ")
return JSONResponse(datadf) return JSONResponse(datadf)
@@ -823,14 +827,13 @@ def strokedatajson(request, id=0):
return HttpResponse("No JSON object could be decoded", status=400) return HttpResponse("No JSON object could be decoded", status=400)
try: try:
df = pd.DataFrame(strokedata) df = pl.DataFrame(strokedata)
except ValueError: # pragma: no cover except ValueError: # pragma: no cover
return HttpResponse("Arrays must all be same length", status=400) return HttpResponse("Arrays must all be same length", status=400)
df.index = df.index.astype(int) df = df.sort("time")
df.sort_index(inplace=True)
try: try:
time = df['time']/1.e3 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) return HttpResponse("There must be time values", status=400)
aantal = len(time) aantal = len(time)
pace = df['pace']/1.e3 pace = df['pace']/1.e3
@@ -869,7 +872,7 @@ def strokedatajson(request, id=0):
dologging("apilog.log",request.user.username+"(POST)") dologging("apilog.log",request.user.username+"(POST)")
data = pd.DataFrame({'TimeStamp (sec)': unixtime, data = pl.DataFrame({'TimeStamp (sec)': unixtime,
' Horizontal (meters)': distance, ' Horizontal (meters)': distance,
' Cadence (stokes/min)': spm, ' Cadence (stokes/min)': spm,
' HRCur (bpm)': hr, ' HRCur (bpm)': hr,
@@ -896,8 +899,8 @@ def strokedatajson(request, id=0):
timestr = row.startdatetime.strftime("%Y%m%d-%H%M%S") timestr = row.startdatetime.strftime("%Y%m%d-%H%M%S")
csvfilename = 'media/Import_'+timestr+'.csv' csvfilename = 'media/Import_'+timestr+'.csv'
res = data.to_csv(csvfilename+'.gz', index_label='index', with gzip.open(csvfilename+'.gz','w') as f:
compression='gzip') res = data.write_csv(f)
row.csvfilename = csvfilename row.csvfilename = csvfilename
row.save() row.save()
@@ -916,7 +919,7 @@ def strokedatajson(request, id=0):
powerperc=powerperc, powerzones=r.powerzones) powerperc=powerperc, powerzones=r.powerzones)
rowdata = rdata(csvfile=row.csvfilename, rower=rr).df 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) rowdata, id=row.id, bands=True, barchart=True, otwpower=True, empower=True)
# mangling # mangling
+1 -1
View File
@@ -208,7 +208,7 @@ def workouts_summaries_email_view(request):
) )
df = dataprep.workout_summary_to_df( df = dataprep.workout_summary_to_df(
r, startdate=startdate, enddate=enddate) r, startdate=startdate, enddate=enddate)
df.to_csv(filename, encoding='utf-8') df.write_csv(filename)
_ = myqueue(queuehigh, handle_sendemailsummary, _ = myqueue(queuehigh, handle_sendemailsummary,
r.user.first_name, r.user.first_name,
r.user.last_name, r.user.last_name,
+23
View File
@@ -1,10 +1,33 @@
from rowers.views.statements import * from rowers.views.statements import *
from rowers.interactiveplots import sleep
from rq import Queue from rq import Queue
from redis import Redis from redis import Redis
from rq.job import Job 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() @login_required()
def download_fit(request, filename=''): def download_fit(request, filename=''):
+1 -1
View File
@@ -3569,7 +3569,7 @@ def rower_trainingplan_execution_view(request,
else: # pragma: no cover else: # pragma: no cover
data, message = get_execution_report(r, startdate, enddate) data, message = get_execution_report(r, startdate, enddate)
if not data.empty: if not data.is_empty():
script, div = interactive_planchart(data, startdate, enddate) script, div = interactive_planchart(data, startdate, enddate)
else: # pragma: no cover else: # pragma: no cover
script = '' script = ''
+8 -6
View File
@@ -16,7 +16,7 @@ from rowers.utils import (
from rowers.celery import result as celery_result from rowers.celery import result as celery_result
from rowers.interactiveplots import * from rowers.interactiveplots import *
from scipy.interpolate import griddata 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 rowers.dataprep import timedeltaconv
from scipy.special import lambertw from scipy.special import lambertw
from io import BytesIO from io import BytesIO
@@ -35,6 +35,7 @@ import threading
import redis import redis
import colorsys import colorsys
import re import re
import gzip
import zipfile import zipfile
import bleach import bleach
import arrow import arrow
@@ -136,9 +137,9 @@ from rowers.forms import (
FitnessMetricForm, PredictedPieceFormNoDistance, FitnessMetricForm, PredictedPieceFormNoDistance,
EmailForm, RegistrationForm, RegistrationFormTermsOfService, EmailForm, RegistrationForm, RegistrationFormTermsOfService,
RegistrationFormUniqueEmail, RegistrationFormSex, RegistrationFormUniqueEmail, RegistrationFormSex,
CNsummaryForm, UpdateWindForm, CNsummaryForm,
StandardsForm, StandardsForm,
UpdateStreamForm, WorkoutMultipleCompareForm, ChartParamChoiceForm, WorkoutMultipleCompareForm, ChartParamChoiceForm,
FusionMetricChoiceForm, BoxPlotChoiceForm, MultiFlexChoiceForm, FusionMetricChoiceForm, BoxPlotChoiceForm, MultiFlexChoiceForm,
TrendFlexModalForm, WorkoutSplitForm, WorkoutJoinParamForm, TrendFlexModalForm, WorkoutSplitForm, WorkoutJoinParamForm,
AnalysisOptionsForm, AnalysisChoiceForm, AnalysisOptionsForm, AnalysisChoiceForm,
@@ -205,6 +206,7 @@ from rowers.rojabo_stuff import rojabo_open
from rowers.integrations import * from rowers.integrations import *
from polars.exceptions import ColumnNotFoundError
import rowers.ownapistuff as ownapistuff import rowers.ownapistuff as ownapistuff
from rowers.ownapistuff import TEST_CLIENT_ID, TEST_CLIENT_SECRET, TEST_REDIRECT_URI from rowers.ownapistuff import TEST_CLIENT_ID, TEST_CLIENT_SECRET, TEST_REDIRECT_URI
@@ -251,7 +253,7 @@ from rowers.tasks import (
handle_sendemailfile, handle_sendemailfile,
handle_sendemailkml, handle_sendemailkml,
handle_sendemailnewresponse, handle_updatedps, handle_sendemailnewresponse, handle_updatedps,
handle_updatecp, long_test_task, long_test_task2, long_test_task, long_test_task2,
handle_zip_file, handle_getagegrouprecords, handle_zip_file, handle_getagegrouprecords,
handle_update_empower, handle_update_empower,
handle_sendemailics, handle_sendemailics,
@@ -1344,9 +1346,9 @@ def trydf(df, aantal, column): # pragma: no cover
s = df[column] s = df[column]
if len(s) != aantal: if len(s) != aantal:
return np.zeros(aantal) return np.zeros(aantal)
if not np.issubdtype(s, np.number): if not s.dtype in pl.NUMERIC_DTYPES:
return np.zeros(aantal) return np.zeros(aantal)
except KeyError: except (KeyError, ColumnNotFoundError):
s = np.zeros(aantal) s = np.zeros(aantal)
return s return s
+38 -494
View File
@@ -126,8 +126,7 @@ def workout_video_view_mini(request, id=''):
hascoordinates = pd.Series(data['latitude']).std() > 0 hascoordinates = pd.Series(data['latitude']).std() > 0
# create map # create map
if hascoordinates and mode == 'water': if hascoordinates and mode == 'water':
mapscript, mapdiv = leaflet_chart_video(data['latitude'], data['longitude'], mapscript, mapdiv = leaflet_chart(data['latitude'], data['longitude'])
w.name)
else: else:
mapscript, mapdiv = interactive_chart_video(data) mapscript, mapdiv = interactive_chart_video(data)
data['longitude'] = data['spm'] data['longitude'] = data['spm']
@@ -237,8 +236,7 @@ def workout_video_view(request, id=''):
hascoordinates = pd.Series(data['latitude']).std() > 0 hascoordinates = pd.Series(data['latitude']).std() > 0
# create map # create map
if hascoordinates and mode == 'water': if hascoordinates and mode == 'water':
mapscript, mapdiv = leaflet_chart_video(data['latitude'], data['longitude'], mapscript, mapdiv = leaflet_chart(data['latitude'], data['longitude'])
w.name)
else: else:
mapscript, mapdiv = interactive_chart_video(data) mapscript, mapdiv = interactive_chart_video(data)
data['longitude'] = data['spm'] data['longitude'] = data['spm']
@@ -349,12 +347,12 @@ def workout_video_create_view(request, id=0):
# get data # get data
data, metrics, maxtime = dataprep.get_video_data( data, metrics, maxtime = dataprep.get_video_data(
w, groups=metricsgroups, mode=mode) w, groups=metricsgroups, mode=mode)
hascoordinates = pd.Series(data['latitude']).std() > 0 hascoordinates = pd.Series(data['latitude']).std() > 0
# create map # create map
if hascoordinates and mode == 'water': if hascoordinates and mode == 'water':
mapscript, mapdiv = leaflet_chart_video(data['latitude'], data['longitude'], mapscript, mapdiv = leaflet_chart(data['latitude'], data['longitude'])
w.name)
else: else:
mapscript, mapdiv = interactive_chart_video(data) mapscript, mapdiv = interactive_chart_video(data)
data['longitude'] = data['spm'] data['longitude'] = data['spm']
@@ -380,6 +378,7 @@ def workout_video_create_view(request, id=0):
template = 'embedded_video.html' template = 'embedded_video.html'
return render(request, return render(request,
template, template,
{ {
@@ -429,12 +428,14 @@ def workout_forcecurve_view(request, id=0, analysis=0, userid=0, workstrokesonly
work_max = forceanalysis.work_max work_max = forceanalysis.work_max
notes = forceanalysis.notes notes = forceanalysis.notes
name = forceanalysis.name name = forceanalysis.name
plotcircles = forceanalysis.plotcircles
plotlines = forceanalysis.plotlines
includereststrokes = forceanalysis.include_rest_strokes includereststrokes = forceanalysis.include_rest_strokes
except (ForceCurveAnalysis.DoesNotExist, ValueError): except (ForceCurveAnalysis.DoesNotExist, ValueError):
pass pass
else: else:
dist_min = 0 dist_min = 0
dist_max = 0 dist_max = row.distance
spm_min = 15 spm_min = 15
spm_max = 55 spm_max = 55
work_min = 0 work_min = 0
@@ -442,6 +443,8 @@ def workout_forcecurve_view(request, id=0, analysis=0, userid=0, workstrokesonly
notes = '' notes = ''
includereststrokes = False includereststrokes = False
name = '' name = ''
plotcircles = True
plotlines = False
form = ForceCurveOptionsForm(initial={ form = ForceCurveOptionsForm(initial={
'spm_min': spm_min, '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_min': work_min,
'work_max': work_max, 'work_max': work_max,
'notes': notes, 'notes': notes,
'plottype': 'line',
'name': name, 'name': name,
'plotcircles': plotcircles,
'plotlines': plotlines,
}) })
plottype = 'line'
if request.method == 'POST': 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'] work_max = form.cleaned_data['work_max']
notes = form.cleaned_data['notes'] notes = form.cleaned_data['notes']
name = form.cleaned_data['name'] name = form.cleaned_data['name']
plotlines = form.cleaned_data['plotlines']
plotcircles = form.cleaned_data['plotcircles']
if not name: if not name:
name = row.name name = row.name
includereststrokes = form.cleaned_data['includereststrokes'] includereststrokes = form.cleaned_data['includereststrokes']
plottype = form.cleaned_data['plottype']
workstrokesonly = not includereststrokes workstrokesonly = not includereststrokes
if "_save" in request.POST and "new" not in request.POST: # pragma: no cover 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_min = spm_min,
spm_max = spm_max, spm_max = spm_max,
rower=row.user, rower=row.user,
plotlines = plotlines,
plotcircles = plotcircles,
include_rest_strokes = includereststrokes, include_rest_strokes = includereststrokes,
) )
else: else:
@@ -501,6 +507,8 @@ def workout_forcecurve_view(request, id=0, analysis=0, userid=0, workstrokesonly
forceanalysis.work_max = work_max forceanalysis.work_max = work_max
forceanalysis.spm_min = spm_min forceanalysis.spm_min = spm_min
forceanalysis.spm_max = spm_max forceanalysis.spm_max = spm_max
forceanalysis.plotcircles = plotcircles
forceanalysis.plotlines = plotlines
forceanalysis.include_rest_strokes = includereststrokes forceanalysis.include_rest_strokes = includereststrokes
forceanalysis.save() forceanalysis.save()
dosave = True dosave = True
@@ -526,20 +534,10 @@ def workout_forcecurve_view(request, id=0, analysis=0, userid=0, workstrokesonly
else: # pragma: no cover else: # pragma: no cover
workstrokesonly = True workstrokesonly = True
plottype = 'line'
script, div, js_resources, css_resources = interactive_forcecurve( script, div = interactive_forcecurve(
[row], [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,
) )
breadcrumbs = [ breadcrumbs = [
@@ -580,8 +578,6 @@ def workout_forcecurve_view(request, id=0, analysis=0, userid=0, workstrokesonly
'work_max': work_max, 'work_max': work_max,
'annotation': notes, 'annotation': notes,
'the_div': div, 'the_div': div,
'js_res': js_resources,
'css_res': css_resources,
'id': id, 'id': id,
'mayedit': mayedit, 'mayedit': mayedit,
'teams': get_my_teams(request.user), 'teams': get_my_teams(request.user),
@@ -633,50 +629,6 @@ def otw_use_gps(request, id=0):
return HttpResponseRedirect(url) 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 # add a workout manually
@login_required() @login_required()
@@ -1343,7 +1295,7 @@ def remove_power_view(request, id=0):
row = rdata(csvfile=f, rower=rr) row = rdata(csvfile=f, rower=rr)
row.df[' Power (watts)'] = 0 row.df[' Power (watts)'] = 0
row.write_csv(f) row.write_csv(f)
_ = dataprep.dataprep(row.df, id=workout.id) _ = dataprep.dataplep(row.df, id=workout.id)
cpdf, delta, cpvalues = dataprep.setcp(workout) cpdf, delta, cpvalues = dataprep.setcp(workout)
workout.normp = 0 workout.normp = 0
@@ -1667,15 +1619,10 @@ def course_compare_view(request, id=0):
except: # pragma: no cover except: # pragma: no cover
labeldict = {} labeldict = {}
res = interactive_multiple_compare_chart(workoutids, xparam, yparam, script, div = interactive_multiple_compare_chart(workoutids, xparam, yparam,
promember=promember, promember=promember,
plottype=plottype, plottype=plottype,
labeldict=labeldict, startenddict=startenddict) labeldict=labeldict, startenddict=startenddict)
script = res[0]
div = res[1]
errormessage = res[3]
if errormessage != '': # pragma: no cover
messages.error(request, errormessage)
breadcrumbs = [ breadcrumbs = [
{ {
@@ -1849,15 +1796,10 @@ def virtualevent_compare_view(request, id=0):
except: # pragma: no cover except: # pragma: no cover
labeldict = {} labeldict = {}
res = interactive_multiple_compare_chart(workoutids, xparam, yparam, script, div = interactive_multiple_compare_chart(workoutids, xparam, yparam,
promember=promember, promember=promember,
plottype=plottype, plottype=plottype,
labeldict=labeldict, startenddict=startenddict) labeldict=labeldict, startenddict=startenddict)
script = res[0]
div = res[1]
errormessage = res[3]
if errormessage != '': # pragma: no cover
messages.error(request, errormessage)
breadcrumbs = [ breadcrumbs = [
{ {
@@ -2237,10 +2179,6 @@ def workouts_view(request, message='', successmessage='',
for w in workoutsnohr: # pragma: no cover for w in workoutsnohr: # pragma: no cover
_ = dataprep.workout_trimp(w) _ = 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 if query: # pragma: no cover
query_list = query.split() query_list = query.split()
workouts = workouts.filter( workouts = workouts.filter(
@@ -2278,7 +2216,7 @@ def workouts_view(request, message='', successmessage='',
if yaxis not in ['duration', 'trimp', 'rscore']: # pragma: no cover if yaxis not in ['duration', 'trimp', 'rscore']: # pragma: no cover
yaxis = 'duration' yaxis = 'duration'
script, div = interactive_activitychart(g_workouts, script, div = interactive_activitychart2(g_workouts,
g_startdate, g_startdate,
g_enddate, g_enddate,
stack=stack, stack=stack,
@@ -2823,348 +2761,6 @@ def workout_downloadmetar_view(request, id=0,
return response 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) @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) return HttpResponseRedirect(url)
try: try:
data = dataprep.getsmallrowdata_db([column], ids=[w.id]) data = dataprep.read_data([column], ids=[w.id])
except: # pragma: no cover except: # pragma: no cover
messages.error(request, 'Invalid column') messages.error(request, 'Invalid column')
url = reverse('workout_data_view', kwargs={ 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, workout = dataprep.getrowdata(id=w.id)
row.df[columnl] = defaultvalue row.df[columnl] = defaultvalue
try:
os.remove(w.csvfilename+'.gz') os.remove(w.csvfilename+'.gz')
except FileNotFoundError:
try:
os.remove(w.csvfilename)
except FileNotFoundError:
pass
row.write_csv(w.csvfilename, gzip=True) row.write_csv(w.csvfilename, gzip=True)
row, workout = dataprep.getrowdata(id=w.id) row, workout = dataprep.getrowdata(id=w.id)
_ = dataprep.dataprep(row.df, id=w.id) _ = dataprep.dataplep(row.df, id=w.id)
if column == 'hr': if column == 'hr':
w.hrtss = 0 w.hrtss = 0
@@ -4141,7 +3743,7 @@ def workout_workflow_view(request, id):
if 'panel_map.html' in r.workflowmiddlepanel and rowhascoordinates(row): if 'panel_map.html' in r.workflowmiddlepanel and rowhascoordinates(row):
rowdata = rdata(csvfile=row.csvfilename) rowdata = rdata(csvfile=row.csvfilename)
mapscript, mapdiv = leaflet_chart2(rowdata.df[' latitude'], mapscript, mapdiv = leaflet_chart(rowdata.df[' latitude'],
rowdata.df[' longitude'], rowdata.df[' longitude'],
row.name) row.name)
else: else:
@@ -4370,7 +3972,7 @@ def workout_flexchart3_view(request, *args, **kwargs):
# create interactive plot # create interactive plot
( (
script, div, js_resources, css_resources, workstrokesonly script, div, workstrokesonly
) = interactive_flex_chart2( ) = interactive_flex_chart2(
encoder.decode_hex(id), request.user.rower, encoder.decode_hex(id), request.user.rower,
xparam=xparam, yparam1=yparam1, xparam=xparam, yparam1=yparam1,
@@ -4456,8 +4058,6 @@ def workout_flexchart3_view(request, *args, **kwargs):
'workout': row, 'workout': row,
'chartform': flexaxesform, 'chartform': flexaxesform,
'optionsform': flexoptionsform, 'optionsform': flexoptionsform,
'js_res': js_resources,
'css_res': css_resources,
'teams': get_my_teams(request.user), 'teams': get_my_teams(request.user),
'id': id, 'id': id,
'xparam': xparam, 'xparam': xparam,
@@ -4505,7 +4105,7 @@ def workout_flexchart_stacked_view(request, *args, **kwargs):
yparam4 = cd['yaxis4'] yparam4 = cd['yaxis4']
( (
script, div, js_resources, css_resources, comment script, div
) = interactive_flexchart_stacked( ) = interactive_flexchart_stacked(
encoder.decode_hex(id), r, xparam=xparam, encoder.decode_hex(id), r, xparam=xparam,
yparam1=yparam1, yparam1=yparam1,
@@ -4515,9 +4115,6 @@ def workout_flexchart_stacked_view(request, *args, **kwargs):
mode=workout.workouttype, mode=workout.workouttype,
) )
if comment is not None: # pragma: no cover
messages.error(request, comment)
initial = { initial = {
'xaxis': xparam, 'xaxis': xparam,
'yaxis1': yparam1, 'yaxis1': yparam1,
@@ -4554,8 +4151,6 @@ def workout_flexchart_stacked_view(request, *args, **kwargs):
'active': 'nav-workouts', 'active': 'nav-workouts',
'workout': workout, 'workout': workout,
'chartform': flexaxesform, 'chartform': flexaxesform,
'js_res': js_resources,
'css_res': css_resources,
'id': id, 'id': id,
'xparam': xparam, 'xparam': xparam,
'yparam1': yparam1, 'yparam1': yparam1,
@@ -4568,57 +4163,6 @@ def workout_flexchart_stacked_view(request, *args, **kwargs):
# The interactive plot with wind corrected pace for OTW outings # 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) row = get_workoutuser(id, request)
if request.user.rower.rowerplan == 'basic' and 'speedcoach2' in row.workoutsource: # pragma: no cover 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: try:
if data['wash'].std() != 0: if data['wash'].std() != 0:
url = reverse('paidplans_view') url = reverse('paidplans_view')
@@ -4793,7 +4337,7 @@ def workout_edit_view(request, id=0, message="", successmessage=""):
pass pass
if request.user.rower.rowerplan == 'basic' and 'nklinklogbook' in row.workoutsource: # pragma: no cover 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: try:
if data['wash'].std() != 0: if data['wash'].std() != 0:
url = reverse('paidplans_view') url = reverse('paidplans_view')
@@ -5063,7 +4607,7 @@ def workout_map_view(request, id=0):
hascoordinates = 0 hascoordinates = 0
if hascoordinates: if hascoordinates:
mapscript, mapdiv = leaflet_chart2(rowdata.df[' latitude'], mapscript, mapdiv = leaflet_chart(rowdata.df[' latitude'],
rowdata.df[' longitude'], rowdata.df[' longitude'],
w.name) w.name)
else: else:
+7
View File
@@ -599,6 +599,13 @@ except KeyError: # pragma: no cover
WORKOUTS_FIT_TOKEN = 'aapnootmies' WORKOUTS_FIT_TOKEN = 'aapnootmies'
WORKOUTS_FIT_URL = 'http://localhost:50053/tojson' 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 # Recaptcha
try: try:
+10
View File
@@ -32,6 +32,16 @@
color: red; 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 { .watermark {
position: absolute; position: absolute;