diff --git a/.coveragerc b/.coveragerc new file mode 100644 index 00000000..190372c2 --- /dev/null +++ b/.coveragerc @@ -0,0 +1,2 @@ +[run] +omit = *migrations* diff --git a/.gitignore b/.gitignore index 665c5589..5d47abd7 100644 --- a/.gitignore +++ b/.gitignore @@ -24,6 +24,11 @@ conftest.py # static plots /static/plots +# temporary test files +/rowers/tests/testdata/temp +/rowers/tests/testdate/testdata.csv.gz +/rowers/tests/testdate/testdata.tcx + # Python egg metadata, regenerated from source files by setuptools. *.egg-info diff --git a/rowers/braintreestuff.py b/rowers/braintreestuff.py index fda05793..bfe978bc 100644 --- a/rowers/braintreestuff.py +++ b/rowers/braintreestuff.py @@ -43,6 +43,7 @@ else: ) ) + from rowers.models import Rower,PaidPlan from rowers.utils import ProcessorCustomerError @@ -117,7 +118,7 @@ def make_payment(rower,data): job = myqueue(queuehigh,handle_send_email_transaction, name, rower.user.email, amount) - return amount + return amount,'' else: return 0,'' @@ -206,10 +207,12 @@ def update_subscription(rower,data,method='up'): def create_subscription(rower,data): - planid = data['plan'] - plan = PaidPlan.objects.get(id=planid) nonce_from_the_client = data['payment_method_nonce'] amount = data['amount'] + + planid = data['plan'] + plan = PaidPlan.objects.get(id=planid) + # create or find payment method result = gateway.payment_method.create({ @@ -217,6 +220,7 @@ def create_subscription(rower,data): "payment_method_nonce": nonce_from_the_client }) + if result.is_success: payment_method_token = result.payment_method.token else: @@ -235,6 +239,7 @@ def create_subscription(rower,data): rower.paymenttype = plan.paymenttype rower.rowerplan = plan.shortname rower.subscription_id = result.subscription.id + rower.save() name = '{f} {l}'.format( f = rower.user.first_name, @@ -269,6 +274,7 @@ def cancel_subscription(rower,id): themessages.append("Subscription canceled") except: errormessages.append("We could not find the subscription record in our customer database. We have notified the site owner, who will contact you.") + name = '{f} {l}'.format(f = rower.user.first_name, l = rower.user.last_name) @@ -308,7 +314,7 @@ def find_subscriptions(rower): try: paypal_accounts = result.paypal_accounts - for account in accuonts: + for account in paypal_accounts: for subscription in account.subscriptions: if subscription.status == 'Active': active_subscriptions.append(subscription) @@ -353,37 +359,40 @@ def get_transactions(start_date,end_date): statuses = [] ids = [] usernames = [] + customerids = [] + transactionids = [] + subscriptionids = [] + ownids = [] + + countlines = [1 for transaction in results] for transaction in results: - try: - rs = Rower.objects.filter( - customer_id=transaction.customer['id'], - paymentprocessor='braintree') - if rs: - r = rs[0] + r = None + rs = Rower.objects.filter( + customer_id=transaction.customer['id'], + paymentprocessor='braintree') + if rs: + r = rs[0] countries.append(r.country) - names.append('{f} {l}'.format( - f = r.user.first_name, - l = r.user.last_name, - ) - ) - emails.append(r.user.email) - ids.append(r.id) + ownids.append(r.id) usernames.append(r.user.username) - except (KeyError,IndexError): + else: countries.append( transaction.credit_card_details.country_of_issuance) - names.append('{f} {l}'.format( - f = transaction.customer['first_name'], - l = transaction.customer['last_name'] - ) - ) - emails.append(transaction.customer.email) - ids.append(transaction.customer['id']) + ownids.append('unknown') usernames.append('unknown') - + + emails.append(transaction.customer_details.email) + names.append('{f} {l}'.format( + f = transaction.customer['first_name'], + l = transaction.customer['last_name'] + ) + ) + customerids.append(transaction.customer['id']) + transactionids.append(transaction.id) + subscriptionids.append(transaction.subscription_id) amounts.append(transaction.amount) dates.append(transaction.created_at) currencies.append(transaction.currency_iso_code) @@ -391,7 +400,7 @@ def get_transactions(start_date,end_date): transaction.credit_card_details.country_of_issuance) statuses.append(transaction.status) - + df = pd.DataFrame({ 'name':names, 'email':emails, @@ -402,10 +411,15 @@ def get_transactions(start_date,end_date): 'card_country':card_countries, 'status':statuses, 'username':usernames, - 'user_id':ids, + 'user_id':ownids, + 'customer_id':customerids, + 'transaction_id':transactionids, + 'subscription_id':subscriptionids } ) return df +def mocktest(rower): + return '5' diff --git a/rowers/c2stuff.py b/rowers/c2stuff.py index 9641a41a..dd345736 100644 --- a/rowers/c2stuff.py +++ b/rowers/c2stuff.py @@ -300,10 +300,14 @@ def summaryfromsplitdata(splitdata,data,filename,sep='|'): sa += iarr results += resarr + + if itime != 0: + ivelo = idist/itime + ipower = 2.8*ivelo**(3.0) + else: + ivelo = 0 + ipower = 0 - ivelo = idist/itime - ipower = 2.8*ivelo**(3.0) - sums += interval_string(intervalnr,idist,itime,ipace,ispm, iavghr,imaxhr,0,ipower,separator=sep) intervalnr+=1 diff --git a/rowers/dataprep.py b/rowers/dataprep.py index dc236d51..45914354 100644 --- a/rowers/dataprep.py +++ b/rowers/dataprep.py @@ -1225,7 +1225,10 @@ def handle_nonpainsled(f2, fileformat, summary=''): try: os.remove(f_to_be_deleted) except: - os.remove(f_to_be_deleted + '.gz') + try: + os.remove(f_to_be_deleted + '.gz') + except: + pass return (f2, summary, oarlength, inboard, fileformat) diff --git a/rowers/imports.py b/rowers/imports.py index 4fdea563..d49625f3 100644 --- a/rowers/imports.py +++ b/rowers/imports.py @@ -103,6 +103,7 @@ def imports_open(user,oauth_data): oauth_data, ) elif tokenexpirydate is None and expirydatename is not None and 'strava' in expirydatename: + print 'noot' token = imports_token_refresh( user, tokenname, @@ -152,6 +153,8 @@ def imports_do_refresh_token(refreshtoken,oauth_data,access_token=''): data=post_data, headers=headers) + + if response.status_code == 200 or response.status_code == 201: token_json = response.json() else: diff --git a/rowers/interactiveplots.py b/rowers/interactiveplots.py index 4cb22c2d..f69b5ec0 100644 --- a/rowers/interactiveplots.py +++ b/rowers/interactiveplots.py @@ -2137,8 +2137,9 @@ def interactive_chart(id=0,promember=0,intervaldata = {}): columns = ['time','pace','hr','fpace','ftime'] datadf = dataprep.getsmallrowdata_db(columns,ids=[id]) - + datadf.dropna(axis=0,how='any',inplace=True) + row = Workout.objects.get(id=id) if datadf.empty: return "","No Valid Data Available" diff --git a/rowers/management/commands/processemail.py b/rowers/management/commands/processemail.py index 6010c55f..52022e06 100644 --- a/rowers/management/commands/processemail.py +++ b/rowers/management/commands/processemail.py @@ -13,6 +13,7 @@ from django_mailbox.models import Message, MessageAttachment,Mailbox from django.core.urlresolvers import reverse from django.conf import settings +from django.utils import timezone from rowers.models import Workout, Rower from rowingdata import rower as rrower @@ -24,6 +25,11 @@ import rowers.polarstuff as polarstuff import rowers.c2stuff as c2stuff import rowers.stravastuff as stravastuff from rowers.opaque import encoder + +from rowers.models import User,VirtualRace,Workout +from rowers.plannedsessions import email_submit_race + + workoutmailbox = Mailbox.objects.get(name='workouts') failedmailbox = Mailbox.objects.get(name='Failed') @@ -65,17 +71,42 @@ def processattachment(rower, fileobj, title, uploadoptions,testing=False): if testing: print 'Creating workout from email' + + # set user + if rower.user.is_staff and 'username' in uploadoptions: + users = User.objects.filter(username=uploadoptions['username']) + if len(users)==1: + therower = users[0].rower + else: + return 0 + else: + therower = rower + workoutid = [ - make_new_workout_from_email(rower, filename, title,testing=testing) + make_new_workout_from_email(therower, filename, title,testing=testing) ] + + if 'raceid' in uploadoptions and workoutid[0] and rower.user.is_staff: + if testing and workoutid[0]: + w = Workout.objects.get(id = workoutid[0]) + w.startdatetime = timezone.now() + w.date = timezone.now().date() + w.save() + try: + race = VirtualRace.objects.get(id=uploadoptions['raceid']) + if race.manager == rower.user: + result = email_submit_race(therower,race,workoutid[0]) + except VirtualRace.DoesNotExist: + pass + if testing: print 'Workout id = {workoutid}'.format(workoutid=workoutid) if workoutid[0]: link = settings.SITE_URL+reverse( - rower.defaultlandingpage, + therower.defaultlandingpage, kwargs = { 'id':encoder.encode_hex(workoutid[0]), } @@ -99,9 +130,9 @@ def processattachment(rower, fileobj, title, uploadoptions,testing=False): ) try: if workoutid and not testing: - if rower.getemailnotifications and not rower.emailbounced: + if therower.getemailnotifications and not therower.emailbounced: email_sent = send_confirm( - rower.user, title, link, + therower.user, title, link, uploadoptions ) time.sleep(10) @@ -109,9 +140,9 @@ def processattachment(rower, fileobj, title, uploadoptions,testing=False): try: time.sleep(10) if workoutid: - if rower.getemailnotifications and not rower.emailbounced: + if therower.getemailnotifications and not therower.emailbounced: email_sent = send_confirm( - rower.user, title, link, + therower.user, title, link, uploadoptions ) except: diff --git a/rowers/models.py b/rowers/models.py index 1b16b6ef..76a535bd 100644 --- a/rowers/models.py +++ b/rowers/models.py @@ -517,6 +517,10 @@ def course_length(course): polygons = GeoPolygon.objects.filter(course=course).order_by("order_in_course") totaldist = 0 + + if not polygons: + return 0 + for i in range(len(polygons)-1): latitude1,longitude1 = polygon_coord_center(polygons[i]) latitude2,longitude2 = polygon_coord_center(polygons[i+1]) @@ -1722,6 +1726,13 @@ class TrainingMicroCycleForm(ModelForm): 'enddate': AdminDateWidget() } +regularsessiontypechoices = ( + ('session','Training Session'), + ('challenge','Challenge'), + ('test','Mandatory Test'), + ('cycletarget','Total for a time period'), + ('coursetest','OTW test over a course'), +) # model for Planned Session (Workout, Challenge, Test) class PlannedSession(models.Model): @@ -1736,6 +1747,14 @@ class PlannedSession(models.Model): ('indoorrace','Indoor Virtual Race'), ) + regularsessiontypechoices = ( + ('session','Training Session'), + ('challenge','Challenge'), + ('test','Mandatory Test'), + ('cycletarget','Total for a time period'), + ('coursetest','OTW test over a course'), + ) + sessionmodechoices = ( ('distance','Distance'), ('time','Time'), @@ -2008,6 +2027,8 @@ class PlannedSessionForm(ModelForm): def __init__(self,*args,**kwargs): super(PlannedSessionForm, self).__init__(*args, **kwargs) self.fields['course'].queryset = GeoCourse.objects.all().order_by("country","name") + self.fields['sessiontype'].choices = regularsessiontypechoices + def get_course_timezone(course): polygons = GeoPolygon.objects.filter(course = course) @@ -2288,6 +2309,13 @@ class VirtualRaceForm(ModelForm): return cd class PlannedSessionFormSmall(ModelForm): + regularsessiontypechoices = ( + ('session','Training Session'), + ('challenge','Challenge'), + ('test','Mandatory Test'), + ('cycletarget','Total for a time period'), + ('coursetest','OTW test over a course'), + ) class Meta: model = PlannedSession @@ -2316,7 +2344,11 @@ class PlannedSessionFormSmall(ModelForm): 'sessionvalue': forms.TextInput(attrs={'style':'width:5em', 'type':'number'}), 'manager': forms.HiddenInput(), - } + } + + def __init__(self,*args,**kwargs): + super(PlannedSessionFormSmall, self).__init__(*args, **kwargs) + self.fields['sessiontype'].choices = regularsessiontypechoices boattypes = mytypes.boattypes diff --git a/rowers/plannedsessions.py b/rowers/plannedsessions.py index bd7f1958..1a48c7ab 100644 --- a/rowers/plannedsessions.py +++ b/rowers/plannedsessions.py @@ -36,6 +36,10 @@ import courses import iso8601 from iso8601 import ParseError from rowers.tasks import handle_check_race_course +from rowers.tasks import ( + handle_sendemail_raceregistration,handle_sendemail_racesubmission + ) +from rowers.utils import totaltime_sec_to_string def get_indoorraces(workout): races1 = VirtualRace.objects.filter( @@ -777,7 +781,6 @@ def race_can_submit(r,race): else: return False - print 'pop' return False def race_can_resubmit(r,race): @@ -877,6 +880,130 @@ def race_can_withdraw(r,race): return True +def email_submit_race(r,race,workoutid): + try: + w = Workout.objects.get(id=workoutid) + except Workout.DoesNotExist: + return 0 + + if race.sessionmode == 'time': + wduration = timefield_to_seconds_duration(w.duration) + delta = wduration - (60.*race.sessionvalue) + + if delta > -2 and delta < 2: + w.duration = totaltime_sec_to_string(60.*race.sessionvalue) + w.save() + + + elif race.sessionmode == 'distance': + delta = w.distance - race.sessionvalue + + if delta > -5 and delta < 5: + w.distance = race.sessionvalue + w.save() + + + if race_can_register(r,race): + teamname = '' + weightcategory = w.weightcategory + sex = r.sex + if sex == 'not specified': + sex = 'male' + + if not r.birthdate: + return 0 + + age = calculate_age(r.birthdate) + + adaptiveclass = r.adaptiveclass + boatclass = w.workouttype + + record = IndoorVirtualRaceResult( + userid = r.id, + teamname=teamname, + race=race, + username = u'{f} {l}'.format( + f = r.user.first_name, + l = r.user.last_name + ), + weightcategory=weightcategory, + adaptiveclass=adaptiveclass, + duration=dt.time(0,0), + boatclass=boatclass, + coursecompleted=False, + sex=sex, + age=age + ) + + record.save() + + result = add_rower_race(r,race) + + otherrecords = IndoorVirtualRaceResult.objects.filter( + race = race) + + + for otherrecord in otherrecords: + otheruser = Rower.objects.get(id=otherrecord.userid) + othername = otheruser.user.first_name+' '+otheruser.user.last_name + registeredname = r.user.first_name+' '+r.user.last_name + if otherrecord.emailnotifications: + job = myqueue( + queue, + handle_sendemail_raceregistration, + otheruser.user.email, othername, + registeredname, + race.name, + race.id + ) + + + if race_can_submit(r,race): + records = IndoorVirtualRaceResult.objects.filter( + userid = r.id, + race=race + ) + + if not records: + return 0 + + record = records[0] + + workouts = Workout.objects.filter(id=w.id) + + result,comments,errors,jobid = add_workout_indoorrace( + workouts,race,r,recordid=record.id + ) + + + if result: + otherrecords = IndoorVirtualRaceResult.objects.filter( + race = race) + + for otherrecord in otherrecords: + otheruser = Rower.objects.get(id=otherrecord.userid) + othername = otheruser.user.first_name+' '+otheruser.user.last_name + registeredname = r.user.first_name+' '+r.user.last_name + if otherrecord.emailnotifications: + job = myqueue( + queue, + handle_sendemail_racesubmission, + otheruser.user.email, othername, + registeredname, + race.name, + race.id + ) + + return 1 + else: + return 0 + else: + + return 0 + + return 0 + + def race_can_register(r,race): if race.sessiontype == 'race': recordobj = VirtualRaceResult diff --git a/rowers/stravastuff.py b/rowers/stravastuff.py index ac19fb00..36f0cb52 100644 --- a/rowers/stravastuff.py +++ b/rowers/stravastuff.py @@ -107,7 +107,7 @@ def get_strava_workout_list(user,limit_n=0): params = {} else: params = {'per_page':limit_n} - + s = requests.get(url,headers=headers,params=params) @@ -265,6 +265,12 @@ from utils import get_strava_stream # Get a Strava workout summary data and stroke data by ID def get_workout(user,stravaid): + try: + thetoken = strava_open(user) + except NoTokenError: + s = "Token error" + return custom_exception_handler(401,s) + r = Rower.objects.get(user=user) if (r.stravatoken == '') or (r.stravatoken is None): s = "Token doesn't exist. Need to authorize" diff --git a/rowers/tasks.py b/rowers/tasks.py index 08191929..d521462c 100644 --- a/rowers/tasks.py +++ b/rowers/tasks.py @@ -1413,7 +1413,7 @@ def handle_otwsetpower(self,f1, boattype, weightvalue, secret = PROGRESS_CACHE_SECRET progressurl += "/rowers/record-progress/" - progressurl += job_id + progressurl += job_id+'/' # determine cache file name physics_cache = 'media/'+str(boattype)+'_'+str(int(weightvalue)) diff --git a/rowers/teams.py b/rowers/teams.py index d6179919..6bdbf566 100644 --- a/rowers/teams.py +++ b/rowers/teams.py @@ -133,25 +133,11 @@ def count_invites(manager): return count -def count_members(id): - t = Team.objects.get(id=id) - return Rower.objects.filter(team=t).count() def count_club_members(manager): ts = Team.objects.filter(manager=manager) return Rower.objects.filter(team__in=ts).distinct().count() -def get_club_members(manager): - ts = Team.objects.filter(manager=manager) - return Rower.objects.filter(team__in=ts).distinct() - -def get_team_members(id): - t = Team.objects.get(id=id) - return Rower.objects.filter(team=t) - -def get_team_workouts(id): - t = Team.objects.get(id=id) - return Workout.objects.filter(team=t).order_by("-date", "-starttime") # Medium level functionality diff --git a/rowers/templates/developers.html b/rowers/templates/developers.html index 889080ae..ce4f8d05 100644 --- a/rowers/templates/developers.html +++ b/rowers/templates/developers.html @@ -147,7 +147,7 @@ separately.

diff --git a/rowers/templates/plannedsession_multicreate.html b/rowers/templates/plannedsession_multicreate.html index 03716395..3c76bda1 100644 --- a/rowers/templates/plannedsession_multicreate.html +++ b/rowers/templates/plannedsession_multicreate.html @@ -8,7 +8,7 @@

Create Sessions for {{ rower.user.first_name }} {{ rower.user.last_name }}