Private
Public Access
1
0
Files
rowsandall/rowers/uploads.py
2020-05-03 16:51:34 +02:00

595 lines
17 KiB
Python

from __future__ import absolute_import
from __future__ import division
from __future__ import print_function
from __future__ import unicode_literals
# 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 rowers.rower_rules import ispromember
from PIL import Image
import numpy as np
import yaml
import argparse
import yamllint
from subprocess import call
import re
import sys
from verbalexpressions import VerEx
import django_rq
queue = django_rq.get_queue('default')
queuelow = django_rq.get_queue('low')
queuehigh = django_rq.get_queue('high')
from rowers.mytypes import workouttypes,boattypes,otwtypes,workoutsources, workouttypes_ordered
try:
from cStringIO import StringIO
except:
from io import StringIO
from rowers.utils import (
geo_distance,serialize_list,deserialize_list,uniqify,
str2bool,range_to_color_hex,absolute,myqueue,NoTokenError
)
def cleanbody(body):
try:
body = body.decode('utf-8')
except AttributeError:
pass
regex = r".*---\n([\s\S]*?)\.\.\..*"
matches = re.finditer(regex,body)
for m in matches:
if m != None:
body = m.group(0)
return body
sources = [s for s,name in workoutsources]
def matchsource(line):
results = []
for s in sources:
testert = '^source.*(%s)' % s
tester = re.compile(testert)
if tester.match(line.lower()):
return tester.match(line.lower()).group(1)
# 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 matchuser(line):
testert = '^(user)'
tester = re.compile(testert)
if tester.match(line.lower()):
words = line.split()
return words[1]
return None
def matchrace(line):
testert = '^(race)'
tester = re.compile(testert)
if tester.match(line.lower()):
words = line.split()
try:
return int(words[1])
except:
return None
return None
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 getstravaid(uploadoptions,body):
stravaid = 0
tester = re.compile('^(stravaid)(.*?)(\d+)')
for line in body.splitlines():
if tester.match(line.lower()):
stravaid = tester.match(line.lower()).group(3)
uploadoptions['stravaid'] = int(stravaid)
return uploadoptions
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_ordered.items():
str1 = '^(workout)(.*)({a})'.format(
a = typ.lower()
)
testert = re.compile(str1)
if testert.match(line.lower()):
uploadoptions['workouttype'] = typ
break
if testerb.match(line.lower()):
for typ,verb in boattypes:
str1 = '^(boat)(.*)({a})'.format(
a = typ.replace('+','\+')
)
testert = re.compile(str1)
if testert.match(line.lower()):
uploadoptions['boattype'] = typ
break
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 getworkoutsources(uploadoptions,body):
for line in body.splitlines():
workoutsource = matchsource(line)
if workoutsource:
uploadoptions['workoutsource'] = workoutsource
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 getuseroptions_body2(uploadoptions,body):
for line in body.splitlines():
user = matchuser(line)
if user:
uploadoptions['username'] = user
return uploadoptions
def getraceoptions_body2(uploadoptions,body):
for line in body.splitlines():
raceid = matchrace(line)
if raceid:
uploadoptions['raceid'] = raceid
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 typ,verb in workouttypes_ordered.items():
if value == typ:
workouttype = typ
break
if value == verb:
workouttype = typ
break
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 getuser(uploadoptions,value,key):
uploadoptions['username'] = value
return uploadoptions
def getrace(uploadoptions,value,key):
try:
raceid = int(value)
uploadoptions['raceid'] = raceid
except:
pass
return uploadoptions
def getsource(uploadoptions,value,key):
workoutsource = 'unknown'
for type,verb in workoutsources:
if value == type:
workoutsource = type
if value == verb:
workoutsource = type
uploadoptions[key] = workoutsource
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 = {
'boattype':'1x',
'workouttype': 'rower',
}
body = cleanbody(body)
try:
yml = (yaml.safe_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' or lowkey == 'export':
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')
if 'source' in lowkey:
uploadoptions = getsource(uploadoptions,value,'workoutsource')
if 'username' in lowkey:
uploadoptions = getuser(uploadoptions,value,'username')
if 'raceid' in lowkey:
uploadoptions = getraceid(uploadoptions,value,'raceid')
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)
uploadoptions = gettypeoptions_body2(uploadoptions,body)
uploadoptions = getstravaid(uploadoptions,body)
uploadoptions = getworkoutsources(uploadoptions,body)
uploadoptions = getuseroptions_body2(uploadoptions,body)
uploadoptions = getraceoptions_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 otwtypes:
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,
}
axis = r.staticgrids
if axis == None:
gridtrue = False
axis = 'both'
else:
gridtrue = True
if plotnr == 0:
plotnr = plotnrs[plottype]
if w.workouttype in otwtypes:
plotnr = plotnr+3
otwrange = [r.fastpaceotw.total_seconds(),r.slowpaceotw.total_seconds()]
oterange = [r.fastpaceerg.total_seconds(),r.slowpaceerg.total_seconds()]
job = myqueue(queuehigh,handle_makeplot,f1,f2,
title,hrpwrdata,
plotnr,imagename,gridtrue=gridtrue,axis=axis,
otwrange=otwrange,oterange=oterange)
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 rowers.c2stuff as c2stuff
import rowers.stravastuff as stravastuff
import rowers.sporttracksstuff as sporttracksstuff
import rowers.runkeeperstuff as runkeeperstuff
import rowers.underarmourstuff as underarmourstuff
import rowers.tpstuff as tpstuff
from rowers.rower_rules import is_promember
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 set_workoutsource(w,options):
try:
w.workoutsource = options['workoutsource']
w.save()
except KeyError:
pass
def make_private(w,options):
if 'makeprivate' in options and options['makeprivate']:
w.privacy = 'hidden'
w.save()
return 1
def do_sync(w,options, quick=False):
try:
upload_to_strava = options['upload_to_Strava']
except KeyError:
upload_to_strava = False
try:
if options['stravaid'] != 0 and options['stravaid'] != '':
w.uploadedtostrava = options['stravaid']
upload_to_strava = False
w.save()
except KeyError:
pass
if ('upload_to_C2' in options and options['upload_to_C2']) or (w.user.c2_auto_export and ispromember(w.user.user)):
try:
message,id = c2stuff.workout_c2_upload(w.user.user,w,asynchron=True)
except NoTokenError:
id = 0
message = "Something went wrong with the Concept2 sync"
except:
pass
if ('upload_to_Strava' in options and upload_to_strava) or (w.user.strava_auto_export and ispromember(w.user.user)):
try:
message,id = stravastuff.workout_strava_upload(
w.user.user,w,quick=quick,asynchron=True,
)
except NoTokenError:
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 and ispromember(w.user.user)):
try:
message,id = sporttracksstuff.workout_sporttracks_upload(
w.user.user,w,asynchron=True,
)
except NoTokenError:
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 and ispromember(w.user.user)):
try:
message,id = runkeeperstuff.workout_runkeeper_upload(
w.user.user,w,asynchron=True,
)
except NoTokenError:
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 and ispromember(w.user.user)):
try:
message,id = underarmourstuff.workout_ua_upload(
w.user.user,w
)
except NoTokenError:
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 and ispromember(w.user.user)):
try:
message,id = tpstuff.workout_tp_upload(
w.user.user,w
)
except NoTokenError:
message = "Please connect to TrainingPeaks first"
id = 0
return 1