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