diff --git a/rowers/alerts.py b/rowers/alerts.py
index ef74ddf0..2beeca2e 100644
--- a/rowers/alerts.py
+++ b/rowers/alerts.py
@@ -1,6 +1,6 @@
from rowers.models import Alert, Condition, User, Rower, Workout
from rowers.teams import coach_getcoachees
-from rowers.dataprep import getsmallrowdata_db, getrowdata_db
+from rowers.dataprep import getrowdata_db, read_data, remove_nulls_pl
import datetime
import numpy as np
import math
@@ -101,8 +101,10 @@ def alert_get_stats(alert, nperiod=0): # pragma: no cover
ids = [w.id for w in workouts]
try:
- df = getsmallrowdata_db(columns, ids=ids, doclean=True,
+ df = getsmallrowdata_pd(columns, ids=ids, doclean=True,
workstrokesonly=workstrokesonly)
+ df.dropna(axis=1,how='all',inplace=True)
+ df.dropna(axis=0,how='all',inplace=True)
except:
return {
'workouts': workouts.count(),
diff --git a/rowers/courses.py b/rowers/courses.py
index d3eab241..060d15ae 100644
--- a/rowers/courses.py
+++ b/rowers/courses.py
@@ -1,5 +1,4 @@
from rowers.courseutils import coursetime_paths, coursetime_first, time_in_path
-import pandas as pd
from rowers.models import (
Rower, Workout,
GeoPoint, GeoPolygon, GeoCourse,
@@ -420,62 +419,6 @@ def createcourse(
return c
-def get_time_course(ws, course): # pragma: no cover
- coursetimeseconds = 0.0
- coursecompleted = False
-
- w = ws[0]
- columns = ['time', ' latitude', ' longitude', 'cum_dist']
- rowdata = dataprep.getsmallrowdata_db(
- columns,
- ids=[w.id],
- doclean=False,
- workstrokesonly=False
- )
-
- rowdata.rename(columns={
- ' latitude': 'latitude',
- ' longitude': 'longitude',
- }, inplace=True)
-
- rowdata['time'] = rowdata['time']/1000.
-
- rowdata.fillna(method='backfill', inplace=True)
-
- rowdata['time'] = rowdata['time']-rowdata.ix[0, 'time']
- # we may want to expand the time (interpolate)
- rowdata['dt'] = rowdata['time'].apply(
- lambda x: timedelta(seconds=x)
- )
- rowdata = rowdata.resample('100ms', on='dt').mean()
- rowdata = rowdata.interpolate()
-
- # create path
- polygons = GeoPolygon.objects.filter(
- course=course).order_by("order_in_course")
- paths = []
- for polygon in polygons:
- path = polygon_to_path(polygon)
- paths.append(path)
-
- (
- coursetimeseconds,
- coursemeters,
- coursecompleted,
-
- ) = coursetime_paths(rowdata, paths)
- (
- coursetimefirst,
- coursemetersfirst,
- firstcompleted
- ) = coursetime_first(
- rowdata, paths)
-
- coursetimeseconds = coursetimeseconds-coursetimefirst
- coursemeters = coursemeters-coursemetersfirst
-
- return coursetimeseconds, coursemeters, coursecompleted
-
def replacecourse(course1, course2):
ps = PlannedSession.objects.filter(course=course1)
diff --git a/rowers/dataprep.py b/rowers/dataprep.py
index cca3b186..4c8e94a9 100644
--- a/rowers/dataprep.py
+++ b/rowers/dataprep.py
@@ -8,6 +8,7 @@ from rowers.datautils import p0
from scipy import optimize
from rowers.utils import calculate_age
import datetime
+import gzip
from scipy.signal import savgol_filter
from rowers.opaque import encoder
from rowers.database import *
@@ -27,6 +28,8 @@ from fitparse import FitFile
import itertools
import numpy as np
import pandas as pd
+import polars as pl
+from polars.exceptions import ColumnNotFoundError
from zipfile import BadZipFile
import zipfile
import os
@@ -73,6 +76,7 @@ import pytz
import collections
import pendulum
from rowingdata import rowingdata as rrdata
+from rowingdata import rowingdata_pl as rrdata_pl
from rowingdata import rower as rrower
@@ -88,10 +92,9 @@ from rowers.dataroutines import *
from rowers.tasks import (
handle_sendemail_newftp,
- handle_sendemail_unrecognized, handle_setcp,
+ handle_sendemail_unrecognized,
handle_getagegrouprecords, handle_update_wps,
handle_request_post, handle_calctrimp,
- handle_updatecp, handle_updateergcp,
handle_sendemail_breakthrough,
handle_sendemail_hard,
)
@@ -123,6 +126,10 @@ from rq import Queue
from rowers.datautils import rpetotss
def rscore_approx(row):
+ if isinstance(row, pl.DataFrame):
+ row = {'rscore': row['rscore'][0]}
+ if isinstance(row, pl.Series):
+ row = {'rscore': row['rscore'][0]}
if row['rscore'] > 0:
return row['rscore']
if row['rscore'] == 0: # pragma: no cover
@@ -215,18 +222,18 @@ def check_marker(workout):
ids.append(w.id)
gms.append(gmstandard)
- df = pd.DataFrame({
+ df = pl.DataFrame({
'id': ids,
'gms': gms,
})
- if df.empty: # pragma: no cover
+ if df.is_empty(): # pragma: no cover
workout.ranking = True
workout.save()
return workout
- indexmax = df['gms'].idxmax()
- theid = df.loc[indexmax, 'id']
+ theid = df.filter(pl.col("gms") == pl.col("gms").max())['id'][0]
+
wmax = Workout.objects.get(id=theid)
# gms_max = wmax.goldmedalstandard
@@ -322,7 +329,7 @@ def workout_summary_to_df(
goldstandarddurations.append(int(goldstandardduration))
rankingpieces.append(w.rankingpiece)
- df = pd.DataFrame({
+ df = pl.DataFrame({
'ID': ids,
'date': startdatetimes,
'name': names,
@@ -392,7 +399,7 @@ def resample(id, r, parent, overwrite='copy'):
row.write_csv(parent.csvfilename, gzip=True)
- _ = dataprep(row.df, id=parent.id, bands=True, barchart=True,
+ _ = dataplep(row.df, id=parent.id, bands=True, barchart=True,
otwpower=True, empower=True, inboard=parent.inboard)
isbreakthrough, ishard = checkbreakthrough(parent, r)
_ = check_marker(parent)
@@ -414,18 +421,23 @@ def resample(id, r, parent, overwrite='copy'):
def calculate_goldmedalstandard(rower, workout, recurrance=True):
cpfile = 'media/cpdata_{id}.parquet.gz'.format(id=workout.id)
try:
- df = pd.read_parquet(cpfile)
+ df = pl.read_parquet(cpfile)
except:
+ df = read_data(['power'], ids=[workout.id])
+ df = remove_nulls_pl(df)
background = True
if settings.TESTING:
background = False
- df, delta, cpvalues = setcp(workout, background=background)
- if df.empty:
- return 0, 0
+ if recurrance:
+ df, delta, cpvalues = setcp(workout, background=background)
+ if df.is_empty():
+ return 0, 0
+ else:
+ return 0,0
- if df.empty and recurrance: # pragma: no cover
+ if df.is_empty() and recurrance: # pragma: no cover
df, delta, cpvalues = setcp(workout, recurrance=False, background=True)
- if df.empty:
+ if df.is_empty():
return 0, 0
age = calculate_age(rower.birthdate, today=workout.date)
@@ -453,7 +465,7 @@ def calculate_goldmedalstandard(rower, workout, recurrance=True):
if getrecords: # pragma: no cover
durations = [1, 4, 30, 60]
distances = [100, 500, 1000, 2000, 5000, 6000, 10000, 21097, 42195]
- df2 = pd.DataFrame(
+ df2 = pl.DataFrame(
list(
C2WorldClassAgePerformance.objects.filter(
sex=rower.sex,
@@ -461,7 +473,7 @@ def calculate_goldmedalstandard(rower, workout, recurrance=True):
).values()
)
)
- jsondf = df2.to_json()
+ jsondf = df2.write_json()
_ = myqueue(queuelow, handle_getagegrouprecords,
jsondf, distances, durations, age, rower.sex, rower.weightcategory)
@@ -489,9 +501,10 @@ def calculate_goldmedalstandard(rower, workout, recurrance=True):
scores = 100.*powers/wcpowers
try:
- indexmax = scores.idxmax()
- delta = int(df.loc[indexmax, 'delta'])
- maxvalue = scores.max()
+ df = pl.DataFrame({'times': times, 'scores': scores})
+ df = df.filter(pl.col("scores") == pl.col("scores").max())
+ delta = df[0, "times"]
+ maxvalue = df[0, "scores"]
except (ValueError, TypeError, KeyError): # pragma: no cover
indexmax = 0
delta = 0
@@ -504,21 +517,22 @@ def calculate_goldmedalstandard(rower, workout, recurrance=True):
def setcp(workout, background=False, recurrance=True):
try:
filename = 'media/cpdata_{id}.parquet.gz'.format(id=workout.id)
- df = pd.read_parquet(filename)
+ df = pl.read_parquet(filename)
- if not df.empty:
+ if not df.is_empty():
# check dts
tarr = datautils.getlogarr(4000)
if df['delta'][0] in tarr:
return(df, df['delta'], df['cp'])
- except:
+ except Exception as e:
pass
- strokesdf = getsmallrowdata_db(
+ strokesdf = read_data(
['power', 'workoutid', 'time'], ids=[workout.id])
+ strokesdf = remove_nulls_pl(strokesdf)
- if strokesdf.empty:
- return pd.DataFrame({'delta': [], 'cp': []}), pd.Series(dtype='float'), pd.Series(dtype='float')
+ if strokesdf.is_empty():
+ return pl.DataFrame({'delta': [], 'cp': []}), pl.Series(dtype=pl.Float64), pl.Series(dtype=pl.Float64)
totaltime = strokesdf['time'].max()
maxt = totaltime/1000.
@@ -533,7 +547,7 @@ def setcp(workout, background=False, recurrance=True):
elif os.path.exists(csvfilename+'.gz'): # pragma: no cover
csvfile = csvfilename+'.gz'
else: # pragma: no cover
- return pd.DataFrame({'delta': [], 'cp': []}), pd.Series(dtype='float'), pd.Series(dtype='float')
+ return pl.DataFrame({'delta': [], 'cp': []}), pl.Series(dtype=pl.Float64), pl.Series(dtype=pl.Float64)
csvfile = os.path.abspath(csvfile)
@@ -547,7 +561,7 @@ def setcp(workout, background=False, recurrance=True):
grpc.channel_ready_future(channel).result(timeout=10)
except grpc.FutureTimeoutError: # pragma: no cover
dologging('metrics.log','grpc channel time out in setcp')
- return pd.DataFrame({'delta': [], 'cp': []}), pd.Series(dtype='float'), pd.Series(dtype='float')
+ return pl.DataFrame({'delta': [], 'cp': []}), pl.Series(dtype=pl.Float64), pl.Series(dtype=pl.Float64)
stub = metrics_pb2_grpc.MetricsStub(channel)
req = metrics_pb2.CPRequest(filename = csvfile, filetype = "CSV", tarr = logarr)
@@ -556,25 +570,29 @@ def setcp(workout, background=False, recurrance=True):
response = stub.GetCP(req, timeout=60)
except Exception as e:
dologging('metrics.log', traceback.format_exc())
- return pd.DataFrame({'delta': [], 'cp': []}), pd.Series(dtype='float'), pd.Series(dtype='float')
+ return pl.DataFrame({'delta': [], 'cp': []}), pl.Series(dtype=pl.Float64), pl.Series(dtype=pl.Float64)
- delta = pd.Series(np.array(response.delta))
- cpvalues = pd.Series(np.array(response.power))
+ delta = pl.Series(np.array(response.delta))
+ cpvalues = pl.Series(np.array(response.power))
powermean = response.avgpower
-
-
- df = pd.DataFrame({
+ df = pl.DataFrame({
'delta': delta,
'cp': cpvalues,
'id': workout.id,
})
- df.to_parquet(filename, engine='fastparquet', compression='GZIP')
+ df = df.drop_nulls()
+
+ with gzip.open(filename, 'w') as f:
+ df.write_parquet(f)
+
+
+ #df.to_parquet(filename, engine='fastparquet', compression='GZIP')
if recurrance:
goldmedalstandard, goldmedalduration = calculate_goldmedalstandard(
- workout.user, workout)
+ workout.user, workout, recurrance=False)
workout.goldmedalstandard = goldmedalstandard
workout.goldmedalduration = goldmedalduration
workout.save()
@@ -602,14 +620,10 @@ def update_wps(r, types, mode='water', asynchron=True):
mode
)
- df = getsmallrowdata_db(['time', 'driveenergy'], ids=ids)
+ df = read_data(['time', 'driveenergy'], ids=ids)
try:
- mask = df['driveenergy'] > 100
- except (KeyError, TypeError):
- return False
- try:
- wps_median = int(df.loc[mask, 'driveenergy'].median())
+ wps_median = int(df.filter(pl.col("driveenergy")>100)["driveenergy"].median())
if mode == 'water':
r.median_wps = wps_median
else: # pragma: no cover
@@ -618,6 +632,10 @@ def update_wps(r, types, mode='water', asynchron=True):
r.save()
except ValueError: # pragma: no cover
pass
+ except OverflowError:
+ pass
+ except ColumnNotFoundError:
+ pass
return True
@@ -718,77 +736,28 @@ def fetchcp_new(rower, workouts):
data = []
for workout in workouts:
- cpfile = 'media/cpdata_{id}.parquet.gz'.format(id=workout.id)
- try:
- df, delta, cpvalues = setcp(workout)
- df = pd.read_parquet(cpfile)
- df['workout'] = str(workout)
- df['url'] = workout.url()
- data.append(df)
- except:
- # CP data file doesn't exist yet. has to be created
- df, delta, cpvalues = setcp(workout)
- df['workout'] = str(workout)
- df['url'] = workout.url()
+ df, delta, cpvalues = setcp(workout)
+ df = df.drop('id')
+ df = df.with_columns((pl.lit(str(workout))).alias("workout"))
+ df = df.with_columns((pl.lit(workout.url())).alias("url"))
+ if not df.is_empty():
data.append(df)
if len(data) == 0:
- return pd.Series(dtype='float'), pd.Series(dtype='float'), 0, pd.Series(dtype='float'), pd.Series(dtype='float')
+ return pl.Series(dtype=pl.Float64), pl.Series(dtype=pl.Float64), 0, pl.Series(dtype=pl.Float64), pl.Series(dtype=pl.Float64)
if len(data) > 1:
- df = pd.concat(data, axis=0)
-
+ df = pl.concat(data)
try:
- df = df[df['cp'] == df.groupby(['delta'])['cp'].transform('max')]
- except KeyError: # pragma: no cover
- return pd.Series(dtype='float'), pd.Series(dtype='float'), 0, pd.Series(dtype='float'), pd.Series(dtype='float')
-
- df = df.sort_values(['delta']).reset_index()
- df = df[df['cp']>20]
+ df = df.group_by(pl.col("delta")).agg(pl.max("cp"), pl.max("workout"), pl.max("url")).sort("delta")
+ except (KeyError, ColumnNotFoundError): # pragma: no cover
+ return pl.Series(dtype=pl.Float64), pl.Series(dtype=pl.Float64), 0, pl.Series(dtype=pl.Float64), pl.Series(dtype=pl.Float64)
+ df = df.filter(pl.col("cp")>20)
return df['delta'], df['cp'], 0, df['workout'], df['url']
-def fetchcp(rower, theworkouts, table='cpdata'): # pragma: no cover
- # get all power data from database (plus workoutid)
- theids = [int(w.id) for w in theworkouts]
- columns = ['power', 'workoutid', 'time']
- df = getsmallrowdata_db(columns, ids=theids)
- df.dropna(inplace=True, axis=0)
- if df.empty:
- avgpower2 = {}
- for id in theids:
- avgpower2[id] = 0
- return pd.Series([], dtype='float'), pd.Series([], dtype='float'), avgpower2
-
- try:
- dfgrouped = df.groupby(['workoutid'])
- except KeyError:
- avgpower2 = {}
- return pd.Series([], dtype='float'), pd.Series([], dtype='float'), avgpower2
- try:
- avgpower2 = dict(dfgrouped.mean()['power'].astype(int))
- except KeyError:
- avgpower2 = {}
- for id in theids:
- avgpower2[id] = 0
- return pd.Series([], dtype='float'), pd.Series([], dtype='float'), avgpower2
-
- cpdf = getcpdata_sql(rower.id, table=table)
-
- if not cpdf.empty:
- return cpdf['delta'], cpdf['cp'], avgpower2
- else:
- _ = myqueue(queuelow,
- handle_updatecp,
- rower.id,
- theids,
- table=table)
-
- return pd.Series([], dtype='float'), pd.Series([], dtype='float'), avgpower2
-
- return pd.Series([], dtype='float'), pd.Series([], dtype='float'), avgpower2
def update_rolling_cp(r, types, mode='water', dosend=False):
@@ -801,16 +770,18 @@ def update_rolling_cp(r, types, mode='water', dosend=False):
delta, cp, avgpower, workoutnames, urls = fetchcp_new(r, workouts)
- powerdf = pd.DataFrame({
+ powerdf = pl.DataFrame({
'Delta': delta,
'CP': cp,
})
- powerdf = powerdf[powerdf['CP'] > 0]
- powerdf.dropna(axis=0, inplace=True)
- powerdf.sort_values(['Delta', 'CP'], ascending=[1, 0], inplace=True)
- powerdf.drop_duplicates(subset='Delta', keep='first', inplace=True)
+ powerdf = powerdf.filter(pl.col("CP")>0)
+ powerdf = powerdf.fill_nan(None).drop_nulls().sort(["Delta", "CP"])
+ powerdf = powerdf.unique(subset=["Delta"], keep="first")
+ if powerdf.is_empty():
+ return False
+
res2 = datautils.cpfit(powerdf)
p1 = res2[0]
# calculate FTP
@@ -1051,7 +1022,7 @@ def checkbreakthrough(w, r):
workouttype = w.workouttype
if workouttype in rowtypes:
cpdf, delta, cpvalues = setcp(w)
- if not cpdf.empty:
+ if not cpdf.is_empty():
if workouttype in otwtypes:
try:
res, btvalues, res2 = utils.isbreakthrough(
@@ -1397,7 +1368,12 @@ def save_workout_database(f2, r, dosmooth=True, workouttype='rower',
w.team.add(t)
# put stroke data in database
- _ = dataprep(row.df, id=w.id, bands=True,
+ try:
+ row = rrdata_pl(df=pl.from_pandas(row.df))
+ except:
+ pass
+
+ _ = dataplep(row.df, id=w.id, bands=True,
barchart=True, otwpower=True, empower=True, inboard=inboard)
isbreakthrough, ishard = checkbreakthrough(w, r)
diff --git a/rowers/dataroutines.py b/rowers/dataroutines.py
index 0e893166..b08fc1a7 100644
--- a/rowers/dataroutines.py
+++ b/rowers/dataroutines.py
@@ -1,4 +1,4 @@
-from rowers.metrics import axes, calc_trimp, rowingmetrics, dtypes, metricsgroups
+from rowers.metrics import axes, calc_trimp, rowingmetrics, dtypes, metricsgroups, metricsdicts
from rowers.utils import lbstoN, wavg, dologging
from rowers.mytypes import otwtypes, otetypes, rowtypes
import glob
@@ -31,6 +31,12 @@ from zipfile import BadZipFile
import zipfile
import os
from rowers.models import strokedatafields
+import polars as pl
+import polars.selectors as cs
+from polars.exceptions import (
+ ColumnNotFoundError, SchemaError, ComputeError,
+ InvalidOperationError, ShapeError
+)
from rowingdata import (
KinoMapParser,
@@ -71,6 +77,7 @@ from pytz.exceptions import UnknownTimeZoneError
import collections
import pendulum
from rowingdata import rowingdata as rrdata
+from rowingdata import rowingdata_pl as rrdata_pl
from rowingdata import rower as rrower
@@ -170,6 +177,29 @@ columndict = {
'cumdist': 'cum_dist',
}
+def remove_nulls_pl(data):
+ data = data.lazy().with_columns(
+ pl.when(
+ pl.all().is_infinite()
+ ).then(None).otherwise(pl.all()).keep_name()
+ )
+ data = data.select(pl.all().forward_fill())
+ data = data.select(pl.all().backward_fill())
+ data = data.fill_nan(None)
+
+ data = data.select(cs.by_dtype(pl.NUMERIC_DTYPES)).collect()
+ data = data[[s.name for s in data if not s.is_infinite().sum()]]
+ data = data[[s.name for s in data if not (s.null_count() == data.height)]]
+
+
+ if not data.is_empty():
+ try:
+ data = data.drop_nulls()
+ except: # pragma: no cover
+ pass
+
+ return data
+
def get_video_data(w, groups=['basic'], mode='water'):
modes = [mode, 'both', 'basic']
@@ -177,8 +207,10 @@ def get_video_data(w, groups=['basic'], mode='water'):
columns += [name for name, d in rowingmetrics if d['group']
in groups and d['mode'] in modes]
columns = list(set(columns))
- df = getsmallrowdata_db(columns, ids=[w.id],
+ df = getsmallrowdata_pd(columns, ids=[w.id],
workstrokesonly=False, doclean=False, compute=False)
+ df.dropna(axis=0, how='all', inplace=True)
+ df.dropna(axis=1, how='all', inplace=True)
df['time'] = (df['time']-df['time'].min())/1000.
@@ -199,7 +231,6 @@ def get_video_data(w, groups=['basic'], mode='water'):
df2 = df2.round(decimals=2)
- boatspeed = (100*df2['velo']).astype(int)/100.
try:
coordinates = get_latlon_time(w.id)
@@ -213,10 +244,12 @@ def get_video_data(w, groups=['basic'], mode='water'):
coordinates.set_index(pd.to_timedelta(
coordinates['time'], unit='s'), inplace=True)
- coordinates = coordinates.resample('1s').mean().interpolate()
- coordinates['time'] = coordinates['time']-coordinates['time'].min()
- latitude = coordinates['latitude']
- longitude = coordinates['longitude']
+ coordinates = coordinates.resample('1s').first().interpolate().fillna(method='ffill')
+ #coordinates['time'] = coordinates['time']-coordinates['time'].min()
+ df2 = pd.concat([df2, coordinates], axis=1)
+ latitude = df2['latitude']
+ longitude = df2['longitude']
+ boatspeed = (100*df2['velo']).astype(int)/100.
# bundle data
data = {
@@ -235,7 +268,10 @@ def get_video_data(w, groups=['basic'], mode='water'):
else:
sigfigs = dict(rowingmetrics)[c]['sigfigs']
if (c != 'pace'):
- da = ((10**sigfigs)*df2[c]).astype(int)/(10**sigfigs)
+ try:
+ da = ((10**sigfigs)*df2[c]).astype(int)/(10**sigfigs)
+ except:
+ da = df2[c]
else:
da = df2[c]
data[c] = da.values.tolist()
@@ -253,6 +289,14 @@ def get_video_data(w, groups=['basic'], mode='water'):
maxtime = coordinates['time'].max()
+ data = pd.DataFrame(data)
+ data.replace([np.inf, -np.inf], np.nan, inplace=True)
+ data.dropna(inplace=True)
+
+ data = pl.from_pandas(data)
+
+ data = data.to_dict(as_series=False)
+
return data, metrics, maxtime
@@ -373,7 +417,7 @@ def filter_df(datadf, fieldname, value, largerthan=True):
mask = datadf[fieldname] >= value
datadf.loc[mask, fieldname] = np.nan
- except TypeError:
+ except TypeError: # pragma: no cover
pass
return datadf
@@ -394,18 +438,22 @@ def df_resample(datadf):
def clean_df_stats(datadf, workstrokesonly=True, ignorehr=True,
- ignoreadvanced=False):
+ ignoreadvanced=False, for_chart=False):
# clean data remove zeros and negative values
try:
_ = datadf['workoutid'].unique()
except KeyError:
- datadf['workoutid'] = 0
+ try:
+ datadf['workoutid'] = 0
+ except TypeError: # pragma: no cover
+ datadf = datadf.with_columns(pl.lit(0).alias("workoutid"))
before = {}
- for workoutid in datadf['workoutid'].unique():
+ ids = datadf['workoutid'].unique()
+ for workoutid in ids:
before[workoutid] = len(datadf[datadf['workoutid'] == workoutid])
-
+
data_orig = datadf.copy()
# bring metrics which have negative values to positive domain
@@ -493,7 +541,7 @@ def clean_df_stats(datadf, workstrokesonly=True, ignorehr=True,
pass
# clean data for useful ranges per column
- if not ignorehr:
+ if not ignorehr: # pragma: no cover
try:
mask = datadf['hr'] < 30
datadf.mask(mask, inplace=True)
@@ -564,6 +612,7 @@ def clean_df_stats(datadf, workstrokesonly=True, ignorehr=True,
except KeyError:
pass
+
if not ignoreadvanced:
try:
mask = datadf['rhythm'] < 0
@@ -642,6 +691,9 @@ def clean_df_stats(datadf, workstrokesonly=True, ignorehr=True,
pass
after = {}
+
+ if for_chart: # pragma: no cover
+ return datadf
for workoutid in data_orig['workoutid'].unique():
after[workoutid] = len(
datadf[datadf['workoutid'] == workoutid].dropna())
@@ -649,6 +701,195 @@ def clean_df_stats(datadf, workstrokesonly=True, ignorehr=True,
if ratio < 0.01 or after[workoutid] < 2:
return data_orig
+ return datadf # pragma: no cover
+
+def replace_zeros_with_nan(x): # pragma: no cover
+ return np.nan if x == 0 else x
+
+def clean_df_stats_pl(datadf, workstrokesonly=True, ignorehr=True,
+ ignoreadvanced=False, for_chart=False): # pragma: no cover
+ # clean data remove zeros and negative values
+ try:
+ _ = datadf['workoutid'].unique()
+ except KeyError: # pragma: no cover
+ try:
+ datadf['workoutid'] = 0
+ except TypeError:
+ datadf = datadf.with_columns(pl.lit(0).alias("workoutid"))
+ except ColumnNotFoundError: # pragma: no cover
+ datadf = datadf.with_columns(pl.lit(0).alias("workoutid"))
+
+ before = {}
+ ids = list(datadf['workoutid'].unique())
+ for workoutid in ids:
+ before[workoutid] = len(datadf.filter(pl.col("workoutid")==workoutid))
+
+ data_orig = datadf.clone()
+
+ # bring metrics which have negative values to positive domain
+ if len(datadf) == 0: # pragma: no cover
+ return data_orig
+ try:
+ datadf = datadf.with_columns((-pl.col('catch')).alias('catch'))
+ except (KeyError, TypeError): # pragma: no cover
+ pass
+ except(ComputeError, InvalidOperationError, ColumnNotFoundError):
+ return data_orig
+
+ try: # pragma: no cover
+ datadf = datadf.with_columns((pl.col('peakforceangle')+1000).alias('peakforceangle'))
+ except (KeyError, TypeError):
+ pass
+ except(ComputeError, InvalidOperationError, ColumnNotFoundError):
+ return data_orig
+
+ try: # pragma: no cover
+ datadf = datadf.with_columns((pl.col('hr')+10).alias('hr'))
+ except (KeyError, TypeError):
+ pass
+ except(ComputeError, InvalidOperationError, ColumnNotFoundError):
+ return data_orig
+
+ # protect 0 spm values from being nulled
+ try: # pragma: no cover
+ datadf = datadf.with_columns((pl.col('spm')+1.0).alias('spm'))
+ except (KeyError, TypeError):
+ pass
+ except(ComputeError, InvalidOperationError, ColumnNotFoundError):
+ return data_orig
+
+ # protect 0 workoutstate values from being nulled
+ try: # pragma: no cover
+ datadf = datadf.with_columns((pl.col('workoutstate')+1).alias('workoutstate'))
+ except (KeyError, TypeError):
+ pass
+ except(ComputeError, InvalidOperationError, ColumnNotFoundError):
+ return data_orig
+
+ try: # pragma: no cover
+ datadf = datadf.select(pl.all().clip(lower_bound=0))
+ # datadf = datadf.clip(lower=0)
+ except (TypeError):
+ pass
+ except(ComputeError, InvalidOperationError, ColumnNotFoundError):
+ return data_orig
+
+ # protect advanced metrics columns
+ advancedcols = [
+ 'rhythm',
+ 'power',
+ 'drivelength',
+ 'forceratio',
+ 'drivespeed',
+ 'driveenergy',
+ 'catch',
+ 'finish',
+ 'averageforce',
+ 'peakforce',
+ 'slip',
+ 'wash',
+ 'peakforceangle',
+ 'effectiveangle',
+ ] # pragma: no cover
+
+ for col in datadf.columns: # pragma: no cover
+ datadf = datadf.with_columns(
+ pl.when(datadf[col] == 0).then(pl.lit(np.nan)).otherwise(datadf[col]),
+ name=col
+ )
+
+ # datadf = datadf.map_partitions(lambda df:df.replace(to_replace=0,value=np.nan))
+
+ # bring spm back to real values
+ try: # pragma: no cover
+ datadf = datadf.with_columns((pl.col('spm')-1.0).alias('spm'))
+ except (TypeError, KeyError):
+ pass
+
+ # bring workoutstate back to real values
+ try: # pragma: no cover
+ datadf = datadf.with_columns((pl.col('workoutstate')-1).alias('workoutstate'))
+ except (TypeError, KeyError):
+ pass
+
+ # return from positive domain to negative
+ try: # pragma: no cover
+ datadf = datadf.with_columns((-pl.col('catch')).alias('catch'))
+ except (KeyError, TypeError):
+ pass
+
+ try: # pragma: no cover
+ datadf = datadf.with_columns((pl.col('peakforceangle')-1000).alias('peakforceangle'))
+ except (KeyError, TypeError):
+ pass
+
+ try:
+ datadf = datadf.with_columns((pl.col('hr')+10).alias('hr'))
+ except (KeyError, TypeError):
+ pass
+
+ # clean data for useful ranges per column
+ if not ignorehr:
+ datadf = datadf.filter(pl.col("hr")>=30)
+
+
+ datadf = datadf.filter(
+ pl.col("spm") >=0,
+ pl.col("spm")>=10,
+ pl.col("pace")<=300*1000.,
+ pl.col("pace")>=60*1000,
+ pl.col("power")<=5000,
+ pl.col("spm")<=120,
+ )
+
+
+ # try to guess ignoreadvanced
+ if not ignoreadvanced:
+ for metric in advancedcols:
+ try:
+ sum = datadf[metric].std()
+ if sum == 0 or np.isnan(sum):
+ ignoreadvanced = True
+ except (KeyError, TypeError):
+ pass
+
+ if not ignoreadvanced:
+ datadf = datadf.filter(pl.col("rhythm")>=0,
+ pl.col("rhythm")<=70,
+ pl.col("power")>=20,
+ pl.col("efficiency")<=200,
+ pl.col("drivelength")>=0.5,
+ pl.col("wash")>=1,
+ pl.col("efficiency")>=0,
+ pl.col("forceratio")>=0.2,
+ pl.col("forceratio")<=1.0,
+ pl.col("drivespeed")>=0.5,
+ pl.col("drivespeed")<=4,
+ pl.col("driveenergy")<=2000,
+ pl.col("driveenergy")>=100,
+ pl.col("catch")<=-30)
+
+
+
+ # workoutstateswork = [1, 4, 5, 8, 9, 6, 7]
+ workoutstatesrest = [3]
+ # workoutstatetransition = [0, 2, 10, 11, 12, 13]
+
+ if workstrokesonly == 'True' or workstrokesonly is True:
+ datadf = datadf.filter(~pl.col("workoutstate").is_in(workoutstatesrest))
+
+ after = {}
+
+ if for_chart:
+ return datadf
+ for workoutid in data_orig['workoutid'].unique():
+ after[workoutid] = len(datadf.filter(pl.col("workoutid")==workoutid))
+ ratio = float(after[workoutid])/float(before[workoutid])
+ if ratio < 0.01 or after[workoutid] < 2:
+ return data_orig
+
+
+
return datadf
@@ -860,12 +1101,6 @@ def get_workoutsummaries(userid, startdate): # pragma: no cover
return df
-
-
-
-
-
-
def checkduplicates(r, workoutdate, workoutstartdatetime, workoutenddatetime):
duplicate = False
ws = Workout.objects.filter(user=r, date=workoutdate, duplicate=False).exclude(
@@ -1192,16 +1427,16 @@ def delete_strokedata(id, debug=False):
def update_strokedata(id, df, debug=False):
delete_strokedata(id, debug=debug)
- _ = dataprep(df, id=id, bands=True, barchart=True, otwpower=True)
+ _ = dataplep(df, id=id, bands=True, barchart=True, otwpower=True)
# Test that all data are of a numerical time
def testdata(time, distance, pace, spm): # pragma: no cover
- t1 = np.issubdtype(time, np.number)
- t2 = np.issubdtype(distance, np.number)
- t3 = np.issubdtype(pace, np.number)
- t4 = np.issubdtype(spm, np.number)
+ t1 = time.dtype in pl.NUMERIC_DTYPES
+ t2 = distance.dtype in pl.NUMERIC_DTYPES
+ t3 = pace.dtype in pl.NUMERIC_DTYPES
+ t4 = spm.dtype in pl.NUMERIC_DTYPES
return t1 and t2 and t3 and t4
@@ -1210,7 +1445,7 @@ def testdata(time, distance, pace, spm): # pragma: no cover
def getrowdata_db(id=0, doclean=False, convertnewtons=True,
- checkefficiency=True):
+ checkefficiency=True, for_chart=False):
data = read_df_sql(id)
try:
data['deltat'] = data['time'].diff()
@@ -1220,7 +1455,7 @@ def getrowdata_db(id=0, doclean=False, convertnewtons=True,
if data.empty:
rowdata, row = getrowdata(id=id)
if not rowdata.empty: # pragma: no cover
- data = dataprep(rowdata.df, id=id, bands=True,
+ data = dataplep(rowdata.df, id=id, bands=True,
barchart=True, otwpower=True)
else:
data = pd.DataFrame() # returning empty dataframe
@@ -1235,15 +1470,207 @@ def getrowdata_db(id=0, doclean=False, convertnewtons=True,
data = add_efficiency(id=id)
if doclean: # pragma: no cover
- data = clean_df_stats(data, ignorehr=True)
+ data = clean_df_stats(data, ignorehr=True, for_chart=for_chart)
return data, row
-# Fetch a subset of the data from the DB
+def getrowdata_pl(id=0, doclean=False, convertnewtons=True,
+ checkefficiency=True, for_chart=False):
+ data = read_df_sql(id,polars=True)
+ try:
+ data = data.with_columns((pl.col('time').diff()).alias("deltat")) # data['time'].diff()
+ except KeyError: # pragma: no cover
+ data = pl.DataFrame()
+
+ if data.is_empty():
+ rowdata, row = getrowdata(id=id)
+ if not rowdata.empty: # pragma: no cover
+ data = dataplep(rowdata.df, id=id, bands=True,
+ barchart=True, otwpower=True, polars=True)
+ else:
+ data = pl.DataFrame() # returning empty dataframe
+ else:
+ row = Workout.objects.get(id=id)
+
+ if checkefficiency is True and not data.is_empty():
+ try:
+ if data['efficiency'].mean() == 0 and data['power'].mean() != 0: # pragma: no cover
+ data = add_efficiency_pl(id=id, polars=True)
+ except KeyError: # pragma: no cover
+ data = add_efficiency_pl(id=id)
+
+ if doclean: # pragma: no cover
+ data = clean_df_stats(data, ignorehr=True, for_chart=for_chart)
+
+ return data, row
-def getsmallrowdata_db(columns, ids=[], doclean=True, workstrokesonly=True, compute=True,
- debug=False):
+
+def read_data(columns, ids=[], doclean=True, workstrokesonly=True, debug=False, for_chart=False, compute=True,
+ startenddict={}):
+ if ids:
+ csvfilenames = [
+ 'media/strokedata_{id}.parquet.gz'.format(id=id) for id in ids]
+ else:
+ return pl.DataFrame()
+
+ data = []
+ columns = [c for c in columns if c != 'None'] + ['distance', 'spm', 'workoutid','workoutstate', 'driveenergy']
+ columns = list(set(columns))
+
+ for id, f in zip(ids, csvfilenames):
+ if os.path.isfile(f):
+ df = pl.scan_parquet(f)
+ if startenddict:
+ try:
+ startsecond, endsecond = startenddict[id]
+ df = df.filter(pl.col("time") >= 1.0e3*startsecond,
+ pl.col("time") <= 1.0e3*endsecond)
+ df = df.with_columns(time = pl.col("time")-1.0e3*startsecond)
+ if 'cumdist' in columns:
+ df = df.collect()
+ df = df.with_columns(cumdist = pl.col("cumdist")-df[0, "cumdist"]).lazy()
+ except KeyError:
+ pass
+ data.append(df)
+ else:
+ rowdata, row = getrowdata(id=id)
+ try:
+ shutil.rmtree(f)
+ except:
+ pass
+ if rowdata and len(rowdata.df):
+ _ = dataplep(rowdata.df, id=id,
+ bands=True, otwpower=True, barchart=True,
+ polars=True)
+ df = pl.scan_parquet(f)
+ if startenddict:
+ try:
+ startsecond, endsecond = startenddict[id]
+ df = df.filter(pl.col("time") >= 1.0e3*startsecond,
+ pl.col("time") <= 1.0e3*endsecond)
+ df = df.with_columns(time = pl.col("time")-1.0e3*startsecond)
+ if 'cumdist' in columns:
+ df = df.collect()
+ df = df.with_columns(cumdist = pl.col("cumdist")-df[0, "cumdist"]).lazy()
+ except KeyError:
+ pass
+ data.append(df)
+
+ data = pl.collect_all(data)
+ if len(data)==0:
+ return pl.DataFrame()
+
+ try:
+ datadf = pl.concat(data).select(columns)
+ except (SchemaError, ShapeError):
+ data = [
+ df.select(columns)
+ for df in data]
+
+ # float columns
+ floatcolumns = []
+ intcolumns = []
+ for c in columns:
+ try:
+ if metricsdicts[c]['numtype'] == 'float':
+ floatcolumns.append(c)
+ if metricsdicts[c]['numtype'] == 'integer':
+ intcolumns.append(c)
+ except KeyError:
+ pass
+ data = [
+ df.with_columns(
+ cs.float().cast(pl.Float64)
+ ).with_columns(
+ cs.integer().cast(pl.Int64)
+ ).with_columns(
+ cs.by_name(intcolumns).cast(pl.Int64)
+ ).with_columns(
+ cs.by_name(floatcolumns).cast(pl.Float64)
+ )
+ for df in data
+ ]
+
+ try:
+ datadf = pl.concat(data)
+ except SchemaError:
+ data = [
+ df.with_columns(cs.integer().cast(pl.Float64)) for df in data
+ ]
+ datadf = pl.concat(data)
+
+
+
+
+ exprs = []
+
+ if workstrokesonly:
+ workoutstatesrest = [3]
+ exprs.append(~pl.col("workoutstate").is_in(workoutstatesrest))
+
+ # got data
+ if not doclean:
+ if exprs:
+ datadf2 = datadf.filter(exprs)
+ if not datadf2.is_empty():
+ return datadf2
+
+ return datadf
+
+ # do clean
+ if "spm" in datadf.columns:
+ exprs.append(pl.col("spm") >= 10 )
+ exprs.append(pl.col("spm") <= 120)
+ if "pace" in datadf.columns:
+ exprs.append(pl.col("pace") <= 300*1000.)
+ exprs.append(pl.col("pace") >= 60*1000.)
+ if "power" in datadf.columns:
+ exprs.append(pl.col("power") <= 5000)
+ exprs.append(pl.col("power")>=20)
+
+ if "rhythm" in datadf.columns:
+ exprs.append(pl.col("rhythm")>=0)
+ exprs.append(pl.col("rhythm")<=70)
+ if "efficiency" in datadf.columns:
+ exprs.append(pl.col("efficiency")<=200)
+ exprs.append(pl.col("efficiency")>=0)
+ if "wash" in datadf.columns:
+ exprs.append(pl.col("wash")>=1)
+ if "drivelength" in datadf.columns:
+ exprs.append(pl.col("drivelength")>=0.5)
+ if "forceratio" in datadf.columns:
+ exprs.append(pl.col("forceratio")>=0.2)
+ exprs.append(pl.col("forceratio")<=1.0)
+ if "drivespeed" in datadf.columns:
+ exprs.append(pl.col("drivespeed")>=0.5)
+ exprs.append(pl.col("drivespeed")<=4)
+ if "driveenergy" in datadf.columns:
+ exprs.append(pl.col("driveenergy")<=2000)
+ exprs.append(pl.col("driveenergy")>=100)
+ if "catch" in datadf.columns:
+ exprs.append(pl.col("catch")<=-30)
+
+ if exprs:
+ datadf2 = datadf.filter(exprs)
+
+ if not datadf2.is_empty():
+ return datadf2
+
+ exprs = []
+ if workstrokesonly:
+ workoutstatesrest = [3]
+ exprs.append(~pl.col("workoutstate").is_in(workoutstatesrest))
+
+ if exprs:
+ datadf2 = datadf.filter(exprs)
+ if not datadf2.is_empty():
+ return datadf2
+
+ return datadf
+
+def getsmallrowdata_pd(columns, ids=[], doclean=True, workstrokesonly=True, compute=True,
+ debug=False, for_chart=False):
# prepmultipledata(ids)
if ids:
@@ -1266,14 +1693,13 @@ def getsmallrowdata_db(columns, ids=[], doclean=True, workstrokesonly=True, comp
except (OSError, ArrowInvalid, IndexError): # pragma: no cover
rowdata, row = getrowdata(id=id)
if rowdata and len(rowdata.df):
- _ = dataprep(rowdata.df, id=id,
+ _ = dataplep(rowdata.df, id=id,
bands=True, otwpower=True, barchart=True)
try:
df = pd.read_parquet(f, columns=columns)
data.append(df)
except (OSError, ArrowInvalid, IndexError):
pass
-
try:
df = pd.concat(data, axis=0)
except ValueError: # pragma: no cover
@@ -1286,7 +1712,7 @@ def getsmallrowdata_db(columns, ids=[], doclean=True, workstrokesonly=True, comp
except (OSError, IndexError, ArrowInvalid):
rowdata, row = getrowdata(id=ids[0])
if rowdata and len(rowdata.df): # pragma: no cover
- data = dataprep(
+ data = dataplep(
rowdata.df, id=ids[0], bands=True, otwpower=True, barchart=True)
try:
df = pd.read_parquet(csvfilenames[0], columns=columns)
@@ -1297,7 +1723,7 @@ def getsmallrowdata_db(columns, ids=[], doclean=True, workstrokesonly=True, comp
except:
rowdata, row = getrowdata(id=ids[0])
if rowdata and len(rowdata.df): # pragma: no cover
- data = dataprep(
+ data = dataplep(
rowdata.df, id=ids[0], bands=True, otwpower=True, barchart=True)
try:
df = pd.read_parquet(csvfilenames[0], columns=columns)
@@ -1311,9 +1737,10 @@ def getsmallrowdata_db(columns, ids=[], doclean=True, workstrokesonly=True, comp
data = df.copy()
if doclean:
data = clean_df_stats(data, ignorehr=True,
- workstrokesonly=workstrokesonly)
+ workstrokesonly=workstrokesonly,
+ for_chart=for_chart)
data.dropna(axis=1, how='all', inplace=True)
- data.dropna(axis=0, how='any', inplace=True)
+ data.dropna(axis=0, how='all', inplace=True)
return data
except TypeError:
pass
@@ -1363,13 +1790,51 @@ def prepmultipledata(ids, verbose=False): # pragma: no cover
if verbose:
print(id)
if rowdata and len(rowdata.df):
- _ = dataprep(rowdata.df, id=id, bands=True,
+ _ = dataplep(rowdata.df, id=id, bands=True,
barchart=True, otwpower=True)
return ids
# Read a set of columns for a set of workout ids, returns data as a
# pandas dataframe
+def read_cols_pl(ids, columns):
+ extracols = []
+
+
+ columns = list(columns) + ['distance', 'spm', 'workoutid']
+ columns = [x for x in columns if x != 'None']
+ columns = list(set(columns))
+ ids = [int(id) for id in ids]
+
+ df = pl.DataFrame()
+
+ if len(ids) == 0:
+ return pl.DataFrame()
+
+ df = read_data(columns, ids=ids, doclean=False, compute=False)
+
+ if 'peakforce' in columns:
+ funits = ((w.id, w.forceunit)
+ for w in Workout.objects.filter(id__in=ids))
+ for id, u in funits:
+ if u == 'lbs':
+ df = df.with_columns(
+ peakforce=pl.when(pl.col('workoutid')==id)
+ .then(pl.col('peakforce') * lbstoN)
+ .otherwise(pl.col('peakforce')))
+ if 'averageforce' in columns:
+ funits = ((w.id, w.forceunit)
+ for w in Workout.objects.filter(id__in=ids))
+ for id, u in funits:
+ if u == 'lbs':
+ df = df.with_columns(
+ averageforce=pl.when(pl.col('workoutid')==id)
+ .then(pl.col('averageforce') * lbstoN)
+ .otherwise(pl.col('averageforce')))
+
+
+ return df, extracols
+
def read_cols_df_sql(ids, columns, convertnewtons=True):
# drop columns that are not in offical list
@@ -1396,7 +1861,7 @@ def read_cols_df_sql(ids, columns, convertnewtons=True):
except OSError:
rowdata, row = getrowdata(id=ids[0])
if rowdata and len(rowdata.df):
- _ = dataprep(rowdata.df,
+ _ = dataplep(rowdata.df,
id=ids[0], bands=True, otwpower=True, barchart=True)
pq_file = pq.ParquetDataset(filename)
columns_in_file = [c for c in columns if c in pq_file.schema.names]
@@ -1414,7 +1879,7 @@ def read_cols_df_sql(ids, columns, convertnewtons=True):
except (OSError, IndexError, ArrowInvalid):
rowdata, row = getrowdata(id=id)
if rowdata and len(rowdata.df): # pragma: no cover
- _ = dataprep(rowdata.df, id=id,
+ _ = dataplep(rowdata.df, id=id,
bands=True, otwpower=True, barchart=True)
pq_file = pq.ParquetDataset(f)
columns_in_file = [c for c in columns if c in pq_file.schema.names]
@@ -1451,14 +1916,35 @@ def read_cols_df_sql(ids, columns, convertnewtons=True):
# Read stroke data from the DB for a Workout ID. Returns a pandas dataframe
-def read_df_sql(id):
+def read_df_sql(id, polars=False):
+ if polars:
+ try:
+ f = 'media/strokedata_{id}.parquet.gz'.format(id=id)
+ df = pl.read_parquet(f)
+ except (IsADirectoryError, FileNotFoundError, OSError, ArrowInvalid, IndexError): # pragma: no cover
+ rowdata, row = getrowdata(id=id)
+ try:
+ shutil.rmtree(f)
+ except:
+ pass
+ if rowdata and len(rowdata.df):
+ _ = dataplep(rowdata.df, id=id,
+ bands=True, otwpower=True, barchart=True,
+ polars=True)
+ try:
+ df = pl.read_parquet(f, columns=columns)
+ except (OSError, ArrowInvalid, IndexError):
+ pass
+ df = df.fill_nan(None).drop_nulls()
+
+ return df
try:
f = 'media/strokedata_{id}.parquet.gz'.format(id=id)
df = pd.read_parquet(f)
except (OSError, ArrowInvalid, IndexError): # pragma: no cover
rowdata, row = getrowdata(id=id)
if rowdata and len(rowdata.df):
- data = dataprep(rowdata.df, id=id, bands=True,
+ data = dataplep(rowdata.df, id=id, bands=True,
otwpower=True, barchart=True)
try:
df = pd.read_parquet(f)
@@ -1496,7 +1982,7 @@ def datafusion(id1, id2, columns, offset):
df1[' latitude'] = latitude
df1[' longitude'] = longitude
- df2 = getsmallrowdata_db(['time'] + columns, ids=[id2], doclean=False)
+ df2 = getsmallrowdata_pd(['time'] + columns, ids=[id2], doclean=False)
forceunit = 'N'
@@ -1533,7 +2019,7 @@ def datafusion(id1, id2, columns, offset):
def fix_newtons(id=0, limit=3000): # pragma: no cover
# rowdata,row = getrowdata_db(id=id,doclean=False,convertnewtons=False)
- rowdata = getsmallrowdata_db(['peakforce'], ids=[id], doclean=False)
+ rowdata = read_data(['peakforce'], ids=[id], doclean=False)
try:
peakforce = rowdata['peakforce']
if peakforce.mean() > limit:
@@ -1546,6 +2032,13 @@ def fix_newtons(id=0, limit=3000): # pragma: no cover
pass
+def remove_invalid_columns_pl(df): # pragma: no cover
+ for c in df.get_columns():
+ if c not in allowedcolumns:
+ df = df.drop(c)
+
+ return df
+
def remove_invalid_columns(df): # pragma: no cover
for c in df.columns:
if c not in allowedcolumns:
@@ -1553,6 +2046,36 @@ def remove_invalid_columns(df): # pragma: no cover
return df
+def add_efficiency_pl(id=0): # pragma: no cover
+ rowdata, row = getrowdata_pl(id=id,
+ doclean=False,
+ convertnewtons=False,
+ checkefficiency=False)
+ power = rowdata['power']
+ pace = rowdata['pace'] / 1.0e3
+ velo = 500. / pace
+ ergpw = 2.8 * velo**3
+ efficiency = 100. * ergpw / power
+
+ efficiency = efficiency.replace([-np.inf, np.inf], np.nan)
+ efficiency.fillna(method='ffill')
+ rowdata = rowdata.with_columns(pl.col(efficiency).alias("efficiency")) # ['efficiency'] = efficiency
+
+ rowdata = remove_invalid_columns_pl(rowdata)
+ rowdata = rowdata.replace([-np.inf, np.inf], np.nan)
+ rowdata = rowdata.fillna(method='ffill')
+
+ delete_strokedata(id)
+
+
+ if id != 0:
+ rowdata = rowdata.with_column(pl.lit(id).alias("workoutid"))
+ filename = 'media/strokedata_{id}.parquet.gz'.format(id=id)
+ rowdata.write_parquet(filename, compression='gzip')
+
+
+ return rowdata
+
def add_efficiency(id=0): # pragma: no cover
rowdata, row = getrowdata_db(id=id,
@@ -1588,9 +2111,242 @@ def add_efficiency(id=0): # pragma: no cover
# saves it to the stroke_data table in the database
# Takes a rowingdata object's DataFrame as input
+# polars
+def dataplep(rowdatadf, id=0, inboard=0.88, forceunit='lbs', bands=True, barchart=True, otwpower=True,
+ empower=True, debug=False, polars=True
+ ):
+ # rowdatadf is pd.DataFrame
+
+
+ if isinstance(rowdatadf, pd.DataFrame):
+ if rowdatadf.empty:
+ return 0
+ try:
+ df = pl.from_pandas(rowdatadf)
+ except ArrowInvalid:
+ for k, v in dtypes.items():
+ try:
+ rowdatadf[k] = rowdatadf[k].astype(v)
+ except KeyError: # pragma: no cover
+ pass
+ try:
+ df = pl.from_pandas(rowdatadf)
+ except ArrowInvalid:
+ return dataprep(rowdatadf, id=id, inboard=inboard, forceunit=forceunit, bands=bands, barchart=barchart,
+ otwpower=otwpower, debug=debug,polars=True)
+
+ else:
+ df = rowdatadf
+ if df.is_empty():
+ return 0
+
+ df = df.with_columns((pl.col("TimeStamp (sec)")-df[0, "TimeStamp (sec)"]).alias("TimeStamp (sec)"))
+ df = df.with_columns((pl.col(" Stroke500mPace (sec/500m)").clip(1,3000)).alias(" Stroke500mPace"))
+ if ' AverageBoatSpeed (m/s)' not in df.columns:
+ df = df.with_columns((500./pl.col(' Stroke500mPace (sec/500m)')).alias(' AverageBoatSpeed (m/s)'))
+ if ' WorkoutState' not in df.columns:
+ df = df.with_columns((pl.lit(0)).alias(" WorkoutState"))
+ if df[" DriveTime (ms)"].mean() > 0:
+ df = df.with_columns((100.*pl.col(" DriveTime (ms)")/(pl.col(" DriveTime (ms)")+pl.col(" StrokeRecoveryTime (ms)"))).alias("rhythm"))
+ else:
+ df = df.with_columns((pl.lit(0)).alias("rhythm"))
+ if df[" PeakDriveForce (lbs)"].mean() > 0:
+ df = df.with_columns((pl.col(" AverageDriveForce (lbs)")/pl.col(" PeakDriveForce (lbs)")).alias("forceratio"))
+ else:
+ df = df.with_columns((pl.lit(0)).alias("forceratio"))
+ f = df['TimeStamp (sec)'].diff().mean()
+ if f != 0 and not np.isinf(f):
+ try:
+ windowsize = 2 * (int(10. / (f))) + 1
+ except ValueError:
+ windowsize = 1
+ else:
+ windowsize = 1
+
+ if windowsize <= 3:
+ windowsize = 5
+
+ try:
+ df.with_columns(
+ (pl.col(" Cadence (stokes/min)").map_batches(lambda x: savgol_filter(x.to_numpy(), windowsize, 3)).explode()
+ ).alias(" Cadence (stokes/min)"))
+ except ComputeError:
+ pass
+ try:
+ df.with_columns(
+ (pl.col(" DriveLength (meters)").map_batches(lambda x: savgol_filter(x.to_numpy(), windowsize, 3)).explode()
+ ).alias(" DriveLength (meters)"))
+ except ComputeError:
+ pass
+ try:
+ df.with_columns(
+ (pl.col(" HRCur (bpm)").map_batches(lambda x: savgol_filter(x.to_numpy(), windowsize, 3)).explode()
+ ).alias(" HRCur (bpm)"))
+ except ComputeError:
+ pass
+ try:
+ df.with_columns((pl.col("forceratio").map_batches(lambda x: savgol_filter(x.to_numpy(), windowsize, 3)).explode()).alias("forceratio"))
+ except ComputeError:
+ pass
+
+ df = df.with_columns((pl.col(" DriveLength (meters)") / pl.col(" DriveTime (ms)") * 1.0e3).alias("drivespeed"))
+ if df[" DriveTime (ms)"].mean() == 0:
+ df = df.with_columns((pl.lit(0)).alias("drivespeed"))
+
+
+ if 'driveenergy' not in df.columns:
+ if forceunit == 'lbs':
+ df = df.with_columns((pl.col(" DriveLength (meters)") * pl.col(" AverageDriveForce (lbs)") * lbstoN).alias("driveenergy"))
+ else:
+ df = df.with_columns((pl.col(" DriveLength (meters)") * pl.col(" AverageDriveForce (lbs)")).alias("driveenergy"))
+
+
+ if forceunit == 'lbs':
+ df = df.with_columns((pl.col(" AverageDriveForce (lbs)") * lbstoN).alias(" AverageDriveForce (lbs)"))
+ df = df.with_columns((pl.col(" PeakDriveForce (lbs)") * lbstoN).alias(" PeakDriveForce (lbs)"))
+
+ if df["driveenergy"].mean() == 0 and df["driveenergy"].std() == 0:
+ df = df.with_columns((0.0*pl.col("driveenergy")+100).alias("driveenergy"))
+
+ df = df.with_columns((60. * pl.col(" AverageBoatSpeed (m/s)")/pl.col(" Cadence (stokes/min)")).alias("distanceperstroke"))
+
+ t2 = df["TimeStamp (sec)"].map_elements(lambda x: timedeltaconv(x), return_dtype=pl.Datetime)
+ p2 = df[" Stroke500mPace"].map_elements(lambda x: timedeltaconv(x), return_dtype=pl.Datetime)
+
+ data = pl.DataFrame(
+ dict(
+ time=df["TimeStamp (sec)"] * 1e3,
+ hr=df[" HRCur (bpm)"],
+ pace=df[" Stroke500mPace"] * 1e3,
+ spm=df[" Cadence (stokes/min)"],
+ velo=df[" AverageBoatSpeed (m/s)"],
+ cumdist=df["cum_dist"],
+ ftime=niceformat(t2),
+ fpace=nicepaceformat(p2),
+ driveenergy=df["driveenergy"],
+ power=df[' Power (watts)'],
+ workoutstate=df[" WorkoutState"],
+ averageforce=df[" AverageDriveForce (lbs)"],
+ drivelength=df[" DriveLength (meters)"],
+ peakforce=df[" PeakDriveForce (lbs)"],
+ forceratio=df["forceratio"],
+ distance=df["cum_dist"],
+ drivespeed=df["drivespeed"],
+ rhythm=df["rhythm"],
+ distanceperstroke=df["distanceperstroke"],
+ )
+ )
+
+ data = data.with_columns(
+ hr_ut2 = df['hr_ut2'],
+ hr_ut1 = df['hr_ut1'],
+ hr_at = df['hr_at'],
+ hr_tr = df['hr_tr'],
+ hr_an = df['hr_an'],
+ hr_max = df['hr_max'],
+ hr_bottom = 0.0*df[' HRCur (bpm)'],
+ )
+
+ if 'wash' not in df.columns:
+ data = data.with_columns(
+ wash = pl.lit(0.0),
+ catch = pl.lit(0.0),
+ peakforceangle = pl.lit(0.0),
+ finish = pl.lit(0.0),
+ slip = pl.lit(0.0),
+ totalangle = pl.lit(0.0),
+ effectiveangle = pl.lit(0.0),
+ efficiency = pl.lit(0.0),
+ )
+ else:
+ wash = df['wash']
+ catch = df['catch']
+ finish = df['finish']
+ peakforceangle = df['peakforceangle']
+ arclength = (inboard - 0.05) * (np.radians(finish) - np.radians(catch))
+ if arclength.mean() > 0:
+ drivelength = arclength
+ else:
+ drivelength = data['drivelength']
+
+ slip = df['slip']
+ totalangle = finish - catch
+ effectiveangle = finish - wash - catch - slip
+
+ if windowsize > 3 and windowsize < len(slip):
+ try:
+ wash = savgol_filter(wash, windowsize, 3)
+ except TypeError: # pragma: no cover
+ pass
+ try:
+ slip = savgol_filter(slip, windowsize, 3)
+ except TypeError: # pragma: no cover
+ pass
+ try:
+ catch = savgol_filter(catch, windowsize, 3)
+ except TypeError: # pragma: no cover
+ pass
+ try:
+ finish = savgol_filter(finish, windowsize, 3)
+ except TypeError: # pragma: no cover
+ pass
+ try:
+ peakforceangle = savgol_filter(peakforceangle, windowsize, 3)
+ except TypeError: # pragma: no cover
+ pass
+ try:
+ drivelength = savgol_filter(drivelength, windowsize, 3)
+ except TypeError: # pragma: no cover
+ pass
+ try:
+ totalangle = savgol_filter(totalangle, windowsize, 3)
+ except TypeError: # pragma: no cover
+ pass
+ try:
+ effectiveangle = savgol_filter(effectiveangle, windowsize, 3)
+ except TypeError: # pragma: no cover
+ pass
+
+ data = data.with_columns(
+ wash = wash,
+ catch = catch,
+ slip = slip,
+ finish = finish,
+ peakforceangle = peakforceangle,
+ drivelength = drivelength,
+ totalangle = totalangle,
+ effectiveangle = effectiveangle,
+ )
+
+ ergpw = 2.8*data['velo']**3
+ efficiency = 100. * ergpw / data['power']
+ if data['power'].mean() == 0:
+ efficiency = 100.+0.0*data['power']
+
+ data = data.with_columns(efficiency=efficiency)
+
+ if id != 0:
+ data = data.with_columns(
+ workoutid = pl.lit(id)
+ )
+ # cast data
+ for k, v in dtypes.items():
+ if v == 'int':
+ data = data.cast({k: pl.Int64})
+ filename = 'media/strokedata_{id}.parquet.gz'.format(id=id)
+ try:
+ data.write_parquet(filename, compression='gzip')
+ except IsADirectoryError:
+ shutil.rmtree(filename)
+ data.write_parquet(filename, compression='gzip')
+
+
+ return data
+
+# pandas/a little polars
def dataprep(rowdatadf, id=0, bands=True, barchart=True, otwpower=True,
- empower=True, inboard=0.88, forceunit='lbs', debug=False):
+ empower=True, inboard=0.88, forceunit='lbs', debug=False, polars=True):
if rowdatadf.empty:
return 0
@@ -1870,14 +2626,28 @@ def dataprep(rowdatadf, id=0, bands=True, barchart=True, otwpower=True,
filename = 'media/strokedata_{id}.parquet.gz'.format(id=id)
df = dd.from_pandas(data, npartitions=1)
- try:
- df.to_parquet(filename, engine='fastparquet', compression='GZIP')
- except FileNotFoundError:
- df2 = dd.from_pandas(df, npartitions=1)
- df2.to_parquet(filename, engine='fastparquet', compression='GZIP')
- except FileExistsError:
- os.remove(filename)
- df.to_parquet(filename, engine='fastparquet', compression='GZIP')
+
+ if polars:
+ pldf = pl.from_pandas(data)
+ try:
+ pldf.write_parquet(filename, compression='gzip')
+ except IsADirectoryError:
+ shutil.rmtree(filename)
+ pldf.write_parquet(filename, compression='gzip')
+ else:
+ try:
+ df.to_parquet(filename, engine='fastparquet', compression='gzip')
+ except FileNotFoundError:
+ df2 = dd.from_pandas(df, npartitions=1)
+ df2.to_parquet(filename, engine='fastparquet', compression='gzip')
+ except FileExistsError:
+ os.remove(filename)
+ df.to_parquet(filename, engine='fastparquet', compression='GZIP')
+
+ if polars:
+ pldf = pl.from_pandas(data)
+ return pldf
+
return data
@@ -2005,18 +2775,20 @@ def add_c2_stroke_data_db(strokedata, workoutid, starttimeunix, csvfilename,
' lapIdx': lapidx,
' WorkoutState': 4,
' ElapsedTime (sec)': seconds,
- 'cum_dist': dist2
+ 'cum_dist': dist2,
})
df.sort_values(by='TimeStamp (sec)', ascending=True)
# Create CSV file name and save data to CSV file
-
- res = df.to_csv(csvfilename, index_label='index',
- compression='gzip')
+ row = rrdata(df=df)
+ row.write_csv(csvfilename)
+ row = rrdata_pl(df=pl.from_pandas(row.df))
+ #res = df.to_csv(csvfilename, index_label='index',
+ # compression='gzip')
- data = dataprep(df, id=workoutid, bands=False, debug=debug)
+ data = dataplep(row.df, id=workoutid, bands=False, debug=debug)
return data
@@ -2059,7 +2831,7 @@ def create_c2_stroke_data_db(
else:
power = 0
- df = pd.DataFrame({
+ df = pl.DataFrame({
'TimeStamp (sec)': unixtime,
' Horizontal (meters)': d,
' Cadence (stokes/min)': spm,
@@ -2080,11 +2852,12 @@ def create_c2_stroke_data_db(
'cum_dist': d
})
- df[' ElapsedTime (sec)'] = df['TimeStamp (sec)']
+ df = df.with_columns((pl.col("TimeStamp (sec)")).alias(" ElapsedTime (sec)"))
- _ = df.to_csv(csvfilename, index_label='index', compression='gzip')
+ row = rrdata_pl(df=df)
+ row.writecsv(csvfilename, compression=True)
- data = dataprep(df, id=workoutid, bands=False, debug=debug)
+ data = dataplep(df, id=workoutid, bands=False, debug=debug)
return data
@@ -2123,7 +2896,7 @@ def update_empower(id, inboard, oarlength, boattype, df, f1, debug=False): # pr
if debug: # pragma: no cover
print("not updated ", id)
- _ = dataprep(df, id=id, bands=True, barchart=True, otwpower=True, debug=debug)
+ _ = dataplep(df, id=id, bands=True, barchart=True, otwpower=True, debug=debug)
row = rrdata(df=df)
row.write_csv(f1, gzip=True)
diff --git a/rowers/datautils.py b/rowers/datautils.py
index cb17a357..0b24d252 100644
--- a/rowers/datautils.py
+++ b/rowers/datautils.py
@@ -1,8 +1,10 @@
import pandas as pd
+import polars as pl
import numpy as np
from scipy.interpolate import griddata
from scipy import optimize
+
from rowers.mytypes import otwtypes, otetypes, rowtypes
from rowers.models import Workout
@@ -81,8 +83,9 @@ def cpfit(powerdf, fraclimit=0.0001, nmax=1000):
p1 = p0
- thesecs = powerdf['Delta']
- theavpower = powerdf['CP']
+ thesecs = powerdf['Delta'].to_numpy()
+ theavpower = powerdf['CP'].to_numpy()
+
if len(thesecs) >= 4:
try:
@@ -95,6 +98,7 @@ def cpfit(powerdf, fraclimit=0.0001, nmax=1000):
else:
factor = fitfunc(p0, thesecs.mean())/theavpower.mean()
p1 = [p0[0]/factor, p0[1]/factor, p0[2], p0[3]]
+
p1 = [abs(p) for p in p1]
fitt = pd.Series(10**(4*np.arange(100)/100.))
@@ -102,7 +106,7 @@ def cpfit(powerdf, fraclimit=0.0001, nmax=1000):
fitpower = fitfunc(p1, fitt)
fitpoints = fitfunc(p1, thesecs)
- fitpoints0 = fitpoints.copy()
+ fitpoints0 = fitpoints
dd = fitpoints-theavpower
ddmin = dd.min()
@@ -356,8 +360,8 @@ def getmaxwattinterval(tt, ww, i):
def getfastest(df, thevalue, mode='distance'):
- tt = df['time'].copy()
- dd = df['cumdist'].copy()
+ tt = df['time'].clone()
+ dd = df['cumdist'].clone()
tmax = tt.max()
if mode == 'distance': # pragma: no cover
@@ -368,40 +372,28 @@ def getfastest(df, thevalue, mode='distance'):
return 0
-# if tmax > 500000:
-# newlen=int(tmax/2000.)
-# newt = np.arange(newlen)*tmax/float(newlen)
-# deltat = newt[1]-newt[0]
-# else:
-# newt = np.arange(0,tmax,10.)
-# deltat = 10.
-
newlen = 1000
newt = np.arange(newlen)*tmax/float(newlen)
deltat = newt[1]-newt[0]
- dd = griddata(tt.values,
- dd.values, newt, method='linear', rescale=True)
+ dd = griddata(tt.to_numpy(),
+ dd.to_numpy(), newt, method='linear', rescale=True)
- tt = pd.Series(newt, dtype='float')
- dd = pd.Series(dd, dtype='float')
+ tt = pl.Series(newt, dtype=pl.Float64)
+ dd = pl.Series(dd, dtype=pl.Float64)
+
+ G = pl.concat([pl.Series([0.0]), dd])
- G = pd.concat([pd.Series([0]), dd])
- # T = pd.concat([pd.Series([0]), dd])
- # h = np.mgrid[0:len(tt)+1:1, 0:len(tt)+1:1]
- # distances = pd.DataFrame(h[1]-h[0])
ones = 1+np.zeros(len(G))
Ghor = np.outer(ones, G)
- # Thor = np.outer(ones, T)
- # Tver = np.outer(T, ones)
+
Gver = np.outer(G, ones)
Gdif = Ghor-Gver
Gdif = np.tril(Gdif.T).T
- Gdif = pd.DataFrame(Gdif)
+ Gdif = pl.DataFrame(Gdif)
F = Gdif
- F.fillna(inplace=True, method='ffill', axis=1)
- F.fillna(inplace=True, value=0)
+ F = F.fill_nan(0)
restime = []
distance = []
@@ -412,7 +404,7 @@ def getfastest(df, thevalue, mode='distance'):
restime.append(deltat*i)
cp = np.diag(F, i).max()
loc = np.argmax(np.diag(F, i))
- thestarttime = tt[loc]
+ thestarttime = tt.to_numpy()[loc]
starttimes.append(thestarttime)
distance.append(cp)
@@ -422,10 +414,6 @@ def getfastest(df, thevalue, mode='distance'):
distance = np.array(distance)
starttimes = np.array(starttimes)
- # for i in range(len(restime)):
- # if restime[i]= rower.ut2).filter(pl.col("hr") < rower.ut1)
+ frac_ut2 = totalseconds*qrydata.collect()['deltat'].sum()/sumtimehr
- qry = '{ut1} <= hr < {at}'.format(ut1=rower.ut1, at=rower.at)
- frac_ut1 = totalseconds*df.query(qry)['deltat'].sum()/sumtimehr
+ qrydata = df.lazy().filter(pl.col("hr") >= rower.ut1).filter(pl.col("hr") < rower.at)
+ frac_ut1 = totalseconds*qrydata.collect()['deltat'].sum()/sumtimehr
- qry = '{at} <= hr < {tr}'.format(at=rower.at, tr=rower.tr)
- frac_at = totalseconds*df.query(qry)['deltat'].sum()/sumtimehr
+ qrydata = df.lazy().filter(pl.col("hr") >= rower.at).filter(pl.col("hr") < rower.tr)
+ frac_at = totalseconds*qrydata.collect()['deltat'].sum()/sumtimehr
- qry = '{tr} <= hr < {an}'.format(tr=rower.tr, an=rower.an)
- frac_tr = totalseconds*df.query(qry)['deltat'].sum()/sumtimehr
+ qrydata = df.lazy().filter(pl.col("hr") >= rower.tr).filter(pl.col("hr") < rower.an)
+ frac_tr = totalseconds*qrydata.collect()['deltat'].sum()/sumtimehr
- qry = 'hr >= {an}'.format(an=rower.an)
- frac_an = totalseconds*df.query(qry)['deltat'].sum()/sumtimehr
+ qrydata = df.filter(pl.col("hr") >= rower.an)
+ frac_an = totalseconds*qrydata['deltat'].sum()/sumtimehr
datadict = {
'<{ut2}'.format(ut2=hrzones[1]): frac_lut2,
@@ -249,24 +256,14 @@ def interactive_hr_piechart(df, rower, title, totalseconds=0):
data['totaltime'] = pd.Series([pretty_timedelta(v) for v in data['value']])
- TOOLS = 'save,hover'
-
- z = figure(title="HR "+title, x_range=(-0.5, 1), height=375,
- tools=TOOLS, toolbar_location=None, tooltips="@zone: @totaltime",
- )
-
- z.wedge(x=0, y=1, radius=0.4,
- start_angle=cumsum('angle', include_zero=True), end_angle=cumsum('angle'),
- line_color='white', fill_color='color', source=data, legend_group='zone')
-
- z.axis.axis_label = None
- z.axis.visible = False
- z.grid.grid_line_color = None
- z.outline_line_color = None
- z.toolbar_location = 'right'
-
- return components(z)
+ data_dict = data.to_dict("records")
+ chart_data = {
+ 'data': data_dict,
+ 'title': "HR "+ title
+ }
+ script, div = get_chart("/hrpie", chart_data)
+ return script, div
def pretty_timedelta(secs):
hours, remainder = divmod(secs, 3600)
@@ -315,26 +312,24 @@ def interactive_workouttype_piechart(workouts):
except KeyError: # pragma: no cover
pass
- p = figure(height=350, title="Types", toolbar_location=None,
- tools="hover,save", tooltips="@type: @totaltime", x_range=(-0.5, 1.0))
+ data_dict = data.to_dict("records")
- p.wedge(x=0, y=1, radius=0.4,
- start_angle=cumsum('angle', include_zero=True), end_angle=cumsum('angle'),
- line_color="white", fill_color='color', source=data, legend_group='type', )
+ chart_data = {
+ "data": data_dict,
+ "title": "Types"
+ }
- p.axis.axis_label = None
- p.axis.visible = False
- p.grid.grid_line_color = None
- p.outline_line_color = None
- p.toolbar_location = 'right'
+
+ script, div = get_chart("/workouttypepie", chart_data, debug=False)
+
+ return script, div
- return components(p)
def interactive_boxchart(datadf, fieldname, extratitle='',
spmmin=0, spmmax=0, workmin=0, workmax=0):
- if datadf.empty: # pragma: no cover
+ if datadf.is_empty(): # pragma: no cover
return '', 'It looks like there are no data matching your filter'
columns = datadf.columns
@@ -345,319 +340,53 @@ def interactive_boxchart(datadf, fieldname, extratitle='',
if 'date' not in columns: # pragma: no cover
return '', 'Not enough data'
- tooltips = [
- ('Value', '@'+fieldname),
- ]
- hover = HoverTool(tooltips=tooltips)
-
- TOOLS = [hover]
-
- hv.extension('bokeh')
-
- try:
- boxwhiskers = hv.BoxWhisker(datadf, 'date', fieldname)
- boxwhiskers.opts(tools=TOOLS, outlier_color='white')
- except DataError: # pragma: no cover
- return "", "Invalid Data"
-
- plot = hv.render(boxwhiskers)
-
- yrange1 = Range1d(start=yaxminima[fieldname], end=yaxmaxima[fieldname])
- plot.y_range = yrange1
- #plot.sizing_mode = 'stretch_both'
-
- if extratitle:
- plot.title.text = extratitle
-
- plot.xaxis.axis_label = 'Date'
- plot.yaxis.axis_label = axlabels[fieldname]
-
- plot.xaxis.formatter = DatetimeTickFormatter(
- days=["%d %B %Y"],
- months=["%d %B %Y"],
- years=["%d %B %Y"],
- )
-
- if fieldname == 'pace': # pragma: no cover
- plot.yaxis[0].formatter = DatetimeTickFormatter(
- seconds=["%S"],
- minutes=["%M"]
- )
-
- plot.xaxis.major_label_orientation = pi/4
-
- plot.width = 920
- plot.height = 600
-
- slidertext = 'SPM: {:.0f}-{:.0f}, WpS: {:.0f}-{:.0f}'.format(
- spmmin, spmmax, workmin, workmax
- )
- sliderlabel = Label(x=50, y=20, x_units='screen', y_units='screen',
- text=slidertext,
- background_fill_alpha=0.7,
- background_fill_color='white',
- text_color='black', text_font_size='10pt',
- )
-
- plot.add_layout(sliderlabel)
-
- script, div = components(plot)
+
+ datadf = datadf.with_columns((pl.col("date").dt.strftime("%Y-%m-%d")).alias("date"))
+ datadf = datadf.with_columns((pl.col(fieldname)).alias("value"))
+
+ data_dict = datadf.to_dicts()
+ boxplot_data = {
+ "metric": metricsdicts[fieldname]["verbose_name"],
+ "data": data_dict
+ }
+
+ script, div = get_chart("/boxplot", boxplot_data, debug=False)
return script, div
def interactive_planchart(data, startdate, enddate):
- hv.extension('bokeh')
+ # data = data.melt(id_vars=['startdate'], value_vars=['executed', 'planned'])
- yaxmaximum = data['executed'].max()
- if data['planned'].max() > yaxmaximum: # pragma: no cover
- yaxmaximum = data['planned'].max()
-
- if yaxmaximum == 0: # pragma: no cover
- yaxmaximum = 250
-
- yrange1 = Range1d(start=0, end=1.1*yaxmaximum)
-
- tidy_df = data.melt(id_vars=['startdate'], value_vars=[
- 'executed', 'planned'])
- bars = hv.Bars(tidy_df, ['startdate', 'variable'], ['value'])
- bars.opts(
- opts.Bars(show_legend=True, tools=['tap', 'hover'], legend_position='bottom', show_frame=True))
-
- p = hv.render(bars)
-
- p.width = 550
- p.height = 350
- p.y_range = yrange1
- p.toolbar_location = 'above'
- #p.sizing_mode = 'stretch_both'
-
- script, div = components(p)
-
- return script, div
-
-
-def interactive_activitychart(workouts, startdate, enddate, stack='type', toolbar_location=None,
- yaxis='trimp'):
-
- dates = []
- dates_sorting = []
- types = []
- rowers = []
- durations = []
- rscores = []
- trimps = []
- links = []
-
- rowersinitials = {}
- seen = ['seen']
- idseen = []
-
- startdate = datetime.datetime(
- year=startdate.year, month=startdate.month, day=startdate.day)
- enddate = datetime.datetime(
- year=enddate.year, month=enddate.month, day=enddate.day)
-
- duration = enddate-startdate
-
- totaldays = duration.total_seconds()/(24*3600)
-
- for w in workouts:
- aantal = 1
- initials = w.user.user.first_name[0:aantal] + \
- w.user.user.last_name[0:aantal]
- if w.user.id not in idseen:
- while initials in seen: # pragma: no cover
- aantal += 1
- initials = w.user.user.first_name[0:aantal] + \
- w.user.user.last_name[0:aantal]
-
- seen.append(initials)
- idseen.append(w.user.id)
- rowersinitials[w.user.id] = initials
-
- for w in workouts:
- dd = w.date.strftime('%m/%d')
- dd2 = w.date.strftime('%Y/%m/%d')
- dd3 = w.date.strftime('%Y/%m')
- du = w.duration.hour*60+w.duration.minute
- rscore = w.rscore
- trimp = w.trimp
-
- if rscore == 0: # pragma: no cover
- rscore = w.hrtss
-
- if totaldays < 30:
- dates.append(dd)
- dates_sorting.append(dd2)
- else: # pragma: no cover
- dates.append(dd3)
- dates_sorting.append(dd3)
- durations.append(du)
- rscores.append(rscore)
- trimps.append(trimp)
- links.append(
- "{siteurl}/rowers/workout/{code}/".format(
- siteurl=settings.SITE_URL,
- code=encoder.encode_hex(w.id)
- )
- )
-
- types.append(w.workouttype)
- try:
- rowers.append(rowersinitials[w.user.id])
- except IndexError: # pragma: no cover
- rowers.append(str(w.user))
-
- try:
- d = utc.localize(startdate)
- except (ValueError, AttributeError): # pragma: no cover
- d = startdate
-
- try:
- enddate = utc.localize(enddate)
- except (ValueError, AttributeError): # pragma: no cover
- pass
-
- # add dates with no activity
- while d <= enddate:
- dd = d.strftime('%d')
-
- if totaldays < 30:
- dates.append(d.strftime('%m/%d'))
- dates_sorting.append(d.strftime('%Y/%m/%d'))
- else: # pragma: no cover
- dates.append(d.strftime('%Y/%m'))
- dates_sorting.append(d.strftime('%Y/%m'))
- durations.append(0)
- rscores.append(0)
- trimps.append(0)
- links.append('')
- try:
- types.append(types[0])
- except IndexError:
- types.append('rower')
-
- try:
- rowers.append(rowers[0])
- except IndexError:
- try:
- rowers.append(str(workouts[0].user))
- except IndexError:
- rowers.append(' ')
-
- d += datetime.timedelta(days=1)
-
- thedict = {
- 'date': dates,
- 'date_sorting': dates_sorting,
- 'duration': durations,
- 'trimp': trimps,
- 'rscore': rscores,
- 'type': types,
- 'rower': rowers,
- 'link': links,
+ data = data.with_columns((pl.col("startdate").dt.strftime("%Y-%m-%d")).alias("startdate"))
+ data_dict = data.to_dicts()
+ chart_data = {
+ 'data': data_dict,
}
- df = pd.DataFrame(thedict)
-
- df.sort_values('date_sorting', inplace=True)
-
- hv.extension('bokeh')
-
- if stack == 'type':
- table = hv.Table(df, [('date', 'Date'), ('type', 'Workout Type')],
- [('duration', 'Minutes'), ('rscore', 'rScore'), ('trimp', 'TRIMP'), ('link', 'link')])
-
- else:
- table = hv.Table(df, [('date', 'Date'), ('rower', 'Rower')],
- [('duration', 'Minutes'), ('rscore', 'rScore'), ('trimp', 'TRIMP'), ('link', 'link')])
-
- bars = table.to.bars(['date', stack], [yaxis])
- if stack == 'type':
- bars.opts(
- opts.Bars(cmap=mytypes.color_map, show_legend=True, stacked=True,
- tools=['tap', 'hover'], width=550, xrotation=45, padding=(0, (0, .1)),
- legend_position='bottom', show_frame=True))
- else:
- bars.opts(
- opts.Bars(cmap='Category10', show_legend=True, stacked=True,
- tools=['tap', 'hover'], width=550, xrotation=45, padding=(0, (0, .1)),
- legend_position='bottom', show_frame=True))
-
- p = hv.render(bars)
-
- p.title.text = 'Activity {d1} to {d2}'.format(
- d1=startdate.strftime("%Y-%m-%d"),
- d2=enddate.strftime("%Y-%m-%d"),
- )
-
- p.width = 550
- p.height = 350
- p.toolbar_location = toolbar_location
- p.y_range.start = 0
- #p.sizing_mode = 'stretch_both'
- taptool = p.select(type=TapTool)
-
- callback = CustomJS(args={'links': df.link}, code="""
- var index = cb_data.source.selected['1d'].indices[0];
- console.log(links);
- console.log(index);
- console.log(links[index]);
- window.location.href = links[index]
- """)
-
- taptool.js_on_event('tap', callback)
-
- script, div = components(p)
+ script, div = get_chart("/plan", chart_data, debug=False)
return script, div
+
-
-def interactive_activitychart2(workouts, startdate, enddate, stack='type', toolbar_location=None,
+def interactive_activitychart2(workouts, startdate, enddate, stack='type',
yaxis='duration'):
- dates = []
- dates_sorting = []
- types = []
- rowers = []
- durations = []
- rscores = []
- trimps = []
- links = []
- distances = []
-
- rowersinitials = {}
- seen = ['seen']
- idseen = []
startdate = datetime.datetime(
year=startdate.year, month=startdate.month, day=startdate.day)
enddate = datetime.datetime(
year=enddate.year, month=enddate.month, day=enddate.day)
- duration = enddate-startdate
+ totaldays = (enddate-startdate).days
- totaldays = duration.total_seconds()/(24*3600)
+ data_dicts = []
+ aantal = 1
for w in workouts:
- aantal = 1
- initials = w.user.user.first_name[0:aantal] + \
- w.user.user.last_name[0:aantal]
- if w.user.id not in idseen:
- while initials in seen: # pragma: no cover
- aantal += 1
- initials = w.user.user.first_name[0:aantal] + \
- w.user.user.last_name[0:aantal]
-
- seen.append(initials)
- idseen.append(w.user.id)
- rowersinitials[w.user.id] = initials
-
- for w in workouts:
- dd = w.date.strftime('%m/%d')
- dd2 = w.date.strftime('%Y/%m/%d')
- dd3 = w.date.strftime('%Y/%m')
+ rr = w.user
+ rowersinitials = rr.user.first_name[0:aantal]+rr.user.last_name[0:aantal]
+ dd = w.date.strftime('%Y-%m-%d')
du = w.duration.hour*60+w.duration.minute
trimp = w.trimp
@@ -666,845 +395,94 @@ def interactive_activitychart2(workouts, startdate, enddate, stack='type', toolb
if rscore == 0: # pragma: no cover
rscore = w.hrtss
- if totaldays <= 30: # pragma: no cover
- dates.append(dd)
- dates_sorting.append(dd2)
- else:
- dates.append(dd3)
- dates_sorting.append(dd3)
- durations.append(du)
- trimps.append(trimp)
- rscores.append(rscore)
- distances.append(distance)
- links.append(
- "{siteurl}/rowers/workout/{code}/".format(
+
+ link = "{siteurl}/rowers/workout/{code}/".format(
siteurl=settings.SITE_URL,
code=encoder.encode_hex(w.id)
)
- )
- types.append(w.workouttype)
- try:
- rowers.append(rowersinitials[w.user.id])
- except IndexError: # pragma: no cover
- rowers.append(str(w.user))
+ data_dicts.append({
+ 'date': dd,
+ 'duration': du,
+ 'distance': distance,
+ 'trimp': trimp,
+ 'rscore': rscore,
+ 'type': w.workouttype,
+ 'link': link,
+ 'rower': rowersinitials
+ })
- try:
- d = utc.localize(startdate)
- except (ValueError, AttributeError): # pragma: no cover
- d = startdate
+
- try:
- enddate = utc.localize(enddate)
- except (ValueError, AttributeError): # pragma: no cover
- pass
+ if totaldays < 30:
+ datebin = "day"
+ elif totaldays < 50:
+ datebin = "week"
+ else:
+ datebin = "month"
- # add dates with no activity
- while d <= enddate:
- dd = d.strftime('%d')
-
- if totaldays <= 30:
- dates.append(d.strftime('%m/%d'))
- dates_sorting.append(d.strftime('%Y/%m/%d'))
- else:
- dates.append(d.strftime('%Y/%m'))
- dates_sorting.append(d.strftime('%Y/%m'))
- durations.append(0)
- trimps.append(0)
- rscores.append(0)
- distances.append(0)
- links.append('')
- types.append('rower')
-
- try:
- rowers.append(rowers[0])
- except IndexError: # pragma: no cover
- try:
- rowers.append(str(workouts[0].user))
- except IndexError:
- rowers.append(' ')
-
- d += datetime.timedelta(days=1)
-
- thedict = {
- 'date': dates,
- 'date_sorting': dates_sorting,
- 'duration': durations,
- 'trimp': trimps,
- 'rscore': rscores,
- 'type': types,
- 'rower': rowers,
- 'distance': distances,
- 'link': links,
+ stacknames = {
+ 'TRIMP': 'trimp',
+ 'distance': 'distance',
+ 'time': 'duration',
+ 'rScore': 'rscore',
+ 'duration': 'duration',
}
- df = pd.DataFrame(thedict)
+ chart_data = {
+ 'data': data_dicts,
+ 'title': 'Activity {d1} to {d2}'.format(
+ d1=startdate.strftime("%Y-%m-%d"),
+ d2=enddate.strftime("%Y-%m-%d"),
+ ),
+ 'datebin': datebin,
+ 'colorby': stack,
+ 'stackby': stacknames[yaxis],
+ 'doreduce': True,
+ 'dosort': True,
+ 'colors': mytypes.color_map,
+ }
- if totaldays > 30 and yaxis == 'duration': # pragma: no cover
- df['duration'] = df['duration']/60
- elif yaxis == 'TRIMP':
- df.drop('duration', inplace=True, axis='columns')
- df.drop('rscore', inplace=True, axis='columns')
- df.drop('distance', inplace=True, axis='columns')
- elif yaxis == 'rScore': # pragma: no cover
- df.drop('duration', inplace=True, axis='columns')
- df.drop('trimp', inplace=True, axis='columns')
- df.drop('distance', inplace=True, axis='columns')
- elif yaxis == 'distance': # pragma: no cover
- df.drop('duration', inplace=True, axis='columns')
- df.drop('trimp', inplace=True, axis='columns')
- df.drop('rscore', inplace=True, axis='columns')
+
+ script, div = get_chart("/activity_bar", chart_data, debug=False)
- df['color'] = df['type'].apply(lambda x: mapcolors(x))
-
- df.sort_values('date_sorting', inplace=True)
-
- hv.extension('bokeh')
-
- # table = hv.Table(df,[('date','Date'),('type','Workout Type')],
- # [('duration','Minutes'),('trimp','TRIMP'),('rscore','rScore'),('link','link')])
-
- types_order = mytypes.workouttypes_ordered
- # bars=table.to.bars(['date',stack],[yaxis])
- bars = hv.Bars(df, kdims=['date', stack]).aggregate(
- function=np.sum).redim.values(types=types_order)
-
- # print(mytypes.color_map)
- bars.opts(
- opts.Bars(cmap=mytypes.color_map, show_legend=True, stacked=True,
- tools=['tap', 'hover'], width=550, xrotation=45, padding=(0, (0, .1)),
- legend_position='bottom', show_frame=True))
-
- p = hv.render(bars)
-
- p.title.text = 'Activity {d1} to {d2}'.format(
- d1=startdate.strftime("%Y-%m-%d"),
- d2=enddate.strftime("%Y-%m-%d"),
- )
-
- p.xaxis.axis_label = 'Period'
- if yaxis == 'duration':
- p.yaxis.axis_label = 'Duration (min)'
- if totaldays > 30: # pragma: no cover
- p.yaxis.axis_label = 'Duration (h)'
- elif yaxis == 'TRIMP':
- p.yaxis.axis_label = 'TRIMP'
- elif yaxis == 'distance': # pragma: no cover
- p.yaxis.axis_label = 'Distance (m)'
- else: # pragma: no cover
- p.yaxis.axis_label = 'rScore'
-
- p.width = 550
- p.height = 350
- p.toolbar_location = toolbar_location
- #p.sizing_mode = 'stretch_both'
- p.y_range.start = 0
- taptool = p.select(type=TapTool)
-
- callback = CustomJS(args={'links': df['link']}, code="""
- var index = cb_data.source.selected['1d'].indices[0];
- console.log(links);
- console.log(index);
- console.log(links[index]);
- window.location.href = links[index]
- """)
-
- taptool.js_on_event('tap', callback)
-
- script, div = components(p)
return script, div
-
-def interactive_forcecurve(theworkouts, workstrokesonly=True, plottype='scatter',
- spm_min=15, spm_max=45,
- notes='',
- dist_min=0,dist_max=0,
- work_min=0,work_max=1500):
- TOOLS = 'save,pan,box_zoom,wheel_zoom,reset,tap,hover,crosshair'
-
+def interactive_forcecurve(theworkouts):
ids = [int(w.id) for w in theworkouts]
boattype = theworkouts[0].boattype
columns = ['catch', 'slip', 'wash', 'finish', 'averageforce',
'peakforceangle', 'peakforce', 'spm', 'distance',
- 'workoutstate', 'driveenergy']
+ 'workoutstate', 'driveenergy', 'cumdist', 'workoutid']
+ columns = columns + [name for name, d in metrics.rowingmetrics]
- rowdata = dataprep.getsmallrowdata_db(columns, ids=ids,
- workstrokesonly=workstrokesonly)
+
+ rowdata = dataprep.read_data(columns, ids=ids,
+ workstrokesonly=False)
- rowdata.dropna(axis=1, how='all', inplace=True)
- rowdata.dropna(axis=0, how='any', inplace=True)
+ if rowdata.is_empty():
+ return "", "No Valid Data Available"
- workoutstatesrest = [3]
+ rowdata = dataprep.remove_nulls_pl(rowdata)
- if workstrokesonly:
- try:
- rowdata = rowdata[~rowdata['workoutstate'].isin(workoutstatesrest)]
- except KeyError: # pragma: no cover
- pass
+ data_dict = rowdata.to_dicts()
- if rowdata.empty:
- return "", "No Valid Data Available", "", ""
+ thresholdforce = 100. if 'x' in boattype else 200.
+
+ chart_data = {
+ 'title': theworkouts[0].name,
+ 'data': data_dict,
+ 'thresholdforce': thresholdforce,
+ }
- try:
- covariancematrix = np.cov(
- rowdata['peakforceangle'], y=rowdata['peakforce'])
- eig_vals, eig_vecs = np.linalg.eig(covariancematrix)
+ script, div = get_chart("/forcecurve", chart_data, debug=False)
+ return script, div
- a = rowdata['peakforceangle']-rowdata['peakforceangle'].median()
- F = rowdata['peakforce']-rowdata['peakforce'].median()
-
- Rinv = eig_vecs
- R = np.linalg.inv(Rinv)
-
- x = R[0, 0]*a+R[0, 1]*F
- y = R[1, 0]*a+R[1, 1]*F
-
- x05 = x.quantile(q=0.01)
- x25 = x.quantile(q=0.15)
- x75 = x.quantile(q=0.85)
- x95 = x.quantile(q=0.99)
-
- y05 = y.quantile(q=0.01)
- y25 = y.quantile(q=0.15)
- y75 = y.quantile(q=0.85)
- y95 = y.quantile(q=0.99)
-
- a25 = Rinv[0, 0]*x25 + rowdata['peakforceangle'].median()
- F25 = Rinv[1, 0]*x25 + rowdata['peakforce'].median()
-
- a25b = Rinv[0, 1]*y25 + rowdata['peakforceangle'].median()
- F25b = Rinv[1, 1]*y25 + rowdata['peakforce'].median()
-
- a75 = Rinv[0, 0]*x75 + rowdata['peakforceangle'].median()
- F75 = Rinv[1, 0]*x75 + rowdata['peakforce'].median()
-
- a75b = Rinv[0, 1]*y75 + rowdata['peakforceangle'].median()
- F75b = Rinv[1, 1]*y75 + rowdata['peakforce'].median()
-
- a05 = Rinv[0, 0]*x05 + rowdata['peakforceangle'].median()
- F05 = Rinv[1, 0]*x05 + rowdata['peakforce'].median()
-
- a05b = Rinv[0, 1]*y05 + rowdata['peakforceangle'].median()
- F05b = Rinv[1, 1]*y05 + rowdata['peakforce'].median()
-
- a95 = Rinv[0, 0]*x95 + rowdata['peakforceangle'].median()
- F95 = Rinv[1, 0]*x95 + rowdata['peakforce'].median()
-
- a95b = Rinv[0, 1]*y95 + rowdata['peakforceangle'].median()
- F95b = Rinv[1, 1]*y95 + rowdata['peakforce'].median()
- except KeyError: # pragma: no cover
- a25 = 0
- F25 = 0
-
- a25b = 0
- F25b = 0
-
- a75 = 0
- F75 = 0
-
- a75b = 0
- F75b = 0
-
- a05 = 0
- F05 = 0
-
- a05b = 0
- F05b = 0
-
- a95 = 0
- F95 = 0
-
- a95b = 0
- F95b = 0
-
- try:
- catchav = rowdata['catch'].median()
- catch25 = rowdata['catch'].quantile(q=0.25)
- catch75 = rowdata['catch'].quantile(q=0.75)
- catch05 = rowdata['catch'].quantile(q=0.05)
- catch95 = rowdata['catch'].quantile(q=0.95)
- except KeyError: # pragma: no cover
- catchav = 0
- catch25 = 0
- catch75 = 0
- catch05 = 0
- catch95 = 0
-
- try:
- finishav = rowdata['finish'].median()
- finish25 = rowdata['finish'].quantile(q=0.25)
- finish75 = rowdata['finish'].quantile(q=0.75)
- finish05 = rowdata['finish'].quantile(q=0.05)
- finish95 = rowdata['finish'].quantile(q=0.95)
- except KeyError: # pragma: no cover
- finishav = 0
- finish25 = 0
- finish75 = 0
- finish05 = 0
- finish95 = 0
-
- try:
- washav = (rowdata['finish']-rowdata['wash']).median()
- wash25 = (rowdata['finish']-rowdata['wash']).quantile(q=0.25)
- wash75 = (rowdata['finish']-rowdata['wash']).quantile(q=0.75)
- wash05 = (rowdata['finish']-rowdata['wash']).quantile(q=0.05)
- wash95 = (rowdata['finish']-rowdata['wash']).quantile(q=0.95)
- except KeyError: # pragma: no cover
- washav = 0
- wash25 = 0
- wash75 = 0
- wash05 = 0
- wash95 = 0
-
- try:
- slipav = (rowdata['slip']+rowdata['catch']).median()
- slip25 = (rowdata['slip']+rowdata['catch']).quantile(q=0.25)
- slip75 = (rowdata['slip']+rowdata['catch']).quantile(q=0.75)
- slip05 = (rowdata['slip']+rowdata['catch']).quantile(q=0.05)
- slip95 = (rowdata['slip']+rowdata['catch']).quantile(q=0.95)
- except KeyError: # pragma: no cover
- slipav = 0
- slip25 = 0
- slip75 = 0
- slip05 = 0
- slip95 = 0
-
- try:
- peakforceav = rowdata['peakforce'].median()
- except KeyError: # pragma: no cover
- peakforceav = 0
-
- try:
- averageforceav = rowdata['averageforce'].median()
- except KeyError: # pragma: no cover
- averageforceav = 0
-
- try:
- peakforceangleav = rowdata['peakforceangle'].median()
- except KeyError: # pragma: no cover
- peakforceangleav = 0
-
- # thresholdforce /= 4.45 # N to lbs
- thresholdforce = 100 if 'x' in boattype else 200
- points2575 = [
- (catch25, 0), # 0
- (slip25, thresholdforce), # 1
- (a75, F75), # 4
- (a25b, F25b), # 9
- (a25, F25), # 2
- (wash75, thresholdforce), # 5
- (finish75, 0), # 6
- (finish25, 0), # 7
- (wash25, thresholdforce), # 8
- (a75b, F75b), # 3
- (slip75, thresholdforce), # 10
- (catch75, 0), # 11
- ]
-
- points0595 = [
- (catch05, 0), # 0
- (slip05, thresholdforce), # 1
- (a95, F95), # 4
- (a05b, F05b), # 9
- (a05, F05), # 2
- (wash95, thresholdforce), # 5
- (finish95, 0), # 6
- (finish05, 0), # 7
- (wash05, thresholdforce), # 8
- (a95b, F95b), # 3
- (slip95, thresholdforce), # 10
- (catch95, 0), # 11
- ]
-
- angles2575 = []
- forces2575 = []
-
- for x, y in points2575:
- angles2575.append(x)
- forces2575.append(y)
-
- angles0595 = []
- forces0595 = []
-
- for x, y in points0595:
- angles0595.append(x)
- forces0595.append(y)
-
- x = [catchav,
- slipav,
- peakforceangleav,
- washav,
- finishav]
-
- y = [0, thresholdforce,
- peakforceav,
- thresholdforce, 0]
-
- source = ColumnDataSource(
- data=dict(
- x=x,
- y=y,
- ))
-
- sourceslipwash = ColumnDataSource(
- data=dict(
- xslip=[slipav, washav],
- yslip=[thresholdforce, thresholdforce]
- )
- )
-
- source2 = ColumnDataSource(
- rowdata
- )
-
- if plottype == 'scatter': # pragma: no cover
- try:
- sourcepoints = ColumnDataSource(
- data=dict(
- peakforceangle=rowdata['peakforceangle'],
- peakforce=rowdata['peakforce']
- )
- )
- except KeyError:
- sourcepoints = ColumnDataSource(
- data=dict(
- peakforceangle=[],
- peakforce=[]
- )
- )
- else:
- sourcepoints = ColumnDataSource(
- data=dict(
- peakforceangle=[],
- peakforce=[]
- ))
-
- sourcerange = ColumnDataSource(
- data=dict(
- x2575=angles2575,
- y2575=forces2575,
- x0595=angles0595,
- y0595=forces0595,
- )
- )
-
- plot = figure(tools=TOOLS,
- toolbar_sticky=False, toolbar_location="above", width=800, height=600)
- #plot.sizing_mode = 'stretch_both'
-
- # add watermark
- watermarkurl = "/static/img/logo7.png"
-
- watermarkrange = Range1d(start=0, end=1)
- watermarkalpha = 0.6
- watermarkx = 0.99
- watermarky = 0.01
- watermarkw = 184
- watermarkh = 35
- watermarkanchor = 'bottom_right'
- plot.extra_y_ranges = {"watermark": watermarkrange}
- plot.extra_x_ranges = {"watermark": watermarkrange}
-
- plot.image_url([watermarkurl], watermarkx, watermarky,
- watermarkw, watermarkh,
- global_alpha=watermarkalpha,
- w_units='screen',
- h_units='screen',
- anchor=watermarkanchor,
- dilate=True,
- x_range_name="watermark",
- y_range_name="watermark",
- )
-
- avf = Span(location=averageforceav, dimension='width', line_color='blue',
- line_dash=[6, 6], line_width=2)
-
- plot.patch('x0595', 'y0595', source=sourcerange, color="red", alpha=0.05)
- plot.patch('x2575', 'y2575', source=sourcerange, color="red", alpha=0.2)
- plot.line('x', 'y', source=source, color="red")
- plot.circle('xslip', 'yslip', source=sourceslipwash, color="red")
-
- plot.circle('peakforceangle', 'peakforce',
- source=sourcepoints, color='black', alpha=0.1)
-
- if plottype == 'line':
- multilinedatax = []
- multilinedatay = []
- for i in range(len(rowdata)):
- try:
- x = [
- rowdata['catch'].values[i],
- rowdata['slip'].values[i]+rowdata['catch'].values[i],
- rowdata['peakforceangle'].values[i],
- rowdata['finish'].values[i]-rowdata['wash'].values[i],
- rowdata['finish'].values[i]
- ]
-
- y = [
- 0,
- thresholdforce,
- rowdata['peakforce'].values[i],
- thresholdforce,
- 0]
- except KeyError: # pragma: no cover
- x = [0, 0]
- y = [0, 0]
-
- multilinedatax.append(x)
- multilinedatay.append(y)
-
- sourcemultiline = ColumnDataSource(dict(
- x=multilinedatax,
- y=multilinedatay,
- ))
-
- sourcemultiline2 = ColumnDataSource(dict(
- x=multilinedatax,
- y=multilinedatay,
- ))
-
- glyph = MultiLine(xs='x', ys='y', line_color='black', line_alpha=0.05)
- plot.add_glyph(sourcemultiline, glyph)
- else: # pragma: no cover
- sourcemultiline = ColumnDataSource(dict(
- x=[], y=[]))
-
- sourcemultiline2 = ColumnDataSource(dict(
- x=[], y=[]))
-
- plot.line('x', 'y', source=source, color="red")
-
- plot.add_layout(avf)
-
- peakflabel = Label(x=760, y=460, x_units='screen', y_units='screen',
- text="Fpeak: {peakforceav:6.2f}".format(
- peakforceav=peakforceav),
- background_fill_alpha=.7,
- background_fill_color='white',
- text_color='blue',
- )
-
- avflabel = Label(x=770, y=430, x_units='screen', y_units='screen',
- text="Favg: {averageforceav:6.2f}".format(
- averageforceav=averageforceav),
- background_fill_alpha=.7,
- background_fill_color='white',
- text_color='blue',
- )
-
- catchlabel = Label(x=765, y=400, x_units='screen', y_units='screen',
- text="Catch: {catchav:6.2f}".format(catchav=catchav),
- background_fill_alpha=0.7,
- background_fill_color='white',
- text_color='red',
- )
-
- peakforceanglelabel = Label(x=725, y=370, x_units='screen', y_units='screen',
- text="Peak angle: {peakforceangleav:6.2f}".format(
- peakforceangleav=peakforceangleav),
- background_fill_alpha=0.7,
- background_fill_color='white',
- text_color='red',
- )
-
- finishlabel = Label(x=760, y=340, x_units='screen', y_units='screen',
- text="Finish: {finishav:6.2f}".format(
- finishav=finishav),
- background_fill_alpha=0.7,
- background_fill_color='white',
- text_color='red',
- )
-
- sliplabel = Label(x=775, y=310, x_units='screen', y_units='screen',
- text="Slip: {slipav:6.2f}".format(slipav=slipav-catchav),
- background_fill_alpha=0.7,
- background_fill_color='white',
- text_color='red',
- )
-
- washlabel = Label(x=765, y=280, x_units='screen', y_units='screen',
- text="Wash: {washav:6.2f}".format(
- washav=finishav-washav),
- background_fill_alpha=0.7,
- background_fill_color='white',
- text_color='red',
- )
-
- lengthlabel = Label(x=755, y=250, x_units='screen', y_units='screen',
- text="Length: {length:6.2f}".format(
- length=finishav-catchav),
- background_fill_alpha=0.7,
- background_fill_color='white',
- text_color='green'
- )
-
- efflengthlabel = Label(x=690, y=220, x_units='screen', y_units='screen',
- text="Effective Length: {length:6.2f}".format(
- length=washav-slipav),
- background_fill_alpha=0.7,
- background_fill_color='white',
- text_color='green'
- )
-
- annolabel = Label(x=50, y=450, x_units='screen', y_units='screen',
- text='',
- background_fill_alpha=0.7,
- background_fill_color='white',
- text_color='black',
- )
-
- sliderlabel = Label(x=10, y=470, x_units='screen', y_units='screen',
- text='',
- background_fill_alpha=0.7,
- background_fill_color='white',
- text_color='black', text_font_size='10pt',
- )
-
- plot.add_layout(peakflabel)
- plot.add_layout(peakforceanglelabel)
- plot.add_layout(avflabel)
- plot.add_layout(catchlabel)
- plot.add_layout(sliplabel)
- plot.add_layout(washlabel)
- plot.add_layout(finishlabel)
- plot.add_layout(annolabel)
- plot.add_layout(sliderlabel)
- plot.add_layout(lengthlabel)
- plot.add_layout(efflengthlabel)
-
- plot.xaxis.axis_label = "Angle"
- plot.yaxis.axis_label = "Force (N)"
- try:
- plot.title.text = theworkouts[0].name
- except ValueError: # pragma: no cover
- plot.title.text = ""
- plot.title.text_font_size = "1.0em"
-
- yrange1 = Range1d(start=0, end=900)
- plot.y_range = yrange1
-
- xrange1 = Range1d(start=yaxmaxima['catch'], end=yaxmaxima['finish'])
- plot.x_range = xrange1
-
- callback = CustomJS(args=dict(
- source=source,
- source2=source2,
- sourceslipwash=sourceslipwash,
- sourcepoints=sourcepoints,
- avf=avf,
- avflabel=avflabel,
- catchlabel=catchlabel,
- finishlabel=finishlabel,
- sliplabel=sliplabel,
- washlabel=washlabel,
- peakflabel=peakflabel,
- peakforceanglelabel=peakforceanglelabel,
- annolabel=annolabel,
- sliderlabel=sliderlabel,
- lengthlabel=lengthlabel,
- efflengthlabel=efflengthlabel,
- plottype=plottype,
- sourcemultiline=sourcemultiline,
- sourcemultiline2=sourcemultiline2
- ), code="""
- var data = source.data
- var data2 = source2.data
- var dataslipwash = sourceslipwash.data
- var datapoints = sourcepoints.data
- var multilines = sourcemultiline.data
- var multilines2 = sourcemultiline2.data
- var plottype = plottype
-
- var multilinesx = multilines2['x']
- var multilinesy = multilines2['y']
-
- var x = data['x']
- var y = data['y']
-
- var xslip = dataslipwash['xslip']
-
- var spm1 = data2['spm']
- var distance1 = data2['distance']
- var driveenergy1 = data2['driveenergy']
-
- var thresholdforce = y[1]
-
- var c = source2.data['catch']
- var finish = data2['finish']
- var slip = data2['slip']
- var wash = data2['wash']
- var peakforceangle = data2['peakforceangle']
- var peakforce = data2['peakforce']
- var averageforce = data2['averageforce']
-
- var peakforcepoints = datapoints['peakforce']
- var peakforceanglepoints = datapoints['peakforceangle']
-
- var annotation = annotation.value
- var minspm = minspm.value
- var maxspm = maxspm.value
- var mindist = mindist.value
- var maxdist = maxdist.value
- var minwork = minwork.value
- var maxwork = maxwork.value
-
- sliderlabel.text = 'SPM: '+minspm.toFixed(0)+'-'+maxspm.toFixed(0)
- sliderlabel.text += ', Dist: '+mindist.toFixed(0)+'-'+maxdist.toFixed(0)
- sliderlabel.text += ', WpS: '+minwork.toFixed(0)+'-'+maxwork.toFixed(0)
-
- var catchav = 0
- var finishav = 0
- var slipav = 0
- var washav = 0
- var peakforceangleav = 0
- var averageforceav = 0
- var peakforceav = 0
- var count = 0
-
- datapoints['peakforceangle'] = []
- datapoints['peakforce'] = []
- multilines['x'] = []
- multilines['y'] = []
-
- for (var i=0; i=minspm && spm1[i]<=maxspm) {
- if (distance1[i]>=mindist && distance1[i]<=maxdist) {
- if (driveenergy1[i]>=minwork && driveenergy1[i]<=maxwork) {
- if (plottype=='scatter') {
- datapoints['peakforceangle'].push(peakforceangle[i])
- datapoints['peakforce'].push(peakforce[i])
- }
- if (plottype=='line') {
- multilines['x'].push(multilinesx[i])
- multilines['y'].push(multilinesy[i])
- }
- catchav += c[i]
- finishav += finish[i]
- slipav += slip[i]
- washav += wash[i]
- peakforceangleav += peakforceangle[i]
- averageforceav += averageforce[i]
- peakforceav += peakforce[i]
- count += 1
- }
- }
- }
- }
-
- catchav /= count
- finishav /= count
- slipav /= count
- washav /= count
- peakforceangleav /= count
- peakforceav /= count
- averageforceav /= count
-
- data['x'] = [catchav,catchav+slipav,peakforceangleav,finishav-washav,finishav]
- data['y'] = [0,thresholdforce,peakforceav,thresholdforce,0]
-
- dataslipwash['xslip'] = [catchav+slipav,finishav-washav]
- dataslipwash['yslip'] = [thresholdforce,thresholdforce]
-
- var length = finishav-catchav
- var efflength = length-slipav-washav
-
- avf.location = averageforceav
- avflabel.text = 'Favg: '+averageforceav.toFixed(2)
- catchlabel.text = 'Catch: '+catchav.toFixed(2)
- finishlabel.text = 'Finish: '+finishav.toFixed(2)
- sliplabel.text = 'Slip: '+slipav.toFixed(2)
- washlabel.text = 'Wash: '+washav.toFixed(2)
- peakflabel.text = 'Fpeak: '+peakforceav.toFixed(2)
- peakforceanglelabel.text = 'Peak angle: '+peakforceangleav.toFixed(2)
- annolabel.text = annotation
- lengthlabel.text = 'Length: '+length.toFixed(2)
- efflengthlabel.text = 'Effective Length: '+efflength.toFixed(2)
-
- // console.log(count);
- // console.log(multilines['x'].length);
- // console.log(multilines['y'].length);
-
- // change DOM elements
- document.getElementById("id_spm_min").value = minspm;
- document.getElementById("id_spm_max").value = maxspm;
- document.getElementById("id_dist_min").value = mindist;
- document.getElementById("id_dist_max").value = maxdist;
- document.getElementById("id_notes").value = annotation;
- document.getElementById("id_work_min").value = minwork;
- document.getElementById("id_work_max").value = maxwork;
-
- // source.trigger('change');
- source.change.emit();
- sourceslipwash.change.emit()
- sourcepoints.change.emit();
- sourcemultiline.change.emit();
- """)
-
- annotation = TextInput(
- width=140, title="Type your plot notes here", value="", name="annotation")
- annotation.js_on_change('value', callback)
- callback.args["annotation"] = annotation
-
- slider_spm_min = Slider(width=140, start=15.0, end=55, value=15, step=.1,
- title="Min SPM", name="min_spm_slider")
- slider_spm_min.js_on_change('value', callback)
- callback.args["minspm"] = slider_spm_min
-
- slider_spm_max = Slider(width=140, start=15.0, end=55, value=55, step=.1,
- title="Max SPM", name="max_spm_slider")
- slider_spm_max.js_on_change('value', callback)
- callback.args["maxspm"] = slider_spm_max
-
- slider_work_min = Slider(width=140, start=0, end=1500, value=0, step=10,
- title="Min Work per Stroke", name="min_work_slider")
- slider_work_min.js_on_change('value', callback)
- callback.args["minwork"] = slider_work_min
-
- slider_work_max = Slider(width=140, start=0, end=1500, value=1500, step=10,
- title="Max Work per Stroke", name="max_work_slider")
- slider_work_max.js_on_change('value', callback)
- callback.args["maxwork"] = slider_work_max
-
- distmax = 100+100*int(rowdata['distance'].max()/100.)
-
- slider_dist_min = Slider(width=140, start=0, end=distmax, value=0, step=50,
- title="Min Distance", name="min_dist_slider")
- slider_dist_min.js_on_change('value', callback)
- callback.args["mindist"] = slider_dist_min
-
- if dist_max == 0:
- dist_max = distmax
-
- slider_dist_max = Slider(width=140, start=0, end=distmax, value=distmax,
- step=50,
- title="Max Distance", name="max_dist_slider")
- slider_dist_max.js_on_change('value', callback)
- callback.args["maxdist"] = slider_dist_max
-
- thesliders = layoutcolumn([annotation,
- slider_spm_min,
- slider_spm_max,
- slider_dist_min,
- slider_dist_max,
- slider_work_min,
- slider_work_max,
- ]
- )
-
- mylayout = layoutrow([thesliders, plot])
-
- #mylayout.sizing_mode = 'stretch_both'
-
- script, div = components(mylayout)
- js_resources = INLINE.render_js()
- css_resources = INLINE.render_css()
-
- return [script, div, js_resources, css_resources]
def weightfromrecord(row,metricchoice):
- vv = row[metricchoice]
+ vv = row[metricchoice][0]
if vv > 0:
return vv
if metricchoice == 'rscore': # pragma: no cover
@@ -1530,18 +508,19 @@ def getfatigues(
lambda_c = 2/(kfitness+1)
nrdays = (enddate-startdate).days
+
for i in range(nrdays+1):
date = startdate+datetime.timedelta(days=i)
datekey = date.strftime('%Y-%m-%d')
weight = 0
try:
- df2 = df.loc[date.date()]
+ df2 = df.filter(pl.col("date") == date.date())
- if type(df2) == pd.Series: # pragma: no cover
+ if type(df2) == pl.Series: # pragma: no cover
weight += weightfromrecord(df2,metricchoice)
else:
- for index, row in df2.iterrows():
+ for row in df2.iter_slices(n_rows=1):
weight += weightfromrecord(row,metricchoice)
except KeyError:
pass
@@ -1568,7 +547,6 @@ def goldmedalscorechart(user, startdate=None, enddate=None):
enddate = arrow.get(enddate).datetime.replace(
hour=0, minute=0, second=0, microsecond=0)
- TOOLS = 'save,pan,box_zoom,wheel_zoom,reset,tap,hover,crosshair'
# marker workouts
workouts = Workout.objects.filter(user=user.rower, date__gte=startdate,
@@ -1585,20 +563,20 @@ def goldmedalscorechart(user, startdate=None, enddate=None):
testduration = [
w.goldmedalseconds if w.rankingpiece else 0 for w in markerworkouts]
- df = pd.DataFrame({
+ df = pl.DataFrame({
'id': outids,
'date': dates,
'testpower': testpower,
'testduration': testduration,
})
- df.sort_values(['date'], inplace=True)
+ df = df.sort('date')
+ df = df.drop_nulls()
- mask = df['testpower'].isnull()
- dates = df.mask(mask)['date'].dropna().values
- testpower = df.mask(mask)['testpower'].dropna().values
- ids = df.mask(mask)['id'].dropna().values
-
- outids = df.mask(mask)['id'].dropna().unique()
+ dates = df['date']
+ testpower = df['testpower']
+ ids = df['id']
+
+ outids = ids.unique()
# all workouts
alldates, alltestpower, allduration, allids = all_goldmedalstandards(
@@ -1640,7 +618,7 @@ def goldmedalscorechart(user, startdate=None, enddate=None):
duration.append(np.nan)
workoutid.append(0)
- df = pd.DataFrame({
+ df = pl.DataFrame({
'markerscore': markerscore,
'markerduration': markerduration,
'score': score,
@@ -1649,95 +627,29 @@ def goldmedalscorechart(user, startdate=None, enddate=None):
'id': workoutid,
})
- df['url'] = df['id'].apply(lambda x: settings.SITE_URL +
- '/rowers/workout/{id}/'.format(id=encoder.encode_hex(x)))
- df['workout'] = df['id'].apply(lambda x: workoutname(x))
+ df = df.with_columns((pl.col("id").map_elements(lambda x: settings.SITE_URL +
+ '/rowers/workout/{id}/'.format(id=encoder.encode_hex(x)))).alias("url"))
+ df = df.with_columns((pl.col("id").map_elements(lambda x: workoutname(x))).alias("workout"))
- df.sort_values(['date'], inplace=True)
+ df = df.sort('date')
# find index values where score is max
- idx = df.groupby(['date'])['score'].transform(max) == df['score']
- df = df[idx]
+ dfmax = df.group_by("date", maintain_order=True).max()
+ dfmax = dfmax.fill_nan(0)
+ dfmax = dfmax.with_columns((pl.col("date").dt.strftime("%Y-%m-%d")).alias("date"))
+ dfmax = dfmax.with_columns((pl.col("duration").map_elements(lambda x: totaltime_sec_to_string(x, shorten=True))).alias("duration"))
+ dfmax = dfmax.with_columns((pl.col("markerduration").map_elements(lambda x: totaltime_sec_to_string(x, shorten=True))).alias("markerduration"))
- source = ColumnDataSource(
- data=dict(
- markerscore=df['markerscore'],
- score=df['score'],
- markerduration=df['markerduration'].apply(
- lambda x: totaltime_sec_to_string(x, shorten=True)),
- duration=df['duration'].apply(
- lambda x: totaltime_sec_to_string(x, shorten=True)),
- date=df['date'],
- fdate=df['date'].map(lambda x: x.strftime('%d-%m-%Y')),
- url=df['url'],
- workout=df['workout']
- )
- )
-
- plot = figure(tools=TOOLS, x_axis_type='datetime',
- width=900, height=600,
- toolbar_location='above',
- toolbar_sticky=False)
-
- # add watermark
- watermarkurl = "/static/img/logo7.png"
- watermarkrange = Range1d(start=0, end=1)
- watermarkalpha = 0.6
- watermarkx = 0.99
- watermarky = 0.01
- watermarkw = 184
- watermarkh = 35
- watermarkanchor = 'bottom_right'
- plot.extra_y_ranges = {"watermark": watermarkrange}
- plot.extra_x_ranges = {"watermark": watermarkrange}
-
- plot.image_url([watermarkurl], watermarkx, watermarky,
- watermarkw, watermarkh,
- global_alpha=watermarkalpha,
- w_units='screen',
- h_units='screen',
- anchor=watermarkanchor,
- dilate=True,
- x_range_name="watermark",
- y_range_name="watermark",
- )
-
- plot.xaxis.axis_label = 'Date'
- plot.yaxis.axis_label = 'Gold Medal Score'
-
- plot.circle('date', 'score', source=source, fill_color='blue',
- size=10,
- legend_label='Workouts')
-
- plot.circle('date', 'markerscore', source=source, fill_color='red',
- size=10,
- legend_label='Marker Workouts')
-
- plot.legend.location = "bottom_left"
-
- plot.x_range = Range1d(
- startdate, enddate+datetime.timedelta(days=5),
- )
-
- hover = plot.select(dict(type=HoverTool))
-
- hover.tooltips = OrderedDict([
- ('Marker', '@markerscore{int}'),
- ('Test', '@markerduration'),
- ('Score', '@score{int}'),
- ('Duration', '@duration'),
- ('Date', '@fdate'),
- ('Workout', '@workout')
- ])
-
- taptool = plot.select(type=TapTool)
- taptool.callback = OpenURL(url='@url')
-
- script, div = components(plot)
+ data_dicts = dfmax.to_dicts()
+ chart_data = {
+ 'data': data_dicts
+ }
+ script, div = get_chart("/markerworkouts", chart_data)
return script, div, outids
+
def performance_chart(user, startdate=None, enddate=None, kfitness=42, kfatigue=7,
metricchoice='trimp', doform=False, dofatigue=False,
showtests=False):
@@ -1785,10 +697,9 @@ def performance_chart(user, startdate=None, enddate=None, kfitness=42, kfatigue=
}
records.append(dd)
- df = pd.DataFrame.from_records(records)
- if df.empty: # pragma: no cover
+ df = pl.from_records(records)
+ if df.is_empty(): # pragma: no cover
return ['', 'No Data', 0, 0, 0, outids]
- df.set_index('date', inplace=True)
markerworkouts = Workout.objects.filter(
user=user.rower, date__gte=startdate-datetime.timedelta(days=90),
@@ -1820,7 +731,7 @@ def performance_chart(user, startdate=None, enddate=None, kfitness=42, kfatigue=
kfatigue, kfitness
)
- df = pd.DataFrame({
+ df = pl.DataFrame({
'date': dates,
'testpower': testpower,
'testduration': testduration,
@@ -1835,186 +746,48 @@ def performance_chart(user, startdate=None, enddate=None, kfitness=42, kfatigue=
endform = endfitness-endfatigue
if modelchoice == 'banister': # pragma: no cover
- df['fatigue'] = k2*df['fatigue']
- df['fitness'] = p0+k1*df['fitness']
+ df = df.with_columns((pl.col("fatigue")*k2))
+ df = df.with_columns((p0+pl.col("fitness")*k1))
- df['form'] = df['fitness']-df['fatigue']
+ df = df.with_columns((pl.col("fitness")-pl.col("fatigue")).alias("form"))
+ df = df.sort("date")
+
+ df = df.group_by('date').max()
+ startdate = startdate.replace(tzinfo=None)
+ startdate = pytz.utc.localize(startdate)
- df.sort_values(['date'], inplace=True)
- df = df.groupby(['date']).max()
- df['date'] = df.index.values
- mask = df['date'] > np.datetime64(startdate.astimezone(
- tz=datetime.timezone.utc).replace(tzinfo=None))
- df = df.loc[mask]
+ df = df.filter(pl.col("date") > startdate)
- source = ColumnDataSource(
- data=dict(
- testpower=df['testpower'],
- testduration=df['testduration'].apply(
- lambda x: totaltime_sec_to_string(x, shorten=True)),
- date=df['date'],
- fdate=df['date'].map(lambda x: x.strftime('%d-%m-%Y')),
- fitness=df['fitness'],
- fatigue=df['fatigue'],
- form=df['form'],
- impulse=df['impulse']
- )
- )
+ df2 = pl.DataFrame({
+ "testpower" :df['testpower'],
+ "testduration":df['testduration'].apply(
+ lambda x: totaltime_sec_to_string(x, shorten=True)),
+ "fitness":df['fitness'],
+ "fatigue":df['fatigue'],
+ "form":df['form'],
+ "impulse":df['impulse'],
+ "date": df['date'].dt.strftime('%Y-%m-%d'),
+ })
- plot = figure(tools=TOOLS, x_axis_type='datetime',
- width=900, height=300,
- toolbar_location="above",
- toolbar_sticky=False)
+ df2 = df2.fill_nan(0)
- # add watermark
- watermarkurl = "/static/img/logo7.png"
- watermarkrange = Range1d(start=0, end=1)
- watermarkalpha = 0.6
- watermarkx = 0.99
- watermarky = 0.01
- watermarkw = 184
- watermarkh = 35
- watermarkanchor = 'bottom_right'
- plot.extra_y_ranges = {"watermark": watermarkrange}
- plot.extra_x_ranges = {"watermark": watermarkrange}
+ data_dict = df2.to_dicts()
- plot.image_url([watermarkurl], watermarkx, watermarky,
- watermarkw, watermarkh,
- global_alpha=watermarkalpha,
- w_units='screen',
- h_units='screen',
- anchor=watermarkanchor,
- dilate=True,
- x_range_name="watermark",
- y_range_name="watermark",
- )
-
- fitlabel = 'Fitness'
- fatiguelabel = 'Fatigue'
- formlabel = 'Freshness'
- rightaxlabel = 'Freshness'
- if dofatigue: # pragma: no cover
- yaxlabel = 'Fitness/Fatigue'
- else: # pragma: no cover
- yaxlabel = 'Fitness'
-
- if modelchoice == 'banister': # pragma: no cover
- fitlabel = 'PTE (fitness)'
- fatiguelabel = 'NTE (fatigue)'
- formlabel = 'Performance'
- rightaxlabel = 'Performance'
- if dofatigue:
- yaxlabel = 'PTE/NTE'
- else:
- yaxlabel = 'PTE'
+ chart_data = {
+ 'data': data_dict,
+ 'title': 'Performance Manager '+user.first_name,
+ 'plotform' : doform,
+ 'plotfatigue': dofatigue,
+ }
- plot.xaxis.axis_label = None
- plot.yaxis.axis_label = yaxlabel
-
- y2rangemin = df.loc[:, ['form']].min().min()
- y2rangemax = df.loc[:, ['form']].max().max()
-
- if dofatigue: # pragma: no cover
- y1rangemax = df.loc[:, ['fitness', 'fatigue']].max().max()*1.02
- else: # pragma: no cover
- y1rangemax = df.loc[:, ['fitness']].max().max()*1.02
-
- if doform: # pragma: no cover
- plot.extra_y_ranges["yax2"] = Range1d(start=y2rangemin, end=y2rangemax)
- plot.add_layout(LinearAxis(y_range_name="yax2",
- axis_label=rightaxlabel), "right")
-
- plot.line('date', 'fitness', source=source, color='blue',
- legend_label=fitlabel)
- band = Band(base='date', upper='fitness', lower=0, source=source, level='underlay',
- fill_alpha=0.2, fill_color='blue')
- plot.add_layout(band)
-
- if dofatigue: # pragma: no cover
- plot.line('date', 'fatigue', source=source, color='red',
- legend_label=fatiguelabel)
- if doform: # pragma: no cover
- plot.line('date', 'form', source=source, color='green',
- legend_label=formlabel, y_range_name="yax2")
-
- plot.legend.location = "top_left"
-
- #plot.sizing_mode = 'scale_both'
-
- startdate = datetime.datetime.combine(
- startdate, datetime.datetime.min.time())
- enddate = datetime.datetime.combine(enddate, datetime.datetime.min.time())
-
- xrange = Range1d(
- startdate, enddate,
- )
- plot.x_range = xrange
- plot.y_range = Range1d(
- start=0, end=y1rangemax,
- )
- plot.title.text = 'Performance Manager '+user.first_name
-
- hover = plot.select(dict(type=HoverTool))
-
- linked_crosshair = CrosshairTool(dimensions='height')
-
- hover.tooltips = OrderedDict([
- ('Date', '@fdate'),
- (fitlabel, '@fitness{int}'),
- (fatiguelabel, '@fatigue{int}'),
- (formlabel, '@form{int}'),
- ('Impulse', '@impulse{int}')
- ])
-
- if showtests:
- hover.tooltips = OrderedDict([
- ('Date', '@fdate'),
- (fitlabel, '@fitness{int}'),
- (fatiguelabel, '@fatigue{int}'),
- (formlabel, '@form{int}'),
- ('Impulse', '@impulse{int}'),
- ('Gold Medal Score', '@testpower{int}'),
- ('Test', '@testduration'),
- ])
-
- plot2 = figure(tools=TOOLS2, x_axis_type='datetime',
- width=900, height=150,
- toolbar_location=None,
- toolbar_sticky=False)
-
- plot2.x_range = xrange
- plot2.y_range = Range1d(0, df['impulse'].max())
-
- plot2.vbar(x=df['date'], top=df['impulse'], color='gray')
- plot2.vbar(x=df['date'], top=0*df['testpower']+df['impulse'], color='red')
-
- #plot2.sizing_mode = 'scale_both'
- plot2.yaxis.axis_label = 'Impulse'
- plot2.xaxis.axis_label = 'Date'
-
- plot.add_tools(linked_crosshair)
- plot2.add_tools(linked_crosshair)
-
- mylayout = layoutcolumn([plot, plot2])
-
- try:
- script, div = components(mylayout)
- except Exception as e: # pragma: no cover
- df.dropna(inplace=True, axis=0, how='any')
- return (
- '',
- 'Something went wrong with the chart ({nrworkouts} workouts, {nrdata} datapoints, error {e})'.format(
- nrworkouts=workouts.count(),
- nrdata=len(df),
- e=e,
- ), 0, 0, 0, []
- )
+ script, div = get_chart("/performance", chart_data)
return [script, div, endfitness, endfatigue, endform, outids]
+
def interactive_histoall(theworkouts, histoparam, includereststrokes,
spmmin=0, spmmax=55,
extratitle='',
@@ -2023,25 +796,19 @@ def interactive_histoall(theworkouts, histoparam, includereststrokes,
ids = [int(w.id) for w in theworkouts]
+ columns = [histoparam, 'spm', 'driveenergy', 'distance', 'workoutstate', 'workoutid']
+
workstrokesonly = not includereststrokes
- rowdata = dataprep.getsmallrowdata_db(
- [histoparam], ids=ids, doclean=True, workstrokesonly=workstrokesonly)
+ rowdata = dataprep.read_data(
+ columns, ids=ids, doclean=True, workstrokesonly=workstrokesonly)
- rowdata.dropna(axis=0, how='any', inplace=True)
+ rowdata = rowdata.fill_nan(None).drop_nulls()
- rowdata = dataprep.filter_df(rowdata, 'spm', spmmin, largerthan=True)
- rowdata = dataprep.filter_df(rowdata, 'spm', spmmax, largerthan=False)
-
- rowdata = dataprep.filter_df(
- rowdata, 'driveenergy', workmin, largerthan=True)
- rowdata = dataprep.filter_df(
- rowdata, 'driveenergy', workmax, largerthan=False)
-
- if rowdata.empty:
+ if rowdata.is_empty():
return "", "No Valid Data Available"
try:
- histopwr = rowdata[histoparam].values
+ histopwr = rowdata[histoparam].to_numpy()
except KeyError:
return "", "No data"
if len(histopwr) == 0: # pragma: no cover
@@ -2056,545 +823,32 @@ def interactive_histoall(theworkouts, histoparam, includereststrokes,
histopwr = histopwr[histopwr > yaxminima[histoparam]]
histopwr = histopwr[histopwr < yaxmaxima[histoparam]]
- plot = figure(tools=TOOLS, width=900,
- toolbar_sticky=False,
- toolbar_location="above"
- )
+ data_dict = {"data": histopwr.tolist(),
+ "metric": metricsdicts[histoparam]["verbose_name"]}
- if extratitle:
- plot.title.text = extratitle
+ script, div = get_chart("/histogram", data_dict, debug=False)
-
- # add watermark
- watermarkurl = "/static/img/logo7.png"
- watermarkrange = Range1d(start=0, end=1)
- watermarkalpha = 0.6
- watermarkx = 0.99
- watermarky = 0.01
- watermarkw = 184
- watermarkh = 35
- watermarkanchor = 'bottom_right'
- plot.extra_y_ranges = {"watermark": watermarkrange}
- plot.extra_x_ranges = {"watermark": watermarkrange}
-
- plot.image_url([watermarkurl], watermarkx, watermarky,
- watermarkw, watermarkh,
- global_alpha=watermarkalpha,
- w_units='screen',
- h_units='screen',
- anchor=watermarkanchor,
- dilate=True,
- x_range_name="watermark",
- y_range_name="watermark",
- )
-
- hist, edges = np.histogram(histopwr, bins=150)
-
- histsum = np.cumsum(hist)
- histsum = 100.*histsum/max(histsum)
-
- hist_norm = 100.*hist/float(hist.sum())
-
- source = ColumnDataSource(
- data=dict(
- left=edges[:-1],
- right=edges[1:],
- histsum=histsum,
- hist_norm=hist_norm,
- )
- )
-
-
-# plot.quad(top='hist_norm',bottom=0,left=edges[:-1],right=edges[1:])
- plot.quad(top='hist_norm', bottom=0, left='left',
- right='right', source=source)
-
- plot.xaxis.axis_label = axlabels[histoparam]
- plot.yaxis.axis_label = "% of strokes"
- plot.y_range = Range1d(0, 1.05*max(hist_norm))
-
- hover = plot.select(dict(type=HoverTool))
-
- hover.tooltips = OrderedDict([
- (axlabels[histoparam], '@left{int}'),
- ('% of strokes', '@hist_norm'),
- ('Cumulative %', '@histsum{int}'),
- ])
-
- hover.mode = 'mouse'
-
- plot.extra_y_ranges["fraction"] = Range1d(start=0, end=105)
- plot.line('right', 'histsum', source=source, color="red",
- y_range_name="fraction")
- plot.add_layout(LinearAxis(y_range_name="fraction",
- axis_label="Cumulative % of strokes"), 'right')
-
- #plot.sizing_mode = 'stretch_both'
-
- annolabel = Label(x=50, y=450, x_units='screen', y_units='screen',
- text='',
- background_fill_alpha=0.7,
- background_fill_color='white',
- text_color='black',
- )
-
- plot.add_layout(annolabel)
-
- callback = CustomJS(args=dict(
- annolabel=annolabel,
- ), code="""
- var annotation = annotation.value
- annolabel.text = annotation
- """)
-
- annotation = TextInput(
- width=140, title="Type your plot notes here", value="")
- annotation.js_on_change('value', callback)
- callback.args["annotation"] = annotation
-
- mylayout = layoutcolumn([annotation, plot])
-
- try:
- script, div = components(mylayout)
- except ValueError: # pragma: no cover
- script = ''
- div = ''
-
- return [script, div]
+ return script, div
+
def course_map(course):
- latmean, lonmean, coordinates = course_coord_center(course)
- if course.with_cn_nav_waypoints:
- latmean, lonmean, coordinates = course_coord_crewnerd_navigation(course)
- lat_min, lat_max, long_min, long_max = course_coord_maxmin(course)
-
- coordinates = course_spline(coordinates)
-
- scoordinates = "["
-
- for index, row in coordinates.iterrows():
- scoordinates += """[{x},{y}],
- """.format(
- x=row['latitude'],
- y=row['longitude']
- )
-
- scoordinates += "]"
-
- polygons = GeoPolygon.objects.filter(
- course=course).order_by("order_in_course")
-
- plabels = ''
-
- for p in polygons:
- coords = polygon_coord_center(p)
-
- plabels += """
- var marker = L.marker([{latbegin}, {longbegin}]).addTo(mymap);
- marker.bindPopup("{name}");
-
- """.format(
- latbegin=coords[0],
- longbegin=coords[1],
- name=p.name
- )
-
- pcoordinates = """[
- """
-
- for p in polygons:
- pcoordinates += """[
- ["""
-
- points = GeoPoint.objects.filter(polygon=p).order_by("order_in_poly")
-
- for pt in points:
- pcoordinates += "[{x},{y}],".format(
- x=pt.latitude,
- y=pt.longitude
- )
-
- # remove last comma
- pcoordinates = pcoordinates[:-1]
- pcoordinates += """]
- ],
- """
-
- pcoordinates += """
- ]"""
-
- script = """
-
- """.format(
- id=course.id,
- latmean=latmean,
- lonmean=lonmean,
- scoordinates=scoordinates,
- pcoordinates=pcoordinates,
- plabels=plabels
- )
-
- div = """
-
- """.format(
- id=course.id,
- )
+ course_dict = GeoCourseSerializer(course).data
+
+ script, div = get_chart("/map", course_dict)
+
return script, div
-def get_map_script_course(
- latmean,
- lonmean,
- latbegin,
- latend,
- longbegin,
- longend,
- scoordinates,
- course,
-): # pragma: no cover
- latmean, lonmean, coordinates = course_coord_center(course)
- lat_min, lat_max, long_min, long_max = course_coord_maxmin(course)
-
- coordinates = course_spline(coordinates)
-
- scoordinates = "["
-
- for index, row in coordinates.iterrows():
- scoordinates += """[{x},{y}],
- """.format(
- x=row['latitude'],
- y=row['longitude']
- )
-
- scoordinates += "]"
-
- polygons = GeoPolygon.objects.filter(
- course=course).order_by("order_in_course")
-
- plabels = ''
-
- for p in polygons:
- coords = polygon_coord_center(p)
-
- plabels += """
- var marker = L.marker([{latbegin}, {longbegin}]).addTo(mymap);
- marker.bindPopup("{name}");
-
- """.format(
- latbegin=coords[0],
- longbegin=coords[1],
- name=p.name
- )
-
- pcoordinates = """[
- """
-
- for p in polygons:
- pcoordinates += """[
- ["""
-
- points = GeoPoint.objects.filter(polygon=p).order_by("order_in_poly")
-
- for pt in points:
- pcoordinates += "[{x},{y}],".format(
- x=pt.latitude,
- y=pt.longitude
- )
-
- # remove last comma
- pcoordinates = pcoordinates[:-1]
- pcoordinates += """]
- ],
- """
-
- pcoordinates += """
- ]"""
-
- script = """
-
- """.format(
- latmean=latmean,
- lonmean=lonmean,
- scoordinates=scoordinates,
- pcoordinates=pcoordinates,
- plabels=plabels
- )
-
- return script
-
-
-def get_map_script(
- latmean,
- lonmean,
- latbegin,
- latend,
- longbegin,
- longend,
- scoordinates,
-):
- script = """
-
- """.format(
- latmean=latmean,
- lonmean=lonmean,
- latbegin=latbegin,
- latend=latend,
- longbegin=longbegin,
- longend=longend,
- scoordinates=scoordinates,
- )
-
- return script
-
-
def leaflet_chart(lat, lon, name="", raceresult=0):
- if lat.empty or lon.empty: # pragma: no cover
- return [0, "invalid coordinate data"]
+ try:
+ if lat.empty or lon.empty: # pragma: no cover
+ return [0, "invalid coordinate data"]
+ except AttributeError:
+ if not len(lat) or not len(lon): # pragma: no cover
+ return [0, "invalid coordinate data"]
+
# Throw out 0,0
df = pd.DataFrame({
@@ -2621,48 +875,31 @@ def leaflet_chart(lat, lon, name="", raceresult=0):
coordinates = zip(lat, lon)
- scoordinates = "["
+ data = {
+ 'coordinates': [{'latitude': c[0], 'longitude': c[1]} for c in list(coordinates)],
+ 'latmean': latmean,
+ 'lonmean': lonmean,
+ 'latbegin': latbegin,
+ 'latend': latend,
+ 'longbegin': longbegin,
+ 'longend': longend,
+ }
- for x, y in coordinates:
- scoordinates += """[{x},{y}],
- """.format(
- x=x,
- y=y
- )
-
- scoordinates += "]"
-
- if raceresult == 0:
- script = get_map_script(
- latmean,
- lonmean,
- latbegin,
- latend,
- longbegin,
- longend,
- scoordinates,
- )
- else: # pragma: no cover
+ if raceresult != 0:
record = VirtualRaceResult.objects.get(id=raceresult)
course = record.course
- script = get_map_script_course(
- latmean,
- lonmean,
- latbegin,
- latend,
- longbegin,
- longend,
- scoordinates,
- course,
- )
+ course_dict = GeoCourseSerializer(course).data
+ data['course'] = course_dict
- div = """
-
- """
+
+ coordinates = zip(lat, lon)
+
+ script, div = get_chart("/workoutmap", data)
return script, div
+
def leaflet_chart_compare(course, workoutids, labeldict={}, startenddict={}):
data = []
for id in workoutids:
@@ -2671,7 +908,7 @@ def leaflet_chart_compare(course, workoutids, labeldict={}, startenddict={}):
w = Workout.objects.get(id=id)
rowdata = rdata(w.csvfilename)
time = rowdata.df['TimeStamp (sec)']
- df = pd.DataFrame({
+ df = pl.DataFrame({
'workoutid': id,
'lat': rowdata.df[' latitude'],
'lon': rowdata.df[' longitude'],
@@ -2681,69 +918,28 @@ def leaflet_chart_compare(course, workoutids, labeldict={}, startenddict={}):
except (Workout.DoesNotExist, KeyError): # pragma: no cover
pass
try:
- df = pd.concat(data, axis=0)
+ df = pl.concat(data, rechunk=True)
except ValueError: # pragma: no cover
- df = pd.DataFrame()
+ df = pl.DataFrame()
latmean, lonmean, coordinates = course_coord_center(course)
- lat_min, lat_max, long_min, long_max = course_coord_maxmin(course)
- coordinates = course_spline(coordinates)
-
- polygons = GeoPolygon.objects.filter(
- course=course).order_by("order_in_course")
-
- plabels = ''
-
- for p in polygons:
- coords = polygon_coord_center(p)
-
- plabels += """
- var marker = L.marker([{latbegin}, {longbegin}]).addTo(mymap);
- marker.bindPopup("{name}");
-
- """.format(
- latbegin=coords[0],
- longbegin=coords[1],
- name=p.name
- )
-
- pcoordinates = """[
- """
-
- for p in polygons:
- pcoordinates += """[
- ["""
-
- points = GeoPoint.objects.filter(polygon=p).order_by("order_in_poly")
-
- for pt in points:
- pcoordinates += "[{x},{y}],".format(
- x=pt.latitude,
- y=pt.longitude
- )
-
- # remove last comma
- pcoordinates = pcoordinates[:-1]
- pcoordinates += """]
- ],
- """
-
- pcoordinates += """
- ]"""
+ course_dict = GeoCourseSerializer(course).data
# Throw out 0,0
- df = df.replace(0, np.nan)
- df = df.loc[(df != 0).any(axis=1)]
- df.fillna(method='bfill', axis=0, inplace=True)
- df.fillna(method='ffill', axis=0, inplace=True)
+ df = df.with_columns(
+ (pl.col("lat")+pl.col("lon")).alias("latlon")
+ )
+ df =df.filter(pl.col("latlon")!=0,)
+ df = df.fill_nan(None)
+ df = df.select(pl.all()).interpolate()
try:
lat = df['lat']
lon = df['lon']
except KeyError: # pragma: no cover
return [0, "invalid coordinate data"]
- if lat.empty or lon.empty: # pragma: no cover
+ if lat.is_empty() or lon.is_empty(): # pragma: no cover
return [0, "invalid coordinate data"]
colors = itertools.cycle(palette)
@@ -2752,92 +948,10 @@ def leaflet_chart_compare(course, workoutids, labeldict={}, startenddict={}):
except AttributeError:
items = zip(workoutids, colors)
- script = """
-
- """
-
- div = """
-
- """
+ mapdata = {
+ 'course': course_dict,
+ 'latmean': latmean,
+ 'lonmean': lonmean,
+ 'trajectories': trajectories,
+ }
+
+ script, div = get_chart("/mapcompare", mapdata, debug=False)
return script, div
-
-def leaflet_chart2(lat, lon, name=""):
- if lat.empty or lon.empty: # pragma: no cover
- return [0, "invalid coordinate data"]
-
- # Throw out 0,0
- df = pd.DataFrame({
- 'lat': lat,
- 'lon': lon
- })
-
- df = df.replace(0, np.nan)
- df = df.loc[(df != 0).any(axis=1)]
- df.fillna(method='bfill', axis=0, inplace=True)
- df.fillna(method='ffill', axis=0, inplace=True)
- lat = df['lat']
- lon = df['lon']
- if lat.empty or lon.empty: # pragma: no cover
- return [0, "invalid coordinate data"]
-
- latmean = lat.mean()
- lonmean = lon.mean()
- latbegin = lat[lat.index[0]]
- longbegin = lon[lon.index[0]]
- latend = lat[lat.index[-1]]
- longend = lon[lon.index[-1]]
-
- coordinates = zip(lat, lon)
-
- scoordinates = "["
-
- for x, y in coordinates:
- scoordinates += """[{x},{y}],
- """.format(
- x=x,
- y=y
- )
-
- scoordinates += "]"
-
- script = """
-
- """.format(
- latmean=latmean,
- lonmean=lonmean,
- latbegin=latbegin,
- latend=latend,
- longbegin=longbegin,
- longend=longend,
- scoordinates=scoordinates,
- )
-
- div = """
-
- """
-
- return script, div
-
-
-def leaflet_chart_video(lat, lon, name=""):
- if not len(lat) or not len(lon): # pragma: no cover
- return [0, "invalid coordinate data"]
-
- # Throw out 0,0
- df = pd.DataFrame({
- 'lat': lat,
- 'lon': lon
- })
-
- df = df.replace(0, np.nan)
- df = df.loc[(df != 0).any(axis=1)]
- df.fillna(method='bfill', axis=0, inplace=True)
- df.fillna(method='ffill', axis=0, inplace=True)
- lat = df['lat']
- lon = df['lon']
- if lat.empty or lon.empty: # pragma: no cover
- return [0, "invalid coordinate data"]
-
- latmean = lat.mean()
- lonmean = lon.mean()
- latbegin = lat[lat.index[0]]
- longbegin = lon[lon.index[0]]
-
- coordinates = zip(lat, lon)
-
- scoordinates = "["
-
- for x, y in coordinates:
- scoordinates += """[{x},{y}],
- """.format(
- x=x,
- y=y
- )
-
- scoordinates += "]"
-
- script = """
-
-
- var streets = L.tileLayer(
- 'https://api.mapbox.com/styles/v1/{{id}}/tiles/{{z}}/{{x}}/{{y}}?access_token={{accessToken}}', {{
- attribution: '© Mapbox © OpenStreetMap Improve this map',
- tileSize: 512,
- maxZoom: 18,
- zoomOffset: -1,
- id: 'mapbox/streets-v11',
- accessToken: 'pk.eyJ1Ijoic2FuZGVycm9vc2VuZGFhbCIsImEiOiJjajY3aTRkeWQwNmx6MzJvMTN3andlcnBlIn0.MFG8Xt0kDeSA9j7puZQ9hA'
-}}
- ),
-
- satellite = L.tileLayer(
- 'https://api.mapbox.com/styles/v1/{{id}}/tiles/{{z}}/{{x}}/{{y}}?access_token={{accessToken}}', {{
- attribution: '© Mapbox © OpenStreetMap Improve this map',
- tileSize: 512,
- maxZoom: 18,
- zoomOffset: -1,
- id: 'mapbox/satellite-v9',
- accessToken: 'pk.eyJ1Ijoic2FuZGVycm9vc2VuZGFhbCIsImEiOiJjajY3aTRkeWQwNmx6MzJvMTN3andlcnBlIn0.MFG8Xt0kDeSA9j7puZQ9hA'
-}}
- ),
-
- outdoors = L.tileLayer(
- 'https://api.mapbox.com/styles/v1/{{id}}/tiles/{{z}}/{{x}}/{{y}}?access_token={{accessToken}}', {{
- attribution: '© Mapbox © OpenStreetMap Improve this map',
- tileSize: 512,
- maxZoom: 18,
- zoomOffset: -1,
- id: 'mapbox/outdoors-v11',
- accessToken: 'pk.eyJ1Ijoic2FuZGVycm9vc2VuZGFhbCIsImEiOiJjajY3aTRkeWQwNmx6MzJvMTN3andlcnBlIn0.MFG8Xt0kDeSA9j7puZQ9hA'
-}}
- );
-
-
-
- var mymap = L.map('map_canvas', {{
- center: [{latmean}, {lonmean}],
- zoom: 13,
- layers: [streets, satellite]
- }}).setView([{latmean},{lonmean}], 13);
-
- var navionics = new JNC.Leaflet.NavionicsOverlay({{
- navKey: 'Navionics_webapi_03205',
- chartType: JNC.NAVIONICS_CHARTS.NAUTICAL,
- isTransparent: true,
- zIndex: 1
- }});
-
-
- var osmUrl2='http://tiles.openseamap.org/seamark/{{z}}/{{x}}/{{y}}.png';
- var osmUrl='http://{{s}}.tile.openstreetmap.org/{{z}}/{{x}}/{{y}}.png';
-
-
- //create two TileLayer
- var nautical=new L.TileLayer(osmUrl,{{
- maxZoom:18}});
-
-
- L.control.layers({{
- "Streets": streets,
- "Satellite": satellite,
- "Outdoors": outdoors,
- "Nautical": nautical,
- }},{{
- "Navionics":navionics,
- }},
- {{
- position:'topleft'
- }}).addTo(mymap);
-
- var marker = L.marker([{latbegin}, {longbegin}]).addTo(mymap);
- marker.bindPopup("Start");
-
- var latlongs = {scoordinates}
- var polyline = L.polyline(latlongs, {{color:'red'}}).addTo(mymap)
- mymap.fitBounds(polyline.getBounds())
-
- """.format(
- latmean=latmean,
- lonmean=lonmean,
- latbegin=latbegin,
- longbegin=longbegin,
- scoordinates=scoordinates,
- )
-
- div = """
-
- """
-
- return script, div
-
-
-def interactive_agegroupcpchart(age, normalized=False):
- durations = [1, 4, 30, 60]
- distances = [100, 500, 1000, 2000, 5000, 6000, 10000, 21097, 42195]
-
- fhduration = []
- fhpower = []
-
- for distance in distances:
- worldclasspower = c2stuff.getagegrouprecord(
- age,
- sex='female',
- distance=distance,
- weightcategory='hwt'
- )
- velo = (worldclasspower/2.8)**(1./3.)
- try: # pragma: no cover
- duration = distance/velo
- fhduration.append(duration)
- fhpower.append(worldclasspower)
- except ZeroDivisionError:
- pass
- for duration in durations:
- worldclasspower = c2stuff.getagegrouprecord(
- age,
- sex='female',
- duration=duration,
- weightcategory='hwt'
- )
- try:
- velo = (worldclasspower/2.8)**(1./3.)
- distance = int(60*duration*velo)
- fhduration.append(60.*duration)
- fhpower.append(worldclasspower)
- except ValueError: # pragma: no cover
- pass
-
- flduration = []
- flpower = []
-
- for distance in distances:
- worldclasspower = c2stuff.getagegrouprecord(
- age,
- sex='female',
- distance=distance,
- weightcategory='lwt'
- )
- velo = (worldclasspower/2.8)**(1./3.)
- try: # pragma: no cover
- duration = distance/velo
- flduration.append(duration)
- flpower.append(worldclasspower)
- except ZeroDivisionError:
- pass
- for duration in durations:
- worldclasspower = c2stuff.getagegrouprecord(
- age,
- sex='female',
- duration=duration,
- weightcategory='lwt'
- )
- try:
- velo = (worldclasspower/2.8)**(1./3.)
- distance = int(60*duration*velo)
- flduration.append(60.*duration)
- flpower.append(worldclasspower)
- except ValueError: # pragma: no cover
- pass
-
- mlduration = []
- mlpower = []
-
- for distance in distances:
- worldclasspower = c2stuff.getagegrouprecord(
- age,
- sex='male',
- distance=distance,
- weightcategory='lwt'
- )
- velo = (worldclasspower/2.8)**(1./3.)
- try: # pragma: no cover
- duration = distance/velo
- mlduration.append(duration)
- mlpower.append(worldclasspower)
- except ZeroDivisionError:
- mlduration.append(duration)
- mlpower.append(np.nan)
- for duration in durations:
- worldclasspower = c2stuff.getagegrouprecord(
- age,
- sex='male',
- duration=duration,
- weightcategory='lwt'
- )
- try:
- velo = (worldclasspower/2.8)**(1./3.)
- distance = int(60*duration*velo)
- mlduration.append(60.*duration)
- mlpower.append(worldclasspower)
- except ValueError: # pragma: no cover
- mlduration.append(60.*duration)
- mlpower.append(np.nan)
-
- mhduration = []
- mhpower = []
-
- for distance in distances:
- worldclasspower = c2stuff.getagegrouprecord(
- age,
- sex='male',
- distance=distance,
- weightcategory='hwt'
- )
- velo = (worldclasspower/2.8)**(1./3.)
- try: # pragma: no cover
- duration = distance/velo
- mhduration.append(duration)
- mhpower.append(worldclasspower)
- except ZeroDivisionError:
- mhduration.append(duration)
- mhpower.append(np.nan)
- for duration in durations:
- worldclasspower = c2stuff.getagegrouprecord(
- age,
- sex='male',
- duration=duration,
- weightcategory='hwt'
- )
- try:
- velo = (worldclasspower/2.8)**(1./3.)
- distance = int(60*duration*velo)
- mhduration.append(60.*duration)
- mhpower.append(worldclasspower)
- except ValueError: # pragma: no cover
- mhduration.append(60.*duration)
- mhpower.append(np.nan)
-
- def fitfunc(pars, x):
- return pars[0] / (1+(x/pars[2])) + pars[1]/(1+(x/pars[3]))
-
- def errfunc(pars, x, y):
- return fitfunc(pars, x)-y
-
- # p0 = [500,350,10,8000]
-
- # fitting WC data to three parameter CP model
- if len(fhduration) >= 4:
- p1fh, success = optimize.leastsq(errfunc, p0[:],
- args=(fhduration, fhpower))
- else: # pragma: no cover
- p1fh = None
-
- # fitting WC data to three parameter CP model
- if len(flduration) >= 4:
- p1fl, success = optimize.leastsq(errfunc, p0[:],
- args=(flduration, flpower))
- else: # pragma: no cover
- p1fl = None
-
- # fitting WC data to three parameter CP model
- if len(mlduration) >= 4:
- p1ml, success = optimize.leastsq(errfunc, p0[:],
- args=(mlduration, mlpower))
- else: # pragma: no cover
- p1ml = None
-
- if len(mhduration) >= 4:
- p1mh, success = optimize.leastsq(errfunc, p0[:],
- args=(mhduration, mhpower))
- else: # pragma: no cover
- p1mh = None
-
- fitt = pd.Series(10**(4*np.arange(100)/100.))
-
- fitpowerfh = fitfunc(p1fh, fitt)
- fitpowerfl = fitfunc(p1fl, fitt)
- fitpowerml = fitfunc(p1ml, fitt)
- fitpowermh = fitfunc(p1mh, fitt)
-
- if normalized:
- facfh = fitfunc(p1fh, 60)
- facfl = fitfunc(p1fl, 60)
- facml = fitfunc(p1ml, 60)
- facmh = fitfunc(p1mh, 60)
- fitpowerfh /= facfh
- fitpowerfl /= facfl
- fitpowermh /= facmh
- fitpowerml /= facml
- fhpower /= facfh
- flpower /= facfl
- mlpower /= facml
- mhpower /= facmh
-
- sourcemh = ColumnDataSource(
- data=dict(
- mhduration=mhduration,
- mhpower=mhpower,
- )
- )
-
- sourcefl = ColumnDataSource(
- data=dict(
- flduration=flduration,
- flpower=flpower,
- )
- )
-
- sourcefh = ColumnDataSource(
- data=dict(
- fhduration=fhduration,
- fhpower=fhpower,
- )
- )
-
- sourceml = ColumnDataSource(
- data=dict(
- mlduration=mlduration,
- mlpower=mlpower,
- )
- )
-
- sourcefit = ColumnDataSource(
- data=dict(
- duration=fitt,
- fitpowerfh=fitpowerfh,
- fitpowerfl=fitpowerfl,
- fitpowerml=fitpowerml,
- fitpowermh=fitpowermh,
- )
- )
-
- x_axis_type = 'log'
-
- TOOLS = 'save,pan,box_zoom,wheel_zoom,reset,tap,hover,crosshair'
-
- plot = figure(width=900, x_axis_type=x_axis_type,
- tools=TOOLS)
- #plot.sizing_mode = 'stretch_both'
-
- plot.line('duration', 'fitpowerfh', source=sourcefit,
- legend_label='Female HW', color='blue')
- plot.line('duration', 'fitpowerfl', source=sourcefit,
- legend_label='Female LW', color='red')
-
- plot.line('duration', 'fitpowerml', source=sourcefit,
- legend_label='Male LW', color='green')
-
- plot.line('duration', 'fitpowermh', source=sourcefit,
- legend_label='Male HW', color='orange')
-
- plot.circle('flduration', 'flpower', source=sourcefl,
- fill_color='red', size=15)
-
- plot.circle('fhduration', 'fhpower', source=sourcefh,
- fill_color='blue', size=15)
-
- plot.circle('mlduration', 'mlpower', source=sourceml,
- fill_color='green', size=15)
-
- plot.circle('mhduration', 'mhpower', source=sourcemh,
- fill_color='orange', size=15)
-
- plot.title.text = 'age '+str(age)
-
- plot.xaxis.axis_label = "Duration (seconds)"
- if normalized:
- plot.yaxis.axis_label = "Power (normalized)"
- else:
- plot.yaxis.axis_label = "Power (W)"
-
- script, div = components(plot)
-
- return script, div
-
-
def interactive_otwcpchart(powerdf, promember=0, rowername="", r=None,
cpfit='data',
title='', type='water',
wcpower=[], wcdurations=[], cpoverlay=False):
- powerdf2 = powerdf[~(powerdf == 0).any(axis=1)].copy()
+ powerdf2 = powerdf.filter((pl.col("Delta") > 0) & (pl.col("CP") > 0))
+
# plot tools
if (promember == 1): # pragma: no cover
TOOLS = 'save,pan,box_zoom,wheel_zoom,reset,tap,hover,crosshair'
@@ -3457,11 +1009,9 @@ def interactive_otwcpchart(powerdf, promember=0, rowername="", r=None,
x_axis_type = 'log'
deltas = powerdf2['Delta'].apply(lambda x: timedeltaconv(x))
- powerdf2['ftime'] = deltas.apply(lambda x: strfdelta(x))
- powerdf2['Deltaminutes'] = powerdf2['Delta']/60.
-
- source = ColumnDataSource(
- data=powerdf2
+ powerdf2 = powerdf2.with_columns(
+ ftime = deltas.apply(lambda x: strfdelta(x)),
+ Deltaminutes = pl.col("Delta")/60.
)
# there is no Paul's law for OTW
@@ -3521,8 +1071,8 @@ def interactive_otwcpchart(powerdf, promember=0, rowername="", r=None,
fitpowerfair = 0*fitpower
fitpoweraverage = 0*fitpower
- sourcecomplex = ColumnDataSource(
- data=dict(
+
+ fit_data = pl.DataFrame(dict(
CP=fitpower,
CPmax=ratio*fitpower,
duration=fitt/60.,
@@ -3534,94 +1084,18 @@ def interactive_otwcpchart(powerdf, promember=0, rowername="", r=None,
fitpowerfair=fitpowerfair,
fitpoweraverage=fitpoweraverage,
# url = urls,
- )
- )
+ ))
- # making the plot
- plot = figure(tools=TOOLS, x_axis_type=x_axis_type,
- width=900,
- toolbar_location="above",
- toolbar_sticky=False)
-
- # add watermark
- watermarkurl = "/static/img/logo7.png"
- watermarkrange = Range1d(start=0, end=1)
- watermarkalpha = 0.6
- watermarky = 0.01
- watermarkw = 184
- watermarkh = 35
- watermarkanchor = 'bottom_right'
- plot.extra_y_ranges = {"watermark": watermarkrange}
- #plot.sizing_mode = 'scale_both'
-
- plot.image_url([watermarkurl], 1.8*max(thesecs), watermarky,
- watermarkw, watermarkh,
- global_alpha=watermarkalpha,
- w_units='screen',
- h_units='screen',
- anchor=watermarkanchor,
- dilate=True,
- y_range_name="watermark",
- )
-
- plot.circle('Deltaminutes', 'CP', source=source, fill_color='red', size=15,
- legend_label='Power Data')
- plot.xaxis.axis_label = "Duration (minutes)"
- plot.yaxis.axis_label = "Power (W)"
-
- plot.y_range = Range1d(0, 1.5*max(theavpower))
- plot.x_range = Range1d(0.5*min(thesecs)/60., 2*max(thesecs)/60.)
- plot.legend.orientation = "vertical"
if not title:
title = "Critical Power for "+rowername
- plot.title.text = title
+
+ chart_dict = {
+ 'data': powerdf2.to_dicts(),
+ 'fitdata': fit_data.to_dicts(),
+ 'title': title,
+ }
- plot.xaxis[0].formatter = PrintfTickFormatter(format="%5f")
-
-
- hover = plot.select(dict(type=HoverTool))
-
- hover.tooltips = OrderedDict([
- ('Duration ', '@ftime'),
- ('Power (W)', '@CP{int}'),
- ('Power (W) upper', '@CPmax{int}'),
- ('Workout', '@workout'),
- ('World Class', '@fitpowerwc{int}')
- ])
-
- hover.mode = 'mouse'
-
- taptool = plot.select(type=TapTool)
- taptool.callback = OpenURL(url='@url')
-
- plot.line('duration', 'CP', source=sourcecomplex, legend_label="CP Model",
- color='green')
-
- plot.line('duration', 'CPmax', source=sourcecomplex, legend_label="CP Model",
- color='red')
-
- if p1wc is not None: # pragma: no cover
- plot.line('duration', 'fitpowerwc', source=sourcecomplex,
- legend_label="Gold Medal Standard",
- color='darkgoldenrod', line_dash='dotted')
-
- plot.line('duration', 'fitpowerexcellent', source=sourcecomplex,
- legend_label="90% percentile",
- color='goldenrod', line_dash='dotted')
-
- plot.line('duration', 'fitpowergood', source=sourcecomplex,
- legend_label="75% percentile",
- color='sandybrown', line_dash='dotted')
-
- plot.line('duration', 'fitpowerfair', source=sourcecomplex,
- legend_label="50% percentile",
- color='rosybrown', line_dash='dotted')
-
- plot.line('duration', 'fitpoweraverage', source=sourcecomplex,
- legend_label="25% percentile",
- color='tan', line_dash='dotted')
-
- script, div = components(plot)
+ script, div = get_chart("/cp", chart_dict)
return [script, div, p1, ratio, message]
@@ -3705,561 +1179,53 @@ def interactive_agegroup_plot(df, distance=2000, duration=None,
return script, div
-
-def interactive_cpchart(rower, thedistances, thesecs, theavpower,
- theworkouts, promember=0,
- wcpower=[], wcdurations=[]):
-
- message = 0
- # plot tools
- if (promember == 1):
- TOOLS = 'save,pan,box_zoom,wheel_zoom,reset,tap,hover,crosshair'
- else: # pragma: no cover
- TOOLS = 'pan,box_zoom,wheel_zoom,reset,tap,hover,crosshair'
-
- x_axis_type = 'log'
-
- thesecs = pd.Series(thesecs)
-
- velo = thedistances/thesecs
- p = pd.Series(500./velo)
-
- p2 = p.fillna(method='ffill').apply(lambda x: timedeltaconv(x))
-
- source = ColumnDataSource(
- data=dict(
- dist=thedistances,
- duration=thesecs,
- spm=0*theavpower,
- tim=niceformat(
- thesecs.fillna(method='ffill').apply(
- lambda x: timedeltaconv(x))
- ),
-
- power=theavpower,
- fpace=nicepaceformat(p2),
- )
- )
-
- # fitting the data to Paul
- if len(thedistances) >= 2:
- paulslope, paulintercept, r, p, stderr = linregress(
- np.log10(thedistances), p)
- else: # pragma: no cover
- paulslope = 5.0/np.log10(2.0)
- paulintercept = p[0]-paulslope*np.log10(thedistances[0])
-
- fitx = pd.Series(np.arange(100)*2*max(np.log10(thedistances))/100.)
-
- fitp = paulslope*fitx+paulintercept
-
- fitvelo = 500./fitp
- fitpower = 2.8*(fitvelo**3)
- fitt = 10**fitx/fitvelo
- fitp2 = fitp.fillna(method='ffill').apply(lambda x: timedeltaconv(x))
-
- sourcepaul = ColumnDataSource(
- data=dict(
- dist=10**fitx,
- duration=fitt,
- power=fitpower,
- spm=0*fitpower,
- tim=niceformat(
- fitt.fillna(method='ffill').apply(lambda x: timedeltaconv(x))
- ),
- fpace=nicepaceformat(fitp2),
- )
- )
-
- def fitfunc(pars, x):
- return pars[0] / (1+(x/pars[2])) + pars[1]/(1+(x/pars[3]))
-
- def errfunc(pars, x, y):
- return fitfunc(pars, x)-y
-
- # p0 = [500,350,10,8000]
- wcpower = pd.Series(wcpower, dtype='float')
- wcdurations = pd.Series(wcdurations, dtype='float')
-
- # fitting WC data to three parameter CP model
- if len(wcdurations) >= 4:
- p1wc, success = optimize.leastsq(errfunc, p0[:],
- args=(wcdurations, wcpower))
- else: # pragma: no cover
- p1wc = None
-
- # fitting the data to three parameter CP model
-
- success = 0
- p1 = p0
- if len(thesecs) >= 4:
- try:
- p1, success = optimize.leastsq(
- errfunc, p0[:], args=(thesecs, theavpower))
- except (RuntimeError, RuntimeWarning): # pragma: no cover
- factor = fitfunc(p0, thesecs.mean())/theavpower.mean()
- p1 = [p0[0]/factor, p0[1]/factor, p0[2], p0[3]]
- success = 0
- else: # pragma: no cover
- factor = fitfunc(p0, thesecs.mean())/theavpower.mean()
- p1 = [p0[0]/factor, p0[1]/factor, p0[2], p0[3]]
- success = 0
-
- # Get stayer score
- if success == 1: # pragma: no cover
- power4min = fitfunc(p1, 240.)
- power1h = fitfunc(p1, 3600.)
- power10sec = fitfunc(p1, 10.)
- r10sec4min = 100.*power10sec/power4min
- r1h4min = 100.*power1h/power4min
-
- combined = r1h4min-0.2*(r10sec4min-100)
-
- dataset = pd.read_csv('static/stats/combined_set.csv')
-
- stayerscore = int(percentileofscore(dataset['combined'], combined))
- else:
- stayerscore = None
-
- fitt = pd.Series(10**(4*np.arange(100)/100.))
-
- fitpower = fitfunc(p1, fitt)
- if p1wc is not None:
- fitpowerwc = 0.95*fitfunc(p1wc, fitt)
- fitpowerexcellent = 0.7*fitfunc(p1wc, fitt)
- fitpowergood = 0.6*fitfunc(p1wc, fitt)
- fitpowerfair = 0.5*fitfunc(p1wc, fitt)
- fitpoweraverage = 0.4*fitfunc(p1wc, fitt)
-
- else: # pragma: no cover
- fitpowerwc = 0*fitpower
- fitpowerexcellent = 0*fitpower
- fitpowergood = 0*fitpower
- fitpowerfair = 0*fitpower
- fitpoweraverage = 0*fitpower
-
- message = ""
- if len(fitpower[fitpower < 0]) > 0: # pragma: no cover
- message = "CP model fit didn't give correct results"
-
- fitvelo = (fitpower/2.8)**(1./3.)
- fitdist = fitt*fitvelo
- fitp = 500./fitvelo
- fitp2 = fitp.fillna(method='ffill').apply(lambda x: timedeltaconv(x))
-
- sourcecomplex = ColumnDataSource(
- data=dict(
- dist=fitdist,
- duration=fitt,
- tim=niceformat(
- fitt.fillna(method='ffill').apply(lambda x: timedeltaconv(x))
- ),
- spm=0*fitpower,
- power=fitpower,
- fitpowerwc=fitpowerwc,
- fitpowerexcellent=fitpowerexcellent,
- fitpowergood=fitpowergood,
- fitpowerfair=fitpowerfair,
- fitpoweraverage=fitpoweraverage,
- fpace=nicepaceformat(fitp2),
- )
- )
-
- # making the plot
- plot = figure(tools=TOOLS, x_axis_type=x_axis_type,
- width=900,
- toolbar_location="above",
- toolbar_sticky=False)
-
- # add watermark
- watermarkurl = "/static/img/logo7.png"
- watermarkrange = Range1d(start=0, end=1)
- watermarkalpha = 0.6
- watermarky = 0.01
- watermarkw = 184
- watermarkh = 35
- watermarkanchor = 'bottom_right'
- plot.extra_y_ranges = {"watermark": watermarkrange}
- #plot.sizing_mode = 'scale_both'
-
- plot.image_url([watermarkurl], 1.8*max(thesecs), watermarky,
- watermarkw, watermarkh,
- global_alpha=watermarkalpha,
- w_units='screen',
- h_units='screen',
- anchor=watermarkanchor,
- dilate=True,
- y_range_name="watermark",
- )
-
- plot.circle('duration', 'power', source=source, fill_color='red', size=15,
- legend_label='Power')
- plot.xaxis.axis_label = "Duration (seconds)"
- plot.yaxis.axis_label = "Power (W)"
-
- if stayerscore is not None: # pragma: no cover
- plot.add_layout(
- Label(x=100, y=100, x_units='screen', y_units='screen',
- text='Stayer Score '+str(stayerscore)+'%',
- background_fill_alpha=0.7,
- background_fill_color='white',
- text_color='black')
- )
-# plot.add_layout(
-# Label(x=100,y=120,x_units='screen',y_units='screen',
-# text='Stayer Score (6min) '+str(stayerscore2)+'%',
-# background_fill_alpha=0.7,
-# background_fill_color='white',
-# text_color='black')
-# )
-
- cpdata = dataprep.fetchcperg(rower, theworkouts)
-
- if cpdata.empty: # pragma: no cover
- message = 'Calculations are running in the background. Please refresh this page to see updated results'
- return ['', '', paulslope, paulintercept, p1, message, p1wc]
-
- velo = cpdata['distance']/cpdata['delta']
-
- p = 500./velo
-
- p2 = p.fillna(method='ffill').apply(lambda x: timedeltaconv(x))
-
- source2 = ColumnDataSource(
- data=dict(
- duration=cpdata['delta'],
- power=cpdata['cp'],
- tim=niceformat(
- cpdata['delta'].fillna(method='ffill').apply(
- lambda x: timedeltaconv(x))
- ),
- dist=cpdata['distance'],
- pace=nicepaceformat(p2),
- )
- )
-
- plot.circle('duration', 'power', source=source2,
- fill_color='blue', size=3,
- legend_label='Power from segments')
-
- hover = plot.select(dict(type=HoverTool))
-
- hover.tooltips = OrderedDict([
- ('Duration ', '@tim'),
- ('Power (W)', '@power{int}'),
- ('Distance (m)', '@dist{int}'),
- ('Pace (/500m)', '@fpace'),
- ])
-
- hover.mode = 'mouse'
-
- plot.y_range = Range1d(0, 1.5*max(theavpower))
- plot.x_range = Range1d(1, 2*max(thesecs))
- plot.legend.orientation = "vertical"
-
- plot.line('duration', 'power', source=sourcepaul,
- legend_label="Paul's Law")
- plot.line('duration', 'power', source=sourcecomplex, legend_label="CP Model",
- color='green')
- if p1wc is not None:
- plot.line('duration', 'fitpowerwc', source=sourcecomplex,
- legend_label="World Class",
- color='Maroon', line_dash='dotted')
-
- plot.line('duration', 'fitpowerexcellent', source=sourcecomplex,
- legend_label="90% percentile",
- color='Purple', line_dash='dotted')
-
- plot.line('duration', 'fitpowergood', source=sourcecomplex,
- legend_label="75% percentile",
- color='Olive', line_dash='dotted')
-
- plot.line('duration', 'fitpowerfair', source=sourcecomplex,
- legend_label="50% percentile",
- color='Gray', line_dash='dotted')
-
- plot.line('duration', 'fitpoweraverage', source=sourcecomplex,
- legend_label="25% percentile",
- color='SkyBlue', line_dash='dotted')
-
- script, div = components(plot)
-
- return [script, div, paulslope, paulintercept, p1, message, p1wc]
-
-
-def interactive_windchart(id=0, promember=0):
- # check if valid ID exists (workout exists)
- row = Workout.objects.get(id=id)
- # g = GraphImage.objects.filter(workout=row).order_by("-creationdatetime")
-
- f1 = row.csvfilename
-
- # create interactive plot
- plot = figure(width=400, height=300)
-
- # get user
- # u = User.objects.get(id=row.user.id)
- r = row.user
-
- rr = rrower(hrmax=r.max, hrut2=r.ut2,
- hrut1=r.ut1, hrat=r.at,
- hrtr=r.tr, hran=r.an, ftp=r.ftp)
-
- rowdata = rdata(f1, rower=rr)
- if rowdata == 0: # pragma: no cover
- return 0
-
- try:
- dist = rowdata.df.loc[:, 'cum_dist']
- except KeyError:
- return ['', 'No Data Found']
-
- try: # pragma: no cover
- vwind = rowdata.df.loc[:, 'vwind']
- winddirection = rowdata.df.loc[:, 'winddirection']
- bearing = rowdata.df.loc[:, 'bearing']
- except KeyError:
- rowdata.add_wind(0, 0)
- rowdata.add_bearing()
- vwind = rowdata.df.loc[:, 'vwind']
- winddirection = rowdata.df.loc[:, 'winddirection']
- bearing = rowdata.df.loc[:, 'winddirection']
- rowdata.write_csv(f1, gzip=True)
- dataprep.update_strokedata(id, rowdata.df)
-
- winddirection = winddirection % 360
- winddirection = (winddirection + 360) % 360
-
- tw = tailwind(bearing, vwind, 1.0*winddirection)
-
- source = ColumnDataSource(
- data=dict(
- dist=dist,
- vwind=vwind,
- tw=tw,
- winddirection=winddirection,
- )
- )
-
- # plot tools
- if (promember == 1):
- TOOLS = 'save,pan,box_zoom,wheel_zoom,reset,tap,crosshair'
- else: # pragma: no cover
- TOOLS = 'pan,box_zoom,wheel_zoom,reset,tap,crosshair'
-
- # making the plot
- plot = figure(tools=TOOLS, width=400, height=500,
- # toolbar_location="below",
- toolbar_sticky=False,
- )
- plot.line('dist', 'vwind', source=source, legend_label="Wind Speed (m/s)")
- plot.line('dist', 'tw', source=source,
- legend_label="Tail (+)/Head (-) Wind (m/s)", color='black')
- try:
- plot.title.text = row.name
- except ValueError: # pragma: no cover
- plot.title.text = ""
- # plot.title.text_font_size="1.0em"
- plot.title.text_font = "1.0em"
- plot.xaxis.axis_label = "Distance (m)"
- plot.yaxis.axis_label = "Wind Speed (m/s)"
- plot.y_range = Range1d(-7, 7)
- #plot.sizing_mode = 'stretch_both'
-
- plot.extra_y_ranges = {"winddirection": Range1d(start=0, end=360)}
- plot.line('dist', 'winddirection', source=source,
- legend_label='Wind Direction', color="red",
- y_range_name="winddirection")
- plot.add_layout(LinearAxis(y_range_name="winddirection",
- axis_label="Wind Direction (degree)"), 'right')
-
- script, div = components(plot)
-
- return [script, div]
-
-
-def interactive_streamchart(id=0, promember=0):
- # check if valid ID exists (workout exists)
- row = Workout.objects.get(id=id)
- # g = GraphImage.objects.filter(workout=row).order_by("-creationdatetime")
-
- f1 = row.csvfilename
-
- # create interactive plot
- plot = figure(width=400,
- )
- # get user
- # u = User.objects.get(id=row.user.id)
- r = row.user
-
- rr = rrower(hrmax=r.max, hrut2=r.ut2,
- hrut1=r.ut1, hrat=r.at,
- hrtr=r.tr, hran=r.an, ftp=r.ftp)
-
- rowdata = rdata(f1, rower=rr)
- if rowdata == 0: # pragma: no cover
- return "", "No Valid Data Available"
-
- try:
- dist = rowdata.df.loc[:, 'cum_dist']
- except KeyError:
- return ['', 'No Data found']
-
- try:
- vstream = rowdata.df.loc[:, 'vstream']
- except KeyError:
- rowdata.add_stream(0)
- vstream = rowdata.df.loc[:, 'vstream']
- rowdata.write_csv(f1, gzip=True)
- dataprep.update_strokedata(id, rowdata.df)
-
- # plot tools
- if (promember == 1):
- TOOLS = 'save,pan,box_zoom,wheel_zoom,reset,tap,crosshair'
- else: # pragma: no cover
- TOOLS = 'pan,box_zoom,wheel_zoom,reset,tap,crosshair'
-
- # making the plot
- plot = figure(tools=TOOLS, width=400, height=500,
- # toolbar_location="below",
- toolbar_sticky=False,
- )
- plot.line(dist, vstream, legend_label="River Stream Velocity (m/s)")
- try:
- plot.title.text = row.name
- except ValueError: # pragma: no cover
- plot.title.text = ""
- plot.title.text_font_size = "1.0em"
- plot.xaxis.axis_label = "Distance (m)"
- plot.yaxis.axis_label = "River Current (m/s)"
- plot.y_range = Range1d(-2, 2)
- #plot.sizing_mode = 'stretch_both'
-
- script, div = components(plot)
-
- return [script, div]
-
def forcecurve_multi_interactive_chart(selected): # pragma: no cover
- df_plot = pd.DataFrame()
ids = [analysis.id for analysis in selected]
+ workoutids = [analysis.workout.id for analysis in selected]
+
+ selected_dict = [ForceCurveAnalysisSerializer(analysis).data for analysis in selected]
columns = ['catch', 'slip', 'wash', 'finish', 'averageforce',
'peakforceangle', 'peakforce', 'spm', 'distance',
- 'workoutstate', 'driveenergy']
+ 'workoutstate', 'workoutid', 'driveenergy', 'cumdist']
+ columns = columns + [name for name, d in metrics.rowingmetrics]
+ rowdata = dataprep.read_data(columns, ids=workoutids,
+ workstrokesonly=False)
+
+ rowdata = rowdata.fill_nan(None).drop_nulls()
+
+ if rowdata.is_empty():
+ return "", "No Valid Data Available", "", ""
+
+ data_dict = rowdata.to_dicts()
+
+ thresholdforces = []
for analysis in selected:
- workstrokesonly = not analysis.include_rest_strokes
- spm_min = analysis.spm_min
- spm_max = analysis.spm_max
- dist_min = analysis.dist_min
- dist_max = analysis.dist_max
- work_min = analysis.work_min
- work_max = analysis.work_max
- rowdata = dataprep.getsmallrowdata_db(columns, ids=[analysis.workout.id],
- workstrokesonly=workstrokesonly)
+ boattype = analysis.workout.boattype
+ thresholdforce = 100. if 'x' in boattype else 200.
+ thresholdforces.append({'id': analysis.workout.id, 'thresholdforce': thresholdforce})
- rowdata = rowdata[rowdata['spm']>spm_min]
- rowdata = rowdata[rowdata['spm']work_min]
- rowdata = rowdata[rowdata['driveenergy']dist_min]
+ chart_data = {
+ 'title': '',
+ 'data': data_dict,
+ 'thresholdforces': thresholdforces,
+ 'forcecurve_analyses': selected_dict,
+ }
- catchav = rowdata['catch'].median()
- finishav = rowdata['finish'].median()
- washav = (rowdata['finish']-rowdata['wash']).median()
- slipav = (rowdata['slip']+rowdata['catch']).median()
- peakforceav = rowdata['peakforce'].median()
- peakforceangleav = rowdata['peakforceangle'].median()
- thresholdforce = 100 if 'x' in analysis.workout.boattype else 200
- x = [catchav,
- slipav,
- peakforceangleav,
- washav,
- finishav]
-
- y = [0, thresholdforce,
- peakforceav,
- thresholdforce, 0]
-
- xname = 'x_'+str(analysis.id)
- yname = 'y_'+str(analysis.id)
-
- df_plot[xname] = x
- df_plot[yname] = y
-
- source = ColumnDataSource(
- df_plot
- )
-
- TOOLS = 'save,pan,box_zoom,wheel_zoom,reset,tap,crosshair'
- plot = figure(width=920,tools=TOOLS,
- toolbar_location='above',
- toolbar_sticky=False)
-
- #plot.sizing_mode = 'stretch_both'
-
- # add watermark
- watermarkurl = "/static/img/logo7.png"
-
- watermarkrange = Range1d(start=0, end=1)
- watermarkalpha = 0.6
- watermarkx = 0.99
- watermarky = 0.01
- watermarkw = 184
- watermarkh = 35
- watermarkanchor = 'bottom_right'
- plot.extra_y_ranges = {"watermark": watermarkrange}
- plot.extra_x_ranges = {"watermark": watermarkrange}
-
- plot.image_url([watermarkurl], watermarkx, watermarky,
- watermarkw, watermarkh,
- global_alpha=watermarkalpha,
- w_units='screen',
- h_units='screen',
- anchor=watermarkanchor,
- dilate=True,
- x_range_name="watermark",
- y_range_name="watermark",
- )
-
- colors = itertools.cycle(palette)
-
- try:
- items = itertools.izip(ids, colors)
- except AttributeError:
- items = zip(ids, colors)
-
- for id, color in items:
- xname = 'x_'+str(id)
- yname = 'y_'+str(id)
- analysis = ForceCurveAnalysis.objects.get(id=id)
- legendlabel = '{name}'.format(
- name = analysis.name,
- )
- if analysis.notes:
- legendlabel = '{name} - {notes}'.format(
- name = analysis.name,
- notes = analysis.notes
- )
- plot.line(xname,yname,source=source,legend_label=legendlabel,
- line_width=2, color=color)
-
- plot.legend.location = "top_left"
- plot.xaxis.axis_label = "Angle"
- plot.yaxis.axis_label = "Force (N)"
-
- script, div = components(plot)
-
- return (script, div)
+ script, div = get_chart("/forcecurve_compare", chart_data)
+ return script, div
+
def instroke_multi_interactive_chart(selected, *args, **kwargs): # pragma: no cover
- df_plot = pd.DataFrame()
+ df2 = []
ids = [analysis.id for analysis in selected]
metrics = list(set([analysis.metric for analysis in selected]))
maximum_values = {}
+ workouts = []
for metric in metrics:
maximum_values[metric] = 0
+ cntr = 1
for analysis in selected:
#start_second, end_second, spm_min, spm_max, name
activeminutesmin = int(analysis.start_second/60.)
@@ -4279,103 +1245,76 @@ def instroke_multi_interactive_chart(selected, *args, **kwargs): # pragma: no co
if mean_vals.max() > maximum_values[analysis.metric]:
maximum_values[analysis.metric] = mean_vals.max()
xvals = np.arange(len(mean_vals))
- xname = 'x_'+str(analysis.id)
- yname = 'y_'+str(analysis.id)
- df_plot[xname] = pd.Series(xvals)
- df_plot[yname] = pd.Series(mean_vals)
- if len(metrics) > 1:
- for analysis in selected:
- yname = 'y_'+str(analysis.id)
- df_plot[yname] = df_plot[yname] / maximum_values[analysis.metric]
+ data2 = pl.DataFrame({
+ 'x': pl.Series(xvals),
+ 'y': pl.Series(mean_vals),
+
+ })
+ data2 = data2.with_columns((pl.lit(cntr)).alias("id"))
- source = ColumnDataSource(
- df_plot
- )
+ df2.append(data2)
- TOOLS = 'save,pan,box_zoom,wheel_zoom,reset,tap,crosshair'
- plot = figure(width=920,tools=TOOLS,
- toolbar_location='above',
- toolbar_sticky=False)
-
- #plot.sizing_mode = 'stretch_both'
-
- # add watermark
- watermarkurl = "/static/img/logo7.png"
-
- watermarkrange = Range1d(start=0, end=1)
- watermarkalpha = 0.6
- watermarkx = 0.99
- watermarky = 0.01
- watermarkw = 184
- watermarkh = 35
- watermarkanchor = 'bottom_right'
- plot.extra_y_ranges = {"watermark": watermarkrange}
- plot.extra_x_ranges = {"watermark": watermarkrange}
-
- if len(metrics)>1:
- plot.yaxis.axis_label = 'Scaled'
- else:
- plot.yaxis.axis_label = metrics[0]
-
- plot.image_url([watermarkurl], watermarkx, watermarky,
- watermarkw, watermarkh,
- global_alpha=watermarkalpha,
- w_units='screen',
- h_units='screen',
- anchor=watermarkanchor,
- dilate=True,
- x_range_name="watermark",
- y_range_name="watermark",
- )
-
- colors = itertools.cycle(palette)
-
- try:
- items = itertools.izip(ids, colors)
- except AttributeError:
- items = zip(ids, colors)
-
- for id, color in items:
- xname = 'x_'+str(id)
- yname = 'y_'+str(id)
- analysis = InStrokeAnalysis.objects.get(id=id)
legendlabel = '{name} - {metric} - {workout}'.format(
name = analysis.name,
metric = analysis.metric,
date = analysis.date,
workout = str(analysis.workout)
)
- plot.line(xname,yname,source=source,legend_label=legendlabel,
- line_width=2, color=color)
+ workouts.append({'id': cntr, 'label': legendlabel})
+ cntr = cntr + 1
- script, div = components(plot)
+
+ ytitle = metrics[0]
+ if len(metrics) > 1:
+ cntr = 1
+ for analysis in selected:
+ df2[cntr-1] = df2[cntr-1].with_columns(
+ (pl.col("y")/ maximum_values[analysis.metric])
+ )
+ ytitle = 'Scaled'
+ cntr = cntr+1
- return (script, div)
+ df2 = pl.concat(df2)
+
+ data_dict = df2.to_dicts()
+
+ chart_data = {
+ 'title': '',
+ 'data': data_dict,
+ 'ytitle': ytitle,
+ 'workouts': workouts,
+ }
+
+ script, div = get_chart("/instroke_compare", chart_data)
+
+ return script, div
+
def instroke_interactive_chart(df,metric, workout, spm_min, spm_max,
activeminutesmin, activeminutesmax,
individual_curves,
name='',notes=''): # pragma: no cover
- df_pos = (df+abs(df))/2.
- df_min = -(-df+abs(-df))/2.
-
if df.empty:
return "", "No data in selection"
- mean_vals = df.median().replace(0, np.nan)
- q75 = df_pos.quantile(q=0.75).replace(0,np.nan)
- q25 = df_pos.quantile(q=0.25).replace(0,np.nan)
- q75min = df_min.quantile(q=0.75).replace(0,np.nan)
- q25min = df_min.quantile(q=0.25).replace(0,np.nan)
+ df_pos = (df+abs(df))/2.
+ df_min = -(-df+abs(-df))/2.
+
+
+ mean_vals = df.median().replace(0, np.nan)
+ q75 = df_pos.quantile(q = 0.75).replace(0, np.nan)
+ q25 = df_pos.quantile(q=0.25).replace(0, np.nan)
+ q75min = df_min.quantile(q=0.75).replace(0, np.nan)
+ q25min = df_min.quantile(q=0.25).replace(0, np.nan)
mean_vals = mean_vals.interpolate()
xvals = np.arange(len(mean_vals))
- df_plot = pd.DataFrame({
+ df_plot = pl.DataFrame({
'x':xvals,
'median':mean_vals,
'high':q75,
@@ -4384,143 +1323,51 @@ def instroke_interactive_chart(df,metric, workout, spm_min, spm_max,
'low 2': q25,
})
- df_plot['high'].update(df_plot.pop('high 2'))
- df_plot['low'].update(df_plot.pop('low 2'))
- try:
- df_plot.interpolate(axis=1,inplace=True)
- except TypeError:
- pass
-
- TOOLS = 'save,pan,box_zoom,wheel_zoom,reset,tap,crosshair'
- plot = figure(width=920,tools=TOOLS,
- toolbar_location='above',
- toolbar_sticky=False)
-
- #plot.sizing_mode = 'stretch_both'
-
- plot.title.text = str(workout) + ' - ' + metric
-
- # add watermark
- watermarkurl = "/static/img/logo7.png"
-
- watermarkrange = Range1d(start=0, end=1)
- watermarkalpha = 0.6
- watermarkx = 0.99
- watermarky = 0.01
- watermarkw = 184
- watermarkh = 35
- watermarkanchor = 'bottom_right'
- plot.extra_y_ranges = {"watermark": watermarkrange}
- plot.extra_x_ranges = {"watermark": watermarkrange}
-
- plot.image_url([watermarkurl], watermarkx, watermarky,
- watermarkw, watermarkh,
- global_alpha=watermarkalpha,
- w_units='screen',
- h_units='screen',
- anchor=watermarkanchor,
- dilate=True,
- x_range_name="watermark",
- y_range_name="watermark",
- )
-
-
- source = ColumnDataSource(
- df_plot
+ df_plot = df_plot.with_columns(
+ pl.coalesce(["high", "high 2"]).alias("high")
)
-
- TIPS = OrderedDict([
- ('x','@x'),
- ('median','@median'),
- ('high','@high'),
- ('low','@low')
- ])
-
- hover = plot.select(type=HoverTool)
- hover.tooltips = TIPS
-
- s = 'SPM: {spm_min} - {spm_max}'.format(
- spm_min = spm_min,
- spm_max = spm_max,
+ df_plot = df_plot.with_columns(
+ pl.coalesce("low", "low 2").alias("low")
)
- label = Label(x=50, y=450, x_units='screen',y_units='screen',
- text=s,
- background_fill_alpha=0.7,
- background_fill_color='white',
- text_color='black',
- )
-
- s2 = 'Time: {activeminutesmin} - {activeminutesmax}'.format(
- activeminutesmin=datetime.timedelta(seconds=60*activeminutesmin),
- activeminutesmax=datetime.timedelta(seconds=60*activeminutesmax)
- )
-
- label2 = Label(x=50,y=400, x_units='screen', y_units='screen',
- text=s2,
- background_fill_alpha=0.7,
- background_fill_color='white',
- text_color='black',
- )
-
- plot.add_layout(label)
- plot.add_layout(label2)
-
- if name:
- namelabel = Label(x=50, y=480, x_units='screen', y_units='screen',
- text=name,
- background_fill_alpha=0.7,
- background_fill_color='white',
- text_color='black',
- )
- plot.add_layout(namelabel)
-
- if notes:
- noteslabel = Label(x=50, y=50, x_units='screen', y_units='screen',
- text=notes,
- background_fill_alpha=0.7,
- background_fill_color='white',
- text_color='black',
- )
- plot.add_layout(noteslabel)
-
- if individual_curves:
- for index,row in df.iterrows():
- plot.line(xvals,row,color='lightgray',line_width=1)
- else:
- plot.varea('x', y1='high', y2='low',source=source,fill_color="lightgray",alpha=0.5)
-
- plot.line('x','median',source=source,legend_label='median',color="black",
- line_width=3)
-
- medrange = mean_vals.max()-mean_vals.min()
- yrange = Range1d(start=mean_vals.min()-0.2*medrange,
- end=mean_vals.max()+0.2*medrange,)
- plot.y_range = yrange
-
- plot.add_tools(HoverTool(tooltips=TIPS))
+ df_plot = df_plot.drop(["high 2", "low 2"])
+ df_plot = df_plot.drop_nulls()
if metric == 'boat accelerator curve':
- plot.yaxis.axis_label = "Boat acceleration (m/s^2)"
+ ytitle = "Boat acceleration (m/s^2)"
elif metric == 'instroke boat speed':
- plot.yaxis.axis_label = "Boat Speed (m/s)"
+ ytitle = "Boat Speed (m/s)"
vavg = mean_vals.median()
elif metric == 'oar angle velocity curve':
- plot.yaxis.axis_label = "Oar Angular Velocity (degree/s)"
+ ytitle = "Oar Angular Velocity (degree/s)"
elif metric == 'seat curve':
- plot.yaxis.axis_label = "Seat Speed (m/s)"
+ ytitle = "Seat Speed (m/s)"
+
- plot.xaxis.axis_label = 'Time (%)'
+ lines_dict = df.to_dict("records")
+ data_dict = df_plot.to_dicts()
- try:
- script, div = components(plot)
- except ValueError:
- script = ""
- div = "Something went wrong with the chart"
+ chart_data = {
+ 'lines': lines_dict,
+ 'data': data_dict,
+ 'ytitle': ytitle,
+ 'title': str(workout) + ' - ' + metric,
+ 'individual_curves': individual_curves,
+ 'spmmin': spm_min,
+ 'spmmax': spm_max,
+ 'timemin' :'{activeminutesmin}'.format(
+ activeminutesmin=datetime.timedelta(seconds=60*activeminutesmin),
+ ),
+ 'timemax': '{activeminutesmax}'.format(
+ activeminutesmax=datetime.timedelta(seconds=60*activeminutesmax)
+ ),
+ 'analysis_name': name,
+ }
- return (script, div)
+ script, div = get_chart("/instroke", chart_data, debug=False)
+ return script, div
def interactive_chart(id=0, promember=0, intervaldata={}):
@@ -4532,94 +1379,33 @@ def interactive_chart(id=0, promember=0, intervaldata={}):
TOOLS = 'pan,box_zoom,wheel_zoom,reset,tap,hover,crosshair'
columns = ['time', 'pace', 'hr', 'fpace', 'ftime', 'spm']
- datadf = dataprep.getsmallrowdata_db(columns, ids=[id])
+ datadf = dataprep.read_data(columns, ids=[id])
+ if datadf.is_empty():
+ return "", "No Valid Data Available"
-
- datadf.dropna(axis=0, how='any', inplace=True)
+ datadf = datadf.fill_nan(None).drop_nulls()
+
row = Workout.objects.get(id=id)
- if datadf.empty:
+ if datadf.is_empty():
return "", "No Valid Data Available"
try:
_ = datadf['spm']
except KeyError: # pragma: no cover
- datadf['spm'] = 0
+ datadf = datadf.with_columns((pl.lit(0)).alias("spm"))
try:
_ = datadf['pace']
except KeyError: # pragma: no cover
- datadf['pace'] = 0
+ datadf = datadf.with_columns((pl.lit(0)).alias("pace"))
- source = ColumnDataSource(
- datadf
- )
+ data_dict = datadf.to_dicts()
- plot = figure(x_axis_type="datetime", y_axis_type="datetime",
- width=400,
- height=400,
- toolbar_sticky=False,
- tools=TOOLS)
+ metrics_list = [{'name': name, 'rowingmetrics':d } for name, d in metrics.rowingmetrics]
- # add watermark
- watermarkurl = "/static/img/logo7.png"
- watermarkrange = Range1d(start=0, end=1)
- watermarkalpha = 0.6
- watermarkw = 184
- watermarkh = 35
- plot.extra_y_ranges = {"watermark": watermarkrange}
- plot.extra_x_ranges = {"watermark": watermarkrange}
-
- plot.image_url([watermarkurl], 0.01, 0.99,
- 0.5*watermarkw, 0.5*watermarkh,
- global_alpha=watermarkalpha,
- w_units='screen',
- h_units='screen',
- anchor='top_left',
- dilate=True,
- x_range_name="watermark",
- y_range_name="watermark",
- )
-
- plot.line('time', 'pace', source=source, legend_label="Pace", name="pace")
-
- try:
- plot.title.text = row.name
- except ValueError: # pragma: no cover
- plot.title.text = ""
- plot.title.text_font_size = "1.0em"
- #plot.sizing_mode = 'stretch_both'
- plot.xaxis.axis_label = "Time"
- plot.yaxis.axis_label = "Pace (/500m)"
- plot.xaxis[0].formatter = DatetimeTickFormatter(
- hours=["%H"],
- minutes=["%M"],
- seconds=["%S"],
- days=["0"],
- months=[""],
- years=[""]
- )
- plot.yaxis[0].formatter = DatetimeTickFormatter(
- seconds=["%S"],
- minutes=["%M"]
- )
- ymax = 90.
- ymin = 150.
-
- if row.workouttype == 'water':
- ymax = 90.
- ymin = 210.
-
- plot.y_range = Range1d(1.e3*ymin, 1.e3*ymax)
-
- plot.extra_y_ranges["spmax"] = Range1d(start=10, end=45)
- plot.line('time', 'spm', source=source, color="red",
- y_range_name="spmax", legend_label="Stroke Rate", name="spm")
- plot.add_layout(LinearAxis(y_range_name="spmax",
- axis_label="SPM"), 'right')
-
- plot.legend.location = "bottom_right"
+ intervals = []
# add shaded bar chart areas
if intervaldata != {}:
intervaldf = pd.DataFrame(intervaldata)
@@ -4631,30 +1417,24 @@ def interactive_chart(id=0, promember=0, intervaldata={}):
intervaldf['value'] = 100
mask = intervaldf['itype'] == 3
intervaldf.loc[mask, 'value'] = 0
- intervaldf['bottom'] = 10
+ intervaldf['bottom'] = 0
- intervalsource = ColumnDataSource(
- intervaldf
- )
+ intervals = intervaldf.to_dict("records")
- plot.quad(left='time', top='value', bottom='bottom',
- right='time_r', source=intervalsource, color='mediumvioletred',
- y_range_name='spmax', fill_alpha=0.2, line_alpha=0.2)
+ chart_data = {
+ 'title': row.name,
+ 'x': "time",
+ 'y1': "pace",
+ 'y2': "spm",
+ 'data': data_dict,
+ 'metrics': metrics_list,
+ 'intervals': intervals,
+ }
+
+ script, div = get_chart("/interactive", chart_data)
- hover = plot.select(dict(type=HoverTool))
+ return script, div
- hover.tooltips = OrderedDict([
- ('Time', '@ftime'),
- ('Pace', '@fpace'),
- ('HR', '@hr{int}'),
- ('SPM', '@spm{1.1}'),
- ])
-
- hover.mode = 'mouse'
- # hover.name = ["spm", "pace"]
- script, div = components(plot)
-
- return [script, div]
def interactive_chart_video(videodata):
@@ -4667,12 +1447,10 @@ def interactive_chart_video(videodata):
data = zip(time, spm)
- data2 = "["
+ data2 = []
for t, s in data:
- data2 += "{x: %s, y: %s}, " % (t, s)
-
- data2 = data2[:-2] + "]"
+ data2.append( {'x': t, 'y': s})
markerpoint = {
'x': time[0],
@@ -4680,89 +1458,13 @@ def interactive_chart_video(videodata):
'r': 10,
}
- div = """
-
- """
-
- script = """
- var ctx = document.getElementById("myChart").getContext('2d');
- var data = %s
-
- var myChart = new Chart(ctx, {
- type: 'scatter',
- label: 'SPM',
- animationSteps: 10,
- options: {
- legend: {
- display: false,
- },
- animation: {
- duration: 100,
- },
- scales: {
- yAxes: [{
- scaleLabel: {
- display: true,
- labelString: 'Stroke Rate'
- }
- }],
- xAxes: [{
- scaleLabel: {
- type: 'linear',
- display: true,
- labelString: 'Time (seconds)'
- }
- }],
- }
- },
- data:
- {
- datasets: [
- {
- type: 'bubble',
- label: 'now',
- data: [ %s ],
- backgroundColor: '#36a2eb',
- },
- {
- label: 'spm',
- data: data,
- backgroundColor: "#ff0000",
- borderColor: "#ff0000",
- fill: false,
- borderDash: [0, 0],
- pointRadius: 1,
- pointHoverRadius: 1,
- showLine: true,
- tension: 0,
- },
-
- ]
- },
-
- });
-
- var marker = {
- datapoint: %s ,
- setLatLng: function (LatLng) {
- var lat = LatLng.lat;
- var lng = LatLng.lng;
- this.datapoint = {
- 'x': lat,
- 'y': lng,
- 'r': 10,
- }
- myChart.data.datasets[0].data[0] = this.datapoint;
- myChart.update();
+ chart_data = {
+ 'data': data2,
+ 'markerpoint': markerpoint,
}
- }
- marker.setLatLng({
- 'lat': data[0]['x'],
- 'lng': data[0]['y']
- })
- """ % (data2, markerpoint, markerpoint)
+
+ script, div = get_chart("/videochart", chart_data)
return [script, div]
@@ -4833,156 +1535,24 @@ def interactive_multiflex(datadf, xparam, yparam, groupby, extratitle='',
yaxmax = yaxmaxima[yparam]
yaxmin = yaxminima[yparam]
- x_axis_type = 'linear'
- y_axis_type = 'linear'
- if xparam == 'time': # pragma: no cover
- x_axis_type = 'datetime'
- if yparam == 'pace':
- y_axis_type = 'datetime'
+ data_dict = datadf.to_dict("records")
+
+ metrics_list = [{'name': name, 'rowingmetrics':d } for name, d in metrics.rowingmetrics]
+
+ chart_data = {
+ 'title': title,
+ 'x': xparam,
+ 'y': yparam,
+ 'data': data_dict,
+ 'metrics': metrics_list,
+ 'errorbars':ploterrorbars,
+ 'groupname': groupname,
+ }
- datadf.index.names = ['index']
+ script, div = get_chart("/trendflex", chart_data,debug=False)
- source = ColumnDataSource(
- datadf,
- )
-
- TOOLS = 'save,pan,box_zoom,wheel_zoom,reset,tap'
-
- if groupby != 'date':
- hover = HoverTool(tooltips=[
- (groupby, '@groupval{1.1}'),
- (xparamname, '@x{1.1}'),
- (yparamname, '@y')
- ])
- else: # pragma: no cover
- hover = HoverTool(
- tooltips=[
- (groupby, '@groupval'),
- (xparamname, '@x{1.1}'),
- (yparamname, '@y'),
- ])
-
- hover.mode = 'mouse'
- TOOLS = [SaveTool(), PanTool(), BoxZoomTool(), WheelZoomTool(),
- ResetTool(), TapTool(), hover]
-
- plot = figure(x_axis_type=x_axis_type, y_axis_type=y_axis_type,
- tools=TOOLS,
- toolbar_location="above",
- toolbar_sticky=False, width=920)
-
- # add watermark
- watermarkurl = "/static/img/logo7.png"
- watermarkrange = Range1d(start=0, end=1)
- watermarkalpha = 0.6
- watermarkx = 0.99
- watermarky = 0.01
- watermarkw = 184
- watermarkh = 35
- watermarkanchor = 'bottom_right'
- plot.extra_y_ranges = {"watermark": watermarkrange}
- plot.extra_x_ranges = {"watermark": watermarkrange}
-
- plot.title.text = title
- plot.title.text_font_size = "1.0em"
- #plot.sizing_mode = 'stretch_both'
-
- plot.image_url([watermarkurl], watermarkx, watermarky,
- watermarkw, watermarkh,
- global_alpha=watermarkalpha,
- w_units='screen',
- h_units='screen',
- anchor=watermarkanchor,
- dilate=True,
- x_range_name="watermark",
- y_range_name="watermark",
- )
-
- errorbar(plot, xparam, yparam, source=source,
- xerr=ploterrorbars,
- yerr=ploterrorbars,
- point_kwargs={
- 'line_color': "#969696",
- 'size': "groupsize",
- 'fill_color': "color",
- 'fill_alpha': 1.0,
- },
- )
-
- for nr, gvalue, color in colorlegend:
- box = BoxAnnotation(bottom=75+20*nr, left=50, top=95+20*nr,
- right=70,
- bottom_units='screen',
- top_units='screen',
- left_units='screen',
- right_units='screen',
- fill_color=color,
- fill_alpha=1.0,
- line_color=color)
- legendlabel = Label(x=71, y=78+20*nr, x_units='screen',
- y_units='screen',
- text="{gvalue:3.0f}".format(gvalue=gvalue),
- background_fill_alpha=1.0,
- text_color='black',
- text_font_size="0.7em")
- plot.add_layout(box)
- plot.add_layout(legendlabel)
-
- if colorlegend:
- legendlabel = Label(x=322, y=250, x_units='screen',
- y_units='screen',
- text='group legend',
- text_color='black',
- text_font_size="0.7em",
- angle=90,
- angle_units='deg')
-
- if xparam == 'workoutid': # pragma: no cover
- plot.xaxis.axis_label = 'Workout'
- else:
- plot.xaxis.axis_label = axlabels[xparam]
-
- if yparam == 'workoutid': # pragma: no cover
- plot.xaxis.axis_label = 'Workout'
- else:
- plot.yaxis.axis_label = axlabels[yparam]
-
- binlabel = Label(x=50, y=50, x_units='screen',
- y_units='screen',
- text="Bin size {binsize:3.1f}".format(binsize=binsize),
- background_fill_alpha=0.7,
- background_fill_color='white',
- text_color='black', text_font_size='10pt',
- )
-
- slidertext = "SPM: {:.0f}-{:.0f}, WpS: {:.0f}-{:.0f}".format(
- spmmin, spmmax, workmin, workmax
- )
- sliderlabel = Label(x=50, y=20, x_units='screen', y_units='screen',
- text=slidertext,
- background_fill_alpha=0.7,
- background_fill_color='white',
- text_color='black', text_font_size='10pt',
- )
-
- plot.add_layout(binlabel)
- plot.add_layout(sliderlabel)
-
- yrange1 = Range1d(start=yaxmin, end=yaxmax)
- plot.y_range = yrange1
-
- xrange1 = Range1d(start=xaxmin, end=xaxmax)
- plot.x_range = xrange1
-
- if yparam == 'pace':
- plot.yaxis[0].formatter = DatetimeTickFormatter(
- seconds=["%S"],
- minutes=["%M"]
- )
-
- script, div = components(plot)
-
- return [script, div]
+ return script, div
+
def interactive_cum_flex_chart2(theworkouts, promember=0,
@@ -4993,394 +1563,119 @@ def interactive_cum_flex_chart2(theworkouts, promember=0,
extratitle='',
trendline=False):
- # datadf = dataprep.smalldataprep(theworkouts,xparam,yparam1,yparam2)
ids = [int(w.id) for w in theworkouts]
- columns = [xparam, yparam1, yparam2, 'spm', 'driveenergy', 'distance']
- datadf = dataprep.getsmallrowdata_db(columns, ids=ids, doclean=True,
- workstrokesonly=workstrokesonly)
+ columns = [name for name, d in metrics.rowingmetrics]
+ columns_basic = [name for name, d in metrics.rowingmetrics if d['group'] == 'basic']
+ columns = columns + ['spm', 'driveenergy', 'distance' ,'workoutstate']
+ columns_basic = columns_basic + ['spm', 'driveenergy', 'distance', 'workoutstate']
+
+ datadf = pd.DataFrame()
+ if promember:
+ datadf = dataprep.read_data(columns, ids=ids, doclean=True,
+ workstrokesonly=workstrokesonly, for_chart=True)
+ else:
+ datadf = dataprep.read_data(columns_basic, ids=ids, doclean=True,
+ workstrokesonly=workstrokesonly, for_chart=True)
try:
_ = datadf[yparam2]
- except KeyError: # pragma: no cover
+ except (KeyError, ColumnNotFoundError): # pragma: no cover
yparam2 = 'None'
try:
_ = datadf[yparam1]
- except KeyError:
+ except (KeyError, ColumnNotFoundError):
yparam1 = 'None'
- datadf.dropna(axis=1, how='all', inplace=True)
- datadf.dropna(axis=0, how='any', inplace=True)
+ datadf = dataprep.remove_nulls_pl(datadf)
# test if we have drive energy
try: # pragma: no cover
_ = datadf['driveenergy'].mean()
- except KeyError: # pragma: no cover
- datadf['driveenergy'] = 500.
+ except (KeyError, ColumnNotFoundError): # pragma: no cover
+ datadf = datadf.with_columns((pl.lit(500)).alias("driveenergy"))
# test if we have power
try: # pragma: no cover
_ = datadf['power'].mean()
- except KeyError: # pragma: no cover
- datadf['power'] = 50.
+ except (KeyError, ColumnNotFoundError): # pragma: no cover
+ datadf = datadf.with_columns((pl.lit(50)).alias("power"))
yparamname1 = axlabels[yparam1]
if yparam2 != 'None':
yparamname2 = axlabels[yparam2]
# check if dataframe not empty
- if datadf.empty: # pragma: no cover
+ if datadf.is_empty(): # pragma: no cover
return ['', 'No non-zero data in selection
', '', '']
try:
- datadf['x1'] = datadf.loc[:, xparam]
- except KeyError: # pragma: no cover
+ datadf = datadf.with_columns(pl.col(xparam).alias("x1"))
+ except (KeyError, ColumnNotFoundError): # pragma: no cover
try:
- datadf['x1'] = datadf['distance']
- except KeyError:
+ datadf = datadf.with_columns(pl.col("distance").alias("x1"))
+ except (KeyError, ColumnNotFoundError):
try:
- datadf['x1'] = datadf['time']
- except KeyError: # pragma: no cover
+ datadf = datadf.with_columns(pl.col('time').alias("x1"))
+ except (KeyError, ColumnNotFoundError): # pragma: no cover
return ['', 'No non-zero data in selection
', '', '']
try:
- datadf['y1'] = datadf.loc[:, yparam1]
- except KeyError:
+ datadf = datadf.with_columns(pl.col(yparam1).alias("y1"))
+ except (KeyError, ColumnNotFoundError):
try:
- datadf['y1'] = datadf['pace']
- except KeyError: # pragma: no cover
+ datadf = datadf.with_columns(pl.col('pace').alias("y1"))
+ except (KeyError, ColumnNotFoundError): # pragma: no cover
return ['', 'No non-zero data in selection
', '', '']
if yparam2 != 'None':
try:
- datadf['y2'] = datadf.loc[:, yparam2]
- except KeyError: # pragma: no cover
- datadf['y2'] = datadf['y1']
+ datadf = datadf.with_columns(pl.col(yparam2).alias("y2"))
+ except (KeyError, ColumnNotFoundError): # pragma: no cover
+ datadf = datadf.with_columns(pl.col("y1").alias("y2"))
else: # pragma: no cover
- datadf['y2'] = datadf['y1']
+ datadf = datadf.with_columns(pl.col("y1").alias("y2"))
- # average values
- x1mean = datadf['x1'].mean()
- y1mean = datadf['y1'].mean()
- y2mean = datadf['y2'].mean()
+ datadf = datadf.with_columns(xname = pl.lit(axlabels[xparam]))
+ datadf = datadf.with_columns(yname1 = pl.lit(axlabels[yparam1]))
- x_axis_type = 'linear'
- y_axis_type = 'linear'
- if xparam == 'time':
- x_axis_type = 'datetime'
-
- if yparam1 == 'pace': # pragma: no cover
- y_axis_type = 'datetime'
- y1mean = datadf.loc[:, 'pace'].mean()
-
- datadf['xname'] = axlabels[xparam]
- datadf['yname1'] = axlabels[yparam1]
if yparam2 != 'None':
- datadf['yname2'] = axlabels[yparam2]
+ datadf = datadf.with_columns(yname2 = pl.lit(axlabels[yparam2]))
else: # pragma: no cover
- datadf['yname2'] = axlabels[yparam1]
+ datadf = datadf.with_columns(yname2 = pl.lit(axlabels[yparam1]))
def func(x, a, b):
return a*x+b
x1 = datadf['x1']
y1 = datadf['y1']
- popt, pcov = optimize.curve_fit(func, x1, y1)
- ytrend = func(x1, popt[0], popt[1])
- datadf['ytrend'] = ytrend
-
- source = ColumnDataSource(
- datadf
- )
-
- source2 = ColumnDataSource(
- datadf.copy()
- )
-
- # Add hover to this comma-separated string and see what changes
- if (promember == 1):
- TOOLS = 'save,pan,box_zoom,wheel_zoom,reset,tap,crosshair'
- else: # pragma: no cover
- TOOLS = 'pan,box_zoom,wheel_zoom,reset,tap,crosshair'
-
- plot = figure(x_axis_type=x_axis_type, y_axis_type=y_axis_type,
- tools=TOOLS,
- toolbar_location="above",
- toolbar_sticky=False)
-
- # add watermark
- watermarkurl = "/static/img/logo7.png"
- watermarkrange = Range1d(start=0, end=1)
- watermarkalpha = 0.6
- watermarkx = 0.99
- watermarky = 0.01
- watermarkw = 184
- watermarkh = 35
- watermarkanchor = 'bottom_right'
- plot.extra_y_ranges = {"watermark": watermarkrange}
- plot.extra_x_ranges = {"watermark": watermarkrange}
- #plot.sizing_mode = 'stretch_both'
-
- if extratitle:
- plot.title.text = extratitle
-
-
- plot.image_url([watermarkurl], watermarkx, watermarky,
- watermarkw, watermarkh,
- global_alpha=watermarkalpha,
- w_units='screen',
- h_units='screen',
- anchor=watermarkanchor,
- dilate=True,
- x_range_name="watermark",
- y_range_name="watermark",
- )
-
- x1means = Span(location=x1mean, dimension='height', line_color='green',
- line_dash=[6, 6], line_width=2)
-
- y1means = Span(location=y1mean, dimension='width', line_color='blue',
- line_dash=[6, 6], line_width=2)
- y2means = y1means
-
- xlabel = Label(x=50, y=80, x_units='screen', y_units='screen',
- text=axlabels[xparam] +
- ": {x1mean:6.2f}".format(x1mean=x1mean),
- background_fill_alpha=.7,
- background_fill_color='white',
- text_color='green',
- )
-
- sliderlabel = Label(x=10, y=470, x_units='screen', y_units='screen',
- text='',
- background_fill_alpha=0.7,
- background_fill_color='white',
- text_color='black', text_font_size='10pt',
- )
-
- plot.add_layout(x1means)
- plot.add_layout(xlabel)
- plot.add_layout(y1means)
- plot.add_layout(sliderlabel)
-
- y1label = Label(x=50, y=50, x_units='screen', y_units='screen',
- text=axlabels[yparam1] +
- ": {y1mean:6.2f}".format(y1mean=y1mean),
- background_fill_alpha=.7,
- background_fill_color='white',
- text_color='blue',
- )
-
- if yparam1 != 'time' and yparam1 != 'pace':
- plot.add_layout(y1label)
-
- y2label = y1label
- plot.circle('x1', 'y1', source=source2, fill_alpha=0.3, line_color=None,
- legend_label=yparamname1,
- )
-
- plot.xaxis.axis_label = axlabels[xparam]
- plot.yaxis.axis_label = axlabels[yparam1]
-
- yrange1 = Range1d(start=yaxminima[yparam1], end=yaxmaxima[yparam1])
- plot.y_range = yrange1
-
- xrange1 = Range1d(start=yaxminima[xparam], end=yaxmaxima[xparam])
- plot.x_range = xrange1
-
- if yparam1 == 'pace': # pragma: no cover
- plot.yaxis[0].formatter = DatetimeTickFormatter(
- seconds=["%S"],
- minutes=["%M"]
- )
-
- # trendline
- if trendline:
- plot.line('x1', 'ytrend', source=source2, legend_label=yparamname1+' (trend)')
-
- if yparam2 != 'None':
- yrange2 = Range1d(start=yaxminima[yparam2], end=yaxmaxima[yparam2])
- plot.extra_y_ranges["yax2"] = yrange2
-
- plot.circle('x1', 'y2', color="red", y_range_name="yax2",
- legend_label=yparamname2,
- source=source2, fill_alpha=0.3, line_color=None)
-
- plot.add_layout(LinearAxis(y_range_name="yax2",
- axis_label=axlabels[yparam2]), 'right')
-
- y2means = Span(location=y2mean, dimension='width', line_color='red',
- line_dash=[6, 6], line_width=2, y_range_name="yax2")
-
- plot.add_layout(y2means)
- y2label = Label(x=50, y=20, x_units='screen', y_units='screen',
- text=axlabels[yparam2] +
- ": {y2mean:6.2f}".format(y2mean=y2mean),
- background_fill_alpha=.7,
- background_fill_color='white',
- text_color='red',
- )
- if yparam2 != 'pace' and yparam2 != 'time':
- plot.add_layout(y2label)
-
- callback = CustomJS(args=dict(source=source, source2=source2,
- x1means=x1means,
- y1means=y1means,
- y1label=y1label,
- y2label=y2label,
- xlabel=xlabel,
- sliderlabel=sliderlabel,
- y2means=y2means), code="""
- var data = source.data
- var data2 = source2.data
- var x1 = data['x1']
- var y1 = data['y1']
- var y2 = data['y2']
- var spm1 = data['spm']
-
- var index1 = data['index']
-
- var distance1 = data['distance']
- var power1 = data['power']
- var driveenergy1 = data['driveenergy']
- var xname = data['xname']
- var yname1 = data['yname1']
- var yname2 = data['yname2']
- var workoutid1 = data['workoutid']
- var ytrend = data['ytrend']
-
- var minspm = minspm.value
- var maxspm = maxspm.value
- var mindist = mindist.value
- var maxdist = maxdist.value
- var minwork = minwork.value
- var maxwork = maxwork.value
-
- sliderlabel.text = 'SPM: '+minspm.toFixed(0)+'-'+maxspm.toFixed(0)
- sliderlabel.text += ', Dist: '+mindist.toFixed(0)+'-'+maxdist.toFixed(0)
- sliderlabel.text += ', WpS: '+minwork.toFixed(0)+'-'+maxwork.toFixed(0)
-
- var xm = 0
- var ym1 = 0
- var ym2 = 0
-
- data2['x1'] = []
- data2['y1'] = []
- data2['y2'] = []
- data2['distance'] = []
- data2['power'] = []
- data2['x1mean'] = []
- data2['y1mean'] = []
- data2['y2mean'] = []
- data2['driveenergy'] = []
- data2['workoutid'] = []
- data2['xname'] = []
- data2['yname1'] = []
- data2['yname2'] = []
- data2['spm'] = []
- data2['ytrend'] = []
-
- for (var i=0; i=minspm && spm1[i]<=maxspm) {
- if (distance1[i]>=mindist && distance1[i]<=maxdist) {
- if (driveenergy1[i]>=minwork && driveenergy1[i]<=maxwork) {
- data2['x1'].push(x1[i])
- data2['y1'].push(y1[i])
- data2['y2'].push(y2[i])
- data2['spm'].push(spm1[i])
- data2['driveenergy'].push(driveenergy1[i])
- data2['distance'].push(distance1[i])
- data2['power'].push(power1[i])
- data2['workoutid'].push(0)
- data2['xname'].push(0)
- data2['yname1'].push(0)
- data2['yname2'].push(0)
- data2['ytrend'].push(ytrend[i])
-
- xm += x1[i]
- ym1 += y1[i]
- ym2 += y2[i]
- }
- }
- }
- }
-
-
- xm /= data2['x1'].length
- ym1 /= data2['x1'].length
- ym2 /= data2['x1'].length
-
- for (var i=0; i 1: # pragma: no cover
for column in columns:
@@ -5406,338 +1708,89 @@ def interactive_flexchart_stacked(id, r, xparam='time',
if metricsdicts[column]['maysmooth']:
nrsteps = int(log2(r.usersmooth))
for i in range(nrsteps):
- rowdata[column] = utils.ewmovingaverage(
- rowdata[column], 5)
+ rowdata = rowdata.with_columns(
+ utils.ewmovingaverage(
+ rowdata[column], 5).alias(column)
+ )
except KeyError:
pass
if len(rowdata) < 2:
- rowdata = dataprep.getsmallrowdata_db(columns, ids=[id],
- doclean=False,
- workstrokesonly=False)
+ if ispromember(r.user):
+ rowdata = dataprep.read_data(columns, ids=[id],
+ doclean=False,
+ workstrokesonly=False,
+ for_chart=True)
+ else:
+ rowdata = dataprep.read_data(columns_basic, ids=[id],
+ doclean=False,
+ workstrokesonly=False,
+ for_chart=True)
- if rowdata.empty:
- return "", "No valid data", '', '', comment
+ rowdata = dataprep.remove_nulls_pl(rowdata)
+
+
+ if rowdata.is_empty():
+ return "", "No valid data"
try:
- tseconds = rowdata.loc[:, 'time']
- except KeyError: # pragma: no cover
- return '', 'No time data - cannot make flex plot', '', '', comment
+ tseconds = rowdata['time']
+ except (KeyError, ColumnNotFoundError): # pragma: no cover
+ return '', 'No time data - cannot make flex plot'
try:
- rowdata['x1'] = rowdata.loc[:, xparam]
- except KeyError: # pragma: no cover
- rowdata['x1'] = 0*rowdata.loc[:, 'time']
+ rowdata = rowdata.with_columns(x1=pl.col(xparam))
+ except (KeyError, ColumnNotFoundError): # pragma: no cover
+ rowdata = rowdata.with_columns(x1=pl.lit(0))
try:
- rowdata['y1'] = rowdata.loc[:, yparam1]
- except KeyError: # pragma: no cover
- rowdata['y1'] = 0*rowdata.loc[:, 'time']
- rowdata[yparam1] = rowdata['y1']
-
- try: # pragma: no cover
- rowdata['y2'] = rowdata.loc[:, yparam2]
- except KeyError:
- rowdata['y2'] = 0*rowdata.loc[:, 'time']
- rowdata[yparam2] = rowdata['y2']
+ rowdata = rowdata.with_columns(y1=pl.col(yparam1))
+ except (KeyError, ColumnNotFoundError): # pragma: no cover
+ rowdata = rowdata.with_columns(y1=pl.col("time"))
+ rowdata = rowdata.with_columns((pl.col("y1")).alias(yparam1))
try:
- rowdata['y3'] = rowdata.loc[:, yparam3]
- except KeyError: # pragma: no cover
- rowdata['y3'] = 0*rowdata.loc[:, 'time']
- rowdata[yparam3] = rowdata['y3']
+ rowdata = rowdata.with_columns(y2=pl.col(yparam2))
+ except (KeyError, ColumnNotFoundError): # pragma: no cover
+ rowdata = rowdata.with_columns(y2=pl.col("time"))
+ rowdata = rowdata.with_columns((pl.col("y2")).alias(yparam2))
try:
- rowdata['y4'] = rowdata.loc[:, yparam4]
- except KeyError: # pragma: no cover
- rowdata['y4'] = 0*rowdata.loc[:, 'time']
- rowdata[yparam4] = rowdata['y4']
-
- if xparam == 'time':
- xaxmax = tseconds.max()
- xaxmin = tseconds.min()
- elif xparam == 'distance' or xparam == 'cumdist': # pragma: no cover
- xaxmax = rowdata['x1'].max()
- xaxmin = rowdata['x1'].min()
- else: # pragma: no cover
- try:
- xaxmax = get_yaxmaxima(r, xparam, mode)
- xaxmin = get_yaxminima(r, xparam, mode)
- except KeyError:
- xaxmax = rowdata['x1'].max()
- xaxmin = rowdata['x1'].min()
-
- x_axis_type = 'linear'
- y1_axis_type = 'linear'
- y2_axis_type = 'linear'
- y3_axis_type = 'linear'
- y4_axis_type = 'linear'
- if xparam == 'time':
- x_axis_type = 'datetime'
-
- if yparam1 == 'pace': # pragma: no cover
- y1_axis_type = 'datetime'
-
- if yparam2 == 'pace': # pragma: no cover
- y2_axis_type = 'datetime'
-
- if yparam3 == 'pace': # pragma: no cover
- y3_axis_type = 'datetime'
-
- if yparam4 == 'pace': # pragma: no cover
- y4_axis_type = 'datetime'
+ rowdata = rowdata.with_columns(y1=pl.col(yparam3))
+ except (KeyError, ColumnNotFoundError): # pragma: no cover
+ rowdata = rowdata.with_columns(y3=pl.col("time"))
+ rowdata = rowdata.with_columns((pl.col("y3")).alias(yparam3))
try:
- rowdata['xname'] = axlabels[xparam]
- except KeyError: # pragma: no cover
- rowdata['xname'] = xparam
+ rowdata = rowdata.with_columns(y4=pl.col(yparam1))
+ except (KeyError, ColumnNotFoundError): # pragma: no cover
+ rowdata = rowdata.with_columns(y4=pl.col("time"))
+ rowdata = rowdata.with_columns((pl.col("y4")).alias(yparam4))
- try:
- rowdata['yname1'] = axlabels[yparam1]
- except KeyError: # pragma: no cover
- rowdata['yname1'] = yparam1
+
+ # replace nans
+ rowdata = rowdata.fill_nan(0)
- try:
- rowdata['yname2'] = axlabels[yparam2]
- except KeyError: # pragma: no cover
- rowdata['yname2'] = yparam2
- try:
- rowdata['yname3'] = axlabels[yparam3]
- except KeyError: # pragma: no cover
- rowdata['yname3'] = yparam3
+ data_dict = rowdata.to_dicts()
- try:
- rowdata['yname4'] = axlabels[yparam4]
- except KeyError: # pragma: no cover
- rowdata['yname4'] = yparam4
+ metrics_list = [{'name': name, 'rowingmetrics':d } for name, d in metrics.rowingmetrics]
+
+ chart_data = {
+ 'title': row.name,
+ 'x': xparam,
+ 'y1': yparam1,
+ 'y2': yparam2,
+ 'y3': yparam3,
+ 'y4': yparam4,
+ 'data': data_dict,
+ 'metrics': metrics_list,
+ }
- # prepare data
- source = ColumnDataSource(
- rowdata
- )
+ script, div = get_chart("/stacked", chart_data, debug=False)
- TOOLS = 'box_zoom,wheel_zoom,reset,tap,hover'
- TOOLS2 = 'box_zoom,hover'
+ return script, div
- plot1 = figure(x_axis_type=x_axis_type, y_axis_type=y1_axis_type, width=920, height=150,
- tools=TOOLS, toolbar_location='above')
- plot2 = figure(x_axis_type=x_axis_type, y_axis_type=y2_axis_type, width=920, height=150,
- tools=TOOLS2, toolbar_location=None)
- plot3 = figure(x_axis_type=x_axis_type, y_axis_type=y3_axis_type, width=920, height=150,
- tools=TOOLS2, toolbar_location=None)
- plot4 = figure(x_axis_type=x_axis_type, y_axis_type=y4_axis_type, width=920, height=150,
- tools=TOOLS2, toolbar_location=None)
-
- plot1.xaxis.visible = False
- plot2.xaxis.visible = False
- plot3.xaxis.visible = False
-
- #plot1.sizing_mode = 'stretch_both'
- #plot2.sizing_mode = 'stretch_both'
- #plot3.sizing_mode = 'stretch_both'
- #plot4.sizing_mode = 'stretch_both'
-
- linked_crosshair = CrosshairTool(dimensions="height")
- plot1.add_tools(linked_crosshair)
- plot2.add_tools(linked_crosshair)
- plot3.add_tools(linked_crosshair)
- plot4.add_tools(linked_crosshair)
-
- xaxlabel = axlabels.get(xparam, xparam)
- yax1label = axlabels.get(yparam1, yparam1)
-
- plot1.yaxis.axis_label = yax1label
-
- yax2label = axlabels.get(yparam2, yparam2)
-
- plot2.yaxis.axis_label = yax2label
-
- yax3label = axlabels.get(yparam3, yparam3)
-
- plot3.yaxis.axis_label = yax3label
-
- yax4label = axlabels.get(yparam4, yparam4)
-
- plot4.yaxis.axis_label = yax4label
-
- plot4.xaxis.axis_label = xaxlabel
-
- xrange1 = Range1d(start=xaxmin, end=xaxmax)
- plot1.x_range = xrange1
- plot2.x_range = xrange1
- plot3.x_range = xrange1
- plot4.x_range = xrange1
-
- if xparam == 'time':
- plot4.xaxis[0].formatter = DatetimeTickFormatter(
- hours=["%H"],
- minutes=["%M"],
- seconds=["%S"],
- days=["0"],
- months=[""],
- years=[""]
- )
-
- hover1 = plot1.select(dict(type=HoverTool))
- hover2 = plot2.select(dict(type=HoverTool))
- hover3 = plot3.select(dict(type=HoverTool))
- hover4 = plot4.select(dict(type=HoverTool))
-
- if yparam1 == 'pace':
- y1tooltip = '@fpace'
- elif yparam1 != 'None': # pragma: no cover
- y1tooltip = '@{yparam1}'.format(yparam1=yparam1)
- if metricsdicts[yparam1]['numtype'] == 'integer' or yparam1 == 'power':
- y1tooltip += '{int}'
- else: # pragma: no cover
- y1tooltip += '{0.00}'
- else: # pragma: no cover
- y1tooltip = ''
- comment = 'The metric in the first chart is only accessible with a Pro plan or higher'
-
- if yparam2 == 'pace': # pragma: no cover
- y2tooltip = '@fpace'
- elif yparam2 != 'None':
- y2tooltip = '@{yparam2}'.format(yparam2=yparam2)
- if metricsdicts[yparam2]['numtype'] == 'integer' or yparam2 == 'power':
- y2tooltip += '{int}'
- else: # pragma: no cover
- y2tooltip += '{0.00}'
- else: # pragma: no cover
- y2tooltip = ''
- comment = 'The metric in the second chart is only accessible with a Pro plan or higher'
-
- if yparam3 == 'pace': # pragma: no cover
- y3tooltip = '@fpace'
- elif yparam3 != 'None':
- y3tooltip = '@{yparam3}'.format(yparam3=yparam3)
- if metricsdicts[yparam3]['numtype'] == 'integer' or yparam3 == 'power':
- y3tooltip += '{int}'
- else: # pragma: no cover
- y3tooltip += '{0.00}'
- else: # pragma: no cover
- y3tooltip = ''
- comment = 'The metric in the third chart is only accessible with a Pro plan or higher'
-
- if yparam4 == 'pace': # pragma: no cover
- y4tooltip = '@fpace'
- elif yparam4 != 'None':
- y4tooltip = '@{yparam4}'.format(yparam4=yparam4)
- if metricsdicts[yparam4]['numtype'] == 'integer' or yparam4 == 'power': # pragma: no cover
- y4tooltip += '{int}'
- else: # pragma: no cover
- y4tooltip += '{0.00}'
- else: # pragma: no cover
- y4tooltip = ''
- comment = 'The metric in the fourth chart is only accessible with a Pro plan or higher'
-
- if yparam1 != 'None':
- hover1.tooltips = OrderedDict([
- ('Time', '@ftime'),
- ('Distance', '@distance{int}'),
- (axlabels[yparam1], y1tooltip),
- (axlabels[yparam2], y2tooltip),
- (axlabels[yparam3], y3tooltip),
- (axlabels[yparam4], y4tooltip),
- ])
- if yparam2 != 'None':
- hover2.tooltips = OrderedDict([
- ('Time', '@ftime'),
- ('Distance', '@distance{int}'),
- (axlabels[yparam1], y1tooltip),
- (axlabels[yparam2], y2tooltip),
- (axlabels[yparam3], y3tooltip),
- (axlabels[yparam4], y4tooltip),
- ])
-
- if yparam3 != 'None':
- hover3.tooltips = OrderedDict([
- ('Time', '@ftime'),
- ('Distance', '@distance{int}'),
- (axlabels[yparam1], y1tooltip),
- (axlabels[yparam2], y2tooltip),
- (axlabels[yparam3], y3tooltip),
- (axlabels[yparam4], y4tooltip),
- ])
-
- if yparam4 != 'None':
- hover4.tooltips = OrderedDict([
- ('Time', '@ftime'),
- ('Distance', '@distance{int}'),
- (axlabels[yparam1], y1tooltip),
- (axlabels[yparam2], y2tooltip),
- (axlabels[yparam3], y3tooltip),
- (axlabels[yparam4], y4tooltip),
- ])
-
- hover1.mode = 'vline'
- hover2.mode = 'vline'
- hover3.mode = 'vline'
- hover4.mode = 'vline'
-
- y1min = get_yaxminima(r, yparam1, mode)
- y2min = get_yaxminima(r, yparam2, mode)
- y3min = get_yaxminima(r, yparam3, mode)
- y4min = get_yaxminima(r, yparam4, mode)
-
- y1max = get_yaxmaxima(r, yparam1, mode)
- y2max = get_yaxmaxima(r, yparam2, mode)
- y3max = get_yaxmaxima(r, yparam3, mode)
- y4max = get_yaxmaxima(r, yparam4, mode)
-
- plot1.y_range = Range1d(start=y1min, end=y1max)
- plot2.y_range = Range1d(start=y2min, end=y2max)
- plot3.y_range = Range1d(start=y3min, end=y3max)
- plot4.y_range = Range1d(start=y4min, end=y4max)
-
- if yparam1 == 'pace':
- plot1.yaxis[0].formatter = DatetimeTickFormatter(
- seconds=["%S"],
- minutes=["%M"]
- )
- plot1.y_range = Range1d(y1min, y1max)
-
- if yparam2 == 'pace': # pragma: no cover
- plot2.yaxis[0].formatter = DatetimeTickFormatter(
- seconds=["%S"],
- minutes=["%M"]
- )
- plot2.y_range = Range1d(y2min, y2max)
-
- if yparam3 == 'pace': # pragma: no cover
- plot3.yaxis[0].formatter = DatetimeTickFormatter(
- seconds=["%S"],
- minutes=["%M"]
- )
- plot3.y_range = Range1d(y3min, y3max)
-
- if yparam4 == 'pace': # pragma: no cover
- plot4.yaxis[0].formatter = DatetimeTickFormatter(
- seconds=["%S"],
- minutes=["%M"]
- )
- plot4.y_range = Range1d(y4min, y4max)
-
- plot1.line('x1', 'y1', source=source, color=palette2[1])
- plot2.line('x1', 'y2', source=source, color=palette2[3])
- plot3.line('x1', 'y3', source=source, color=palette2[0])
- plot4.line('x1', 'y4', source=source, color=palette2[2])
-
- mylayout = layoutcolumn([
- plot1,
- plot2,
- plot3,
- plot4,
- ])
-
- #mylayout.sizing_mode = 'stretch_both'
-
- script, div = components(mylayout)
- js_resources = INLINE.render_js()
- css_resources = INLINE.render_css()
-
- return script, div, js_resources, css_resources, comment
def interactive_flex_chart2(id, r, promember=0,
@@ -5749,117 +1802,100 @@ def interactive_flex_chart2(id, r, promember=0,
trendline=False,
mode='rower'):
- watermarkurl = "/static/img/logo7.png"
- watermarkrange = Range1d(start=0, end=1)
- watermarkalpha = 0.6
- watermarkx = 0.99
- watermarky = 0.01
- watermarkw = 184
- watermarkh = 35
- watermarkanchor = 'bottom_right'
+ columns = [name for name, d in metrics.rowingmetrics]
+ columns_basic = [name for name, d in metrics.rowingmetrics if d['group'] == 'basic']
+ columns = columns + ['spm', 'driveenergy', 'distance','workoutstate']
+ columns_basic = columns_basic + ['spm', 'driveenergy', 'distance','workoutstate']
- columns = [xparam, yparam1, yparam2,
- 'ftime', 'distance', 'fpace',
- 'power', 'hr', 'spm', 'driveenergy',
- 'time', 'pace', 'workoutstate']
+ if promember:
+ rowdata = dataprep.read_data(columns, ids=[id], doclean=True,
+ workstrokesonly=workstrokesonly, for_chart=True)
+ else:
+ rowdata = dataprep.read_data(columns_basic, ids=[id], doclean=True,
+ workstrokesonly=workstrokesonly, for_chart=True)
- rowdata = dataprep.getsmallrowdata_db(columns, ids=[id], doclean=True,
- workstrokesonly=workstrokesonly)
+
if r.usersmooth > 1: # pragma: no cover
for column in columns:
try:
if metricsdicts[column]['maysmooth']:
nrsteps = int(log2(r.usersmooth))
for i in range(nrsteps):
- rowdata[column] = utils.ewmovingaverage(
- rowdata[column], 5)
+ rowdata = rowdata.with_columns(
+ (utils.ewmovingaverage(
+ rowdata[column], 5)).alias(column)
+ )
except KeyError:
pass
- try:
- if len(rowdata) < 2:
- rowdata = dataprep.getsmallrowdata_db(columns, ids=[id],
- doclean=False,
- workstrokesonly=False)
+ if len(rowdata) < 2:
+ if promember:
+ rowdata = dataprep.read_data(columns, ids=[id],
+ doclean=False,
+ workstrokesonly=False, for_chart=True)
+ else:
+ rowdata = dataprep.read_data(columns_basic, ids=[id], doclean=False,
+ workstrokesonly=False, for_chart=True)
workstrokesonly = False
- except (KeyError, TypeError): # pragma: no cover
- workstrokesonly = False
+
try:
_ = rowdata[yparam2]
- except (KeyError, TypeError): # pragma: no cover
+ except (KeyError, TypeError, ColumnNotFoundError): # pragma: no cover
yparam2 = 'None'
try:
_ = rowdata[yparam1]
- except (TypeError, KeyError): # pragma: no cover
+ except (TypeError, KeyError, ColumnNotFoundError): # pragma: no cover
yparam1 = 'None'
# test if we have drive energy
try:
_ = rowdata['driveenergy'].mean()
- except (KeyError, TypeError):
- rowdata['driveenergy'] = 500.
+ except (KeyError, TypeError, ColumnNotFoundError):
+ rowdata = rowdata.with_columns(driveenergy=pl.lit(500))
# test if we have power
try:
_ = rowdata['power'].mean()
- except (KeyError, TypeError):
- rowdata['power'] = 50.
+ except (KeyError, TypeError, ColumnNotFoundError):
+ rowdata = rowdata.with_columns(power=pl.lit(50))
+
# replace nans
- rowdata.fillna(value=0, inplace=True)
+ rowdata = dataprep.remove_nulls_pl(rowdata)
row = Workout.objects.get(id=id)
- if rowdata.empty:
- return "", "No valid data", '', '', workstrokesonly
-
- workoutstatesrest = [3]
-
- if workstrokesonly: # pragma: no cover
- try:
- rowdata = rowdata[~rowdata['workoutstate'].isin(workoutstatesrest)]
- except KeyError:
- pass
+ if rowdata.is_empty():
+ return "", "No valid data", workstrokesonly
try:
- tseconds = rowdata.loc[:, 'time']
- except KeyError: # pragma: no cover
- return '', 'No time data - cannot make flex plot', '', '', workstrokesonly
+ tseconds = rowdata['time']
+ except (KeyError, ColumnNotFoundError): # pragma: no cover
+ return '', 'No time data - cannot make flex plot', workstrokesonly
try:
- rowdata['x1'] = rowdata.loc[:, xparam]
- except KeyError: # pragma: no cover
- rowdata['x1'] = 0*rowdata.loc[:, 'time']
+ rowdata = rowdata.with_columns(x1 = pl.col(xparam))
+ except (KeyError, ColumnNotFoundError): # pragma: no cover
+ rowdata = rowdata.with_columns(x1 = pl.col("time"))
try:
- rowdata['y1'] = rowdata.loc[:, yparam1]
- except KeyError: # pragma: no cover
- rowdata['y1'] = 0*rowdata.loc[:, 'time']
- rowdata[yparam1] = rowdata['y1']
+ rowdata = rowdata.with_columns(y1 = pl.col(yparam1))
+ except (KeyError, ColumnNotFoundError): # pragma: no cover
+ rowdata = rowdata.with_columns(y1 = pl.col("time"))
+ rowdata = rowdata.with_columns(yparam1 = pl.col("y1"))
+
if yparam2 != 'None':
try:
- rowdata['y2'] = rowdata.loc[:, yparam2]
- except KeyError: # pragma: no cover
- rowdata['y2'] = 0*rowdata.loc[:, 'time']
- rowdata[yparam2] = rowdata['y2']
- else: # pragma: no cover
- rowdata['y2'] = rowdata['y1']
+ rowdata = rowdata.with_columns(y2 = pl.col(yparam2))
+ except (KeyError, ColumnNotFoundError): # pragma: no cover
+ rowdata = rowdata.with_columns(y2 = pl.col("time"))
+ rowdata = rowdata.with_columns(yparam2 = pl.col("y2"))
- if xparam == 'time':
- xaxmax = tseconds.max()
- xaxmin = tseconds.min()
- elif xparam == 'distance' or xparam == 'cumdist':
- xaxmax = rowdata['x1'].max()
- xaxmin = rowdata['x1'].min()
else: # pragma: no cover
- try:
- xaxmax = get_yaxmaxima(r, xparam, mode)
- xaxmin = get_yaxminima(r, xparam, mode)
- except KeyError:
- xaxmax = rowdata['x1'].max()
- xaxmin = rowdata['x1'].min()
+ rowdata = rowdata.with_columns(y2=pl.col("y1"))
+
# average values
if xparam != 'time':
@@ -5873,43 +1909,25 @@ def interactive_flex_chart2(id, r, promember=0,
y1mean = rowdata['y1'].mean()
y2mean = rowdata['y2'].mean()
- if xparam != 'time':
- xvals = xaxmin+np.arange(100)*(xaxmax-xaxmin)/100.
- else:
- xvals = np.arange(100)
-
- # constant power plot
- if yparam1 == 'driveenergy':
- if xparam == 'spm': # pragma: no cover
- yconstantpower = rowdata['y1'].mean()*rowdata['x1'].mean()/xvals
-
- x_axis_type = 'linear'
- y_axis_type = 'linear'
- if xparam == 'time':
- x_axis_type = 'datetime'
-
- if yparam1 == 'pace':
- y_axis_type = 'datetime'
- try:
- y1mean = rowdata.loc[:, 'pace'].mean()
- except KeyError: # pragma: no cover
- y1mean = 0
try:
- rowdata['xname'] = axlabels[xparam]
- except KeyError: # pragma: no cover
- rowdata['xname'] = xparam
+ rowdata = rowdata.with_columns((pl.lit(axlabels[xparam])).alias("xname"))
+ except (KeyError, ColumnNotFoundError): # pragma: no cover
+ rowdata = rowdata.with_columns((pl.lit(xparam)).alias("xname"))
+
try:
- rowdata['yname1'] = axlabels[yparam1]
- except KeyError: # pragma: no cover
- rowdata['yname1'] = yparam1
+ rowdata = rowdata.with_columns((pl.lit(axlabels[yparam1])).alias("yname1"))
+ except (KeyError, ColumnNotFoundError): # pragma: no cover
+ rowdata = rowdata.with_columns((pl.lit(yparam1)).alias("yname1"))
+
if yparam2 != 'None':
try:
- rowdata['yname2'] = axlabels[yparam2]
- except KeyError: # pragma: no cover
- rowdata['yname2'] = yparam2
+ rowdata = rowdata.with_columns((pl.lit(axlabels[yparam2])).alias("yname2"))
+ except (KeyError, ColumnNotFoundError): # pragma: no cover
+ rowdata = rowdata.with_columns((pl.lit(yparam2)).alias("yname2"))
+
else: # pragma: no cover
- rowdata['yname2'] = rowdata['yname1']
+ rowdata = rowdata.with_columns((pl.col("yname1")).alias("yname2"))
def func(x, a, b):
return a*x+b
@@ -5919,423 +1937,32 @@ def interactive_flex_chart2(id, r, promember=0,
try:
popt, pcov = optimize.curve_fit(func, x1, y1)
ytrend = func(x1, popt[0], popt[1])
- rowdata['ytrend'] = ytrend
+ rowdata = rowdata.with_columns(ytrend=ytrend)
except TypeError: # pragma: no cover
- rowdata['ytrend'] = y1
-
- # prepare data
- source = ColumnDataSource(
- rowdata
- )
+ rowdata = rowdata.with_columns(ytrend=pl.col("y1"))
- # second source for filtering
- source2 = ColumnDataSource(
- rowdata.copy()
- )
- # Add hover to this comma-separated string and see what changes
- if (promember == 1):
- TOOLS = 'save,pan,box_zoom,wheel_zoom,reset,tap,hover,crosshair'
- else:
- TOOLS = 'pan,box_zoom,wheel_zoom,reset,tap,hover,crosshair'
+ #rowdata = rowdata.replace([np.inf, -np.inf], np.nan)
+ rowdata = rowdata.fill_nan(None).drop_nulls()
- plot = figure(x_axis_type=x_axis_type, y_axis_type=y_axis_type,
- tools=TOOLS, toolbar_location='above',
- toolbar_sticky=False, width=800, height=600,
- )
- #plot.sizing_mode = 'stretch_both'
+ data_dict = rowdata.to_dicts()
- # add watermark
- watermarkurl = "/static/img/logo7.png"
- watermarkrange = Range1d(start=0, end=1)
- watermarkalpha = 0.6
- watermarkx = 0.99
- watermarky = 0.01
- watermarkw = 184
- watermarkh = 35
- watermarkanchor = 'bottom_right'
- plot.extra_y_ranges = {"watermark": watermarkrange}
- plot.extra_x_ranges = {"watermark": watermarkrange}
- #plot.sizing_mode = 'stretch_both'
+ metrics_list = [{'name': name, 'rowingmetrics':d } for name, d in metrics.rowingmetrics]
+
+ chart_data = {
+ 'title': row.name,
+ 'x': xparam,
+ 'y1': yparam1,
+ 'y2': yparam2,
+ 'data': data_dict,
+ 'metrics': metrics_list,
+ 'trendline': trendline,
+ 'plottype': plottype,
+ }
- plot.image_url([watermarkurl], watermarkx, watermarky,
- watermarkw, watermarkh,
- global_alpha=watermarkalpha,
- w_units='screen',
- h_units='screen',
- anchor=watermarkanchor,
- dilate=True,
- x_range_name="watermark",
- y_range_name="watermark",
- )
+ script, div = get_chart("/flex", chart_data, debug=False)
- x1means = Span(location=x1mean, dimension='height', line_color='green',
- line_dash=[6, 6], line_width=2)
-
- y1means = Span(location=y1mean, dimension='width', line_color='blue',
- line_dash=[6, 6], line_width=2)
- y2means = y1means
-
- try:
- xlabeltext = axlabels[xparam]+": {x1mean:6.2f}".format(
- x1mean=x1mean
- )
- except KeyError: # pragma: no cover
- xlabeltext = xparam+": {x1mean:6.2f}".format(x1mean=x1mean)
-
- xlabel = Label(x=50, y=80, x_units='screen', y_units='screen',
- text=xlabeltext,
- background_fill_alpha=.7,
- background_fill_color='white',
- text_color='green',
- )
-
- annolabel = Label(x=50, y=450, x_units='screen', y_units='screen',
- text='',
- background_fill_alpha=0.7,
- background_fill_color='white',
- text_color='black',
- )
-
- sliderlabel = Label(x=10, y=470, x_units='screen', y_units='screen',
- text='',
- background_fill_alpha=0.7,
- background_fill_color='white',
- text_color='black', text_font_size='10pt',
- )
-
- if (xparam != 'time') and (xparam != 'distance') and (xparam != 'cumdist'): # pragma: no cover
- plot.add_layout(x1means)
- plot.add_layout(xlabel)
-
- plot.add_layout(y1means)
- plot.add_layout(annolabel)
- plot.add_layout(sliderlabel)
-
- try:
- yaxlabel = axlabels[yparam1]
- except KeyError: # pragma: no cover
- yaxlabel = str(yparam1)+' '
-
- try:
- xaxlabel = axlabels[xparam]
- except KeyError: # pragma: no cover
- xaxlabel = xparam
-
- y1label = Label(x=50, y=50, x_units='screen', y_units='screen',
- text=yaxlabel+": {y1mean:6.2f}".format(y1mean=y1mean),
- background_fill_alpha=.7,
- background_fill_color='white',
- text_color='blue',
- )
- if yparam1 != 'time' and yparam1 != 'pace': # pragma: no cover
- plot.add_layout(y1label)
- y2label = y1label
-
- # average values
- if yparam1 == 'driveenergy': # pragma: no cover
- if xparam == 'spm':
- plot.line(xvals, yconstantpower, color="green",
- legend_label="Constant Power")
-
- # trendline
- if trendline: # pragma: no cover
- plot.line('x1', 'ytrend', source=source2, legend_label=yaxlabel+' (trend)')
-
- if plottype == 'line':
- plot.line('x1', 'y1', source=source2, legend_label=yaxlabel)
- elif plottype == 'scatter': # pragma: no cover
- plot.scatter('x1', 'y1', source=source2, legend_label=yaxlabel, fill_alpha=0.4,
- line_color=None)
-
- try:
- plot.title.text = row.name
- except ValueError: # pragma: no cover
- plot.title.text = ""
- plot.title.text_font_size = "1.0em"
-
- #plot.sizing_mode = 'stretch_both'
- plot.xaxis.axis_label = xaxlabel
-
- plot.yaxis.axis_label = yaxlabel
-
- try:
- yrange1 = Range1d(start=get_yaxminima(r, yparam1, mode),
- end=get_yaxmaxima(r, yparam1, mode))
- except KeyError: # pragma: no cover
- yrange1 = Range1d(start=rowdata[yparam1].min(),
- end=rowdata[yparam1].max())
-
- plot.y_range = yrange1
-
- if (xparam != 'time') and (xparam != 'distance') and (xparam != 'cumdist'): # pragma: no cover
- try:
- xrange1 = Range1d(start=get_yaxminima(r, xparam, mode),
- end=get_yaxmaxima(r, xparam, mode))
- except KeyError:
- xrange1 = Range1d(start=rowdata[xparam].min(),
- end=rowdata[xparam].max())
-
- plot.x_range = xrange1
-
- if xparam == 'time':
- xrange1 = Range1d(start=xaxmin, end=xaxmax)
- plot.x_range = xrange1
- plot.xaxis[0].formatter = DatetimeTickFormatter(
- hours=["%H"],
- minutes=["%M"],
- seconds=["%S"],
- days=["0"],
- months=[""],
- years=[""]
- )
-
- if yparam1 == 'pace':
- plot.yaxis[0].formatter = DatetimeTickFormatter(
- seconds=["%S"],
- minutes=["%M"]
- )
-
- if yparam2 != 'None':
- try:
- yrange2 = Range1d(start=get_yaxminima(r, yparam2, mode),
- end=get_yaxmaxima(r, yparam2, mode))
- except KeyError: # pragma: no cover
- yrange2 = Range1d(start=rowdata[yparam2].min(),
- end=rowdata[yparam2].max())
-
- plot.extra_y_ranges["yax2"] = yrange2
- # = {"yax2": yrange2}
- try:
- axlegend = axlabels[yparam2]
- except KeyError: # pragma: no cover
- axlegend = str(yparam2)+' '
-
- if plottype == 'line':
- plot.line('x1', 'y2', color="red", y_range_name="yax2",
- legend_label=axlegend,
- source=source2)
-
- elif plottype == 'scatter': # pragma: no cover
- plot.scatter('x1', 'y2', source=source2, legend_label=axlegend,
- fill_alpha=0.4,
- line_color=None, color="red", y_range_name="yax2")
-
- plot.add_layout(LinearAxis(y_range_name="yax2",
- axis_label=axlegend), 'right')
-
- y2means = Span(location=y2mean, dimension='width', line_color='red',
- line_dash=[6, 6], line_width=2, y_range_name="yax2")
-
- plot.add_layout(y2means)
- y2label = Label(x=50, y=20, x_units='screen', y_units='screen',
- text=axlegend+": {y2mean:6.2f}".format(y2mean=y2mean),
- background_fill_alpha=.7,
- background_fill_color='white',
- text_color='red',
- )
- if yparam2 != 'pace' and yparam2 != 'time':
- plot.add_layout(y2label)
-
- hover = plot.select(dict(type=HoverTool))
-
- hover.tooltips = OrderedDict([
- ('Time', '@ftime'),
- ('Distance', '@distance{int}'),
- ('Pace', '@fpace'),
- ('HR', '@hr{int}'),
- ('SPM', '@spm{1.1}'),
- ('Power', '@power{int}'),
- ])
-
- hover.mode = 'mouse'
-
- callback = CustomJS(args=dict(source=source, source2=source2,
- x1means=x1means,
- y1means=y1means,
- y1label=y1label,
- y2label=y2label,
- xlabel=xlabel,
- annolabel=annolabel,
- sliderlabel=sliderlabel,
- y2means=y2means,
- ), code="""
- var data = source.data
- var data2 = source2.data
- var x1 = data['x1']
- var y1 = data['y1']
- var y2 = data['y2']
- var spm1 = data['spm']
- var time1 = data['time']
- var ftime1 = data['ftime']
- var pace1 = data['pace']
- var hr1 = data['hr']
- var fpace1 = data['fpace']
- var distance1 = data['distance']
- var power1 = data['power']
- var driveenergy1 = data['driveenergy']
- var xname = data['xname']
- var yname1 = data['yname1']
- var yname2 = data['yname2']
- var workoutid1 = data['workoutid']
- var workoutstate1 = data['workoutstate']
- var ytrend = data['ytrend']
-
- var annotation = annotation.value
- var minspm = minspm.value
- var maxspm = maxspm.value
- var mindist = mindist.value
- var maxdist = maxdist.value
- var minwork = minwork.value
- var maxwork = maxwork.value
-
- sliderlabel.text = 'SPM: '+minspm.toFixed(0)+'-'+maxspm.toFixed(0)
- sliderlabel.text += ', Dist: '+mindist.toFixed(0)+'-'+maxdist.toFixed(0)
- sliderlabel.text += ', WpS: '+minwork.toFixed(0)+'-'+maxwork.toFixed(0)
-
- var xm = 0
- var ym1 = 0
- var ym2 = 0
-
- data2['x1'] = []
- data2['y1'] = []
- data2['y2'] = []
- data2['spm'] = []
- data2['time'] = []
- data2['ftime'] = []
- data2['pace'] = []
- data2['hr'] = []
- data2['fpace'] = []
- data2['distance'] = []
- data2['power'] = []
- data2['x1mean'] = []
- data2['y1mean'] = []
- data2['y2mean'] = []
- data2['driveenergy'] = []
- data2['workoutid'] = []
- data2['workoutstate'] = []
- data2['xname'] = []
- data2['yname1'] = []
- data2['yname2'] = []
- data2['ytrend'] = []
-
-
- for (var i=0; i=minspm && spm1[i]<=maxspm) {
- if (distance1[i]>=mindist && distance1[i]<=maxdist) {
- if (driveenergy1[i]>=minwork && driveenergy1[i]<=maxwork) {
- data2['x1'].push(x1[i])
- data2['y1'].push(y1[i])
- data2['y2'].push(y2[i])
- data2['spm'].push(spm1[i])
- data2['time'].push(time1[i])
- data2['ftime'].push(ftime1[i])
- data2['fpace'].push(fpace1[i])
- data2['driveenergy'].push(driveenergy1[i])
- data2['pace'].push(pace1[i])
- data2['hr'].push(hr1[i])
- data2['distance'].push(distance1[i])
- data2['power'].push(power1[i])
- data2['workoutid'].push(0)
- data2['workoutstate'].push(0)
- data2['xname'].push(0)
- data2['yname1'].push(0)
- data2['yname2'].push(0)
- data2['ytrend'].push(ytrend[i])
-
-
- xm += x1[i]
- ym1 += y1[i]
- ym2 += y2[i]
- }
- }
- }
- }
-
- xm /= data2['x1'].length
- ym1 /= data2['x1'].length
- ym2 /= data2['x1'].length
-
- for (var i=0; iNo non-zero data in selection
', '']
+
+ datadf = datadf.with_columns(pl.col("workoutid").cast(pl.UInt32).keep_name())
+
+ # filter for start end dict
+
nrworkouts = len(ids)
try:
- tseconds = datadf.loc[:, 'time']
- except KeyError: # pragma: no cover
+ tseconds = datadf['time']
+ except (KeyError, ColumnNotFoundError): # pragma: no cover
try:
- tseconds = datadf.loc[:, xparam]
+ tseconds = datadf[xparam]
except:
- return ['', 'A chart data error occurred
', '', 'A chart data error occurred']
+ return ['A chart data error occurred
', '']
- # check if dataframe not empty
- if datadf.empty: # pragma: no cover
- return ['', 'No non-zero data in selection
', '', 'No non-zero data in selection']
+ if (xparam == 'time'):
+ datadf = datadf.with_columns((pl.col(xparam)-datadf[0,xparam]).alias(xparam))
+
+ data_dict = datadf.to_dicts()
- if xparam != 'distance' and xparam != 'time' and xparam != 'cumdist': # pragma: no cover
- xaxmax = yaxmaxima[xparam]
- xaxmin = yaxminima[xparam]
- elif xparam == 'time' and not startenddict:
- xaxmax = tseconds.max()
- xaxmin = tseconds.min()
- elif xparam == 'time' and startenddict: # pragma: no cover
- deltas = [pair[1]-pair[0] for key, pair in startenddict.items()]
- xaxmin = 0
- xaxmax = pd.Series(deltas).max()*1000.
- if xaxmax == 0:
- xaxmax = tseconds.max()
- else:
- xaxmax = datadf['distance'].max()
- xaxmin = datadf['distance'].min()
+ metrics_list = [{'name': name, 'rowingmetrics':d } for name, d in metrics.rowingmetrics]
- if yparam == 'distance': # pragma: no cover
- yaxmin = datadf['distance'].min()
- yaxmax = datadf['distance'].max()
- elif yparam == 'cumdist': # pragma: no cover
- yaxmin = datadf['cumdist'].min()
- yaxmax = datadf['cumdist'].max()
- else:
- yaxmin = yaxminima[yparam]
- yaxmax = yaxmaxima[yparam]
+ workoutsdict = [{'id': id, 'label': labeldict[id]} for id in ids]
- x_axis_type = 'linear'
- y_axis_type = 'linear'
+ chart_data = {
+ 'title': '',
+ 'x': xparam,
+ 'y': yparam,
+ 'data': data_dict,
+ 'metrics': metrics_list,
+ 'plottype': plottype,
+ 'workouts': workoutsdict,
+ }
- # Add hover to this comma-separated string and see what changes
- if (promember == 1):
- TOOLS = 'save,pan,box_zoom,wheel_zoom,reset,tap,crosshair'
- else: # pragma: no cover
- TOOLS = 'pan,box_zoom,wheel_zoom,reset,tap,crosshair'
+ script, div = get_chart("/compare", chart_data, debug=False)
+ return script, div
- if yparam == 'pace':
- y_axis_type = 'datetime'
- yaxmax = 90.*1e3
- yaxmin = 150.*1e3
-
- if xparam == 'time':
- x_axis_type = 'datetime'
-
- plot = figure(x_axis_type=x_axis_type, y_axis_type=y_axis_type,
- tools=TOOLS,
- toolbar_location="above",
- width=920, height=500,
- toolbar_sticky=False)
-
- # add watermark
- watermarkurl = "/static/img/logo7.png"
- watermarkrange = Range1d(start=0, end=1)
- watermarkalpha = 0.6
- watermarkw = 184
- watermarkh = 35
- plot.extra_y_ranges = {"watermark": watermarkrange}
- plot.extra_x_ranges = {"watermark": watermarkrange}
- #plot.sizing_mode = 'stretch_both'
-
- plot.image_url([watermarkurl], 0.05, 0.9,
- watermarkw, watermarkh,
- global_alpha=watermarkalpha,
- w_units='screen',
- h_units='screen',
- anchor='top_left',
- dilate=True,
- x_range_name="watermark",
- y_range_name="watermark",
- )
-
- colors = itertools.cycle(palette)
-
- cntr = 0
- l1 = []
-
- try:
- items = itertools.izip(ids, colors)
- except AttributeError:
- items = zip(ids, colors)
-
- for id, color in items:
- group = datadf[datadf['workoutid'] == int(id)].copy()
- try:
- startsecond, endsecond = startenddict[id]
- except KeyError:
- startsecond = 0
- endsecond = 0
-
- group.sort_values(by='time', ascending=True, inplace=True)
-
- if endsecond > 0:
- group['time'] = group['time'] - 1.e3*startsecond
- mask = group['time'] < 0
- group.mask(mask, inplace=True)
- mask = group['time'] > 1.e3*(endsecond-startsecond)
- group.mask(mask, inplace=True)
-
- if xparam == 'cumdist':
- group['cumdist'] = group['cumdist'] - group['cumdist'].min()
- res = make_cumvalues(group[xparam])
- group[xparam] = res[0]
- elif xparam == 'distance':
- group['distance'] = group['distance'] - group['distance'].min()
-
- try:
- group['x'] = group[xparam]
- except KeyError: # pragma: no cover
- group['x'] = group['time']
- errormessage = xparam+' has no values. Plot invalid'
- try:
- group['y'] = group[yparam]
- except KeyError:
- group['y'] = 0.0*group['x']
-
- ymean = group['y'].mean()
- f = group['time'].diff().mean()
- if f != 0 and not np.isnan(f):
- windowsize = 2 * (int(20000./(f))) + 1
- else:
- windowsize = 1
-
- if windowsize > 3 and windowsize < len(group['y']):
- try:
- group['y'] = savgol_filter(group['y'], windowsize, 3)
- except ValueError: # pragma: no cover
- pass
-
- ylabel = Label(x=100, y=60+nrworkouts*20-20*cntr,
- x_units='screen', y_units='screen',
- text=axlabels[yparam] +
- ": {ymean:6.2f}".format(ymean=ymean),
- background_fill_alpha=.7,
- background_fill_color='white',
- text_color=color,
- )
- if yparam != 'time' and yparam != 'pace':
- plot.add_layout(ylabel)
-
- source = ColumnDataSource(
- group
- )
-
- TIPS = OrderedDict([
- ('time', '@ftime'),
- ('pace', '@fpace'),
- ('hr', '@hr'),
- ('spm', '@spm{1.1}'),
- ('distance', '@distance{5}'),
- ])
-
- hover = plot.select(type=HoverTool)
- hover.tooltips = TIPS
-
- if labeldict:
- try:
- legend_label = labeldict[id]
- except KeyError: # pragma: no cover
- legend_label = str(id)
- else: # pragma: no cover
- legend_label = str(id)
-
- if plottype == 'line':
- l1.append(plot.line('x', 'y', source=source, color=color,
- legend_label=legend_label, line_width=2))
- else:
- l1.append(plot.scatter('x', 'y', source=source, color=color, legend_label=legend_label,
- fill_alpha=0.4, line_color=None))
-
- plot.add_tools(HoverTool(renderers=[l1[cntr]], tooltips=TIPS))
- cntr += 1
-
- plot.legend.location = 'top_right'
- plot.xaxis.axis_label = axlabels[xparam]
- plot.yaxis.axis_label = axlabels[yparam]
-
- if (xparam != 'time') and (xparam != 'distance') and (xparam != 'cumdist'): # pragma: no cover
- xrange1 = Range1d(start=yaxminima[xparam], end=yaxmaxima[xparam])
- plot.x_range = xrange1
-
- yrange1 = Range1d(start=yaxmin, end=yaxmax)
- plot.y_range = yrange1
-
- if xparam == 'time':
- xrange1 = Range1d(start=xaxmin, end=xaxmax)
- plot.x_range = xrange1
- plot.xaxis[0].formatter = DatetimeTickFormatter(
- hours=["%H"],
- minutes=["%M"],
- seconds=["%S"],
- days=["0"],
- months=[""],
- years=[""]
- )
-
- if yparam == 'pace':
- plot.yaxis[0].formatter = DatetimeTickFormatter(
- seconds=["%S"],
- minutes=["%M"]
- )
-
- script, div = components(plot)
-
- return [script, div, message, errormessage]
-
-
-def interactive_otw_advanced_pace_chart(id=0, promember=0):
- # check if valid ID exists (workout exists)
- rowdata, row = dataprep.getrowdata_db(id=id)
- rowdata.dropna(axis=1, how='all', inplace=True)
- rowdata.dropna(axis=0, how='any', inplace=True)
-
- if rowdata.empty:
- return "", "No Valid Data Available"
-
- # Add hover to this comma-separated string and see what changes
- if (promember == 1):
- TOOLS = 'save,pan,box_zoom,wheel_zoom,reset,tap,hover,crosshair'
- else: # pragma: no cover
- TOOLS = 'pan,box_zoom,wheel_zoom,reset,tap,hover,crosshair'
-
- source = ColumnDataSource(
- rowdata
- )
-
- plot = figure(x_axis_type="datetime", y_axis_type="datetime",
- tools=TOOLS,
- width=920,
- toolbar_sticky=False)
-
- # add watermark
- watermarkurl = "/static/img/logo7.png"
- watermarkrange = Range1d(start=0, end=1)
- watermarkalpha = 0.6
- watermarkx = 0.99
- watermarky = 0.01
- watermarkw = 184
- watermarkh = 35
- watermarkanchor = 'bottom_right'
- plot.extra_y_ranges = {"watermark": watermarkrange}
- plot.extra_x_ranges = {"watermark": watermarkrange}
- #plot.sizing_mode = 'scale_both'
-
- plot.image_url([watermarkurl], watermarkx, watermarky,
- watermarkw, watermarkh,
- global_alpha=watermarkalpha,
- w_units='screen',
- h_units='screen',
- anchor=watermarkanchor,
- dilate=True,
- x_range_name="watermark",
- y_range_name="watermark",
- )
-
- try:
- plot.title.text = row.name
- except ValueError: # pragma: no cover
- plot.title.text = ""
- #plot.title.text_font_size = value("1.2em")
- plot.xaxis.axis_label = "Time"
- plot.yaxis.axis_label = "Pace (/500m)"
- plot.xaxis[0].formatter = DatetimeTickFormatter(
- hours=["%H"],
- minutes=["%M"],
- seconds=["%S"],
- days=["0"],
- months=[""],
- years=[""]
- )
- plot.yaxis[0].formatter = DatetimeTickFormatter(
- seconds=["%S"],
- minutes=["%M"]
- )
-
- ymax = 1.0e3*90
- ymin = 1.0e3*210
-
- plot.y_range = Range1d(ymin, ymax)
-
- hover = plot.select(dict(type=HoverTool))
-
- plot.line('time', 'pace', source=source,
- legend_label="Pace", color="black")
- plot.line('time', 'nowindpace', source=source,
- legend_label="Corrected Pace", color="red")
-
- hover.tooltips = OrderedDict([
- ('Time', '@ftime'),
- ('Pace', '@fpace'),
- ('Corrected Pace', '@fnowindpace'),
- ('HR', '@hr{int}'),
- ('SPM', '@spm{1.1}'),
- ])
-
- hover.mode = 'mouse'
-
- try:
- script, div = components(plot)
- except: # pragma: no cover
- script = ''
- div = ''
-
- return [script, div]
-
-
-def get_zones_report(rower, startdate, enddate, trainingzones='hr', date_agg='week',
+def get_zones_report_pl(rower, startdate, enddate, trainingzones='hr', date_agg='week',
yaxis='time'):
- dates = []
- dates_sorting = []
- minutes = []
- hours = []
- zones = []
+ data = []
enddate = enddate + datetime.timedelta(days=1)
@@ -6925,329 +2190,209 @@ def get_zones_report(rower, startdate, enddate, trainingzones='hr', date_agg='we
columns = ['workoutid', 'hr', 'power', 'time']
- df = dataprep.getsmallrowdata_db(columns, ids=ids)
+ df = dataprep.read_data(columns, ids=ids, workstrokesonly=False, doclean=False)
+ df = dataprep.remove_nulls_pl(df)
+
try:
- df['deltat'] = df['time'].diff().clip(lower=0).clip(upper=20*1e3)
- except KeyError: # pragma: no cover
+ df = df.with_columns((pl.col("time").diff().clip(0, 20*1.e3)).alias("deltat")).lazy()
+ except ColumnNotFoundError:
pass
-
- df = dataprep.clean_df_stats(df, workstrokesonly=False,
- ignoreadvanced=True, ignorehr=False)
-
+
hrzones = rower.hrzones
powerzones = rower.powerzones
for w in workouts:
- dd3 = w.date.strftime('%Y/%m')
- dd4 = '{year}/{week:02d}'.format(
- week=arrow.get(w.date).isocalendar()[1],
- year=w.date.strftime('%y')
- )
- dd4 = (w.date - datetime.timedelta(days=w.date.weekday())
- ).strftime('%y/%m/%d')
-
- # print(w.date,arrow.get(w.date),arrow.get(w.date).isocalendar())
iswater = w.workouttype in mytypes.otwtypes
- qryw = 'workoutid == {workoutid}'.format(workoutid=w.id)
- qry = 'hr < {ut2}'.format(ut2=rower.ut2)
- if trainingzones == 'power':
- qry = 'power < {ut2}'.format(ut2=rower.pw_ut2)
- timeinzone = df.query(qry).query(qryw)['deltat'].sum()/(60*1e3)
- if date_agg == 'week':
- dates.append(dd4)
- dates_sorting.append(dd4)
- else: # pragma: no cover
- dates.append(dd3)
- dates_sorting.append(dd3)
- minutes.append(timeinzone)
- hours.append(timeinzone/60.)
- if trainingzones == 'hr':
- zones.append('<{ut2}'.format(ut2=hrzones[1]))
- else:
- zones.append('<{ut2}'.format(ut2=powerzones[1]))
- # print(w,dd,timeinzone,'= rower.ut2,
+ pl.col("hr") < rower.ut1,
+ )
+ time_pw_ut1 = df.filter(
+ pl.col("workoutid") == w.id,
+ pl.col("power") >= pw_ut2,
+ pl.col("power") < pw_ut1,
+ )
- qry = '{at} <= hr < {tr}'.format(at=rower.at, tr=rower.tr)
- if trainingzones == 'power':
- qry = '{at} <= power < {tr}'.format(at=rower.pw_at, tr=rower.pw_tr)
- if iswater:
- qry = '{at} <= power < {tr}'.format(at=rower.pw_at*rower.otwslack/100.,
- tr=rower.pw_tr*rower.otwslack/100.)
- timeinzone = df.query(qry).query(qryw)['deltat'].sum()/(60*1e3)
- if date_agg == 'week':
- dates.append(dd4)
- dates_sorting.append(dd4)
- else: # pragma: no cover
- dates.append(dd3)
- dates_sorting.append(dd3)
- minutes.append(timeinzone)
- hours.append(timeinzone/60.)
- if trainingzones == 'hr':
- zones.append(hrzones[3])
- else:
- zones.append(powerzones[3])
- # print(w,dd,timeinzone,'AT')
+ #3
+ time_at = df.filter(
+ pl.col("workoutid") == w.id,
+ pl.col("hr") >= rower.ut1,
+ pl.col("hr") < rower.at,
+ )
+ time_pw_at = df.filter(
+ pl.col("workoutid") == w.id,
+ pl.col("power") >= pw_ut1,
+ pl.col("power") < pw_at,
+ )
- qry = '{tr} <= hr < {an}'.format(tr=rower.tr, an=rower.an)
- if trainingzones == 'power':
- qry = '{tr} <= power < {an}'.format(tr=rower.pw_tr, an=rower.pw_an)
- if iswater:
- qry = '{tr} <= power < {an}'.format(tr=rower.pw_tr*rower.otwslack/100.,
- an=rower.pw_an*rower.otwslack/100.)
- timeinzone = df.query(qry).query(qryw)['deltat'].sum()/(60*1e3)
- if date_agg == 'week':
- dates.append(dd4)
- dates_sorting.append(dd4)
- else: # pragma: no cover
- dates.append(dd3)
- dates_sorting.append(dd3)
- minutes.append(timeinzone)
- hours.append(timeinzone/60.)
- if trainingzones == 'hr':
- zones.append(hrzones[4])
- else:
- zones.append(powerzones[4])
- # print(w,dd,timeinzone,'TR')
+ #4
+ time_tr = df.filter(
+ pl.col("workoutid") == w.id,
+ pl.col("hr") >= rower.at,
+ pl.col("hr") < rower.tr,
+ )
+ time_pw_tr = df.filter(
+ pl.col("workoutid") == w.id,
+ pl.col("power") >= pw_at,
+ pl.col("power") < pw_tr,
+ )
- qry = 'hr >= {an}'.format(an=rower.an)
- if trainingzones == 'power':
- qry = 'power >= {an}'.format(an=rower.pw_an)
- if iswater:
- qry = 'power >= {an}'.format(an=rower.pw_an*rower.otwslack/100.)
- timeinzone = df.query(qry).query(qryw)['deltat'].sum()/(60*1e3)
- if date_agg == 'week':
- dates.append(dd4)
- dates_sorting.append(dd4)
- else: # pragma: no cover
- dates.append(dd3)
- dates_sorting.append(dd3)
- minutes.append(timeinzone)
- hours.append(timeinzone/60.)
- if trainingzones == 'hr':
- zones.append(hrzones[5])
- else:
- zones.append(powerzones[5])
- # print(w,dd,timeinzone,'AN')
+ #5
+ time_an = df.filter(
+ pl.col("workoutid") == w.id,
+ pl.col("hr") >= rower.tr,
+ pl.col("hr") < rower.an,
+ )
+ time_pw_an = df.filter(
+ pl.col("workoutid") == w.id,
+ pl.col("power") >= pw_tr,
+ pl.col("power") < pw_an,
+ )
+
+ time_max = df.filter(
+ pl.col("workoutid") == w.id,
+ pl.col("hr") >= rower.an,
+ )
+ time_pw_max = df.filter(
+ pl.col("workoutid") == w.id,
+ pl.col("power") >= pw_an,
+ )
+
+ time_in_ut2 = time_ut2.collect()['deltat'].sum()/(60*1.e3)
+ time_in_ut2_pw = time_pw_ut2.collect()['deltat'].sum()/(60*1.e3)
- try:
- d = utc.localize(startdate)
- except (ValueError, AttributeError): # pragma: no cover
- d = startdate
+ time_in_ut1 = time_ut1.collect()['deltat'].sum()/(60*1.e3)
+ time_in_ut1_pw = time_pw_ut1.collect()['deltat'].sum()/(60*1.e3)
- try:
- enddate = utc.localize(enddate)
- except (ValueError, AttributeError): # pragma: no cover
- pass
+ time_in_at = time_at.collect()['deltat'].sum()/(60*1.e3)
+ time_in_at_pw = time_pw_at.collect()['deltat'].sum()/(60*1.e3)
- while d <= enddate:
- if date_agg == 'week':
- dd4 = '{year}/{week:02d}'.format(
- week=arrow.get(d).isocalendar()[1],
- year=d.strftime('%y')
- )
- dd4 = (d - datetime.timedelta(days=d.weekday())).strftime('%y/%m/%d')
+ time_in_tr = time_tr.collect()['deltat'].sum()/(60*1.e3)
+ time_in_tr_pw = time_pw_tr.collect()['deltat'].sum()/(60*1.e3)
- dates.append(dd4)
- dates_sorting.append(dd4)
- else: # pragma: no cover
- dates.append(d.strftime('%Y/%m'))
- dates_sorting.append(d.strftime('%Y/%m'))
+ time_in_an = time_an.collect()['deltat'].sum()/(60*1.e3)
+ time_in_an_pw = time_pw_an.collect()['deltat'].sum()/(60*1.e3)
- minutes.append(0)
- hours.append(0)
- if trainingzones == 'hr':
- zones.append(hrzones[1])
- else:
- zones.append(powerzones[1])
+ time_in_max = time_max.collect()['deltat'].sum()/(60*1.e3)
+ time_in_max_pw = time_pw_max.collect()['deltat'].sum()/(60*1.e3)
- d += datetime.timedelta(days=1)
+ data.append({
+ 'date': w.date.strftime("%Y-%m-%d"),
+ 'id': w.id,
+ 'zonename': '<{ut2}'.format(ut2=hrzones[1]),
+ 'time_in_zone': time_in_ut2,
+ 'pw_zonename': '<{ut2}'.format(ut2=powerzones[1]),
+ 'pw_time_in_zone': time_in_ut2_pw,
+ })
- # this should be renamed with rower zones
- data = {
- 'date': dates,
- 'date_sorting': dates_sorting,
- 'minutes': minutes,
- 'zones': zones,
- 'hours': hours,
+ data.append({
+ 'date': w.date.strftime("%Y-%m-%d"),
+ 'id': w.id,
+ 'zonename': hrzones[1],
+ 'time_in_zone': time_in_ut1,
+ 'pw_zonename': powerzones[1],
+ 'pw_time_in_zone': time_in_ut1_pw,
+ })
+
+ data.append({
+ 'date': w.date.strftime("%Y-%m-%d"),
+ 'id': w.id,
+ 'zonename': hrzones[2],
+ 'time_in_zone': time_in_at,
+ 'pw_zonename': powerzones[2],
+ 'pw_time_in_zone': time_in_at_pw,
+ })
+
+ data.append({
+ 'date': w.date.strftime("%Y-%m-%d"),
+ 'id': w.id,
+ 'zonename': hrzones[3],
+ 'time_in_zone': time_in_tr,
+ 'pw_zonename': powerzones[3],
+ 'pw_time_in_zone': time_in_tr_pw,
+ })
+
+ data.append({
+ 'date': w.date.strftime("%Y-%m-%d"),
+ 'id': w.id,
+ 'zonename': hrzones[4],
+ 'time_in_zone': time_in_an,
+ 'pw_zonename': powerzones[4],
+ 'pw_time_in_zone': time_in_an_pw,
+ })
+
+ data.append({
+ 'date': w.date.strftime("%Y-%m-%d"),
+ 'id': w.id,
+ 'zonename': hrzones[5],
+ 'time_in_zone': time_in_max,
+ 'pw_zonename': powerzones[5],
+ 'pw_time_in_zone': time_in_max_pw,
+ })
+
+
+ chart_data = {
+ 'data': data,
+ 'hrzones': hrzones,
+ 'powerzones': powerzones,
}
- # print(pd.DataFrame(data).head())
-
- return data
+ return chart_data
-def interactive_zoneschart(rower, data, startdate, enddate, trainingzones='hr', date_agg='week',
+def interactive_zoneschart2(rower, data, startdate, enddate, trainingzones='hr', date_agg='week',
yaxis='time'):
if startdate >= enddate: # pragma: no cover
st = startdate
startdate = enddate
enddate = st
- hrzones = rower.hrzones
- powerzones = rower.powerzones
+ hrzones = data['hrzones']
+ powerzones = data['powerzones']
- color_map = {
- '<{ut2}'.format(ut2=hrzones[1]): 'green',
- hrzones[1]: 'lime',
- hrzones[2]: 'yellow',
- hrzones[3]: 'blue',
- hrzones[4]: 'purple',
- hrzones[5]: 'red',
- }
- if trainingzones == 'power':
- color_map = {
- '<{ut2}'.format(ut2=powerzones[1]): 'green',
- powerzones[1]: 'lime',
- powerzones[2]: 'yellow',
- powerzones[3]: 'blue',
- powerzones[4]: 'purple',
- powerzones[5]: 'red',
- }
-
- zones_order = [
- '<{ut2}'.format(ut2=hrzones[1]),
- hrzones[1],
- hrzones[2],
- hrzones[3],
- hrzones[4],
- hrzones[5]
- ]
-
- if trainingzones == 'power':
- zones_order = [
- '<{ut2}'.format(ut2=powerzones[1]),
- powerzones[1],
- powerzones[2],
- powerzones[3],
- powerzones[4],
- powerzones[5]
- ]
-
- df = pd.DataFrame(data)
- df2 = pd.DataFrame(data)
-
- df.drop('minutes', inplace=True, axis='columns')
-
- df.sort_values('date_sorting', inplace=True)
- df.drop('date_sorting', inplace=True, axis='columns')
- df['totaltime'] = 0
- if df.empty: # pragma: no cover
- return '', 'No Data Found'
-
- if yaxis == 'percentage':
- dates = list(set(df['date'].values))
- for date in dates:
- qry = 'date == "{d}"'.format(d=date)
-
- totaltime = df.query(qry)['hours'].sum()
-
- mask = df['date'] == date
- df.loc[mask, 'totaltime'] = totaltime
-
- df['percentage'] = 100.*df['hours']/df['totaltime']
- df.drop('hours', inplace=True, axis='columns')
- df.drop('totaltime', inplace=True, axis='columns')
-
- hv.extension('bokeh')
-
- xrotation = 0
- nrdates = len(list(set(df['date'].values)))
- if nrdates > 10:
- xrotation = 45
-
- bars = hv.Bars(df, kdims=['date', 'zones']).aggregate(
- function=np.sum).redim.values(zones=zones_order)
-
- bars.opts(
- opts.Bars(cmap=color_map, show_legend=True, stacked=True,
- tools=['tap', 'hover'], width=550, padding=(0, (0, .1)),
- legend_position='bottom',
- xrotation=xrotation,
- show_frame=False)
- )
-
- p = hv.render(bars)
-
- p.title.text = 'Activity {d1} to {d2} for {r}'.format(
+ data['yaxis'] = yaxis
+ data['title'] = 'Activity {d1} to {d2} for {r}'.format(
d1=startdate.strftime("%Y-%m-%d"),
d2=enddate.strftime("%Y-%m-%d"),
r=str(rower),
)
+
+ data['stackBy'] = 'time_in_zone'
+ data['colorBy'] = 'zonename'
+ if trainingzones == 'power':
+ data['stackBy'] = 'pw_time_in_zone'
+ data['colorBy'] = 'pw_zonename'
+ data['doReduce'] = True
+ data['datebin'] = date_agg
+ data['colors'] = ['green', 'lime', 'yellow', 'blue', 'purple', 'red']
- if date_agg == 'week':
- p.xaxis.axis_label = 'Week'
- else: # pragma: no cover
- p.xaxis.axis_label = 'Month'
-
- if yaxis == 'percentage':
- p.yaxis.axis_label = 'Percentage'
-
- p.width = 550
- p.height = 350
- p.toolbar_location = 'right'
- p.y_range.start = 0
- #p.sizing_mode = 'stretch_both'
-
- if yaxis == 'percentage':
- tidy_df = df2.groupby(['date']).sum()
-
- source2 = ColumnDataSource(tidy_df)
- y2rangemax = tidy_df.loc[:, 'hours'].max()*1.1
- p.extra_y_ranges["yax2"] = Range1d(start=0, end=y2rangemax)
- p.line('date', 'hours', source=source2,
- y_range_name="yax2", color="black", width=5)
- p.circle('date', 'hours', source=source2, y_range_name="yax2", color="black", size=10)
-
-# p.circle('date', 'hours', source=source2, y_range_name="yax2", color="black", size=10,
-# legend_label='Hours')
- p.add_layout(LinearAxis(y_range_name="yax2",
- axis_label='Hours'), 'right')
-
- script, div = components(p)
+ script, div = get_chart("/zones", data, debug=False)
return script, div
+
+
diff --git a/rowers/metrics.py b/rowers/metrics.py
index 285529b3..3766addb 100644
--- a/rowers/metrics.py
+++ b/rowers/metrics.py
@@ -364,6 +364,7 @@ for key, dict in rowingmetrics:
metricsdicts[key] = dict
+
metricsgroups = list(set([d['group'] for n, d in rowingmetrics]))
dtypes = {}
diff --git a/rowers/models.py b/rowers/models.py
index 6ba7658d..a05de7c5 100644
--- a/rowers/models.py
+++ b/rowers/models.py
@@ -3833,9 +3833,12 @@ def auto_delete_file_on_delete(sender, instance, **kwargs):
# remove parquet file
try:
dirname = 'media/strokedata_{id}.parquet.gz'.format(id=instance.id)
- shutil.rmtree(dirname)
+ os.remove(dirname)
except FileNotFoundError:
- pass
+ try:
+ shutil.rmtree(dirname)
+ except:
+ pass
# remove parquet file
try:
@@ -5257,6 +5260,8 @@ class ForceCurveAnalysis(models.Model):
average_spm = models.FloatField(default=23)
average_boatspeed = models.FloatField(default=4.0)
include_rest_strokes = models.BooleanField(default=False)
+ plotcircles = models.BooleanField(default=False)
+ plotlines = models.BooleanField(default=False)
def __str__(self):
s = 'Force Curve Analysis {name} ({date})'.format(name = self.name,
diff --git a/rowers/plannedsessions.py b/rowers/plannedsessions.py
index 3ecda13e..c8ca0ab2 100644
--- a/rowers/plannedsessions.py
+++ b/rowers/plannedsessions.py
@@ -18,13 +18,12 @@ import rowers.dataprep as dataprep
import numpy as np
import rowers.metrics as metrics
import rowers.mytypes as mytypes
-from rowers.courses import get_time_course
from rowers.utils import to_pace
from rowers.opaque import encoder
from rowingdata import rower as rrower
from rowingdata import rowingdata as rrdata
import arrow
-import pandas as pd
+import polars as pl
import json
# Python
@@ -203,16 +202,8 @@ def get_execution_report(rower, startdate, enddate, plan=None):
startdate__gte=enddate)
else: # pragma: no cover
plans = TrainingPlan.objects.filter(rowers__in=[rower])
- #plans2 = TrainingPlan.objects.filter(
- # enddate__lte=enddate, startdate__lte=enddate, rowers__in=[rower])
- #plans = plans | plans2
-
- #plans = plans.exclude(status=False).order_by("-enddate")
if not plans:
- # make week cycles here
- # get monday before startdate
-
micros = []
else:
sorted_plans = sorted(plans, key= lambda t: t.overlap(startdate,enddate))
@@ -290,7 +281,7 @@ def get_execution_report(rower, startdate, enddate, plan=None):
else:
plannedscore += 60
actualscore += 0
- elif w.hrtss != 0:
+ elif w.hrtss >= 0:
if ratio > 0:
plannedscore += w.hrtss/ratio
actualscore += w.hrtss
@@ -313,7 +304,7 @@ def get_execution_report(rower, startdate, enddate, plan=None):
planned += [plannedscore]
executed += [actualscore]
- data = pd.DataFrame({
+ data = pl.DataFrame({
'startdate': startdates,
'planned': planned,
'executed': executed,
@@ -444,8 +435,9 @@ def add_workouts_plannedsession(ws, ps, r):
for record in records:
record.delete()
- df = dataprep.getsmallrowdata_db(
+ df = dataprep.read_data(
['time', 'cumdist'], ids=[w.id])
+ df = dataprep.remove_nulls_pl(df)
fastest_milliseconds, starttime, endtime = datautils.getfastest(
df, ps.sessionvalue, mode='distance')
@@ -475,11 +467,11 @@ def add_workouts_plannedsession(ws, ps, r):
for record in records:
record.delete()
- df = dataprep.getsmallrowdata_db(
+ df = dataprep.read_data(
['time', 'cumdist'], ids=[w.id])
+ df = dataprep.remove_nulls_pl(df)
fastest_meters, starttime, endtime = datautils.getfastest(
df, ps.sessionvalue, mode='time')
-
if fastest_meters > 0:
w.plannedsession = ps
w.save()
@@ -1694,8 +1686,9 @@ def add_workout_fastestrace(ws, race, r, recordid=0, doregister=False):
record.coursecompleted = True
record.workoutid = ws[0].id
if race.sessiontype == 'fastest_distance':
- df = dataprep.getsmallrowdata_db(
+ df = dataprep.read_data(
['time', 'cumdist'], ids=[ws[0].id])
+ df = dataprep.remove_nulls_pl(df)
fastest_milliseconds, startsecond, endsecond = datautils.getfastest(
df, race.sessionvalue, mode='distance')
velo = race.sessionvalue/fastest_milliseconds
@@ -1711,8 +1704,9 @@ def add_workout_fastestrace(ws, race, r, recordid=0, doregister=False):
record.endsecond = endsecond
record.save()
if race.sessiontype == 'fastest_time': # pragma: no cover
- df = dataprep.getsmallrowdata_db(
+ df = dataprep.read_data(
['time', 'cumdist'], ids=[ws[0].id])
+ df = dataprep.remove_nulls_pl(df)
fastest_meters, startsecond, endsecond = datautils.getfastest(
df, race.sessionvalue, mode='time')
velo = fastest_meters/(60.*race.sessionvalue)
diff --git a/rowers/serializers.py b/rowers/serializers.py
index cb9de92b..1dc594ef 100644
--- a/rowers/serializers.py
+++ b/rowers/serializers.py
@@ -6,7 +6,7 @@ from rest_framework import serializers
from rowers.models import (
Workout, Rower, FavoriteChart, VirtualRaceResult,
VirtualRace, GeoCourse, StandardCollection, CourseStandard,
- GeoPolygon, GeoPoint, PlannedSession,
+ GeoPolygon, GeoPoint, PlannedSession, ForceCurveAnalysis
)
from django.core.exceptions import PermissionDenied
@@ -297,7 +297,23 @@ class GeoPointSerializer(serializers.ModelSerializer):
)
extra_kwargs = {'id': {'read_only': False, 'required': True}}
+class ForceCurveAnalysisSerializer(serializers.ModelSerializer):
+ class Meta:
+ model = ForceCurveAnalysis
+ fields = (
+ 'id',
+ 'name',
+ 'workout',
+ 'dist_min',
+ 'dist_max',
+ 'spm_min',
+ 'spm_max',
+ 'work_min',
+ 'work_max',
+ 'include_rest_strokes'
+ )
+
class GeoPolygonSerializer(serializers.ModelSerializer):
points = GeoPointSerializer(many=True)
diff --git a/rowers/tasks.py b/rowers/tasks.py
index f42891c9..8c46ca9f 100644
--- a/rowers/tasks.py
+++ b/rowers/tasks.py
@@ -56,7 +56,7 @@ from scipy.signal import savgol_filter
from scipy.interpolate import griddata
import rowingdata
-from rowingdata import make_cumvalues
+from rowingdata import make_cumvalues, make_cumvalues_array
from uuid import uuid4
from rowingdata import rowingdata as rdata
@@ -115,6 +115,9 @@ tpapilocation = TP_API_LOCATION
from requests_oauthlib import OAuth1, OAuth1Session
import pandas as pd
+import polars as pl
+from polars.exceptions import ColumnNotFoundError
+
from django_rq import job
from django.utils import timezone
@@ -127,7 +130,8 @@ from rowers import mytypes
from rowers.dataroutines import (
- getsmallrowdata_db, updatecpdata_sql, update_c2id_sql,
+ getsmallrowdata_pd, updatecpdata_sql, update_c2id_sql,
+ read_data,
#update_workout_field_sql,
update_agegroup_db, update_strokedata,
add_c2_stroke_data_db, totaltime_sec_to_string,
@@ -559,7 +563,7 @@ def handle_sporttracks_workout_from_data(user, importid, source,
strokedata = pd.DataFrame.from_dict({
key: pd.Series(value, dtype='object') for key, value in data.items()
})
-
+
try:
workouttype = data['type']
except KeyError: # pragma: no cover
@@ -2549,9 +2553,10 @@ def handle_otwsetpower(self, f1, boattype, boatclass, coastalbrand, weightvalue,
totaltime = totaltime + rowdata.df.loc[0, ' ElapsedTime (sec)']
except KeyError: # pragma: no cover
pass
- df = getsmallrowdata_db(
+ df = getsmallrowdata_pd(
['power', 'workoutid', 'time'], ids=[workoutid],
debug=debug)
+
thesecs = totaltime
maxt = 1.05 * thesecs
logarr = datautils.getlogarr(maxt)
@@ -2583,53 +2588,6 @@ def handle_otwsetpower(self, f1, boattype, boatclass, coastalbrand, weightvalue,
return 1
-@app.task
-def handle_updateergcp(rower_id, workoutfilenames, debug=False, **kwargs):
- therows = []
- for f1 in workoutfilenames:
- try:
- rowdata = rdata(csvfile=f1)
- except IOError: # pragma: no cover
- try:
- rowdata = rdata(csvfile=f1 + '.csv')
- except IOError:
- try:
- rowdata = rdata(csvfile=f1 + '.gz')
- except IOError:
- rowdata = 0
- if rowdata != 0:
- therows.append(rowdata)
-
- cpdata = rowingdata.cumcpdata(therows)
- cpdata.columns = cpdata.columns.str.lower()
-
- updatecpdata_sql(rower_id, cpdata['delta'], cpdata['cp'],
- table='ergcpdata', distance=cpdata['distance'],
- debug=debug)
-
- return 1
-
-
-@app.task
-def handle_updatecp(rower_id, workoutids, debug=False, table='cpdata', **kwargs):
- columns = ['power', 'workoutid', 'time']
- df = getsmallrowdata_db(columns, ids=workoutids, debug=debug)
-
- if df.empty: # pragma: no cover
- return 0
-
- maxt = 1.05*df['time'].max()/1000.
-
- logarr = datautils.getlogarr(maxt)
-
- dfgrouped = df.groupby(['workoutid'])
-
- delta, cpvalue, avgpower = datautils.getcp(dfgrouped, logarr)
-
- updatecpdata_sql(rower_id, delta, cpvalue, debug=debug, table=table)
-
- return 1
-
@app.task
def handle_makeplot(f1, f2, t, hrdata, plotnr, imagename,
@@ -3179,43 +3137,6 @@ def handle_sendemail_invite_reject(email, name, teamname, managername,
return 1
-@app.task
-def handle_setcp(strokesdf, filename, workoutid, debug=False, **kwargs):
- try:
- os.remove(filename)
- except FileNotFoundError:
- pass
- if not strokesdf.empty:
-
- try:
- totaltime = strokesdf['time'].max()
- except KeyError: # pragma: no cover
- return 0
- try:
- powermean = strokesdf['power'].mean()
- except KeyError: # pragma: no cover
- powermean = 0
-
- if powermean != 0:
- thesecs = totaltime
- maxt = 1.05 * thesecs
-
- if maxt > 0:
- logarr = datautils.getlogarr(maxt)
- dfgrouped = strokesdf.groupby(['workoutid'])
- delta, cpvalues, avgpower = datautils.getcp(dfgrouped, logarr)
-
- df = pd.DataFrame({
- 'delta': delta,
- 'cp': cpvalues,
- 'id': workoutid,
- })
- df.to_parquet(filename, engine='fastparquet',
- compression='GZIP')
- return 1
-
- return 1 # pragma: no cover
-
@app.task
def handle_sendemail_invite_accept(email, name, teamname, managername,
@@ -3251,23 +3172,22 @@ graphql_url = "https://rp3rowing-app.com/graphql"
@app.task
def handle_update_wps(rid, types, ids, mode, debug=False, **kwargs):
- df = getsmallrowdata_db(['time', 'driveenergy'], ids=ids)
+ df = read_data(['time', 'driveenergy'], ids=ids)
try:
- mask = df['driveenergy'] > 100
- except (KeyError, TypeError): # pragma: no cover
- return 0
- try:
- wps_median = int(df.loc[mask, 'driveenergy'].median())
+ wps_median = int(df.filter(pl.col("driveenergy")>100)["driveenergy"].median())
+ rower = Rower.objects.get(id=rid)
+ if mode == 'water':
+ rower.median_wps = wps_median
+ else: # pragma: no cover
+ rower.median_wps_erg = wps_median
+
+ rower.save()
except ValueError: # pragma: no cover
- return 0
-
- rower = Rower.objects.get(id=rid)
- if mode == 'water':
- rower.median_wps = wps_median
- else:
- rower.median_wps_erg = wps_median
-
- rower.save()
+ wps_median = 0
+ except OverflowError:
+ wps_median = 0
+ except ColumnNotFoundError:
+ wps_median = 0
return wps_median
@@ -3647,7 +3567,7 @@ def handle_c2_async_workout(alldata, userid, c2token, c2id, delaysec,
loncoord = np.zeros(nr_rows)
try:
- strokelength = strokedata.loc[:, 'strokelength']
+ strokelength = strokedata.loc[:,'strokelength']
except: # pragma: no cover
strokelength = np.zeros(nr_rows)
@@ -3901,7 +3821,7 @@ def fetch_strava_workout(stravatoken, oauth_data, stravaid, csvfilename, userid,
pace[np.isinf(pace)] = 0.0
try:
- strokedata = pd.DataFrame({'t': 10*t,
+ strokedata = pl.DataFrame({'t': 10*t,
'd': 10*d,
'p': 10*pace,
'spm': spm,
@@ -3947,18 +3867,18 @@ def fetch_strava_workout(stravatoken, oauth_data, stravaid, csvfilename, userid,
starttimeunix = arrow.get(rowdatetime).timestamp()
- res = make_cumvalues(0.1*strokedata['t'])
- cum_time = res[0]
- lapidx = res[1]
+ res = make_cumvalues_array(0.1*strokedata['t'].to_numpy())
+ cum_time = pl.Series(res[0])
+ lapidx = pl.Series(res[1])
unixtime = cum_time+starttimeunix
- seconds = 0.1*strokedata.loc[:, 't']
+ seconds = 0.1*strokedata['t']
nr_rows = len(unixtime)
try:
- latcoord = strokedata.loc[:, 'lat']
- loncoord = strokedata.loc[:, 'lon']
+ latcoord = strokedata['lat']
+ loncoord = strokedata['lon']
if latcoord.std() == 0 and loncoord.std() == 0 and workouttype == 'water': # pragma: no cover
workouttype = 'rower'
except: # pragma: no cover
@@ -3968,29 +3888,29 @@ def fetch_strava_workout(stravatoken, oauth_data, stravaid, csvfilename, userid,
workouttype = 'rower'
try:
- strokelength = strokedata.loc[:, 'strokelength']
+ strokelength = strokedata['strokelength']
except: # pragma: no cover
strokelength = np.zeros(nr_rows)
- dist2 = 0.1*strokedata.loc[:, 'd']
+ dist2 = 0.1*strokedata['d']
try:
- spm = strokedata.loc[:, 'spm']
- except KeyError: # pragma: no cover
+ spm = strokedata['spm']
+ except (KeyError, ColumnNotFoundError): # pragma: no cover
spm = 0*dist2
try:
- hr = strokedata.loc[:, 'hr']
- except KeyError: # pragma: no cover
+ hr = strokedata['hr']
+ except (KeyError, ColumnNotFoundError): # pragma: no cover
hr = 0*spm
- pace = strokedata.loc[:, 'p']/10.
+ pace = strokedata['p']/10.
pace = np.clip(pace, 0, 1e4)
- pace = pace.replace(0, 300)
+ pace = pl.Series(pace).replace(0, 300)
velo = 500./pace
try:
- power = strokedata.loc[:, 'power']
+ power = strokedata['power']
except KeyError: # pragma: no cover
power = 2.8*velo**3
@@ -3999,7 +3919,7 @@ def fetch_strava_workout(stravatoken, oauth_data, stravaid, csvfilename, userid,
# save csv
# Create data frame with all necessary data to write to csv
- df = pd.DataFrame({'TimeStamp (sec)': unixtime,
+ df = pl.DataFrame({'TimeStamp (sec)': unixtime,
' Horizontal (meters)': dist2,
' Cadence (stokes/min)': spm,
' HRCur (bpm)': hr,
@@ -4019,10 +3939,10 @@ def fetch_strava_workout(stravatoken, oauth_data, stravaid, csvfilename, userid,
'cum_dist': dist2,
})
- df.sort_values(by='TimeStamp (sec)', ascending=True)
+ df.sort('TimeStamp (sec)')
- row = rowingdata.rowingdata(df=df)
- row.write_csv(csvfilename, gzip=False)
+ row = rowingdata.rowingdata_pl(df=df)
+ row.write_csv(csvfilename, compressed=False)
# summary = row.allstats()
# maxdist = df['cum_dist'].max()
diff --git a/rowers/templates/course_edit_view.html b/rowers/templates/course_edit_view.html
index 58b2cb64..c281b2b7 100644
--- a/rowers/templates/course_edit_view.html
+++ b/rowers/templates/course_edit_view.html
@@ -32,8 +32,7 @@
{{ mapdiv|safe }}
-
- {{ mapscript|safe }}
+ {{ mapscript|safe }}
diff --git a/rowers/templates/course_replace.html b/rowers/templates/course_replace.html
index 53569c1f..067c90ff 100644
--- a/rowers/templates/course_replace.html
+++ b/rowers/templates/course_replace.html
@@ -37,8 +37,9 @@
{{ mapdiv|safe }}
-
- {{ mapscript|safe }}
+
diff --git a/rowers/templates/course_replace_confirm.html b/rowers/templates/course_replace_confirm.html
index 965b9180..c057f2c1 100644
--- a/rowers/templates/course_replace_confirm.html
+++ b/rowers/templates/course_replace_confirm.html
@@ -45,7 +45,9 @@
{{ mapdiv|safe }}
- {{ mapscript|safe }}
+
diff --git a/rowers/templates/course_view.html b/rowers/templates/course_view.html
index e50e35fa..decc0089 100644
--- a/rowers/templates/course_view.html
+++ b/rowers/templates/course_view.html
@@ -54,8 +54,7 @@
{{ mapdiv|safe }}
-
- {{ mapscript|safe }}
+ {{ mapscript|safe }}
{% if records %}
diff --git a/rowers/templates/coursemap.html b/rowers/templates/coursemap.html
index d926f731..c7dd9b7a 100644
--- a/rowers/templates/coursemap.html
+++ b/rowers/templates/coursemap.html
@@ -19,7 +19,7 @@
{{ course.name }}
{{ mapdiv|safe }}
- {{ mapscript|safe }}
+ {{ mapscript|safe }}
{% endblock %}
diff --git a/rowers/templates/disqualification_view.html b/rowers/templates/disqualification_view.html
index dfacbe67..5a6406cb 100644
--- a/rowers/templates/disqualification_view.html
+++ b/rowers/templates/disqualification_view.html
@@ -102,14 +102,11 @@
{% endif %}
-
-
-
- {{ interactiveplot |safe }}
+
{{ the_div|safe }}
+ {{ interactiveplot |safe }}
+
diff --git a/rowers/templates/embedded_video.html b/rowers/templates/embedded_video.html
index 0ff08fbe..0fe35134 100644
--- a/rowers/templates/embedded_video.html
+++ b/rowers/templates/embedded_video.html
@@ -116,7 +116,7 @@ function copyText() {
{% if user.is_authenticated and user == workout.user.user and not locked %}
- Paste link to you tube video below
+ Paste link to Youtube video below
Use the slider to locate start point for video on workout map
Playing the video will advance the data in synchonization. Use the regular YouTube
controls
@@ -169,9 +169,8 @@ function copyText() {
-
+
+
+Film Deaths
+
+
+
+
+ -
+
+
+ {{ the_div|safe }}
+
+
+
+
+{% endblock %}
+
+{% block scripts %}
+
+
+
+{% endblock %}
+
+{% block sidebar %}
+{% include 'menu_analytics.html' %}
+{% endblock %}
diff --git a/rowers/templates/flexchart3otw.html b/rowers/templates/flexchart3otw.html
index 19f7cbeb..01be6c6c 100644
--- a/rowers/templates/flexchart3otw.html
+++ b/rowers/templates/flexchart3otw.html
@@ -8,16 +8,6 @@
{% localtime on %}
{% block main %}
-{{ js_res | safe }}
-{{ css_res| safe }}
-
-
-
-
-
-{{ the_script |safe }}
{% if workout|previousworkout:rower.user %}
@@ -34,10 +24,9 @@
Flexible Chart
+
-
-
- {{ the_div|safe }}
-
+ {{ the_div|safe }}
-
+
+
+{{ the_script |safe }}
+
{% endblock %}
{% endlocaltime %}
diff --git a/rowers/templates/flexchartstacked.html b/rowers/templates/flexchartstacked.html
index d5788f79..58aa7ffb 100644
--- a/rowers/templates/flexchartstacked.html
+++ b/rowers/templates/flexchartstacked.html
@@ -8,16 +8,8 @@
{% localtime on %}
{% block main %}
-{{ js_res | safe }}
-{{ css_res| safe }}
+
-
-
-
-
-{{ the_script |safe }}
{% if workout|previousworkout:rower.user %}
@@ -34,6 +26,7 @@
Chart Stack
+
-
{{ the_div|safe }}
@@ -57,6 +50,7 @@
+{{ the_script |safe }}
{% endblock %}
{% endlocaltime %}
diff --git a/rowers/templates/forcecurve_analysis.html b/rowers/templates/forcecurve_analysis.html
index b46f8d37..2216bd01 100644
--- a/rowers/templates/forcecurve_analysis.html
+++ b/rowers/templates/forcecurve_analysis.html
@@ -6,25 +6,20 @@
{% block main %}
-{{ js_res | safe }}
-{{ css_res| safe }}
+
+
-
-
-
-
-{{ the_script |safe }}
Force Curve Analysis for {{ rower.user.first_name }} {{ rower.user.last_name }}
+
{% if the_div %}
-
{{ the_div|safe }}
+ {{ the_script |safe }}
{% endif %}
diff --git a/rowers/templates/forcecurve_single.html b/rowers/templates/forcecurve_single.html
index d9ea9729..1d95d9a9 100644
--- a/rowers/templates/forcecurve_single.html
+++ b/rowers/templates/forcecurve_single.html
@@ -8,32 +8,26 @@
{% localtime on %}
{% block main %}
-{{ js_res | safe }}
-{{ css_res| safe }}
+
-
-
-
-
-{{ the_script |safe }}
Empower Force Curve
+
-
{{ the_div|safe }}
-
+ {{ the_script |safe }}
+
-