Private
Public Access
1
0
Files
rowsandall/rowers/tasks.py
Sander Roosendaal 6d6d269ce9 emails with site url
2018-12-02 20:37:02 +01:00

1840 lines
48 KiB
Python

""" Background tasks done by Celery (develop) or QR (production) """
import os
import time
import gc
import gzip
import shutil
import numpy as np
import re
from scipy import optimize
from scipy.signal import savgol_filter
import rowingdata
from rowingdata import rowingdata as rdata
from datetime import timedelta
from sqlalchemy import create_engine
from celery import app
import datetime
import pytz
import iso8601
from matplotlib.backends.backend_agg import FigureCanvas
#from matplotlib.backends.backend_cairo import FigureCanvasCairo as FigureCanvas
import matplotlib.pyplot as plt
from matplotlib import path
from rowsandall_app.settings import SITE_URL
from rowsandall_app.settings_dev import SITE_URL as SITE_URL_DEV
from rowsandall_app.settings import PROGRESS_CACHE_SECRET
from rowsandall_app.settings import SETTINGS_NAME
from rowsandall_app.settings import workoutemailbox
import pandas as pd
from django_rq import job
from django.utils import timezone
from django.utils.html import strip_tags
from utils import deserialize_list,ewmovingaverage,wavg
from HTMLParser import HTMLParser
class MLStripper(HTMLParser):
def __init__(self):
self.reset()
self.fed = []
def handle_data(self, d):
self.fed.append(d)
def get_data(self):
return ''.join(self.fed)
def strip_tags(html):
s = MLStripper()
s.feed(html)
return s.get_data()
from rowers.dataprepnodjango import (
update_strokedata, new_workout_from_file,
getsmallrowdata_db, updatecpdata_sql,
update_agegroup_db,fitnessmetric_to_sql,
add_c2_stroke_data_db,totaltime_sec_to_string,
create_c2_stroke_data_db,update_empower,
database_url_debug,database_url,dataprep,
# create_strava_stroke_data_db
)
from django.core.mail import (
send_mail,
EmailMessage,EmailMultiAlternatives,
)
from django.template import Context
from django.db.utils import OperationalError
from jinja2 import Template,Environment,FileSystemLoader
env = Environment(loader = FileSystemLoader(["rowers/templates"]))
import datautils
import utils
import requests
import longtask
import arrow
from utils import get_strava_stream
siteurl = SITE_URL
# testing task
from rowers.emails import send_template_email
from rowers.courseutils import coursetime_paths, coursetime_first, time_in_path
@app.task
def add(x, y):
return x + y
@app.task
def handle_c2_import_stroke_data(c2token,
c2id,workoutid,
starttimeunix,
csvfilename,debug=True,**kwargs):
if 'workouttype' in kwargs:
workouttype = kwargs['workouttype']
else:
workouttype = 'rower'
authorizationstring = str('Bearer ' + c2token)
headers = {'Authorization': authorizationstring,
'user-agent': 'sanderroosendaal',
'Content-Type': 'application/json'}
url = "https://log.concept2.com/api/users/me/results/"+str(c2id)+"/strokes"
s = requests.get(url,headers=headers)
if s.status_code == 200:
strokedata = pd.DataFrame.from_dict(s.json()['data'])
result = add_c2_stroke_data_db(
strokedata,workoutid,starttimeunix,
csvfilename,debug=debug,workouttype=workouttype
)
return 1
else:
url = "https://log.concept2.com/api/users/me/results/"+str(c2id)
s = requests.get(url,headers=headers)
if s.status_code == 200:
workoutdata = s.json()['data']
distance = workoutdata['distance']
c2id = workoutdata['id']
workouttype = workoutdata['type']
verified = workoutdata['verified']
startdatetime = iso8601.parse_date(workoutdata['date'])
weightclass = workoutdata['weight_class']
weightcategory = 'hwt'
if weightclass == "L":
weightcategory = 'lwt'
totaltime = workoutdata['time']/10.
duration = totaltime_sec_to_string(totaltime)
duration = datetime.datetime.strptime(duration,'%H:%M:%S.%f').time()
try:
timezone_str = tz(workoutdata['timezone'])
except:
timezone_str = 'UTC'
workoutdate = startdatetime.astimezone(
pytz.timezone(timezone_str)
).strftime('%Y-%m-%d')
starttime = startdatetime.astimezone(
pytz.timezone(timezone_str)
).strftime('%H:%M:%S')
result = create_c2_stroke_data_db(
distance,duration,workouttype,
workoutid,starttimeunix,
csvfilename,debug=debug,
)
return 1
return 0
return 0
def getagegrouprecord(age,sex='male',weightcategory='hwt',
distance=2000,duration=None,indf=pd.DataFrame()):
if not duration:
df = indf[indf['distance'] == distance]
else:
duration = 60*int(duration)
df = indf[indf['duration'] == duration]
if not df.empty:
ages = df['age']
powers = df['power']
#poly_coefficients = np.polyfit(ages,powers,6)
fitfunc = lambda pars, x: np.abs(pars[0])*(1-x/max(120,pars[1]))-np.abs(pars[2])*np.exp(-x/np.abs(pars[3]))+np.abs(pars[4])*(np.sin(np.pi*x/max(50,pars[5])))
errfunc = lambda pars, x,y: fitfunc(pars,x)-y
p0 = [700,120,700,10,100,100]
p1, success = optimize.leastsq(errfunc,p0[:],
args = (ages,powers))
if success:
power = fitfunc(p1, float(age))
#power = np.polyval(poly_coefficients,age)
power = 0.5*(np.abs(power)+power)
else:
power = 0
else:
power = 0
return power
def polygon_to_path(polygon,debug=True):
pid = polygon[0]
query = "SELECT id, latitude, longitude FROM rowers_geopoint WHERE polygon_id = {pid} ORDER BY order_in_poly ASC".format(
pid=pid
)
if debug:
engine = create_engine(database_url_debug, echo=False)
else:
engine = create_engine(database_url, echo=False)
with engine.connect() as conn, conn.begin():
result = conn.execute(query)
points = result.fetchall()
conn.close()
engine.dispose()
s = []
for point in points:
s.append([point[1],point[2]])
p = path.Path(s[:-1])
return p
@app.task(bind=True)
def handle_check_race_course(self,
f1,workoutid,courseid,
recordid,**kwargs):
if 'debug' in kwargs:
debug = kwargs['debug']
else:
debug = False
if 'splitsecond' in kwargs:
splitsecond = kwargs['splitsecond']
else:
splitsecond = 0
mode = 'race'
if 'mode' in kwargs:
mode = kwargs['mode']
columns = ['time',' latitude',' longitude','cum_dist']
try:
row = rdata(csvfile=f1)
except IOError:
try:
row = rdata(f1 + '.csv')
except IOError:
try:
row = rdata(f1 + '.gz')
except IOError:
return 0
rowdata = row.df
try:
s = rowdata[' latitude']
except KeyError:
return 0
rowdata.rename(columns = {
' latitude':'latitude',
' longitude':'longitude',
'TimeStamp (sec)': 'time',
}, inplace=True)
rowdata.fillna(method='backfill',inplace=True)
rowdata['time'] = rowdata['time']-rowdata.ix[0,'time']
rowdata = rowdata[rowdata['time']>splitsecond]
# 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()
# initiate database engine
if debug:
engine = create_engine(database_url_debug, echo=False)
else:
engine = create_engine(database_url, echo=False)
# get polygons
query = "SELECT id FROM rowers_geopolygon WHERE course_id = {courseid} ORDER BY order_in_course ASC".format(
courseid=courseid
)
with engine.connect() as conn, conn.begin():
result = conn.execute(query)
polygons = result.fetchall()
conn.close()
engine.dispose()
paths = []
for polygon in polygons:
path = polygon_to_path(polygon,debug=debug)
paths.append(path)
# check how many times went through start polygon
entrytimes,entrydistances = time_in_path(rowdata,paths[0],maxmin='max',getall=True)
cseconds = []
cmeters = []
ccomplete = []
for startt in entrytimes:
rowdata2 = rowdata[rowdata['time']>(startt-10.)]
(
coursetimeseconds,
coursemeters,
coursecompleted,
) = coursetime_paths(rowdata2,paths)
(
coursetimefirst,
coursemetersfirst,
firstcompleted
) = coursetime_first(
rowdata2,paths)
coursetimeseconds = coursetimeseconds-coursetimefirst
coursemeters = coursemeters-coursemetersfirst
cseconds.append(coursetimeseconds)
cmeters.append(coursemeters)
ccomplete.append(coursecompleted)
records = pd.DataFrame({
'coursetimeseconds':cseconds,
'coursecompleted': ccomplete,
'coursemeters': cmeters
})
records = records[records['coursecompleted'] == True]
if len(records):
coursecompleted = True
mintime = records['coursetimeseconds'].min()
coursetimeseconds = records[records['coursetimeseconds'] == mintime]['coursetimeseconds'].min()
coursemeters = records[records['coursetimeseconds'] == mintime]['coursemeters'].min()
else:
coursecompleted = False
if coursecompleted:
query = 'UPDATE rowers_virtualraceresult SET coursecompleted = 1, duration = "{duration}", distance = {distance}, workoutid = {workoutid} WHERE id={recordid}'.format(
recordid=recordid,
duration=totaltime_sec_to_string(coursetimeseconds),
distance=int(coursemeters),
workoutid=workoutid,
)
if mode == 'coursetest':
query = 'UPDATE rowers_coursetestresult SET coursecompleted = 1, duration = "{duration}", distance = {distance}, workoutid = {workoutid} WHERE id={recordid}'.format(
recordid=recordid,
duration=totaltime_sec_to_string(coursetimeseconds),
distance=int(coursemeters),
workoutid=workoutid,
)
with engine.connect() as conn, conn.begin():
result = conn.execute(query)
conn.close()
engine.dispose()
return 1
else:
return 2
return 0
@app.task(bind=True)
def handle_getagegrouprecords(self,
df,
distances,durations,
age,sex,weightcategory,
**kwargs):
wcdurations = []
wcpower = []
if 'debug' in kwargs:
debug = kwargs['debug']
else:
debug = False
df = pd.read_json(df)
if sex == 'not specified':
return 0
for distance in distances:
worldclasspower = getagegrouprecord(
age,
sex=sex,
distance=distance,
weightcategory=weightcategory,indf=df,
)
velo = (worldclasspower/2.8)**(1./3.)
if not np.isinf(worldclasspower) and not np.isnan(worldclasspower):
try:
duration = distance/velo
wcdurations.append(duration)
wcpower.append(worldclasspower)
except ZeroDivisionError:
pass
for duration in durations:
worldclasspower = getagegrouprecord(
age,
sex=sex,
duration=duration,
weightcategory=weightcategory,indf=df
)
if not np.isinf(worldclasspower) and not np.isnan(worldclasspower):
try:
velo = (worldclasspower/2.8)**(1./3.)
distance = int(60*duration*velo)
wcdurations.append(60.*duration)
wcpower.append(worldclasspower)
except ValueError:
pass
update_agegroup_db(age,sex,weightcategory,wcdurations,wcpower,
debug=debug)
return 1
@app.task(bind=True)
def long_test_task(self,aantal,debug=False,job=None,session_key=None):
job = self.request
return longtask.longtask(aantal,jobid=job.id,debug=debug,
session_key=session_key)
@app.task(bind=True)
def long_test_task2(self,aantal,**kwargs):
#debug=False,job=None,jobid='aap'):
job = self.request
job_id = job.id
if 'jobkey' in kwargs:
job_id = kwargs.pop('jobkey')
kwargs['jobid'] = job_id
return longtask.longtask2(aantal,**kwargs)
# create workout
@app.task
def handle_new_workout_from_file(r, f2,
workouttype='rower',
boattype='1x',
makeprivate=False,
notes='',debug=False):
return new_workout_from_file(r, f2, workouttype,
title, makeprivate, notes)
# process and update workouts
@app.task(bind=True)
def handle_update_empower(self,
useremail,
workoutdicts,
debug=False, **kwargs):
job = self.request
job_id = job.id
if 'jobkey' in kwargs:
job_id = kwargs.pop('jobkey')
aantal = len(workoutdicts)
counter = 0
for workoutdict in workoutdicts:
wid = workoutdict['id']
inboard = workoutdict['inboard']
oarlength = workoutdict['oarlength']
boattype = workoutdict['boattype']
f1 = workoutdict['filename']
# oarlength consistency checks will be done in view
havedata = 1
try:
rowdata = rdata(f1)
except IOError:
try:
rowdata = rdata(f1 + '.csv')
except IOError:
try:
rowdata = rdata(f1 + '.gz')
except IOError:
havedata = 0
progressurl = SITE_URL
siteurl = SITE_URL
if debug:
progressurl = SITE_URL_DEV
siteurl = SITE_URL_DEV
secret = PROGRESS_CACHE_SECRET
kwargs['job_id'] = job_id
progressurl += "/rowers/record-progress/"
progressurl += job_id
if havedata:
success = update_empower(wid, inboard, oarlength, boattype,
rowdata.df, f1, debug=debug)
counter += 1
progress = 100.*float(counter)/float(aantal)
post_data = {
"secret":secret,
"value":progress,
}
s = requests.post(progressurl, data=post_data)
status_code = s.status_code
subject = "Rowsandall.com Your Old Empower Oarlock data have been corrected"
message = """
We have updated Power and Work per Stroke data according to the instructions by Nielsen-Kellerman.
"""
email = EmailMessage(subject, message,
'Rowsandall <info@rowsandall.com>',
[useremail])
if 'emailbounced' in kwargs:
emailbounced = kwargs['emailbounced']
else:
emailbounced = False
if not emailbounced:
res = email.send()
return 1
@app.task
def handle_calctrimp(id,
csvfilename,
ftp,
sex,
hrftp,
hrmax,
hrmin,
debug=False, **kwargs):
if debug:
engine = create_engine(database_url_debug, echo=False)
else:
engine = create_engine(database_url, echo=False)
try:
rowdata = rdata(csvfilename)
except IOError:
try:
rowdata = rdata(csvfilename + '.csv')
except IOError:
try:
rowdata = rdata(csvfilename + '.gz')
except IOError:
return 0
df = rowdata.df
try:
df['deltat'] = df[' ElapsedTime (sec)'].diff().abs()
except KeyError:
return 0
df2 = df.copy()
df2['time'] = df2[' ElapsedTime (sec)']
df2['time'] = df2['time'].apply(
lambda x:timedelta(seconds=x)
)
duration = df['TimeStamp (sec)'].max()-df['TimeStamp (sec)'].min()
df2 = df2.resample('1s',on='time').mean()
df2.fillna(method='ffill',inplace=True)
df2 = df2.rolling(30).mean()
df2[' Power (watts)'] = df2[' Power (watts)'].abs()
df2['pwr4'] = df2[' Power (watts)']**(4.0)
# pwr4mean = wavg(df,'pwr4','deltat')
pwr4mean = df2['pwr4'].mean()
pwrmean = df2[' Power (watts)'].mean()
# pwrmean = wavg(df,' Power (watts)','deltat')
if pwr4mean > 0:
normp = (pwr4mean)**(0.25)
else:
normp = pwrmean
intensityfactor = normp/float(ftp)
tss = 100.*((duration*normp*intensityfactor)/(3600.*ftp))
if sex == 'male':
f = 1.92
else:
f = 1.67
dt = df['TimeStamp (sec)'].diff()/60.
hrr = (df[' HRCur (bpm)']-hrmin)/(hrmax-hrmin)
hrrftp = (hrftp-hrmin)/float(hrmax-hrmin)
trimp1hr = 60.*hrrftp*0.64*np.exp(f*hrrftp)
trimpdata = dt*hrr*0.64*np.exp(f*hrr)
trimp = trimpdata.sum()
hrtss = 100.*trimp/trimp1hr
pp = 8.0
df['v4'] = df[' AverageBoatSpeed (m/s)']**(pp)
v4mean = wavg(df,'v4','deltat')
normv = v4mean**(1./pp)
try:
df['w4'] = df['driveenergy']**(pp)
w4mean = wavg(df,'w4','deltat')
normw = w4mean**(1./pp)
except KeyError:
normw = 0
if not np.isfinite(normv):
normv = 500./120.
if not np.isfinite(normw):
normw = 0
try:
dum = int(tss)
except ValueError:
tss = 0
try:
dum = int(normp)
except ValueError:
normp = 0
try:
dump = int(trimp)
except ValueError:
trimp = 0
try:
dump = int(hrtss)
except ValueError:
hrtss = 0
query = 'UPDATE rowers_workout SET rscore = {tss}, normp = {normp}, trimp={trimp}, hrtss={hrtss}, normv={normv}, normw={normw} WHERE id={id}'.format(
tss = int(tss),
normp = int(normp),
trimp = int(trimp),
hrtss = int(hrtss),
normv=normv,
normw=normw,
id = id,
)
with engine.connect() as conn, conn.begin():
result = conn.execute(query)
conn.close()
engine.dispose()
return 1
@app.task
def handle_updatedps(useremail, workoutids, debug=False,**kwargs):
for wid, f1 in workoutids:
havedata = 1
try:
rowdata = rdata(f1)
except IOError:
try:
rowdata = rdata(f1 + '.csv')
except IOError:
try:
rowdata = rdata(f1 + '.gz')
except IOError:
havedata = 0
if havedata:
update_strokedata(wid, rowdata.df, debug=debug)
subject = "Rowsandall.com Your Distance per Stroke metric has been updated"
message = "All your workouts now have Distance per Stroke"
email = EmailMessage(subject, message,
'Rowsandall <info@rowsandall.com>',
[useremail])
if 'emailbounced' in kwargs:
emailbounced = kwargs['emailbounced']
else:
emailbounced = False
if not emailbounced:
res = email.send()
return 1
# send email when a breakthrough workout is uploaded
@app.task
def handle_sendemail_raceregistration(
useremail, username, registeredname, racename, raceid, **kwargs):
if 'debug' in kwargs:
debug = kwargs['debug']
else:
debug = True
subject = "A new competitor has registered for virtual race {n}".format(
n = racename
)
from_email = 'Rowsandall <info@rowsandall.com>'
d = {
'username':username,
'registeredname':registeredname,
'siteurl':siteurl,
'racename':racename,
'raceid':raceid,
}
res = send_template_email(from_email,[useremail],
subject,
'raceregisteredemail.html',
d,**kwargs)
return 1
@app.task
def handle_sendemail_racesubmission(
useremail, username, registeredname, racename, raceid, **kwargs):
if 'debug' in kwargs:
debug = kwargs['debug']
else:
debug = True
subject = "A new result has been submitted for virtual race {n}".format(
n = racename
)
from_email = 'Rowsandall <info@rowsandall.com>'
d = {
'username':username,
'siteurl':siteurl,
'registeredname':registeredname,
'racename':racename,
'raceid':raceid,
}
res = send_template_email(from_email,[useremail],
subject,
'racesubmissionemail.html',
d,**kwargs)
return 1
@app.task
def handle_send_disqualification_email(
useremail,username,reason,message, racename, **kwargs):
if 'debug' in kwargs:
debug = kwargs['debug']
else:
debug = True
subject = "Your result for {n} has been disqualified on rowsandall.com".format(
n = racename
)
from_email = 'Rowsandall <support@rowsandall.com>'
d = {
'username':username,
'reason':reason,
'siteurl':siteurl,
'message': strip_tags(message),
'racename':racename,
}
res = send_template_email(from_email,[useremail],
subject,
'disqualificationemail.html',
d,**kwargs)
return 1
@app.task
def handle_sendemail_expired(useremail,userfirstname,userlastname,expireddate,
**kwargs):
if 'debug' in kwargs:
debug = kwargs['debug']
else:
debug = False
subject = "Your rowsandall.com paid account has expired"
from_email = 'Rowsandall <info@rowsandall.com>'
d = {
'first_name':userfirstname,
'last_name':userlastname,
'siteurl':siteurl,
'expireddate':expireddate,
}
res = send_template_email(from_email,[useremail],
subject,'accountexpiredemail.html',
d,cc=['support@rowsandall.com'],**kwargs)
return 1
@app.task
def handle_sendemail_breakthrough(workoutid, useremail,
userfirstname, userlastname,
btvalues=pd.DataFrame().to_json(),
**kwargs):
if 'debug' in kwargs:
debug = kwargs['debug']
else:
debug = False
btvalues = pd.read_json(btvalues)
btvalues.sort_values('delta', axis=0, inplace=True)
tablevalues = [
{'delta': t.delta,
'cpvalue': t.cpvalues,
'pwr': t.pwr
} for t in btvalues.itertuples()
]
# send email with attachment
subject = "A breakthrough workout on rowsandall.com"
from_email = 'Rowsandall <info@rowsandall.com>'
d = {
'first_name':userfirstname,
'siteurl':siteurl,
'workoutid':workoutid,
'btvalues':tablevalues,
}
res = send_template_email(from_email,[useremail],
subject,'breakthroughemail.html',
d,**kwargs)
return 1
# send email when a breakthrough workout is uploaded
@app.task
def handle_sendemail_hard(workoutid, useremail,
userfirstname, userlastname,
btvalues=pd.DataFrame().to_json(),
debug=False,**kwargs):
if 'debug' in kwargs:
debug = kwargs['debug']
else:
debug = False
btvalues = pd.read_json(btvalues)
btvalues.sort_values('delta', axis=0, inplace=True)
tablevalues = [
{'delta': t.delta,
'cpvalue': t.cpvalues,
'pwr': t.pwr
} for t in btvalues.itertuples()
]
# send email with attachment
subject = "That was a pretty hard workout on rowsandall.com"
from_email = 'Rowsandall <info@rowsandall.com>'
d = {
'first_name':userfirstname,
'siteurl':siteurl,
'workoutid':workoutid,
'btvalues':tablevalues,
}
res = send_template_email(from_email,[useremail],
subject,'hardemail.html',d,**kwargs)
return 1
# send email when user deletes account
@app.task
def handle_sendemail_userdeleted(name, email, debug=False, **kwargs):
fullemail = 'roosendaalsander@gmail.com'
subject = 'User account deleted'
message = 'Sander,\n\n'
message += 'The user {name} ({email}) has just deleted his account'.format(
name=name,
email=email
)
email = EmailMessage(subject,message,
'Rowsandall <info@rowsandall.com>',
[fullemail])
if 'emailbounced' in kwargs:
emailbounced = kwargs['emailbounced']
else:
emailbounced = False
if not emailbounced:
res = email.send()
return 1
# send email to me when an unrecognized file is uploaded
@app.task
def handle_sendemail_unrecognized(unrecognizedfile, useremail,
debug=False,**kwargs):
# send email with attachment
fullemail = 'roosendaalsander@gmail.com'
subject = "Unrecognized file from Rowsandall.com"
message = "Dear Sander,\n\n"
message += "Please find attached a file that someone tried to upload to rowsandall.com. The file was not recognized as a valid file type.\n\n"
message += "User Email " + useremail + "\n\n"
message += "Best Regards, the Rowsandall Team"
email = EmailMessage(subject, message,
'Rowsandall <info@rowsandall.com>',
[fullemail])
try:
email.attach_file(unrecognizedfile)
except IOError:
pass
if 'emailbounced' in kwargs:
emailbounced = kwargs['emailbounced']
else:
emailbounced = False
if not emailbounced:
res = email.send()
# remove tcx file
try:
os.remove(unrecognizedfile)
except:
pass
return 1
# send email to owner when an unrecognized file is uploaded
@app.task
def handle_sendemail_unrecognizedowner(useremail, userfirstname,
debug=False,**kwargs):
# send email with attachment
fullemail = useremail
subject = "Unrecognized file from Rowsandall.com"
d = {
'first_name':userfirstname,
'siteurl':siteurl,
}
from_email = 'Rowsandall <info@rowsandall.com>'
res = send_template_email(from_email,[fullemail],
subject,'unrecognizedemail.html',d,
**kwargs)
return 1
@app.task
def handle_sendemailics(first_name, last_name, email, icsfile, **kwargs):
# send email with attachment
fullemail = first_name + " " + last_name + " " + "<" + email + ">"
subject = "Calendar File from Rowsandall.com"
d = {'first_name':first_name,
'siteurl':siteurl,
}
from_email = 'Rowsandall <info@rowsandall.com>'
res = send_template_email(from_email,[fullemail],
subject,'icsemail.html',d,
attach_file=icsfile,**kwargs)
os.remove(icsfile)
return 1
@app.task
def handle_sendemailkml(first_name, last_name, email, kmlfile,**kwargs):
# send email with attachment
fullemail = first_name + " " + last_name + " " + "<" + email + ">"
subject = "File from Rowsandall.com"
d = {'first_name':first_name,
'siteurl':siteurl,
}
from_email = 'Rowsandall <info@rowsandall.com>'
res = send_template_email(from_email,[fullemail],
subject,'kmlemail.html',d,
attach_file=kmlfile,**kwargs)
os.remove(kmlfile)
return 1
# Send email with TCX attachment
@app.task
def handle_sendemailtcx(first_name, last_name, email, tcxfile,**kwargs):
# send email with attachment
fullemail = first_name + " " + last_name + " " + "<" + email + ">"
subject = "File from Rowsandall.com"
d = {'first_name':first_name,
'siteurl':siteurl,
}
from_email = 'Rowsandall <info@rowsandall.com>'
res = send_template_email(from_email,[fullemail],
subject,'tcxemail.html',d,
attach_file=tcxfile,**kwargs)
os.remove(tcxfile)
return 1
@app.task
def handle_zip_file(emailfrom, subject, file,**kwargs):
message = "... zip processing ... "
try:
debug = kwargs['debug']
except KeyError:
debug = False
if debug:
print message
email = EmailMessage(subject, message,
emailfrom,
['workouts@rowsandall.com'])
email.attach_file(file)
if debug:
print "attaching"
res = email.send()
if debug:
print "sent"
time.sleep(60)
return 1
# Send email with CSV attachment
@app.task
def handle_sendemailsummary(first_name, last_name, email, csvfile, **kwargs):
fullemail = first_name + " " + last_name + " " + "<" + email + ">"
subject = "File from Rowsandall.com"
d = {'first_name':first_name,
'siteurl':siteurl,
}
from_email = 'Rowsandall <info@rowsandall.com>'
res = send_template_email(from_email,[fullemail],
subject,'summarymail.html',d,
attach_file=csvfile,
**kwargs)
try:
os.remove(csvfile)
except:
pass
return 1
#from rowers.emails import sendemail
@app.task
def handle_sendemailcsv(first_name, last_name, email, csvfile,**kwargs):
# send email with attachment
fullemail = first_name + " " + last_name + " " + "<" + email + ">"
subject = "File from Rowsandall.com"
d = {'first_name':first_name,
'siteurl':siteurl,
}
from_email = 'Rowsandall <info@rowsandall.com>'
res = send_template_email(from_email,[fullemail],
subject,'csvemail.html',d,
attach_file=csvfile,**kwargs)
return 1
@app.task
def handle_sendemailfile(first_name, last_name, email, csvfile,**kwargs):
# send email with attachment
fullemail = first_name + " " + last_name + " " + "<" + email + ">"
subject = "File from Rowsandall.com"
d = {'first_name':first_name,
'siteurl':siteurl,
}
from_email = 'Rowsandall <info@rowsandall.com>'
res = send_template_email(from_email,[fullemail],
subject,'fileemail.html',d,
attach_file=csvfile,**kwargs)
if 'delete' in kwargs:
dodelete = kwargs['delete']
else:
dodelete = False
if dodelete:
try:
os.remove(csvfile)
except:
pass
return 1
# Calculate wind and stream corrections for OTW rowing
@app.task(bind=True)
def handle_otwsetpower(self,f1, boattype, weightvalue,
first_name, last_name, email, workoutid,
**kwargs):
job = self.request
job_id = job.id
if 'jobkey' in kwargs:
job_id = kwargs.pop('jobkey')
if 'ps' in kwargs:
ps = kwargs['ps']
else:
ps = [1,1,1,1]
if 'ratio' in kwargs:
ratio = kwargs['ratio']
else:
ratio = 1.0
if 'debug' in kwargs:
debug = kwargs['debug']
else:
debug = False
if 'quick_calc' in kwargs:
usetable = kwargs['quick_calc']
else:
usetable = False
kwargs['jobid'] = job_id
try:
rowdata = rdata(f1)
except IOError:
try:
rowdata = rdata(f1 + '.csv')
except IOError:
rowdata = rdata(f1 + '.gz')
weightvalue = float(weightvalue)
# do something with boat type
boatfile = {
'1x': 'static/rigging/1x.txt',
'2x': 'static/rigging/2x.txt',
'2-': 'static/rigging/2-.txt',
'4x': 'static/rigging/4x.txt',
'4-': 'static/rigging/4-.txt',
'8+': 'static/rigging/8+.txt',
}
try:
rg = rowingdata.getrigging(boatfile[boattype])
except KeyError:
rg = rowingdata.getrigging('static/rigging/1x.txt')
# do calculation, but do not overwrite NK Empower Power data
powermeasured = False
try:
w = rowdata.df['wash']
if w.mean() != 0:
powermeasured = True
except KeyError:
pass
progressurl = SITE_URL
siteurl = SITE_URL
if debug:
progressurl = SITE_URL_DEV
siteurl = SITE_URL_DEV
secret = PROGRESS_CACHE_SECRET
progressurl += "/rowers/record-progress/"
progressurl += job_id
# determine cache file name
physics_cache = 'media/'+str(boattype)+'_'+str(int(weightvalue))
rowdata.otw_setpower(skiprows=5, mc=weightvalue, rg=rg,
powermeasured=powermeasured,
progressurl=progressurl,
secret=secret,
silent=True,
usetable=usetable,storetable=physics_cache,
)
# save data
rowdata.write_csv(f1, gzip=True)
update_strokedata(workoutid, rowdata.df, debug=debug)
totaltime = rowdata.df['TimeStamp (sec)'].max(
) - rowdata.df['TimeStamp (sec)'].min()
try:
totaltime = totaltime + rowdata.df.ix[0, ' ElapsedTime (sec)']
except KeyError:
pass
df = getsmallrowdata_db(
['power', 'workoutid', 'time'], ids=[workoutid],
debug=debug)
thesecs = totaltime
maxt = 1.05 * thesecs
logarr = datautils.getlogarr(maxt)
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)
if res:
handle_sendemail_breakthrough(
workoutid, email,
first_name,
last_name, btvalues=btvalues.to_json())
subject = "Your OTW Physics Calculations are ready"
from_email = 'Rowsandall <info@rowsandall.com>'
fullemail = first_name + " " + last_name + " " + "<" + email + ">"
d = {
'first_name':first_name,
'siteurl':siteurl,
'workoutid':workoutid,
}
res = send_template_email(from_email,[fullemail],
subject,'otwpoweremail.html',d,
**kwargs)
return 1
@app.task
def handle_updateergcp(rower_id,workoutfilenames,debug=False,**kwargs):
therows = []
for f1 in workoutfilenames:
try:
rowdata = rdata(f1)
except IOError:
try:
rowdata = rdata(f1 + '.csv')
except IOError:
try:
rowdata = rdata(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
def cp_from_workoutids(workoutids,debug=False):
columns = ['power','workoutid','time']
df = getsmallrowdata_db(columns,ids=workoutids,debug=debug)
df.dropna(inplace=True,axis=0)
if df.empty:
# change this
return 0,0,0
# df is not empty. We continue
dfgrouped = df.groupby(['workoutid'])
maxt = 1.05*df['time'].max()/1000.
logarr = datautils.getlogarr(maxt)
delta,cpvalue,avgpower = datautils.getcp(dfgrouped,logarr)
powerdf = pd.DataFrame({
'Delta':delta,
'CP':cpvalue,
})
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)
p1,fitt,fitpower,ratio = datautils.cpfit(powerdf)
# This is code duplication from datautils -- correct asap
fitfunc = lambda pars,x: abs(pars[0])/(1+(x/abs(pars[2]))) + abs(pars[1])/(1+(x/abs(pars[3])))
powerfourmin = fitfunc(p1,240.)
powerhour = fitfunc(p1,3600.)
# 2k power
velofourmin = (powerfourmin/2.8)**(1./3.)
dfourmin = 240.*velofourmin
dratio = 2000./dfourmin
pacefourmin = 500./velofourmin
# assume 5 sec per doubling drop
pace2k = pacefourmin + 5.*np.log10(dratio)/np.log10(2.)
velo2k = 500./pace2k
t2k = 2000./velo2k
pwr2k = fitfunc(p1,t2k)
velo2 = (pwr2k/2.8)**(1./3.)
if np.isnan(velo2) or velo2 <= 0:
velo2 = 1.0
t2 = 2000./velo2
pwr2k = fitfunc(p1,t2)
velo3 = (pwr2k/2.8)**(1./3.)
t3 = 2000./velo3
power2k = fitfunc(p1,t3)
return powerfourmin,power2k,powerhour
@app.task
def handle_updatefitnessmetric(user_id,mode,workoutids,debug=False,
**kwargs):
powerfourmin = -1
power2k = -1
powerhour = -1
mdict = {
'user_id': user_id,
'PowerFourMin': powerfourmin,
'PowerTwoK': power2k,
'PowerOneHour': powerhour,
'workoutmode': mode,
'last_workout': max(workoutids),
'date': timezone.now().strftime('%Y-%m-%d'),
}
result = fitnessmetric_to_sql(mdict,debug=debug,doclean=False)
powerfourmin,power2k,powerhour = cp_from_workoutids(workoutids,debug=debug)
if powerfourmin > 0 and power2k > 0 and powerhour > 0:
mdict = {
'user_id': user_id,
'PowerFourMin': powerfourmin,
'PowerTwoK': power2k,
'PowerOneHour': powerhour,
'workoutmode': mode,
'last_workout': max(workoutids),
'date': timezone.now().strftime('%Y-%m-%d'),
}
result = fitnessmetric_to_sql(mdict,debug=debug,doclean=True)
else:
result = 0
return result
@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)
dfgrouped = df.groupby(['workoutid'])
if not df.empty:
maxt = 1.05*df['time'].max()/1000.
else:
maxt = 1000.
logarr = datautils.getlogarr(maxt)
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,
debug=False,**kwargs):
hrmax = hrdata['hrmax']
hrut2 = hrdata['hrut2']
hrut1 = hrdata['hrut1']
hrat = hrdata['hrat']
hrtr = hrdata['hrtr']
hran = hrdata['hran']
ftp = hrdata['ftp']
powerzones = deserialize_list(hrdata['powerzones'])
powerperc = np.array(deserialize_list(hrdata['powerperc'])).astype(int)
rr = rowingdata.rower(hrmax=hrmax, hrut2=hrut2,
hrut1=hrut1, hrat=hrat,
hrtr=hrtr, hran=hran,
ftp=ftp, powerperc=powerperc,
powerzones=powerzones)
try:
row = rdata(f2, rower=rr)
except IOError:
row = rdata(f2 + '.gz', rower=rr)
haspower = row.df[' Power (watts)'].mean() > 50
nr_rows = len(row.df)
if (plotnr in [1, 2, 4, 5, 8, 11, 9, 12]) and (nr_rows > 1200):
bin = int(nr_rows / 1200.)
df = row.df.groupby(lambda x: x / bin).mean()
row.df = df
nr_rows = len(row.df)
if (plotnr == 1):
fig1 = row.get_timeplot_erg(t)
elif (plotnr == 2):
fig1 = row.get_metersplot_erg(t)
elif (plotnr == 3):
t += ' - Heart Rate Distribution'
fig1 = row.get_piechart(t)
elif (plotnr == 4):
if haspower:
fig1 = row.get_timeplot_otwempower(t)
else:
fig1 = row.get_timeplot_otw(t)
elif (plotnr == 5):
if haspower:
fig1 = row.get_metersplot_otwempower(t)
else:
fig1 = row.get_metersplot_otw(t)
elif (plotnr == 6):
t += ' - Heart Rate Distribution'
fig1 = row.get_piechart(t)
elif (plotnr == 7) or (plotnr == 10):
fig1 = row.get_metersplot_erg2(t)
elif (plotnr == 8) or (plotnr == 11):
fig1 = row.get_timeplot_erg2(t)
elif (plotnr == 9) or (plotnr == 12):
fig1 = row.get_time_otwpower(t)
elif (plotnr == 13) or (plotnr == 16):
t += ' - Power Distribution'
fig1 = row.get_power_piechart(t)
canvas = FigureCanvas(fig1)
# plt.savefig('static/plots/'+imagename,format='png')
canvas.print_figure('static/plots/' + imagename)
# plt.imsave(fname='static/plots/'+imagename)
plt.close(fig1)
fig1.clf()
gc.collect()
return imagename
# Team related remote tasks
@app.task
def handle_sendemail_invite(email, name, code, teamname, manager,
debug=False,**kwargs):
fullemail = email
subject = 'Invitation to join team ' + teamname
siteurl = SITE_URL
if debug:
siteurl = SITE_URL_DEV
d = {
'name':name,
'manager':manager,
'code':code,
'teamname':teamname,
'siteurl':siteurl
}
from_email = 'Rowsandall <info@rowsandall.com>'
res = send_template_email(from_email,[fullemail],
subject,'teaminviteemail.html',d,
**kwargs)
return 1
@app.task
def handle_sendemailnewresponse(first_name, last_name,
email,
commenter_first_name,
commenter_last_name,
comment,
workoutname, workoutid, commentid,
debug=False,**kwargs):
fullemail = email
from_email = 'Rowsandall <info@rowsandall.com>'
subject = 'New comment on workout ' + workoutname
siteurl = SITE_URL
if debug:
siteurl = SITE_URL_DEV
d = {
'first_name':first_name,
'commenter_first_name':commenter_first_name,
'commenter_last_name':commenter_last_name,
'comment':comment,
'workoutname':workoutname,
'siteurl':siteurl,
'workoutid':workoutid,
'commentid':commentid
}
res = send_template_email(from_email,[fullemail],subject,'teamresponseemail.html',d,**kwargs)
return 1
@app.task
def handle_sendemailnewcomment(first_name,
last_name,
email,
commenter_first_name,
commenter_last_name,
comment, workoutname,
workoutid,
debug=False,**kwargs):
fullemail = email
from_email = 'Rowsandall <info@rowsandall.com>'
subject = 'New comment on workout ' + workoutname
siteurl = SITE_URL
if debug:
siteurl = SITE_URL_DEV
d = {
'first_name':first_name,
'commenter_first_name':commenter_first_name,
'commenter_last_name':commenter_last_name,
'comment':comment,
'workoutname':workoutname,
'siteurl':siteurl,
'workoutid':workoutid,
}
res = send_template_email(from_email,[fullemail],subject,
'teamresponseemail.html',d,**kwargs)
return 1
@app.task
def handle_sendemail_request(email, name, code, teamname, requestor, id,
debug=False,**kwargs):
fullemail = email
subject = 'Request to join team ' + teamname
from_email = 'Rowsandall <info@rowsandall.com>'
siteurl = SITE_URL
if debug:
siteurl = SITE_URL_DEV
d = {
'requestor':requestor,
'teamname':teamname,
'siteurl':siteurl,
'id':id,
'first_name':name,
}
res = send_template_email(from_email,[fullemail],subject,
'teamrequestemail.html',d,**kwargs)
return 1
@app.task
def handle_sendemail_request_accept(email, name, teamname, managername,
debug=False,**kwargs):
fullemail = email
subject = 'Welcome to ' + teamname
from_email = 'Rowsandall <info@rowsandall.com>'
siteurl = SITE_URL
if debug:
siteurl = SITE_URL_DEV
d = {
'first_name':name,
'managername':managername,
'teamname':teamname,
'siteurl':siteurl,
}
res = send_template_email(from_email,[fullemail],subject,
'teamwelcomeemail.html',d,**kwargs)
return 1
@app.task
def handle_sendemail_request_reject(email, name, teamname, managername,
debug=False,**kwargs):
fullemail = email
subject = 'Your application to ' + teamname + ' was rejected'
from_email = 'Rowsandall <info@rowsandall.com>'
siteurl = SITE_URL
if debug:
siteurl = SITE_URL_DEV
d = {
'first_name':name,
'managername':managername,
'teamname':teamname,
'siteurl':siteurl,
}
res = send_template_email(from_email,[fullemail],subject,
'teamrejectemail.html',d,**kwargs)
return 1
@app.task
def handle_sendemail_member_dropped(email, name, teamname, managername,
debug=False,**kwargs):
fullemail = email
subject = 'You were removed from ' + teamname
from_email = 'Rowsandall <info@rowsandall.com>'
siteurl = SITE_URL
if debug:
siteurl = SITE_URL_DEV
d = {
'first_name':name,
'managername':managername,
'teamname':teamname,
'siteurl':siteurl,
}
res = send_template_email(from_email,[fullemail],subject,
'teamdropemail.html',d,**kwargs)
return 1
@app.task
def handle_sendemail_team_removed(email, name, teamname, managername,
debug=False,**kwargs):
fullemail = email
subject = 'You were removed from ' + teamname
from_email = 'Rowsandall <info@rowsandall.com>'
siteurl = SITE_URL
if debug:
siteurl = SITE_URL_DEV
d = {
'first_name':name,
'managername':managername,
'teamname':teamname,
'siteurl':siteurl,
}
res = send_template_email(from_email,[fullemail],subject,
'teamremoveemail.html',d,**kwargs)
return 1
@app.task
def handle_sendemail_invite_reject(email, name, teamname, managername,
debug=False,**kwargs):
fullemail = email
subject = 'Your invitation to ' + name + ' was rejected'
from_email = 'Rowsandall <info@rowsandall.com>'
siteurl = SITE_URL
if debug:
siteurl = SITE_URL_DEV
d = {
'first_name':name,
'managername':managername,
'teamname':teamname,
'siteurl':siteurl,
}
res = send_template_email(from_email,[fullemail],subject,
'teaminviterejectemail.html',d,**kwargs)
return 1
@app.task
def handle_sendemail_invite_accept(email, name, teamname, managername,
debug=False,**kwargs):
fullemail = email
subject = 'Your invitation to ' + name + ' was accepted'
from_email = 'Rowsandall <info@rowsandall.com>'
siteurl = SITE_URL
if debug:
siteurl = SITE_URL_DEV
d = {
'first_name':name,
'managername':managername,
'teamname':teamname,
'siteurl':siteurl,
}
res = send_template_email(from_email,[fullemail],subject,
'teaminviteacceptemail.html',d,**kwargs)
return 1
# Another simple task for debugging purposes
def add2(x, y,debug=False,**kwargs):
return x + y