433 lines
13 KiB
Python
433 lines
13 KiB
Python
# for actions related to uploads
|
|
from django.conf import settings
|
|
from django.utils import timezone,translation
|
|
from rowers.tasks import (
|
|
handle_sendemail_unrecognized,handle_sendemailnewcomment,
|
|
handle_sendemailnewresponse, handle_updatedps,
|
|
handle_makeplot,handle_otwsetpower,handle_sendemailtcx,
|
|
handle_sendemailcsv
|
|
)
|
|
|
|
from rowers.models import GraphImage
|
|
|
|
from PIL import Image
|
|
|
|
import numpy as np
|
|
import yaml
|
|
import argparse
|
|
import yamllint
|
|
from subprocess import call
|
|
import re
|
|
|
|
from verbalexpressions import VerEx
|
|
import re
|
|
|
|
import django_rq
|
|
queue = django_rq.get_queue('default')
|
|
queuelow = django_rq.get_queue('low')
|
|
queuehigh = django_rq.get_queue('low')
|
|
|
|
from types import workouttypes,boattypes
|
|
|
|
try:
|
|
from cStringIO import StringIO
|
|
except:
|
|
from StringIO import StringIO
|
|
|
|
from rowers.utils import (
|
|
geo_distance,serialize_list,deserialize_list,uniqify,
|
|
str2bool,range_to_color_hex,absolute,myqueue
|
|
)
|
|
|
|
def cleanbody(body):
|
|
regex = r".*---\n([\s\S]*?)\.\.\..*"
|
|
matches = re.finditer(regex,body)
|
|
|
|
for m in matches:
|
|
|
|
if m != None:
|
|
body = m.group(0)
|
|
|
|
return body
|
|
|
|
# currently only matches one chart
|
|
def matchchart(line):
|
|
results = []
|
|
testert = '^((chart)|(plot))'
|
|
tester2t = testert+'(.*)(dist)'
|
|
tester3t = testert+'(.*)(time)'
|
|
tester4t = testert+'(.*)(pie)'
|
|
|
|
tester = re.compile(testert)
|
|
tester2 = re.compile(tester2t)
|
|
tester3 = re.compile(tester3t)
|
|
tester4 = re.compile(tester4t)
|
|
|
|
if tester.match(line.lower()):
|
|
if tester2.match(line.lower()):
|
|
return 'distanceplot'
|
|
if tester3.match(line.lower()):
|
|
return 'timeplot'
|
|
if tester3.match(line.lower()):
|
|
return 'pieplot'
|
|
|
|
def matchsync(line):
|
|
results = []
|
|
tester = '((sync)|(synchronization)|(export))'
|
|
tester2 = tester+'(.*)((c2)|(concept2)|(logbook))'
|
|
tester3 = tester+'(.*)((tp)|(trainingpeaks))'
|
|
tester4 = tester+'(.*)(strava)'
|
|
tester5 = tester+'(.*)((st)|(sporttracks))'
|
|
tester6 = tester+'(.*)((rk)|(runkeeper))'
|
|
tester7 = tester+'(.*)((mapmyfitness)|(underarmour)|(ua))'
|
|
|
|
tester = re.compile(tester)
|
|
|
|
if tester.match(line.lower()):
|
|
testers = [
|
|
('upload_to_C2',re.compile(tester2)),
|
|
('upload_totp',re.compile(tester3)),
|
|
('upload_to_Strava',re.compile(tester4)),
|
|
('upload_to_SportTracks',re.compile(tester5)),
|
|
('upload_to_RunKeeper',re.compile(tester6)),
|
|
('upload_to_MapMyFitness',re.compile(tester7)),
|
|
]
|
|
for t in testers:
|
|
if t[1].match(line.lower()):
|
|
results.append(t[0])
|
|
|
|
return results
|
|
|
|
def gettypeoptions_body2(uploadoptions,body):
|
|
tester = re.compile('^(workout)')
|
|
testerb = re.compile('^(boat)')
|
|
for line in body.splitlines():
|
|
if tester.match(line.lower()):
|
|
for typ,verb in workouttypes:
|
|
str1 = '^(workout)(.*)({a})'.format(
|
|
a = typ
|
|
)
|
|
testert = re.compile(str1)
|
|
if testert.match(line.lower()):
|
|
uploadoptions['workouttype'] = typ
|
|
if testerb.match(line.lower()):
|
|
for typ,verb in boattypes:
|
|
str1 = '^(boat)(.*)({a})'.format(
|
|
a = typ
|
|
)
|
|
testert = re.compile(str1)
|
|
if testert.match(line.lower()):
|
|
uploadoptions['boattype'] = typ
|
|
|
|
return uploadoptions
|
|
|
|
def getprivateoptions_body2(uploadoptions,body):
|
|
tester = re.compile('^(priva)')
|
|
for line in body.splitlines():
|
|
if tester.match(line.lower()):
|
|
v = True
|
|
negs = ['false','False','None','no']
|
|
for neg in negs:
|
|
tstr = re.compile('^(.*)'+neg)
|
|
|
|
if tstr.match(line.lower()):
|
|
v = False
|
|
|
|
uploadoptions['makeprivate'] = v
|
|
|
|
return uploadoptions
|
|
|
|
def getplotoptions_body2(uploadoptions,body):
|
|
for line in body.splitlines():
|
|
chart = matchchart(line)
|
|
if chart:
|
|
uploadoptions['make_plot'] = True
|
|
uploadoptions['plottype'] = chart
|
|
|
|
return uploadoptions
|
|
|
|
def getsyncoptions_body2(uploadoptions,body):
|
|
result = []
|
|
for line in body.splitlines():
|
|
result = result+matchsync(line)
|
|
|
|
result = list(set(result))
|
|
|
|
for r in result:
|
|
uploadoptions[r] = True
|
|
|
|
return uploadoptions
|
|
|
|
def getsyncoptions(uploadoptions,values):
|
|
try:
|
|
value = values.lower()
|
|
values = [values]
|
|
except AttributeError:
|
|
pass
|
|
|
|
for v in values:
|
|
try:
|
|
v = v.lower()
|
|
|
|
if v in ['c2','concept2','logbook']:
|
|
uploadoptions['upload_to_C2'] = True
|
|
if v in ['tp','trainingpeaks']:
|
|
uploadoptions['upload_totp'] = True
|
|
if v in ['strava']:
|
|
uploadoptions['upload_to_Strava'] = True
|
|
if v in ['st','sporttracks']:
|
|
uploadoptions['upload_to_SportTracks'] = True
|
|
if v in ['rk','runkeeper']:
|
|
uploadoptions['upload_to_RunKeeper'] = True
|
|
if v in ['ua','underarmour','mapmyfitness']:
|
|
uploadoptions['upload_to_MapMyFitness'] = True
|
|
except AttributeError:
|
|
pass
|
|
|
|
return uploadoptions
|
|
|
|
def getplotoptions(uploadoptions,value):
|
|
try:
|
|
v = value.lower()
|
|
if v in ['pieplot','timeplot','distanceplot']:
|
|
uploadoptions['make_plot'] = True
|
|
uploadoptions['plottype'] = v
|
|
elif 'pie' in v:
|
|
uploadoptions['make_plot'] = True
|
|
uploadoptions['plottype'] = 'pieplot'
|
|
elif 'distance' in v:
|
|
uploadoptions['make_plot'] = True
|
|
uploadoptions['plottype'] = 'distanceplot'
|
|
elif 'time' in v:
|
|
uploadoptions['make_plot'] = True
|
|
uploadoptions['plottype'] = 'timeplot'
|
|
except TypeError:
|
|
pass
|
|
|
|
return uploadoptions
|
|
|
|
|
|
def gettype(uploadoptions,value,key):
|
|
workouttype = 'rower'
|
|
for type,verb in workouttypes:
|
|
if value == type:
|
|
workouttype = type
|
|
if value == verb:
|
|
workouttype = type
|
|
|
|
uploadoptions[key] = workouttype
|
|
|
|
return uploadoptions
|
|
|
|
def getboattype(uploadoptions,value,key):
|
|
boattype = '1x'
|
|
for type,verb in boattypes:
|
|
if value == type:
|
|
boattype = type
|
|
if value == verb:
|
|
boattype = type
|
|
|
|
uploadoptions[key] = boattype
|
|
|
|
return uploadoptions
|
|
|
|
|
|
def getboolean(uploadoptions,value,key):
|
|
b = True
|
|
if not value:
|
|
b = False
|
|
if value in [False,'false','False',None,'no']:
|
|
b = False
|
|
|
|
uploadoptions[key] = b
|
|
|
|
return uploadoptions
|
|
|
|
def upload_options(body):
|
|
uploadoptions = {}
|
|
body = cleanbody(body)
|
|
try:
|
|
yml = (yaml.load(body))
|
|
if yml and 'fromuploadform' in yml:
|
|
return yml
|
|
try:
|
|
for key, value in yml.iteritems():
|
|
lowkey = key.lower()
|
|
if lowkey == 'sync' or lowkey == 'synchronization':
|
|
uploadoptions = getsyncoptions(uploadoptions,value)
|
|
if lowkey == 'chart' or lowkey == 'static' or lowkey == 'plot':
|
|
uploadoptions = getplotoptions(uploadoptions,value)
|
|
if 'priva' in lowkey:
|
|
uploadoptions = getboolean(uploadoptions,value,'makeprivate')
|
|
if 'workout' in lowkey:
|
|
uploadoptions = gettype(uploadoptions,value,'workouttype')
|
|
if 'boat' in lowkey:
|
|
uploadoptions = getboattype(uploadoptions,value,'boattype')
|
|
except AttributeError:
|
|
#pass
|
|
raise yaml.YAMLError
|
|
except yaml.YAMLError as exc:
|
|
try:
|
|
uploadoptions = getplotoptions_body2(uploadoptions,body)
|
|
uploadoptions = getsyncoptions_body2(uploadoptions,body)
|
|
uploadoptions = getprivateoptions_body2(uploadoptions,body)
|
|
typeoptions = gettypeoptions_body2(uploadoptions,body)
|
|
except IOError:
|
|
pm = exc.problem_mark
|
|
strpm = str(pm)
|
|
pbm = "Your email has an issue on line {} at position {}. The error is: ".format(
|
|
pm.line+1,
|
|
pm.column+1,
|
|
)+strpm
|
|
return {'error':pbm}
|
|
|
|
if uploadoptions == {}:
|
|
uploadoptions['message'] = 'No parsing issue. No valid commands detected'
|
|
|
|
return uploadoptions
|
|
|
|
def make_plot(r,w,f1,f2,plottype,title,imagename='',plotnr=0):
|
|
if imagename == '':
|
|
imagename = f1[:-4]+'.png'
|
|
fullpathimagename = 'static/plots/'+imagename
|
|
|
|
powerperc = 100*np.array([r.pw_ut2,
|
|
r.pw_ut1,
|
|
r.pw_at,
|
|
r.pw_tr,r.pw_an])/r.ftp
|
|
|
|
ftp = float(r.ftp)
|
|
if w.workouttype in ('water','coastal'):
|
|
ftp = ftp*(100.-r.otwslack)/100.
|
|
|
|
hrpwrdata = {
|
|
'hrmax':r.max,
|
|
'hrut2':r.ut2,
|
|
'hrut1':r.ut1,
|
|
'hrat':r.at,
|
|
'hrtr':r.tr,
|
|
'hran':r.an,
|
|
'ftp':ftp,
|
|
'powerperc':serialize_list(powerperc),
|
|
'powerzones':serialize_list(r.powerzones),
|
|
}
|
|
|
|
# make plot - asynchronous task
|
|
plotnrs = {
|
|
'timeplot':1,
|
|
'distanceplot':2,
|
|
'pieplot':3,
|
|
}
|
|
|
|
if plotnr == 0:
|
|
plotnr = plotnrs[plottype]
|
|
|
|
if w.workouttype in ('water','coastal'):
|
|
plotnr = plotnr+3
|
|
|
|
|
|
job = myqueue(queue,handle_makeplot,f1,f2,
|
|
title,hrpwrdata,
|
|
plotnr,imagename)
|
|
|
|
try:
|
|
width,height = Image.open(fullpathimagename).size
|
|
except:
|
|
width = 1200
|
|
height = 600
|
|
|
|
imgs = GraphImage.objects.filter(workout=w)
|
|
if len(imgs) < 7:
|
|
i = GraphImage(workout=w,
|
|
creationdatetime=timezone.now(),
|
|
filename=fullpathimagename,
|
|
width=width,height=height)
|
|
|
|
i.save()
|
|
else:
|
|
return 0,'You have reached the maximum number of static images for this workout. Delete an image first'
|
|
|
|
return i.id,job.id
|
|
|
|
import c2stuff,stravastuff,sporttracksstuff,runkeeperstuff
|
|
import underarmourstuff,tpstuff
|
|
|
|
def set_workouttype(w,options):
|
|
try:
|
|
w.workouttype = options['workouttype']
|
|
w.save()
|
|
except KeyError:
|
|
pass
|
|
try:
|
|
w.boattype = options['boattype']
|
|
w.save()
|
|
except KeyError:
|
|
pass
|
|
|
|
return 1
|
|
|
|
def make_private(w,options):
|
|
if 'makeprivate' in options and options['makeprivate']:
|
|
w.privacy = 'hidden'
|
|
w.save()
|
|
|
|
return 1
|
|
|
|
def do_sync(w,options):
|
|
if ('upload_to_C2' in options and options['upload_to_C2']) or w.user.c2_auto_export:
|
|
try:
|
|
message,id = c2stuff.workout_c2_upload(w.user.user,w)
|
|
except c2stuff.C2NoTokenError:
|
|
id = 0
|
|
message = "Something went wrong with the Concept2 sync"
|
|
|
|
if ('upload_to_Strava' in options and options['upload_to_Strava']) or w.user.strava_auto_export:
|
|
try:
|
|
message,id = stravastuff.workout_strava_upload(
|
|
w.user.user,w
|
|
)
|
|
except stravastuff.StravaNoTokenError:
|
|
id = 0
|
|
message = "Please connect to Strava first"
|
|
|
|
|
|
if ('upload_to_SportTracks' in options and options['upload_to_SportTracks']) or w.user.sporttracks_auto_export:
|
|
try:
|
|
message,id = sporttracksstuff.workout_sporttracks_upload(
|
|
w.user.user,w
|
|
)
|
|
except sporttracksstuff.SportTracksNoTokenError:
|
|
message = "Please connect to SportTracks first"
|
|
id = 0
|
|
|
|
|
|
if ('upload_to_RunKeeper' in options and options['upload_to_RunKeeper']) or w.user.runkeeper_auto_export:
|
|
try:
|
|
message,id = runkeeperstuff.workout_runkeeper_upload(
|
|
w.user.user,w
|
|
)
|
|
except runkeeperstuff.RunKeeperNoTokenError:
|
|
message = "Please connect to Runkeeper first"
|
|
id = 0
|
|
|
|
if ('upload_to_MapMyFitness' in options and options['upload_to_MapMyFitness']) or w.user.mapmyfitness_auto_export:
|
|
try:
|
|
message,id = underarmourstuff.workout_ua_upload(
|
|
w.user.user,w
|
|
)
|
|
except underarmourstuff.UnderArmourNoTokenError:
|
|
message = "Please connect to MapMyFitness first"
|
|
id = 0
|
|
|
|
|
|
if ('upload_to_TrainingPeaks' in options and options['upload_to_TrainingPeaks']) or w.user.trainingpeaks_auto_export:
|
|
try:
|
|
message,id = tpstuff.workout_tp_upload(
|
|
w.user.user,w
|
|
)
|
|
except tpstuff.TPNoTokenError:
|
|
message = "Please connect to TrainingPeaks first"
|
|
id = 0
|
|
|
|
return 1
|