Private
Public Access
1
0
Files
rowsandall/rowers/uploads.py
2021-04-25 17:41:04 +02:00

669 lines
20 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
import time
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: # pragma: no cover
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()): # pragma: no cover
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()): # pragma: no cover
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: # pragma: no cover
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()): # pragma: no cover
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()): # pragma: no cover
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()): # pragma: no cover
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: # pragma: no cover
uploadoptions['workoutsource'] = workoutsource
return uploadoptions
def getplotoptions_body2(uploadoptions,body):
for line in body.splitlines():
chart = matchchart(line)
if chart: # pragma: no cover
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: # pragma: no cover
uploadoptions[r] = True
return uploadoptions
def getsyncoptions(uploadoptions,values): # pragma: no cover
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): # pragma: no cover
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): # pragma: no cover
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): # pragma: no cover
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): # pragma: no cover
uploadoptions['username'] = value
return uploadoptions
def getrace(uploadoptions,value,key): # pragma: no cover
try:
raceid = int(value)
uploadoptions['raceid'] = raceid
except:
pass
return uploadoptions
def getsource(uploadoptions,value,key): # pragma: no cover
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): # pragma: no cover
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: # pragma: no cover
return yml
try:
for key, value in yml.iteritems(): # pragma: no cover
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: # pragma: no cover
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 == {}: # pragma: no cover
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),
'hrzones':serialize_list(r.hrzones),
}
# make plot - asynchronous task
plotnrs = {
'timeplot':1,
'distanceplot':2,
'pieplot':3,
'None':0,
}
axis = r.staticgrids
if axis == None: # pragma: no cover
gridtrue = False
axis = 'both'
else:
gridtrue = True
if plotnr == 0:
plotnr = plotnrs[plottype]
if plotnr == 0:
return 0,0
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: # pragma: no cover
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: # pragma: no cover
pass
try:
w.boattype = options['boattype']
w.save()
except KeyError: # pragma: no cover
pass
return 1
def set_workoutsource(w,options): # pragma: no cover
try:
w.workoutsource = options['workoutsource']
w.save()
except KeyError: # pragma: no cover
pass
def make_private(w,options): # pragma: no cover
if 'makeprivate' in options and options['makeprivate']:
w.privacy = 'hidden'
w.save()
return 1
def do_sync(w,options, quick=False):
do_strava_export = w.user.strava_auto_export
try:
upload_to_strava = options['upload_to_Strava'] or do_strava_export
except KeyError:
upload_to_strava = False
try:
if options['stravaid'] != 0 and options['stravaid'] != '': # pragma: no cover
w.uploadedtostrava = options['stravaid']
upload_to_strava = False
do_strava_export = False
w.save()
except KeyError:
pass
try:
if options['nkid'] != 0 and options['nkid'] != '': # pragma: no cover
w.uploadedtonk = options['nkid']
w.save()
except KeyError:
pass
try:
if options['inboard'] != 0 and options['inboard'] != '': # pragma: no cover
w.inboard = options['inboard']
except KeyError:
pass
try:
if options['oarlength'] != 0 and options['oarlength'] != '': # pragma: no cover
w.oarlength = options['oarlength']
except KeyError:
pass
try:
if options['garminid'] != 0 and options['garminid'] != '': # pragma: no cover
w.uploadedtogarmin = options['garminid']
w.save()
except KeyError:
pass
do_c2_export = w.user.c2_auto_export
try:
upload_to_c2 = options['upload_to_C2'] or do_c2_export
except KeyError:
upload_to_c2 = False
try:
if options['c2id'] != 0 and options['c2id'] != '': # pragma: no cover
w.uploadedtoc2 = options['c2id']
upload_to_c2 = False
do_c2_export = False
w.save()
except KeyError:
pass
try:
if options['rp3id'] != 0 and options['rp3id'] != '': # pragma: no cover
w.uploadedtorp3 = options['rp3id']
w.save()
except KeyError:
pass
if w.duplicate:
return 0
if do_c2_export:
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: # pragma: no cover
pass
if do_strava_export: # pragma: no cover
try:
message,id = stravastuff.workout_strava_upload(
w.user.user,w,quick=quick,asynchron=True,
)
except NoTokenError: # pragma: no cover
id = 0
message = "Please connect to Strava first"
except:
e = sys.exc_info()[0]
t = time.localtime()
timestamp = time.strftime('%b-%d-%Y_%H%M', t)
with open('stravalog.log','a') as f:
f.write('\n')
f.write(timestamp)
f.write(str(e))
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,asynchron=True,
)
with open('st_export.log','a') as logfile: # pragma: no cover
logfile.write(str(timezone.now())+': ')
logfile.write('Workout uploaded '+str(w.id)+'\n')
except NoTokenError:
with open('st_export.log','a') as logfile:
logfile.write(str(timezone.now())+': ')
logfile.write(str(w.user)+' NoTokenError\n')
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): # pragma: no cover
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): # pragma: no cover
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): # pragma: no cover
try:
message,id = tpstuff.workout_tp_upload(
w.user.user,w
)
except NoTokenError:
message = "Please connect to TrainingPeaks first"
id = 0
return 1