Merge branch 'release/v4.5'
This commit is contained in:
+70
-69
@@ -61,7 +61,6 @@ queuelow = django_rq.get_queue('low')
|
||||
queuehigh = django_rq.get_queue('default')
|
||||
|
||||
|
||||
|
||||
user = settings.DATABASES['default']['USER']
|
||||
password = settings.DATABASES['default']['PASSWORD']
|
||||
database_name = settings.DATABASES['default']['NAME']
|
||||
@@ -106,6 +105,7 @@ from scipy.signal import savgol_filter
|
||||
|
||||
import datetime
|
||||
|
||||
|
||||
def get_latlon(id):
|
||||
try:
|
||||
w = Workout.objects.get(id=id)
|
||||
@@ -126,6 +126,7 @@ def get_latlon(id):
|
||||
|
||||
return [pd.Series([]), pd.Series([])]
|
||||
|
||||
|
||||
def get_workouts(ids, userid):
|
||||
goodids = []
|
||||
for id in ids:
|
||||
@@ -135,6 +136,7 @@ def get_workouts(ids,userid):
|
||||
|
||||
return [Workout.objects.get(id=id) for id in goodids]
|
||||
|
||||
|
||||
def filter_df(datadf, fieldname, value, largerthan=True):
|
||||
|
||||
try:
|
||||
@@ -142,7 +144,6 @@ def filter_df(datadf,fieldname,value,largerthan=True):
|
||||
except KeyError:
|
||||
return datadf
|
||||
|
||||
|
||||
if largerthan:
|
||||
mask = datadf[fieldname] < value
|
||||
else:
|
||||
@@ -150,7 +151,6 @@ def filter_df(datadf,fieldname,value,largerthan=True):
|
||||
|
||||
datadf.loc[mask, fieldname] = np.nan
|
||||
|
||||
|
||||
return datadf
|
||||
|
||||
|
||||
@@ -182,6 +182,7 @@ def clean_df_stats(datadf,workstrokesonly=True,ignorehr=True,
|
||||
except KeyError:
|
||||
pass
|
||||
|
||||
|
||||
try:
|
||||
datadf = datadf.clip(lower=0)
|
||||
except TypeError:
|
||||
@@ -220,7 +221,6 @@ def clean_df_stats(datadf,workstrokesonly=True,ignorehr=True,
|
||||
except KeyError:
|
||||
pass
|
||||
|
||||
|
||||
try:
|
||||
mask = datadf['pace'] / 1000. > 300.
|
||||
datadf.loc[mask, 'pace'] = np.nan
|
||||
@@ -245,14 +245,12 @@ def clean_df_stats(datadf,workstrokesonly=True,ignorehr=True,
|
||||
except KeyError:
|
||||
pass
|
||||
|
||||
|
||||
try:
|
||||
mask = datadf['wash'] < 1
|
||||
datadf.loc[mask, 'wash'] = np.nan
|
||||
except KeyError:
|
||||
pass
|
||||
|
||||
|
||||
if not ignoreadvanced:
|
||||
try:
|
||||
mask = datadf['rhythm'] < 5
|
||||
@@ -260,79 +258,66 @@ def clean_df_stats(datadf,workstrokesonly=True,ignorehr=True,
|
||||
except KeyError:
|
||||
pass
|
||||
|
||||
|
||||
try:
|
||||
mask = datadf['rhythm'] > 70
|
||||
datadf.loc[mask, 'rhythm'] = np.nan
|
||||
except KeyError:
|
||||
pass
|
||||
|
||||
|
||||
try:
|
||||
mask = datadf['power'] < 20
|
||||
datadf.loc[mask, 'power'] = np.nan
|
||||
except KeyError:
|
||||
pass
|
||||
|
||||
|
||||
try:
|
||||
mask = datadf['drivelength'] < 0.5
|
||||
datadf.loc[mask, 'drivelength'] = np.nan
|
||||
except KeyError:
|
||||
pass
|
||||
|
||||
|
||||
try:
|
||||
mask = datadf['forceratio'] < 0.2
|
||||
datadf.loc[mask, 'forceratio'] = np.nan
|
||||
except KeyError:
|
||||
pass
|
||||
|
||||
|
||||
try:
|
||||
mask = datadf['forceratio'] > 1.0
|
||||
datadf.loc[mask, 'forceratio'] = np.nan
|
||||
except KeyError:
|
||||
pass
|
||||
|
||||
|
||||
|
||||
try:
|
||||
mask = datadf['drivespeed'] < 0.5
|
||||
datadf.loc[mask, 'drivespeed'] = np.nan
|
||||
except KeyError:
|
||||
pass
|
||||
|
||||
|
||||
try:
|
||||
mask = datadf['drivespeed'] > 4
|
||||
datadf.loc[mask, 'drivespeed'] = np.nan
|
||||
except KeyError:
|
||||
pass
|
||||
|
||||
|
||||
try:
|
||||
mask = datadf['driveenergy'] > 2000
|
||||
datadf.loc[mask, 'driveenergy'] = np.nan
|
||||
except KeyError:
|
||||
pass
|
||||
|
||||
|
||||
try:
|
||||
mask = datadf['driveenergy'] < 100
|
||||
datadf.loc[mask, 'driveenergy'] = np.nan
|
||||
except KeyError:
|
||||
pass
|
||||
|
||||
|
||||
|
||||
try:
|
||||
mask = datadf['catch'] > -30.
|
||||
datadf.loc[mask, 'catch'] = np.nan
|
||||
except KeyError:
|
||||
pass
|
||||
|
||||
|
||||
workoutstateswork = [1, 4, 5, 8, 9, 6, 7]
|
||||
workoutstatesrest = [3]
|
||||
workoutstatetransition = [0, 2, 10, 11, 12, 13]
|
||||
@@ -345,6 +330,7 @@ def clean_df_stats(datadf,workstrokesonly=True,ignorehr=True,
|
||||
|
||||
return datadf
|
||||
|
||||
|
||||
def getstatsfields():
|
||||
# Get field names and remove those that are not useful in stats
|
||||
fields = StrokeData._meta.get_fields()
|
||||
@@ -389,6 +375,8 @@ def niceformat(values):
|
||||
return out
|
||||
|
||||
# A nice printable format for time delta values
|
||||
|
||||
|
||||
def strfdelta(tdelta):
|
||||
try:
|
||||
minutes, seconds = divmod(tdelta.seconds, 60)
|
||||
@@ -406,25 +394,28 @@ def strfdelta(tdelta):
|
||||
return res
|
||||
|
||||
# A nice printable format for pace values
|
||||
|
||||
|
||||
def nicepaceformat(values):
|
||||
out = []
|
||||
for v in values:
|
||||
formattedv = strfdelta(v)
|
||||
out.append(formattedv)
|
||||
|
||||
|
||||
return out
|
||||
|
||||
# Convert seconds to a Time Delta value, replacing NaN with a 5:50 pace
|
||||
|
||||
|
||||
def timedeltaconv(x):
|
||||
if np.isfinite(x) and x != 0 and x > 0 and x < 175000:
|
||||
dt = datetime.timedelta(seconds=x)
|
||||
else:
|
||||
dt = datetime.timedelta(seconds=350.)
|
||||
|
||||
|
||||
return dt
|
||||
|
||||
|
||||
def paceformatsecs(values):
|
||||
out = []
|
||||
for v in values:
|
||||
@@ -435,6 +426,8 @@ def paceformatsecs(values):
|
||||
return out
|
||||
|
||||
# Processes painsled CSV file to database
|
||||
|
||||
|
||||
def save_workout_database(f2, r, dosmooth=True, workouttype='rower',
|
||||
dosummary=True, title='Workout',
|
||||
workoutsource='unknown',
|
||||
@@ -474,7 +467,8 @@ def save_workout_database(f2,r,dosmooth=True,workouttype='rower',
|
||||
if not value:
|
||||
allchecks = 0
|
||||
if consistencychecks:
|
||||
a_messages.error(r.user,'Failed consistency check: '+key+', autocorrected')
|
||||
a_messages.error(
|
||||
r.user, 'Failed consistency check: ' + key + ', autocorrected')
|
||||
else:
|
||||
pass
|
||||
# a_messages.error(r.user,'Failed consistency check: '+key+', not corrected')
|
||||
@@ -485,7 +479,6 @@ def save_workout_database(f2,r,dosmooth=True,workouttype='rower',
|
||||
# row.repair()
|
||||
pass
|
||||
|
||||
|
||||
if row == 0:
|
||||
return (0, 'Error: CSV data file not found')
|
||||
|
||||
@@ -537,7 +530,8 @@ def save_workout_database(f2,r,dosmooth=True,workouttype='rower',
|
||||
if totaldist == 0:
|
||||
totaldist = row.df['cum_dist'].max()
|
||||
if totaltime == 0:
|
||||
totaltime = row.df['TimeStamp (sec)'].max()-row.df['TimeStamp (sec)'].min()
|
||||
totaltime = row.df['TimeStamp (sec)'].max(
|
||||
) - row.df['TimeStamp (sec)'].min()
|
||||
try:
|
||||
totaltime = totaltime + row.df.ix[0, ' ElapsedTime (sec)']
|
||||
except KeyError:
|
||||
@@ -577,7 +571,6 @@ def save_workout_database(f2,r,dosmooth=True,workouttype='rower',
|
||||
#summary += '\n'
|
||||
#summary += row.intervalstats()
|
||||
|
||||
|
||||
#workoutstartdatetime = row.rowdatetime
|
||||
timezone_str = 'UTC'
|
||||
try:
|
||||
@@ -585,8 +578,6 @@ def save_workout_database(f2,r,dosmooth=True,workouttype='rower',
|
||||
except ValueError:
|
||||
workoutstartdatetime = row.rowdatetime
|
||||
|
||||
|
||||
|
||||
try:
|
||||
latavg = row.df[' latitude'].mean()
|
||||
lonavg = row.df[' longitude'].mean()
|
||||
@@ -612,8 +603,6 @@ def save_workout_database(f2,r,dosmooth=True,workouttype='rower',
|
||||
except KeyError:
|
||||
timezone_str = r.defaulttimezone
|
||||
|
||||
|
||||
|
||||
workoutdate = workoutstartdatetime.astimezone(
|
||||
pytz.timezone(timezone_str)
|
||||
).strftime('%Y-%m-%d')
|
||||
@@ -640,9 +629,6 @@ def save_workout_database(f2,r,dosmooth=True,workouttype='rower',
|
||||
message = "Warning: This workout probably already exists in the database"
|
||||
privacy = 'hidden'
|
||||
|
||||
|
||||
|
||||
|
||||
w = Workout(user=r, name=title, date=workoutdate,
|
||||
workouttype=workouttype,
|
||||
duration=duration, distance=totaldist,
|
||||
@@ -657,7 +643,6 @@ def save_workout_database(f2,r,dosmooth=True,workouttype='rower',
|
||||
timezone=timezone_str,
|
||||
privacy=privacy)
|
||||
|
||||
|
||||
w.save()
|
||||
|
||||
isbreakthrough = False
|
||||
@@ -672,7 +657,8 @@ def save_workout_database(f2,r,dosmooth=True,workouttype='rower',
|
||||
dfgrouped = df.groupby(['workoutid'])
|
||||
delta, cpvalues, avgpower = datautils.getcp(dfgrouped, logarr)
|
||||
|
||||
res,btvalues,res2 = utils.isbreakthrough(delta,cpvalues,r.p0,r.p1,r.p2,r.p3,r.cpratio)
|
||||
res, btvalues, res2 = utils.isbreakthrough(
|
||||
delta, cpvalues, r.p0, r.p1, r.p2, r.p3, r.cpratio)
|
||||
else:
|
||||
res = 0
|
||||
res2 = 0
|
||||
@@ -684,7 +670,8 @@ def save_workout_database(f2,r,dosmooth=True,workouttype='rower',
|
||||
|
||||
# submit email task to send email about breakthrough workout
|
||||
if isbreakthrough:
|
||||
a_messages.info(r.user,'It looks like you have a new breakthrough workout')
|
||||
a_messages.info(
|
||||
r.user, 'It looks like you have a new breakthrough workout')
|
||||
if settings.DEBUG and r.getemailnotifications:
|
||||
res = handle_sendemail_breakthrough.delay(w.id, r.user.email,
|
||||
r.user.first_name,
|
||||
@@ -734,6 +721,7 @@ def save_workout_database(f2,r,dosmooth=True,workouttype='rower',
|
||||
|
||||
return (w.id, message)
|
||||
|
||||
|
||||
def handle_nonpainsled(f2, fileformat, summary=''):
|
||||
oarlength = 2.89
|
||||
inboard = 0.88
|
||||
@@ -756,7 +744,6 @@ def handle_nonpainsled(f2,fileformat,summary=''):
|
||||
if (fileformat == 'ergdata'):
|
||||
row = ErgDataParser(f2)
|
||||
|
||||
|
||||
# handle CoxMate
|
||||
if (fileformat == 'coxmate'):
|
||||
row = CoxMateParser(f2)
|
||||
@@ -790,7 +777,6 @@ def handle_nonpainsled(f2,fileformat,summary=''):
|
||||
except:
|
||||
pass
|
||||
|
||||
|
||||
# handle ErgStick
|
||||
if (fileformat == 'ergstick'):
|
||||
row = ErgStickParser(f2)
|
||||
@@ -805,7 +791,6 @@ def handle_nonpainsled(f2,fileformat,summary=''):
|
||||
except:
|
||||
pass
|
||||
|
||||
|
||||
f_to_be_deleted = f2
|
||||
# should delete file
|
||||
f2 = f2[:-4] + 'o.csv'
|
||||
@@ -822,6 +807,8 @@ def handle_nonpainsled(f2,fileformat,summary=''):
|
||||
# Create new workout from file and store it in the database
|
||||
# This routine should be used everywhere in views.py and mailprocessing.py
|
||||
# Currently there is code duplication
|
||||
|
||||
|
||||
def new_workout_from_file(r, f2,
|
||||
workouttype='rower',
|
||||
title='Workout',
|
||||
@@ -873,7 +860,6 @@ def new_workout_from_file(r,f2,
|
||||
message = "Rowsandall could not process this file. The extension is supported but the file seems corrupt. Contact info@rowsandall.com if you think this is incorrect."
|
||||
return (0, message, f2)
|
||||
|
||||
|
||||
# Some people try to upload RowPro summary logs
|
||||
if fileformat == 'rowprolog':
|
||||
os.remove(f2)
|
||||
@@ -906,8 +892,6 @@ def new_workout_from_file(r,f2,
|
||||
message = 'Something went wrong: ' + errorstring
|
||||
return (0, message, '')
|
||||
|
||||
|
||||
|
||||
dosummary = (fileformat != 'fit')
|
||||
id, message = save_workout_database(f2, r,
|
||||
workouttype=workouttype,
|
||||
@@ -920,6 +904,7 @@ def new_workout_from_file(r,f2,
|
||||
|
||||
return (id, message, f2)
|
||||
|
||||
|
||||
def split_workout(r, parent, splitsecond, splitmode):
|
||||
data, row = getrowdata_db(id=parent.id)
|
||||
latitude, longitude = get_latlon(parent.id)
|
||||
@@ -942,7 +927,6 @@ def split_workout(r,parent,splitsecond,splitmode):
|
||||
data1['time'] = data1.index
|
||||
data1.reset_index(drop=True, inplace=True)
|
||||
|
||||
|
||||
data2 = data2.sort_values(['time'])
|
||||
data2 = data2.interpolate(method='linear', axis=0, limit_direction='both',
|
||||
limit=10)
|
||||
@@ -956,7 +940,6 @@ def split_workout(r,parent,splitsecond,splitmode):
|
||||
data1['pace'] = data1['pace'] / 1000.
|
||||
data2['pace'] = data2['pace'] / 1000.
|
||||
|
||||
|
||||
data1.drop_duplicates(subset='time', inplace=True)
|
||||
data2.drop_duplicates(subset='time', inplace=True)
|
||||
|
||||
@@ -995,7 +978,6 @@ def split_workout(r,parent,splitsecond,splitmode):
|
||||
messages.append(message)
|
||||
ids.append(id)
|
||||
|
||||
|
||||
if not 'keep original' in splitmode:
|
||||
if 'keep second' in splitmode or 'keep first' in splitmode:
|
||||
parent.delete()
|
||||
@@ -1012,6 +994,8 @@ def split_workout(r,parent,splitsecond,splitmode):
|
||||
# Create new workout from data frame and store it in the database
|
||||
# This routine should be used everywhere in views.py and mailprocessing.py
|
||||
# Currently there is code duplication
|
||||
|
||||
|
||||
def new_workout_from_df(r, df,
|
||||
title='New Workout',
|
||||
parent=None,
|
||||
@@ -1046,7 +1030,6 @@ def new_workout_from_df(r,df,
|
||||
if setprivate:
|
||||
makeprivate = True
|
||||
|
||||
|
||||
timestr = strftime("%Y%m%d-%H%M%S")
|
||||
|
||||
csvfilename = 'media/df_' + timestr + '.csv'
|
||||
@@ -1079,11 +1062,9 @@ def new_workout_from_df(r,df,
|
||||
dosmooth=False,
|
||||
consistencychecks=False)
|
||||
|
||||
|
||||
return (id, message)
|
||||
|
||||
|
||||
|
||||
# Compare the data from the CSV file and the database
|
||||
# Currently only calculates number of strokes. To be expanded with
|
||||
# more elaborate testing if needed
|
||||
@@ -1115,6 +1096,8 @@ def compare_data(id):
|
||||
|
||||
# Repair data for workouts where the CSV file is lost (or the DB entries
|
||||
# don't exist)
|
||||
|
||||
|
||||
def repair_data(verbose=False):
|
||||
ws = Workout.objects.all()
|
||||
for w in ws:
|
||||
@@ -1157,6 +1140,8 @@ def repair_data(verbose=False):
|
||||
pass
|
||||
|
||||
# A wrapper around the rowingdata class, with some error catching
|
||||
|
||||
|
||||
def rdata(file, rower=rrower()):
|
||||
try:
|
||||
res = rrdata(csvfile=file, rower=rower)
|
||||
@@ -1171,6 +1156,8 @@ def rdata(file,rower=rrower()):
|
||||
return res
|
||||
|
||||
# Remove all stroke data for workout ID from database
|
||||
|
||||
|
||||
def delete_strokedata(id):
|
||||
engine = create_engine(database_url, echo=False)
|
||||
query = sa.text('DELETE FROM strokedata WHERE workoutid={id};'.format(
|
||||
@@ -1185,11 +1172,15 @@ def delete_strokedata(id):
|
||||
engine.dispose()
|
||||
|
||||
# Replace stroke data in DB with data from CSV file
|
||||
|
||||
|
||||
def update_strokedata(id, df):
|
||||
delete_strokedata(id)
|
||||
rowdata = dataprep(df, id=id, bands=True, barchart=True, otwpower=True)
|
||||
|
||||
# Test that all data are of a numerical time
|
||||
|
||||
|
||||
def testdata(time, distance, pace, spm):
|
||||
t1 = np.issubdtype(time, np.number)
|
||||
t2 = np.issubdtype(distance, np.number)
|
||||
@@ -1200,6 +1191,8 @@ def testdata(time,distance,pace,spm):
|
||||
|
||||
# Get data from DB for one workout (fetches all data). If data
|
||||
# is not in DB, read from CSV file (and create DB entry)
|
||||
|
||||
|
||||
def getrowdata_db(id=0, doclean=False, convertnewtons=True):
|
||||
data = read_df_sql(id)
|
||||
data['x_right'] = data['x_right'] / 1.0e6
|
||||
@@ -1207,7 +1200,8 @@ def getrowdata_db(id=0,doclean=False,convertnewtons=True):
|
||||
if data.empty:
|
||||
rowdata, row = getrowdata(id=id)
|
||||
if rowdata:
|
||||
data = dataprep(rowdata.df,id=id,bands=True,barchart=True,otwpower=True)
|
||||
data = dataprep(rowdata.df, id=id, bands=True,
|
||||
barchart=True, otwpower=True)
|
||||
else:
|
||||
data = pd.DataFrame() # returning empty dataframe
|
||||
else:
|
||||
@@ -1219,27 +1213,26 @@ def getrowdata_db(id=0,doclean=False,convertnewtons=True):
|
||||
if doclean:
|
||||
data = clean_df_stats(data, ignorehr=True)
|
||||
|
||||
|
||||
return data, row
|
||||
|
||||
# Fetch a subset of the data from the DB
|
||||
|
||||
|
||||
def getsmallrowdata_db(columns, ids=[], doclean=True, workstrokesonly=True):
|
||||
prepmultipledata(ids)
|
||||
data = read_cols_df_sql(ids, columns)
|
||||
|
||||
# convert newtons
|
||||
|
||||
|
||||
|
||||
if doclean:
|
||||
data = clean_df_stats(data, ignorehr=True,
|
||||
workstrokesonly=workstrokesonly)
|
||||
|
||||
|
||||
|
||||
return data
|
||||
|
||||
# Fetch both the workout and the workout stroke data (from CSV file)
|
||||
|
||||
|
||||
def getrowdata(id=0):
|
||||
|
||||
# check if valid ID exists (workout exists)
|
||||
@@ -1266,6 +1259,8 @@ def getrowdata(id=0):
|
||||
# In theory, this should never yield any work, but it's a good
|
||||
# safety net for programming errors elsewhere in the app
|
||||
# Also used heavily when I moved from CSV file only to CSV+Stroke data
|
||||
|
||||
|
||||
def prepmultipledata(ids, verbose=False):
|
||||
query = sa.text('SELECT DISTINCT workoutid FROM strokedata')
|
||||
engine = create_engine(database_url, echo=False)
|
||||
@@ -1287,11 +1282,14 @@ def prepmultipledata(ids,verbose=False):
|
||||
if verbose:
|
||||
print id
|
||||
if rowdata and len(rowdata.df):
|
||||
data = dataprep(rowdata.df,id=id,bands=True,barchart=True,otwpower=True)
|
||||
data = dataprep(rowdata.df, id=id, bands=True,
|
||||
barchart=True, otwpower=True)
|
||||
return res
|
||||
|
||||
# Read a set of columns for a set of workout ids, returns data as a
|
||||
# pandas dataframe
|
||||
|
||||
|
||||
def read_cols_df_sql(ids, columns, convertnewtons=True):
|
||||
# drop columns that are not in offical list
|
||||
# axx = [ax[0] for ax in axes]
|
||||
@@ -1325,29 +1323,33 @@ def read_cols_df_sql(ids,columns,convertnewtons=True):
|
||||
ids=tuple(ids),
|
||||
))
|
||||
|
||||
|
||||
connection = engine.raw_connection()
|
||||
df = pd.read_sql_query(query, engine)
|
||||
|
||||
df = df.fillna(value=0)
|
||||
|
||||
if 'peakforce' in columns:
|
||||
funits = ((w.id,w.forceunit) for w in Workout.objects.filter(id__in=ids))
|
||||
funits = ((w.id, w.forceunit)
|
||||
for w in Workout.objects.filter(id__in=ids))
|
||||
for id, u in funits:
|
||||
if u == 'lbs':
|
||||
mask = df['workoutid'] == id
|
||||
df.loc[mask, 'peakforce'] = df.loc[mask, 'peakforce'] * lbstoN
|
||||
if 'averageforce' in columns:
|
||||
funits = ((w.id,w.forceunit) for w in Workout.objects.filter(id__in=ids))
|
||||
funits = ((w.id, w.forceunit)
|
||||
for w in Workout.objects.filter(id__in=ids))
|
||||
for id, u in funits:
|
||||
if u == 'lbs':
|
||||
mask = df['workoutid'] == id
|
||||
df.loc[mask,'averageforce'] = df.loc[mask,'averageforce']*lbstoN
|
||||
df.loc[mask, 'averageforce'] = df.loc[mask,
|
||||
'averageforce'] * lbstoN
|
||||
|
||||
engine.dispose()
|
||||
return df
|
||||
|
||||
# Read stroke data from the DB for a Workout ID. Returns a pandas dataframe
|
||||
|
||||
|
||||
def read_df_sql(id):
|
||||
engine = create_engine(database_url, echo=False)
|
||||
|
||||
@@ -1374,6 +1376,8 @@ def read_df_sql(id):
|
||||
|
||||
# Get the necessary data from the strokedata table in the DB.
|
||||
# For the flex plot
|
||||
|
||||
|
||||
def smalldataprep(therows, xparam, yparam1, yparam2):
|
||||
df = pd.DataFrame()
|
||||
if yparam2 == 'None':
|
||||
@@ -1432,10 +1436,11 @@ def smalldataprep(therows,xparam,yparam1,yparam2):
|
||||
except IOError:
|
||||
pass
|
||||
|
||||
|
||||
return df
|
||||
|
||||
# data fusion
|
||||
|
||||
|
||||
def datafusion(id1, id2, columns, offset):
|
||||
workout1 = Workout.objects.get(id=id1)
|
||||
workout2 = Workout.objects.get(id=id2)
|
||||
@@ -1460,7 +1465,6 @@ def datafusion(id1,id2,columns,offset):
|
||||
df1[' latitude'] = latitude
|
||||
df1[' longitude'] = longitude
|
||||
|
||||
|
||||
df2 = getsmallrowdata_db(['time'] + columns, ids=[id2], doclean=False)
|
||||
|
||||
forceunit = 'N'
|
||||
@@ -1469,13 +1473,11 @@ def datafusion(id1,id2,columns,offset):
|
||||
offsetmillisecs += offset.days * (3600 * 24 * 1000)
|
||||
df2['time'] = df2['time'] + offsetmillisecs
|
||||
|
||||
|
||||
keep1 = {c: c for c in set(df1.columns)}
|
||||
|
||||
for c in columns:
|
||||
keep1.pop(c)
|
||||
|
||||
|
||||
for c in df1.columns:
|
||||
if not c in keep1:
|
||||
df1 = df1.drop(c, 1, errors='ignore')
|
||||
@@ -1497,6 +1499,7 @@ def datafusion(id1,id2,columns,offset):
|
||||
|
||||
return df, forceunit
|
||||
|
||||
|
||||
def fix_newtons(id=0, limit=3000):
|
||||
# rowdata,row = getrowdata_db(id=id,doclean=False,convertnewtons=False)
|
||||
rowdata = getsmallrowdata_db(['peakforce'], ids=[id], doclean=False)
|
||||
@@ -1512,6 +1515,7 @@ def fix_newtons(id=0,limit=3000):
|
||||
except KeyError:
|
||||
pass
|
||||
|
||||
|
||||
def add_efficiency(id=0):
|
||||
rowdata, row = getrowdata_db(id=id, doclean=False, convertnewtons=False)
|
||||
power = rowdata['power']
|
||||
@@ -1528,7 +1532,8 @@ def add_efficiency(id=0):
|
||||
rowdata['workoutid'] = id
|
||||
engine = create_engine(database_url, echo=False)
|
||||
with engine.connect() as conn, conn.begin():
|
||||
rowdata.to_sql('strokedata',engine,if_exists='append',index=False)
|
||||
rowdata.to_sql('strokedata', engine,
|
||||
if_exists='append', index=False)
|
||||
conn.close()
|
||||
engine.dispose()
|
||||
return rowdata
|
||||
@@ -1537,6 +1542,8 @@ def add_efficiency(id=0):
|
||||
# it reindexes, sorts, filters, and smooths the data, then
|
||||
# saves it to the stroke_data table in the database
|
||||
# Takes a rowingdata object's DataFrame as input
|
||||
|
||||
|
||||
def dataprep(rowdatadf, id=0, bands=True, barchart=True, otwpower=True,
|
||||
empower=True, inboard=0.88, forceunit='lbs'):
|
||||
if rowdatadf.empty:
|
||||
@@ -1593,7 +1600,6 @@ def dataprep(rowdatadf,id=0,bands=True,barchart=True,otwpower=True,
|
||||
except TypeError:
|
||||
t2 = 0 * t
|
||||
|
||||
|
||||
p2 = p.fillna(method='ffill').apply(lambda x: timedeltaconv(x))
|
||||
|
||||
try:
|
||||
@@ -1601,7 +1607,6 @@ def dataprep(rowdatadf,id=0,bands=True,barchart=True,otwpower=True,
|
||||
except TypeError:
|
||||
drivespeed = 0.0 * rowdatadf['TimeStamp (sec)']
|
||||
|
||||
|
||||
drivespeed = drivespeed.fillna(value=0)
|
||||
|
||||
try:
|
||||
@@ -1617,7 +1622,6 @@ def dataprep(rowdatadf,id=0,bands=True,barchart=True,otwpower=True,
|
||||
|
||||
distanceperstroke = 60. * velo / spm
|
||||
|
||||
|
||||
data = DataFrame(
|
||||
dict(
|
||||
time=t * 1e3,
|
||||
@@ -1686,7 +1690,6 @@ def dataprep(rowdatadf,id=0,bands=True,barchart=True,otwpower=True,
|
||||
except KeyError:
|
||||
peakforceangle = 0 * power
|
||||
|
||||
|
||||
if data['driveenergy'].mean() == 0:
|
||||
try:
|
||||
driveenergy = rowdatadf.ix[:, 'driveenergy']
|
||||
@@ -1695,7 +1698,6 @@ def dataprep(rowdatadf,id=0,bands=True,barchart=True,otwpower=True,
|
||||
else:
|
||||
driveenergy = data['driveenergy']
|
||||
|
||||
|
||||
arclength = (inboard - 0.05) * (np.radians(finish) - np.radians(catch))
|
||||
if arclength.mean() > 0:
|
||||
drivelength = arclength
|
||||
@@ -1754,7 +1756,6 @@ def dataprep(rowdatadf,id=0,bands=True,barchart=True,otwpower=True,
|
||||
|
||||
velo = 500. / p
|
||||
|
||||
|
||||
ergpw = 2.8 * velo**3
|
||||
efficiency = 100. * ergpw / power
|
||||
|
||||
|
||||
@@ -12,6 +12,7 @@ import dataprep
|
||||
import types
|
||||
import datetime
|
||||
from django.forms import formset_factory
|
||||
from utils import landingpages
|
||||
|
||||
# login form
|
||||
class LoginForm(forms.Form):
|
||||
@@ -180,6 +181,10 @@ class UploadOptionsForm(forms.Form):
|
||||
makeprivate = forms.BooleanField(initial=False,required=False,
|
||||
label='Make Workout Private')
|
||||
|
||||
landingpage = forms.ChoiceField(choices=landingpages,
|
||||
initial='workout_edit_view',
|
||||
label='Landing Page')
|
||||
|
||||
class Meta:
|
||||
fields = ['make_plot','plottype','upload_toc2','makeprivate']
|
||||
|
||||
|
||||
@@ -2046,7 +2046,6 @@ def interactive_flex_chart2(id=0,promember=0,
|
||||
plottype='line',
|
||||
workstrokesonly=False):
|
||||
|
||||
|
||||
#rowdata,row = dataprep.getrowdata_db(id=id)
|
||||
columns = [xparam,yparam1,yparam2,
|
||||
'ftime','distance','fpace',
|
||||
@@ -2510,7 +2509,16 @@ def thumbnails_set(r,id,favorites):
|
||||
columns += [f.yparam2 for f in favorites]
|
||||
|
||||
columns += ['time']
|
||||
try:
|
||||
rowdata = dataprep.getsmallrowdata_db(columns,ids=[id],doclean=True)
|
||||
except:
|
||||
return [
|
||||
{'script':"",
|
||||
'div':"",
|
||||
'notes':""
|
||||
}]
|
||||
|
||||
|
||||
rowdata.dropna(axis=1,how='all',inplace=True)
|
||||
|
||||
if rowdata.empty:
|
||||
|
||||
+65
-161
@@ -1,24 +1,19 @@
|
||||
# Processes emails sent to workouts@rowsandall.com
|
||||
""" Processes emails sent to workouts@rowsandall.com """
|
||||
import shutil
|
||||
import time
|
||||
from django.conf import settings
|
||||
from rowers.tasks import handle_sendemail_unrecognized
|
||||
from django_mailbox.models import Mailbox,Message,MessageAttachment
|
||||
from rowers.models import Workout, User, Rower, WorkoutForm,RowerForm,GraphImage,AdvancedWorkoutForm
|
||||
from django.core.files.base import ContentFile
|
||||
from django.core.mail import send_mail, BadHeaderError,EmailMessage
|
||||
from rowsandall_app.settings import BASE_DIR
|
||||
from django_mailbox.models import Message, MessageAttachment
|
||||
from rowers.models import User, Rower, RowerForm
|
||||
|
||||
from django.core.mail import EmailMessage
|
||||
|
||||
from rowingdata import rower as rrower
|
||||
from rowingdata import main as rmain
|
||||
from rowingdata import rowingdata as rrdata
|
||||
from rowingdata import TCXParser,RowProParser,ErgDataParser
|
||||
from rowingdata import MysteryParser,BoatCoachParser
|
||||
from rowingdata import painsledDesktopParser,speedcoachParser,ErgStickParser
|
||||
from rowingdata import SpeedCoach2Parser,FITParser,fitsummarydata
|
||||
from rowingdata import make_cumvalues
|
||||
from rowingdata import summarydata,get_file_type
|
||||
|
||||
from scipy.signal import savgol_filter
|
||||
from rowingdata import rowingdata as rrdata
|
||||
|
||||
from rowingdata import get_file_type
|
||||
|
||||
import zipfile
|
||||
import os
|
||||
@@ -30,10 +25,13 @@ queuelow = django_rq.get_queue('low')
|
||||
queuehigh = django_rq.get_queue('default')
|
||||
|
||||
# Sends a confirmation with a link to the workout
|
||||
def send_confirm(u,name,link,options):
|
||||
fullemail = u.email
|
||||
|
||||
|
||||
def send_confirm(user, name, link, options):
|
||||
""" Send confirmation email to user when email has been processed """
|
||||
fullemail = user.email
|
||||
subject = 'Workout added: ' + name
|
||||
message = 'Dear '+u.first_name+',\n\n'
|
||||
message = 'Dear ' + user.first_name + ',\n\n'
|
||||
message += "Your workout has been added to Rowsandall.com.\n"
|
||||
message += "Link to workout: " + link + "\n\n"
|
||||
message += "Best Regards, the Rowsandall Team"
|
||||
@@ -41,7 +39,6 @@ def send_confirm(u,name,link,options):
|
||||
if options:
|
||||
message += "\n\n" + str(options)
|
||||
|
||||
|
||||
email = EmailMessage(subject, message,
|
||||
'Rowsandall <info@rowsandall.com>',
|
||||
[fullemail])
|
||||
@@ -51,176 +48,86 @@ def send_confirm(u,name,link,options):
|
||||
return 1
|
||||
|
||||
# Reads a "rowingdata" object, plus some error protections
|
||||
|
||||
|
||||
def rdata(file, rower=rrower()):
|
||||
""" Reads rowingdata data or returns 0 on Error """
|
||||
try:
|
||||
res = rrdata(file,rower=rower)
|
||||
result = rrdata(file, rower=rower)
|
||||
except IOError:
|
||||
try:
|
||||
res = rrdata(file+'.gz',rower=rower)
|
||||
result = rrdata(file + '.gz', rower=rower)
|
||||
except IOError:
|
||||
res = 0
|
||||
result = 0
|
||||
|
||||
return res
|
||||
|
||||
# Some error protection around process attachments
|
||||
def safeprocessattachments():
|
||||
try:
|
||||
return processattachments()
|
||||
except:
|
||||
return [0]
|
||||
|
||||
# This is duplicated in management/commands/processemail
|
||||
# Need to double check the code there, update here, and only
|
||||
# use the code here.
|
||||
def processattachments():
|
||||
# in res, we store the ids of the new workouts
|
||||
res = []
|
||||
attachments = MessageAttachment.objects.all()
|
||||
for a in attachments:
|
||||
donotdelete = 0
|
||||
m = Message.objects.get(id=a.message_id)
|
||||
from_address = m.from_address[0]
|
||||
name = m.subject
|
||||
|
||||
# get a list of users
|
||||
theusers = User.objects.filter(email=from_address)
|
||||
for u in theusers:
|
||||
try:
|
||||
rr = Rower.objects.get(user=u.id)
|
||||
# move attachment and make workout
|
||||
try:
|
||||
wid = [make_new_workout_from_email(rr,a.document,name)]
|
||||
res += wid
|
||||
link = 'https://rowsandall.com/rowers/workout/'+str(wid[0])+'/edit'
|
||||
if wid != 1:
|
||||
dd = send_confirm(u,name,link)
|
||||
except:
|
||||
# replace with code to process error
|
||||
res += ['fail: '+name]
|
||||
donotdelete = 1
|
||||
except Rower.DoesNotExist:
|
||||
pass
|
||||
|
||||
|
||||
# remove attachment
|
||||
if donotdelete == 0:
|
||||
a.delete()
|
||||
|
||||
if m.attachments.exists()==False:
|
||||
# no attachments, so can be deleted
|
||||
m.delete()
|
||||
|
||||
# Delete remaining messages (which should not have attachments)
|
||||
mm = Message.objects.all()
|
||||
for m in mm:
|
||||
if m.attachments.exists()==False:
|
||||
m.delete()
|
||||
|
||||
return res
|
||||
|
||||
# As above, but with some print commands for debugging purposes
|
||||
def processattachments_debug():
|
||||
res = []
|
||||
attachments = MessageAttachment.objects.all()
|
||||
for a in attachments:
|
||||
donotdelete = 1
|
||||
m = Message.objects.get(id=a.message_id)
|
||||
from_address = m.from_address[0]
|
||||
name = m.subject
|
||||
|
||||
# get a list of users
|
||||
theusers = User.objects.filter(email=from_address)
|
||||
print theusers
|
||||
for u in theusers:
|
||||
try:
|
||||
rr = Rower.objects.get(user=u.id)
|
||||
doorgaan = 1
|
||||
except:
|
||||
doorgaan = 0
|
||||
if doorgaan:
|
||||
# move attachment and make workout
|
||||
print a.document
|
||||
print name
|
||||
wid = [make_new_workout_from_email(rr,a.document,name)]
|
||||
res += wid
|
||||
link = 'https://rowsandall.com/rowers/workout/'+str(wid[0])+'/edit'
|
||||
if wid != 1:
|
||||
dd = send_confirm(u,name,link)
|
||||
return result
|
||||
|
||||
|
||||
|
||||
# remove attachment
|
||||
if donotdelete == 0:
|
||||
a.delete()
|
||||
|
||||
if m.attachments.exists()==False:
|
||||
# no attachments, so can be deleted
|
||||
m.delete()
|
||||
|
||||
mm = Message.objects.all()
|
||||
for m in mm:
|
||||
if m.attachments.exists()==False:
|
||||
m.delete()
|
||||
|
||||
return res
|
||||
|
||||
# Process the attachment file, create new workout
|
||||
# The code here is duplication of the code in views.py (workout_upload_view)
|
||||
# Need to move the code to a subroutine used both in views.py and here
|
||||
def make_new_workout_from_email(rr,f2,name,cntr=0):
|
||||
def make_new_workout_from_email(rower, datafile, name, cntr=0):
|
||||
""" This one is used in processemail """
|
||||
workouttype = 'rower'
|
||||
|
||||
try:
|
||||
f2 = f2.name
|
||||
fileformat = get_file_type('media/'+f2)
|
||||
datafilename = datafile.name
|
||||
fileformat = get_file_type('media/' + datafilename)
|
||||
except IOError:
|
||||
f2 = f2.name+'.gz'
|
||||
fileformat = get_file_type('media/'+f2)
|
||||
datafilename = datafile.name + '.gz'
|
||||
fileformat = get_file_type('media/' + datafilename)
|
||||
except AttributeError:
|
||||
fileformat = get_file_type('media/'+f2)
|
||||
datafilename = datafile
|
||||
fileformat = get_file_type('media/' + datafile)
|
||||
|
||||
if len(fileformat) == 3 and fileformat[0] == 'zip':
|
||||
f_to_be_deleted = f2
|
||||
with zipfile.ZipFile('media/'+f2) as z:
|
||||
f2 = z.extract(z.namelist()[0],path='media/')[6:]
|
||||
with zipfile.ZipFile('media/' + datafilename) as zip_file:
|
||||
datafilename = zip_file.extract(
|
||||
zip_file.namelist()[0],
|
||||
path='media/')[6:]
|
||||
fileformat = fileformat[2]
|
||||
|
||||
if fileformat == 'unknown':
|
||||
# extension = datafilename[-4:].lower()
|
||||
# fcopy = "media/"+datafilename[:-4]+"_copy"+extension
|
||||
# with open('media/'+datafilename, 'r') as f_in, open(fcopy, 'w') as f_out:
|
||||
# shutil.copyfileobj(f_in,f_out)
|
||||
fcopy = "media/"+datafilename
|
||||
if settings.DEBUG:
|
||||
res = handle_sendemail_unrecognized.delay(f2,
|
||||
"roosendaalsander@gmail.com")
|
||||
|
||||
res = handle_sendemail_unrecognized.delay(
|
||||
fcopy,
|
||||
rower.user.email
|
||||
)
|
||||
res = handle_sendemail_unrecognizedowner.delay(
|
||||
rower.user.email,
|
||||
rower.user.first_name
|
||||
)
|
||||
else:
|
||||
res = queuehigh.enqueue(handle_sendemail_unrecognized,
|
||||
f2,"roosendaalsander@gmail.com")
|
||||
|
||||
return 1
|
||||
fcopy,
|
||||
rower.user.email)
|
||||
res = queuehigh.enqueue(handle_sendemail_unrecognizedowner,
|
||||
rower.user.email,
|
||||
rower.user.first_name
|
||||
)
|
||||
return 0
|
||||
|
||||
summary = ''
|
||||
# handle non-Painsled
|
||||
if fileformat != 'csv':
|
||||
f3,summary,oarlength,inboard = dataprep.handle_nonpainsled('media/'+f2,fileformat,summary)
|
||||
filename_mediadir, summary, oarlength, inboard = dataprep.handle_nonpainsled(
|
||||
'media/' + datafilename, fileformat, summary)
|
||||
else:
|
||||
f3 = 'media/'+f2
|
||||
filename_mediadir = 'media/' + datafilename
|
||||
inboard = 0.88
|
||||
oarlength = 2.89
|
||||
|
||||
|
||||
|
||||
|
||||
# make workout and put in database
|
||||
#r = rrower(hrmax=rr.max,hrut2=rr.ut2,
|
||||
# hrut1=rr.ut1,hrat=rr.at,
|
||||
# hrtr=rr.tr,hran=rr.an,ftp=r.ftp)
|
||||
row = rdata(f3) #,rower=r)
|
||||
row = rdata(filename_mediadir)
|
||||
if row == 0:
|
||||
return 0
|
||||
|
||||
|
||||
# change filename
|
||||
if f2[:5] != 'media':
|
||||
if datafilename[:5] != 'media':
|
||||
timestr = time.strftime("%Y%m%d-%H%M%S")
|
||||
f2 = 'media/'+timestr+str(cntr)+'o.csv'
|
||||
datafilename = 'media/' + timestr + str(cntr) + 'o.csv'
|
||||
|
||||
try:
|
||||
avglat = row.df[' latitude'].mean()
|
||||
@@ -230,24 +137,21 @@ def make_new_workout_from_email(rr,f2,name,cntr=0):
|
||||
except KeyError:
|
||||
pass
|
||||
|
||||
row.write_csv(f2,gzip=True)
|
||||
row.write_csv(datafilename, gzip=True)
|
||||
dosummary = (fileformat != 'fit')
|
||||
|
||||
if name == '':
|
||||
name = 'imported through email'
|
||||
|
||||
id,message = dataprep.save_workout_database(f2,rr,
|
||||
id, message = dataprep.save_workout_database(
|
||||
datafilename, rower,
|
||||
workouttype=workouttype,
|
||||
dosummary=dosummary,
|
||||
inboard=inboard,
|
||||
oarlength=oarlength,
|
||||
title=name,
|
||||
workoutsource=fileformat,
|
||||
notes='imported through email')
|
||||
|
||||
notes='imported through email'
|
||||
)
|
||||
|
||||
return id
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -1,162 +1,148 @@
|
||||
#!/srv/venv/bin/python
|
||||
""" Process emails """
|
||||
import sys
|
||||
import os
|
||||
|
||||
import zipfile
|
||||
|
||||
import time
|
||||
from time import strftime
|
||||
|
||||
from django.core.management.base import BaseCommand
|
||||
from django_mailbox.models import Message, MessageAttachment
|
||||
from django.core.urlresolvers import reverse
|
||||
from django.conf import settings
|
||||
|
||||
from rowers.models import Workout, Rower
|
||||
|
||||
from rowingdata import rower as rrower
|
||||
from rowingdata import rowingdata as rrdata
|
||||
|
||||
import rowers.uploads as uploads
|
||||
from rowers.mailprocessing import make_new_workout_from_email, send_confirm
|
||||
|
||||
# If you find a solution that does not need the two paths, please comment!
|
||||
sys.path.append('$path_to_root_of_project$')
|
||||
sys.path.append('$path_to_root_of_project$/$project_name$')
|
||||
|
||||
os.environ['DJANGO_SETTINGS_MODULE'] = '$project_name$.settings'
|
||||
|
||||
import zipfile
|
||||
if not getattr(__builtins__, "WindowsError", None):
|
||||
class WindowsError(OSError): pass
|
||||
|
||||
from django.core.management.base import BaseCommand, CommandError
|
||||
from django.conf import settings
|
||||
#from rowers.mailprocessing import processattachments
|
||||
import time
|
||||
from time import strftime
|
||||
from django.conf import settings
|
||||
from rowers.tasks import handle_sendemail_unrecognized
|
||||
from django_mailbox.models import Mailbox,Message,MessageAttachment
|
||||
from rowers.models import Workout, User, Rower, WorkoutForm,RowerForm,GraphImage,AdvancedWorkoutForm
|
||||
from django.core.files.base import ContentFile
|
||||
|
||||
from rowsandall_app.settings import BASE_DIR
|
||||
|
||||
from rowingdata import rower as rrower
|
||||
from rowingdata import main as rmain
|
||||
from rowingdata import rowingdata as rrdata
|
||||
|
||||
from rowingdata import make_cumvalues
|
||||
from rowingdata import summarydata,get_file_type
|
||||
|
||||
from scipy.signal import savgol_filter
|
||||
from rowers.mailprocessing import make_new_workout_from_email,send_confirm
|
||||
import rowers.uploads as uploads
|
||||
|
||||
def rdata(file,rower=rrower()):
|
||||
def rdata(file_obj, rower=rrower()):
|
||||
""" Read rowing data file and return 0 if file doesn't exist"""
|
||||
try:
|
||||
res = rrdata(file,rower=rower)
|
||||
result = rrdata(file_obj, rower=rower)
|
||||
except IOError:
|
||||
res = 0
|
||||
result = 0
|
||||
|
||||
return res
|
||||
return result
|
||||
|
||||
def processattachment(rower, fileobj, title, uploadoptions):
|
||||
try:
|
||||
filename = fileobj.name
|
||||
except AttributeError:
|
||||
filename = fileobj[6:]
|
||||
|
||||
workoutid = [
|
||||
make_new_workout_from_email(rower, filename, title)
|
||||
]
|
||||
if workoutid[0]:
|
||||
link = settings.SITE_URL+reverse(
|
||||
rower.defaultlandingpage,
|
||||
kwargs = {
|
||||
'id':workoutid[0],
|
||||
}
|
||||
)
|
||||
|
||||
if uploadoptions and not 'error' in uploadoptions:
|
||||
workout = Workout.objects.get(id=workoutid[0])
|
||||
uploads.do_sync(workout, uploadoptions)
|
||||
uploads.make_private(workout, uploadoptions)
|
||||
if 'make_plot' in uploadoptions:
|
||||
plottype = uploadoptions['plottype']
|
||||
workoutcsvfilename = workout.csvfilename[6:-4]
|
||||
timestr = strftime("%Y%m%d-%H%M%S")
|
||||
imagename = workoutcsvfilename + timestr + '.png'
|
||||
result = uploads.make_plot(
|
||||
workout.user, workout, workoutcsvfilename,
|
||||
workout.csvfilename,
|
||||
plottype, title,
|
||||
imagename=imagename
|
||||
)
|
||||
try:
|
||||
if workoutid:
|
||||
email_sent = send_confirm(
|
||||
rower.user, title, link,
|
||||
uploadoptions
|
||||
)
|
||||
time.sleep(10)
|
||||
except:
|
||||
try:
|
||||
time.sleep(10)
|
||||
if workoutid:
|
||||
email_sent = send_confirm(
|
||||
rower.user, title, link,
|
||||
uploadoptions
|
||||
)
|
||||
except:
|
||||
pass
|
||||
|
||||
return workoutid
|
||||
|
||||
class Command(BaseCommand):
|
||||
"""Run the Email processing command """
|
||||
def handle(self, *args, **options):
|
||||
res = []
|
||||
attachments = MessageAttachment.objects.all()
|
||||
cntr = 0
|
||||
for a in attachments:
|
||||
extension = a.document.name[-3:].lower()
|
||||
donotdelete = 0
|
||||
m = Message.objects.get(id=a.message_id)
|
||||
body = "\n".join(m.text.splitlines())
|
||||
for attachment in attachments:
|
||||
extension = attachment.document.name[-3:].lower()
|
||||
message = Message.objects.get(id=attachment.message_id)
|
||||
body = "\n".join(message.text.splitlines())
|
||||
uploadoptions = uploads.upload_options(body)
|
||||
from_address = m.from_address[0].lower()
|
||||
name = m.subject
|
||||
cntr += 1
|
||||
from_address = message.from_address[0].lower()
|
||||
name = message.subject
|
||||
# get a list of users
|
||||
# theusers = User.objects.filter(email=from_address)
|
||||
ther = [
|
||||
rowers = [
|
||||
r for r in Rower.objects.all() if r.user.email.lower() == from_address
|
||||
]
|
||||
for rr in ther:
|
||||
for rower in rowers:
|
||||
if extension == 'zip':
|
||||
z = zipfile.ZipFile(a.document)
|
||||
for f in z.namelist():
|
||||
f2 = z.extract(f,path='media/')
|
||||
title = os.path.basename(f2)
|
||||
wid = [
|
||||
make_new_workout_from_email(rr,f2[6:],title)
|
||||
]
|
||||
res += wid
|
||||
link = 'http://rowsandall.com/rowers/workout/'+str(wid[0])+'/edit'
|
||||
if uploadoptions and not 'error' in uploadoptions:
|
||||
w = Workout.objects.get(id=wid[0])
|
||||
r = w.user
|
||||
uploads.do_sync(w,uploadoptions)
|
||||
uploads.make_private(w,uploadoptions)
|
||||
if 'make_plot' in uploadoptions:
|
||||
plottype = uploadoptions['plottype']
|
||||
f1 = w.csvfilename[6:-4]
|
||||
timestr = strftime("%Y%m%d-%H%M%S")
|
||||
imagename = f1+timestr+'.png'
|
||||
resu = uploads.make_plot(r,w,f1,
|
||||
w.csvfilename,
|
||||
plottype,name,
|
||||
imagename=imagename)
|
||||
try:
|
||||
if wid != 1:
|
||||
dd = send_confirm(rr.user,title,link,
|
||||
uploadoptions)
|
||||
time.sleep(10)
|
||||
except:
|
||||
try:
|
||||
time.sleep(10)
|
||||
if wid != 1:
|
||||
dd = send_confirm(rr.user,title,link,
|
||||
uploadoptions)
|
||||
except:
|
||||
pass
|
||||
|
||||
zip_file = zipfile.ZipFile(attachment.document)
|
||||
for filename in zip_file.namelist():
|
||||
datafile = zip_file.extract(filename, path='media/')
|
||||
title = os.path.basename(datafile)
|
||||
workoutid = processattachment(
|
||||
rower, datafile, title, uploadoptions
|
||||
)
|
||||
else:
|
||||
# move attachment and make workout
|
||||
try:
|
||||
wid = [
|
||||
make_new_workout_from_email(rr,
|
||||
a.document,
|
||||
name)
|
||||
]
|
||||
res += wid
|
||||
link = 'http://rowsandall.com/rowers/workout/'+str(wid[0])+'/edit'
|
||||
if uploadoptions:
|
||||
w = Workout.objects.get(id=wid[0])
|
||||
r = w.user
|
||||
uploads.do_sync(w,uploadoptions)
|
||||
uploads.make_private(w,uploadoptions)
|
||||
if 'make_plot' in uploadoptions:
|
||||
plottype = uploadoptions['plottype']
|
||||
f1 = w.csvfilename[6:-4]
|
||||
timestr = strftime("%Y%m%d-%H%M%S")
|
||||
imagename = f1+timestr+'.png'
|
||||
resu = uploads.make_plot(r,w,f1,
|
||||
w.csvfilename,
|
||||
plottype,name,
|
||||
imagename=imagename)
|
||||
workoutid = processattachment(
|
||||
rower, attachment.document, name, uploadoptions
|
||||
)
|
||||
|
||||
except:
|
||||
# replace with code to process error
|
||||
res += ['fail: '+name]
|
||||
donotdelete = 1
|
||||
wid = 1
|
||||
# We're done with the attachment. It can be deleted
|
||||
try:
|
||||
if wid != 1:
|
||||
dd = send_confirm(rr.user,name,link,
|
||||
uploadoptions)
|
||||
time.sleep(10)
|
||||
except:
|
||||
pass
|
||||
|
||||
try:
|
||||
a.delete()
|
||||
attachment.delete()
|
||||
except IOError:
|
||||
pass
|
||||
# remove attachment
|
||||
#if donotdelete == 0:
|
||||
except WindowsError:
|
||||
time.sleep(2)
|
||||
try:
|
||||
attachment.delete()
|
||||
except WindowsError:
|
||||
pass
|
||||
|
||||
if m.attachments.exists()==False:
|
||||
if message.attachments.exists() is False:
|
||||
# no attachments, so can be deleted
|
||||
m.delete()
|
||||
|
||||
mm = Message.objects.all()
|
||||
for m in mm:
|
||||
if m.attachments.exists()==False:
|
||||
m.delete()
|
||||
|
||||
self.stdout.write(self.style.SUCCESS('Successfully processed email attachments'))
|
||||
|
||||
|
||||
message.delete()
|
||||
|
||||
messages = Message.objects.all()
|
||||
for message in messages:
|
||||
if message.attachments.exists() is False:
|
||||
message.delete()
|
||||
|
||||
self.stdout.write(self.style.SUCCESS(
|
||||
'Successfully processed email attachments'))
|
||||
|
||||
+2
-2
@@ -269,7 +269,7 @@ in case you recorded your heart rate during your workout""",
|
||||
heart rate versus time. """,
|
||||
},
|
||||
{
|
||||
'yparam1':'strokeenergy',
|
||||
'yparam1':'driveenergy',
|
||||
'yparam2':'hr',
|
||||
'xparam':'time',
|
||||
'plottype':'line',
|
||||
@@ -291,7 +291,7 @@ as you increase stroke rate. Typical values are > 10m for steady state
|
||||
dropping to 8m for race pace in the single.""",
|
||||
},
|
||||
{
|
||||
'yparam1':'strokeenergy',
|
||||
'yparam1':'driveenergy',
|
||||
'yparam2':'None',
|
||||
'xparam':'spm',
|
||||
'plottype':'line',
|
||||
|
||||
+7
-3
@@ -198,7 +198,7 @@ class TeamRequest(models.Model):
|
||||
|
||||
from utils import (
|
||||
workflowleftpanel,workflowmiddlepanel,
|
||||
defaultleft,defaultmiddle
|
||||
defaultleft,defaultmiddle,landingpages
|
||||
)
|
||||
|
||||
# Extension of User with rowing specific data
|
||||
@@ -281,8 +281,11 @@ class Rower(models.Model):
|
||||
|
||||
# Site Settings
|
||||
workflowleftpanel = TemplateListField(default=defaultleft)
|
||||
|
||||
workflowmiddlepanel = TemplateListField(default=defaultmiddle)
|
||||
defaultlandingpage = models.CharField(default='workout_edit_view',
|
||||
max_length=200,
|
||||
choices=landingpages,
|
||||
verbose_name="Default Landing Page")
|
||||
|
||||
# Access tokens
|
||||
c2token = models.CharField(default='',max_length=200,blank=True,null=True)
|
||||
@@ -889,7 +892,8 @@ class AccountRowerForm(ModelForm):
|
||||
class Meta:
|
||||
model = Rower
|
||||
fields = ['weightcategory','getemailnotifications',
|
||||
'defaulttimezone','showfavoritechartnotes']
|
||||
'defaulttimezone','showfavoritechartnotes',
|
||||
'defaultlandingpage']
|
||||
|
||||
class UserForm(ModelForm):
|
||||
class Meta:
|
||||
|
||||
+78
-33
@@ -1,4 +1,4 @@
|
||||
from celery import Celery,app
|
||||
""" Background tasks done by Celery (develop) or QR (production) """
|
||||
import os
|
||||
import time
|
||||
import gc
|
||||
@@ -7,40 +7,44 @@ import shutil
|
||||
import numpy as np
|
||||
|
||||
import rowingdata
|
||||
from rowingdata import main as rmain
|
||||
|
||||
from rowingdata import rowingdata as rdata
|
||||
import rowingdata
|
||||
from async_messages import message_user,messages
|
||||
|
||||
from celery import app
|
||||
|
||||
from matplotlib.backends.backend_agg import FigureCanvas
|
||||
#from matplotlib.backends.backend_cairo import FigureCanvasCairo as FigureCanvas
|
||||
import matplotlib.pyplot as plt
|
||||
from matplotlib import figure
|
||||
|
||||
import stravalib
|
||||
|
||||
|
||||
import pandas as pd
|
||||
|
||||
from django_rq import job
|
||||
|
||||
from utils import serialize_list,deserialize_list
|
||||
from utils import deserialize_list
|
||||
|
||||
from rowers.dataprepnodjango import (
|
||||
update_strokedata, new_workout_from_file,
|
||||
getsmallrowdata_db,read_df_sql,columndict,
|
||||
getsmallrowdata_db,
|
||||
)
|
||||
|
||||
from django.core.mail import send_mail, BadHeaderError,EmailMessage
|
||||
from django.core.mail import send_mail, EmailMessage
|
||||
|
||||
|
||||
import datautils
|
||||
import utils
|
||||
|
||||
# testing task
|
||||
|
||||
|
||||
@app.task
|
||||
def add(x, y):
|
||||
return x + y
|
||||
|
||||
# create workout
|
||||
|
||||
|
||||
@app.task
|
||||
def handle_new_workout_from_file(r, f2,
|
||||
workouttype='rower',
|
||||
@@ -51,6 +55,8 @@ def handle_new_workout_from_file(r,f2,
|
||||
title, makeprivate, notes)
|
||||
|
||||
# process and update workouts
|
||||
|
||||
|
||||
@app.task
|
||||
def handle_updatedps(useremail, workoutids, debug=False):
|
||||
for wid, f1 in workoutids:
|
||||
@@ -81,6 +87,8 @@ def handle_updatedps(useremail,workoutids,debug=False):
|
||||
return 1
|
||||
|
||||
# send email when a breakthrough workout is uploaded
|
||||
|
||||
|
||||
@app.task
|
||||
def handle_sendemail_breakthrough(workoutid, useremail,
|
||||
userfirstname, userlastname,
|
||||
@@ -124,7 +132,6 @@ def handle_sendemail_breakthrough(workoutid,useremail,
|
||||
pwr=pwr
|
||||
)
|
||||
|
||||
|
||||
message += "To opt out of these email notifications, deselect the checkbox on your Profile page under Account Information.\n\n"
|
||||
|
||||
message += "Best Regards, the Rowsandall Team"
|
||||
@@ -133,13 +140,14 @@ def handle_sendemail_breakthrough(workoutid,useremail,
|
||||
'Rowsandall <info@rowsandall.com>',
|
||||
[useremail])
|
||||
|
||||
|
||||
res = email.send()
|
||||
|
||||
# remove tcx file
|
||||
return 1
|
||||
|
||||
# send email when a breakthrough workout is uploaded
|
||||
|
||||
|
||||
@app.task
|
||||
def handle_sendemail_hard(workoutid, useremail,
|
||||
userfirstname, userlastname,
|
||||
@@ -167,7 +175,6 @@ def handle_sendemail_hard(workoutid,useremail,
|
||||
'Rowsandall <info@rowsandall.com>',
|
||||
[useremail])
|
||||
|
||||
|
||||
res = email.send()
|
||||
|
||||
# remove tcx file
|
||||
@@ -206,6 +213,37 @@ def handle_sendemail_unrecognized(unrecognizedfile,useremail):
|
||||
return 1
|
||||
|
||||
|
||||
# send email to owner when an unrecognized file is uploaded
|
||||
@app.task
|
||||
def handle_sendemail_unrecognizedowner(useremail, userfirstname):
|
||||
|
||||
# send email with attachment
|
||||
fullemail = useremail
|
||||
subject = "Unrecognized file from Rowsandall.com"
|
||||
message = "Dear " + userfirstname + ",\n\n"
|
||||
message += """
|
||||
The file you tried to send to rowsandall.com was not recognized by
|
||||
our email processing system. You may have sent a file in a format
|
||||
that is not supported. Sometimes, rowing apps make file format changes.
|
||||
When that happens, it takes some time for rowsandall.comm to make
|
||||
the necessary changes on our side and support the app again.
|
||||
|
||||
The file has been sent to the developer at rowsandall.com for evaluation.
|
||||
|
||||
|
||||
"""
|
||||
|
||||
message += "Best Regards, the Rowsandall Team"
|
||||
|
||||
email = EmailMessage(subject, message,
|
||||
'Rowsandall <info@rowsandall.com>',
|
||||
[fullemail])
|
||||
|
||||
res = email.send()
|
||||
|
||||
return 1
|
||||
|
||||
|
||||
# Send email with TCX attachment
|
||||
@app.task
|
||||
def handle_sendemailtcx(first_name, last_name, email, tcxfile):
|
||||
@@ -221,7 +259,6 @@ def handle_sendemailtcx(first_name,last_name,email,tcxfile):
|
||||
'Rowsandall <info@rowsandall.com>',
|
||||
[fullemail])
|
||||
|
||||
|
||||
email.attach_file(tcxfile)
|
||||
|
||||
res = email.send()
|
||||
@@ -230,6 +267,7 @@ def handle_sendemailtcx(first_name,last_name,email,tcxfile):
|
||||
os.remove(tcxfile)
|
||||
return 1
|
||||
|
||||
|
||||
@app.task
|
||||
def handle_zip_file(emailfrom, subject, file):
|
||||
message = "... zip processing ... "
|
||||
@@ -242,6 +280,8 @@ def handle_zip_file(emailfrom,subject,file):
|
||||
return 1
|
||||
|
||||
# Send email with CSV attachment
|
||||
|
||||
|
||||
@app.task
|
||||
def handle_sendemailcsv(first_name, last_name, email, csvfile):
|
||||
|
||||
@@ -256,7 +296,6 @@ def handle_sendemailcsv(first_name,last_name,email,csvfile):
|
||||
'Rowsandall <info@rowsandall.com>',
|
||||
[fullemail])
|
||||
|
||||
|
||||
if os.path.isfile(csvfile):
|
||||
email.attach_file(csvfile)
|
||||
else:
|
||||
@@ -272,9 +311,12 @@ def handle_sendemailcsv(first_name,last_name,email,csvfile):
|
||||
return 1
|
||||
|
||||
# Calculate wind and stream corrections for OTW rowing
|
||||
|
||||
|
||||
@app.task
|
||||
def handle_otwsetpower(f1, boattype, weightvalue,
|
||||
first_name,last_name,email,workoutid,ps=[1,1,1,1],
|
||||
first_name, last_name, email, workoutid, ps=[
|
||||
1, 1, 1, 1],
|
||||
ratio=1.0,
|
||||
debug=False):
|
||||
try:
|
||||
@@ -317,7 +359,8 @@ def handle_otwsetpower(f1,boattype,weightvalue,
|
||||
rowdata.write_csv(f1, gzip=True)
|
||||
update_strokedata(workoutid, rowdata.df, debug=debug)
|
||||
|
||||
totaltime = rowdata.df['TimeStamp (sec)'].max()-rowdata.df['TimeStamp (sec)'].min()
|
||||
totaltime = rowdata.df['TimeStamp (sec)'].max(
|
||||
) - rowdata.df['TimeStamp (sec)'].min()
|
||||
try:
|
||||
totaltime = totaltime + rowdata.df.ix[0, ' ElapsedTime (sec)']
|
||||
except KeyError:
|
||||
@@ -329,9 +372,9 @@ def handle_otwsetpower(f1,boattype,weightvalue,
|
||||
dfgrouped = df.groupby(['workoutid'])
|
||||
delta, cpvalues, avgpower = datautils.getcp(dfgrouped, logarr)
|
||||
|
||||
|
||||
#delta,cpvalues,avgpower = datautils.getsinglecp(rowdata.df)
|
||||
res,btvalues,res2 = utils.isbreakthrough(delta,cpvalues,ps[0],ps[1],ps[2],ps[3],ratio)
|
||||
res, btvalues, res2 = utils.isbreakthrough(
|
||||
delta, cpvalues, ps[0], ps[1], ps[2], ps[3], ratio)
|
||||
if res:
|
||||
handle_sendemail_breakthrough(workoutid, email,
|
||||
first_name,
|
||||
@@ -358,6 +401,8 @@ def handle_otwsetpower(f1,boattype,weightvalue,
|
||||
return 1
|
||||
|
||||
# This function generates all the static (PNG image) plots
|
||||
|
||||
|
||||
@app.task
|
||||
def handle_makeplot(f1, f2, t, hrdata, plotnr, imagename):
|
||||
|
||||
@@ -381,7 +426,6 @@ def handle_makeplot(f1,f2,t,hrdata,plotnr,imagename):
|
||||
except IOError:
|
||||
row = rdata(f2 + '.gz', rower=rr)
|
||||
|
||||
|
||||
haspower = row.df[' Power (watts)'].mean() > 50
|
||||
|
||||
nr_rows = len(row.df)
|
||||
@@ -430,6 +474,7 @@ def handle_makeplot(f1,f2,t,hrdata,plotnr,imagename):
|
||||
|
||||
# Team related remote tasks
|
||||
|
||||
|
||||
@app.task
|
||||
def handle_sendemail_invite(email, name, code, teamname, manager):
|
||||
fullemail = name + ' <' + email + '>'
|
||||
@@ -461,11 +506,11 @@ def handle_sendemail_invite(email,name,code,teamname,manager):
|
||||
'Rowsandall <info@rowsandall.com>',
|
||||
[fullemail])
|
||||
|
||||
|
||||
res = email.send()
|
||||
|
||||
return 1
|
||||
|
||||
|
||||
@app.task
|
||||
def handle_sendemailnewresponse(first_name, last_name,
|
||||
email,
|
||||
@@ -482,21 +527,23 @@ def handle_sendemailnewresponse(first_name,last_name,
|
||||
message += comment
|
||||
message += '\n\n'
|
||||
message += 'You can read the comment here:\n'
|
||||
message += 'https://rowsandall.com/rowers/workout/'+str(workoutid)+'/comment'
|
||||
message += 'https://rowsandall.com/rowers/workout/' + \
|
||||
str(workoutid) + '/comment'
|
||||
message += '\n\n'
|
||||
message += 'You are receiving this email because you are subscribed '
|
||||
message += 'to comments on this workout. To unsubscribe, follow this link:\n'
|
||||
message += 'https://rowsandall.com/rowers/workout/'+str(workoutid)+'/unsubscribe'
|
||||
message += 'https://rowsandall.com/rowers/workout/' + \
|
||||
str(workoutid) + '/unsubscribe'
|
||||
|
||||
email = EmailMessage(subject, message,
|
||||
'Rowsandall <info@rowsandall.com>',
|
||||
[fullemail])
|
||||
|
||||
|
||||
res = email.send()
|
||||
|
||||
return 1
|
||||
|
||||
|
||||
@app.task
|
||||
def handle_sendemailnewcomment(first_name,
|
||||
last_name,
|
||||
@@ -514,13 +561,13 @@ def handle_sendemailnewcomment(first_name,
|
||||
message += comment
|
||||
message += '\n\n'
|
||||
message += 'You can read the comment here:\n'
|
||||
message += 'https://rowsandall.com/rowers/workout/'+str(workoutid)+'/comment'
|
||||
message += 'https://rowsandall.com/rowers/workout/' + \
|
||||
str(workoutid) + '/comment'
|
||||
|
||||
email = EmailMessage(subject, message,
|
||||
'Rowsandall <info@rowsandall.com>',
|
||||
[fullemail])
|
||||
|
||||
|
||||
res = email.send()
|
||||
|
||||
return 1
|
||||
@@ -545,11 +592,11 @@ def handle_sendemail_request(email,name,code,teamname,requestor,id):
|
||||
'Rowsandall <info@rowsandall.com>',
|
||||
[fullemail])
|
||||
|
||||
|
||||
res = email.send()
|
||||
|
||||
return 1
|
||||
|
||||
|
||||
@app.task
|
||||
def handle_sendemail_request_accept(email, name, teamname, managername):
|
||||
fullemail = name + ' <' + email + '>'
|
||||
@@ -565,11 +612,11 @@ def handle_sendemail_request_accept(email,name,teamname,managername):
|
||||
'Rowsandall <info@rowsandall.com>',
|
||||
[fullemail])
|
||||
|
||||
|
||||
res = email.send()
|
||||
|
||||
return 1
|
||||
|
||||
|
||||
@app.task
|
||||
def handle_sendemail_request_reject(email, name, teamname, managername):
|
||||
fullemail = name + ' <' + email + '>'
|
||||
@@ -586,11 +633,11 @@ def handle_sendemail_request_reject(email,name,teamname,managername):
|
||||
'Rowsandall <info@rowsandall.com>',
|
||||
[fullemail])
|
||||
|
||||
|
||||
res = email.send()
|
||||
|
||||
return 1
|
||||
|
||||
|
||||
@app.task
|
||||
def handle_sendemail_member_dropped(email, name, teamname, managername):
|
||||
fullemail = name + ' <' + email + '>'
|
||||
@@ -607,11 +654,11 @@ def handle_sendemail_member_dropped(email,name,teamname,managername):
|
||||
'Rowsandall <info@rowsandall.com>',
|
||||
[fullemail])
|
||||
|
||||
|
||||
res = email.send()
|
||||
|
||||
return 1
|
||||
|
||||
|
||||
@app.task
|
||||
def handle_sendemail_team_removed(email, name, teamname, managername):
|
||||
fullemail = name + ' <' + email + '>'
|
||||
@@ -629,11 +676,11 @@ def handle_sendemail_team_removed(email,name,teamname,managername):
|
||||
'Rowsandall <info@rowsandall.com>',
|
||||
[fullemail])
|
||||
|
||||
|
||||
res = email.send()
|
||||
|
||||
return 1
|
||||
|
||||
|
||||
@app.task
|
||||
def handle_sendemail_invite_reject(email, name, teamname, managername):
|
||||
fullemail = managername + ' <' + email + '>'
|
||||
@@ -650,11 +697,11 @@ def handle_sendemail_invite_reject(email,name,teamname,managername):
|
||||
'Rowsandall <info@rowsandall.com>',
|
||||
[fullemail])
|
||||
|
||||
|
||||
res = email.send()
|
||||
|
||||
return 1
|
||||
|
||||
|
||||
@app.task
|
||||
def handle_sendemail_invite_accept(email, name, teamname, managername):
|
||||
fullemail = managername + ' <' + email + '>'
|
||||
@@ -667,13 +714,11 @@ def handle_sendemail_invite_accept(email,name,teamname,managername):
|
||||
'Rowsandall <info@rowsandall.com>',
|
||||
[fullemail])
|
||||
|
||||
|
||||
res = email.send()
|
||||
|
||||
return 1
|
||||
|
||||
|
||||
|
||||
# Another simple task for debugging purposes
|
||||
def add2(x, y):
|
||||
return x + y
|
||||
|
||||
@@ -24,7 +24,12 @@
|
||||
<a class="button gray small" href="/rowers/workout/{{ workout.id }}/edit">Edit Workout</a>
|
||||
</p>
|
||||
</div>
|
||||
<div class="grid_2 suffix_2 omega">
|
||||
<div class="grid_2">
|
||||
<p>
|
||||
<a class="button gray small" href="/rowers/workout/{{ workout.id }}/workflow">Workflow View</a>
|
||||
</p>
|
||||
</div>
|
||||
<div class="grid_2 omega">
|
||||
<p>
|
||||
<a class="button gray small" href="/rowers/workout/{{ workout.id }}/export">Export</a>
|
||||
</p>
|
||||
|
||||
@@ -23,7 +23,12 @@
|
||||
<a class="button gray small" href="/rowers/workout/{{ workout.id }}/edit">Edit Workout</a>
|
||||
</p>
|
||||
</div>
|
||||
<div class="grid_2 suffix_2 omega">
|
||||
<div class="grid_2">
|
||||
<p>
|
||||
<a class="button gray small" href="/rowers/workout/{{ workout.id }}/workflow">Workflow View</a>
|
||||
</p>
|
||||
</div>
|
||||
<div class="grid_2 omega">
|
||||
<p>
|
||||
<a class="button gray small" href="/rowers/workout/{{ workout.id }}/export">Export</a>
|
||||
</p>
|
||||
|
||||
@@ -38,23 +38,10 @@
|
||||
You can select one static plot to be generated immediately for
|
||||
this workout. You can select to export to major fitness
|
||||
platforms automatically.
|
||||
If you check "make private", this workout will not be visible to your followers and will not show up in your teams' workouts list.
|
||||
If you check "make private", this workout will not be visible to your followers and will not show up in your teams' workouts list. With the Landing Page option, you can select to which (workout related) page you will be
|
||||
taken after a successfull upload.
|
||||
</p>
|
||||
|
||||
<p>
|
||||
Valid file types are:
|
||||
<ul>
|
||||
<li>Painsled iOS Stroke Export (CSV)</li>
|
||||
<li>Painsled desktop version Stroke Export (CSV)</li>
|
||||
<li>A TCX file with location data (lat,long) - with or without Heart Rate value, for example from RiM or CrewNerd</li>
|
||||
<li>RowPro CSV export</li>
|
||||
<li>SpeedCoach GPS and SpeedCoach GPS 2 CSV export</li>
|
||||
<li>ErgData CSV export</li>
|
||||
<li>ErgStick CSV export</li>
|
||||
<li>BoatCoach CSV export</li>
|
||||
<li>A FIT file with location data (experimental)</li>
|
||||
</ul>
|
||||
</p>
|
||||
</div>
|
||||
|
||||
|
||||
|
||||
@@ -15,7 +15,12 @@
|
||||
<a class="button gray small" href="/rowers/workout/{{ workout.id }}/edit">Edit Workout</a>
|
||||
</p>
|
||||
</div>
|
||||
<div class="grid_2 suffix_2 omega">
|
||||
<div class="grid_2">
|
||||
<p>
|
||||
<a class="button gray small" href="/rowers/workout/{{ workout.id }}/workflow">Workflow View</a>
|
||||
</p>
|
||||
</div>
|
||||
<div class="grid_2 omega">
|
||||
<p>
|
||||
<a class="button gray small" href="/rowers/workout/{{ workout.id }}/advanced">Advanced Edit</a>
|
||||
</p>
|
||||
|
||||
@@ -32,7 +32,12 @@
|
||||
<a class="button gray small" href="/rowers/workout/{{ id }}/edit">Edit Workout</a>
|
||||
</p>
|
||||
</div>
|
||||
<div class="grid_2 suffix_8 omega">
|
||||
<div class="grid_2">
|
||||
<p>
|
||||
<a class="button gray small" href="/rowers/workout/{{ id }}/workflow">Workflow View</a>
|
||||
</p>
|
||||
</div>
|
||||
<div class="grid_2 suffix_6 omega">
|
||||
<p>
|
||||
<a class="button gray small" href="/rowers/workout/{{ id }}/advanced">Advanced Edit</a>
|
||||
</p>
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
{% if charts %}
|
||||
<h2>Flex Charts</h2>
|
||||
<p>Click on the thumbnails to view the full chart.</p>
|
||||
{% for chart in charts %}
|
||||
<div class="grid_3 alpha">
|
||||
<big>{{ forloop.counter }}</big>
|
||||
|
||||
@@ -84,9 +84,9 @@
|
||||
[RANKING PIECE]
|
||||
{% endif %}
|
||||
{% if workout.name != '' %}
|
||||
<a href="/rowers/workout/{{ workout.id }}/edit">{{ workout.name }}</a> </td>
|
||||
<a href={% url rower.defaultlandingpage id=workout.id %}>{{ workout.name }}</a> </td>
|
||||
{% else %}
|
||||
<a href="/rowers/workout/{{ workout.id }}/edit">No Name</a> </td>
|
||||
<a href={% url rower.defaultlandingpage id=workout.id %}>No Name</a> </td>
|
||||
{% endif %}
|
||||
{% else %}
|
||||
{% if workout.name != '' %}
|
||||
|
||||
@@ -43,7 +43,13 @@
|
||||
<a class="button gray small" href="/rowers/workout/{{ workout.id }}/edit">Edit Workout</a>
|
||||
</p>
|
||||
</div>
|
||||
<div class="grid_2 suffix_2 omega">
|
||||
<div class="grid_2">
|
||||
<p>
|
||||
<a class="button gray small" href="/rowers/workout/{{ workout.id }}/workflow">Workflow View</a>
|
||||
</p>
|
||||
|
||||
</div>
|
||||
<div class="grid_2 omega">
|
||||
<p>
|
||||
<a class="button gray small" href="/rowers/workout/{{ workout.id }}/advanced">Advanced Edit</a>
|
||||
</p>
|
||||
|
||||
@@ -14,7 +14,13 @@
|
||||
<a class="button gray small" href="/rowers/workout/{{ workout.id }}/edit">Edit Workout</a>
|
||||
</p>
|
||||
</div>
|
||||
<div class="grid_2 suffix_2 omega">
|
||||
<div class="grid_2">
|
||||
<p>
|
||||
<a class="button gray small" href="/rowers/workout/{{ workout.id }}/workflow">Workflow View</a>
|
||||
</p>
|
||||
|
||||
</div>
|
||||
<div class="grid_2 omega">
|
||||
<p>
|
||||
<a class="button gray small" href="/rowers/workout/{{ workout.id }}/advanced">Advanced Edit</a>
|
||||
</p>
|
||||
|
||||
@@ -0,0 +1,31 @@
|
||||
{% load rowerfilters %}
|
||||
{% load tz %}
|
||||
<div class="grid_6 suffix_3 alpha">
|
||||
<table width=100%>
|
||||
<tr>
|
||||
{% localtime on %}
|
||||
<th>Date/Time:</th><td>{{ workout.startdatetime|localtime}}</td>
|
||||
{% endlocaltime %}
|
||||
</tr><tr>
|
||||
<th>Distance:</th><td>{{ workout.distance }}m</td>
|
||||
</tr><tr>
|
||||
<th>Duration:</th><td>{{ workout.duration |durationprint:"%H:%M:%S.%f" }}</td>
|
||||
</tr><tr>
|
||||
<th>Public link to this workout</th>
|
||||
<td>
|
||||
<a href="/rowers/workout/{{ workout.id }}">https://rowsandall.com/rowers/workout/{{ workout.id }}</a>
|
||||
</td>
|
||||
</tr><tr>
|
||||
<th>Comments</th>
|
||||
<td>
|
||||
<a href="/rowers/workout/{{ workout.id }}/comment">Comment ({{ aantalcomments }})</a>
|
||||
</td>
|
||||
|
||||
</tr><tr>
|
||||
<th>Public link to interactive chart</th>
|
||||
<td>
|
||||
<a href="/rowers/workout/{{ workout.id }}/interactiveplot">https://rowsandall.com/rowers/workout/{{ workout.id }}/interactiveplot</a>
|
||||
<td>
|
||||
</tr>
|
||||
</table>
|
||||
</div>
|
||||
@@ -0,0 +1,5 @@
|
||||
<div class="grid_2 alpha">
|
||||
<p>
|
||||
<a class="button red small" href="/rowers/workout/{{ workout.id }}/deleteconfirm">Delete</a>
|
||||
</p>
|
||||
</div>
|
||||
@@ -0,0 +1,5 @@
|
||||
<div class="grid_2 alpha">
|
||||
<p>
|
||||
<a class="button gray small" href="/rowers/workout/{{ workout.id }}/export">Export Workout</a>
|
||||
</p>
|
||||
</div>
|
||||
@@ -0,0 +1,9 @@
|
||||
<div style="height:100%;" id="theplot" class="grid_9 alpha flexplot">
|
||||
{{ mapdiv|safe }}
|
||||
|
||||
|
||||
{{ mapscript|safe }}
|
||||
</div>
|
||||
|
||||
|
||||
|
||||
@@ -0,0 +1,5 @@
|
||||
<div class="grid_2 alpha">
|
||||
<p>
|
||||
<a class="button gray small" href="/rowers/workout/{{ workout.id }}/map">Map View</a>
|
||||
</p>
|
||||
</div>
|
||||
@@ -0,0 +1,14 @@
|
||||
<div class="grid_9">
|
||||
<div class="grid_1 alpha">
|
||||
<div class="fb-share-button" data-href="https://rowsandall.com/rowers/workout/{{ workout.id }}" data-layout="button" data-size="small" data-mobile-iframe="false">
|
||||
<a class="fb-xfbml-parse-ignore" target="_blank" href="https://www.facebook.com/sharer/sharer.php?u=https://rowsandall.com/rowers/workout/{{ workout.id }}">Share</a>
|
||||
</div>
|
||||
</div>
|
||||
<div class="grid_1 suffix_7 omega">
|
||||
<a class="twitter-share-button"
|
||||
href="https://twitter.com/intent/tweet"
|
||||
data-url="https://rowsandall.com/rowers/workout/{{ workout.id }}"
|
||||
data-text="@rowsandall #rowingdata">Tweet</a>
|
||||
</div>
|
||||
|
||||
|
||||
@@ -0,0 +1,14 @@
|
||||
<div class="grid_2 alpha">
|
||||
<div class="fb-share-button" data-href="https://rowsandall.com/rowers/workout/{{ workout.id }}" data-layout="button" data-size="small" data-mobile-iframe="false">
|
||||
<a class="fb-xfbml-parse-ignore" target="_blank" href="https://www.facebook.com/sharer/sharer.php?u=https://rowsandall.com/rowers/workout/{{ workout.id }}">Share</a>
|
||||
</div>
|
||||
</div>
|
||||
<div class="grid_2 alpha">
|
||||
<p> </p>
|
||||
<a class="twitter-share-button"
|
||||
href="https://twitter.com/intent/tweet"
|
||||
data-url="https://rowsandall.com/rowers/workout/{{ workout.id }}"
|
||||
data-text="@rowsandall #rowingdata">Tweet</a>
|
||||
</div>
|
||||
|
||||
|
||||
@@ -29,7 +29,10 @@
|
||||
</div>
|
||||
</div>
|
||||
<div class="grid_6 alpha">
|
||||
<div class="grid_2 prefix_4 alpha">
|
||||
<div class="grid_2 prefix_2 alpha">
|
||||
<p><a class="button gray small" href="/rowers/workout/{{ workout.id }}/workflow">Workflow View</a></p>
|
||||
</div>
|
||||
<div class="grid_2 omega">
|
||||
<p><a class="button blue small" href="/rowers/workout/{{ workout.id }}/wind">Wind Edit</a></p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -23,7 +23,7 @@
|
||||
</div>
|
||||
<div class="grid_2">
|
||||
<p>
|
||||
<a class="button gray small" href="/rowers/workout/{{ workout.id }}/export">Export</a>
|
||||
<a class="button gray small" href="/rowers/workout/{{ workout.id }}/workflow">Workflow View</a>
|
||||
|
||||
</p>
|
||||
</div>
|
||||
|
||||
@@ -28,7 +28,10 @@
|
||||
</div>
|
||||
</div>
|
||||
<div class="grid_6 alpha">
|
||||
<div class="grid_2 prefix_4 alpha">
|
||||
<div class="grid_2 prefix_2 alpha">
|
||||
<p><a class="button gray small" href="/rowers/workout/{{ workout.id }}/workflow">Workflow View</a></p>
|
||||
</div>
|
||||
<div class="grid_2 omega">
|
||||
<p><a class="button blue small" href="/rowers/workout/{{ workout.id }}/stream">Stream Edit</a></p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -3,7 +3,6 @@
|
||||
{% load rowerfilters %}
|
||||
{% load tz %}
|
||||
|
||||
|
||||
{% get_current_timezone as TIME_ZONE %}
|
||||
|
||||
{% block title %}{{ workout.name }}{% endblock %}
|
||||
@@ -40,9 +39,6 @@
|
||||
{% include templateName %}
|
||||
{% endfor %}
|
||||
{% endblock %}
|
||||
<div class="grid_2 alpha">
|
||||
<p>Click on the thumbnails to view the full chart</p>
|
||||
</div>
|
||||
</div>
|
||||
<div id="middlepanel" class="grid_9">
|
||||
{% block middle_panel %}
|
||||
|
||||
@@ -44,7 +44,12 @@
|
||||
</div>
|
||||
</div>
|
||||
<div class="grid_6 alpha">
|
||||
<div class="grid_2 prefix_2 alpha">
|
||||
<div class="grid_2 alpha">
|
||||
<p>
|
||||
<a class="button gray small" href="/rowers/workout/{{ workout.id }}/workflow">Workflow View</a>
|
||||
</p>
|
||||
</div>
|
||||
<div class="grid_2">
|
||||
<p>
|
||||
<a class="button gray small" href="/rowers/workout/{{ workout.id }}/map">Map View</a>
|
||||
</p>
|
||||
|
||||
@@ -18,7 +18,7 @@
|
||||
</div>
|
||||
<div class="grid_2">
|
||||
<p>
|
||||
<a class="button gray small" href="/rowers/workout/{{ workout.id }}/export">Export</a>
|
||||
<a class="button gray small" href="/rowers/workout/{{ workout.id }}/workflow">Workflow View</a>
|
||||
</p>
|
||||
|
||||
</div>
|
||||
|
||||
@@ -118,7 +118,6 @@ def user_teams(user):
|
||||
return teams
|
||||
|
||||
|
||||
|
||||
@register.filter
|
||||
def has_teams(user):
|
||||
try:
|
||||
|
||||
+4
-2
@@ -191,7 +191,8 @@ urlpatterns = [
|
||||
url(r'^workout/compare/(?P<id>\d+)/$',views.workout_comparison_list),
|
||||
url(r'^workout/compare2/(?P<id1>\d+)/(?P<id2>\d+)/(?P<xparam>\w+.*)/(?P<yparam>\w+.*)/$',views.workout_comparison_view),
|
||||
url(r'^workout/compare/(?P<id>\d+)/(?P<startdatestring>\d+-\d+-\d+)/(?P<enddatestring>\w+.*)$',views.workout_comparison_list),
|
||||
url(r'^workout/(?P<id>\d+)/edit$',views.workout_edit_view),
|
||||
url(r'^workout/(?P<id>\d+)/edit$',views.workout_edit_view,
|
||||
name='workout_edit_view'),
|
||||
url(r'^workout/(?P<id>\d+)/navionics$',views.workout_edit_view_navionics),
|
||||
url(r'^workout/(?P<id>\d+)/map$',views.workout_map_view),
|
||||
url(r'^workout/(?P<id>\d+)/setprivate$',views.workout_setprivate_view),
|
||||
@@ -329,7 +330,8 @@ urlpatterns = [
|
||||
url(r'^legal', TemplateView.as_view(template_name='legal.html'),name='legal'),
|
||||
url(r'^register$',views.rower_register_view),
|
||||
url(r'^register/thankyou/$', TemplateView.as_view(template_name='registerthankyou.html'), name='registerthankyou'),
|
||||
url(r'^workout/(?P<id>\d+)/workflow$',views.workout_workflow_view),
|
||||
url(r'^workout/(?P<id>\d+)/workflow$',views.workout_workflow_view,
|
||||
name='workout_workflow_view'),
|
||||
url(r'^workout/(?P<id>\d+)/flexchart/(?P<xparam>\w+.*)/(?P<yparam1>\w+.*)/(?P<yparam2>\w+.*)/(?P<plottype>\w+)/$',views.workout_flexchart3_view),
|
||||
url(r'^workout/(?P<id>\d+)/flexchart/(?P<xparam>\w+.*)/(?P<yparam1>\w+.*)/(?P<yparam2>\w+.*)/(?P<plottype>\w+.*)$',views.workout_flexchart3_view),
|
||||
url(r'^workout/(?P<id>\d+)/flexchart/(?P<xparam>\w+.*)/(?P<yparam1>\w+.*)/(?P<yparam2>\w+.*)$',views.workout_flexchart3_view),
|
||||
|
||||
+18
-3
@@ -5,19 +5,33 @@ import colorsys
|
||||
|
||||
lbstoN = 4.44822
|
||||
|
||||
landingpages = (
|
||||
('workout_edit_view','Edit View'),
|
||||
('workout_workflow_view','Workflow View'),
|
||||
)
|
||||
|
||||
|
||||
workflowmiddlepanel = (
|
||||
('panel_statcharts.html','Static Charts'),
|
||||
('flexthumbnails.html','Flex Charts'),
|
||||
('panel_summary.html','Summary'),
|
||||
('panel_map.html','Map'),
|
||||
('panel_comments.html','Basic Info and Links'),
|
||||
('panel_middlesocial.html','Social Media Share Buttons'),
|
||||
)
|
||||
|
||||
defaultmiddle = ['panel_statcharts.html',
|
||||
defaultmiddle = ['panel_middlesocial.html',
|
||||
'panel_statcharts.html',
|
||||
'flexthumbnails.html',
|
||||
'panel_summary.html']
|
||||
'panel_summary.html',
|
||||
'panel_map.html']
|
||||
|
||||
workflowleftpanel = (
|
||||
('panel_navigationheader.html','Navigation Header'),
|
||||
('panel_editbuttons.html','Edit Workout Button'),
|
||||
('panel_delete.html','Delete Workout Button'),
|
||||
('panel_export.html','Export Workout Button'),
|
||||
('panel_social.html','Social Media Share Buttons'),
|
||||
('panel_advancededit.html','Advanced Workout Edit Button'),
|
||||
('panel_editintervals.html','Edit Intervals Button'),
|
||||
('panel_stats.html','Workout Statistics Button'),
|
||||
@@ -25,7 +39,8 @@ workflowleftpanel = (
|
||||
('panel_geekyheader.html','Geeky Header'),
|
||||
('panel_editwind.html','Edit Wind Data'),
|
||||
('panel_editstream.html','Edit Stream Data'),
|
||||
('panel_otwpower.html','Run OTW Power Calculations')
|
||||
('panel_otwpower.html','Run OTW Power Calculations'),
|
||||
('panel_mapview.html','Map'),
|
||||
)
|
||||
|
||||
defaultleft = [
|
||||
|
||||
+99
-20
@@ -12,6 +12,7 @@ from PIL import Image
|
||||
from numbers import Number
|
||||
from django.views.generic.base import TemplateView
|
||||
from django.db.models import Q
|
||||
from django import template
|
||||
from django.db import IntegrityError, transaction
|
||||
from django.shortcuts import render
|
||||
from django.http import (
|
||||
@@ -157,6 +158,27 @@ from interactiveplots import *
|
||||
# Define the API documentation
|
||||
schema_view = get_swagger_view(title='Rowsandall API')
|
||||
|
||||
# Test if row data include candidates
|
||||
def rowhascoordinates(row):
|
||||
# create interactive plot
|
||||
f1 = row.csvfilename
|
||||
u = row.user.user
|
||||
r = getrower(u)
|
||||
rowdata = rdata(f1)
|
||||
hascoordinates = 1
|
||||
if rowdata != 0:
|
||||
try:
|
||||
latitude = rowdata.df[' latitude']
|
||||
if not latitude.std():
|
||||
hascoordinates = 0
|
||||
except KeyError,AttributeError:
|
||||
hascoordinates = 0
|
||||
|
||||
else:
|
||||
hascoordinates = 0
|
||||
|
||||
return hascoordinates
|
||||
|
||||
# Custom error pages with Rowsandall headers
|
||||
def error500_view(request):
|
||||
response = render_to_response('500.html', {},
|
||||
@@ -4432,6 +4454,7 @@ def workouts_view(request,message='',successmessage='',
|
||||
|
||||
return render(request, 'list_workouts.html',
|
||||
{'workouts': workouts,
|
||||
'rower':r,
|
||||
'dateform':dateform,
|
||||
'startdate':startdate,
|
||||
'enddate':enddate,
|
||||
@@ -5231,7 +5254,11 @@ def workout_otwsetpower_view(request,id=0,message="",successmessage=""):
|
||||
kwargs = {
|
||||
'id':int(id)}
|
||||
|
||||
try:
|
||||
url = request.session['referer']
|
||||
except KeyError:
|
||||
url = reverse(workout_advanced_view,kwargs=kwargs)
|
||||
|
||||
response = HttpResponseRedirect(url)
|
||||
return response
|
||||
|
||||
@@ -6006,15 +6033,41 @@ def workout_workflow_view(request,id):
|
||||
|
||||
charts = []
|
||||
|
||||
if favorites:
|
||||
if favorites and 'flexthumbnails.html' in r.workflowmiddlepanel:
|
||||
charts = thumbnails_set(r,id,favorites)
|
||||
if charts[0]['script'] == '':
|
||||
charts = []
|
||||
|
||||
if 'panel_map.html' in r.workflowmiddlepanel and rowhascoordinates(row):
|
||||
rowdata = rdata(row.csvfilename)
|
||||
mapscript,mapdiv = leaflet_chart2(rowdata.df[' latitude'],
|
||||
rowdata.df[' longitude'],
|
||||
row.name)
|
||||
else:
|
||||
mapscript = ''
|
||||
mapdiv = ''
|
||||
|
||||
|
||||
|
||||
statcharts = GraphImage.objects.filter(workout=row)
|
||||
|
||||
# This will be user configurable in the future
|
||||
middleTemplates = r.workflowmiddlepanel
|
||||
|
||||
leftTemplates = r.workflowleftpanel
|
||||
middleTemplates = []
|
||||
for t in r.workflowmiddlepanel:
|
||||
try:
|
||||
template.loader.get_template(t)
|
||||
middleTemplates.append(t)
|
||||
except template.TemplateDoesNotExist:
|
||||
pass
|
||||
|
||||
leftTemplates = []
|
||||
for t in r.workflowleftpanel:
|
||||
try:
|
||||
template.loader.get_template(t)
|
||||
leftTemplates.append(t)
|
||||
except template.TemplateDoesNotExist:
|
||||
pass
|
||||
|
||||
|
||||
return render(request,
|
||||
'workflow.html',
|
||||
@@ -6023,6 +6076,8 @@ def workout_workflow_view(request,id):
|
||||
'leftTemplates':leftTemplates,
|
||||
'charts':charts,
|
||||
'workout':row,
|
||||
'mapscript':mapscript,
|
||||
'mapdiv':mapdiv,
|
||||
'statcharts':statcharts,
|
||||
})
|
||||
|
||||
@@ -6142,6 +6197,19 @@ def workout_flexchart3_view(request,*args,**kwargs):
|
||||
else:
|
||||
workstrokesonly = False
|
||||
|
||||
if not promember:
|
||||
for name,d in rowingmetrics:
|
||||
if d['type'] != 'basic':
|
||||
if xparam == name:
|
||||
xparam = 'time'
|
||||
messages.info(request,'To use '+d['verbose_name']+', you have to be Pro member')
|
||||
if yparam1 == name:
|
||||
yparam1 = 'pace'
|
||||
messages.info(request,'To use '+d['verbose_name']+', you have to be Pro member')
|
||||
if yparam2 == name:
|
||||
yparam2 = 'spm'
|
||||
messages.info(request,'To use '+d['verbose_name']+', you have to be Pro member')
|
||||
|
||||
# create interactive plot
|
||||
try:
|
||||
script,div,js_resources,css_resources,workstrokesonly = interactive_flex_chart2(id,xparam=xparam,yparam1=yparam1,
|
||||
@@ -7533,13 +7601,17 @@ def workout_upload_view(request,
|
||||
'make_plot':False,
|
||||
'upload_to_C2':False,
|
||||
'plottype':'timeplot',
|
||||
'landingpage':'workout_edit_view',
|
||||
},
|
||||
docformoptions={
|
||||
'workouttype':'rower',
|
||||
}):
|
||||
|
||||
r = getrower(request.user)
|
||||
|
||||
if 'uploadoptions' in request.session:
|
||||
uploadoptions = request.session['uploadoptions']
|
||||
uploadoptions['landingpage'] = r.defaultlandingpage
|
||||
else:
|
||||
request.session['uploadoptions'] = uploadoptions
|
||||
|
||||
@@ -7568,36 +7640,40 @@ def workout_upload_view(request,
|
||||
plottype = 'timeplot'
|
||||
|
||||
try:
|
||||
upload_toc2 = uploadoptions['upload_to_C2']
|
||||
landingpage = uploadoptions['landingpage']
|
||||
except KeyError:
|
||||
upload_toc2 = False
|
||||
landingpage = r.defaultlandingpage
|
||||
|
||||
try:
|
||||
upload_tostrava = uploadoptions['upload_to_Strava']
|
||||
upload_to_c2 = uploadoptions['upload_to_C2']
|
||||
except KeyError:
|
||||
upload_tostrava = False
|
||||
upload_to_c2 = False
|
||||
|
||||
try:
|
||||
upload_tost = uploadoptions['upload_to_SportTracks']
|
||||
upload_to_strava = uploadoptions['upload_to_Strava']
|
||||
except KeyError:
|
||||
upload_tost = False
|
||||
upload_to_strava = False
|
||||
|
||||
try:
|
||||
upload_tork = uploadoptions['upload_to_RunKeeper']
|
||||
upload_to_st = uploadoptions['upload_to_SportTracks']
|
||||
except KeyError:
|
||||
upload_tork = False
|
||||
upload_to_st = False
|
||||
|
||||
try:
|
||||
upload_toua = uploadoptions['upload_to_MapMyFitness']
|
||||
upload_to_rk = uploadoptions['upload_to_RunKeeper']
|
||||
except KeyError:
|
||||
upload_toua = False
|
||||
upload_to_rk = False
|
||||
|
||||
try:
|
||||
upload_totp = uploadoptions['upload_to_TrainingPeaks']
|
||||
upload_to_ua = uploadoptions['upload_to_MapMyFitness']
|
||||
except KeyError:
|
||||
upload_totp = False
|
||||
upload_to_ua = False
|
||||
|
||||
try:
|
||||
upload_to_tp = uploadoptions['upload_to_TrainingPeaks']
|
||||
except KeyError:
|
||||
upload_to_tp = False
|
||||
|
||||
r = getrower(request.user)
|
||||
if request.method == 'POST':
|
||||
form = DocumentsForm(request.POST,request.FILES)
|
||||
optionsform = UploadOptionsForm(request.POST)
|
||||
@@ -7622,6 +7698,7 @@ def workout_upload_view(request,
|
||||
upload_to_ua = optionsform.cleaned_data['upload_to_MapMyFitness']
|
||||
upload_to_tp = optionsform.cleaned_data['upload_to_TrainingPeaks']
|
||||
makeprivate = optionsform.cleaned_data['makeprivate']
|
||||
landingpage = optionsform.cleaned_data['landingpage']
|
||||
|
||||
uploadoptions = {
|
||||
'makeprivate':makeprivate,
|
||||
@@ -7633,6 +7710,7 @@ def workout_upload_view(request,
|
||||
'upload_to_RunKeeper':upload_to_rk,
|
||||
'upload_to_MapMyFitness':upload_to_ua,
|
||||
'upload_to_TrainingPeaks':upload_to_tp,
|
||||
'landingpage':r.defaultlandingpage,
|
||||
}
|
||||
|
||||
|
||||
@@ -7756,7 +7834,7 @@ def workout_upload_view(request,
|
||||
else:
|
||||
messages.error(request,message)
|
||||
|
||||
url = reverse(workout_edit_view,
|
||||
url = reverse(landingpage,
|
||||
kwargs = {
|
||||
'id':w.id,
|
||||
})
|
||||
@@ -8474,10 +8552,10 @@ def rower_favoritecharts_view(request):
|
||||
|
||||
if request.method == 'POST':
|
||||
favorites_formset = FavoriteChartFormSet(request.POST)
|
||||
|
||||
if favorites_formset.is_valid():
|
||||
new_instances = []
|
||||
for favorites_form in favorites_formset:
|
||||
print 'mies'
|
||||
yparam1 = favorites_form.cleaned_data.get('yparam1')
|
||||
yparam2 = favorites_form.cleaned_data.get('yparam2')
|
||||
xparam = favorites_form.cleaned_data.get('xparam')
|
||||
@@ -8503,7 +8581,6 @@ def rower_favoritecharts_view(request):
|
||||
except IntegrityError:
|
||||
message = "something went wrong"
|
||||
messages.error(request,message)
|
||||
|
||||
else:
|
||||
favorites_formset = FavoriteChartFormSet(initial=favorites_data)
|
||||
|
||||
@@ -8724,6 +8801,7 @@ def rower_edit_view(request,message=""):
|
||||
first_name = ucd['first_name']
|
||||
last_name = ucd['last_name']
|
||||
email = ucd['email']
|
||||
defaultlandingpage = cd['defaultlandingpage']
|
||||
weightcategory = cd['weightcategory']
|
||||
getemailnotifications = cd['getemailnotifications']
|
||||
defaulttimezone=cd['defaulttimezone']
|
||||
@@ -8740,6 +8818,7 @@ def rower_edit_view(request,message=""):
|
||||
r.defaulttimezone=defaulttimezone
|
||||
r.weightcategory = weightcategory
|
||||
r.getemailnotifications = getemailnotifications
|
||||
r.defaultlandingpage = defaultlandingpage
|
||||
r.save()
|
||||
form = RowerForm(instance=r)
|
||||
powerform = RowerPowerForm(instance=r)
|
||||
|
||||
@@ -261,6 +261,9 @@ TP_CLIENT_SECRET = CFG["tp_client_secret"]
|
||||
TP_REDIRECT_URI = CFG["tp_redirect_uri"]
|
||||
TP_CLIENT_KEY = TP_CLIENT_ID
|
||||
|
||||
# Full Site URL
|
||||
SITE_URL = "https://rowsandall.com"
|
||||
|
||||
# RQ stuff
|
||||
|
||||
RQ_QUEUES = {
|
||||
|
||||
@@ -76,6 +76,8 @@ LOGIN_REDIRECT_URL = '/rowers/list-workouts/'
|
||||
|
||||
SESSION_ENGINE = "django.contrib.sessions.backends.signed_cookies"
|
||||
|
||||
SITE_URL = "http://localhost:8000"
|
||||
|
||||
#EMAIL_BACKEND = 'django.core.mail.backends.console.EmailBackend'
|
||||
|
||||
#EMAIL_BACKEND = 'django.core.mail.backends.dummy.EmailBackend'
|
||||
|
||||
Reference in New Issue
Block a user