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