diff --git a/rowers/admin.py b/rowers/admin.py index b16db095..3d9214f7 100644 --- a/rowers/admin.py +++ b/rowers/admin.py @@ -7,63 +7,63 @@ from django.contrib.auth.admin import UserAdmin from django.contrib.auth.models import User from .models import ( - Rower, Workout,GraphImage,FavoriteChart,SiteAnnouncement, - Team,TeamInvite,TeamRequest, - WorkoutComment,C2WorldClassAgePerformance,PlannedSession, - GeoCourse,GeoPolygon,GeoPoint,VirtualRace,VirtualRaceResult, - PaidPlan,IndoorVirtualRaceResult,ShareKey, - CourseStandard,StandardCollection, - ) + Rower, Workout, GraphImage, FavoriteChart, SiteAnnouncement, + Team, TeamInvite, TeamRequest, + WorkoutComment, C2WorldClassAgePerformance, PlannedSession, + GeoCourse, GeoPolygon, GeoPoint, VirtualRace, VirtualRaceResult, + PaidPlan, IndoorVirtualRaceResult, ShareKey, + CourseStandard, StandardCollection, +) # Register your models here so you can use them in the Admin module - # Rower details directly under the User class RowerInline(admin.StackedInline): model = Rower can_delete = False verbose_name_plural = 'rower' - filter_horizontal = ('team','friends') + filter_horizontal = ('team', 'friends') fieldsets = ( ('Billing Details', - {'fields':('street_address','city','postal_code','country','paymentprocessor','customer_id')}), + {'fields': ('street_address', 'city', 'postal_code', 'country', 'paymentprocessor', 'customer_id')}), ('Rower Plan', - {'fields':('paidplan','rowerplan','paymenttype','planexpires','teamplanexpires','protrialexpires','plantrialexpires','eurocredits','clubsize','offercoaching')}), + {'fields': ('paidplan', 'rowerplan', 'paymenttype', 'planexpires', 'teamplanexpires', 'protrialexpires', 'plantrialexpires', 'eurocredits', 'clubsize', 'offercoaching')}), ('Rower Settings', {'fields': - ('surveydone','surveydonedate','gdproptin','gdproptindate','weightcategory','sex','adaptiveclass','birthdate','getemailnotifications', - 'getimportantemails','emailbounced','defaultlandingpage', - 'defaulttimezone','showfavoritechartnotes')}), + ('surveydone', 'surveydonedate', 'gdproptin', 'gdproptindate', 'weightcategory', 'sex', 'adaptiveclass', 'birthdate', 'getemailnotifications', + 'getimportantemails', 'emailbounced', 'defaultlandingpage', + 'defaulttimezone', 'showfavoritechartnotes')}), ('Rower Zones', {'fields': - ('ftp','otwslack','pw_ut2','pw_ut1','pw_at','pw_tr','pw_an','max', - 'rest','ut2','ut1','at','tr','an','hrftp',)}), + ('ftp', 'otwslack', 'pw_ut2', 'pw_ut1', 'pw_at', 'pw_tr', 'pw_an', 'max', + 'rest', 'ut2', 'ut1', 'at', 'tr', 'an', 'hrftp',)}), ('Import/Export Keys', - {'fields':('c2token','tokenexpirydate', - 'c2refreshtoken','c2_auto_export','c2_auto_import', - 'sporttrackstoken','sporttrackstokenexpirydate', - 'sporttracksrefreshtoken', - 'sporttracks_auto_export', - 'tptoken','tptokenexpirydate','tprefreshtoken', - 'trainingpeaks_auto_export', - 'polartoken','polartokenexpirydate', - 'polarrefreshtoken','polaruserid', - 'polar_auto_import', - 'stravatoken','stravatokenexpirydate','stravarefreshtoken', - 'stravaexportas','strava_auto_export', - 'strava_auto_import', - 'garmintoken','garminrefreshtoken')}), + {'fields': ('c2token', 'tokenexpirydate', + 'c2refreshtoken', 'c2_auto_export', 'c2_auto_import', + 'sporttrackstoken', 'sporttrackstokenexpirydate', + 'sporttracksrefreshtoken', + 'sporttracks_auto_export', + 'tptoken', 'tptokenexpirydate', 'tprefreshtoken', + 'trainingpeaks_auto_export', + 'polartoken', 'polartokenexpirydate', + 'polarrefreshtoken', 'polaruserid', + 'polar_auto_import', + 'stravatoken', 'stravatokenexpirydate', 'stravarefreshtoken', + 'stravaexportas', 'strava_auto_export', + 'strava_auto_import', + 'garmintoken', 'garminrefreshtoken')}), ('Team', - {'fields':('friends','privacy','team')}), - ) + {'fields': ('friends', 'privacy', 'team')}), + ) -#class UserAdmin(UserAdmin): +# class UserAdmin(UserAdmin): class UserAdmin(admin.ModelAdmin): inlines = (RowerInline,) - list_display = ('username','email','first_name','last_name','rowerplan','clubsize') + list_display = ('username', 'email', 'first_name', + 'last_name', 'rowerplan', 'clubsize') fieldsets = ( ('Personal info', @@ -73,96 +73,121 @@ class UserAdmin(admin.ModelAdmin): {'fields': ('is_active', 'is_staff', 'is_superuser', 'groups', 'user_permissions',)}),) - search_fields = ["username","first_name","last_name","email"] + search_fields = ["username", "first_name", "last_name", "email"] - def rowerplan(self, obj): # pragma: no cover + def rowerplan(self, obj): # pragma: no cover return obj.rower.rowerplan - def clubsize(self, obj): # pragma: no cover + def clubsize(self, obj): # pragma: no cover return obj.rower.clubsize + class WorkoutAdmin(admin.ModelAdmin): - list_display = ('date','user','name','workouttype','boattype') + list_display = ('date', 'user', 'name', 'workouttype', 'boattype') + class FavoriteChartAdmin(admin.ModelAdmin): - list_display = ('user','xparam','yparam1','yparam2','plottype','workouttype','reststrokes') + list_display = ('user', 'xparam', 'yparam1', 'yparam2', + 'plottype', 'workouttype', 'reststrokes') + class C2WorldClassAgePerformanceAdmin(admin.ModelAdmin): - list_display = ('sex','weightcategory','age','distance','power','name','season') + list_display = ('sex', 'weightcategory', 'age', + 'distance', 'power', 'name', 'season') + class SiteAnnouncementAdmin(admin.ModelAdmin): - list_display = ('announcement','created','modified','expires','dotweet') + list_display = ('announcement', 'created', + 'modified', 'expires', 'dotweet') + class TeamAdmin(admin.ModelAdmin): - list_display = ('name','manager') + list_display = ('name', 'manager') + class TeamInviteAdmin(admin.ModelAdmin): - list_display = ('issuedate','team','user','code') + list_display = ('issuedate', 'team', 'user', 'code') + class TeamRequestAdmin(admin.ModelAdmin): - list_display = ('issuedate','team','user','code') + list_display = ('issuedate', 'team', 'user', 'code') + class WorkoutCommentAdmin(admin.ModelAdmin): - list_display = ('created','user','workout') + list_display = ('created', 'user', 'workout') + class PlannedSessionAdmin(admin.ModelAdmin): - list_display = ('name','startdate','enddate','manager','sessionvalue','sessionunit') + list_display = ('name', 'startdate', 'enddate', + 'manager', 'sessionvalue', 'sessionunit') + class GraphImageAdmin(admin.ModelAdmin): - list_display = ('creationdatetime','workout','filename') + list_display = ('creationdatetime', 'workout', 'filename') + class GeoPolygonInline(admin.StackedInline): model = GeoPolygon + class ShareKeyAdmin(admin.ModelAdmin): - list_display = ('location','creation_date','expiration_date') + list_display = ('location', 'creation_date', 'expiration_date') class GeoCourseAdmin(admin.ModelAdmin): - list_display = ('manager','name') + list_display = ('manager', 'name') inlines = (GeoPolygonInline,) search_fields = ['name'] class VirtualRaceAdmin(admin.ModelAdmin): - list_display = ('manager','name','startdate','country') - search_fields = ['name','country'] + list_display = ('manager', 'name', 'startdate', 'country') + search_fields = ['name', 'country'] + class VirtualRaceResultAdmin(admin.ModelAdmin): - list_display = ('race','userid','username','boattype','age','weightcategory') - search_fields = ['race__name','username'] + list_display = ('race', 'userid', 'username', + 'boattype', 'age', 'weightcategory') + search_fields = ['race__name', 'username'] + class IndoorVirtualRaceResultAdmin(admin.ModelAdmin): - list_display = ('race','userid','username','boatclass','age','weightcategory') - search_fields = ['race__name','username'] + list_display = ('race', 'userid', 'username', + 'boatclass', 'age', 'weightcategory') + search_fields = ['race__name', 'username'] + class PaidPlanAdmin(admin.ModelAdmin): - list_display = ('name','shortname','price','paymenttype','paymentprocessor','external_id') + list_display = ('name', 'shortname', 'price', 'paymenttype', + 'paymentprocessor', 'external_id') + class StandardCollectionAdmin(admin.ModelAdmin): - list_display = ('name','manager') + list_display = ('name', 'manager') + class CourseStandardAdmin(admin.ModelAdmin): - list_display = ('name','standardcollection') + list_display = ('name', 'standardcollection') + admin.site.unregister(User) -admin.site.register(User,UserAdmin) -admin.site.register(Workout,WorkoutAdmin) -admin.site.register(GraphImage,GraphImageAdmin) -admin.site.register(Team,TeamAdmin) -admin.site.register(FavoriteChart,FavoriteChartAdmin) -admin.site.register(SiteAnnouncement,SiteAnnouncementAdmin) -admin.site.register(TeamInvite,TeamInviteAdmin) -admin.site.register(TeamRequest,TeamRequestAdmin) -admin.site.register(WorkoutComment,WorkoutCommentAdmin) +admin.site.register(User, UserAdmin) +admin.site.register(Workout, WorkoutAdmin) +admin.site.register(GraphImage, GraphImageAdmin) +admin.site.register(Team, TeamAdmin) +admin.site.register(FavoriteChart, FavoriteChartAdmin) +admin.site.register(SiteAnnouncement, SiteAnnouncementAdmin) +admin.site.register(TeamInvite, TeamInviteAdmin) +admin.site.register(TeamRequest, TeamRequestAdmin) +admin.site.register(WorkoutComment, WorkoutCommentAdmin) admin.site.register(C2WorldClassAgePerformance, C2WorldClassAgePerformanceAdmin) -admin.site.register(PlannedSession,PlannedSessionAdmin) +admin.site.register(PlannedSession, PlannedSessionAdmin) admin.site.register(GeoCourse, GeoCourseAdmin) admin.site.register(VirtualRace, VirtualRaceAdmin) admin.site.register(VirtualRaceResult, VirtualRaceResultAdmin) admin.site.register(IndoorVirtualRaceResult, IndoorVirtualRaceResultAdmin) -admin.site.register(PaidPlan,PaidPlanAdmin) -admin.site.register(ShareKey,ShareKeyAdmin) -admin.site.register(CourseStandard,CourseStandardAdmin) -admin.site.register(StandardCollection,StandardCollectionAdmin) +admin.site.register(PaidPlan, PaidPlanAdmin) +admin.site.register(ShareKey, ShareKeyAdmin) +admin.site.register(CourseStandard, CourseStandardAdmin) +admin.site.register(StandardCollection, StandardCollectionAdmin) diff --git a/rowers/alerts.py b/rowers/alerts.py index 57d5f4ee..c2414e62 100644 --- a/rowers/alerts.py +++ b/rowers/alerts.py @@ -1,25 +1,27 @@ from rowers.models import Alert, Condition, User, Rower, Workout from rowers.teams import coach_getcoachees -from rowers.dataprep import getsmallrowdata_db,getrowdata_db +from rowers.dataprep import getsmallrowdata_db, getrowdata_db import datetime -## BASIC operations +# BASIC operations # create alert -def create_alert(manager, rower, measured,period=7, emailalert=True, - reststrokes=False, workouttype='water',boattype='1x', - name='',**kwargs): + + +def create_alert(manager, rower, measured, period=7, emailalert=True, + reststrokes=False, workouttype='water', boattype='1x', + name='', **kwargs): # check if manager is coach of rower. If not return 0 - if manager.rower != rower: # pragma: no cover + if manager.rower != rower: # pragma: no cover if rower not in coach_getcoachees(manager.rower): - return 0,'You are not allowed to create this alert' + return 0, 'You are not allowed to create this alert' m = Condition( - metric = measured['metric'], - value1 = measured['value1'], - value2 = measured['value2'], + metric=measured['metric'], + value1=measured['value1'], + value2=measured['value2'], condition=measured['condition'] - ) + ) m.save() @@ -41,23 +43,21 @@ def create_alert(manager, rower, measured,period=7, emailalert=True, for f in filters: if f['metric'] and f['condition']: m = Condition( - metric = f['metric'], - value1 = f['value1'], - value2 = f['value2'], - condition = f['condition'] + metric=f['metric'], + value1=f['value1'], + value2=f['value2'], + condition=f['condition'] ) m.save() alert.filter.add(m) - - return alert.id,'Your alert was created' - + return alert.id, 'Your alert was created' # update alert -def alert_add_filters(alert,filters): +def alert_add_filters(alert, filters): for f in alert.filter.all(): alert.filter.remove(f) f.delete() @@ -70,10 +70,10 @@ def alert_add_filters(alert,filters): if condition and metric and value1: m = Condition( - metric = f['metric'], - value1 = f['value1'], - value2 = f['value2'], - condition = f['condition'] + metric=f['metric'], + value1=f['value1'], + value2=f['value2'], + condition=f['condition'] ) m.save() @@ -85,77 +85,79 @@ def alert_add_filters(alert,filters): # get alert stats # nperiod = 0: current period, i.e. next_run - n days to today # nperiod = 1: 1 period ago , i.e. next_run -2n days to next_run -n days -def alert_get_stats(alert,nperiod=0): # pragma: no cover + + +def alert_get_stats(alert, nperiod=0): # pragma: no cover # get strokes workstrokesonly = not alert.reststrokes - startdate = (alert.next_run - datetime.timedelta(days=(nperiod+1)*alert.period-1)) + startdate = (alert.next_run - + datetime.timedelta(days=(nperiod+1)*alert.period-1)) enddate = alert.next_run - datetime.timedelta(days=(nperiod)*alert.period) columns = [alert.measured.metric] for condition in alert.filter.all(): columns += condition.metric - workouts = Workout.objects.filter(date__gte=startdate,date__lte=enddate,user=alert.rower, - workouttype=alert.workouttype,duplicate=False, + workouts = Workout.objects.filter(date__gte=startdate, date__lte=enddate, user=alert.rower, + workouttype=alert.workouttype, duplicate=False, boattype=alert.boattype) ids = [w.id for w in workouts] - df = getsmallrowdata_db(columns,ids=ids,doclean=True,workstrokesonly=workstrokesonly) + df = getsmallrowdata_db(columns, ids=ids, doclean=True, + workstrokesonly=workstrokesonly) if df.empty: return { - 'workouts':workouts.count(), - 'startdate':startdate, - 'enddate':enddate, - 'nr_strokes':0, - 'nr_strokes_qualifying':0, - 'percentage':0, - 'nperiod':nperiod, + 'workouts': workouts.count(), + 'startdate': startdate, + 'enddate': enddate, + 'nr_strokes': 0, + 'nr_strokes_qualifying': 0, + 'percentage': 0, + 'nperiod': nperiod, 'median': 0, 'median_q': 0, 'standard_dev': 0, } # check if filters are in columns list - pdcolumns = set(df.columns) # pragma: no cover + pdcolumns = set(df.columns) # pragma: no cover # drop strokes through filter - if set(columns) <= pdcolumns: # pragma: no cover + if set(columns) <= pdcolumns: # pragma: no cover for condition in alert.filter.all(): if condition.condition == '>': mask = df[condition.metric] > condition.value1 - df.loc[mask,alert.measured.metric] = np.nan + df.loc[mask, alert.measured.metric] = np.nan elif condition.condition == '<': mask = df[condition.metric] < condition.value1 - df.loc[mask,alert.measured.metric] = np.nan + df.loc[mask, alert.measured.metric] = np.nan elif condition.condition == 'between': mask = df[condition.metric] > condition.value1 mask2 = df[condition.metric] < condition.value2 - df.loc[mask & mask2,alert.measured.metric] = np.nan + df.loc[mask & mask2, alert.measured.metric] = np.nan elif condition.condition == '=': mask = df[condition.metric] == condition.value1 - df.loc[mask,alert.measured.metric] = np.nan + df.loc[mask, alert.measured.metric] = np.nan - df.dropna(inplace=True,axis=0) - else: # pragma: no cover + df.dropna(inplace=True, axis=0) + else: # pragma: no cover return { - 'workouts':workouts.count(), - 'startdate':startdate, - 'enddate':enddate, - 'nr_strokes':0, - 'nr_strokes_qualifying':0, - 'percentage':0, - 'nperiod':nperiod, + 'workouts': workouts.count(), + 'startdate': startdate, + 'enddate': enddate, + 'nr_strokes': 0, + 'nr_strokes_qualifying': 0, + 'percentage': 0, + 'nperiod': nperiod, 'median': 0, 'median_q': 0, 'standard_dev': 0, } - # count strokes nr_strokes = len(df) - # count qualifying if alert.measured.condition == '>': mask = df[alert.measured.metric] > alert.measured.value1 @@ -183,24 +185,26 @@ def alert_get_stats(alert,nperiod=0): # pragma: no cover std = df[alert.measured.metric].std() return { - 'workouts':workouts.count(), - 'startdate':startdate, - 'enddate':enddate, - 'nr_strokes':nr_strokes, - 'nr_strokes_qualifying':nr_strokes_qualifying, + 'workouts': workouts.count(), + 'startdate': startdate, + 'enddate': enddate, + 'nr_strokes': nr_strokes, + 'nr_strokes_qualifying': nr_strokes_qualifying, 'percentage': percentage, - 'nperiod':nperiod, - 'median':median, - 'median_q':median_q, - 'standard_dev':std, + 'nperiod': nperiod, + 'median': median, + 'median_q': median_q, + 'standard_dev': std, } # run alert report # check alert permission -def checkalertowner(alert,user): + + +def checkalertowner(alert, user): if alert.manager == user: return True - if alert.rower.user == user: # pragma: no cover + if alert.rower.user == user: # pragma: no cover return True - return False # pragma: no cover + return False # pragma: no cover diff --git a/rowers/apps.py b/rowers/apps.py index 4cdc7101..750e0562 100644 --- a/rowers/apps.py +++ b/rowers/apps.py @@ -6,5 +6,7 @@ from __future__ import unicode_literals from django.apps import AppConfig # Store metadata for the app + + class RowersConfig(AppConfig): name = 'rowers' diff --git a/rowers/braintreestuff.py b/rowers/braintreestuff.py index 17584108..081d979e 100644 --- a/rowers/braintreestuff.py +++ b/rowers/braintreestuff.py @@ -1,4 +1,25 @@ from __future__ import absolute_import +from rowers.models import Rower, PaidPlan, CoachingGroup +from rowers.utils import ProcessorCustomerError +from rowsandall_app.settings import ( + BRAINTREE_MERCHANT_ID, BRAINTREE_PUBLIC_KEY, BRAINTREE_PRIVATE_KEY, + BRAINTREE_SANDBOX_MERCHANT_ID, BRAINTREE_SANDBOX_PUBLIC_KEY, + BRAINTREE_SANDBOX_PRIVATE_KEY, BRAINTREE_MERCHANT_ACCOUNT_ID +) +import pandas as pd +from rowers.utils import dologging +from rowers import credits +from rowers.tasks import ( + handle_send_email_transaction, + handle_send_email_subscription_update, + handle_send_email_subscription_create, + handle_send_email_failed_cancel, + # handle_send_email_transaction_notification, +) +from rowers.utils import myqueue +import rowers.fakturoid as fakturoid +from braintree.exceptions.invalid_signature_error import InvalidSignatureError +import time from __future__ import division from __future__ import print_function from __future__ import unicode_literals @@ -13,32 +34,8 @@ queue = django_rq.get_queue('default') queuelow = django_rq.get_queue('low') queuehigh = django_rq.get_queue('low') -import time -from braintree.exceptions.invalid_signature_error import InvalidSignatureError -import rowers.fakturoid as fakturoid - -from rowers.utils import myqueue -from rowers.tasks import ( - handle_send_email_transaction, - handle_send_email_subscription_update, - handle_send_email_subscription_create, - handle_send_email_failed_cancel, - #handle_send_email_transaction_notification, - ) - -from rowers import credits -from rowers.utils import dologging - -import pandas as pd - -from rowsandall_app.settings import ( - BRAINTREE_MERCHANT_ID,BRAINTREE_PUBLIC_KEY,BRAINTREE_PRIVATE_KEY, - BRAINTREE_SANDBOX_MERCHANT_ID,BRAINTREE_SANDBOX_PUBLIC_KEY, - BRAINTREE_SANDBOX_PRIVATE_KEY, BRAINTREE_MERCHANT_ACCOUNT_ID - ) - -if settings.DEBUG or 'dev' in settings.SITE_URL: # pragma: no cover +if settings.DEBUG or 'dev' in settings.SITE_URL: # pragma: no cover gateway = braintree.BraintreeGateway( braintree.Configuration( braintree.Environment.Sandbox, @@ -58,12 +55,9 @@ else: ) -from rowers.models import Rower,PaidPlan, CoachingGroup -from rowers.utils import ProcessorCustomerError - def process_webhook(notification): - if not settings.TESTING: # pragma: no cover - with open('braintreewebhooks.log','a') as f: + if not settings.TESTING: # pragma: no cover + with open('braintreewebhooks.log', 'a') as f: t = time.localtime() timestamp = time.strftime('%b-%d-%Y_%H%M', t) try: @@ -73,8 +67,8 @@ def process_webhook(notification): if notification.kind == 'subscription_charged_successfully': subscription = notification.subscription rs = Rower.objects.filter(subscription_id=subscription.id) - if rs.count() == 0: # pragma: no cover - dologging('braintreewebhooks.log','Could not find rowers with subscription ID {id}'.format( + if rs.count() == 0: # pragma: no cover + dologging('braintreewebhooks.log', 'Could not find rowers with subscription ID {id}'.format( id=subscription.id )) else: @@ -82,57 +76,61 @@ def process_webhook(notification): transactions = subscription.transactions if transactions: amount = int(transactions[0].amount) - eurocredits = credits.upgrade(amount,r) - eurocredits = credits.upgrade(amount,r) + eurocredits = credits.upgrade(amount, r) + eurocredits = credits.upgrade(amount, r) return send_invoice(notification.subscription) if notification.kind == 'subscription_canceled': subscription = notification.subscription rs = Rower.objects.filter(subscription_id=subscription.id) - if rs.count() == 0: # pragma: no cover - with open('braintreewebhooks.log','a') as f: - f.write('Could not find rowers with subscription ID '+subscription.id+'\n') + if rs.count() == 0: # pragma: no cover + with open('braintreewebhooks.log', 'a') as f: + f.write('Could not find rowers with subscription ID ' + + subscription.id+'\n') return 0 r = rs[0] - result,mesg,errormsg = cancel_subscription(r,subscription.id) + result, mesg, errormsg = cancel_subscription(r, subscription.id) if result: - with open('braintreewebhooks.log','a') as f: + with open('braintreewebhooks.log', 'a') as f: f.write('Subscription canceled: '+str(subscription.id)+'\n') return subscription.id - with open('braintreewebhooks.log','a') as f: # pragma: no cover - f.write('Could not cancel Subscription: '+str(subscription.id)+'\n') - return 0 # pragma: no cover + with open('braintreewebhooks.log', 'a') as f: # pragma: no cover + f.write('Could not cancel Subscription: ' + + str(subscription.id)+'\n') + return 0 # pragma: no cover return 0 + def send_invoice(subscription): - with open('braintreewebhooks.log','a') as f: + with open('braintreewebhooks.log', 'a') as f: t = time.localtime() timestamp = time.strftime('%b-%d-%Y_%H%M', t) f.write('Subscription ID '+str(subscription.id)+'\n') subscription_id = subscription.id rs = Rower.objects.filter(subscription_id=subscription_id) - if rs.count() == 0: # pragma: no cover + if rs.count() == 0: # pragma: no cover return 0 else: r = rs[0] - with open('braintreewebhooks.log','a') as f: + with open('braintreewebhooks.log', 'a') as f: f.write('Rower '+str(r)+'\n') fakturoid_contact_id = fakturoid.get_contacts(r) - with open('braintreewebhooks.log','a') as f: + with open('braintreewebhooks.log', 'a') as f: f.write('Fakturoid Contact ID '+str(fakturoid_contact_id)+'\n') - if not fakturoid_contact_id: # pragma: no cover + if not fakturoid_contact_id: # pragma: no cover fakturoid_contact_id = fakturoid.create_contact(r) - with open('braintreewebhooks.log','a') as f: - f.write('Created Fakturoid Contact ID '+str(fakturoid_contact_id)+'\n') + with open('braintreewebhooks.log', 'a') as f: + f.write('Created Fakturoid Contact ID ' + + str(fakturoid_contact_id)+'\n') transactions = subscription.transactions if transactions: amount = transactions[0].amount - with open('braintreewebhooks.log','a') as f: + with open('braintreewebhooks.log', 'a') as f: f.write('Transaction amount '+str(amount)+'\n') - id = fakturoid.create_invoice(r,amount,subscription_id,dosend=True, + id = fakturoid.create_invoice(r, amount, subscription_id, dosend=True, contact_id=fakturoid_contact_id) return id - return 0 # pragma: no cover + return 0 # pragma: no cover def webhook(request): @@ -140,41 +138,40 @@ def webhook(request): webhook_notification = gateway.webhook_notification.parse( str(request.POST['bt_signature']), request.POST['bt_payload']) - except InvalidSignatureError: # pragma: no cover + except InvalidSignatureError: # pragma: no cover return 4 result = process_webhook(webhook_notification) - return result -def create_customer(rower,force=False): + +def create_customer(rower, force=False): if not rower.customer_id or force: result = gateway.customer.create( { - 'first_name':rower.user.first_name, - 'last_name':rower.user.last_name, - 'email':rower.user.email, - }) - if not result.is_success: # pragma: no cover + 'first_name': rower.user.first_name, + 'last_name': rower.user.last_name, + 'email': rower.user.email, + }) + if not result.is_success: # pragma: no cover raise ProcessorCustomerError else: rower.customer_id = result.customer.id rower.paymentprocessor = 'braintree' rower.save() return rower.customer_id - else: # pragma: no cover + else: # pragma: no cover return rower.customer_id - def get_client_token(rower): try: client_token = gateway.client_token.generate({ - "customer_id":rower.customer_id, + "customer_id": rower.customer_id, }) - except ValueError: # pragma: no cover - customer_id = create_customer(rower,force=True) + except ValueError: # pragma: no cover + customer_id = create_customer(rower, force=True) client_token = gateway.client_token.generate({ "customer_id": customer_id, @@ -182,7 +179,8 @@ def get_client_token(rower): return client_token -def get_plans_costs(): # pragma: no cover + +def get_plans_costs(): # pragma: no cover plans = gateway.plan.all() localplans = PaidPlan.object.filter(paymentprocessor='braintree') @@ -195,21 +193,23 @@ def get_plans_costs(): # pragma: no cover return plans -def make_payment(rower,data): + +def make_payment(rower, data): nonce_from_the_client = data['payment_method_nonce'] nonce = gateway.payment_method_nonce.find(nonce_from_the_client) info = nonce.three_d_secure_info - if nonce.type.lower() == 'creditcard': # pragma: no cover + if nonce.type.lower() == 'creditcard': # pragma: no cover if info is None or not info.liability_shifted: - return False,0 + return False, 0 amount = data['amount'] amount = '{amount}'.format(amount=amount) if 'plan' in data: theplan = data['plan'] - additional_text = 'Rowsandall Purchase Plan nr {theplan}'.format(theplan=theplan) - else: # pragma: no cover + additional_text = 'Rowsandall Purchase Plan nr {theplan}'.format( + theplan=theplan) + else: # pragma: no cover additional_text = 'Rowsandall Purchase' result = gateway.transaction.sale({ @@ -223,54 +223,53 @@ def make_payment(rower,data): transaction = result.transaction amount = transaction.amount name = '{f} {l}'.format( - f = rower.user.first_name, - l = rower.user.last_name, - ) + f=rower.user.first_name, + l=rower.user.last_name, + ) fakturoid_contact_id = fakturoid.get_contacts(rower) if not fakturoid_contact_id: fakturoid_contact_id = fakturoid.create_contact(rower) - id = fakturoid.create_invoice(rower,amount,transaction.id,dosend=True,contact_id=fakturoid_contact_id, + id = fakturoid.create_invoice(rower, amount, transaction.id, dosend=True, contact_id=fakturoid_contact_id, name=additional_text) try: - job = myqueue(queuehigh,handle_send_email_transaction, - name, rower.user.email, amount) - job = myqueue(queuehigh,handle_send_email_transation_notification, + job = myqueue(queuehigh, handle_send_email_transaction, + name, rower.user.email, amount) + job = myqueue(queuehigh, handle_send_email_transation_notification, name.rower.user.email, amount, additional_text) - except: # pragma: no cover + except: # pragma: no cover pass - return amount,True - else: # pragma: no cover - return 0,False + return amount, True + else: # pragma: no cover + return 0, False -def update_subscription(rower,data,method='up'): + +def update_subscription(rower, data, method='up'): planid = data['plan'] plan = PaidPlan.objects.get(id=planid) nonce_from_the_client = data['payment_method_nonce'] nonce = gateway.payment_method_nonce.find(nonce_from_the_client) info = nonce.three_d_secure_info - if nonce.type.lower() == 'creditcard': # pragma: no cover + if nonce.type.lower() == 'creditcard': # pragma: no cover if info is None or not info.liability_shifted: - return False,0 + return False, 0 amount = data['amount'] amount = '{amount:.2f}'.format(amount=amount) - gatewaydata = { - "price": amount, - "plan_id": plan.external_id, - "payment_method_nonce": nonce_from_the_client, - "options": { - "prorate_charges":True, - }, - } - + "price": amount, + "plan_id": plan.external_id, + "payment_method_nonce": nonce_from_the_client, + "options": { + "prorate_charges": True, + }, + } if plan.paymenttype == 'single': gatewaydata['number_of_billing_cycles'] = 1 - else: # pragma: no cover + else: # pragma: no cover gatewaydata['never_expires'] = True try: @@ -278,14 +277,14 @@ def update_subscription(rower,data,method='up'): rower.subscription_id, gatewaydata ) - except: # pragma: no cover - return False,0 + except: # pragma: no cover + return False, 0 if result.is_success: yesterday = (timezone.now()-datetime.timedelta(days=1)).date() rower.paidplan = plan amount_int = int(float(amount)) - eurocredits = credits.upgrade(amount_int,rower) + eurocredits = credits.upgrade(amount_int, rower) rower.planexpires = result.subscription.billing_period_end_date rower.teamplanexpires = result.subscription.billing_period_end_date rower.clubsize = plan.clubsize @@ -296,21 +295,22 @@ def update_subscription(rower,data,method='up'): rower.plantrialexpires = yesterday rower.save() name = '{f} {l}'.format( - f = rower.user.first_name, - l = rower.user.last_name, - ) + f=rower.user.first_name, + l=rower.user.last_name, + ) if rower.paidplan != 'coach': try: coachgroup = rower.mycoachgroup - except CoachingGroup.DoesNotExist: # pragma: no cover + except CoachingGroup.DoesNotExist: # pragma: no cover coachgroup = CoachingGroup() coachgroup.save() rower.mycoachgroup = coachgroup rower.save() - athletes = Rower.objects.filter(coachinggroups__in=[rower.mycoachgroup]).distinct() - for athlete in athletes: # pragma: no cover + athletes = Rower.objects.filter( + coachinggroups__in=[rower.mycoachgroup]).distinct() + for athlete in athletes: # pragma: no cover athlete.coachinggroups.remove(rower.mycoachgroup) if method == 'up': @@ -318,12 +318,11 @@ def update_subscription(rower,data,method='up'): if transactions: amount = transactions[0].amount - else: # pragma: no cover + else: # pragma: no cover amount = 0 - else: # pragma: no cover + else: # pragma: no cover amount = 0 - job = myqueue(queuehigh, handle_send_email_subscription_update, name, rower.user.email, @@ -331,60 +330,58 @@ def update_subscription(rower,data,method='up'): plan.paymenttype, plan.price, amount, - result.subscription.billing_period_end_date.strftime('%Y-%m-%d'), + result.subscription.billing_period_end_date.strftime( + '%Y-%m-%d'), method) - return True,amount - else: # pragma: no cover + return True, amount + else: # pragma: no cover errors = result.errors.for_object("subscription") codes = [str(e.code) for e in errors] create_new = False - proceed_codes = ['81901','81910'] + proceed_codes = ['81901', '81910'] for c in codes: if c in proceed_codes: create_new = True if create_new: - return create_subscription(rower,data) + return create_subscription(rower, data) - return False,0 + return False, 0 - return False,0 # pragma: no cover + return False, 0 # pragma: no cover -def create_subscription(rower,data): +def create_subscription(rower, data): nonce_from_the_client = data['payment_method_nonce'] nonce = gateway.payment_method_nonce.find(nonce_from_the_client) info = nonce.three_d_secure_info paymenttype = nonce.type - if nonce.type != 'PayPalAccount': # pragma: no cover - if info is None or not info.liability_shifted: - return False,0 + if nonce.type != 'PayPalAccount': # pragma: no cover + if info is None or not info.liability_shifted: + return False, 0 amount = data['amount'] planid = data['plan'] plan = PaidPlan.objects.get(id=planid) - # create or find payment method result = gateway.payment_method.create({ "customer_id": rower.customer_id, "payment_method_nonce": nonce_from_the_client - }) - - + }) if result.is_success: payment_method_token = result.payment_method.token - else: # pragma: no cover - return False,0 + else: # pragma: no cover + return False, 0 result = gateway.subscription.create({ "payment_method_token": payment_method_token, "plan_id": plan.external_id, -# "merchant_account_id": BRAINTREE_MERCHANT_ACCOUNT_ID, - }) + # "merchant_account_id": BRAINTREE_MERCHANT_ACCOUNT_ID, + }) if result.is_success: yesterday = (timezone.now()-datetime.timedelta(days=1)).date() @@ -400,10 +397,9 @@ def create_subscription(rower,data): rower.save() name = '{f} {l}'.format( - f = rower.user.first_name, - l = rower.user.last_name, - ) - + f=rower.user.first_name, + l=rower.user.last_name, + ) recurring = plan.paymenttype @@ -417,33 +413,33 @@ def create_subscription(rower,data): plan.price, result.subscription.billing_period_end_date.strftime('%Y-%m-%d') ) - return True,plan.price - else: # pragma: no cover - return False,0 + return True, plan.price + else: # pragma: no cover + return False, 0 + + return False, 0 # pragma: no cover - return False,0 # pragma: no cover - -def cancel_subscription(rower,id): +def cancel_subscription(rower, id): themessages = [] errormessages = [] try: - result = gateway.subscription.cancel(id) + result = gateway.subscription.cancel(id) themessages.append("Subscription canceled") - except: # pragma: no cover - 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) + except: # pragma: no cover + 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) job = myqueue(queuehigh, handle_send_email_failed_cancel, - name, rower.user.email,rower.user.username,id) + name, rower.user.email, rower.user.username, id) return False, themessages, errormessages - basicplans = PaidPlan.objects.filter(price=0,paymentprocessor='braintree') + basicplans = PaidPlan.objects.filter(price=0, paymentprocessor='braintree') rower.paidplan = basicplans[0] rower.teamplanexpires = timezone.now() rower.planexpires = timezone.now() @@ -453,35 +449,36 @@ def cancel_subscription(rower,id): rower.save() themessages.append("Your plan was reset to basic") - return True, themessages,errormessages + return True, themessages, errormessages def find_subscriptions(rower): try: result = gateway.customer.find(rower.customer_id) - except: # pragma: no cover - raise ProcessorCustomerError("We could not find the customer in the database") + except: # pragma: no cover + raise ProcessorCustomerError( + "We could not find the customer in the database") active_subscriptions = [] cards = result.credit_cards - for card in cards: # pragma: no cover + for card in cards: # pragma: no cover for subscription in card.subscriptions: if subscription.status == 'Active': active_subscriptions.append(subscription) try: paypal_accounts = result.paypal_accounts - for account in paypal_accounts: # pragma: no cover + for account in paypal_accounts: # pragma: no cover for subscription in account.subscriptions: if subscription.status == 'Active': active_subscriptions.append(subscription) - except AttributeError: # pragma: no cover + except AttributeError: # pragma: no cover pass result = [] - for subscription in active_subscriptions: # pragma: no cover + for subscription in active_subscriptions: # pragma: no cover plan = PaidPlan.objects.filter(paymentprocessor="braintree", external_id=subscription.plan_id)[0] @@ -493,19 +490,20 @@ def find_subscriptions(rower): 'id': subscription.id, 'plan': plan.name, 'never_expires': subscription.never_expires - } + } result.append(thedict) return result -def get_transactions(start_date,end_date): # pragma: no cover + +def get_transactions(start_date, end_date): # pragma: no cover results = gateway.transaction.search( braintree.TransactionSearch.created_at.between( start_date, end_date, - ) ) + ) amounts = [] countries = [] @@ -534,9 +532,9 @@ def get_transactions(start_date,end_date): # pragma: no cover r = rs[0] countries.append(r.country) addresses.append('{street}, {city}, {postal_code}'.format( - street = r.street_address, - city = r.city, - postal_code = r.postal_code)) + street=r.street_address, + city=r.city, + postal_code=r.postal_code)) ownids.append(r.id) usernames.append(r.user.username) @@ -547,11 +545,10 @@ def get_transactions(start_date,end_date): # pragma: no cover usernames.append('unknown') addresses.append('') - emails.append(transaction.customer_details.email) names.append('{f} {l}'.format( - f = transaction.customer['first_name'], - l = transaction.customer['last_name'] + f=transaction.customer['first_name'], + l=transaction.customer['last_name'] ) ) customerids.append(transaction.customer['id']) @@ -564,27 +561,26 @@ def get_transactions(start_date,end_date): # pragma: no cover transaction.credit_card_details.country_of_issuance) statuses.append(transaction.status) - df = pd.DataFrame({ - 'name':names, - 'email':emails, - 'date':dates, - 'amount':amounts, - 'currency':currencies, - 'country':countries, - 'card_country':card_countries, - 'status':statuses, - 'username':usernames, - 'user_id':ownids, - 'customer_id':customerids, - 'transaction_id':transactionids, - 'subscription_id':subscriptionids, - 'address':addresses + 'name': names, + 'email': emails, + 'date': dates, + 'amount': amounts, + 'currency': currencies, + 'country': countries, + 'card_country': card_countries, + 'status': statuses, + 'username': usernames, + 'user_id': ownids, + 'customer_id': customerids, + 'transaction_id': transactionids, + 'subscription_id': subscriptionids, + 'address': addresses } ) return df -def mocktest(rower): # pragma: no cover +def mocktest(rower): # pragma: no cover return '5' diff --git a/rowers/c2stuff.py b/rowers/c2stuff.py index 6fdbda9c..d85962d0 100644 --- a/rowers/c2stuff.py +++ b/rowers/c2stuff.py @@ -1,4 +1,7 @@ from __future__ import absolute_import +from django.core.exceptions import PermissionDenied +from rowers.models import C2WorldClassAgePerformance +from rowers.utils import myqueue from __future__ import division from __future__ import print_function from __future__ import unicode_literals @@ -13,7 +16,7 @@ import datetime from requests import Request, Session import rowers.mytypes as mytypes from rowers.mytypes import otwtypes -from rowers.rower_rules import is_workout_user,ispromember +from rowers.rower_rules import is_workout_user, ispromember from iso8601 import ParseError import numpy @@ -27,25 +30,22 @@ from rowers.utils import dologging from rowsandall_app.settings import ( C2_CLIENT_ID, C2_REDIRECT_URI, C2_CLIENT_SECRET, UPLOAD_SERVICE_URL, UPLOAD_SERVICE_SECRET - ) +) from rowers.tasks import ( handle_c2_import_stroke_data, handle_c2_sync, handle_c2_async_workout, handle_c2_getworkout - ) +) import django_rq queue = django_rq.get_queue('default') queuelow = django_rq.get_queue('low') queuehigh = django_rq.get_queue('high') -from rowers.utils import myqueue -from rowers.models import C2WorldClassAgePerformance -from django.core.exceptions import PermissionDenied -def getagegrouprecord(age,sex='male',weightcategory='hwt', - distance=2000,duration=None,indf=pd.DataFrame()): +def getagegrouprecord(age, sex='male', weightcategory='hwt', + distance=2000, duration=None, indf=pd.DataFrame()): - if not indf.empty: # pragma: no cover + if not indf.empty: # pragma: no cover if not duration: df = indf[indf['distance'] == distance] else: @@ -63,7 +63,7 @@ def getagegrouprecord(age,sex='male',weightcategory='hwt', ) ) else: - duration=60*int(duration) + duration = 60*int(duration) df = pd.DataFrame( list( C2WorldClassAgePerformance.objects.filter( @@ -79,15 +79,17 @@ def getagegrouprecord(age,sex='male',weightcategory='hwt', powers = df['power'] #poly_coefficients = np.polyfit(ages,powers,6) - fitfunc = lambda pars, x: np.abs(pars[0])*(1-x/max(120,pars[1]))-np.abs(pars[2])*np.exp(-x/np.abs(pars[3]))+np.abs(pars[4])*(np.sin(np.pi*x/max(50,pars[5]))) - errfunc = lambda pars, x,y: fitfunc(pars,x)-y + def fitfunc(pars, x): return np.abs(pars[0])*(1-x/max(120, pars[1]))-np.abs( + pars[2])*np.exp(-x/np.abs(pars[3]))+np.abs(pars[4])*(np.sin(np.pi*x/max(50, pars[5]))) - p0 = [700,120,700,10,100,100] + def errfunc(pars, x, y): return fitfunc(pars, x)-y + + p0 = [700, 120, 700, 10, 100, 100] try: - p1, success = optimize.leastsq(errfunc,p0[:], - args = (ages,powers)) - except: # pragma: no cover + p1, success = optimize.leastsq(errfunc, p0[:], + args=(ages, powers)) + except: # pragma: no cover p1 = p0 success = 0 @@ -97,7 +99,7 @@ def getagegrouprecord(age,sex='male',weightcategory='hwt', #power = np.polyval(poly_coefficients,age) power = 0.5*(np.abs(power)+power) - else: # pragma: no cover + else: # pragma: no cover power = 0 else: power = 0 @@ -116,8 +118,8 @@ oauth_data = { 'expirydatename': 'tokenexpirydate', 'bearer_auth': True, 'base_url': "https://log.concept2.com/oauth/access_token", - 'scope':'write', - } + 'scope': 'write', +} # Checks if user has Concept2 tokens, resets tokens if they are @@ -128,28 +130,29 @@ def c2_open(user): s = "Token doesn't exist. Need to authorize" raise NoTokenError("User has no token") else: - if (timezone.now()>r.tokenexpirydate): + if (timezone.now() > r.tokenexpirydate): res = rower_c2_token_refresh(user) - if res == None: # pragma: no cover + if res == None: # pragma: no cover raise NoTokenError("User has no token") if res[0] != None: thetoken = res[0] - else: # pragma: no cover + else: # pragma: no cover raise NoTokenError("User has no token") else: thetoken = r.c2token return thetoken -def get_c2_workouts(rower,page=1,do_async=True): + +def get_c2_workouts(rower, page=1, do_async=True): try: thetoken = c2_open(rower.user) - except NoTokenError: # pragma: no cover + except NoTokenError: # pragma: no cover return 0 - res = get_c2_workout_list(rower.user,page=page) + res = get_c2_workout_list(rower.user, page=page) - if (res.status_code != 200): # pragma: no cover + if (res.status_code != 200): # pragma: no cover return 0 else: c2ids = [item['id'] for item in res.json()['data']] @@ -168,13 +171,12 @@ def get_c2_workouts(rower,page=1,do_async=True): # get "blocked" c2ids parkedids = [] try: - with open('c2blocked.json','r') as c2blocked: + with open('c2blocked.json', 'r') as c2blocked: jsondata = json.load(c2blocked) parkedids = jsondata['ids'] - except FileNotFoundError: # pragma: no cover + except FileNotFoundError: # pragma: no cover pass - knownc2ids = uniqify(knownc2ids+tombstones+parkedids) newids = [c2id for c2id in c2ids if not c2id in knownc2ids] @@ -183,33 +185,34 @@ def get_c2_workouts(rower,page=1,do_async=True): newparkedids = uniqify(newids+parkedids) - with open('c2blocked.json','wt') as c2blocked: - data = {'ids':newparkedids} - json.dump(data,c2blocked) + with open('c2blocked.json', 'wt') as c2blocked: + data = {'ids': newparkedids} + json.dump(data, c2blocked) counter = 0 for c2id in newids: - if do_async: # pragma: no cover + if do_async: # pragma: no cover res = myqueue(queuehigh, - handle_c2_async_workout, - alldata, - rower.user.id, - rower.c2token, - c2id, - counter, - rower.defaulttimezone - ) + handle_c2_async_workout, + alldata, + rower.user.id, + rower.c2token, + c2id, + counter, + rower.defaulttimezone + ) #res = handle_c2_async_workout(alldata,rower.user.id,rower.c2token,c2id,counter) counter = counter+1 else: workoutid = create_async_workout(alldata, - rower.user,c2id) - + rower.user, c2id) return 1 # get workout metrics, then relay stroke data to an asynchronous task -def create_async_workout(alldata,user,c2id): + + +def create_async_workout(alldata, user, c2id): data = alldata[c2id] splitdata = None @@ -235,14 +238,14 @@ def create_async_workout(alldata,user,c2id): weightcategory = 'lwt' # Create CSV file name and save data to CSV file - csvfilename ='media/Import_'+str(c2id)+'.csv.gz' + csvfilename = 'media/Import_'+str(c2id)+'.csv.gz' totaltime = data['time']/10. duration = dataprep.totaltime_sec_to_string(totaltime) try: timezone_str = data['timezone'] - except: # pragma: no cover + except: # pragma: no cover timezone_str = 'UTC' workoutdate = startdatetime.astimezone( @@ -255,24 +258,25 @@ def create_async_workout(alldata,user,c2id): try: notes = data['comments'] name = notes[:40] - except (KeyError,TypeError): - notes = 'C2 Import Workout from {startdatetime}'.format(startdatetime=startdatetime) + except (KeyError, TypeError): + notes = 'C2 Import Workout from {startdatetime}'.format( + startdatetime=startdatetime) name = notes r = Rower.objects.get(user=user) authorizationstring = str('Bearer ' + r.c2token) headers = {'Authorization': authorizationstring, - 'user-agent': 'sanderroosendaal', - 'Content-Type': 'application/json'} + 'user-agent': 'sanderroosendaal', + 'Content-Type': 'application/json'} url2 = "https://log.concept2.com/api/users/me/results"+str(c2id) url = "https://log.concept2.com/api/users/me/results/"+str(c2id)+"/strokes" try: - s = requests.get(url,headers=headers) - except ConnectionError: # pragma: no cover + s = requests.get(url, headers=headers) + except ConnectionError: # pragma: no cover return 0 - if s.status_code != 200: # pragma: no cover + if s.status_code != 200: # pragma: no cover return 0 strokedata = pd.DataFrame.from_dict(s.json()['data']) @@ -286,112 +290,110 @@ def create_async_workout(alldata,user,c2id): unixtime = cum_time+starttimeunix # unixtime[0] = starttimeunix - seconds = 0.1*strokedata.loc[:,'t'] + seconds = 0.1*strokedata.loc[:, 't'] nr_rows = len(unixtime) - try: # pragma: no cover - latcoord = strokedata.loc[:,'lat'] - loncoord = strokedata.loc[:,'lon'] + try: # pragma: no cover + latcoord = strokedata.loc[:, 'lat'] + loncoord = strokedata.loc[:, 'lon'] except: latcoord = np.zeros(nr_rows) loncoord = np.zeros(nr_rows) - try: - strokelength = strokedata.loc[:,'strokelength'] + strokelength = strokedata.loc[:, 'strokelength'] except: strokelength = np.zeros(nr_rows) - dist2 = 0.1*strokedata.loc[:,'d'] + dist2 = 0.1*strokedata.loc[:, 'd'] try: - spm = strokedata.loc[:,'spm'] - except KeyError: # pragma: no cover + spm = strokedata.loc[:, 'spm'] + except KeyError: # pragma: no cover spm = 0*dist2 try: - hr = strokedata.loc[:,'hr'] - except KeyError: # pragma: no cover + hr = strokedata.loc[:, 'hr'] + except KeyError: # pragma: no cover hr = 0*spm - pace = strokedata.loc[:,'p']/10. - pace = np.clip(pace,0,1e4) - pace = pace.replace(0,300) + pace = strokedata.loc[:, 'p']/10. + pace = np.clip(pace, 0, 1e4) + pace = pace.replace(0, 300) velo = 500./pace power = 2.8*velo**3 - if workouttype == 'bike': # pragma: no cover + if workouttype == 'bike': # pragma: no cover velo = 1000./pace - df = pd.DataFrame({'TimeStamp (sec)':unixtime, + df = pd.DataFrame({'TimeStamp (sec)': unixtime, ' Horizontal (meters)': dist2, - ' Cadence (stokes/min)':spm, - ' HRCur (bpm)':hr, - ' longitude':loncoord, - ' latitude':latcoord, - ' Stroke500mPace (sec/500m)':pace, - ' Power (watts)':power, - ' DragFactor':np.zeros(nr_rows), - ' DriveLength (meters)':np.zeros(nr_rows), - ' StrokeDistance (meters)':strokelength, - ' DriveTime (ms)':np.zeros(nr_rows), - ' StrokeRecoveryTime (ms)':np.zeros(nr_rows), - ' AverageDriveForce (lbs)':np.zeros(nr_rows), - ' PeakDriveForce (lbs)':np.zeros(nr_rows), - ' lapIdx':lapidx, + ' Cadence (stokes/min)': spm, + ' HRCur (bpm)': hr, + ' longitude': loncoord, + ' latitude': latcoord, + ' Stroke500mPace (sec/500m)': pace, + ' Power (watts)': power, + ' DragFactor': np.zeros(nr_rows), + ' DriveLength (meters)': np.zeros(nr_rows), + ' StrokeDistance (meters)': strokelength, + ' DriveTime (ms)': np.zeros(nr_rows), + ' StrokeRecoveryTime (ms)': np.zeros(nr_rows), + ' AverageDriveForce (lbs)': np.zeros(nr_rows), + ' PeakDriveForce (lbs)': np.zeros(nr_rows), + ' lapIdx': lapidx, ' WorkoutState': 4, - ' ElapsedTime (sec)':seconds, + ' ElapsedTime (sec)': seconds, 'cum_dist': dist2 }) + df.sort_values(by='TimeStamp (sec)', ascending=True) - df.sort_values(by='TimeStamp (sec)',ascending=True) - - res = df.to_csv(csvfilename,index_label='index', - compression='gzip') + res = df.to_csv(csvfilename, index_label='index', + compression='gzip') userid = r.user.id uploadoptions = { - 'secret':UPLOAD_SERVICE_SECRET, - 'user':userid, + 'secret': UPLOAD_SERVICE_SECRET, + 'user': userid, 'file': csvfilename, 'title': title, - 'workouttype':workouttype, - 'boattype':'1x', - 'c2id':c2id, + 'workouttype': workouttype, + 'boattype': '1x', + 'c2id': c2id, } session = requests.session() newHeaders = {'Content-type': 'application/json', 'Accept': 'text/plain'} session.headers.update(newHeaders) - response = session.post(UPLOAD_SERVICE_URL,json=uploadoptions) + response = session.post(UPLOAD_SERVICE_URL, json=uploadoptions) - if response.status_code != 200: # pragma: no cover + if response.status_code != 200: # pragma: no cover return 0 try: workoutid = response.json()['id'] - except KeyError: # pragma: no cover + except KeyError: # pragma: no cover workoutid = 1 newc2id = Workout.objects.get(id=workoutid).uploadedtoc2 parkedids = [] - with open('c2blocked.json','r') as c2blocked: + with open('c2blocked.json', 'r') as c2blocked: jsondata = json.load(c2blocked) parkedids = jsondata['ids'] newparkedids = [id for id in parkedids if id != newc2id] - with open('c2blocked.json','wt') as c2blocked: - data = {'ids':newparkedids} + with open('c2blocked.json', 'wt') as c2blocked: + data = {'ids': newparkedids} c2blocked.seek(0) - json.dump(data,c2blocked) + json.dump(data, c2blocked) # summary - if 'workout' in data: # pragma: no cover + if 'workout' in data: # pragma: no cover if 'splits' in data['workout']: splitdata = data['workout']['splits'] elif 'intervals' in data['workout']: @@ -401,8 +403,9 @@ def create_async_workout(alldata,user,c2id): else: splitdata = False - if splitdata: # pragma: no cover - summary,sa,results = c2stuff.summaryfromsplitdata(splitdata,data,csvfilename,workouttype=workouttype) + if splitdata: # pragma: no cover + summary, sa, results = c2stuff.summaryfromsplitdata( + splitdata, data, csvfilename, workouttype=workouttype) w = Workout.objects.get(id=workoutid) w.summary = summary w.save() @@ -410,17 +413,16 @@ def create_async_workout(alldata,user,c2id): from rowingdata.trainingparser import getlist if sa: values = getlist(sa) - units = getlist(sa,sel='unit') - types = getlist(sa,sel='type') + units = getlist(sa, sel='unit') + types = getlist(sa, sel='type') rowdata = rdata(w.csvfilename) if rowdata: rowdata.updateintervaldata(values, - units,types,results) - - rowdata.write_csv(w.csvfilename,gzip=True) - dataprep.update_strokedata(w.id,rowdata.df) + units, types, results) + rowdata.write_csv(w.csvfilename, gzip=True) + dataprep.update_strokedata(w.id, rowdata.df) return workoutid @@ -431,8 +433,10 @@ def makeseconds(t): return seconds # convert our weight class code to Concept2 weight class code + + def c2wc(weightclass): - if (weightclass=="lwt"): # pragma: no cover + if (weightclass == "lwt"): # pragma: no cover res = "L" else: res = "H" @@ -442,138 +446,139 @@ def c2wc(weightclass): # Concept2 logbook sends over split data for each interval # We use it here to generate a custom summary # Some users complained about small differences -def summaryfromsplitdata(splitdata,data,filename,sep='|',workouttype='rower'): + + +def summaryfromsplitdata(splitdata, data, filename, sep='|', workouttype='rower'): workouttype = workouttype.lower() totaldist = data['distance'] totaltime = data['time']/10. try: spm = data['stroke_rate'] - except KeyError: # pragma: no cover + except KeyError: # pragma: no cover spm = 0 try: resttime = data['rest_time']/10. - except KeyError: # pragma: no cover + except KeyError: # pragma: no cover resttime = 0 try: restdistance = data['rest_distance'] - except KeyError: # pragma: no cover + except KeyError: # pragma: no cover restdistance = 0 try: avghr = data['heart_rate']['average'] - except KeyError: # pragma: no cover + except KeyError: # pragma: no cover avghr = 0 try: maxhr = data['heart_rate']['max'] - except KeyError: # pragma: no cover + except KeyError: # pragma: no cover maxhr = 0 try: avgpace = 500.*totaltime/totaldist - except (ZeroDivisionError,OverflowError): # pragma: no cover + except (ZeroDivisionError, OverflowError): # pragma: no cover avgpace = 0. try: restpace = 500.*resttime/restdistance - except (ZeroDivisionError,OverflowError): # pragma: no cover + except (ZeroDivisionError, OverflowError): # pragma: no cover restpace = 0. velo = totaldist/totaltime avgpower = 2.8*velo**(3.0) - if workouttype in ['bike','bikeerg']: # pragma: no cover + if workouttype in ['bike', 'bikeerg']: # pragma: no cover velo = velo/2. avgpower = 2.8*velo**(3.0) velo = velo*2 - try: restvelo = restdistance/resttime - except (ZeroDivisionError,OverflowError): # pragma: no cover + except (ZeroDivisionError, OverflowError): # pragma: no cover restvelo = 0 restpower = 2.8*restvelo**(3.0) - if workouttype in ['bike','bikeerg']: # pragma: no cover + if workouttype in ['bike', 'bikeerg']: # pragma: no cover restvelo = restvelo/2. restpower = 2.8*restvelo**(3.0) restvelo = restvelo*2 try: avgdps = totaldist/data['stroke_count'] - except (ZeroDivisionError,OverflowError,KeyError): # pragma: no cover + except (ZeroDivisionError, OverflowError, KeyError): # pragma: no cover avgdps = 0 - from rowingdata import summarystring,workstring,interval_string + from rowingdata import summarystring, workstring, interval_string - - sums = summarystring(totaldist,totaltime,avgpace,spm,avghr,maxhr, - avgdps,avgpower,readFile=filename, + sums = summarystring(totaldist, totaltime, avgpace, spm, avghr, maxhr, + avgdps, avgpower, readFile=filename, separator=sep) - sums += workstring(totaldist,totaltime,avgpace,spm,avghr,maxhr, - avgdps,avgpower,separator=sep,symbol='W') + sums += workstring(totaldist, totaltime, avgpace, spm, avghr, maxhr, + avgdps, avgpower, separator=sep, symbol='W') - sums += workstring(restdistance,resttime,restpace,0,0,0,0,restpower, + sums += workstring(restdistance, resttime, restpace, 0, 0, 0, 0, restpower, separator=sep, symbol='R') sums += '\nWorkout Details\n' sums += '#-{sep}SDist{sep}-Split-{sep}-SPace-{sep}-Pwr-{sep}SPM-{sep}AvgHR{sep}MaxHR{sep}DPS-\n'.format( sep=sep - ) + ) - intervalnr=0 + intervalnr = 0 sa = [] results = [] try: - timebased = data['workout_type'] in ['FixedTimeSplits','FixedTimeInterval'] - except KeyError: # pragma: no cover + timebased = data['workout_type'] in [ + 'FixedTimeSplits', 'FixedTimeInterval'] + except KeyError: # pragma: no cover timebased = False for interval in splitdata: try: idist = interval['distance'] - except KeyError: # pragma: no cover + except KeyError: # pragma: no cover idist = 0 try: itime = interval['time']/10. - except KeyError: # pragma: no cover + except KeyError: # pragma: no cover itime = 0 try: ipace = 500.*itime/idist - except (ZeroDivisionError,OverflowError): # pragma: no cover + except (ZeroDivisionError, OverflowError): # pragma: no cover ipace = 180. try: ispm = interval['stroke_rate'] - except KeyError: # pragma: no cover + except KeyError: # pragma: no cover ispm = 0 try: irest_time = interval['rest_time']/10. - except KeyError: # pragma: no cover + except KeyError: # pragma: no cover irest_time = 0 try: iavghr = interval['heart_rate']['average'] - except KeyError: # pragma: no cover + except KeyError: # pragma: no cover iavghr = 0 try: imaxhr = interval['heart_rate']['average'] - except KeyError: # pragma: no cover + except KeyError: # pragma: no cover imaxhr = 0 # create interval values - iarr = [idist,'meters','work'] + iarr = [idist, 'meters', 'work'] resarr = [itime] - if timebased: # pragma: no cover - iarr = [itime,'seconds','work'] + if timebased: # pragma: no cover + iarr = [itime, 'seconds', 'work'] resarr = [idist] if irest_time > 0: - iarr += [irest_time,'seconds','rest'] + iarr += [irest_time, 'seconds', 'rest'] try: resarr += [interval['rest_distance']] - except KeyError: # pragma: no cover + except KeyError: # pragma: no cover resarr += [np.nan] sa += iarr @@ -582,17 +587,17 @@ def summaryfromsplitdata(splitdata,data,filename,sep='|',workouttype='rower'): if itime != 0: ivelo = idist/itime ipower = 2.8*ivelo**(3.0) - if workouttype in ['bike','bikeerg']: # pragma: no cover + if workouttype in ['bike', 'bikeerg']: # pragma: no cover ipower = 2.8*(ivelo/2.)**(3.0) - else: # pragma: no cover + else: # pragma: no cover ivelo = 0 ipower = 0 - sums += interval_string(intervalnr,idist,itime,ipace,ispm, - iavghr,imaxhr,0,ipower,separator=sep) - intervalnr+=1 + sums += interval_string(intervalnr, idist, itime, ipace, ispm, + iavghr, imaxhr, 0, ipower, separator=sep) + intervalnr += 1 - return sums,sa,results + return sums, sa, results # Create the Data object for the stroke data to be sent to Concept2 logbook @@ -601,13 +606,13 @@ def createc2workoutdata(w): filename = w.csvfilename try: row = rowingdata(csvfile=filename) - except IOError: # pragma: no cover + except IOError: # pragma: no cover return 0 try: averagehr = int(row.df[' HRCur (bpm)'].mean()) maxhr = int(row.df[' HRCur (bpm)'].max()) - except (ValueError,KeyError): # pragma: no cover + except (ValueError, KeyError): # pragma: no cover averagehr = 0 maxhr = 0 @@ -615,22 +620,24 @@ def createc2workoutdata(w): itime, idist, itype = row.intervalstats_values() try: lapnames = row.df[' lapIdx'].unique() - except KeyError: # pragma: no cover + except KeyError: # pragma: no cover lapnames = range(len(itime)) nrintervals = len(itime) if len(lapnames) != nrintervals: newlapnames = [] for name in lapnames: - newlapnames += [name,name] + newlapnames += [name, name] lapnames = newlapnames intervaldata = [] for i in range(nrintervals): - if itime[i]>0: - mask = (row.df[' lapIdx'] == lapnames[i]) & (row.df[' WorkoutState'] == itype[i]) + if itime[i] > 0: + mask = (row.df[' lapIdx'] == lapnames[i]) & ( + row.df[' WorkoutState'] == itype[i]) try: - spmav = int(row.df[' Cadence (stokes/min)'][mask].mean().astype(int)) + spmav = int(row.df[' Cadence (stokes/min)'] + [mask].mean().astype(int)) hrav = int(row.df[' HRCur (bpm)'][mask].mean().astype(int)) - except AttributeError: # pragma: no cover + except AttributeError: # pragma: no cover try: spmav = int(row.df[' Cadence (stokes/min)'][mask].mean()) hrav = int(row.df[' HRCur (bpm)'][mask].mean()) @@ -641,32 +648,33 @@ def createc2workoutdata(w): except ValuError: hrav = 0 intervaldict = { - 'type': 'distance', - 'time': int(10*itime[i]), - 'distance': int(idist[i]), - 'heart_rate': { - 'average':hrav, - }, - 'stroke_rate': spmav, + 'type': 'distance', + 'time': int(10*itime[i]), + 'distance': int(idist[i]), + 'heart_rate': { + 'average': hrav, + }, + 'stroke_rate': spmav, } intervaldata.append(intervaldict) # adding diff, trying to see if this is valid - t = 10*row.df.loc[:,'TimeStamp (sec)'].values-10*row.df.loc[:,'TimeStamp (sec)'].iloc[0] + t = 10*row.df.loc[:, 'TimeStamp (sec)'].values - \ + 10*row.df.loc[:, 'TimeStamp (sec)'].iloc[0] try: t[0] = t[1] - except IndexError: # pragma: no cover + except IndexError: # pragma: no cover pass - d = 10*row.df.loc[:,' Horizontal (meters)'].values + d = 10*row.df.loc[:, ' Horizontal (meters)'].values try: d[0] = d[1] - except IndexError: # pragma: no cover + except IndexError: # pragma: no cover pass - p = abs(10*row.df.loc[:,' Stroke500mPace (sec/500m)'].values) - p = np.clip(p,0,3600) - if w.workouttype == 'bike': # pragma: no cover + p = abs(10*row.df.loc[:, ' Stroke500mPace (sec/500m)'].values) + p = np.clip(p, 0, 3600) + if w.workouttype == 'bike': # pragma: no cover p = 2.0*p t = t.astype(int) @@ -676,11 +684,11 @@ def createc2workoutdata(w): try: spm[0] = spm[1] - except (KeyError,IndexError): # pragma: no cover + except (KeyError, IndexError): # pragma: no cover spm = 0*t try: hr = row.df[' HRCur (bpm)'].astype(int) - except ValueError: # pragma: no cover + except ValueError: # pragma: no cover hr = 0*d stroke_data = [] @@ -691,32 +699,34 @@ def createc2workoutdata(w): hr = hr.tolist() for i in range(len(t)): - thisrecord = {"t":t[i], - "d":d[i], - "p":p[i], - "spm":spm[i], - "hr":hr[i]} + thisrecord = {"t": t[i], + "d": d[i], + "p": p[i], + "spm": spm[i], + "hr": hr[i]} stroke_data.append(thisrecord) try: - durationstr = datetime.datetime.strptime(str(w.duration),"%H:%M:%S.%f") + durationstr = datetime.datetime.strptime( + str(w.duration), "%H:%M:%S.%f") except ValueError: - durationstr = datetime.datetime.strptime(str(w.duration),"%H:%M:%S") + durationstr = datetime.datetime.strptime(str(w.duration), "%H:%M:%S") workouttype = w.workouttype if workouttype in otwtypes: workouttype = 'water' - if w.timezone == 'tzutc()': # pragma: no cover + if w.timezone == 'tzutc()': # pragma: no cover w.timezone = 'UTC' w.save() - - wendtime = w.startdatetime.astimezone(pytz.timezone(w.timezone))+datetime.timedelta(seconds=makeseconds(durationstr)) + wendtime = w.startdatetime.astimezone(pytz.timezone( + w.timezone))+datetime.timedelta(seconds=makeseconds(durationstr)) data = { "type": mytypes.c2mapping[workouttype], - "date": wendtime.strftime('%Y-%m-%d %H:%M:%S'), #w.startdatetime.isoformat(), + # w.startdatetime.isoformat(), + "date": wendtime.strftime('%Y-%m-%d %H:%M:%S'), "stroke_count": int(row.stroke_count), "timezone": w.timezone, "distance": int(w.distance), @@ -729,46 +739,48 @@ def createc2workoutdata(w): "heart_rate": { "average": averagehr, "max": maxhr, - }, + }, "stroke_data": stroke_data, 'workout': { 'splits': intervaldata, - } } + } return data # Refresh Concept2 authorization token + + def do_refresh_token(refreshtoken): scope = "results:write,user:read" client_auth = requests.auth.HTTPBasicAuth(C2_CLIENT_ID, C2_CLIENT_SECRET) post_data = {"grant_type": "refresh_token", "client_secret": C2_CLIENT_SECRET, - "client_id":C2_CLIENT_ID, + "client_id": C2_CLIENT_ID, "refresh_token": refreshtoken, } headers = {'user-agent': 'sanderroosendaal'} url = "https://log.concept2.com/oauth/access_token" s = Session() - req = Request('POST',url, data=post_data, headers=headers) + req = Request('POST', url, data=post_data, headers=headers) prepped = req.prepare() - prepped.body+="&scope=" - prepped.body+=scope + prepped.body += "&scope=" + prepped.body += scope response = s.send(prepped) try: token_json = response.json() - except JSONDecodeError: # pragma: no cover - return [None,None,None] + except JSONDecodeError: # pragma: no cover + return [None, None, None] try: thetoken = token_json['access_token'] expires_in = token_json['expires_in'] refresh_token = token_json['refresh_token'] - except: # pragma: no cover - with open("media/c2errors.log","a") as errorlog: + except: # pragma: no cover + with open("media/c2errors.log", "a") as errorlog: errorstring = str(sys.exc_info()[0]) timestr = time.strftime("%Y%m%d-%H%M%S") errorlog.write(timestr+errorstring+"\r\n") @@ -777,27 +789,29 @@ def do_refresh_token(refreshtoken): expires_in = None refresh_token = None - return [thetoken,expires_in,refresh_token] + return [thetoken, expires_in, refresh_token] # Exchange authorization code for authorization token + + def get_token(code): - messg='' + messg = '' scope = "user:read,results:write" client_auth = requests.auth.HTTPBasicAuth(C2_CLIENT_ID, C2_CLIENT_SECRET) post_data = {"grant_type": "authorization_code", "code": code, "redirect_uri": C2_REDIRECT_URI, "client_secret": C2_CLIENT_SECRET, - "client_id":C2_CLIENT_ID, + "client_id": C2_CLIENT_ID, } headers = {'user-agent': 'sanderroosendaal'} url = "https://log.concept2.com/oauth/access_token" s = Session() - req = Request('POST',url, data=post_data, headers=headers) + req = Request('POST', url, data=post_data, headers=headers) prepped = req.prepare() - prepped.body+="&scope=" - prepped.body+=scope + prepped.body += "&scope=" + prepped.body += scope response = s.send(prepped) @@ -806,25 +820,27 @@ def get_token(code): try: status_code = response.status_code # status_code = token_json['status_code'] - except AttributeError: # pragma: no cover - # except KeyError: - return (0,response.text) + except AttributeError: # pragma: no cover + # except KeyError: + return (0, response.text) try: status_code = token_json.status_code - except AttributeError: # pragma: no cover - return (0,'Attribute Error on c2_get_token') + except AttributeError: # pragma: no cover + return (0, 'Attribute Error on c2_get_token') if status_code == 200: thetoken = token_json['access_token'] expires_in = token_json['expires_in'] refresh_token = token_json['refresh_token'] - else: # pragma: no cover - return (0,token_json['message']) + else: # pragma: no cover + return (0, token_json['message']) - return (thetoken,expires_in,refresh_token,messg) + return (thetoken, expires_in, refresh_token, messg) # Make URL for authorization and load it -def make_authorization_url(request): # pragma: no cover + + +def make_authorization_url(request): # pragma: no cover # Generate a random string for the state parameter # Save it for use later to prevent xsrf attacks from uuid import uuid4 @@ -834,13 +850,16 @@ def make_authorization_url(request): # pragma: no cover params = {"client_id": C2_CLIENT_ID, "response_type": "code", "redirect_uri": C2_REDIRECT_URI} - url = "https://log.concept2.com/oauth/authorize?"+ urllib.parse.urlencode(params) + url = "https://log.concept2.com/oauth/authorize?" + \ + urllib.parse.urlencode(params) url += "&scope="+scope return HttpResponseRedirect(url) # Get workout from C2 ID -def get_workout(user,c2id,do_async=True): + + +def get_workout(user, c2id, do_async=True): r = Rower.objects.get(user=user) thetoken = c2_open(user) @@ -856,15 +875,15 @@ def get_workout(user,c2id,do_async=True): # Get list of C2 workouts. We load only the first page, # assuming that users don't want to import their old workouts -def get_c2_workout_list(user,page=1): +def get_c2_workout_list(user, page=1): r = Rower.objects.get(user=user) - if (r.c2token == '') or (r.c2token is None): # pragma: no cover + if (r.c2token == '') or (r.c2token is None): # pragma: no cover s = "Token doesn't exist. Need to authorize" - return custom_exception_handler(401,s) - elif (timezone.now()>r.tokenexpirydate): # pragma: no cover + return custom_exception_handler(401, s) + elif (timezone.now() > r.tokenexpirydate): # pragma: no cover s = "Token expired. Needs to refresh." - return custom_exception_handler(401,s) + return custom_exception_handler(401, s) else: # ready to fetch. Hurray authorizationstring = str('Bearer ' + r.c2token) @@ -874,27 +893,26 @@ def get_c2_workout_list(user,page=1): url = "https://log.concept2.com/api/users/me/results" url += "?page={page}".format(page=page) - s = requests.get(url,headers=headers) + s = requests.get(url, headers=headers) return s # Get username, having access token. # Handy for checking if the API access is working -def get_username(access_token): # pragma: no cover +def get_username(access_token): # pragma: no cover authorizationstring = str('Bearer ' + access_token) headers = {'Authorization': authorizationstring, 'user-agent': 'sanderroosendaal', 'Content-Type': 'application/json'} import urllib url = "https://log.concept2.com/api/users/me" - response = requests.get(url,headers=headers) - + response = requests.get(url, headers=headers) me_json = response.json() try: - res = me_json['data']['username'] + res = me_json['data']['username'] id = me_json['data']['id'] except KeyError: res = None @@ -903,6 +921,8 @@ def get_username(access_token): # pragma: no cover # Get user id, having access token # Handy for checking if the API access is working + + def get_userid(access_token): authorizationstring = str('Bearer ' + access_token) headers = {'Authorization': authorizationstring, @@ -911,47 +931,52 @@ def get_userid(access_token): import urllib url = "https://log.concept2.com/api/users/me" try: - response = requests.get(url,headers=headers) - except: # pragma: no cover + response = requests.get(url, headers=headers) + except: # pragma: no cover return 0 - try: me_json = response.json() - except: # pragma: no cover + except: # pragma: no cover return 0 try: res = me_json['data']['id'] - except KeyError: # pragma: no cover + except KeyError: # pragma: no cover return 0 return res # For debugging purposes -def process_callback(request): # pragma: no cover + + +def process_callback(request): # pragma: no cover # need error handling code = request.GET['code'] access_token = get_token(code) - username,id = get_username(access_token) + username, id = get_username(access_token) return HttpResponse("got a user name: %s" % username) -def default(o): # pragma: no cover - if isinstance(o, numpy.int64): return int(o) + +def default(o): # pragma: no cover + if isinstance(o, numpy.int64): + return int(o) raise TypeError # Uploading workout -def workout_c2_upload(user,w,asynchron=False): + + +def workout_c2_upload(user, w, asynchron=False): message = 'trying C2 upload' try: - if mytypes.c2mapping[w.workouttype] is None: # pragma: no cover - return "This workout type cannot be uploaded to Concept2",0 - except KeyError: # pragma: no cover - return "This workout type cannot be uploaded to Concept2",0 + if mytypes.c2mapping[w.workouttype] is None: # pragma: no cover + return "This workout type cannot be uploaded to Concept2", 0 + except KeyError: # pragma: no cover + return "This workout type cannot be uploaded to Concept2", 0 thetoken = c2_open(user) @@ -959,18 +984,19 @@ def workout_c2_upload(user,w,asynchron=False): # ready to upload. Hurray - if (is_workout_user(user,w)): + if (is_workout_user(user, w)): c2userid = get_userid(r.c2token) - if not c2userid: # pragma: no cover + if not c2userid: # pragma: no cover raise NoTokenError("User has no token") - dologging('debuglog.log','Upload to C2 user {userid}'.format(userid=user.id)) + dologging('debuglog.log', + 'Upload to C2 user {userid}'.format(userid=user.id)) data = createc2workoutdata(w) - dologging('debuglog.log',json.dumps(data)) + dologging('debuglog.log', json.dumps(data)) - if data == 0: # pragma: no cover - return "Error: No data file. Contact info@rowsandall.com if the problem persists",0 + if data == 0: # pragma: no cover + return "Error: No data file. Contact info@rowsandall.com if the problem persists", 0 authorizationstring = str('Bearer ' + r.c2token) headers = {'Authorization': authorizationstring, @@ -978,11 +1004,11 @@ def workout_c2_upload(user,w,asynchron=False): 'Content-Type': 'application/json'} import urllib url = "https://log.concept2.com/api/users/%s/results" % (c2userid) - if not asynchron: # pragma: no cover - response = requests.post(url,headers=headers,data=json.dumps(data,default=default)) + if not asynchron: # pragma: no cover + response = requests.post( + url, headers=headers, data=json.dumps(data, default=default)) - - if (response.status_code == 409 ): # pragma: no cover + if (response.status_code == 409): # pragma: no cover message = "Concept2 Duplicate error" w.uploadedtoc2 = -1 c2id = -1 @@ -994,22 +1020,24 @@ def workout_c2_upload(user,w,asynchron=False): w.uploadedtoc2 = c2id w.save() message = "Upload to Concept2 was successful" - else: # pragma: no cover + else: # pragma: no cover message = "Something went wrong in workout_c2_upload_view. Response code 200/201 but C2 sync failed: "+response.text c2id = 0 - else: # pragma: no cover + else: # pragma: no cover job = myqueue(queue, handle_c2_sync, w.id, url, headers, - json.dumps(data,default=default)) + json.dumps(data, default=default)) c2id = 0 - return message,c2id + return message, c2id # This is token refresh. Looks for tokens in our database, then refreshes + + def rower_c2_token_refresh(user): r = Rower.objects.get(user=user) res = do_refresh_token(r.c2refreshtoken) @@ -1026,5 +1054,5 @@ def rower_c2_token_refresh(user): r.save() return r.c2token - else: # pragma: no cover + else: # pragma: no cover return None diff --git a/rowers/celery.py b/rowers/celery.py index aa35dd3b..d25d2a5a 100644 --- a/rowers/celery.py +++ b/rowers/celery.py @@ -18,13 +18,14 @@ from django.conf import settings # noqa app = Celery('tasks', - broker='redis://localhost', - backend='redis://localhost',) + broker='redis://localhost', + backend='redis://localhost',) class Config: CELERY_TIMEZONE = 'Europe/Prague' + # Using a string here means the worker will not have to # pickle the object when using Windows. app.config_from_object('django.conf:settings') @@ -33,7 +34,6 @@ app.autodiscover_tasks(lambda: settings.INSTALLED_APPS) database_url = 'sqlite:///db.sqlite3' -#@app.task(bind=True) -#def debug_task(self): +# @app.task(bind=True) +# def debug_task(self): # print('Request: {0!r}'.format(self.request)) - diff --git a/rowers/context_processors.py b/rowers/context_processors.py index de8bfbdb..cb4f0842 100644 --- a/rowers/context_processors.py +++ b/rowers/context_processors.py @@ -3,9 +3,10 @@ from __future__ import division from __future__ import print_function from __future__ import unicode_literals -from django.conf import settings # import the settings file +from django.conf import settings # import the settings file -def braintree_merchant(request): # pragma: no cover + +def braintree_merchant(request): # pragma: no cover # return the value you want as a dictionnary. you may add multiple values in there. # return {'BRAINTREE_MERCHANT_ID': settings.BRAINTREE_MERCHANT_ID} - return {'BRAINTREE_MERCHANT_ID': 'jytq7yxsm66qqdzb' } + return {'BRAINTREE_MERCHANT_ID': 'jytq7yxsm66qqdzb'} diff --git a/rowers/courses.py b/rowers/courses.py index 3a401af5..a773992b 100644 --- a/rowers/courses.py +++ b/rowers/courses.py @@ -1,4 +1,17 @@ from __future__ import absolute_import +from rowers.courseutils import coursetime_paths, coursetime_first, time_in_path +import pandas as pd +from rowers.models import ( + Rower, Workout, + GeoPoint, GeoPolygon, GeoCourse, + course_length, course_coord_center, course_coord_maxmin, + polygon_coord_center, PlannedSession, + polygon_to_path, coordinate_in_path +) +from rowers.utils import geo_distance +import rowers.dataprep as dataprep +from timezonefinder import TimezoneFinder +import numpy as np from __future__ import division from __future__ import print_function from __future__ import unicode_literals @@ -25,38 +38,42 @@ from xml.dom import minidom from rowers.models import VirtualRace # distance of course from lat_lon in km -def howfaris(lat_lon,course): + + +def howfaris(lat_lon, course): coords = course.coord - distance = geo_distance(lat_lon[0],lat_lon[1],coords[0],coords[1])[0] + distance = geo_distance(lat_lon[0], lat_lon[1], coords[0], coords[1])[0] return distance #whatisnear = 150 # get nearest races -def getnearestraces(lat_lon,races,whatisnear=150): + + +def getnearestraces(lat_lon, races, whatisnear=150): newlist = [] counter = 0 for race in races: - if race.course is None: # pragma: no cover + if race.course is None: # pragma: no cover newlist.append(race) else: c = race.course coords = c.coord - distance = howfaris(lat_lon,c) + distance = howfaris(lat_lon, c) if distance < whatisnear: newlist.append(race) counter += 1 - if counter>0: + if counter > 0: races = newlist else: courseraces = races.exclude(course__isnull=True) - orders = [(c.id,howfaris(lat_lon,c.course)) for c in courseraces] - orders = sorted(orders,key = lambda tup:tup[1]) - ids = [id for id,distance in orders[0:4]] - for id, distance in orders[5:]: # pragma: no cover - if distance0: + if counter > 0: courses = newlist elif strict: courses = newlist else: - orders = [(c.id,howfaris(lat_lon,c)) for c in courses] - orders = sorted(orders,key = lambda tup:tup[1]) - ids = [id for id,distance in orders[0:4]] + orders = [(c.id, howfaris(lat_lon, c)) for c in courses] + orders = sorted(orders, key=lambda tup: tup[1]) + ids = [id for id, distance in orders[0:4]] for id, distance in orders[5:]: - if distance1: + if len(df[b == 2]) > 1: f.write(b' passes found') else: f.write(b' pass found') - if getall: # pragma: no cover - return df[b==2]['time'],df[b==2]['cum_dist'] + if getall: # pragma: no cover + return df[b == 2]['time'], df[b == 2]['cum_dist'] else: - return df[b==2]['time'].min(),df[b==2]['cum_dist'].min() + return df[b == 2]['time'].min(), df[b == 2]['cum_dist'].min() - if logfile is not None: # pragma: no cover + if logfile is not None: # pragma: no cover t = time.localtime() - timestamp = bytes('{t}'.format(t=time.strftime('%b-%d-%Y_%H%M', t)),'utf-8') - with open(logfile,'ab') as f: + timestamp = bytes('{t}'.format( + t=time.strftime('%b-%d-%Y_%H%M', t)), 'utf-8') + with open(logfile, 'ab') as f: f.write(b'\n') f.write(timestamp) f.write(b' ') - f.write(bytes(name,'utf-8')) + f.write(bytes(name, 'utf-8')) f.write(b' ') - f.write(bytes(maxmin,'utf-8')) + f.write(bytes(maxmin, 'utf-8')) f.write(b' ') - f.write(bytes(str(getall),'utf-8')) + f.write(bytes(str(getall), 'utf-8')) f.write(b' ') - f.write(bytes(str(len(df[b==2])),'utf-8')) + f.write(bytes(str(len(df[b == 2])), 'utf-8')) f.write(b' ') f.write(b' pass not found') - raise InvalidTrajectoryError("Trajectory doesn't go through path") # pragma: no cover + raise InvalidTrajectoryError( + "Trajectory doesn't go through path") # pragma: no cover + + return 0 # pragma: no cover - return 0 # pragma: no cover - - -def coursetime_first(data,paths,polygons=[],logfile=None): +def coursetime_first(data, paths, polygons=[], logfile=None): entrytime = data['time'].max() entrydistance = data['cum_dist'].max() coursecompleted = False if len(polygons) == 0: - polygons = [(0,str(i)) for i in range(len(paths))] - + polygons = [(0, str(i)) for i in range(len(paths))] try: - entrytime,entrydistance = time_in_path(data,paths[0],maxmin='max',name=polygons[0][1],logfile=logfile) + entrytime, entrydistance = time_in_path( + data, paths[0], maxmin='max', name=polygons[0][1], logfile=logfile) coursecompleted = True - except InvalidTrajectoryError: # pragma: no cover + except InvalidTrajectoryError: # pragma: no cover entrytime = data['time'].max() entrydistance = data['cum_dist'].max() coursecompleted = False return entrytime, entrydistance, coursecompleted -def coursetime_paths(data,paths,finalmaxmin='min',polygons=[],logfile=None): + +def coursetime_paths(data, paths, finalmaxmin='min', polygons=[], logfile=None): entrytime = data['time'].max() entrydistance = data['cum_dist'].max() coursecompleted = False if len(polygons) == 0: - polygons = [(0,str(i)) for i in range(len(paths))] + polygons = [(0, str(i)) for i in range(len(paths))] # corner case - empty list of paths - if len(paths) == 0: # pragma: no cover - return 0,0,True + if len(paths) == 0: # pragma: no cover + return 0, 0, True # end - just the Finish polygon if len(paths) == 1: @@ -122,29 +124,30 @@ def coursetime_paths(data,paths,finalmaxmin='min',polygons=[],logfile=None): ( entrytime, entrydistance - ) = time_in_path(data,paths[0],maxmin=finalmaxmin,name = polygons[0][1],logfile=logfile) + ) = time_in_path(data, paths[0], maxmin=finalmaxmin, name=polygons[0][1], logfile=logfile) coursecompleted = True - except InvalidTrajectoryError: # pragma: no cover + except InvalidTrajectoryError: # pragma: no cover entrytime = data['time'].max() entrydistance = data['cum_dist'].max() coursecompleted = False - return entrytime,entrydistance,coursecompleted + return entrytime, entrydistance, coursecompleted if len(paths) > 1: try: - time,dist = time_in_path(data, paths[0],name=polygons[0][1],logfile=logfile) - data2 = data[data['time']>time].copy() - data2['time'] = data2['time'].apply(lambda x:x-time) - data2['cum_dist'] = data2['cum_dist'].apply(lambda x:x-dist) + time, dist = time_in_path( + data, paths[0], name=polygons[0][1], logfile=logfile) + data2 = data[data['time'] > time].copy() + data2['time'] = data2['time'].apply(lambda x: x-time) + data2['cum_dist'] = data2['cum_dist'].apply(lambda x: x-dist) ( timenext, distnext, coursecompleted - ) = coursetime_paths(data2,paths[1:],polygons=polygons[1:],logfile=logfile) - return time+timenext, dist+distnext,coursecompleted - except InvalidTrajectoryError: # pragma: no cover + ) = coursetime_paths(data2, paths[1:], polygons=polygons[1:], logfile=logfile) + return time+timenext, dist+distnext, coursecompleted + except InvalidTrajectoryError: # pragma: no cover entrytime = data['time'].max() entrydistance = data['cum_dist'].max() coursecompleted = False - return entrytime, entrydistance, coursecompleted # pragma: no cover + return entrytime, entrydistance, coursecompleted # pragma: no cover diff --git a/rowers/credits.py b/rowers/credits.py index 3ddb6cfb..e0883bf3 100644 --- a/rowers/credits.py +++ b/rowers/credits.py @@ -3,17 +3,19 @@ class InsufficientCreditError(Exception): """Raised when trying to subtract more than available""" pass + def upgrade(amount, rower): - if rower.eurocredits > amount: # pragma: no cover + if rower.eurocredits > amount: # pragma: no cover return rower.eurocredits else: rower.eurocredits = amount rower.save() return rower.eurocredits - return rower.eurocredits # pragma: no cover + return rower.eurocredits # pragma: no cover + def withdraw(amount, rower): - if rower.eurocredits < amount: # pragma: no cover + if rower.eurocredits < amount: # pragma: no cover rower.eurocredits = 0 rower.save() raise InsufficientCreditError @@ -22,9 +24,10 @@ def withdraw(amount, rower): rower.save() return rower.eurocredits - return rower.eurocredits # pragma: no cover + return rower.eurocredits # pragma: no cover -def discount(amount,rower): # pragma: no cover + +def discount(amount, rower): # pragma: no cover if amount < rower.eurocredits: return amount else: @@ -32,10 +35,11 @@ def discount(amount,rower): # pragma: no cover return 0 -def discounted(amount,rower): - if amount > rower.eurocredits: # pragma: no cover + +def discounted(amount, rower): + if amount > rower.eurocredits: # pragma: no cover return amount-rower.eurocredits else: return 0 - return 0 # pragma: no cover + return 0 # pragma: no cover diff --git a/rowers/database.py b/rowers/database.py index 46bc39d8..653ad966 100644 --- a/rowers/database.py +++ b/rowers/database.py @@ -17,7 +17,7 @@ database_url = 'mysql://{user}:{password}@{host}:{port}/{database_name}'.format( database_name=database_name, host=host, port=port, - ) +) -if settings.DEBUG or user=='': +if settings.DEBUG or user == '': database_url = 'sqlite:///db.sqlite3' diff --git a/rowers/dataprep.py b/rowers/dataprep.py index 21b7770a..23dfe45d 100644 --- a/rowers/dataprep.py +++ b/rowers/dataprep.py @@ -1,4 +1,38 @@ from __future__ import absolute_import +from rowers.metrics import axes, calc_trimp, rowingmetrics, dtypes, metricsgroups +from rowers.utils import lbstoN, myqueue, wavg, dologging +from rowers.mytypes import otwtypes, otetypes, rowtypes +import glob +import rowingdata.tcxtools as tcxtools +from rowers.utils import totaltime_sec_to_string +from rowers.datautils import p0 +from scipy import optimize +from rowers.utils import calculate_age +import datetime +from scipy.signal import savgol_filter +from rowers.opaque import encoder +from rowers.database import * +from rowers import mytypes +from rowsandall_app.settings import SITE_URL +import django_rq +from timezonefinder import TimezoneFinder +import rowers.datautils as datautils +import rowers.utils as utils +import sys +import sqlalchemy as sa +from sqlalchemy import create_engine +from django.conf import settings +import math +from fitparse.base import FitHeaderError +from fitparse import FitFile +import itertools +import numpy as np +import pandas as pd +from zipfile import BadZipFile +import zipfile +import os +from rowers.models import strokedatafields +from rowingdata.csvparsers import HumonParser from __future__ import division from __future__ import print_function from __future__ import unicode_literals @@ -7,9 +41,9 @@ from __future__ import unicode_literals # be defined here from __future__ import unicode_literals, absolute_import from rowers.models import ( - Workout, Team, CalcAgePerformance,C2WorldClassAgePerformance, + Workout, Team, CalcAgePerformance, C2WorldClassAgePerformance, User - ) +) import pytz import collections @@ -23,14 +57,14 @@ import shutil from shutil import copyfile from rowingdata import ( - get_file_type, get_empower_rigging,get_empower_firmware - ) + get_file_type, get_empower_rigging, get_empower_firmware +) from rowers.tasks import ( - handle_sendemail_unrecognized,handle_setcp, + handle_sendemail_unrecognized, handle_setcp, handle_getagegrouprecords, handle_update_wps, handle_request_post - ) +) from rowers.tasks import handle_zip_file from pandas import DataFrame, Series @@ -52,66 +86,18 @@ from time import strftime import arrow thetimezone = get_current_timezone() -from rowingdata import ( - TCXParser, RowProParser, ErgDataParser, - CoxMateParser, HeroParser, SmartRowParser, - BoatCoachParser, RowPerfectParser, BoatCoachAdvancedParser, - ETHParser, - MysteryParser, BoatCoachOTWParser,QuiskeParser, - painsledDesktopParser, speedcoachParser, ErgStickParser, - SpeedCoach2Parser, FITParser, fitsummarydata, - RitmoTimeParser,KinoMapParser,NKLiNKLogbookParser, - make_cumvalues,cumcpdata,ExcelTemplate, - summarydata, get_file_type, -) -from rowingdata.csvparsers import HumonParser - - -from rowers.metrics import axes,calc_trimp,rowingmetrics,dtypes,metricsgroups -from rowers.models import strokedatafields #allowedcolumns = [item[0] for item in rowingmetrics] -allowedcolumns = [key for key,value in strokedatafields.items()] +allowedcolumns = [key for key, value in strokedatafields.items()] #from async_messages import messages as a_messages -import os -import zipfile -from zipfile import BadZipFile -import pandas as pd -import numpy as np -import itertools -from fitparse import FitFile -from fitparse.base import FitHeaderError -import math -from rowers.tasks import ( - handle_sendemail_unrecognized, handle_sendemail_breakthrough, - handle_sendemail_hard, handle_updatecp,handle_updateergcp, - handle_calctrimp, -) -from django.conf import settings -from sqlalchemy import create_engine -import sqlalchemy as sa -import sys -import rowers.utils as utils -import rowers.datautils as datautils -from rowers.utils import lbstoN,myqueue,wavg,dologging -from timezonefinder import TimezoneFinder - -import django_rq queue = django_rq.get_queue('default') queuelow = django_rq.get_queue('low') queuehigh = django_rq.get_queue('default') -from rowsandall_app.settings import SITE_URL -from rowers.mytypes import otwtypes,otetypes,rowtypes -from rowers import mytypes - -from rowers.database import * -from rowers.opaque import encoder - # mapping the DB column names to the CSV file column names columndict = { @@ -135,38 +121,30 @@ columndict = { } -from scipy.signal import savgol_filter - -import datetime - -def get_video_data(w,groups=['basic'],mode='water'): - modes = [mode,'both','basic'] - columns = ['time','velo','spm'] - columns += [name for name,d in rowingmetrics if d['group'] in groups and d['mode'] in modes] +def get_video_data(w, groups=['basic'], mode='water'): + modes = [mode, 'both', 'basic'] + columns = ['time', 'velo', 'spm'] + columns += [name for name, d in rowingmetrics if d['group'] + in groups and d['mode'] in modes] columns = list(set(columns)) - df = getsmallrowdata_db(columns,ids=[w.id], - workstrokesonly=False,doclean=False,compute=False) - + df = getsmallrowdata_db(columns, ids=[w.id], + workstrokesonly=False, doclean=False, compute=False) df['time'] = (df['time']-df['time'].min())/1000. - df.sort_values(by='time',inplace=True) + df.sort_values(by='time', inplace=True) - - df.set_index(pd.to_timedelta(df['time'],unit='s'),inplace=True) + df.set_index(pd.to_timedelta(df['time'], unit='s'), inplace=True) df2 = df.resample('1s').first().fillna(method='ffill') df2['time'] = df2.index.total_seconds() if 'pace' in columns: df2['pace'] = df2['pace']/1000. p = df2['pace'] - p = p.apply(lambda x:timedeltaconv(x)) + p = p.apply(lambda x: timedeltaconv(x)) p = nicepaceformat(p) df2['pace'] = p - - - #mask = df2['time'] < delay #df2 = df2.mask(mask).dropna() df2['time'] = (df2['time']-df2['time'].min()) @@ -177,7 +155,7 @@ def get_video_data(w,groups=['basic'],mode='water'): try: coordinates = get_latlon_time(w.id) - except KeyError: # pragma: no cover + except KeyError: # pragma: no cover nulseries = df['time']*0 coordinates = pd.DataFrame({ 'time': df['time'], @@ -185,7 +163,8 @@ def get_video_data(w,groups=['basic'],mode='water'): 'longitude': nulseries, }) - coordinates.set_index(pd.to_timedelta(coordinates['time'],unit='s'),inplace=True) + coordinates.set_index(pd.to_timedelta( + coordinates['time'], unit='s'), inplace=True) coordinates = coordinates.resample('1s').mean().interpolate() #mask = coordinates['time'] < delay #coordinates = coordinates.mask(mask).dropna() @@ -195,18 +174,17 @@ def get_video_data(w,groups=['basic'],mode='water'): # bundle data data = { - 'boatspeed':boatspeed.values.tolist(), - 'latitude':latitude.values.tolist(), - 'longitude':longitude.values.tolist(), - } - + 'boatspeed': boatspeed.values.tolist(), + 'latitude': latitude.values.tolist(), + 'longitude': longitude.values.tolist(), + } metrics = {} for c in columns: if c != 'time': try: - if dict(rowingmetrics)[c]['numtype'] == 'integer': # pragma: no cover + if dict(rowingmetrics)[c]['numtype'] == 'integer': # pragma: no cover data[c] = df2[c].astype(int).tolist() else: sigfigs = dict(rowingmetrics)[c]['sigfigs'] @@ -220,7 +198,7 @@ def get_video_data(w,groups=['basic'],mode='water'): 'metric': c, 'unit': '' } - except KeyError: # pragma: no cover + except KeyError: # pragma: no cover pass metrics['boatspeed'] = metrics.pop('velo') @@ -229,25 +207,22 @@ def get_video_data(w,groups=['basic'],mode='water'): maxtime = coordinates['time'].max() - return data, metrics, maxtime -def polarization_index(df,rower): +def polarization_index(df, rower): df['dt'] = df['time'].diff()/6.e4 # remove rest (spm<15) - df.dropna(axis=0,inplace=True) - df['dt'] = df['dt'].clip(upper=4,lower=0) + df.dropna(axis=0, inplace=True) + df['dt'] = df['dt'].clip(upper=4, lower=0) + masklow = (df['power'] > 0) & (df['power'] < int(rower.pw_at)) + maskmid = (df['power'] >= rower.pw_at) & (df['power'] < int(rower.pw_an)) + maskhigh = (df['power'] > rower.pw_an) - masklow = (df['power']>0) & (df['power']=rower.pw_at) & (df['power']rower.pw_an) - - - time_low_pw = df.loc[masklow,'dt'].sum() - time_mid_pw = df.loc[maskmid,'dt'].sum() - time_high_pw = df.loc[maskhigh,'dt'].sum() + time_low_pw = df.loc[masklow, 'dt'].sum() + time_mid_pw = df.loc[maskmid, 'dt'].sum() + time_high_pw = df.loc[maskhigh, 'dt'].sum() frac_low = time_low_pw/(time_low_pw+time_mid_pw+time_high_pw) frac_mid = time_mid_pw/(time_low_pw+time_mid_pw+time_high_pw) @@ -261,14 +236,13 @@ def polarization_index(df,rower): def get_latlon(id): try: w = Workout.objects.get(id=id) - except Workout.DoesNotExist: # pragma: no cover + except Workout.DoesNotExist: # pragma: no cover return False - rowdata = rdata(w.csvfilename) - if rowdata.df.empty: # pragma: no cover - return [pd.Series([],dtype='float'), pd.Series([],dtype='float')] + if rowdata.df.empty: # pragma: no cover + return [pd.Series([], dtype='float'), pd.Series([], dtype='float')] try: try: @@ -278,64 +252,65 @@ def get_latlon(id): latitude = 0 * rowdata.df.loc[:, 'TimeStamp (sec)'] longitude = 0 * rowdata.df.loc[:, 'TimeStamp (sec)'] return [latitude, longitude] - except AttributeError: # pragma: no cover - return [pd.Series([],dtype='float'), pd.Series([],dtype='float')] + except AttributeError: # pragma: no cover + return [pd.Series([], dtype='float'), pd.Series([], dtype='float')] + + return [pd.Series([], dtype='float'), pd.Series([], dtype='float')] # pragma: no cover - return [pd.Series([],dtype='float'), pd.Series([],dtype='float')] # pragma: no cover def get_latlon_time(id): try: w = Workout.objects.get(id=id) - except Workout.DoesNotExist: # pragma: no cover + except Workout.DoesNotExist: # pragma: no cover return False - rowdata = rdata(w.csvfilename) - if rowdata.df.empty: # pragma: no cover - return [pd.Series([],dtype='float'), pd.Series([],dtype='float')] + if rowdata.df.empty: # pragma: no cover + return [pd.Series([], dtype='float'), pd.Series([], dtype='float')] try: try: latitude = rowdata.df.loc[:, ' latitude'] longitude = rowdata.df.loc[:, ' longitude'] - except KeyError: # pragma: no cover + except KeyError: # pragma: no cover latitude = 0 * rowdata.df.loc[:, 'TimeStamp (sec)'] longitude = 0 * rowdata.df.loc[:, 'TimeStamp (sec)'] - except AttributeError: # pragma: no cover + except AttributeError: # pragma: no cover return pd.DataFrame() df = pd.DataFrame({ 'time': rowdata.df['TimeStamp (sec)']-rowdata.df['TimeStamp (sec)'].min(), 'latitude': rowdata.df[' latitude'], 'longitude': rowdata.df[' longitude'] - }) + }) return df + def workout_has_latlon(id): latitude, longitude = get_latlon(id) latmean = latitude.mean() lonmean = longitude.mean() if latmean == 0 and lonmean == 0: - return False,latmean,lonmean + return False, latmean, lonmean if latitude.std() > 0 and longitude.std() > 0: - return True, latmean,lonmean + return True, latmean, lonmean - return False, latmean,lonmean + return False, latmean, lonmean def workout_summary_to_df( rower, - startdate=datetime.datetime(1970,1,1), + startdate=datetime.datetime(1970, 1, 1), enddate=timezone.now()+timezone.timedelta(days=1)): ws = Workout.objects.filter( - user=rower,date__gte=startdate,date__lte=enddate, + user=rower, date__gte=startdate, date__lte=enddate, duplicate=False - ).order_by("startdatetime") + ).order_by("startdatetime") types = [] names = [] @@ -364,8 +339,8 @@ def workout_summary_to_df( for w in ws: counter1 += 1 - if counter1 % 10 == 0: # pragma: no cover - print(counter1,'/',counter2) + if counter1 % 10 == 0: # pragma: no cover + print(counter1, '/', counter2) types.append(w.workouttype) names.append(w.name) ids.append(encoder.encode_hex(w.id)) @@ -380,11 +355,11 @@ def workout_summary_to_df( notes.append(w.notes) tcx_link = SITE_URL+'/rowers/workout/{id}/emailtcx'.format( id=encoder.encode_hex(w.id) - ) + ) tcx_links.append(tcx_link) csv_link = SITE_URL+'/rowers/workout/{id}/emailcsv'.format( id=encoder.encode_hex(w.id) - ) + ) csv_links.append(csv_link) workout_link = SITE_URL+'/rowers/workout/{id}/'.format( id=encoder.encode_hex(w.id) @@ -394,38 +369,39 @@ def workout_summary_to_df( rscore = workout_rscore(w) rscores.append(int(rscore[0])) hrtss.append(int(w.hrtss)) - goldstandard,goldstandardduration = workout_goldmedalstandard(w) + goldstandard, goldstandardduration = workout_goldmedalstandard(w) goldstandards.append(int(goldstandard)) goldstandarddurations.append(int(goldstandardduration)) rankingpieces.append(w.rankingpiece) df = pd.DataFrame({ 'ID': ids, - 'date':startdatetimes, - 'name':names, - 'link':workout_links, - 'timezone':timezones, - 'type':types, - 'boat type':boattypes, - 'distance (m)':distances, - 'duration ':durations, - 'ranking piece':rankingpieces, - 'weight category':weightcategories, - 'adaptive classification':adaptivetypes, - 'weight (kg)':weightvalues, - 'Stroke Data TCX':tcx_links, - 'Stroke Data CSV':csv_links, - 'TRIMP Training Load':trimps, - 'TSS Training Load':rscores, - 'hrTSS Training Load':hrtss, - 'GS':goldstandards, - 'GS_secs':goldstandarddurations, - 'notes':notes, + 'date': startdatetimes, + 'name': names, + 'link': workout_links, + 'timezone': timezones, + 'type': types, + 'boat type': boattypes, + 'distance (m)': distances, + 'duration ': durations, + 'ranking piece': rankingpieces, + 'weight category': weightcategories, + 'adaptive classification': adaptivetypes, + 'weight (kg)': weightvalues, + 'Stroke Data TCX': tcx_links, + 'Stroke Data CSV': csv_links, + 'TRIMP Training Load': trimps, + 'TSS Training Load': rscores, + 'hrTSS Training Load': hrtss, + 'GS': goldstandards, + 'GS_secs': goldstandarddurations, + 'notes': notes, }) return df -def get_workouts(ids, userid): # pragma: no cover + +def get_workouts(ids, userid): # pragma: no cover goodids = [] for id in ids: w = Workout.objects.get(id=id) @@ -455,15 +431,17 @@ def filter_df(datadf, fieldname, value, largerthan=True): return datadf # joins workouts -def join_workouts(r,ids,title='Joined Workout', + + +def join_workouts(r, ids, title='Joined Workout', parent=None, setprivate=False, - forceunit='lbs',killparents=False): + forceunit='lbs', killparents=False): message = None summary = '' - if parent: # pragma: no cover + if parent: # pragma: no cover oarlength = parent.oarlength inboard = parent.inboard workouttype = parent.workouttype @@ -484,12 +462,11 @@ def join_workouts(r,ids,title='Joined Workout', makeprivate = False startdatetime = timezone.now() - if setprivate == True and makeprivate == False: # pragma: no cover + if setprivate == True and makeprivate == False: # pragma: no cover makeprivate = True - elif setprivate == False and makeprivate == True: # pragma: no cover + elif setprivate == False and makeprivate == True: # pragma: no cover makeprivate = False - # reorder in chronological order ws = Workout.objects.filter(id__in=ids).order_by("startdatetime") @@ -516,7 +493,7 @@ def join_workouts(r,ids,title='Joined Workout', timestr = strftime("%Y%m%d-%H%M%S") csvfilename = 'media/df_' + timestr + '.csv' - row.write_csv(csvfilename,gzip=True) + row.write_csv(csvfilename, gzip=True) id, message = save_workout_database(csvfilename, r, workouttype=workouttype, title=title, @@ -527,12 +504,12 @@ def join_workouts(r,ids,title='Joined Workout', dosmooth=False, consistencychecks=False) - if killparents: # pragma: no cover + if killparents: # pragma: no cover for w in ws: w.delete() w = Workout.objects.get(id=id) - w.duplicate = False + w.duplicate = False w.save() if message is not None and "duplicate" in message: message = "" @@ -547,23 +524,25 @@ def df_resample(datadf): newdf = datadf.groupby(['timestamps']).mean() return newdf -def resample(id,r,parent,overwrite='copy'): + +def resample(id, r, parent, overwrite='copy'): data, row = getrowdata_db(id=id) messages = [] # resample startdatetime = row.startdatetime - data['datetime'] = data['time'].apply(lambda x:startdatetime+datetime.timedelta(seconds=x/1000.)) + data['datetime'] = data['time'].apply( + lambda x: startdatetime+datetime.timedelta(seconds=x/1000.)) - data = data.resample('S',on='datetime').mean() - data.interpolate(method='linear',inplace=True) - data.reset_index(drop=True,inplace=True) + data = data.resample('S', on='datetime').mean() + data.interpolate(method='linear', inplace=True) + data.reset_index(drop=True, inplace=True) - #data.drop('datetime',inplace=True) + # data.drop('datetime',inplace=True) data['pace'] = data['pace'] / 1000. data['time'] = data['time'] / 1000. - if overwrite=='overwrite': + if overwrite == 'overwrite': # remove CP data try: cpfile = 'media/cpdata_{id}.parquet.gz'.format(id=parent.id) @@ -571,7 +550,7 @@ def resample(id,r,parent,overwrite='copy'): except FileNotFoundError: pass # save - data.rename(columns=columndict,inplace=True) + data.rename(columns=columndict, inplace=True) starttimeunix = arrow.get(startdatetime).timestamp() data[' ElapsedTime (sec)'] = data['TimeStamp (sec)'] @@ -580,19 +559,20 @@ def resample(id,r,parent,overwrite='copy'): row = rrdata(df=data) - row.write_csv(parent.csvfilename,gzip=True) + row.write_csv(parent.csvfilename, gzip=True) - res = dataprep(row.df, id=parent.id, bands=True, barchart=True, otwpower=True, empower=True,inboard=parent.inboard) + res = dataprep(row.df, id=parent.id, bands=True, barchart=True, + otwpower=True, empower=True, inboard=parent.inboard) isbreakthrough, ishard = checkbreakthrough(parent, r) marker = check_marker(parent) - result = update_wps(r,mytypes.otwtypes) - result = update_wps(r,mytypes.otetypes) + result = update_wps(r, mytypes.otwtypes) + result = update_wps(r, mytypes.otetypes) - tss,normp = workout_rscore(parent) - goldmedalstandard,goldmedalseconds = workout_goldmedalstandard(parent) + tss, normp = workout_rscore(parent) + goldmedalstandard, goldmedalseconds = workout_goldmedalstandard(parent) else: id, message = new_workout_from_df(r, data, title=parent.name + '(Resampled)', - parent=parent,forceunit='N') + parent=parent, forceunit='N') messages.append(message) return data, id, messages @@ -602,7 +582,6 @@ def clean_df_stats(datadf, workstrokesonly=True, ignorehr=True, ignoreadvanced=False): # clean data remove zeros and negative values - try: workoutids = datadf['workoutid'].unique() except KeyError: @@ -610,43 +589,40 @@ def clean_df_stats(datadf, workstrokesonly=True, ignorehr=True, before = {} for workoutid in datadf['workoutid'].unique(): - before[workoutid] = len(datadf[datadf['workoutid']==workoutid]) - - + before[workoutid] = len(datadf[datadf['workoutid'] == workoutid]) data_orig = datadf.copy() # bring metrics which have negative values to positive domain - if len(datadf)==0: + if len(datadf) == 0: return datadf try: datadf['catch'] = -datadf['catch'] - except (KeyError,TypeError): + except (KeyError, TypeError): pass try: datadf['peakforceangle'] = datadf['peakforceangle'] + 1000 - except (KeyError,TypeError): + except (KeyError, TypeError): pass try: datadf['hr'] = datadf['hr'] + 10 - except (KeyError,TypeError): + except (KeyError, TypeError): pass # protect 0 spm values from being nulled try: datadf['spm'] = datadf['spm'] + 1.0 - except (KeyError,TypeError) as e: + except (KeyError, TypeError) as e: pass # protect 0 workoutstate values from being nulled try: datadf['workoutstate'] = datadf['workoutstate'] + 1 - except (KeyError,TypeError) as e: + except (KeyError, TypeError) as e: pass - try: datadf = datadf.clip(lower=0) except TypeError: @@ -676,96 +652,91 @@ def clean_df_stats(datadf, workstrokesonly=True, ignorehr=True, # bring spm back to real values try: datadf['spm'] = datadf['spm'] - 1 - except (TypeError,KeyError) as e: + except (TypeError, KeyError) as e: pass # bring workoutstate back to real values try: datadf['workoutstate'] = datadf['workoutstate'] - 1 - except (TypeError,KeyError) as e: + except (TypeError, KeyError) as e: pass - # return from positive domain to negative try: datadf['catch'] = -datadf['catch'] - except (KeyError,TypeError): + except (KeyError, TypeError): pass try: datadf['peakforceangle'] = datadf['peakforceangle'] - 1000 - except (KeyError,TypeError): + except (KeyError, TypeError): pass try: datadf['hr'] = datadf['hr'] - 10 - except (KeyError,TypeError): + except (KeyError, TypeError): pass # clean data for useful ranges per column if not ignorehr: try: mask = datadf['hr'] < 30 - datadf.mask(mask,inplace=True) - except (KeyError,TypeError): # pragma: no cover + datadf.mask(mask, inplace=True) + except (KeyError, TypeError): # pragma: no cover pass try: mask = datadf['spm'] < 0 - datadf.mask(mask,inplace=True) - except (KeyError,TypeError): + datadf.mask(mask, inplace=True) + except (KeyError, TypeError): pass - try: mask = datadf['efficiency'] > 200. - datadf.mask(mask,inplace=True) - except (KeyError,TypeError): + datadf.mask(mask, inplace=True) + except (KeyError, TypeError): pass - try: mask = datadf['spm'] < 10 - datadf.mask(mask,inplace=True) - except (KeyError,TypeError): + datadf.mask(mask, inplace=True) + except (KeyError, TypeError): pass - try: mask = datadf['pace'] / 1000. > 300. - datadf.mask(mask,inplace=True) - except (KeyError,TypeError): + datadf.mask(mask, inplace=True) + except (KeyError, TypeError): pass try: mask = datadf['efficiency'] < 0. - datadf.mask(mask,inplace=True) - except (KeyError,TypeError): + datadf.mask(mask, inplace=True) + except (KeyError, TypeError): pass try: mask = datadf['pace'] / 1000. < 60. - datadf.mask(mask,inplace=True) - except (KeyError,TypeError): + datadf.mask(mask, inplace=True) + except (KeyError, TypeError): pass try: mask = datadf['power'] > 5000 - datadf.mask(mask,inplace=True) - except (KeyError,TypeError): + datadf.mask(mask, inplace=True) + except (KeyError, TypeError): pass try: mask = datadf['spm'] > 120 - datadf.mask(mask,inplace=True) - except (KeyError,TypeError): + datadf.mask(mask, inplace=True) + except (KeyError, TypeError): pass - try: mask = datadf['wash'] < 1 datadf.loc[mask, 'wash'] = np.nan - except (KeyError,TypeError): + except (KeyError, TypeError): pass # try to guess ignoreadvanced @@ -781,71 +752,70 @@ def clean_df_stats(datadf, workstrokesonly=True, ignorehr=True, if not ignoreadvanced: try: mask = datadf['rhythm'] < 0 - datadf.mask(mask,inplace=True) - except (KeyError,TypeError): + datadf.mask(mask, inplace=True) + except (KeyError, TypeError): pass try: mask = datadf['rhythm'] > 70 - datadf.mask(mask,inplace=True) - except (KeyError,TypeError): + datadf.mask(mask, inplace=True) + except (KeyError, TypeError): pass try: mask = datadf['power'] < 20 - datadf.mask(mask,inplace=True) - except (KeyError,TypeError): + datadf.mask(mask, inplace=True) + except (KeyError, TypeError): pass try: mask = datadf['drivelength'] < 0.5 - datadf.mask(mask,inplace=True) - except (KeyError,TypeError): + datadf.mask(mask, inplace=True) + except (KeyError, TypeError): pass try: mask = datadf['forceratio'] < 0.2 - datadf.mask(mask,inplace=True) - except (KeyError,TypeError): + datadf.mask(mask, inplace=True) + except (KeyError, TypeError): pass try: mask = datadf['forceratio'] > 1.0 - datadf.mask(mask,inplace=True) - except (KeyError,TypeError): + datadf.mask(mask, inplace=True) + except (KeyError, TypeError): pass try: mask = datadf['drivespeed'] < 0.5 - datadf.mask(mask,inplace=True) - except (KeyError,TypeError): + datadf.mask(mask, inplace=True) + except (KeyError, TypeError): pass try: mask = datadf['drivespeed'] > 4 - datadf.mask(mask,inplace=True) - except (KeyError,TypeError): + datadf.mask(mask, inplace=True) + except (KeyError, TypeError): pass try: mask = datadf['driveenergy'] > 2000 - datadf.mask(mask,inplace=True) - except (KeyError,TypeError): + datadf.mask(mask, inplace=True) + except (KeyError, TypeError): pass try: mask = datadf['driveenergy'] < 100 - datadf.mask(mask,inplace=True) - except (KeyError,TypeError): + datadf.mask(mask, inplace=True) + except (KeyError, TypeError): pass try: mask = datadf['catch'] > -30. - datadf.mask(mask,inplace=True) - except (KeyError,TypeError): + datadf.mask(mask, inplace=True) + except (KeyError, TypeError): pass - workoutstateswork = [1, 4, 5, 8, 9, 6, 7] workoutstatesrest = [3] workoutstatetransition = [0, 2, 10, 11, 12, 13] @@ -858,25 +828,27 @@ def clean_df_stats(datadf, workstrokesonly=True, ignorehr=True, after = {} for workoutid in data_orig['workoutid'].unique(): - after[workoutid] = len(datadf[datadf['workoutid']==workoutid].dropna()) + after[workoutid] = len( + datadf[datadf['workoutid'] == workoutid].dropna()) ratio = float(after[workoutid])/float(before[workoutid]) if ratio < 0.01 or after[workoutid] < 2: return data_orig return datadf -def getpartofday(row,r): + +def getpartofday(row, r): workoutstartdatetime = row.rowdatetime - try: # pragma: no cover + try: # pragma: no cover latavg = row.df[' latitude'].mean() lonavg = row.df[' longitude'].mean() tf = TimezoneFinder() try: timezone_str = tf.timezone_at(lng=lonavg, lat=latavg) - except (ValueError,OverflowError): # pragma: no cover + except (ValueError, OverflowError): # pragma: no cover timezone_str = 'UTC' - if timezone_str == None: # pragma: no cover + if timezone_str == None: # pragma: no cover timezone_str = tf.closest_timezone_at(lng=lonavg, lat=latavg) if timezone_str == None: @@ -893,19 +865,20 @@ def getpartofday(row,r): h = workoutstartdatetime.astimezone(pytz.timezone(timezone_str)).hour - if h < 12: # pragma: no cover + if h < 12: # pragma: no cover return "Morning" - elif h < 18: # pragma: no cover + elif h < 18: # pragma: no cover return "Afternoon" - elif h < 22: # pragma: no cover + elif h < 22: # pragma: no cover return "Evening" - else: # pragma: no cover + else: # pragma: no cover return "Night" - return None # pragma: no cover + return None # pragma: no cover + def getstatsfields(): - fielddict = {name:d['verbose_name'] for name,d in rowingmetrics} + fielddict = {name: d['verbose_name'] for name, d in rowingmetrics} # fielddict.pop('ergpace') # fielddict.pop('hr_an') @@ -937,7 +910,6 @@ def getstatsfields(): return fieldlist, fielddict - # A string representation for time deltas def niceformat(values): out = [] @@ -954,7 +926,7 @@ def strfdelta(tdelta): try: minutes, seconds = divmod(tdelta.seconds, 60) tenths = int(tdelta.microseconds / 1e5) - except AttributeError: # pragma: no cover + except AttributeError: # pragma: no cover minutes, seconds = divmod(tdelta.view(np.int64), 60e9) seconds, rest = divmod(seconds, 1e9) tenths = int(rest / 1e8) @@ -966,7 +938,8 @@ def strfdelta(tdelta): return res -def timedelta_to_seconds(tdelta): # pragma: no cover + +def timedelta_to_seconds(tdelta): # pragma: no cover return 60.*tdelta.minute+tdelta.second @@ -1002,11 +975,13 @@ def paceformatsecs(values): return out -def update_c2id_sql(id,c2id): + +def update_c2id_sql(id, c2id): engine = create_engine(database_url, echo=False) table = 'rowers_workout' - query = "UPDATE %s SET uploadedtoc2 = %s WHERE `id` = %s;" % (table,c2id,id) + query = "UPDATE %s SET uploadedtoc2 = %s WHERE `id` = %s;" % ( + table, c2id, id) with engine.connect() as conn, conn.begin(): result = conn.execute(query) @@ -1017,24 +992,24 @@ def update_c2id_sql(id,c2id): return 1 - -def getcpdata_sql(rower_id,table='cpdata'): +def getcpdata_sql(rower_id, table='cpdata'): engine = create_engine(database_url, echo=False) query = sa.text('SELECT * from {table} WHERE user={rower_id};'.format( rower_id=rower_id, table=table, - )) + )) connection = engine.raw_connection() df = pd.read_sql_query(query, engine) return df -def deletecpdata_sql(rower_id,table='cpdata'): # pragma: no cover + +def deletecpdata_sql(rower_id, table='cpdata'): # pragma: no cover engine = create_engine(database_url, echo=False) query = sa.text('DELETE from {table} WHERE user={rower_id};'.format( rower_id=rower_id, table=table, - )) + )) with engine.connect() as conn, conn.begin(): try: result = conn.execute(query) @@ -1044,14 +1019,13 @@ def deletecpdata_sql(rower_id,table='cpdata'): # pragma: no cover engine.dispose() - -def updatecpdata_sql(rower_id,delta,cp,table='cpdata',distance=[]): # pragma: no cover +def updatecpdata_sql(rower_id, delta, cp, table='cpdata', distance=[]): # pragma: no cover deletecpdata_sql(rower_id) df = pd.DataFrame( { - 'delta':delta, - 'cp':cp, - 'user':rower_id + 'delta': delta, + 'cp': cp, + 'user': rower_id } ) @@ -1064,10 +1038,11 @@ def updatecpdata_sql(rower_id,delta,cp,table='cpdata',distance=[]): # pragma: no conn.close() engine.dispose() -def fetchcperg(rower,theworkouts): + +def fetchcperg(rower, theworkouts): theids = [int(w.id) for w in theworkouts] thefilenames = [w.csvfilename for w in theworkouts] - cpdf = getcpdata_sql(rower.id,table='ergcpdata') + cpdf = getcpdata_sql(rower.id, table='ergcpdata') job = myqueue( queuelow, @@ -1077,24 +1052,23 @@ def fetchcperg(rower,theworkouts): return cpdf -from rowers.datautils import p0 -from rowers.utils import calculate_age -from scipy import optimize -def get_workoutsummaries(userid,startdate): # pragma: no cover +def get_workoutsummaries(userid, startdate): # pragma: no cover u = User.objects.get(id=userid) r = u.rower - df = workout_summary_to_df(r,startdate=startdate) - df.drop(['Stroke Data TCX','Stroke Data CSV'],axis=1,inplace=True) - df = df.sort_values('date',ascending=False) + df = workout_summary_to_df(r, startdate=startdate) + df.drop(['Stroke Data TCX', 'Stroke Data CSV'], axis=1, inplace=True) + df = df.sort_values('date', ascending=False) return df -def workout_goldmedalstandard(workout,reset=False): + +def workout_goldmedalstandard(workout, reset=False): if workout.goldmedalstandard > 0 and not reset: - return workout.goldmedalstandard,workout.goldmedalseconds + return workout.goldmedalstandard, workout.goldmedalseconds if workout.workouttype in rowtypes: - goldmedalstandard,goldmedalseconds = calculate_goldmedalstandard(workout.user,workout) + goldmedalstandard, goldmedalseconds = calculate_goldmedalstandard( + workout.user, workout) if workout.workouttype in otwtypes: factor = 100./(100.-workout.user.otwslack) goldmedalstandard = goldmedalstandard*factor @@ -1103,40 +1077,41 @@ def workout_goldmedalstandard(workout,reset=False): workout.save() return goldmedalstandard, goldmedalseconds else: - return 0,0 + return 0, 0 + def check_marker(workout): r = workout.user - gmstandard,gmseconds = workout_goldmedalstandard(workout) - if gmseconds<60: + gmstandard, gmseconds = workout_goldmedalstandard(workout) + if gmseconds < 60: return None dd = arrow.get(workout.date).datetime-datetime.timedelta(days=r.kfit) ws = Workout.objects.filter(date__gte=dd, date__lte=workout.date, - user=r,duplicate=False, + user=r, duplicate=False, workouttype__in=mytypes.rowtypes, ).order_by("date") ids = [] gms = [] for w in ws: - gmstandard,gmseconds = workout_goldmedalstandard(w) - if gmseconds>60: + gmstandard, gmseconds = workout_goldmedalstandard(w) + if gmseconds > 60: ids.append(w.id) gms.append(gmstandard) df = pd.DataFrame({ - 'id':ids, - 'gms':gms, + 'id': ids, + 'gms': gms, }) - if df.empty: # pragma: no cover + if df.empty: # pragma: no cover workout.ranking = True workout.save() return workout indexmax = df['gms'].idxmax() - theid = df.loc[indexmax,'id'] + theid = df.loc[indexmax, 'id'] wmax = Workout.objects.get(id=theid) gms_max = wmax.goldmedalstandard @@ -1150,18 +1125,17 @@ def check_marker(workout): return wmax lastranking = rankingworkouts[len(rankingworkouts)-1] - if lastranking.goldmedalstandard+0.2 < wmax.goldmedalstandard: # pragma: no cover + if lastranking.goldmedalstandard+0.2 < wmax.goldmedalstandard: # pragma: no cover wmax.rankingpiece = True wmax.save() return wmax - else: # pragma: no cover + else: # pragma: no cover return wmax return None - -def calculate_goldmedalstandard(rower,workout,recurrance=True): +def calculate_goldmedalstandard(rower, workout, recurrance=True): cpfile = 'media/cpdata_{id}.parquet.gz'.format(id=workout.id) try: df = pd.read_parquet(cpfile) @@ -1169,44 +1143,40 @@ def calculate_goldmedalstandard(rower,workout,recurrance=True): background = True if settings.TESTING: background = False - df, delta, cpvalues = setcp(workout,background=background) + df, delta, cpvalues = setcp(workout, background=background) if df.empty: - return 0,0 + return 0, 0 - - if df.empty and recurrance: # pragma: no cover - df, delta, cpvalues = setcp(workout,recurrance=False,background=True) + if df.empty and recurrance: # pragma: no cover + df, delta, cpvalues = setcp(workout, recurrance=False, background=True) if df.empty: - return 0,0 - - age = calculate_age(rower.birthdate,today=workout.date) - + return 0, 0 + age = calculate_age(rower.birthdate, today=workout.date) agerecords = CalcAgePerformance.objects.filter( age=age, sex=rower.sex, - weightcategory = rower.weightcategory + weightcategory=rower.weightcategory ) - wcdurations = [] wcpower = [] getrecords = False - if not settings.TESTING: # pragma: no cover - if len(agerecords) == 0: # pragma: no cover + if not settings.TESTING: # pragma: no cover + if len(agerecords) == 0: # pragma: no cover getrecords = True - for record in agerecords: # pragma: no cover + for record in agerecords: # pragma: no cover if record.power > 0: wcdurations.append(record.duration) wcpower.append(record.power) else: getrecords = True - if getrecords: # pragma: no cover - durations = [1,4,30,60] - distances = [100,500,1000,2000,5000,6000,10000,21097,42195] + if getrecords: # pragma: no cover + durations = [1, 4, 30, 60] + distances = [100, 500, 1000, 2000, 5000, 6000, 10000, 21097, 42195] df2 = pd.DataFrame( list( C2WorldClassAgePerformance.objects.filter( @@ -1216,42 +1186,44 @@ def calculate_goldmedalstandard(rower,workout,recurrance=True): ) ) jsondf = df2.to_json() - job = myqueue(queuelow,handle_getagegrouprecords, - jsondf,distances,durations,age,rower.sex,rower.weightcategory) + job = myqueue(queuelow, handle_getagegrouprecords, + jsondf, distances, durations, age, rower.sex, rower.weightcategory) - wcpower = pd.Series(wcpower,dtype='float') - wcdurations = pd.Series(wcdurations,dtype='float') + wcpower = pd.Series(wcpower, dtype='float') + wcdurations = pd.Series(wcdurations, dtype='float') - fitfunc = lambda pars,x: pars[0]/(1+(x/pars[2])) + pars[1]/(1+(x/pars[3])) - errfunc = lambda pars,x,y: fitfunc(pars,x)-y + def fitfunc(pars, x): return pars[0] / \ + (1+(x/pars[2])) + pars[1]/(1+(x/pars[3])) - if len(wcdurations)>=4: # pragma: no cover - p1wc, success = optimize.leastsq(errfunc, p0[:],args=(wcdurations,wcpower)) + def errfunc(pars, x, y): return fitfunc(pars, x)-y + + if len(wcdurations) >= 4: # pragma: no cover + p1wc, success = optimize.leastsq( + errfunc, p0[:], args=(wcdurations, wcpower)) else: - factor = fitfunc(p0,wcdurations.mean()/wcpower.mean()) - p1wc = [p0[0]/factor,p0[1]/factor,p0[2],p0[3]] + factor = fitfunc(p0, wcdurations.mean()/wcpower.mean()) + p1wc = [p0[0]/factor, p0[1]/factor, p0[2], p0[3]] success = 0 - return 0,0 - + return 0, 0 times = df['delta'] powers = df['cp'] - wcpowers = fitfunc(p1wc,times) + wcpowers = fitfunc(p1wc, times) scores = 100.*powers/wcpowers - try: indexmax = scores.idxmax() - delta = int(df.loc[indexmax,'delta']) + delta = int(df.loc[indexmax, 'delta']) maxvalue = scores.max() - except (ValueError,TypeError): # pragma: no cover + except (ValueError, TypeError): # pragma: no cover indexmax = 0 delta = 0 maxvalue = 0 - return maxvalue,delta + return maxvalue, delta -def fetchcp_new(rower,workouts): + +def fetchcp_new(rower, workouts): data = [] for workout in workouts: @@ -1268,43 +1240,42 @@ def fetchcp_new(rower,workouts): df['url'] = workout.url() data.append(df) - if len(data) == 0: - return pd.Series(dtype='float'),pd.Series(dtype='float'),0,pd.Series(dtype='float'),pd.Series(dtype='float') - if len(data)>1: - df = pd.concat(data,axis=0) + return pd.Series(dtype='float'), pd.Series(dtype='float'), 0, pd.Series(dtype='float'), pd.Series(dtype='float') + if len(data) > 1: + df = pd.concat(data, axis=0) try: df = df[df['cp'] == df.groupby(['delta'])['cp'].transform('max')] - except KeyError: # pragma: no cover - return pd.Series(dtype='float'),pd.Series(dtype='float'),0,pd.Series(dtype='float'),pd.Series(dtype='float') - + except KeyError: # pragma: no cover + return pd.Series(dtype='float'), pd.Series(dtype='float'), 0, pd.Series(dtype='float'), pd.Series(dtype='float') df = df.sort_values(['delta']).reset_index() - return df['delta'],df['cp'],0,df['workout'],df['url'] + return df['delta'], df['cp'], 0, df['workout'], df['url'] -def setcp(workout,background=False,recurrance=True): + +def setcp(workout, background=False, recurrance=True): filename = 'media/cpdata_{id}.parquet.gz'.format(id=workout.id) - strokesdf = getsmallrowdata_db(['power','workoutid','time'],ids = [workout.id]) - + strokesdf = getsmallrowdata_db( + ['power', 'workoutid', 'time'], ids=[workout.id]) try: - if strokesdf['power'].std()==0: - return pd.DataFrame(),pd.Series(dtype='float'),pd.Series(dtype='float') + if strokesdf['power'].std() == 0: + return pd.DataFrame(), pd.Series(dtype='float'), pd.Series(dtype='float') except KeyError: - return pd.DataFrame(),pd.Series(dtype='float'),pd.Series(dtype='float') + return pd.DataFrame(), pd.Series(dtype='float'), pd.Series(dtype='float') - if background: # pragma: no cover - job = myqueue(queuelow,handle_setcp,strokesdf,filename,workout.id) - return pd.DataFrame({'delta':[],'cp':[]}),pd.Series(dtype='float'),pd.Series(dtype='float') + if background: # pragma: no cover + job = myqueue(queuelow, handle_setcp, strokesdf, filename, workout.id) + return pd.DataFrame({'delta': [], 'cp': []}), pd.Series(dtype='float'), pd.Series(dtype='float') if not strokesdf.empty: totaltime = strokesdf['time'].max() try: powermean = strokesdf['power'].mean() - except KeyError: # pragma: no cover + except KeyError: # pragma: no cover powermean = 0 if powermean != 0: @@ -1317,26 +1288,29 @@ def setcp(workout,background=False,recurrance=True): delta, cpvalues, avgpower = datautils.getcp(dfgrouped, logarr) df = pd.DataFrame({ - 'delta':delta, - 'cp':cpvalues, - 'id':workout.id, + 'delta': delta, + 'cp': cpvalues, + 'id': workout.id, }) - df.to_parquet(filename,engine='fastparquet',compression='GZIP') + df.to_parquet(filename, engine='fastparquet', + compression='GZIP') if recurrance: - goldmedalstandard, goldmedalduration = calculate_goldmedalstandard(workout.user,workout) - workout.goldmedalstandard = goldmedalstandard + goldmedalstandard, goldmedalduration = calculate_goldmedalstandard( + workout.user, workout) + workout.goldmedalstandard = goldmedalstandard workout.goldmedalduration = goldmedalduration workout.save() - return df,delta,cpvalues + return df, delta, cpvalues - return pd.DataFrame({'delta':[],'cp':[]}),pd.Series(dtype='float'),pd.Series(dtype='float') + return pd.DataFrame({'delta': [], 'cp': []}), pd.Series(dtype='float'), pd.Series(dtype='float') -def update_wps(r,types,mode='water',asynchron=True): + +def update_wps(r, types, mode='water', asynchron=True): firstdate = timezone.now()-datetime.timedelta(days=r.cprange) workouts = Workout.objects.filter( date__gte=firstdate, workouttype__in=types, - user = r + user=r ) ids = [w.id for w in workouts] @@ -1350,47 +1324,45 @@ def update_wps(r,types,mode='water',asynchron=True): mode ) - df = getsmallrowdata_db(['time','driveenergy'],ids=ids) + df = getsmallrowdata_db(['time', 'driveenergy'], ids=ids) try: mask = df['driveenergy'] > 100 except (KeyError, TypeError): return False try: - wps_median = int(df.loc[mask,'driveenergy'].median()) + wps_median = int(df.loc[mask, 'driveenergy'].median()) if mode == 'water': r.median_wps = wps_median - else:# pragma: no cover + else: # pragma: no cover r.median_wps_erg = wps_median r.save() - except ValueError: # pragma: no cover + except ValueError: # pragma: no cover pass - return True - -def update_rolling_cp(r,types,mode='water'): +def update_rolling_cp(r, types, mode='water'): firstdate = timezone.now()-datetime.timedelta(days=r.cprange) workouts = Workout.objects.filter( date__gte=firstdate, workouttype__in=types, - user = r + user=r ) - delta, cp, avgpower, workoutnames,urls = fetchcp_new(r,workouts) + delta, cp, avgpower, workoutnames, urls = fetchcp_new(r, workouts) powerdf = pd.DataFrame({ - 'Delta':delta, - 'CP':cp, + 'Delta': delta, + 'CP': cp, }) - powerdf = powerdf[powerdf['CP']>0] - powerdf.dropna(axis=0,inplace=True) - powerdf.sort_values(['Delta','CP'],ascending=[1,0],inplace=True) - powerdf.drop_duplicates(subset='Delta',keep='first',inplace=True) + powerdf = powerdf[powerdf['CP'] > 0] + powerdf.dropna(axis=0, inplace=True) + powerdf.sort_values(['Delta', 'CP'], ascending=[1, 0], inplace=True) + powerdf.drop_duplicates(subset='Delta', keep='first', inplace=True) res2 = datautils.cpfit(powerdf) if len(powerdf) != 0: @@ -1414,35 +1386,36 @@ def update_rolling_cp(r,types,mode='water'): return True return False -def fetchcp(rower,theworkouts,table='cpdata'): # pragma: no cover + +def fetchcp(rower, theworkouts, table='cpdata'): # pragma: no cover # get all power data from database (plus workoutid) theids = [int(w.id) for w in theworkouts] - columns = ['power','workoutid','time'] - df = getsmallrowdata_db(columns,ids=theids) - df.dropna(inplace=True,axis=0) + columns = ['power', 'workoutid', 'time'] + df = getsmallrowdata_db(columns, ids=theids) + df.dropna(inplace=True, axis=0) if df.empty: avgpower2 = {} - for id in theids: + for id in theids: avgpower2[id] = 0 - return pd.Series([],dtype='float'),pd.Series([],dtype='float'),avgpower2 + return pd.Series([], dtype='float'), pd.Series([], dtype='float'), avgpower2 try: dfgrouped = df.groupby(['workoutid']) except KeyError: avgpower2 = {} - return pd.Series([],dtype='float'),pd.Series([],dtype='float'),avgpower2 + return pd.Series([], dtype='float'), pd.Series([], dtype='float'), avgpower2 try: avgpower2 = dict(dfgrouped.mean()['power'].astype(int)) except KeyError: avgpower2 = {} for id in theids: avgpower2[id] = 0 - return pd.Series([],dtype='float'),pd.Series([],dtype='float'),avgpower2 + return pd.Series([], dtype='float'), pd.Series([], dtype='float'), avgpower2 - cpdf = getcpdata_sql(rower.id,table=table) + cpdf = getcpdata_sql(rower.id, table=table) if not cpdf.empty: - return cpdf['delta'],cpdf['cp'],avgpower2 + return cpdf['delta'], cpdf['cp'], avgpower2 else: job = myqueue(queuelow, handle_updatecp, @@ -1450,18 +1423,17 @@ def fetchcp(rower,theworkouts,table='cpdata'): # pragma: no cover theids, table=table) - return pd.Series([],dtype='float'),pd.Series([],dtype='float'),avgpower2 + return pd.Series([], dtype='float'), pd.Series([], dtype='float'), avgpower2 - - return pd.Series([],dtype='float'),pd.Series([],dtype='float'),avgpower2 + return pd.Series([], dtype='float'), pd.Series([], dtype='float'), avgpower2 # create a new workout from manually entered data -def create_row_df(r,distance,duration,startdatetime,workouttype='rower', - avghr=None,avgpwr=None,avgspm=None, - rankingpiece = False, - duplicate=False,rpe=-1, - title='Manual entry',notes='',weightcategory='hwt', +def create_row_df(r, distance, duration, startdatetime, workouttype='rower', + avghr=None, avgpwr=None, avgspm=None, + rankingpiece=False, + duplicate=False, rpe=-1, + title='Manual entry', notes='', weightcategory='hwt', adaptiveclass='None'): if duration is not None: @@ -1469,24 +1441,23 @@ def create_row_df(r,distance,duration,startdatetime,workouttype='rower', totalseconds += duration.minute*60. totalseconds += duration.second totalseconds += duration.microsecond/1.e6 - else: # pragma: no cover + else: # pragma: no cover totalseconds = 60. - if distance is None: # pragma: no cover + if distance is None: # pragma: no cover distance = 0 try: nr_strokes = int(distance/10.) - except TypeError: # pragma: no cover + except TypeError: # pragma: no cover nr_strokes = int(20.*totalseconds) - if nr_strokes == 0: # pragma: no cover + if nr_strokes == 0: # pragma: no cover nr_strokes = 100 unixstarttime = arrow.get(startdatetime).timestamp() - - if not avgspm: # pragma: no cover + if not avgspm: # pragma: no cover try: spm = 60.*nr_strokes/totalseconds except ZeroDivisionError: @@ -1506,33 +1477,33 @@ def create_row_df(r,distance,duration,startdatetime,workouttype='rower', try: pace = 500.*totalseconds/distance - except ZeroDivisionError: # pragma: no cover + except ZeroDivisionError: # pragma: no cover pace = 240. - if workouttype in ['rower','slides','dynamic']: + if workouttype in ['rower', 'slides', 'dynamic']: try: velo = distance/totalseconds - except ZeroDivisionError: # pragma: no cover + except ZeroDivisionError: # pragma: no cover velo = 2.4 power = 2.8*velo**3 - elif avgpwr is not None: # pragma: no cover + elif avgpwr is not None: # pragma: no cover power = avgpwr - else: # pragma: no cover + else: # pragma: no cover power = 0 if avghr is not None: hr = avghr - else: # pragma: no cover + else: # pragma: no cover hr = 0 df = pd.DataFrame({ 'TimeStamp (sec)': unixtime, ' Horizontal (meters)': d, ' Cadence (stokes/min)': spm, - ' Stroke500mPace (sec/500m)':pace, - ' ElapsedTime (sec)':elapsed, - ' Power (watts)':power, - ' HRCur (bpm)':hr, + ' Stroke500mPace (sec/500m)': pace, + ' ElapsedTime (sec)': elapsed, + ' Power (watts)': power, + ' HRCur (bpm)': hr, }) timestr = strftime("%Y%m%d-%H%M%S") @@ -1542,7 +1513,7 @@ def create_row_df(r,distance,duration,startdatetime,workouttype='rower', row = rrdata(df=df) - row.write_csv(csvfilename, gzip = True) + row.write_csv(csvfilename, gzip=True) id, message = save_workout_database(csvfilename, r, title=title, @@ -1558,30 +1529,29 @@ def create_row_df(r,distance,duration,startdatetime,workouttype='rower', return (id, message) -from rowers.utils import totaltime_sec_to_string def checkbreakthrough(w, r): isbreakthrough = False ishard = False workouttype = w.workouttype if workouttype in rowtypes: - cpdf,delta,cpvalues = setcp(w) + cpdf, delta, cpvalues = setcp(w) if not cpdf.empty: if workouttype in otwtypes: res, btvalues, res2 = utils.isbreakthrough( delta, cpvalues, r.p0, r.p1, r.p2, r.p3, r.cpratio) - success = update_rolling_cp(r,otwtypes,'water') + success = update_rolling_cp(r, otwtypes, 'water') elif workouttype in otetypes: res, btvalues, res2 = utils.isbreakthrough( delta, cpvalues, r.ep0, r.ep1, r.ep2, r.ep3, r.ecpratio) - success = update_rolling_cp(r,otetypes,'erg') - else: # pragma: no cover + success = update_rolling_cp(r, otetypes, 'erg') + else: # pragma: no cover res = 0 res2 = 0 if res: isbreakthrough = True - if res2 and not isbreakthrough: # pragma: no cover + if res2 and not isbreakthrough: # pragma: no cover ishard = True # submit email task to send email about breakthrough workout @@ -1589,8 +1559,8 @@ def checkbreakthrough(w, r): if not w.duplicate: w.rankingpiece = True w.save() - if r.getemailnotifications and not r.emailbounced: # pragma: no cover - job = myqueue(queuehigh,handle_sendemail_breakthrough, + if r.getemailnotifications and not r.emailbounced: # pragma: no cover + job = myqueue(queuehigh, handle_sendemail_breakthrough, w.id, r.user.email, r.user.first_name, @@ -1598,12 +1568,12 @@ def checkbreakthrough(w, r): btvalues=btvalues.to_json()) # submit email task to send email about breakthrough workout - if ishard: # pragma: no cover + if ishard: # pragma: no cover if not w.duplicate: w.rankingpiece = True w.save() if r.getemailnotifications and not r.emailbounced: - job = myqueue(queuehigh,handle_sendemail_hard, + job = myqueue(queuehigh, handle_sendemail_hard, w.id, r.user.email, r.user.first_name, @@ -1613,9 +1583,9 @@ def checkbreakthrough(w, r): return isbreakthrough, ishard -def checkduplicates(r,workoutdate,workoutstartdatetime,workoutenddatetime): +def checkduplicates(r, workoutdate, workoutstartdatetime, workoutenddatetime): duplicate = False - ws = Workout.objects.filter(user=r,date=workoutdate,duplicate=False).exclude( + ws = Workout.objects.filter(user=r, date=workoutdate, duplicate=False).exclude( startdatetime__gt=workoutenddatetime ) @@ -1623,12 +1593,12 @@ def checkduplicates(r,workoutdate,workoutstartdatetime,workoutenddatetime): for ww in ws: t = ww.duration - delta = datetime.timedelta(hours=t.hour, minutes=t.minute, seconds=t.second) + delta = datetime.timedelta( + hours=t.hour, minutes=t.minute, seconds=t.second) enddatetime = ww.startdatetime+delta if enddatetime > workoutstartdatetime: ws2.append(ww) - if (len(ws2) != 0): message = "Warning: This workout overlaps with an existing one and was marked as a duplicate" duplicate = True @@ -1670,8 +1640,8 @@ def save_workout_database(f2, r, dosmooth=True, workouttype='rower', powerperc=powerperc, powerzones=r.powerzones) row = rdata(f2, rower=rr) - - startdatetime,startdate,starttime,timezone_str,partofday = get_startdate_time_zone(r,row,startdatetime=startdatetime) + startdatetime, startdate, starttime, timezone_str, partofday = get_startdate_time_zone( + r, row, startdatetime=startdatetime) if title is None or title == '': title = 'Workout' @@ -1680,9 +1650,9 @@ def save_workout_database(f2, r, dosmooth=True, workouttype='rower', title = '{partofday} {workouttype}'.format( partofday=partofday, workouttype=workouttype, - ) + ) - if row.df.empty: # pragma: no cover + if row.df.empty: # pragma: no cover return (0, 'Error: CSV data file was empty') dtavg = row.df['TimeStamp (sec)'].diff().mean() @@ -1694,23 +1664,23 @@ def save_workout_database(f2, r, dosmooth=True, workouttype='rower', except: pass return new_workout_from_df(r, newdf, - title=title,boattype=boattype, + title=title, boattype=boattype, workouttype=workouttype, - workoutsource=workoutsource,startdatetime=startdatetime) + workoutsource=workoutsource, startdatetime=startdatetime) try: checks = row.check_consistency() allchecks = 1 for key, value in checks.items(): if not value: allchecks = 0 - except ZeroDivisionError: # pragma: no cover + except ZeroDivisionError: # pragma: no cover pass if not allchecks and consistencychecks: # row.repair() pass - if row == 0: # pragma: no cover + if row == 0: # pragma: no cover return (0, 'Error: CSV data file not found') try: @@ -1728,17 +1698,17 @@ def save_workout_database(f2, r, dosmooth=True, workouttype='rower', f = row.df['TimeStamp (sec)'].diff().mean() if f != 0 and not np.isnan(f): windowsize = 2 * (int(10. / (f))) + 1 - else: # pragma: no cover + else: # pragma: no cover windowsize = 1 if not 'originalvelo' in row.df: row.df['originalvelo'] = velo if windowsize > 3 and windowsize < len(velo): velo2 = savgol_filter(velo, windowsize, 3) - else: # pragma: no cover + else: # pragma: no cover velo2 = velo - velo3 = pd.Series(velo2,dtype='float') + velo3 = pd.Series(velo2, dtype='float') velo3 = velo3.replace([-np.inf, np.inf], np.nan) velo3 = velo3.fillna(method='ffill') @@ -1773,23 +1743,20 @@ def save_workout_database(f2, r, dosmooth=True, workouttype='rower', ) - row.df['TimeStamp (sec)'].min() try: totaltime = totaltime + row.df.loc[:, ' ElapsedTime (sec)'].iloc[0] - except KeyError: # pragma: no cover + except KeyError: # pragma: no cover pass - if np.isnan(totaltime): # pragma: no cover + if np.isnan(totaltime): # pragma: no cover totaltime = 0 - if dosummary: summary = row.allstats() - workoutstartdatetime = startdatetime - - dologging('debuglog.log','Dataprep line 1721, Workout Startdatetime {workoutstartdatetime}'.format( + dologging('debuglog.log', 'Dataprep line 1721, Workout Startdatetime {workoutstartdatetime}'.format( workoutstartdatetime=workoutstartdatetime, - )) + )) duration = totaltime_sec_to_string(totaltime) @@ -1800,10 +1767,9 @@ def save_workout_database(f2, r, dosmooth=True, workouttype='rower', workoutdate=workoutdate, workoutstarttime=workoutstarttime, ) - dologging('debuglog.log',s) + dologging('debuglog.log', s) - - if makeprivate: # pragma: no cover + if makeprivate: # pragma: no cover privacy = 'hidden' else: privacy = 'visible' @@ -1818,23 +1784,24 @@ def save_workout_database(f2, r, dosmooth=True, workouttype='rower', if workouttype in otetypes: dragfactor = row.dragfactor - t = datetime.datetime.strptime(duration,"%H:%M:%S.%f") - delta = datetime.timedelta(hours=t.hour, minutes=t.minute, seconds=t.second) + t = datetime.datetime.strptime(duration, "%H:%M:%S.%f") + delta = datetime.timedelta( + hours=t.hour, minutes=t.minute, seconds=t.second) workoutenddatetime = workoutstartdatetime+delta # check for duplicate start times and duration - duplicate = checkduplicates(r,workoutdate,workoutstartdatetime,workoutenddatetime) + duplicate = checkduplicates( + r, workoutdate, workoutstartdatetime, workoutenddatetime) if duplicate: rankingpiece = False # test title length - if title is not None and len(title)>140: # pragma: no cover + if title is not None and len(title) > 140: # pragma: no cover title = title[0:140] timezone_str = str(workoutstartdatetime.tzinfo) - w = Workout(user=r, name=title, date=workoutdate, workouttype=workouttype, boattype=boattype, @@ -1857,14 +1824,12 @@ def save_workout_database(f2, r, dosmooth=True, workouttype='rower', impeller=impeller) try: w.save() - except ValidationError: # pragma: no cover + except ValidationError: # pragma: no cover try: w.startdatetime = timezone.now() w.save() except ValidationError: - return (0,'Unable to create your workout') - - + return (0, 'Unable to create your workout') if privacy == 'visible': ts = Team.objects.filter(rower=r) @@ -1875,23 +1840,24 @@ def save_workout_database(f2, r, dosmooth=True, workouttype='rower', res = dataprep(row.df, id=w.id, bands=True, barchart=True, otwpower=True, empower=True, inboard=inboard) - isbreakthrough, ishard = checkbreakthrough(w, r) marker = check_marker(w) - result = update_wps(r,mytypes.otwtypes) - result = update_wps(r,mytypes.otetypes) + result = update_wps(r, mytypes.otwtypes) + result = update_wps(r, mytypes.otetypes) - job = myqueue(queuehigh,handle_calctrimp,w.id,f2,r.ftp,r.sex,r.hrftp,r.max,r.rest) + job = myqueue(queuehigh, handle_calctrimp, w.id, f2, + r.ftp, r.sex, r.hrftp, r.max, r.rest) return (w.id, message) + parsers = { 'kinomap': KinoMapParser, 'xls': ExcelTemplate, 'rp': RowProParser, - 'tcx':TCXParser, - 'mystery':MysteryParser, - 'ritmotime':RitmoTimeParser, + 'tcx': TCXParser, + 'mystery': MysteryParser, + 'ritmotime': RitmoTimeParser, 'quiske': QuiskeParser, 'rowperfect3': RowPerfectParser, 'coxmate': CoxMateParser, @@ -1909,13 +1875,14 @@ parsers = { 'nklinklogbook': NKLiNKLogbookParser, 'hero': HeroParser, 'smartrow': SmartRowParser, - } +} -def get_startdate_time_zone(r,row,startdatetime=None): + +def get_startdate_time_zone(r, row, startdatetime=None): if startdatetime is not None and startdatetime != '': try: timezone_str = pendulum.instance(startdatetime).timezone.name - except ValueError: # pragma: no cover + except ValueError: # pragma: no cover timezone_str = 'Ect/GMT' elif startdatetime == '': startdatetime = row.rowdatetime @@ -1924,16 +1891,16 @@ def get_startdate_time_zone(r,row,startdatetime=None): try: tz = startdatetime.tzinfo - except AttributeError: # pragma: no cover + except AttributeError: # pragma: no cover startdatetime = row.rowdatetime - partofday = getpartofday(row,r) + partofday = getpartofday(row, r) - if startdatetime.tzinfo is None or str(startdatetime.tzinfo) in ['tzutc()','Ect/GMT']: + if startdatetime.tzinfo is None or str(startdatetime.tzinfo) in ['tzutc()', 'Ect/GMT']: timezone_str = 'UTC' try: startdatetime = timezone.make_aware(startdatetime) - except ValueError: # pragma: no cover + except ValueError: # pragma: no cover pass try: @@ -1944,24 +1911,24 @@ def get_startdate_time_zone(r,row,startdatetime=None): if row.df[' latitude'].std() != 0: try: timezone_str = tf.timezone_at(lng=lonavg, lat=latavg) - except (ValueError,OverflowError): # pragma: no cover + except (ValueError, OverflowError): # pragma: no cover timezone_str = 'UTC' - if timezone_str is None: # pragma: no cover + if timezone_str is None: # pragma: no cover timezone_str = tf.closest_timezone_at(lng=lonavg, - lat=latavg) - if timezone_str is None: # pragma: no cover + lat=latavg) + if timezone_str is None: # pragma: no cover timezone_str = r.defaulttimezone else: timezone_str = r.defaulttimezone try: startdatetime = pytz.timezone(timezone_str).localize( row.rowdatetime - ) - except ValueError: # pragma: no cover + ) + except ValueError: # pragma: no cover startdatetime = startdatetime.astimezone( pytz.timezone(timezone_str) - ) - except KeyError: # pragma: no cover + ) + except KeyError: # pragma: no cover timezone_str = r.defaulttimezone else: timezone_str = str(startdatetime.tzinfo) @@ -1972,34 +1939,33 @@ def get_startdate_time_zone(r,row,startdatetime=None): starttime = startdatetime.strftime('%H:%M:%S') if timezone_str == 'tzutc()': - timezone_str = 'UTC' # pragma: no cover + timezone_str = 'UTC' # pragma: no cover + + return startdatetime, startdate, starttime, timezone_str, partofday - return startdatetime,startdate,starttime,timezone_str,partofday - - - -def parsenonpainsled(fileformat,f2,summary,startdatetime='',empowerfirmware=None,inboard=None,oarlength=None): +def parsenonpainsled(fileformat, f2, summary, startdatetime='', empowerfirmware=None, inboard=None, oarlength=None): try: - if fileformat == 'nklinklogbook' and empowerfirmware is not None: # pragma: no cover + if fileformat == 'nklinklogbook' and empowerfirmware is not None: # pragma: no cover if inboard is not None and oarlength is not None: - row = NKLiNKLogbookParser(f2,firmware=empowerfirmware,inboard=inboard,oarlength=oarlength) + row = NKLiNKLogbookParser( + f2, firmware=empowerfirmware, inboard=inboard, oarlength=oarlength) else: row = NKLiNKLogbookParser(f2) else: row = parsers[fileformat](f2) - if startdatetime != '': # pragma: no cover + if startdatetime != '': # pragma: no cover row.rowdatetime = arrow.get(startdatetime).datetime hasrecognized = True - except (KeyError,IndexError,ValueError): # pragma: no cover + except (KeyError, IndexError, ValueError): # pragma: no cover hasrecognized = False return None, hasrecognized, '', 'unknown' s = 'Parsenonpainsled, start date time = {startdatetime}'.format( - startdatetime = startdatetime, + startdatetime=startdatetime, #rowdatetime = row.rowdatetime ) - dologging('debuglog.log',s) + dologging('debuglog.log', s) # handle speed coach GPS 2 if (fileformat == 'speedcoach2'): @@ -2007,17 +1973,17 @@ def parsenonpainsled(fileformat,f2,summary,startdatetime='',empowerfirmware=None empowerfirmware = get_empower_firmware(f2) if empowerfirmware != '': fileformat = fileformat+'v'+str(empowerfirmware) - else: # pragma: no cover + else: # pragma: no cover fileformat = 'speedcoach2v0' try: summary = row.allstats() - except ZeroDivisionError: # pragma: no cover + except ZeroDivisionError: # pragma: no cover summary = '' else: fileformat = fileformat+'v'+str(empowerfirmware) # handle FIT - if (fileformat == 'fit'): # pragma: no cover + if (fileformat == 'fit'): # pragma: no cover try: s = fitsummarydata(f2) s.setsummary() @@ -2026,32 +1992,29 @@ def parsenonpainsled(fileformat,f2,summary,startdatetime='',empowerfirmware=None pass hasrecognized = True + return row, hasrecognized, summary, fileformat - return row,hasrecognized,summary,fileformat -def handle_nonpainsled(f2, fileformat, summary='',startdatetime='',empowerfirmware=None,impeller=False): +def handle_nonpainsled(f2, fileformat, summary='', startdatetime='', empowerfirmware=None, impeller=False): oarlength = 2.89 inboard = 0.88 hasrecognized = False - - row,hasrecognized,summary,fileformat = parsenonpainsled(fileformat,f2,summary,startdatetime=startdatetime, - empowerfirmware=empowerfirmware) + row, hasrecognized, summary, fileformat = parsenonpainsled(fileformat, f2, summary, startdatetime=startdatetime, + empowerfirmware=empowerfirmware) # Handle c2log - if (fileformat == 'c2log' or fileformat == 'rowprolog'): # pragma: no cover - return (0,'',0,0,'',impeller) + if (fileformat == 'c2log' or fileformat == 'rowprolog'): # pragma: no cover + return (0, '', 0, 0, '', impeller) - if not hasrecognized: # pragma: no cover - return (0,'',0,0,'',impeller) + if not hasrecognized: # pragma: no cover + return (0, '', 0, 0, '', impeller) f_to_be_deleted = f2 # should delete file f2 = f2[:-4] + 'o.csv' - row2 = rrdata(df = row.df) - - + row2 = rrdata(df=row.df) if 'speedcoach2' in fileformat or 'nklinklogbook' in fileformat: # impeller consistency @@ -2066,12 +2029,10 @@ def handle_nonpainsled(f2, fileformat, summary='',startdatetime='',empowerfirmwa row2.write_csv(f2, gzip=True) - - # os.remove(f2) try: os.remove(f_to_be_deleted) - except: # pragma: no cover + except: # pragma: no cover try: os.remove(f_to_be_deleted + '.gz') except: @@ -2082,34 +2043,34 @@ def handle_nonpainsled(f2, fileformat, summary='',startdatetime='',empowerfirmwa # Create new workout from file and store it in the database # This routine should be used everywhere in views.py -def get_workouttype_from_fit(filename,workouttype='water'): + +def get_workouttype_from_fit(filename, workouttype='water'): try: - fitfile = FitFile(filename,check_crc=False) - except FitHeaderError: # pragma: no cover + fitfile = FitFile(filename, check_crc=False) + except FitHeaderError: # pragma: no cover return workouttype records = fitfile.messages fittype = 'rowing' for record in records: - if record.name in ['sport','lap']: + if record.name in ['sport', 'lap']: try: fittype = record.get_values()['sport'].lower() - except (KeyError,AttributeError): # pragma: no cover + except (KeyError, AttributeError): # pragma: no cover return 'water' try: workouttype = mytypes.fitmappinginv[fittype] - except KeyError: # pragma: no cover + except KeyError: # pragma: no cover return workouttype return workouttype -import rowingdata.tcxtools as tcxtools -def get_workouttype_from_tcx(filename,workouttype='water'): +def get_workouttype_from_tcx(filename, workouttype='water'): tcxtype = 'rowing' if workouttype in mytypes.otwtypes: return workouttype - try: # pragma: no cover + try: # pragma: no cover d = tcxtools.tcx_getdict(filename) try: tcxtype = d['Activities']['Activity']['@Sport'].lower() @@ -2118,15 +2079,16 @@ def get_workouttype_from_tcx(filename,workouttype='water'): except KeyError: return workouttype - except TypeError: # pragma: no cover + except TypeError: # pragma: no cover pass - try: # pragma: no cover + try: # pragma: no cover workouttype = mytypes.garminmappinginv[tcxtype.upper()] - except KeyError: # pragma: no cover + except KeyError: # pragma: no cover return workouttype - return workouttype # pragma: no cover + return workouttype # pragma: no cover + def new_workout_from_file(r, f2, workouttype='rower', @@ -2141,12 +2103,12 @@ def new_workout_from_file(r, f2, inboard=None, oarlength=None, impeller=False, - uploadoptions={'boattype':'1x','workouttype':'rower'}): + uploadoptions={'boattype': '1x', 'workouttype': 'rower'}): message = "" try: fileformat = get_file_type(f2) - except (IOError,UnicodeDecodeError): # pragma: no cover + except (IOError, UnicodeDecodeError): # pragma: no cover os.remove(f2) message = "Rowsandall could not process this file. The extension is supported but the file seems corrupt. Contact info@rowsandall.com if you think this is incorrect." return (0, message, f2) @@ -2156,15 +2118,15 @@ def new_workout_from_file(r, f2, inboard = 0.88 # Save zip files to email box for further processing - if len(fileformat) == 3 and fileformat[0] == 'zip': # pragma: no cover + if len(fileformat) == 3 and fileformat[0] == 'zip': # pragma: no cover uploadoptions['secret'] = settings.UPLOAD_SERVICE_SECRET uploadoptions['user'] = r.user.id uploadoptions['title'] = title try: zip_file = zipfile.ZipFile(f2) - for id,filename in enumerate(zip_file.namelist()): + for id, filename in enumerate(zip_file.namelist()): datafile = zip_file.extract(filename, path='media/') - if id>0: + if id > 0: uploadoptions['title'] = title+' ('+str(id+1)+')' else: uploadoptions['title'] = title @@ -2173,24 +2135,23 @@ def new_workout_from_file(r, f2, url = settings.UPLOAD_SERVICE_URL job = myqueue(queuehigh, - handle_request_post, - url, - uploadoptions - ) + handle_request_post, + url, + uploadoptions + ) - except BadZipFile: # pragma: no cover + except BadZipFile: # pragma: no cover pass - return -1, message, f2 # Some people try to upload Concept2 logbook summaries - if fileformat == 'imageformat': # pragma: no cover + if fileformat == 'imageformat': # pragma: no cover os.remove(f2) message = "You cannot upload image files here" return (0, message, f2) - if fileformat == 'json': # pragma: no cover + if fileformat == 'json': # pragma: no cover os.remove(f2) message = "JSON format not supported in direct upload" return (0, message, f2) @@ -2200,24 +2161,24 @@ def new_workout_from_file(r, f2, message = "This summary does not contain stroke data. Use the files containing stroke by stroke data." return (0, message, f2) - if fileformat == 'nostrokes': # pragma: no cover + if fileformat == 'nostrokes': # pragma: no cover os.remove(f2) message = "It looks like this file doesn't contain stroke data." return (0, message, f2) - if fileformat == 'kml': # pragma: no cover + if fileformat == 'kml': # pragma: no cover os.remove(f2) message = "KML files are not supported" return (0, message, f2) # Some people upload corrupted zip files - if fileformat == 'notgzip': # pragma: no cover + if fileformat == 'notgzip': # pragma: no cover os.remove(f2) message = "Rowsandall could not process this file. The extension is supported but the file seems corrupt. Contact info@rowsandall.com if you think this is incorrect." return (0, message, f2) # Some people try to upload RowPro summary logs - if fileformat == 'rowprolog': # pragma: no cover + if fileformat == 'rowprolog': # pragma: no cover os.remove(f2) message = "This RowPro logbook summary does not contain stroke data. Please use the Stroke Data CSV file for the individual workout in your log." return (0, message, f2) @@ -2226,13 +2187,13 @@ def new_workout_from_file(r, f2, # Send an email to info@rowsandall.com with the file attached # for me to check if it is a bug, or a new file type # worth supporting - if fileformat == 'gpx': # pragma: no cover + if fileformat == 'gpx': # pragma: no cover os.remove(f2) message = "GPX files support is on our roadmap. Check back soon." return (0, message, f2) - if fileformat == 'unknown': # pragma: no cover + if fileformat == 'unknown': # pragma: no cover message = "We couldn't recognize the file type" extension = os.path.splitext(f2)[1] filename = os.path.splitext(f2)[0] @@ -2241,7 +2202,7 @@ def new_workout_from_file(r, f2, extension2 = os.path.splitext(filename)[1]+extension extension = extension2 f4 = filename+'a'+extension - copyfile(f2,f4) + copyfile(f2, f4) job = myqueue(queuehigh, handle_sendemail_unrecognized, f4, @@ -2249,14 +2210,14 @@ def new_workout_from_file(r, f2, return (0, message, f2) - if fileformat == 'att': # pragma: no cover + if fileformat == 'att': # pragma: no cover # email attachment which can safely be ignored return (0, '', f2) # Get workout type from fit & tcx - if (fileformat == 'fit'): # pragma: no cover - workouttype = get_workouttype_from_fit(f2,workouttype=workouttype) - #if (fileformat == 'tcx'): + if (fileformat == 'fit'): # pragma: no cover + workouttype = get_workouttype_from_fit(f2, workouttype=workouttype) + # if (fileformat == 'tcx'): # workouttype_from_tcx = get_workouttype_from_tcx(f2,workouttype=workouttype) # if workouttype != 'rower' and workouttype_from_tcx not in mytypes.otwtypes: # workouttype = workouttype_from_tcx @@ -2271,14 +2232,10 @@ def new_workout_from_file(r, f2, empowerfirmware=oarlockfirmware, impeller=impeller, ) - if not f2: # pragma: no cover + if not f2: # pragma: no cover message = 'Something went wrong' return (0, message, '') - - - - dosummary = (fileformat != 'fit' and 'speedcoach2' not in fileformat) dosummary = dosummary or summary == '' @@ -2288,7 +2245,7 @@ def new_workout_from_file(r, f2, if workoutsource is None: workoutsource = fileformat - dologging('debuglog.log','Saving to database with start date time {startdatetime}'.format( + dologging('debuglog.log', 'Saving to database with start date time {startdatetime}'.format( startdatetime=startdatetime, )) @@ -2356,7 +2313,7 @@ def split_workout(r, parent, splitsecond, splitmode): ids = [] if 'keep first' in splitmode: - if 'firstprivate' in splitmode: # pragma: no cover + if 'firstprivate' in splitmode: # pragma: no cover setprivate = True else: setprivate = False @@ -2376,12 +2333,12 @@ def split_workout(r, parent, splitsecond, splitmode): data2['distance'] = data2['distance'] - data2.iloc[ 0, data2.columns.get_loc('distance') - ] + ] data2['time'] = data2['time'] - data2.iloc[ 0, data2.columns.get_loc('time') - ] - if 'secondprivate' in splitmode: # pragma: no cover + ] + if 'secondprivate' in splitmode: # pragma: no cover setprivate = True else: setprivate = False @@ -2396,14 +2353,14 @@ def split_workout(r, parent, splitsecond, splitmode): messages.append(message) ids.append(encoder.encode_hex(id)) - if not 'keep original' in splitmode: # pragma: no cover + if not 'keep original' in splitmode: # pragma: no cover if 'keep second' in splitmode or 'keep first' in splitmode: parent.delete() messages.append('Deleted Workout: ' + parent.name) else: messages.append('That would delete your workout') ids.append(encoder.encode_hex(parent.id)) - elif 'originalprivate' in splitmode: # pragma: no cover + elif 'originalprivate' in splitmode: # pragma: no cover parent.privacy = 'hidden' parent.save() @@ -2438,7 +2395,7 @@ def new_workout_from_df(r, df, notes = parent.notes summary = parent.summary rpe = parent.rpe - if parent.privacy == 'hidden': # pragma: no cover + if parent.privacy == 'hidden': # pragma: no cover makeprivate = True else: makeprivate = False @@ -2451,10 +2408,10 @@ def new_workout_from_df(r, df, summary = '' makeprivate = False rpe = 0 - if startdatetime == '': # pragma: no cover + if startdatetime == '': # pragma: no cover startdatetime = timezone.now() - if setprivate: # pragma: no cover + if setprivate: # pragma: no cover makeprivate = True timestr = strftime("%Y%m%d-%H%M%S") @@ -2475,7 +2432,6 @@ def new_workout_from_df(r, df, row = rrdata(df=df) - row.write_csv(csvfilename, gzip=True) # res = df.to_csv(csvfilename+'.gz',index_label='index', @@ -2494,32 +2450,30 @@ def new_workout_from_df(r, df, rpe=rpe, consistencychecks=False) - job = myqueue(queuehigh,handle_calctrimp,id,csvfilename,r.ftp,r.sex,r.hrftp,r.max,r.rest) + job = myqueue(queuehigh, handle_calctrimp, id, csvfilename, + r.ftp, r.sex, r.hrftp, r.max, r.rest) return (id, message) - - # A wrapper around the rowingdata class, with some error catching def rdata(file, rower=rrower()): try: res = rrdata(csvfile=file, rower=rower) - except (IOError, IndexError): # pragma: no cover + except (IOError, IndexError): # pragma: no cover try: res = rrdata(csvfile=file + '.gz', rower=rower) except (IOError, IndexError): res = rrdata() except: res = rrdata() - except EOFError: # pragma: no cover + except EOFError: # pragma: no cover res = rrdata() - except: # pragma: no cover + except: # pragma: no cover res = rrdata() - return res # Remove all stroke data for workout ID from database @@ -2534,7 +2488,7 @@ def delete_strokedata(id): os.remove(dirname) except FileNotFoundError: pass - except FileNotFoundError: # pragma: no cover + except FileNotFoundError: # pragma: no cover pass # Replace stroke data in DB with data from CSV file @@ -2547,7 +2501,7 @@ def update_strokedata(id, df): # Test that all data are of a numerical time -def testdata(time, distance, pace, spm): # pragma: no cover +def testdata(time, distance, pace, spm): # pragma: no cover t1 = np.issubdtype(time, np.number) t2 = np.issubdtype(distance, np.number) t3 = np.issubdtype(pace, np.number) @@ -2564,7 +2518,7 @@ def getrowdata_db(id=0, doclean=False, convertnewtons=True, data = read_df_sql(id) try: data['deltat'] = data['time'].diff() - except KeyError: # pragma: no cover + except KeyError: # pragma: no cover data = pd.DataFrame() if data.empty: @@ -2577,29 +2531,27 @@ def getrowdata_db(id=0, doclean=False, convertnewtons=True, else: row = Workout.objects.get(id=id) - - - if checkefficiency==True and not data.empty: + if checkefficiency == True and not data.empty: try: - if data['efficiency'].mean() == 0 and data['power'].mean() != 0: # pragma: no cover + if data['efficiency'].mean() == 0 and data['power'].mean() != 0: # pragma: no cover data = add_efficiency(id=id) - except KeyError: # pragma: no cover + except KeyError: # pragma: no cover data = add_efficiency(id=id) - if doclean: # pragma: no cover + if doclean: # pragma: no cover data = clean_df_stats(data, ignorehr=True) - return data, row # Fetch a subset of the data from the DB -def getsmallrowdata_db(columns, ids=[], doclean=True,workstrokesonly=True,compute=True): + +def getsmallrowdata_db(columns, ids=[], doclean=True, workstrokesonly=True, compute=True): # prepmultipledata(ids) - if ids: - csvfilenames = ['media/strokedata_{id}.parquet.gz'.format(id=id) for id in ids] + csvfilenames = [ + 'media/strokedata_{id}.parquet.gz'.format(id=id) for id in ids] else: return pd.DataFrame() @@ -2607,36 +2559,37 @@ def getsmallrowdata_db(columns, ids=[], doclean=True,workstrokesonly=True,comput columns = [c for c in columns if c != 'None'] columns = list(set(columns)) - - if len(ids)>1: - for id,f in zip(ids,csvfilenames): + if len(ids) > 1: + for id, f in zip(ids, csvfilenames): try: #df = dd.read_parquet(f,columns=columns,engine='pyarrow') - df = pd.read_parquet(f,columns=columns) + df = pd.read_parquet(f, columns=columns) data.append(df) - except (OSError,ArrowInvalid,IndexError): # pragma: no cover + except (OSError, ArrowInvalid, IndexError): # pragma: no cover rowdata, row = getrowdata(id=id) if rowdata and len(rowdata.df): - datadf = dataprep(rowdata.df,id=id,bands=True,otwpower=True,barchart=True) + datadf = dataprep(rowdata.df, id=id, + bands=True, otwpower=True, barchart=True) # df = dd.read_parquet(f,columns=columns,engine='pyarrow') - df = pd.read_parquet(f,columns=columns) + df = pd.read_parquet(f, columns=columns) data.append(df) try: - df = pd.concat(data,axis=0) - except ValueError: # pragma: no cover + df = pd.concat(data, axis=0) + except ValueError: # pragma: no cover return pd.DataFrame() # df = dd.concat(data,axis=0) else: try: - df = pd.read_parquet(csvfilenames[0],columns=columns) + df = pd.read_parquet(csvfilenames[0], columns=columns) rowdata, row = getrowdata(id=ids[0]) - except (OSError,ArrowInvalid,IndexError): - rowdata,row = getrowdata(id=ids[0]) - if rowdata and len(rowdata.df): # pragma: no cover - data = dataprep(rowdata.df,id=ids[0],bands=True,otwpower=True,barchart=True) - df = pd.read_parquet(csvfilenames[0],columns=columns) + except (OSError, ArrowInvalid, IndexError): + rowdata, row = getrowdata(id=ids[0]) + if rowdata and len(rowdata.df): # pragma: no cover + data = dataprep( + rowdata.df, id=ids[0], bands=True, otwpower=True, barchart=True) + df = pd.read_parquet(csvfilenames[0], columns=columns) # df = dd.read_parquet(csvfilenames[0], # column=columns,engine='pyarrow', # ) @@ -2645,14 +2598,13 @@ def getsmallrowdata_db(columns, ids=[], doclean=True,workstrokesonly=True,comput else: df = pd.DataFrame() - if compute and len(df): data = df.copy() if doclean: data = clean_df_stats(data, ignorehr=True, - workstrokesonly=workstrokesonly) - data.dropna(axis=1,how='all',inplace=True) - data.dropna(axis=0,how='any',inplace=True) + workstrokesonly=workstrokesonly) + data.dropna(axis=1, how='all', inplace=True) + data.dropna(axis=0, how='any', inplace=True) return data return df @@ -2665,8 +2617,8 @@ def getrowdata(id=0): # check if valid ID exists (workout exists) try: row = Workout.objects.get(id=id) - except Workout.DoesNotExist: # pragma: no cover - return rrdata(),None + except Workout.DoesNotExist: # pragma: no cover + return rrdata(), None f1 = row.csvfilename @@ -2690,11 +2642,11 @@ def getrowdata(id=0): # safety net for programming errors elsewhere in the app # Also used heavily when I moved from CSV file only to CSV+Stroke data -import glob -def prepmultipledata(ids, verbose=False): # pragma: no cover +def prepmultipledata(ids, verbose=False): # pragma: no cover filenames = glob.glob('media/*.parquet') - ids = [id for id in ids if 'media/strokedata_{id}.parquet.gz'.format(id=id) not in filenames] + ids = [ + id for id in ids if 'media/strokedata_{id}.parquet.gz'.format(id=id) not in filenames] for id in ids: rowdata, row = getrowdata(id=id) @@ -2722,38 +2674,39 @@ def read_cols_df_sql(ids, columns, convertnewtons=True): df = pd.DataFrame() - - if len(ids) == 0: # pragma: no cover - return pd.DataFrame(),extracols - elif len(ids) == 1: # pragma: no cover + if len(ids) == 0: # pragma: no cover + return pd.DataFrame(), extracols + elif len(ids) == 1: # pragma: no cover try: filename = 'media/strokedata_{id}.parquet.gz'.format(id=ids[0]) - df = pd.read_parquet(filename,columns=columns) + df = pd.read_parquet(filename, columns=columns) except OSError: - rowdata,row = getrowdata(id=ids[0]) + rowdata, row = getrowdata(id=ids[0]) if rowdata and len(rowdata.df): - datadf = dataprep(rowdata.df,id=ids[0],bands=True,otwpower=True,barchart=True) - df = pd.read_parquet(filename,columns=columns) + datadf = dataprep( + rowdata.df, id=ids[0], bands=True, otwpower=True, barchart=True) + df = pd.read_parquet(filename, columns=columns) else: data = [] - filenames = ['media/strokedata_{id}.parquet.gz'.format(id=id) for id in ids] - for id,f in zip(ids,filenames): + filenames = [ + 'media/strokedata_{id}.parquet.gz'.format(id=id) for id in ids] + for id, f in zip(ids, filenames): try: - df = pd.read_parquet(f,columns=columns) + df = pd.read_parquet(f, columns=columns) data.append(df) - except (OSError,IndexError,ArrowInvalid): - rowdata,row = getrowdata(id=id) - if rowdata and len(rowdata.df): # pragma: no cover - datadf = dataprep(rowdata.df,id=id,bands=True,otwpower=True,barchart=True) - df = pd.read_parquet(f,columns=columns) + except (OSError, IndexError, ArrowInvalid): + rowdata, row = getrowdata(id=id) + if rowdata and len(rowdata.df): # pragma: no cover + datadf = dataprep(rowdata.df, id=id, + bands=True, otwpower=True, barchart=True) + df = pd.read_parquet(f, columns=columns) data.append(df) try: - df = pd.concat(data,axis=0) - except ValueError: # pragma: no cover + df = pd.concat(data, axis=0) + except ValueError: # pragma: no cover return pd.DataFrame(), extracols - df = df.fillna(value=0) if 'peakforce' in columns: @@ -2772,22 +2725,25 @@ def read_cols_df_sql(ids, columns, convertnewtons=True): df.loc[mask, 'averageforce'] = df.loc[mask, 'averageforce'] * lbstoN - return df,extracols + return df, extracols def initiate_cp(r): - success = update_rolling_cp(r,otwtypes,'water') - success = update_rolling_cp(r,otetypes,'erg') + success = update_rolling_cp(r, otwtypes, 'water') + success = update_rolling_cp(r, otetypes, 'erg') # Read stroke data from the DB for a Workout ID. Returns a pandas dataframe + + def read_df_sql(id): try: f = 'media/strokedata_{id}.parquet.gz'.format(id=id) df = pd.read_parquet(f) - except (OSError,ArrowInvalid,IndexError): # pragma: no cover - rowdata,row = getrowdata(id=id) + except (OSError, ArrowInvalid, IndexError): # pragma: no cover + rowdata, row = getrowdata(id=id) if rowdata and len(rowdata.df): - data = dataprep(rowdata.df,id=id,bands=True,otwpower=True,barchart=True) + data = dataprep(rowdata.df, id=id, bands=True, + otwpower=True, barchart=True) try: df = pd.read_parquet(f) except OSError: @@ -2800,7 +2756,6 @@ def read_df_sql(id): return df - # data fusion @@ -2863,7 +2818,7 @@ def datafusion(id1, id2, columns, offset): return df, forceunit -def fix_newtons(id=0, limit=3000): # pragma: no cover +def fix_newtons(id=0, limit=3000): # pragma: no cover # rowdata,row = getrowdata_db(id=id,doclean=False,convertnewtons=False) rowdata = getsmallrowdata_db(['peakforce'], ids=[id], doclean=False) try: @@ -2878,14 +2833,16 @@ def fix_newtons(id=0, limit=3000): # pragma: no cover except KeyError: pass -def remove_invalid_columns(df): # pragma: no cover + +def remove_invalid_columns(df): # pragma: no cover for c in df.columns: if not c in allowedcolumns: - df.drop(labels=c,axis=1,inplace=True) + df.drop(labels=c, axis=1, inplace=True) return df -def add_efficiency(id=0): # pragma: no cover + +def add_efficiency(id=0): # pragma: no cover rowdata, row = getrowdata_db(id=id, doclean=False, convertnewtons=False, @@ -2909,8 +2866,8 @@ def add_efficiency(id=0): # pragma: no cover if id != 0: rowdata['workoutid'] = id filename = 'media/strokedata_{id}.parquet.gz'.format(id=id) - df = dd.from_pandas(rowdata,npartitions=1) - df.to_parquet(filename,engine='fastparquet',compression='GZIP') + df = dd.from_pandas(rowdata, npartitions=1) + df.to_parquet(filename, engine='fastparquet', compression='GZIP') return rowdata @@ -2935,8 +2892,8 @@ def dataprep(rowdatadf, id=0, bands=True, barchart=True, otwpower=True, p = rowdatadf.loc[:, ' Stroke500mPace (sec/500m)'] try: - velo = rowdatadf.loc[:,' AverageBoatSpeed (m/s)'] - except KeyError: # pragma: no cover + velo = rowdatadf.loc[:, ' AverageBoatSpeed (m/s)'] + except KeyError: # pragma: no cover velo = 500./p hr = rowdatadf.loc[:, ' HRCur (bpm)'] @@ -2947,7 +2904,7 @@ def dataprep(rowdatadf, id=0, bands=True, barchart=True, otwpower=True, drivelength = rowdatadf.loc[:, ' DriveLength (meters)'] try: workoutstate = rowdatadf.loc[:, ' WorkoutState'] - except KeyError: # pragma: no cover + except KeyError: # pragma: no cover workoutstate = 0 * hr peakforce = rowdatadf.loc[:, ' PeakDriveForce (lbs)'] @@ -2960,14 +2917,14 @@ def dataprep(rowdatadf, id=0, bands=True, barchart=True, otwpower=True, recoverytime = rowdatadf.loc[:, ' StrokeRecoveryTime (ms)'] rhythm = 100. * drivetime / (recoverytime + drivetime) rhythm = rhythm.fillna(value=0) - except: # pragma: no cover + except: # pragma: no cover rhythm = 0.0 * forceratio f = rowdatadf['TimeStamp (sec)'].diff().mean() if f != 0 and not np.isinf(f): try: windowsize = 2 * (int(10. / (f))) + 1 - except ValueError: # pragma: no cover + except ValueError: # pragma: no cover windowsize = 1 else: windowsize = 1 @@ -2982,21 +2939,21 @@ def dataprep(rowdatadf, id=0, bands=True, barchart=True, otwpower=True, try: t2 = t.fillna(method='ffill').apply(lambda x: timedeltaconv(x)) - except TypeError: # pragma: no cover + except TypeError: # pragma: no cover t2 = 0 * t p2 = p.fillna(method='ffill').apply(lambda x: timedeltaconv(x)) try: drivespeed = drivelength / rowdatadf[' DriveTime (ms)'] * 1.0e3 - except TypeError: # pragma: no cover + except TypeError: # pragma: no cover drivespeed = 0.0 * rowdatadf['TimeStamp (sec)'] drivespeed = drivespeed.fillna(value=0) try: driveenergy = rowdatadf['driveenergy'] - except KeyError: # pragma: no cover + except KeyError: # pragma: no cover if forceunit == 'lbs': driveenergy = drivelength * averageforce * lbstoN else: @@ -3017,7 +2974,6 @@ def dataprep(rowdatadf, id=0, bands=True, barchart=True, otwpower=True, distanceperstroke = 60. * velo / spm - data = DataFrame( dict( time=t * 1e3, @@ -3039,11 +2995,10 @@ def dataprep(rowdatadf, id=0, bands=True, barchart=True, otwpower=True, drivespeed=drivespeed, rhythm=rhythm, distanceperstroke=distanceperstroke, -# powerhr=powerhr, + # powerhr=powerhr, ) ) - if bands: # HR bands data['hr_ut2'] = rowdatadf.loc[:, 'hr_ut2'] @@ -3056,7 +3011,7 @@ def dataprep(rowdatadf, id=0, bands=True, barchart=True, otwpower=True, try: tel = rowdatadf.loc[:, ' ElapsedTime (sec)'] - except KeyError: # pragma: no cover + except KeyError: # pragma: no cover rowdatadf[' ElapsedTime (sec)'] = rowdatadf['TimeStamp (sec)'] if empower: @@ -3080,7 +3035,7 @@ def dataprep(rowdatadf, id=0, bands=True, barchart=True, otwpower=True, except KeyError: peakforceangle = 0 * power - if data['driveenergy'].mean() == 0: # pragma: no cover + if data['driveenergy'].mean() == 0: # pragma: no cover try: driveenergy = rowdatadf.loc[:, 'driveenergy'] except KeyError: @@ -3102,46 +3057,46 @@ def dataprep(rowdatadf, id=0, bands=True, barchart=True, otwpower=True, try: totalangle = finish - catch effectiveangle = finish - wash - catch - slip - except ValueError: # pragma: no cover + except ValueError: # pragma: no cover totalangle = 0 * power effectiveangle = 0 * power if windowsize > 3 and windowsize < len(slip): try: wash = savgol_filter(wash, windowsize, 3) - except TypeError: # pragma: no cover + except TypeError: # pragma: no cover pass try: slip = savgol_filter(slip, windowsize, 3) - except TypeError: # pragma: no cover + except TypeError: # pragma: no cover pass try: catch = savgol_filter(catch, windowsize, 3) - except TypeError: # pragma: no cover + except TypeError: # pragma: no cover pass try: finish = savgol_filter(finish, windowsize, 3) - except TypeError: # pragma: no cover + except TypeError: # pragma: no cover pass try: peakforceangle = savgol_filter(peakforceangle, windowsize, 3) - except TypeError: # pragma: no cover + except TypeError: # pragma: no cover pass try: driveenergy = savgol_filter(driveenergy, windowsize, 3) - except TypeError: # pragma: no cover + except TypeError: # pragma: no cover pass try: drivelength = savgol_filter(drivelength, windowsize, 3) - except TypeError: # pragma: no cover + except TypeError: # pragma: no cover pass try: totalangle = savgol_filter(totalangle, windowsize, 3) - except TypeError: # pragma: no cover + except TypeError: # pragma: no cover pass try: effectiveangle = savgol_filter(effectiveangle, windowsize, 3) - except TypeError: # pragma: no cover + except TypeError: # pragma: no cover pass velo = 500. / p @@ -3163,7 +3118,7 @@ def dataprep(rowdatadf, id=0, bands=True, barchart=True, otwpower=True, data['totalangle'] = totalangle data['effectiveangle'] = effectiveangle data['efficiency'] = efficiency - except ValueError: # pragma: no cover + except ValueError: # pragma: no cover pass if otwpower: @@ -3195,27 +3150,25 @@ def dataprep(rowdatadf, id=0, bands=True, barchart=True, otwpower=True, # write data if id given if id != 0: data['workoutid'] = id - data.fillna(0,inplace=True) + data.fillna(0, inplace=True) for k, v in dtypes.items(): try: data[k] = data[k].astype(v) - except KeyError: # pragma: no cover + except KeyError: # pragma: no cover pass - filename = 'media/strokedata_{id}.parquet.gz'.format(id=id) - df = dd.from_pandas(data,npartitions=1) - df.to_parquet(filename,engine='fastparquet',compression='GZIP') + df = dd.from_pandas(data, npartitions=1) + df.to_parquet(filename, engine='fastparquet', compression='GZIP') return data - -def workout_trimp(w,reset=False): +def workout_trimp(w, reset=False): r = w.user if w.trimp > 0 and not reset: - return w.trimp,w.hrtss + return w.trimp, w.hrtss r = w.user ftp = float(r.ftp) @@ -3227,14 +3180,13 @@ def workout_trimp(w,reset=False): r.hrftp = int(hrftp) r.save() - if w.averagehr is None: rowdata = rdata(w.csvfilename) try: avghr = rowdata.df[' HRCur (bpm)'].mean() maxhr = rowdata.df[' HRCur (bpm)'].max() - except KeyError: # pragma: no cover + except KeyError: # pragma: no cover avghr = None maxhr = None @@ -3253,11 +3205,12 @@ def workout_trimp(w,reset=False): r.max, r.rest) - return 0,0 + return 0, 0 -def workout_rscore(w,reset=False): + +def workout_rscore(w, reset=False): if w.rscore > 0 and not reset: - return w.rscore,w.normp + return w.rscore, w.normp r = w.user ftp = float(r.ftp) @@ -3269,8 +3222,6 @@ def workout_rscore(w,reset=False): r.hrftp = int(hrftp) r.save() - - job = myqueue( queuehigh, handle_calctrimp, @@ -3282,24 +3233,23 @@ def workout_rscore(w,reset=False): r.max, r.rest) - return 0,0 + return 0, 0 -def workout_normv(w,pp=4.0): - if w.normv > 0: # pragma: no cover - return w.normv,w.normw + +def workout_normv(w, pp=4.0): + if w.normv > 0: # pragma: no cover + return w.normv, w.normw r = w.user ftp = float(r.ftp) if w.workouttype in otwtypes: ftp = ftp*(100.-r.otwslack)/100. - if r.hrftp == 0: # pragma: no cover + if r.hrftp == 0: # pragma: no cover hrftp = (r.an+r.tr)/2. r.hrftp = int(hrftp) r.save() - - job = myqueue( queuehigh, handle_calctrimp, @@ -3311,4 +3261,4 @@ def workout_normv(w,pp=4.0): r.max, r.rest) - return 0,0 + return 0, 0 diff --git a/rowers/dataprepnodjango.py b/rowers/dataprepnodjango.py index 45710bf5..fb89fd0e 100644 --- a/rowers/dataprepnodjango.py +++ b/rowers/dataprepnodjango.py @@ -1,4 +1,8 @@ from __future__ import absolute_import +from rowers.utils import totaltime_sec_to_string +from rowers.metrics import dtypes +import datetime +from scipy.signal import savgol_filter from __future__ import division from __future__ import print_function from __future__ import unicode_literals @@ -9,10 +13,10 @@ from rowingdata import rowingdata as rrdata from rowingdata import make_cumvalues from rowingdata import rower as rrower from rowingdata import main as rmain -from rowingdata import empower_bug_correction,get_empower_rigging, get_file_type +from rowingdata import empower_bug_correction, get_empower_rigging, get_file_type from rowingdata.csvparsers import make_cumvalues_array from time import strftime -from pandas import DataFrame,Series +from pandas import DataFrame, Series import shutil from shutil import copyfile @@ -38,24 +42,24 @@ from timezonefinder import TimezoneFinder try: user = DATABASES['default']['USER'] -except KeyError: # pragma: no cover +except KeyError: # pragma: no cover user = '' try: password = DATABASES['default']['PASSWORD'] -except KeyError: # pragma: no cover +except KeyError: # pragma: no cover password = '' try: database_name = DATABASES['default']['NAME'] -except KeyError: # pragma: no cover +except KeyError: # pragma: no cover database_name = '' try: host = DATABASES['default']['HOST'] -except KeyError: # pragma: no cover +except KeyError: # pragma: no cover host = '' try: port = DATABASES['default']['PORT'] -except KeyError: # pragma: no cover +except KeyError: # pragma: no cover port = '' database_url = 'mysql://{user}:{password}@{host}:{port}/{database_name}'.format( @@ -64,7 +68,7 @@ database_url = 'mysql://{user}:{password}@{host}:{port}/{database_name}'.format( database_name=database_name, host=host, port=port, - ) +) database_name_dev = DEV_DATABASES['default']['NAME'] @@ -75,31 +79,27 @@ if use_sqlite: database_url = database_url_debug - # mapping the DB column names to the CSV file column names columndict = { - 'time':'TimeStamp (sec)', - 'hr':' HRCur (bpm)', + 'time': 'TimeStamp (sec)', + 'hr': ' HRCur (bpm)', 'velo': ' AverageBoatSpeed (m/s)', - 'pace':' Stroke500mPace (sec/500m)', - 'spm':' Cadence (stokes/min)', - 'power':' Power (watts)', - 'averageforce':' AverageDriveForce (lbs)', - 'drivelength':' DriveLength (meters)', - 'peakforce':' PeakDriveForce (lbs)', - 'distance':' Horizontal (meters)', - 'catch':'catch', - 'finish':'finish', - 'peakforceangle':'peakforceangle', - 'wash':'wash', - 'slip':'wash', - 'workoutstate':' WorkoutState', - 'cumdist':'cum_dist', - } + 'pace': ' Stroke500mPace (sec/500m)', + 'spm': ' Cadence (stokes/min)', + 'power': ' Power (watts)', + 'averageforce': ' AverageDriveForce (lbs)', + 'drivelength': ' DriveLength (meters)', + 'peakforce': ' PeakDriveForce (lbs)', + 'distance': ' Horizontal (meters)', + 'catch': 'catch', + 'finish': 'finish', + 'peakforceangle': 'peakforceangle', + 'wash': 'wash', + 'slip': 'wash', + 'workoutstate': ' WorkoutState', + 'cumdist': 'cum_dist', +} -from scipy.signal import savgol_filter - -import datetime def niceformat(values): out = [] @@ -109,63 +109,61 @@ def niceformat(values): return out + def strfdelta(tdelta): try: - minutes,seconds = divmod(tdelta.seconds,60) + minutes, seconds = divmod(tdelta.seconds, 60) tenths = int(tdelta.microseconds/1e5) - except AttributeError: # pragma: no cover - minutes,seconds = divmod(tdelta.view(np.int64),60e9) - seconds,rest = divmod(seconds,1e9) + except AttributeError: # pragma: no cover + minutes, seconds = divmod(tdelta.view(np.int64), 60e9) + seconds, rest = divmod(seconds, 1e9) tenths = int(rest/1e8) res = "{minutes:0>2}:{seconds:0>2}.{tenths:0>1}".format( minutes=minutes, seconds=seconds, tenths=tenths, - ) + ) return res + def nicepaceformat(values): out = [] for v in values: formattedv = strfdelta(v) out.append(formattedv) - return out + def timedeltaconv(x): if not np.isnan(x): dt = datetime.timedelta(seconds=x) - else: # pragma: no cover + else: # pragma: no cover dt = datetime.timedelta(seconds=350.) - return dt -def rdata(file,rower=rrower()): # pragma: no cover + +def rdata(file, rower=rrower()): # pragma: no cover try: - res = rrdata(csvfile=file,rower=rower) + res = rrdata(csvfile=file, rower=rower) except IOError: try: - res = rrdata(csvfile=file+'.gz',rower=rower) + res = rrdata(csvfile=file+'.gz', rower=rower) except IOError: res = 0 return res -from rowers.utils import totaltime_sec_to_string -from rowers.metrics import dtypes - # Creates C2 stroke data def create_c2_stroke_data_db( - distance,duration,workouttype, - workoutid,starttimeunix,csvfilename,debug=False): # pragma: no cover + distance, duration, workouttype, + workoutid, starttimeunix, csvfilename, debug=False): # pragma: no cover nr_strokes = int(distance/10.) - totalseconds = duration.hour*3600. totalseconds += duration.minute*60. totalseconds += duration.second @@ -191,7 +189,7 @@ def create_c2_stroke_data_db( pace = 500.*totalseconds/distance - if workouttype in ['rower','slides','dynamic']: + if workouttype in ['rower', 'slides', 'dynamic']: try: velo = distance/totalseconds except ZeroDivisionError: @@ -200,25 +198,24 @@ def create_c2_stroke_data_db( else: power = 0 - df = pd.DataFrame({ 'TimeStamp (sec)': unixtime, ' Horizontal (meters)': d, ' Cadence (stokes/min)': spm, - ' Stroke500mPace (sec/500m)':pace, - ' ElapsedTime (sec)':elapsed, - ' Power (watts)':power, - ' HRCur (bpm)':np.zeros(nr_strokes), - ' longitude':np.zeros(nr_strokes), - ' latitude':np.zeros(nr_strokes), - ' DragFactor':np.zeros(nr_strokes), - ' DriveLength (meters)':np.zeros(nr_strokes), - ' StrokeDistance (meters)':np.zeros(nr_strokes), - ' DriveTime (ms)':np.zeros(nr_strokes), - ' StrokeRecoveryTime (ms)':np.zeros(nr_strokes), - ' AverageDriveForce (lbs)':np.zeros(nr_strokes), - ' PeakDriveForce (lbs)':np.zeros(nr_strokes), - ' lapIdx':np.zeros(nr_strokes), + ' Stroke500mPace (sec/500m)': pace, + ' ElapsedTime (sec)': elapsed, + ' Power (watts)': power, + ' HRCur (bpm)': np.zeros(nr_strokes), + ' longitude': np.zeros(nr_strokes), + ' latitude': np.zeros(nr_strokes), + ' DragFactor': np.zeros(nr_strokes), + ' DriveLength (meters)': np.zeros(nr_strokes), + ' StrokeDistance (meters)': np.zeros(nr_strokes), + ' DriveTime (ms)': np.zeros(nr_strokes), + ' StrokeRecoveryTime (ms)': np.zeros(nr_strokes), + ' AverageDriveForce (lbs)': np.zeros(nr_strokes), + ' PeakDriveForce (lbs)': np.zeros(nr_strokes), + ' lapIdx': np.zeros(nr_strokes), 'cum_dist': d }) @@ -226,16 +223,18 @@ def create_c2_stroke_data_db( df[' ElapsedTime (sec)'] = df['TimeStamp (sec)'] - res = df.to_csv(csvfilename,index_label='index', - compression='gzip') + res = df.to_csv(csvfilename, index_label='index', + compression='gzip') - data = dataprep(df,id=workoutid,bands=False,debug=debug) + data = dataprep(df, id=workoutid, bands=False, debug=debug) return data # Saves C2 stroke data to CSV and database -def add_c2_stroke_data_db(strokedata,workoutid,starttimeunix,csvfilename, - debug=False,workouttype='rower'): + + +def add_c2_stroke_data_db(strokedata, workoutid, starttimeunix, csvfilename, + debug=False, workouttype='rower'): res = make_cumvalues(0.1*strokedata['t']) cum_time = res[0] @@ -243,97 +242,91 @@ def add_c2_stroke_data_db(strokedata,workoutid,starttimeunix,csvfilename, unixtime = cum_time+starttimeunix # unixtime[0] = starttimeunix - seconds = 0.1*strokedata.loc[:,'t'] + seconds = 0.1*strokedata.loc[:, 't'] nr_rows = len(unixtime) - try: # pragma: no cover - latcoord = strokedata.loc[:,'lat'] - loncoord = strokedata.loc[:,'lon'] + try: # pragma: no cover + latcoord = strokedata.loc[:, 'lat'] + loncoord = strokedata.loc[:, 'lon'] except: latcoord = np.zeros(nr_rows) loncoord = np.zeros(nr_rows) - try: - strokelength = strokedata.loc[:,'strokelength'] + strokelength = strokedata.loc[:, 'strokelength'] except: strokelength = np.zeros(nr_rows) - dist2 = 0.1*strokedata.loc[:,'d'] + dist2 = 0.1*strokedata.loc[:, 'd'] try: - spm = strokedata.loc[:,'spm'] - except KeyError: # pragma: no cover + spm = strokedata.loc[:, 'spm'] + except KeyError: # pragma: no cover spm = 0*dist2 try: - hr = strokedata.loc[:,'hr'] - except KeyError: # pragma: no cover + hr = strokedata.loc[:, 'hr'] + except KeyError: # pragma: no cover hr = 0*spm - pace = strokedata.loc[:,'p']/10. - pace = np.clip(pace,0,1e4) - pace = pace.replace(0,300) + pace = strokedata.loc[:, 'p']/10. + pace = np.clip(pace, 0, 1e4) + pace = pace.replace(0, 300) velo = 500./pace power = 2.8*velo**3 - if workouttype == 'bike': # pragma: no cover + if workouttype == 'bike': # pragma: no cover velo = 1000./pace - - # save csv # Create data frame with all necessary data to write to csv - df = pd.DataFrame({'TimeStamp (sec)':unixtime, + df = pd.DataFrame({'TimeStamp (sec)': unixtime, ' Horizontal (meters)': dist2, - ' Cadence (stokes/min)':spm, - ' HRCur (bpm)':hr, - ' longitude':loncoord, - ' latitude':latcoord, - ' Stroke500mPace (sec/500m)':pace, - ' Power (watts)':power, - ' DragFactor':np.zeros(nr_rows), - ' DriveLength (meters)':np.zeros(nr_rows), - ' StrokeDistance (meters)':strokelength, - ' DriveTime (ms)':np.zeros(nr_rows), - ' StrokeRecoveryTime (ms)':np.zeros(nr_rows), - ' AverageDriveForce (lbs)':np.zeros(nr_rows), - ' PeakDriveForce (lbs)':np.zeros(nr_rows), - ' lapIdx':lapidx, + ' Cadence (stokes/min)': spm, + ' HRCur (bpm)': hr, + ' longitude': loncoord, + ' latitude': latcoord, + ' Stroke500mPace (sec/500m)': pace, + ' Power (watts)': power, + ' DragFactor': np.zeros(nr_rows), + ' DriveLength (meters)': np.zeros(nr_rows), + ' StrokeDistance (meters)': strokelength, + ' DriveTime (ms)': np.zeros(nr_rows), + ' StrokeRecoveryTime (ms)': np.zeros(nr_rows), + ' AverageDriveForce (lbs)': np.zeros(nr_rows), + ' PeakDriveForce (lbs)': np.zeros(nr_rows), + ' lapIdx': lapidx, ' WorkoutState': 4, - ' ElapsedTime (sec)':seconds, + ' ElapsedTime (sec)': seconds, 'cum_dist': dist2 }) - - df.sort_values(by='TimeStamp (sec)',ascending=True) + df.sort_values(by='TimeStamp (sec)', ascending=True) timestr = strftime("%Y%m%d-%H%M%S") - # Create CSV file name and save data to CSV file - res = df.to_csv(csvfilename,index_label='index', - compression='gzip') - + res = df.to_csv(csvfilename, index_label='index', + compression='gzip') try: - data = dataprep(df,id=workoutid,bands=False,debug=debug) - except: # pragma: no cover + data = dataprep(df, id=workoutid, bands=False, debug=debug) + except: # pragma: no cover return 0 return data -def handle_nonpainsled(f2,fileformat,summary=''): # pragma: no cover +def handle_nonpainsled(f2, fileformat, summary=''): # pragma: no cover oarlength = 2.89 inboard = 0.88 # handle RowPro: if (fileformat == 'rp'): row = RowProParser(f2) # handle TCX - if (fileformat == 'tcx'): + if (fileformat == 'tcx'): row = TCXParser(f2) # handle Mystery @@ -376,12 +369,11 @@ def handle_nonpainsled(f2,fileformat,summary=''): # pragma: no cover if (fileformat == 'speedcoach2'): row = SpeedCoach2Parser(f2) try: - oarlength,inboard = get_empower_rigging(f2) + oarlength, inboard = get_empower_rigging(f2) summary = row.allstats() except: pass - # handle ErgStick if (fileformat == 'ergstick'): row = ErgStickParser(f2) @@ -393,39 +385,39 @@ def handle_nonpainsled(f2,fileformat,summary=''): # pragma: no cover s.setsummary() summary = s.summarytext - f_to_be_deleted = f2 # should delete file f2 = f2[:-4]+'o.csv' - row.write_csv(f2,gzip=True) + row.write_csv(f2, gzip=True) - #os.remove(f2) + # os.remove(f2) try: os.remove(f_to_be_deleted) except: os.remove(f_to_be_deleted+'.gz') - return (f2,summary,oarlength,inboard) + return (f2, summary, oarlength, inboard) - -def delete_strokedata(id,debug=False): +def delete_strokedata(id, debug=False): dirname = 'media/strokedata_{id}.parquet.gz'.format(id=id) try: shutil.rmtree(dirname) - except FileNotFoundError: # pragma: no cover + except FileNotFoundError: # pragma: no cover pass -def update_strokedata(id,df,debug=False): - delete_strokedata(id,debug=debug) - if debug: # pragma: no cover # pragma: no cover - print("updating ",id) - rowdata = dataprep(df,id=id,bands=True,barchart=True,otwpower=True, + +def update_strokedata(id, df, debug=False): + delete_strokedata(id, debug=debug) + if debug: # pragma: no cover # pragma: no cover + print("updating ", id) + rowdata = dataprep(df, id=id, bands=True, barchart=True, otwpower=True, debug=debug) return rowdata -def update_empower(id, inboard, oarlength, boattype, df, f1, debug=False): # pragma: no cover + +def update_empower(id, inboard, oarlength, boattype, df, f1, debug=False): # pragma: no cover corr_factor = 1.0 if 'x' in boattype: @@ -437,7 +429,7 @@ def update_empower(id, inboard, oarlength, boattype, df, f1, debug=False): # pra a = 0.15 b = 0.275 - corr_factor = empower_bug_correction(oarlength,inboard,a,b) + corr_factor = empower_bug_correction(oarlength, inboard, a, b) success = False @@ -451,45 +443,44 @@ def update_empower(id, inboard, oarlength, boattype, df, f1, debug=False): # pra pass if success: - delete_strokedata(id,debug=debug) - if debug: # pragma: no cover - print("updated ",id) - print("correction ",corr_factor) + delete_strokedata(id, debug=debug) + if debug: # pragma: no cover + print("updated ", id) + print("correction ", corr_factor) else: - if debug: # pragma: no cover - print("not updated ",id) + if debug: # pragma: no cover + print("not updated ", id) - - rowdata = dataprep(df,id=id,bands=True,barchart=True,otwpower=True, + rowdata = dataprep(df, id=id, bands=True, barchart=True, otwpower=True, debug=debug) row = rrdata(df=df) - row.write_csv(f1,gzip=True) + row.write_csv(f1, gzip=True) return success -def testdata(time,distance,pace,spm): # pragma: no cover - t1 = np.issubdtype(time,np.number) - t2 = np.issubdtype(distance,np.number) - t3 = np.issubdtype(pace,np.number) - t4 = np.issubdtype(spm,np.number) +def testdata(time, distance, pace, spm): # pragma: no cover + t1 = np.issubdtype(time, np.number) + t2 = np.issubdtype(distance, np.number) + t3 = np.issubdtype(pace, np.number) + t4 = np.issubdtype(spm, np.number) return t1 and t2 and t3 and t4 - -def getsmallrowdata_db(columns,ids=[],debug=False): - csvfilenames = ['media/strokedata_{id}.parquet.gz'.format(id=id) for id in ids] +def getsmallrowdata_db(columns, ids=[], debug=False): + csvfilenames = [ + 'media/strokedata_{id}.parquet.gz'.format(id=id) for id in ids] data = [] columns = [c for c in columns if c != 'None'] df = pd.DataFrame() - if len(ids)>1: # pragma: no cover - for id, f in zip(ids,csvfilenames): + if len(ids) > 1: # pragma: no cover + for id, f in zip(ids, csvfilenames): try: - df = pd.read_parquet(f,columns=columns,engine='pyarrow') + df = pd.read_parquet(f, columns=columns, engine='pyarrow') data.append(df) except OSError: pass @@ -497,30 +488,31 @@ def getsmallrowdata_db(columns,ids=[],debug=False): pass try: - df = pd.concat(data,axis=0) + df = pd.concat(data, axis=0) except ValueError: df = pd.DataFrame() - elif len(ids)==1: + elif len(ids) == 1: try: - df = pd.read_parquet(csvfilenames[0],columns=columns,engine='pyarrow') - except (OSError,IndexError): # pragma: no cover + df = pd.read_parquet( + csvfilenames[0], columns=columns, engine='pyarrow') + except (OSError, IndexError): # pragma: no cover df = pd.DataFrame() - else: # pragma: no cover + else: # pragma: no cover df = pd.DataFrame() - return df -def update_workout_field_sql(workoutid,fieldname,value,debug=False): - if debug: # pragma: no cover # pragma: no cover + +def update_workout_field_sql(workoutid, fieldname, value, debug=False): + if debug: # pragma: no cover # pragma: no cover engine = create_engine(database_url_debug, echo=False) else: engine = create_engine(database_url, echo=False) table = 'rowers_workout' - query = "UPDATE %s SET %s = '%s' WHERE `id` = %s;" % (table,fieldname,value,workoutid) - + query = "UPDATE %s SET %s = '%s' WHERE `id` = %s;" % ( + table, fieldname, value, workoutid) with engine.connect() as conn, conn.begin(): result = conn.execute(query) @@ -530,11 +522,13 @@ def update_workout_field_sql(workoutid,fieldname,value,debug=False): return 1 -def update_c2id_sql(id,c2id): # pragma: no cover + +def update_c2id_sql(id, c2id): # pragma: no cover engine = create_engine(database_url, echo=False) table = 'rowers_workout' - query = "UPDATE %s SET uploadedtoc2 = %s WHERE `id` = %s;" % (table,c2id,id) + query = "UPDATE %s SET uploadedtoc2 = %s WHERE `id` = %s;" % ( + table, c2id, id) with engine.connect() as conn, conn.begin(): result = conn.execute(query) @@ -545,41 +539,38 @@ def update_c2id_sql(id,c2id): # pragma: no cover return 1 - - - -def read_cols_df_sql(ids,columns,debug=False): # pragma: no cover - columns = list(columns)+['distance','spm'] +def read_cols_df_sql(ids, columns, debug=False): # pragma: no cover + columns = list(columns)+['distance', 'spm'] columns = [x for x in columns if x != 'None'] columns = list(set(columns)) ids = [int(id) for id in ids] - if len(ids) == 0: return pd.DataFrame() elif len(ids) == 1: try: filename = 'media/strokedata_{id}.parquet.gz'.format(id=ids[0]) - df = pd.read_parquet(filename,columns=columns) + df = pd.read_parquet(filename, columns=columns) except OSError: pass else: data = [] - filenames = ['media/strokedata_{id}.parquet.gz'.format(id=id) for id in ids] - for id,f in zip(ids,filenames): + filenames = [ + 'media/strokedata_{id}.parquet.gz'.format(id=id) for id in ids] + for id, f in zip(ids, filenames): try: - df = pd.read_parquet(f,columns=columns) + df = pd.read_parquet(f, columns=columns) data.append(df) except OSError: pass - df = pd.concat(data,axis=0) + df = pd.concat(data, axis=0) return df -def read_df_sql(id,debug=False): # pragma: no cover +def read_df_sql(id, debug=False): # pragma: no cover try: f = 'media/strokedata_{id}.parquet.gz'.format(id=id) df = pd.read_parquet(f) @@ -590,8 +581,9 @@ def read_df_sql(id,debug=False): # pragma: no cover return df -def getcpdata_sql(rower_id,table='cpdata',debug=False): # pragma: no cover - if debug: # pragma: no cover + +def getcpdata_sql(rower_id, table='cpdata', debug=False): # pragma: no cover + if debug: # pragma: no cover engine = create_engine(database_url_debug, echo=False) else: engine = create_engine(database_url, echo=False) @@ -599,14 +591,15 @@ def getcpdata_sql(rower_id,table='cpdata',debug=False): # pragma: no cover query = sa.text('SELECT * from {table} WHERE user={rower_id};'.format( rower_id=rower_id, table=table, - )) + )) connection = engine.raw_connection() df = pd.read_sql_query(query, engine) return df -def deletecpdata_sql(rower_id,table='cpdata',debug=False): # pragma: no cover - if debug: # pragma: no cover + +def deletecpdata_sql(rower_id, table='cpdata', debug=False): # pragma: no cover + if debug: # pragma: no cover engine = create_engine(database_url_debug, echo=False) else: engine = create_engine(database_url, echo=False) @@ -614,19 +607,20 @@ def deletecpdata_sql(rower_id,table='cpdata',debug=False): # pragma: no cover query = sa.text('DELETE from {table} WHERE user={rower_id};'.format( rower_id=rower_id, table=table, - )) + )) with engine.connect() as conn, conn.begin(): try: result = conn.execute(query) - except: # pragma: no cover + except: # pragma: no cover print("Database locked") conn.close() engine.dispose() -def delete_agegroup_db(age,sex,weightcategory,debug=False): - if debug: # pragma: no cover + +def delete_agegroup_db(age, sex, weightcategory, debug=False): + if debug: # pragma: no cover engine = create_engine(database_url_debug, echo=False) - else: # pragma: no cover + else: # pragma: no cover engine = create_engine(database_url, echo=False) query = sa.text('DELETE from {table} WHERE age={age} and weightcategory = {weightcategory} and sex={sex};'.format( @@ -634,37 +628,40 @@ def delete_agegroup_db(age,sex,weightcategory,debug=False): age=age, weightcategory=weightcategory, table='calcagegrouprecords' - )) + )) with engine.connect() as conn, conn.begin(): try: result = conn.execute(query) - except: # pragma: no cover + except: # pragma: no cover print("Database locked") conn.close() engine.dispose() -def update_agegroup_db(age,sex,weightcategory,wcdurations,wcpower, + +def update_agegroup_db(age, sex, weightcategory, wcdurations, wcpower, debug=False): - delete_agegroup_db(age,sex,weightcategory,debug=debug) + delete_agegroup_db(age, sex, weightcategory, debug=debug) - wcdurations = [None if type(y) is float and np.isnan(y) else y for y in wcdurations] - wcpower = [None if type(y) is float and np.isnan(y) else y for y in wcpower] + wcdurations = [None if type(y) is float and np.isnan( + y) else y for y in wcdurations] + wcpower = [None if type(y) is float and np.isnan(y) + else y for y in wcpower] df = pd.DataFrame( { - 'duration':wcdurations, - 'power':wcpower, - } + 'duration': wcdurations, + 'power': wcpower, + } ) df['sex'] = sex df['age'] = age df['weightcategory'] = weightcategory - df.replace([np.inf,-np.inf],np.nan,inplace=True) - df.dropna(axis=0,inplace=True) + df.replace([np.inf, -np.inf], np.nan, inplace=True) + df.dropna(axis=0, inplace=True) - if debug: # pragma: no cover # pragma: no cover + if debug: # pragma: no cover # pragma: no cover engine = create_engine(database_url_debug, echo=False) else: engine = create_engine(database_url, echo=False) @@ -676,37 +673,31 @@ def update_agegroup_db(age,sex,weightcategory,wcdurations,wcpower, engine.dispose() -def updatecpdata_sql(rower_id,delta,cp,table='cpdata',distance=pd.Series([],dtype='float'),debug=False): - deletecpdata_sql(rower_id,table=table,debug=debug) +def updatecpdata_sql(rower_id, delta, cp, table='cpdata', distance=pd.Series([], dtype='float'), debug=False): + deletecpdata_sql(rower_id, table=table, debug=debug) df = pd.DataFrame( { - 'delta':delta, - 'cp':cp, - 'user':rower_id + 'delta': delta, + 'cp': cp, + 'user': rower_id } ) - if not distance.empty: df['distance'] = distance - if debug: # pragma: no cover + if debug: # pragma: no cover engine = create_engine(database_url_debug, echo=False) else: engine = create_engine(database_url, echo=False) - with engine.connect() as conn, conn.begin(): df.to_sql(table, engine, if_exists='append', index=False) conn.close() engine.dispose() - - - - -def smalldataprep(therows,xparam,yparam1,yparam2): # pragma: no cover +def smalldataprep(therows, xparam, yparam1, yparam2): # pragma: no cover df = pd.DataFrame() if yparam2 == 'None': yparam2 = 'power' @@ -726,9 +717,9 @@ def smalldataprep(therows,xparam,yparam1,yparam2): # pragma: no cover yparam2: rowdata[yparam2], 'distance': rowdata['distance'], 'spm': rowdata['spm'], - } - ) - df = pd.concat([df,rowdata],ignore_index=True) + } + ) + df = pd.concat([df, rowdata], ignore_index=True) except IOError: try: rowdata = dataprep(rrdata(csvfile=f1+'.gz').df) @@ -737,104 +728,103 @@ def smalldataprep(therows,xparam,yparam1,yparam2): # pragma: no cover yparam2: rowdata[yparam2], 'distance': rowdata['distance'], 'spm': rowdata['spm'], - } - ) - df = pd.concat([df,rowdata],ignore_index=True) + } + ) + df = pd.concat([df, rowdata], ignore_index=True) except IOError: pass return df -def dataprep(rowdatadf,id=0,bands=True,barchart=True,otwpower=True, - empower=True,debug=False,inboard=0.88,forceunit='lbs'): +def dataprep(rowdatadf, id=0, bands=True, barchart=True, otwpower=True, + empower=True, debug=False, inboard=0.88, forceunit='lbs'): - if rowdatadf.empty: # pragma: no cover - if debug: # pragma: no cover + if rowdatadf.empty: # pragma: no cover + if debug: # pragma: no cover print("empty") return 0 # rowdatadf.set_index([range(len(rowdatadf))],inplace=True) - t = rowdatadf.loc[:,'TimeStamp (sec)'] - t = pd.Series(t-rowdatadf.loc[:,'TimeStamp (sec)'].iloc[0]) + t = rowdatadf.loc[:, 'TimeStamp (sec)'] + t = pd.Series(t-rowdatadf.loc[:, 'TimeStamp (sec)'].iloc[0]) - row_index = rowdatadf.loc[:,' Stroke500mPace (sec/500m)'] > 3000 - rowdatadf.loc[row_index,' Stroke500mPace (sec/500m)'] = 3000. + row_index = rowdatadf.loc[:, ' Stroke500mPace (sec/500m)'] > 3000 + rowdatadf.loc[row_index, ' Stroke500mPace (sec/500m)'] = 3000. - p = rowdatadf.loc[:,' Stroke500mPace (sec/500m)'] + p = rowdatadf.loc[:, ' Stroke500mPace (sec/500m)'] try: - velo = rowdatadf.loc[:,' AverageBoatSpeed (m/s)'] + velo = rowdatadf.loc[:, ' AverageBoatSpeed (m/s)'] except KeyError: velo = 500./p - hr = rowdatadf.loc[:,' HRCur (bpm)'] - spm = rowdatadf.loc[:,' Cadence (stokes/min)'] - cumdist = rowdatadf.loc[:,'cum_dist'] + hr = rowdatadf.loc[:, ' HRCur (bpm)'] + spm = rowdatadf.loc[:, ' Cadence (stokes/min)'] + cumdist = rowdatadf.loc[:, 'cum_dist'] - power = rowdatadf.loc[:,' Power (watts)'] - averageforce = rowdatadf.loc[:,' AverageDriveForce (lbs)'] - drivelength = rowdatadf.loc[:,' DriveLength (meters)'] + power = rowdatadf.loc[:, ' Power (watts)'] + averageforce = rowdatadf.loc[:, ' AverageDriveForce (lbs)'] + drivelength = rowdatadf.loc[:, ' DriveLength (meters)'] try: - workoutstate = rowdatadf.loc[:,' WorkoutState'] - except KeyError: # pragma: no cover + workoutstate = rowdatadf.loc[:, ' WorkoutState'] + except KeyError: # pragma: no cover workoutstate = 0*hr - peakforce = rowdatadf.loc[:,' PeakDriveForce (lbs)'] + peakforce = rowdatadf.loc[:, ' PeakDriveForce (lbs)'] forceratio = averageforce/peakforce forceratio = forceratio.fillna(value=0) try: - drivetime = rowdatadf.loc[:,' DriveTime (ms)'] - recoverytime = rowdatadf.loc[:,' StrokeRecoveryTime (ms)'] + drivetime = rowdatadf.loc[:, ' DriveTime (ms)'] + recoverytime = rowdatadf.loc[:, ' StrokeRecoveryTime (ms)'] rhythm = 100.*drivetime/(recoverytime+drivetime) rhythm = rhythm.fillna(value=0) - except: # pragma: no cover + except: # pragma: no cover rhythm = 0.0*forceratio f = rowdatadf['TimeStamp (sec)'].diff().mean() if f != 0: try: windowsize = 2*(int(10./(f)))+1 - except ValueError: # pragma: no cover + except ValueError: # pragma: no cover windowsize = 1 - else: # pragma: no cover + else: # pragma: no cover windowsize = 1 - if windowsize <= 3: # pragma: no cover + if windowsize <= 3: # pragma: no cover windowsize = 5 - if windowsize > 3 and windowsize 3 and windowsize < len(hr): + spm = savgol_filter(spm, windowsize, 3) + hr = savgol_filter(hr, windowsize, 3) + drivelength = savgol_filter(drivelength, windowsize, 3) + forceratio = savgol_filter(forceratio, windowsize, 3) try: t2 = t.fillna(method='ffill').apply(lambda x: timedeltaconv(x)) - except TypeError: # pragma: no cover + except TypeError: # pragma: no cover t2 = 0*t - p2 = p.fillna(method='ffill').apply(lambda x: timedeltaconv(x)) try: drivespeed = drivelength/rowdatadf[' DriveTime (ms)']*1.0e3 - except KeyError: # pragma: no cover + except KeyError: # pragma: no cover drivespeed = 0.0*rowdatadf['TimeStamp (sec)'] - except TypeError: # pragma: no cover + except TypeError: # pragma: no cover drivespeed = 0.0*rowdatadf['TimeStamp (sec)'] drivespeed = drivespeed.fillna(value=0) try: driveenergy = rowdatadf['driveenergy'] - except KeyError: # pragma: no cover + except KeyError: # pragma: no cover if forceunit == 'lbs': driveenergy = drivelength*averageforce*lbstoN - else: # pragma: no cover + else: # pragma: no cover drivenergy = drivelength*averageforce - distance = rowdatadf.loc[:,'cum_dist'] + distance = rowdatadf.loc[:, 'cum_dist'] velo = 500./p @@ -844,7 +834,6 @@ def dataprep(rowdatadf,id=0,bands=True,barchart=True,otwpower=True, averageforce *= lbstoN peakforce *= lbstoN - data = DataFrame( dict( time=t * 1e3, @@ -871,107 +860,102 @@ def dataprep(rowdatadf,id=0,bands=True,barchart=True,otwpower=True, if bands: # HR bands - data['hr_ut2'] = rowdatadf.loc[:,'hr_ut2'] - data['hr_ut1'] = rowdatadf.loc[:,'hr_ut1'] - data['hr_at'] = rowdatadf.loc[:,'hr_at'] - data['hr_tr'] = rowdatadf.loc[:,'hr_tr'] - data['hr_an'] = rowdatadf.loc[:,'hr_an'] - data['hr_max'] = rowdatadf.loc[:,'hr_max'] + data['hr_ut2'] = rowdatadf.loc[:, 'hr_ut2'] + data['hr_ut1'] = rowdatadf.loc[:, 'hr_ut1'] + data['hr_at'] = rowdatadf.loc[:, 'hr_at'] + data['hr_tr'] = rowdatadf.loc[:, 'hr_tr'] + data['hr_an'] = rowdatadf.loc[:, 'hr_an'] + data['hr_max'] = rowdatadf.loc[:, 'hr_max'] data['hr_bottom'] = 0.0*data['hr'] - try: - tel = rowdatadf.loc[:,' ElapsedTime (sec)'] - except KeyError: # pragma: no cover + tel = rowdatadf.loc[:, ' ElapsedTime (sec)'] + except KeyError: # pragma: no cover rowdatadf[' ElapsedTime (sec)'] = rowdatadf['TimeStamp (sec)'] - if empower: try: - wash = rowdatadf.loc[:,'wash'] + wash = rowdatadf.loc[:, 'wash'] except KeyError: wash = 0*t try: - catch = rowdatadf.loc[:,'catch'] + catch = rowdatadf.loc[:, 'catch'] except KeyError: catch = 0*t try: - finish = rowdatadf.loc[:,'finish'] + finish = rowdatadf.loc[:, 'finish'] except KeyError: finish = 0*t try: - peakforceangle = rowdatadf.loc[:,'peakforceangle'] + peakforceangle = rowdatadf.loc[:, 'peakforceangle'] except KeyError: peakforceangle = 0*t - if data['driveenergy'].mean() == 0: try: - driveenergy = rowdatadf.loc[:,'driveenergy'] + driveenergy = rowdatadf.loc[:, 'driveenergy'] except KeyError: driveenergy = power*60/spm else: driveenergy = data['driveenergy'] - arclength = (inboard-0.05)*(np.radians(finish)-np.radians(catch)) - if arclength.mean()>0: # pragma: no cover + if arclength.mean() > 0: # pragma: no cover drivelength = arclength elif drivelength.mean() == 0: drivelength = driveenergy/(averageforce*4.44822) try: - slip = rowdatadf.loc[:,'slip'] + slip = rowdatadf.loc[:, 'slip'] except KeyError: slip = 0*t try: totalangle = finish-catch effectiveangle = finish-wash-catch-slip - except ValueError: # pragma: no cover + except ValueError: # pragma: no cover totalangle = 0*t effectiveangle = 0*t - - if windowsize > 3 and windowsize 3 and windowsize < len(slip): try: - wash = savgol_filter(wash,windowsize,3) - except TypeError: # pragma: no cover + wash = savgol_filter(wash, windowsize, 3) + except TypeError: # pragma: no cover pass try: - slip = savgol_filter(slip,windowsize,3) - except TypeError: # pragma: no cover + slip = savgol_filter(slip, windowsize, 3) + except TypeError: # pragma: no cover pass try: - catch = savgol_filter(catch,windowsize,3) - except TypeError: # pragma: no cover + catch = savgol_filter(catch, windowsize, 3) + except TypeError: # pragma: no cover pass try: - finish = savgol_filter(finish,windowsize,3) - except TypeError: # pragma: no cover + finish = savgol_filter(finish, windowsize, 3) + except TypeError: # pragma: no cover pass try: - peakforceangle = savgol_filter(peakforceangle,windowsize,3) - except TypeError: # pragma: no cover + peakforceangle = savgol_filter(peakforceangle, windowsize, 3) + except TypeError: # pragma: no cover pass try: - driveenergy = savgol_filter(driveenergy,windowsize,3) - except TypeError: # pragma: no cover + driveenergy = savgol_filter(driveenergy, windowsize, 3) + except TypeError: # pragma: no cover pass try: - drivelength = savgol_filter(drivelength,windowsize,3) - except TypeError: # pragma: no cover + drivelength = savgol_filter(drivelength, windowsize, 3) + except TypeError: # pragma: no cover pass try: - totalangle = savgol_filter(totalangle,windowsize,3) - except TypeError: # pragma: no cover + totalangle = savgol_filter(totalangle, windowsize, 3) + except TypeError: # pragma: no cover pass try: - effectiveangle = savgol_filter(effectiveangle,windowsize,3) - except TypeError: # pragma: no cover + effectiveangle = savgol_filter(effectiveangle, windowsize, 3) + except TypeError: # pragma: no cover pass velo = 500./p @@ -979,7 +963,7 @@ def dataprep(rowdatadf,id=0,bands=True,barchart=True,otwpower=True, ergpw = 2.8*velo**3 efficiency = 100.*ergpw/power - efficiency = efficiency.replace([-np.inf,np.inf],np.nan) + efficiency = efficiency.replace([-np.inf, np.inf], np.nan) efficiency.fillna(method='ffill') try: @@ -993,16 +977,16 @@ def dataprep(rowdatadf,id=0,bands=True,barchart=True,otwpower=True, data['totalangle'] = totalangle data['effectiveangle'] = effectiveangle data['efficiency'] = efficiency - except ValueError: # pragma: no cover + except ValueError: # pragma: no cover pass if otwpower: try: - nowindpace = rowdatadf.loc[:,'nowindpace'] + nowindpace = rowdatadf.loc[:, 'nowindpace'] except KeyError: nowindpace = p try: - equivergpower = rowdatadf.loc[:,'equivergpower'] + equivergpower = rowdatadf.loc[:, 'equivergpower'] except KeyError: equivergpower = 0*p+50. @@ -1013,8 +997,6 @@ def dataprep(rowdatadf,id=0,bands=True,barchart=True,otwpower=True, ergpace[ergpace == np.inf] = 240. ergpace2 = ergpace.apply(lambda x: timedeltaconv(x)) - - data['ergpace'] = ergpace*1.e3 data['nowindpace'] = nowindpace*1.e3 data['equivergpower'] = equivergpower @@ -1022,17 +1004,16 @@ def dataprep(rowdatadf,id=0,bands=True,barchart=True,otwpower=True, data['fnowindpace'] = nicepaceformat(nowindpace2) data['efficiency'] = efficiency - - data = data.replace([-np.inf,np.inf],np.nan) + data = data.replace([-np.inf, np.inf], np.nan) data = data.fillna(method='ffill') - data.dropna(axis=0,inplace=True,how='all') - data.dropna(axis=1,inplace=True,how='any') + data.dropna(axis=0, inplace=True, how='all') + data.dropna(axis=1, inplace=True, how='any') # write data if id given if id != 0: data['workoutid'] = id - data.fillna(0,inplace=True) + data.fillna(0, inplace=True) for k, v in dtypes.items(): try: data[k] = data[k].astype(v) @@ -1040,7 +1021,7 @@ def dataprep(rowdatadf,id=0,bands=True,barchart=True,otwpower=True, pass filename = 'media/strokedata_{id}.parquet.gz'.format(id=id) - df = dd.from_pandas(data,npartitions=1) - df.to_parquet(filename,engine='fastparquet',compression='GZIP') + df = dd.from_pandas(data, npartitions=1) + df.to_parquet(filename, engine='fastparquet', compression='GZIP') return data diff --git a/rowers/datautils.py b/rowers/datautils.py index ede1bfd3..a097e89b 100644 --- a/rowers/datautils.py +++ b/rowers/datautils.py @@ -8,26 +8,27 @@ import numpy as np from scipy.interpolate import griddata from scipy import optimize -from rowers.mytypes import otwtypes,otetypes,rowtypes +from rowers.mytypes import otwtypes, otetypes, rowtypes #p0 = [500,350,10,8000] -p0 = [190,200,33,16000] +p0 = [190, 200, 33, 16000] # RPE to TSS rpetotss = { - 1:20, - 2:30, - 3:40, - 4:50, - 5:60, - 6:70, - 7:80, - 8:100, - 9:120, - 10:140, + 1: 20, + 2: 30, + 3: 40, + 4: 50, + 5: 60, + 6: 70, + 7: 80, + 8: 100, + 9: 120, + 10: 140, } -def updatecp(delta,cpvalues,r,workouttype='water'): # pragma: no cover + +def updatecp(delta, cpvalues, r, workouttype='water'): # pragma: no cover if workouttype in otwtypes: p0 = r.p0 p1 = r.p1 @@ -45,14 +46,13 @@ def updatecp(delta,cpvalues,r,workouttype='water'): # pragma: no cover cp = cpvalues.append(cp2) powerdf = pd.DataFrame({ - 'Delta':delta, - 'CP':cp, - }) - - powerdf.dropna(axis=0,inplace=True) - powerdf.sort_values(['Delta','CP'],ascending=[1,0],inplace=True) - powerdf.drop_duplicates(subset='Delta',keep='first',inplace=True) + 'Delta': delta, + 'CP': cp, + }) + powerdf.dropna(axis=0, inplace=True) + powerdf.sort_values(['Delta', 'CP'], ascending=[1, 0], inplace=True) + powerdf.drop_duplicates(subset='Delta', keep='first', inplace=True) res = cpfit(powerdf) p1 = res[0] @@ -74,35 +74,37 @@ def updatecp(delta,cpvalues,r,workouttype='water'): # pragma: no cover return 1 -def cpfit(powerdf,fraclimit=0.0001,nmax=1000): - # Fit the data to thee parameter CP model - fitfunc = lambda pars,x: abs(pars[0])/(1+(x/abs(pars[2]))) + abs(pars[1])/(1+(x/abs(pars[3]))) - errfunc = lambda pars,x,y: fitfunc(pars,x)-y +def cpfit(powerdf, fraclimit=0.0001, nmax=1000): + # Fit the data to thee parameter CP model + def fitfunc(pars, x): return abs( + pars[0])/(1+(x/abs(pars[2]))) + abs(pars[1])/(1+(x/abs(pars[3]))) + + def errfunc(pars, x, y): return fitfunc(pars, x)-y p1 = p0 thesecs = powerdf['Delta'] theavpower = powerdf['CP'] - if len(thesecs)>=4: + if len(thesecs) >= 4: try: - p1, success = optimize.leastsq(errfunc, p0[:], args = (thesecs,theavpower)) - except: # pragma: no cover - factor = fitfunc(p0,thesecs.mean())/theavpower.mean() - p1 = [p0[0]/factor,p0[1]/factor,p0[2],p0[3]] + p1, success = optimize.leastsq( + errfunc, p0[:], args=(thesecs, theavpower)) + except: # pragma: no cover + factor = fitfunc(p0, thesecs.mean())/theavpower.mean() + p1 = [p0[0]/factor, p0[1]/factor, p0[2], p0[3]] else: - factor = fitfunc(p0,thesecs.mean())/theavpower.mean() - p1 = [p0[0]/factor,p0[1]/factor,p0[2],p0[3]] - + factor = fitfunc(p0, thesecs.mean())/theavpower.mean() + p1 = [p0[0]/factor, p0[1]/factor, p0[2], p0[3]] p1 = [abs(p) for p in p1] fitt = pd.Series(10**(4*np.arange(100)/100.)) - fitpower = fitfunc(p1,fitt) + fitpower = fitfunc(p1, fitt) - fitpoints = fitfunc(p1,thesecs) + fitpoints = fitfunc(p1, thesecs) fitpoints0 = fitpoints.copy() dd = fitpoints-theavpower @@ -111,7 +113,7 @@ def cpfit(powerdf,fraclimit=0.0001,nmax=1000): frac = abs(ddmin)/fitpoints.mean() counter = 0 - while frac>fraclimit and counter fraclimit and counter < nmax: fitpoints = fitpoints*(fitpoints.mean()-ddmin)/(fitpoints.mean()) dd = fitpoints-theavpower ddmin = dd.min() @@ -120,11 +122,12 @@ def cpfit(powerdf,fraclimit=0.0001,nmax=1000): ratio = fitpoints.mean()/fitpoints0.mean() - return p1,fitt,fitpower,ratio + return p1, fitt, fitpower, ratio + def getlogarr(maxt): maxlog10 = np.log10(maxt-5) - #print(maxlog10,round(maxlog10)) + # print(maxlog10,round(maxlog10)) aantal = 10*round(maxlog10) logarr = np.arange(aantal+1)/10. @@ -132,17 +135,18 @@ def getlogarr(maxt): for la in logarr: try: v = 5+int(10.**(la)) - except ValueError: # pragma: no cover + except ValueError: # pragma: no cover v = 0 res.append(v) - logarr = pd.Series(res,dtype='float') - logarr.drop_duplicates(keep='first',inplace=True) + logarr = pd.Series(res, dtype='float') + logarr.drop_duplicates(keep='first', inplace=True) logarr = logarr.values return logarr -def getsinglecp(df): # pragma: no cover + +def getsinglecp(df): # pragma: no cover thesecs = df['TimeStamp (sec)'].max()-df['TimeStamp (sec)'].min() if thesecs != 0: maxt = 1.05*thesecs @@ -151,26 +155,25 @@ def getsinglecp(df): # pragma: no cover logarr = getlogarr(maxt) - dfnew = pd.DataFrame({ - 'time':1000*(df['TimeStamp (sec)']-df.loc[:,'TimeStamp (sec)'].iloc[0]), - 'power':df[' Power (watts)'] + 'time': 1000*(df['TimeStamp (sec)']-df.loc[:, 'TimeStamp (sec)'].iloc[0]), + 'power': df[' Power (watts)'] }) dfnew['workoutid'] = 0 dfgrouped = dfnew.groupby(['workoutid']) - delta,cpvalue,avgpower = getcp(dfgrouped,logarr) + delta, cpvalue, avgpower = getcp(dfgrouped, logarr) - return delta,cpvalue,avgpower + return delta, cpvalue, avgpower -def getcp_new(dfgrouped,logarr): # pragma: no cover + +def getcp_new(dfgrouped, logarr): # pragma: no cover delta = [] cpvalue = [] avgpower = {} - #print(dfgrouped) - + # print(dfgrouped) for id, group in dfgrouped: tt = group['time'].copy() @@ -189,28 +192,28 @@ def getcp_new(dfgrouped,logarr): # pragma: no cover newt = np.arange(newlen)*tmax/float(newlen) deltat = newt[1]-newt[0] else: - newt = np.arange(0,tmax,10.) + newt = np.arange(0, tmax, 10.) deltat = 10. ww = griddata(tt.values, ww.values, - newt,method='linear', + newt, method='linear', rescale=True) - tt = pd.Series(newt,dtype='float') - ww = pd.Series(ww,dtype='float') + tt = pd.Series(newt, dtype='float') + ww = pd.Series(ww, dtype='float') - G = pd.Series(ww.cumsum(),dtype='float') - G = pd.concat([pd.Series([0],dtype='float'),G]) + G = pd.Series(ww.cumsum(), dtype='float') + G = pd.concat([pd.Series([0], dtype='float'), G]) - h = np.mgrid[0:len(tt)+1:1,0:len(tt)+1:1] + h = np.mgrid[0:len(tt)+1:1, 0:len(tt)+1:1] distances = pd.DataFrame(h[1]-h[0]) ones = 1+np.zeros(len(G)) - Ghor = np.outer(ones,G) - Gver = np.outer(G,ones) + Ghor = np.outer(ones, G) + Gver = np.outer(G, ones) Gdif = Ghor - Gver @@ -220,15 +223,15 @@ def getcp_new(dfgrouped,logarr): # pragma: no cover F = Gdif/distances - F.fillna(inplace=True,method='ffill',axis=1) - F.fillna(inplace=True,value=0) + F.fillna(inplace=True, method='ffill', axis=1) + F.fillna(inplace=True, value=0) restime = [] power = [] - for i in np.arange(0,len(tt)+1,1): + for i in np.arange(0, len(tt)+1, 1): restime.append(deltat*i) - cp = np.diag(F,i).max() + cp = np.diag(F, i).max() power.append(cp) power[0] = power[1] @@ -236,13 +239,10 @@ def getcp_new(dfgrouped,logarr): # pragma: no cover restime = np.array(restime) power = np.array(power) - #power[0] = power[1] - cpvalues = griddata(restime,power, - logarr,method='linear', fill_value=0) - - + cpvalues = griddata(restime, power, + logarr, method='linear', fill_value=0) for cpv in cpvalues: cpvalue.append(cpv) @@ -250,27 +250,27 @@ def getcp_new(dfgrouped,logarr): # pragma: no cover delta.append(d) df = pd.DataFrame({ - 'delta':delta, - 'cpvalue':cpvalue - }) + 'delta': delta, + 'cpvalue': cpvalue + }) - df.dropna(axis=0, how='any',inplace=True) - df = df.sort_values(['delta','cp'], ascending=[1, 0]) + df.dropna(axis=0, how='any', inplace=True) + df = df.sort_values(['delta', 'cp'], ascending=[1, 0]) df = df.drop_duplicates(subset='Delta', keep='first') delta = df['delta'] cpvalue = df['cpvalue'] - return delta,cpvalue,avgpower + return delta, cpvalue, avgpower -def getcp(dfgrouped,logarr): +def getcp(dfgrouped, logarr): delta = [] cpvalue = [] avgpower = {} #avgpower[0] = 0 - for id,group in dfgrouped: + for id, group in dfgrouped: tt = group['time'].copy() ww = group['power'].copy() @@ -289,26 +289,25 @@ def getcp(dfgrouped,logarr): try: avgpower[id] = int(ww.mean()) - except ValueError: # pragma: no cover + except ValueError: # pragma: no cover avgpower[id] = '---' if not np.isnan(ww.mean()): length = len(ww) dt = [] cpw = [] for i in range(length-2): - deltat,wmax = getmaxwattinterval(tt,ww,i) + deltat, wmax = getmaxwattinterval(tt, ww, i) if not np.isnan(deltat) and not np.isnan(wmax): dt.append(deltat) cpw.append(wmax) - - dt = pd.Series(dt,dtype='float') - cpw = pd.Series(cpw,dtype='float') - if len(dt)>2: + dt = pd.Series(dt, dtype='float') + cpw = pd.Series(cpw, dtype='float') + if len(dt) > 2: cpvalues = griddata(dt.values, cpw.values, - logarr,method='linear', + logarr, method='linear', rescale=True) for cpv in cpvalues: @@ -316,25 +315,23 @@ def getcp(dfgrouped,logarr): for d in logarr: delta.append(d) - - - delta = pd.Series(delta,name='Delta',dtype='float') - cpvalue = pd.Series(cpvalue,name='CP',dtype='float') - + delta = pd.Series(delta, name='Delta', dtype='float') + cpvalue = pd.Series(cpvalue, name='CP', dtype='float') cpdf = pd.DataFrame({ - 'delta':delta, - 'cpvalue':cpvalue - }) + 'delta': delta, + 'cpvalue': cpvalue + }) - cpdf.dropna(axis=0, how='any',inplace=True) + cpdf.dropna(axis=0, how='any', inplace=True) delta = cpdf['delta'] cpvalue = cpdf['cpvalue'] - return delta,cpvalue,avgpower + return delta, cpvalue, avgpower -def getmaxwattinterval(tt,ww,i): + +def getmaxwattinterval(tt, ww, i): w_roll = ww.rolling(i+2).mean().dropna() if len(w_roll): # now goes with # data points - should be fixed seconds @@ -349,7 +346,7 @@ def getmaxwattinterval(tt,ww,i): if testres: deltat = 1.0e-3*(t_0-t_1) wmax = w_roll.loc[indexmaxpos] - #if wmax > 800 or wmax*5.0e-4*deltat > 800.0: + # if wmax > 800 or wmax*5.0e-4*deltat > 800.0: # wmax = 0 else: wmax = 0 @@ -357,21 +354,22 @@ def getmaxwattinterval(tt,ww,i): except KeyError: wmax = 0 deltat = 0 - else: # pragma: no cover + else: # pragma: no cover wmax = 0 deltat = 0 - return deltat,wmax + return deltat, wmax -def getfastest(df,thevalue,mode='distance'): + +def getfastest(df, thevalue, mode='distance'): tt = df['time'].copy() dd = df['cumdist'].copy() tmax = tt.max() - if mode == 'distance': # pragma: no cover + if mode == 'distance': # pragma: no cover if dd.max() < thevalue: return 0 - else: # pragma: no cover + else: # pragma: no cover if tt.max() < thevalue: return 0 @@ -388,68 +386,72 @@ def getfastest(df,thevalue,mode='distance'): newt = np.arange(newlen)*tmax/float(newlen) deltat = newt[1]-newt[0] - dd = griddata(tt.values, - dd.values,newt,method='linear',rescale=True) + dd.values, newt, method='linear', rescale=True) - tt = pd.Series(newt,dtype='float') - dd = pd.Series(dd,dtype='float') + tt = pd.Series(newt, dtype='float') + dd = pd.Series(dd, dtype='float') - G = pd.concat([pd.Series([0]),dd]) - T = pd.concat([pd.Series([0]),dd]) - h = np.mgrid[0:len(tt)+1:1,0:len(tt)+1:1] + G = pd.concat([pd.Series([0]), dd]) + T = pd.concat([pd.Series([0]), dd]) + h = np.mgrid[0:len(tt)+1:1, 0:len(tt)+1:1] distances = pd.DataFrame(h[1]-h[0]) ones = 1+np.zeros(len(G)) - Ghor = np.outer(ones,G) - Thor = np.outer(ones,T) - Tver = np.outer(T,ones) - Gver = np.outer(G,ones) + Ghor = np.outer(ones, G) + Thor = np.outer(ones, T) + Tver = np.outer(T, ones) + Gver = np.outer(G, ones) Gdif = Ghor-Gver Gdif = np.tril(Gdif.T).T Gdif = pd.DataFrame(Gdif) F = Gdif - F.fillna(inplace=True,method='ffill',axis=1) - F.fillna(inplace=True,value=0) + F.fillna(inplace=True, method='ffill', axis=1) + F.fillna(inplace=True, value=0) restime = [] distance = [] starttimes = [] endtime = [] - for i in np.arange(0,len(tt)+1,1): + for i in np.arange(0, len(tt)+1, 1): restime.append(deltat*i) - cp = np.diag(F,i).max() - loc = np.argmax(np.diag(F,i)) + cp = np.diag(F, i).max() + loc = np.argmax(np.diag(F, i)) thestarttime = tt[loc] starttimes.append(thestarttime) distance.append(cp) - distance[0] = distance[1] restime = np.array(restime) distance = np.array(distance) starttimes = np.array(starttimes) - #for i in range(len(restime)): + # for i in range(len(restime)): # if restime[i]') - return html.replace('\n','
') -def send_template_email(from_email,to_email,subject, - template,context, - *args,**kwargs): +def newlinetobr(html): + html = html.replace('\n\n', '
') + return html.replace('\n', '
') + + +def send_template_email(from_email, to_email, subject, + template, context, + *args, **kwargs): htmly = env.get_template(template) @@ -80,60 +82,61 @@ def send_template_email(from_email,to_email,subject, text_content = textify(html_content) # html_content = newlinetobr(html_content) - - if 'bcc' in kwargs and 'cc' in kwargs: # pragma: no cover - msg = EmailMultiAlternatives(subject, text_content, from_email, to_email,cc=kwargs['cc'], + if 'bcc' in kwargs and 'cc' in kwargs: # pragma: no cover + msg = EmailMultiAlternatives(subject, text_content, from_email, to_email, cc=kwargs['cc'], bcc=kwargs['bcc']) - elif 'bcc' in kwargs: # pragma: no cover - msg = EmailMultiAlternatives(subject, text_content, from_email, to_email,bcc=kwargs['bcc']) - elif 'cc' in kwargs: # pragma: no cover - msg = EmailMultiAlternatives(subject, text_content, from_email, to_email,cc=kwargs['cc']) + elif 'bcc' in kwargs: # pragma: no cover + msg = EmailMultiAlternatives( + subject, text_content, from_email, to_email, bcc=kwargs['bcc']) + elif 'cc' in kwargs: # pragma: no cover + msg = EmailMultiAlternatives( + subject, text_content, from_email, to_email, cc=kwargs['cc']) else: - msg = EmailMultiAlternatives(subject, text_content, from_email, to_email) + msg = EmailMultiAlternatives( + subject, text_content, from_email, to_email) msg.attach_alternative(html_content, "text/html") - if 'attach_file' in kwargs: # pragma: no cover + if 'attach_file' in kwargs: # pragma: no cover fileobj = kwargs['attach_file'] if os.path.isfile(fileobj): msg.attach_file(fileobj) else: try: fileobj2 = fileobj - with gzip.open(fileobj+'.gz','rb') as f_in, open(fileobj2,'wb') as f_out: - shutil.copyfileobj(f_in,f_out) + with gzip.open(fileobj+'.gz', 'rb') as f_in, open(fileobj2, 'wb') as f_out: + shutil.copyfileobj(f_in, f_out) msg.attach_file(fileobj2) os.remove(fileobj2) except IOError: pass - if 'emailbounced' in kwargs: # pragma: no cover + if 'emailbounced' in kwargs: # pragma: no cover emailbounced = kwargs['emailbounced'] else: emailbounced = False - - if not emailbounced: res = msg.send() - else: # pragma: no cover + else: # pragma: no cover return 0 return res -def send_confirm(user, name, link, options): # pragma: no cover + +def send_confirm(user, name, link, options): # pragma: no cover d = { - 'first_name':user.first_name, - 'name':name, - 'link':link, - } + 'first_name': user.first_name, + 'name': name, + 'link': link, + } fullemail = user.email subject = 'New Workout Added: '+name res = send_template_email('Rowsandall ', [fullemail], - subject,'confirmemail.html', + subject, 'confirmemail.html', d ) diff --git a/rowers/fakturoid.py b/rowers/fakturoid.py index 04022778..ff540092 100644 --- a/rowers/fakturoid.py +++ b/rowers/fakturoid.py @@ -4,154 +4,159 @@ from requests.auth import HTTPBasicAuth import urllib.parse from rowsandall_app.settings import ( - FAKTUROID_EMAIL, FAKTUROID_API_KEY, - FAKTUROID_SLUG + FAKTUROID_EMAIL, FAKTUROID_API_KEY, + FAKTUROID_SLUG ) slug = FAKTUROID_SLUG -invoices_url = 'https://app.fakturoid.cz/api/v2/accounts/{slug}/invoices.json'.format(slug=slug) -contacts_url = 'https://app.fakturoid.cz/api/v2/accounts/{slug}/subjects.json'.format(slug=slug) -contacts_search_url = 'https://app.fakturoid.cz/api/v2/accounts/{slug}/subjects/search.json'.format(slug=slug) +invoices_url = 'https://app.fakturoid.cz/api/v2/accounts/{slug}/invoices.json'.format( + slug=slug) +contacts_url = 'https://app.fakturoid.cz/api/v2/accounts/{slug}/subjects.json'.format( + slug=slug) +contacts_search_url = 'https://app.fakturoid.cz/api/v2/accounts/{slug}/subjects/search.json'.format( + slug=slug) auth = HTTPBasicAuth(FAKTUROID_EMAIL, FAKTUROID_API_KEY) headers = { - 'Content-Type': 'application/json', - 'User-Agent': 'rowsandall (admin@rowsandall.com)' + 'Content-Type': 'application/json', + 'User-Agent': 'rowsandall (admin@rowsandall.com)' } + def get_contacts(rower): - res = requests.get(contacts_url, auth=auth, headers=headers) - url = contacts_search_url+'?query='+urllib.parse.quote(rower.user.email) - with open('braintreewebhooks.log','a') as f: - f.write('Searching Contact url :'+str(url)+'\n') + res = requests.get(contacts_url, auth=auth, headers=headers) + url = contacts_search_url+'?query='+urllib.parse.quote(rower.user.email) + with open('braintreewebhooks.log', 'a') as f: + f.write('Searching Contact url :'+str(url)+'\n') + res = requests.get(url, auth=auth, headers=headers) - res = requests.get(url, auth=auth, headers=headers) + with open('braintreewebhooks.log', 'a') as f: + f.write('Searching Contact Status code '+str(res.status_code)+'\n') - with open('braintreewebhooks.log','a') as f: - f.write('Searching Contact Status code '+str(res.status_code)+'\n') + if res.status_code != 200: # pragma: no cover + return None - if res.status_code != 200: # pragma: no cover - return None + with open('braintreewebhooks.log', 'a') as f: + f.write('Status Code '+json.dumps(res.json())+'\n') - with open('braintreewebhooks.log','a') as f: - f.write('Status Code '+json.dumps(res.json())+'\n') + if len(res.json()) >= 1: + r = res.json()[0] + return r['id'] - if len(res.json())>=1: - r = res.json()[0] - return r['id'] - - - return None # pragma: no cover + return None # pragma: no cover # this should be triggered on braintree payment + + def create_contact(rower): - post_data = { - "name": str(rower), - "street": rower.street_address, - "street2": None, - "city": rower.city, - "zip": rower.postal_code, - "country": rower.country.code, - "vat_no": "", - "bank_account": "", - "iban": "", - "variable_symbol": rower.id, - "full_name": rower.user.first_name+" "+rower.user.last_name, - "email": rower.user.email, - "email_copy": "", - "phone": "", - "web": "" + post_data = { + "name": str(rower), + "street": rower.street_address, + "street2": None, + "city": rower.city, + "zip": rower.postal_code, + "country": rower.country.code, + "vat_no": "", + "bank_account": "", + "iban": "", + "variable_symbol": rower.id, + "full_name": rower.user.first_name+" "+rower.user.last_name, + "email": rower.user.email, + "email_copy": "", + "phone": "", + "web": "" } - with open('braintreewebhooks.log','a') as f: - f.write('Creating fakturoid contact for '+str(rower.user.email)+'\n') + with open('braintreewebhooks.log', 'a') as f: + f.write('Creating fakturoid contact for '+str(rower.user.email)+'\n') + res = requests.post(contacts_url, data=json.dumps( + post_data), auth=auth, headers=headers) - res = requests.post(contacts_url, data=json.dumps(post_data), auth=auth,headers=headers) + with open('braintreewebhooks.log', 'a') as f: + f.write('Status Code '+str(res.status_code)+'\n') - with open('braintreewebhooks.log','a') as f: - f.write('Status Code '+str(res.status_code)+'\n') + if res.status_code not in [200, 201]: # pragma: no cover + return 0 - if res.status_code not in [200,201]: # pragma: no cover - return 0 + with open('braintreewebhooks.log', 'a') as f: + f.write('Contact ID'+str(res.json()['id'])+'\n') - with open('braintreewebhooks.log','a') as f: - f.write('Contact ID'+str(res.json()['id'])+'\n') - - return res.json()['id'] + return res.json()['id'] # this should be triggered by a Braintree webhook -def create_invoice(rower,amount,braintreeid,dosend=True, - contact_id=None,name=None): - - if not contact_id: # pragma: no cover - contact_id = get_contacts(rower) - - if not name: - name = 'Rowsandall Subscription' - with open('braintreewebhooks.log','a') as f: - f.write('Creating invoice for contact iD '+str(contact_id)+'\n') +def create_invoice(rower, amount, braintreeid, dosend=True, + contact_id=None, name=None): - if not contact_id: # pragma: no cover - return 0 + if not contact_id: # pragma: no cover + contact_id = get_contacts(rower) - post_data = { - 'subject_id': contact_id, - 'custom_id': braintreeid, - 'language':'en', - 'payment_method': 'card', - 'currency': 'EUR', - 'paid_amount': str(amount), - 'status': 'paid', - 'lines': [{ - 'name': name, - 'quantity': '1', - 'unit_price': str(amount), - 'vat_rate': 0, - } - ] - } + if not name: + name = 'Rowsandall Subscription' + with open('braintreewebhooks.log', 'a') as f: + f.write('Creating invoice for contact iD '+str(contact_id)+'\n') - res = requests.post(invoices_url, data=json.dumps(post_data), auth=auth,headers=headers) - with open('braintreewebhooks.log','a') as f: - f.write('Invoice Created - status code '+str(res.status_code)+'\n') + if not contact_id: # pragma: no cover + return 0 - if res.status_code not in [200,201]: # pragma: no cover - return 0 + post_data = { + 'subject_id': contact_id, + 'custom_id': braintreeid, + 'language': 'en', + 'payment_method': 'card', + 'currency': 'EUR', + 'paid_amount': str(amount), + 'status': 'paid', + 'lines': [{ + 'name': name, + 'quantity': '1', + 'unit_price': str(amount), + 'vat_rate': 0, + } + ] + } - url = res.json()['url'] - id = res.json()['id'] + res = requests.post(invoices_url, data=json.dumps( + post_data), auth=auth, headers=headers) + with open('braintreewebhooks.log', 'a') as f: + f.write('Invoice Created - status code '+str(res.status_code)+'\n') - urlpay = 'https://app.fakturoid.cz/api/v2/accounts/{slug}/invoices/{id}/fire.json?event=pay'.format( - id=id,slug=slug - ) - urlsend = 'https://app.fakturoid.cz/api/v2/accounts/{slug}/invoices/{id}/fire.json?event=deliver'.format( - id=id,slug=slug - ) + if res.status_code not in [200, 201]: # pragma: no cover + return 0 + url = res.json()['url'] + id = res.json()['id'] - res = requests.post(urlpay,auth=auth,headers=headers) + urlpay = 'https://app.fakturoid.cz/api/v2/accounts/{slug}/invoices/{id}/fire.json?event=pay'.format( + id=id, slug=slug + ) + urlsend = 'https://app.fakturoid.cz/api/v2/accounts/{slug}/invoices/{id}/fire.json?event=deliver'.format( + id=id, slug=slug + ) - with open('braintreewebhooks.log','a') as f: - f.write('Invoice Set to paid - status code '+str(res.status_code)+'\n') + res = requests.post(urlpay, auth=auth, headers=headers) - if res.status_code not in [200,201]: # pragma: no cover - return 0 + with open('braintreewebhooks.log', 'a') as f: + f.write('Invoice Set to paid - status code ' + + str(res.status_code)+'\n') - if dosend: - res = requests.post(urlsend,auth=auth,headers=headers) + if res.status_code not in [200, 201]: # pragma: no cover + return 0 - with open('braintreewebhooks.log','a') as f: - f.write('Invoice Sent - status code '+str(res.status_code)+'\n') + if dosend: + res = requests.post(urlsend, auth=auth, headers=headers) - return id + with open('braintreewebhooks.log', 'a') as f: + f.write('Invoice Sent - status code '+str(res.status_code)+'\n') - #curl -u vas@email.cz:API_TOKEN -H 'User-Agent: YourApp (yourname@example.com)' \ - # -H 'Content-Type: application/json' \ - # -X POST -d '{ "subject_id": "28" }' \ - # https://app.fakturoid.cz/api/v2/accounts/{slug}/invoices.json + return id + + # curl -u vas@email.cz:API_TOKEN -H 'User-Agent: YourApp (yourname@example.com)' \ + # -H 'Content-Type: application/json' \ + # -X POST -d '{ "subject_id": "28" }' \ + # https://app.fakturoid.cz/api/v2/accounts/{slug}/invoices.json diff --git a/rowers/formfields.py b/rowers/formfields.py index 44fbaeda..40e85c52 100644 --- a/rowers/formfields.py +++ b/rowers/formfields.py @@ -11,7 +11,7 @@ class GroupedModelChoiceIterator(ModelChoiceIterator): super().__init__(field) def __iter__(self): - if self.field.empty_label is not None: # pragma: no cover + if self.field.empty_label is not None: # pragma: no cover yield ("", self.field.empty_label) queryset = self.queryset # Can't use iterator() when queryset uses prefetch_related() @@ -25,7 +25,9 @@ class GroupedModelChoiceField(ModelChoiceField): def __init__(self, *args, choices_groupby, **kwargs): if isinstance(choices_groupby, str): choices_groupby = attrgetter(choices_groupby) - elif not callable(choices_groupby): # pragma: no cover - raise TypeError('choices_groupby must either be a str or a callable accepting a single argument') - self.iterator = partial(GroupedModelChoiceIterator, groupby=choices_groupby) + elif not callable(choices_groupby): # pragma: no cover + raise TypeError( + 'choices_groupby must either be a str or a callable accepting a single argument') + self.iterator = partial( + GroupedModelChoiceIterator, groupby=choices_groupby) super().__init__(*args, **kwargs) diff --git a/rowers/forms.py b/rowers/forms.py index 01039f61..42be40a5 100644 --- a/rowers/forms.py +++ b/rowers/forms.py @@ -1,4 +1,12 @@ from __future__ import absolute_import +from rowers.models import VirtualRace, GeoCourse +from rowers.utils import rankingdistances, rankingdurations +from rowers.utils import ( + workflowleftpanel, workflowmiddlepanel, + defaultleft, defaultmiddle +) +from rowers.utils import palettes +from time import strftime from __future__ import division from __future__ import print_function from __future__ import unicode_literals @@ -7,37 +15,39 @@ from __future__ import unicode_literals, absolute_import from django import forms from django.contrib.admin.widgets import FilteredSelectMultiple from rowers.models import ( - Workout,Rower,Team,PlannedSession,GeoCourse, - VirtualRace,VirtualRaceResult,IndoorVirtualRaceResult, + Workout, Rower, Team, PlannedSession, GeoCourse, + VirtualRace, VirtualRaceResult, IndoorVirtualRaceResult, PaidPlan - ) -from rowers.rows import validate_file_extension,must_be_csv,validate_image_extension,validate_kml +) +from rowers.rows import validate_file_extension, must_be_csv, validate_image_extension, validate_kml from django.contrib.auth.forms import UserCreationForm from django.contrib.auth.models import User from django.contrib.admin.widgets import AdminDateWidget -from django.forms.widgets import SelectDateWidget,HiddenInput +from django.forms.widgets import SelectDateWidget, HiddenInput #from django.forms.extras.widgets import SelectDateWidget -from django.utils import timezone,translation +from django.utils import timezone, translation from django.forms import ModelForm, Select import rowers.dataprep as dataprep import rowers.mytypes as mytypes import datetime from django.forms import formset_factory from rowers.utils import landingpages -from rowers.metrics import axes, metricsgroups,rowingmetrics +from rowers.metrics import axes, metricsgroups, rowingmetrics from rowers.metrics import axlabels from rowers.rower_rules import user_is_not_basic formaxlabels = axlabels.copy() formaxlabels.pop('None') -parchoices = list(sorted(formaxlabels.items(), key = lambda x:x[1])) +parchoices = list(sorted(formaxlabels.items(), key=lambda x: x[1])) + class SurveyForm(forms.Form): surveydone = forms.ChoiceField( required=True, - choices=(('YES','YES'),('NO','NO')), + choices=(('YES', 'YES'), ('NO', 'NO')), label='Will you take a 2 minute survey to help improve rowsandall?', - widget = forms.RadioSelect) + widget=forms.RadioSelect) + class FlexibleDecimalField(forms.DecimalField): @@ -51,25 +61,28 @@ class FlexibleDecimalField(forms.DecimalField): pass try: dot_index = value.index('.') - except ValueError: # pragma: no cover + except ValueError: # pragma: no cover pass if value: - if comma_index > dot_index: # pragma: no cover + if comma_index > dot_index: # pragma: no cover value = value.replace('.', '').replace(',', '.') return super(FlexibleDecimalField, self).to_python(value) + class ResampleForm(forms.Form): resamplechoices = ( - ('overwrite','Overwrite Workout'), - ('copy','Create a Duplicate Workout') + ('overwrite', 'Overwrite Workout'), + ('copy', 'Create a Duplicate Workout') ) - resamplechoice = forms.ChoiceField(initial='copy',choices=resamplechoices,label='Copy behavior') + resamplechoice = forms.ChoiceField( + initial='copy', choices=resamplechoices, label='Copy behavior') + class TrainingZonesForm(forms.Form): zoneschoices = ( - ('power','Power Zones'), - ('hr','Heart Rate Zones') + ('power', 'Power Zones'), + ('hr', 'Heart Rate Zones') ) datechoices = ( @@ -78,123 +91,133 @@ class TrainingZonesForm(forms.Form): ) yaxischoices = ( - ('time','Time'), - ('percentage','Percentage of Time') + ('time', 'Time'), + ('percentage', 'Percentage of Time') ) - zones = forms.ChoiceField(initial='hr',label='Training Zones',choices=zoneschoices) - dates = forms.ChoiceField(initial='week',label='Date Aggregation', choices=datechoices) - yaxis = forms.ChoiceField(initial='percentage',label='Y axis',choices=yaxischoices) + zones = forms.ChoiceField( + initial='hr', label='Training Zones', choices=zoneschoices) + dates = forms.ChoiceField( + initial='week', label='Date Aggregation', choices=datechoices) + yaxis = forms.ChoiceField(initial='percentage', + label='Y axis', choices=yaxischoices) startdate = forms.DateField( initial=timezone.now()-datetime.timedelta(days=42), - widget=AdminDateWidget(), #format='%Y-%m-%d'), + widget=AdminDateWidget(), # format='%Y-%m-%d'), label='Start Date') enddate = forms.DateField( initial=timezone.now(), # widget=SelectDateWidget(years=range(1990,2050)), - widget=AdminDateWidget(), #format='%Y-%m-%d'), + widget=AdminDateWidget(), # format='%Y-%m-%d'), label='End Date') class InstantPlanSelectForm(forms.Form): datechoices = ( - ('startdate','start date'), + ('startdate', 'start date'), ('enddate', 'end date'), - ('target','target') + ('target', 'target') ) - name = forms.CharField(max_length=255,label='Plan Name',required=False) + name = forms.CharField(max_length=255, label='Plan Name', required=False) startdate = forms.DateField( initial=timezone.now(), # widget=SelectDateWidget(years=range(1990,2050)), - widget=AdminDateWidget(), #format='%Y-%m-%d'), + widget=AdminDateWidget(), # format='%Y-%m-%d'), label='Start Date') enddate = forms.DateField( initial=timezone.now()+datetime.timedelta(days=21), - widget=AdminDateWidget(), #format='%Y-%m-%d'), + widget=AdminDateWidget(), # format='%Y-%m-%d'), label='End Date') target = forms.ChoiceField(required=False) - datechoice = forms.ChoiceField(choices=datechoices,initial='enddate',label='Plan by target, start or end date', + datechoice = forms.ChoiceField(choices=datechoices, initial='enddate', label='Plan by target, start or end date', widget=forms.RadioSelect) notes = forms.CharField(required=False, - max_length=200,label='Plan Notes', + max_length=200, label='Plan Notes', widget=forms.Textarea) def __init__(self, *args, **kwargs): - targets = kwargs.pop('targets',None) - instantplan = kwargs.pop('instantplan',None) + targets = kwargs.pop('targets', None) + instantplan = kwargs.pop('instantplan', None) super(InstantPlanSelectForm, self).__init__(*args, **kwargs) if targets: - targetchoices = [(x.id,x) for x in targets] - targetchoices.append((None,'---')) + targetchoices = [(x.id, x) for x in targets] + targetchoices.append((None, '---')) self.fields['target'].choices = targetchoices else: datechoices = ( - ('startdate','start date'), + ('startdate', 'start date'), ('enddate', 'end date'), ) self.fields['datechoice'].choices = datechoices self.fields['datechoice'].label = 'Plan by start or end date' self.fields.pop('target') if instantplan: - self.fields['enddate'].initial = timezone.now()+datetime.timedelta(days=instantplan.duration) - + self.fields['enddate'].initial = timezone.now( + )+datetime.timedelta(days=instantplan.duration) # Video Analysis creation form class VideoAnalysisCreateForm(forms.Form): - name = forms.CharField(max_length=255,label='Analysis Name',required=False) - url = forms.CharField(max_length=255,required=True,label='YouTube Video URL') - delay = forms.IntegerField(initial=0,label='Delay (seconds)') + name = forms.CharField( + max_length=255, label='Analysis Name', required=False) + url = forms.CharField(max_length=255, required=True, + label='YouTube Video URL') + delay = forms.IntegerField(initial=0, label='Delay (seconds)') + def get_metricschoices(mode='water'): - modes = [mode,'both','basic'] + modes = [mode, 'both', 'basic'] metricsdescriptions = {} for m in metricsgroups: metricsdescriptions[m] = m+' (' - for name,d in rowingmetrics: - if d['group']==m and d['mode'] in modes: - metricsdescriptions[m]+=d['verbose_name']+', ' - metricsdescriptions[m]=metricsdescriptions[m][0:-2]+')' + for name, d in rowingmetrics: + if d['group'] == m and d['mode'] in modes: + metricsdescriptions[m] += d['verbose_name']+', ' + metricsdescriptions[m] = metricsdescriptions[m][0:-2]+')' - metricsgroupschoices = ((m,metricsdescriptions[m]) for m in metricsgroups) + metricsgroupschoices = ((m, metricsdescriptions[m]) for m in metricsgroups) return metricsgroupschoices + class VideoAnalysisMetricsForm(forms.Form): groups = forms.MultipleChoiceField(label='Metrics Groups', - choices=get_metricschoices(mode='water'), + choices=get_metricschoices( + mode='water'), widget=forms.CheckboxSelectMultiple,) class Meta: mode = 'water' def __init__(self, *args, **kwargs): - mode = kwargs.pop('mode','water') + mode = kwargs.pop('mode', 'water') super(VideoAnalysisMetricsForm, self).__init__(*args, **kwargs) self.fields['groups'].choices = get_metricschoices(mode=mode) # BillingForm form class BillingForm(forms.Form): - amount = FlexibleDecimalField(required=True,decimal_places=2, - max_digits=8) + amount = FlexibleDecimalField(required=True, decimal_places=2, + max_digits=8) plan = forms.IntegerField(widget=forms.HiddenInput()) - payment_method_nonce = forms.CharField(max_length=255,required=True) - paymenttype = forms.CharField(max_length=255,required=True) - tac= forms.BooleanField(required=True,initial=False) + payment_method_nonce = forms.CharField(max_length=255, required=True) + paymenttype = forms.CharField(max_length=255, required=True) + tac = forms.BooleanField(required=True, initial=False) # TrainingPlanBillingForm form + + class TrainingPlanBillingForm(forms.Form): - amount = FlexibleDecimalField(required=True,decimal_places=2, - max_digits=8) + amount = FlexibleDecimalField(required=True, decimal_places=2, + max_digits=8) plan = forms.IntegerField(widget=forms.HiddenInput()) - payment_method_nonce = forms.CharField(max_length=255,required=True) - paymenttype = forms.CharField(max_length=255,required=True) + payment_method_nonce = forms.CharField(max_length=255, required=True) + paymenttype = forms.CharField(max_length=255, required=True) enddate = forms.DateField(widget=forms.HiddenInput) - name = forms.CharField(max_length=255,required=False) - notes = forms.CharField(max_length=255,required=False) - status = forms.CharField(max_length=255,required=True) - tac= forms.BooleanField(required=True,initial=False) + name = forms.CharField(max_length=255, required=False) + notes = forms.CharField(max_length=255, required=False) + status = forms.CharField(max_length=255, required=True) + tac = forms.BooleanField(required=True, initial=False) # login form @@ -203,14 +226,15 @@ class LoginForm(forms.Form): password = forms.CharField(widget=forms.PasswordInput()) # search form + + class SearchForm(forms.Form): - q = forms.CharField(max_length=255,required=False, + q = forms.CharField(max_length=255, required=False, widget=forms.TextInput( attrs={'placeholder': 'keyword or leave empty'}), label='Filter by Keyword') - # simple form for Contact page. Sends email to info@rowsandall.com class EmailForm(forms.Form): firstname = forms.CharField(max_length=255) @@ -220,82 +244,96 @@ class EmailForm(forms.Form): message = forms.CharField(widget=forms.Textarea()) - disqualificationreasons = ( - ('noimage','No monitor screenshot or data evidence was included'), - ('suspicious','The result is not plausible for the boat class or athlete gorup'), - ('duplicate','Appears to be a duplicate entry'), - ('other','Other Reason'), + ('noimage', 'No monitor screenshot or data evidence was included'), + ('suspicious', 'The result is not plausible for the boat class or athlete gorup'), + ('duplicate', 'Appears to be a duplicate entry'), + ('other', 'Other Reason'), ) disqualifiers = {} for key, value in disqualificationreasons: disqualifiers[key] = value + class DisqualificationForm(forms.Form): reason = forms.ChoiceField(required=True, choices=disqualificationreasons, - widget = forms.RadioSelect,) + widget=forms.RadioSelect,) + + message = forms.CharField(required=True, widget=forms.Textarea) - message = forms.CharField(required=True,widget=forms.Textarea) class HistorySelectForm(forms.Form): - typeselectchoices = [("All","All")] - for wtype,verbose in mytypes.workouttypes_ordered.items(): - typeselectchoices.append((wtype,verbose)) + typeselectchoices = [("All", "All")] + for wtype, verbose in mytypes.workouttypes_ordered.items(): + typeselectchoices.append((wtype, verbose)) startdate = forms.DateField( initial=timezone.now()-datetime.timedelta(days=15), # widget=SelectDateWidget(years=range(1990,2050)), - widget=AdminDateWidget(), #format='%Y-%m-%d'), + widget=AdminDateWidget(), # format='%Y-%m-%d'), label='Start Date') enddate = forms.DateField( initial=timezone.now(), - widget=AdminDateWidget(), #format='%Y-%m-%d'), + widget=AdminDateWidget(), # format='%Y-%m-%d'), label='End Date') - workouttype = forms.ChoiceField(initial='All',choices=typeselectchoices) + workouttype = forms.ChoiceField(initial='All', choices=typeselectchoices) metricchoices = ( - ("time","duration"), - ("TRIMP","trimp"), - ("rScore","rscore"), - ("distance","distance") + ("time", "duration"), + ("TRIMP", "trimp"), + ("rScore", "rscore"), + ("distance", "distance") ) - yaxis = forms.ChoiceField(initial='time',choices=metricchoices,label="Measure by") + yaxis = forms.ChoiceField( + initial='time', choices=metricchoices, label="Measure by") class Meta: - fields = ['startdate','enddate'] - input_formats=("%Y-%m-%d") + fields = ['startdate', 'enddate'] + input_formats = ("%Y-%m-%d") dateTimeOptions = { 'format': '%Y-%m-%d', 'autoclose': True, - } + } + class MetricsForm(forms.Form): - avghr = forms.IntegerField(required=False,label='Average Heart Rate') - avgpwr = forms.IntegerField(required=False,label='Average Power') - avgspm = forms.FloatField(required=False,label='Average SPM') + avghr = forms.IntegerField(required=False, label='Average Heart Rate') + avgpwr = forms.IntegerField(required=False, label='Average Power') + avgspm = forms.FloatField(required=False, label='Average SPM') # Upload the CrewNerd Summary CSV + + class CNsummaryForm(forms.Form): - file = forms.FileField(required=True,validators=[must_be_csv]) + file = forms.FileField(required=True, validators=[must_be_csv]) # The little window to type '4x2000m/500m' to update the workout summary + + class SummaryStringForm(forms.Form): - intervalstring = forms.CharField(max_length=255,label='Workout Description') + intervalstring = forms.CharField( + max_length=255, label='Workout Description') # little window to type a Team invitation code + + class TeamInviteCodeForm(forms.Form): - code = forms.CharField(max_length=10,label='Team Code', + code = forms.CharField(max_length=10, label='Team Code', ) # Used for testing the POST API for StrokeData + + class StrokeDataForm(forms.Form): - strokedata = forms.CharField(label='payload',widget=forms.Textarea) + strokedata = forms.CharField(label='payload', widget=forms.Textarea) # The form used for uploading images + + class ImageForm(forms.Form): file = forms.FileField(required=False, validators=[validate_image_extension]) @@ -307,32 +345,35 @@ class ImageForm(forms.Form): # The form used for uploading images class CourseForm(forms.Form): - name = forms.CharField(max_length=150,label='Course Name',required=False) + name = forms.CharField(max_length=150, label='Course Name', required=False) file = forms.FileField(required=False, validators=[validate_kml]) notes = forms.CharField(required=False, - max_length=200,label='Course Notes', + max_length=200, label='Course Notes', widget=forms.Textarea) - country = forms.CharField(required=False,max_length=150,label='Country') + country = forms.CharField(required=False, max_length=150, label='Country') def __init__(self, *args, **kwargs): from django.forms.widgets import HiddenInput super(CourseForm, self).__init__(*args, **kwargs) + class CourseConfirmForm(forms.Form): BOOL_CHOICES = ((True, 'Yes'), (False, 'No')) doupdate = forms.TypedChoiceField( initial=False, - coerce=lambda x: x =='True', choices=((False, 'No'), (True, 'Yes')), widget=forms.RadioSelect, + coerce=lambda x: x == 'True', choices=((False, 'No'), (True, 'Yes')), widget=forms.RadioSelect, label='Update Course with new markers?') # The form used for uploading files + + class StandardsForm(forms.Form): - name = forms.CharField(max_length=150,label='Course Name') + name = forms.CharField(max_length=150, label='Course Name') file = forms.FileField(required=False, validators=[must_be_csv]) notes = forms.CharField(required=False, - max_length=200,label='Course Notes', + max_length=200, label='Course Notes', widget=forms.Textarea) def __init__(self, *args, **kwargs): @@ -340,9 +381,11 @@ class StandardsForm(forms.Form): super(StandardsForm, self).__init__(*args, **kwargs) # The form used for uploading files + + class DocumentsForm(forms.Form): rpechoices = Workout.rpechoices - rpechoices = tuple([(-1,'---')]+list(rpechoices)) + rpechoices = tuple([(-1, '---')]+list(rpechoices)) title = forms.CharField(required=False) file = forms.FileField(required=False, validators=[validate_file_extension]) @@ -353,31 +396,27 @@ class DocumentsForm(forms.Form): boattype = forms.ChoiceField(required=True, choices=mytypes.boattypes, - label = "Boat Type") + label="Boat Type") rpe = forms.ChoiceField(required=False, choices=rpechoices, - label='Rate of Perceived Exertion',initial=-1) + label='Rate of Perceived Exertion', initial=-1) notes = forms.CharField(required=False, widget=forms.Textarea) - offline = forms.BooleanField(initial=False,required=False, - label='Process in Background') + offline = forms.BooleanField(initial=False, required=False, + label='Process in Background') + class Meta: - fields = ['title','file','workouttype','boattype','fileformat','offline'] + fields = ['title', 'file', 'workouttype', + 'boattype', 'fileformat', 'offline'] def __init__(self, *args, **kwargs): from django.forms.widgets import HiddenInput super(DocumentsForm, self).__init__(*args, **kwargs) # self.fields['offline'].widget = HiddenInput() -from rowers.utils import ( - workflowleftpanel,workflowmiddlepanel, - defaultleft,defaultmiddle - ) - - # Form to change Workflow page layout class WorkFlowLeftPanelForm(forms.Form): @@ -395,28 +434,25 @@ class WorkFlowLeftPanelForm(forms.Form): class Media: css = { - 'all':['admin/css/widgets.css',] -# 'css/uid-manage-form.css'], - } + 'all': ['admin/css/widgets.css', ] + # 'css/uid-manage-form.css'], + } js = ['/admin/jsi18n/'] - - def __init__(self, *args, **kwargs): # pragma: no cover + def __init__(self, *args, **kwargs): # pragma: no cover if 'instance' in kwargs: r = kwargs.pop('instance') panels = r.workflowleftpanel - self.base_fields['leftpanel'].initial=panels + self.base_fields['leftpanel'].initial = panels else: panels = defaultleft - - super(WorkFlowLeftPanelForm,self).__init__(*args, **kwargs) + super(WorkFlowLeftPanelForm, self).__init__(*args, **kwargs) class WorkFlowMiddlePanelForm(forms.Form): panels = defaultmiddle - middlepanel = forms.MultipleChoiceField( label='', choices=workflowmiddlepanel, @@ -429,70 +465,74 @@ class WorkFlowMiddlePanelForm(forms.Form): class Media: css = { - 'all':['admin/css/widgets.css',] -# 'css/uid-manage-form.css'], - } + 'all': ['admin/css/widgets.css', ] + # 'css/uid-manage-form.css'], + } js = ['/admin/jsi18n/'] - def __init__(self, *args, **kwargs): # pragma: no cover + def __init__(self, *args, **kwargs): # pragma: no cover if 'instance' in kwargs: r = kwargs.pop('instance') panels = r.workflowmiddlepanel - self.base_fields['middlepanel'].initial=panels + self.base_fields['middlepanel'].initial = panels else: panels = defaultmiddle - super(WorkFlowMiddlePanelForm,self).__init__(*args, **kwargs) + super(WorkFlowMiddlePanelForm, self).__init__(*args, **kwargs) + class WorkFlowLeftPanelElement(forms.Form): - panelchoices = tuple(list(workflowleftpanel)+[('None','None')]) + panelchoices = tuple(list(workflowleftpanel)+[('None', 'None')]) panel = forms.ChoiceField( label='', choices=panelchoices, initial='None', - ) + ) + class WorkFlowMiddlePanelElement(forms.Form): - panelchoices = tuple(list(workflowmiddlepanel)+[('None','None')]) + panelchoices = tuple(list(workflowmiddlepanel)+[('None', 'None')]) panel = forms.ChoiceField( label='', choices=panelchoices, initial='None', - ) + ) # The form to indicate additional actions to be performed immediately # after a successful upload nextpages = list(landingpages) -nextpages.append(('workout_upload_view','Upload Another File')) +nextpages.append(('workout_upload_view', 'Upload Another File')) nextpages = tuple(nextpages) + class LandingPageForm(forms.Form): landingpage = forms.ChoiceField(choices=nextpages, initial='workout_edit_view', label='After Upload, go to') + class UploadOptionsForm(forms.Form): plotchoices = ( - ('timeplot','Time Plot'), - ('distanceplot','Distance Plot'), - ('pieplot','Heart Rate Pie Chart'), - ) - make_plot = forms.BooleanField(initial=False,required=False) + ('timeplot', 'Time Plot'), + ('distanceplot', 'Distance Plot'), + ('pieplot', 'Heart Rate Pie Chart'), + ) + make_plot = forms.BooleanField(initial=False, required=False) plottype = forms.ChoiceField(required=False, choices=plotchoices, initial='timeplot', label='Plot Type') - upload_to_C2 = forms.BooleanField(initial=False,required=False, + upload_to_C2 = forms.BooleanField(initial=False, required=False, label='Export to Concept2 logbook') - upload_to_Strava = forms.BooleanField(initial=False,required=False, + upload_to_Strava = forms.BooleanField(initial=False, required=False, label='Export to Strava') - upload_to_SportTracks = forms.BooleanField(initial=False,required=False, + upload_to_SportTracks = forms.BooleanField(initial=False, required=False, label='Export to SportTracks') - upload_to_RunKeeper = forms.BooleanField(initial=False,required=False, + upload_to_RunKeeper = forms.BooleanField(initial=False, required=False, label='Export to RunKeeper') upload_to_MapMyFitness = forms.BooleanField(initial=False, required=False, @@ -501,71 +541,73 @@ class UploadOptionsForm(forms.Form): required=False, label='Export to TrainingPeaks') # do_physics = forms.BooleanField(initial=False,required=False,label='Power Estimate (OTW)') - makeprivate = forms.BooleanField(initial=False,required=False, + makeprivate = forms.BooleanField(initial=False, required=False, label='Make Workout Private') submitrace = forms.ChoiceField( - label='Submit as challenge Result', - required=False) + label='Submit as challenge Result', + required=False) landingpage = forms.ChoiceField(choices=nextpages, initial='workout_edit_view', label='After Upload, go to') - raceid = forms.IntegerField(initial=0,widget=HiddenInput()) + raceid = forms.IntegerField(initial=0, widget=HiddenInput()) class Meta: - fields = ['make_plot','plottype','upload_toc2','makeprivate'] + fields = ['make_plot', 'plottype', 'upload_toc2', 'makeprivate'] def __init__(self, *args, **kwargs): - self.request = kwargs.pop('request',None) - raceid = kwargs.pop('raceid',0) + self.request = kwargs.pop('request', None) + raceid = kwargs.pop('raceid', 0) super(UploadOptionsForm, self).__init__(*args, **kwargs) r = Rower.objects.get(user=self.request.user) races = VirtualRace.objects.filter( registration_closure__gt=timezone.now()) registrations = IndoorVirtualRaceResult.objects.filter( - race__in = races, - userid = r.id) + race__in=races, + userid=r.id) registrations2 = VirtualRaceResult.objects.filter( - race__in = races, - userid = r.id, + race__in=races, + userid=r.id, ) - choices1 = [(r.id,str(r)) for r in registrations] - choices2 = [(r.id,str(r)) for r in registrations2] - choices3 = [(0,'---')] + choices1 = [(r.id, str(r)) for r in registrations] + choices2 = [(r.id, str(r)) for r in registrations2] + choices3 = [(0, '---')] noregistrations = [] - for ra in VirtualRace.objects.filter(registration_closure__gt=timezone.now(),sessiontype='race'): # pragma: no cover - rs = VirtualRaceResult.objects.filter(race = ra,userid=r.id) - if rs.count()==0: - noregistrations.append((-ra.id,ra.name)) - for ra in VirtualRace.objects.filter(registration_closure__gt=timezone.now(),sessiontype='indoorrace'): # pragma: no cover - rs = IndoorVirtualRaceResult.objects.filter(race = ra,userid=r.id) - if rs.count()==0: - noregistrations.append((-ra.id,ra.name)) + for ra in VirtualRace.objects.filter(registration_closure__gt=timezone.now(), sessiontype='race'): # pragma: no cover + rs = VirtualRaceResult.objects.filter(race=ra, userid=r.id) + if rs.count() == 0: + noregistrations.append((-ra.id, ra.name)) + for ra in VirtualRace.objects.filter(registration_closure__gt=timezone.now(), sessiontype='indoorrace'): # pragma: no cover + rs = IndoorVirtualRaceResult.objects.filter(race=ra, userid=r.id) + if rs.count() == 0: + noregistrations.append((-ra.id, ra.name)) choices = choices3+choices1+choices2+noregistrations - if int(raceid) in [r.id for r in races]: # pragma: no cover + if int(raceid) in [r.id for r in races]: # pragma: no cover therace = VirtualRace.objects.get(id=raceid) self.fields['raceid'].initial = therace.id if therace.sessiontype == 'race': - registrations = VirtualRaceResult.objects.filter(race=therace,userid=r.id) + registrations = VirtualRaceResult.objects.filter( + race=therace, userid=r.id) else: - registrations = IndoorVirtualRaceResult.objects.filter(race=therace,userid=r.id) + registrations = IndoorVirtualRaceResult.objects.filter( + race=therace, userid=r.id) - if registrations.count()==0: + if registrations.count() == 0: race = VirtualRace.objects.get(id=raceid) - choices = [(-int(raceid),race.name)] + choices = [(-int(raceid), race.name)] else: - choices = [(r.id,str(r)) for r in registrations] - choices = choices+[(0,'---')] + choices = [(r.id, str(r)) for r in registrations] + choices = choices+[(0, '---')] - if races: # pragma: no cover + if races: # pragma: no cover self.fields['submitrace'].choices = choices else: del self.fields['submitrace'] @@ -576,23 +618,23 @@ class UploadOptionsForm(forms.Form): # a team member class TeamUploadOptionsForm(forms.Form): plotchoices = ( - ('timeplot','Time Plot'), - ('distanceplot','Distance Plot'), - ('pieplot','Pie Chart'), - ) - make_plot = forms.BooleanField(initial=False,required=False) + ('timeplot', 'Time Plot'), + ('distanceplot', 'Distance Plot'), + ('pieplot', 'Pie Chart'), + ) + make_plot = forms.BooleanField(initial=False, required=False) plottype = forms.ChoiceField(required=False, choices=plotchoices, initial='timeplot', label='Plot Type') - upload_to_C2 = forms.BooleanField(initial=False,required=False, + upload_to_C2 = forms.BooleanField(initial=False, required=False, label='Export to Concept2 logbook') - upload_to_Strava = forms.BooleanField(initial=False,required=False, + upload_to_Strava = forms.BooleanField(initial=False, required=False, label='Export to Strava') - upload_to_SportTracks = forms.BooleanField(initial=False,required=False, + upload_to_SportTracks = forms.BooleanField(initial=False, required=False, label='Export to SportTracks') - upload_to_RunKeeper = forms.BooleanField(initial=False,required=False, + upload_to_RunKeeper = forms.BooleanField(initial=False, required=False, label='Export to RunKeeper') upload_to_MapMyFitness = forms.BooleanField(initial=False, required=False, @@ -601,32 +643,30 @@ class TeamUploadOptionsForm(forms.Form): required=False, label='Export to TrainingPeaks') # do_physics = forms.BooleanField(initial=False,required=False,label='Power Estimate (OTW)') - makeprivate = forms.BooleanField(initial=False,required=False, + makeprivate = forms.BooleanField(initial=False, required=False, label='Make Workout Private') - - class Meta: - fields = ['make_plot','plottype'] + fields = ['make_plot', 'plottype'] # This form is used on the Workout Split page class WorkoutSplitForm(forms.Form): splitchoices = ( - ('keep original','Keep Original'), - ('keep first','Keep First Part'), - ('keep second','Keep Second Part'), - ('firstprivate','Set First Part Private'), - ('secondprivate','Set Second Part Private'), - ('originalprivate','Set Original Private'), - ) + ('keep original', 'Keep Original'), + ('keep first', 'Keep First Part'), + ('keep second', 'Keep Second Part'), + ('firstprivate', 'Set First Part Private'), + ('secondprivate', 'Set Second Part Private'), + ('originalprivate', 'Set Original Private'), + ) splittime = forms.TimeField(input_formats=['%H:%M:%S.%f', '%H:%M:%S', '%H:%M:%S', '%M:%S.%f', '%M:%S', '%M'], - label = 'Split Time') + label='Split Time') splitmode = forms.MultipleChoiceField( initial=['keep original', 'keep first', @@ -635,62 +675,65 @@ class WorkoutSplitForm(forms.Form): 'secondprivate'], label='Split Mode', choices=splitchoices, - widget = forms.CheckboxSelectMultiple()) + widget=forms.CheckboxSelectMultiple()) + # This form is used on the Analysis page to add a custom distance/time # trial and predict the pace -from rowers.utils import rankingdistances,rankingdurations -from time import strftime + + class OteWorkoutTypeForm(forms.Form): choices = ( - ('rower','Indoor Rower'), - ('dynamic','Dynamic Indoor Rower'), - ('slides','Indoor Rower on Slides'), + ('rower', 'Indoor Rower'), + ('dynamic', 'Dynamic Indoor Rower'), + ('slides', 'Indoor Rower on Slides'), ) workouttypes = forms.MultipleChoiceField( required=True, choices=choices, label='Workout Types', - initial = [a for a,b in choices], - ) + initial=[a for a, b in choices], + ) + class PredictedPieceForm(forms.Form): unitchoices = ( - ('t','minutes'), - ('d','meters'), - ) + ('t', 'minutes'), + ('d', 'meters'), + ) rankingdistancechoices = [] rankingdurationchoices = [] for d in rankingdistances: - thetuple = (d,str(d)+' m') + thetuple = (d, str(d)+' m') rankingdistancechoices.append(thetuple) for d in rankingdurations: timestr = d.strftime("%H:%M:%S") - thetuple = (timestr,timestr) + thetuple = (timestr, timestr) rankingdurationchoices.append(thetuple) trankingdistances = forms.MultipleChoiceField( required=True, - choices=rankingdistancechoices,initial=rankingdistances, + choices=rankingdistancechoices, initial=rankingdistances, label='Ranking Distances' ) trankingdurations = forms.MultipleChoiceField( required=True, choices=rankingdurationchoices, - initial=[a for a,b in rankingdurationchoices], + initial=[a for a, b in rankingdurationchoices], label='Ranking Durations' ) - value = forms.FloatField(initial=10,label='Free ranking piece (minutes)') - pieceunit = forms.ChoiceField(required=True,choices=unitchoices, - initial='t',label='Unit') + value = forms.FloatField(initial=10, label='Free ranking piece (minutes)') + pieceunit = forms.ChoiceField(required=True, choices=unitchoices, + initial='t', label='Unit') class Meta: - fields = ['value','pieceunit'] + fields = ['value', 'pieceunit'] + class PredictedPieceFormNoDistance(forms.Form): @@ -698,87 +741,91 @@ class PredictedPieceFormNoDistance(forms.Form): for d in rankingdurations: timestr = d.strftime("%H:%M:%S") - thetuple = (timestr,timestr) + thetuple = (timestr, timestr) rankingdurationchoices.append(thetuple) - trankingdurations = forms.MultipleChoiceField( required=True, choices=rankingdurationchoices, - initial=[a for a,b in rankingdurationchoices], + initial=[a for a, b in rankingdurationchoices], label='Ranking Durations' ) - value = forms.FloatField(initial=10,label='Free ranking piece') + value = forms.FloatField(initial=10, label='Free ranking piece') # On the Geeky side, to update stream information for river dwellers class UpdateStreamForm(forms.Form): unitchoices = ( - ('m','m/s'), - ('f','foot/s'), - ('k','knots'), - ('p','pace difference (sec/500m)'), - ) - dist1 = forms.FloatField(initial=0,label = 'Distance 1') - dist2 = forms.FloatField(initial=1000,label = 'Distance 2') - stream1 = forms.FloatField(initial=0,label = 'Stream velocity 1') - stream2 = forms.FloatField(initial=0,label = 'Stream velocity 2') + ('m', 'm/s'), + ('f', 'foot/s'), + ('k', 'knots'), + ('p', 'pace difference (sec/500m)'), + ) + dist1 = forms.FloatField(initial=0, label='Distance 1') + dist2 = forms.FloatField(initial=1000, label='Distance 2') + stream1 = forms.FloatField(initial=0, label='Stream velocity 1') + stream2 = forms.FloatField(initial=0, label='Stream velocity 2') streamunit = forms.ChoiceField(required=True, choices=unitchoices, initial='m', label='Unit') class Meta: - fields = ['dist1','dist2','stream1', 'stream2','streamunit'] + fields = ['dist1', 'dist2', 'stream1', 'stream2', 'streamunit'] # add wind information to your workout + + class UpdateWindForm(forms.Form): unitchoices = ( - ('m','m/s'), - ('k','knots'), - ('b','beaufort'), - ('kmh','km/h'), - ('mph','miles/hour'), - ) - dist1 = forms.FloatField(initial=0,label = 'Distance 1') - dist2 = forms.FloatField(initial=1000,label = 'Distance 2') - vwind1 = forms.FloatField(initial=0,required=False,label = 'Wind Speed 1') - vwind2 = forms.FloatField(initial=0,required=False,label = 'Wind Speed 2') + ('m', 'm/s'), + ('k', 'knots'), + ('b', 'beaufort'), + ('kmh', 'km/h'), + ('mph', 'miles/hour'), + ) + dist1 = forms.FloatField(initial=0, label='Distance 1') + dist2 = forms.FloatField(initial=1000, label='Distance 2') + vwind1 = forms.FloatField(initial=0, required=False, label='Wind Speed 1') + vwind2 = forms.FloatField(initial=0, required=False, label='Wind Speed 2') windunit = forms.ChoiceField(required=True, choices=unitchoices, initial='m', label='Unit') - winddirection1 = forms.IntegerField(initial=0,required=False, - label = 'Wind Direction 1') - winddirection2 = forms.IntegerField(initial=0,required=False, - label = 'Wind Direction 2') + winddirection1 = forms.IntegerField(initial=0, required=False, + label='Wind Direction 1') + winddirection2 = forms.IntegerField(initial=0, required=False, + label='Wind Direction 2') class Meta: - fields = ['dist1','dist2', - 'vwind1','vwind2', + fields = ['dist1', 'dist2', + 'vwind1', 'vwind2', 'windunit', - 'winddirection1','winddirection2'] + 'winddirection1', 'winddirection2'] # Form to select a data range to show workouts from a certain time period + + class DateRangeForm(forms.Form): startdate = forms.DateField( initial=timezone.now()-datetime.timedelta(days=365), # widget=SelectDateWidget(years=range(1990,2050)), - widget=AdminDateWidget(), #format='%Y-%m-%d'), + widget=AdminDateWidget(), # format='%Y-%m-%d'), label='Start Date') enddate = forms.DateField( initial=timezone.now(), - widget=AdminDateWidget(), #format='%Y-%m-%d'), + widget=AdminDateWidget(), # format='%Y-%m-%d'), label='End Date') class Meta: - fields = ['startdate','enddate'] - input_formats=("%Y-%m-%d") + fields = ['startdate', 'enddate'] + input_formats = ("%Y-%m-%d") dateTimeOptions = { 'format': '%Y-%m-%d', 'autoclose': True, - } + } + class FitnessMetricForm(forms.Form): startdate = forms.DateField( @@ -792,9 +839,9 @@ class FitnessMetricForm(forms.Form): label='End Date') modechoices = ( - ('rower','indoor rower'), - ('water','on the water') - ) + ('rower', 'indoor rower'), + ('water', 'on the water') + ) mode = forms.ChoiceField(required=True, choices=modechoices, @@ -803,7 +850,8 @@ class FitnessMetricForm(forms.Form): ) class Meta: - fields = ['startdate','enddate','mode'] + fields = ['startdate', 'enddate', 'mode'] + class PerformanceManagerForm(forms.Form): startdate = forms.DateField( @@ -817,8 +865,8 @@ class PerformanceManagerForm(forms.Form): label='End Date') metricchoices = ( - ('hrtss','Use Heart Rate Data'), - ('rscore','Use Power and Heart Rate Data'), + ('hrtss', 'Use Heart Rate Data'), + ('rscore', 'Use Power and Heart Rate Data'), ) metricchoice = forms.ChoiceField( @@ -829,10 +877,10 @@ class PerformanceManagerForm(forms.Form): widget=forms.RadioSelect ) - dofatigue = forms.BooleanField(required=False,initial=False, + dofatigue = forms.BooleanField(required=False, initial=False, label='Fatigue') - doform = forms.BooleanField(required=False,initial=False, + doform = forms.BooleanField(required=False, initial=False, label='Freshness') @@ -848,48 +896,49 @@ class FitnessFitForm(forms.Form): label='End Date') modechoices = ( - ('rower','indoor rower'), - ('water','on the water') - ) + ('rower', 'indoor rower'), + ('water', 'on the water') + ) metricchoices = ( - ('trimp','TRIMP'), - ('rscore','rScore') + ('trimp', 'TRIMP'), + ('rscore', 'rScore') ) modelchoices = ( - ('banister','Banister Impulse-Response model'), - ('tsb','Coggan Training Stress Balance model'), + ('banister', 'Banister Impulse-Response model'), + ('tsb', 'Coggan Training Stress Balance model'), ) - fitnesstest = forms.IntegerField(required=True,initial=20, + fitnesstest = forms.IntegerField(required=True, initial=20, label='Test Duration (minutes)') - usegoldmedalstandard = forms.BooleanField(required=False,initial=False, - label='Use best performance against world class') + usegoldmedalstandard = forms.BooleanField(required=False, initial=False, + label='Use best performance against world class') - kfitness = forms.IntegerField(initial=42,required=True, - label='Fitness Time Constant (days)') + kfitness = forms.IntegerField(initial=42, required=True, + label='Fitness Time Constant (days)') - kfatigue = forms.IntegerField(initial=7,required=True, - label='Fatigue Time Constant (days)') + kfatigue = forms.IntegerField(initial=7, required=True, + label='Fatigue Time Constant (days)') metricchoice = forms.ChoiceField(required=True, - choices=metricchoices, - initial='rscore', - label='Workload Metric') + choices=metricchoices, + initial='rscore', + label='Workload Metric') modelchoice = forms.ChoiceField(required=True, - choices=modelchoices, - initial='tsb', - label='Model to use') + choices=modelchoices, + initial='tsb', + label='Model to use') # temporary - k1 = forms.FloatField(required=True,initial=1.0, + k1 = forms.FloatField(required=True, initial=1.0, label='k1') - k2 = forms.FloatField(required=True,initial=1.0, + k2 = forms.FloatField(required=True, initial=1.0, label='k2') - p0 = forms.IntegerField(required=True,initial=100,label='Unfit Performance') + p0 = forms.IntegerField(required=True, initial=100, + label='Unfit Performance') mode = forms.ChoiceField(required=True, choices=modechoices, @@ -898,9 +947,10 @@ class FitnessFitForm(forms.Form): ) class Meta: - fields = ['startdate','enddate','mode','fitnesstest', - 'kfitness','kfatigue','metricchoice', - 'k1','k2','p0'] + fields = ['startdate', 'enddate', 'mode', 'fitnesstest', + 'kfitness', 'kfatigue', 'metricchoice', + 'k1', 'k2', 'p0'] + class SessionDateShiftForm(forms.Form): shiftstartdate = forms.DateField( @@ -912,8 +962,11 @@ class SessionDateShiftForm(forms.Form): fields = ['shiftstartdate'] # Form used to select workouts for the past N days + + class DeltaDaysForm(forms.Form): - deltadays = forms.IntegerField(initial=7,required=False,label='') + deltadays = forms.IntegerField(initial=7, required=False, label='') + class RegistrationForm(UserCreationForm): """ @@ -930,13 +983,15 @@ class RegistrationForm(UserCreationForm): class Meta: model = User - fields = ("username", "first_name", "last_name", "email", "password1", "password2") + fields = ("username", "first_name", "last_name", + "email", "password1", "password2") widgets = { - 'username': forms.TextInput(attrs={'autocomplete':'new-password'}), - 'password1': forms.PasswordInput(attrs={'autocomplete':'new-password'}), - 'password2': forms.PasswordInput(attrs={'autocomplete':'new-password'}), - } + 'username': forms.TextInput(attrs={'autocomplete': 'new-password'}), + 'password1': forms.PasswordInput(attrs={'autocomplete': 'new-password'}), + 'password2': forms.PasswordInput(attrs={'autocomplete': 'new-password'}), + } + class RegistrationFormTermsOfService(RegistrationForm): """ @@ -948,31 +1003,33 @@ class RegistrationFormTermsOfService(RegistrationForm): error_messages={'required': "You must agree to the terms to register"}) - class RegistrationFormUniqueEmail(RegistrationFormTermsOfService): """ Subclass of ``RegistrationFormTermsOfService`` which enforces uniqueness of email addresses. """ + def clean_email(self): """ Validate that the supplied email address is unique for the site. """ - if User.objects.filter(email__iexact=self.cleaned_data['email']): # pragma: no cover - raise forms.ValidationError("This email address is already in use. Please supply a different email address.") + if User.objects.filter(email__iexact=self.cleaned_data['email']): # pragma: no cover + raise forms.ValidationError( + "This email address is already in use. Please supply a different email address.") return self.cleaned_data['email'] + class RegistrationFormSex(RegistrationFormUniqueEmail): sexcategories = ( - ('female','female'), - ('male','male'), - ('not specified','not specified'), - ) + ('female', 'female'), + ('male', 'male'), + ('not specified', 'not specified'), + ) weightcategories = ( - ('hwt','heavy-weight'), - ('lwt','light-weight'), + ('hwt', 'heavy-weight'), + ('lwt', 'light-weight'), ) adaptivecategories = mytypes.adaptivetypes @@ -980,16 +1037,17 @@ class RegistrationFormSex(RegistrationFormUniqueEmail): thisyear = timezone.now().year birthdate = forms.DateTimeField( - widget=SelectDateWidget(years=range(1900,thisyear)), - initial = datetime.date(year=1970, - month=4, - day=15)) + widget=SelectDateWidget(years=range(1900, thisyear)), + initial=datetime.date(year=1970, + month=4, + day=15)) def clean_birthdate(self): dob = self.cleaned_data['birthdate'] age = (timezone.now() - dob).days/365 - if age < 16: # pragma: no cover - raise forms.ValidationError('Must be at least 16 years old to register') + if age < 16: # pragma: no cover + raise forms.ValidationError( + 'Must be at least 16 years old to register') return self.cleaned_data['birthdate'] sex = forms.ChoiceField(required=False, @@ -998,66 +1056,76 @@ class RegistrationFormSex(RegistrationFormUniqueEmail): label='Sex') weightcategory = forms.ChoiceField(label='Weight Category', - choices=weightcategories,initial='hwt',required=False) + choices=weightcategories, initial='hwt', required=False) adaptiveclass = forms.ChoiceField(label='Adaptive Classification', - choices=adaptivecategories,initial='None',required=False) + choices=adaptivecategories, initial='None', required=False) # def __init__(self, *args, **kwargs): # self.fields['sex'].initial = 'not specified' # Time field supporting microseconds. Not used, I believe. + + class MyTimeField(forms.TimeField): - def __init__(self, *args, **kwargs): # pragma: no cover + def __init__(self, *args, **kwargs): # pragma: no cover super(MyTimeField, self).__init__(*args, **kwargs) supports_microseconds = True # Form used to automatically define intervals by pace or power + + class PowerIntervalUpdateForm(forms.Form): selectorchoices = ( - ('power','Power'), - ('pace','Pace'), - ('work','Work per Stroke'), - ('spm','Stroke Rate') - ) + ('power', 'Power'), + ('pace', 'Pace'), + ('work', 'Work per Stroke'), + ('spm', 'Stroke Rate') + ) - pace = forms.DurationField(required=False,label='Pace (/500m)') - power = forms.IntegerField(required=False,label='Power (W)') - work = forms.IntegerField(required=False,label='Work per Stroke (J)') - spm = forms.IntegerField(required=False,label='Stroke Rate') + pace = forms.DurationField(required=False, label='Pace (/500m)') + power = forms.IntegerField(required=False, label='Power (W)') + work = forms.IntegerField(required=False, label='Work per Stroke (J)') + spm = forms.IntegerField(required=False, label='Stroke Rate') selector = forms.ChoiceField(choices=selectorchoices, required=True, initial='power', label='Use') - activeminutesmin = forms.IntegerField(required=False,initial=0,widget=forms.HiddenInput()) - activeminutesmax = forms.IntegerField(required=False,initial=0,widget=forms.HiddenInput()) + activeminutesmin = forms.IntegerField( + required=False, initial=0, widget=forms.HiddenInput()) + activeminutesmax = forms.IntegerField( + required=False, initial=0, widget=forms.HiddenInput()) boattypes = mytypes.boattypes workouttypes = mytypes.workouttypes ww = list(workouttypes) -ww.append(tuple(('all','All'))) +ww.append(tuple(('all', 'All'))) workouttypes = tuple(ww) + class DataFrameColumnsForm(forms.Form): - cols = ['ftime','cumdist','fpace','spm', - 'hr','power','driveenergy','drivelength','averageforce', - 'peakforce','distance','drivespeed','workoutstate', - 'catch','finish','peakforceangle','wash','slip','rhythm', - 'effectiveangle','totalangle','distanceperstroke','velo'] + cols = ['ftime', 'cumdist', 'fpace', 'spm', + 'hr', 'power', 'driveenergy', 'drivelength', 'averageforce', + 'peakforce', 'distance', 'drivespeed', 'workoutstate', + 'catch', 'finish', 'peakforceangle', 'wash', 'slip', 'rhythm', + 'effectiveangle', 'totalangle', 'distanceperstroke', 'velo'] colchoices = [ (c, c) for c in cols - ] + ] cols = forms.MultipleChoiceField(choices=colchoices, label='Table Columns') + class HistoForm(forms.Form): - includereststrokes = forms.BooleanField(initial=False,label='Include Rest Strokes',required=False) - histoparam = forms.ChoiceField(choices=parchoices,initial='power', - label='Metric') + includereststrokes = forms.BooleanField( + initial=False, label='Include Rest Strokes', required=False) + histoparam = forms.ChoiceField(choices=parchoices, initial='power', + label='Metric') + class AnalysisOptionsForm(forms.Form): modality = forms.ChoiceField(choices=workouttypes, @@ -1065,8 +1133,7 @@ class AnalysisOptionsForm(forms.Form): initial='all') waterboattype = forms.MultipleChoiceField(choices=boattypes, label='Water Boat Type', - initial = mytypes.waterboattype) - + initial=mytypes.waterboattype) # form to select modality and boat type for trend flex @@ -1076,37 +1143,36 @@ class TrendFlexModalForm(forms.Form): initial='all') waterboattype = forms.MultipleChoiceField(choices=boattypes, label='Water Boat Type', - initial = mytypes.waterboattype) - - + initial=mytypes.waterboattype) # This form sets options for the summary stats page class StatsOptionsForm(forms.Form): - includereststrokes = forms.BooleanField(initial=False,label='Include Rest Strokes',required=False) + includereststrokes = forms.BooleanField( + initial=False, label='Include Rest Strokes', required=False) - water = forms.BooleanField(initial=False,required=False) + water = forms.BooleanField(initial=False, required=False) waterboattype = forms.MultipleChoiceField(choices=boattypes, label='Water Boat Type', widget=forms.CheckboxSelectMultiple(), - initial = mytypes.waterboattype) + initial=mytypes.waterboattype) - - - def __init__(self, *args, **kwargs): # pragma: no cover - super(StatsOptionsForm, self).__init__(*args,**kwargs) + def __init__(self, *args, **kwargs): # pragma: no cover + super(StatsOptionsForm, self).__init__(*args, **kwargs) for type in mytypes.checktypes: - self.fields[type] = forms.BooleanField(initial=True,required=False) + self.fields[type] = forms.BooleanField( + initial=True, required=False) + class PlanSelectForm(forms.Form): plan = forms.ModelChoiceField(queryset=PaidPlan.objects.all(), - widget=forms.RadioSelect,required=True) + widget=forms.RadioSelect, required=True) def __init__(self, *args, **kwargs): - paymentprocessor = kwargs.pop('paymentprocessor',None) - rower = kwargs.pop('rower',None) - includeall = kwargs.pop('includeall',False) + paymentprocessor = kwargs.pop('paymentprocessor', None) + rower = kwargs.pop('rower', None) + includeall = kwargs.pop('includeall', False) super(PlanSelectForm, self).__init__(*args, **kwargs) self.fields['plan'].empty_label = None if paymentprocessor: @@ -1116,7 +1182,7 @@ class PlanSelectForm(forms.Form): ).exclude( shortname="basic" ).order_by( - "price","shortname" + "price", "shortname" ) if rower and not includeall: try: @@ -1126,45 +1192,51 @@ class PlanSelectForm(forms.Form): self.fields['plan'].queryset = PaidPlan.objects.filter( paymentprocessor=rower.paymentprocessor, active=True - ).exclude( - price__lte=amount - ).order_by( - "price","shortname" - ) + ).exclude( + price__lte=amount + ).order_by( + "price", "shortname" + ) + class CourseSelectForm(forms.Form): course = forms.ModelChoiceField(queryset=GeoCourse.objects.filter()) - def __init__(self, *args, **kwargs): # pragma: no cover - course = kwargs.pop('course',None) - manager = kwargs.pop('manager',None) - choices = kwargs.pop('choices',[]) - super(CourseSelectForm,self).__init__(*args,**kwargs) - if len(choices)>0: - self.fields['course'].queryset = GeoCourse.objects.filter(id__in=[c.id for c in choices]) + def __init__(self, *args, **kwargs): # pragma: no cover + course = kwargs.pop('course', None) + manager = kwargs.pop('manager', None) + choices = kwargs.pop('choices', []) + super(CourseSelectForm, self).__init__(*args, **kwargs) + if len(choices) > 0: + self.fields['course'].queryset = GeoCourse.objects.filter( + id__in=[c.id for c in choices]) if course is not None: d_min = 0.5*course.distance d_max = 2*course.distance country = course.country - countries = ['unknown',country] + countries = ['unknown', country] self.fields['course'].queryset = self.fields['course'].queryset.filter( - distance__gt = d_min,distance__lt = d_max, - country__in = countries + distance__gt=d_min, distance__lt=d_max, + country__in=countries ).exclude(id=course.id) if manager is not None: - self.fields['course'].queryset = self.fields['course'].queryset.filter(manager=manager) + self.fields['course'].queryset = self.fields['course'].queryset.filter( + manager=manager) + class WorkoutSingleSelectForm(forms.Form): workout = forms.ModelChoiceField( queryset=Workout.objects.filter(), widget=forms.RadioSelect - ) + ) def __init__(self, *args, **kwargs): - workouts = kwargs.pop('workouts',Workout.objects.filter().order_by('-date')) - super(WorkoutSingleSelectForm,self).__init__(*args,**kwargs) - self.fields['workout'].queryset = workouts + workouts = kwargs.pop( + 'workouts', Workout.objects.filter().order_by('-date')) + super(WorkoutSingleSelectForm, self).__init__(*args, **kwargs) + self.fields['workout'].queryset = workouts + class WorkoutMultipleCompareForm(forms.Form): workouts = forms.ModelMultipleChoiceField( @@ -1172,8 +1244,8 @@ class WorkoutMultipleCompareForm(forms.Form): widget=forms.CheckboxSelectMultiple()) def __init__(self, *args, **kwargs): - super(WorkoutMultipleCompareForm,self).__init__(*args,**kwargs) - self.fields['workouts'].queryset = Workout.objects.filter() + super(WorkoutMultipleCompareForm, self).__init__(*args, **kwargs) + self.fields['workouts'].queryset = Workout.objects.filter() class PlannedSessionMultipleCloneForm(forms.Form): @@ -1181,7 +1253,7 @@ class PlannedSessionMultipleCloneForm(forms.Form): queryset=PlannedSession.objects.all(), widget=forms.CheckboxSelectMultiple(), label='Planned Sessions' - ) + ) grouplabels = axlabels.copy() @@ -1189,64 +1261,63 @@ grouplabels['date'] = 'Date' grouplabels['workoutid'] = 'Workout' grouplabels.pop('None') grouplabels.pop('time') -groupchoices = list(sorted(grouplabels.items(), key = lambda x:x[1])) +groupchoices = list(sorted(grouplabels.items(), key=lambda x: x[1])) formaxlabelsmultiflex = formaxlabels.copy() formaxlabelsmultiflex.pop('time') formaxlabelsmultiflex.pop('distance') formaxlabelsmultiflex['workoutid'] = 'Workout' -parchoicesmultiflex = list(sorted(formaxlabelsmultiflex.items(), key = lambda x:x[1])) +parchoicesmultiflex = list( + sorted(formaxlabelsmultiflex.items(), key=lambda x: x[1])) -from rowers.utils import palettes -palettechoices = tuple((p,p) for p in palettes.keys()) +palettechoices = tuple((p, p) for p in palettes.keys()) analysischoices = ( - ('boxplot','Box Chart'), - ('trendflex','Trend Flex'), - ('histo','Histogram'), - ('flexall','Cumulative Flex Chart'), - ('stats','Statistics'), - ('compare','Compare'), - ('cp','CP chart'), - ) - + ('boxplot', 'Box Chart'), + ('trendflex', 'Trend Flex'), + ('histo', 'Histogram'), + ('flexall', 'Cumulative Flex Chart'), + ('stats', 'Statistics'), + ('compare', 'Compare'), + ('cp', 'CP chart'), +) class AnalysisChoiceForm(forms.Form): axchoices = list( - (ax[0],ax[1]) for ax in axes if ax[0] not in ['cumdist','None'] - ) - axchoices = dict((x,y) for x,y in axchoices) - axchoices = list(sorted(axchoices.items(), key = lambda x:x[1])) + (ax[0], ax[1]) for ax in axes if ax[0] not in ['cumdist', 'None'] + ) + axchoices = dict((x, y) for x, y in axchoices) + axchoices = list(sorted(axchoices.items(), key=lambda x: x[1])) - - yaxchoices = list((ax[0],ax[1]) for ax in axes if ax[0] not in ['cumdist','distance','time']) - yaxchoices = dict((x,y) for x,y in yaxchoices) - yaxchoices = list(sorted(yaxchoices.items(), key = lambda x:x[1])) + yaxchoices = list((ax[0], ax[1]) for ax in axes if ax[0] + not in ['cumdist', 'distance', 'time']) + yaxchoices = dict((x, y) for x, y in yaxchoices) + yaxchoices = list(sorted(yaxchoices.items(), key=lambda x: x[1])) plotchoices = ( - ('line','Line Plot'), - ('scatter','Scatter Plot'), - ) + ('line', 'Line Plot'), + ('scatter', 'Scatter Plot'), + ) yaxchoices2 = list( - (ax[0],ax[1]) for ax in axes if ax[0] not in ['cumdist','distance','time'] - ) - yaxchoices2 = dict((x,y) for x,y in yaxchoices2) - yaxchoices2 = list(sorted(yaxchoices2.items(), key = lambda x:x[1])) + (ax[0], ax[1]) for ax in axes if ax[0] not in ['cumdist', 'distance', 'time'] + ) + yaxchoices2 = dict((x, y) for x, y in yaxchoices2) + yaxchoices2 = list(sorted(yaxchoices2.items(), key=lambda x: x[1])) - function = forms.ChoiceField(choices=analysischoices,initial='boxplot', + function = forms.ChoiceField(choices=analysischoices, initial='boxplot', label='Analysis') xaxis = forms.ChoiceField( - choices=axchoices,label='X-Axis',required=True,initial='spm') + choices=axchoices, label='X-Axis', required=True, initial='spm') yaxis1 = forms.ChoiceField( - choices=yaxchoices,label='Left Axis',required=True,initial='power') + choices=yaxchoices, label='Left Axis', required=True, initial='power') yaxis2 = forms.ChoiceField( - choices=yaxchoices2,label='Right Axis',required=True,initial='None') - plottype = forms.ChoiceField(choices=plotchoices,initial='scatter') + choices=yaxchoices2, label='Right Axis', required=True, initial='None') + plottype = forms.ChoiceField(choices=plotchoices, initial='scatter') - plotfield = forms.ChoiceField(choices=parchoices,initial='spm', - label='Metric') + plotfield = forms.ChoiceField(choices=parchoices, initial='spm', + label='Metric') xparam = forms.ChoiceField(choices=parchoicesmultiflex, initial='hr', label='X axis') @@ -1254,47 +1325,46 @@ class AnalysisChoiceForm(forms.Form): initial='pace', label='Y axis') - groupby = forms.ChoiceField(choices=groupchoices,initial='spm', + groupby = forms.ChoiceField(choices=groupchoices, initial='spm', label='Group By') - binsize = forms.FloatField(initial=1,required=False,label = 'Bin Size') + binsize = forms.FloatField(initial=1, required=False, label='Bin Size') ploterrorbars = forms.BooleanField(initial=False, required=False, label='Plot Error Bars') palette = forms.ChoiceField(choices=palettechoices, - label = 'Color Scheme', + label='Color Scheme', initial='monochrome_blue') spmmin = forms.FloatField(initial=15, - required=False,label = 'Min SPM') + required=False, label='Min SPM') spmmax = forms.FloatField(initial=55, - required=False,label = 'Max SPM') + required=False, label='Max SPM') workmin = forms.FloatField(initial=0, - required=False,label = 'Min Work per Stroke') + required=False, label='Min Work per Stroke') workmax = forms.FloatField(initial=1500, - required=False,label = 'Max Work per Stroke') + required=False, label='Max Work per Stroke') cpfitchoices = ( - ('data','Fit to Selected Workouts'), - ('automatic','Critical Power Rolling Data') + ('data', 'Fit to Selected Workouts'), + ('automatic', 'Critical Power Rolling Data') ) cpfit = forms.ChoiceField(choices=cpfitchoices, - label = 'Model Fit',initial='data',required=False) + label='Model Fit', initial='data', required=False) cpoverlay = forms.BooleanField(initial=False, label='Overlay Gold Medal Performance', required=False) - piece = forms.IntegerField(initial=4,label='Ranking Piece (minutes)', - required=False) + piece = forms.IntegerField(initial=4, label='Ranking Piece (minutes)', + required=False) includereststrokes = forms.BooleanField(initial=False, required=False, label='Include Rest Strokes') - def __init__(self, *args, **kwargs): super(AnalysisChoiceForm, self).__init__(*args, **kwargs) @@ -1304,23 +1374,23 @@ class AnalysisChoiceForm(forms.Form): self.fields['plottype'].initial = 'line' - class BoxPlotChoiceForm(forms.Form): - yparam = forms.ChoiceField(choices=parchoices,initial='spm', + yparam = forms.ChoiceField(choices=parchoices, initial='spm', label='Metric') spmmin = forms.FloatField(initial=15, - required=False,label = 'Min SPM') + required=False, label='Min SPM') spmmax = forms.FloatField(initial=55, - required=False,label = 'Max SPM') + required=False, label='Max SPM') workmin = forms.FloatField(initial=0, - required=False,label = 'Min Work per Stroke') + required=False, label='Min Work per Stroke') workmax = forms.FloatField(initial=1500, - required=False,label = 'Max Work per Stroke') + required=False, label='Max Work per Stroke') includereststrokes = forms.BooleanField(initial=False, required=False, label='Include Rest Strokes') + class MultiFlexChoiceForm(forms.Form): xparam = forms.ChoiceField(choices=parchoicesmultiflex, initial='hr', @@ -1328,17 +1398,17 @@ class MultiFlexChoiceForm(forms.Form): yparam = forms.ChoiceField(choices=parchoicesmultiflex, initial='pace', label='Y axis') - groupby = forms.ChoiceField(choices=groupchoices,initial='spm', + groupby = forms.ChoiceField(choices=groupchoices, initial='spm', label='Group By') - binsize = forms.FloatField(initial=1,required=False,label = 'Bin Size') + binsize = forms.FloatField(initial=1, required=False, label='Bin Size') spmmin = forms.FloatField(initial=15, - required=False,label = 'Min SPM') + required=False, label='Min SPM') spmmax = forms.FloatField(initial=55, - required=False,label = 'Max SPM') + required=False, label='Max SPM') workmin = forms.FloatField(initial=0, - required=False,label = 'Min Work per Stroke') + required=False, label='Min Work per Stroke') workmax = forms.FloatField(initial=1500, - required=False,label = 'Max Work per Stroke') + required=False, label='Max Work per Stroke') ploterrorbars = forms.BooleanField(initial=False, required=False, label='Plot Error Bars') @@ -1346,42 +1416,47 @@ class MultiFlexChoiceForm(forms.Form): required=False, label='Include Rest Strokes') palette = forms.ChoiceField(choices=palettechoices, - label = 'Color Scheme', + label='Color Scheme', initial='monochrome_blue') + class ChartParamChoiceForm(forms.Form): plotchoices = ( - ('line','Line Plot'), - ('scatter','Scatter Plot'), - ) - xparam = forms.ChoiceField(choices=parchoices,initial='distance') - yparam = forms.ChoiceField(choices=parchoices,initial='hr') - plottype = forms.ChoiceField(choices=plotchoices,initial='scatter') + ('line', 'Line Plot'), + ('scatter', 'Scatter Plot'), + ) + xparam = forms.ChoiceField(choices=parchoices, initial='distance') + yparam = forms.ChoiceField(choices=parchoices, initial='hr') + plottype = forms.ChoiceField(choices=plotchoices, initial='scatter') teamid = forms.IntegerField(widget=forms.HiddenInput()) + formaxlabels.pop('time') -metricchoices = list(sorted(formaxlabels.items(), key = lambda x:x[1])) +metricchoices = list(sorted(formaxlabels.items(), key=lambda x: x[1])) + class WorkoutJoinParamForm(forms.Form): - workout_name = forms.CharField(required = True, initial = 'Joined Workout') - set_private = forms.BooleanField(initial=False, required = False) - killparents = forms.BooleanField(initial=False, required = False, + workout_name = forms.CharField(required=True, initial='Joined Workout') + set_private = forms.BooleanField(initial=False, required=False) + killparents = forms.BooleanField(initial=False, required=False, label='Delete original workouts') + class FusionMetricChoiceForm(ModelForm): class Meta: model = Workout fields = [] posneg = ( - ('pos','Workout 2 starts after Workout 1'), - ('neg','Workout 2 starts before Workout 1'), - ) + ('pos', 'Workout 2 starts after Workout 1'), + ('neg', 'Workout 2 starts before Workout 1'), + ) columns = forms.MultipleChoiceField(choices=metricchoices, initial=[], widget=forms.CheckboxSelectMultiple()) - posneg = forms.ChoiceField(choices=posneg,initial='pos') - offset = forms.DurationField(label='Time Offset',initial=datetime.timedelta()) + posneg = forms.ChoiceField(choices=posneg, initial='pos') + offset = forms.DurationField( + label='Time Offset', initial=datetime.timedelta()) def __init__(self, *args, **kwargs): super(FusionMetricChoiceForm, self).__init__(*args, **kwargs) @@ -1392,28 +1467,31 @@ class FusionMetricChoiceForm(ModelForm): id = self.instance.id df = dataprep.getrowdata_db(id=id)[0] - labeldict = {key:value for key,value in self.fields['columns'].choices} + labeldict = {key: value for key, + value in self.fields['columns'].choices} for label in labeldict: - if df.loc[:,label].std() == 0: + if df.loc[:, label].std() == 0: try: formaxlabels2.pop(label) - except KeyError: # pragma: no cover + except KeyError: # pragma: no cover pass - metricchoices = list(sorted(formaxlabels2.items(), key = lambda x:x[1])) + metricchoices = list( + sorted(formaxlabels2.items(), key=lambda x: x[1])) self.fields['columns'].choices = metricchoices + class PlannedSessionSelectForm(forms.Form): def __init__(self, sessionchoices, *args, **kwargs): - initialsession = kwargs.pop('initialsession',None) - super(PlannedSessionSelectForm, self).__init__(*args,**kwargs) + initialsession = kwargs.pop('initialsession', None) + super(PlannedSessionSelectForm, self).__init__(*args, **kwargs) self.fields['plannedsession'] = forms.ChoiceField( label='Sessions', - choices = sessionchoices, - widget = forms.RadioSelect, + choices=sessionchoices, + widget=forms.RadioSelect, initial=initialsession ) @@ -1426,32 +1504,32 @@ class WorkoutSessionSelectForm(forms.Form): self.fields['workouts'] = forms.MultipleChoiceField( label='Workouts', - choices = workoutdata['choices'], + choices=workoutdata['choices'], initial=workoutdata['initial'], - widget = forms.CheckboxSelectMultiple, - ) - + widget=forms.CheckboxSelectMultiple, + ) class RaceResultFilterForm(forms.Form): - boatclasses = (type for type in mytypes.workouttypes if type[0] in mytypes.rowtypes) + boatclasses = ( + type for type in mytypes.workouttypes if type[0] in mytypes.rowtypes) boatclassinitial = [t for t in mytypes.rowtypes] sexchoices = ( - ('female','Female'), - ('male','Male'), - ('mixed','Mixed'), - ) + ('female', 'Female'), + ('male', 'Male'), + ('mixed', 'Mixed'), + ) weightcategories = ( - ('hwt','heavy-weight'), - ('lwt','light-weight'), + ('hwt', 'heavy-weight'), + ('lwt', 'light-weight'), ) adaptivecategories = mytypes.adaptivetypes sex = forms.MultipleChoiceField( choices=sexchoices, - initial=['male','female','mixed'], + initial=['male', 'female', 'mixed'], label='Gender', widget=forms.CheckboxSelectMultiple()) @@ -1467,39 +1545,37 @@ class RaceResultFilterForm(forms.Form): initial=mytypes.waterboattype, widget=forms.CheckboxSelectMultiple()) - age_min = forms.IntegerField(label='Min Age',initial=16) - age_max = forms.IntegerField(label='Max Age',initial=100) + age_min = forms.IntegerField(label='Min Age', initial=16) + age_max = forms.IntegerField(label='Max Age', initial=100) weightcategory = forms.MultipleChoiceField( choices=weightcategories, label='Weight Category', - initial=['hwt','lwt'], + initial=['hwt', 'lwt'], widget=forms.CheckboxSelectMultiple()) adaptivecategory = forms.MultipleChoiceField( choices=adaptivecategories, label='Adaptive Class', - initial=['None','PR1','PR2','PR3','FES'], + initial=['None', 'PR1', 'PR2', 'PR3', 'FES'], widget=forms.CheckboxSelectMultiple()) entrycategory = forms.MultipleChoiceField( - choices = [], - label = 'Groups', + choices=[], + label='Groups', widget=forms.CheckboxSelectMultiple(), required=False, ) def __init__(self, *args, **kwargs): - records = kwargs.pop('records',None) - groups = kwargs.pop('groups',None) + records = kwargs.pop('records', None) + groups = kwargs.pop('groups', None) - - - super(RaceResultFilterForm,self).__init__(*args,**kwargs) + super(RaceResultFilterForm, self).__init__(*args, **kwargs) if records: # group - if groups: # pragma: no cover + if groups: # pragma: no cover thecategories = [record.entrycategory for record in records] thecategories = list(set(thecategories)) if len(thecategories) <= 1: @@ -1509,10 +1585,11 @@ class RaceResultFilterForm(forms.Form): for category in thecategories: if category is not None: categorychoices.append( - (category.id,category) - ) + (category.id, category) + ) self.fields['entrycategory'].choices = categorychoices - self.fields['entrycategory'].initial = [cat[0] for cat in categorychoices] + self.fields['entrycategory'].initial = [cat[0] + for cat in categorychoices] else: del self.fields['entrycategory'] @@ -1520,7 +1597,7 @@ class RaceResultFilterForm(forms.Form): thesexes = [record.sex for record in records] thesexes = list(set(thesexes)) - if len(thesexes)<= 1: + if len(thesexes) <= 1: del self.fields['sex'] else: sexchoices = [] @@ -1533,9 +1610,9 @@ class RaceResultFilterForm(forms.Form): theboatclasses = [record.boatclass for record in records] theboatclasses = list(set(theboatclasses)) - if len(theboatclasses)<= 1: + if len(theboatclasses) <= 1: del self.fields['boatclass'] - else: # pragma: no cover + else: # pragma: no cover boatclasschoices = [] for choice in self.fields['boatclass'].choices: if choice[0] in theboatclasses: @@ -1546,10 +1623,10 @@ class RaceResultFilterForm(forms.Form): try: theboattypees = [record.boattype for record in records] theboattypees = list(set(theboattypees)) - except AttributeError: # pragma: no cover + except AttributeError: # pragma: no cover theboattypees = [] - if len(theboattypees)<= 1: + if len(theboattypees) <= 1: del self.fields['boattype'] else: boattypechoices = [] @@ -1562,9 +1639,9 @@ class RaceResultFilterForm(forms.Form): theweightcategoryes = [record.weightcategory for record in records] theweightcategoryes = list(set(theweightcategoryes)) - if len(theweightcategoryes)<= 1: + if len(theweightcategoryes) <= 1: del self.fields['weightcategory'] - else: # pragma: no cover + else: # pragma: no cover weightcategorychoices = [] for choice in self.fields['weightcategory'].choices: if choice[0] in theweightcategoryes: @@ -1572,49 +1649,53 @@ class RaceResultFilterForm(forms.Form): self.fields['weightcategory'].choices = weightcategorychoices # adaptivecategory - theadaptivecategoryes = [record.adaptiveclass for record in records] + theadaptivecategoryes = [ + record.adaptiveclass for record in records] theadaptivecategoryes = list(set(theadaptivecategoryes)) - if len(theadaptivecategoryes)<= 1: + if len(theadaptivecategoryes) <= 1: del self.fields['adaptivecategory'] - else: # pragma: no cover + else: # pragma: no cover adaptivecategorychoices = [] for choice in self.fields['adaptivecategory'].choices: if choice[0] in theadaptivecategoryes: adaptivecategorychoices.append(choice) self.fields['adaptivecategory'].choices = adaptivecategorychoices -class WorkoutRaceSelectForm(forms.Form): -# evaluate_after = forms.TimeField( -# input_formats=['%H:%M:%S.%f', -# '%H:%M:%S', -# '%H:%M:%S', -# '%M:%S.%f', -# '%M:%S', -# '%M'], -# label = 'Only Evaluate After:', -# required=False) - def __init__(self, workoutdata,entries, *args, **kwargs): +class WorkoutRaceSelectForm(forms.Form): + # evaluate_after = forms.TimeField( + # input_formats=['%H:%M:%S.%f', + # '%H:%M:%S', + # '%H:%M:%S', + # '%M:%S.%f', + # '%M:%S', + # '%M'], + # label = 'Only Evaluate After:', + # required=False) + + def __init__(self, workoutdata, entries, *args, **kwargs): super(WorkoutRaceSelectForm, self).__init__(*args, **kwargs) self.fields['workouts'] = forms.ChoiceField( label='Workouts', - choices = workoutdata['choices'], + choices=workoutdata['choices'], initial=workoutdata['initial'], widget=forms.RadioSelect, - ) + ) self.fields['record'] = forms.ChoiceField( - label = 'Entry', - choices = entries['choices'], - initial = entries['initial'], - ) + label='Entry', + choices=entries['choices'], + initial=entries['initial'], + ) # self.fields['evaluate_after'] = # form to send messages to team members + + class TeamMessageForm(forms.Form): message = forms.CharField(required=True, initial='', @@ -1622,11 +1703,13 @@ class TeamMessageForm(forms.Form): ) # Form to select team by rower + + class RowerTeamForm(forms.Form): team = forms.ModelChoiceField( queryset=Team.objects.all(), required=False, - ) + ) def __init__(self, user, *args, **kwargs): super(RowerTeamForm, self).__init__(*args, **kwargs) @@ -1641,19 +1724,19 @@ class PlannedSessionTeamForm(forms.Form): widget=forms.CheckboxSelectMultiple()) def __init__(self, user, *args, **kwargs): - super(PlannedSessionTeamForm,self).__init__(*args, **kwargs) + super(PlannedSessionTeamForm, self).__init__(*args, **kwargs) self.fields['team'].queryset = Team.objects.filter(manager=user) def clean(self): - if any(self.errors): # pragma: no cover + if any(self.errors): # pragma: no cover return cd = self.cleaned_data if not cd['team']: raise forms.ValidationError( 'You must select at least one team' - ) + ) return cd @@ -1664,121 +1747,120 @@ class PlannedSessionTeamMemberForm(forms.Form): widget=forms.CheckboxSelectMultiple()) def __init__(self, thesession, *args, **kwargs): - super(PlannedSessionTeamMemberForm,self).__init__(*args,**kwargs) + super(PlannedSessionTeamMemberForm, self).__init__(*args, **kwargs) self.fields['members'].queryset = thesession.rower.all() -from rowers.models import VirtualRace,GeoCourse def get_countries(): try: - countries = VirtualRace.objects.order_by('country').values_list('country').distinct() - countries = tuple([(c[0],c[0]) for c in countries]) - countries = countries+(('All','All'),) - except: # pragma: no cover - countries = (('All','All')) + countries = VirtualRace.objects.order_by( + 'country').values_list('country').distinct() + countries = tuple([(c[0], c[0]) for c in countries]) + countries = countries+(('All', 'All'),) + except: # pragma: no cover + countries = (('All', 'All')) return countries - class VirtualRaceSelectForm(forms.Form): regattatypechoices = ( - ('upcoming','Upcoming Challenges'), - ('ongoing','Ongoing Challenges'), - ('previous','Previous Challenges'), - ('my','My Challenges'), - ('all','All Challenges'), - ) + ('upcoming', 'Upcoming Challenges'), + ('ongoing', 'Ongoing Challenges'), + ('previous', 'Previous Challenges'), + ('my', 'My Challenges'), + ('all', 'All Challenges'), + ) regattatype = forms.ChoiceField( label='Type', - choices = regattatypechoices, - initial = 'upcoming', - ) + choices=regattatypechoices, + initial='upcoming', + ) country = forms.ChoiceField( label='Country', - choices = get_countries() + choices=get_countries() ) - def __init__(self, *args, **kwargs): super(VirtualRaceSelectForm, self).__init__(*args, **kwargs) self.fields['country'] = forms.ChoiceField( - choices = get_countries(),initial='All' - ) + choices=get_countries(), initial='All' + ) class FlexOptionsForm(forms.Form): - includereststrokes = forms.BooleanField(initial=True, required = False, - label='Include Rest Strokes') + includereststrokes = forms.BooleanField(initial=True, required=False, + label='Include Rest Strokes') plotchoices = ( - ('line','Line Plot'), - ('scatter','Scatter Plot'), - ) - plottype = forms.ChoiceField(choices=plotchoices,initial='line', + ('line', 'Line Plot'), + ('scatter', 'Scatter Plot'), + ) + plottype = forms.ChoiceField(choices=plotchoices, initial='line', label='Chart Type') + class ForceCurveOptionsForm(forms.Form): - includereststrokes = forms.BooleanField(initial=False, required = False, - label='Include Rest Strokes') + includereststrokes = forms.BooleanField(initial=False, required=False, + label='Include Rest Strokes') plotchoices = ( - ('line','Force Curve Collection Plot'), - ('scatter','Peak Force Scatter Plot'), - ('none','Only aggregrate data') - ) - plottype = forms.ChoiceField(choices=plotchoices,initial='line', + ('line', 'Force Curve Collection Plot'), + ('scatter', 'Peak Force Scatter Plot'), + ('none', 'Only aggregrate data') + ) + plottype = forms.ChoiceField(choices=plotchoices, initial='line', label='Individual Stroke Chart Type') axchoices = list( - (ax[0],ax[1]) for ax in axes if ax[0] not in ['cumdist','None'] - ) -axchoices = dict((x,y) for x,y in axchoices) -axchoices = list(sorted(axchoices.items(), key = lambda x:x[1])) + (ax[0], ax[1]) for ax in axes if ax[0] not in ['cumdist', 'None'] +) +axchoices = dict((x, y) for x, y in axchoices) +axchoices = list(sorted(axchoices.items(), key=lambda x: x[1])) -yaxchoices = list((ax[0],ax[1]) for ax in axes if ax[0] not in ['cumdist','distance','time']) -yaxchoices = dict((x,y) for x,y in yaxchoices) -yaxchoices = list(sorted(yaxchoices.items(), key = lambda x:x[1])) +yaxchoices = list((ax[0], ax[1]) for ax in axes if ax[0] + not in ['cumdist', 'distance', 'time']) +yaxchoices = dict((x, y) for x, y in yaxchoices) +yaxchoices = list(sorted(yaxchoices.items(), key=lambda x: x[1])) yaxchoices2 = list( - (ax[0],ax[1]) for ax in axes if ax[0] not in ['cumdist','distance','time'] - ) -yaxchoices2 = dict((x,y) for x,y in yaxchoices2) -yaxchoices2 = list(sorted(yaxchoices2.items(), key = lambda x:x[1])) + (ax[0], ax[1]) for ax in axes if ax[0] not in ['cumdist', 'distance', 'time'] +) +yaxchoices2 = dict((x, y) for x, y in yaxchoices2) +yaxchoices2 = list(sorted(yaxchoices2.items(), key=lambda x: x[1])) + class StravaChartForm(forms.Form): xaxischoices = ( - ('cumdist','Distance'), - ('time','Time') + ('cumdist', 'Distance'), + ('time', 'Time') ) xaxis = forms.ChoiceField( - choices = xaxischoices,label='X-Axis',required=True) - + choices=xaxischoices, label='X-Axis', required=True) yaxis1 = forms.ChoiceField( - choices=yaxchoices,label='First Chart',required=True) + choices=yaxchoices, label='First Chart', required=True) yaxis2 = forms.ChoiceField( - choices=yaxchoices2,label='Second Chart',required=True) + choices=yaxchoices2, label='Second Chart', required=True) yaxis3 = forms.ChoiceField( - choices=yaxchoices,label='Third Chart',required=True) + choices=yaxchoices, label='Third Chart', required=True) yaxis4 = forms.ChoiceField( - choices=yaxchoices2,label='Fourth Chart',required=True) + choices=yaxchoices2, label='Fourth Chart', required=True) - def __init__(self,request,*args,**kwargs): - extrametrics = kwargs.pop('extrametrics',[]) + def __init__(self, request, *args, **kwargs): + extrametrics = kwargs.pop('extrametrics', []) super(StravaChartForm, self).__init__(*args, **kwargs) - rower = Rower.objects.get(user=request.user) axchoicespro = ( - ('',ax[1]) if ax[4] == 'pro' and ax[0] else (ax[0],ax[1]) for ax in axes - ) + ('', ax[1]) if ax[4] == 'pro' and ax[0] else (ax[0], ax[1]) for ax in axes + ) axchoicesbasicx = [] axchoicesbasicy = [] @@ -1786,15 +1868,14 @@ class StravaChartForm(forms.Form): for ax in axes: if ax[4] != 'pro' and ax[0] != 'cumdist': if ax[0] != 'None': - axchoicesbasicx.insert(0,(ax[0],ax[1])) - if ax[0] not in ['cumdist','distance','time']: - axchoicesbasicy.insert(0,(ax[0],ax[1])) + axchoicesbasicx.insert(0, (ax[0], ax[1])) + if ax[0] not in ['cumdist', 'distance', 'time']: + axchoicesbasicy.insert(0, (ax[0], ax[1])) else: if ax[0] != 'None': - axchoicesbasicx.insert(0,('None',ax[1]+' (PRO)')) - if ax[0] not in ['cumdist','distance','time']: - axchoicesbasicy.insert(0,('None',ax[1]+' (PRO)')) - + axchoicesbasicx.insert(0, ('None', ax[1]+' (PRO)')) + if ax[0] not in ['cumdist', 'distance', 'time']: + axchoicesbasicy.insert(0, ('None', ax[1]+' (PRO)')) if not user_is_not_basic(rower.user): self.fields['xaxis'].choices = axchoicesbasicx @@ -1807,22 +1888,21 @@ class StravaChartForm(forms.Form): class FlexAxesForm(forms.Form): xaxis = forms.ChoiceField( - choices=axchoices,label='X-Axis',required=True) + choices=axchoices, label='X-Axis', required=True) yaxis1 = forms.ChoiceField( - choices=yaxchoices,label='Left Axis',required=True) + choices=yaxchoices, label='Left Axis', required=True) yaxis2 = forms.ChoiceField( - choices=yaxchoices2,label='Right Axis',required=True) + choices=yaxchoices2, label='Right Axis', required=True) - def __init__(self,request,*args,**kwargs): - extrametrics = kwargs.pop('extrametrics',[]) + def __init__(self, request, *args, **kwargs): + extrametrics = kwargs.pop('extrametrics', []) super(FlexAxesForm, self).__init__(*args, **kwargs) - rower = Rower.objects.get(user=request.user) axchoicespro = ( - ('',ax[1]) if ax[4] == 'pro' and ax[0] else (ax[0],ax[1]) for ax in axes - ) + ('', ax[1]) if ax[4] == 'pro' and ax[0] else (ax[0], ax[1]) for ax in axes + ) axchoicesbasicx = [] axchoicesbasicy = [] @@ -1830,15 +1910,14 @@ class FlexAxesForm(forms.Form): for ax in axes: if ax[4] != 'pro' and ax[0] != 'cumdist': if ax[0] != 'None': - axchoicesbasicx.insert(0,(ax[0],ax[1])) - if ax[0] not in ['cumdist','distance','time']: - axchoicesbasicy.insert(0,(ax[0],ax[1])) + axchoicesbasicx.insert(0, (ax[0], ax[1])) + if ax[0] not in ['cumdist', 'distance', 'time']: + axchoicesbasicy.insert(0, (ax[0], ax[1])) else: if ax[0] != 'None': - axchoicesbasicx.insert(0,('None',ax[1]+' (PRO)')) - if ax[0] not in ['cumdist','distance','time']: - axchoicesbasicy.insert(0,('None',ax[1]+' (PRO)')) - + axchoicesbasicx.insert(0, ('None', ax[1]+' (PRO)')) + if ax[0] not in ['cumdist', 'distance', 'time']: + axchoicesbasicy.insert(0, ('None', ax[1]+' (PRO)')) if not user_is_not_basic(rower.user): self.fields['xaxis'].choices = axchoicesbasicx diff --git a/rowers/garmin_stuff.py b/rowers/garmin_stuff.py index c6c3973d..8bba69e1 100644 --- a/rowers/garmin_stuff.py +++ b/rowers/garmin_stuff.py @@ -1,14 +1,20 @@ +from rowers.models import C2WorldClassAgePerformance, Rower, Workout, TombStone +from rowers import utils +from rowers.utils import custom_exception_handler, NoTokenError +from rowingdata import rowingdata +from django.core.exceptions import PermissionDenied +from rowers.utils import myqueue from rowers.imports import * import datetime import requests from requests import Session, Request -from requests_oauthlib import OAuth1,OAuth1Session +from requests_oauthlib import OAuth1, OAuth1Session from requests_oauthlib.oauth1_session import TokenRequestDenied from requests import Request, Session import rowers.mytypes as mytypes from rowers.mytypes import otwtypes -from rowers.rower_rules import is_workout_user,ispromember +from rowers.rower_rules import is_workout_user, ispromember from iso8601 import ParseError from rowers.plannedsessions import ps_dict_get_description @@ -25,15 +31,15 @@ import logging from rowsandall_app.settings import ( GARMIN_CLIENT_KEY, GARMIN_REDIRECT_URI, GARMIN_CLIENT_SECRET - ) +) from pytz import timezone as tz, utc # You must initialize logging, otherwise you'll not see debug output. -#logging.basicConfig() -#logging.getLogger().setLevel(logging.DEBUG) +# logging.basicConfig() +# logging.getLogger().setLevel(logging.DEBUG) #requests_log = logging.getLogger("requests.packages.urllib3") -#requests_log.setLevel(logging.DEBUG) +# requests_log.setLevel(logging.DEBUG) #requests_log.propagate = True @@ -42,13 +48,7 @@ import django_rq queue = django_rq.get_queue('default') queuelow = django_rq.get_queue('low') queuehigh = django_rq.get_queue('low') -from rowers.utils import myqueue -from rowers.models import C2WorldClassAgePerformance,Rower,Workout,TombStone -from django.core.exceptions import PermissionDenied - -from rowers.utils import custom_exception_handler,NoTokenError -from rowingdata import rowingdata oauth_data = { 'client_id': GARMIN_CLIENT_KEY, @@ -61,20 +61,20 @@ oauth_data = { 'expirydatename': 'garmintokenexpirydate', 'bearer_auth': True, 'base_url': "https://connect.garmin.com/oauthConfirm", - 'scope':'write', + 'scope': 'write', 'headers': 'Authorization: OAuth oauth_version="1.0"' - } +} columns = { - 'startTimeInSeconds':'TimeStamp (sec)', - 'latitudeInDegree':' latitude', - 'longitudeInDegree':' longitude', - 'heartRate':' HRCur (bpm)', - 'speedMetersPerSecond':' AverageBoatSpeed (m/s)', - 'totalDistanceInMeters':' Horizontal (meters)', - 'clockDurationInSeconds':' ElapsedTime (sec)', - 'powerInWatts':' Power (watts)', - 'bikeCadenceInRPM':' Cadence (stokes/min)', + 'startTimeInSeconds': 'TimeStamp (sec)', + 'latitudeInDegree': ' latitude', + 'longitudeInDegree': ' longitude', + 'heartRate': ' HRCur (bpm)', + 'speedMetersPerSecond': ' AverageBoatSpeed (m/s)', + 'totalDistanceInMeters': ' Horizontal (meters)', + 'clockDurationInSeconds': ' ElapsedTime (sec)', + 'powerInWatts': ' Power (watts)', + 'bikeCadenceInRPM': ' Cadence (stokes/min)', } targettypes = { @@ -96,35 +96,37 @@ targettypes = { repeattypes = { "RepeatUntilStepsCmplt": "REPEAT_UNTIL_STEPS_CMPLT", - "RepeatUntilTime": "REPEAT_UNTIL_TIME", - "RepeatUntilDistance": "REPEAT_UNTIL_TIME", - "RepeatUntilCalories": "REPEAT_UNTIL_CALORIES" , - "RepeatUntilHrLessThan": "REPEAT_UNTIL_HR_LESS_THAN" , - "RepeatUntilHrGreaterThan": "REPEAT_UNTIL_HR_GREATER_THAN", - "RepeatUntilPowerLessThan": "REPEAT_UNTIL_POWER_LESS_THAN", - "RepeatUntilPowerGreaterThan": "REPEAT_UNTIL_POWER_GREATER_THAN", + "RepeatUntilTime": "REPEAT_UNTIL_TIME", + "RepeatUntilDistance": "REPEAT_UNTIL_TIME", + "RepeatUntilCalories": "REPEAT_UNTIL_CALORIES", + "RepeatUntilHrLessThan": "REPEAT_UNTIL_HR_LESS_THAN", + "RepeatUntilHrGreaterThan": "REPEAT_UNTIL_HR_GREATER_THAN", + "RepeatUntilPowerLessThan": "REPEAT_UNTIL_POWER_LESS_THAN", + "RepeatUntilPowerGreaterThan": "REPEAT_UNTIL_POWER_GREATER_THAN", "RepeatUntilPowerLapLessThan": "REPEAT_UNTIL_POWER_LAP_LESS_THAN", "RepeatUntilPowerLapGreaterThan": "REPEAT_UNTIL_POWER_LAP_GREATER_THAN", } -def garmin_authorize(): # pragma: no cover + +def garmin_authorize(): # pragma: no cover redirect_uri = oauth_data['redirect_uri'] client_secret = oauth_data['client_secret'] client_id = oauth_data['client_id'] base_uri = oauth_data['base_url'] - garmin = OAuth1Session(oauth_data['client_id'], client_secret=oauth_data['client_secret'], ) - fetch_response = garmin.fetch_request_token(oauth_data['authorization_uri']) + fetch_response = garmin.fetch_request_token( + oauth_data['authorization_uri']) resource_owner_key = fetch_response.get('oauth_token') resource_owner_secret = fetch_response.get('oauth_token_secret') authorization_url = garmin.authorization_url(base_uri) - return authorization_url,resource_owner_key,resource_owner_secret + return authorization_url, resource_owner_key, resource_owner_secret -def garmin_processcallback(redirect_response,resource_owner_key,resource_owner_secret): # pragma: no cover + +def garmin_processcallback(redirect_response, resource_owner_key, resource_owner_secret): # pragma: no cover garmin = OAuth1Session(oauth_data['client_id'], client_secret=oauth_data['client_secret'], ) @@ -134,13 +136,12 @@ def garmin_processcallback(redirect_response,resource_owner_key,resource_owner_s token = oauth_response.get('oauth_token') access_token_url = 'https://connectapi.garmin.com/oauth-service/oauth/access_token' - # Using OAuth1Session garmin = OAuth1Session(oauth_data['client_id'], - client_secret=oauth_data['client_secret'], - resource_owner_key=resource_owner_key, - resource_owner_secret=resource_owner_secret, - verifier=verifier,) + client_secret=oauth_data['client_secret'], + resource_owner_key=resource_owner_key, + resource_owner_secret=resource_owner_secret, + verifier=verifier,) try: oauth_tokens = garmin.fetch_access_token(access_token_url) garmintoken = oauth_tokens.get('oauth_token') @@ -149,9 +150,10 @@ def garmin_processcallback(redirect_response,resource_owner_key,resource_owner_s garmintoken = '' garminrefreshtoken = '' - return garmintoken,garminrefreshtoken + return garmintoken, garminrefreshtoken -def garmin_open(user): # pragma: no cover + +def garmin_open(user): # pragma: no cover r = Rower.objects.get(user=user) token = Rower.garmintoken @@ -160,7 +162,8 @@ def garmin_open(user): # pragma: no cover return token -def get_garmin_file(r,callbackURL,starttime,fileType): # pragma: no cover + +def get_garmin_file(r, callbackURL, starttime, fileType): # pragma: no cover job = myqueue( queue, handle_get_garmin_file, @@ -171,15 +174,16 @@ def get_garmin_file(r,callbackURL,starttime,fileType): # pragma: no cover r.user.id, callbackURL, fileType, - ) + ) return job.id -def get_garmin_workout_list(user): # pragma: no cover + +def get_garmin_workout_list(user): # pragma: no cover r = Rower.objects.get(user=user) if (r.garmintoken == '') or (r.garmintoken is None): s = "Token doesn't exist. Need to authorize" - return custom_exception_handler(401,s) + return custom_exception_handler(401, s) garmin = OAuth1Session(oauth_data['client_id'], client_secret=oauth_data['client_secret'], @@ -193,18 +197,18 @@ def get_garmin_workout_list(user): # pragma: no cover return result + def garmin_can_export_session(user): - if user.rower.rowerplan not in ['coach','plan']: - return False # pragma: no cover + if user.rower.rowerplan not in ['coach', 'plan']: + return False # pragma: no cover result = get_garmin_permissions(user) if 'WORKOUT_IMPORT' in result: return True - return False # pragma: no cover + return False # pragma: no cover -from rowers import utils -def step_to_garmin(step,order=0): +def step_to_garmin(step, order=0): durationtype = step['dict']['durationType'] durationvalue = step['dict']['durationValue'] durationvaluetype = None @@ -222,35 +226,35 @@ def step_to_garmin(step,order=0): durationtype = 'DISTANCE' durationvalue = int(durationvalue/100) durationvaluetype = 'METER' - elif durationtype == 'HrLessThan': # pragma: no cover + elif durationtype == 'HrLessThan': # pragma: no cover durationtype = 'HR_LESS_THAN' if durationvalue <= 100: durationvaluetype = 'PERCENT' else: durationvaluetype = None durationvalue -= 100 - elif durationtype == 'HrGreaterThan': # pragma: no cover + elif durationtype == 'HrGreaterThan': # pragma: no cover durationtype = 'HR_GREATER_THAN' if durationvalue <= 100: durationvaluetype = 'PERCENT' else: durationvaluetype = None durationvalue -= 100 - elif durationtype == 'PowerLessThan': # pragma: no cover + elif durationtype == 'PowerLessThan': # pragma: no cover durationtype = 'POWER_LESS_THAN' if durationvalue <= 1000: durationvaluetype = 'PERCENT' else: durationvaluetype = None durationvalue -= 1000 - elif durationtype == 'PowerGreaterThan': # pragma: no cover + elif durationtype == 'PowerGreaterThan': # pragma: no cover durationtype = 'POWER_GREATER_THAN' if durationvalue <= 1000: durationvaluetype = 'PERCENT' else: durationvaluetype = None durationvalue -= 1000 - elif durationtype == 'Reps': # pragma: no cover + elif durationtype == 'Reps': # pragma: no cover durationtype = 'REPS' try: @@ -261,7 +265,7 @@ def step_to_garmin(step,order=0): try: targetValue = step['dict']['targetValue'] - if targetValue == 0: # pragma: no cover + if targetValue == 0: # pragma: no cover targetValue = None except KeyError: targetValue = None @@ -269,40 +273,38 @@ def step_to_garmin(step,order=0): if targetType is not None and targetType.lower() == "power": targetType = 'POWER' if targetValue is not None and targetValue <= 1000: - targetValueType = 'PERCENT' # pragma: no cover + targetValueType = 'PERCENT' # pragma: no cover else: targetValueType = None targetValue -= 1000 - try: targetValueLow = step['dict']['targetValueLow'] - if targetValueLow == 0 and targetValue is not None and targetValue > 0: # pragma: no cover + if targetValueLow == 0 and targetValue is not None and targetValue > 0: # pragma: no cover targetValueLow = targetValue targetValue = None - elif targetValueLow == 0: # pragma: no cover + elif targetValueLow == 0: # pragma: no cover targetValueLow = None - elif targetValueLow <= 1000 and targetType == 'POWER': # pragma: no cover + elif targetValueLow <= 1000 and targetType == 'POWER': # pragma: no cover targetValueType = 'PERCENT' - elif targetValueLow > 1000 and targetType == 'POWER': # pragma: no cover + elif targetValueLow > 1000 and targetType == 'POWER': # pragma: no cover targetValueLow -= 1000 except KeyError: targetValueLow = None try: targetValueHigh = step['dict']['targetValueHigh'] - if targetValue is not None and targetValue > 0 and targetValueHigh == 0: # pragma: no cover + if targetValue is not None and targetValue > 0 and targetValueHigh == 0: # pragma: no cover targetValueHigh = targetValue targetValue = 0 - elif targetValueHigh <= 1000 and targetType == 'POWER': # pragma: no cover + elif targetValueHigh <= 1000 and targetType == 'POWER': # pragma: no cover targetValueType = 'PERCENT' - elif targetValueHigh > 1000 and targetType == 'POWER': # pragma: no cover + elif targetValueHigh > 1000 and targetType == 'POWER': # pragma: no cover targetValueHigh -= 1000 - elif targetValueHigh == 0: # pragma: no cover + elif targetValueHigh == 0: # pragma: no cover targetValueHigh = None except KeyError: targetValueHigh = None - if targetValue is None and targetValueLow is None and targetValueHigh is None: targetType = None @@ -321,18 +323,18 @@ def step_to_garmin(step,order=0): out = { 'type': steptype, - 'stepOrder':order, - 'repeatType':repeattype, - 'repeatValue':step['repeatValue'], - 'intensity':intensity, - 'description':step['dict']['wkt_step_name'], - 'durationType':durationtype, - 'durationValue':durationvalue, - 'durationValueType':durationvaluetype, - 'targetType':targetType, - 'targetValue':targetValue, - 'targetValueLow':targetValueLow, - 'targetValueHigh':targetValueHigh, + 'stepOrder': order, + 'repeatType': repeattype, + 'repeatValue': step['repeatValue'], + 'intensity': intensity, + 'description': step['dict']['wkt_step_name'], + 'durationType': durationtype, + 'durationValue': durationvalue, + 'durationValueType': durationvaluetype, + 'targetType': targetType, + 'targetValue': targetValue, + 'targetValueLow': targetValueLow, + 'targetValueHigh': targetValueHigh, } try: steps = step['steps'] @@ -350,12 +352,13 @@ def step_to_garmin(step,order=0): return out, order -def ps_to_garmin(ps,r): + +def ps_to_garmin(ps, r): payload = { 'workoutName': ps.name, - 'sport':r.garminactivity, - 'description':ps_dict_get_description(ps.steps), - 'estimatedDurationInSecs':60*ps.approximate_duration, + 'sport': r.garminactivity, + 'description': ps_dict_get_description(ps.steps), + 'estimatedDurationInSecs': 60*ps.approximate_duration, 'estimatedDistanceInMeters': ps.approximate_distance, 'workoutProvider': 'Rowsandall.com', 'workoutSourceId': 'Rowsandall.com', @@ -365,7 +368,6 @@ def ps_to_garmin(ps,r): steplist = utils.ps_dict_order_dict(ps.steps) - while steplist: step, steplist = utils.peel(steplist) steps.append(step) @@ -375,7 +377,7 @@ def ps_to_garmin(ps,r): lijst = [] i = 0 for step in steps: - gstep, i = step_to_garmin(step,i) + gstep, i = step_to_garmin(step, i) lijst.append(gstep) payload['steps'] = lijst @@ -383,65 +385,63 @@ def ps_to_garmin(ps,r): return payload - def get_garmin_permissions(user): r = Rower.objects.get(user=user) - if (r.garmintoken == '') or (r.garmintoken is None): # pragma: no cover + if (r.garmintoken == '') or (r.garmintoken is None): # pragma: no cover s = "Token doesn't exist. Need to authorize" - return custom_exception_handler(401,s) + return custom_exception_handler(401, s) garminheaders = OAuth1( - client_key = oauth_data['client_id'], + client_key=oauth_data['client_id'], client_secret=oauth_data['client_secret'], resource_owner_key=r.garmintoken, resource_owner_secret=r.garminrefreshtoken, signature_method='HMAC-SHA1', - ) - + ) url = 'https://apis.garmin.com/userPermissions' - result = requests.get(url,auth=garminheaders) + result = requests.get(url, auth=garminheaders) if result.status_code == 200: return result.json() - return [] # pragma: no cover + return [] # pragma: no cover -def garmin_session_create(ps,user): + +def garmin_session_create(ps, user): if not ps.steps: - return 0 # pragma: no cover + return 0 # pragma: no cover if not garmin_can_export_session(user): - return 0 # pragma: no cover + return 0 # pragma: no cover if ps.garmin_schedule_id != 0: - return ps.garmin_schedule_id # pragma: no cover + return ps.garmin_schedule_id # pragma: no cover r = Rower.objects.get(user=user) - if (r.garmintoken == '') or (r.garmintoken is None): # pragma: no cover + if (r.garmintoken == '') or (r.garmintoken is None): # pragma: no cover s = "Token doesn't exist. Need to authorize" - return custom_exception_handler(401,s) + return custom_exception_handler(401, s) - payload = ps_to_garmin(ps,r) + payload = ps_to_garmin(ps, r) url = 'https://apis.garmin.com/training-api/workout' garminheaders = OAuth1( - client_key = oauth_data['client_id'], + client_key=oauth_data['client_id'], client_secret=oauth_data['client_secret'], resource_owner_key=r.garmintoken, resource_owner_secret=r.garminrefreshtoken, signature_method='HMAC-SHA1', - ) + ) - response = requests.post(url,auth=garminheaders,json=payload) + response = requests.post(url, auth=garminheaders, json=payload) - if response.status_code != 200: # pragma: no cover + if response.status_code != 200: # pragma: no cover return 0 garmin_workout_id = response.json()['workoutId'] - url = 'https://apis.garmin.com/training-api/schedule' payload = { @@ -449,11 +449,9 @@ def garmin_session_create(ps,user): 'date': ps.preferreddate.strftime('%Y-%m-%d') } + response = requests.post(url, auth=garminheaders, json=payload) - response = requests.post(url,auth=garminheaders,json=payload) - - - if response.status_code != 200: # pragma: no cover + if response.status_code != 200: # pragma: no cover return 0 ps.garmin_schedule_id = response.json() @@ -462,12 +460,13 @@ def garmin_session_create(ps,user): return garmin_workout_id -def garmin_getworkout(garminid,r,activity): + +def garmin_getworkout(garminid, r, activity): starttime = activity['startTimeInSeconds'] startdatetime = arrow.get(starttime) try: offset = activity['startTimeOffsetInSeconds'] - except KeyError: # pragma: no cover + except KeyError: # pragma: no cover offset = 0 durationseconds = activity['durationInSeconds'] duration = dataprep.totaltime_sec_to_string(durationseconds) @@ -481,47 +480,47 @@ def garmin_getworkout(garminid,r,activity): try: averagehr = activity['averageHeartRateInBeatsPerMinute'] maxhr = activity['maxHeartRateInBeatsPerMinute'] - except KeyError: # pragma: no cover + except KeyError: # pragma: no cover averagehr = 0 maxhr = 0 try: w = Workout.objects.get(uploadedtogarmin=garminid) except Workout.DoesNotExist: - newcsvfile='media/garmin{code}_{importid}.csv'.format( + newcsvfile = 'media/garmin{code}_{importid}.csv'.format( code=uuid4().hex[:16], importid=garminid, ) - w = Workout(user=r,csvfilename=newcsvfile) + w = Workout(user=r, csvfilename=newcsvfile) utc_offset = datetime.timedelta(seconds=offset) now = datetime.datetime.now(pytz.utc) zones = [tz.zone for tz in map(pytz.timezone, pytz.all_timezones_set) - if now.astimezone(tz).utcoffset() == utc_offset] - if r.defaulttimezone in zones: # pragma: no cover + if now.astimezone(tz).utcoffset() == utc_offset] + if r.defaulttimezone in zones: # pragma: no cover thetimezone = r.defaulttimezone elif len(zones): thetimezone = zones[0] - else: # pragma: no cover + else: # pragma: no cover thetimezone = utc - startdatetime = datetime.datetime( + startdatetime = datetime.datetime( year=startdatetime.year, month=startdatetime.month, day=startdatetime.day, hour=startdatetime.hour, minute=startdatetime.minute, second=startdatetime.second, - ).astimezone(pytz.timezone(thetimezone)) + ).astimezone(pytz.timezone(thetimezone)) w.startdatetime = startdatetime w.starttime = w.startdatetime.time() try: - w.duration = datetime.datetime.strptime(duration,"%H:%M:%S.%f").time() - except ValueError: # pragma: no cover - w.duration = datetime.datetime.strptime(duration,"%H:%M:%S") + w.duration = datetime.datetime.strptime(duration, "%H:%M:%S.%f").time() + except ValueError: # pragma: no cover + w.duration = datetime.datetime.strptime(duration, "%H:%M:%S") try: w.workouttype = mytypes.garminmappinginv[activitytype] - except KeyError: # pragma: no cover + except KeyError: # pragma: no cover w.workouttype = 'other' w.name = name w.date = date @@ -530,26 +529,26 @@ def garmin_getworkout(garminid,r,activity): w.save() - return w + def garmin_workouts_from_details(data): activities = data['activityDetails'] for activity in activities: - try: # pragma: no cover + try: # pragma: no cover garmintoken = activity['userAccessToken'] - except KeyError: # pragma: no cover + except KeyError: # pragma: no cover return 0 - except TypeError: # pragma: no cover + except TypeError: # pragma: no cover return 0 try: r = Rower.objects.get(garmintoken=garmintoken) garminid = activity['summaryId'][:-7] summary = activity['summary'] - w = garmin_getworkout(garminid,r,summary) + w = garmin_getworkout(garminid, r, summary) samples = activity['samples'] df = pd.DataFrame(samples) - df.rename(columns=columns,inplace=True) + df.rename(columns=columns, inplace=True) try: pace = 500./df[' AverageBoatSpeed (m/s)'] @@ -572,27 +571,28 @@ def garmin_workouts_from_details(data): df[' DriveTime (ms)'] = 0 rowdata = rowingdata(df=df) - rowdata.write_csv(w.csvfilename,gzip=True) - data = dataprep.dataprep(rowdata.df,id=w.id) + rowdata.write_csv(w.csvfilename, gzip=True) + data = dataprep.dataprep(rowdata.df, id=w.id) summary = rowdata.allstats() - w.summary=summary + w.summary = summary w.uploadedtogarmin = garminid w.save() - trimp,hrtss = dataprep.workout_trimp(w) - rscore,normp = dataprep.workout_rscore(w) - except Rower.DoesNotExist: # pragma: no cover + trimp, hrtss = dataprep.workout_trimp(w) + rscore, normp = dataprep.workout_rscore(w) + except Rower.DoesNotExist: # pragma: no cover pass return 1 + def garmin_workouts_from_summaries(activities): for activity in activities: garmintoken = activity['userAccessToken'] try: r = Rower.objects.get(garmintoken=garmintoken) id = activity['summaryId'] - w = garmin_getworkout(id,r,activity) - except Rower.DoesNotExist: # pragma: no cover + w = garmin_getworkout(id, r, activity) + except Rower.DoesNotExist: # pragma: no cover pass return 1 diff --git a/rowers/imports.py b/rowers/imports.py index 700b673a..d34c41cf 100644 --- a/rowers/imports.py +++ b/rowers/imports.py @@ -24,15 +24,16 @@ from time import strftime import rowers.dataprep as dataprep import math -from math import sin,cos,atan2,sqrt -import os,sys +from math import sin, cos, atan2, sqrt +import os +import sys import urllib import iso8601 from uuid import uuid4 # Django -from django.http import HttpResponseRedirect, HttpResponse,JsonResponse +from django.http import HttpResponseRedirect, HttpResponse, JsonResponse from django.conf import settings from django.contrib.auth import authenticate, login, logout from django.contrib.auth.models import User @@ -42,19 +43,19 @@ from django.contrib.auth.decorators import login_required # from .models import Profile from rowingdata import rowingdata, make_cumvalues import pandas as pd -from rowers.models import Rower,Workout,TombStone +from rowers.models import Rower, Workout, TombStone import rowers.mytypes as mytypes from rowsandall_app.settings import ( C2_CLIENT_ID, C2_REDIRECT_URI, C2_CLIENT_SECRET, STRAVA_CLIENT_ID, STRAVA_REDIRECT_URI, STRAVA_CLIENT_SECRET, SPORTTRACKS_CLIENT_SECRET, SPORTTRACKS_CLIENT_ID, SPORTTRACKS_REDIRECT_URI - ) +) from rowers.utils import ( NoTokenError, custom_exception_handler, ewmovingaverage, - geo_distance,uniqify - ) + geo_distance, uniqify +) # Splits SportTracks data which is one long sequence of @@ -63,28 +64,27 @@ from rowers.utils import ( def splitstdata(lijst): t = [] latlong = [] - while len(lijst)>=2: + while len(lijst) >= 2: t.append(lijst[0]) latlong.append(lijst[1]) lijst = lijst[2:] - return [np.array(t),np.array(latlong)] + return [np.array(t), np.array(latlong)] -def imports_open(user,oauth_data): +def imports_open(user, oauth_data): r = Rower.objects.get(user=user) - token = getattr(r,oauth_data['tokenname']) + token = getattr(r, oauth_data['tokenname']) try: - refreshtoken = getattr(r,oauth_data['refreshtokenname']) - except (TypeError,AttributeError,KeyError): # pragma: no cover + refreshtoken = getattr(r, oauth_data['refreshtokenname']) + except (TypeError, AttributeError, KeyError): # pragma: no cover refreshtoken = None try: - tokenexpirydate = getattr(r,oauth_data['expirydatename']) - except (TypeError,AttributeError,KeyError): # pragma: no cover + tokenexpirydate = getattr(r, oauth_data['expirydatename']) + except (TypeError, AttributeError, KeyError): # pragma: no cover tokenexpirydate = None - if (token == '') or (token is None): s = "Token doesn't exist. Need to authorize" raise NoTokenError("User has no token") @@ -92,7 +92,7 @@ def imports_open(user,oauth_data): tokenname = oauth_data['tokenname'] refreshtokenname = oauth_data['refreshtokenname'] expirydatename = oauth_data['expirydatename'] - if tokenexpirydate and timezone.now()+timedelta(seconds=60)>tokenexpirydate: + if tokenexpirydate and timezone.now()+timedelta(seconds=60) > tokenexpirydate: token = imports_token_refresh( user, tokenname, @@ -100,25 +100,24 @@ def imports_open(user,oauth_data): expirydatename, oauth_data, ) - elif tokenexpirydate is None and expirydatename is not None and 'strava' in expirydatename: # pragma: no cover + elif tokenexpirydate is None and expirydatename is not None and 'strava' in expirydatename: # pragma: no cover token = imports_token_refresh( user, tokenname, refreshtokenname, expirydatename, oauth_data, - ) - + ) return token # Refresh token using refresh token -def imports_do_refresh_token(refreshtoken,oauth_data,access_token=''): +def imports_do_refresh_token(refreshtoken, oauth_data, access_token=''): client_auth = requests.auth.HTTPBasicAuth( oauth_data['client_id'], oauth_data['client_secret'] - ) + ) post_data = {"grant_type": "refresh_token", "client_secret": oauth_data['client_secret'], @@ -133,8 +132,8 @@ def imports_do_refresh_token(refreshtoken,oauth_data,access_token=''): if 'grant_type' in oauth_data: if oauth_data['grant_type']: post_data['grant_type'] = oauth_data['grant_type'] - else: # pragma: no cover - grant_type = post_data.pop('grant_type',None) + else: # pragma: no cover + grant_type = post_data.pop('grant_type', None) if oauth_data['bearer_auth']: headers['authorization'] = 'Bearer %s' % access_token @@ -144,29 +143,27 @@ def imports_do_refresh_token(refreshtoken,oauth_data,access_token=''): if 'json' in oauth_data['content_type']: try: response = requests.post(baseurl, - data=json.dumps(post_data), - headers=headers,verify=False) - except: # pragma: no cover + data=json.dumps(post_data), + headers=headers, verify=False) + except: # pragma: no cover raise NoTokenError("Failed to get token") else: try: response = requests.post(baseurl, - data=post_data, - headers=headers,verify=False, - ) - except: # pragma: no cover + data=post_data, + headers=headers, verify=False, + ) + except: # pragma: no cover raise NoTokenError("Failed to get token") - - if response.status_code == 200 or response.status_code == 201: token_json = response.json() - else: # pragma: no cover + else: # pragma: no cover raise NoTokenError("User has no token") try: thetoken = token_json['access_token'] - except KeyError: # pragma: no cover + except KeyError: # pragma: no cover raise NoTokenError("User has no token") try: @@ -175,35 +172,34 @@ def imports_do_refresh_token(refreshtoken,oauth_data,access_token=''): try: expires_at = arrow.get(token_json['expires_at']).timestamp() expires_in = expires_at - arrow.now().timestamp() - except KeyError: # pragma: no cover + except KeyError: # pragma: no cover expires_in = 0 try: refresh_token = token_json['refresh_token'] - except KeyError: # pragma: no cover + except KeyError: # pragma: no cover refresh_token = refreshtoken try: expires_in = int(expires_in) - except (TypeError,ValueError): # pragma: no cover + except (TypeError, ValueError): # pragma: no cover expires_in = 0 - return [thetoken,expires_in,refresh_token] + return [thetoken, expires_in, refresh_token] # Exchange ST access code for long-lived ST access token -def imports_get_token( - code,oauth_data - ): +def imports_get_token( + code, oauth_data +): + redirect_uri = oauth_data['redirect_uri'] client_secret = oauth_data['client_secret'] client_id = oauth_data['client_id'] base_uri = oauth_data['base_url'] - client_auth = requests.auth.HTTPBasicAuth( - client_id,client_secret - ) - + client_id, client_secret + ) post_data = {"grant_type": "authorization_code", "code": code, @@ -225,47 +221,47 @@ def imports_get_token( post_data['grant_type'] = oauth_data['grant_type'] if 'strava' in oauth_data['autorization_uri']: post_data['grant_type'] = "authorization_code" - else: # pragma: no cover - grant_type = post_data.pop('grant_type',None) - + else: # pragma: no cover + grant_type = post_data.pop('grant_type', None) if 'json' in oauth_data['content_type']: response = requests.post( base_uri, data=json.dumps(post_data), headers=headers) - else: # pragma: no cover + else: # pragma: no cover response = requests.post( base_uri, data=post_data, - headers=headers,verify=False) + headers=headers, verify=False) if response.status_code == 200 or response.status_code == 201: token_json = response.json() try: thetoken = token_json['access_token'] - except KeyError: # pragma: no cover - return [0,0,0] + except KeyError: # pragma: no cover + return [0, 0, 0] try: refresh_token = token_json['refresh_token'] - except KeyError: # pragma: no cover + except KeyError: # pragma: no cover refresh_token = '' try: expires_in = token_json['expires_in'] - except KeyError: # pragma: no cover + except KeyError: # pragma: no cover expires_in = 0 try: expires_in = int(expires_in) - except (ValueError,TypeError): # pragma: no cover + except (ValueError, TypeError): # pragma: no cover expires_in = 0 - else: # pragma: no cover - return [0,response.text,0] + else: # pragma: no cover + return [0, response.text, 0] - - return [thetoken,expires_in,refresh_token] + return [thetoken, expires_in, refresh_token] # Make authorization URL including random string -def imports_make_authorization_url(oauth_data): # pragma: no cover + + +def imports_make_authorization_url(oauth_data): # pragma: no cover # Generate a random string for the state parameter # Save it for use later to prevent xsrf attacks @@ -274,9 +270,8 @@ def imports_make_authorization_url(oauth_data): # pragma: no cover params = {"client_id": oauth_data['client_id'], "response_type": "code", "redirect_uri": oauth_data['redirect_uri'], - "scope":oauth_data['scope'], - "state":state} - + "scope": oauth_data['scope'], + "state": state} import urllib url = oauth_data['authorizaton_uri']+urllib.parse.urlencode(params) @@ -284,27 +279,28 @@ def imports_make_authorization_url(oauth_data): # pragma: no cover return HttpResponseRedirect(url) # This is token refresh. Looks for tokens in our database, then refreshes -def imports_token_refresh(user,tokenname,refreshtokenname,expirydatename,oauth_data): + + +def imports_token_refresh(user, tokenname, refreshtokenname, expirydatename, oauth_data): r = Rower.objects.get(user=user) - refreshtoken = getattr(r,refreshtokenname) + refreshtoken = getattr(r, refreshtokenname) # for Strava transition - if not refreshtoken: # pragma: no cover - refreshtoken = getattr(r,tokenname) + if not refreshtoken: # pragma: no cover + refreshtoken = getattr(r, tokenname) - - res = imports_do_refresh_token(refreshtoken,oauth_data) + res = imports_do_refresh_token(refreshtoken, oauth_data) access_token = res[0] expires_in = res[1] refresh_token = res[2] expirydatetime = timezone.now()+timedelta(seconds=expires_in) - setattr(r,tokenname,access_token) + setattr(r, tokenname, access_token) if expirydatename is not None: - setattr(r,expirydatename,expirydatetime) + setattr(r, expirydatename, expirydatetime) if refreshtokenname is not None: - setattr(r,refreshtokenname,refresh_token) + setattr(r, refreshtokenname, refresh_token) r.save() return access_token diff --git a/rowers/interactiveplots.py b/rowers/interactiveplots.py index 77c77250..bdc8e404 100644 --- a/rowers/interactiveplots.py +++ b/rowers/interactiveplots.py @@ -1,20 +1,70 @@ from __future__ import absolute_import +from rowers.metrics import axes, axlabels, yaxminima, yaxmaxima, get_yaxminima, get_yaxmaxima +from rowers.dataprep import nicepaceformat, niceformat, strfdelta +from rowers.datautils import p0, rpetotss +from rowers.metrics import rowingmetrics, metricsdicts +from scipy.spatial import ConvexHull, Delaunay +from scipy.stats import linregress, percentileofscore +from pytz import timezone as tz, utc +from rowers.models import course_spline, VirtualRaceResult +from bokeh.palettes import Category20c, Category10 +from bokeh.layouts import layout, widgetbox +from bokeh.resources import CDN, INLINE +from math import pi +from rowers.dataprep import timedeltaconv +from pandas.core.groupby.groupby import DataError +import rowers.datautils as datautils +from rowers.utils import lbstoN +import rowers.c2stuff as c2stuff +import rowers.metrics as metrics +import rowers.dataprep as dataprep +from rowers.dataprep import rdata +import rowers.stravastuff as stravastuff +from scipy.interpolate import griddata +from scipy.signal import savgol_filter +from scipy import optimize +from django.utils.timezone import activate +from django.utils.timezone import get_current_timezone +from holoviews import opts +import holoviews as hv +import pandas as pd +import numpy as np +import math +import datetime +from rowers import mytypes +from rowers.courses import ( + course_coord_center, course_coord_maxmin, + polygon_coord_center +) +from django.conf import settings +from collections import OrderedDict +from bokeh.core.properties import value +from rowers.opaque import encoder +from bokeh.models import OpenURL, TapTool +from bokeh.models.glyphs import ImageURL +from bokeh.transform import cumsum +from bokeh.models import ( + LinearAxis, LogAxis, Range1d, DatetimeTickFormatter, HoverTool, Axis, PrintfTickFormatter +) +from bokeh.layouts import column as layoutcolumn +from bokeh.layouts import row as layoutrow +from bokeh.embed import components from __future__ import division from __future__ import print_function from __future__ import unicode_literals import colorsys from rowers.models import ( - Workout, User, Rower, WorkoutForm,RowerForm, - GraphImage,GeoPolygon,GeoCourse,GeoPoint, - ) + Workout, User, Rower, WorkoutForm, RowerForm, + GraphImage, GeoPolygon, GeoCourse, GeoPoint, +) from rowers.tasks import handle_setcp from rowingdata import rower as rrower from rowingdata import main as rmain -from rowingdata import cumcpdata,histodata +from rowingdata import cumcpdata, histodata from rowingdata import rowingdata as rrdata -from math import pi,log2 +from math import pi, log2 from django.utils import timezone from rowingdata import make_cumvalues @@ -22,8 +72,8 @@ from bokeh.palettes import Dark2_8 as palette from bokeh.palettes import Set1_4 as palette2 from bokeh.models.glyphs import MultiLine import itertools -from bokeh.plotting import figure, ColumnDataSource, Figure,curdoc -from bokeh.models import CustomJS,Slider, TextInput,BoxAnnotation, Band +from bokeh.plotting import figure, ColumnDataSource, Figure, curdoc +from bokeh.models import CustomJS, Slider, TextInput, BoxAnnotation, Band import arrow @@ -33,77 +83,13 @@ queue = django_rq.get_queue('default') queuelow = django_rq.get_queue('low') queuehigh = django_rq.get_queue('low') -from bokeh.resources import CDN,INLINE -from bokeh.embed import components -from bokeh.layouts import layout,widgetbox -from bokeh.palettes import Category20c,Category10 -from bokeh.layouts import row as layoutrow -from bokeh.layouts import column as layoutcolumn -from bokeh.models import ( - LinearAxis,LogAxis,Range1d,DatetimeTickFormatter,HoverTool,Axis,PrintfTickFormatter - ) #from bokeh.io import output_file, show, vplot -from bokeh.models import ( - GMapPlot, GMapOptions, ColumnDataSource, Circle, - DataRange1d, PanTool, WheelZoomTool, BoxSelectTool, - SaveTool, # ResizeTool, - ResetTool, TapTool,CrosshairTool,BoxZoomTool, - Span, Label -) -from bokeh.transform import cumsum -from bokeh.models.glyphs import ImageURL -from bokeh.models import OpenURL, TapTool -from rowers.opaque import encoder #from bokeh.models.widgets import Slider, Select, TextInput -from bokeh.core.properties import value -from collections import OrderedDict -from django.conf import settings -from rowers.courses import ( - course_coord_center,course_coord_maxmin, - polygon_coord_center - ) - -from rowers import mytypes -from rowers.models import course_spline,VirtualRaceResult - -import datetime -import math -import numpy as np -import pandas as pd -import holoviews as hv -from holoviews import opts -from pytz import timezone as tz,utc -from django.utils.timezone import get_current_timezone -from django.utils.timezone import activate -from django.utils import timezone activate(settings.TIME_ZONE) thetimezone = get_current_timezone() -from scipy.stats import linregress,percentileofscore -from scipy.spatial import ConvexHull,Delaunay -from scipy import optimize -from scipy.signal import savgol_filter -from scipy.interpolate import griddata - - -import rowers.stravastuff as stravastuff -from rowers.metrics import rowingmetrics,metricsdicts - -from rowers.dataprep import rdata -import rowers.dataprep as dataprep -import rowers.metrics as metrics -import rowers.c2stuff as c2stuff - -from rowers.metrics import axes,axlabels,yaxminima,yaxmaxima,get_yaxminima,get_yaxmaxima - -from rowers.utils import lbstoN -from rowers.datautils import p0,rpetotss -import rowers.datautils as datautils - -from pandas.core.groupby.groupby import DataError - def workoutname(id): try: @@ -113,41 +99,38 @@ def workoutname(id): return str(w) -def all_goldmedalstandards(workouts,startdate,enddate): + +def all_goldmedalstandards(workouts, startdate, enddate): dates = [] testpowers = [] testduration = [] ids = [] for w in workouts: - goldmedalstandard, goldmedalseconds = dataprep.workout_goldmedalstandard(w) + goldmedalstandard, goldmedalseconds = dataprep.workout_goldmedalstandard( + w) if goldmedalseconds > 60: dates.append(arrow.get(w.date).datetime) testpowers.append(goldmedalstandard) testduration.append(goldmedalseconds) ids.append(w.id) - return dates,testpowers,testduration,ids - - + return dates, testpowers, testduration, ids def errorbar(fig, x, y, source=ColumnDataSource(), xerr=False, yerr=False, color='black', point_kwargs={}, error_kwargs={}): - xvalues = source.data[x] yvalues = source.data[y] - xerrvalues = source.data['xerror'] yerrvalues = source.data['yerror'] try: colorvalues = source.data['color'] - except KeyError: # pragma: no cover - colorvalues = ["#%02x%02x%02x" % (255,0,0) for x in xvalues] - + except KeyError: # pragma: no cover + colorvalues = ["#%02x%02x%02x" % (255, 0, 0) for x in xvalues] try: a = xvalues[0]+1 @@ -158,11 +141,12 @@ def errorbar(fig, x, y, source=ColumnDataSource(), for px, py, err, color in zip(xvalues, yvalues, xerrvalues, colorvalues): x_err_x.append((px - err, px + err)) x_err_y.append((py, py)) - (r, g, b) = tuple(int(color[i:i+2],16) for i in (1, 3, 5)) - h,s,v = colorsys.rgb_to_hsv(r/255., g/255., b/255.) + (r, g, b) = tuple(int(color[i:i+2], 16) for i in (1, 3, 5)) + h, s, v = colorsys.rgb_to_hsv(r/255., g/255., b/255.) v = v*0.8 r, g, b = colorsys.hsv_to_rgb(h, s, v) - color2 = "#%02x%02x%02x" % (int(255.*r), int(255.*g), int(255*b)) + color2 = "#%02x%02x%02x" % ( + int(255.*r), int(255.*g), int(255*b)) err_color.append(color2) fig.multi_line(x_err_x, x_err_y, color=err_color, @@ -180,23 +164,24 @@ def errorbar(fig, x, y, source=ColumnDataSource(), for px, py, err, color in zip(xvalues, yvalues, yerrvalues, colorvalues): y_err_x.append((px, px)) y_err_y.append((py - err, py + err)) - (r, g, b) = tuple(int(color[i:i+2],16) for i in (1, 3, 5)) - h,s,v = colorsys.rgb_to_hsv(r/255., g/255., b/255.) + (r, g, b) = tuple(int(color[i:i+2], 16) for i in (1, 3, 5)) + h, s, v = colorsys.rgb_to_hsv(r/255., g/255., b/255.) v = v*0.8 r, g, b = colorsys.hsv_to_rgb(h, s, v) - color2 = "#%02x%02x%02x" % (int(255.*r), int(255.*g), int(255*b)) + color2 = "#%02x%02x%02x" % ( + int(255.*r), int(255.*g), int(255*b)) err_color.append(color2) fig.multi_line(y_err_x, y_err_y, color=err_color, - name='yerr',**error_kwargs) - except TypeError: # pragma: no cover + name='yerr', **error_kwargs) + except TypeError: # pragma: no cover pass - fig.circle(x, y, source=source, name='data',color=color, + fig.circle(x, y, source=source, name='data', color=color, **point_kwargs) -def tailwind(bearing,vwind,winddir): +def tailwind(bearing, vwind, winddir): """ Calculates head-on head/tailwind in direction of rowing positive numbers are tail wind @@ -211,18 +196,11 @@ def tailwind(bearing,vwind,winddir): return vtail -from rowers.dataprep import nicepaceformat,niceformat,strfdelta -from rowers.dataprep import timedeltaconv - -from math import pi - -def interactive_hr_piechart(df,rower,title,totalseconds=0): +def interactive_hr_piechart(df, rower, title, totalseconds=0): if df.empty: - return "","Not enough data to make a chart" + return "", "Not enough data to make a chart" - - - df.sort_values(by='hr',inplace=True) + df.sort_values(by='hr', inplace=True) df['timehr'] = df['deltat']*df['hr'] sumtimehr = df['deltat'].sum() @@ -236,24 +214,23 @@ def interactive_hr_piechart(df,rower,title,totalseconds=0): qrydata = df.query(qry) frac_lut2 = totalseconds*qrydata['deltat'].sum()/sumtimehr - - qry = '{ut2} <= hr < {ut1}'.format(ut1=rower.ut1,ut2=rower.ut2) + qry = '{ut2} <= hr < {ut1}'.format(ut1=rower.ut1, ut2=rower.ut2) frac_ut2 = totalseconds*df.query(qry)['deltat'].sum()/sumtimehr - qry = '{ut1} <= hr < {at}'.format(ut1=rower.ut1,at=rower.at) + qry = '{ut1} <= hr < {at}'.format(ut1=rower.ut1, at=rower.at) frac_ut1 = totalseconds*df.query(qry)['deltat'].sum()/sumtimehr - qry = '{at} <= hr < {tr}'.format(at=rower.at,tr=rower.tr) + qry = '{at} <= hr < {tr}'.format(at=rower.at, tr=rower.tr) frac_at = totalseconds*df.query(qry)['deltat'].sum()/sumtimehr - qry = '{tr} <= hr < {an}'.format(tr=rower.tr,an=rower.an) + qry = '{tr} <= hr < {an}'.format(tr=rower.tr, an=rower.an) frac_tr = totalseconds*df.query(qry)['deltat'].sum()/sumtimehr qry = 'hr >= {an}'.format(an=rower.an) frac_an = totalseconds*df.query(qry)['deltat'].sum()/sumtimehr datadict = { - '<{ut2}'.format(ut2=hrzones[1]):frac_lut2, + '<{ut2}'.format(ut2=hrzones[1]): frac_lut2, '{ut2}'.format(ut2=hrzones[1]): frac_ut2, '{ut1}'.format(ut1=hrzones[2]): frac_ut1, '{at}'.format(at=hrzones[3]): frac_at, @@ -261,13 +238,10 @@ def interactive_hr_piechart(df,rower,title,totalseconds=0): '{an}'.format(an=hrzones[5]): frac_an, } + colors = ['gray', 'yellow', 'lime', 'blue', 'purple', 'red'] - - colors = ['gray','yellow','lime','blue','purple','red'] - - - - data = pd.Series(datadict).reset_index(name='value').rename(columns={'index':'zone'}) + data = pd.Series(datadict).reset_index( + name='value').rename(columns={'index': 'zone'}) data['angle'] = data['value']/data['value'].sum() * 2*pi data['color'] = colors data['zone'] = [ @@ -281,23 +255,16 @@ def interactive_hr_piechart(df,rower,title,totalseconds=0): data['totaltime'] = pd.Series([pretty_timedelta(v) for v in data['value']]) - - - size=350 + size = 350 TOOLS = 'save,hover' + z = figure(title="HR "+title, x_range=(-0.5, 1), plot_height=375, + tools=TOOLS, toolbar_location=None, tooltips="@zone: @totaltime", + ) - z = figure(title="HR "+title, x_range=(-0.5,1), plot_height=375, - tools=TOOLS,toolbar_location=None,tooltips="@zone: @totaltime", - ) - - - - z.wedge(x=0,y=1, radius=0.4, - start_angle=cumsum('angle',include_zero=True), end_angle=cumsum('angle'), - line_color='white',fill_color='color',source=data,legend_group='zone') - - + z.wedge(x=0, y=1, radius=0.4, + start_angle=cumsum('angle', include_zero=True), end_angle=cumsum('angle'), + line_color='white', fill_color='color', source=data, legend_group='zone') z.axis.axis_label = None z.axis.visible = False @@ -305,66 +272,65 @@ def interactive_hr_piechart(df,rower,title,totalseconds=0): z.outline_line_color = None z.toolbar_location = 'right' - return components(z) -def pretty_timedelta(secs): - hours, remainder = divmod(secs,3600) - minutes, seconds = divmod(remainder,60) - return '{}:{:02}:{:02}'.format(int(hours),int(minutes),int(seconds)) +def pretty_timedelta(secs): + hours, remainder = divmod(secs, 3600) + minutes, seconds = divmod(remainder, 60) + + return '{}:{:02}:{:02}'.format(int(hours), int(minutes), int(seconds)) + def mapcolors(x): try: return mytypes.color_map[x] - except KeyError: # pragma: no cover + except KeyError: # pragma: no cover return mytypes.colors[-1] + def interactive_workouttype_piechart(workouts): if len(workouts) == 0: - return "","Not enough workouts to make a chart" + return "", "Not enough workouts to make a chart" datadict = {} - for w in workouts: try: # label = mytypes.workouttypes_ordered[w.workouttype] label = w.workouttype - except KeyError: # pragma: no cover + except KeyError: # pragma: no cover label = w.workouttype try: - datadict[label] += 60*(60*w.duration.hour+w.duration.minute)+w.duration.second + datadict[label] += 60*(60*w.duration.hour + + w.duration.minute)+w.duration.second except KeyError: - datadict[label] = 60*(60*w.duration.hour+w.duration.minute)+w.duration.second + datadict[label] = 60*(60*w.duration.hour + + w.duration.minute)+w.duration.second - data = pd.Series(datadict).reset_index(name='value').rename(columns={'index':'type'}) + data = pd.Series(datadict).reset_index( + name='value').rename(columns={'index': 'type'}) data['angle'] = data['value']/data['value'].sum() * 2*pi - - - data = pd.DataFrame(data) - data['color'] = data['type'].apply(lambda x:mapcolors(x)) - data['totaltime'] = data['value'].apply(lambda x:pretty_timedelta(x)) + data['color'] = data['type'].apply(lambda x: mapcolors(x)) + data['totaltime'] = data['value'].apply(lambda x: pretty_timedelta(x)) try: - data['type'] = data['type'].apply(lambda x:mytypes.workouttypes_ordered[x]) - except KeyError: # pragma: no cover + data['type'] = data['type'].apply( + lambda x: mytypes.workouttypes_ordered[x]) + except KeyError: # pragma: no cover pass - - - p = figure(plot_height=350, title="Types", toolbar_location=None, - tools="hover,save", tooltips="@type: @totaltime", x_range=(-0.5, 1.0)) + tools="hover,save", tooltips="@type: @totaltime", x_range=(-0.5, 1.0)) p.wedge(x=0, y=1, radius=0.4, - start_angle=cumsum('angle', include_zero=True), end_angle=cumsum('angle'), - line_color="white", fill_color='color', source=data,legend_group='type', ) + start_angle=cumsum('angle', include_zero=True), end_angle=cumsum('angle'), + line_color="white", fill_color='color', source=data, legend_group='type', ) - p.axis.axis_label=None - p.axis.visible=False + p.axis.axis_label = None + p.axis.visible = False p.grid.grid_line_color = None p.outline_line_color = None p.toolbar_location = 'right' @@ -372,119 +338,113 @@ def interactive_workouttype_piechart(workouts): return components(p) +def interactive_boxchart(datadf, fieldname, extratitle='', + spmmin=0, spmmax=0, workmin=0, workmax=0): -def interactive_boxchart(datadf,fieldname,extratitle='', - spmmin=0,spmmax=0,workmin=0,workmax=0): - - if datadf.empty: # pragma: no cover - return '','It looks like there are no data matching your filter' + if datadf.empty: # pragma: no cover + return '', 'It looks like there are no data matching your filter' columns = datadf.columns - if not fieldname in columns: # pragma: no cover - return '','It looks like there are no data matching your filter' - - if not 'date' in columns: # pragma: no cover - return '','Not enough data' + if not fieldname in columns: # pragma: no cover + return '', 'It looks like there are no data matching your filter' + if not 'date' in columns: # pragma: no cover + return '', 'Not enough data' tooltips = [ ('Value', '@'+fieldname), - ] + ] hover = HoverTool(tooltips=tooltips) TOOLS = [hover] - hv.extension('bokeh') try: - boxwhiskers = hv.BoxWhisker(datadf,'date',fieldname) - boxwhiskers.opts(tools=TOOLS,outlier_color='white') - except DataError: # pragma: no cover - return "","Invalid Data" - + boxwhiskers = hv.BoxWhisker(datadf, 'date', fieldname) + boxwhiskers.opts(tools=TOOLS, outlier_color='white') + except DataError: # pragma: no cover + return "", "Invalid Data" plot = hv.render(boxwhiskers) - yrange1 = Range1d(start=yaxminima[fieldname],end=yaxmaxima[fieldname]) + yrange1 = Range1d(start=yaxminima[fieldname], end=yaxmaxima[fieldname]) plot.y_range = yrange1 plot.sizing_mode = 'stretch_both' plot.xaxis.axis_label = 'Date' plot.yaxis.axis_label = axlabels[fieldname] - plot.xaxis.formatter = DatetimeTickFormatter( days=["%d %B %Y"], months=["%d %B %Y"], years=["%d %B %Y"], - ) + ) - if fieldname == 'pace': # pragma: no cover + if fieldname == 'pace': # pragma: no cover plot.yaxis[0].formatter = DatetimeTickFormatter( - seconds = ["%S"], - minutes = ["%M"] - ) - + seconds=["%S"], + minutes=["%M"] + ) plot.xaxis.major_label_orientation = pi/4 - plot.plot_width=920 - plot.plot_height=600 + plot.plot_width = 920 + plot.plot_height = 600 slidertext = 'SPM: {:.0f}-{:.0f}, WpS: {:.0f}-{:.0f}'.format( - spmmin,spmmax,workmin,workmax - ) - sliderlabel = Label(x=50,y=20,x_units='screen',y_units='screen', + spmmin, spmmax, workmin, workmax + ) + sliderlabel = Label(x=50, y=20, x_units='screen', y_units='screen', text=slidertext, background_fill_alpha=0.7, background_fill_color='white', - text_color='black',text_font_size='10pt', + text_color='black', text_font_size='10pt', ) plot.add_layout(sliderlabel) script, div = components(plot) - return script,div + return script, div -def interactive_planchart(data,startdate,enddate): +def interactive_planchart(data, startdate, enddate): source = ColumnDataSource(data) hv.extension('bokeh') - yaxmaximum = data['executed'].max() - if data['planned'].max() > yaxmaximum: # pragma: no cover + if data['planned'].max() > yaxmaximum: # pragma: no cover yaxmaximum = data['planned'].max() - if yaxmaximum == 0: # pragma: no cover + if yaxmaximum == 0: # pragma: no cover yaxmaximum = 250 - yrange1 = Range1d(start=0,end=1.1*yaxmaximum) + yrange1 = Range1d(start=0, end=1.1*yaxmaximum) - - tidy_df = data.melt(id_vars=['startdate'],value_vars=['executed','planned']) - bars = hv.Bars(tidy_df,['startdate','variable'],['value']) + tidy_df = data.melt(id_vars=['startdate'], value_vars=[ + 'executed', 'planned']) + bars = hv.Bars(tidy_df, ['startdate', 'variable'], ['value']) bars.opts( - opts.Bars(show_legend=True,tools=['tap','hover'],legend_position='bottom',show_frame=True)) + opts.Bars(show_legend=True, tools=['tap', 'hover'], legend_position='bottom', show_frame=True)) p = hv.render(bars) - p.plot_width=550 - p.plot_height=350 + p.plot_width = 550 + p.plot_height = 350 p.y_range = yrange1 p.toolbar_location = 'above' p.sizing_mode = 'stretch_both' - script,div = components(p) + script, div = components(p) - return script,div + return script, div -def interactive_activitychart(workouts,startdate,enddate,stack='type',toolbar_location=None, + +def interactive_activitychart(workouts, startdate, enddate, stack='type', toolbar_location=None, yaxis='trimp'): dates = [] @@ -496,33 +456,33 @@ def interactive_activitychart(workouts,startdate,enddate,stack='type',toolbar_lo trimps = [] links = [] - rowersinitials = {} seen = ['seen'] idseen = [] - startdate = datetime.datetime(year=startdate.year,month=startdate.month,day=startdate.day) - enddate = datetime.datetime(year=enddate.year,month=enddate.month,day=enddate.day) + startdate = datetime.datetime( + year=startdate.year, month=startdate.month, day=startdate.day) + enddate = datetime.datetime( + year=enddate.year, month=enddate.month, day=enddate.day) duration = enddate-startdate totaldays = duration.total_seconds()/(24*3600) - for w in workouts: - aantal=1 - initials = w.user.user.first_name[0:aantal]+w.user.user.last_name[0:aantal] + aantal = 1 + initials = w.user.user.first_name[0:aantal] + \ + w.user.user.last_name[0:aantal] if w.user.id not in idseen: - while initials in seen: # pragma: no cover + while initials in seen: # pragma: no cover aantal += 1 - initials = w.user.user.first_name[0:aantal]+w.user.user.last_name[0:aantal] + initials = w.user.user.first_name[0:aantal] + \ + w.user.user.last_name[0:aantal] seen.append(initials) idseen.append(w.user.id) rowersinitials[w.user.id] = initials - - for w in workouts: dd = w.date.strftime('%m/%d') dd2 = w.date.strftime('%Y/%m/%d') @@ -531,13 +491,13 @@ def interactive_activitychart(workouts,startdate,enddate,stack='type',toolbar_lo rscore = w.rscore trimp = w.trimp - if rscore == 0: # pragma: no cover + if rscore == 0: # pragma: no cover rscore = w.hrtss - if totaldays<30: + if totaldays < 30: dates.append(dd) dates_sorting.append(dd2) - else: # pragma: no cover + else: # pragma: no cover dates.append(dd3) dates_sorting.append(dd3) durations.append(du) @@ -545,36 +505,35 @@ def interactive_activitychart(workouts,startdate,enddate,stack='type',toolbar_lo trimps.append(trimp) links.append( "{siteurl}/rowers/workout/{code}/".format( - siteurl = settings.SITE_URL, - code = encoder.encode_hex(w.id) - ) + siteurl=settings.SITE_URL, + code=encoder.encode_hex(w.id) + ) ) - types.append(w.workouttype) try: rowers.append(rowersinitials[w.user.id]) - except IndexError: # pragma: no cover + except IndexError: # pragma: no cover rowers.append(str(w.user)) try: d = utc.localize(startdate) - except (ValueError,AttributeError): # pragma: no cover + except (ValueError, AttributeError): # pragma: no cover d = startdate try: enddate = utc.localize(enddate) - except (ValueError,AttributeError): # pragma: no cover + except (ValueError, AttributeError): # pragma: no cover pass # add dates with no activity - while d<=enddate: + while d <= enddate: dd = d.strftime('%d') - if totaldays<30: + if totaldays < 30: dates.append(d.strftime('%m/%d')) dates_sorting.append(d.strftime('%Y/%m/%d')) - else: # pragma: no cover + else: # pragma: no cover dates.append(d.strftime('%Y/%m')) dates_sorting.append(d.strftime('%Y/%m')) durations.append(0) @@ -596,17 +555,16 @@ def interactive_activitychart(workouts,startdate,enddate,stack='type',toolbar_lo d += datetime.timedelta(days=1) - thedict = { - 'date':dates, - 'date_sorting':dates_sorting, - 'duration':durations, - 'trimp':trimps, - 'rscore':rscores, - 'type':types, - 'rower':rowers, - 'link':links, - } + 'date': dates, + 'date_sorting': dates_sorting, + 'duration': durations, + 'trimp': trimps, + 'rscore': rscores, + 'type': types, + 'rower': rowers, + 'link': links, + } dim_expr = hv.dim('type').categorize(mytypes.color_map) @@ -614,50 +572,47 @@ def interactive_activitychart(workouts,startdate,enddate,stack='type',toolbar_lo source = ColumnDataSource(df) - df.sort_values('date_sorting',inplace=True) + df.sort_values('date_sorting', inplace=True) #clrs = hv.Cycle(df['colors'].values) hv.extension('bokeh') if stack == 'type': - table = hv.Table(df,[('date','Date'),('type','Workout Type')], - [('duration','Minutes'),('rscore','rScore'),('trimp','TRIMP'),('link','link')]) + table = hv.Table(df, [('date', 'Date'), ('type', 'Workout Type')], + [('duration', 'Minutes'), ('rscore', 'rScore'), ('trimp', 'TRIMP'), ('link', 'link')]) else: - table = hv.Table(df,[('date','Date'),('rower','Rower')], - [('duration','Minutes'),('rscore','rScore'),('trimp','TRIMP'),('link','link')]) + table = hv.Table(df, [('date', 'Date'), ('rower', 'Rower')], + [('duration', 'Minutes'), ('rscore', 'rScore'), ('trimp', 'TRIMP'), ('link', 'link')]) - - bars=table.to.bars(['date',stack],[yaxis]) + bars = table.to.bars(['date', stack], [yaxis]) if stack == 'type': bars.opts( opts.Bars(cmap=mytypes.color_map, show_legend=True, stacked=True, - tools=['tap','hover'], width=550, xrotation=45,padding=(0,(0,.1)), - legend_position='bottom',show_frame=True)) + tools=['tap', 'hover'], width=550, xrotation=45, padding=(0, (0, .1)), + legend_position='bottom', show_frame=True)) else: bars.opts( opts.Bars(cmap='Category10', show_legend=True, stacked=True, - tools=['tap','hover'], width=550, xrotation=45,padding=(0,(0,.1)), - legend_position='bottom',show_frame=True)) + tools=['tap', 'hover'], width=550, xrotation=45, padding=(0, (0, .1)), + legend_position='bottom', show_frame=True)) p = hv.render(bars) p.title.text = 'Activity {d1} to {d2}'.format( - d1 = startdate.strftime("%Y-%m-%d"), - d2 = enddate.strftime("%Y-%m-%d"), - ) + d1=startdate.strftime("%Y-%m-%d"), + d2=enddate.strftime("%Y-%m-%d"), + ) - p.plot_width=550 - p.plot_height=350 + p.plot_width = 550 + p.plot_height = 350 p.toolbar_location = toolbar_location p.y_range.start = 0 p.sizing_mode = 'stretch_both' url = "http://rowsandall.com/rowers/workout/@duration/" taptool = p.select(type=TapTool) - - - callback = CustomJS(args={'links':df.link}, code=""" + callback = CustomJS(args={'links': df.link}, code=""" var index = cb_data.source.selected['1d'].indices[0]; console.log(links); console.log(index); @@ -665,16 +620,15 @@ def interactive_activitychart(workouts,startdate,enddate,stack='type',toolbar_lo window.location.href = links[index] """) - taptool.js_on_event('tap',callback) + taptool.js_on_event('tap', callback) + + script, div = components(p) + return script, div - script,div = components(p) - return script,div - -def interactive_activitychart2(workouts,startdate,enddate,stack='type',toolbar_location=None, +def interactive_activitychart2(workouts, startdate, enddate, stack='type', toolbar_location=None, yaxis='duration'): - dates = [] dates_sorting = [] types = [] @@ -689,28 +643,29 @@ def interactive_activitychart2(workouts,startdate,enddate,stack='type',toolbar_l seen = ['seen'] idseen = [] - startdate = datetime.datetime(year=startdate.year,month=startdate.month,day=startdate.day) - enddate = datetime.datetime(year=enddate.year,month=enddate.month,day=enddate.day) + startdate = datetime.datetime( + year=startdate.year, month=startdate.month, day=startdate.day) + enddate = datetime.datetime( + year=enddate.year, month=enddate.month, day=enddate.day) duration = enddate-startdate totaldays = duration.total_seconds()/(24*3600) - for w in workouts: - aantal=1 - initials = w.user.user.first_name[0:aantal]+w.user.user.last_name[0:aantal] + aantal = 1 + initials = w.user.user.first_name[0:aantal] + \ + w.user.user.last_name[0:aantal] if w.user.id not in idseen: - while initials in seen: # pragma: no cover + while initials in seen: # pragma: no cover aantal += 1 - initials = w.user.user.first_name[0:aantal]+w.user.user.last_name[0:aantal] + initials = w.user.user.first_name[0:aantal] + \ + w.user.user.last_name[0:aantal] seen.append(initials) idseen.append(w.user.id) rowersinitials[w.user.id] = initials - - for w in workouts: dd = w.date.strftime('%m/%d') dd2 = w.date.strftime('%Y/%m/%d') @@ -720,10 +675,10 @@ def interactive_activitychart2(workouts,startdate,enddate,stack='type',toolbar_l trimp = w.trimp rscore = w.rscore distance = w.distance - if rscore == 0: # pragma: no cover + if rscore == 0: # pragma: no cover rscore = w.hrtss - if totaldays<=30: # pragma: no cover + if totaldays <= 30: # pragma: no cover dates.append(dd) dates_sorting.append(dd2) else: @@ -735,34 +690,32 @@ def interactive_activitychart2(workouts,startdate,enddate,stack='type',toolbar_l distances.append(distance) links.append( "{siteurl}/rowers/workout/{code}/".format( - siteurl = settings.SITE_URL, - code = encoder.encode_hex(w.id) - ) + siteurl=settings.SITE_URL, + code=encoder.encode_hex(w.id) + ) ) - types.append(w.workouttype) try: rowers.append(rowersinitials[w.user.id]) - except IndexError: # pragma: no cover + except IndexError: # pragma: no cover rowers.append(str(w.user)) try: d = utc.localize(startdate) - except (ValueError,AttributeError): # pragma: no cover + except (ValueError, AttributeError): # pragma: no cover d = startdate try: enddate = utc.localize(enddate) - except (ValueError,AttributeError): # pragma: no cover + except (ValueError, AttributeError): # pragma: no cover pass # add dates with no activity - while d<=enddate: + while d <= enddate: dd = d.strftime('%d') - - if totaldays<=30: + if totaldays <= 30: dates.append(d.strftime('%m/%d')) dates_sorting.append(d.strftime('%Y/%m/%d')) else: @@ -785,99 +738,88 @@ def interactive_activitychart2(workouts,startdate,enddate,stack='type',toolbar_l d += datetime.timedelta(days=1) - thedict = { - 'date':dates, - 'date_sorting':dates_sorting, - 'duration':durations, - 'trimp':trimps, - 'rscore':rscores, - 'type':types, - 'rower':rowers, - 'distance':distances, - 'link':links, - } + 'date': dates, + 'date_sorting': dates_sorting, + 'duration': durations, + 'trimp': trimps, + 'rscore': rscores, + 'type': types, + 'rower': rowers, + 'distance': distances, + 'link': links, + } dim_expr = hv.dim('type').categorize(mytypes.color_map) - - df = pd.DataFrame(thedict) - if totaldays>30 and yaxis=='duration': # pragma: no cover + if totaldays > 30 and yaxis == 'duration': # pragma: no cover df['duration'] = df['duration']/60 elif yaxis == 'TRIMP': - df.drop('duration',inplace=True,axis='columns') - df.drop('rscore',inplace=True,axis='columns') - df.drop('distance',inplace=True, axis='columns') - elif yaxis == 'rScore': # pragma: no cover - df.drop('duration',inplace=True,axis='columns') - df.drop('trimp',inplace=True,axis='columns' ) - df.drop('distance',inplace=True, axis='columns') - elif yaxis == 'distance': # pragma: no cover - df.drop('duration',inplace=True,axis='columns') - df.drop('trimp', inplace=True,axis='columns') - df.drop('rscore',inplace=True,axis='columns') + df.drop('duration', inplace=True, axis='columns') + df.drop('rscore', inplace=True, axis='columns') + df.drop('distance', inplace=True, axis='columns') + elif yaxis == 'rScore': # pragma: no cover + df.drop('duration', inplace=True, axis='columns') + df.drop('trimp', inplace=True, axis='columns') + df.drop('distance', inplace=True, axis='columns') + elif yaxis == 'distance': # pragma: no cover + df.drop('duration', inplace=True, axis='columns') + df.drop('trimp', inplace=True, axis='columns') + df.drop('rscore', inplace=True, axis='columns') - df['color'] = df['type'].apply(lambda x:mapcolors(x)) + df['color'] = df['type'].apply(lambda x: mapcolors(x)) - df.sort_values('date_sorting',inplace=True) + df.sort_values('date_sorting', inplace=True) #clrs = hv.Cycle(df['colors'].values) source = ColumnDataSource(df) hv.extension('bokeh') - - #table = hv.Table(df,[('date','Date'),('type','Workout Type')], + # table = hv.Table(df,[('date','Date'),('type','Workout Type')], # [('duration','Minutes'),('trimp','TRIMP'),('rscore','rScore'),('link','link')]) types_order = mytypes.workouttypes_ordered - #bars=table.to.bars(['date',stack],[yaxis]) - bars = hv.Bars(df, kdims=['date',stack]).aggregate(function=np.sum).redim.values(types=types_order) + # bars=table.to.bars(['date',stack],[yaxis]) + bars = hv.Bars(df, kdims=['date', stack]).aggregate( + function=np.sum).redim.values(types=types_order) - - #print(mytypes.color_map) + # print(mytypes.color_map) bars.opts( opts.Bars(cmap=mytypes.color_map, show_legend=True, stacked=True, - tools=['tap','hover'], width=550, xrotation=45,padding=(0,(0,.1)), - legend_position='bottom',show_frame=True)) - + tools=['tap', 'hover'], width=550, xrotation=45, padding=(0, (0, .1)), + legend_position='bottom', show_frame=True)) p = hv.render(bars) p.title.text = 'Activity {d1} to {d2}'.format( - d1 = startdate.strftime("%Y-%m-%d"), - d2 = enddate.strftime("%Y-%m-%d"), - ) + d1=startdate.strftime("%Y-%m-%d"), + d2=enddate.strftime("%Y-%m-%d"), + ) p.xaxis.axis_label = 'Period' if yaxis == 'duration': p.yaxis.axis_label = 'Duration (min)' - if totaldays>30: # pragma: no cover + if totaldays > 30: # pragma: no cover p.yaxis.axis_label = 'Duration (h)' elif yaxis == 'TRIMP': p.yaxis.axis_label = 'TRIMP' - elif yaxis == 'distance': # pragma: no cover + elif yaxis == 'distance': # pragma: no cover p.yaxis.axis_label = 'Distance (m)' - else: # pragma: no cover + else: # pragma: no cover p.yaxis.axis_label = 'rScore' - - - - - p.plot_width=550 - p.plot_height=350 + p.plot_width = 550 + p.plot_height = 350 p.toolbar_location = toolbar_location p.sizing_mode = 'stretch_both' p.y_range.start = 0 url = "http://rowsandall.com/rowers/workout/@duration/" taptool = p.select(type=TapTool) - - - callback = CustomJS(args={'links':df['link']}, code=""" + callback = CustomJS(args={'links': df['link']}, code=""" var index = cb_data.source.selected['1d'].indices[0]; console.log(links); console.log(index); @@ -885,54 +827,54 @@ def interactive_activitychart2(workouts,startdate,enddate,stack='type',toolbar_l window.location.href = links[index] """) - taptool.js_on_event('tap',callback) + taptool.js_on_event('tap', callback) + + script, div = components(p) + return script, div - script,div = components(p) - return script,div - -def interactive_forcecurve(theworkouts,workstrokesonly=True,plottype='scatter'): +def interactive_forcecurve(theworkouts, workstrokesonly=True, plottype='scatter'): TOOLS = 'save,pan,box_zoom,wheel_zoom,reset,tap,hover,crosshair' ids = [int(w.id) for w in theworkouts] boattype = theworkouts[0].boattype - columns = ['catch','slip','wash','finish','averageforce', - 'peakforceangle','peakforce','spm','distance', - 'workoutstate','driveenergy'] + columns = ['catch', 'slip', 'wash', 'finish', 'averageforce', + 'peakforceangle', 'peakforce', 'spm', 'distance', + 'workoutstate', 'driveenergy'] - - rowdata = dataprep.getsmallrowdata_db(columns,ids=ids, + rowdata = dataprep.getsmallrowdata_db(columns, ids=ids, workstrokesonly=workstrokesonly) - rowdata.dropna(axis=1,how='all',inplace=True) - rowdata.dropna(axis=0,how='any',inplace=True) + rowdata.dropna(axis=1, how='all', inplace=True) + rowdata.dropna(axis=0, how='any', inplace=True) - workoutstateswork = [1,4,5,8,9,6,7] + workoutstateswork = [1, 4, 5, 8, 9, 6, 7] workoutstatesrest = [3] - workoutstatetransition = [0,2,10,11,12,13] + workoutstatetransition = [0, 2, 10, 11, 12, 13] if workstrokesonly: try: rowdata = rowdata[~rowdata['workoutstate'].isin(workoutstatesrest)] - except KeyError: # pragma: no cover + except KeyError: # pragma: no cover pass if rowdata.empty: - return "","No Valid Data Available","","" - + return "", "No Valid Data Available", "", "" # quick linear regression # peakforce = slope*peakforceangle + intercept try: - slope, intercept, r,p,stderr = linregress(rowdata['peakforceangle'],rowdata['peakforce']) - except KeyError: # pragma: no cover + slope, intercept, r, p, stderr = linregress( + rowdata['peakforceangle'], rowdata['peakforce']) + except KeyError: # pragma: no cover slope = 0 intercept = 0 try: - covariancematrix = np.cov(rowdata['peakforceangle'],y=rowdata['peakforce']) + covariancematrix = np.cov( + rowdata['peakforceangle'], y=rowdata['peakforce']) eig_vals, eig_vecs = np.linalg.eig(covariancematrix) a = rowdata['peakforceangle']-rowdata['peakforceangle'].median() @@ -941,9 +883,8 @@ def interactive_forcecurve(theworkouts,workstrokesonly=True,plottype='scatter'): Rinv = eig_vecs R = np.linalg.inv(Rinv) - x = R[0,0]*a+R[0,1]*F - y = R[1,0]*a+R[1,1]*F - + x = R[0, 0]*a+R[0, 1]*F + y = R[1, 0]*a+R[1, 1]*F x05 = x.quantile(q=0.01) x25 = x.quantile(q=0.15) @@ -955,31 +896,30 @@ def interactive_forcecurve(theworkouts,workstrokesonly=True,plottype='scatter'): y75 = y.quantile(q=0.85) y95 = y.quantile(q=0.99) - a25 = Rinv[0,0]*x25 + rowdata['peakforceangle'].median() - F25 = Rinv[1,0]*x25 + rowdata['peakforce'].median() + a25 = Rinv[0, 0]*x25 + rowdata['peakforceangle'].median() + F25 = Rinv[1, 0]*x25 + rowdata['peakforce'].median() - a25b = Rinv[0,1]*y25 + rowdata['peakforceangle'].median() - F25b = Rinv[1,1]*y25 + rowdata['peakforce'].median() + a25b = Rinv[0, 1]*y25 + rowdata['peakforceangle'].median() + F25b = Rinv[1, 1]*y25 + rowdata['peakforce'].median() - a75 = Rinv[0,0]*x75 + rowdata['peakforceangle'].median() - F75 = Rinv[1,0]*x75 + rowdata['peakforce'].median() + a75 = Rinv[0, 0]*x75 + rowdata['peakforceangle'].median() + F75 = Rinv[1, 0]*x75 + rowdata['peakforce'].median() - a75b = Rinv[0,1]*y75 + rowdata['peakforceangle'].median() - F75b = Rinv[1,1]*y75 + rowdata['peakforce'].median() + a75b = Rinv[0, 1]*y75 + rowdata['peakforceangle'].median() + F75b = Rinv[1, 1]*y75 + rowdata['peakforce'].median() + a05 = Rinv[0, 0]*x05 + rowdata['peakforceangle'].median() + F05 = Rinv[1, 0]*x05 + rowdata['peakforce'].median() - a05 = Rinv[0,0]*x05 + rowdata['peakforceangle'].median() - F05 = Rinv[1,0]*x05 + rowdata['peakforce'].median() + a05b = Rinv[0, 1]*y05 + rowdata['peakforceangle'].median() + F05b = Rinv[1, 1]*y05 + rowdata['peakforce'].median() - a05b = Rinv[0,1]*y05 + rowdata['peakforceangle'].median() - F05b = Rinv[1,1]*y05 + rowdata['peakforce'].median() + a95 = Rinv[0, 0]*x95 + rowdata['peakforceangle'].median() + F95 = Rinv[1, 0]*x95 + rowdata['peakforce'].median() - a95 = Rinv[0,0]*x95 + rowdata['peakforceangle'].median() - F95 = Rinv[1,0]*x95 + rowdata['peakforce'].median() - - a95b = Rinv[0,1]*y95 + rowdata['peakforceangle'].median() - F95b = Rinv[1,1]*y95 + rowdata['peakforce'].median() - except KeyError: # pragma: no cover + a95b = Rinv[0, 1]*y95 + rowdata['peakforceangle'].median() + F95b = Rinv[1, 1]*y95 + rowdata['peakforce'].median() + except KeyError: # pragma: no cover a25 = 0 F25 = 0 @@ -992,7 +932,6 @@ def interactive_forcecurve(theworkouts,workstrokesonly=True,plottype='scatter'): a75b = 0 F75b = 0 - a05 = 0 F05 = 0 @@ -1005,15 +944,13 @@ def interactive_forcecurve(theworkouts,workstrokesonly=True,plottype='scatter'): a95b = 0 F95b = 0 - - try: catchav = rowdata['catch'].median() catch25 = rowdata['catch'].quantile(q=0.25) catch75 = rowdata['catch'].quantile(q=0.75) catch05 = rowdata['catch'].quantile(q=0.05) catch95 = rowdata['catch'].quantile(q=0.95) - except KeyError: # pragma: no cover + except KeyError: # pragma: no cover catchav = 0 catch25 = 0 catch75 = 0 @@ -1026,7 +963,7 @@ def interactive_forcecurve(theworkouts,workstrokesonly=True,plottype='scatter'): finish75 = rowdata['finish'].quantile(q=0.75) finish05 = rowdata['finish'].quantile(q=0.05) finish95 = rowdata['finish'].quantile(q=0.95) - except KeyError: # pragma: no cover + except KeyError: # pragma: no cover finishav = 0 finish25 = 0 finish75 = 0 @@ -1039,7 +976,7 @@ def interactive_forcecurve(theworkouts,workstrokesonly=True,plottype='scatter'): wash75 = (rowdata['finish']-rowdata['wash']).quantile(q=0.75) wash05 = (rowdata['finish']-rowdata['wash']).quantile(q=0.05) wash95 = (rowdata['finish']-rowdata['wash']).quantile(q=0.95) - except KeyError: # pragma: no cover + except KeyError: # pragma: no cover washav = 0 wash25 = 0 wash75 = 0 @@ -1052,7 +989,7 @@ def interactive_forcecurve(theworkouts,workstrokesonly=True,plottype='scatter'): slip75 = (rowdata['slip']+rowdata['catch']).quantile(q=0.75) slip05 = (rowdata['slip']+rowdata['catch']).quantile(q=0.05) slip95 = (rowdata['slip']+rowdata['catch']).quantile(q=0.95) - except KeyError: # pragma: no cover + except KeyError: # pragma: no cover slipav = 0 slip25 = 0 slip75 = 0 @@ -1065,17 +1002,16 @@ def interactive_forcecurve(theworkouts,workstrokesonly=True,plottype='scatter'): peakforce75 = rowdata['peakforce'].quantile(q=0.75) peakforce05 = rowdata['peakforce'].quantile(q=0.05) peakforce95 = rowdata['peakforce'].quantile(q=0.95) - except KeyError: # pragma: no cover + except KeyError: # pragma: no cover peakforceav = 0 peakforce25 = 0 peakforce75 = 0 peakforce05 = 0 peakforce95 = 0 - try: averageforceav = rowdata['averageforce'].median() - except KeyError: # pragma: no cover + except KeyError: # pragma: no cover averageforceav = 0 try: @@ -1084,152 +1020,141 @@ def interactive_forcecurve(theworkouts,workstrokesonly=True,plottype='scatter'): peakforceangle25 = rowdata['peakforceangle'].quantile(q=0.25) peakforceangle75 = rowdata['peakforceangle'].quantile(q=0.75) peakforceangle95 = rowdata['peakforceangle'].quantile(q=0.95) - except KeyError: # pragma: no cover + except KeyError: # pragma: no cover peakforceangleav = 0 peakforceangle25 = 0 peakforceangle75 = 0 peakforceangle05 = 0 peakforceangle95 = 0 - - #thresholdforce /= 4.45 # N to lbs + # thresholdforce /= 4.45 # N to lbs thresholdforce = 100 if 'x' in boattype else 200 points2575 = [ - (catch25,0), #0 - (slip25,thresholdforce), #1 - (a75,F75),#4 - (a25b,F25b), #9 - (a25,F25), #2 - (wash75,thresholdforce), #5 - (finish75,0), #6 - (finish25,0), #7 - (wash25,thresholdforce), #8 - (a75b,F75b), #3 - (slip75,thresholdforce), #10 - (catch75,0), #11 - ] + (catch25, 0), # 0 + (slip25, thresholdforce), # 1 + (a75, F75), # 4 + (a25b, F25b), # 9 + (a25, F25), # 2 + (wash75, thresholdforce), # 5 + (finish75, 0), # 6 + (finish25, 0), # 7 + (wash25, thresholdforce), # 8 + (a75b, F75b), # 3 + (slip75, thresholdforce), # 10 + (catch75, 0), # 11 + ] points0595 = [ - (catch05,0), #0 - (slip05,thresholdforce), #1 - (a95,F95),#4 - (a05b,F05b), #9 - (a05,F05), #2 - (wash95,thresholdforce), #5 - (finish95,0), #6 - (finish05,0), #7 - (wash05,thresholdforce), #8 - (a95b,F95b), #3 - (slip95,thresholdforce), #10 - (catch95,0), #11 - ] - - + (catch05, 0), # 0 + (slip05, thresholdforce), # 1 + (a95, F95), # 4 + (a05b, F05b), # 9 + (a05, F05), # 2 + (wash95, thresholdforce), # 5 + (finish95, 0), # 6 + (finish05, 0), # 7 + (wash05, thresholdforce), # 8 + (a95b, F95b), # 3 + (slip95, thresholdforce), # 10 + (catch95, 0), # 11 + ] angles2575 = [] forces2575 = [] - for x,y in points2575: + for x, y in points2575: angles2575.append(x) forces2575.append(y) - angles0595 = [] forces0595 = [] - for x,y in points0595: + for x, y in points0595: angles0595.append(x) forces0595.append(y) - - - - x = [catchav, slipav, peakforceangleav, washav, finishav] - y = [0,thresholdforce, + y = [0, thresholdforce, peakforceav, - thresholdforce,0] - - + thresholdforce, 0] source = ColumnDataSource( - data = dict( - x = x, - y = y, + data=dict( + x=x, + y=y, )) sourceslipwash = ColumnDataSource( - data = dict( - xslip = [slipav,washav], - yslip = [thresholdforce,thresholdforce] - ) + data=dict( + xslip=[slipav, washav], + yslip=[thresholdforce, thresholdforce] ) + ) sourcetrend = ColumnDataSource( - data = dict( - x = [peakforceangle25,peakforceangle75], - y = [peakforce25,peakforce75] - ) + data=dict( + x=[peakforceangle25, peakforceangle75], + y=[peakforce25, peakforce75] ) + ) sourcefit = ColumnDataSource( - data = dict( - x = np.array([peakforceangle25,peakforceangle75]), - y = slope*np.array([peakforceangle25,peakforceangle75])+intercept - ) + data=dict( + x=np.array([peakforceangle25, peakforceangle75]), + y=slope*np.array([peakforceangle25, peakforceangle75])+intercept ) + ) source2 = ColumnDataSource( rowdata - ) + ) - if plottype == 'scatter': # pragma: no cover + if plottype == 'scatter': # pragma: no cover try: sourcepoints = ColumnDataSource( - data = dict( - peakforceangle = rowdata['peakforceangle'], - peakforce = rowdata['peakforce'] + data=dict( + peakforceangle=rowdata['peakforceangle'], + peakforce=rowdata['peakforce'] ) ) except KeyError: sourcepoints = ColumnDataSource( - data = dict( - peakforceangle = [], - peakforce = [] - ) + data=dict( + peakforceangle=[], + peakforce=[] ) + ) else: sourcepoints = ColumnDataSource( - data = dict( - peakforceangle = [], - peakforce = [] - )) - + data=dict( + peakforceangle=[], + peakforce=[] + )) sourcerange = ColumnDataSource( - data = dict( - x2575 = angles2575, - y2575 = forces2575, - x0595 = angles0595, - y0595 = forces0595, - ) + data=dict( + x2575=angles2575, + y2575=forces2575, + x0595=angles0595, + y0595=forces0595, ) + ) plot = Figure(tools=TOOLS, - toolbar_sticky=False,toolbar_location="above",plot_width=800,plot_height=600) + toolbar_sticky=False, toolbar_location="above", plot_width=800, plot_height=600) plot.sizing_mode = 'stretch_both' # add watermark watermarkurl = "/static/img/logo7.png" watermarksource = ColumnDataSource(dict( - url = [watermarkurl],)) + url=[watermarkurl],)) - watermarkrange = Range1d(start=0,end=1) + watermarkrange = Range1d(start=0, end=1) watermarkalpha = 0.6 watermarkx = 0.99 watermarky = 0.01 @@ -1239,27 +1164,27 @@ def interactive_forcecurve(theworkouts,workstrokesonly=True,plottype='scatter'): plot.extra_y_ranges = {"watermark": watermarkrange} plot.extra_x_ranges = {"watermark": watermarkrange} - - plot.image_url([watermarkurl],watermarkx,watermarky, - watermarkw,watermarkh, + plot.image_url([watermarkurl], watermarkx, watermarky, + watermarkw, watermarkh, global_alpha=watermarkalpha, w_units='screen', h_units='screen', anchor=watermarkanchor, dilate=True, - x_range_name = "watermark", - y_range_name = "watermark", + x_range_name="watermark", + y_range_name="watermark", ) - avf = Span(location=averageforceav,dimension='width',line_color='blue', - line_dash=[6,6],line_width=2) + avf = Span(location=averageforceav, dimension='width', line_color='blue', + line_dash=[6, 6], line_width=2) - plot.patch('x0595','y0595',source=sourcerange,color="red",alpha=0.05) - plot.patch('x2575','y2575',source=sourcerange,color="red",alpha=0.2) - plot.line('x','y',source=source,color="red") - plot.circle('xslip','yslip',source=sourceslipwash,color="red") + plot.patch('x0595', 'y0595', source=sourcerange, color="red", alpha=0.05) + plot.patch('x2575', 'y2575', source=sourcerange, color="red", alpha=0.2) + plot.line('x', 'y', source=source, color="red") + plot.circle('xslip', 'yslip', source=sourceslipwash, color="red") - plot.circle('peakforceangle','peakforce',source=sourcepoints,color='black',alpha=0.1) + plot.circle('peakforceangle', 'peakforce', + source=sourcepoints, color='black', alpha=0.1) if plottype == 'line': multilinedatax = [] @@ -1274,16 +1199,15 @@ def interactive_forcecurve(theworkouts,workstrokesonly=True,plottype='scatter'): rowdata['finish'].values[i] ] - y = [ 0, thresholdforce, rowdata['peakforce'].values[i], thresholdforce, 0] - except KeyError: # pragma: no cover - x = [0,0] - y = [0,0] + except KeyError: # pragma: no cover + x = [0, 0] + y = [0, 0] multilinedatax.append(x) multilinedatay.append(y) @@ -1291,105 +1215,110 @@ def interactive_forcecurve(theworkouts,workstrokesonly=True,plottype='scatter'): sourcemultiline = ColumnDataSource(dict( x=multilinedatax, y=multilinedatay, - )) + )) sourcemultiline2 = ColumnDataSource(dict( x=multilinedatax, y=multilinedatay, - )) + )) - glyph = MultiLine(xs='x',ys='y',line_color='black',line_alpha=0.05) - plot.add_glyph(sourcemultiline,glyph) - else: # pragma: no cover + glyph = MultiLine(xs='x', ys='y', line_color='black', line_alpha=0.05) + plot.add_glyph(sourcemultiline, glyph) + else: # pragma: no cover sourcemultiline = ColumnDataSource(dict( - x=[],y=[])) + x=[], y=[])) sourcemultiline2 = ColumnDataSource(dict( - x=[],y=[])) + x=[], y=[])) - - plot.line('x','y',source=source,color="red") + plot.line('x', 'y', source=source, color="red") plot.add_layout(avf) - peakflabel = Label(x=760,y=460,x_units='screen',y_units='screen', - text="Fpeak: {peakforceav:6.2f}".format(peakforceav=peakforceav), + peakflabel = Label(x=760, y=460, x_units='screen', y_units='screen', + text="Fpeak: {peakforceav:6.2f}".format( + peakforceav=peakforceav), background_fill_alpha=.7, background_fill_color='white', text_color='blue', - ) + ) - avflabel = Label(x=770,y=430,x_units='screen',y_units='screen', - text="Favg: {averageforceav:6.2f}".format(averageforceav=averageforceav), + avflabel = Label(x=770, y=430, x_units='screen', y_units='screen', + text="Favg: {averageforceav:6.2f}".format( + averageforceav=averageforceav), background_fill_alpha=.7, background_fill_color='white', text_color='blue', - ) + ) - catchlabel = Label(x=765,y=400,x_units='screen',y_units='screen', + catchlabel = Label(x=765, y=400, x_units='screen', y_units='screen', text="Catch: {catchav:6.2f}".format(catchav=catchav), background_fill_alpha=0.7, background_fill_color='white', text_color='red', ) - peakforceanglelabel = Label(x=725,y=370,x_units='screen',y_units='screen', - text="Peak angle: {peakforceangleav:6.2f}".format(peakforceangleav=peakforceangleav), + peakforceanglelabel = Label(x=725, y=370, x_units='screen', y_units='screen', + text="Peak angle: {peakforceangleav:6.2f}".format( + peakforceangleav=peakforceangleav), background_fill_alpha=0.7, background_fill_color='white', text_color='red', - ) + ) - finishlabel = Label(x=760,y=340,x_units='screen',y_units='screen', - text="Finish: {finishav:6.2f}".format(finishav=finishav), + finishlabel = Label(x=760, y=340, x_units='screen', y_units='screen', + text="Finish: {finishav:6.2f}".format( + finishav=finishav), background_fill_alpha=0.7, background_fill_color='white', text_color='red', - ) + ) - sliplabel = Label(x=775,y=310,x_units='screen',y_units='screen', + sliplabel = Label(x=775, y=310, x_units='screen', y_units='screen', text="Slip: {slipav:6.2f}".format(slipav=slipav-catchav), background_fill_alpha=0.7, background_fill_color='white', text_color='red', - ) + ) - washlabel = Label(x=765,y=280,x_units='screen',y_units='screen', - text="Wash: {washav:6.2f}".format(washav=finishav-washav), + washlabel = Label(x=765, y=280, x_units='screen', y_units='screen', + text="Wash: {washav:6.2f}".format( + washav=finishav-washav), background_fill_alpha=0.7, background_fill_color='white', text_color='red', - ) + ) - lengthlabel = Label(x=755,y=250, x_units='screen',y_units='screen', - text="Length: {length:6.2f}".format(length=finishav-catchav), + lengthlabel = Label(x=755, y=250, x_units='screen', y_units='screen', + text="Length: {length:6.2f}".format( + length=finishav-catchav), background_fill_alpha=0.7, background_fill_color='white', text_color='green' ) - efflengthlabel = Label(x=690,y=220, x_units='screen',y_units='screen', - text="Effective Length: {length:6.2f}".format(length=washav-slipav), - background_fill_alpha=0.7, - background_fill_color='white', - text_color='green' - ) + efflengthlabel = Label(x=690, y=220, x_units='screen', y_units='screen', + text="Effective Length: {length:6.2f}".format( + length=washav-slipav), + background_fill_alpha=0.7, + background_fill_color='white', + text_color='green' + ) - annolabel = Label(x=50,y=450,x_units='screen',y_units='screen', + annolabel = Label(x=50, y=450, x_units='screen', y_units='screen', text='', background_fill_alpha=0.7, background_fill_color='white', text_color='black', - ) + ) - sliderlabel = Label(x=10,y=470,x_units='screen',y_units='screen', + sliderlabel = Label(x=10, y=470, x_units='screen', y_units='screen', text='', background_fill_alpha=0.7, background_fill_color='white', - text_color='black',text_font_size='10pt', + text_color='black', text_font_size='10pt', ) - plot.add_layout(peakflabel) plot.add_layout(peakforceanglelabel) plot.add_layout(avflabel) @@ -1405,15 +1334,15 @@ def interactive_forcecurve(theworkouts,workstrokesonly=True,plottype='scatter'): plot.xaxis.axis_label = "Angle" plot.yaxis.axis_label = "Force (N)" plot.title.text = theworkouts[0].name - plot.title.text_font_size=value("1.0em") + plot.title.text_font_size = value("1.0em") - yrange1 = Range1d(start=0,end=900) + yrange1 = Range1d(start=0, end=900) plot.y_range = yrange1 - xrange1 = Range1d(start=yaxmaxima['catch'],end=yaxmaxima['finish']) + xrange1 = Range1d(start=yaxmaxima['catch'], end=yaxmaxima['finish']) plot.x_range = xrange1 - callback = CustomJS(args = dict( + callback = CustomJS(args=dict( source=source, source2=source2, sourceslipwash=sourceslipwash, @@ -1558,43 +1487,42 @@ def interactive_forcecurve(theworkouts,workstrokesonly=True,plottype='scatter'): sourcemultiline.change.emit(); """) - annotation = TextInput(width=140, title="Type your plot notes here", value="") - annotation.js_on_change('value',callback) + annotation = TextInput( + width=140, title="Type your plot notes here", value="") + annotation.js_on_change('value', callback) callback.args["annotation"] = annotation - slider_spm_min = Slider(width=140, start=15.0, end=55,value=15.0, step=.1, - title="Min SPM") - slider_spm_min.js_on_change('value',callback) + slider_spm_min = Slider(width=140, start=15.0, end=55, value=15.0, step=.1, + title="Min SPM") + slider_spm_min.js_on_change('value', callback) callback.args["minspm"] = slider_spm_min - - slider_spm_max = Slider(width=140, start=15.0, end=55,value=55.0, step=.1, - title="Max SPM") - slider_spm_max.js_on_change('value',callback) + slider_spm_max = Slider(width=140, start=15.0, end=55, value=55.0, step=.1, + title="Max SPM") + slider_spm_max.js_on_change('value', callback) callback.args["maxspm"] = slider_spm_max - slider_work_min = Slider(width=140, start=0, end=1500,value=0, step=10, - title="Min Work per Stroke") - slider_work_min.js_on_change('value',callback) + slider_work_min = Slider(width=140, start=0, end=1500, value=0, step=10, + title="Min Work per Stroke") + slider_work_min.js_on_change('value', callback) callback.args["minwork"] = slider_work_min - - slider_work_max = Slider(width=140, start=0, end=1500,value=1500, step=10, - title="Max Work per Stroke") - slider_work_max.js_on_change('value',callback) + slider_work_max = Slider(width=140, start=0, end=1500, value=1500, step=10, + title="Max Work per Stroke") + slider_work_max.js_on_change('value', callback) callback.args["maxwork"] = slider_work_max distmax = 100+100*int(rowdata['distance'].max()/100.) - slider_dist_min = Slider(width=140, start=0,end=distmax,value=0,step=50, + slider_dist_min = Slider(width=140, start=0, end=distmax, value=0, step=50, title="Min Distance") - slider_dist_min.js_on_change('value',callback) + slider_dist_min.js_on_change('value', callback) callback.args["mindist"] = slider_dist_min - slider_dist_max = Slider(width=140, start=0,end=distmax,value=distmax, + slider_dist_max = Slider(width=140, start=0, end=distmax, value=distmax, step=50, title="Max Distance") - slider_dist_max.js_on_change('value',callback) + slider_dist_max.js_on_change('value', callback) callback.args["maxdist"] = slider_dist_max #annotation.sizing_mode = 'fixed' @@ -1606,14 +1534,14 @@ def interactive_forcecurve(theworkouts,workstrokesonly=True,plottype='scatter'): #slider_dist_max.sizing_mode = 'fixed' thesliders = layoutcolumn([annotation, - slider_spm_min, - slider_spm_max, - slider_dist_min, - slider_dist_max, - slider_work_min, - slider_work_max, + slider_spm_min, + slider_spm_max, + slider_dist_min, + slider_dist_max, + slider_work_min, + slider_work_max, ] - ) + ) #thesliders.sizing_mode = 'fixed' layout = layoutrow([thesliders, @@ -1625,14 +1553,13 @@ def interactive_forcecurve(theworkouts,workstrokesonly=True,plottype='scatter'): js_resources = INLINE.render_js() css_resources = INLINE.render_css() + return [script, div, js_resources, css_resources] - return [script,div,js_resources,css_resources] - def getfatigues( - fatigues,fitnesses,dates,testpower,testduration, - impulses, - startdate,enddate,user,metricchoice,kfatigue,kfitness): + fatigues, fitnesses, dates, testpower, testduration, + impulses, + startdate, enddate, user, metricchoice, kfatigue, kfitness): fatigue = 0 fitness = 0 @@ -1647,25 +1574,24 @@ def getfatigues( for i in range(nrdays+1): date = startdate+datetime.timedelta(days=i) - ws = Workout.objects.filter(user=user.rower,date=date,duplicate=False) + ws = Workout.objects.filter( + user=user.rower, date=date, duplicate=False) weight = 0 for w in ws: - if getattr(w,metricchoice) > 0: - weight += getattr(w,metricchoice) - if getattr(w,metricchoice) <= 0: - if metricchoice == 'rscore' and w.hrtss > 0: # pragma: no cover - weight+= w.hrtss + if getattr(w, metricchoice) > 0: + weight += getattr(w, metricchoice) + if getattr(w, metricchoice) <= 0: + if metricchoice == 'rscore' and w.hrtss > 0: # pragma: no cover + weight += w.hrtss else: - trimp,hrtss = dataprep.workout_trimp(w) - rscore,normp = dataprep.workout_rscore(w) - if w.rpe and w.rpe > 0: # pragma: no cover + trimp, hrtss = dataprep.workout_trimp(w) + rscore, normp = dataprep.workout_rscore(w) + if w.rpe and w.rpe > 0: # pragma: no cover dd = 3600*w.duration.hour+60*w.duration.minute+w.duration.second dd = dd/3600 weight += rpetotss[w.rpe]*dd - - impulses.append(weight) fatigue = (1-lambda_a)*fatigue+weight*lambda_a @@ -1677,20 +1603,21 @@ def getfatigues( testpower.append(np.nan) testduration.append(np.nan) + return fatigues, fitnesses, dates, testpower, testduration, impulses - return fatigues,fitnesses,dates,testpower,testduration,impulses - -def goldmedalscorechart(user,startdate=None,enddate=None): +def goldmedalscorechart(user, startdate=None, enddate=None): # to avoid data mess later on - startdate = arrow.get(startdate).datetime.replace(hour=0,minute=0,second=0,microsecond=0) + startdate = arrow.get(startdate).datetime.replace( + hour=0, minute=0, second=0, microsecond=0) enddate = enddate+datetime.timedelta(days=1) - enddate = arrow.get(enddate).datetime.replace(hour=0,minute=0,second=0,microsecond=0) + enddate = arrow.get(enddate).datetime.replace( + hour=0, minute=0, second=0, microsecond=0) TOOLS = 'save,pan,box_zoom,wheel_zoom,reset,tap,hover,crosshair' # marker workouts - workouts = Workout.objects.filter(user=user.rower,date__gte=startdate, + workouts = Workout.objects.filter(user=user.rower, date__gte=startdate, date__lte=enddate, workouttype__in=mytypes.rowtypes, duplicate=False).order_by('date') @@ -1698,18 +1625,19 @@ def goldmedalscorechart(user,startdate=None,enddate=None): markerworkouts = workouts.filter(rankingpiece=True) outids = [w.id for w in markerworkouts] dates = [arrow.get(w.date).datetime for w in markerworkouts] - testpower = [w.goldmedalstandard if w.rankingpiece else np.nan for w in markerworkouts] + testpower = [ + w.goldmedalstandard if w.rankingpiece else np.nan for w in markerworkouts] - testduration = [w.goldmedalseconds if w.rankingpiece else 0 for w in markerworkouts] + testduration = [ + w.goldmedalseconds if w.rankingpiece else 0 for w in markerworkouts] df = pd.DataFrame({ - 'id':outids, - 'date':dates, - 'testpower':testpower, - 'testduration':testduration, + 'id': outids, + 'date': dates, + 'testpower': testpower, + 'testduration': testduration, }) - df.sort_values(['date'],inplace=True) - + df.sort_values(['date'], inplace=True) mask = df['testpower'].isnull() dates = df.mask(mask)['date'].dropna().values @@ -1719,8 +1647,8 @@ def goldmedalscorechart(user,startdate=None,enddate=None): outids = df.mask(mask)['id'].dropna().unique() # all workouts - alldates,alltestpower,allduration,allids = all_goldmedalstandards(workouts,startdate,enddate) - + alldates, alltestpower, allduration, allids = all_goldmedalstandards( + workouts, startdate, enddate) nrdays = (enddate-startdate).days @@ -1737,7 +1665,7 @@ def goldmedalscorechart(user,startdate=None,enddate=None): id = ids[i] w = Workout.objects.get(id=id) dd = str(dates[i]) - #td.append(arrow.get(dd).datetime) + # td.append(arrow.get(dd).datetime) td.append(arrow.get(w.date).datetime) markerscore.append(testpower[i]) markerduration.append(testduration[i]) @@ -1761,51 +1689,51 @@ def goldmedalscorechart(user,startdate=None,enddate=None): duration.append(np.nan) workoutid.append(0) - df = pd.DataFrame({ - 'markerscore':markerscore, - 'markerduration':markerduration, - 'score':score, - 'duration':duration, - 'date':td, - 'id':workoutid, + 'markerscore': markerscore, + 'markerduration': markerduration, + 'score': score, + 'duration': duration, + 'date': td, + 'id': workoutid, }) - df['url'] = df['id'].apply(lambda x:settings.SITE_URL+'/rowers/workout/{id}/'.format(id=encoder.encode_hex(x))) - df['workout'] = df['id'].apply(lambda x:workoutname(x)) + df['url'] = df['id'].apply(lambda x: settings.SITE_URL + + '/rowers/workout/{id}/'.format(id=encoder.encode_hex(x))) + df['workout'] = df['id'].apply(lambda x: workoutname(x)) - - df.sort_values(['date'],inplace=True) + df.sort_values(['date'], inplace=True) # find index values where score is max idx = df.groupby(['date'])['score'].transform(max) == df['score'] df = df[idx] - source = ColumnDataSource( - data = dict( - markerscore = df['markerscore'], - score = df['score'], - markerduration = df['markerduration'].apply(lambda x:totaltime_sec_to_string(x,shorten=True)), - duration = df['duration'].apply(lambda x:totaltime_sec_to_string(x,shorten=True)), - date = df['date'], - fdate = df['date'].map(lambda x: x.strftime('%d-%m-%Y')), - url = df['url'], - workout = df['workout'] + data=dict( + markerscore=df['markerscore'], + score=df['score'], + markerduration=df['markerduration'].apply( + lambda x: totaltime_sec_to_string(x, shorten=True)), + duration=df['duration'].apply( + lambda x: totaltime_sec_to_string(x, shorten=True)), + date=df['date'], + fdate=df['date'].map(lambda x: x.strftime('%d-%m-%Y')), + url=df['url'], + workout=df['workout'] ) ) - plot = Figure(tools=TOOLS,x_axis_type='datetime', - plot_width=900,plot_height=600, + plot = Figure(tools=TOOLS, x_axis_type='datetime', + plot_width=900, plot_height=600, toolbar_location='above', toolbar_sticky=False) # add watermark watermarkurl = "/static/img/logo7.png" watermarksource = ColumnDataSource(dict( - url = [watermarkurl],)) + url=[watermarkurl],)) - watermarkrange = Range1d(start=0,end=1) + watermarkrange = Range1d(start=0, end=1) watermarkalpha = 0.6 watermarkx = 0.99 watermarky = 0.01 @@ -1815,43 +1743,43 @@ def goldmedalscorechart(user,startdate=None,enddate=None): plot.extra_y_ranges = {"watermark": watermarkrange} plot.extra_x_ranges = {"watermark": watermarkrange} - plot.image_url([watermarkurl],watermarkx,watermarky, - watermarkw,watermarkh, + plot.image_url([watermarkurl], watermarkx, watermarky, + watermarkw, watermarkh, global_alpha=watermarkalpha, w_units='screen', h_units='screen', anchor=watermarkanchor, dilate=True, - x_range_name = "watermark", - y_range_name = "watermark", + x_range_name="watermark", + y_range_name="watermark", ) plot.xaxis.axis_label = 'Date' plot.yaxis.axis_label = 'Gold Medal Score' - plot.circle('date','score',source=source,fill_color='blue', + plot.circle('date', 'score', source=source, fill_color='blue', size=10, legend_label='Workouts') - plot.circle('date','markerscore',source=source,fill_color='red', + plot.circle('date', 'markerscore', source=source, fill_color='red', size=10, legend_label='Marker Workouts') plot.legend.location = "bottom_left" plot.x_range = Range1d( - startdate,enddate+datetime.timedelta(days=5), + startdate, enddate+datetime.timedelta(days=5), ) hover = plot.select(dict(type=HoverTool)) hover.tooltips = OrderedDict([ - ('Marker','@markerscore{int}'), + ('Marker', '@markerscore{int}'), ('Test', '@markerduration'), - ('Score','@score{int}'), + ('Score', '@score{int}'), ('Duration', '@duration'), - ('Date','@fdate'), - ('Workout','@workout') + ('Date', '@fdate'), + ('Workout', '@workout') ]) taptool = plot.select(type=TapTool) @@ -1859,20 +1787,22 @@ def goldmedalscorechart(user,startdate=None,enddate=None): script, div = components(plot) - return script, div,outids + return script, div, outids -def performance_chart(user,startdate=None,enddate=None,kfitness=42,kfatigue=7, - metricchoice='trimp',doform=False,dofatigue=False, + +def performance_chart(user, startdate=None, enddate=None, kfitness=42, kfatigue=7, + metricchoice='trimp', doform=False, dofatigue=False, showtests=False): TOOLS = 'save,pan,box_zoom,wheel_zoom,reset,tap,hover,crosshair' TOOLS2 = 'box_zoom,hover' # to avoid data mess later on - startdate = arrow.get(startdate).datetime.replace(hour=0,minute=0,second=0,microsecond=0) + startdate = arrow.get(startdate).datetime.replace( + hour=0, minute=0, second=0, microsecond=0) enddate = enddate+datetime.timedelta(days=1) - enddate = arrow.get(enddate).datetime.replace(hour=0,minute=0,second=0,microsecond=0) - + enddate = arrow.get(enddate).datetime.replace( + hour=0, minute=0, second=0, microsecond=0) modelchoice = 'coggan' p0 = 0 @@ -1888,8 +1818,7 @@ def performance_chart(user,startdate=None,enddate=None,kfitness=42,kfatigue=7, outids = [] - - workouts = Workout.objects.filter(user=user.rower,date__gte=startdate-datetime.timedelta(days=90), + workouts = Workout.objects.filter(user=user.rower, date__gte=startdate-datetime.timedelta(days=90), date__lte=enddate, workouttype__in=mytypes.rowtypes, duplicate=False).order_by('date') @@ -1897,80 +1826,77 @@ def performance_chart(user,startdate=None,enddate=None,kfitness=42,kfatigue=7, markerworkouts = workouts.filter(rankingpiece=True) outids = [w.id for w in markerworkouts] dates = [arrow.get(w.date).datetime for w in workouts] - testpower = [w.goldmedalstandard if w.rankingpiece else np.nan for w in workouts] + testpower = [ + w.goldmedalstandard if w.rankingpiece else np.nan for w in workouts] impulses = [np.nan for w in workouts] - testduration = [w.goldmedalseconds if w.rankingpiece else 0 for w in workouts] + testduration = [ + w.goldmedalseconds if w.rankingpiece else 0 for w in workouts] fitnesses = [np.nan for w in workouts] fatigues = [np.nan for w in workouts] - fatigues,fitnesses,dates,testpower,testduration,impulses = getfatigues(fatigues, - fitnesses, - dates, - testpower,testduration, - impulses, - startdate-datetime.timedelta(days=90), - enddate, - user,metricchoice, - kfatigue,kfitness) - + fatigues, fitnesses, dates, testpower, testduration, impulses = getfatigues(fatigues, + fitnesses, + dates, + testpower, testduration, + impulses, + startdate - + datetime.timedelta( + days=90), + enddate, + user, metricchoice, + kfatigue, kfitness) df = pd.DataFrame({ - 'date':dates, - 'testpower':testpower, + 'date': dates, + 'testpower': testpower, 'testduration': testduration, - 'fatigue':fatigues, - 'fitness':fitnesses, - 'impulse':impulses, + 'fatigue': fatigues, + 'fitness': fitnesses, + 'impulse': impulses, }) - endfitness = fitnesses[-2] endfatigue = fatigues[-2] endform = endfitness-endfatigue - if modelchoice == 'banister': # pragma: no cover + if modelchoice == 'banister': # pragma: no cover df['fatigue'] = k2*df['fatigue'] df['fitness'] = p0+k1*df['fitness'] - df['form'] = df['fitness']-df['fatigue'] - - - df.sort_values(['date'],inplace=True) + df.sort_values(['date'], inplace=True) df = df.groupby(['date']).max() df['date'] = df.index.values - mask = df['date'] > np.datetime64(startdate.astimezone(tz=datetime.timezone.utc).replace(tzinfo=None)) + mask = df['date'] > np.datetime64(startdate.astimezone( + tz=datetime.timezone.utc).replace(tzinfo=None)) df = df.loc[mask] - - source = ColumnDataSource( - data = dict( - testpower = df['testpower'], - testduration = df['testduration'].apply(lambda x:totaltime_sec_to_string(x,shorten=True)), - date = df['date'], - fdate = df['date'].map(lambda x: x.strftime('%d-%m-%Y')), - fitness = df['fitness'], - fatigue = df['fatigue'], - form = df['form'], - impulse = df['impulse'] + data=dict( + testpower=df['testpower'], + testduration=df['testduration'].apply( + lambda x: totaltime_sec_to_string(x, shorten=True)), + date=df['date'], + fdate=df['date'].map(lambda x: x.strftime('%d-%m-%Y')), + fitness=df['fitness'], + fatigue=df['fatigue'], + form=df['form'], + impulse=df['impulse'] ) ) - - plot = Figure(tools=TOOLS,x_axis_type='datetime', - plot_width=900,plot_height=300, + plot = Figure(tools=TOOLS, x_axis_type='datetime', + plot_width=900, plot_height=300, toolbar_location="above", toolbar_sticky=False) - # add watermark watermarkurl = "/static/img/logo7.png" watermarksource = ColumnDataSource(dict( - url = [watermarkurl],)) + url=[watermarkurl],)) - watermarkrange = Range1d(start=0,end=1) + watermarkrange = Range1d(start=0, end=1) watermarkalpha = 0.6 watermarkx = 0.99 watermarky = 0.01 @@ -1980,18 +1906,18 @@ def performance_chart(user,startdate=None,enddate=None,kfitness=42,kfatigue=7, plot.extra_y_ranges = {"watermark": watermarkrange} plot.extra_x_ranges = {"watermark": watermarkrange} - plot.image_url([watermarkurl],watermarkx,watermarky, - watermarkw,watermarkh, + plot.image_url([watermarkurl], watermarkx, watermarky, + watermarkw, watermarkh, global_alpha=watermarkalpha, w_units='screen', h_units='screen', anchor=watermarkanchor, dilate=True, - x_range_name = "watermark", - y_range_name = "watermark", + x_range_name="watermark", + y_range_name="watermark", ) - if modelchoice == 'banister': # pragma: no cover + if modelchoice == 'banister': # pragma: no cover fitlabel = 'PTE (fitness)' fatiguelabel = 'NTE (fatigue)' formlabel = 'Performance' @@ -2005,109 +1931,104 @@ def performance_chart(user,startdate=None,enddate=None,kfitness=42,kfatigue=7, fatiguelabel = 'Fatigue' formlabel = 'Freshness' rightaxlabel = 'Freshness' - if dofatigue: # pragma: no cover + if dofatigue: # pragma: no cover yaxlabel = 'Fitness/Fatigue' else: yaxlabel = 'Fitness' - - #if showtests: + # if showtests: # plot.circle('date','testpower',source=source,fill_color='green',size=10, # legend_label='Your best workouts') plot.xaxis.axis_label = None plot.yaxis.axis_label = yaxlabel - - y2rangemin = df.loc[:,['form']].min().min() - y2rangemax = df.loc[:,['form']].max().max() - #if dofatigue and showtests: + y2rangemin = df.loc[:, ['form']].min().min() + y2rangemax = df.loc[:, ['form']].max().max() + # if dofatigue and showtests: # y1rangemin = df.loc[:,['testpower','fitness','fatigue']].min().min() # y1rangemax = df.loc[:,['testpower','fitness','fatigue']].max().max()*1.02 - #elif showtests: + # elif showtests: # y1rangemin = df.loc[:,['testpower','fitness']].min().min() # y1rangemax = df.loc[:,['testpower','fitness']].max().max()*1.02 - if dofatigue: # pragma: no cover - y1rangemin = df.loc[:,['fitness','fatigue']].min().min() - y1rangemax = df.loc[:,['fitness','fatigue']].max().max()*1.02 + if dofatigue: # pragma: no cover + y1rangemin = df.loc[:, ['fitness', 'fatigue']].min().min() + y1rangemax = df.loc[:, ['fitness', 'fatigue']].max().max()*1.02 else: - y1rangemin = df.loc[:,['fitness']].min().min() - y1rangemax = df.loc[:,['fitness']].max().max()*1.02 + y1rangemin = df.loc[:, ['fitness']].min().min() + y1rangemax = df.loc[:, ['fitness']].max().max()*1.02 - if doform: # pragma: no cover - plot.extra_y_ranges["yax2"] = Range1d(start=y2rangemin,end=y2rangemax) - plot.add_layout(LinearAxis(y_range_name="yax2",axis_label=rightaxlabel),"right") + if doform: # pragma: no cover + plot.extra_y_ranges["yax2"] = Range1d(start=y2rangemin, end=y2rangemax) + plot.add_layout(LinearAxis(y_range_name="yax2", + axis_label=rightaxlabel), "right") - plot.line('date','fitness',source=source,color='blue', + plot.line('date', 'fitness', source=source, color='blue', legend_label=fitlabel) band = Band(base='date', upper='fitness', source=source, level='underlay', - fill_alpha=0.2, fill_color='blue') + fill_alpha=0.2, fill_color='blue') plot.add_layout(band) - - if dofatigue: # pragma: no cover - plot.line('date','fatigue',source=source,color='red', - legend_label=fatiguelabel) - if doform: # pragma: no cover - plot.line('date','form',source=source,color='green', - legend_label=formlabel,y_range_name="yax2") + if dofatigue: # pragma: no cover + plot.line('date', 'fatigue', source=source, color='red', + legend_label=fatiguelabel) + if doform: # pragma: no cover + plot.line('date', 'form', source=source, color='green', + legend_label=formlabel, y_range_name="yax2") plot.legend.location = "top_left" plot.sizing_mode = 'scale_both' #plot.y_range = Range1d(0,1.5*max(df['testpower'])) - startdate = datetime.datetime.combine(startdate,datetime.datetime.min.time()) - enddate = datetime.datetime.combine(enddate,datetime.datetime.min.time()) + startdate = datetime.datetime.combine( + startdate, datetime.datetime.min.time()) + enddate = datetime.datetime.combine(enddate, datetime.datetime.min.time()) xrange = Range1d( - startdate,enddate, + startdate, enddate, ) plot.x_range = xrange plot.y_range = Range1d( - start=0,end=y1rangemax, + start=0, end=y1rangemax, ) plot.title.text = 'Performance Manager '+user.first_name - hover = plot.select(dict(type=HoverTool)) linked_crosshair = CrosshairTool(dimensions='height') - hover.tooltips = OrderedDict([ - #(legend_label,'@testpower'), - ('Date','@fdate'), - (fitlabel,'@fitness{int}'), - (fatiguelabel,'@fatigue{int}'), - (formlabel,'@form{int}'), - ('Impulse','@impulse{int}') + # (legend_label,'@testpower'), + ('Date', '@fdate'), + (fitlabel, '@fitness{int}'), + (fatiguelabel, '@fatigue{int}'), + (formlabel, '@form{int}'), + ('Impulse', '@impulse{int}') ]) if showtests: hover.tooltips = OrderedDict([ - #(legend_label,'@testpower'), - ('Date','@fdate'), - (fitlabel,'@fitness{int}'), - (fatiguelabel,'@fatigue{int}'), - (formlabel,'@form{int}'), - ('Impulse','@impulse{int}'), - ('Gold Medal Score','@testpower{int}'), + # (legend_label,'@testpower'), + ('Date', '@fdate'), + (fitlabel, '@fitness{int}'), + (fatiguelabel, '@fatigue{int}'), + (formlabel, '@form{int}'), + ('Impulse', '@impulse{int}'), + ('Gold Medal Score', '@testpower{int}'), ('Test', '@testduration'), ]) - plot2 = Figure(tools=TOOLS2,x_axis_type='datetime', - plot_width=900,plot_height=150, - toolbar_location=None, - toolbar_sticky=False) + plot2 = Figure(tools=TOOLS2, x_axis_type='datetime', + plot_width=900, plot_height=150, + toolbar_location=None, + toolbar_sticky=False) + plot2.x_range = xrange + plot2.y_range = Range1d(0, df['impulse'].max()) - - plot2.x_range = xrange - plot2.y_range = Range1d(0,df['impulse'].max()) - - plot2.vbar(x = df['date'], top = df['impulse'],color='gray') - plot2.vbar(x = df['date'], top = 0*df['testpower']+df['impulse'], color='red') + plot2.vbar(x=df['date'], top=df['impulse'], color='gray') + plot2.vbar(x=df['date'], top=0*df['testpower']+df['impulse'], color='red') plot2.sizing_mode = 'scale_both' plot2.yaxis.axis_label = 'Impulse' @@ -2116,65 +2037,66 @@ def performance_chart(user,startdate=None,enddate=None,kfitness=42,kfatigue=7, plot.add_tools(linked_crosshair) plot2.add_tools(linked_crosshair) - layout = layoutcolumn([plot,plot2]) + layout = layoutcolumn([plot, plot2]) layout.sizing_mode = 'stretch_both' try: - script,div = components(layout) - except Exception as e: # pragma: no cover - df.dropna(inplace=True,axis=0,how='any') + script, div = components(layout) + except Exception as e: # pragma: no cover + df.dropna(inplace=True, axis=0, how='any') return ( '', 'Something went wrong with the chart ({nrworkouts} workouts, {nrdata} datapoints, error {e})'.format( - nrworkouts = workouts.count(), - nrdata = len(df), - e = e, - ),0,0,0,[] - ) + nrworkouts=workouts.count(), + nrdata=len(df), + e=e, + ), 0, 0, 0, [] + ) - return [script,div,endfitness,endfatigue,endform,outids] + return [script, div, endfitness, endfatigue, endform, outids] - - -def interactive_histoall(theworkouts,histoparam,includereststrokes, - spmmin=0,spmmax=55, - workmin=0,workmax=1500): +def interactive_histoall(theworkouts, histoparam, includereststrokes, + spmmin=0, spmmax=55, + workmin=0, workmax=1500): TOOLS = 'save,pan,box_zoom,wheel_zoom,reset,tap,hover,crosshair' ids = [int(w.id) for w in theworkouts] workstrokesonly = not includereststrokes - rowdata = dataprep.getsmallrowdata_db([histoparam],ids=ids,doclean=True,workstrokesonly=workstrokesonly) + rowdata = dataprep.getsmallrowdata_db( + [histoparam], ids=ids, doclean=True, workstrokesonly=workstrokesonly) - rowdata.dropna(axis=0,how='any',inplace=True) + rowdata.dropna(axis=0, how='any', inplace=True) - rowdata = dataprep.filter_df(rowdata,'spm',spmmin,largerthan=True) - rowdata = dataprep.filter_df(rowdata,'spm',spmmax,largerthan=False) + rowdata = dataprep.filter_df(rowdata, 'spm', spmmin, largerthan=True) + rowdata = dataprep.filter_df(rowdata, 'spm', spmmax, largerthan=False) - rowdata = dataprep.filter_df(rowdata,'driveenergy',workmin,largerthan=True) - rowdata = dataprep.filter_df(rowdata,'driveenergy',workmax,largerthan=False) + rowdata = dataprep.filter_df( + rowdata, 'driveenergy', workmin, largerthan=True) + rowdata = dataprep.filter_df( + rowdata, 'driveenergy', workmax, largerthan=False) if rowdata.empty: - return "","No Valid Data Available" + return "", "No Valid Data Available" try: histopwr = rowdata[histoparam].values except KeyError: - return "","No data" - if len(histopwr) == 0: # pragma: no cover - return "","No valid data available" + return "", "No data" + if len(histopwr) == 0: # pragma: no cover + return "", "No valid data available" # throw out nans histopwr = histopwr[~np.isinf(histopwr)] - if histoparam == 'catch': # pragma: no cover + if histoparam == 'catch': # pragma: no cover histopwr = histopwr[histopwr < yaxminima[histoparam]] histopwr = histopwr[histopwr > yaxmaxima[histoparam]] else: histopwr = histopwr[histopwr > yaxminima[histoparam]] histopwr = histopwr[histopwr < yaxmaxima[histoparam]] - plot = Figure(tools=TOOLS,plot_width=900, + plot = Figure(tools=TOOLS, plot_width=900, toolbar_sticky=False, toolbar_location="above" ) @@ -2182,9 +2104,9 @@ def interactive_histoall(theworkouts,histoparam,includereststrokes, # add watermark watermarkurl = "/static/img/logo7.png" watermarksource = ColumnDataSource(dict( - url = [watermarkurl],)) + url=[watermarkurl],)) - watermarkrange = Range1d(start=0,end=1) + watermarkrange = Range1d(start=0, end=1) watermarkalpha = 0.6 watermarkx = 0.99 watermarky = 0.01 @@ -2194,18 +2116,18 @@ def interactive_histoall(theworkouts,histoparam,includereststrokes, plot.extra_y_ranges = {"watermark": watermarkrange} plot.extra_x_ranges = {"watermark": watermarkrange} - plot.image_url([watermarkurl],watermarkx,watermarky, - watermarkw,watermarkh, + plot.image_url([watermarkurl], watermarkx, watermarky, + watermarkw, watermarkh, global_alpha=watermarkalpha, w_units='screen', h_units='screen', anchor=watermarkanchor, dilate=True, - x_range_name = "watermark", - y_range_name = "watermark", + x_range_name="watermark", + y_range_name="watermark", ) - hist,edges = np.histogram(histopwr,bins=150) + hist, edges = np.histogram(histopwr, bins=150) histsum = np.cumsum(hist) histsum = 100.*histsum/max(histsum) @@ -2213,89 +2135,92 @@ def interactive_histoall(theworkouts,histoparam,includereststrokes, hist_norm = 100.*hist/float(hist.sum()) source = ColumnDataSource( - data = dict( - left = edges[:-1], - right = edges[1:], - histsum = histsum, - hist_norm = hist_norm, - ) + data=dict( + left=edges[:-1], + right=edges[1:], + histsum=histsum, + hist_norm=hist_norm, ) + ) # plot.quad(top='hist_norm',bottom=0,left=edges[:-1],right=edges[1:]) - plot.quad(top='hist_norm',bottom=0,left='left',right='right',source=source) + plot.quad(top='hist_norm', bottom=0, left='left', + right='right', source=source) plot.xaxis.axis_label = axlabels[histoparam] plot.yaxis.axis_label = "% of strokes" - plot.y_range = Range1d(0,1.05*max(hist_norm)) - + plot.y_range = Range1d(0, 1.05*max(hist_norm)) hover = plot.select(dict(type=HoverTool)) hover.tooltips = OrderedDict([ - (axlabels[histoparam],'@left{int}'), - ('% of strokes','@hist_norm'), - ('Cumulative %','@histsum{int}'), - ]) + (axlabels[histoparam], '@left{int}'), + ('% of strokes', '@hist_norm'), + ('Cumulative %', '@histsum{int}'), + ]) hover.mode = 'mouse' - plot.extra_y_ranges["fraction"] = Range1d(start=0,end=105) - plot.line('right','histsum',source=source,color="red", + plot.extra_y_ranges["fraction"] = Range1d(start=0, end=105) + plot.line('right', 'histsum', source=source, color="red", y_range_name="fraction") plot.add_layout(LinearAxis(y_range_name="fraction", - axis_label="Cumulative % of strokes"),'right') + axis_label="Cumulative % of strokes"), 'right') plot.sizing_mode = 'stretch_both' - annolabel = Label(x=50,y=450,x_units='screen',y_units='screen', + annolabel = Label(x=50, y=450, x_units='screen', y_units='screen', text='', background_fill_alpha=0.7, background_fill_color='white', text_color='black', - ) + ) plot.add_layout(annolabel) - callback = CustomJS(args = dict( + callback = CustomJS(args=dict( annolabel=annolabel, - ), code=""" + ), code=""" var annotation = annotation.value annolabel.text = annotation """) - annotation = TextInput(width=140, title="Type your plot notes here", value="") - annotation.js_on_change('value',callback) + annotation = TextInput( + width=140, title="Type your plot notes here", value="") + annotation.js_on_change('value', callback) callback.args["annotation"] = annotation - layout = layoutcolumn([annotation,plot]) + layout = layoutcolumn([annotation, plot]) try: script, div = components(layout) - except ValueError: # pragma: no cover + except ValueError: # pragma: no cover script = '' div = '' - return [script,div] + return [script, div] + def course_map(course): - latmean,lonmean,coordinates = course_coord_center(course) + latmean, lonmean, coordinates = course_coord_center(course) lat_min, lat_max, long_min, long_max = course_coord_maxmin(course) coordinates = course_spline(coordinates) scoordinates = "[" - for index,row in coordinates.iterrows(): + for index, row in coordinates.iterrows(): scoordinates += """[{x},{y}], """.format( x=row['latitude'], y=row['longitude'] ) - scoordinates +="]" + scoordinates += "]" - polygons = GeoPolygon.objects.filter(course=course).order_by("order_in_course") + polygons = GeoPolygon.objects.filter( + course=course).order_by("order_in_course") plabels = '' @@ -2307,10 +2232,10 @@ def course_map(course): marker.bindPopup("{name}"); """.format( - latbegin = coords[0], - longbegin = coords[1], - name = p.name - ) + latbegin=coords[0], + longbegin=coords[1], + name=p.name + ) pcoordinates = """[ """ @@ -2323,9 +2248,9 @@ def course_map(course): for pt in points: pcoordinates += "[{x},{y}],".format( - x = pt.latitude, - y = pt.longitude - ) + x=pt.latitude, + y=pt.longitude + ) # remove last comma pcoordinates = pcoordinates[:-1] @@ -2336,8 +2261,6 @@ def course_map(course): pcoordinates += """ ]""" - - script = """ """.format( - latmean=latmean, - lonmean=lonmean, - scoordinates=scoordinates, - pcoordinates=pcoordinates, - plabels = plabels - ) + latmean=latmean, + lonmean=lonmean, + scoordinates=scoordinates, + pcoordinates=pcoordinates, + plabels=plabels + ) div = """
""" - return script,div + return script, div def get_map_script_course( - latmean, - lonmean, - latbegin, - latend, - longbegin, - longend, - scoordinates, - course, - ): # pragma: no cover - latmean,lonmean,coordinates = course_coord_center(course) + latmean, + lonmean, + latbegin, + latend, + longbegin, + longend, + scoordinates, + course, +): # pragma: no cover + latmean, lonmean, coordinates = course_coord_center(course) lat_min, lat_max, long_min, long_max = course_coord_maxmin(course) coordinates = course_spline(coordinates) scoordinates = "[" - for index,row in coordinates.iterrows(): + for index, row in coordinates.iterrows(): scoordinates += """[{x},{y}], """.format( x=row['latitude'], y=row['longitude'] ) - scoordinates +="]" + scoordinates += "]" - polygons = GeoPolygon.objects.filter(course=course).order_by("order_in_course") + polygons = GeoPolygon.objects.filter( + course=course).order_by("order_in_course") plabels = '' @@ -2472,10 +2396,10 @@ def get_map_script_course( marker.bindPopup("{name}"); """.format( - latbegin = coords[0], - longbegin = coords[1], - name = p.name - ) + latbegin=coords[0], + longbegin=coords[1], + name=p.name + ) pcoordinates = """[ """ @@ -2488,9 +2412,9 @@ def get_map_script_course( for pt in points: pcoordinates += "[{x},{y}],".format( - x = pt.latitude, - y = pt.longitude - ) + x=pt.latitude, + y=pt.longitude + ) # remove last comma pcoordinates = pcoordinates[:-1] @@ -2589,29 +2513,29 @@ def get_map_script_course( """.format( - latmean=latmean, - lonmean=lonmean, - latbegin = latbegin, - latend=latend, - longbegin=longbegin, - longend=longend, - scoordinates=scoordinates, - pcoordinates=pcoordinates, - plabels=plabels - ) + latmean=latmean, + lonmean=lonmean, + latbegin=latbegin, + latend=latend, + longbegin=longbegin, + longend=longend, + scoordinates=scoordinates, + pcoordinates=pcoordinates, + plabels=plabels + ) return script def get_map_script( - latmean, - lonmean, - latbegin, - latend, - longbegin, - longend, - scoordinates, - ): + latmean, + lonmean, + latbegin, + latend, + longbegin, + longend, + scoordinates, +): script = """ """.format( - latmean=latmean, - lonmean=lonmean, - latbegin = latbegin, - latend=latend, - longbegin=longbegin, - longend=longend, - scoordinates=scoordinates, - ) + latmean=latmean, + lonmean=lonmean, + latbegin=latbegin, + latend=latend, + longbegin=longbegin, + longend=longend, + scoordinates=scoordinates, + ) return script -def leaflet_chart(lat,lon,name="",raceresult=0): - if lat.empty or lon.empty: # pragma: no cover - return [0,"invalid coordinate data"] +def leaflet_chart(lat, lon, name="", raceresult=0): + if lat.empty or lon.empty: # pragma: no cover + return [0, "invalid coordinate data"] # Throw out 0,0 df = pd.DataFrame({ - 'lat':lat, - 'lon':lon - }) + 'lat': lat, + 'lon': lon + }) - df = df.replace(0,np.nan) - df = df.loc[(df!=0).any(axis=1)] - df.fillna(method='bfill',axis=0,inplace=True) - df.fillna(method='ffill',axis=0,inplace=True) + df = df.replace(0, np.nan) + df = df.loc[(df != 0).any(axis=1)] + df.fillna(method='bfill', axis=0, inplace=True) + df.fillna(method='ffill', axis=0, inplace=True) lat = df['lat'] lon = df['lon'] - if lat.empty or lon.empty: # pragma: no cover - return [0,"invalid coordinate data"] + if lat.empty or lon.empty: # pragma: no cover + return [0, "invalid coordinate data"] latmean = lat.mean() lonmean = lon.mean() @@ -2733,16 +2657,16 @@ def leaflet_chart(lat,lon,name="",raceresult=0): latend = lat[lat.index[-1]] longend = lon[lon.index[-1]] - coordinates = zip(lat,lon) + coordinates = zip(lat, lon) scoordinates = "[" - for x,y in coordinates: + for x, y in coordinates: scoordinates += """[{x},{y}], """.format( x=x, y=y - ) + ) scoordinates += "]" @@ -2755,7 +2679,7 @@ def leaflet_chart(lat,lon,name="",raceresult=0): longbegin, longend, scoordinates, - ) + ) else: # pragma: no cover record = VirtualRaceResult.objects.get(id=raceresult) course = record.course @@ -2768,17 +2692,16 @@ def leaflet_chart(lat,lon,name="",raceresult=0): longend, scoordinates, course, - ) + ) div = """

 

""" + return script, div - return script,div - -def leaflet_chart_compare(course,workoutids,labeldict={},startenddict={}): +def leaflet_chart_compare(course, workoutids, labeldict={}, startenddict={}): data = [] for id in workoutids: if id != 0 and id is not None: @@ -2787,27 +2710,24 @@ def leaflet_chart_compare(course,workoutids,labeldict={},startenddict={}): rowdata = rdata(w.csvfilename) time = rowdata.df['TimeStamp (sec)'] df = pd.DataFrame({ - 'workoutid':id, - 'lat':rowdata.df[' latitude'], - 'lon':rowdata.df[' longitude'], - 'time':time-time[0], - }) + 'workoutid': id, + 'lat': rowdata.df[' latitude'], + 'lon': rowdata.df[' longitude'], + 'time': time-time[0], + }) data.append(df) - except (Workout.DoesNotExist,KeyError): # pragma: no cover + except (Workout.DoesNotExist, KeyError): # pragma: no cover pass + df = pd.concat(data, axis=0) - - df = pd.concat(data,axis=0) - - latmean,lonmean,coordinates = course_coord_center(course) + latmean, lonmean, coordinates = course_coord_center(course) lat_min, lat_max, long_min, long_max = course_coord_maxmin(course) coordinates = course_spline(coordinates) - - - polygons = GeoPolygon.objects.filter(course=course).order_by("order_in_course") + polygons = GeoPolygon.objects.filter( + course=course).order_by("order_in_course") plabels = '' @@ -2819,10 +2739,10 @@ def leaflet_chart_compare(course,workoutids,labeldict={},startenddict={}): marker.bindPopup("{name}"); """.format( - latbegin = coords[0], - longbegin = coords[1], - name = p.name - ) + latbegin=coords[0], + longbegin=coords[1], + name=p.name + ) pcoordinates = """[ """ @@ -2835,9 +2755,9 @@ def leaflet_chart_compare(course,workoutids,labeldict={},startenddict={}): for pt in points: pcoordinates += "[{x},{y}],".format( - x = pt.latitude, - y = pt.longitude - ) + x=pt.latitude, + y=pt.longitude + ) # remove last comma pcoordinates = pcoordinates[:-1] @@ -2848,18 +2768,16 @@ def leaflet_chart_compare(course,workoutids,labeldict={},startenddict={}): pcoordinates += """ ]""" - # Throw out 0,0 - df = df.replace(0,np.nan) - df = df.loc[(df!=0).any(axis=1)] - df.fillna(method='bfill',axis=0,inplace=True) - df.fillna(method='ffill',axis=0,inplace=True) - + df = df.replace(0, np.nan) + df = df.loc[(df != 0).any(axis=1)] + df.fillna(method='bfill', axis=0, inplace=True) + df.fillna(method='ffill', axis=0, inplace=True) lat = df['lat'] lon = df['lon'] - if lat.empty or lon.empty: # pragma: no cover - return [0,"invalid coordinate data"] + if lat.empty or lon.empty: # pragma: no cover + return [0, "invalid coordinate data"] latbegin = lat.values[0] longbegin = lon.values[0] @@ -2868,11 +2786,9 @@ def leaflet_chart_compare(course,workoutids,labeldict={},startenddict={}): colors = itertools.cycle(palette) try: - items = itertools.izip(workoutids,colors) + items = itertools.izip(workoutids, colors) except AttributeError: - items = zip(workoutids,colors) - - + items = zip(workoutids, colors) script = """ """ - - - div = """

 

""" + return script, div - return script,div - -def leaflet_chart2(lat,lon,name=""): - if lat.empty or lon.empty: # pragma: no cover - return [0,"invalid coordinate data"] - +def leaflet_chart2(lat, lon, name=""): + if lat.empty or lon.empty: # pragma: no cover + return [0, "invalid coordinate data"] # Throw out 0,0 df = pd.DataFrame({ - 'lat':lat, - 'lon':lon - }) + 'lat': lat, + 'lon': lon + }) - df = df.replace(0,np.nan) - df = df.loc[(df!=0).any(axis=1)] - df.fillna(method='bfill',axis=0,inplace=True) - df.fillna(method='ffill',axis=0,inplace=True) + df = df.replace(0, np.nan) + df = df.loc[(df != 0).any(axis=1)] + df.fillna(method='bfill', axis=0, inplace=True) + df.fillna(method='ffill', axis=0, inplace=True) lat = df['lat'] lon = df['lon'] - if lat.empty or lon.empty: # pragma: no cover - return [0,"invalid coordinate data"] + if lat.empty or lon.empty: # pragma: no cover + return [0, "invalid coordinate data"] latmean = lat.mean() lonmean = lon.mean() @@ -3057,16 +2968,16 @@ def leaflet_chart2(lat,lon,name=""): latend = lat[lat.index[-1]] longend = lon[lon.index[-1]] - coordinates = zip(lat,lon) + coordinates = zip(lat, lon) scoordinates = "[" - for x,y in coordinates: + for x, y in coordinates: scoordinates += """[{x},{y}], """.format( x=x, y=y - ) + ) scoordinates += "]" @@ -3152,42 +3063,40 @@ def leaflet_chart2(lat,lon,name=""): """.format( - latmean=latmean, - lonmean=lonmean, - latbegin = latbegin, - latend=latend, - longbegin=longbegin, - longend=longend, - scoordinates=scoordinates, - ) + latmean=latmean, + lonmean=lonmean, + latbegin=latbegin, + latend=latend, + longbegin=longbegin, + longend=longend, + scoordinates=scoordinates, + ) div = """

 

""" + return script, div - return script,div - -def leaflet_chart_video(lat,lon,name=""): - if not len(lat) or not len(lon): # pragma: no cover - return [0,"invalid coordinate data"] - +def leaflet_chart_video(lat, lon, name=""): + if not len(lat) or not len(lon): # pragma: no cover + return [0, "invalid coordinate data"] # Throw out 0,0 df = pd.DataFrame({ - 'lat':lat, - 'lon':lon - }) + 'lat': lat, + 'lon': lon + }) - df = df.replace(0,np.nan) - df = df.loc[(df!=0).any(axis=1)] - df.fillna(method='bfill',axis=0,inplace=True) - df.fillna(method='ffill',axis=0,inplace=True) + df = df.replace(0, np.nan) + df = df.loc[(df != 0).any(axis=1)] + df.fillna(method='bfill', axis=0, inplace=True) + df.fillna(method='ffill', axis=0, inplace=True) lat = df['lat'] lon = df['lon'] - if lat.empty or lon.empty: # pragma: no cover - return [0,"invalid coordinate data"] + if lat.empty or lon.empty: # pragma: no cover + return [0, "invalid coordinate data"] latmean = lat.mean() lonmean = lon.mean() @@ -3196,16 +3105,16 @@ def leaflet_chart_video(lat,lon,name=""): latend = lat[lat.index[-1]] longend = lon[lon.index[-1]] - coordinates = zip(lat,lon) + coordinates = zip(lat, lon) scoordinates = "[" - for x,y in coordinates: + for x, y in coordinates: scoordinates += """[{x},{y}], """.format( x=x, y=y - ) + ) scoordinates += "]" @@ -3290,27 +3199,25 @@ def leaflet_chart_video(lat,lon,name=""): mymap.fitBounds(polyline.getBounds()) """.format( - latmean=latmean, - lonmean=lonmean, - latbegin = latbegin, - latend=latend, - longbegin=longbegin, - longend=longend, - scoordinates=scoordinates, - ) + latmean=latmean, + lonmean=lonmean, + latbegin=latbegin, + latend=latend, + longbegin=longbegin, + longend=longend, + scoordinates=scoordinates, + ) div = """

 

""" + return script, div - return script,div - - -def interactive_agegroupcpchart(age,normalized=False): - durations = [1,4,30,60] - distances = [100,500,1000,2000,5000,6000,10000,21097,42195] +def interactive_agegroupcpchart(age, normalized=False): + durations = [1, 4, 30, 60] + distances = [100, 500, 1000, 2000, 5000, 6000, 10000, 21097, 42195] fhduration = [] fhpower = [] @@ -3323,7 +3230,7 @@ def interactive_agegroupcpchart(age,normalized=False): weightcategory='hwt' ) velo = (worldclasspower/2.8)**(1./3.) - try: # pragma: no cover + try: # pragma: no cover duration = distance/velo fhduration.append(duration) fhpower.append(worldclasspower) @@ -3335,13 +3242,13 @@ def interactive_agegroupcpchart(age,normalized=False): sex='female', duration=duration, weightcategory='hwt' - ) + ) try: velo = (worldclasspower/2.8)**(1./3.) distance = int(60*duration*velo) fhduration.append(60.*duration) fhpower.append(worldclasspower) - except ValueError: # pragma: no cover + except ValueError: # pragma: no cover pass flduration = [] @@ -3355,7 +3262,7 @@ def interactive_agegroupcpchart(age,normalized=False): weightcategory='lwt' ) velo = (worldclasspower/2.8)**(1./3.) - try: # pragma: no cover + try: # pragma: no cover duration = distance/velo flduration.append(duration) flpower.append(worldclasspower) @@ -3367,13 +3274,13 @@ def interactive_agegroupcpchart(age,normalized=False): sex='female', duration=duration, weightcategory='lwt' - ) + ) try: velo = (worldclasspower/2.8)**(1./3.) distance = int(60*duration*velo) flduration.append(60.*duration) flpower.append(worldclasspower) - except ValueError: # pragma: no cover + except ValueError: # pragma: no cover pass mlduration = [] @@ -3387,7 +3294,7 @@ def interactive_agegroupcpchart(age,normalized=False): weightcategory='lwt' ) velo = (worldclasspower/2.8)**(1./3.) - try: # pragma: no cover + try: # pragma: no cover duration = distance/velo mlduration.append(duration) mlpower.append(worldclasspower) @@ -3400,17 +3307,16 @@ def interactive_agegroupcpchart(age,normalized=False): sex='male', duration=duration, weightcategory='lwt' - ) + ) try: velo = (worldclasspower/2.8)**(1./3.) distance = int(60*duration*velo) mlduration.append(60.*duration) mlpower.append(worldclasspower) - except ValueError: # pragma: no cover + except ValueError: # pragma: no cover mlduration.append(60.*duration) mlpower.append(np.nan) - mhduration = [] mhpower = [] @@ -3422,7 +3328,7 @@ def interactive_agegroupcpchart(age,normalized=False): weightcategory='hwt' ) velo = (worldclasspower/2.8)**(1./3.) - try: # pragma: no cover + try: # pragma: no cover duration = distance/velo mhduration.append(duration) mhpower.append(worldclasspower) @@ -3435,62 +3341,62 @@ def interactive_agegroupcpchart(age,normalized=False): sex='male', duration=duration, weightcategory='hwt' - ) + ) try: velo = (worldclasspower/2.8)**(1./3.) distance = int(60*duration*velo) mhduration.append(60.*duration) mhpower.append(worldclasspower) - except ValueError: # pragma: no cover + except ValueError: # pragma: no cover mhduration.append(60.*duration) mhpower.append(np.nan) + def fitfunc(pars, x): return pars[0] / \ + (1+(x/pars[2])) + pars[1]/(1+(x/pars[3])) - - fitfunc = lambda pars,x: pars[0]/(1+(x/pars[2])) + pars[1]/(1+(x/pars[3])) - errfunc = lambda pars,x,y: fitfunc(pars,x)-y + def errfunc(pars, x, y): return fitfunc(pars, x)-y # p0 = [500,350,10,8000] # fitting WC data to three parameter CP model - if len(fhduration)>=4: + if len(fhduration) >= 4: p1fh, success = optimize.leastsq(errfunc, p0[:], - args = (fhduration,fhpower)) - else: # pragma: no cover + args=(fhduration, fhpower)) + else: # pragma: no cover p1fh = None # fitting WC data to three parameter CP model - if len(flduration)>=4: + if len(flduration) >= 4: p1fl, success = optimize.leastsq(errfunc, p0[:], - args = (flduration,flpower)) - else: # pragma: no cover + args=(flduration, flpower)) + else: # pragma: no cover p1fl = None # fitting WC data to three parameter CP model - if len(mlduration)>=4: + if len(mlduration) >= 4: p1ml, success = optimize.leastsq(errfunc, p0[:], - args = (mlduration,mlpower)) - else: # pragma: no cover + args=(mlduration, mlpower)) + else: # pragma: no cover p1ml = None - if len(mhduration)>=4: + if len(mhduration) >= 4: p1mh, success = optimize.leastsq(errfunc, p0[:], - args = (mhduration,mhpower)) - else: # pragma: no cover + args=(mhduration, mhpower)) + else: # pragma: no cover p1mh = None fitt = pd.Series(10**(4*np.arange(100)/100.)) - fitpowerfh = fitfunc(p1fh,fitt) - fitpowerfl = fitfunc(p1fl,fitt) - fitpowerml = fitfunc(p1ml,fitt) - fitpowermh = fitfunc(p1mh,fitt) + fitpowerfh = fitfunc(p1fh, fitt) + fitpowerfl = fitfunc(p1fl, fitt) + fitpowerml = fitfunc(p1ml, fitt) + fitpowermh = fitfunc(p1mh, fitt) if normalized: - facfh = fitfunc(p1fh,60) - facfl = fitfunc(p1fl,60) - facml = fitfunc(p1ml,60) - facmh = fitfunc(p1mh,60) + facfh = fitfunc(p1fh, 60) + facfl = fitfunc(p1fl, 60) + facml = fitfunc(p1ml, 60) + facmh = fitfunc(p1mh, 60) fitpowerfh /= facfh fitpowerfl /= facfl fitpowermh /= facmh @@ -3500,43 +3406,41 @@ def interactive_agegroupcpchart(age,normalized=False): mlpower /= facml mhpower /= facmh - - sourcemh = ColumnDataSource( - data = dict( - mhduration = mhduration, - mhpower = mhpower, - ) + data=dict( + mhduration=mhduration, + mhpower=mhpower, ) + ) sourcefl = ColumnDataSource( - data = dict( - flduration = flduration, - flpower = flpower, - ) + data=dict( + flduration=flduration, + flpower=flpower, ) + ) sourcefh = ColumnDataSource( - data = dict( - fhduration = fhduration, - fhpower = fhpower, - ) + data=dict( + fhduration=fhduration, + fhpower=fhpower, ) + ) sourceml = ColumnDataSource( - data = dict( - mlduration = mlduration, - mlpower = mlpower, - ) + data=dict( + mlduration=mlduration, + mlpower=mlpower, ) + ) sourcefit = ColumnDataSource( - data = dict( - duration = fitt, - fitpowerfh = fitpowerfh, - fitpowerfl = fitpowerfl, - fitpowerml = fitpowerml, - fitpowermh = fitpowermh, + data=dict( + duration=fitt, + fitpowerfh=fitpowerfh, + fitpowerfl=fitpowerfl, + fitpowerml=fitpowerml, + fitpowermh=fitpowermh, ) ) @@ -3545,33 +3449,32 @@ def interactive_agegroupcpchart(age,normalized=False): TOOLS = 'save,pan,box_zoom,wheel_zoom,reset,tap,hover,crosshair' - plot = Figure(plot_width=900,x_axis_type=x_axis_type, + plot = Figure(plot_width=900, x_axis_type=x_axis_type, tools=TOOLS) plot.sizing_mode = 'stretch_both' - plot.line('duration','fitpowerfh',source=sourcefit, - legend_label='Female HW',color='blue') - plot.line('duration','fitpowerfl',source=sourcefit, - legend_label='Female LW',color='red') + plot.line('duration', 'fitpowerfh', source=sourcefit, + legend_label='Female HW', color='blue') + plot.line('duration', 'fitpowerfl', source=sourcefit, + legend_label='Female LW', color='red') - plot.line('duration','fitpowerml',source=sourcefit, - legend_label='Male LW',color='green') + plot.line('duration', 'fitpowerml', source=sourcefit, + legend_label='Male LW', color='green') - plot.line('duration','fitpowermh',source=sourcefit, - legend_label='Male HW',color='orange') + plot.line('duration', 'fitpowermh', source=sourcefit, + legend_label='Male HW', color='orange') + plot.circle('flduration', 'flpower', source=sourcefl, + fill_color='red', size=15) - plot.circle('flduration','flpower',source=sourcefl, - fill_color='red',size=15) + plot.circle('fhduration', 'fhpower', source=sourcefh, + fill_color='blue', size=15) - plot.circle('fhduration','fhpower',source=sourcefh, - fill_color='blue',size=15) + plot.circle('mlduration', 'mlpower', source=sourceml, + fill_color='green', size=15) - plot.circle('mlduration','mlpower',source=sourceml, - fill_color='green',size=15) - - plot.circle('mhduration','mhpower',source=sourcemh, - fill_color='orange',size=15) + plot.circle('mhduration', 'mhpower', source=sourcemh, + fill_color='orange', size=15) plot.title.text = 'age '+str(age) @@ -3581,59 +3484,54 @@ def interactive_agegroupcpchart(age,normalized=False): else: plot.yaxis.axis_label = "Power (W)" - script,div = components(plot) + script, div = components(plot) - return script,div + return script, div -def interactive_otwcpchart(powerdf,promember=0,rowername="",r=None,cpfit='data', - title='',type='water', - wcpower=[],wcdurations=[],cpoverlay=False): +def interactive_otwcpchart(powerdf, promember=0, rowername="", r=None, cpfit='data', + title='', type='water', + wcpower=[], wcdurations=[], cpoverlay=False): powerdf2 = powerdf[~(powerdf == 0).any(axis=1)].copy() # plot tools - if (promember==1): # pragma: no cover + if (promember == 1): # pragma: no cover TOOLS = 'save,pan,box_zoom,wheel_zoom,reset,tap,hover,crosshair' else: TOOLS = 'pan,box_zoom,wheel_zoom,reset,tap,hover,crosshair' - x_axis_type = 'log' y_axis_type = 'linear' deltas = powerdf2['Delta'].apply(lambda x: timedeltaconv(x)) - powerdf2['ftime'] = deltas.apply(lambda x:strfdelta(x)) + powerdf2['ftime'] = deltas.apply(lambda x: strfdelta(x)) powerdf2['Deltaminutes'] = powerdf2['Delta']/60. - source = ColumnDataSource( - data = powerdf2 - ) - - + data=powerdf2 + ) # there is no Paul's law for OTW thesecs = powerdf2['Delta'] theavpower = powerdf2['CP'] - - p1,fitt,fitpower,ratio = datautils.cpfit(powerdf2) + p1, fitt, fitpower, ratio = datautils.cpfit(powerdf2) if cpfit == 'automatic' and r is not None: if type == 'water': - p1 = [r.p0,r.p1,r.p2,r.p3] + p1 = [r.p0, r.p1, r.p2, r.p3] ratio = r.cpratio - elif type == 'erg' : # pragma: no cover - p1 = [r.ep0,r.ep1,r.ep2,r.ep3] + elif type == 'erg': # pragma: no cover + p1 = [r.ep0, r.ep1, r.ep2, r.ep3] ratio = r.ecpratio - fitfunc = lambda pars,x: abs(pars[0])/(1+(x/abs(pars[2]))) + abs(pars[1])/(1+(x/abs(pars[3]))) - fitpower = fitfunc(p1,fitt) + def fitfunc(pars, x): return abs( + pars[0])/(1+(x/abs(pars[2]))) + abs(pars[1])/(1+(x/abs(pars[3]))) + fitpower = fitfunc(p1, fitt) message = "" - #if len(fitpower[fitpower<0]) > 0: - # message = "CP model fit didn't give correct results" - + # if len(fitpower[fitpower<0]) > 0: + # message = "CP model fit didn't give correct results" deltas = fitt.apply(lambda x: timedeltaconv(x)) ftime = niceformat(deltas) @@ -3641,25 +3539,26 @@ def interactive_otwcpchart(powerdf,promember=0,rowername="",r=None,cpfit='data', urls = powerdf2['url'] # add world class - wcpower = pd.Series(wcpower,dtype='float') - wcdurations = pd.Series(wcdurations,dtype='float') - + wcpower = pd.Series(wcpower, dtype='float') + wcdurations = pd.Series(wcdurations, dtype='float') # fitting WC data to three parameter CP model - if len(wcdurations)>=4: # pragma: no cover - fitfunc = lambda pars,x: pars[0]/(1+(x/pars[2])) + pars[1]/(1+(x/pars[3])) - errfunc = lambda pars,x,y: fitfunc(pars,x)-y + if len(wcdurations) >= 4: # pragma: no cover + def fitfunc(pars, x): return pars[0] / \ + (1+(x/pars[2])) + pars[1]/(1+(x/pars[3])) + + def errfunc(pars, x, y): return fitfunc(pars, x)-y p1wc, success = optimize.leastsq(errfunc, p0[:], - args = (wcdurations,wcpower)) + args=(wcdurations, wcpower)) else: p1wc = None - if p1wc is not None and cpoverlay: # pragma: no cover - fitpowerwc = fitfunc(p1wc,fitt) - fitpowerexcellent = 0.7*fitfunc(p1wc,fitt) - fitpowergood = 0.6*fitfunc(p1wc,fitt) - fitpowerfair = 0.5*fitfunc(p1wc,fitt) - fitpoweraverage = 0.4*fitfunc(p1wc,fitt) + if p1wc is not None and cpoverlay: # pragma: no cover + fitpowerwc = fitfunc(p1wc, fitt) + fitpowerexcellent = 0.7*fitfunc(p1wc, fitt) + fitpowergood = 0.6*fitfunc(p1wc, fitt) + fitpowerfair = 0.5*fitfunc(p1wc, fitt) + fitpoweraverage = 0.4*fitfunc(p1wc, fitt) else: fitpowerwc = 0*fitpower @@ -3668,32 +3567,31 @@ def interactive_otwcpchart(powerdf,promember=0,rowername="",r=None,cpfit='data', fitpowerfair = 0*fitpower fitpoweraverage = 0*fitpower - sourcecomplex = ColumnDataSource( - data = dict( - CP = fitpower, - CPmax = ratio*fitpower, - duration = fitt/60., - ftime = ftime, -# workout = workouts, - fitpowerwc = fitpowerwc, - fitpowerexcellent = fitpowerexcellent, - fitpowergood = fitpowergood, - fitpowerfair = fitpowerfair, - fitpoweraverage = fitpoweraverage, -# url = urls, - ) - ) - - sourceannot= ColumnDataSource( - data = dict( - workout = workouts, - url = urls, + data=dict( + CP=fitpower, + CPmax=ratio*fitpower, + duration=fitt/60., + ftime=ftime, + # workout = workouts, + fitpowerwc=fitpowerwc, + fitpowerexcellent=fitpowerexcellent, + fitpowergood=fitpowergood, + fitpowerfair=fitpowerfair, + fitpoweraverage=fitpoweraverage, + # url = urls, ) ) - # making the plot - plot = Figure(tools=TOOLS,x_axis_type=x_axis_type, + sourceannot = ColumnDataSource( + data=dict( + workout=workouts, + url=urls, + ) + ) + + # making the plot + plot = Figure(tools=TOOLS, x_axis_type=x_axis_type, plot_width=900, toolbar_location="above", toolbar_sticky=False) @@ -3701,9 +3599,9 @@ def interactive_otwcpchart(powerdf,promember=0,rowername="",r=None,cpfit='data', # add watermark watermarkurl = "/static/img/logo7.png" watermarksource = ColumnDataSource(dict( - url = [watermarkurl],)) + url=[watermarkurl],)) - watermarkrange = Range1d(start=0,end=1) + watermarkrange = Range1d(start=0, end=1) watermarkalpha = 0.6 watermarkx = 0.99 watermarky = 0.01 @@ -3713,25 +3611,23 @@ def interactive_otwcpchart(powerdf,promember=0,rowername="",r=None,cpfit='data', plot.extra_y_ranges = {"watermark": watermarkrange} plot.sizing_mode = 'scale_both' - - - plot.image_url([watermarkurl],1.8*max(thesecs),watermarky, - watermarkw,watermarkh, + plot.image_url([watermarkurl], 1.8*max(thesecs), watermarky, + watermarkw, watermarkh, global_alpha=watermarkalpha, w_units='screen', h_units='screen', anchor=watermarkanchor, dilate=True, - y_range_name = "watermark", + y_range_name="watermark", ) - plot.circle('Deltaminutes','CP',source=source,fill_color='red',size=15, + plot.circle('Deltaminutes', 'CP', source=source, fill_color='red', size=15, legend_label='Power Data') plot.xaxis.axis_label = "Duration (minutes)" plot.yaxis.axis_label = "Power (W)" - plot.y_range = Range1d(0,1.5*max(theavpower)) - plot.x_range = Range1d(0.5*min(thesecs)/60.,2*max(thesecs)/60.) + plot.y_range = Range1d(0, 1.5*max(theavpower)) + plot.x_range = Range1d(0.5*min(thesecs)/60., 2*max(thesecs)/60.) plot.legend.orientation = "vertical" if not title: title = "Critical Power for "+rowername @@ -3740,65 +3636,65 @@ def interactive_otwcpchart(powerdf,promember=0,rowername="",r=None,cpfit='data', xaxis = plot.select(dict(type=Axis, layout="below"))[0] xaxis.formatter = PrintfTickFormatter() - hover = plot.select(dict(type=HoverTool)) hover.tooltips = OrderedDict([ - ('Duration ','@ftime'), - ('Power (W)','@CP{int}'), - ('Power (W) upper','@CPmax{int}'), - ('Workout','@workout'), - ('World Class','@fitpowerwc{int}') - ]) + ('Duration ', '@ftime'), + ('Power (W)', '@CP{int}'), + ('Power (W) upper', '@CPmax{int}'), + ('Workout', '@workout'), + ('World Class', '@fitpowerwc{int}') + ]) hover.mode = 'mouse' taptool = plot.select(type=TapTool) taptool.callback = OpenURL(url='@url') - plot.line('duration','CP',source=sourcecomplex,legend_label="CP Model", + plot.line('duration', 'CP', source=sourcecomplex, legend_label="CP Model", color='green') - plot.line('duration','CPmax',source=sourcecomplex,legend_label="CP Model", + plot.line('duration', 'CPmax', source=sourcecomplex, legend_label="CP Model", color='red') - if p1wc is not None: # pragma: no cover - plot.line('duration','fitpowerwc',source=sourcecomplex, + if p1wc is not None: # pragma: no cover + plot.line('duration', 'fitpowerwc', source=sourcecomplex, legend_label="Gold Medal Standard", - color='darkgoldenrod',line_dash='dotted') + color='darkgoldenrod', line_dash='dotted') - plot.line('duration','fitpowerexcellent',source=sourcecomplex, + plot.line('duration', 'fitpowerexcellent', source=sourcecomplex, legend_label="90% percentile", - color='goldenrod',line_dash='dotted') + color='goldenrod', line_dash='dotted') - plot.line('duration','fitpowergood',source=sourcecomplex, + plot.line('duration', 'fitpowergood', source=sourcecomplex, legend_label="75% percentile", - color='sandybrown',line_dash='dotted') + color='sandybrown', line_dash='dotted') - plot.line('duration','fitpowerfair',source=sourcecomplex, + plot.line('duration', 'fitpowerfair', source=sourcecomplex, legend_label="50% percentile", - color='rosybrown',line_dash='dotted') + color='rosybrown', line_dash='dotted') - plot.line('duration','fitpoweraverage',source=sourcecomplex, + plot.line('duration', 'fitpoweraverage', source=sourcecomplex, legend_label="25% percentile", - color='tan',line_dash='dotted') + color='tan', line_dash='dotted') script, div = components(plot) - return [script,div,p1,ratio,message] + return [script, div, p1, ratio, message] -def interactive_agegroup_plot(df,distance=2000,duration=None, - sex='male',weightcategory='hwt'): + +def interactive_agegroup_plot(df, distance=2000, duration=None, + sex='male', weightcategory='hwt'): if df.empty: - return '','' + return '', '' age = df['age'] power = df['power'] name = df['name'] season = df['season'] - if duration: # pragma: no cover + if duration: # pragma: no cover duration2 = int(duration/60.) plottitle = sex+' '+weightcategory+' %s min' % duration2 else: @@ -3806,48 +3702,49 @@ def interactive_agegroup_plot(df,distance=2000,duration=None, # poly_coefficients = np.polyfit(age,power,6) - age2 = np.linspace(11,95) + age2 = np.linspace(11, 95) # poly_vals = np.polyval(poly_coefficients,age2) # poly_vals = 0.5*(np.abs(poly_vals)+poly_vals) - fitfunc = lambda pars, x: np.abs(pars[0])*(1-x/max(120,pars[1]))-np.abs(pars[2])*np.exp(-x/np.abs(pars[3]))+np.abs(pars[4])*(np.sin(np.pi*x/max(50,pars[5]))) - errfunc = lambda pars, x,y: fitfunc(pars,x)-y + def fitfunc(pars, x): return np.abs(pars[0])*(1-x/max(120, pars[1]))-np.abs( + pars[2])*np.exp(-x/np.abs(pars[3]))+np.abs(pars[4])*(np.sin(np.pi*x/max(50, pars[5]))) - p0age = [700,120,700,10,100,100] + def errfunc(pars, x, y): return fitfunc(pars, x)-y - p1, success = optimize.leastsq(errfunc,p0age[:], - args = (age,power)) + p0age = [700, 120, 700, 10, 100, 100] + + p1, success = optimize.leastsq(errfunc, p0age[:], + args=(age, power)) expo_vals = fitfunc(p1, age2) expo_vals = 0.5*(np.abs(expo_vals)+expo_vals) - source = ColumnDataSource( - data = dict( - age = age, - power = power, + data=dict( + age=age, + power=power, #age2 = age2, #expo_vals = expo_vals, - season = season, + season=season, name=name, - ) + ) ) sourcefit = ColumnDataSource( - data = dict( - age2 = age2, - expo_vals = expo_vals, + data=dict( + age2=age2, + expo_vals=expo_vals, ) ) TOOLS = 'save,pan,box_zoom,wheel_zoom,reset,tap,hover,crosshair' - plot = Figure(tools=TOOLS,plot_width=900) - plot.sizing_mode='stretch_both' - plot.circle('age','power',source=source,fill_color='red',size=15, + plot = Figure(tools=TOOLS, plot_width=900) + plot.sizing_mode = 'stretch_both' + plot.circle('age', 'power', source=source, fill_color='red', size=15, legend_label='World Record') - plot.line('age2','expo_vals',source=sourcefit) + plot.line('age2', 'expo_vals', source=sourcefit) plot.xaxis.axis_label = "Age" plot.yaxis.axis_label = "Concept2 power" plot.title.text = plottitle @@ -3855,28 +3752,28 @@ def interactive_agegroup_plot(df,distance=2000,duration=None, hover = plot.select(dict(type=HoverTool)) hover.tooltips = OrderedDict([ - ('Name ','@name'), - ('Season ','@season'), - ]) + ('Name ', '@name'), + ('Season ', '@season'), + ]) hover.mode = 'mouse' - script,div = components(plot) + script, div = components(plot) - return script,div + return script, div -def interactive_cpchart(rower,thedistances,thesecs,theavpower, - theworkouts,promember=0, - wcpower=[],wcdurations=[]): + +def interactive_cpchart(rower, thedistances, thesecs, theavpower, + theworkouts, promember=0, + wcpower=[], wcdurations=[]): message = 0 # plot tools - if (promember==1): + if (promember == 1): TOOLS = 'save,pan,box_zoom,wheel_zoom,reset,tap,hover,crosshair' - else: # pragma: no cover + else: # pragma: no cover TOOLS = 'pan,box_zoom,wheel_zoom,reset,tap,hover,crosshair' - x_axis_type = 'log' y_axis_type = 'linear' @@ -3888,27 +3785,28 @@ def interactive_cpchart(rower,thedistances,thesecs,theavpower, p2 = p.fillna(method='ffill').apply(lambda x: timedeltaconv(x)) source = ColumnDataSource( - data = dict( - dist = thedistances, - duration = thesecs, - spm = 0*theavpower, - tim = niceformat( - thesecs.fillna(method='ffill').apply(lambda x: timedeltaconv(x)) - ), + data=dict( + dist=thedistances, + duration=thesecs, + spm=0*theavpower, + tim=niceformat( + thesecs.fillna(method='ffill').apply( + lambda x: timedeltaconv(x)) + ), - power = theavpower, - fpace = nicepaceformat(p2), - ) + power=theavpower, + fpace=nicepaceformat(p2), ) + ) # fitting the data to Paul - if len(thedistances)>=2: - paulslope, paulintercept,r,p,stderr = linregress(np.log10(thedistances),p) - else: # pragma: no cover + if len(thedistances) >= 2: + paulslope, paulintercept, r, p, stderr = linregress( + np.log10(thedistances), p) + else: # pragma: no cover paulslope = 5.0/np.log10(2.0) paulintercept = p[0]-paulslope*np.log10(thedistances[0]) - fitx = pd.Series(np.arange(100)*2*max(np.log10(thedistances))/100.) fitp = paulslope*fitx+paulintercept @@ -3918,60 +3816,60 @@ def interactive_cpchart(rower,thedistances,thesecs,theavpower, fitt = 10**fitx/fitvelo fitp2 = fitp.fillna(method='ffill').apply(lambda x: timedeltaconv(x)) - sourcepaul = ColumnDataSource( - data = dict( - dist = 10**fitx, - duration = fitt, - power = fitpower, - spm = 0*fitpower, - tim = niceformat( + data=dict( + dist=10**fitx, + duration=fitt, + power=fitpower, + spm=0*fitpower, + tim=niceformat( fitt.fillna(method='ffill').apply(lambda x: timedeltaconv(x)) - ), - fpace = nicepaceformat(fitp2), - ) + ), + fpace=nicepaceformat(fitp2), ) + ) + def fitfunc(pars, x): return pars[0] / \ + (1+(x/pars[2])) + pars[1]/(1+(x/pars[3])) - fitfunc = lambda pars,x: pars[0]/(1+(x/pars[2])) + pars[1]/(1+(x/pars[3])) - errfunc = lambda pars,x,y: fitfunc(pars,x)-y + def errfunc(pars, x, y): return fitfunc(pars, x)-y # p0 = [500,350,10,8000] - wcpower = pd.Series(wcpower,dtype='float') - wcdurations = pd.Series(wcdurations,dtype='float') + wcpower = pd.Series(wcpower, dtype='float') + wcdurations = pd.Series(wcdurations, dtype='float') # fitting WC data to three parameter CP model - if len(wcdurations)>=4: + if len(wcdurations) >= 4: p1wc, success = optimize.leastsq(errfunc, p0[:], - args = (wcdurations,wcpower)) - else: # pragma: no cover + args=(wcdurations, wcpower)) + else: # pragma: no cover p1wc = None # fitting the data to three parameter CP model success = 0 p1 = p0 - if len(thesecs)>=4: + if len(thesecs) >= 4: try: - p1, success = optimize.leastsq(errfunc, p0[:], args = (thesecs,theavpower)) - except (RuntimeError,RuntimeWarning): # pragma: no cover - factor = fitfunc(p0,thesecs.mean())/theavpower.mean() - p1 = [p0[0]/factor,p0[1]/factor,p0[2],p0[3]] + p1, success = optimize.leastsq( + errfunc, p0[:], args=(thesecs, theavpower)) + except (RuntimeError, RuntimeWarning): # pragma: no cover + factor = fitfunc(p0, thesecs.mean())/theavpower.mean() + p1 = [p0[0]/factor, p0[1]/factor, p0[2], p0[3]] success = 0 - else: # pragma: no cover - factor = fitfunc(p0,thesecs.mean())/theavpower.mean() - p1 = [p0[0]/factor,p0[1]/factor,p0[2],p0[3]] + else: # pragma: no cover + factor = fitfunc(p0, thesecs.mean())/theavpower.mean() + p1 = [p0[0]/factor, p0[1]/factor, p0[2], p0[3]] success = 0 - # Get stayer score - if success == 1: # pragma: no cover - power1min = fitfunc(p1,60.) - power4min = fitfunc(p1,240.) - power6min = fitfunc(p1,360.) - power30min = fitfunc(p1,1800.) - power1h = fitfunc(p1,3600.) - power10sec = fitfunc(p1,10.) + if success == 1: # pragma: no cover + power1min = fitfunc(p1, 60.) + power4min = fitfunc(p1, 240.) + power6min = fitfunc(p1, 360.) + power30min = fitfunc(p1, 1800.) + power1h = fitfunc(p1, 3600.) + power10sec = fitfunc(p1, 10.) r10sec4min = 100.*power10sec/power4min r1h4min = 100.*power1h/power4min r1min6min = 100.*power1min/power6min @@ -3983,34 +3881,31 @@ def interactive_cpchart(rower,thedistances,thesecs,theavpower, dataset = pd.read_csv('static/stats/combined_set.csv') dataset2 = pd.read_csv('static/stats/combined_set6min.csv') - stayerscore = int(percentileofscore(dataset['combined'],combined)) - stayerscore2 = int(percentileofscore(dataset2['combined'],combined2)) + stayerscore = int(percentileofscore(dataset['combined'], combined)) + stayerscore2 = int(percentileofscore(dataset2['combined'], combined2)) else: stayerscore = None stayerscore2 = None - fitt = pd.Series(10**(4*np.arange(100)/100.)) - fitpower = fitfunc(p1,fitt) + fitpower = fitfunc(p1, fitt) if p1wc is not None: - fitpowerwc = 0.95*fitfunc(p1wc,fitt) - fitpowerexcellent = 0.7*fitfunc(p1wc,fitt) - fitpowergood = 0.6*fitfunc(p1wc,fitt) - fitpowerfair = 0.5*fitfunc(p1wc,fitt) - fitpoweraverage = 0.4*fitfunc(p1wc,fitt) + fitpowerwc = 0.95*fitfunc(p1wc, fitt) + fitpowerexcellent = 0.7*fitfunc(p1wc, fitt) + fitpowergood = 0.6*fitfunc(p1wc, fitt) + fitpowerfair = 0.5*fitfunc(p1wc, fitt) + fitpoweraverage = 0.4*fitfunc(p1wc, fitt) - else: # pragma: no cover + else: # pragma: no cover fitpowerwc = 0*fitpower fitpowerexcellent = 0*fitpower fitpowergood = 0*fitpower fitpowerfair = 0*fitpower fitpoweraverage = 0*fitpower - - message = "" - if len(fitpower[fitpower<0]) > 0: # pragma: no cover + if len(fitpower[fitpower < 0]) > 0: # pragma: no cover message = "CP model fit didn't give correct results" fitvelo = (fitpower/2.8)**(1./3.) @@ -4019,27 +3914,25 @@ def interactive_cpchart(rower,thedistances,thesecs,theavpower, fitp2 = fitp.fillna(method='ffill').apply(lambda x: timedeltaconv(x)) sourcecomplex = ColumnDataSource( - data = dict( - dist = fitdist, - duration = fitt, - tim = niceformat( + data=dict( + dist=fitdist, + duration=fitt, + tim=niceformat( fitt.fillna(method='ffill').apply(lambda x: timedeltaconv(x)) - ), - spm = 0*fitpower, - power = fitpower, - fitpowerwc = fitpowerwc, - fitpowerexcellent = fitpowerexcellent, - fitpowergood = fitpowergood, - fitpowerfair = fitpowerfair, - fitpoweraverage = fitpoweraverage, - fpace = nicepaceformat(fitp2), - ) + ), + spm=0*fitpower, + power=fitpower, + fitpowerwc=fitpowerwc, + fitpowerexcellent=fitpowerexcellent, + fitpowergood=fitpowergood, + fitpowerfair=fitpowerfair, + fitpoweraverage=fitpoweraverage, + fpace=nicepaceformat(fitp2), ) - - + ) # making the plot - plot = Figure(tools=TOOLS,x_axis_type=x_axis_type, + plot = Figure(tools=TOOLS, x_axis_type=x_axis_type, plot_width=900, toolbar_location="above", toolbar_sticky=False) @@ -4047,9 +3940,9 @@ def interactive_cpchart(rower,thedistances,thesecs,theavpower, # add watermark watermarkurl = "/static/img/logo7.png" watermarksource = ColumnDataSource(dict( - url = [watermarkurl],)) + url=[watermarkurl],)) - watermarkrange = Range1d(start=0,end=1) + watermarkrange = Range1d(start=0, end=1) watermarkalpha = 0.6 watermarkx = 0.99 watermarky = 0.01 @@ -4059,30 +3952,29 @@ def interactive_cpchart(rower,thedistances,thesecs,theavpower, plot.extra_y_ranges = {"watermark": watermarkrange} plot.sizing_mode = 'scale_both' - - plot.image_url([watermarkurl],1.8*max(thesecs),watermarky, - watermarkw,watermarkh, + plot.image_url([watermarkurl], 1.8*max(thesecs), watermarky, + watermarkw, watermarkh, global_alpha=watermarkalpha, w_units='screen', h_units='screen', anchor=watermarkanchor, dilate=True, - y_range_name = "watermark", + y_range_name="watermark", ) - plot.circle('duration','power',source=source,fill_color='red',size=15, + plot.circle('duration', 'power', source=source, fill_color='red', size=15, legend_label='Power') plot.xaxis.axis_label = "Duration (seconds)" plot.yaxis.axis_label = "Power (W)" - if stayerscore is not None: # pragma: no cover + if stayerscore is not None: # pragma: no cover plot.add_layout( - Label(x=100,y=100,x_units='screen',y_units='screen', + Label(x=100, y=100, x_units='screen', y_units='screen', text='Stayer Score '+str(stayerscore)+'%', background_fill_alpha=0.7, background_fill_color='white', text_color='black') - ) + ) # plot.add_layout( # Label(x=100,y=120,x_units='screen',y_units='screen', # text='Stayer Score (6min) '+str(stayerscore2)+'%', @@ -4093,10 +3985,9 @@ def interactive_cpchart(rower,thedistances,thesecs,theavpower, cpdata = dataprep.fetchcperg(rower, theworkouts) - - if cpdata.empty: # pragma: no cover + if cpdata.empty: # pragma: no cover message = 'Calculations are running in the background. Please refresh this page to see updated results' - return ['','',paulslope,paulintercept,p1,message,p1wc] + return ['', '', paulslope, paulintercept, p1, message, p1wc] velo = cpdata['distance']/cpdata['delta'] @@ -4105,68 +3996,68 @@ def interactive_cpchart(rower,thedistances,thesecs,theavpower, p2 = p.fillna(method='ffill').apply(lambda x: timedeltaconv(x)) source2 = ColumnDataSource( - data = dict( - duration = cpdata['delta'], - power = cpdata['cp'], - tim = niceformat( - cpdata['delta'].fillna(method='ffill').apply(lambda x: timedeltaconv(x)) - ), - dist = cpdata['distance'], - pace = nicepaceformat(p2), - ) + data=dict( + duration=cpdata['delta'], + power=cpdata['cp'], + tim=niceformat( + cpdata['delta'].fillna(method='ffill').apply( + lambda x: timedeltaconv(x)) + ), + dist=cpdata['distance'], + pace=nicepaceformat(p2), ) + ) - plot.circle('duration','power',source=source2, - fill_color='blue',size=3, - legend_label = 'Power from segments') + plot.circle('duration', 'power', source=source2, + fill_color='blue', size=3, + legend_label='Power from segments') hover = plot.select(dict(type=HoverTool)) hover.tooltips = OrderedDict([ - ('Duration ','@tim'), - ('Power (W)','@power{int}'), - ('Distance (m)','@dist{int}'), - ('Pace (/500m)','@fpace'), - ]) + ('Duration ', '@tim'), + ('Power (W)', '@power{int}'), + ('Distance (m)', '@dist{int}'), + ('Pace (/500m)', '@fpace'), + ]) hover.mode = 'mouse' - plot.y_range = Range1d(0,1.5*max(theavpower)) - plot.x_range = Range1d(1,2*max(thesecs)) + plot.y_range = Range1d(0, 1.5*max(theavpower)) + plot.x_range = Range1d(1, 2*max(thesecs)) plot.legend.orientation = "vertical" - - plot.line('duration','power',source=sourcepaul,legend_label="Paul's Law") - plot.line('duration','power',source=sourcecomplex,legend_label="CP Model", + plot.line('duration', 'power', source=sourcepaul, + legend_label="Paul's Law") + plot.line('duration', 'power', source=sourcecomplex, legend_label="CP Model", color='green') if p1wc is not None: - plot.line('duration','fitpowerwc',source=sourcecomplex, + plot.line('duration', 'fitpowerwc', source=sourcecomplex, legend_label="World Class", - color='Maroon',line_dash='dotted') + color='Maroon', line_dash='dotted') - plot.line('duration','fitpowerexcellent',source=sourcecomplex, + plot.line('duration', 'fitpowerexcellent', source=sourcecomplex, legend_label="90% percentile", - color='Purple',line_dash='dotted') + color='Purple', line_dash='dotted') - plot.line('duration','fitpowergood',source=sourcecomplex, + plot.line('duration', 'fitpowergood', source=sourcecomplex, legend_label="75% percentile", - color='Olive',line_dash='dotted') + color='Olive', line_dash='dotted') - plot.line('duration','fitpowerfair',source=sourcecomplex, + plot.line('duration', 'fitpowerfair', source=sourcecomplex, legend_label="50% percentile", - color='Gray',line_dash='dotted') + color='Gray', line_dash='dotted') - plot.line('duration','fitpoweraverage',source=sourcecomplex, + plot.line('duration', 'fitpoweraverage', source=sourcecomplex, legend_label="25% percentile", - color='SkyBlue',line_dash='dotted') - + color='SkyBlue', line_dash='dotted') script, div = components(plot) + return [script, div, paulslope, paulintercept, p1, message, p1wc] - return [script,div,paulslope,paulintercept,p1,message,p1wc] -def interactive_windchart(id=0,promember=0): +def interactive_windchart(id=0, promember=0): # check if valid ID exists (workout exists) row = Workout.objects.get(id=id) # g = GraphImage.objects.filter(workout=row).order_by("-creationdatetime") @@ -4174,92 +4065,88 @@ def interactive_windchart(id=0,promember=0): f1 = row.csvfilename # create interactive plot - plot = Figure(plot_width=400,plot_height=300) + plot = Figure(plot_width=400, plot_height=300) # get user # u = User.objects.get(id=row.user.id) r = row.user u = r.user + rr = rrower(hrmax=r.max, hrut2=r.ut2, + hrut1=r.ut1, hrat=r.at, + hrtr=r.tr, hran=r.an, ftp=r.ftp) - - rr = rrower(hrmax=r.max,hrut2=r.ut2, - hrut1=r.ut1,hrat=r.at, - hrtr=r.tr,hran=r.an,ftp=r.ftp) - - rowdata = rdata(f1,rower=rr) - if rowdata == 0: # pragma: no cover + rowdata = rdata(f1, rower=rr) + if rowdata == 0: # pragma: no cover return 0 try: - dist = rowdata.df.loc[:,'cum_dist'] + dist = rowdata.df.loc[:, 'cum_dist'] except KeyError: - return ['','No Data Found'] + return ['', 'No Data Found'] - try: # pragma: no cover - vwind = rowdata.df.loc[:,'vwind'] - winddirection = rowdata.df.loc[:,'winddirection'] - bearing = rowdata.df.loc[:,'bearing'] + try: # pragma: no cover + vwind = rowdata.df.loc[:, 'vwind'] + winddirection = rowdata.df.loc[:, 'winddirection'] + bearing = rowdata.df.loc[:, 'bearing'] except KeyError: - rowdata.add_wind(0,0) + rowdata.add_wind(0, 0) rowdata.add_bearing() - vwind = rowdata.df.loc[:,'vwind'] - winddirection = rowdata.df.loc[:,'winddirection'] - bearing = rowdata.df.loc[:,'winddirection'] - rowdata.write_csv(f1,gzip=True) - dataprep.update_strokedata(id,rowdata.df) + vwind = rowdata.df.loc[:, 'vwind'] + winddirection = rowdata.df.loc[:, 'winddirection'] + bearing = rowdata.df.loc[:, 'winddirection'] + rowdata.write_csv(f1, gzip=True) + dataprep.update_strokedata(id, rowdata.df) winddirection = winddirection % 360 winddirection = (winddirection + 360) % 360 - tw = tailwind(bearing,vwind,1.0*winddirection) - + tw = tailwind(bearing, vwind, 1.0*winddirection) source = ColumnDataSource( - data = dict( + data=dict( dist=dist, vwind=vwind, tw=tw, winddirection=winddirection, - ) ) + ) # plot tools - if (promember==1): + if (promember == 1): TOOLS = 'save,pan,box_zoom,wheel_zoom,reset,tap,crosshair' - else: # pragma: no cover + else: # pragma: no cover TOOLS = 'pan,box_zoom,wheel_zoom,reset,tap,crosshair' - - # making the plot - plot = Figure(tools=TOOLS,plot_width=400,height=500, -# toolbar_location="below", + plot = Figure(tools=TOOLS, plot_width=400, height=500, + # toolbar_location="below", toolbar_sticky=False, ) - plot.line('dist','vwind',source=source,legend_label="Wind Speed (m/s)") - plot.line('dist','tw',source=source,legend_label="Tail (+)/Head (-) Wind (m/s)",color='black') + plot.line('dist', 'vwind', source=source, legend_label="Wind Speed (m/s)") + plot.line('dist', 'tw', source=source, + legend_label="Tail (+)/Head (-) Wind (m/s)", color='black') plot.title.text = row.name # plot.title.text_font_size=value("1.0em") - plot.title.text_font="1.0em" + plot.title.text_font = "1.0em" plot.xaxis.axis_label = "Distance (m)" plot.yaxis.axis_label = "Wind Speed (m/s)" - plot.y_range = Range1d(-7,7) + plot.y_range = Range1d(-7, 7) plot.sizing_mode = 'stretch_both' - - plot.extra_y_ranges = {"winddirection": Range1d(start=0,end=360)} - plot.line('dist','winddirection',source=source, - legend_label='Wind Direction',color="red", + plot.extra_y_ranges = {"winddirection": Range1d(start=0, end=360)} + plot.line('dist', 'winddirection', source=source, + legend_label='Wind Direction', color="red", y_range_name="winddirection") - plot.add_layout(LinearAxis(y_range_name="winddirection",axis_label="Wind Direction (degree)"),'right') - + plot.add_layout(LinearAxis(y_range_name="winddirection", + axis_label="Wind Direction (degree)"), 'right') script, div = components(plot) - return [script,div] + return [script, div] -def interactive_streamchart(id=0,promember=0): + +def interactive_streamchart(id=0, promember=0): # check if valid ID exists (workout exists) row = Workout.objects.get(id=id) # g = GraphImage.objects.filter(workout=row).order_by("-creationdatetime") @@ -4274,88 +4161,83 @@ def interactive_streamchart(id=0,promember=0): r = row.user u = r.user + rr = rrower(hrmax=r.max, hrut2=r.ut2, + hrut1=r.ut1, hrat=r.at, + hrtr=r.tr, hran=r.an, ftp=r.ftp) - - rr = rrower(hrmax=r.max,hrut2=r.ut2, - hrut1=r.ut1,hrat=r.at, - hrtr=r.tr,hran=r.an,ftp=r.ftp) - - rowdata = rdata(f1,rower=rr) - if rowdata == 0: # pragma: no cover - return "","No Valid Data Available" + rowdata = rdata(f1, rower=rr) + if rowdata == 0: # pragma: no cover + return "", "No Valid Data Available" try: - dist = rowdata.df.loc[:,'cum_dist'] + dist = rowdata.df.loc[:, 'cum_dist'] except KeyError: - return ['','No Data found'] + return ['', 'No Data found'] try: - vstream = rowdata.df.loc[:,'vstream'] + vstream = rowdata.df.loc[:, 'vstream'] except KeyError: rowdata.add_stream(0) - vstream = rowdata.df.loc[:,'vstream'] - rowdata.write_csv(f1,gzip=True) - dataprep.update_strokedata(id,rowdata.df) + vstream = rowdata.df.loc[:, 'vstream'] + rowdata.write_csv(f1, gzip=True) + dataprep.update_strokedata(id, rowdata.df) # plot tools - if (promember==1): + if (promember == 1): TOOLS = 'save,pan,box_zoom,wheel_zoom,reset,tap,crosshair' - else: # pragma: no cover + else: # pragma: no cover TOOLS = 'pan,box_zoom,wheel_zoom,reset,tap,crosshair' - - # making the plot - plot = Figure(tools=TOOLS,plot_width=400,height=500, -# toolbar_location="below", + plot = Figure(tools=TOOLS, plot_width=400, height=500, + # toolbar_location="below", toolbar_sticky=False, ) - plot.line(dist,vstream,legend_label="River Stream Velocity (m/s)") + plot.line(dist, vstream, legend_label="River Stream Velocity (m/s)") plot.title.text = row.name - plot.title.text_font_size=value("1.0em") + plot.title.text_font_size = value("1.0em") plot.xaxis.axis_label = "Distance (m)" plot.yaxis.axis_label = "River Current (m/s)" - plot.y_range = Range1d(-2,2) + plot.y_range = Range1d(-2, 2) plot.sizing_mode = 'stretch_both' - script, div = components(plot) - return [script,div] + return [script, div] -def interactive_chart(id=0,promember=0,intervaldata = {}): + +def interactive_chart(id=0, promember=0, intervaldata={}): # Add hover to this comma-separated string and see what changes - if (promember==1): + if (promember == 1): TOOLS = 'save,pan,box_zoom,wheel_zoom,reset,tap,hover,crosshair' else: TOOLS = 'pan,box_zoom,wheel_zoom,reset,tap,hover,crosshair' + columns = ['time', 'pace', 'hr', 'fpace', 'ftime', 'spm'] + datadf = dataprep.getsmallrowdata_db(columns, ids=[id]) - columns = ['time','pace','hr','fpace','ftime','spm'] - datadf = dataprep.getsmallrowdata_db(columns,ids=[id]) - - datadf.dropna(axis=0,how='any',inplace=True) + datadf.dropna(axis=0, how='any', inplace=True) row = Workout.objects.get(id=id) if datadf.empty: - return "","No Valid Data Available" + return "", "No Valid Data Available" try: spm = datadf['spm'] - except KeyError: # pragma: no cover + except KeyError: # pragma: no cover datadf['spm'] = 0 try: pace = datadf['pace'] - except KeyError: # pragma: no cover + except KeyError: # pragma: no cover datadf['pace'] = 0 source = ColumnDataSource( datadf - ) + ) - plot = Figure(x_axis_type="datetime",y_axis_type="datetime", + plot = Figure(x_axis_type="datetime", y_axis_type="datetime", plot_width=400, plot_height=400, toolbar_sticky=False, @@ -4364,9 +4246,9 @@ def interactive_chart(id=0,promember=0,intervaldata = {}): # add watermark watermarkurl = "/static/img/logo7.png" watermarksource = ColumnDataSource(dict( - url = [watermarkurl],)) + url=[watermarkurl],)) - watermarkrange = Range1d(start=0,end=1) + watermarkrange = Range1d(start=0, end=1) watermarkalpha = 0.6 watermarkx = 0.99 watermarky = 0.01 @@ -4376,35 +4258,35 @@ def interactive_chart(id=0,promember=0,intervaldata = {}): plot.extra_y_ranges = {"watermark": watermarkrange} plot.extra_x_ranges = {"watermark": watermarkrange} - plot.image_url([watermarkurl],0.01,0.99, - 0.5*watermarkw,0.5*watermarkh, + plot.image_url([watermarkurl], 0.01, 0.99, + 0.5*watermarkw, 0.5*watermarkh, global_alpha=watermarkalpha, w_units='screen', h_units='screen', anchor='top_left', dilate=True, - x_range_name = "watermark", - y_range_name = "watermark", + x_range_name="watermark", + y_range_name="watermark", ) - plot.line('time','pace',source=source,legend_label="Pace",name="pace") + plot.line('time', 'pace', source=source, legend_label="Pace", name="pace") plot.title.text = row.name - plot.title.text_font_size=value("1.0em") + plot.title.text_font_size = value("1.0em") plot.sizing_mode = 'stretch_both' plot.xaxis.axis_label = "Time" plot.yaxis.axis_label = "Pace (/500m)" plot.xaxis[0].formatter = DatetimeTickFormatter( - hours = ["%H"], - minutes = ["%M"], - seconds = ["%S"], - days = ["0"], - months = [""], - years = [""] - ) + hours=["%H"], + minutes=["%M"], + seconds=["%S"], + days=["0"], + months=[""], + years=[""] + ) plot.yaxis[0].formatter = DatetimeTickFormatter( - seconds = ["%S"], - minutes = ["%M"] - ) + seconds=["%S"], + minutes=["%M"] + ) ymax = 90. ymin = 150. @@ -4412,13 +4294,13 @@ def interactive_chart(id=0,promember=0,intervaldata = {}): ymax = 90. ymin = 210. - plot.y_range = Range1d(1.e3*ymin,1.e3*ymax) + plot.y_range = Range1d(1.e3*ymin, 1.e3*ymax) - - plot.extra_y_ranges["spmax"] = Range1d(start=10,end=45) - plot.line('time','spm',source=source,color="red", - y_range_name="spmax", legend_label="Stroke Rate",name="spm") - plot.add_layout(LinearAxis(y_range_name="spmax",axis_label="SPM"),'right') + plot.extra_y_ranges["spmax"] = Range1d(start=10, end=45) + plot.line('time', 'spm', source=source, color="red", + y_range_name="spmax", legend_label="Stroke Rate", name="spm") + plot.add_layout(LinearAxis(y_range_name="spmax", + axis_label="SPM"), 'right') plot.legend.location = "bottom_right" @@ -4428,53 +4310,52 @@ def interactive_chart(id=0,promember=0,intervaldata = {}): intervaldf['itime'] = intervaldf['itime']*1.e3 intervaldf['time'] = intervaldf['itime'].cumsum() intervaldf['time'] = intervaldf['time'].shift(1) - intervaldf.loc[0,'time'] = 0 - intervaldf['time_r'] = intervaldf['time'] +intervaldf['itime'] + intervaldf.loc[0, 'time'] = 0 + intervaldf['time_r'] = intervaldf['time'] + intervaldf['itime'] intervaldf['value'] = 100 mask = intervaldf['itype'] == 3 - intervaldf.loc[mask,'value'] = 0 + intervaldf.loc[mask, 'value'] = 0 intervaldf['bottom'] = 10 intervalsource = ColumnDataSource( intervaldf ) - plot.quad(left='time',top='value',bottom='bottom', - right='time_r',source=intervalsource,color='mediumvioletred', - y_range_name='spmax',fill_alpha=0.2,line_alpha=0.2) - + plot.quad(left='time', top='value', bottom='bottom', + right='time_r', source=intervalsource, color='mediumvioletred', + y_range_name='spmax', fill_alpha=0.2, line_alpha=0.2) hover = plot.select(dict(type=HoverTool)) - hover.tooltips = OrderedDict([ - ('Time','@ftime'), - ('Pace','@fpace'), - ('HR','@hr{int}'), - ('SPM','@spm{1.1}'), - ]) + ('Time', '@ftime'), + ('Pace', '@fpace'), + ('HR', '@hr{int}'), + ('SPM', '@spm{1.1}'), + ]) hover.mode = 'mouse' - hover.names = ["spm","pace"] + hover.names = ["spm", "pace"] script, div = components(plot) - return [script,div] + return [script, div] + def interactive_chart_video(videodata): try: spm = videodata['spm'] - except KeyError: # pragma: no cover - return "","No SPM data" + except KeyError: # pragma: no cover + return "", "No SPM data" time = range(len(spm)) - data = zip(time,spm) + data = zip(time, spm) data2 = "[" - for t,s in data: - data2 += "{x: %s, y: %s}, " % (t,s) + for t, s in data: + data2 += "{x: %s, y: %s}, " % (t, s) data2 = data2[:-2] + "]" @@ -4568,68 +4449,66 @@ def interactive_chart_video(videodata): """ % (data2, markerpoint, markerpoint) - return [script,div] + return [script, div] - -def interactive_multiflex(datadf,xparam,yparam,groupby,extratitle='', +def interactive_multiflex(datadf, xparam, yparam, groupby, extratitle='', ploterrorbars=False, - title=None,binsize=1,colorlegend=[], - spmmin=0,spmmax=0,workmin=0,workmax=0): + title=None, binsize=1, colorlegend=[], + spmmin=0, spmmax=0, workmin=0, workmax=0): - if datadf.empty: # pragma: no cover - return ['','

No non-zero data in selection

'] + if datadf.empty: # pragma: no cover + return ['', '

No non-zero data in selection

'] - if xparam == 'workoutid': # pragma: no cover + if xparam == 'workoutid': # pragma: no cover xparamname = 'Workout' else: xparamname = axlabels[xparam] - if yparam == 'workoutid': # pragma: no cover + if yparam == 'workoutid': # pragma: no cover yparamname = 'Workout' else: yparamname = axlabels[yparam] - if groupby == 'workoutid': # pragma: no cover + if groupby == 'workoutid': # pragma: no cover groupname = 'Workout' - elif groupby == 'date': # pragma: no cover + elif groupby == 'date': # pragma: no cover groupname = 'Date' else: groupname = axlabels[groupby] - - if title==None: + if title == None: title = '{y} vs {x} grouped by {gr}'.format( - x = xparamname, - y = yparamname, - gr = groupname, - ) + x=xparamname, + y=yparamname, + gr=groupname, + ) - if xparam == 'cumdist': # pragma: no cover + if xparam == 'cumdist': # pragma: no cover res = make_cumvalues(datadf[xparam]) datadf[xparam] = res[0] - if xparam=='distance': # pragma: no cover + if xparam == 'distance': # pragma: no cover xaxmax = datadf[xparam].max() xaxmin = datadf[xparam].min() - elif xparam=='time': # pragma: no cover - tseconds = datadf.loc[:,'time'] + elif xparam == 'time': # pragma: no cover + tseconds = datadf.loc[:, 'time'] xaxmax = tseconds.max() xaxmin = 0 - elif xparam == 'workoutid': # pragma: no cover + elif xparam == 'workoutid': # pragma: no cover xaxmax = datadf[xparam].max()-5 xaxmin = datadf[xparam].min()+5 else: xaxmax = yaxmaxima[xparam] xaxmin = yaxminima[xparam] - if yparam=='distance':# pragma: no cover + if yparam == 'distance': # pragma: no cover yaxmax = datadf[yparam].max() yaxmin = datadf[yparam].min() - elif yparam=='time': # pragma: no cover - tseconds = datadf.loc[:,'time'] + elif yparam == 'time': # pragma: no cover + tseconds = datadf.loc[:, 'time'] yaxmax = tseconds.max() yaxmin = 0 - elif yparam == 'workoutid': # pragma: no cover + elif yparam == 'workoutid': # pragma: no cover yaxmax = datadf[yparam].max()-5 yaxmin = datadf[yparam].min()+5 else: @@ -4638,7 +4517,7 @@ def interactive_multiflex(datadf,xparam,yparam,groupby,extratitle='', x_axis_type = 'linear' y_axis_type = 'linear' - if xparam == 'time': # pragma: no cover + if xparam == 'time': # pragma: no cover x_axis_type = 'datetime' if yparam == 'pace': y_axis_type = 'datetime' @@ -4647,43 +4526,40 @@ def interactive_multiflex(datadf,xparam,yparam,groupby,extratitle='', source = ColumnDataSource( datadf, - ) - + ) TOOLS = 'save,pan,box_zoom,wheel_zoom,reset,tap' if groupby != 'date': hover = HoverTool(names=['data'], - tooltips = [ - (groupby,'@groupval{1.1}'), - (xparamname,'@x{1.1}'), - (yparamname,'@y') - ]) - else: # pragma: no cover + tooltips=[ + (groupby, '@groupval{1.1}'), + (xparamname, '@x{1.1}'), + (yparamname, '@y') + ]) + else: # pragma: no cover hover = HoverTool(names=['data'], - tooltips = [ - (groupby,'@groupval'), - (xparamname,'@x{1.1}'), - (yparamname,'@y') - , - ]) + tooltips=[ + (groupby, '@groupval'), + (xparamname, '@x{1.1}'), + (yparamname, '@y'), + ]) hover.mode = 'mouse' - TOOLS = [SaveTool(),PanTool(),BoxZoomTool(),WheelZoomTool(), - ResetTool(),TapTool(),hover] + TOOLS = [SaveTool(), PanTool(), BoxZoomTool(), WheelZoomTool(), + ResetTool(), TapTool(), hover] - - plot = Figure(x_axis_type=x_axis_type,y_axis_type=y_axis_type, + plot = Figure(x_axis_type=x_axis_type, y_axis_type=y_axis_type, tools=TOOLS, toolbar_location="above", - toolbar_sticky=False,plot_width=920) + toolbar_sticky=False, plot_width=920) # add watermark watermarkurl = "/static/img/logo7.png" watermarksource = ColumnDataSource(dict( - url = [watermarkurl],)) + url=[watermarkurl],)) - watermarkrange = Range1d(start=0,end=1) + watermarkrange = Range1d(start=0, end=1) watermarkalpha = 0.6 watermarkx = 0.99 watermarky = 0.01 @@ -4694,35 +4570,33 @@ def interactive_multiflex(datadf,xparam,yparam,groupby,extratitle='', plot.extra_x_ranges = {"watermark": watermarkrange} plot.title.text = title - plot.title.text_font_size=value("1.0em") + plot.title.text_font_size = value("1.0em") plot.sizing_mode = 'stretch_both' - plot.image_url([watermarkurl],watermarkx,watermarky, - watermarkw,watermarkh, + plot.image_url([watermarkurl], watermarkx, watermarky, + watermarkw, watermarkh, global_alpha=watermarkalpha, w_units='screen', h_units='screen', anchor=watermarkanchor, dilate=True, - x_range_name = "watermark", - y_range_name = "watermark", + x_range_name="watermark", + y_range_name="watermark", ) - errorbar(plot,xparam,yparam,source=source, + errorbar(plot, xparam, yparam, source=source, xerr=ploterrorbars, yerr=ploterrorbars, point_kwargs={ - 'line_color':"#969696", - 'size':"groupsize", - 'fill_color':"color", - 'fill_alpha':1.0, + 'line_color': "#969696", + 'size': "groupsize", + 'fill_color': "color", + 'fill_alpha': 1.0, }, - ) - - + ) for nr, gvalue, color in colorlegend: - box = BoxAnnotation(bottom=75+20*nr,left=50,top=95+20*nr, + box = BoxAnnotation(bottom=75+20*nr, left=50, top=95+20*nr, right=70, bottom_units='screen', top_units='screen', @@ -4731,9 +4605,9 @@ def interactive_multiflex(datadf,xparam,yparam,groupby,extratitle='', fill_color=color, fill_alpha=1.0, line_color=color) - legendlabel = Label(x=71,y=78+20*nr,x_units='screen', + legendlabel = Label(x=71, y=78+20*nr, x_units='screen', y_units='screen', - text = "{gvalue:3.0f}".format(gvalue=gvalue), + text="{gvalue:3.0f}".format(gvalue=gvalue), background_fill_alpha=1.0, text_color='black', text_font_size="0.7em") @@ -4741,65 +4615,63 @@ def interactive_multiflex(datadf,xparam,yparam,groupby,extratitle='', plot.add_layout(legendlabel) if colorlegend: - legendlabel = Label(x=322,y=250,x_units='screen', + legendlabel = Label(x=322, y=250, x_units='screen', y_units='screen', - text = 'group legend', + text='group legend', text_color='black', text_font_size="0.7em", angle=90, angle_units='deg') - if xparam == 'workoutid': # pragma: no cover + if xparam == 'workoutid': # pragma: no cover plot.xaxis.axis_label = 'Workout' else: plot.xaxis.axis_label = axlabels[xparam] - if yparam == 'workoutid': # pragma: no cover + if yparam == 'workoutid': # pragma: no cover plot.xaxis.axis_label = 'Workout' else: plot.yaxis.axis_label = axlabels[yparam] - binlabel = Label(x=50,y=50,x_units='screen', + binlabel = Label(x=50, y=50, x_units='screen', y_units='screen', text="Bin size {binsize:3.1f}".format(binsize=binsize), background_fill_alpha=0.7, background_fill_color='white', - text_color='black',text_font_size='10pt', + text_color='black', text_font_size='10pt', ) slidertext = 'SPM: {:.0f}-{:.0f}, WpS: {:.0f}-{:.0f}'.format( - spmmin,spmmax,workmin,workmax - ) - sliderlabel = Label(x=50,y=20,x_units='screen',y_units='screen', + spmmin, spmmax, workmin, workmax + ) + sliderlabel = Label(x=50, y=20, x_units='screen', y_units='screen', text=slidertext, background_fill_alpha=0.7, background_fill_color='white', - text_color='black',text_font_size='10pt', + text_color='black', text_font_size='10pt', ) - plot.add_layout(binlabel) plot.add_layout(sliderlabel) - yrange1 = Range1d(start=yaxmin,end=yaxmax) + yrange1 = Range1d(start=yaxmin, end=yaxmax) plot.y_range = yrange1 - xrange1 = Range1d(start=xaxmin,end=xaxmax) + xrange1 = Range1d(start=xaxmin, end=xaxmax) plot.x_range = xrange1 if yparam == 'pace': plot.yaxis[0].formatter = DatetimeTickFormatter( - seconds = ["%S"], - minutes = ["%M"] - ) + seconds=["%S"], + minutes=["%M"] + ) + + script, div = components(plot) + + return [script, div] - script,div = components(plot) - - - return [script,div] - -def interactive_cum_flex_chart2(theworkouts,promember=0, +def interactive_cum_flex_chart2(theworkouts, promember=0, xparam='spm', yparam1='power', yparam2='spm', @@ -4807,13 +4679,13 @@ def interactive_cum_flex_chart2(theworkouts,promember=0, # datadf = dataprep.smalldataprep(theworkouts,xparam,yparam1,yparam2) ids = [int(w.id) for w in theworkouts] - columns = [xparam,yparam1,yparam2,'spm','driveenergy','distance'] - datadf = dataprep.getsmallrowdata_db(columns,ids=ids,doclean=True, + columns = [xparam, yparam1, yparam2, 'spm', 'driveenergy', 'distance'] + datadf = dataprep.getsmallrowdata_db(columns, ids=ids, doclean=True, workstrokesonly=workstrokesonly) try: tests = datadf[yparam2] - except KeyError: # pragma: no cover + except KeyError: # pragma: no cover yparam2 = 'None' try: @@ -4821,64 +4693,60 @@ def interactive_cum_flex_chart2(theworkouts,promember=0, except KeyError: yparam1 = 'None' - datadf.dropna(axis=1,how='all',inplace=True) - datadf.dropna(axis=0,how='any',inplace=True) + datadf.dropna(axis=1, how='all', inplace=True) + datadf.dropna(axis=0, how='any', inplace=True) # test if we have drive energy nowork = 1 - try: # pragma: no cover + try: # pragma: no cover test = datadf['driveenergy'].mean() nowork = 0 - except KeyError: # pragma: no cover + except KeyError: # pragma: no cover datadf['driveenergy'] = 500. # test if we have power nopower = 1 - try: # pragma: no cover + try: # pragma: no cover test = datadf['power'].mean() nopower = 0 - except KeyError: # pragma: no cover + except KeyError: # pragma: no cover datadf['power'] = 50. - yparamname1 = axlabels[yparam1] if yparam2 != 'None': yparamname2 = axlabels[yparam2] - # check if dataframe not empty - if datadf.empty: # pragma: no cover - return ['','

No non-zero data in selection

','',''] - + if datadf.empty: # pragma: no cover + return ['', '

No non-zero data in selection

', '', ''] try: - datadf['x1'] = datadf.loc[:,xparam] - except KeyError: # pragma: no cover + datadf['x1'] = datadf.loc[:, xparam] + except KeyError: # pragma: no cover try: datadf['x1'] = datadf['distance'] except KeyError: try: datadf['x1'] = datadf['time'] - except KeyError: # pragma: no cover - return ['','

No non-zero data in selection

','',''] + except KeyError: # pragma: no cover + return ['', '

No non-zero data in selection

', '', ''] try: - datadf['y1'] = datadf.loc[:,yparam1] + datadf['y1'] = datadf.loc[:, yparam1] except KeyError: try: datadf['y1'] = datadf['pace'] - except KeyError: # pragma: no cover - return ['','

No non-zero data in selection

','',''] + except KeyError: # pragma: no cover + return ['', '

No non-zero data in selection

', '', ''] if yparam2 != 'None': try: - datadf['y2'] = datadf.loc[:,yparam2] - except KeyError: # pragma: no cover + datadf['y2'] = datadf.loc[:, yparam2] + except KeyError: # pragma: no cover datadf['y2'] = datadf['y1'] - else: # pragma: no cover + else: # pragma: no cover datadf['y2'] = datadf['y1'] - - if xparam=='distance': # pragma: no cover + if xparam == 'distance': # pragma: no cover xaxmax = datadf['x1'].max() xaxmin = datadf['x1'].min() else: @@ -4891,7 +4759,6 @@ def interactive_cum_flex_chart2(theworkouts,promember=0, y1mean = datadf['y1'].mean() y2mean = datadf['y2'].mean() - xvals = pd.Series(xaxmin+np.arange(100)*(xaxmax-xaxmin)/100.) x_axis_type = 'linear' @@ -4899,33 +4766,32 @@ def interactive_cum_flex_chart2(theworkouts,promember=0, if xparam == 'time': x_axis_type = 'datetime' - if yparam1 == 'pace': # pragma: no cover + if yparam1 == 'pace': # pragma: no cover y_axis_type = 'datetime' - y1mean = datadf.loc[:,'pace'].mean() + y1mean = datadf.loc[:, 'pace'].mean() datadf['xname'] = axlabels[xparam] datadf['yname1'] = axlabels[yparam1] if yparam2 != 'None': datadf['yname2'] = axlabels[yparam2] - else: # pragma: no cover + else: # pragma: no cover datadf['yname2'] = axlabels[yparam1] - source = ColumnDataSource( datadf - ) + ) source2 = ColumnDataSource( datadf.copy() - ) + ) # Add hover to this comma-separated string and see what changes - if (promember==1): + if (promember == 1): TOOLS = 'save,pan,box_zoom,wheel_zoom,reset,tap,crosshair' - else: # pragma: no cover + else: # pragma: no cover TOOLS = 'pan,box_zoom,wheel_zoom,reset,tap,crosshair' - plot = Figure(x_axis_type=x_axis_type,y_axis_type=y_axis_type, + plot = Figure(x_axis_type=x_axis_type, y_axis_type=y_axis_type, tools=TOOLS, toolbar_location="above", toolbar_sticky=False) @@ -4933,9 +4799,9 @@ def interactive_cum_flex_chart2(theworkouts,promember=0, # add watermark watermarkurl = "/static/img/logo7.png" watermarksource = ColumnDataSource(dict( - url = [watermarkurl],)) + url=[watermarkurl],)) - watermarkrange = Range1d(start=0,end=1) + watermarkrange = Range1d(start=0, end=1) watermarkalpha = 0.6 watermarkx = 0.99 watermarky = 0.01 @@ -4946,108 +4812,108 @@ def interactive_cum_flex_chart2(theworkouts,promember=0, plot.extra_x_ranges = {"watermark": watermarkrange} plot.sizing_mode = 'stretch_both' - plot.image_url([watermarkurl],watermarkx,watermarky, - watermarkw,watermarkh, + plot.image_url([watermarkurl], watermarkx, watermarky, + watermarkw, watermarkh, global_alpha=watermarkalpha, w_units='screen', h_units='screen', anchor=watermarkanchor, dilate=True, - x_range_name = "watermark", - y_range_name = "watermark", + x_range_name="watermark", + y_range_name="watermark", ) - x1means = Span(location=x1mean,dimension='height',line_color='green', - line_dash=[6,6], line_width=2) + x1means = Span(location=x1mean, dimension='height', line_color='green', + line_dash=[6, 6], line_width=2) - y1means = Span(location=y1mean,dimension='width',line_color='blue', - line_dash=[6,6],line_width=2) + y1means = Span(location=y1mean, dimension='width', line_color='blue', + line_dash=[6, 6], line_width=2) y2means = y1means - xlabel = Label(x=50,y=80,x_units='screen',y_units='screen', - text=axlabels[xparam]+": {x1mean:6.2f}".format(x1mean=x1mean), + xlabel = Label(x=50, y=80, x_units='screen', y_units='screen', + text=axlabels[xparam] + + ": {x1mean:6.2f}".format(x1mean=x1mean), background_fill_alpha=.7, background_fill_color='white', text_color='green', - ) + ) - sliderlabel = Label(x=10,y=470,x_units='screen',y_units='screen', + sliderlabel = Label(x=10, y=470, x_units='screen', y_units='screen', text='', background_fill_alpha=0.7, background_fill_color='white', - text_color='black',text_font_size='10pt', + text_color='black', text_font_size='10pt', ) - plot.add_layout(x1means) plot.add_layout(xlabel) plot.add_layout(y1means) plot.add_layout(sliderlabel) - y1label = Label(x=50,y=50,x_units='screen',y_units='screen', - text=axlabels[yparam1]+": {y1mean:6.2f}".format(y1mean=y1mean), + y1label = Label(x=50, y=50, x_units='screen', y_units='screen', + text=axlabels[yparam1] + + ": {y1mean:6.2f}".format(y1mean=y1mean), background_fill_alpha=.7, background_fill_color='white', text_color='blue', - ) + ) if yparam1 != 'time' and yparam1 != 'pace': plot.add_layout(y1label) y2label = y1label - plot.circle('x1','y1',source=source2,fill_alpha=0.3,line_color=None, - legend_label=yparamname1, - ) + plot.circle('x1', 'y1', source=source2, fill_alpha=0.3, line_color=None, + legend_label=yparamname1, + ) plot.xaxis.axis_label = axlabels[xparam] plot.yaxis.axis_label = axlabels[yparam1] - - yrange1 = Range1d(start=yaxminima[yparam1],end=yaxmaxima[yparam1]) + yrange1 = Range1d(start=yaxminima[yparam1], end=yaxmaxima[yparam1]) plot.y_range = yrange1 - xrange1 = Range1d(start=yaxminima[xparam],end=yaxmaxima[xparam]) + xrange1 = Range1d(start=yaxminima[xparam], end=yaxmaxima[xparam]) plot.x_range = xrange1 - if yparam1 == 'pace': # pragma: no cover + if yparam1 == 'pace': # pragma: no cover plot.yaxis[0].formatter = DatetimeTickFormatter( - seconds = ["%S"], - minutes = ["%M"] - ) + seconds=["%S"], + minutes=["%M"] + ) if yparam2 != 'None': - yrange2 = Range1d(start=yaxminima[yparam2],end=yaxmaxima[yparam2]) + yrange2 = Range1d(start=yaxminima[yparam2], end=yaxmaxima[yparam2]) plot.extra_y_ranges["yax2"] = yrange2 - plot.circle('x1','y2',color="red",y_range_name="yax2", + plot.circle('x1', 'y2', color="red", y_range_name="yax2", legend_label=yparamname2, - source=source2,fill_alpha=0.3,line_color=None) + source=source2, fill_alpha=0.3, line_color=None) plot.add_layout(LinearAxis(y_range_name="yax2", - axis_label=axlabels[yparam2]),'right') - - y2means = Span(location=y2mean,dimension='width',line_color='red', - line_dash=[6,6],line_width=2,y_range_name="yax2") + axis_label=axlabels[yparam2]), 'right') + y2means = Span(location=y2mean, dimension='width', line_color='red', + line_dash=[6, 6], line_width=2, y_range_name="yax2") plot.add_layout(y2means) - y2label = Label(x=50,y=20,x_units='screen',y_units='screen', - text=axlabels[yparam2]+": {y2mean:6.2f}".format(y2mean=y2mean), + y2label = Label(x=50, y=20, x_units='screen', y_units='screen', + text=axlabels[yparam2] + + ": {y2mean:6.2f}".format(y2mean=y2mean), background_fill_alpha=.7, background_fill_color='white', text_color='red', - ) + ) if yparam2 != 'pace' and yparam2 != 'time': plot.add_layout(y2label) - callback = CustomJS(args = dict(source=source,source2=source2, - x1means=x1means, - y1means=y1means, - y1label=y1label, - y2label=y2label, - xlabel=xlabel, - sliderlabel=sliderlabel, - y2means=y2means), code=""" + callback = CustomJS(args=dict(source=source, source2=source2, + x1means=x1means, + y1means=y1means, + y1label=y1label, + y2label=y2label, + xlabel=xlabel, + sliderlabel=sliderlabel, + y2means=y2means), code=""" var data = source.data var data2 = source2.data var x1 = data['x1'] @@ -5140,42 +5006,40 @@ def interactive_cum_flex_chart2(theworkouts,promember=0, source2.change.emit(); """) - slider_spm_min = Slider(width=140, start=15.0, end=55,value=15.0, step=.1, - title="Min SPM") - slider_spm_min.js_on_change('value',callback) + slider_spm_min = Slider(width=140, start=15.0, end=55, value=15.0, step=.1, + title="Min SPM") + slider_spm_min.js_on_change('value', callback) callback.args["minspm"] = slider_spm_min - - slider_spm_max = Slider(width=140, start=15.0, end=55,value=55.0, step=.1, - title="Max SPM") - slider_spm_max.js_on_change('value',callback) + slider_spm_max = Slider(width=140, start=15.0, end=55, value=55.0, step=.1, + title="Max SPM") + slider_spm_max.js_on_change('value', callback) callback.args["maxspm"] = slider_spm_max - slider_work_min = Slider(width=140, start=0.0, end=1500,value=0.0, step=10, - title="Min Work per Stroke") - slider_work_min.js_on_change('value',callback) + slider_work_min = Slider(width=140, start=0.0, end=1500, value=0.0, step=10, + title="Min Work per Stroke") + slider_work_min.js_on_change('value', callback) callback.args["minwork"] = slider_work_min - - slider_work_max = Slider(width=140, start=0.0, end=1500,value=1500.0, step=10, - title="Max Work per Stroke") - slider_work_max.js_on_change('value',callback) + slider_work_max = Slider(width=140, start=0.0, end=1500, value=1500.0, step=10, + title="Max Work per Stroke") + slider_work_max.js_on_change('value', callback) callback.args["maxwork"] = slider_work_max try: distmax = 100+100*int(datadf['distance'].max()/100.) - except KeyError: # pragma: no cover + except KeyError: # pragma: no cover distmax = 1000. - slider_dist_min = Slider(width=140, start=0,end=distmax,value=0,step=50, + slider_dist_min = Slider(width=140, start=0, end=distmax, value=0, step=50, title="Min Distance") - slider_dist_min.js_on_change('value',callback) + slider_dist_min.js_on_change('value', callback) callback.args["mindist"] = slider_dist_min - slider_dist_max = Slider(width=140, start=0,end=distmax,value=distmax, + slider_dist_max = Slider(width=140, start=0, end=distmax, value=distmax, step=50, title="Max Distance") - slider_dist_max.js_on_change('value',callback) + slider_dist_max.js_on_change('value', callback) callback.args["maxdist"] = slider_dist_max #slider_spm_min.sizing_mode = 'fixed' @@ -5186,13 +5050,13 @@ def interactive_cum_flex_chart2(theworkouts,promember=0, #slider_dist_max.sizing_mode = 'fixed' thesliders = layoutcolumn([slider_spm_min, - slider_spm_max, - slider_dist_min, - slider_dist_max, - slider_work_min, - slider_work_max, + slider_spm_max, + slider_dist_min, + slider_dist_max, + slider_work_min, + slider_work_max, ], - ) + ) #thesliders.sizing_mode = 'fixed' @@ -5205,102 +5069,99 @@ def interactive_cum_flex_chart2(theworkouts,promember=0, js_resources = INLINE.render_js() css_resources = INLINE.render_css() + return [script, div, js_resources, css_resources] - return [script,div,js_resources,css_resources] - - -def interactive_flexchart_stacked(id,r,xparam='time', +def interactive_flexchart_stacked(id, r, xparam='time', yparam1='pace', yparam2='power', yparam3='hr', yparam4='spm', mode='erg'): - columns = [xparam,yparam1,yparam2,yparam3,yparam4, - 'ftime','distance','fpace', - 'power','hr','spm','driveenergy', - 'time','pace','workoutstate'] + columns = [xparam, yparam1, yparam2, yparam3, yparam4, + 'ftime', 'distance', 'fpace', + 'power', 'hr', 'spm', 'driveenergy', + 'time', 'pace', 'workoutstate'] comment = None - rowdata = dataprep.getsmallrowdata_db(columns,ids=[id],doclean=True, + rowdata = dataprep.getsmallrowdata_db(columns, ids=[id], doclean=True, workstrokesonly=False) - - if r.usersmooth > 1: # pragma: no cover + if r.usersmooth > 1: # pragma: no cover for column in columns: try: if metricsdicts[column]['maysmooth']: nrsteps = int(log2(r.usersmooth)) for i in range(nrsteps): - rowdata[column] = stravastuff.ewmovingaverage(rowdata[column],5) + rowdata[column] = stravastuff.ewmovingaverage( + rowdata[column], 5) except KeyError: pass - if len(rowdata)<2: - rowdata = dataprep.getsmallrowdata_db(columns,ids=[id], + if len(rowdata) < 2: + rowdata = dataprep.getsmallrowdata_db(columns, ids=[id], doclean=False, workstrokesonly=False) row = Workout.objects.get(id=id) if rowdata.empty: - return "","No valid data",'','',comment + return "", "No valid data", '', '', comment try: - tseconds = rowdata.loc[:,'time'] - except KeyError: # pragma: no cover - return '','No time data - cannot make flex plot','','',comment + tseconds = rowdata.loc[:, 'time'] + except KeyError: # pragma: no cover + return '', 'No time data - cannot make flex plot', '', '', comment try: - rowdata['x1'] = rowdata.loc[:,xparam] + rowdata['x1'] = rowdata.loc[:, xparam] rowmin = rowdata[xparam].min() - except KeyError: # pragma: no cover - rowdata['x1'] = 0*rowdata.loc[:,'time'] + except KeyError: # pragma: no cover + rowdata['x1'] = 0*rowdata.loc[:, 'time'] try: - rowdata['y1'] = rowdata.loc[:,yparam1] + rowdata['y1'] = rowdata.loc[:, yparam1] rowmin = rowdata[yparam1].min() - except KeyError: # pragma: no cover - rowdata['y1'] = 0*rowdata.loc[:,'time'] + except KeyError: # pragma: no cover + rowdata['y1'] = 0*rowdata.loc[:, 'time'] rowdata[yparam1] = rowdata['y1'] - try: # pragma: no cover - rowdata['y2'] = rowdata.loc[:,yparam2] + try: # pragma: no cover + rowdata['y2'] = rowdata.loc[:, yparam2] rowmin = rowdata[yparam2].min() except KeyError: - rowdata['y2'] = 0*rowdata.loc[:,'time'] + rowdata['y2'] = 0*rowdata.loc[:, 'time'] rowdata[yparam2] = rowdata['y2'] try: - rowdata['y3'] = rowdata.loc[:,yparam3] + rowdata['y3'] = rowdata.loc[:, yparam3] rowmin = rowdata[yparam3].min() - except KeyError: # pragma: no cover - rowdata['y3'] = 0*rowdata.loc[:,'time'] + except KeyError: # pragma: no cover + rowdata['y3'] = 0*rowdata.loc[:, 'time'] rowdata[yparam3] = rowdata['y3'] try: - rowdata['y4'] = rowdata.loc[:,yparam4] + rowdata['y4'] = rowdata.loc[:, yparam4] rowmin = rowdata[yparam4].min() - except KeyError: # pragma: no cover - rowdata['y4'] = 0*rowdata.loc[:,'time'] + except KeyError: # pragma: no cover + rowdata['y4'] = 0*rowdata.loc[:, 'time'] rowdata[yparam4] = rowdata['y4'] - if xparam=='time': + if xparam == 'time': xaxmax = tseconds.max() xaxmin = tseconds.min() - elif xparam=='distance' or xparam=='cumdist': # pragma: no cover + elif xparam == 'distance' or xparam == 'cumdist': # pragma: no cover xaxmax = rowdata['x1'].max() xaxmin = rowdata['x1'].min() - else: # pragma: no cover + else: # pragma: no cover try: - xaxmax = get_yaxmaxima(r,xparam,mode) - xaxmin = get_yaxminima(r,xparam,mode) + xaxmax = get_yaxmaxima(r, xparam, mode) + xaxmin = get_yaxminima(r, xparam, mode) except KeyError: xaxmax = rowdata['x1'].max() xaxmin = rowdata['x1'].min() - x_axis_type = 'linear' y1_axis_type = 'linear' y2_axis_type = 'linear' @@ -5309,63 +5170,63 @@ def interactive_flexchart_stacked(id,r,xparam='time', if xparam == 'time': x_axis_type = 'datetime' - if yparam1 == 'pace': # pragma: no cover + if yparam1 == 'pace': # pragma: no cover y1_axis_type = 'datetime' - if yparam2 == 'pace': # pragma: no cover + if yparam2 == 'pace': # pragma: no cover y2_axis_type = 'datetime' - if yparam3 == 'pace': # pragma: no cover + if yparam3 == 'pace': # pragma: no cover y3_axis_type = 'datetime' - if yparam4 == 'pace': # pragma: no cover + if yparam4 == 'pace': # pragma: no cover y4_axis_type = 'datetime' try: rowdata['xname'] = axlabels[xparam] - except KeyError: # pragma: no cover + except KeyError: # pragma: no cover rowdata['xname'] = xparam try: rowdata['yname1'] = axlabels[yparam1] - except KeyError: # pragma: no cover + except KeyError: # pragma: no cover rowdata['yname1'] = yparam1 try: rowdata['yname2'] = axlabels[yparam2] - except KeyError: # pragma: no cover + except KeyError: # pragma: no cover rowdata['yname2'] = yparam2 try: rowdata['yname3'] = axlabels[yparam3] - except KeyError: # pragma: no cover + except KeyError: # pragma: no cover rowdata['yname3'] = yparam3 try: rowdata['yname4'] = axlabels[yparam4] - except KeyError: # pragma: no cover + except KeyError: # pragma: no cover rowdata['yname4'] = yparam4 # prepare data source = ColumnDataSource( rowdata - ) + ) TOOLS = 'box_zoom,wheel_zoom,reset,tap,hover' TOOLS2 = 'box_zoom,hover' - plot1 = Figure(x_axis_type=x_axis_type,y_axis_type=y1_axis_type,plot_width=920,plot_height=150, - tools=TOOLS,toolbar_location='above') - plot2 = Figure(x_axis_type=x_axis_type,y_axis_type=y2_axis_type,plot_width=920,plot_height=150, - tools=TOOLS2,toolbar_location=None) - plot3 = Figure(x_axis_type=x_axis_type,y_axis_type=y3_axis_type,plot_width=920,plot_height=150, - tools=TOOLS2,toolbar_location=None) - plot4 = Figure(x_axis_type=x_axis_type,y_axis_type=y4_axis_type,plot_width=920,plot_height=150, - tools=TOOLS2,toolbar_location=None) + plot1 = Figure(x_axis_type=x_axis_type, y_axis_type=y1_axis_type, plot_width=920, plot_height=150, + tools=TOOLS, toolbar_location='above') + plot2 = Figure(x_axis_type=x_axis_type, y_axis_type=y2_axis_type, plot_width=920, plot_height=150, + tools=TOOLS2, toolbar_location=None) + plot3 = Figure(x_axis_type=x_axis_type, y_axis_type=y3_axis_type, plot_width=920, plot_height=150, + tools=TOOLS2, toolbar_location=None) + plot4 = Figure(x_axis_type=x_axis_type, y_axis_type=y4_axis_type, plot_width=920, plot_height=150, + tools=TOOLS2, toolbar_location=None) - plot1.xaxis.visible=False - plot2.xaxis.visible=False - plot3.xaxis.visible=False + plot1.xaxis.visible = False + plot2.xaxis.visible = False + plot3.xaxis.visible = False plot1.sizing_mode = 'stretch_both' plot2.sizing_mode = 'stretch_both' @@ -5378,28 +5239,26 @@ def interactive_flexchart_stacked(id,r,xparam='time', plot3.add_tools(linked_crosshair) plot4.add_tools(linked_crosshair) - xaxlabel = axlabels.get(xparam,xparam) - yax1label = axlabels.get(yparam1,yparam1) + xaxlabel = axlabels.get(xparam, xparam) + yax1label = axlabels.get(yparam1, yparam1) plot1.yaxis.axis_label = yax1label - yax2label = axlabels.get(yparam2,yparam2) + yax2label = axlabels.get(yparam2, yparam2) plot2.yaxis.axis_label = yax2label - yax3label = axlabels.get(yparam3,yparam3) - + yax3label = axlabels.get(yparam3, yparam3) plot3.yaxis.axis_label = yax3label - yax4label = axlabels.get(yparam4,yparam4) + yax4label = axlabels.get(yparam4, yparam4) plot4.yaxis.axis_label = yax4label plot4.xaxis.axis_label = xaxlabel - - xrange1 = Range1d(start=xaxmin,end=xaxmax) + xrange1 = Range1d(start=xaxmin, end=xaxmax) plot1.x_range = xrange1 plot2.x_range = xrange1 plot3.x_range = xrange1 @@ -5407,13 +5266,13 @@ def interactive_flexchart_stacked(id,r,xparam='time', if xparam == 'time': plot4.xaxis[0].formatter = DatetimeTickFormatter( - hours = ["%H"], - minutes = ["%M"], - seconds = ["%S"], - days = ["0"], - months = [""], - years = [""] - ) + hours=["%H"], + minutes=["%M"], + seconds=["%S"], + days=["0"], + months=[""], + years=[""] + ) hover1 = plot1.select(dict(type=HoverTool)) hover2 = plot2.select(dict(type=HoverTool)) @@ -5422,144 +5281,143 @@ def interactive_flexchart_stacked(id,r,xparam='time', if yparam1 == 'pace': y1tooltip = '@fpace' - elif yparam1 != 'None': # pragma: no cover + elif yparam1 != 'None': # pragma: no cover y1tooltip = '@{yparam1}'.format(yparam1=yparam1) if metricsdicts[yparam1]['numtype'] == 'integer' or yparam1 == 'power': - y1tooltip+='{int}' - else: # pragma: no cover - y1tooltip+='{0.00}' - else: # pragma: no cover + y1tooltip += '{int}' + else: # pragma: no cover + y1tooltip += '{0.00}' + else: # pragma: no cover y1tooltip = '' comment = 'The metric in the first chart is only accessible with a Pro plan or higher' - if yparam2 == 'pace': # pragma: no cover + if yparam2 == 'pace': # pragma: no cover y2tooltip = '@fpace' elif yparam2 != 'None': y2tooltip = '@{yparam2}'.format(yparam2=yparam2) if metricsdicts[yparam2]['numtype'] == 'integer' or yparam2 == 'power': - y2tooltip+='{int}' - else: # pragma: no cover - y2tooltip+='{0.00}' - else: # pragma: no cover + y2tooltip += '{int}' + else: # pragma: no cover + y2tooltip += '{0.00}' + else: # pragma: no cover y2tooltip = '' comment = 'The metric in the second chart is only accessible with a Pro plan or higher' - if yparam3 == 'pace': # pragma: no cover + if yparam3 == 'pace': # pragma: no cover y3tooltip = '@fpace' elif yparam3 != 'None': y3tooltip = '@{yparam3}'.format(yparam3=yparam3) if metricsdicts[yparam3]['numtype'] == 'integer' or yparam3 == 'power': - y3tooltip+='{int}' - else: # pragma: no cover - y3tooltip+='{0.00}' - else: # pragma: no cover + y3tooltip += '{int}' + else: # pragma: no cover + y3tooltip += '{0.00}' + else: # pragma: no cover y3tooltip = '' comment = 'The metric in the third chart is only accessible with a Pro plan or higher' - if yparam4 == 'pace': # pragma: no cover + if yparam4 == 'pace': # pragma: no cover y4tooltip = '@fpace' elif yparam4 != 'None': y4tooltip = '@{yparam4}'.format(yparam4=yparam4) - if metricsdicts[yparam4]['numtype'] == 'integer' or yparam4 == 'power': # pragma: no cover - y4tooltip+='{int}' - else: # pragma: no cover - y4tooltip+='{0.00}' - else: # pragma: no cover + if metricsdicts[yparam4]['numtype'] == 'integer' or yparam4 == 'power': # pragma: no cover + y4tooltip += '{int}' + else: # pragma: no cover + y4tooltip += '{0.00}' + else: # pragma: no cover y4tooltip = '' comment = 'The metric in the fourth chart is only accessible with a Pro plan or higher' if yparam1 != 'None': hover1.tooltips = OrderedDict([ - ('Time','@ftime'), - ('Distance','@distance{int}'), - (axlabels[yparam1],y1tooltip), - (axlabels[yparam2],y2tooltip), - (axlabels[yparam3],y3tooltip), - (axlabels[yparam4],y4tooltip), - ]) + ('Time', '@ftime'), + ('Distance', '@distance{int}'), + (axlabels[yparam1], y1tooltip), + (axlabels[yparam2], y2tooltip), + (axlabels[yparam3], y3tooltip), + (axlabels[yparam4], y4tooltip), + ]) if yparam2 != 'None': hover2.tooltips = OrderedDict([ - ('Time','@ftime'), - ('Distance','@distance{int}'), - (axlabels[yparam1],y1tooltip), - (axlabels[yparam2],y2tooltip), - (axlabels[yparam3],y3tooltip), - (axlabels[yparam4],y4tooltip), - ]) + ('Time', '@ftime'), + ('Distance', '@distance{int}'), + (axlabels[yparam1], y1tooltip), + (axlabels[yparam2], y2tooltip), + (axlabels[yparam3], y3tooltip), + (axlabels[yparam4], y4tooltip), + ]) if yparam3 != 'None': hover3.tooltips = OrderedDict([ - ('Time','@ftime'), - ('Distance','@distance{int}'), - (axlabels[yparam1],y1tooltip), - (axlabels[yparam2],y2tooltip), - (axlabels[yparam3],y3tooltip), - (axlabels[yparam4],y4tooltip), - ]) + ('Time', '@ftime'), + ('Distance', '@distance{int}'), + (axlabels[yparam1], y1tooltip), + (axlabels[yparam2], y2tooltip), + (axlabels[yparam3], y3tooltip), + (axlabels[yparam4], y4tooltip), + ]) if yparam4 != 'None': hover4.tooltips = OrderedDict([ - ('Time','@ftime'), - ('Distance','@distance{int}'), - (axlabels[yparam1],y1tooltip), - (axlabels[yparam2],y2tooltip), - (axlabels[yparam3],y3tooltip), - (axlabels[yparam4],y4tooltip), - ]) + ('Time', '@ftime'), + ('Distance', '@distance{int}'), + (axlabels[yparam1], y1tooltip), + (axlabels[yparam2], y2tooltip), + (axlabels[yparam3], y3tooltip), + (axlabels[yparam4], y4tooltip), + ]) hover1.mode = 'vline' hover2.mode = 'vline' hover3.mode = 'vline' hover4.mode = 'vline' - y1min = get_yaxminima(r,yparam1,mode) - y2min = get_yaxminima(r,yparam2,mode) - y3min = get_yaxminima(r,yparam3,mode) - y4min = get_yaxminima(r,yparam4,mode) + y1min = get_yaxminima(r, yparam1, mode) + y2min = get_yaxminima(r, yparam2, mode) + y3min = get_yaxminima(r, yparam3, mode) + y4min = get_yaxminima(r, yparam4, mode) - y1max = get_yaxmaxima(r,yparam1,mode) - y2max = get_yaxmaxima(r,yparam2,mode) - y3max = get_yaxmaxima(r,yparam3,mode) - y4max = get_yaxmaxima(r,yparam4,mode) + y1max = get_yaxmaxima(r, yparam1, mode) + y2max = get_yaxmaxima(r, yparam2, mode) + y3max = get_yaxmaxima(r, yparam3, mode) + y4max = get_yaxmaxima(r, yparam4, mode) - plot1.y_range = Range1d(start=y1min,end=y1max) - plot2.y_range = Range1d(start=y2min,end=y2max) - plot3.y_range = Range1d(start=y3min,end=y3max) - plot4.y_range = Range1d(start=y4min,end=y4max) + plot1.y_range = Range1d(start=y1min, end=y1max) + plot2.y_range = Range1d(start=y2min, end=y2max) + plot3.y_range = Range1d(start=y3min, end=y3max) + plot4.y_range = Range1d(start=y4min, end=y4max) if yparam1 == 'pace': plot1.yaxis[0].formatter = DatetimeTickFormatter( - seconds = ["%S"], - minutes = ["%M"] - ) - plot1.y_range = Range1d(y1min,y1max) + seconds=["%S"], + minutes=["%M"] + ) + plot1.y_range = Range1d(y1min, y1max) - if yparam2 == 'pace': # pragma: no cover + if yparam2 == 'pace': # pragma: no cover plot2.yaxis[0].formatter = DatetimeTickFormatter( - seconds = ["%S"], - minutes = ["%M"] - ) - plot2.y_range = Range1d(y2min,y2max) + seconds=["%S"], + minutes=["%M"] + ) + plot2.y_range = Range1d(y2min, y2max) - - if yparam3 == 'pace': # pragma: no cover + if yparam3 == 'pace': # pragma: no cover plot3.yaxis[0].formatter = DatetimeTickFormatter( - seconds = ["%S"], - minutes = ["%M"] - ) - plot3.y_range = Range1d(y3min,y3max) + seconds=["%S"], + minutes=["%M"] + ) + plot3.y_range = Range1d(y3min, y3max) - if yparam4 == 'pace': # pragma: no cover + if yparam4 == 'pace': # pragma: no cover plot4.yaxis[0].formatter = DatetimeTickFormatter( - seconds = ["%S"], - minutes = ["%M"] - ) - plot4.y_range = Range1d(y4min,y4max) + seconds=["%S"], + minutes=["%M"] + ) + plot4.y_range = Range1d(y4min, y4max) - plot1.line('x1','y1',source=source,color=palette2[1]) - plot2.line('x1','y2',source=source,color=palette2[3]) - plot3.line('x1','y3',source=source,color=palette2[0]) - plot4.line('x1','y4',source=source,color=palette2[2]) + plot1.line('x1', 'y1', source=source, color=palette2[1]) + plot2.line('x1', 'y2', source=source, color=palette2[3]) + plot3.line('x1', 'y3', source=source, color=palette2[0]) + plot4.line('x1', 'y4', source=source, color=palette2[2]) layout = layoutcolumn([ plot1, @@ -5570,28 +5428,26 @@ def interactive_flexchart_stacked(id,r,xparam='time', layout.sizing_mode = 'stretch_both' - script, div = components(layout) js_resources = INLINE.render_js() css_resources = INLINE.render_css() - return script,div,js_resources,css_resources,comment + return script, div, js_resources, css_resources, comment - -def interactive_flex_chart2(id,r,promember=0, - xparam='time', - yparam1='pace', - yparam2='hr', - plottype='line', - workstrokesonly=False, - mode='rower'): +def interactive_flex_chart2(id, r, promember=0, + xparam='time', + yparam1='pace', + yparam2='hr', + plottype='line', + workstrokesonly=False, + mode='rower'): watermarkurl = "/static/img/logo7.png" watermarksource = ColumnDataSource(dict( - url = [watermarkurl],)) + url=[watermarkurl],)) - watermarkrange = Range1d(start=0,end=1) + watermarkrange = Range1d(start=0, end=1) watermarkalpha = 0.6 watermarkx = 0.99 watermarky = 0.01 @@ -5600,49 +5456,44 @@ def interactive_flex_chart2(id,r,promember=0, watermarkanchor = 'bottom_right' #rowdata,row = dataprep.getrowdata_db(id=id) - columns = [xparam,yparam1,yparam2, - 'ftime','distance','fpace', - 'power','hr','spm','driveenergy', - 'time','pace','workoutstate'] + columns = [xparam, yparam1, yparam2, + 'ftime', 'distance', 'fpace', + 'power', 'hr', 'spm', 'driveenergy', + 'time', 'pace', 'workoutstate'] - rowdata = dataprep.getsmallrowdata_db(columns,ids=[id],doclean=True, + rowdata = dataprep.getsmallrowdata_db(columns, ids=[id], doclean=True, workstrokesonly=workstrokesonly) - - if r.usersmooth > 1: # pragma: no cover + if r.usersmooth > 1: # pragma: no cover for column in columns: try: if metricsdicts[column]['maysmooth']: nrsteps = int(log2(r.usersmooth)) for i in range(nrsteps): - rowdata[column] = stravastuff.ewmovingaverage(rowdata[column],5) + rowdata[column] = stravastuff.ewmovingaverage( + rowdata[column], 5) except KeyError: pass - - - if len(rowdata)<2: - rowdata = dataprep.getsmallrowdata_db(columns,ids=[id],doclean=True, + if len(rowdata) < 2: + rowdata = dataprep.getsmallrowdata_db(columns, ids=[id], doclean=True, workstrokesonly=False) - workstrokesonly=False - if len(rowdata)<2: - rowdata = dataprep.getsmallrowdata_db(columns,ids=[id], + workstrokesonly = False + if len(rowdata) < 2: + rowdata = dataprep.getsmallrowdata_db(columns, ids=[id], doclean=False, workstrokesonly=False) - workstrokesonly=False + workstrokesonly = False try: tests = rowdata[yparam2] - except KeyError: # pragma: no cover + except KeyError: # pragma: no cover yparam2 = 'None' - - try: tests = rowdata[yparam1] - except KeyError: # pragma: no cover + except KeyError: # pragma: no cover yparam1 = 'None' - # test if we have drive energy nowork = 1 try: @@ -5660,64 +5511,60 @@ def interactive_flex_chart2(id,r,promember=0, rowdata['power'] = 50. # replace nans - rowdata.fillna(value=0,inplace=True) - - + rowdata.fillna(value=0, inplace=True) row = Workout.objects.get(id=id) if rowdata.empty: - return "","No valid data",'','',workstrokesonly + return "", "No valid data", '', '', workstrokesonly - - workoutstateswork = [1,4,5,8,9,6,7] + workoutstateswork = [1, 4, 5, 8, 9, 6, 7] workoutstatesrest = [3] - workoutstatetransition = [0,2,10,11,12,13] + workoutstatetransition = [0, 2, 10, 11, 12, 13] - if workstrokesonly: # pragma: no cover + if workstrokesonly: # pragma: no cover try: rowdata = rowdata[~rowdata['workoutstate'].isin(workoutstatesrest)] except KeyError: pass try: - tseconds = rowdata.loc[:,'time'] - except KeyError: # pragma: no cover - return '','No time data - cannot make flex plot','','',workstrokesonly - + tseconds = rowdata.loc[:, 'time'] + except KeyError: # pragma: no cover + return '', 'No time data - cannot make flex plot', '', '', workstrokesonly try: - rowdata['x1'] = rowdata.loc[:,xparam] + rowdata['x1'] = rowdata.loc[:, xparam] rowmin = rowdata[xparam].min() - except KeyError: # pragma: no cover - rowdata['x1'] = 0*rowdata.loc[:,'time'] + except KeyError: # pragma: no cover + rowdata['x1'] = 0*rowdata.loc[:, 'time'] try: - rowdata['y1'] = rowdata.loc[:,yparam1] + rowdata['y1'] = rowdata.loc[:, yparam1] rowmin = rowdata[yparam1].min() - except KeyError: # pragma: no cover - rowdata['y1'] = 0*rowdata.loc[:,'time'] + except KeyError: # pragma: no cover + rowdata['y1'] = 0*rowdata.loc[:, 'time'] rowdata[yparam1] = rowdata['y1'] if yparam2 != 'None': try: - rowdata['y2'] = rowdata.loc[:,yparam2] + rowdata['y2'] = rowdata.loc[:, yparam2] rowmin = rowdata[yparam2].min() - except KeyError: # pragma: no cover - rowdata['y2'] = 0*rowdata.loc[:,'time'] + except KeyError: # pragma: no cover + rowdata['y2'] = 0*rowdata.loc[:, 'time'] rowdata[yparam2] = rowdata['y2'] - else: # pragma: no cover + else: # pragma: no cover rowdata['y2'] = rowdata['y1'] - if xparam=='time': + if xparam == 'time': xaxmax = tseconds.max() xaxmin = tseconds.min() - elif xparam=='distance' or xparam=='cumdist': + elif xparam == 'distance' or xparam == 'cumdist': xaxmax = rowdata['x1'].max() xaxmin = rowdata['x1'].min() - else: # pragma: no cover + else: # pragma: no cover try: - xaxmax = get_yaxmaxima(r,xparam,mode) - xaxmin = get_yaxminima(r,xparam,mode) + xaxmax = get_yaxmaxima(r, xparam, mode) + xaxmin = get_yaxminima(r, xparam, mode) except KeyError: xaxmax = rowdata['x1'].max() xaxmin = rowdata['x1'].min() @@ -5726,15 +5573,14 @@ def interactive_flex_chart2(id,r,promember=0, if xparam != 'time': try: x1mean = rowdata['x1'].mean() - except TypeError: # pragma: no cover + except TypeError: # pragma: no cover x1mean = 0 - else: # pragma: no cover + else: # pragma: no cover x1mean = 0 y1mean = rowdata['y1'].mean() y2mean = rowdata['y2'].mean() - if xparam != 'time': xvals = xaxmin+np.arange(100)*(xaxmax-xaxmin)/100. else: @@ -5742,7 +5588,7 @@ def interactive_flex_chart2(id,r,promember=0, # constant power plot if yparam1 == 'driveenergy': - if xparam == 'spm': # pragma: no cover + if xparam == 'spm': # pragma: no cover yconstantpower = rowdata['y1'].mean()*rowdata['x1'].mean()/xvals x_axis_type = 'linear' @@ -5753,61 +5599,55 @@ def interactive_flex_chart2(id,r,promember=0, if yparam1 == 'pace': y_axis_type = 'datetime' try: - y1mean = rowdata.loc[:,'pace'].mean() - except KeyError: # pragma: no cover + y1mean = rowdata.loc[:, 'pace'].mean() + except KeyError: # pragma: no cover y1mean = 0 try: rowdata['xname'] = axlabels[xparam] - except KeyError: # pragma: no cover + except KeyError: # pragma: no cover rowdata['xname'] = xparam try: rowdata['yname1'] = axlabels[yparam1] - except KeyError: # pragma: no cover + except KeyError: # pragma: no cover rowdata['yname1'] = yparam1 if yparam2 != 'None': try: rowdata['yname2'] = axlabels[yparam2] - except KeyError: # pragma: no cover + except KeyError: # pragma: no cover rowdata['yname2'] = yparam2 - else: # pragma: no cover + else: # pragma: no cover rowdata['yname2'] = rowdata['yname1'] - # prepare data source = ColumnDataSource( rowdata - ) - - + ) # second source for filtering source2 = ColumnDataSource( rowdata.copy() - ) + ) # Add hover to this comma-separated string and see what changes - if (promember==1): + if (promember == 1): TOOLS = 'save,pan,box_zoom,wheel_zoom,reset,tap,hover,crosshair' else: TOOLS = 'pan,box_zoom,wheel_zoom,reset,tap,hover,crosshair' - - - plot = Figure(x_axis_type=x_axis_type,y_axis_type=y_axis_type, - tools=TOOLS,toolbar_location='above', - toolbar_sticky=False,plot_width=800,plot_height=600, + plot = Figure(x_axis_type=x_axis_type, y_axis_type=y_axis_type, + tools=TOOLS, toolbar_location='above', + toolbar_sticky=False, plot_width=800, plot_height=600, ) plot.sizing_mode = 'stretch_both' #plot.width_policy = 'max' - # add watermark watermarkurl = "/static/img/logo7.png" watermarksource = ColumnDataSource(dict( - url = [watermarkurl],)) + url=[watermarkurl],)) - watermarkrange = Range1d(start=0,end=1) + watermarkrange = Range1d(start=0, end=1) watermarkalpha = 0.6 watermarkx = 0.99 watermarky = 0.01 @@ -5818,116 +5658,113 @@ def interactive_flex_chart2(id,r,promember=0, plot.extra_x_ranges = {"watermark": watermarkrange} plot.sizing_mode = 'stretch_both' - plot.image_url([watermarkurl],watermarkx,watermarky, - watermarkw,watermarkh, + plot.image_url([watermarkurl], watermarkx, watermarky, + watermarkw, watermarkh, global_alpha=watermarkalpha, w_units='screen', h_units='screen', anchor=watermarkanchor, dilate=True, - x_range_name = "watermark", - y_range_name = "watermark", + x_range_name="watermark", + y_range_name="watermark", ) - x1means = Span(location=x1mean,dimension='height',line_color='green', - line_dash=[6,6], line_width=2) + x1means = Span(location=x1mean, dimension='height', line_color='green', + line_dash=[6, 6], line_width=2) - y1means = Span(location=y1mean,dimension='width',line_color='blue', - line_dash=[6,6],line_width=2) + y1means = Span(location=y1mean, dimension='width', line_color='blue', + line_dash=[6, 6], line_width=2) y2means = y1means try: xlabeltext = axlabels[xparam]+": {x1mean:6.2f}".format( x1mean=x1mean ) - except KeyError: # pragma: no cover + except KeyError: # pragma: no cover xlabeltext = xparam+": {x1mean:6.2f}".format(x1mean=x1mean) - xlabel = Label(x=50,y=80,x_units='screen',y_units='screen', + xlabel = Label(x=50, y=80, x_units='screen', y_units='screen', text=xlabeltext, background_fill_alpha=.7, background_fill_color='white', text_color='green', - ) + ) - annolabel = Label(x=50,y=450,x_units='screen',y_units='screen', + annolabel = Label(x=50, y=450, x_units='screen', y_units='screen', text='', background_fill_alpha=0.7, background_fill_color='white', text_color='black', - ) + ) - sliderlabel = Label(x=10,y=470,x_units='screen',y_units='screen', + sliderlabel = Label(x=10, y=470, x_units='screen', y_units='screen', text='', background_fill_alpha=0.7, background_fill_color='white', - text_color='black',text_font_size='10pt', + text_color='black', text_font_size='10pt', ) - - if (xparam != 'time') and (xparam != 'distance') and (xparam != 'cumdist'): # pragma: no cover + if (xparam != 'time') and (xparam != 'distance') and (xparam != 'cumdist'): # pragma: no cover plot.add_layout(x1means) plot.add_layout(xlabel) - plot.add_layout(y1means) plot.add_layout(annolabel) plot.add_layout(sliderlabel) try: yaxlabel = axlabels[yparam1] - except KeyError: # pragma: no cover + except KeyError: # pragma: no cover yaxlabel = str(yparam1)+' ' try: xaxlabel = axlabels[xparam] - except KeyError: # pragma: no cover + except KeyError: # pragma: no cover xaxlabel = xparam - y1label = Label(x=50,y=50,x_units='screen',y_units='screen', + y1label = Label(x=50, y=50, x_units='screen', y_units='screen', text=yaxlabel+": {y1mean:6.2f}".format(y1mean=y1mean), background_fill_alpha=.7, background_fill_color='white', text_color='blue', - ) - if yparam1 != 'time' and yparam1 != 'pace': # pragma: no cover + ) + if yparam1 != 'time' and yparam1 != 'pace': # pragma: no cover plot.add_layout(y1label) y2label = y1label # average values - if yparam1 == 'driveenergy': # pragma: no cover + if yparam1 == 'driveenergy': # pragma: no cover if xparam == 'spm': - plot.line(xvals,yconstantpower,color="green",legend_label="Constant Power") + plot.line(xvals, yconstantpower, color="green", + legend_label="Constant Power") - if plottype=='line': - plot.line('x1','y1',source=source2,legend_label=yaxlabel) - elif plottype=='scatter': # pragma: no cover - plot.scatter('x1','y1',source=source2,legend_label=yaxlabel,fill_alpha=0.4, + if plottype == 'line': + plot.line('x1', 'y1', source=source2, legend_label=yaxlabel) + elif plottype == 'scatter': # pragma: no cover + plot.scatter('x1', 'y1', source=source2, legend_label=yaxlabel, fill_alpha=0.4, line_color=None) plot.title.text = row.name - plot.title.text_font_size=value("1.0em") + plot.title.text_font_size = value("1.0em") plot.sizing_mode = 'stretch_both' plot.xaxis.axis_label = xaxlabel plot.yaxis.axis_label = yaxlabel - - try: - yrange1 = Range1d(start=get_yaxminima(r,yparam1,mode), - end=get_yaxmaxima(r,yparam1,mode)) - except KeyError: # pragma: no cover + yrange1 = Range1d(start=get_yaxminima(r, yparam1, mode), + end=get_yaxmaxima(r, yparam1, mode)) + except KeyError: # pragma: no cover yrange1 = Range1d(start=rowdata[yparam1].min(), end=rowdata[yparam1].max()) plot.y_range = yrange1 - if (xparam != 'time') and (xparam != 'distance') and (xparam != 'cumdist'): # pragma: no cover + if (xparam != 'time') and (xparam != 'distance') and (xparam != 'cumdist'): # pragma: no cover try: - xrange1 = Range1d(start=get_yaxminima(r,xparam,mode), - end=get_yaxmaxima(r,xparam,mode)) + xrange1 = Range1d(start=get_yaxminima(r, xparam, mode), + end=get_yaxmaxima(r, xparam, mode)) except KeyError: xrange1 = Range1d(start=rowdata[xparam].min(), end=rowdata[xparam].max()) @@ -5935,92 +5772,87 @@ def interactive_flex_chart2(id,r,promember=0, plot.x_range = xrange1 if xparam == 'time': - xrange1 = Range1d(start=xaxmin,end=xaxmax) + xrange1 = Range1d(start=xaxmin, end=xaxmax) plot.x_range = xrange1 plot.xaxis[0].formatter = DatetimeTickFormatter( - hours = ["%H"], - minutes = ["%M"], - seconds = ["%S"], - days = ["0"], - months = [""], - years = [""] - ) - + hours=["%H"], + minutes=["%M"], + seconds=["%S"], + days=["0"], + months=[""], + years=[""] + ) if yparam1 == 'pace': plot.yaxis[0].formatter = DatetimeTickFormatter( - seconds = ["%S"], - minutes = ["%M"] - ) - + seconds=["%S"], + minutes=["%M"] + ) if yparam2 != 'None': try: - yrange2 = Range1d(start=get_yaxminima(r,yparam2,mode), - end=get_yaxmaxima(r,yparam2,mode)) - except KeyError: # pragma: no cover + yrange2 = Range1d(start=get_yaxminima(r, yparam2, mode), + end=get_yaxmaxima(r, yparam2, mode)) + except KeyError: # pragma: no cover yrange2 = Range1d(start=rowdata[yparam2].min(), end=rowdata[yparam2].max()) plot.extra_y_ranges["yax2"] = yrange2 - #= {"yax2": yrange2} + # = {"yax2": yrange2} try: axlegend = axlabels[yparam2] - except KeyError: # pragma: no cover + except KeyError: # pragma: no cover axlegend = str(yparam2)+' ' - if plottype=='line': - plot.line('x1','y2',color="red",y_range_name="yax2", + if plottype == 'line': + plot.line('x1', 'y2', color="red", y_range_name="yax2", legend_label=axlegend, source=source2) - elif plottype=='scatter': # pragma: no cover - plot.scatter('x1','y2',source=source2,legend_label=axlegend, + elif plottype == 'scatter': # pragma: no cover + plot.scatter('x1', 'y2', source=source2, legend_label=axlegend, fill_alpha=0.4, - line_color=None,color="red",y_range_name="yax2") + line_color=None, color="red", y_range_name="yax2") plot.add_layout(LinearAxis(y_range_name="yax2", - axis_label=axlegend),'right') - - y2means = Span(location=y2mean,dimension='width',line_color='red', - line_dash=[6,6],line_width=2,y_range_name="yax2") + axis_label=axlegend), 'right') + y2means = Span(location=y2mean, dimension='width', line_color='red', + line_dash=[6, 6], line_width=2, y_range_name="yax2") plot.add_layout(y2means) - y2label = Label(x=50,y=20,x_units='screen',y_units='screen', + y2label = Label(x=50, y=20, x_units='screen', y_units='screen', text=axlegend+": {y2mean:6.2f}".format(y2mean=y2mean), background_fill_alpha=.7, background_fill_color='white', text_color='red', - ) + ) if yparam2 != 'pace' and yparam2 != 'time': plot.add_layout(y2label) hover = plot.select(dict(type=HoverTool)) - hover.tooltips = OrderedDict([ - ('Time','@ftime'), - ('Distance','@distance{int}'), - ('Pace','@fpace'), - ('HR','@hr{int}'), - ('SPM','@spm{1.1}'), - ('Power','@power{int}'), - ]) + ('Time', '@ftime'), + ('Distance', '@distance{int}'), + ('Pace', '@fpace'), + ('HR', '@hr{int}'), + ('SPM', '@spm{1.1}'), + ('Power', '@power{int}'), + ]) hover.mode = 'mouse' - - callback = CustomJS(args = dict(source=source,source2=source2, - x1means=x1means, - y1means=y1means, - y1label=y1label, - y2label=y2label, - xlabel=xlabel, - annolabel=annolabel, - sliderlabel=sliderlabel, - y2means=y2means, - ), code=""" + callback = CustomJS(args=dict(source=source, source2=source2, + x1means=x1means, + y1means=y1means, + y1label=y1label, + y2label=y2label, + xlabel=xlabel, + annolabel=annolabel, + sliderlabel=sliderlabel, + y2means=y2means, + ), code=""" var data = source.data var data2 = source2.data var x1 = data['x1'] @@ -6133,46 +5965,45 @@ def interactive_flex_chart2(id,r,promember=0, source2.change.emit(); """) - annotation = TextInput(width=140, title="Type your plot notes here", value="") - annotation.js_on_change('value',callback) + annotation = TextInput( + width=140, title="Type your plot notes here", value="") + annotation.js_on_change('value', callback) callback.args["annotation"] = annotation - slider_spm_min = Slider(width=140, start=15.0, end=55,value=15.0, step=.1, - title="Min SPM") - slider_spm_min.js_on_change('value',callback) + slider_spm_min = Slider(width=140, start=15.0, end=55, value=15.0, step=.1, + title="Min SPM") + slider_spm_min.js_on_change('value', callback) callback.args["minspm"] = slider_spm_min - - slider_spm_max = Slider(width=140, start=15.0, end=55,value=55.0, step=.1, - title="Max SPM") - slider_spm_max.js_on_change('value',callback) + slider_spm_max = Slider(width=140, start=15.0, end=55, value=55.0, step=.1, + title="Max SPM") + slider_spm_max.js_on_change('value', callback) callback.args["maxspm"] = slider_spm_max - slider_work_min = Slider(width=140, start=0.0, end=1500,value=0.0, step=10, - title="Min Work per Stroke") - slider_work_min.js_on_change('value',callback) + slider_work_min = Slider(width=140, start=0.0, end=1500, value=0.0, step=10, + title="Min Work per Stroke") + slider_work_min.js_on_change('value', callback) callback.args["minwork"] = slider_work_min - - slider_work_max = Slider(width=140, start=0.0, end=1500,value=1500.0, step=10, - title="Max Work per Stroke") - slider_work_max.js_on_change('value',callback) + slider_work_max = Slider(width=140, start=0.0, end=1500, value=1500.0, step=10, + title="Max Work per Stroke") + slider_work_max.js_on_change('value', callback) callback.args["maxwork"] = slider_work_max try: distmax = 100+100*int(rowdata['distance'].max()/100.) - except (KeyError,ValueError): # pragma: no cover + except (KeyError, ValueError): # pragma: no cover distmax = 100 - slider_dist_min = Slider(width=140, start=0,end=distmax,value=0,step=50, + slider_dist_min = Slider(width=140, start=0, end=distmax, value=0, step=50, title="Min Distance") - slider_dist_min.js_on_change('value',callback) + slider_dist_min.js_on_change('value', callback) callback.args["mindist"] = slider_dist_min - slider_dist_max = Slider(width=140, start=0,end=distmax,value=distmax, + slider_dist_max = Slider(width=140, start=0, end=distmax, value=distmax, step=50, title="Max Distance") - slider_dist_max.js_on_change('value',callback) + slider_dist_max.js_on_change('value', callback) callback.args["maxdist"] = slider_dist_max #annotation.sizing_mode = 'fixed' @@ -6205,9 +6036,10 @@ def interactive_flex_chart2(id,r,promember=0, js_resources = INLINE.render_js() css_resources = INLINE.render_css() - return [script,div,js_resources,css_resources,workstrokesonly] + return [script, div, js_resources, css_resources, workstrokesonly] -def thumbnails_set(r,id,favorites): + +def thumbnails_set(r, id, favorites): charts = [] columns = [f.xparam for f in favorites] @@ -6215,26 +6047,26 @@ def thumbnails_set(r,id,favorites): columns += [f.yparam2 for f in favorites] columns += ['time'] - rowdata = dataprep.getsmallrowdata_db(columns,ids=[id],doclean=True) + rowdata = dataprep.getsmallrowdata_db(columns, ids=[id], doclean=True) - rowdata.dropna(axis=1,how='all',inplace=True) + rowdata.dropna(axis=1, how='all', inplace=True) if rowdata.empty: try: - rowdata = dataprep.getsmallrowdata_db(columns,ids=[id],doclean=False, + rowdata = dataprep.getsmallrowdata_db(columns, ids=[id], doclean=False, workstrokesonly=False) - except: # pragma: no cover + except: # pragma: no cover return [ - {'script':"", - 'div':"", - 'notes':"" - }] + {'script': "", + 'div': "", + 'notes': "" + }] if rowdata.empty: return [ - {'script':"", - 'div':"", - 'notes':"" + {'script': "", + 'div': "", + 'notes': "" }] # else: @@ -6247,15 +6079,16 @@ def thumbnails_set(r,id,favorites): maxlength = 50 if l > maxlength: try: - bins = np.linspace(rowdata['time'].min(),rowdata['time'].max(),maxlength) - groups = rowdata.groupby(np.digitize(rowdata['time'],bins)) + bins = np.linspace(rowdata['time'].min(), + rowdata['time'].max(), maxlength) + groups = rowdata.groupby(np.digitize(rowdata['time'], bins)) rowdata = groups.mean() - except KeyError: # pragma: no cover + except KeyError: # pragma: no cover pass for f in favorites: workstrokesonly = not f.reststrokes - script,div = thumbnail_flex_chart( + script, div = thumbnail_flex_chart( rowdata, id=id, xparam=f.xparam, @@ -6264,23 +6097,21 @@ def thumbnails_set(r,id,favorites): plottype=f.plottype, ) - charts.append({ - 'script':script, - 'div':div, - 'notes':f.notes}) + 'script': script, + 'div': div, + 'notes': f.notes}) return charts -def thumbnail_flex_chart(rowdata,id=0,promember=0, +def thumbnail_flex_chart(rowdata, id=0, promember=0, xparam='time', yparam1='pace', yparam2='hr', plottype='line', workstrokesonly=False): - try: tests = rowdata[yparam2] except KeyError: @@ -6291,36 +6122,33 @@ def thumbnail_flex_chart(rowdata,id=0,promember=0, except KeyError: yparam1 = 'None' - + try: + tseconds = rowdata.loc[:, 'time'] + except KeyError: # pragma: no cover + return '', 'No time data - cannot make flex plot' try: - tseconds = rowdata.loc[:,'time'] - except KeyError: # pragma: no cover - return '','No time data - cannot make flex plot' - + rowdata['x1'] = rowdata.loc[:, xparam] + except KeyError: # pragma: no cover + rowdata['x1'] = 0*rowdata.loc[:, 'time'] try: - rowdata['x1'] = rowdata.loc[:,xparam] - except KeyError: # pragma: no cover - rowdata['x1'] = 0*rowdata.loc[:,'time'] - - try: - rowdata['y1'] = rowdata.loc[:,yparam1] - except KeyError: # pragma: no cover - rowdata['y1'] = 0*rowdata.loc[:,'time'] + rowdata['y1'] = rowdata.loc[:, yparam1] + except KeyError: # pragma: no cover + rowdata['y1'] = 0*rowdata.loc[:, 'time'] if yparam2 != 'None': try: - rowdata['y2'] = rowdata.loc[:,yparam2] - except KeyError: # pragma: no cover - rowdata['y2'] = 0*rowdata.loc[:,'time'] + rowdata['y2'] = rowdata.loc[:, yparam2] + except KeyError: # pragma: no cover + rowdata['y2'] = 0*rowdata.loc[:, 'time'] else: rowdata['y2'] = rowdata['y1'] - if xparam=='time': + if xparam == 'time': xaxmax = tseconds.max() xaxmin = tseconds.min() - elif xparam=='distance' or xparam=='cumdist': # pragma: no cover + elif xparam == 'distance' or xparam == 'cumdist': # pragma: no cover xaxmax = rowdata['x1'].max() xaxmin = rowdata['x1'].min() else: @@ -6332,35 +6160,31 @@ def thumbnail_flex_chart(rowdata,id=0,promember=0, if xparam == 'time': x_axis_type = 'datetime' - if yparam1 == 'pace': # pragma: no cover + if yparam1 == 'pace': # pragma: no cover y_axis_type = 'datetime' - y1mean = rowdata.loc[:,'pace'].mean() - + y1mean = rowdata.loc[:, 'pace'].mean() rowdata['xname'] = axlabels[xparam] try: rowdata['yname1'] = axlabels[yparam1] - except KeyError: # pragma: no cover + except KeyError: # pragma: no cover rowdata['yname1'] = axlabels[xparam] if yparam2 != 'None': rowdata['yname2'] = axlabels[yparam2] else: rowdata['yname2'] = axlabels[yparam1] - # prepare data source = ColumnDataSource( rowdata - ) - + ) sizing_mode = 'fixed' # 'stretch_both' also looks nice with this example - plot = Figure(x_axis_type=x_axis_type,y_axis_type=y_axis_type, - plot_width=200,plot_height=150, + plot = Figure(x_axis_type=x_axis_type, y_axis_type=y_axis_type, + plot_width=200, plot_height=150, ) - # plot.sizing_mode = 'stretch_both' plot.sizing_mode = 'fixed' plot.toolbar.logo = None @@ -6371,90 +6195,87 @@ def thumbnail_flex_chart(rowdata,id=0,promember=0, plot.xaxis.major_label_text_font_size = "7pt" plot.yaxis.major_label_text_font_size = "7pt" - if plottype=='line': - plot.line('x1','y1',source=source) - elif plottype=='scatter': - plot.scatter('x1','y1',source=source,fill_alpha=0.4, + if plottype == 'line': + plot.line('x1', 'y1', source=source) + elif plottype == 'scatter': + plot.scatter('x1', 'y1', source=source, fill_alpha=0.4, line_color=None) try: plot.xaxis.axis_label = axlabels[xparam] - except KeyError: # pragma: no cover + except KeyError: # pragma: no cover plot.xaxis.axis_label = 'X' try: plot.yaxis.axis_label = axlabels[yparam1] - except KeyError: # pragma: no cover + except KeyError: # pragma: no cover plot.yaxis.axis_label = 'Y' - try: - yrange1 = Range1d(start=yaxminima[yparam1],end=yaxmaxima[yparam1]) - except KeyError: # pragma: no cover + yrange1 = Range1d(start=yaxminima[yparam1], end=yaxmaxima[yparam1]) + except KeyError: # pragma: no cover yrange1 = Range1d(start=yparam1.min(), end=yparam1.max()) plot.y_range = yrange1 if (xparam != 'time') and (xparam != 'distance') and (xparam != 'cumdist'): - xrange1 = Range1d(start=yaxminima[xparam],end=yaxmaxima[xparam]) + xrange1 = Range1d(start=yaxminima[xparam], end=yaxmaxima[xparam]) plot.x_range = xrange1 if xparam == 'time': - xrange1 = Range1d(start=xaxmin,end=xaxmax) + xrange1 = Range1d(start=xaxmin, end=xaxmax) plot.x_range = xrange1 plot.xaxis[0].formatter = DatetimeTickFormatter( - hours = ["%H"], - minutes = ["%M"], - seconds = ["%S"], - days = ["0"], - months = [""], - years = [""] - ) + hours=["%H"], + minutes=["%M"], + seconds=["%S"], + days=["0"], + months=[""], + years=[""] + ) - - if yparam1 == 'pace': # pragma: no cover + if yparam1 == 'pace': # pragma: no cover plot.yaxis[0].formatter = DatetimeTickFormatter( - seconds = ["%S"], - minutes = ["%M"] - ) - + seconds=["%S"], + minutes=["%M"] + ) if yparam2 != 'None': - yrange2 = Range1d(start=yaxminima[yparam2],end=yaxmaxima[yparam2]) + yrange2 = Range1d(start=yaxminima[yparam2], end=yaxmaxima[yparam2]) plot.extra_y_ranges["yax2"] = yrange2 - #= {"yax2": yrange2} + # = {"yax2": yrange2} - if plottype=='line': - plot.line('x1','y2',color="red",y_range_name="yax2", + if plottype == 'line': + plot.line('x1', 'y2', color="red", y_range_name="yax2", source=source) - elif plottype=='scatter': # pragma: no cover - plot.scatter('x1','y2',source=source, + elif plottype == 'scatter': # pragma: no cover + plot.scatter('x1', 'y2', source=source, fill_alpha=0.4, - line_color=None,color="red",y_range_name="yax2") + line_color=None, color="red", y_range_name="yax2") plot.add_layout(LinearAxis(y_range_name="yax2", axis_label=axlabels[yparam2], major_label_text_font_size="7pt", axis_label_text_font_size="7pt", - ),'right', - ) - + ), 'right', + ) script, div = components(plot) - return [script,div] + return [script, div] -def interactive_multiple_compare_chart(ids,xparam,yparam,plottype='line', - promember=0,workstrokesonly=True, - labeldict=None,startenddict={}): + +def interactive_multiple_compare_chart(ids, xparam, yparam, plottype='line', + promember=0, workstrokesonly=True, + labeldict=None, startenddict={}): message = '' errormessage = '' - columns = [xparam,yparam, - 'ftime','distance','fpace', - 'power','hr','spm', - 'time','pace','workoutstate', + columns = [xparam, yparam, + 'ftime', 'distance', 'fpace', + 'power', 'hr', 'spm', + 'time', 'pace', 'workoutstate', 'workoutid'] compute = False @@ -6462,21 +6283,20 @@ def interactive_multiple_compare_chart(ids,xparam,yparam,plottype='line', if workstrokesonly: compute = True doclean = True - datadf = dataprep.getsmallrowdata_db(columns,ids=ids,doclean=doclean,compute=compute, + datadf = dataprep.getsmallrowdata_db(columns, ids=ids, doclean=doclean, compute=compute, workstrokesonly=workstrokesonly) - datadf.dropna(axis=1,how='all',inplace=True) - datadf.dropna(axis=0,how='any',inplace=True) - + datadf.dropna(axis=1, how='all', inplace=True) + datadf.dropna(axis=0, how='any', inplace=True) nrworkouts = len(ids) try: - tseconds = datadf.loc[:,'time'] - except KeyError: # pragma: no cover + tseconds = datadf.loc[:, 'time'] + except KeyError: # pragma: no cover try: - tseconds = datadf.loc[:,xparam] + tseconds = datadf.loc[:, xparam] except: - return ['','

A chart data error occurred

','','A chart data error occurred'] + return ['', '

A chart data error occurred

', '', 'A chart data error occurred'] yparamname = axlabels[yparam] @@ -6485,17 +6305,17 @@ def interactive_multiple_compare_chart(ids,xparam,yparam,plottype='line', #datadf = datadf[datadf[xparam] > 0] # check if dataframe not empty - if datadf.empty: # pragma: no cover - return ['','

No non-zero data in selection

','','No non-zero data in selection'] + if datadf.empty: # pragma: no cover + return ['', '

No non-zero data in selection

', '', 'No non-zero data in selection'] - if xparam != 'distance' and xparam != 'time' and xparam != 'cumdist': # pragma: no cover + if xparam != 'distance' and xparam != 'time' and xparam != 'cumdist': # pragma: no cover xaxmax = yaxmaxima[xparam] xaxmin = yaxminima[xparam] elif xparam == 'time' and not startenddict: xaxmax = tseconds.max() xaxmin = tseconds.min() - elif xparam == 'time' and startenddict: # pragma: no cover - deltas = [pair[1]-pair[0] for key,pair in startenddict.items()] + elif xparam == 'time' and startenddict: # pragma: no cover + deltas = [pair[1]-pair[0] for key, pair in startenddict.items()] xaxmin = 0 xaxmax = pd.Series(deltas).max()*1000. if xaxmax == 0: @@ -6504,10 +6324,10 @@ def interactive_multiple_compare_chart(ids,xparam,yparam,plottype='line', xaxmax = datadf['distance'].max() xaxmin = datadf['distance'].min() - if yparam == 'distance': # pragma: no cover + if yparam == 'distance': # pragma: no cover yaxmin = datadf['distance'].min() yaxmax = datadf['distance'].max() - elif yparam == 'cumdist': # pragma: no cover + elif yparam == 'cumdist': # pragma: no cover yaxmin = datadf['cumdist'].min() yaxmax = datadf['cumdist'].max() else: @@ -6518,9 +6338,9 @@ def interactive_multiple_compare_chart(ids,xparam,yparam,plottype='line', y_axis_type = 'linear' # Add hover to this comma-separated string and see what changes - if (promember==1): + if (promember == 1): TOOLS = 'save,pan,box_zoom,wheel_zoom,reset,tap,crosshair' - else: # pragma: no cover + else: # pragma: no cover TOOLS = 'pan,box_zoom,wheel_zoom,reset,tap,crosshair' if yparam == 'pace': @@ -6536,18 +6356,18 @@ def interactive_multiple_compare_chart(ids,xparam,yparam,plottype='line', else: xvals = np.arange(100) - plot = Figure(x_axis_type=x_axis_type,y_axis_type=y_axis_type, + plot = Figure(x_axis_type=x_axis_type, y_axis_type=y_axis_type, tools=TOOLS, toolbar_location="above", - plot_width=920,plot_height=500, + plot_width=920, plot_height=500, toolbar_sticky=False) # add watermark watermarkurl = "/static/img/logo7.png" watermarksource = ColumnDataSource(dict( - url = [watermarkurl],)) + url=[watermarkurl],)) - watermarkrange = Range1d(start=0,end=1) + watermarkrange = Range1d(start=0, end=1) watermarkalpha = 0.6 watermarkx = 0.99 watermarky = 0.01 @@ -6558,16 +6378,15 @@ def interactive_multiple_compare_chart(ids,xparam,yparam,plottype='line', plot.extra_x_ranges = {"watermark": watermarkrange} plot.sizing_mode = 'stretch_both' - - plot.image_url([watermarkurl],0.05,0.9, - watermarkw,watermarkh, + plot.image_url([watermarkurl], 0.05, 0.9, + watermarkw, watermarkh, global_alpha=watermarkalpha, w_units='screen', h_units='screen', anchor='top_left', dilate=True, - x_range_name = "watermark", - y_range_name = "watermark", + x_range_name="watermark", + y_range_name="watermark", ) colors = itertools.cycle(palette) @@ -6576,26 +6395,26 @@ def interactive_multiple_compare_chart(ids,xparam,yparam,plottype='line', l1 = [] try: - items = itertools.izip(ids,colors) + items = itertools.izip(ids, colors) except AttributeError: - items = zip(ids,colors) + items = zip(ids, colors) - for id,color in items: - group = datadf[datadf['workoutid']==int(id)].copy() + for id, color in items: + group = datadf[datadf['workoutid'] == int(id)].copy() try: - startsecond,endsecond = startenddict[id] + startsecond, endsecond = startenddict[id] except KeyError: startsecond = 0 endsecond = 0 - group.sort_values(by='time',ascending=True,inplace=True) + group.sort_values(by='time', ascending=True, inplace=True) if endsecond > 0: group['time'] = group['time'] - 1.e3*startsecond mask = group['time'] < 0 - group.mask(mask,inplace=True) + group.mask(mask, inplace=True) mask = group['time'] > 1.e3*(endsecond-startsecond) - group.mask(mask,inplace=True) + group.mask(mask, inplace=True) if xparam == 'cumdist': group['cumdist'] = group['cumdist'] - group['cumdist'].min() @@ -6604,10 +6423,9 @@ def interactive_multiple_compare_chart(ids,xparam,yparam,plottype='line', elif xparam == 'distance': group['distance'] = group['distance'] - group['distance'].min() - try: group['x'] = group[xparam] - except KeyError: # pragma: no cover + except KeyError: # pragma: no cover group['x'] = group['time'] errormessage = xparam+' has no values. Plot invalid' try: @@ -6615,40 +6433,40 @@ def interactive_multiple_compare_chart(ids,xparam,yparam,plottype='line', except KeyError: group['y'] = 0.0*group['x'] - ymean = group['y'].mean() f = group['time'].diff().mean() if f != 0 and not np.isnan(f): - windowsize = 2* (int(20000./(f))) + 1 + windowsize = 2 * (int(20000./(f))) + 1 else: windowsize = 1 if windowsize > 3 and windowsize < len(group['y']): try: - group['y'] = savgol_filter(group['y'],windowsize,3) - except ValueError: # pragma: no cover + group['y'] = savgol_filter(group['y'], windowsize, 3) + except ValueError: # pragma: no cover pass - ylabel = Label(x=100,y=60+nrworkouts*20-20*cntr, - x_units='screen',y_units='screen', - text=axlabels[yparam]+": {ymean:6.2f}".format(ymean=ymean), + ylabel = Label(x=100, y=60+nrworkouts*20-20*cntr, + x_units='screen', y_units='screen', + text=axlabels[yparam] + + ": {ymean:6.2f}".format(ymean=ymean), background_fill_alpha=.7, background_fill_color='white', text_color=color, - ) + ) if yparam != 'time' and yparam != 'pace': plot.add_layout(ylabel) source = ColumnDataSource( group - ) + ) TIPS = OrderedDict([ - ('time','@ftime'), - ('pace','@fpace'), - ('hr','@hr'), - ('spm','@spm{1.1}'), - ('distance','@distance{5}'), + ('time', '@ftime'), + ('pace', '@fpace'), + ('hr', '@hr'), + ('spm', '@spm{1.1}'), + ('distance', '@distance{5}'), ]) hover = plot.select(type=HoverTool) @@ -6656,82 +6474,76 @@ def interactive_multiple_compare_chart(ids,xparam,yparam,plottype='line', if labeldict: try: - legend_label=labeldict[id] - except KeyError: # pragma: no cover + legend_label = labeldict[id] + except KeyError: # pragma: no cover legend = str(id) - else: # pragma: no cover - legend_label=str(id) + else: # pragma: no cover + legend_label = str(id) - if plottype=='line': - l1.append(plot.line('x','y',source=source,color=color,legend_label=legend_label,line_width=2)) + if plottype == 'line': + l1.append(plot.line('x', 'y', source=source, color=color, + legend_label=legend_label, line_width=2)) else: - l1.append(plot.scatter('x','y',source=source,color=color,legend_label=legend_label, - fill_alpha=0.4,line_color=None)) + l1.append(plot.scatter('x', 'y', source=source, color=color, legend_label=legend_label, + fill_alpha=0.4, line_color=None)) - plot.add_tools(HoverTool(renderers=[l1[cntr]],tooltips=TIPS)) + plot.add_tools(HoverTool(renderers=[l1[cntr]], tooltips=TIPS)) cntr += 1 - plot.legend.location='top_right' + plot.legend.location = 'top_right' plot.xaxis.axis_label = axlabels[xparam] plot.yaxis.axis_label = axlabels[yparam] - if (xparam != 'time') and (xparam != 'distance') and (xparam != 'cumdist'): # pragma: no cover - xrange1 = Range1d(start=yaxminima[xparam],end=yaxmaxima[xparam]) + if (xparam != 'time') and (xparam != 'distance') and (xparam != 'cumdist'): # pragma: no cover + xrange1 = Range1d(start=yaxminima[xparam], end=yaxmaxima[xparam]) plot.x_range = xrange1 - yrange1 = Range1d(start=yaxmin,end=yaxmax) + yrange1 = Range1d(start=yaxmin, end=yaxmax) plot.y_range = yrange1 if xparam == 'time': - xrange1 = Range1d(start=xaxmin,end=xaxmax) + xrange1 = Range1d(start=xaxmin, end=xaxmax) plot.x_range = xrange1 plot.xaxis[0].formatter = DatetimeTickFormatter( - hours = ["%H"], - minutes = ["%M"], - seconds = ["%S"], - days = ["0"], - months = [""], - years = [""] - ) - + hours=["%H"], + minutes=["%M"], + seconds=["%S"], + days=["0"], + months=[""], + years=[""] + ) if yparam == 'pace': plot.yaxis[0].formatter = DatetimeTickFormatter( - seconds = ["%S"], - minutes = ["%M"] - ) + seconds=["%S"], + minutes=["%M"] + ) script, div = components(plot) - return [script,div,message,errormessage] + return [script, div, message, errormessage] - - -def interactive_otw_advanced_pace_chart(id=0,promember=0): +def interactive_otw_advanced_pace_chart(id=0, promember=0): # check if valid ID exists (workout exists) - rowdata,row = dataprep.getrowdata_db(id=id) - rowdata.dropna(axis=1,how='all',inplace=True) - rowdata.dropna(axis=0,how='any',inplace=True) + rowdata, row = dataprep.getrowdata_db(id=id) + rowdata.dropna(axis=1, how='all', inplace=True) + rowdata.dropna(axis=0, how='any', inplace=True) if rowdata.empty: - return "","No Valid Data Available" + return "", "No Valid Data Available" # Add hover to this comma-separated string and see what changes - if (promember==1): + if (promember == 1): TOOLS = 'save,pan,box_zoom,wheel_zoom,reset,tap,hover,crosshair' - else: # pragma: no cover + else: # pragma: no cover TOOLS = 'pan,box_zoom,wheel_zoom,reset,tap,hover,crosshair' source = ColumnDataSource( rowdata - ) + ) - - - - - plot = Figure(x_axis_type="datetime",y_axis_type="datetime", + plot = Figure(x_axis_type="datetime", y_axis_type="datetime", tools=TOOLS, plot_width=920, toolbar_sticky=False) @@ -6739,9 +6551,9 @@ def interactive_otw_advanced_pace_chart(id=0,promember=0): # add watermark watermarkurl = "/static/img/logo7.png" watermarksource = ColumnDataSource(dict( - url = [watermarkurl],)) + url=[watermarkurl],)) - watermarkrange = Range1d(start=0,end=1) + watermarkrange = Range1d(start=0, end=1) watermarkalpha = 0.6 watermarkx = 0.99 watermarky = 0.01 @@ -6752,65 +6564,66 @@ def interactive_otw_advanced_pace_chart(id=0,promember=0): plot.extra_x_ranges = {"watermark": watermarkrange} plot.sizing_mode = 'scale_both' - - plot.image_url([watermarkurl],watermarkx,watermarky, - watermarkw,watermarkh, + plot.image_url([watermarkurl], watermarkx, watermarky, + watermarkw, watermarkh, global_alpha=watermarkalpha, w_units='screen', h_units='screen', anchor=watermarkanchor, dilate=True, - x_range_name = "watermark", - y_range_name = "watermark", + x_range_name="watermark", + y_range_name="watermark", ) plot.title.text = row.name - plot.title.text_font_size=value("1.2em") + plot.title.text_font_size = value("1.2em") plot.xaxis.axis_label = "Time" plot.yaxis.axis_label = "Pace (/500m)" plot.xaxis[0].formatter = DatetimeTickFormatter( - hours = ["%H"], - minutes = ["%M"], - seconds = ["%S"], - days = ["0"], - months = [""], - years = [""] - ) + hours=["%H"], + minutes=["%M"], + seconds=["%S"], + days=["0"], + months=[""], + years=[""] + ) plot.yaxis[0].formatter = DatetimeTickFormatter( - seconds = ["%S"], - minutes = ["%M"] - ) + seconds=["%S"], + minutes=["%M"] + ) ymax = 1.0e3*90 ymin = 1.0e3*210 - plot.y_range = Range1d(ymin,ymax) - + plot.y_range = Range1d(ymin, ymax) hover = plot.select(dict(type=HoverTool)) - plot.line('time','pace',source=source,legend_label="Pace",color="black") - plot.line('time','nowindpace',source=source,legend_label="Corrected Pace",color="red") + plot.line('time', 'pace', source=source, + legend_label="Pace", color="black") + plot.line('time', 'nowindpace', source=source, + legend_label="Corrected Pace", color="red") hover.tooltips = OrderedDict([ - ('Time','@ftime'), - ('Pace','@fpace'), - ('Corrected Pace','@fnowindpace'), - ('HR','@hr{int}'), - ('SPM','@spm{1.1}'), - ]) + ('Time', '@ftime'), + ('Pace', '@fpace'), + ('Corrected Pace', '@fnowindpace'), + ('HR', '@hr{int}'), + ('SPM', '@spm{1.1}'), + ]) hover.mode = 'mouse' try: script, div = components(plot) - except: # pragma: no cover + except: # pragma: no cover script = '' div = '' - return [script,div] + return [script, div] -def get_zones_report(rower,startdate,enddate,trainingzones='hr',date_agg='week', + +def get_zones_report(rower, startdate, enddate, trainingzones='hr', date_agg='week', yaxis='time'): duration = enddate-startdate @@ -6825,7 +6638,6 @@ def get_zones_report(rower,startdate,enddate,trainingzones='hr',date_agg='week', enddate = enddate + datetime.timedelta(days=1) - workouts = Workout.objects.filter( user=rower, startdatetime__gte=startdate, @@ -6835,16 +6647,16 @@ def get_zones_report(rower,startdate,enddate,trainingzones='hr',date_agg='week', ids = [w.id for w in workouts] - columns = ['workoutid','hr','power','time'] + columns = ['workoutid', 'hr', 'power', 'time'] - df = dataprep.getsmallrowdata_db(columns,ids=ids) + df = dataprep.getsmallrowdata_db(columns, ids=ids) try: df['deltat'] = df['time'].diff().clip(lower=0).clip(upper=20*1e3) - except KeyError: # pragma: no cover + except KeyError: # pragma: no cover pass - df = dataprep.clean_df_stats(df,workstrokesonly=False, - ignoreadvanced=True,ignorehr=False) + df = dataprep.clean_df_stats(df, workstrokesonly=False, + ignoreadvanced=True, ignorehr=False) #totalmeters,totalhours, totalminutes, totalseconds = get_totals(workouts) @@ -6857,11 +6669,12 @@ def get_zones_report(rower,startdate,enddate,trainingzones='hr',date_agg='week', dd3 = w.date.strftime('%Y/%m') dd4 = '{year}/{week:02d}'.format( week=arrow.get(w.date).isocalendar()[1], - year= w.date.strftime('%y') - ) - dd4 = (w.date - datetime.timedelta(days = w.date.weekday())).strftime('%y/%m/%d') + year=w.date.strftime('%y') + ) + dd4 = (w.date - datetime.timedelta(days=w.date.weekday()) + ).strftime('%y/%m/%d') - #print(w.date,arrow.get(w.date),arrow.get(w.date).isocalendar()) + # print(w.date,arrow.get(w.date),arrow.get(w.date).isocalendar()) qryw = 'workoutid == {workoutid}'.format(workoutid=w.id) @@ -6872,7 +6685,7 @@ def get_zones_report(rower,startdate,enddate,trainingzones='hr',date_agg='week', if date_agg == 'week': dates.append(dd4) dates_sorting.append(dd4) - else: # pragma: no cover + else: # pragma: no cover dates.append(dd3) dates_sorting.append(dd3) minutes.append(timeinzone) @@ -6881,16 +6694,17 @@ def get_zones_report(rower,startdate,enddate,trainingzones='hr',date_agg='week', zones.append('<{ut2}'.format(ut2=hrzones[1])) else: zones.append('<{ut2}'.format(ut2=powerzones[1])) - #print(w,dd,timeinzone,'= enddate: # pragma: no cover + if startdate >= enddate: # pragma: no cover st = startdate startdate = enddate enddate = st @@ -7033,28 +6847,27 @@ def interactive_zoneschart(rower,data,startdate,enddate,trainingzones='hr',date_ totaldays = duration.total_seconds()/(24*3600) - colors = ['gray','yellow','lime','blue','purple','red'] + colors = ['gray', 'yellow', 'lime', 'blue', 'purple', 'red'] hrzones = rower.hrzones powerzones = rower.powerzones - color_map = { - '<{ut2}'.format(ut2=hrzones[1]):'green', - hrzones[1]:'lime', - hrzones[2]:'yellow', - hrzones[3]:'blue', - hrzones[4]:'purple', - hrzones[5]:'red', + '<{ut2}'.format(ut2=hrzones[1]): 'green', + hrzones[1]: 'lime', + hrzones[2]: 'yellow', + hrzones[3]: 'blue', + hrzones[4]: 'purple', + hrzones[5]: 'red', } if trainingzones == 'power': color_map = { - '<{ut2}'.format(ut2=powerzones[1]):'green', - powerzones[1]:'lime', - powerzones[2]:'yellow', - powerzones[3]:'blue', - powerzones[4]:'purple', - powerzones[5]:'red', + '<{ut2}'.format(ut2=powerzones[1]): 'green', + powerzones[1]: 'lime', + powerzones[2]: 'yellow', + powerzones[3]: 'blue', + powerzones[4]: 'purple', + powerzones[5]: 'red', } zones_order = [ @@ -7064,7 +6877,7 @@ def interactive_zoneschart(rower,data,startdate,enddate,trainingzones='hr',date_ hrzones[3], hrzones[4], hrzones[5] - ] + ] if trainingzones == 'power': zones_order = [ @@ -7074,24 +6887,22 @@ def interactive_zoneschart(rower,data,startdate,enddate,trainingzones='hr',date_ powerzones[3], powerzones[4], powerzones[5] - ] - + ] df = pd.DataFrame(data) df2 = pd.DataFrame(data) - df.drop('minutes',inplace=True,axis='columns') - - #df.drop('hours',inplace=True,axis='columns') + df.drop('minutes', inplace=True, axis='columns') + # df.drop('hours',inplace=True,axis='columns') source = ColumnDataSource(df) - df.sort_values('date_sorting',inplace=True) - df.drop('date_sorting',inplace=True,axis='columns') - df['totaltime'] = 0 - if df.empty: # pragma: no cover - return '','No Data Found' + df.sort_values('date_sorting', inplace=True) + df.drop('date_sorting', inplace=True, axis='columns') + df['totaltime'] = 0 + if df.empty: # pragma: no cover + return '', 'No Data Found' if yaxis == 'percentage': dates = list(set(df['date'].values)) @@ -7101,13 +6912,11 @@ def interactive_zoneschart(rower,data,startdate,enddate,trainingzones='hr',date_ totaltime = df.query(qry)['hours'].sum() mask = df['date'] == date - df.loc[mask,'totaltime'] = totaltime + df.loc[mask, 'totaltime'] = totaltime df['percentage'] = 100.*df['hours']/df['totaltime'] - df.drop('hours',inplace=True,axis='columns') - df.drop('totaltime',inplace=True,axis='columns') - - + df.drop('hours', inplace=True, axis='columns') + df.drop('totaltime', inplace=True, axis='columns') hv.extension('bokeh') @@ -7116,12 +6925,13 @@ def interactive_zoneschart(rower,data,startdate,enddate,trainingzones='hr',date_ if nrdates > 10: xrotation = 45 - bars = hv.Bars(df, kdims = ['date','zones']).aggregate(function=np.sum).redim.values(zones=zones_order) + bars = hv.Bars(df, kdims=['date', 'zones']).aggregate( + function=np.sum).redim.values(zones=zones_order) #bars = table.to.bars(['date','zones'],['minutes']) bars.opts( - opts.Bars(cmap=color_map,show_legend=True,stacked=True, - tools=['tap','hover'],width=550,padding=(0,(0,.1)), + opts.Bars(cmap=color_map, show_legend=True, stacked=True, + tools=['tap', 'hover'], width=550, padding=(0, (0, .1)), legend_position='bottom', xrotation=xrotation, show_frame=False) @@ -7130,21 +6940,21 @@ def interactive_zoneschart(rower,data,startdate,enddate,trainingzones='hr',date_ p = hv.render(bars) p.title.text = 'Activity {d1} to {d2} for {r}'.format( - d1 = startdate.strftime("%Y-%m-%d"), - d2 = enddate.strftime("%Y-%m-%d"), - r = str(rower), - ) + d1=startdate.strftime("%Y-%m-%d"), + d2=enddate.strftime("%Y-%m-%d"), + r=str(rower), + ) if date_agg == 'week': p.xaxis.axis_label = 'Week' - else: # pragma: no cover + else: # pragma: no cover p.xaxis.axis_label = 'Month' if yaxis == 'percentage': p.yaxis.axis_label = 'Percentage' - p.plot_width=550 - p.plot_height=350 + p.plot_width = 550 + p.plot_height = 350 p.toolbar_location = 'right' p.y_range.start = 0 p.sizing_mode = 'stretch_both' @@ -7153,15 +6963,15 @@ def interactive_zoneschart(rower,data,startdate,enddate,trainingzones='hr',date_ tidy_df = df2.groupby(['date']).sum() source2 = ColumnDataSource(tidy_df) - y2rangemax = tidy_df.loc[:,'hours'].max()*1.1 - p.extra_y_ranges["yax2"] = Range1d(start=0,end=y2rangemax) - p.line('date','hours',source=source2,y_range_name="yax2",color="black",width=5) - p.circle('date','hours',source=source2,y_range_name="yax2",color="black",size=10, + y2rangemax = tidy_df.loc[:, 'hours'].max()*1.1 + p.extra_y_ranges["yax2"] = Range1d(start=0, end=y2rangemax) + p.line('date', 'hours', source=source2, + y_range_name="yax2", color="black", width=5) + p.circle('date', 'hours', source=source2, y_range_name="yax2", color="black", size=10, legend_label='Hours') p.add_layout(LinearAxis(y_range_name="yax2", - axis_label='Hours'),'right') + axis_label='Hours'), 'right') + script, div = components(p) - script,div = components(p) - - return script,div + return script, div diff --git a/rowers/longtask.py b/rowers/longtask.py index 3c16c8ea..48a693f9 100644 --- a/rowers/longtask.py +++ b/rowers/longtask.py @@ -1,4 +1,9 @@ from __future__ import absolute_import +from rowsandall_app.settings_dev import SITE_URL as SITE_URL_DEV +from rowsandall_app.settings import SITE_URL +import requests +import threading +import redis from __future__ import division from __future__ import print_function from __future__ import unicode_literals @@ -7,20 +12,14 @@ from __future__ import absolute_import import numpy as np import time -from redis import StrictRedis,Redis +from redis import StrictRedis, Redis from celery import result as celery_result import json redis_connection = StrictRedis() -import redis -import threading -import requests -from rowsandall_app.settings import SITE_URL -from rowsandall_app.settings_dev import SITE_URL as SITE_URL_DEV - -def getvalue(data): # pragma: no cover +def getvalue(data): # pragma: no cover perc = 0 total = 1 done = 0 @@ -36,12 +35,11 @@ def getvalue(data): # pragma: no cover if i[0] == 'session_key': session_key = i[1] - return total,done,id,session_key + return total, done, id, session_key - -def longtask(aantal,jobid=None,debug=False, - session_key=None): # pragma: no cover +def longtask(aantal, jobid=None, debug=False, + session_key=None): # pragma: no cover counter = 0 channel = 'tasks' @@ -53,20 +51,19 @@ def longtask(aantal,jobid=None,debug=False, if debug: progress = 100.*i/aantal if jobid != None: - redis_connection.publish(channel,json.dumps( + redis_connection.publish(channel, json.dumps( { - 'done':i, - 'total':aantal, - 'id':jobid, - 'session_key':session_key, - } - )) - - + 'done': i, + 'total': aantal, + 'id': jobid, + 'session_key': session_key, + } + )) return 1 -def longtask2(aantal,jobid=None,debug=False,secret=''): # pragma: no cover + +def longtask2(aantal, jobid=None, debug=False, secret=''): # pragma: no cover counter = 0 channel = 'tasks' @@ -85,11 +82,10 @@ def longtask2(aantal,jobid=None,debug=False,secret=''): # pragma: no cover url = SITE_URL url += "/rowers/record-progress/" url += str(progress)+"/"+jobid - post_data = {"secret":secret} + post_data = {"secret": secret} s = requests.post(url, data=post_data) if debug: print(url) print(s) - return 1 diff --git a/rowers/metrics.py b/rowers/metrics.py index 3c88d392..7f68bc10 100644 --- a/rowers/metrics.py +++ b/rowers/metrics.py @@ -11,17 +11,17 @@ import pandas as pd from scipy import optimize from django.utils import timezone -from math import log10,log2 -from rowers.mytypes import otwtypes,otetypes +from math import log10, log2 +from rowers.mytypes import otwtypes, otetypes nometrics = [ 'originalvelo', -# 'cumdist', + # 'cumdist', 'strokes_slsh_min', ' WorkPerStroke (joules)', ' activityIdx', ' lapIdx', -# ' pointIdx', + # ' pointIdx', ' WorkoutType', ' IntervalType', ' WorkoutState', @@ -46,353 +46,353 @@ nometrics = [ 'Distance (IMP)', 'equivergpower', 'cum_dist.1' - ] +] rowingmetrics = ( - ('time',{ - 'numtype':'float', - 'null':True, + ('time', { + 'numtype': 'float', + 'null': True, 'verbose_name': 'Time', 'ax_min': 0, 'ax_max': 1e5, - 'mode':'both', + 'mode': 'both', 'type': 'basic', - 'group':'basic', + 'group': 'basic', 'maysmooth': False, 'sigfigs': -1}), - ('hr',{ - 'numtype':'integer', - 'null':True, + ('hr', { + 'numtype': 'integer', + 'null': True, 'verbose_name': 'Heart Rate (bpm)', 'ax_min': 100, 'ax_max': 200, - 'mode':'both', + 'mode': 'both', 'type': 'basic', 'maysmooth': False, - 'group':'athlete', - 'sigfigs':0,}), + 'group': 'athlete', + 'sigfigs': 0, }), - ('pace',{ - 'numtype':'float', - 'null':True, + ('pace', { + 'numtype': 'float', + 'null': True, 'verbose_name': 'Pace (/500m)', 'ax_min': 1.0e3*210, 'ax_max': 1.0e3*75, - 'mode':'both', + 'mode': 'both', 'type': 'basic', 'sigfigs': 1, 'maysmooth': True, 'group': 'basic'}), - ('velo',{ - 'numtype':'float', - 'null':True, + ('velo', { + 'numtype': 'float', + 'null': True, 'verbose_name': 'Boat Speed (m/s)', 'ax_min': 0, 'ax_max': 8, 'default': 0, - 'mode':'both', + 'mode': 'both', 'sigfigs': 1, 'maysmooth': True, - 'type':'pro', + 'type': 'pro', 'group': 'basic'}), -# ('powerhr',{ -# 'numtype':'float', -# 'null':True, -# 'verbose_name': 'Power Heart Rate Efficiency (J/beat)', -# 'ax_min':0, -# 'ax_max':300, -# 'mode':'both', -# 'type':'pro', -# 'group':'athlete'}), + # ('powerhr',{ + # 'numtype':'float', + # 'null':True, + # 'verbose_name': 'Power Heart Rate Efficiency (J/beat)', + # 'ax_min':0, + # 'ax_max':300, + # 'mode':'both', + # 'type':'pro', + # 'group':'athlete'}), - ('spm',{ - 'numtype':'float', - 'null':True, + ('spm', { + 'numtype': 'float', + 'null': True, 'verbose_name': 'Stroke Rate (spm)', 'ax_min': 15, 'ax_max': 45, - 'mode':'both', + 'mode': 'both', 'sigfigs': 1, 'type': 'basic', 'maysmooth': True, - 'group':'basic'}), + 'group': 'basic'}), - ('driveenergy',{ - 'numtype':'float', - 'null':True, + ('driveenergy', { + 'numtype': 'float', + 'null': True, 'verbose_name': 'Work Per Stroke (J)', 'ax_min': 0, 'ax_max': 1000, - 'mode':'both', + 'mode': 'both', 'sigfigs': 0, 'type': 'pro', 'maysmooth': True, - 'group':'forcepower'}), + 'group': 'forcepower'}), - ('power',{ - 'numtype':'float', - 'null':True, + ('power', { + 'numtype': 'float', + 'null': True, 'verbose_name': 'Power (W)', 'ax_min': 0, 'ax_max': 600, - 'mode':'both', + 'mode': 'both', 'sigfigs': 0, 'type': 'basic', 'maysmooth': True, - 'group':'forcepower'}), + 'group': 'forcepower'}), - ('averageforce',{ - 'numtype':'float', - 'null':True, + ('averageforce', { + 'numtype': 'float', + 'null': True, 'verbose_name': 'Average Drive Force (N)', 'ax_min': 0, 'ax_max': 1200, - 'mode':'both', + 'mode': 'both', 'sigfigs': 0, 'maysmooth': True, 'type': 'pro', - 'group':'forcepower'}), + 'group': 'forcepower'}), - ('peakforce',{ - 'numtype':'float', - 'null':True, + ('peakforce', { + 'numtype': 'float', + 'null': True, 'verbose_name': 'Peak Drive Force (N)', 'ax_min': 0, 'ax_max': 1500, 'sigfigs': 0, - 'mode':'both', + 'mode': 'both', 'maysmooth': True, 'type': 'pro', - 'group':'forcepower'}), + 'group': 'forcepower'}), - ('drivelength',{ - 'numtype':'float', - 'null':True, + ('drivelength', { + 'numtype': 'float', + 'null': True, 'verbose_name': 'Drive Length (m)', 'ax_min': 0, 'ax_max': 2.5, - 'mode':'rower', + 'mode': 'rower', 'sigfigs': 2, 'maysmooth': False, 'type': 'pro', - 'group':'stroke'}), + 'group': 'stroke'}), - ('forceratio',{ - 'numtype':'float', - 'null':True, + ('forceratio', { + 'numtype': 'float', + 'null': True, 'verbose_name': 'Average/Peak Force Ratio', 'ax_min': 0, 'ax_max': 1, 'sigfigs': 2, 'maysmooth': True, - 'mode':'both', + 'mode': 'both', 'type': 'pro', 'group': 'forcepower'}), - ('distance',{ - 'numtype':'float', - 'null':True, + ('distance', { + 'numtype': 'float', + 'null': True, 'verbose_name': 'Interval Distance (m)', 'ax_min': 0, 'ax_max': 1e5, 'sigfigs': 0, - 'mode':'both', + 'mode': 'both', 'maysmooth': False, 'type': 'basic', - 'group':'basic'}), + 'group': 'basic'}), - ('cumdist',{ - 'numtype':'float', - 'null':True, + ('cumdist', { + 'numtype': 'float', + 'null': True, 'verbose_name': 'Cumulative Distance (m)', 'ax_min': 0, 'ax_max': 1e5, - 'mode':'both', + 'mode': 'both', 'sigfigs': 0, 'maysmooth': False, 'type': 'basic', - 'group':'basic'}), + 'group': 'basic'}), - ('drivespeed',{ - 'numtype':'float', - 'null':True, + ('drivespeed', { + 'numtype': 'float', + 'null': True, 'verbose_name': 'Drive Speed (m/s)', 'ax_min': 0, 'ax_max': 4, 'default': 0, 'sigfigs': 2, 'maysmooth': True, - 'mode':'both', + 'mode': 'both', 'type': 'pro', 'group': 'stroke'}), - ('catch',{ - 'numtype':'float', - 'null':True, + ('catch', { + 'numtype': 'float', + 'null': True, 'verbose_name': 'Catch Angle (degrees)', 'ax_min': -40, 'ax_max': -75, 'default': 0, 'sigfigs': 0, - 'mode':'water', + 'mode': 'water', 'maysmooth': True, 'type': 'pro', 'group': 'stroke'}), - ('slip',{ - 'numtype':'float', - 'null':True, + ('slip', { + 'numtype': 'float', + 'null': True, 'verbose_name': 'Slip (degrees)', 'ax_min': 0, 'ax_max': 20, 'default': 0, 'sigfigs': 1, 'maysmooth': True, - 'mode':'water', + 'mode': 'water', 'type': 'pro', 'group': 'stroke'}), - ('finish',{ - 'numtype':'float', - 'null':True, + ('finish', { + 'numtype': 'float', + 'null': True, 'verbose_name': 'Finish Angle (degrees)', 'ax_min': 20, 'ax_max': 55, 'default': 0, 'sigfigs': 0, 'maysmooth': True, - 'mode':'water', + 'mode': 'water', 'type': 'pro', 'group': 'stroke'}), - ('wash',{ - 'numtype':'float', - 'null':True, + ('wash', { + 'numtype': 'float', + 'null': True, 'verbose_name': 'Wash (degrees)', 'ax_min': 0, 'ax_max': 30, 'default': 0, 'sigfigs': 1, 'maysmooth': True, - 'mode':'water', + 'mode': 'water', 'type': 'pro', 'group': 'stroke'}), - ('peakforceangle',{ - 'numtype':'float', - 'null':True, + ('peakforceangle', { + 'numtype': 'float', + 'null': True, 'verbose_name': 'Peak Force Angle', 'ax_min': -50, 'ax_max': 50, 'default': 0, 'sigfigs': 0, 'maysmooth': True, - 'mode':'water', + 'mode': 'water', 'type': 'pro', - 'group':'stroke'}), + 'group': 'stroke'}), - ('totalangle',{ - 'numtype':'float', - 'null':True, + ('totalangle', { + 'numtype': 'float', + 'null': True, 'verbose_name': 'Drive Length (deg)', 'ax_min': 40, 'ax_max': 140, 'default': 0, 'sigfigs': 0, 'maysmooth': True, - 'mode':'water', + 'mode': 'water', 'type': 'pro', - 'group':'stroke'}), + 'group': 'stroke'}), - ('effectiveangle',{ - 'numtype':'float', - 'null':True, + ('effectiveangle', { + 'numtype': 'float', + 'null': True, 'verbose_name': 'Effective Drive Length (deg)', 'ax_min': 40, 'ax_max': 140, 'default': 0, 'sigfigs': 0, - 'mode':'water', + 'mode': 'water', 'maysmooth': True, 'type': 'pro', - 'group':'stroke'}), + 'group': 'stroke'}), - ('rhythm',{ - 'numtype':'float', - 'null':True, + ('rhythm', { + 'numtype': 'float', + 'null': True, 'verbose_name': 'Stroke Rhythm', 'ax_min': 20, 'ax_max': 55, 'default': 1.0, 'sigfigs': 0, - 'mode':'erg', + 'mode': 'erg', 'maysmooth': True, 'type': 'pro', - 'group':'stroke'}), + 'group': 'stroke'}), - ('efficiency',{ - 'numtype':'float', - 'null':True, + ('efficiency', { + 'numtype': 'float', + 'null': True, 'verbose_name': 'OTW Efficiency (%)', 'ax_min': 0, 'ax_max': 110, 'default': 0, 'sigfigs': 0, - 'mode':'water', + 'mode': 'water', 'maysmooth': True, 'type': 'pro', - 'group':'forcepower'}), + 'group': 'forcepower'}), - ('distanceperstroke',{ - 'numtype':'float', - 'null':True, + ('distanceperstroke', { + 'numtype': 'float', + 'null': True, 'verbose_name': 'Distance per Stroke (m)', 'ax_min': 0, 'ax_max': 15, 'default': 0, 'sigfigs': 1, 'maysmooth': True, - 'mode':'both', + 'mode': 'both', 'type': 'basic', - 'group':'basic'}), + 'group': 'basic'}), - ) +) metricsdicts = {} -for key,dict in rowingmetrics: +for key, dict in rowingmetrics: metricsdicts[key] = dict - -metricsgroups = list(set([d['group'] for n,d in rowingmetrics])) +metricsgroups = list(set([d['group'] for n, d in rowingmetrics])) dtypes = {} -for name,d in rowingmetrics: +for name, d in rowingmetrics: if d['numtype'] == 'float': dtypes[name] = float - elif d['numtype'] == 'int': # pragma: no cover + elif d['numtype'] == 'int': # pragma: no cover dtypes[name] = int axesnew = [ - (name,d['verbose_name'],d['ax_min'],d['ax_max'],d['type']) for name,d in rowingmetrics - ] + (name, d['verbose_name'], d['ax_min'], d['ax_max'], d['type']) for name, d in rowingmetrics +] -axes = tuple(axesnew+[('None','',0,1,'basic')]) +axes = tuple(axesnew+[('None', '', 0, 1, 'basic')]) -axlabels = {ax[0]:ax[1] for ax in axes} +axlabels = {ax[0]: ax[1] for ax in axes} -yaxminima = {ax[0]:ax[2] for ax in axes} +yaxminima = {ax[0]: ax[2] for ax in axes} -yaxmaxima = {ax[0]:ax[3] for ax in axes} +yaxmaxima = {ax[0]: ax[3] for ax in axes} -def get_yaxminima(r,metric,mode): + +def get_yaxminima(r, metric, mode): if metric == 'pace': if mode in otetypes: return r.slowpaceerg @@ -401,7 +401,8 @@ def get_yaxminima(r,metric,mode): return yaxminima[metric] -def get_yaxmaxima(r,metric,mode): + +def get_yaxmaxima(r, metric, mode): if metric == 'pace': if mode in otetypes: return r.fastpaceerg @@ -411,75 +412,73 @@ def get_yaxmaxima(r,metric,mode): return yaxmaxima[metric] - defaultfavoritecharts = ( { - 'yparam1':'pace', - 'yparam2':'spm', - 'xparam':'time', - 'plottype':'line', - 'workouttype':'both', - 'reststrokes':True, - 'notes':"""This chart shows your pace and stroke rate versus time. """, - }, + 'yparam1': 'pace', + 'yparam2': 'spm', + 'xparam': 'time', + 'plottype': 'line', + 'workouttype': 'both', + 'reststrokes': True, + 'notes': """This chart shows your pace and stroke rate versus time. """, + }, { - 'yparam1':'pace', - 'yparam2':'hr', - 'xparam':'time', - 'plottype':'line', - 'workouttype':'both', - 'reststrokes':True, - 'notes':"""This chart shows your pace and heart rate versus time. + 'yparam1': 'pace', + 'yparam2': 'hr', + 'xparam': 'time', + 'plottype': 'line', + 'workouttype': 'both', + 'reststrokes': True, + 'notes': """This chart shows your pace and heart rate versus time. Heart rate values will be shown only when it is in your data, i.e. in case you recorded your heart rate during your workout""", - }, + }, { - 'yparam1':'distanceperstroke', - 'yparam2':'hr', - 'xparam':'time', - 'plottype':'line', - 'workouttype':'otw', - 'reststrokes':True, - 'notes':"""This chart shows the Distance covered per stroke, and your + 'yparam1': 'distanceperstroke', + 'yparam2': 'hr', + 'xparam': 'time', + 'plottype': 'line', + 'workouttype': 'otw', + 'reststrokes': True, + 'notes': """This chart shows the Distance covered per stroke, and your heart rate versus time. """, - }, + }, { - 'yparam1':'driveenergy', - 'yparam2':'hr', - 'xparam':'time', - 'plottype':'line', - 'workouttype':'ote', - 'reststrokes':True, - 'notes':"""This chart shows the Work per Stroke and your heart rate + 'yparam1': 'driveenergy', + 'yparam2': 'hr', + 'xparam': 'time', + 'plottype': 'line', + 'workouttype': 'ote', + 'reststrokes': True, + 'notes': """This chart shows the Work per Stroke and your heart rate plotted versus time. """, - }, + }, { - 'yparam1':'distanceperstroke', - 'yparam2':'None', - 'xparam':'spm', - 'plottype':'scatter', - 'workouttype':'otw', - 'reststrokes':True, - 'notes':"""This chart shows the Distance per Stroke versus stroke + 'yparam1': 'distanceperstroke', + 'yparam2': 'None', + 'xparam': 'spm', + 'plottype': 'scatter', + 'workouttype': 'otw', + 'reststrokes': True, + 'notes': """This chart shows the Distance per Stroke versus stroke stroke rate. You should see a steady decline of the Distance per Stroke as you increase stroke rate. Typical values are > 10m for steady state dropping to 8m for race pace in the single.""", - }, + }, { - 'yparam1':'driveenergy', - 'yparam2':'None', - 'xparam':'spm', - 'plottype':'scatter', - 'workouttype':'ote', - 'reststrokes':True, - 'notes':"""This chart shows the Work per Stroke versus Stroke Rate. + 'yparam1': 'driveenergy', + 'yparam2': 'None', + 'xparam': 'spm', + 'plottype': 'scatter', + 'workouttype': 'ote', + 'reststrokes': True, + 'notes': """This chart shows the Work per Stroke versus Stroke Rate. This value should be fairly constant across all stroke rates.""", - }, - ) + }, +) - -def calc_trimp(df,sex,hrmax,hrmin,hrftp): # pragma: no cover +def calc_trimp(df, sex, hrmax, hrmin, hrftp): # pragma: no cover if sex == 'male': f = 1.92 else: @@ -496,4 +495,4 @@ def calc_trimp(df,sex,hrmax,hrmin,hrftp): # pragma: no cover hrtss = 100*trimp/trimp1hr - return trimp,hrtss + return trimp, hrtss diff --git a/rowers/middleware.py b/rowers/middleware.py index 08a6d564..7c59b0c3 100644 --- a/rowers/middleware.py +++ b/rowers/middleware.py @@ -1,3 +1,8 @@ +from django.shortcuts import redirect +from django.http import HttpResponse +from django.contrib import messages +from rowers.mytypes import otwtypes +from rowers.tasks import handle_sendemail_expired from django.utils import timezone from rowers.models import Workout, PowerTimeFitnessMetric, Rower, PaidPlan import datetime @@ -5,10 +10,7 @@ from rowers.utils import myqueue import django_rq queue = django_rq.get_queue('default') queuelow = django_rq.get_queue('low') -from rowers.tasks import handle_sendemail_expired -from rowers.mytypes import otwtypes -from django.contrib import messages -from django.http import HttpResponse + def getrower(user): try: @@ -19,7 +21,6 @@ def getrower(user): return r -from django.shortcuts import redirect allowed_paths = [ '/rowers/me/delete', @@ -44,10 +45,11 @@ class SurveyMiddleWare(object): if request.user.is_authenticated and request.path not in allowed_paths: r = getrower(request.user) nexturl = request.path - if 'survey' in nexturl: # pragma: no cover + if 'survey' in nexturl: # pragma: no cover nexturl = '/rowers/list-workouts' - mustseesurvey = request.user.date_joined <= timezone.now()-datetime.timedelta(days=14) and not r.surveydone - if mustseesurvey: # pragma: no cover + mustseesurvey = request.user.date_joined <= timezone.now( + )-datetime.timedelta(days=14) and not r.surveydone + if mustseesurvey: # pragma: no cover return redirect( '/rowers/survey/?next=%s' % nexturl ) @@ -56,6 +58,7 @@ class SurveyMiddleWare(object): return response + class GDPRMiddleWare(object): def __init__(self, get_response): self.get_response = get_response @@ -64,7 +67,7 @@ class GDPRMiddleWare(object): if request.user.is_authenticated and request.path not in allowed_paths: r = getrower(request.user) nexturl = request.path - if 'optin' in nexturl: # pragma: no cover + if 'optin' in nexturl: # pragma: no cover nexturl = '/rowers/list-workouts' if not r.gdproptin: return redirect( @@ -75,20 +78,21 @@ class GDPRMiddleWare(object): return response + class RowerPlanMiddleWare(object): def __init__(self, get_response): self.get_response = get_response def __call__(self, request): - if request.user.is_authenticated and request.user.rower.rowerplan not in ['basic','freecoach']: + if request.user.is_authenticated and request.user.rower.rowerplan not in ['basic', 'freecoach']: if request.user.rower.paymenttype == 'single': - if request.user.rower.planexpires < timezone.now().date(): # pragma: no cover + if request.user.rower.planexpires < timezone.now().date(): # pragma: no cover messg = 'Your paid plan has expired. We have reset you to a free basic plan.' - messages.error(request,messg) + messages.error(request, messg) r = getrower(request.user) r.rowerplan = 'basic' r.paymenttype = 'single' - basicplans = PaidPlan.objects.filter(shortname='basic',price=0, + basicplans = PaidPlan.objects.filter(shortname='basic', price=0, paymentprocessor='braintree') r.paidplan = basicplans[0] r.save() diff --git a/rowers/models.py b/rowers/models.py index ff201d6e..6bb4d986 100644 --- a/rowers/models.py +++ b/rowers/models.py @@ -1,23 +1,31 @@ -from __future__ import absolute_import -from __future__ import division -from __future__ import print_function -from __future__ import unicode_literals -from __future__ import unicode_literals, absolute_import +from django.core.validators import RegexValidator, validate_email +from rowers.metrics import rowingmetrics +from django.db.models.signals import m2m_changed +from rowers.courseutils import coordinate_in_path +from rowers.utils import ( + workflowleftpanel, workflowmiddlepanel, + defaultleft, defaultmiddle, landingpages, landingpages2, + steps_read_fit, steps_write_fit, ps_dict_order +) +from rowers.metrics import axlabels +from rowers.utils import geo_distance +from rowers.formfields import * +from rowers.database import * import uuid -from django.db import models,IntegrityError +from django.db import models, IntegrityError from django.contrib.auth.models import User from django.core.validators import validate_email from django.core.exceptions import ValidationError from django import forms from django.forms import ModelForm from django.dispatch import receiver -from django.forms.widgets import SplitDateTimeWidget,SelectDateWidget +from django.forms.widgets import SplitDateTimeWidget, SelectDateWidget #from django.forms.extras.widgets import SelectDateWidget from django.forms.formsets import BaseFormSet #from datetimewidget.widgets import DateTimeWidget -from django.contrib.admin.widgets import AdminDateWidget,AdminTimeWidget,AdminSplitDateTime +from django.contrib.admin.widgets import AdminDateWidget, AdminTimeWidget, AdminSplitDateTime from django.core.validators import validate_email import os import json @@ -27,7 +35,7 @@ import pytz from django_countries.fields import CountryField import tempfile -from scipy.interpolate import splprep, splev, CubicSpline,interp1d +from scipy.interpolate import splprep, splev, CubicSpline, interp1d import numpy as np @@ -64,8 +72,7 @@ from rowsandall_app.settings import ( TWEET_ACCESS_TOKEN_SECRET, TWEET_CONSUMER_KEY, TWEET_CONSUMER_SECRET, - ) - +) # END PERMISSIONS @@ -75,54 +82,56 @@ tweetapi = twitter.Api(consumer_key=TWEET_CONSUMER_KEY, access_token_key=TWEET_ACCESS_TOKEN_KEY, access_token_secret=TWEET_ACCESS_TOKEN_SECRET) -from rowers.database import * -from rowers.formfields import * timezones = ( - (x,x) for x in pytz.common_timezones + (x, x) for x in pytz.common_timezones ) favanalysischoices = ( - ('compare','Compare'), - ('flexall','Cumulative Flex Chart'), - ('histogram','Histogram'), - ('stats','Statistics'), - ('boxplot','Box Chart'), - ('trendflex','Trend Flex'), - ('cp','Critical Power'), + ('compare', 'Compare'), + ('flexall', 'Cumulative Flex Chart'), + ('histogram', 'Histogram'), + ('stats', 'Statistics'), + ('boxplot', 'Box Chart'), + ('trendflex', 'Trend Flex'), + ('cp', 'Critical Power'), ) smoothingchoices = ( - (1,1), - (2,2), - (4,4), - (8,8), - (16,16), + (1, 1), + (2, 2), + (4, 4), + (8, 8), + (16, 16), ) + def half_year_from_now(ttz=None): if ttz is None: return (datetime.datetime.now(tz=timezone.utc)+timezone.timedelta(days=182)).date() - return (datetime.datetime.utcnow()+timezone.timedelta(days=182)).astimezone(pytz.timezone(ttz)).date() # pragma: no cover + return (datetime.datetime.utcnow()+timezone.timedelta(days=182)).astimezone(pytz.timezone(ttz)).date() # pragma: no cover + def a_week_from_now(ttz=None): if ttz is None: return (datetime.datetime.now(tz=timezone.utc)+timezone.timedelta(days=7)).date() - return (datetime.datetime.utcnow()+timezone.timedelta(days=7)).astimezone(pytz.timezone(ttz)).date() # pragma: no cover + return (datetime.datetime.utcnow()+timezone.timedelta(days=7)).astimezone(pytz.timezone(ttz)).date() # pragma: no cover + def current_day(ttz=None): if ttz is None: return (datetime.datetime.now(tz=timezone.utc)).date() - return datetime.datetime.utcnow().astimezone(pytz.timezone(ttz)).date() # pragma: no cover + return datetime.datetime.utcnow().astimezone(pytz.timezone(ttz)).date() # pragma: no cover -def current_time(ttz=None): # pragma: no cover + +def current_time(ttz=None): # pragma: no cover if ttz is None: return datetime.datetime.now(tz=timezone.utc) - return (datetime.datetime.utcnow()).astimezone(pytz.timezone(ttz)) # pragma: no cover + return (datetime.datetime.utcnow()).astimezone(pytz.timezone(ttz)) # pragma: no cover class UserFullnameChoiceField(forms.ModelChoiceField): - def label_from_instance(self,obj): + def label_from_instance(self, obj): return obj.get_full_name() @@ -133,80 +142,88 @@ def get_file_path(instance, filename): # return os.path.join(settings.MEDIA_ROOT, filename) # model for configurable template field + + class TemplateListField(models.TextField): def __init__(self, *args, **kwargs): - self.token = kwargs.pop('token',',') + self.token = kwargs.pop('token', ',') super(TemplateListField, self).__init__(*args, **kwargs) - def to_python(self, value): # pragma: no cover - if not value: return + def to_python(self, value): # pragma: no cover + if not value: + return if isinstance(value, list): return value # remove double quotes and brackets - value = re.sub(r'u\"','',value) - value = re.sub(r'u\'','',value) - value = re.sub(r'\\','',value) - value = re.sub(r'\"','',value) - value = re.sub(r'\'','',value) - value = re.sub(r'\[','',value) - value = re.sub(r'\]','',value) - value = re.sub(r'\[\[','[',value) - value = re.sub(r'\]\]',']',value) - value = re.sub(r'\ \ ',' ',value) - value = re.sub(r', ',',',value) + value = re.sub(r'u\"', '', value) + value = re.sub(r'u\'', '', value) + value = re.sub(r'\\', '', value) + value = re.sub(r'\"', '', value) + value = re.sub(r'\'', '', value) + value = re.sub(r'\[', '', value) + value = re.sub(r'\]', '', value) + value = re.sub(r'\[\[', '[', value) + value = re.sub(r'\]\]', ']', value) + value = re.sub(r'\ \ ', ' ', value) + value = re.sub(r', ', ',', value) return value.split(self.token) - def from_db_value(self,value, expression, connection): - if value is None: # pragma: no cover + def from_db_value(self, value, expression, connection): + if value is None: # pragma: no cover return value - if isinstance(value, list): # pragma: no cover + if isinstance(value, list): # pragma: no cover return value return value.split(self.token) def get_db_prep_value(self, value, connection, prepared=False): - if not value: return + if not value: + return assert(isinstance(value, list) or isinstance(value, tuple)) return self.token.join([str(s) for s in value]) - def value_to_string(self, obj): # pragma: no cover + def value_to_string(self, obj): # pragma: no cover value = self._get_val_from_obj(obj) return self.get_deb_prep_value(value) # model for Emails field + + class AlternativeEmails(models.TextField): def __init__(self, *args, **kwargs): - self.token = kwargs.pop('token',',') - super(AlternativeEmails, self).__init__(*args,**kwargs) + self.token = kwargs.pop('token', ',') + super(AlternativeEmails, self).__init__(*args, **kwargs) - def to_python(self, value): # pragma: no cover - if not value: return + def to_python(self, value): # pragma: no cover + if not value: + return if isinstance(value, list): return value # remove double quotes and brackets - value = re.sub(r'u\"','',value) - value = re.sub(r'u\'','',value) - value = re.sub(r'\\','',value) - value = re.sub(r'\"','',value) - value = re.sub(r'\'','',value) - value = re.sub(r'\[','',value) - value = re.sub(r'\]','',value) - value = re.sub(r'\[\[','[',value) - value = re.sub(r'\]\]',']',value) - value = re.sub(r'\ \ ',' ',value) - value = re.sub(r', ',',',value) + value = re.sub(r'u\"', '', value) + value = re.sub(r'u\'', '', value) + value = re.sub(r'\\', '', value) + value = re.sub(r'\"', '', value) + value = re.sub(r'\'', '', value) + value = re.sub(r'\[', '', value) + value = re.sub(r'\]', '', value) + value = re.sub(r'\[\[', '[', value) + value = re.sub(r'\]\]', ']', value) + value = re.sub(r'\ \ ', ' ', value) + value = re.sub(r', ', ',', value) return value.split(self.token) - def from_db_value(self,value, expression, connection): + def from_db_value(self, value, expression, connection): if value is None: return value - if isinstance(value, list): # pragma: no cover + if isinstance(value, list): # pragma: no cover return value return value.split(self.token) def get_db_prep_value(self, value, connection, prepared=False): - if not value: return + if not value: + return assert(isinstance(value, list) or isinstance(value, tuple)) newlist = [] for s in value: @@ -214,111 +231,122 @@ class AlternativeEmails(models.TextField): try: validate_email(s) newlist.append(s) - except ValidationError: # pragma: no cover + except ValidationError: # pragma: no cover pass return self.token.join([str(s) for s in newlist]) - def value_to_string(self, obj): # pragma: no cover + def value_to_string(self, obj): # pragma: no cover value = self._get_val_from_obj(obj) return self.get_deb_prep_value(value) # model for Planned Session Steps + + class PlannedSessionStepField(models.TextField): def __init__(self, *args, **kwargs): super(PlannedSessionStepField, self).__init__(*args, **kwargs) - def to_python(self, value): # pragma: no cover - if not value: return + def to_python(self, value): # pragma: no cover + if not value: + return return json.loads(value) def from_db_value(self, value, expression, connection): - if not value: return + if not value: + return return json.loads(value) def get_db_prep_value(self, value, connection, prepared=False): - if not value: return + if not value: + return return json.dumps(value) - def value_to_string(self, obj): # pragma: no cover + def value_to_string(self, obj): # pragma: no cover value = self._get_val_from_obj(obj) return self.get_deb_prep_value(value) # model for Power Zone names + + class PowerZonesField(models.TextField): -# __metaclass__ = models.SubfieldBase + # __metaclass__ = models.SubfieldBase def __init__(self, *args, **kwargs): - self.token = kwargs.pop('token',',') + self.token = kwargs.pop('token', ',') super(PowerZonesField, self).__init__(*args, **kwargs) - def to_python(self, value): # pragma: no cover - if not value: return + def to_python(self, value): # pragma: no cover + if not value: + return if isinstance(value, list): return value # remove double quotes and brackets - value = re.sub(r'u\"','',value) - value = re.sub(r'u\'','',value) - value = re.sub(r'\\','',value) - value = re.sub(r'\"','',value) - value = re.sub(r'\'','',value) - value = re.sub(r'\[','',value) - value = re.sub(r'\]','',value) - value = re.sub(r'\[\[','[',value) - value = re.sub(r'\]\]',']',value) - value = re.sub(r'\ \ ',' ',value) - value = re.sub(r', ',',',value) + value = re.sub(r'u\"', '', value) + value = re.sub(r'u\'', '', value) + value = re.sub(r'\\', '', value) + value = re.sub(r'\"', '', value) + value = re.sub(r'\'', '', value) + value = re.sub(r'\[', '', value) + value = re.sub(r'\]', '', value) + value = re.sub(r'\[\[', '[', value) + value = re.sub(r'\]\]', ']', value) + value = re.sub(r'\ \ ', ' ', value) + value = re.sub(r', ', ',', value) return value.split(self.token) - def from_db_value(self,value, expression, connection): - if value is None: # pragma: no cover + def from_db_value(self, value, expression, connection): + if value is None: # pragma: no cover return value - if isinstance(value, list): # pragma: no cover + if isinstance(value, list): # pragma: no cover return value return value.split(self.token) def get_db_prep_value(self, value, connection, prepared=False): - if not value: return + if not value: + return assert(isinstance(value, list) or isinstance(value, tuple)) return self.token.join([str(s) for s in value]) - def value_to_string(self, obj): # pragma: no cover + def value_to_string(self, obj): # pragma: no cover value = self._get_val_from_obj(obj) return self.get_deb_prep_value(value) c2url = 'http://www.concept2.com/indoor-rowers/racing/records/world?machine=1&event=All&gender=All&age=All&weight=All' -def update_records(url=c2url,verbose=True): + +def update_records(url=c2url, verbose=True): try: - dfs = pd.read_html(url,attrs={'class':'views-table'}) + dfs = pd.read_html(url, attrs={'class': 'views-table'}) df = dfs[0] df.columns = df.columns.str.strip() success = 1 - except: # pragma: no cover + except: # pragma: no cover df = pd.DataFrame() if not df.empty: C2WorldClassAgePerformance.objects.all().delete() - df.Gender = df.Gender.apply(lambda x: 'male' if x=='M' else 'female') + df.Gender = df.Gender.apply(lambda x: 'male' if x == 'M' else 'female') df['Distance'] = df['Event'] df['Duration'] = 0 - for nr,row in df.iterrows(): + for nr, row in df.iterrows(): if 'm' in row['Record']: - df.loc[nr,'Distance'] = row['Record'][:-1] - df.loc[nr,'Duration'] = 60*row['Event'] + df.loc[nr, 'Distance'] = row['Record'][:-1] + df.loc[nr, 'Duration'] = 60*row['Event'] else: - df.loc[nr,'Distance'] = row['Event'] + df.loc[nr, 'Distance'] = row['Event'] try: - tobj = datetime.datetime.strptime(row['Record'],'%M:%S.%f') + tobj = datetime.datetime.strptime(row['Record'], '%M:%S.%f') except ValueError: - tobj = datetime.datetime.strptime(row['Record'],'%H:%M:%S.%f') - df.loc[nr,'Duration'] = 3600.*tobj.hour+60.*tobj.minute+tobj.second+tobj.microsecond/1.e6 + tobj = datetime.datetime.strptime(row['Record'], '%H:%M:%S.%f') + df.loc[nr, 'Duration'] = 3600.*tobj.hour+60. * \ + tobj.minute+tobj.second+tobj.microsecond/1.e6 - for nr,row in df.iterrows(): + for nr, row in df.iterrows(): try: weightcategory = row.Weight.lower() except AttributeError: @@ -335,27 +363,26 @@ def update_records(url=c2url,verbose=True): power = int(2.8*velo**3) record = C2WorldClassAgePerformance( - age = age, - weightcategory = weightcategory, + age=age, + weightcategory=weightcategory, sex=sex, - distance = distance, - duration = duration, - power = power, - season = season, - name = name, + distance=distance, + duration=duration, + power=power, + season=season, + name=name, ) try: - if verbose: # pragma: no cover + if verbose: # pragma: no cover print(record) record.save() except: - if verbose: # pragma: no cover - print(record,'*') + if verbose: # pragma: no cover + print(record, '*') else: pass - class CalcAgePerformance(models.Model): weightcategories = mytypes.weightcategories @@ -369,37 +396,39 @@ class CalcAgePerformance(models.Model): max_length=30, choices=sexcategories) - age = models.IntegerField(default=19,verbose_name="Age") + age = models.IntegerField(default=19, verbose_name="Age") - duration = models.FloatField(default=1,blank=True) + duration = models.FloatField(default=1, blank=True) power = models.IntegerField(default=200) class Meta: db_table = 'calcagegrouprecords' - def __str_(self): # pragma: no cover - stri = 'Calculated World Class Performance for {s}, {a}, {d} secs, {p} Watts'.format( - s = self.sex, - a = self.age, - d = self.duration, - p = self.power + def __str_(self): # pragma: no cover + stri = 'Calculated World Class Performance for {s}, {a}, {d} secs, {p} Watts'.format( + s=self.sex, + a=self.age, + d=self.duration, + p=self.power ) return stri + class PowerTimeFitnessMetric(models.Model): modechoices = ( - ('rower','Rower'), - ('water','On the water') - ) + ('rower', 'Rower'), + ('water', 'On the water') + ) date = models.DateField(default=current_day) last_workout = models.IntegerField(default=0) - user = models.ForeignKey(User,on_delete=models.CASCADE) + user = models.ForeignKey(User, on_delete=models.CASCADE) PowerFourMin = models.FloatField(default=0) PowerTwoK = models.FloatField(default=0) PowerOneHour = models.FloatField(default=0) - workoutmode = models.CharField(default='rower',choices=modechoices, + workoutmode = models.CharField(default='rower', choices=modechoices, max_length=41,) + class Meta: db_table = 'powertimefitnessmetric' @@ -408,9 +437,9 @@ class C2WorldClassAgePerformance(models.Model): weightcategories = mytypes.weightcategories sexcategories = ( - ('male','male'), - ('female','female'), - ) + ('male', 'male'), + ('female', 'female'), + ) weightcategory = models.CharField(default="hwt", max_length=30, @@ -420,52 +449,52 @@ class C2WorldClassAgePerformance(models.Model): max_length=30, choices=sexcategories) - age = models.IntegerField(default=19,verbose_name="Age") + age = models.IntegerField(default=19, verbose_name="Age") distance = models.IntegerField(default=2000) - name = models.CharField(max_length=200,blank=True) - duration = models.FloatField(default=1,blank=True) + name = models.CharField(max_length=200, blank=True) + duration = models.FloatField(default=1, blank=True) season = models.IntegerField(default=2013) power = models.IntegerField(default=200) class Meta: - unique_together = ('age','sex','weightcategory','distance') + unique_together = ('age', 'sex', 'weightcategory', 'distance') - def __str__(self): # pragma: no cover + def __str__(self): # pragma: no cover thestring = '{s} {w} {n} age {a} ({season}) {distance}m {duration} seconds'.format( - s = self.sex, - w = self.weightcategory, - n = self.name, - a = self.age, - season = self.season, - distance = self.distance, - duration = self.duration, - ) + s=self.sex, + w=self.weightcategory, + n=self.name, + a=self.age, + season=self.season, + distance=self.distance, + duration=self.duration, + ) return thestring - class Team(models.Model): choices = ( - ('private','private'), - ('open','open'), - ) + ('private', 'private'), + ('open', 'open'), + ) viewchoices = ( - ('coachonly','Coach Only'), - ('allmembers','All Members') - ) + ('coachonly', 'Coach Only'), + ('allmembers', 'All Members') + ) - name = models.CharField(max_length=150,unique=True,verbose_name='Team Name') - notes = models.CharField(blank=True,max_length=200,verbose_name='Team Purpose') - manager = models.ForeignKey(User, null=True,on_delete=models.CASCADE) - private = models.CharField(max_length=30,choices=choices,default='open', + name = models.CharField(max_length=150, unique=True, + verbose_name='Team Name') + notes = models.CharField(blank=True, max_length=200, + verbose_name='Team Purpose') + manager = models.ForeignKey(User, null=True, on_delete=models.CASCADE) + private = models.CharField(max_length=30, choices=choices, default='open', verbose_name='Team Type') - viewing = models.CharField(max_length=30,choices=viewchoices,default='allmembers',verbose_name='Sharing Behavior') - - + viewing = models.CharField(max_length=30, choices=viewchoices, + default='allmembers', verbose_name='Sharing Behavior') def __str__(self): return self.name @@ -473,58 +502,52 @@ class Team(models.Model): def save(self, *args, **kwargs): manager = self.manager if not can_have_teams(manager): - raise ValidationError( - "Basic user cannot be team manager" - ) + raise ValidationError( + "Basic user cannot be team manager" + ) if not self.id: # new model instance if not can_add_team(manager): raise ValidationError( "Pro and Self-Coach users cannot have more than one team" - ) + ) - super(Team, self).save(*args,**kwargs) + super(Team, self).save(*args, **kwargs) class TeamForm(ModelForm): class Meta: model = Team - fields = ['name','notes','private','viewing'] + fields = ['name', 'notes', 'private', 'viewing'] widgets = { 'notes': forms.Textarea, - } + } + class TeamInvite(models.Model): - team = models.ForeignKey(Team,on_delete=models.CASCADE) - user = models.ForeignKey(User,null=True,on_delete=models.CASCADE) + team = models.ForeignKey(Team, on_delete=models.CASCADE) + user = models.ForeignKey(User, null=True, on_delete=models.CASCADE) issuedate = models.DateField(default=current_day) - code = models.CharField(max_length=150,unique=True) - email = models.CharField(max_length=150,null=True,blank=True) + code = models.CharField(max_length=150, unique=True) + email = models.CharField(max_length=150, null=True, blank=True) + class TeamInviteForm(ModelForm): - user = UserFullnameChoiceField(queryset=User.objects.all(),required=False) + user = UserFullnameChoiceField(queryset=User.objects.all(), required=False) email = forms.EmailField(required=False) + class Meta: model = TeamInvite - fields = ['user','email'] - - + fields = ['user', 'email'] class TeamRequest(models.Model): - team = models.ForeignKey(Team,on_delete=models.CASCADE) - user = models.ForeignKey(User,null=True,on_delete=models.CASCADE) + team = models.ForeignKey(Team, on_delete=models.CASCADE) + user = models.ForeignKey(User, null=True, on_delete=models.CASCADE) issuedate = models.DateField(default=current_day) - code = models.CharField(max_length=150,unique=True) + code = models.CharField(max_length=150, unique=True) -from rowers.utils import ( - workflowleftpanel,workflowmiddlepanel, - defaultleft,defaultmiddle,landingpages,landingpages2, - steps_read_fit, steps_write_fit, ps_dict_order - ) - -from rowers.utils import geo_distance def polygon_coord_center(polygon): @@ -536,18 +559,16 @@ def polygon_coord_center(polygon): return latitudes.mean(), longitudes.mean() - def polygon_to_path(polygon): points = GeoPoint.objects.filter(polygon=polygon).order_by("order_in_poly") s = [] for point in points: - s.append([point.latitude,point.longitude]) + s.append([point.latitude, point.longitude]) p = path.Path(s[:-1]) return p -from rowers.courseutils import coordinate_in_path def course_spline(coordinates): latitudes = coordinates['latitude'].values @@ -555,18 +576,18 @@ def course_spline(coordinates): # spline parameters s = 1.0 - k = min([5,len(latitudes)-1]) + k = min([5, len(latitudes)-1]) nest = -1 - t = np.linspace(0,1,len(latitudes)) - tnew = np.linspace(0,1,100) + t = np.linspace(0, 1, len(latitudes)) + tnew = np.linspace(0, 1, 100) try: #latnew = CubicSpline(t,latitudes,bc_type='not-a-knot')(tnew) #lonnew = CubicSpline(t,longitudes,bc_type='not-a-knot')(tnew) - latnew = interp1d(t,latitudes)(tnew) - lonnew = interp1d(t,longitudes)(tnew) - except ValueError: # pragma: no cover + latnew = interp1d(t, latitudes)(tnew) + lonnew = interp1d(t, longitudes)(tnew) + except ValueError: # pragma: no cover latnew = latitudes lonnew = longitudes @@ -577,45 +598,47 @@ def course_spline(coordinates): # tnew,latnew,lonnew = splev(np.linspace(0,1,100),tckp) newcoordinates = pd.DataFrame({ - 'latitude':latnew, - 'longitude':lonnew, - }) + 'latitude': latnew, + 'longitude': lonnew, + }) return newcoordinates + def course_coord_center(course): - polygons = GeoPolygon.objects.filter(course=course).order_by("order_in_course") + polygons = GeoPolygon.objects.filter( + course=course).order_by("order_in_course") latitudes = [] longitudes = [] for p in polygons: - latitude,longitude = polygon_coord_center(p) + latitude, longitude = polygon_coord_center(p) latitudes.append(latitude) longitudes.append(longitude) latitude = pd.Series(latitudes).median() longitude = pd.Series(longitudes).median() - coordinates = pd.DataFrame({ - 'latitude':latitudes, - 'longitude':longitudes, - }) + 'latitude': latitudes, + 'longitude': longitudes, + }) + return latitude, longitude, coordinates - return latitude,longitude,coordinates def course_coord_maxmin(course): - polygons = GeoPolygon.objects.filter(course=course).order_by("order_in_course") + polygons = GeoPolygon.objects.filter( + course=course).order_by("order_in_course") latitudes = [] longitudes = [] for p in polygons: - latitude,longitude = polygon_coord_center(p) + latitude, longitude = polygon_coord_center(p) latitudes.append(latitude) longitudes.append(longitude) @@ -624,100 +647,105 @@ def course_coord_maxmin(course): long_min = pd.Series(longitudes).min() long_max = pd.Series(longitudes).max() + return lat_min, lat_max, long_min, long_max - return lat_min,lat_max,long_min,long_max -def get_dir_vector(polygon1,polygon2): - lat1,lon1 = polygon_coord_center(polygon1) - lat2,lon2 = polygon_coord_center(polygon2) +def get_dir_vector(polygon1, polygon2): + lat1, lon1 = polygon_coord_center(polygon1) + lat2, lon2 = polygon_coord_center(polygon2) - return [lat2-lat1,lon2-lon1] + return [lat2-lat1, lon2-lon1] -def get_delta(vector,polygon): + +def get_delta(vector, polygon): x = pd.Series(range(10000))/9999. vlat = vector[0] vlon = vector[1] - lat1,lon1 = polygon_coord_center(polygon) + lat1, lon1 = polygon_coord_center(polygon) - lat = x.apply(lambda x:lat1+x*vlat) - lon = x.apply(lambda x:lon1+x*vlon) + lat = x.apply(lambda x: lat1+x*vlat) + lon = x.apply(lambda x: lon1+x*vlon) - totdist,bearing = geo_distance(lat1,lon1,lat1+vlat,lon1+vlon) + totdist, bearing = geo_distance(lat1, lon1, lat1+vlat, lon1+vlon) dist = x*totdist p = polygon_to_path(polygon) - f = lambda x: coordinate_in_path(x['lat'],x['lon'],p) + def f(x): return coordinate_in_path(x['lat'], x['lon'], p) - df = pd.DataFrame({'x':x, - 'lat':lat, - 'lon':lon, - 'dist':dist, + df = pd.DataFrame({'x': x, + 'lat': lat, + 'lon': lon, + 'dist': dist, }) - df['inpolygon'] = df.apply(f,axis=1) - + df['inpolygon'] = df.apply(f, axis=1) b = (~df['inpolygon']).shift(-1)+df['inpolygon'] + if len(df[b == 2]): + return 1.0e3*df[b == 2]['dist'].min() - if len(df[b==2]): - return 1.0e3*df[b==2]['dist'].min() - - else: # pragma: no cover + else: # pragma: no cover return 0 -def get_delta_start(course): # pragma: no cover - polygons = GeoPolygon.objects.filter(course=course).order_by("order_in_course") - vector = get_dir_vector(polygons[0],polygons[1]) - delta = get_delta(vector,polygons[0]) +def get_delta_start(course): # pragma: no cover + polygons = GeoPolygon.objects.filter( + course=course).order_by("order_in_course") + vector = get_dir_vector(polygons[0], polygons[1]) + delta = get_delta(vector, polygons[0]) return delta -def get_delta_finish(course): # pragma: no cover - polygons = GeoPolygon.objects.filter(course=course).order_by("-order_in_course") - vector = get_dir_vector(polygons[0],polygons[1]) - delta = get_delta(vector,polygons[0]) + +def get_delta_finish(course): # pragma: no cover + polygons = GeoPolygon.objects.filter( + course=course).order_by("-order_in_course") + vector = get_dir_vector(polygons[0], polygons[1]) + delta = get_delta(vector, polygons[0]) return delta + def course_length(course): - polygons = GeoPolygon.objects.filter(course=course).order_by("order_in_course") + polygons = GeoPolygon.objects.filter( + course=course).order_by("order_in_course") totaldist = 0 - if not polygons: # pragma: no cover + if not polygons: # pragma: no cover return 0 for i in range(polygons.count()-1): - latitude1,longitude1 = polygon_coord_center(polygons[i]) - latitude2,longitude2 = polygon_coord_center(polygons[i+1]) + latitude1, longitude1 = polygon_coord_center(polygons[i]) + latitude2, longitude2 = polygon_coord_center(polygons[i+1]) - dist = geo_distance(latitude1,longitude1, - latitude2,longitude2,) + dist = geo_distance(latitude1, longitude1, + latitude2, longitude2,) totaldist += 1000.*dist[0] try: - vector = get_dir_vector(polygons[0],polygons[1]) - deltastart = get_delta(vector,polygons[0]) + vector = get_dir_vector(polygons[0], polygons[1]) + deltastart = get_delta(vector, polygons[0]) polygons = polygons.reverse() - vector = get_dir_vector(polygons[0],polygons[1]) - deltafinish = get_delta(vector,polygons[0]) - except IndexError: # pragma: no cover + vector = get_dir_vector(polygons[0], polygons[1]) + deltafinish = get_delta(vector, polygons[0]) + except IndexError: # pragma: no cover deltastart = 0 deltafinish = 0 return int(totaldist-deltastart-deltafinish) + sexcategories = ( - ('male','male'), - ('female','female'), - ('not specified','not specified'), + ('male', 'male'), + ('female', 'female'), + ('not specified', 'not specified'), ) weightcategories = mytypes.weightcategories @@ -725,33 +753,34 @@ weightcategories = mytypes.weightcategories # Plan plans = ( - ('basic','basic'), - ('pro','pro'), - ('plan','plan'), - ('coach','coach'), - ('freecoach','freecoach'), + ('basic', 'basic'), + ('pro', 'pro'), + ('plan', 'plan'), + ('coach', 'coach'), + ('freecoach', 'freecoach'), ) paymenttypes = ( - ('single','single'), - ('recurring','recurring') + ('single', 'single'), + ('recurring', 'recurring') ) paymentprocessors = ( - ('paypal','PayPal'), - ('braintree','BrainTree') - ) + ('paypal', 'PayPal'), + ('braintree', 'BrainTree') +) class PaidPlan(models.Model): - shortname = models.CharField(max_length=50,choices=plans) + shortname = models.CharField(max_length=50, choices=plans) name = models.CharField(max_length=200) - external_id = models.CharField(blank=True,null=True,default=None,max_length=200) - price = models.FloatField(blank=True,null=True,default=None) + external_id = models.CharField( + blank=True, null=True, default=None, max_length=200) + price = models.FloatField(blank=True, null=True, default=None) paymentprocessor = models.CharField( - max_length=50,choices=paymentprocessors,default='braintree') + max_length=50, choices=paymentprocessors, default='braintree') paymenttype = models.CharField( - default='single',max_length=30, + default='single', max_length=30, verbose_name='Payment Type', choices=paymenttypes, ) @@ -762,22 +791,23 @@ class PaidPlan(models.Model): def __str__(self): return '{name} - {shortname} at {price:.2f} EURO ({paymenttype} payment)'.format( - name = self.name, - shortname = self.shortname, - price = self.price, - paymenttype = self.paymenttype, - paymentprocessor = self.paymentprocessor, - ) + name=self.name, + shortname=self.shortname, + price=self.price, + paymenttype=self.paymenttype, + paymentprocessor=self.paymentprocessor, + ) class CoachingGroup(models.Model): - name = models.CharField(default='group',max_length=30,null=True,blank=True) + name = models.CharField( + default='group', max_length=30, null=True, blank=True) - def __str__(self): # pragma: no cover + def __str__(self): # pragma: no cover return 'Coaching Group {id}: {name}'.format( - id = self.pk, - name = self.name - ) + id=self.pk, + name=self.name + ) def __len__(self): rs = Rower.objects.filter(coachinggroups__in=[self]) @@ -788,117 +818,122 @@ class CoachingGroup(models.Model): # Extension of User with rowing specific data + class Rower(models.Model): adaptivetypes = mytypes.adaptivetypes stravatypes = ( - ('match','Variable - try to match the type'), - ('Ride','Ride'), - ('Kitesurf','Kitesurf'), - ('Run','Run'), - ('NordicSki','NordicSki'), - ('Swim','Swim'), - ('RockClimbing','RockClimbing'), - ('Hike','Hike'), - ('RollerSki','RollerSki'), - ('Walk','Walk'), - ('Rowing','Rowing'), - ('AlpineSki','AlpineSki'), - ('Snowboard','Snowboard'), - ('BackcountrySki','BackcountrySki'), - ('Snowshoe','Snowshoe'), - ('Canoeing','Canoeing'), - ('StairStepper','StairStepper'), - ('Crossfit','Crossfit'), - ('StandUpPaddling','StandUpPaddling'), - ('EBikeRide','EBikeRide'), - ('Surfing','Surfing'), - ('Elliptical','Elliptical'), - ('VirtualRide','VirtualRide'), - ('IceSkate','IceSkate'), - ('WeightTraining','WeightTraining'), - ('InlineSkate','InlineSkate'), - ('Windsurf','Windsurf'), - ('Kayaking','Kayaking'), - ('Workout','Workout'), - ('Yoga','Yoga'), - ) + ('match', 'Variable - try to match the type'), + ('Ride', 'Ride'), + ('Kitesurf', 'Kitesurf'), + ('Run', 'Run'), + ('NordicSki', 'NordicSki'), + ('Swim', 'Swim'), + ('RockClimbing', 'RockClimbing'), + ('Hike', 'Hike'), + ('RollerSki', 'RollerSki'), + ('Walk', 'Walk'), + ('Rowing', 'Rowing'), + ('AlpineSki', 'AlpineSki'), + ('Snowboard', 'Snowboard'), + ('BackcountrySki', 'BackcountrySki'), + ('Snowshoe', 'Snowshoe'), + ('Canoeing', 'Canoeing'), + ('StairStepper', 'StairStepper'), + ('Crossfit', 'Crossfit'), + ('StandUpPaddling', 'StandUpPaddling'), + ('EBikeRide', 'EBikeRide'), + ('Surfing', 'Surfing'), + ('Elliptical', 'Elliptical'), + ('VirtualRide', 'VirtualRide'), + ('IceSkate', 'IceSkate'), + ('WeightTraining', 'WeightTraining'), + ('InlineSkate', 'InlineSkate'), + ('Windsurf', 'Windsurf'), + ('Kayaking', 'Kayaking'), + ('Workout', 'Workout'), + ('Yoga', 'Yoga'), + ) gridtypes = ( - ('none',None), - ('both','both'), - ('x','x'), - ('y','y'), + ('none', None), + ('both', 'both'), + ('x', 'x'), + ('y', 'y'), ) cppresets = ( - (42,'6 weeks'), - (91,'13 weeks'), - (183,'26 weeks'), - (365,'a year') + (42, '6 weeks'), + (91, '13 weeks'), + (183, '26 weeks'), + (365, 'a year') ) plotchoices = ( - ('timeplot','Time Plot'), - ('distanceplot','Distance Plot'), - ('pieplot','Heart Rate Pie Chart'), - ('hrpieplot','Power Pie Chart'), - ('None','None'), - ) - - garminsports = ( - ('GENERIC','Custom'), - ('RUNNING','Running'), - ('CYCLING','Cycling'), - ('LAP_SWIMMING','Lap Swimming'), - ('STRENGTH_TRAINING','Strength Training'), - ('CARDIO_TRAINING','Cardio Training'), + ('timeplot', 'Time Plot'), + ('distanceplot', 'Distance Plot'), + ('pieplot', 'Heart Rate Pie Chart'), + ('hrpieplot', 'Power Pie Chart'), + ('None', 'None'), ) - user = models.OneToOneField(User,on_delete=models.CASCADE) + garminsports = ( + ('GENERIC', 'Custom'), + ('RUNNING', 'Running'), + ('CYCLING', 'Cycling'), + ('LAP_SWIMMING', 'Lap Swimming'), + ('STRENGTH_TRAINING', 'Strength Training'), + ('CARDIO_TRAINING', 'Cardio Training'), + ) - #billing details + user = models.OneToOneField(User, on_delete=models.CASCADE) + + # billing details country = CountryField(default=None, null=True, blank=True) - street_address = models.CharField(default='',blank=True,null=True,max_length=200) - city = models.CharField(default='',blank=True,null=True,max_length=200) - postal_code = models.CharField(default='',blank=True,null=True,max_length=200) + street_address = models.CharField( + default='', blank=True, null=True, max_length=200) + city = models.CharField(default='', blank=True, null=True, max_length=200) + postal_code = models.CharField( + default='', blank=True, null=True, max_length=200) - customer_id = models.CharField(default=None,null=True,blank=True,max_length=200) - subscription_id = models.CharField(default=None,null=True, - blank=True,max_length=200) + customer_id = models.CharField( + default=None, null=True, blank=True, max_length=200) + subscription_id = models.CharField(default=None, null=True, + blank=True, max_length=200) - rowerplan = models.CharField(default='basic',max_length=30, + rowerplan = models.CharField(default='basic', max_length=30, choices=plans) paymenttype = models.CharField( - default='single',max_length=30, + default='single', max_length=30, verbose_name='Payment Type', choices=paymenttypes, ) paymentprocessor = models.CharField(max_length=50, choices=paymentprocessors, - null=True,blank=True, + null=True, blank=True, default='braintree') eurocredits = models.IntegerField(default=0) - paidplan = models.ForeignKey(PaidPlan,null=True,default=None,on_delete=models.SET_NULL) + paidplan = models.ForeignKey( + PaidPlan, null=True, default=None, on_delete=models.SET_NULL) planexpires = models.DateField(default=current_day) teamplanexpires = models.DateField(default=current_day) clubsize = models.IntegerField(default=0) - protrialexpires = models.DateField(default=datetime.date(1970,1,1)) - plantrialexpires = models.DateField(default=datetime.date(1970,1,1)) - offercoaching = models.BooleanField(default=False, verbose_name='Offer Remote Coaching') - + protrialexpires = models.DateField(default=datetime.date(1970, 1, 1)) + plantrialexpires = models.DateField(default=datetime.date(1970, 1, 1)) + offercoaching = models.BooleanField( + default=False, verbose_name='Offer Remote Coaching') # Privacy Data gdproptin = models.BooleanField(default=False) - gdproptindate = models.DateTimeField(blank=True,null=True) + gdproptindate = models.DateTimeField(blank=True, null=True) surveydone = models.BooleanField(default=False) - surveydonedate = models.DateTimeField(blank=True,null=True) - - birthdate = models.DateField(null=True,blank=True) - emailalternatives = AlternativeEmails(default=[],null=True,blank=True,verbose_name='Alternative Email addresses (separate with ",")') + surveydonedate = models.DateTimeField(blank=True, null=True) + birthdate = models.DateField(null=True, blank=True) + emailalternatives = AlternativeEmails( + default=[], null=True, blank=True, verbose_name='Alternative Email addresses (separate with ",")') emailbounced = models.BooleanField(default=False, verbose_name='Email Address Bounced') @@ -907,27 +942,25 @@ class Rower(models.Model): verbose_name='Get Important Emails') share_course_results = models.BooleanField(default=True, - verbose_name = 'Share Course Results') - + verbose_name='Share Course Results') sex = models.CharField(default="not specified", max_length=30, choices=sexcategories) - adaptiveclass = models.CharField(choices=adaptivetypes,max_length=50, - default='None', - verbose_name='Adaptive Classification') - + adaptiveclass = models.CharField(choices=adaptivetypes, max_length=50, + default='None', + verbose_name='Adaptive Classification') # Heart Rate Zone data - max = models.IntegerField(default=192,verbose_name="Max Heart Rate") - rest = models.IntegerField(default=48,verbose_name="Resting Heart Rate") - ut2 = models.IntegerField(default=105,verbose_name="UT2 band lower HR") - ut1 = models.IntegerField(default=146,verbose_name="UT1 band lower HR") - at = models.IntegerField(default=160,verbose_name="AT band lower HR") - tr = models.IntegerField(default=167,verbose_name="TR band lower HR") - an = models.IntegerField(default=180,verbose_name="AN band lower HR") - hrftp = models.IntegerField(default=0,verbose_name="FTP heart rate") + max = models.IntegerField(default=192, verbose_name="Max Heart Rate") + rest = models.IntegerField(default=48, verbose_name="Resting Heart Rate") + ut2 = models.IntegerField(default=105, verbose_name="UT2 band lower HR") + ut1 = models.IntegerField(default=146, verbose_name="UT1 band lower HR") + at = models.IntegerField(default=160, verbose_name="AT band lower HR") + tr = models.IntegerField(default=167, verbose_name="TR band lower HR") + an = models.IntegerField(default=180, verbose_name="AN band lower HR") + hrftp = models.IntegerField(default=0, verbose_name="FTP heart rate") # Weight Category (for sync to C2) weightcategory = models.CharField(default="hwt", @@ -935,38 +968,39 @@ class Rower(models.Model): choices=weightcategories) # Power Zone Data - ftp = models.IntegerField(default=226,verbose_name="Functional Threshold Power") + ftp = models.IntegerField( + default=226, verbose_name="Functional Threshold Power") - p0 = models.FloatField(default=1.0,verbose_name="CP p1") - p1 = models.FloatField(default=1.0,verbose_name="CP p2") - p2 = models.FloatField(default=1.0,verbose_name="CP p3") - p3 = models.FloatField(default=1.0,verbose_name="CP p4") - cpratio = models.FloatField(default=1.0,verbose_name="CP fit ratio") + p0 = models.FloatField(default=1.0, verbose_name="CP p1") + p1 = models.FloatField(default=1.0, verbose_name="CP p2") + p2 = models.FloatField(default=1.0, verbose_name="CP p3") + p3 = models.FloatField(default=1.0, verbose_name="CP p4") + cpratio = models.FloatField(default=1.0, verbose_name="CP fit ratio") + ep0 = models.FloatField(default=1.0, verbose_name="erg CP p1") + ep1 = models.FloatField(default=1.0, verbose_name="erg CP p2") + ep2 = models.FloatField(default=1.0, verbose_name="erg CP p3") + ep3 = models.FloatField(default=1.0, verbose_name="erg CP p4") + ecpratio = models.FloatField(default=1.0, verbose_name="erg CP fit ratio") - ep0 = models.FloatField(default=1.0,verbose_name="erg CP p1") - ep1 = models.FloatField(default=1.0,verbose_name="erg CP p2") - ep2 = models.FloatField(default=1.0,verbose_name="erg CP p3") - ep3 = models.FloatField(default=1.0,verbose_name="erg CP p4") - ecpratio = models.FloatField(default=1.0,verbose_name="erg CP fit ratio") - - cprange = models.IntegerField(default=42,verbose_name="Range for calculation of breakthrough workouts and fitness (CP)", + cprange = models.IntegerField(default=42, verbose_name="Range for calculation of breakthrough workouts and fitness (CP)", choices=cppresets) - - otwslack = models.IntegerField(default=0,verbose_name="OTW Power slack") + otwslack = models.IntegerField(default=0, verbose_name="OTW Power slack") # performance manager stuff - kfit = models.IntegerField(default=42,verbose_name='Fitness Time Decay Constant (days)') - kfatigue = models.IntegerField(default=7,verbose_name='Fatigue Time Decay Constant (days)') + kfit = models.IntegerField( + default=42, verbose_name='Fitness Time Decay Constant (days)') + kfatigue = models.IntegerField( + default=7, verbose_name='Fatigue Time Decay Constant (days)') showfit = models.BooleanField(default=False) showfresh = models.BooleanField(default=False) - pw_ut2 = models.IntegerField(default=124,verbose_name="UT2 Power") - pw_ut1 = models.IntegerField(default=171,verbose_name="UT1 Power") - pw_at = models.IntegerField(default=203,verbose_name="AT Power") - pw_tr = models.IntegerField(default=237,verbose_name="TR Power") - pw_an = models.IntegerField(default=273,verbose_name="AN Power") + pw_ut2 = models.IntegerField(default=124, verbose_name="UT2 Power") + pw_ut1 = models.IntegerField(default=171, verbose_name="UT1 Power") + pw_at = models.IntegerField(default=203, verbose_name="AT Power") + pw_tr = models.IntegerField(default=237, verbose_name="TR Power") + pw_an = models.IntegerField(default=273, verbose_name="AN Power") powerzones = PowerZonesField(default=['Rest', 'Pwr UT2', @@ -976,15 +1010,17 @@ class Rower(models.Model): 'Pwr AN']) hrzones = PowerZonesField(default=['Rest', - 'UT2', - 'UT1', - 'AT', - 'TR', - 'AN','max']) + 'UT2', + 'UT1', + 'AT', + 'TR', + 'AN', 'max']) # median WpS - median_wps = models.IntegerField(default=400,verbose_name='Median Work per Stroke (OTW)') - median_wps_erg = models.IntegerField(default=400,verbose_name='Median Work per Stroke (ergometer)') + median_wps = models.IntegerField( + default=400, verbose_name='Median Work per Stroke (OTW)') + median_wps_erg = models.IntegerField( + default=400, verbose_name='Median Work per Stroke (ergometer)') # Site Settings workflowleftpanel = TemplateListField(default=defaultleft) @@ -994,66 +1030,75 @@ class Rower(models.Model): choices=landingpages, verbose_name="Default Landing Page") defaultlandingpage2 = models.CharField(default='workout_flexchart_stacked_view', - max_length=200, - choices=landingpages2, - verbose_name="Alternative Landing Page") + max_length=200, + choices=landingpages2, + verbose_name="Alternative Landing Page") defaultlandingpage3 = models.CharField(default='workout_view', - max_length=200, - choices=landingpages2, - verbose_name="Title link on workout list") - + max_length=200, + choices=landingpages2, + verbose_name="Title link on workout list") # Access tokens - c2token = models.CharField(default='',max_length=200,blank=True,null=True) - tokenexpirydate = models.DateTimeField(blank=True,null=True) - c2refreshtoken = models.CharField(default='',max_length=200,blank=True,null=True) + c2token = models.CharField( + default='', max_length=200, blank=True, null=True) + tokenexpirydate = models.DateTimeField(blank=True, null=True) + c2refreshtoken = models.CharField( + default='', max_length=200, blank=True, null=True) c2_auto_export = models.BooleanField(default=False) c2_auto_import = models.BooleanField(default=False) - sporttrackstoken = models.CharField(default='',max_length=200,blank=True,null=True) - sporttrackstokenexpirydate = models.DateTimeField(blank=True,null=True) - sporttracksrefreshtoken = models.CharField(default='',max_length=200, - blank=True,null=True) + sporttrackstoken = models.CharField( + default='', max_length=200, blank=True, null=True) + sporttrackstokenexpirydate = models.DateTimeField(blank=True, null=True) + sporttracksrefreshtoken = models.CharField(default='', max_length=200, + blank=True, null=True) sporttracks_auto_export = models.BooleanField(default=False) - tptoken = models.CharField(default='',max_length=1000,blank=True,null=True) - tptokenexpirydate = models.DateTimeField(blank=True,null=True) - tprefreshtoken = models.CharField(default='',max_length=1000, - blank=True,null=True) - rp3token = models.TextField(default='',max_length=1000,blank=True,null=True) - rp3tokenexpirydate = models.DateTimeField(blank=True,null=True) - rp3refreshtoken = models.TextField(default='',max_length=1000, - blank=True,null=True) + tptoken = models.CharField( + default='', max_length=1000, blank=True, null=True) + tptokenexpirydate = models.DateTimeField(blank=True, null=True) + tprefreshtoken = models.CharField(default='', max_length=1000, + blank=True, null=True) + rp3token = models.TextField( + default='', max_length=1000, blank=True, null=True) + rp3tokenexpirydate = models.DateTimeField(blank=True, null=True) + rp3refreshtoken = models.TextField(default='', max_length=1000, + blank=True, null=True) rp3_auto_import = models.BooleanField(default=False) - nktoken = models.TextField(default='',max_length=1000,blank=True,null=True) - nktokenexpirydate = models.DateTimeField(blank=True,null=True) - nkrefreshtoken = models.TextField(default='',max_length=1000, - blank=True,null=True) + nktoken = models.TextField( + default='', max_length=1000, blank=True, null=True) + nktokenexpirydate = models.DateTimeField(blank=True, null=True) + nkrefreshtoken = models.TextField(default='', max_length=1000, + blank=True, null=True) nk_owner_id = models.BigIntegerField(default=0) - nk_auto_import = models.BooleanField(default=False,verbose_name='NK Logbook auto import') + nk_auto_import = models.BooleanField( + default=False, verbose_name='NK Logbook auto import') trainingpeaks_auto_export = models.BooleanField(default=False) - polartoken = models.CharField(default='',max_length=1000,blank=True,null=True) - polartokenexpirydate = models.DateTimeField(blank=True,null=True) - polarrefreshtoken = models.CharField(default='',max_length=1000, - blank=True,null=True) + polartoken = models.CharField( + default='', max_length=1000, blank=True, null=True) + polartokenexpirydate = models.DateTimeField(blank=True, null=True) + polarrefreshtoken = models.CharField(default='', max_length=1000, + blank=True, null=True) polaruserid = models.IntegerField(default=0) polar_auto_import = models.BooleanField(default=False) - garmintoken = models.CharField(default='',max_length=200,blank=True,null=True) - garminrefreshtoken = models.CharField(default='',max_length=1000, - blank=True,null=True) + garmintoken = models.CharField( + default='', max_length=200, blank=True, null=True) + garminrefreshtoken = models.CharField(default='', max_length=1000, + blank=True, null=True) - garminactivity = models.CharField(default='RUNNING',max_length=200, + garminactivity = models.CharField(default='RUNNING', max_length=200, verbose_name='Garmin Activity for Structured Workouts', choices=garminsports) - stravatoken = models.CharField(default='',max_length=200,blank=True,null=True) - stravatokenexpirydate = models.DateTimeField(blank=True,null=True) - stravarefreshtoken = models.CharField(default='',max_length=1000, - blank=True,null=True) + stravatoken = models.CharField( + default='', max_length=200, blank=True, null=True) + stravatokenexpirydate = models.DateTimeField(blank=True, null=True) + stravarefreshtoken = models.CharField(default='', max_length=1000, + blank=True, null=True) stravaexportas = models.CharField(default="match", max_length=30, choices=stravatypes, @@ -1064,68 +1109,75 @@ class Rower(models.Model): strava_auto_import = models.BooleanField(default=False) strava_auto_delete = models.BooleanField(default=False) - privacychoices = ( - ('visible','Visible'), - ('hidden','Hidden'), - ) + ('visible', 'Visible'), + ('hidden', 'Hidden'), + ) getemailnotifications = models.BooleanField(default=False, verbose_name='Receive email notifications') - # Friends/Team - friends = models.ManyToManyField("self",blank=True) - mycoachgroup = models.ForeignKey(CoachingGroup,related_name='coachingrole',null=True,on_delete=models.SET_NULL) - coachinggroups = models.ManyToManyField(CoachingGroup,related_name='coaches') - privacy = models.CharField(default='visible',max_length=30, - choices=privacychoices) + friends = models.ManyToManyField("self", blank=True) + mycoachgroup = models.ForeignKey( + CoachingGroup, related_name='coachingrole', null=True, on_delete=models.SET_NULL) + coachinggroups = models.ManyToManyField( + CoachingGroup, related_name='coaches') + privacy = models.CharField(default='visible', max_length=30, + choices=privacychoices) - team = models.ManyToManyField(Team,blank=True,related_name='rower') + team = models.ManyToManyField(Team, blank=True, related_name='rower') # Export and Time Zone Settings - defaulttimezone = models.CharField(default='UTC',max_length=100, + defaulttimezone = models.CharField(default='UTC', max_length=100, choices=timezones, verbose_name='Default Time Zone') # Show flex chart notes showfavoritechartnotes = models.BooleanField(default=True, - verbose_name='Show Notes for Favorite Charts') + verbose_name='Show Notes for Favorite Charts') # Static chart and data settings - staticgrids = models.CharField(default='both',choices=gridtypes,null=True,max_length=50, + staticgrids = models.CharField(default='both', choices=gridtypes, null=True, max_length=50, verbose_name='Chart Grid') ergpaceslow = datetime.timedelta(seconds=160) ergpacefast = datetime.timedelta(seconds=85) otwpaceslow = datetime.timedelta(seconds=240) otwpacefast = datetime.timedelta(seconds=85) - slowpaceerg = models.DurationField(default=ergpaceslow,verbose_name='Slowest Erg Pace') - fastpaceerg = models.DurationField(default=ergpacefast,verbose_name='Fastest Erg Pace') - slowpaceotw = models.DurationField(default=otwpaceslow,verbose_name='Slowest OTW Pace') - fastpaceotw = models.DurationField(default=otwpacefast,verbose_name='Fastest OTW Pace') + slowpaceerg = models.DurationField( + default=ergpaceslow, verbose_name='Slowest Erg Pace') + fastpaceerg = models.DurationField( + default=ergpacefast, verbose_name='Fastest Erg Pace') + slowpaceotw = models.DurationField( + default=otwpaceslow, verbose_name='Slowest OTW Pace') + fastpaceotw = models.DurationField( + default=otwpacefast, verbose_name='Fastest OTW Pace') - fav_analysis = models.CharField(default='compare',choices=favanalysischoices, + fav_analysis = models.CharField(default='compare', choices=favanalysischoices, max_length=100, verbose_name='Favorite Analysis') - usersmooth = models.IntegerField(default=1,choices=smoothingchoices, + usersmooth = models.IntegerField(default=1, choices=smoothingchoices, verbose_name="Chart Smoothing") - staticchartonupload = models.CharField(default='None',choices=plotchoices, + staticchartonupload = models.CharField(default='None', choices=plotchoices, max_length=100, verbose_name='Generate a static chart automatically on upload') - dosmooth = models.BooleanField(default=True,verbose_name='Savitzky-Golay Filter (recommended)') - erg_recalculatepower = models.BooleanField(default=True,verbose_name='Erg Power from pace') + dosmooth = models.BooleanField( + default=True, verbose_name='Savitzky-Golay Filter (recommended)') + erg_recalculatepower = models.BooleanField( + default=True, verbose_name='Erg Power from pace') # Auto Join - autojoin = models.BooleanField(default=False,verbose_name='Auto Join Workout Segments') + autojoin = models.BooleanField( + default=False, verbose_name='Auto Join Workout Segments') def __str__(self): return self.user.first_name+' '+self.user.last_name - def clean_email(self): # pragma: no cover + def clean_email(self): # pragma: no cover return self.user.email.lower() def save(self, *args, **kwargs): @@ -1133,9 +1185,9 @@ class Rower(models.Model): for group in self.coachinggroups.all(): try: coach = Rower.objects.get(mycoachgroup=group) - if coach.rowerplan == 'freecoach': # pragma: no cover + if coach.rowerplan == 'freecoach': # pragma: no cover self.coachinggroups.remove(group) - except Rower.DoesNotExist: # pragma: no cover + except Rower.DoesNotExist: # pragma: no cover pass except ValueError: pass @@ -1151,124 +1203,133 @@ class Rower(models.Model): try: coach = Rower.objects.get(mycoachgroup=group) coaches.append(coach) - except Rower.DoesNotExist: # pragma: no cover + except Rower.DoesNotExist: # pragma: no cover pass return coaches def can_coach(self): - if self.rowerplan not in ['coach','freecoach']: + if self.rowerplan not in ['coach', 'freecoach']: return False rs = Rower.objects.filter(coachinggroups__in=[self.mycoachgroup]) rekwests = CoachOffer.objects.filter(coach=self) - if len(rs)+len(rekwests) < self.clubsize and self.offercoaching: # pragma: no cover + if len(rs)+len(rekwests) < self.clubsize and self.offercoaching: # pragma: no cover return True return False @property - def ispaid(self): # pragma: no cover - return self.rowerplan in ['pro','plan','coach'] + def ispaid(self): # pragma: no cover + return self.rowerplan in ['pro', 'plan', 'coach'] + class DeactivateUserForm(forms.ModelForm): class Meta: model = User fields = ['is_active'] + class DeleteUserForm(forms.ModelForm): delete_user = forms.BooleanField(initial=False, - label='Remove my account and all data') + label='Remove my account and all data') class Meta: model = User fields = [] # requestor is user + + class CoachRequest(models.Model): - coach = models.ForeignKey(Rower,on_delete=models.CASCADE) - user = models.ForeignKey(User,null=True,on_delete=models.CASCADE) + coach = models.ForeignKey(Rower, on_delete=models.CASCADE) + user = models.ForeignKey(User, null=True, on_delete=models.CASCADE) issuedate = models.DateField(default=current_day) - code = models.CharField(max_length=150,unique=True) + code = models.CharField(max_length=150, unique=True) # requestor is coach -class CoachOffer(models.Model): - coach = models.ForeignKey(Rower,on_delete=models.CASCADE) - user = models.ForeignKey(User,null=True,on_delete=models.CASCADE) - issuedate = models.DateField(default=current_day) - code = models.CharField(max_length=150,unique=True) -from django.db.models.signals import m2m_changed + +class CoachOffer(models.Model): + coach = models.ForeignKey(Rower, on_delete=models.CASCADE) + user = models.ForeignKey(User, null=True, on_delete=models.CASCADE) + issuedate = models.DateField(default=current_day) + code = models.CharField(max_length=150, unique=True) def check_teams_on_change(sender, **kwargs): instance = kwargs.pop('instance', None) action = kwargs.pop('action', None) - pk_set = kwargs.pop('pk_set',None) + pk_set = kwargs.pop('pk_set', None) if action == 'pre_add': for id in pk_set: team = Team.objects.get(id=id) - if not can_join_team(instance.user,team): + if not can_join_team(instance.user, team): raise ValidationError( "You cannot join a team led by a Pro, Free Coach Plan or Self-Coach user" ) + m2m_changed.connect(check_teams_on_change, sender=Rower.team.through) -#@receiver(models.signals.post_save,sender=Rower) -#def auto_delete_teams_on_change(sender, instance, **kwargs): +# @receiver(models.signals.post_save,sender=Rower) +# def auto_delete_teams_on_change(sender, instance, **kwargs): # if instance.rowerplan != 'coach': # teams = Team.objects.filter(manager=instance.user) # for team in teams: # team.delete() -from rowers.metrics import axlabels favchartlabelsx = axlabels.copy() favchartlabelsy1 = axlabels.copy() favchartlabelsy2 = axlabels.copy() favchartlabelsy1.pop('None') -parchoicesy1 = list(sorted(favchartlabelsy1.items(), key = lambda x:x[1])) -parchoicesy2 = list(sorted(favchartlabelsy2.items(), key = lambda x:x[1])) -parchoicesx = list(sorted(favchartlabelsx.items(), key = lambda x:x[1])) +parchoicesy1 = list(sorted(favchartlabelsy1.items(), key=lambda x: x[1])) +parchoicesy2 = list(sorted(favchartlabelsy2.items(), key=lambda x: x[1])) +parchoicesx = list(sorted(favchartlabelsx.items(), key=lambda x: x[1])) # Saving a chart as a favorite chart + + class FavoriteChart(models.Model): workouttypechoices = [ - ('ote','Erg/SkiErg'), - ('otw','On The Water'), - ('all','All') - ] + ('ote', 'Erg/SkiErg'), + ('otw', 'On The Water'), + ('all', 'All') + ] for workoutsource in mytypes.workoutsources: workouttypechoices.append(workoutsource) plottypes = ( - ('line','Line Chart'), - ('scatter','Scatter Chart') - ) + ('line', 'Line Chart'), + ('scatter', 'Scatter Chart') + ) - yparam1 = models.CharField(max_length=50,choices=parchoicesy1,verbose_name='Y1') - yparam2 = models.CharField(max_length=50,choices=parchoicesy2,verbose_name='Y2',default='None',blank=True) - xparam = models.CharField(max_length=50,choices=parchoicesx,verbose_name='X') - plottype = models.CharField(max_length=50,choices=plottypes, + yparam1 = models.CharField( + max_length=50, choices=parchoicesy1, verbose_name='Y1') + yparam2 = models.CharField( + max_length=50, choices=parchoicesy2, verbose_name='Y2', default='None', blank=True) + xparam = models.CharField( + max_length=50, choices=parchoicesx, verbose_name='X') + plottype = models.CharField(max_length=50, choices=plottypes, default='line', verbose_name='Chart Type') - workouttype = models.CharField(max_length=50,choices=workouttypechoices, + workouttype = models.CharField(max_length=50, choices=workouttypechoices, default='both', verbose_name='Workout Type') - reststrokes = models.BooleanField(default=True,verbose_name="Incl. Rest") - notes = models.CharField(max_length=300,verbose_name='Chart Notes', - default='Flex Chart Notes',blank=True) - user = models.ForeignKey(Rower,on_delete=models.CASCADE) + reststrokes = models.BooleanField(default=True, verbose_name="Incl. Rest") + notes = models.CharField(max_length=300, verbose_name='Chart Notes', + default='Flex Chart Notes', blank=True) + user = models.ForeignKey(Rower, on_delete=models.CASCADE) class FavoriteForm(ModelForm): class Meta: model = FavoriteChart - fields = ['xparam','yparam1','yparam2', - 'plottype','workouttype','reststrokes','notes'] + fields = ['xparam', 'yparam1', 'yparam2', + 'plottype', 'workouttype', 'reststrokes', 'notes'] # widgets = { # 'notes': forms.Textarea, # } @@ -1276,7 +1337,7 @@ class FavoriteForm(ModelForm): # To generate favorite chart forms on the fly class BaseFavoriteFormSet(BaseFormSet): - def clean(self): # pragma: no cover + def clean(self): # pragma: no cover if any(self.errors): return @@ -1292,47 +1353,51 @@ class BaseFavoriteFormSet(BaseFormSet): raise forms.ValidationError( 'Must have x parameter.', code='missing_xparam' - ) + ) if not yparam1: raise forms.ValidationError( 'Must have Y1 parameter.', code='missing_yparam1' - ) + ) if not yparam2: yparam2 = 'None' - class Condition(models.Model): conditionchoices = ( - ('<','<'), - ('>','>'), - ('=','='), - ('between','between') - ) - metric = models.CharField(max_length=50,choices=parchoicesy1,verbose_name='Metric') + ('<', '<'), + ('>', '>'), + ('=', '='), + ('between', 'between') + ) + metric = models.CharField( + max_length=50, choices=parchoicesy1, verbose_name='Metric') value1 = models.FloatField(default=0) - value2 = models.FloatField(default=0,null=True,blank=True) - condition = models.CharField(max_length=20,choices=conditionchoices,null=True) + value2 = models.FloatField(default=0, null=True, blank=True) + condition = models.CharField( + max_length=20, choices=conditionchoices, null=True) + class ConditionEditForm(ModelForm): class Meta: model = Condition - fields = ['metric','condition','value1','value2'] + fields = ['metric', 'condition', 'value1', 'value2'] def clean(self): cd = self.cleaned_data try: - if cd['condition'] == 'between' and cd['value2'] is None: # pragma: no cover - raise forms.ValidationError('When using between, you must fill value 1 and value 2') - except KeyError: # pragma: no cover + if cd['condition'] == 'between' and cd['value2'] is None: # pragma: no cover + raise forms.ValidationError( + 'When using between, you must fill value 1 and value 2') + except KeyError: # pragma: no cover pass + class BaseConditionFormSet(BaseFormSet): def clean(self): - if any(self.errors): # pragma: no cover + if any(self.errors): # pragma: no cover return for form in self.forms: @@ -1343,85 +1408,86 @@ class BaseConditionFormSet(BaseFormSet): value2 = form.cleaned_data['value2'] - rowchoices = [] -for key,value in mytypes.workouttypes: +for key, value in mytypes.workouttypes: if key in mytypes.rowtypes: - rowchoices.append((key,value)) - + rowchoices.append((key, value)) class Alert(models.Model): - name = models.CharField(max_length=150,verbose_name='Alert Name',null=True,blank=True) + name = models.CharField( + max_length=150, verbose_name='Alert Name', null=True, blank=True) manager = models.ForeignKey(User, on_delete=models.CASCADE) rower = models.ForeignKey(Rower, on_delete=models.CASCADE) - measured = models.OneToOneField(Condition,verbose_name='Measuring',on_delete=models.CASCADE, + measured = models.OneToOneField(Condition, verbose_name='Measuring', on_delete=models.CASCADE, related_name='measured') - filter = models.ManyToManyField(Condition, related_name='filters',verbose_name='Filters') - reststrokes = models.BooleanField(default=False,null=True,verbose_name='Include Rest Strokes') - period = models.IntegerField(default=7,verbose_name='Reporting Period (days)') + filter = models.ManyToManyField( + Condition, related_name='filters', verbose_name='Filters') + reststrokes = models.BooleanField( + default=False, null=True, verbose_name='Include Rest Strokes') + period = models.IntegerField( + default=7, verbose_name='Reporting Period (days)') next_run = models.DateField(default=timezone.now) - emailalert = models.BooleanField(default=True,verbose_name='Send email alerts') - workouttype = models.CharField(choices=rowchoices,max_length=50, - verbose_name='Exercise/Boat Class',default='water') - boattype = models.CharField(choices=mytypes.boattypes,max_length=50, - verbose_name='Boat Type',default='1x') - - + emailalert = models.BooleanField( + default=True, verbose_name='Send email alerts') + workouttype = models.CharField(choices=rowchoices, max_length=50, + verbose_name='Exercise/Boat Class', default='water') + boattype = models.CharField(choices=mytypes.boattypes, max_length=50, + verbose_name='Boat Type', default='1x') def __str__(self): - metricdict = {key:value for (key,value) in parchoicesy1} + metricdict = {key: value for (key, value) in parchoicesy1} stri = u'Alert {name} on {metric} for {workouttype} - running on {first_name} every {period} days'.format( - name = self.name, - metric = metricdict[self.measured.metric], - workouttype = self.workouttype, - first_name = self.rower.user.first_name, - period = self.period, - ) + name=self.name, + metric=metricdict[self.measured.metric], + workouttype=self.workouttype, + first_name=self.rower.user.first_name, + period=self.period, + ) return stri - def metricname(self): # pragma: no cover - metricdict = {key:value for (key,value) in parchoicesy1} + def metricname(self): # pragma: no cover + metricdict = {key: value for (key, value) in parchoicesy1} return metricdict[self.measured.metric] - def description(self): # pragma: no cover - metricdict = {key:value for (key,value) in parchoicesy1} + def description(self): # pragma: no cover + metricdict = {key: value for (key, value) in parchoicesy1} if self.measured.condition == 'between': description = 'This alert measures strokes where {metric} is between {value1} and {value2}.'.format( - metric = metricdict[self.measured.metric], - value1 = self.measured.value1, - value2 = self.measured.value2, + metric=metricdict[self.measured.metric], + value1=self.measured.value1, + value2=self.measured.value2, ) elif self.measured.condition == '<': description = 'This alert measures strokes where {metric} is smaller than {value1}.'.format( - metric = metricdict[self.measured.metric], - value1 = self.measured.value1, + metric=metricdict[self.measured.metric], + value1=self.measured.value1, ) else: description = 'This alert measures strokes where {metric} is larger than {value1}.'.format( - metric = metricdict[self.measured.metric], - value1 = self.measured.value1, + metric=metricdict[self.measured.metric], + value1=self.measured.value1, ) return description - def shortdescription(self): # pragma: no cover - metricdict = {key:value for (key,value) in parchoicesy1} + def shortdescription(self): # pragma: no cover + metricdict = {key: value for (key, value) in parchoicesy1} if self.measured.condition == 'between': description = '{value1} < {metric} < {value2}'.format( - metric = self.measured.metric, - value1 = self.measured.value1, - value2 = self.measured.value2, + metric=self.measured.metric, + value1=self.measured.value1, + value2=self.measured.value2, ) else: description = '{metric} {condition} {value1}'.format( - metric = self.measured.metric, - value1 = self.measured.value1, - condition = self.measured.condition + metric=self.measured.metric, + value1=self.measured.value1, + condition=self.measured.condition ) return description @@ -1430,38 +1496,39 @@ class Alert(models.Model): class AlertEditForm(ModelForm): class Meta: model = Alert - fields = ['name','reststrokes','period','emailalert','workouttype','boattype'] + fields = ['name', 'reststrokes', 'period', + 'emailalert', 'workouttype', 'boattype'] widgets = { - 'reststrokes':forms.CheckboxInput() - } + 'reststrokes': forms.CheckboxInput() + } + class BasePlannedSessionFormSet(BaseFormSet): - def clean(self): # pragma: no cover + def clean(self): # pragma: no cover if any(self.serrors): return - - timezones = ( - (x,x) for x in pytz.common_timezones + (x, x) for x in pytz.common_timezones ) # models related to geo data (points, polygon, courses) class GeoCourse(models.Model): - manager = models.ForeignKey(Rower,null=True,on_delete=models.SET_NULL) + manager = models.ForeignKey(Rower, null=True, on_delete=models.SET_NULL) distance = models.IntegerField(default=0) - name = models.CharField(max_length=150,blank=True) - country = models.CharField(max_length=150,blank=True) - notes = models.CharField(blank=True,max_length=200,verbose_name='Course Notes') + name = models.CharField(max_length=150, blank=True) + country = models.CharField(max_length=150, blank=True) + notes = models.CharField(blank=True, max_length=200, + verbose_name='Course Notes') def __str__(self): name = self.name country = self.country d = self.distance - if d == 0: # pragma: no cover + if d == 0: # pragma: no cover self.distance = course_length(self) self.save() d = self.distance @@ -1469,29 +1536,31 @@ class GeoCourse(models.Model): return u'{name} - {d}m'.format( name=name, country=country, - d = d, + d=d, ) @property def coord(self): return course_coord_center(self) + class GeoCourseEditForm(ModelForm): class Meta: model = GeoCourse - fields = ['name','country','notes'] + fields = ['name', 'country', 'notes'] widgets = { 'notes': forms.Textarea, - } + } class GeoPolygon(models.Model): - name = models.CharField(max_length=150,blank=True) - course = models.ForeignKey(GeoCourse, blank=True,on_delete=models.CASCADE,related_name='polygons') + name = models.CharField(max_length=150, blank=True) + course = models.ForeignKey( + GeoCourse, blank=True, on_delete=models.CASCADE, related_name='polygons') order_in_course = models.IntegerField(default=0) - def __str__(self): # pragma: no cover + def __str__(self): # pragma: no cover name = self.name coursename = self.course.name @@ -1507,24 +1576,25 @@ class GeoPolygon(models.Model): class GeoPoint(models.Model): latitude = models.FloatField(default=0) longitude = models.FloatField(default=0) - polygon = models.ForeignKey(GeoPolygon,blank=True,on_delete=models.CASCADE,related_name='points') + polygon = models.ForeignKey( + GeoPolygon, blank=True, on_delete=models.CASCADE, related_name='points') order_in_poly = models.IntegerField(default=0) # need error checking to "insert" new point into existing polygon? This affects order_in_poly # of multiple GeoPoint instances - # models related to training planning - draft # Do we need a separate class TestTarget? class TrainingTarget(models.Model): rowers = models.ManyToManyField(Rower, related_name='targetathletes', verbose_name='Athletes') - manager = models.ForeignKey(Rower,related_name='targetmanager',null=True,on_delete=models.CASCADE) - name = models.CharField(max_length=150,blank=True) + manager = models.ForeignKey( + Rower, related_name='targetmanager', null=True, on_delete=models.CASCADE) + name = models.CharField(max_length=150, blank=True) date = models.DateField( default=half_year_from_now) - notes = models.TextField(max_length=300,blank=True) + notes = models.TextField(max_length=300, blank=True) def __str__(self): date = self.date @@ -1533,43 +1603,48 @@ class TrainingTarget(models.Model): try: ownerfirst = self.manager.user.first_name ownerlast = self.manager.user.last_name - except AttributeError: # pragma: no cover + except AttributeError: # pragma: no cover ownerfirst = '' ownerlast = '' stri = u'#{id}: {n} {d} {ownerfirst} {ownerlast}'.format( - ownerfirst = ownerfirst, - ownerlast = ownerlast, - d = date.strftime('%Y-%m-%d'), - n = name, - id = id, - ) + ownerfirst=ownerfirst, + ownerlast=ownerlast, + d=date.strftime('%Y-%m-%d'), + n=name, + id=id, + ) return stri -def check_trainingtarget_on_change(sender,**kwargs): - instance = kwargs.pop('instance',None) - action = kwargs.pop('action',None) - pk_set = kwargs.pop('pk_set',None) + +def check_trainingtarget_on_change(sender, **kwargs): + instance = kwargs.pop('instance', None) + action = kwargs.pop('action', None) + pk_set = kwargs.pop('pk_set', None) if action == 'pre_add': for id in pk_set: rower = Rower.objects.get(id=id) - if not can_plan_user(instance.manager.user,rower): # pragma: no cover - raise ValidationError("You cannot add this rower. Not your coachee") + if not can_plan_user(instance.manager.user, rower): # pragma: no cover + raise ValidationError( + "You cannot add this rower. Not your coachee") + + +m2m_changed.connect(check_trainingtarget_on_change, + sender=TrainingTarget.rowers.through) -m2m_changed.connect(check_trainingtarget_on_change, sender=TrainingTarget.rowers.through) class TrainingTargetForm(ModelForm): class Meta: model = TrainingTarget - fields = ['name','date','notes','rowers'] + fields = ['name', 'date', 'notes', 'rowers'] widgets = { 'date': AdminDateWidget() - } + } - def __init__(self,*args, **kwargs): - user = kwargs.pop('user',None) + def __init__(self, *args, **kwargs): + user = kwargs.pop('user', None) super(TrainingTargetForm, self).__init__(*args, **kwargs) try: @@ -1577,7 +1652,7 @@ class TrainingTargetForm(ModelForm): except AttributeError: if user: teams = Team.objects.filter(manager=user) - else: # pragma: no cover + else: # pragma: no cover teams = [] if not teams: @@ -1585,26 +1660,34 @@ class TrainingTargetForm(ModelForm): else: qs1 = Rower.objects.filter( team__in=teams - ).distinct().order_by("user__last_name","user__first_name") + ).distinct().order_by("user__last_name", "user__first_name") self.fields['rowers'].queryset = qs1 class InstantPlan(models.Model): - uuid = models.UUIDField(primary_key=False,editable=True,default=uuid.uuid4) - owner = models.ForeignKey(User,on_delete=models.SET_NULL,null=True) - name = models.CharField(max_length=150,blank=True) - goal = models.CharField(max_length=150,blank=True,verbose_name="Goal (one sentence)") - description = models.TextField(max_length=450,blank=True) - duration = models.IntegerField(default=6,verbose_name='Duration in Calendar Days') - target = models.TextField(max_length=450,blank=True,verbose_name='What the plan will achieve') - hoursperweek = models.IntegerField(default=4,verbose_name='Hours Per Week') - sessionsperweek = models.IntegerField(default=3,verbose_name='Number of sessions per week') - yaml = models.FileField(upload_to=get_file_path,verbose_name="Plan YAML file",null=True,blank=True) - price = models.IntegerField(default=0,verbose_name="Price in EURO") - url = models.CharField(max_length=250,blank=True,verbose_name="Link to page with more information") + uuid = models.UUIDField( + primary_key=False, editable=True, default=uuid.uuid4) + owner = models.ForeignKey(User, on_delete=models.SET_NULL, null=True) + name = models.CharField(max_length=150, blank=True) + goal = models.CharField(max_length=150, blank=True, + verbose_name="Goal (one sentence)") + description = models.TextField(max_length=450, blank=True) + duration = models.IntegerField( + default=6, verbose_name='Duration in Calendar Days') + target = models.TextField( + max_length=450, blank=True, verbose_name='What the plan will achieve') + hoursperweek = models.IntegerField( + default=4, verbose_name='Hours Per Week') + sessionsperweek = models.IntegerField( + default=3, verbose_name='Number of sessions per week') + yaml = models.FileField(upload_to=get_file_path, + verbose_name="Plan YAML file", null=True, blank=True) + price = models.IntegerField(default=0, verbose_name="Price in EURO") + url = models.CharField(max_length=250, blank=True, + verbose_name="Link to page with more information") - def __str__(self): # pragma: no cover + def __str__(self): # pragma: no cover return self.name def save(self, *args, **kwargs): @@ -1614,8 +1697,8 @@ class InstantPlan(models.Model): authorizationstring = 'Bearer '+settings.WORKOUTS_FIT_TOKEN url = settings.WORKOUTS_FIT_URL+"/trainingplan/" - headers = {'Authorization':authorizationstring} - response = requests.post(url=url,headers=headers,data=yamltext) + headers = {'Authorization': authorizationstring} + response = requests.post(url=url, headers=headers, data=yamltext) if response.status_code == 200: data = response.json() self.yaml.name = data['filename'] @@ -1626,6 +1709,7 @@ class InstantPlan(models.Model): self.yaml = None super(InstantPlan, self).save(*args, **kwargs) + class InstantPlanForm(ModelForm): class Meta: model = InstantPlan @@ -1640,24 +1724,28 @@ class InstantPlanForm(ModelForm): 'hoursperweek', 'sessionsperweek', 'yaml', - ] + ] class TrainingPlan(models.Model): statuschoices = ( - ('active','active'), - ('deactivated','inactive'), - ) + ('active', 'active'), + ('deactivated', 'inactive'), + ) - rowers = models.ManyToManyField(Rower,related_name='planathletes', + rowers = models.ManyToManyField(Rower, related_name='planathletes', verbose_name='Athletes') - manager = models.ForeignKey(Rower,related_name='planmanager',null=True,on_delete=models.SET_NULL) - name = models.CharField(max_length=150,blank=True, verbose_name="Plan Name") - status = models.BooleanField(default=True,verbose_name='Active') - target = models.ForeignKey(TrainingTarget,blank=True,null=True,on_delete=models.SET_NULL) + manager = models.ForeignKey( + Rower, related_name='planmanager', null=True, on_delete=models.SET_NULL) + name = models.CharField(max_length=150, blank=True, + verbose_name="Plan Name") + status = models.BooleanField(default=True, verbose_name='Active') + target = models.ForeignKey( + TrainingTarget, blank=True, null=True, on_delete=models.SET_NULL) startdate = models.DateField(default=current_day) - notes = models.CharField(blank=True,null=True,max_length=200,verbose_name='Plan Notes') + notes = models.CharField(blank=True, null=True, + max_length=200, verbose_name='Plan Notes') enddate = models.DateField( default=half_year_from_now) @@ -1669,38 +1757,36 @@ class TrainingPlan(models.Model): lastname = self.manager.user.last_name stri = u'Training Plan by {firstname} {lastname} {s} - {e}: {name}'.format( - s = startdate.strftime('%Y-%m-%d'), - e = enddate.strftime('%Y-%m-%d'), - firstname = firstname, - lastname = lastname, + s=startdate.strftime('%Y-%m-%d'), + e=enddate.strftime('%Y-%m-%d'), + firstname=firstname, + lastname=lastname, name=name - ) + ) return stri def save(self, *args, **kwargs): manager = self.manager - if not can_add_plan(manager.user): # pragma: no cover + if not can_add_plan(manager.user): # pragma: no cover raise ValidationError( "Basic user cannot have a training plan" ) - - - if self.enddate < self.startdate: # pragma: no cover + if self.enddate < self.startdate: # pragma: no cover startdate = self.startdate enddate = self.enddate self.startdate = enddate self.enddate = startdate if not self.enddate <= self.startdate: - super(TrainingPlan,self).save(*args, **kwargs) + super(TrainingPlan, self).save(*args, **kwargs) # only add athletes that are allowed to be added for rower in self.rowers.all(): - if can_plan_user(manager.user,rower): + if can_plan_user(manager.user, rower): self.rowers.add(rower) - else: # pragma: no cover + else: # pragma: no cover self.rowers.remove(rower) if self.status: @@ -1709,10 +1795,9 @@ class TrainingPlan(models.Model): pk=self.pk).order_by( "-startdate") - - for otherplan in otherplans: # pragma: no cover + for otherplan in otherplans: # pragma: no cover if otherplan.startdate <= self.enddate and otherplan.startdate >= self.startdate: - for rower in self.rowers.all(): # pragma: no cover + for rower in self.rowers.all(): # pragma: no cover if rower in otherplan.rowers.all(): self.status = False self.save() @@ -1722,57 +1807,59 @@ class TrainingPlan(models.Model): self.status = False self.save() - - - - macrocycles = TrainingMacroCycle.objects.filter(plan = self) + macrocycles = TrainingMacroCycle.objects.filter(plan=self) if not macrocycles: m = TrainingMacroCycle( - plan = self, - name = 'Filler', - startdate = self.startdate, - enddate = self.enddate, - ) + plan=self, + name='Filler', + startdate=self.startdate, + enddate=self.enddate, + ) m.save() else: createmacrofillers(self) + def check_trainingplan_on_change(sender, **kwargs): - instance = kwargs.pop('instance',None) - action = kwargs.pop('action',None) - pk_set = kwargs.pop('pk_set',None) + instance = kwargs.pop('instance', None) + action = kwargs.pop('action', None) + pk_set = kwargs.pop('pk_set', None) if action == 'pre_add': for id in pk_set: rower = Rower.objects.get(id=id) - if not can_plan_user(instance.manager.user,rower): # pragma: no cover - raise ValidationError("You cannot add this rower. Not your coachee") + if not can_plan_user(instance.manager.user, rower): # pragma: no cover + raise ValidationError( + "You cannot add this rower. Not your coachee") -m2m_changed.connect(check_trainingplan_on_change, sender=TrainingPlan.rowers.through) + +m2m_changed.connect(check_trainingplan_on_change, + sender=TrainingPlan.rowers.through) class TrainingPlanForm(ModelForm): class Meta: model = TrainingPlan - fields = ['name','target','startdate','enddate','status','notes','rowers'] + fields = ['name', 'target', 'startdate', + 'enddate', 'status', 'notes', 'rowers'] widgets = { 'startdate': AdminDateWidget(), 'enddate': AdminDateWidget(), 'notes': forms.Textarea() - } + } - def __init__(self,*args, **kwargs): - targets = kwargs.pop('targets',None) - user = kwargs.pop('user',None) + def __init__(self, *args, **kwargs): + targets = kwargs.pop('targets', None) + user = kwargs.pop('user', None) super(TrainingPlanForm, self).__init__(*args, **kwargs) if targets: - targetchoices = [(x.id,x) for x in targets] - targetchoices.append((None,'---')) + targetchoices = [(x.id, x) for x in targets] + targetchoices.append((None, '---')) self.fields['target'].choices = targetchoices - elif self.instance.pk is not None: # pragma: no cover + elif self.instance.pk is not None: # pragma: no cover self.fields['target'].queryset = TrainingTarget.objects.filter( manager=self.instance.manager, date__gte=current_day()).order_by("date") @@ -1784,7 +1871,7 @@ class TrainingPlanForm(ModelForm): except AttributeError: if user: teams = Team.objects.filter(manager=user) - else: # pragma: no cover + else: # pragma: no cover teams = [] if not teams: @@ -1792,108 +1879,111 @@ class TrainingPlanForm(ModelForm): else: self.fields['rowers'].queryset = Rower.objects.filter( team__in=teams - ).distinct().order_by("user__last_name","user__first_name") + ).distinct().order_by("user__last_name", "user__first_name") + cycletypechoices = ( - ('filler','System Defined'), - ('userdefined','User Defined') - ) + ('filler', 'System Defined'), + ('userdefined', 'User Defined') +) + def createmacrofillers(plan): fillers = TrainingMacroCycle.objects.filter( - plan = plan, type = 'filler' - ) + plan=plan, type='filler' + ) for f in fillers: f.delete() cycles = TrainingMacroCycle.objects.filter( - plan = plan - ).order_by("-startdate") + plan=plan + ).order_by("-startdate") if not cycles: macr = TrainingMacroCycle( plan=plan, - startdate = plan.startdate, - enddate = plan.enddate, + startdate=plan.startdate, + enddate=plan.enddate, type='filler', name='Filler' - ) + ) macr.save() thedate = plan.enddate - while cycles: # pragma: no cover + while cycles: # pragma: no cover if cycles[0].enddate < thedate: macr = TrainingMacroCycle( plan=plan, - startdate = cycles[0].enddate+datetime.timedelta(days=1), - enddate = thedate, + startdate=cycles[0].enddate+datetime.timedelta(days=1), + enddate=thedate, type='filler', name='Filler' - ) + ) macr.save() thedate = cycles[0].startdate-datetime.timedelta(days=1) cycles = cycles[1:] cycles = TrainingMacroCycle.objects.filter( - plan = plan - ).order_by("startdate") + plan=plan + ).order_by("startdate") - if cycles[0].startdate > plan.startdate: # pragma: no cover + if cycles[0].startdate > plan.startdate: # pragma: no cover macr = TrainingMacroCycle( plan=plan, - startdate = plan.startdate, - enddate = cycles[0].startdate-datetime.timedelta(days=1), + startdate=plan.startdate, + enddate=cycles[0].startdate-datetime.timedelta(days=1), type='filler', name='Filler' ) macr.save() + def createmesofillers(plan): fillers = TrainingMesoCycle.objects.filter( - plan = plan, type = 'filler' - ) + plan=plan, type='filler' + ) for f in fillers: f.delete() cycles = TrainingMesoCycle.objects.filter( - plan = plan - ).order_by("-startdate") + plan=plan + ).order_by("-startdate") if not cycles: macr = TrainingMesoCycle( - plan = plan, - startdate = plan.startdate, - enddate = plan.enddate, + plan=plan, + startdate=plan.startdate, + enddate=plan.enddate, type='filler', name='Filler' - ) + ) macr.save() thedate = plan.enddate - while cycles: # pragma: no cover + while cycles: # pragma: no cover if cycles[0].enddate < thedate: macr = TrainingMesoCycle( plan=plan, - startdate = cycles[0].enddate+datetime.timedelta(days=1), - enddate = thedate, + startdate=cycles[0].enddate+datetime.timedelta(days=1), + enddate=thedate, type='filler', name='Filler' - ) + ) macr.save() thedate = cycles[0].startdate-datetime.timedelta(days=1) cycles = cycles[1:] cycles = TrainingMesoCycle.objects.filter( - plan = plan - ).order_by("startdate") + plan=plan + ).order_by("startdate") - if cycles[0].startdate > plan.startdate: # pragma: no cover + if cycles[0].startdate > plan.startdate: # pragma: no cover macr = TrainingMesoCycle( plan=plan, - startdate = plan.startdate, - enddate = cycles[0].startdate-datetime.timedelta(days=1), + startdate=plan.startdate, + enddate=cycles[0].startdate-datetime.timedelta(days=1), type='filler', name='Filler' ) @@ -1902,59 +1992,59 @@ def createmesofillers(plan): def createmicrofillers(plan): fillers = TrainingMicroCycle.objects.filter( - plan = plan, type = 'filler' - ) + plan=plan, type='filler' + ) for f in fillers: f.delete() cycles = TrainingMicroCycle.objects.filter( - plan = plan - ).order_by("-startdate") + plan=plan + ).order_by("-startdate") if not cycles: macr = TrainingMicroCycle( plan=plan, - startdate = plan.startdate, - enddate = plan.enddate, - type='filler', - name='Filler' - ) - macr.save() - - thedate = plan.enddate - while cycles: # pragma: no cover - if cycles[0].enddate < thedate: - macr = TrainingMicroCycle( - plan=plan, - startdate = cycles[0].enddate+datetime.timedelta(days=1), - enddate = thedate, - type='filler', - name='Filler' - ) - macr.save() - thedate = cycles[0].startdate-datetime.timedelta(days=1) - cycles = cycles[1:] - - cycles = TrainingMicroCycle.objects.filter( - plan = plan - ).order_by("startdate") - - - if cycles and cycles[0].startdate > plan.startdate: # pragma: no cover - macr = TrainingMicroCycle( - plan=plan, - startdate = plan.startdate, - enddate = cycles[0].startdate-datetime.timedelta(days=1), + startdate=plan.startdate, + enddate=plan.enddate, type='filler', name='Filler' ) macr.save() -def microcyclecheckdates(plan): # pragma: no cover + thedate = plan.enddate + while cycles: # pragma: no cover + if cycles[0].enddate < thedate: + macr = TrainingMicroCycle( + plan=plan, + startdate=cycles[0].enddate+datetime.timedelta(days=1), + enddate=thedate, + type='filler', + name='Filler' + ) + macr.save() + thedate = cycles[0].startdate-datetime.timedelta(days=1) + cycles = cycles[1:] + cycles = TrainingMicroCycle.objects.filter( plan=plan - ).order_by("-startdate") + ).order_by("startdate") + + if cycles and cycles[0].startdate > plan.startdate: # pragma: no cover + macr = TrainingMicroCycle( + plan=plan, + startdate=plan.startdate, + enddate=cycles[0].startdate-datetime.timedelta(days=1), + type='filler', + name='Filler' + ) + macr.save() + + +def microcyclecheckdates(plan): # pragma: no cover + cycles = TrainingMicroCycle.objects.filter( + plan=plan + ).order_by("-startdate") thedate = plan.enddate while cycles: @@ -1970,7 +2060,7 @@ def microcyclecheckdates(plan): # pragma: no cover cycles = TrainingMicroCycle.objects.filter( plan=plan - ).order_by("startdate") + ).order_by("startdate") thedate = plan.startdate while cycles: @@ -1983,10 +2073,11 @@ def microcyclecheckdates(plan): # pragma: no cover pass cycles = cycles[1:] -def mesocyclecheckdates(plan): # pragma: no cover + +def mesocyclecheckdates(plan): # pragma: no cover cycles = TrainingMesoCycle.objects.filter( plan=plan - ).order_by("-startdate") + ).order_by("-startdate") thedate = plan.enddate while cycles: @@ -2002,7 +2093,7 @@ def mesocyclecheckdates(plan): # pragma: no cover cycles = TrainingMesoCycle.objects.filter( plan=plan - ).order_by("startdate") + ).order_by("startdate") thedate = plan.startdate while cycles: @@ -2015,10 +2106,11 @@ def mesocyclecheckdates(plan): # pragma: no cover pass cycles = cycles[1:] -def macrocyclecheckdates(plan): # pragma: no cover + +def macrocyclecheckdates(plan): # pragma: no cover cycles = TrainingMacroCycle.objects.filter( plan=plan - ).order_by("-startdate") + ).order_by("-startdate") thedate = plan.enddate while cycles: @@ -2034,7 +2126,7 @@ def macrocyclecheckdates(plan): # pragma: no cover cycles = TrainingMacroCycle.objects.filter( plan=plan - ).order_by("startdate") + ).order_by("startdate") thedate = plan.startdate while cycles: @@ -2049,58 +2141,60 @@ def macrocyclecheckdates(plan): # pragma: no cover class TrainingMacroCycle(models.Model): - plan = models.ForeignKey(TrainingPlan,on_delete=models.CASCADE) - name = models.CharField(max_length=150,blank=True) + plan = models.ForeignKey(TrainingPlan, on_delete=models.CASCADE) + name = models.CharField(max_length=150, blank=True) startdate = models.DateField(default=current_day) enddate = models.DateField( default=half_year_from_now) - notes = models.TextField(max_length=300,blank=True) + notes = models.TextField(max_length=300, blank=True) type = models.CharField(default='filler', choices=cycletypechoices, max_length=150) - plantime = models.IntegerField(default=0,verbose_name='Planned Duration') - plandistance = models.IntegerField(default=0,verbose_name='Planned Distance') - planrscore = models.IntegerField(default=0,verbose_name='Planned rScore') - plantrimp = models.IntegerField(default=0,verbose_name='Planned TRIMP') + plantime = models.IntegerField(default=0, verbose_name='Planned Duration') + plandistance = models.IntegerField( + default=0, verbose_name='Planned Distance') + planrscore = models.IntegerField(default=0, verbose_name='Planned rScore') + plantrimp = models.IntegerField(default=0, verbose_name='Planned TRIMP') - actualtime = models.IntegerField(default=0,verbose_name='Actual Duration') - actualdistance = models.IntegerField(default=0,verbose_name='Actual Distance') - actualrscore = models.IntegerField(default=0,verbose_name='Actual rScore') - actualtrimp = models.IntegerField(default=0,verbose_name='Actual TRIMP') + actualtime = models.IntegerField(default=0, verbose_name='Actual Duration') + actualdistance = models.IntegerField( + default=0, verbose_name='Actual Distance') + actualrscore = models.IntegerField(default=0, verbose_name='Actual rScore') + actualtrimp = models.IntegerField(default=0, verbose_name='Actual TRIMP') - def __str__(self): # pragma: no cover + def __str__(self): # pragma: no cover stri = 'Macro Cycle - {n} ({sd} - {ed})'.format( - n = self.name, - sd = self.startdate, - ed = self.enddate, - ) + n=self.name, + sd=self.startdate, + ed=self.enddate, + ) return stri def save(self, *args, **kwargs): - if self.enddate < self.startdate: # pragma: no cover + if self.enddate < self.startdate: # pragma: no cover startdate = self.startdate enddate = self.enddate self.startdate = enddate self.enddate = startdate fillers = TrainingMacroCycle.objects.filter( - plan=self.plan,type='filler') + plan=self.plan, type='filler') for f in fillers: f.delete() - if self.enddate > self.plan.enddate: # pragma: no cover + if self.enddate > self.plan.enddate: # pragma: no cover self.enddate = self.plan.enddate - if self.startdate < self.plan.startdate: # pragma: no cover + if self.startdate < self.plan.startdate: # pragma: no cover self.startdate = self.plan.startdate othercycles = TrainingMacroCycle.objects.filter( plan=self.plan).exclude(pk=self.pk).order_by("-startdate") - for othercycle in othercycles: # pragma: no cover + for othercycle in othercycles: # pragma: no cover if othercycle.startdate <= self.enddate and othercycle.startdate >= self.startdate: self.enddate = othercycle.startdate-datetime.timedelta(days=1) @@ -2108,88 +2202,87 @@ class TrainingMacroCycle(models.Model): self.startdate = othercycle.enddate+datetime.timedelta(days=1) if not self.enddate <= self.startdate: - super(TrainingMacroCycle,self).save(*args, **kwargs) + super(TrainingMacroCycle, self).save(*args, **kwargs) - - mesocycles = TrainingMesoCycle.objects.filter(plan = self) + mesocycles = TrainingMesoCycle.objects.filter(plan=self) if not mesocycles: meso = TrainingMesoCycle( - plan = self, - name = 'Filler', - startdate = self.startdate, - enddate = self.enddate, - ) + plan=self, + name='Filler', + startdate=self.startdate, + enddate=self.enddate, + ) meso.save() - else: # pragma: no cover + else: # pragma: no cover createmesofillers(self) - class TrainingMacroCycleForm(ModelForm): class Meta: model = TrainingMacroCycle - fields = ['name','startdate','enddate','notes'] + fields = ['name', 'startdate', 'enddate', 'notes'] widgets = { 'startdate': AdminDateWidget(), 'enddate': AdminDateWidget() - } + } class TrainingMesoCycle(models.Model): - plan = models.ForeignKey(TrainingMacroCycle,on_delete=models.CASCADE) - name = models.CharField(max_length=150,blank=True) + plan = models.ForeignKey(TrainingMacroCycle, on_delete=models.CASCADE) + name = models.CharField(max_length=150, blank=True) startdate = models.DateField(default=current_day) enddate = models.DateField( default=half_year_from_now) - notes = models.TextField(max_length=300,blank=True) + notes = models.TextField(max_length=300, blank=True) type = models.CharField(default='filler', choices=cycletypechoices, max_length=150) - plantime = models.IntegerField(default=0,verbose_name='Planned Duration') - plandistance = models.IntegerField(default=0,verbose_name='Planned Distance') - planrscore = models.IntegerField(default=0,verbose_name='Planned rScore') - plantrimp = models.IntegerField(default=0,verbose_name='Planned TRIMP') + plantime = models.IntegerField(default=0, verbose_name='Planned Duration') + plandistance = models.IntegerField( + default=0, verbose_name='Planned Distance') + planrscore = models.IntegerField(default=0, verbose_name='Planned rScore') + plantrimp = models.IntegerField(default=0, verbose_name='Planned TRIMP') - actualtime = models.IntegerField(default=0,verbose_name='Actual Duration') - actualdistance = models.IntegerField(default=0,verbose_name='Actual Distance') - actualrscore = models.IntegerField(default=0,verbose_name='Actual rScore') - actualtrimp = models.IntegerField(default=0,verbose_name='Actual TRIMP') + actualtime = models.IntegerField(default=0, verbose_name='Actual Duration') + actualdistance = models.IntegerField( + default=0, verbose_name='Actual Distance') + actualrscore = models.IntegerField(default=0, verbose_name='Actual rScore') + actualtrimp = models.IntegerField(default=0, verbose_name='Actual TRIMP') - - def __str__(self): # pragma: no cover + def __str__(self): # pragma: no cover stri = 'Meso Cycle - {n} ({sd} - {ed})'.format( - n = self.name, - sd = self.startdate, - ed = self.enddate, - ) + n=self.name, + sd=self.startdate, + ed=self.enddate, + ) return stri def save(self, *args, **kwargs): - if self.enddate < self.startdate: # pragma: no cover + if self.enddate < self.startdate: # pragma: no cover startdate = self.startdate enddate = self.enddate self.startdate = enddate self.enddate = startdate fillers = TrainingMesoCycle.objects.filter( - plan=self.plan,type='filler') - for f in fillers: # pragma: no cover + plan=self.plan, type='filler') + for f in fillers: # pragma: no cover f.delete() - if self.enddate > self.plan.enddate: # pragma: no cover + if self.enddate > self.plan.enddate: # pragma: no cover self.enddate = self.plan.enddate - if self.startdate < self.plan.startdate: # pragma: no cover + if self.startdate < self.plan.startdate: # pragma: no cover self.startdate = self.plan.startdate othercycles = TrainingMesoCycle.objects.filter( plan=self.plan).exclude(pk=self.pk).order_by("-startdate") - for othercycle in othercycles: # pragma: no cover + for othercycle in othercycles: # pragma: no cover if othercycle.startdate <= self.enddate and othercycle.startdate >= self.startdate: self.enddate = othercycle.startdate-datetime.timedelta(days=1) @@ -2197,82 +2290,80 @@ class TrainingMesoCycle(models.Model): self.startdate = othercycle.enddate+datetime.timedelta(days=1) if not self.enddate <= self.startdate: - super(TrainingMesoCycle,self).save(*args, **kwargs) - else: # pragma: no cover + super(TrainingMesoCycle, self).save(*args, **kwargs) + else: # pragma: no cover self.enddate = self.startdate - super(TrainingMesoCycle,self).save(*args, **kwargs) + super(TrainingMesoCycle, self).save(*args, **kwargs) - microcycles = TrainingMicroCycle.objects.filter(plan = self) + microcycles = TrainingMicroCycle.objects.filter(plan=self) if not microcycles: micro = TrainingMicroCycle( - plan = self, - name = 'Filler', - startdate = self.startdate, - enddate = self.enddate, - ) + plan=self, + name='Filler', + startdate=self.startdate, + enddate=self.enddate, + ) micro.save() - else: # pragma: no cover + else: # pragma: no cover createmicrofillers(self) - class TrainingMicroCycle(models.Model): - plan = models.ForeignKey(TrainingMesoCycle,on_delete=models.CASCADE) - name = models.CharField(max_length=150,blank=True) + plan = models.ForeignKey(TrainingMesoCycle, on_delete=models.CASCADE) + name = models.CharField(max_length=150, blank=True) startdate = models.DateField(default=current_day) enddate = models.DateField( default=half_year_from_now) - notes = models.TextField(max_length=300,blank=True) + notes = models.TextField(max_length=300, blank=True) type = models.CharField(default='filler', choices=cycletypechoices, max_length=150) - plantime = models.IntegerField(default=0,verbose_name='Planned Duration') - plandistance = models.IntegerField(default=0,verbose_name='Planned Distance') - planrscore = models.IntegerField(default=0,verbose_name='Planned rScore') - plantrimp = models.IntegerField(default=0,verbose_name='Planned TRIMP') + plantime = models.IntegerField(default=0, verbose_name='Planned Duration') + plandistance = models.IntegerField( + default=0, verbose_name='Planned Distance') + planrscore = models.IntegerField(default=0, verbose_name='Planned rScore') + plantrimp = models.IntegerField(default=0, verbose_name='Planned TRIMP') - actualtime = models.IntegerField(default=0,verbose_name='Actual Duration') - actualdistance = models.IntegerField(default=0,verbose_name='Actual Distance') - actualrscore = models.IntegerField(default=0,verbose_name='Actual rScore') - actualtrimp = models.IntegerField(default=0,verbose_name='Actual TRIMP') + actualtime = models.IntegerField(default=0, verbose_name='Actual Duration') + actualdistance = models.IntegerField( + default=0, verbose_name='Actual Distance') + actualrscore = models.IntegerField(default=0, verbose_name='Actual rScore') + actualtrimp = models.IntegerField(default=0, verbose_name='Actual TRIMP') - - - - def __str__(self): # pragma: no cover + def __str__(self): # pragma: no cover stri = 'Micro Cycle - {n} ({sd} - {ed})'.format( - n = self.name, - sd = self.startdate, - ed = self.enddate, - ) + n=self.name, + sd=self.startdate, + ed=self.enddate, + ) return stri def save(self, *args, **kwargs): - if self.enddate < self.startdate: # pragma: no cover + if self.enddate < self.startdate: # pragma: no cover startdate = self.startdate enddate = self.enddate self.startdate = enddate self.enddate = startdate fillers = TrainingMicroCycle.objects.filter( - plan=self.plan,type='filler') - for f in fillers: # pragma: no cover + plan=self.plan, type='filler') + for f in fillers: # pragma: no cover f.delete() - if self.enddate > self.plan.enddate: # pragma: no cover + if self.enddate > self.plan.enddate: # pragma: no cover self.enddate = self.plan.enddate - if self.startdate < self.plan.startdate: # pragma: no cover + if self.startdate < self.plan.startdate: # pragma: no cover self.startdate = self.plan.startdate othercycles = TrainingMicroCycle.objects.filter( plan=self.plan).exclude(pk=self.pk).order_by("-startdate") - for othercycle in othercycles: # pragma: no cover + for othercycle in othercycles: # pragma: no cover if othercycle.startdate <= self.enddate and othercycle.startdate >= self.startdate: self.enddate = othercycle.startdate-datetime.timedelta(days=1) @@ -2280,97 +2371,101 @@ class TrainingMicroCycle(models.Model): self.startdate = othercycle.enddate+datetime.timedelta(days=1) if not self.enddate < self.startdate: - super(TrainingMicroCycle,self).save(*args, **kwargs) + super(TrainingMicroCycle, self).save(*args, **kwargs) + class TrainingMesoCycleForm(ModelForm): class Meta: model = TrainingMesoCycle - fields = ['name','startdate','enddate','notes'] + fields = ['name', 'startdate', 'enddate', 'notes'] widgets = { 'startdate': AdminDateWidget(), 'enddate': AdminDateWidget() - } + } + class TrainingMicroCycleForm(ModelForm): class Meta: model = TrainingMicroCycle - fields = ['name','startdate','enddate','notes'] + fields = ['name', 'startdate', 'enddate', 'notes'] widgets = { 'startdate': AdminDateWidget(), 'enddate': AdminDateWidget() - } + } + regularsessiontypechoices = ( - ('session','Training Session'), - ('challenge','Challenge'), - ('test','Mandatory Test'), - ('cycletarget','Total for a time period'), - ('coursetest','OTW test over a course'), - ('fastest_distance','Finds fastest time over a given distance on the water'), - ('fastest_time','Finds largest distance rowed on the water over a given time'), + ('session', 'Training Session'), + ('challenge', 'Challenge'), + ('test', 'Mandatory Test'), + ('cycletarget', 'Total for a time period'), + ('coursetest', 'OTW test over a course'), + ('fastest_distance', 'Finds fastest time over a given distance on the water'), + ('fastest_time', 'Finds largest distance rowed on the water over a given time'), ) # model for Planned Session (Workout, Challenge, Test) + class PlannedSession(models.Model): sessiontypechoices = ( - ('session','Training Session'), - ('challenge','Challenge'), - ('test','Mandatory Test'), - ('cycletarget','Total for a time period'), - ('coursetest','OTW test over a course'), - ('fastest_distance','Finds fastest time over a given distance on the water'), - ('fastest_time','Finds largest distance rowed on the water over a given time'), - ('race','Virtual challenge'), - ('indoorrace','Indoor Virtual challenge'), - ) + ('session', 'Training Session'), + ('challenge', 'Challenge'), + ('test', 'Mandatory Test'), + ('cycletarget', 'Total for a time period'), + ('coursetest', 'OTW test over a course'), + ('fastest_distance', 'Finds fastest time over a given distance on the water'), + ('fastest_time', 'Finds largest distance rowed on the water over a given time'), + ('race', 'Virtual challenge'), + ('indoorrace', 'Indoor Virtual challenge'), + ) regularsessiontypechoices = ( - ('session','Training Session'), - ('challenge','Challenge'), - ('test','Mandatory Test'), - ('cycletarget','Total for a time period'), - ('coursetest','OTW test over a course'), - ('fastest_distance','Finds fastest time over a given distance on the water'), - ('fastest_time','Finds largest distance rowed on the water over a given time'), + ('session', 'Training Session'), + ('challenge', 'Challenge'), + ('test', 'Mandatory Test'), + ('cycletarget', 'Total for a time period'), + ('coursetest', 'OTW test over a course'), + ('fastest_distance', 'Finds fastest time over a given distance on the water'), + ('fastest_time', 'Finds largest distance rowed on the water over a given time'), ) sessionmodechoices = ( - ('distance','Distance'), - ('time','Time'), - ('rScore','rScore'), - ('TRIMP','TRIMP'), - ) + ('distance', 'Distance'), + ('time', 'Time'), + ('rScore', 'rScore'), + ('TRIMP', 'TRIMP'), + ) criteriumchoices = ( - ('none','Approximately'), - ('minimum','At Least'), - ('exact','Exactly'), - ) + ('none', 'Approximately'), + ('minimum', 'At Least'), + ('exact', 'Exactly'), + ) verificationchoices = ( - ('none','None'), - ('automatic','Automatic'), - ('manual','Manual') - ) + ('none', 'None'), + ('automatic', 'Automatic'), + ('manual', 'Manual') + ) sessionunitchoices = ( - ('min','minutes'), - ('m','meters'), - ('None',None), - ) + ('min', 'minutes'), + ('m', 'meters'), + ('None', None), + ) - manager = models.ForeignKey(User,on_delete=models.PROTECT) - course = models.ForeignKey(GeoCourse,blank=True,null=True, - verbose_name='OTW Course',on_delete=models.SET_NULL) + manager = models.ForeignKey(User, on_delete=models.PROTECT) + course = models.ForeignKey(GeoCourse, blank=True, null=True, + verbose_name='OTW Course', on_delete=models.SET_NULL) - name = models.CharField(max_length=150,blank=True, + name = models.CharField(max_length=150, blank=True, verbose_name='Name') - comment = models.TextField(max_length=1000,blank=True, + comment = models.TextField(max_length=1000, blank=True, ) startdate = models.DateField(default=current_day, @@ -2387,21 +2482,24 @@ class PlannedSession(models.Model): max_length=150, verbose_name='Session Type') - sessionsport = models.CharField(default='water',choices=mytypes.workouttypes, - max_length=50,verbose_name='Sport') + sessionsport = models.CharField(default='water', choices=mytypes.workouttypes, + max_length=50, verbose_name='Sport') - sessionvalue = models.IntegerField(default=60,verbose_name='Value') + sessionvalue = models.IntegerField(default=60, verbose_name='Value') - approximate_distance = models.IntegerField(default=0,verbose_name='Approximate Distance') - approximate_duration = models.IntegerField(default=0,verbose_name='Approximate Duration') - approximate_rscore = models.IntegerField(default=0,verbose_name='Approximate rScore') + approximate_distance = models.IntegerField( + default=0, verbose_name='Approximate Distance') + approximate_duration = models.IntegerField( + default=0, verbose_name='Approximate Duration') + approximate_rscore = models.IntegerField( + default=0, verbose_name='Approximate rScore') max_nr_of_workouts = models.IntegerField( - default=0,verbose_name='Maximum number of workouts' - ) + default=0, verbose_name='Maximum number of workouts' + ) sessionunit = models.CharField( - default='min',choices=sessionunitchoices, + default='min', choices=sessionunitchoices, max_length=150, verbose_name='Unit') @@ -2417,8 +2515,8 @@ class PlannedSession(models.Model): choices=verificationchoices ) - team = models.ManyToManyField(Team,blank=True) - rower = models.ManyToManyField(Rower,blank=True) + team = models.ManyToManyField(Team, blank=True) + rower = models.ManyToManyField(Rower, blank=True) sessionmode = models.CharField(default='time', choices=sessionmodechoices, @@ -2431,10 +2529,10 @@ class PlannedSession(models.Model): is_public = models.BooleanField(default=False) can_be_shared = models.BooleanField(default=True) - fitfile = models.FileField(upload_to=get_file_path,blank=True,null=True) + fitfile = models.FileField(upload_to=get_file_path, blank=True, null=True) #steps_json = models.TextField(max_length=10000,default=None,blank=True,null=True) - steps = PlannedSessionStepField(default={},null=True,max_length=1000) - interval_string = models.TextField(max_length=1000,default=None,blank=True,null=True, + steps = PlannedSessionStepField(default={}, null=True, max_length=1000) + interval_string = models.TextField(max_length=1000, default=None, blank=True, null=True, verbose_name='Interval String (optional)') garmin_workout_id = models.BigIntegerField(default=0) garmin_schedule_id = models.BigIntegerField(default=0) @@ -2448,14 +2546,14 @@ class PlannedSession(models.Model): enddate = self.enddate stri = u'{n} {s} - {e}'.format( - s = startdate.strftime('%Y-%m-%d'), - e = enddate.strftime('%Y-%m-%d'), - n = name, + s=startdate.strftime('%Y-%m-%d'), + e=enddate.strftime('%Y-%m-%d'), + n=name, ) return stri - def update_steps(self): # pragma: no cover + def update_steps(self): # pragma: no cover # read file if self.fitfile: steps = steps_read_fit(settings.MEDIA_ROOT+'/'+self.fitfile.name) @@ -2463,21 +2561,19 @@ class PlannedSession(models.Model): self.save() - def save(self, *args, **kwargs): - if self.sessionvalue <= 0: # pragma: no cover + if self.sessionvalue <= 0: # pragma: no cover self.sessionvalue = 1 - manager = self.manager - if self.sessiontype not in ['race','indoorrace']: + if self.sessiontype not in ['race', 'indoorrace']: if not can_add_session(self.manager): raise ValidationError( "You must be a Self-Coach user or higher to create a planned session" ) # interval string - if self.interval_string: # pragma: no cover + if self.interval_string: # pragma: no cover try: dct = trainingparser.parsetodict(self.interval_string) dct = [item for item in dct if item['value'] != 0] @@ -2496,19 +2592,19 @@ class PlannedSession(models.Model): # sort units if self.sessionmode == 'distance': - if self.sessionunit not in ['m','km']: # pragma: no cover + if self.sessionunit not in ['m', 'km']: # pragma: no cover self.sessionunit = 'm' elif self.sessionmode == 'time': self.sessionunit = 'min' else: self.sessionunit = 'None' - if self.sessiontype in ['test','indoorrace','fastest_distance','fastest_time']: - if self.sessionmode not in ['distance','time']: + if self.sessiontype in ['test', 'indoorrace', 'fastest_distance', 'fastest_time']: + if self.sessionmode not in ['distance', 'time']: if self.sessionvalue < 100: self.sessionmode = 'time' self.sessionunit = 'min' - else: # pragma: no cover + else: # pragma: no cover self.sessionmode = 'distance' self.sessionunit = 'm' self.criterium = 'exact' @@ -2516,122 +2612,124 @@ class PlannedSession(models.Model): self.sessionmode = 'distance' self.sessionunit = 'm' self.criterium = 'none' - if self.course == None: # pragma: no cover + if self.course == None: # pragma: no cover self.course = GeoCourse.objects.all()[0] self.sessionvalue = self.course.distance elif self.sessiontype != 'coursetest' and self.sessiontype != 'race': self.course = None - if self.enddate < self.startdate: # pragma: no cover + if self.enddate < self.startdate: # pragma: no cover self.enddate = self.startdate - if self.preferreddate > self.enddate: # pragma: no cover + if self.preferreddate > self.enddate: # pragma: no cover self.preferreddate = self.enddate - if self.preferreddate < self.startdate: # pragma: no cover + if self.preferreddate < self.startdate: # pragma: no cover self.preferreddate = self.startdate #super(PlannedSession,self).save(*args, **kwargs) if self.steps: steps = self.steps - elif self.fitfile: # pragma: no cover - steps = steps_read_fit(os.path.join(settings.MEDIA_ROOT,self.fitfile.name)) + elif self.fitfile: # pragma: no cover + steps = steps_read_fit(os.path.join( + settings.MEDIA_ROOT, self.fitfile.name)) self.steps = steps - if self.steps and not self.fitfile: filename = 'aap.fit' - filename = get_file_path(self,filename) - + filename = get_file_path(self, filename) steps = self.steps - steps['filename'] = os.path.join(settings.MEDIA_ROOT,filename) + steps['filename'] = os.path.join(settings.MEDIA_ROOT, filename) fitfile = steps_write_fit(steps) self.fitfile.name = filename self.steps = steps - # calculate approximate distance if self.steps: - sdict, totalmeters, totalseconds, totalrscore = ps_dict_order(self.steps) + sdict, totalmeters, totalseconds, totalrscore = ps_dict_order( + self.steps) self.approximate_distance = int(totalmeters) self.approximate_duration = int(totalseconds/60.) self.approximate_rscore = int(totalrscore) self.criterium = 'none' if self.sessionmode == 'time': self.sessionvalue = self.approximate_duration - elif self.sessionmode == 'distance': # pragma: no cover + elif self.sessionmode == 'distance': # pragma: no cover self.sessionvalue = self.approximate_distance - elif self.sessionmode == 'rscore': # pragma: no cover + elif self.sessionmode == 'rscore': # pragma: no cover self.sessionvalue = self.approximate_rscore + super(PlannedSession, self).save(*args, **kwargs) - super(PlannedSession,self).save(*args, **kwargs) -@receiver(models.signals.post_delete,sender=PlannedSession) +@receiver(models.signals.post_delete, sender=PlannedSession) def auto_delete_fitfile_on_delete(sender, instance, **kwargs): # delete CSV file - if instance.fitfile: # pragma: no cover - filename = os.path.join(settings.MEDIA_ROOT,instance.fitfile.name) + if instance.fitfile: # pragma: no cover + filename = os.path.join(settings.MEDIA_ROOT, instance.fitfile.name) if os.path.isfile(filename): os.remove(filename) -from django.core.validators import RegexValidator,validate_email class StandardCollection(models.Model): name = models.CharField(max_length=150) - manager = models.ForeignKey(User, null=True,on_delete=models.CASCADE) - notes = models.CharField(blank=True,null=True,max_length=1000) + manager = models.ForeignKey(User, null=True, on_delete=models.CASCADE) + notes = models.CharField(blank=True, null=True, max_length=1000) active = models.BooleanField(default=True) def __str__(self): return self.name + class CourseStandard(models.Model): - name = models.CharField(max_length=150,default='') + name = models.CharField(max_length=150, default='') coursedistance = models.IntegerField(default=0) - coursetime = models.CharField(max_length=100,default="") - referencespeed = models.FloatField(default=5.0) # average boat speed + coursetime = models.CharField(max_length=100, default="") + referencespeed = models.FloatField(default=5.0) # average boat speed agemin = models.IntegerField(default=0) agemax = models.IntegerField(default=120) - boatclass = models.CharField(max_length=150,default='water') # corresponds to workout workouttype - boattype = models.CharField(choices=mytypes.boattypes,max_length=50,default='1x') - sex = models.CharField(max_length=150,default='male') - weightclass = models.CharField(max_length=150,default='hwt') - adaptiveclass = models.CharField(choices=mytypes.adaptivetypes,max_length=50,default="None") - skillclass = models.CharField(max_length=150,default='Open') - standardcollection = models.ForeignKey(StandardCollection,on_delete=models.CASCADE,related_name='standards') + # corresponds to workout workouttype + boatclass = models.CharField(max_length=150, default='water') + boattype = models.CharField( + choices=mytypes.boattypes, max_length=50, default='1x') + sex = models.CharField(max_length=150, default='male') + weightclass = models.CharField(max_length=150, default='hwt') + adaptiveclass = models.CharField( + choices=mytypes.adaptivetypes, max_length=50, default="None") + skillclass = models.CharField(max_length=150, default='Open') + standardcollection = models.ForeignKey( + StandardCollection, on_delete=models.CASCADE, related_name='standards') class Meta: unique_together = ( - ('name','standardcollection') - ) - + ('name', 'standardcollection') + ) def __str__(self): return self.name + registerchoices = ( - ('windowstart','Start of challenge Window'), - ('windowend','End of challenge Window'), - ('deadline','Evaluation Closure Deadline'), - ('manual','Manual - select below'), - ) + ('windowstart', 'Start of challenge Window'), + ('windowend', 'End of challenge Window'), + ('deadline', 'Evaluation Closure Deadline'), + ('manual', 'Manual - select below'), +) class VirtualRace(PlannedSession): -# has_registration = models.BooleanField(default=False) + # has_registration = models.BooleanField(default=False) registration_form = models.CharField( max_length=100, default='windowstart', choices=registerchoices, verbose_name='Registration Closure Quick Selector' ) - registration_closure = models.DateTimeField(blank=True,null=True) - evaluation_closure = models.DateTimeField(blank=True,null=True) - start_time = models.TimeField(blank=True,null=True) - end_time = models.TimeField(blank=True,null=True) - country = models.CharField(max_length=100,blank=True) - + registration_closure = models.DateTimeField(blank=True, null=True) + evaluation_closure = models.DateTimeField(blank=True, null=True) + start_time = models.TimeField(blank=True, null=True) + end_time = models.TimeField(blank=True, null=True) + country = models.CharField(max_length=100, blank=True) timezone = models.CharField(default='UTC', choices=timezones, @@ -2642,13 +2740,14 @@ class VirtualRace(PlannedSession): message="Phone number must be entered in the format: '+999999999'. Up to 15 digits allowed." ) - contact_phone = models.CharField(validators=[phone_regex], max_length=17, blank=True) + contact_phone = models.CharField( + validators=[phone_regex], max_length=17, blank=True) contact_email = models.EmailField(max_length=254, - validators=[validate_email],blank=True) + validators=[validate_email], blank=True) - coursestandards = models.ForeignKey(StandardCollection,null=True,on_delete=models.SET_NULL, - verbose_name='Standard Times',blank=True,default=None) + coursestandards = models.ForeignKey(StandardCollection, null=True, on_delete=models.SET_NULL, + verbose_name='Standard Times', blank=True, default=None) def __str__(self): @@ -2657,62 +2756,64 @@ class VirtualRace(PlannedSession): enddate = self.enddate stri = u'Virtual challenge {n}'.format( - n = name, + n=name, ) return stri - def save(self, *args, **kwargs): # test race window logic start_time = self.start_time start_date = self.startdate - startdatetime = datetime.datetime.combine(start_date,start_time) + startdatetime = datetime.datetime.combine(start_date, start_time) startdatetime = pytz.timezone(self.timezone).localize( startdatetime ) end_time = self.end_time end_date = self.enddate - enddatetime = datetime.datetime.combine(end_date,end_time) + enddatetime = datetime.datetime.combine(end_date, end_time) enddatetime = pytz.timezone(self.timezone).localize( enddatetime ) - if startdatetime > enddatetime: # pragma: no cover + if startdatetime > enddatetime: # pragma: no cover self.start_time = end_time self.startdate = end_date self.end_time = start_time self.enddate = start_date enddatetime = startdatetime - - if self.evaluation_closure < enddatetime: # pragma: no cover + if self.evaluation_closure < enddatetime: # pragma: no cover self.evaluation_closure = enddatetime + timezone.timedelta(days=1) - super(VirtualRace,self).save(*args, **kwargs) + super(VirtualRace, self).save(*args, **kwargs) + class RaceLogo(models.Model): - filename = models.CharField(default='',max_length=150) + filename = models.CharField(default='', max_length=150) creationdatetime = models.DateTimeField() - user = models.ForeignKey(User,on_delete=models.PROTECT) + user = models.ForeignKey(User, on_delete=models.PROTECT) width = models.IntegerField(default=1200) height = models.IntegerField(default=600) - race = models.ManyToManyField(VirtualRace,related_name='logos') + race = models.ManyToManyField(VirtualRace, related_name='logos') - def __str__(self): # pragma: no cover + def __str__(self): # pragma: no cover return self.filename - def delete(self, *args, **kwargs): # pragma: no cover + def delete(self, *args, **kwargs): # pragma: no cover os.remove(self.filename) - super(RaceLogo,self).delete(*args, **kwargs) + super(RaceLogo, self).delete(*args, **kwargs) # Date input utility + + class DateInput(forms.DateInput): input_type = 'date' + class PlannedSessionForm(ModelForm): class Meta: @@ -2731,42 +2832,43 @@ class PlannedSessionForm(ModelForm): 'comment', 'interval_string', 'fitfile', - ] + ] dateTimeOptions = { 'format': 'yyyy-mm-dd', 'autoclose': True, - } + } widgets = { 'comment': forms.Textarea, 'startdate': AdminDateWidget(), 'enddate': AdminDateWidget(), 'preferreddate': AdminDateWidget(), - 'interval_string': forms.Textarea(attrs={'rows':2,'cols':50}), - #'sessiontype': forms.Select(attrs={'style':'width:50px'}) + 'interval_string': forms.Textarea(attrs={'rows': 2, 'cols': 50}), + # 'sessiontype': forms.Select(attrs={'style':'width:50px'}) } - def __init__(self,*args,**kwargs): + def __init__(self, *args, **kwargs): super(PlannedSessionForm, self).__init__(*args, **kwargs) - self.fields['course'].queryset = GeoCourse.objects.all().order_by("country","name") + self.fields['course'].queryset = GeoCourse.objects.all().order_by( + "country", "name") self.fields['sessiontype'].choices = regularsessiontypechoices + class VirtualRaceAthleteForm(ModelForm): class Meta: model = PlannedSession fields = ['rower'] - widgets = { - 'rower':forms.CheckboxSelectMultiple, + 'rower': forms.CheckboxSelectMultiple, } def __init__(self, *args, **kwargs): super(VirtualRaceAthleteForm, self).__init__(*args, **kwargs) self.fields['rower'].queryset = self.instance.rower.all() - self.fields['subject']= forms.CharField(max_length=255) + self.fields['subject'] = forms.CharField(max_length=255) self.fields['message'] = forms.CharField(widget=forms.Textarea()) @@ -2775,59 +2877,65 @@ class PlannedSessionTemplateForm(ModelForm): class Meta: model = PlannedSession fields = [ - 'name', - 'sessionsport', - 'sessiontype', - 'sessionmode', - 'criterium', - 'sessionvalue', - 'sessionunit', - 'course', - 'comment', - 'interval_string', - 'fitfile', - 'tags', + 'name', + 'sessionsport', + 'sessiontype', + 'sessionmode', + 'criterium', + 'sessionvalue', + 'sessionunit', + 'course', + 'comment', + 'interval_string', + 'fitfile', + 'tags', ] dateTimeOptions = { 'format': 'yyyy-mm-dd', 'autoclose': True, - } + } widgets = { 'comment': forms.Textarea, - 'interval_string':forms.Textarea(attrs={'rows':2, 'cols':50}) + 'interval_string': forms.Textarea(attrs={'rows': 2, 'cols': 50}) } - def __init__(self,*args,**kwargs): + def __init__(self, *args, **kwargs): super(PlannedSessionTemplateForm, self).__init__(*args, **kwargs) - self.fields['course'].queryset = GeoCourse.objects.all().order_by("country","name") + 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) - points = GeoPoint.objects.filter(polygon = polygons[0]) + polygons = GeoPolygon.objects.filter(course=course) + points = GeoPoint.objects.filter(polygon=polygons[0]) lat = points[0].latitude lon = points[0].longitude tf = TimezoneFinder() try: - timezone_str = tf.timezone_at(lng=lon,lat=lat) - except ValueError: # pragma: no cover + timezone_str = tf.timezone_at(lng=lon, lat=lat) + except ValueError: # pragma: no cover timezone_str = 'UTC' - if timezone_str is None: # pragma: no cover - timezone_str = tf.closest_timezone_at(lng=lon,lat=lat) + if timezone_str is None: # pragma: no cover + timezone_str = tf.closest_timezone_at(lng=lon, lat=lat) if timezone_str is None: timezone_str = 'UTC' return timezone_str + class IndoorVirtualRaceForm(ModelForm): - registration_closure = forms.SplitDateTimeField(widget=AdminSplitDateTime(),required=False) - evaluation_closure = forms.SplitDateTimeField(widget=AdminSplitDateTime(),required=True) + registration_closure = forms.SplitDateTimeField( + widget=AdminSplitDateTime(), required=False) + evaluation_closure = forms.SplitDateTimeField( + widget=AdminSplitDateTime(), required=True) timezone = forms.ChoiceField(initial='UTC', - choices=[(x,x) for x in pytz.common_timezones], + choices=[(x, x) + for x in pytz.common_timezones], label='Time Zone') class Meta: @@ -2848,7 +2956,7 @@ class IndoorVirtualRaceForm(ModelForm): 'coursestandards', 'contact_phone', 'contact_email', - ] + ] dateTimeOptions = { 'format': 'yyyy-mm-dd', @@ -2861,60 +2969,62 @@ class IndoorVirtualRaceForm(ModelForm): 'enddate': AdminDateWidget(), 'start_time': AdminTimeWidget(), 'end_time': AdminTimeWidget(), - 'registration_closure':AdminSplitDateTime(), - 'evaluation_closure':AdminSplitDateTime(), - } + 'registration_closure': AdminSplitDateTime(), + 'evaluation_closure': AdminSplitDateTime(), + } labels = { 'sessionunit': 'Meters or minutes', 'sessionvalue': 'How far or how long' - } + } - - def __init__(self,*args,**kwargs): - timezone = kwargs.pop('timezone',None) + def __init__(self, *args, **kwargs): + timezone = kwargs.pop('timezone', None) super(IndoorVirtualRaceForm, self).__init__(*args, **kwargs) - self.fields['sessionunit'].choices = [('min','minutes'),('m','meters')] + self.fields['sessionunit'].choices = [ + ('min', 'minutes'), ('m', 'meters')] self.fields['sessionvalue'].initial = 2000 self.fields['sessionunit'].initial = 'm' if timezone: self.fields['timezone'].initial = timezone - self.fields['coursestandards'].queryset = StandardCollection.objects.filter(active=True) + self.fields['coursestandards'].queryset = StandardCollection.objects.filter( + active=True) def clean(self): cd = self.cleaned_data timezone_str = cd['timezone'] value = cd['sessionvalue'] - if value <= 0: # pragma: no cover - raise forms.ValidationError('The Value must be a positive, non-zero value') + if value <= 0: # pragma: no cover + raise forms.ValidationError( + 'The Value must be a positive, non-zero value') unit = cd['sessionunit'] - if unit == 'm' and value < 100: # pragma: no cover + if unit == 'm' and value < 100: # pragma: no cover raise forms.ValidationError('Minimum distance is 100m') start_time = cd['start_time'] - if start_time is None: # pragma: no cover + if start_time is None: # pragma: no cover raise forms.ValidationError( - 'Must have start time', - code='missing_yparam1' - ) + 'Must have start time', + code='missing_yparam1' + ) start_date = cd['startdate'] - startdatetime = datetime.datetime.combine(start_date,start_time) + startdatetime = datetime.datetime.combine(start_date, start_time) startdatetime = pytz.timezone(timezone_str).localize( startdatetime ) end_time = cd['end_time'] - if end_time is None: # pragma: no cover + if end_time is None: # pragma: no cover raise forms.ValidationError( 'Must have end time', code='missing endtime' - ) + ) end_date = cd['enddate'] - enddatetime = datetime.datetime.combine(end_date,end_time) + enddatetime = datetime.datetime.combine(end_date, end_time) enddatetime = pytz.timezone(timezone_str).localize( enddatetime ) @@ -2925,11 +3035,11 @@ class IndoorVirtualRaceForm(ModelForm): try: evaluation_closure = cd['evaluation_closure'] - except KeyError: # pragma: no cover + except KeyError: # pragma: no cover evaluation_closure = enddatetime+datetime.timedelta(days=1) cd['evaluation_closure'] = evaluation_closure - if registration_form == 'manual': # pragma: no cover + if registration_form == 'manual': # pragma: no cover try: registration_closure = pytz.timezone( timezone_str @@ -2938,39 +3048,41 @@ class IndoorVirtualRaceForm(ModelForm): ) except AttributeError: registration_closure = startdatetime - elif registration_form == 'windowstart': # pragma: no cover + elif registration_form == 'windowstart': # pragma: no cover registration_closure = startdatetime - elif registration_form == 'windowend': # pragma: no cover + elif registration_form == 'windowend': # pragma: no cover registration_closure = enddatetime else: registration_closure = evaluation_closure + if registration_closure <= timezone.now(): # pragma: no cover + raise forms.ValidationError( + "Registration Closure cannot be in the past") - if registration_closure <= timezone.now(): # pragma: no cover - raise forms.ValidationError("Registration Closure cannot be in the past") + if startdatetime > enddatetime: # pragma: no cover + raise forms.ValidationError( + "The Start of the challenge Window should be before the End of the challenge Window") + if cd['evaluation_closure'] <= enddatetime: # pragma: no cover + raise forms.ValidationError( + "Evaluation closure deadline should be after the challenge Window closes") - if startdatetime > enddatetime: # pragma: no cover - raise forms.ValidationError("The Start of the challenge Window should be before the End of the challenge Window") - - - if cd['evaluation_closure'] <= enddatetime: # pragma: no cover - raise forms.ValidationError("Evaluation closure deadline should be after the challenge Window closes") - - if cd['evaluation_closure'] <= timezone.now(): # pragma: no cover - raise forms.ValidationError("Evaluation closure cannot be in the past") - + if cd['evaluation_closure'] <= timezone.now(): # pragma: no cover + raise forms.ValidationError( + "Evaluation closure cannot be in the past") return cd class VirtualRaceForm(ModelForm): course = GroupedModelChoiceField( - queryset = GeoCourse.objects, empty_label=None, + queryset=GeoCourse.objects, empty_label=None, choices_groupby='country' - ) - registration_closure = forms.SplitDateTimeField(widget=AdminSplitDateTime(),required=False) - evaluation_closure = forms.SplitDateTimeField(widget=AdminSplitDateTime(),required=True) + ) + registration_closure = forms.SplitDateTimeField( + widget=AdminSplitDateTime(), required=False) + evaluation_closure = forms.SplitDateTimeField( + widget=AdminSplitDateTime(), required=True) class Meta: model = VirtualRace @@ -2980,7 +3092,7 @@ class VirtualRaceForm(ModelForm): 'start_time', 'enddate', 'end_time', -# 'has_registration', + # 'has_registration', 'registration_form', 'registration_closure', 'evaluation_closure', @@ -2994,7 +3106,7 @@ class VirtualRaceForm(ModelForm): dateTimeOptions = { 'format': 'yyyy-mm-dd', 'autoclose': True, - } + } widgets = { 'comment': forms.Textarea, @@ -3002,15 +3114,16 @@ class VirtualRaceForm(ModelForm): 'enddate': AdminDateWidget(), 'start_time': AdminTimeWidget(), 'end_time': AdminTimeWidget(), - 'registration_closure':AdminSplitDateTime(), - 'evaluation_closure':AdminSplitDateTime(), + 'registration_closure': AdminSplitDateTime(), + 'evaluation_closure': AdminSplitDateTime(), } - def __init__(self,*args,**kwargs): + def __init__(self, *args, **kwargs): super(VirtualRaceForm, self).__init__(*args, **kwargs) - self.fields['course'].queryset = GeoCourse.objects.all().order_by("country","name") - self.fields['coursestandards'].queryset = StandardCollection.objects.filter(active=True) - + self.fields['course'].queryset = GeoCourse.objects.all().order_by( + "country", "name") + self.fields['coursestandards'].queryset = StandardCollection.objects.filter( + active=True) def clean(self): cd = self.cleaned_data @@ -3019,51 +3132,48 @@ class VirtualRaceForm(ModelForm): timezone_str = get_course_timezone(geocourse) start_time = cd['start_time'] - if start_time is None: # pragma: no cover + if start_time is None: # pragma: no cover raise forms.ValidationError( - 'Must have start time', - code='missing_yparam1' - ) + 'Must have start time', + code='missing_yparam1' + ) start_date = cd['startdate'] - startdatetime = datetime.datetime.combine(start_date,start_time) + startdatetime = datetime.datetime.combine(start_date, start_time) startdatetime = pytz.timezone(timezone_str).localize( startdatetime ) try: end_time = cd['end_time'] - except KeyError: # pragma: no cover - raise forms.ValidationError( - 'Must have end time', - code='missing endtime' - ) - - - if end_time is None: # pragma: no cover + except KeyError: # pragma: no cover raise forms.ValidationError( 'Must have end time', code='missing endtime' - ) + ) + + if end_time is None: # pragma: no cover + raise forms.ValidationError( + 'Must have end time', + code='missing endtime' + ) try: end_date = cd['enddate'] - except KeyError: # pragma: no cover + except KeyError: # pragma: no cover raise forms.ValidationError( 'Missing or invalid end date', code='missing end date' ) - - - enddatetime = datetime.datetime.combine(end_date,end_time) + enddatetime = datetime.datetime.combine(end_date, end_time) enddatetime = pytz.timezone(timezone_str).localize( enddatetime ) try: registration_closure = cd['registration_closure'] - except KeyError: # pragma: no cover + except KeyError: # pragma: no cover registration_closure = enddatetime+datetime.timedelta(days=1) cd['registration_closure'] = registration_closure @@ -3071,11 +3181,11 @@ class VirtualRaceForm(ModelForm): try: evaluation_closure = cd['evaluation_closure'] - except KeyError: # pragma: no cover + except KeyError: # pragma: no cover evaluation_closure = enddatetime+datetime.timedelta(days=1) cd['evaluation_closure'] = evaluation_closure - if registration_form == 'manual': # pragma: no cover + if registration_form == 'manual': # pragma: no cover try: registration_closure = pytz.timezone( timezone_str @@ -3084,40 +3194,41 @@ class VirtualRaceForm(ModelForm): ) except AttributeError: registration_closure = startdatetime - elif registration_form == 'windowstart': # pragma: no cover + elif registration_form == 'windowstart': # pragma: no cover registration_closure = startdatetime - elif registration_form == 'windowend': # pragma: no cover + elif registration_form == 'windowend': # pragma: no cover registration_closure = enddatetime else: registration_closure = evaluation_closure + if registration_closure <= timezone.now(): # pragma: no cover + raise forms.ValidationError( + "Registration Closure cannot be in the past") - if registration_closure <= timezone.now(): # pragma: no cover - raise forms.ValidationError("Registration Closure cannot be in the past") + if startdatetime > enddatetime: # pragma: no cover + raise forms.ValidationError( + "The Start of the challenge Window should be before the End of the challenge Window") + if cd['evaluation_closure'] <= enddatetime: # pragma: no cover + raise forms.ValidationError( + "Evaluation closure deadline should be after the challenge Window closes") - if startdatetime > enddatetime: # pragma: no cover - raise forms.ValidationError("The Start of the challenge Window should be before the End of the challenge Window") - - - if cd['evaluation_closure'] <= enddatetime: # pragma: no cover - raise forms.ValidationError("Evaluation closure deadline should be after the challenge Window closes") - - if cd['evaluation_closure'] <= timezone.now(): # pragma: no cover - raise forms.ValidationError("Evaluation closure cannot be in the past") - + if cd['evaluation_closure'] <= timezone.now(): # pragma: no cover + raise forms.ValidationError( + "Evaluation closure cannot be in the past") 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'), - ('fastest_distance','Finds fastest time over a given distance on the water'), - ('fastest_time','Finds largest distance rowed on the water over a given time'), + ('session', 'Training Session'), + ('challenge', 'Challenge'), + ('test', 'Mandatory Test'), + ('cycletarget', 'Total for a time period'), + ('coursetest', 'OTW test over a course'), + ('fastest_distance', 'Finds fastest time over a given distance on the water'), + ('fastest_time', 'Finds largest distance rowed on the water over a given time'), ) class Meta: @@ -3132,32 +3243,35 @@ class PlannedSessionFormSmall(ModelForm): 'sessionvalue', 'sessionunit', 'manager', - ] + ] dateTimeOptions = { 'format': '%Y-%m-%d', 'autoclose': True, - } + } - input_formats=('%Y-%m-%d') + input_formats = ('%Y-%m-%d') widgets = { - 'startdate': DateInput(attrs={'size':10, 'class':'datepicker'}, format='%Y-%m-%d'), - 'enddate': DateInput(attrs={'size':10, 'class':'datepicker'}, format='%Y-%m-%d'), - 'preferreddate': DateInput(attrs={'size':10, 'class':'datepicker'}, format='%Y-%m-%d'), - 'name': forms.TextInput(attrs={'size':10}), - 'sessionvalue': forms.TextInput(attrs={'style':'width:5em', - 'type':'number'}), + 'startdate': DateInput(attrs={'size': 10, 'class': 'datepicker'}, format='%Y-%m-%d'), + 'enddate': DateInput(attrs={'size': 10, 'class': 'datepicker'}, format='%Y-%m-%d'), + 'preferreddate': DateInput(attrs={'size': 10, 'class': 'datepicker'}, format='%Y-%m-%d'), + 'name': forms.TextInput(attrs={'size': 10}), + 'sessionvalue': forms.TextInput(attrs={'style': 'width:5em', + 'type': 'number'}), 'manager': forms.HiddenInput(), } - def __init__(self,*args,**kwargs): + def __init__(self, *args, **kwargs): super(PlannedSessionFormSmall, self).__init__(*args, **kwargs) self.fields['sessiontype'].choices = regularsessiontypechoices + boattypes = mytypes.boattypes # Workout + + class Workout(models.Model): workouttypes = mytypes.workouttypes workoutsources = mytypes.workoutsources @@ -3165,56 +3279,61 @@ class Workout(models.Model): adaptivetypes = mytypes.adaptivetypes boatbrands = mytypes.boatbrands rpechoices = ( - (0,'Not Specified'), - (1,'1 Very Easy (a walk in the park)'), # 20 TSS / hour - (2,'2 Easy (You breathe normally, it feels comfortable)'), # 30 TSS / hour - (3,'3 Somewhat easy (You can talk easily but did you notice the beautiful clouds?)'), - (4,'4 Moderate (You can talk in short spurts, breathing more labored, this feels just right)'), # 50 TSS/hour - (5,"5 (It's not that painful, you just don't want to be here all day.)"), - (6,'6 Somewhat Hard (You can say a few words if you need to)'), # 70 TSS / hour - (7,'7 Vigorous (This is starting to get painful)'), - (8,"8 Hard (You can barely talk, breathing heavily, hoping you won't have to this that long)"), # 100 TSS / hour - (9,'9 Very Hard (My goodness, please make it stop)'), # 120 TSS / hour - (10,'10 Max Effort (You can barely remember your name, you would rather rip out your toenails than go through this)') # 140 TSS / hour + (0, 'Not Specified'), + (1, '1 Very Easy (a walk in the park)'), # 20 TSS / hour + (2, '2 Easy (You breathe normally, it feels comfortable)'), # 30 TSS / hour + (3, '3 Somewhat easy (You can talk easily but did you notice the beautiful clouds?)'), + # 50 TSS/hour + (4, '4 Moderate (You can talk in short spurts, breathing more labored, this feels just right)'), + (5, "5 (It's not that painful, you just don't want to be here all day.)"), + (6, '6 Somewhat Hard (You can say a few words if you need to)'), # 70 TSS / hour + (7, '7 Vigorous (This is starting to get painful)'), + # 100 TSS / hour + (8, "8 Hard (You can barely talk, breathing heavily, hoping you won't have to this that long)"), + (9, '9 Very Hard (My goodness, please make it stop)'), # 120 TSS / hour + # 140 TSS / hour + (10, '10 Max Effort (You can barely remember your name, you would rather rip out your toenails than go through this)') ) - user = models.ForeignKey(Rower,on_delete=models.CASCADE) - team = models.ManyToManyField(Team,blank=True) - plannedsession = models.ForeignKey(PlannedSession, blank=True,null=True, - verbose_name='Session',on_delete=models.SET_NULL) - name = models.CharField(max_length=150,blank=True,null=True) - date = models.DateField(blank=True,null=True) - workouttype = models.CharField(choices=workouttypes,max_length=50, + user = models.ForeignKey(Rower, on_delete=models.CASCADE) + team = models.ManyToManyField(Team, blank=True) + plannedsession = models.ForeignKey(PlannedSession, blank=True, null=True, + verbose_name='Session', on_delete=models.SET_NULL) + name = models.CharField(max_length=150, blank=True, null=True) + date = models.DateField(blank=True, null=True) + workouttype = models.CharField(choices=workouttypes, max_length=50, verbose_name='Exercise/Boat Class') workoutsource = models.CharField(max_length=100, default='unknown') - boattype = models.CharField(choices=boattypes,max_length=50, + boattype = models.CharField(choices=boattypes, max_length=50, default='1x', - verbose_name = 'Boat Type') - boatbrand = models.CharField(choices=boatbrands,max_length=50, - default='',verbose_name = 'Boat Brand') - adaptiveclass = models.CharField(choices=adaptivetypes,max_length=50, - default='None', - verbose_name='Adaptive Classification') + verbose_name='Boat Type') + boatbrand = models.CharField(choices=boatbrands, max_length=50, + default='', verbose_name='Boat Brand') + adaptiveclass = models.CharField(choices=adaptivetypes, max_length=50, + default='None', + verbose_name='Adaptive Classification') starttime = models.TimeField(default=timezone.now) - startdatetime = models.DateTimeField(blank=True,null=True) + startdatetime = models.DateTimeField(blank=True, null=True) timezone = models.CharField(default='UTC', - #choices=timezones, + # choices=timezones, max_length=100) distance = models.IntegerField(default=0) duration = models.TimeField(blank=True) - dragfactor = models.IntegerField(default=0,blank=True) + dragfactor = models.IntegerField(default=0, blank=True) # scores - trimp = models.IntegerField(default=-1,blank=True) - rscore = models.IntegerField(default=-1,blank=True) - hrtss = models.IntegerField(default=-1,blank=True) - normp = models.IntegerField(default=-1,blank=True) - normv = models.FloatField(default=-1,blank=True) - normw = models.FloatField(default=-1,blank=True) - goldmedalstandard = models.FloatField(default=-1,blank=True,verbose_name='Gold Medal Standard') - goldmedalseconds = models.IntegerField(default=0,blank=True,verbose_name='Gold Medal Seconds') - rpe = models.IntegerField(default=0,blank=True,choices=rpechoices, + trimp = models.IntegerField(default=-1, blank=True) + rscore = models.IntegerField(default=-1, blank=True) + hrtss = models.IntegerField(default=-1, blank=True) + normp = models.IntegerField(default=-1, blank=True) + normv = models.FloatField(default=-1, blank=True) + normw = models.FloatField(default=-1, blank=True) + goldmedalstandard = models.FloatField( + default=-1, blank=True, verbose_name='Gold Medal Standard') + goldmedalseconds = models.IntegerField( + default=0, blank=True, verbose_name='Gold Medal Seconds') + rpe = models.IntegerField(default=0, blank=True, choices=rpechoices, verbose_name='Rate of Perceived Exertion') weightcategory = models.CharField( @@ -3222,11 +3341,12 @@ class Workout(models.Model): max_length=10, choices=weightcategories, verbose_name='Weight Category') - weightvalue = models.FloatField(default=80.0,blank=True,verbose_name = 'Average Crew Weight (kg)') - csvfilename = models.CharField(blank=True,max_length=150) + weightvalue = models.FloatField( + default=80.0, blank=True, verbose_name='Average Crew Weight (kg)') + csvfilename = models.CharField(blank=True, max_length=150) uploadedtoc2 = models.IntegerField(default=0) - averagehr = models.IntegerField(blank=True,null=True) - maxhr = models.BigIntegerField(blank=True,null=True) + averagehr = models.IntegerField(blank=True, null=True) + maxhr = models.BigIntegerField(blank=True, null=True) uploadedtostrava = models.BigIntegerField(default=0) uploadedtosporttracks = models.BigIntegerField(default=0) uploadedtotp = models.BigIntegerField(default=0) @@ -3234,40 +3354,43 @@ class Workout(models.Model): uploadedtorp3 = models.BigIntegerField(default=0) uploadedtonk = models.BigIntegerField(default=0) forceunit = models.CharField(default='lbs', - choices = ( - ('lbs','lbs'), - ('N','N') - ), + choices=( + ('lbs', 'lbs'), + ('N', 'N') + ), max_length=100) # empower stuff inboard = models.FloatField(default=0.88) oarlength = models.FloatField(default=2.89) - notes = models.CharField(blank=True,null=True,max_length=1000) + notes = models.CharField(blank=True, null=True, max_length=1000) summary = models.TextField(blank=True) - privacy = models.CharField(default='visible',max_length=30, + privacy = models.CharField(default='visible', max_length=30, choices=privacychoices) - rankingpiece = models.BooleanField(default=False,verbose_name='Ranking Piece') - duplicate = models.BooleanField(default=False,verbose_name='Duplicate Workout') - impeller = models.BooleanField(default=False,verbose_name='Impeller') + rankingpiece = models.BooleanField( + default=False, verbose_name='Ranking Piece') + duplicate = models.BooleanField( + default=False, verbose_name='Duplicate Workout') + impeller = models.BooleanField(default=False, verbose_name='Impeller') def url(self): str = '/rowers/workout/{id}/'.format( - id = encoder.encode_hex(self.id) + id=encoder.encode_hex(self.id) ) url = settings.SITE_URL+str return url def save(self, *args, **kwargs): user = self.user - if self.notes is not None and len(self.notes)>1000: # pragma: no cover + if self.notes is not None and len(self.notes) > 1000: # pragma: no cover self.notes = self.notes[0:950] if not can_add_workout(user.user): - raise forms.ValidationError("Free Coach User cannot have any workouts") + raise forms.ValidationError( + "Free Coach User cannot have any workouts") if self.timezone == 'tzutc()': - self.timezone = 'UTC' # pragma: no cover + self.timezone = 'UTC' # pragma: no cover super(Workout, self).save(*args, **kwargs) @@ -3282,53 +3405,56 @@ class Workout(models.Model): boattype = self.boattype workouttype = self.workouttype - if workouttype != 'water': stri = u'{d} {n} {dist}m {duration} {workouttype} {ownerfirst} {ownerlast}'.format( - d = date.strftime('%Y-%m-%d'), - n = name, - dist = distance, - duration = duration.strftime("%H:%M:%S"), - workouttype = workouttype, - ownerfirst = ownerfirst, - ownerlast = ownerlast, + d=date.strftime('%Y-%m-%d'), + n=name, + dist=distance, + duration=duration.strftime("%H:%M:%S"), + workouttype=workouttype, + ownerfirst=ownerfirst, + ownerlast=ownerlast, ) else: stri = u'{d} {n} {dist}m {duration:%H:%M:%S} {workouttype} {boattype} {ownerfirst} {ownerlast}'.format( - d = date.strftime('%Y-%m-%d'), - n = name, - dist = distance, - duration = duration, - workouttype = workouttype, + d=date.strftime('%Y-%m-%d'), + n=name, + dist=distance, + duration=duration, + workouttype=workouttype, boattype=boattype, - ownerfirst = ownerfirst, - ownerlast = ownerlast, + ownerfirst=ownerfirst, + ownerlast=ownerlast, ) return stri + class TombStone(models.Model): - user = models.ForeignKey(Rower,on_delete=models.CASCADE) + user = models.ForeignKey(Rower, on_delete=models.CASCADE) uploadedtoc2 = models.IntegerField(default=0) uploadedtostrava = models.BigIntegerField(default=0) uploadedtosporttracks = models.BigIntegerField(default=0) uploadedtotp = models.BigIntegerField(default=0) uploadedtonk = models.BigIntegerField(default=0) -@receiver(models.signals.pre_delete,sender=Workout) + +@receiver(models.signals.pre_delete, sender=Workout) def create_tombstone_on_delete(sender, instance, **kwargs): t = TombStone( user=instance.user, - uploadedtoc2 = instance.uploadedtoc2, - uploadedtostrava = instance.uploadedtostrava, - uploadedtotp = instance.uploadedtotp, - uploadedtonk = instance.uploadedtonk + uploadedtoc2=instance.uploadedtoc2, + uploadedtostrava=instance.uploadedtostrava, + uploadedtotp=instance.uploadedtotp, + uploadedtonk=instance.uploadedtonk ) t.save() # delete files belonging to workout instance # related GraphImage objects should be deleted automatically -@receiver(models.signals.post_delete,sender=Workout) + + +@receiver(models.signals.post_delete, sender=Workout) def auto_delete_file_on_delete(sender, instance, **kwargs): # delete CSV file if instance.csvfilename: @@ -3352,45 +3478,45 @@ def auto_delete_file_on_delete(sender, instance, **kwargs): pass - -@receiver(models.signals.post_delete,sender=Workout) +@receiver(models.signals.post_delete, sender=Workout) def update_duplicates_on_delete(sender, instance, **kwargs): if instance.id: duplicates = Workout.objects.filter( - user=instance.user,date=instance.date, + user=instance.user, date=instance.date, duplicate=True) - for d in duplicates: # pragma: no cover + for d in duplicates: # pragma: no cover t = d.duration - delta = datetime.timedelta(hours=t.hour, minutes=t.minute, seconds=t.second) + delta = datetime.timedelta( + hours=t.hour, minutes=t.minute, seconds=t.second) workoutenddatetime = d.startdatetime+delta ws = Workout.objects.filter( - user=d.user,date=d.date, + user=d.user, date=d.date, ).exclude( - pk__in=[instance.pk,d.pk] + pk__in=[instance.pk, d.pk] ).exclude( startdatetime__gt=workoutenddatetime - ) - + ) ws2 = [] for ww in ws: t = ww.duration - delta = datetime.timedelta(hours=t.hour, minutes=t.minute, seconds=t.second) + delta = datetime.timedelta( + hours=t.hour, minutes=t.minute, seconds=t.second) enddatetime = ww.startdatetime+delta if enddatetime > d.startdatetime: ws2.append(ww) if len(ws2) == 0: - d.duplicate=False + d.duplicate = False d.save() # Delete stroke data from the database when a workout is deleted -#@receiver(models.signals.post_delete,sender=Workout) -#def auto_delete_strokedata_on_delete(sender, instance, **kwargs): +# @receiver(models.signals.post_delete,sender=Workout) +# def auto_delete_strokedata_on_delete(sender, instance, **kwargs): # if instance.id: # query = sa.text('DELETE FROM strokedata WHERE workoutid={id};'.format( # id=instance.id, @@ -3405,11 +3531,12 @@ def update_duplicates_on_delete(sender, instance, **kwargs): # engine.dispose() class VirtualRaceFollower(models.Model): - user = models.ForeignKey(User,on_delete=models.CASCADE,null=True) - race = models.ForeignKey(VirtualRace,on_delete=models.CASCADE) - emailaddress = models.EmailField(max_length=254,blank=True,null=True, + user = models.ForeignKey(User, on_delete=models.CASCADE, null=True) + race = models.ForeignKey(VirtualRace, on_delete=models.CASCADE) + emailaddress = models.EmailField(max_length=254, blank=True, null=True, verbose_name="Email Address") + class FollowerForm(ModelForm): class Meta: model = VirtualRaceFollower @@ -3417,35 +3544,38 @@ class FollowerForm(ModelForm): # Virtual Race results (for keeping results when workouts are deleted) + class VirtualRaceResult(models.Model): - boatclasses = (type for type in mytypes.workouttypes if type[0] in mytypes.otwtypes) + boatclasses = ( + type for type in mytypes.workouttypes if type[0] in mytypes.otwtypes) userid = models.IntegerField(default=0) - teamname = models.CharField(max_length=80,verbose_name = 'Team Name', - blank=True,null=True) + teamname = models.CharField(max_length=80, verbose_name='Team Name', + blank=True, null=True) username = models.CharField(max_length=150) workoutid = models.IntegerField(null=True) - weightcategory = models.CharField(default="hwt",max_length=10, + weightcategory = models.CharField(default="hwt", max_length=10, choices=weightcategories, verbose_name='Weight Category') - adaptiveclass = models.CharField(default="None",max_length=50, + adaptiveclass = models.CharField(default="None", max_length=50, choices=mytypes.adaptivetypes, verbose_name="Adaptive Class") - skillclass = models.CharField(default="Open",max_length=50, + skillclass = models.CharField(default="Open", max_length=50, verbose_name="Skill Class") - race = models.ForeignKey(VirtualRace,on_delete=models.CASCADE,related_name='entries', - blank=True,null=True) - course = models.ForeignKey(GeoCourse,on_delete=models.CASCADE,null=True,blank=True) + race = models.ForeignKey(VirtualRace, on_delete=models.CASCADE, related_name='entries', + blank=True, null=True) + course = models.ForeignKey( + GeoCourse, on_delete=models.CASCADE, null=True, blank=True) - duration = models.TimeField(default=datetime.time(1,0)) + duration = models.TimeField(default=datetime.time(1, 0)) distance = models.IntegerField(default=0) points = models.FloatField(default=0) boatclass = models.CharField(choices=boatclasses, - max_length=40, - default='water', - verbose_name = 'Boat Class') - boattype = models.CharField(choices=boattypes,max_length=40, + max_length=40, + default='water', + verbose_name='Boat Class') + boattype = models.CharField(choices=boattypes, max_length=40, default='1x', - verbose_name = 'Boat Type' + verbose_name='Boat Type' ) coursecompleted = models.BooleanField(default=False) sex = models.CharField(default="not specified", @@ -3455,18 +3585,18 @@ class VirtualRaceResult(models.Model): age = models.IntegerField(null=True) emailnotifications = models.BooleanField(default=True, - verbose_name = 'Receive challenge notifications by email') + verbose_name='Receive challenge notifications by email') startsecond = models.FloatField(default=0) endsecond = models.FloatField(default=0) referencespeed = models.FloatField(default=5.0) - entrycategory = models.ForeignKey(CourseStandard,null=True,on_delete=models.SET_NULL, + entrycategory = models.ForeignKey(CourseStandard, null=True, on_delete=models.SET_NULL, verbose_name='Group') acceptsocialmedia = models.BooleanField(default=True, - verbose_name = 'I agree with sharing my name in challenge related social media posts (unchecking this does not prevent you from participation)') + verbose_name='I agree with sharing my name in challenge related social media posts (unchecking this does not prevent you from participation)') - def isduplicate(self,other): # pragma: no cover + def isduplicate(self, other): # pragma: no cover if self.userid != other.userid: return False if self.weightcategory != other.weightcategory: @@ -3505,71 +3635,74 @@ class VirtualRaceResult(models.Model): def __str__(self): rr = Rower.objects.get(id=self.userid) name = '{u1} {u2}'.format( - u1 = rr.user.first_name, - u2 = rr.user.last_name, - ) + u1=rr.user.first_name, + u2=rr.user.last_name, + ) if self.teamname: if self.entrycategory: return u'Entry for {n} for "{r}" in {g} with {t}'.format( - n = name, - r = self.race, - g = self.entrycategory, - t = self.teamname, + n=name, + r=self.race, + g=self.entrycategory, + t=self.teamname, ) return u'Entry for {n} for "{r}" in {c} {d} with {t} ({s})'.format( - n = name, - r = self.race, - d = self.boattype, - c = self.boatclass, - t = self.teamname, - s = self.sex, + n=name, + r=self.race, + d=self.boattype, + c=self.boatclass, + t=self.teamname, + s=self.sex, ) - else: # pragma: no cover + else: # pragma: no cover if self.entrycategory: return u'Entry for {n} for "{r}" in {g}'.format( - n = name, - r = self.race, - g = self.entrycategory, + n=name, + r=self.race, + g=self.entrycategory, ) return u'Entry for {n} for "{r}" in {c} {d} ({s})'.format( - n = name, - r = self.race, - d = self.boattype, - c = self.boatclass, - s = self.sex, - ) + n=name, + r=self.race, + d=self.boattype, + c=self.boatclass, + s=self.sex, + ) # Virtual Race results (for keeping results when workouts are deleted) + class IndoorVirtualRaceResult(models.Model): - boatclasses = (type for type in mytypes.workouttypes if type[0] in mytypes.otetypes) - userid = models.IntegerField(default=0) # ID of rower object - teamname = models.CharField(max_length=80,verbose_name = 'Team Name', - blank=True,null=True) + boatclasses = ( + type for type in mytypes.workouttypes if type[0] in mytypes.otetypes) + userid = models.IntegerField(default=0) # ID of rower object + teamname = models.CharField(max_length=80, verbose_name='Team Name', + blank=True, null=True) username = models.CharField(max_length=150) workoutid = models.IntegerField(null=True) - weightcategory = models.CharField(default="hwt",max_length=10, + weightcategory = models.CharField(default="hwt", max_length=10, choices=weightcategories, verbose_name='Weight Category') - adaptiveclass = models.CharField(default="None",max_length=50, + adaptiveclass = models.CharField(default="None", max_length=50, choices=mytypes.adaptivetypes, verbose_name="Adaptive Class") - skillclass = models.CharField(default="Open",max_length=50, + skillclass = models.CharField(default="Open", max_length=50, verbose_name="Skill Class") - race = models.ForeignKey(VirtualRace,on_delete=models.CASCADE,null=True,blank=True) - duration = models.TimeField(default=datetime.time(1,0)) + race = models.ForeignKey( + VirtualRace, on_delete=models.CASCADE, null=True, blank=True) + duration = models.TimeField(default=datetime.time(1, 0)) distance = models.IntegerField(default=0) referencespeed = models.FloatField(default=5.0) points = models.FloatField(default=0) boatclass = models.CharField(choices=boatclasses, - max_length=40, - default='rower', - verbose_name = 'Ergometer Class') + max_length=40, + default='rower', + verbose_name='Ergometer Class') - boattype = models.CharField(choices=boattypes,max_length=40, + boattype = models.CharField(choices=boattypes, max_length=40, default='1x', - verbose_name = 'Boat Type' + verbose_name='Boat Type' ) coursecompleted = models.BooleanField(default=False) sex = models.CharField(default="not specified", @@ -3579,16 +3712,16 @@ class IndoorVirtualRaceResult(models.Model): age = models.IntegerField(null=True) emailnotifications = models.BooleanField(default=True, - verbose_name = 'Receive challenge notifications by email') - entrycategory = models.ForeignKey(CourseStandard,null=True,on_delete=models.SET_NULL, + verbose_name='Receive challenge notifications by email') + entrycategory = models.ForeignKey(CourseStandard, null=True, on_delete=models.SET_NULL, verbose_name='Group') acceptsocialmedia = models.BooleanField(default=True, - verbose_name = 'I agree with sharing my name in challenge related social media posts (unchecking this does not prevent you from participation)') + verbose_name='I agree with sharing my name in challenge related social media posts (unchecking this does not prevent you from participation)') startsecond = models.FloatField(default=0) endsecond = models.FloatField(default=0) - def isduplicate(self,other): # pragma: no cover + def isduplicate(self, other): # pragma: no cover if self.race is None and other.race is not None: return False if self.race is not None and other.race is None: @@ -3620,44 +3753,45 @@ class IndoorVirtualRaceResult(models.Model): def __str__(self): rr = Rower.objects.get(id=self.userid) name = '{u1} {u2}'.format( - u1 = rr.user.first_name, - u2 = rr.user.last_name, - ) + u1=rr.user.first_name, + u2=rr.user.last_name, + ) if self.teamname: - if self.entrycategory: # pragma: no cover + if self.entrycategory: # pragma: no cover return u'Entry for {n} for "{r}" in {g} with {t}'.format( - n = name, - r = self.race, - g = self.entrycategory, - t = self.teamname, + n=name, + r=self.race, + g=self.entrycategory, + t=self.teamname, ) return u'Entry for {n} for "{r}" on {c} with {t} ({s})'.format( - n = name, - r = self.race, - t = self.teamname, - c = self.boatclass, - s = self.sex, + n=name, + r=self.race, + t=self.teamname, + c=self.boatclass, + s=self.sex, ) - else: # pragma: no cover + else: # pragma: no cover if self.entrycategory: return u'Entry for {n} for "{r}" in {g}'.format( - n = name, - r = self.race, - g = self.entrycategory, + n=name, + r=self.race, + g=self.entrycategory, ) return u'Entry for {n} for "{r}" on {c} ({s})'.format( - n = name, - r = self.race, - c = self.boatclass, - s = self.sex, - ) + n=name, + r=self.race, + c=self.boatclass, + s=self.sex, + ) class CourseTestResult(models.Model): userid = models.IntegerField(default=0) workoutid = models.IntegerField(null=True) - plannedsession = models.ForeignKey(PlannedSession,on_delete=models.CASCADE) - duration = models.TimeField(default=datetime.time(1,0)) + plannedsession = models.ForeignKey( + PlannedSession, on_delete=models.CASCADE) + duration = models.TimeField(default=datetime.time(1, 0)) distance = models.IntegerField(default=0) coursecompleted = models.BooleanField(default=False) startsecond = models.FloatField(default=0) @@ -3667,35 +3801,34 @@ class CourseTestResult(models.Model): class IndoorVirtualRaceResultForm(ModelForm): class Meta: model = IndoorVirtualRaceResult - fields = ['teamname','weightcategory','boatclass','age','adaptiveclass', - 'entrycategory','acceptsocialmedia' + fields = ['teamname', 'weightcategory', 'boatclass', 'age', 'adaptiveclass', + 'entrycategory', 'acceptsocialmedia' ] - def __init__(self, *args, **kwargs): - categories = kwargs.pop('categories',None) + categories = kwargs.pop('categories', None) super(IndoorVirtualRaceResultForm, self).__init__(*args, **kwargs) - if categories is not None: # pragma: no cover + if categories is not None: # pragma: no cover self.fields['entrycategory'].queryset = categories self.fields['entrycategory'].empty_label = None else: self.fields.pop('entrycategory') + class VirtualRaceResultForm(ModelForm): class Meta: model = VirtualRaceResult - fields = ['teamname','weightcategory','boatclass','boattype', - 'age','adaptiveclass', - 'entrycategory','acceptsocialmedia' + fields = ['teamname', 'weightcategory', 'boatclass', 'boattype', + 'age', 'adaptiveclass', + 'entrycategory', 'acceptsocialmedia' ] - def __init__(self, *args, **kwargs): - boattypes = kwargs.pop('boattypes',None) - categories = kwargs.pop('categories',None) + boattypes = kwargs.pop('boattypes', None) + categories = kwargs.pop('categories', None) super(VirtualRaceResultForm, self).__init__(*args, **kwargs) - if boattypes: # pragma: no cover + if boattypes: # pragma: no cover self.fields['boattype'].choices = boattypes self.fields['mix'] = forms.BooleanField(initial=False, @@ -3708,28 +3841,27 @@ class VirtualRaceResultForm(ModelForm): else: self.fields.pop('entrycategory') -from rowers.metrics import rowingmetrics strokedatafields = { - 'workoutid':models.IntegerField(null=True), - 'workoutstate':models.IntegerField(null=True,default=1), - 'ftime':models.CharField(max_length=30), - 'fpace':models.CharField(max_length=30), - 'hr_ut2':models.IntegerField(null=True), - 'hr_ut1':models.IntegerField(null=True), - 'hr_at':models.IntegerField(null=True), - 'hr_tr':models.IntegerField(null=True), - 'hr_an':models.IntegerField(null=True), - 'hr_max':models.IntegerField(null=True), - 'hr_bottom':models.IntegerField(null=True), - 'ergpace':models.FloatField(null=True), - 'nowindpace':models.FloatField(null=True), - 'equivergpower':models.FloatField(null=True), - 'fergpace':models.CharField(max_length=30), - 'fnowindpace':models.CharField(max_length=30), + 'workoutid': models.IntegerField(null=True), + 'workoutstate': models.IntegerField(null=True, default=1), + 'ftime': models.CharField(max_length=30), + 'fpace': models.CharField(max_length=30), + 'hr_ut2': models.IntegerField(null=True), + 'hr_ut1': models.IntegerField(null=True), + 'hr_at': models.IntegerField(null=True), + 'hr_tr': models.IntegerField(null=True), + 'hr_an': models.IntegerField(null=True), + 'hr_max': models.IntegerField(null=True), + 'hr_bottom': models.IntegerField(null=True), + 'ergpace': models.FloatField(null=True), + 'nowindpace': models.FloatField(null=True), + 'equivergpower': models.FloatField(null=True), + 'fergpace': models.CharField(max_length=30), + 'fnowindpace': models.CharField(max_length=30), } -for name,d in rowingmetrics: +for name, d in rowingmetrics: if d['numtype'] == 'float': try: strokedatafields[name] = models.FloatField( @@ -3750,11 +3882,14 @@ for name,d in rowingmetrics: strokedatafields[name] = models.IntegerField( null=d['null'], verbose_name=d['verbose_name']) + + class Meta: db_table = 'strokedata' index_together = ['workoutid'] app_label = 'rowers' + attrs = {'__module__': 'rowers.models', 'Meta': Meta} attrs.update(strokedatafields) @@ -3764,7 +3899,7 @@ attrs.update(strokedatafields) # when the StrokeData are expanded. # No Django Instances of this model are managed. Strokedata table is # accesssed directly with SQL commands -#StrokeData = type(str('StrokeData'), (models.Model,), +# StrokeData = type(str('StrokeData'), (models.Model,), # attrs # ) @@ -3792,7 +3927,6 @@ class cpergdata(models.Model): app_label = 'rowers' - # Storing data for the OTW CP chart class ergcpdata(models.Model): delta = models.IntegerField(default=0) @@ -3807,22 +3941,24 @@ class ergcpdata(models.Model): # A wrapper around the png files + class GraphImage(models.Model): - filename = models.CharField(default='',max_length=150,blank=True,null=True) + filename = models.CharField( + default='', max_length=150, blank=True, null=True) creationdatetime = models.DateTimeField() - workout = models.ForeignKey(Workout,on_delete=models.CASCADE) + workout = models.ForeignKey(Workout, on_delete=models.CASCADE) width = models.IntegerField(default=1200) height = models.IntegerField(default=600) - def __str__(self): # pragma: no cover + def __str__(self): # pragma: no cover return self.filename # delete related file object when image is deleted -@receiver(models.signals.post_delete,sender=GraphImage) -def auto_delete_image_on_delete(sender,instance, **kwargs): +@receiver(models.signals.post_delete, sender=GraphImage) +def auto_delete_image_on_delete(sender, instance, **kwargs): if instance.filename: - if os.path.isfile(instance.filename): # pragma: no cover + if os.path.isfile(instance.filename): # pragma: no cover others = GraphImage.objects.filter(filename=instance.filename) if others.count() == 0: os.remove(instance.filename) @@ -3830,12 +3966,9 @@ def auto_delete_image_on_delete(sender,instance, **kwargs): pass - - - # Form to update Workout data class WorkoutForm(ModelForm): -# duration = forms.TimeInput(format='%H:%M:%S.%f') + # duration = forms.TimeInput(format='%H:%M:%S.%f') class Meta: model = Workout fields = ['name', @@ -3859,7 +3992,7 @@ class WorkoutForm(ModelForm): 'starttime': AdminTimeWidget(), 'notes': forms.Textarea, 'duration': forms.TimeInput(format='%H:%M:%S.%f'), - } + } def __init__(self, *args, **kwargs): super(WorkoutForm, self).__init__(*args, **kwargs) @@ -3868,15 +4001,15 @@ class WorkoutForm(ModelForm): label='Private') self.fields['timezone'] = forms.ChoiceField( - choices = ( - (x,x) for x in pytz.common_timezones - ) + choices=( + (x, x) for x in pytz.common_timezones ) + ) if 'instance' in kwargs: if kwargs['instance'].privacy == 'visible': self.fields['private'].initial = False - else: # pragma: no cover + else: # pragma: no cover self.fields['private'].initial = True workout = self.instance @@ -3884,24 +4017,27 @@ class WorkoutForm(ModelForm): rower__in=[workout.user], startdate__lte=workout.date, enddate__gte=workout.date, - ).order_by("preferreddate","startdate","enddate").exclude( - sessiontype__in=['race','indoorrace']) + ).order_by("preferreddate", "startdate", "enddate").exclude( + sessiontype__in=['race', 'indoorrace']) if not sps: del self.fields['plannedsession'] - else: # pragma: no cover + else: # pragma: no cover self.fields['plannedsession'].queryset = sps else: del self.fields['plannedsession'] # Used for the rowing physics calculations + + class AdvancedWorkoutForm(ModelForm): #quick_calc = forms.BooleanField(initial=True,required=False) #go_service = forms.BooleanField(initial=False,required=False,label='Experimental') class Meta: model = Workout - fields = ['boattype','weightvalue','boatbrand'] + fields = ['boattype', 'weightvalue', 'boatbrand'] + class RowerExportForm(ModelForm): class Meta: @@ -3919,24 +4055,29 @@ class RowerExportForm(ModelForm): 'strava_auto_delete', 'trainingpeaks_auto_export', 'rp3_auto_import' - ] + ] # Simple form to set rower's Functional Threshold Power + + class RowerPowerForm(ModelForm): class Meta: model = Rower - fields = ['hrftp','ftp','otwslack'] + fields = ['hrftp', 'ftp', 'otwslack'] + class RowerCPForm(ModelForm): class Meta: model = Rower - fields = ['cprange','kfit','kfatigue'] + fields = ['cprange', 'kfit', 'kfatigue'] # Form to set rower's Power zones, including test routines # to enable consistency + + class RowerHRZonesForm(ModelForm): - hrzones = ['Rest','UT2','UT1','AT','TR','AN','Max'] + hrzones = ['Rest', 'UT2', 'UT1', 'AT', 'TR', 'AN', 'Max'] hrrestname = forms.CharField(initial=hrzones[0]) hrut2name = forms.CharField(initial=hrzones[1]) hrut1name = forms.CharField(initial=hrzones[2]) @@ -3945,13 +4086,13 @@ class RowerHRZonesForm(ModelForm): hranname = forms.CharField(initial=hrzones[5]) hrmaxname = forms.CharField(initial=hrzones[6]) - def __init__(self, *args,**kwargs): + def __init__(self, *args, **kwargs): super(RowerHRZonesForm, self).__init__(*args, **kwargs) if 'instance' in kwargs: hrzones = kwargs['instance'].hrzones else: - hrzones = ['Rest','UT2','UT1','AT','TR','AN','Max'] + hrzones = ['Rest', 'UT2', 'UT1', 'AT', 'TR', 'AN', 'Max'] self.fields['hrrestname'].initial = hrzones[0] self.fields['hrut2name'].initial = hrzones[1] @@ -3963,9 +4104,9 @@ class RowerHRZonesForm(ModelForm): class Meta: model = Rower - fields = ['rest','ut2','ut1','at','tr','an','max'] + fields = ['rest', 'ut2', 'ut1', 'at', 'tr', 'an', 'max'] - def clean(self): # pragma: no cover + def clean(self): # pragma: no cover cleaned_data = super(RowerHRZonesForm, self).clean() try: @@ -4050,7 +4191,6 @@ class RowerHRZonesForm(ModelForm): hrmaxname = 'Max' cleaned_data['hrmaxname'] = 'Max' - if rest >= ut2: e = "{ut2name} should be higher than {restname}".format( restname=hrrestname, @@ -4060,27 +4200,27 @@ class RowerHRZonesForm(ModelForm): if ut1 <= ut2: e = "{ut1name} should be higher than {ut2name}".format( - ut1name = hrut1name, - ut2name= hrut2name, - ) + ut1name=hrut1name, + ut2name=hrut2name, + ) raise forms.ValidationError(e) if at <= ut1: e = "{atname} should be higher than {ut1name}".format( - atname = hratname, - ut1name= hrut1name, - ) + atname=hratname, + ut1name=hrut1name, + ) raise forms.ValidationError(e) if tr <= at: e = "{trname} should be higher than {atname}".format( - atname = hratname, - trname= hrtrname, - ) + atname=hratname, + trname=hrtrname, + ) raise forms.ValidationError(e) if an <= tr: e = "{anname} should be higher than {trname}".format( - anname = hranname, - trname= hrtrname, - ) + anname=hranname, + trname=hrtrname, + ) raise forms.ValidationError(e) if max <= an: @@ -4093,9 +4233,11 @@ class RowerHRZonesForm(ModelForm): # Form to set rower's Power zones, including test routines # to enable consistency + + class RowerPowerZonesForm(ModelForm): - powerzones = ['UT3','UT2','UT1','AT','TR','AN'] + powerzones = ['UT3', 'UT2', 'UT1', 'AT', 'TR', 'AN'] ut3name = forms.CharField(initial=powerzones[0]) ut2name = forms.CharField(initial=powerzones[1]) ut1name = forms.CharField(initial=powerzones[2]) @@ -4103,13 +4245,13 @@ class RowerPowerZonesForm(ModelForm): trname = forms.CharField(initial=powerzones[4]) anname = forms.CharField(initial=powerzones[5]) - def __init__(self, *args,**kwargs): + def __init__(self, *args, **kwargs): super(RowerPowerZonesForm, self).__init__(*args, **kwargs) if 'instance' in kwargs: powerzones = kwargs['instance'].powerzones else: - powerzones = ['UT3','UT2','UT1','AT','TR','AN'] + powerzones = ['UT3', 'UT2', 'UT1', 'AT', 'TR', 'AN'] self.fields['ut3name'].initial = powerzones[0] self.fields['ut2name'].initial = powerzones[1] @@ -4120,9 +4262,9 @@ class RowerPowerZonesForm(ModelForm): class Meta: model = Rower - fields = ['pw_ut2','pw_ut1','pw_at','pw_tr','pw_an'] + fields = ['pw_ut2', 'pw_ut1', 'pw_at', 'pw_tr', 'pw_an'] - def clean(self): # pragma: no cover + def clean(self): # pragma: no cover cleaned_data = super(RowerPowerZonesForm, self).clean() try: @@ -4187,7 +4329,6 @@ class RowerPowerZonesForm(ModelForm): anname = 'AN' cleaned_data['ut1name'] = 'AN' - try: ut3name = cleaned_data['ut3name'] except: @@ -4219,35 +4360,36 @@ class RowerPowerZonesForm(ModelForm): anname = 'AN' cleaned_data['ut1name'] = 'AN' - if pw_ut1 <= pw_ut2: e = "{ut1name} should be higher than {ut2name}".format( - ut1name = ut1name, - ut2name= ut2name, - ) + ut1name=ut1name, + ut2name=ut2name, + ) raise forms.ValidationError(e) if pw_at <= pw_ut1: e = "{atname} should be higher than {ut1name}".format( - atname = atname, - ut1name= ut1name, - ) + atname=atname, + ut1name=ut1name, + ) raise forms.ValidationError(e) if pw_tr <= pw_at: e = "{trname} should be higher than {atname}".format( - atname = atname, - trname= trname, - ) + atname=atname, + trname=trname, + ) raise forms.ValidationError(e) if pw_an <= pw_tr: e = "{anname} should be higher than {trname}".format( - anname = anname, - trname= trname, - ) + anname=anname, + trname=trname, + ) raise forms.ValidationError(e) return cleaned_data # Form to set rower's Auto Import and Export settings + + class RowerImportExportForm(ModelForm): class Meta: model = Rower @@ -4259,9 +4401,11 @@ class RowerImportExportForm(ModelForm): 'strava_auto_export', 'strava_auto_import', 'trainingpeaks_auto_export', - ] + ] # Form to collect rower's Billing Info + + class RowerBillingAddressForm(ModelForm): class Meta: model = Rower @@ -4270,7 +4414,7 @@ class RowerBillingAddressForm(ModelForm): 'city', 'postal_code', 'country' - ] + ] def __init__(self, *args, **kwargs): super(RowerBillingAddressForm, self).__init__(*args, **kwargs) @@ -4281,24 +4425,24 @@ class RowerBillingAddressForm(ModelForm): class AccountRowerForm(ModelForm): class Meta: model = Rower - fields = ['sex','birthdate','weightcategory', + fields = ['sex', 'birthdate', 'weightcategory', 'adaptiveclass', 'getemailnotifications', 'getimportantemails', 'share_course_results', - 'defaulttimezone','showfavoritechartnotes', + 'defaulttimezone', 'showfavoritechartnotes', 'fav_analysis', 'usersmooth', 'defaultlandingpage', 'defaultlandingpage2', 'defaultlandingpage3', - 'offercoaching','autojoin','emailalternatives'] + 'offercoaching', 'autojoin', 'emailalternatives'] widgets = { 'birthdate': SelectDateWidget( years=range( - timezone.now().year-100,timezone.now().year-10)), - } + timezone.now().year-100, timezone.now().year-10)), + } def __init__(self, *args, **kwargs): @@ -4307,7 +4451,8 @@ class AccountRowerForm(ModelForm): self.fields.pop('offercoaching') try: - self.initial['emailalternatives'] = ', '.join(self.instance.emailalternatives) + self.initial['emailalternatives'] = ', '.join( + self.instance.emailalternatives) except TypeError: pass @@ -4316,7 +4461,7 @@ class AccountRowerForm(ModelForm): z = "".join(cd['emailalternatives'].split()).split(',') emailalternatives = [] for addr in z: - try: # pragma: no cover + try: # pragma: no cover validate_email(addr) match = User.objects.filter(email__iexact=addr) if match.count() == 0: @@ -4327,24 +4472,29 @@ class AccountRowerForm(ModelForm): self.cleaned_data['emailalternatives'] = emailalternatives # Form to set static chart settings + + class StaticChartRowerForm(ModelForm): class Meta: model = Rower - fields = ['usersmooth','staticgrids','slowpaceerg','fastpaceerg','slowpaceotw','fastpaceotw','staticchartonupload','fav_analysis'] + fields = ['usersmooth', 'staticgrids', 'slowpaceerg', 'fastpaceerg', + 'slowpaceotw', 'fastpaceotw', 'staticchartonupload', 'fav_analysis'] def __init__(self, *args, **kwargs): super(StaticChartRowerForm, self).__init__(*args, **kwargs) self.fields['staticgrids'].required = False + class DataRowerForm(ModelForm): class Meta: model = Rower - fields = ['dosmooth','erg_recalculatepower','autojoin'] + fields = ['dosmooth', 'erg_recalculatepower', 'autojoin'] + class UserForm(ModelForm): class Meta: model = User - fields = ['first_name','last_name','email'] + fields = ['first_name', 'last_name', 'email'] def clean_first_name(self): first_name = self.cleaned_data.get('first_name') @@ -4352,25 +4502,28 @@ class UserForm(ModelForm): if len(first_name): return first_name - raise forms.ValidationError('Please fill in your first name') # pragma: no cover + raise forms.ValidationError( + 'Please fill in your first name') # pragma: no cover def clean_email(self): email = self.cleaned_data.get('email') try: validate_email(email) - except ValidationError: # pragma: no cover + except ValidationError: # pragma: no cover raise forms.ValidationError( 'Please enter a valid email address') try: - match = User.objects.filter(email__iexact=email).exclude(id=self.instance.id) + match = User.objects.filter( + email__iexact=email).exclude(id=self.instance.id) if match.count() == 0: return email - except User.DoesNotExist: # pragma: no cover + except User.DoesNotExist: # pragma: no cover return email - raise forms.ValidationError('This email address is not allowed') # pragma: no cover + raise forms.ValidationError( + 'This email address is not allowed') # pragma: no cover # Form to set rower's Heart Rate zones, including test routines @@ -4378,105 +4531,114 @@ class UserForm(ModelForm): class RowerForm(ModelForm): class Meta: model = Rower - fields = ['rest','ut2','ut1','at','tr','an','max'] - + fields = ['rest', 'ut2', 'ut1', 'at', 'tr', 'an', 'max'] def clean_rest(self): rest = self.cleaned_data['rest'] - if rest<10: - self.data['rest']=10 - raise forms.ValidationError("Resting heart rate should be higher than 10 bpm") + if rest < 10: + self.data['rest'] = 10 + raise forms.ValidationError( + "Resting heart rate should be higher than 10 bpm") - - if rest>250: # pragma: no cover + if rest > 250: # pragma: no cover self.data['rest'] = 250 - raise forms.ValidationError("Resting heart rate should be lower than 250 bpm") - + raise forms.ValidationError( + "Resting heart rate should be lower than 250 bpm") return rest def clean_ut2(self): ut2 = self.cleaned_data['ut2'] - if ut2<10: # pragma: no cover - raise forms.ValidationError("UT2 heart rate should be higher than 10 bpm") + if ut2 < 10: # pragma: no cover + raise forms.ValidationError( + "UT2 heart rate should be higher than 10 bpm") - if ut2>250: # pragma: no cover - raise forms.ValidationError("UT2 heart rate should be lower than 250 bpm") + if ut2 > 250: # pragma: no cover + raise forms.ValidationError( + "UT2 heart rate should be lower than 250 bpm") return ut2 def clean_ut1(self): ut1 = self.cleaned_data['ut1'] - if ut1<10: # pragma: no cover - raise forms.ValidationError("UT1 heart rate should be higher than 10 bpm") + if ut1 < 10: # pragma: no cover + raise forms.ValidationError( + "UT1 heart rate should be higher than 10 bpm") - if ut1>250: # pragma: no cover - raise forms.ValidationError("Resting heart rate should be lower than 250 bpm") + if ut1 > 250: # pragma: no cover + raise forms.ValidationError( + "Resting heart rate should be lower than 250 bpm") return ut1 def clean_at(self): at = self.cleaned_data['at'] - if at<10: # pragma: no cover - raise forms.ValidationError("AT heart rate should be higher than 10 bpm") + if at < 10: # pragma: no cover + raise forms.ValidationError( + "AT heart rate should be higher than 10 bpm") - if at>250: # pragma: no cover - raise forms.ValidationError("AT heart rate should be lower than 250 bpm") + if at > 250: # pragma: no cover + raise forms.ValidationError( + "AT heart rate should be lower than 250 bpm") return at def clean_tr(self): tr = self.cleaned_data['tr'] - if tr<10: # pragma: no cover - raise forms.ValidationError("TR heart rate should be higher than 10 bpm") + if tr < 10: # pragma: no cover + raise forms.ValidationError( + "TR heart rate should be higher than 10 bpm") - if tr>250: # pragma: no cover - raise forms.ValidationError("TR heart rate should be lower than 250 bpm") + if tr > 250: # pragma: no cover + raise forms.ValidationError( + "TR heart rate should be lower than 250 bpm") return tr def clean_an(self): an = self.cleaned_data['an'] - if an<10: # pragma: no cover - raise forms.ValidationError("AN heart rate should be higher than 10 bpm") + if an < 10: # pragma: no cover + raise forms.ValidationError( + "AN heart rate should be higher than 10 bpm") - if an>250: # pragma: no cover - raise forms.ValidationError("AN heart rate should be lower than 250 bpm") + if an > 250: # pragma: no cover + raise forms.ValidationError( + "AN heart rate should be lower than 250 bpm") return an def clean_max(self): max = int(self.cleaned_data['max']) - if max<10: # pragma: no cover - raise forms.ValidationError("Max heart rate should be higher than 10 bpm") + if max < 10: # pragma: no cover + raise forms.ValidationError( + "Max heart rate should be higher than 10 bpm") - if max>250: # pragma: no cover - raise forms.ValidationError("Max heart rate should be lower than 250 bpm") + if max > 250: # pragma: no cover + raise forms.ValidationError( + "Max heart rate should be lower than 250 bpm") return max - def clean(self): - try: rest = self.cleaned_data['rest'] except: try: rest = int(self.data['rest']) - except ValueError: # pragma: no cover + except ValueError: # pragma: no cover rest = 0 try: ut2 = self.cleaned_data['ut2'] - except: # pragma: no cover + except: # pragma: no cover try: ut2 = int(self.data['ut2']) except ValueError: @@ -4484,7 +4646,7 @@ class RowerForm(ModelForm): try: ut1 = self.cleaned_data['ut1'] - except: # pragma: no cover + except: # pragma: no cover try: ut1 = int(self.data['ut1']) except ValueError: @@ -4492,15 +4654,15 @@ class RowerForm(ModelForm): try: at = self.cleaned_data['at'] - except: # pragma: no cover + except: # pragma: no cover try: at = int(self.data['at']) except ValueError: at = 0 - try: # pragma: no cover + try: # pragma: no cover an = self.cleaned_data['an'] - except: # pragma: no cover + except: # pragma: no cover try: an = int(self.data['an']) except ValueError: @@ -4508,7 +4670,7 @@ class RowerForm(ModelForm): try: tr = self.cleaned_data['tr'] - except: # pragma: no cover + except: # pragma: no cover try: tr = int(self.data['tr']) except ValueError: @@ -4516,25 +4678,26 @@ class RowerForm(ModelForm): try: max = self.cleaned_data['max'] - except: # pragma: no cover + except: # pragma: no cover try: max = int(self.data['max']) except ValueError: max = 0 - if rest>=ut2: - raise forms.ValidationError("Resting heart rate should be lower than UT2") - if ut2>=ut1: + if rest >= ut2: + raise forms.ValidationError( + "Resting heart rate should be lower than UT2") + if ut2 >= ut1: raise forms.ValidationError("UT2 should be lower than UT1") - if ut2>=ut1: # pragma: no cover + if ut2 >= ut1: # pragma: no cover raise forms.ValidationError("UT2 should be lower than UT1") - if ut1>=at: # pragma: no cover + if ut1 >= at: # pragma: no cover raise forms.ValidationError("UT1 should be lower than AT") - if at>=tr: # pragma: no cover + if at >= tr: # pragma: no cover raise forms.ValidationError("AT should be lower than TR") - if tr>=an: # pragma: no cover + if tr >= an: # pragma: no cover raise forms.ValidationError("TR should be lower than AN") - if an>=max: # pragma: no cover + if an >= max: # pragma: no cover raise forms.ValidationError("AN should be lower than Max") @@ -4552,7 +4715,7 @@ class SiteAnnouncement(models.Model): self.created = timezone.now() self.expires = timezone.now()+datetime.timedelta(days=10) self.modified = timezone.now() - if self.dotweet: # pragma: no cover + if self.dotweet: # pragma: no cover try: status = tweetapi.PostUpdate(self.announcement) except: @@ -4560,95 +4723,99 @@ class SiteAnnouncement(models.Model): status = tweetapi.PostUpdate(self.announcement[:270]) except: pass - return super(SiteAnnouncement,self).save(*args, **kwargs) + return super(SiteAnnouncement, self).save(*args, **kwargs) # A comment by a user on a training + class WorkoutComment(models.Model): comment = models.TextField(max_length=300) created = models.DateTimeField(default=timezone.now) read = models.BooleanField(default=False) - notification = models.BooleanField(default=True,verbose_name="Subscribe to new comment notifications") - user = models.ForeignKey(User,on_delete=models.PROTECT) - workout = models.ForeignKey(Workout,on_delete=models.CASCADE) + notification = models.BooleanField( + default=True, verbose_name="Subscribe to new comment notifications") + user = models.ForeignKey(User, on_delete=models.PROTECT) + workout = models.ForeignKey(Workout, on_delete=models.CASCADE) - def __str__(self): # pragma: no cover + def __str__(self): # pragma: no cover return u'Comment to: {w} by {u1} {u2}'.format( w=self.workout, - u1 = self.user.first_name, - u2 = self.user.last_name, - ) + u1=self.user.first_name, + u2=self.user.last_name, + ) class WorkoutCommentForm(ModelForm): class Meta: model = WorkoutComment - fields = ['comment','notification'] + fields = ['comment', 'notification'] widgets = { 'comment': forms.Textarea, - } + } # A comment by a user on a training + class PlannedSessionComment(models.Model): comment = models.TextField(max_length=300) created = models.DateTimeField(default=timezone.now) read = models.BooleanField(default=False) - notification = models.BooleanField(default=True,verbose_name="Subscribe to new comment notifications") - user = models.ForeignKey(User,on_delete=models.PROTECT) - plannedsession = models.ForeignKey(PlannedSession,on_delete=models.CASCADE) + notification = models.BooleanField( + default=True, verbose_name="Subscribe to new comment notifications") + user = models.ForeignKey(User, on_delete=models.PROTECT) + plannedsession = models.ForeignKey( + PlannedSession, on_delete=models.CASCADE) - - def __str__(self): # pragma: no cover + def __str__(self): # pragma: no cover return u'Comment to: {w} by {u1} {u2}'.format( w=self.workout, - u1 = self.user.first_name, - u2 = self.user.last_name, - ) + u1=self.user.first_name, + u2=self.user.last_name, + ) class PlannedSessionCommentForm(ModelForm): class Meta: model = PlannedSessionComment - fields = ['comment','notification'] + fields = ['comment', 'notification'] widgets = { 'comment': forms.Textarea, - } + } class BlogPost(models.Model): title = models.TextField(max_length=300) - link = models.TextField(max_length=300) + link = models.TextField(max_length=300) date = models.DateField() + defaultgroups = ['basic'] + class VideoAnalysis(models.Model): - name = models.CharField(default='', max_length=150,blank=True,null=True) - video_id = models.CharField(default='',max_length=150) + name = models.CharField(default='', max_length=150, blank=True, null=True) + video_id = models.CharField(default='', max_length=150) delay = models.IntegerField(default=0) workout = models.ForeignKey(Workout, on_delete=models.CASCADE) metricsgroups = TemplateListField(default=defaultgroups) class Meta: - unique_together = ('video_id','workout') + unique_together = ('video_id', 'workout') - def __str__(self): # pragma: no cover + def __str__(self): # pragma: no cover return self.name - class ShareKey(models.Model): - location = models.TextField() # absolute path + location = models.TextField() # absolute path token = models.CharField(max_length=40, primary_key=True) creation_date = models.DateTimeField(auto_now_add=True) expiration_seconds = models.BigIntegerField() - @property - def expired(self): # pragma: no cover + def expired(self): # pragma: no cover return self.creation_date + datetime.timedelta(self.expiration_seconds) < timezone.now() @property - def expiration_date(self): # pragma: no cover + def expiration_date(self): # pragma: no cover return self.creation_date + datetime.timedelta(self.expiration_seconds) diff --git a/rowers/mytypes.py b/rowers/mytypes.py index 451f4c8b..1098b45d 100644 --- a/rowers/mytypes.py +++ b/rowers/mytypes.py @@ -1,4 +1,5 @@ from __future__ import absolute_import +from bokeh.palettes import Category10, Category20, Category20c from __future__ import division from __future__ import print_function from __future__ import unicode_literals @@ -6,340 +7,346 @@ from six import iteritems import collections - - workouttypes_ordered = collections.OrderedDict({ - 'water':'Standard Racing Shell', - 'rower':'Indoor Rower', - 'skierg':'Ski Erg', - 'bikeerg':'Bike Erg', - 'dynamic':'Dynamic Indoor Rower', - 'slides':'Indoor Rower on Slides', - 'paddle':'Paddle Adapter', - 'snow':'On-snow', - 'coastal':'Coastal', - 'c-boat':'Dutch C boat', - 'churchboat':'Finnish Church boat', - 'Ride':'Ride', - 'bike':'Bike', - 'Run':'Run', - 'NordicSki':'NordicSki', - 'Swim':'Swim', - 'Hike':'Hike', - 'Walk':'Walk', - 'Canoeing':'Canoeing', - 'Crossfit':'Crossfit', - 'StandUpPaddling':'StandUpPaddling', - 'IceSkate':'IceSkate', - 'WeightTraining':'WeightTraining', - 'InlineSkate':'InlineSkate', - 'Kayaking':'Kayaking', - 'Workout':'Workout', - 'Yoga':'Yoga', -# 'bike':'Bike', - 'other':'Other', - } + 'water': 'Standard Racing Shell', + 'rower': 'Indoor Rower', + 'skierg': 'Ski Erg', + 'bikeerg': 'Bike Erg', + 'dynamic': 'Dynamic Indoor Rower', + 'slides': 'Indoor Rower on Slides', + 'paddle': 'Paddle Adapter', + 'snow': 'On-snow', + 'coastal': 'Coastal', + 'c-boat': 'Dutch C boat', + 'churchboat': 'Finnish Church boat', + 'Ride': 'Ride', + 'bike': 'Bike', + 'Run': 'Run', + 'NordicSki': 'NordicSki', + 'Swim': 'Swim', + 'Hike': 'Hike', + 'Walk': 'Walk', + 'Canoeing': 'Canoeing', + 'Crossfit': 'Crossfit', + 'StandUpPaddling': 'StandUpPaddling', + 'IceSkate': 'IceSkate', + 'WeightTraining': 'WeightTraining', + 'InlineSkate': 'InlineSkate', + 'Kayaking': 'Kayaking', + 'Workout': 'Workout', + 'Yoga': 'Yoga', + # 'bike':'Bike', + 'other': 'Other', +} ) workouttypes_icons = collections.OrderedDict({ - 'water':'rowing.svg', # g - 'rower':'indoor_rower.svg', # g - 'skierg':'ski_erg.svg', #g - 'bikeerg':'bike_erg.svg', #g - 'dynamic':'indoor_rower.svg', - 'slides':'indoor_rower.svg', - 'paddle':'paddle.svg', # could be better - 'snow':'cross_country_ski.svg', # ok - 'coastal':'rowing.svg', # ok - 'c-boat':'rowing.svg', # ok - 'churchboat':'rowing.svg', # ok - 'Ride':'bike.svg', # ok - 'bike':'bike.svg', # ok - 'Run':'run.svg', # ok - 'NordicSki':'cross_country_ski.svg', # ok - 'Swim':'swim.svg', # ok - 'Hike':'hike.svg', # ok - 'Walk':'walk.svg', # o k - 'Canoeing':'canoeing.svg', # ok - 'Crossfit':'crossfit.svg', # ok - 'StandUpPaddling':'standup_paddling.svg', # ok - 'IceSkate':'ice_skating.svg', # ok - 'WeightTraining':'weight_training.svg', # ok - 'InlineSkate':'inline_skating.svg', - 'Kayaking':'kayaking.svg', # ok - 'Workout':'workout.svg', - 'Yoga':'yoga.svg', -# 'bike':'Bike', - 'other':'other.svg', - } + 'water': 'rowing.svg', # g + 'rower': 'indoor_rower.svg', # g + 'skierg': 'ski_erg.svg', # g + 'bikeerg': 'bike_erg.svg', # g + 'dynamic': 'indoor_rower.svg', + 'slides': 'indoor_rower.svg', + 'paddle': 'paddle.svg', # could be better + 'snow': 'cross_country_ski.svg', # ok + 'coastal': 'rowing.svg', # ok + 'c-boat': 'rowing.svg', # ok + 'churchboat': 'rowing.svg', # ok + 'Ride': 'bike.svg', # ok + 'bike': 'bike.svg', # ok + 'Run': 'run.svg', # ok + 'NordicSki': 'cross_country_ski.svg', # ok + 'Swim': 'swim.svg', # ok + 'Hike': 'hike.svg', # ok + 'Walk': 'walk.svg', # o k + 'Canoeing': 'canoeing.svg', # ok + 'Crossfit': 'crossfit.svg', # ok + 'StandUpPaddling': 'standup_paddling.svg', # ok + 'IceSkate': 'ice_skating.svg', # ok + 'WeightTraining': 'weight_training.svg', # ok + 'InlineSkate': 'inline_skating.svg', + 'Kayaking': 'kayaking.svg', # ok + 'Workout': 'workout.svg', + 'Yoga': 'yoga.svg', + # 'bike':'Bike', + 'other': 'other.svg', +} ) -workouttypes = tuple((key, value) for key, value in workouttypes_ordered.items()) +workouttypes = tuple((key, value) + for key, value in workouttypes_ordered.items()) + def Reverse(tuples): new_tup = tuples[::-1] return new_tup + stravacollection = ( - ('water','Rowing'), - ('rower','Rowing'), - ('skierg','NordicSki'), - ('bike','Ride'), - ('bikeerg','Ride'), - ('dynamic','Rowing'), - ('slides','Rowing'), - ('paddle','StandUpPaddling'), - ('snow','NordicSki'), - ('coastal','Rowing'), - ('c-boat','Rowing'), - ('churchboat','Rowing'), - ('Ride','Ride'), - ('Run','Run'), - ('NordicSki','NordicSki'), - ('Swim','Swim'), - ('Hike','Hike'), - ('Walk','Walk'), - ('Canoeing','Canoeing'), - ('Crossfit','Crossfit'), - ('StandUpPaddling','StandUpPaddling'), - ('IceSkate','IceSkate'), - ('WeightTraining','WeightTraining'), - ('InlineSkate','InlineSkate'), - ('Kayaking','Kayaking'), - ('Workout','Workout'), - ('Yoga','Yoga'), - ('other','Workout'), + ('water', 'Rowing'), + ('rower', 'Rowing'), + ('skierg', 'NordicSki'), + ('bike', 'Ride'), + ('bikeerg', 'Ride'), + ('dynamic', 'Rowing'), + ('slides', 'Rowing'), + ('paddle', 'StandUpPaddling'), + ('snow', 'NordicSki'), + ('coastal', 'Rowing'), + ('c-boat', 'Rowing'), + ('churchboat', 'Rowing'), + ('Ride', 'Ride'), + ('Run', 'Run'), + ('NordicSki', 'NordicSki'), + ('Swim', 'Swim'), + ('Hike', 'Hike'), + ('Walk', 'Walk'), + ('Canoeing', 'Canoeing'), + ('Crossfit', 'Crossfit'), + ('StandUpPaddling', 'StandUpPaddling'), + ('IceSkate', 'IceSkate'), + ('WeightTraining', 'WeightTraining'), + ('InlineSkate', 'InlineSkate'), + ('Kayaking', 'Kayaking'), + ('Workout', 'Workout'), + ('Yoga', 'Yoga'), + ('other', 'Workout'), ) -stravamapping = {key:value for key,value in Reverse(stravacollection)} +stravamapping = {key: value for key, value in Reverse(stravacollection)} garmincollection = ( - ('water','ROWING'), - ('rower','INDOOR_ROWING'), - ('skierg','CROSS_COUNTRY_SKIING'), - ('bike','ROAD_BIKING'), - ('bikeerg','INDOOR_CYCLING'), - ('dynamic','INDOOR_ROWING'), - ('slides','INDOOR_ROWING'), - ('paddle','PADDLING'), - ('snow','CROSS_COUNTRY_SKIING'), - ('coastal','ROWING'), - ('c-boat','ROWING'), - ('churchboat','ROWING'), - ('Ride','ROAD_BIKING'), - ('Run','RUNNING'), - ('NordicSki','CROSS_COUNTRY_SKIING'), - ('Swim','SWIMMING'), - ('Hike','HIKING'), - ('Walk','WALKING'), - ('Canoeing','PADDLING'), - ('Crossfit','FITNESS_EQUIPMENT'), - ('StandUpPaddling','STAND_UP_PADDLEBOARDING'), - ('IceSkate','SKATING'), - ('WeightTraining','STRENGTH_TRAINING'), - ('InlineSkate','INLINE_SKATING'), - ('Kayaking','PADDLING'), - ('Workout','OTHER'), - ('Yoga','OTHER'), - ('other','OTHER'), + ('water', 'ROWING'), + ('rower', 'INDOOR_ROWING'), + ('skierg', 'CROSS_COUNTRY_SKIING'), + ('bike', 'ROAD_BIKING'), + ('bikeerg', 'INDOOR_CYCLING'), + ('dynamic', 'INDOOR_ROWING'), + ('slides', 'INDOOR_ROWING'), + ('paddle', 'PADDLING'), + ('snow', 'CROSS_COUNTRY_SKIING'), + ('coastal', 'ROWING'), + ('c-boat', 'ROWING'), + ('churchboat', 'ROWING'), + ('Ride', 'ROAD_BIKING'), + ('Run', 'RUNNING'), + ('NordicSki', 'CROSS_COUNTRY_SKIING'), + ('Swim', 'SWIMMING'), + ('Hike', 'HIKING'), + ('Walk', 'WALKING'), + ('Canoeing', 'PADDLING'), + ('Crossfit', 'FITNESS_EQUIPMENT'), + ('StandUpPaddling', 'STAND_UP_PADDLEBOARDING'), + ('IceSkate', 'SKATING'), + ('WeightTraining', 'STRENGTH_TRAINING'), + ('InlineSkate', 'INLINE_SKATING'), + ('Kayaking', 'PADDLING'), + ('Workout', 'OTHER'), + ('Yoga', 'OTHER'), + ('other', 'OTHER'), ) -garminmapping = {key:value for key,value in Reverse(garmincollection)} +garminmapping = {key: value for key, value in Reverse(garmincollection)} fitcollection = ( -('water','rowing'), -('rower','rowing'), -('skierg','cross_country_skiing'), -('bike','cycling'), -('bikeerg','cycling'), -('dynamic','rowing'), -('slides','rowing'), -('paddle','paddling'), -('snow','cross_country_skiing'), -('coastal','rowing'), -('c-boat','rowing'), -('churchboat','rowing'), -('Ride','cycling'), -('Run','running'), -('NordicSki','cross_country_skiing'), -('Swim','swimming'), -('Hike','hiking'), -('RollerSki','cross_country_skiing'), -('Walk','walking'), -('Canoeing','boating'), -('Crossfit','fitness_equipment'), -('StandUpPaddling','stand_up_paddle_boarding'), -('IceSkate','ice_skating'), -('WeightTraining','training'), -('InlineSkate','inline_skating'), -('Kayaking','kayaking'), -('Workout','generic'), -('Yoga','generic'), -('other','generic'), + ('water', 'rowing'), + ('rower', 'rowing'), + ('skierg', 'cross_country_skiing'), + ('bike', 'cycling'), + ('bikeerg', 'cycling'), + ('dynamic', 'rowing'), + ('slides', 'rowing'), + ('paddle', 'paddling'), + ('snow', 'cross_country_skiing'), + ('coastal', 'rowing'), + ('c-boat', 'rowing'), + ('churchboat', 'rowing'), + ('Ride', 'cycling'), + ('Run', 'running'), + ('NordicSki', 'cross_country_skiing'), + ('Swim', 'swimming'), + ('Hike', 'hiking'), + ('RollerSki', 'cross_country_skiing'), + ('Walk', 'walking'), + ('Canoeing', 'boating'), + ('Crossfit', 'fitness_equipment'), + ('StandUpPaddling', 'stand_up_paddle_boarding'), + ('IceSkate', 'ice_skating'), + ('WeightTraining', 'training'), + ('InlineSkate', 'inline_skating'), + ('Kayaking', 'kayaking'), + ('Workout', 'generic'), + ('Yoga', 'generic'), + ('other', 'generic'), ) - -fitmapping = {key:value for key,value in Reverse(fitcollection)} +fitmapping = {key: value for key, value in Reverse(fitcollection)} stcollection = ( - ('water','Rowing'), - ('rower','Rowing'), - ('skierg','Skiing:Nordic'), - ('bike','Cycling'), - ('bikeerg','Cycling'), - ('dynamic','Rowing'), - ('slides','Rowing'), - ('paddle','Other:Paddling'), - ('snow','Skiing:Nordic'), - ('coastal','Rowing'), - ('c-boat','Rowing'), - ('churchboat','Rowing'), - ('Ride','Cycling'), - ('Run','Running'), - ('NordicSki','Skiing:Nordic'), - ('Swim','Swimming'), - ('Hike','Hiking'), - ('RollerSki','Other:RollerSki'), - ('Walk','Other:Walk'), - ('Canoeing','Other:Canoeing'), - ('Crossfit','Other:Crossfit'), - ('StandUpPaddling','Other:StandUpPaddling'), - ('IceSkate','Skating'), - ('WeightTraining','Other:WeightTraining'), - ('InlineSkate','Skating:InlineSkate'), - ('Kayaking','Other:Kayaking'), - ('Workout','Other:Workout'), - ('Yoga','Other'), - ('other','Other'), + ('water', 'Rowing'), + ('rower', 'Rowing'), + ('skierg', 'Skiing:Nordic'), + ('bike', 'Cycling'), + ('bikeerg', 'Cycling'), + ('dynamic', 'Rowing'), + ('slides', 'Rowing'), + ('paddle', 'Other:Paddling'), + ('snow', 'Skiing:Nordic'), + ('coastal', 'Rowing'), + ('c-boat', 'Rowing'), + ('churchboat', 'Rowing'), + ('Ride', 'Cycling'), + ('Run', 'Running'), + ('NordicSki', 'Skiing:Nordic'), + ('Swim', 'Swimming'), + ('Hike', 'Hiking'), + ('RollerSki', 'Other:RollerSki'), + ('Walk', 'Other:Walk'), + ('Canoeing', 'Other:Canoeing'), + ('Crossfit', 'Other:Crossfit'), + ('StandUpPaddling', 'Other:StandUpPaddling'), + ('IceSkate', 'Skating'), + ('WeightTraining', 'Other:WeightTraining'), + ('InlineSkate', 'Skating:InlineSkate'), + ('Kayaking', 'Other:Kayaking'), + ('Workout', 'Other:Workout'), + ('Yoga', 'Other'), + ('other', 'Other'), ) -stmapping = {key:value for key,value in Reverse(stcollection)} +stmapping = {key: value for key, value in Reverse(stcollection)} polarcollection = ( - ('water','Rowing'), - ('rower','Rowing'), - ('skierg','Skiing'), - ('bike','Cycling'), - ('bikeerg','Cycling'), - ('dynamic','Rowing'), - ('slides','Rowing'), - ('paddle','Other Outdoor'), - ('snow','Skiing'), - ('coastal','Rowing'), - ('c-boat','Rowing'), - ('churchboat','Rowing'), - ('Ride','Cycling'), - ('Run','Running'), - ('NordicSki','Skiing'), - ('Swim','Swimming'), - ('Hike','Hiking'), - ('Walk','Walking'), - ('Canoeing','Canoeing'), - ('Crossfit','Crossfit'), - ('StandUpPaddling','Other Outdoor'), - ('IceSkate','Skating'), - ('WeightTraining','Strength training'), - ('InlineSkate','Skating'), - ('Kayaking','Kayaking'), - ('Workout','Other Indoor'), - ('other','Other'), - ('Yoga','Yoga'), - ) + ('water', 'Rowing'), + ('rower', 'Rowing'), + ('skierg', 'Skiing'), + ('bike', 'Cycling'), + ('bikeerg', 'Cycling'), + ('dynamic', 'Rowing'), + ('slides', 'Rowing'), + ('paddle', 'Other Outdoor'), + ('snow', 'Skiing'), + ('coastal', 'Rowing'), + ('c-boat', 'Rowing'), + ('churchboat', 'Rowing'), + ('Ride', 'Cycling'), + ('Run', 'Running'), + ('NordicSki', 'Skiing'), + ('Swim', 'Swimming'), + ('Hike', 'Hiking'), + ('Walk', 'Walking'), + ('Canoeing', 'Canoeing'), + ('Crossfit', 'Crossfit'), + ('StandUpPaddling', 'Other Outdoor'), + ('IceSkate', 'Skating'), + ('WeightTraining', 'Strength training'), + ('InlineSkate', 'Skating'), + ('Kayaking', 'Kayaking'), + ('Workout', 'Other Indoor'), + ('other', 'Other'), + ('Yoga', 'Yoga'), +) -polarmapping = {key:value for key,value in Reverse(polarcollection)} +polarmapping = {key: value for key, value in Reverse(polarcollection)} tpcollection = ( - ('water','rowing'), - ('rower','rowing'), - ('skierg','xc-ski'), - ('bike','bike'), - ('Bikeerg','bike'), - ('dynamic','rowing'), - ('slides','rowing'), - ('paddle','other'), - ('snow','xc-ski'), - ('coastal','rowing'), - ('c-boat','rowing'), - ('churchboat','rowing'), - ('Ride','cycling'), - ('Run','run'), - ('NordicSki','xc-ski'), - ('Swim','swim'), - ('Hike','other'), - ('Walk','walk'), - ('Canoeing','other'), - ('Crossfit','other'), - ('StandUpPaddling','other'), - ('IceSkate','other'), - ('WeightTraining','strength'), - ('InlineSkate','other'), - ('Kayaking','other'), - ('Workout','other'), - ('other','other'), - ('Yoga','other'), - ) + ('water', 'rowing'), + ('rower', 'rowing'), + ('skierg', 'xc-ski'), + ('bike', 'bike'), + ('Bikeerg', 'bike'), + ('dynamic', 'rowing'), + ('slides', 'rowing'), + ('paddle', 'other'), + ('snow', 'xc-ski'), + ('coastal', 'rowing'), + ('c-boat', 'rowing'), + ('churchboat', 'rowing'), + ('Ride', 'cycling'), + ('Run', 'run'), + ('NordicSki', 'xc-ski'), + ('Swim', 'swim'), + ('Hike', 'other'), + ('Walk', 'walk'), + ('Canoeing', 'other'), + ('Crossfit', 'other'), + ('StandUpPaddling', 'other'), + ('IceSkate', 'other'), + ('WeightTraining', 'strength'), + ('InlineSkate', 'other'), + ('Kayaking', 'other'), + ('Workout', 'other'), + ('other', 'other'), + ('Yoga', 'other'), +) -tpmapping = {key:value for key,value in Reverse(tpcollection)} +tpmapping = {key: value for key, value in Reverse(tpcollection)} c2collection = ( - ('water','water'), - ('rower','rower'), - ('skierg','skierg'), - ('bike','bike'), - ('bikeerg','bike'), - ('dynamic','dynamic'), - ('slides','slides'), - ('paddle','paddle'), - ('snow','snow'), - ('coastal','water'), - ('c-boat','water'), - ('churchboat','water'), - ('Ride','bike'), - ('Run',None), - ('NordicSki','snow'), - ('Swim',None), - ('Hike',None), - ('Walk',None), - ('Canoeing','paddle'), - ('Crossfit',None), - ('StandUpPaddling',None), - ('IceSkate',None), - ('WeightTraining',None), - ('InlineSkate',None), - ('Kayaking',None), - ('Workout',None), - ('other',None), - ('Yoga',None), - ) + ('water', 'water'), + ('rower', 'rower'), + ('skierg', 'skierg'), + ('bike', 'bike'), + ('bikeerg', 'bike'), + ('dynamic', 'dynamic'), + ('slides', 'slides'), + ('paddle', 'paddle'), + ('snow', 'snow'), + ('coastal', 'water'), + ('c-boat', 'water'), + ('churchboat', 'water'), + ('Ride', 'bike'), + ('Run', None), + ('NordicSki', 'snow'), + ('Swim', None), + ('Hike', None), + ('Walk', None), + ('Canoeing', 'paddle'), + ('Crossfit', None), + ('StandUpPaddling', None), + ('IceSkate', None), + ('WeightTraining', None), + ('InlineSkate', None), + ('Kayaking', None), + ('Workout', None), + ('other', None), + ('Yoga', None), +) -c2mapping = {key:value for key,value in Reverse(c2collection)} -c2mappinginv = {value:key for key,value in Reverse(c2collection) if value is not None} +c2mapping = {key: value for key, value in Reverse(c2collection)} +c2mappinginv = {value: key for key, value in Reverse( + c2collection) if value is not None} -stravamappinginv = {value:key for key,value in Reverse(stravacollection) if value is not None} +stravamappinginv = {value: key for key, value in Reverse( + stravacollection) if value is not None} -stmappinginv = {value:key for key,value in Reverse(stcollection) if value is not None} +stmappinginv = {value: key for key, value in Reverse( + stcollection) if value is not None} -polarmappinginv = {value.lower():key for key,value in Reverse(polarcollection) if value is not None} +polarmappinginv = {value.lower(): key for key, value in Reverse( + polarcollection) if value is not None} -garminmappinginv = {value:key for key, value in Reverse(garmincollection) if value is not None} +garminmappinginv = {value: key for key, value in Reverse( + garmincollection) if value is not None} -fitmappinginv = {value:key for key,value in Reverse(fitcollection) if value is not None} +fitmappinginv = {value: key for key, value in Reverse( + fitcollection) if value is not None} otwtypes = ( 'water', 'coastal', 'c-boat', 'churchboat' - ) +) otetypes = ( 'rower', 'dynamic', 'slides' - ) +) rowtypes = ( 'water', @@ -349,72 +356,70 @@ rowtypes = ( 'coastal', 'c-boat', 'churchboat' - ) +) checktypes = [i[0] for i in workouttypes_ordered.items()] -from bokeh.palettes import Category10,Category20, Category20c - #colors = Category10[9] colors = list(set(Category10[9]))+list(set(Category20[19]+Category20c[19])) -color_map = {checktypes[i]:colors[i] for i in range(len(checktypes))} +color_map = {checktypes[i]: colors[i] for i in range(len(checktypes))} color_map = { - 'water': 'blue', #'#2ca02c', - 'rower': 'red', #'#ff7f0e', - 'skierg': 'ghostwhite', #'#8c564b', - 'bikeerg': 'pink', #'#d62728', - 'dynamic': 'darkred', #'#e377c2', - 'slides': 'salmon', #'#9467bd', - 'paddle': 'cyan', #'#7f7f7f', - 'snow': 'snow',#'#1f77b4', - 'coastal': 'navy', #'#bcbd22', - 'c-boat': 'lightsteelblue', #'#ffbb78', - 'churchboat': 'midnightblue', #'#17becf', - 'Ride': 'hotpink', #'#3182bd', + 'water': 'blue', # '#2ca02c', + 'rower': 'red', # '#ff7f0e', + 'skierg': 'ghostwhite', # '#8c564b', + 'bikeerg': 'pink', # '#d62728', + 'dynamic': 'darkred', # '#e377c2', + 'slides': 'salmon', # '#9467bd', + 'paddle': 'cyan', # '#7f7f7f', + 'snow': 'snow', # '#1f77b4', + 'coastal': 'navy', # '#bcbd22', + 'c-boat': 'lightsteelblue', # '#ffbb78', + 'churchboat': 'midnightblue', # '#17becf', + 'Ride': 'hotpink', # '#3182bd', 'Bike': 'deeppink', - 'bike': 'deeppink', #'#e6550d', - 'Run': 'green', #'#ff9896', - 'NordicSki': 'lightgray', #'#74c476', - 'Swim': 'aqua', #'#fd8d3c', - 'Hike': 'lime', #'#fdae6b', - 'Walk': 'lawngreen', #'#756bb1', - 'Canoeing': 'turquoise', #'#fdd0a2', - 'Crossfit': 'yellow', #'#c5b0d5', - 'StandUpPaddling': 'lightseagreen', #'#dadaeb', - 'IceSkate': 'gray', #'#9ecae1', - 'WeightTraining': 'khaki', #'#e377c2', - 'InlineSkate': 'slategray', #'#98df8a', - 'Kayaking': 'teal', #'#31a354', - 'Workout': 'gold',#' #''#f7b6d2', - 'Yoga': 'hotpink', #'#1f77b4', - 'other': 'indigo', #'#bcbd22' - } + 'bike': 'deeppink', # '#e6550d', + 'Run': 'green', # '#ff9896', + 'NordicSki': 'lightgray', # '#74c476', + 'Swim': 'aqua', # '#fd8d3c', + 'Hike': 'lime', # '#fdae6b', + 'Walk': 'lawngreen', # '#756bb1', + 'Canoeing': 'turquoise', # '#fdd0a2', + 'Crossfit': 'yellow', # '#c5b0d5', + 'StandUpPaddling': 'lightseagreen', # '#dadaeb', + 'IceSkate': 'gray', # '#9ecae1', + 'WeightTraining': 'khaki', # '#e377c2', + 'InlineSkate': 'slategray', # '#98df8a', + 'Kayaking': 'teal', # '#31a354', + 'Workout': 'gold', # ' #''#f7b6d2', + 'Yoga': 'hotpink', # '#1f77b4', + 'other': 'indigo', # '#bcbd22' +} workoutsources = ( - ('strava','strava'), - ('concept2','concept2'), - ('sporttracks','sporttracks'), - ('mapmyfitness','mapmyfitness'), - ('csv','painsled'), - ('tcx','tcx'), - ('rp','rowperfect'), - ('mystery','mystery'), -# ('tcxnohr','tcx (no HR)'), - ('rowperfect3','rowperfect3'), - ('ergdata','ergdata'), - ('boatcoach','boatcoach'), - ('boatcoachotw','boatcoachotw'), -# ('bcmike','boatcoach (develop)'), - ('painsleddesktop','painsleddesktop'), - ('speedcoach','speedcoach'), - ('speedcoach2','speedcoach2'), - ('ergstick','ergstick'), - ('fit','fit'), - ('unknown','unknown')) + ('strava', 'strava'), + ('concept2', 'concept2'), + ('sporttracks', 'sporttracks'), + ('mapmyfitness', 'mapmyfitness'), + ('csv', 'painsled'), + ('tcx', 'tcx'), + ('rp', 'rowperfect'), + ('mystery', 'mystery'), + # ('tcxnohr','tcx (no HR)'), + ('rowperfect3', 'rowperfect3'), + ('ergdata', 'ergdata'), + ('boatcoach', 'boatcoach'), + ('boatcoachotw', 'boatcoachotw'), + # ('bcmike','boatcoach (develop)'), + ('painsleddesktop', 'painsleddesktop'), + ('speedcoach', 'speedcoach'), + ('speedcoach2', 'speedcoach2'), + ('ergstick', 'ergstick'), + ('fit', 'fit'), + ('unknown', 'unknown')) boattypes = ( ('1x', '1x (single)'), @@ -422,8 +427,8 @@ boattypes = ( ('2x+', '2x+ (coxed double)'), ('2-', '2- (pair)'), ('2+', '2+ (coxed pair)'), - ('3x+','3x+ (coxed triple)'), - ('3x-','3x- (triple)'), + ('3x+', '3x+ (coxed triple)'), + ('3x-', '3x- (triple)'), ('4x', '4x (quad)'), ('4x+', '4x+ (coxed quad)'), ('4-', '4- (four)'), @@ -433,111 +438,111 @@ boattypes = ( ) adaptivetypes = ( - ('None','Open'), + ('None', 'Open'), ('PR1', 'PR1 (Arms and Shoulders)'), ('PR2', 'PR2 (Trunk and Arms)'), ('PR3', 'PR3 (Leg Trunk and Arms)'), ('FES', 'FES (Functional Electrical Stimulation)'), - ) +) weightcategories = ( - ('hwt','open-weight'), - ('lwt','light-weight'), - ) + ('hwt', 'open-weight'), + ('lwt', 'light-weight'), +) sexcategories = ( - ('male','Open'), - ('female','Female'), - ) + ('male', 'Open'), + ('female', 'Female'), +) waterboattype = [i[0] for i in boattypes] privacychoices = ( - ('private','Private'), - ('visible','Visible'), + ('private', 'Private'), + ('visible', 'Visible'), ) boatbrands = ( - ('other','Other'), - ('alden','Alden Rowing'), - ('averowing','AveRowing Boats'), - ('aylings','Aylings'), - ('bbg','BBG'), - ('burgashell','Burgashell'), - ('burton','Burton Water Sports'), - ('douglas','Carl Douglas Racing Shells'), - ('carbocraft','Carbocraft'), - ('colley','Colley'), - ('cucchietti','Cucchietti'), - ('dirigo','Dirigo'), - ('drew','Drew Harrison Racing'), - ('echo','Echo Rowing'), - ('edon','Edon TS515 Sculling Boats'), - ('edwin','Edwin Phelps'), - ('empacher','Empacher'), - ('eton','Eton Racing Boats (ERB)'), - ('euro','Euro Diffusions'), - ('filippi','Filippi Boats'), - ('fluidesign','Fluidesign'), - ('dragon','Flying Dragon Boat Co (Huangzhou, China)'), - ('gig','Gig Harbor Boat Works'), - ('sharrow','George Sharrow Racing Shells'), - ('harris','Harris'), - ('hitech','Hi-Tech'), - ('hudson','Hudson Boatworks'), - ('janousek','Janousek Racing Boats'), - ('waugh','John Waugh Racing Boats'), - ('laszlo','Laszlo Boats NZ'), - ('leo','Leo Coastal Rowing'), - ('levator','Levator Boatworks'), - ('liangjin','Liangjin Boat'), - ('liteboat','LiteBoat'), - ('littleriver','Little River Marine'), - ('kaschper','Kaschper Racing Shells'), - ('kanghua','Kanghua'), - ('king','King Racing Shells'), - ('kiwi','Kiwi International Rowing Skiffs (KIRS)'), - ('lola','Lola Aylings'), - ('maas','Maas Rowing Shells'), - ('maas','Maas Boat (coastal)'), - ('nelo','Nelo Rowing'), - ('owen','Owen'), - ('peinert','Peinert'), - ('pocock','Pocock Racing Shells'), - ('race1','Race 1 Australia'), - ('radley','Radley'), - ('resolute','Resolute Racing Shells'), - ('salani','Salani'), - ('schoenbrod','Helmut Schoenbrod'), - ('salterbros','Salter Bros'), - ('sims','Ray Sims'), - ('slracing','SL Racing'), - ('stampfli','Stämpfli Racing Boats'), - ('sutton','Sutton'), - ('swastik','Swastik fibchem industry'), - ('swift','Swift Racing'), - ('sykes','Sykes Racing'), - ('vandusen','Van Dusen'), - ('vega','Vega'), - ('vespoli','Vespoli'), - ('vicente','Vicente Dors'), - ('virus','Virus'), - ('whitehall','Whitehall Rowing'), - ('wiersma','Roeiwerf Wiersma'), - ('wintech','WinTech Racing'), - ('worcester','Worcester Oar & Paddle (Joe Garafolo)'), - ('swastik','Swastik Boats'), + ('other', 'Other'), + ('alden', 'Alden Rowing'), + ('averowing', 'AveRowing Boats'), + ('aylings', 'Aylings'), + ('bbg', 'BBG'), + ('burgashell', 'Burgashell'), + ('burton', 'Burton Water Sports'), + ('douglas', 'Carl Douglas Racing Shells'), + ('carbocraft', 'Carbocraft'), + ('colley', 'Colley'), + ('cucchietti', 'Cucchietti'), + ('dirigo', 'Dirigo'), + ('drew', 'Drew Harrison Racing'), + ('echo', 'Echo Rowing'), + ('edon', 'Edon TS515 Sculling Boats'), + ('edwin', 'Edwin Phelps'), + ('empacher', 'Empacher'), + ('eton', 'Eton Racing Boats (ERB)'), + ('euro', 'Euro Diffusions'), + ('filippi', 'Filippi Boats'), + ('fluidesign', 'Fluidesign'), + ('dragon', 'Flying Dragon Boat Co (Huangzhou, China)'), + ('gig', 'Gig Harbor Boat Works'), + ('sharrow', 'George Sharrow Racing Shells'), + ('harris', 'Harris'), + ('hitech', 'Hi-Tech'), + ('hudson', 'Hudson Boatworks'), + ('janousek', 'Janousek Racing Boats'), + ('waugh', 'John Waugh Racing Boats'), + ('laszlo', 'Laszlo Boats NZ'), + ('leo', 'Leo Coastal Rowing'), + ('levator', 'Levator Boatworks'), + ('liangjin', 'Liangjin Boat'), + ('liteboat', 'LiteBoat'), + ('littleriver', 'Little River Marine'), + ('kaschper', 'Kaschper Racing Shells'), + ('kanghua', 'Kanghua'), + ('king', 'King Racing Shells'), + ('kiwi', 'Kiwi International Rowing Skiffs (KIRS)'), + ('lola', 'Lola Aylings'), + ('maas', 'Maas Rowing Shells'), + ('maas', 'Maas Boat (coastal)'), + ('nelo', 'Nelo Rowing'), + ('owen', 'Owen'), + ('peinert', 'Peinert'), + ('pocock', 'Pocock Racing Shells'), + ('race1', 'Race 1 Australia'), + ('radley', 'Radley'), + ('resolute', 'Resolute Racing Shells'), + ('salani', 'Salani'), + ('schoenbrod', 'Helmut Schoenbrod'), + ('salterbros', 'Salter Bros'), + ('sims', 'Ray Sims'), + ('slracing', 'SL Racing'), + ('stampfli', 'Stämpfli Racing Boats'), + ('sutton', 'Sutton'), + ('swastik', 'Swastik fibchem industry'), + ('swift', 'Swift Racing'), + ('sykes', 'Sykes Racing'), + ('vandusen', 'Van Dusen'), + ('vega', 'Vega'), + ('vespoli', 'Vespoli'), + ('vicente', 'Vicente Dors'), + ('virus', 'Virus'), + ('whitehall', 'Whitehall Rowing'), + ('wiersma', 'Roeiwerf Wiersma'), + ('wintech', 'WinTech Racing'), + ('worcester', 'Worcester Oar & Paddle (Joe Garafolo)'), + ('swastik', 'Swastik Boats'), ) -polaraccesslink_sports = { +polaraccesslink_sports = { 'AEROBICS': 'Workout', 'AMERICAN_FOOTBALL': 'other', 'AQUATICS': 'other', 'BACKCOUNTRY_SKIING': 'snow', 'BADMINTON': 'other', - 'BALLET_DANCING':'other', + 'BALLET_DANCING': 'other', 'BALLROOM_DANCING': 'other', - 'BASEBALL':'other', + 'BASEBALL': 'other', 'BASKETBALL': 'other', 'BEACH_VOLLEYBALL': 'other', 'BIATHLON': 'NordicSki', @@ -555,113 +560,113 @@ polaraccesslink_sports = { 'DUATHLON': 'other', 'DUATHLON_CYCLING': 'bike', 'DUATHLON_RUNNING': 'Run', - 'FIELD_HOCKEY':'other', + 'FIELD_HOCKEY': 'other', 'FINNISH_BASEBALL': 'other', - 'FITNESS_DANCING':'other', - 'FITNESS_MARTIAL_ARTS':'other', - 'FITNESS_STEP':'Workout', - 'FLOORBALL':'other', - 'FREE_MULTISPORT':'other', - 'FRISBEEGOLF':'other', - 'FUNCTIONAL_TRAINING':'other', + 'FITNESS_DANCING': 'other', + 'FITNESS_MARTIAL_ARTS': 'other', + 'FITNESS_STEP': 'Workout', + 'FLOORBALL': 'other', + 'FREE_MULTISPORT': 'other', + 'FRISBEEGOLF': 'other', + 'FUNCTIONAL_TRAINING': 'other', 'FUTSAL': 'other', 'GOLF': 'other', - 'GROUP_EXERCISE':'Workout', + 'GROUP_EXERCISE': 'Workout', 'GYMNASTICS': 'other', 'HANDBALL': 'other', - 'HIIT':'Workout', - 'HIKING':'Hike', - 'ICE_HOCKEY':'other', - 'ICE_SKATING':'IceSkate', - 'INDOOR_CYCLING':'Ride', - 'INDOOR_ROWING':'rower', - 'INLINE_SKATING':'InlineSkate', - 'JAZZ_DANCING':'other', - 'JOGGING':'Run', - 'JUDO_MARTIAL_ARTS':'other', - 'KICKBOXING_MARTIAL_ARTS':'other', - 'LATIN_DANCING':'other', - 'LES_MILLS_BARRE':'other', - 'LES_MILLS_BODYATTACK':'other', - 'LES_MILLS_BODYBALANCE':'other', - 'LES_MILLS_BODYCOMBAT':'other', - 'LES_MILLS_BODYJAM':'other', - 'LES_MILLS_BODYPUMP':'other', - 'LES_MILLS_BODYSTEP':'other', - 'LES_MILLS_CXWORKS':'other', - 'LES_MILLS_GRIT_ATHLETIC':'other', - 'LES_MILLS_GRIT_CARDIO':'other', - 'LES_MILLS_GRIT_STRENGTH':'other', - 'LES_MILLS_RPM':'other', - 'LES_MILLS_SHBAM':'other', - 'LES_MILLS_SPRINT':'other', - 'LES_MILLS_TONE':'other', - 'LES_MILLS_TRIP':'other', - 'MOBILITY_DYNAMIC':'other', - 'MOBILITY_STATIC':'other', - 'MODERN_DANCING':'other', - 'MOUNTAIN_BIKING':'bike', - 'NORDIC_WALKING':'Hike', - 'OFFROADDUATHLON':'other', - 'OFFROADDUATHLON_CYCLING':'bike', - 'OFFROADDUATHLON_RUNNING':'Run', - 'OFFROADTRIATHLON':'other', - 'OFFROADTRIATHLON_CYCLING':'bike', - 'OFFROADTRIATHLON_RUNNING':'Run', - 'OFFROADTRIATHLON_SWIMMING':'Swim', - 'OPEN_WATER_SWIMMING':'Swim', - 'ORIENTEERING':'Run', - 'ORIENTEERING_MTB':'bike', - 'ORIENTEERING_SKI':'NordicSki', - 'OTHER_INDOOR':'Workout', - 'OTHER_OUTDOOR':'other', - 'PARASPORTS_WHEELCHAIR':'other', - 'PILATES':'Workout', - 'POOL_SWIMMING':'Swim', - 'RIDING':'Ride', - 'ROAD_BIKING':'bike', - 'ROAD_RUNNING':'Run', - 'ROLLER_BLADING':'InlineSkate', - 'ROLLER_SKIING_CLASSIC':'NordicSki', - 'ROLLER_SKIING_FREESTYLE':'NordicSki', + 'HIIT': 'Workout', + 'HIKING': 'Hike', + 'ICE_HOCKEY': 'other', + 'ICE_SKATING': 'IceSkate', + 'INDOOR_CYCLING': 'Ride', + 'INDOOR_ROWING': 'rower', + 'INLINE_SKATING': 'InlineSkate', + 'JAZZ_DANCING': 'other', + 'JOGGING': 'Run', + 'JUDO_MARTIAL_ARTS': 'other', + 'KICKBOXING_MARTIAL_ARTS': 'other', + 'LATIN_DANCING': 'other', + 'LES_MILLS_BARRE': 'other', + 'LES_MILLS_BODYATTACK': 'other', + 'LES_MILLS_BODYBALANCE': 'other', + 'LES_MILLS_BODYCOMBAT': 'other', + 'LES_MILLS_BODYJAM': 'other', + 'LES_MILLS_BODYPUMP': 'other', + 'LES_MILLS_BODYSTEP': 'other', + 'LES_MILLS_CXWORKS': 'other', + 'LES_MILLS_GRIT_ATHLETIC': 'other', + 'LES_MILLS_GRIT_CARDIO': 'other', + 'LES_MILLS_GRIT_STRENGTH': 'other', + 'LES_MILLS_RPM': 'other', + 'LES_MILLS_SHBAM': 'other', + 'LES_MILLS_SPRINT': 'other', + 'LES_MILLS_TONE': 'other', + 'LES_MILLS_TRIP': 'other', + 'MOBILITY_DYNAMIC': 'other', + 'MOBILITY_STATIC': 'other', + 'MODERN_DANCING': 'other', + 'MOUNTAIN_BIKING': 'bike', + 'NORDIC_WALKING': 'Hike', + 'OFFROADDUATHLON': 'other', + 'OFFROADDUATHLON_CYCLING': 'bike', + 'OFFROADDUATHLON_RUNNING': 'Run', + 'OFFROADTRIATHLON': 'other', + 'OFFROADTRIATHLON_CYCLING': 'bike', + 'OFFROADTRIATHLON_RUNNING': 'Run', + 'OFFROADTRIATHLON_SWIMMING': 'Swim', + 'OPEN_WATER_SWIMMING': 'Swim', + 'ORIENTEERING': 'Run', + 'ORIENTEERING_MTB': 'bike', + 'ORIENTEERING_SKI': 'NordicSki', + 'OTHER_INDOOR': 'Workout', + 'OTHER_OUTDOOR': 'other', + 'PARASPORTS_WHEELCHAIR': 'other', + 'PILATES': 'Workout', + 'POOL_SWIMMING': 'Swim', + 'RIDING': 'Ride', + 'ROAD_BIKING': 'bike', + 'ROAD_RUNNING': 'Run', + 'ROLLER_BLADING': 'InlineSkate', + 'ROLLER_SKIING_CLASSIC': 'NordicSki', + 'ROLLER_SKIING_FREESTYLE': 'NordicSki', 'ROWING': 'water', - 'RUGBY':'other', - 'RUNNING':'Run', - 'SHOW_DANCING':'other', - 'SKATING':'IceSkate', - 'SNOWBOARDING':'snow', - 'SNOWSHOE_TREKKING':'Hike', - 'SOCCER':'other', - 'SPINNING':'bikeerg', - 'SQUASH':'other', - 'STREET_DANCING':'other', - 'STRENGTH_TRAINING':'WeightTraining', - 'STRETCHING':'Workout', - 'SWIMMING':'Swim', - 'TABLE_TENNIS':'other', - 'TELEMARK_SKIING':'NordicSki', - 'TENNIS':'other', - 'TRACK_AND_FIELD_RUNNING':'Run', - 'TRAIL_RUNNING':'Run', - 'TREADMILL_RUNNING':'Run', - 'TRIATHLON':'bike', - 'TRIATHLON_CYCLING':'bike', - 'TRIATHLON_RUNNING':'Run', - 'TRIATHLON_SWIMMING':'Swim', - 'TROTTING':'other', - 'ULTRARUNNING_RUNNING':'Run', - 'VERTICALSPORTS_WALLCLIMBING':'other', - 'VOLLEYBALL':'other', - 'WALKING':'Walk', - 'WATERSPORTS_CANOEING':'Canoeing', - 'WATERSPORTS_KAYAKING':'Kayaking', - 'WATERSPORTS_KITESURFING':'other', - 'WATERSPORTS_SAILING':'other', - 'WATERSPORTS_SURFING':'other', - 'WATERSPORTS_WAKEBOARDING':'other', - 'WATERSPORTS_WATERSKI':'other', - 'WATERSPORTS_WINDSURFING':'other', - 'XC_SKIING_CLASSIC':'NordicSki', - 'XC_SKIING_FREESTYLE':'NordicSki', + 'RUGBY': 'other', + 'RUNNING': 'Run', + 'SHOW_DANCING': 'other', + 'SKATING': 'IceSkate', + 'SNOWBOARDING': 'snow', + 'SNOWSHOE_TREKKING': 'Hike', + 'SOCCER': 'other', + 'SPINNING': 'bikeerg', + 'SQUASH': 'other', + 'STREET_DANCING': 'other', + 'STRENGTH_TRAINING': 'WeightTraining', + 'STRETCHING': 'Workout', + 'SWIMMING': 'Swim', + 'TABLE_TENNIS': 'other', + 'TELEMARK_SKIING': 'NordicSki', + 'TENNIS': 'other', + 'TRACK_AND_FIELD_RUNNING': 'Run', + 'TRAIL_RUNNING': 'Run', + 'TREADMILL_RUNNING': 'Run', + 'TRIATHLON': 'bike', + 'TRIATHLON_CYCLING': 'bike', + 'TRIATHLON_RUNNING': 'Run', + 'TRIATHLON_SWIMMING': 'Swim', + 'TROTTING': 'other', + 'ULTRARUNNING_RUNNING': 'Run', + 'VERTICALSPORTS_WALLCLIMBING': 'other', + 'VOLLEYBALL': 'other', + 'WALKING': 'Walk', + 'WATERSPORTS_CANOEING': 'Canoeing', + 'WATERSPORTS_KAYAKING': 'Kayaking', + 'WATERSPORTS_KITESURFING': 'other', + 'WATERSPORTS_SAILING': 'other', + 'WATERSPORTS_SURFING': 'other', + 'WATERSPORTS_WAKEBOARDING': 'other', + 'WATERSPORTS_WATERSKI': 'other', + 'WATERSPORTS_WINDSURFING': 'other', + 'XC_SKIING_CLASSIC': 'NordicSki', + 'XC_SKIING_FREESTYLE': 'NordicSki', 'YOGA': 'Yoga' - } +} diff --git a/rowers/nkimportutils.py b/rowers/nkimportutils.py index ac23d076..b0c62090 100644 --- a/rowers/nkimportutils.py +++ b/rowers/nkimportutils.py @@ -12,11 +12,12 @@ from rowers.utils import dologging import requests import json + def strfdelta(tdelta): try: minutes, seconds = divmod(tdelta.seconds, 60) tenths = int(tdelta.microseconds / 1e5) - except AttributeError: # pragma: no cover + except AttributeError: # pragma: no cover minutes, seconds = divmod(tdelta.view(np.int64), 60e9) seconds, rest = divmod(seconds, 1e9) tenths = int(rest / 1e8) @@ -29,19 +30,19 @@ def strfdelta(tdelta): return res -def add_workout_from_data(userid,nkid,data,strokedata,source='nk',splitdata=None, +def add_workout_from_data(userid, nkid, data, strokedata, source='nk', splitdata=None, workoutsource='nklinklogbook'): csvfilename = 'media/{code}_{nkid}.csv.gz'.format( nkid=nkid, - code = uuid4().hex[:16] + code=uuid4().hex[:16] ) # dologging('nklog.log',csvfilename) try: - userid=int(userid) - except TypeError: # pragma: no cover + userid = int(userid) + except TypeError: # pragma: no cover userid = userid.id strokedata.to_csv(csvfilename, index_label='index', compression='gzip') @@ -51,32 +52,32 @@ def add_workout_from_data(userid,nkid,data,strokedata,source='nk',splitdata=None elapsedTime = data["elapsedTime"] totalDistanceGps = data["totalDistanceGps"] totalDistanceImp = data["totalDistanceImp"] - intervals = data["intervals"] # add intervals + intervals = data["intervals"] # add intervals oarlockSessions = data["oarlockSessions"] - deviceId = data["deviceId"] # you could get the firmware version + deviceId = data["deviceId"] # you could get the firmware version totalDistance = totalDistanceGps useImpeller = False - if speedInput: # pragma: no cover + if speedInput: # pragma: no cover totdalDistance = totalDistanceImp useImpeller = True - summary = get_nk_allstats(data,strokedata) + summary = get_nk_allstats(data, strokedata) speedInput = data['speedInput'] # 0 = GPS; 1 = Impeller # oarlock inboard, length, boat name if oarlockSessions: - oarlocksession = oarlockSessions[0] # should take seatIndex + oarlocksession = oarlockSessions[0] # should take seatIndex boatName = oarlocksession["boatName"] - oarLength = oarlocksession["oarLength"] # cm - oarInboardLength = oarlocksession["oarInboardLength"] # cm + oarLength = oarlocksession["oarLength"] # cm + oarInboardLength = oarlocksession["oarInboardLength"] # cm seatNumber = oarlocksession["seatNumber"] try: oarlockfirmware = oarlocksession["firmwareVersion"] except KeyError: oarlockfirmware = '' - else: # pragma: no cover + else: # pragma: no cover boatName = '' oarLength = 289 oarInboardLength = 88 @@ -88,17 +89,17 @@ def add_workout_from_data(userid,nkid,data,strokedata,source='nk',splitdata=None uploadoptions = { 'secret': UPLOAD_SERVICE_SECRET, - 'user':userid, + 'user': userid, 'file': csvfilename, 'title': title, 'workouttype': workouttype, 'boattype': boattype, - 'nkid':nkid, + 'nkid': nkid, 'inboard': oarInboardLength/100., 'oarlength': oarLength/100., - 'summary':summary, - 'oarlockfirmware':oarlockfirmware, - 'elapsedTime': elapsedTime/1000., # in seconds + 'summary': summary, + 'oarlockfirmware': oarlockfirmware, + 'elapsedTime': elapsedTime/1000., # in seconds 'totalDistance': totalDistance, 'useImpeller': useImpeller } @@ -109,14 +110,14 @@ def add_workout_from_data(userid,nkid,data,strokedata,source='nk',splitdata=None newHeaders = {'Content-type': 'application/json', 'Accept': 'text/plain'} session.headers.update(newHeaders) - response = session.post(UPLOAD_SERVICE_URL,json=uploadoptions) + response = session.post(UPLOAD_SERVICE_URL, json=uploadoptions) - if response.status_code != 200: # pragma: no cover - return 0,response.text + if response.status_code != 200: # pragma: no cover + return 0, response.text try: workoutid = response.json()['id'] - except KeyError: # pragma: no cover + except KeyError: # pragma: no cover workoutid = 1 # dologging('nklog.log','Workout ID {id}'.format(id=workoutid)) @@ -124,11 +125,12 @@ def add_workout_from_data(userid,nkid,data,strokedata,source='nk',splitdata=None # evt update workout summary # return - return workoutid,"" + return workoutid, "" -def get_nk_intervalstats(workoutdata,strokedata): + +def get_nk_intervalstats(workoutdata, strokedata): intervals = workoutdata['intervals'] - intervals = sorted(intervals, key = lambda k:k['id']) + intervals = sorted(intervals, key=lambda k: k['id']) separator = "|" stri = "Workout Details\n" stri += "#-{sep}SDist{sep}-Split-{sep}-SPace-{sep}-Pwr-{sep}-SPM--{sep}-AvgHR-{sep}DPS-\n".format( @@ -143,10 +145,11 @@ def get_nk_intervalstats(workoutdata,strokedata): avgpacetd = timedelta(seconds=avgpace) newpacestring = strfdelta(avgpacetd) - elapsedSeconds = interval['elapsedTime']/1000. # secs + elapsedSeconds = interval['elapsedTime']/1000. # secs elapsedTime = datetime.datetime.utcfromtimestamp(elapsedSeconds) - newsplitstring = "%s.%i" % (elapsedTime.strftime("%M:%S"), elapsedTime.microsecond/100000) + newsplitstring = "%s.%i" % (elapsedTime.strftime( + "%M:%S"), elapsedTime.microsecond/100000) pwr = interval['avgPower'] avghr = interval['avgHeartRate'] @@ -160,30 +163,32 @@ def get_nk_intervalstats(workoutdata,strokedata): space=newpacestring, pwr=int(pwr), sep=separator, - ) + ) stri += " {spm} {sep} {avghr:0>3} {sep}{dps:0>4.1f}\n".format( sep=separator, avghr=avghr, spm=spm, dps=dps, - ) + ) i += 1 return stri -def get_nk_summary(workoutdata,strokedata): + +def get_nk_summary(workoutdata, strokedata): name = workoutdata['name'] - avgpace = workoutdata['avgPaceGps']/1000. # secs + avgpace = workoutdata['avgPaceGps']/1000. # secs avgpacetd = timedelta(seconds=avgpace) avgpacestring = strfdelta(avgpacetd) - elapsedSeconds = workoutdata['elapsedTime']/1000. # secs + elapsedSeconds = workoutdata['elapsedTime']/1000. # secs elapsedTime = datetime.datetime.utcfromtimestamp(elapsedSeconds) - timestring = "%s.%i" % (elapsedTime.strftime("%H:%M:%S"), elapsedTime.microsecond/100000) + timestring = "%s.%i" % (elapsedTime.strftime( + "%H:%M:%S"), elapsedTime.microsecond/100000) dist = workoutdata['totalDistanceGps'] spm = workoutdata['avgStrokeRate'] @@ -192,34 +197,31 @@ def get_nk_summary(workoutdata,strokedata): maxhr = strokedata['heartRate'].max() pwr = workoutdata['avgPower'] - if dist is None: # pragma: no cover + if dist is None: # pragma: no cover dist = 0 - if spm is None: # pragma: no cover + if spm is None: # pragma: no cover spm = 0 - if avghr is None: # pragma: no cover + if avghr is None: # pragma: no cover avghr = 0 - if avgdps is None: # pragma: no cover + if avgdps is None: # pragma: no cover avgdps = 0 - if maxhr is None: # pragma: no cover + if maxhr is None: # pragma: no cover maxhr = 0 - if pwr is None: # pragma: no cover + if pwr is None: # pragma: no cover pwr = 0 sep = "|" - stri1 = "Workout Summary - " + name + "\n" stri1 += "--{sep}Total{sep}--Total---{sep}--Avg--{sep}-Avg-{sep}-Avg--{sep}-Avg-{sep}-Max-{sep}-Avg\n".format( - sep=sep) + sep=sep) stri1 += "--{sep}Dist-{sep}--Time----{sep}-Pace--{sep}-Pwr-{sep}-SPM--{sep}-HR--{sep}-HR--{sep}-DPS\n".format( - sep=sep) - - + sep=sep) stri1 += "--{sep}{dist:0>5.0f}{sep}".format( sep=sep, @@ -247,12 +249,15 @@ def get_nk_summary(workoutdata,strokedata): return stri1 -def get_nk_allstats(data,workoutdata): + +def get_nk_allstats(data, workoutdata): stri = get_nk_summary(data, workoutdata) + \ get_nk_intervalstats(data, workoutdata) return stri -#def get_workout(user,nkid): +# def get_workout(user,nkid): + + def getdict(x, seatIndex=1): seatStrokes = pd.DataFrame(x) try: @@ -263,32 +268,32 @@ def getdict(x, seatIndex=1): return {} + def strokeDataToDf(strokeData): df = pd.DataFrame.from_dict(strokeData) oarlockData = df['oarlockStrokes'] - oarlockData = oarlockData.apply(lambda x:getdict(x, seatIndex=1)) + oarlockData = oarlockData.apply(lambda x: getdict(x, seatIndex=1)) df2 = pd.DataFrame.from_records(oarlockData.values) - #df.set_index('timestamp',inplace=True) + # df.set_index('timestamp',inplace=True) if not df2.empty: - #df2.set_index('timestamp',inplace=True) - df = df.merge(df2,left_index=True,right_index=True) - df = df.rename(columns={"timestamp_x":"timestamp"}) - - df = df.drop('oarlockStrokes',axis=1) - df.sort_values(by='timestamp',ascending=True,inplace=True) - df.fillna(inplace=True,method='ffill') - + # df2.set_index('timestamp',inplace=True) + df = df.merge(df2, left_index=True, right_index=True) + df = df.rename(columns={"timestamp_x": "timestamp"}) + df = df.drop('oarlockStrokes', axis=1) + df.sort_values(by='timestamp', ascending=True, inplace=True) + df.fillna(inplace=True, method='ffill') return df -def readlogs_summaries(logfile,dosave=0): # pragma: no cover - with open(logfile,'r') as f: + +def readlogs_summaries(logfile, dosave=0): # pragma: no cover + with open(logfile, 'r') as f: while f: s = f.readline() if s == "": @@ -296,7 +301,7 @@ def readlogs_summaries(logfile,dosave=0): # pragma: no cover if "Importing" in s: words = s.split(" ") nkid = words[-1][0:6] - print(nkid,dosave) + print(nkid, dosave) print('') line1 = f.readline() line2 = f.readline() @@ -310,16 +315,16 @@ def readlogs_summaries(logfile,dosave=0): # pragma: no cover df = strokeDataToDf(strokeData) if dosave == 0 or str(nkid) == str(dosave): - print(get_nk_allstats(summaryData,df)) - #print(summaryData) + print(get_nk_allstats(summaryData, df)) + # print(summaryData) if str(dosave) == str(nkid): print('saving') filename = 'strokedata_{id}.json'.format(id=nkid) filename2 = 'nk_summarydata_{id}.json'.format(id=nkid) - with open(filename,'w') as f2: - json.dump(strokeData,f2) - with open(filename2,'w') as f2: - json.dump(summaryData,f2) + with open(filename, 'w') as f2: + json.dump(strokeData, f2) + with open(filename2, 'w') as f2: + json.dump(summaryData, f2) except Exception as e: print(traceback.format_exc()) print("error") diff --git a/rowers/nkstuff.py b/rowers/nkstuff.py index 5e79d566..ad1889d3 100644 --- a/rowers/nkstuff.py +++ b/rowers/nkstuff.py @@ -1,4 +1,18 @@ from __future__ import absolute_import +from requests.auth import HTTPBasicAuth +from rowers.nkimportutils import * +from rowers.imports import * +from rowers.tasks import handle_nk_async_workout +from rowsandall_app.settings import ( + NK_CLIENT_ID, NK_REDIRECT_URI, NK_CLIENT_SECRET, + SITE_URL, NK_API_LOCATION, NK_OAUTH_LOCATION, + UPLOAD_SERVICE_URL, UPLOAD_SERVICE_SECRET, +) +import gzip +import rowers.mytypes as mytypes +from rowers.utils import myqueue +from iso8601 import ParseError +from rowers.rower_rules import is_workout_user, ispromember from __future__ import division from __future__ import print_function from __future__ import unicode_literals @@ -12,8 +26,8 @@ import requests from rowers.utils import dologging from json.decoder import JSONDecodeError -#https:#oauth-stage.nkrowlink.com/oauth/authorizegrant_type=authorization_code&response_type=code&client_id=rowsandall-staging&scope=read&state=fc8fc3d8-ce0a-443e-838a-1c06fb5317c6&redirect_uri=https%3A%2F%2Fdunav.ngrok.io%2Fnk_callback%2F -#https:#oauth-stage.nkrowlink.com/oauth/authorize?grant_type=authorization_code&response_type=code&client_id=rowsandall-staging&scope=read&state=1234&redirect_uri=https%3A%2F%2Fdev.rowsandall.com%2Fnk_callback +# https:#oauth-stage.nkrowlink.com/oauth/authorizegrant_type=authorization_code&response_type=code&client_id=rowsandall-staging&scope=read&state=fc8fc3d8-ce0a-443e-838a-1c06fb5317c6&redirect_uri=https%3A%2F%2Fdunav.ngrok.io%2Fnk_callback%2F +# https:#oauth-stage.nkrowlink.com/oauth/authorize?grant_type=authorization_code&response_type=code&client_id=rowsandall-staging&scope=read&state=1234&redirect_uri=https%3A%2F%2Fdev.rowsandall.com%2Fnk_callback from requests_oauthlib import OAuth2Session @@ -22,30 +36,12 @@ queue = django_rq.get_queue('default') queuelow = django_rq.get_queue('low') queuehigh = django_rq.get_queue('low') -from rowers.rower_rules import is_workout_user, ispromember - -from iso8601 import ParseError -from rowers.utils import myqueue - -import rowers.mytypes as mytypes -import gzip - -from rowsandall_app.settings import ( - NK_CLIENT_ID, NK_REDIRECT_URI, NK_CLIENT_SECRET, - SITE_URL, NK_API_LOCATION,NK_OAUTH_LOCATION, - UPLOAD_SERVICE_URL, UPLOAD_SERVICE_SECRET, - ) - -from rowers.tasks import handle_nk_async_workout - try: from json.decoder import JSONDecodeError -except ImportError: # pragma: no cover +except ImportError: # pragma: no cover JSONDecodeError = ValueError -from rowers.imports import * -from rowers.nkimportutils import * oauth_data = { 'client_id': NK_CLIENT_ID, @@ -58,34 +54,30 @@ oauth_data = { 'expirydatename': 'nktokenexpirydate', 'bearer_auth': True, 'base_url': NK_OAUTH_LOCATION+"/oauth/token", - 'scope':'read', - } + 'scope': 'read', +} -from requests.auth import HTTPBasicAuth -def get_token(code): # pragma: no cover +def get_token(code): # pragma: no cover url = oauth_data['base_url'] - headers = {'Accept': 'application/json', - #'Authorization': auth_header, + # 'Authorization': auth_header, 'Content-Type': 'application/x-www-form-urlencoded', - #'user-agent': 'sanderroosendaal' + # 'user-agent': 'sanderroosendaal' } - - post_data = {"client_id": oauth_data['client_id'], - "grant_type": "authorization_code", - "redirect_uri": oauth_data['redirect_uri'], - "code": code, - } + "grant_type": "authorization_code", + "redirect_uri": oauth_data['redirect_uri'], + "code": code, + } - response = requests.post(url,auth=HTTPBasicAuth(oauth_data['client_id'],oauth_data['client_secret']), + response = requests.post(url, auth=HTTPBasicAuth(oauth_data['client_id'], oauth_data['client_secret']), data=post_data) if response.status_code != 200: - return [0,response.text,0,0] + return [0, response.text, 0, 0] token_json = response.json() @@ -94,20 +86,20 @@ def get_token(code): # pragma: no cover expires_in = token_json['expires_in'] nk_owner_id = token_json['user_id'] - return [access_token, expires_in, refresh_token,nk_owner_id] + return [access_token, expires_in, refresh_token, nk_owner_id] def nk_open(user): r = Rower.objects.get(user=user) - if (r.nktoken == '') or (r.nktoken is None): # pragma: no cover + if (r.nktoken == '') or (r.nktoken is None): # pragma: no cover s = "Token doesn't exist. Need to authorize" raise NoTokenError("User has no token") else: - if (timezone.now()>r.nktokenexpirydate): + if (timezone.now() > r.nktokenexpirydate): thetoken = rower_nk_token_refresh(user) - if thetoken == None: # pragma: no cover + if thetoken == None: # pragma: no cover raise NoTokenError("User has no token") return thetoken else: @@ -115,15 +107,16 @@ def nk_open(user): return thetoken -def get_nk_workouts(rower, do_async=True,before=0,after=0): + +def get_nk_workouts(rower, do_async=True, before=0, after=0): try: thetoken = nk_open(rower.user) - except NoTokenError: # pragma: no cover + except NoTokenError: # pragma: no cover return 0 - res = get_nk_workout_list(rower.user,before=before,after=after) + res = get_nk_workout_list(rower.user, before=before, after=after) - if res.status_code != 200: # pragma: no cover + if res.status_code != 200: # pragma: no cover return 0 nkids = [item['id'] for item in res.json()] @@ -141,22 +134,22 @@ def get_nk_workouts(rower, do_async=True,before=0,after=0): parkedids = [] try: - with open('nkblocked.json','r') as nkblocked: + with open('nkblocked.json', 'r') as nkblocked: jsondata = json.load(nkblocked) parkedids = jsondata['ids'] - except (FileNotFoundError,JSONDecodeError): # pragma: no cover + except (FileNotFoundError, JSONDecodeError): # pragma: no cover pass knownnkids = uniqify(knownnkids+tombstones+parkedids) newids = [nkid for nkid in nkids if not nkid in knownnkids] s = 'New NK IDs {newids}'.format(newids=newids) - dologging('nklog.log',s) + dologging('nklog.log', s) newparkedids = uniqify(newids+parkedids) - with open('nkblocked.json','wt') as nkblocked: - data = {'ids':newparkedids} - json.dump(data,nkblocked) + with open('nkblocked.json', 'wt') as nkblocked: + data = {'ids': newparkedids} + json.dump(data, nkblocked) counter = 0 for nkid in newids: @@ -174,19 +167,19 @@ def get_nk_workouts(rower, do_async=True,before=0,after=0): return 1 - def do_refresh_token(refreshtoken): post_data = {"grant_type": "refresh_token", - #"client_id":NK_CLIENT_ID, + # "client_id":NK_CLIENT_ID, "refresh_token": refreshtoken, } url = oauth_data['base_url'] - response = requests.post(url,data=post_data,auth=HTTPBasicAuth(oauth_data['client_id'],oauth_data['client_secret'])) + response = requests.post(url, data=post_data, auth=HTTPBasicAuth( + oauth_data['client_id'], oauth_data['client_secret'])) - if response.status_code != 200: # pragma: no cover - return [0,0,0] + if response.status_code != 200: # pragma: no cover + return [0, 0, 0] token_json = response.json() @@ -212,24 +205,26 @@ def rower_nk_token_refresh(user): return r.nktoken -def make_authorization_url(request): # pragma: no cover + +def make_authorization_url(request): # pragma: no cover return imports_make_authorization_url(oauth_data) -def get_nk_workout_list(user,fake=False,after=0,before=0): + +def get_nk_workout_list(user, fake=False, after=0, before=0): r = Rower.objects.get(user=user) - if (r.nktoken == '') or (r.nktoken is None): # pragma: no cover + if (r.nktoken == '') or (r.nktoken is None): # pragma: no cover s = "Token doesn't exist. Need to authorize" - return custom_exception_handler(401,s) - elif (r.nktokenexpirydate is None or timezone.now()+timedelta(seconds=10)>r.nktokenexpirydate): # pragma: no cover + return custom_exception_handler(401, s) + elif (r.nktokenexpirydate is None or timezone.now()+timedelta(seconds=10) > r.nktokenexpirydate): # pragma: no cover s = "Token expired. Needs to refresh." - return custom_exception_handler(401,s) + return custom_exception_handler(401, s) else: # ready to fetch. Hurray - if not before: # pragma: no cover + if not before: # pragma: no cover before = arrow.now()+timedelta(days=1) before = str(int(before.timestamp())*1000) - if not after: # pragma: no cover + if not after: # pragma: no cover after = arrow.now()-timedelta(days=7) after = str(int(after.timestamp())*1000) authorizationstring = str('Bearer ' + r.nktoken) @@ -241,25 +236,25 @@ def get_nk_workout_list(user,fake=False,after=0,before=0): url = NK_API_LOCATION+"api/v1/sessions" params = { - 'after':after, - 'before':before, - } # start / end time - - s = requests.get(url,headers=headers,params=params) + 'after': after, + 'before': before, + } # start / end time + s = requests.get(url, headers=headers, params=params) return s # -def get_workout(user,nkid,do_async=True,startdate='',enddate=''): + +def get_workout(user, nkid, do_async=True, startdate='', enddate=''): r = Rower.objects.get(user=user) - if (r.nktoken == '') or (r.nktoken is None): # pragma: no cover + if (r.nktoken == '') or (r.nktoken is None): # pragma: no cover s = "Token doesn't exist. Need to authorize" - return custom_exception_handler(401,s) ,0 - elif (timezone.now()>r.nktokenexpirydate): # pragma: no cover + return custom_exception_handler(401, s), 0 + elif (timezone.now() > r.nktokenexpirydate): # pragma: no cover s = "Token expired. Needs to refresh." - return custom_exception_handler(401,s),0 + return custom_exception_handler(401, s), 0 params = { 'sessionIds': nkid, @@ -267,15 +262,15 @@ def get_workout(user,nkid,do_async=True,startdate='',enddate=''): before = 0 after = 0 - if startdate: # pragma: no cover + if startdate: # pragma: no cover startdate = arrow.get(startdate) after = str(int(startdate.timestamp())*1000) - if enddate: # pragma: no cover + if enddate: # pragma: no cover enddate = arrow.get(enddate) before = str(int(enddate.timestamp())*1000) - res = get_nk_workout_list(r.user,before=before,after=after) - if res.status_code != 200: # pragma: no cover + res = get_nk_workout_list(r.user, before=before, after=after) + if res.status_code != 200: # pragma: no cover # dologging('nklog.log','Status code {code}'.format(code=res.status_code)) return 0 alldata = {} @@ -291,6 +286,6 @@ def get_workout(user,nkid,do_async=True,startdate='',enddate=''): nkid, 0, r.defaulttimezone, - ) + ) return res diff --git a/rowers/opaque.py b/rowers/opaque.py index 4f0cffe3..72e9e3db 100644 --- a/rowers/opaque.py +++ b/rowers/opaque.py @@ -13,12 +13,13 @@ from rowsandall_app.settings import ( SPORTTRACKS_CLIENT_ID, SPORTTRACKS_REDIRECT_URI, SPORTTRACKS_CLIENT_SECRET, UNDERARMOUR_CLIENT_ID, UNDERARMOUR_REDIRECT_URI, - UNDERARMOUR_CLIENT_SECRET,UNDERARMOUR_CLIENT_KEY, - RUNKEEPER_CLIENT_ID,RUNKEEPER_REDIRECT_URI,RUNKEEPER_CLIENT_SECRET, - TP_CLIENT_ID,TP_REDIRECT_URI,TP_CLIENT_KEY,TP_CLIENT_SECRET, - BRAINTREE_MERCHANT_ID,BRAINTREE_PUBLIC_KEY,BRAINTREE_PRIVATE_KEY, - PAYMENT_PROCESSING_ON,OPAQUE_SECRET_KEY - ) + UNDERARMOUR_CLIENT_SECRET, UNDERARMOUR_CLIENT_KEY, + RUNKEEPER_CLIENT_ID, RUNKEEPER_REDIRECT_URI, RUNKEEPER_CLIENT_SECRET, + TP_CLIENT_ID, TP_REDIRECT_URI, TP_CLIENT_KEY, TP_CLIENT_SECRET, + BRAINTREE_MERCHANT_ID, BRAINTREE_PUBLIC_KEY, BRAINTREE_PRIVATE_KEY, + PAYMENT_PROCESSING_ON, OPAQUE_SECRET_KEY +) + class OpaqueEncoder: """ @@ -31,7 +32,7 @@ class OpaqueEncoder: def __init__(self, key): self.key = key - self.extra_chars = b'.-'; + self.extra_chars = b'.-' def transform(self, i): """Produce an integer hash of a 16-bit integer, returning a transformed 16-bit integer.""" @@ -48,7 +49,7 @@ class OpaqueEncoder: """Transcode an integer and return it as an 8-character hex string.""" return "%08x" % self.transcode(i) - def encode_base64(self, i): # pragma: no cover + def encode_base64(self, i): # pragma: no cover """Transcode an integer and return it as a 6-character base64 string.""" return base64.b64encode(struct.pack('!L', self.transcode(i)), self.extra_chars)[:6] @@ -56,7 +57,7 @@ class OpaqueEncoder: """Decode an 8-character hex string, returning the original integer.""" return self.transcode(int(str(s), 16)) - def decode_base64(self, s): # pragma: no cover + def decode_base64(self, s): # pragma: no cover """Decode a 6-character base64 string, returning the original integer.""" return self.transcode(struct.unpack('!L', base64.b64decode(s + '==', self.extra_chars))[0]) diff --git a/rowers/otw_power_calculator_pb2.py b/rowers/otw_power_calculator_pb2.py index af2be801..f0c2091c 100644 --- a/rowers/otw_power_calculator_pb2.py +++ b/rowers/otw_power_calculator_pb2.py @@ -2,145 +2,142 @@ # Generated by the protocol buffer compiler. DO NOT EDIT! # source: otw-power-calculator.proto -import sys -_b=sys.version_info[0]<3 and (lambda x:x) or (lambda x:x.encode('latin1')) -from google.protobuf import descriptor as _descriptor -from google.protobuf import message as _message -from google.protobuf import reflection as _reflection from google.protobuf import symbol_database as _symbol_database +from google.protobuf import reflection as _reflection +from google.protobuf import message as _message +from google.protobuf import descriptor as _descriptor +import sys +_b = sys.version_info[0] < 3 and ( + lambda x: x) or (lambda x: x.encode('latin1')) # @@protoc_insertion_point(imports) _sym_db = _symbol_database.Default() - - DESCRIPTOR = _descriptor.FileDescriptor( - name='otw-power-calculator.proto', - package='otw_power_calculator', - syntax='proto3', - serialized_options=None, - serialized_pb=_b('\n\x1aotw-power-calculator.proto\x12\x14otw_power_calculator\"\xc0\x01\n\x13WorkoutPowerRequest\x12\x10\n\x08\x66ilename\x18\x01 \x01(\t\x12\x10\n\x08\x62oattype\x18\x02 \x01(\t\x12\x10\n\x08\x63rewmass\x18\x03 \x01(\x01\x12\x15\n\rpowermeasured\x18\x04 \x01(\x08\x12\x13\n\x0bprogressurl\x18\x05 \x01(\t\x12\x0e\n\x06secret\x18\x06 \x01(\t\x12\x0e\n\x06silent\x18\x07 \x01(\x08\x12\x11\n\tboatclass\x18\x08 \x01(\t\x12\x14\n\x0c\x63oastalbrand\x18\t \x01(\t\"#\n\x11\x43\x61lculationResult\x12\x0e\n\x06result\x18\x01 \x01(\x05\x32j\n\x05Power\x12\x61\n\tCalcPower\x12).otw_power_calculator.WorkoutPowerRequest\x1a\'.otw_power_calculator.CalculationResult\"\x00\x62\x06proto3') + name='otw-power-calculator.proto', + package='otw_power_calculator', + syntax='proto3', + serialized_options=None, + serialized_pb=_b('\n\x1aotw-power-calculator.proto\x12\x14otw_power_calculator\"\xc0\x01\n\x13WorkoutPowerRequest\x12\x10\n\x08\x66ilename\x18\x01 \x01(\t\x12\x10\n\x08\x62oattype\x18\x02 \x01(\t\x12\x10\n\x08\x63rewmass\x18\x03 \x01(\x01\x12\x15\n\rpowermeasured\x18\x04 \x01(\x08\x12\x13\n\x0bprogressurl\x18\x05 \x01(\t\x12\x0e\n\x06secret\x18\x06 \x01(\t\x12\x0e\n\x06silent\x18\x07 \x01(\x08\x12\x11\n\tboatclass\x18\x08 \x01(\t\x12\x14\n\x0c\x63oastalbrand\x18\t \x01(\t\"#\n\x11\x43\x61lculationResult\x12\x0e\n\x06result\x18\x01 \x01(\x05\x32j\n\x05Power\x12\x61\n\tCalcPower\x12).otw_power_calculator.WorkoutPowerRequest\x1a\'.otw_power_calculator.CalculationResult\"\x00\x62\x06proto3') ) - - _WORKOUTPOWERREQUEST = _descriptor.Descriptor( - name='WorkoutPowerRequest', - full_name='otw_power_calculator.WorkoutPowerRequest', - filename=None, - file=DESCRIPTOR, - containing_type=None, - fields=[ - _descriptor.FieldDescriptor( - name='filename', full_name='otw_power_calculator.WorkoutPowerRequest.filename', index=0, - number=1, type=9, cpp_type=9, label=1, - has_default_value=False, default_value=_b("").decode('utf-8'), - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - serialized_options=None, file=DESCRIPTOR), - _descriptor.FieldDescriptor( - name='boattype', full_name='otw_power_calculator.WorkoutPowerRequest.boattype', index=1, - number=2, type=9, cpp_type=9, label=1, - has_default_value=False, default_value=_b("").decode('utf-8'), - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - serialized_options=None, file=DESCRIPTOR), - _descriptor.FieldDescriptor( - name='crewmass', full_name='otw_power_calculator.WorkoutPowerRequest.crewmass', index=2, - number=3, type=1, cpp_type=5, label=1, - has_default_value=False, default_value=float(0), - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - serialized_options=None, file=DESCRIPTOR), - _descriptor.FieldDescriptor( - name='powermeasured', full_name='otw_power_calculator.WorkoutPowerRequest.powermeasured', index=3, - number=4, type=8, cpp_type=7, label=1, - has_default_value=False, default_value=False, - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - serialized_options=None, file=DESCRIPTOR), - _descriptor.FieldDescriptor( - name='progressurl', full_name='otw_power_calculator.WorkoutPowerRequest.progressurl', index=4, - number=5, type=9, cpp_type=9, label=1, - has_default_value=False, default_value=_b("").decode('utf-8'), - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - serialized_options=None, file=DESCRIPTOR), - _descriptor.FieldDescriptor( - name='secret', full_name='otw_power_calculator.WorkoutPowerRequest.secret', index=5, - number=6, type=9, cpp_type=9, label=1, - has_default_value=False, default_value=_b("").decode('utf-8'), - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - serialized_options=None, file=DESCRIPTOR), - _descriptor.FieldDescriptor( - name='silent', full_name='otw_power_calculator.WorkoutPowerRequest.silent', index=6, - number=7, type=8, cpp_type=7, label=1, - has_default_value=False, default_value=False, - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - serialized_options=None, file=DESCRIPTOR), - _descriptor.FieldDescriptor( - name='boatclass', full_name='otw_power_calculator.WorkoutPowerRequest.boatclass', index=7, - number=8, type=9, cpp_type=9, label=1, - has_default_value=False, default_value=_b("").decode('utf-8'), - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - serialized_options=None, file=DESCRIPTOR), - _descriptor.FieldDescriptor( - name='coastalbrand', full_name='otw_power_calculator.WorkoutPowerRequest.coastalbrand', index=8, - number=9, type=9, cpp_type=9, label=1, - has_default_value=False, default_value=_b("").decode('utf-8'), - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - serialized_options=None, file=DESCRIPTOR), - ], - extensions=[ - ], - nested_types=[], - enum_types=[ - ], - serialized_options=None, - is_extendable=False, - syntax='proto3', - extension_ranges=[], - oneofs=[ - ], - serialized_start=53, - serialized_end=245, + name='WorkoutPowerRequest', + full_name='otw_power_calculator.WorkoutPowerRequest', + filename=None, + file=DESCRIPTOR, + containing_type=None, + fields=[ + _descriptor.FieldDescriptor( + name='filename', full_name='otw_power_calculator.WorkoutPowerRequest.filename', index=0, + number=1, type=9, cpp_type=9, label=1, + has_default_value=False, default_value=_b("").decode('utf-8'), + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, file=DESCRIPTOR), + _descriptor.FieldDescriptor( + name='boattype', full_name='otw_power_calculator.WorkoutPowerRequest.boattype', index=1, + number=2, type=9, cpp_type=9, label=1, + has_default_value=False, default_value=_b("").decode('utf-8'), + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, file=DESCRIPTOR), + _descriptor.FieldDescriptor( + name='crewmass', full_name='otw_power_calculator.WorkoutPowerRequest.crewmass', index=2, + number=3, type=1, cpp_type=5, label=1, + has_default_value=False, default_value=float(0), + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, file=DESCRIPTOR), + _descriptor.FieldDescriptor( + name='powermeasured', full_name='otw_power_calculator.WorkoutPowerRequest.powermeasured', index=3, + number=4, type=8, cpp_type=7, label=1, + has_default_value=False, default_value=False, + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, file=DESCRIPTOR), + _descriptor.FieldDescriptor( + name='progressurl', full_name='otw_power_calculator.WorkoutPowerRequest.progressurl', index=4, + number=5, type=9, cpp_type=9, label=1, + has_default_value=False, default_value=_b("").decode('utf-8'), + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, file=DESCRIPTOR), + _descriptor.FieldDescriptor( + name='secret', full_name='otw_power_calculator.WorkoutPowerRequest.secret', index=5, + number=6, type=9, cpp_type=9, label=1, + has_default_value=False, default_value=_b("").decode('utf-8'), + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, file=DESCRIPTOR), + _descriptor.FieldDescriptor( + name='silent', full_name='otw_power_calculator.WorkoutPowerRequest.silent', index=6, + number=7, type=8, cpp_type=7, label=1, + has_default_value=False, default_value=False, + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, file=DESCRIPTOR), + _descriptor.FieldDescriptor( + name='boatclass', full_name='otw_power_calculator.WorkoutPowerRequest.boatclass', index=7, + number=8, type=9, cpp_type=9, label=1, + has_default_value=False, default_value=_b("").decode('utf-8'), + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, file=DESCRIPTOR), + _descriptor.FieldDescriptor( + name='coastalbrand', full_name='otw_power_calculator.WorkoutPowerRequest.coastalbrand', index=8, + number=9, type=9, cpp_type=9, label=1, + has_default_value=False, default_value=_b("").decode('utf-8'), + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, file=DESCRIPTOR), + ], + extensions=[ + ], + nested_types=[], + enum_types=[ + ], + serialized_options=None, + is_extendable=False, + syntax='proto3', + extension_ranges=[], + oneofs=[ + ], + serialized_start=53, + serialized_end=245, ) _CALCULATIONRESULT = _descriptor.Descriptor( - name='CalculationResult', - full_name='otw_power_calculator.CalculationResult', - filename=None, - file=DESCRIPTOR, - containing_type=None, - fields=[ - _descriptor.FieldDescriptor( - name='result', full_name='otw_power_calculator.CalculationResult.result', index=0, - number=1, type=5, cpp_type=1, label=1, - has_default_value=False, default_value=0, - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - serialized_options=None, file=DESCRIPTOR), - ], - extensions=[ - ], - nested_types=[], - enum_types=[ - ], - serialized_options=None, - is_extendable=False, - syntax='proto3', - extension_ranges=[], - oneofs=[ - ], - serialized_start=247, - serialized_end=282, + name='CalculationResult', + full_name='otw_power_calculator.CalculationResult', + filename=None, + file=DESCRIPTOR, + containing_type=None, + fields=[ + _descriptor.FieldDescriptor( + name='result', full_name='otw_power_calculator.CalculationResult.result', index=0, + number=1, type=5, cpp_type=1, label=1, + has_default_value=False, default_value=0, + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, file=DESCRIPTOR), + ], + extensions=[ + ], + nested_types=[], + enum_types=[ + ], + serialized_options=None, + is_extendable=False, + syntax='proto3', + extension_ranges=[], + oneofs=[ + ], + serialized_start=247, + serialized_end=282, ) DESCRIPTOR.message_types_by_name['WorkoutPowerRequest'] = _WORKOUTPOWERREQUEST @@ -148,40 +145,39 @@ DESCRIPTOR.message_types_by_name['CalculationResult'] = _CALCULATIONRESULT _sym_db.RegisterFileDescriptor(DESCRIPTOR) WorkoutPowerRequest = _reflection.GeneratedProtocolMessageType('WorkoutPowerRequest', (_message.Message,), { - 'DESCRIPTOR' : _WORKOUTPOWERREQUEST, - '__module__' : 'otw_power_calculator_pb2' - # @@protoc_insertion_point(class_scope:otw_power_calculator.WorkoutPowerRequest) - }) + 'DESCRIPTOR': _WORKOUTPOWERREQUEST, + '__module__': 'otw_power_calculator_pb2' + # @@protoc_insertion_point(class_scope:otw_power_calculator.WorkoutPowerRequest) +}) _sym_db.RegisterMessage(WorkoutPowerRequest) CalculationResult = _reflection.GeneratedProtocolMessageType('CalculationResult', (_message.Message,), { - 'DESCRIPTOR' : _CALCULATIONRESULT, - '__module__' : 'otw_power_calculator_pb2' - # @@protoc_insertion_point(class_scope:otw_power_calculator.CalculationResult) - }) + 'DESCRIPTOR': _CALCULATIONRESULT, + '__module__': 'otw_power_calculator_pb2' + # @@protoc_insertion_point(class_scope:otw_power_calculator.CalculationResult) +}) _sym_db.RegisterMessage(CalculationResult) - _POWER = _descriptor.ServiceDescriptor( - name='Power', - full_name='otw_power_calculator.Power', - file=DESCRIPTOR, - index=0, - serialized_options=None, - serialized_start=284, - serialized_end=390, - methods=[ - _descriptor.MethodDescriptor( - name='CalcPower', - full_name='otw_power_calculator.Power.CalcPower', + name='Power', + full_name='otw_power_calculator.Power', + file=DESCRIPTOR, index=0, - containing_service=None, - input_type=_WORKOUTPOWERREQUEST, - output_type=_CALCULATIONRESULT, serialized_options=None, - ), -]) + serialized_start=284, + serialized_end=390, + methods=[ + _descriptor.MethodDescriptor( + name='CalcPower', + full_name='otw_power_calculator.Power.CalcPower', + index=0, + containing_service=None, + input_type=_WORKOUTPOWERREQUEST, + output_type=_CALCULATIONRESULT, + serialized_options=None, + ), + ]) _sym_db.RegisterServiceDescriptor(_POWER) DESCRIPTOR.services_by_name['Power'] = _POWER diff --git a/rowers/otw_power_calculator_pb2_grpc.py b/rowers/otw_power_calculator_pb2_grpc.py index 54282445..f6d10b5a 100644 --- a/rowers/otw_power_calculator_pb2_grpc.py +++ b/rowers/otw_power_calculator_pb2_grpc.py @@ -5,42 +5,43 @@ import rowers.otw_power_calculator_pb2 as otw__power__calculator__pb2 class PowerStub(object): - """Power service definition - """ - - def __init__(self, channel): - """Constructor. - - Args: - channel: A grpc.Channel. + """Power service definition """ - self.CalcPower = channel.unary_unary( - '/otw_power_calculator.Power/CalcPower', - request_serializer=otw__power__calculator__pb2.WorkoutPowerRequest.SerializeToString, - response_deserializer=otw__power__calculator__pb2.CalculationResult.FromString, + + def __init__(self, channel): + """Constructor. + + Args: + channel: A grpc.Channel. + """ + self.CalcPower = channel.unary_unary( + '/otw_power_calculator.Power/CalcPower', + request_serializer=otw__power__calculator__pb2.WorkoutPowerRequest.SerializeToString, + response_deserializer=otw__power__calculator__pb2.CalculationResult.FromString, ) class PowerServicer(object): - """Power service definition - """ + """Power service definition + """ - def CalcPower(self, request, context): # pragma: no cover - # missing associated documentation comment in .proto file - pass - context.set_code(grpc.StatusCode.UNIMPLEMENTED) # pragma: no cover - context.set_details('Method not implemented!') # pragma: no cover - raise NotImplementedError('Method not implemented!') # pragma: no cover + def CalcPower(self, request, context): # pragma: no cover + # missing associated documentation comment in .proto file + pass + context.set_code(grpc.StatusCode.UNIMPLEMENTED) # pragma: no cover + context.set_details('Method not implemented!') # pragma: no cover + raise NotImplementedError( + 'Method not implemented!') # pragma: no cover -def add_PowerServicer_to_server(servicer, server): # pragma: no cover - rpc_method_handlers = { - 'CalcPower': grpc.unary_unary_rpc_method_handler( - servicer.CalcPower, - request_deserializer=otw__power__calculator__pb2.WorkoutPowerRequest.FromString, - response_serializer=otw__power__calculator__pb2.CalculationResult.SerializeToString, - ), - } # pragma: no cover - generic_handler = grpc.method_handlers_generic_handler( - 'otw_power_calculator.Power', rpc_method_handlers) - server.add_generic_rpc_handlers((generic_handler,)) +def add_PowerServicer_to_server(servicer, server): # pragma: no cover + rpc_method_handlers = { + 'CalcPower': grpc.unary_unary_rpc_method_handler( + servicer.CalcPower, + request_deserializer=otw__power__calculator__pb2.WorkoutPowerRequest.FromString, + response_serializer=otw__power__calculator__pb2.CalculationResult.SerializeToString, + ), + } # pragma: no cover + generic_handler = grpc.method_handlers_generic_handler( + 'otw_power_calculator.Power', rpc_method_handlers) + server.add_generic_rpc_handlers((generic_handler,)) diff --git a/rowers/ownapistuff.py b/rowers/ownapistuff.py index e5e183f3..92c86ea8 100644 --- a/rowers/ownapistuff.py +++ b/rowers/ownapistuff.py @@ -17,13 +17,13 @@ import numpy as np from dateutil import parser import time import math -from math import sin,cos,atan2,sqrt +from math import sin, cos, atan2, sqrt import urllib import rowers.c2stuff as c2stuff # Django -from django.http import HttpResponseRedirect, HttpResponse,JsonResponse +from django.http import HttpResponseRedirect, HttpResponse, JsonResponse from django.conf import settings from django.contrib.auth import authenticate, login, logout from django.contrib.auth.models import User @@ -33,7 +33,7 @@ from django.contrib.auth.decorators import login_required # from .models import Profile from rowingdata import rowingdata import pandas as pd -from rowers.models import Rower,Workout +from rowers.models import Rower, Workout from rowsandall_app.settings import C2_CLIENT_ID, C2_REDIRECT_URI, C2_CLIENT_SECRET, STRAVA_CLIENT_ID, STRAVA_REDIRECT_URI, STRAVA_CLIENT_SECRET, SPORTTRACKS_CLIENT_SECRET, SPORTTRACKS_CLIENT_ID, SPORTTRACKS_REDIRECT_URI @@ -42,16 +42,17 @@ TEST_CLIENT_SECRET = "aapnootmies" TEST_REDIRECT_URI = "http://localhost:8000/rowers/test_callback" -def custom_exception_handler(exc,message): # pragma: no cover + +def custom_exception_handler(exc, message): # pragma: no cover response = { "errors": [ { "code": str(exc), "detail": message, - } - ] - } + } + ] + } res = HttpResponse(message) res.status_code = 401 @@ -59,11 +60,13 @@ def custom_exception_handler(exc,message): # pragma: no cover return res -def do_refresh_token(refreshtoken): # pragma: no cover - client_auth = requests.auth.HTTPBasicAuth(TEST_CLIENT_ID, TEST_CLIENT_SECRET) + +def do_refresh_token(refreshtoken): # pragma: no cover + client_auth = requests.auth.HTTPBasicAuth( + TEST_CLIENT_ID, TEST_CLIENT_SECRET) post_data = {"grant_type": "refresh_token", "client_secret": TEST_CLIENT_SECRET, - "client_id":TEST_CLIENT_ID, + "client_id": TEST_CLIENT_ID, "refresh_token": refreshtoken, } headers = {'user-agent': 'sanderroosendaal', @@ -84,23 +87,23 @@ def do_refresh_token(refreshtoken): # pragma: no cover except KeyError: refresh_token = refreshtoken - return [thetoken,expires_in,refresh_token] + return [thetoken, expires_in, refresh_token] -def get_token(code): # pragma: no cover - client_auth = requests.auth.HTTPBasicAuth(TEST_CLIENT_ID, TEST_CLIENT_SECRET) +def get_token(code): # pragma: no cover + client_auth = requests.auth.HTTPBasicAuth( + TEST_CLIENT_ID, TEST_CLIENT_SECRET) post_data = {"grant_type": "authorization_code", "code": code, "redirect_uri": "http://localhost:8000/rowers/test_callback", "client_secret": "aapnootmies", - "client_id":1, + "client_id": 1, } headers = {'Accept': 'application/json', 'Content-Type': 'application/json'} url = "http://localhost:8000/rowers/o/token/" - response = requests.post(url, data=json.dumps(post_data), headers=headers) @@ -110,10 +113,10 @@ def get_token(code): # pragma: no cover expires_in = token_json['expires_in'] refresh_token = token_json['refresh_token'] + return [thetoken, expires_in, refresh_token] - return [thetoken,expires_in,refresh_token] -def make_authorization_url(request): # pragma: no cover +def make_authorization_url(request): # pragma: no cover # Generate a random string for the state parameter # Save it for use later to prevent xsrf attacks from uuid import uuid4 @@ -122,17 +125,17 @@ def make_authorization_url(request): # pragma: no cover params = {"client_id": TEST_CLIENT_ID, "response_type": "code", "redirect_uri": TEST_REDIRECT_URI, - "scope":"write", - "state":state} - + "scope": "write", + "state": state} import urllib - url = "http://localhost:8000/rowers/o/authorize" +urllib.parse.urlencode(params) + url = "http://localhost:8000/rowers/o/authorize" + \ + urllib.parse.urlencode(params) return HttpResponseRedirect(url) -def rower_ownapi_token_refresh(user): # pragma: no cover +def rower_ownapi_token_refresh(user): # pragma: no cover r = Rower.objects.get(user=user) res = do_refresh_token(r.ownapirefreshtoken) access_token = res[0] @@ -148,14 +151,15 @@ def rower_ownapi_token_refresh(user): # pragma: no cover r.save() return r.ownapitoken -def get_ownapi_workout_list(user): # pragma: no cover + +def get_ownapi_workout_list(user): # pragma: no cover r = Rower.objects.get(user=user) if (r.ownapitoken == '') or (r.ownapitoken is None): s = "Token doesn't exist. Need to authorize" - return custom_exception_handler(401,s) - elif (timezone.now()>r.ownapitokenexpirydate): + return custom_exception_handler(401, s) + elif (timezone.now() > r.ownapitokenexpirydate): s = "Token expired. Needs to refresh." - return custom_exception_handler(401,s) + return custom_exception_handler(401, s) else: # ready to fetch. Hurray authorizationstring = str('Bearer ' + r.ownapitoken) @@ -163,19 +167,19 @@ def get_ownapi_workout_list(user): # pragma: no cover 'user-agent': 'sanderroosendaal', 'Content-Type': 'application/json'} url = "https://api.ownapi.mobi/api/v2/fitnessActivities" - s = requests.get(url,headers=headers) + s = requests.get(url, headers=headers) return s -def get_ownapi_workout(user,ownapiid): # pragma: no cover +def get_ownapi_workout(user, ownapiid): # pragma: no cover r = Rower.objects.get(user=user) if (r.ownapitoken == '') or (r.ownapitoken is None): - return custom_exception_handler(401,s) + return custom_exception_handler(401, s) s = "Token doesn't exist. Need to authorize" - elif (timezone.now()>r.ownapitokenexpirydate): + elif (timezone.now() > r.ownapitokenexpirydate): s = "Token expired. Needs to refresh." - return custom_exception_handler(401,s) + return custom_exception_handler(401, s) else: # ready to fetch. Hurray authorizationstring = str('Bearer ' + r.ownapitoken) @@ -183,20 +187,22 @@ def get_ownapi_workout(user,ownapiid): # pragma: no cover 'user-agent': 'sanderroosendaal', 'Content-Type': 'application/json'} url = "https://api.ownapi.mobi/api/v2/fitnessActivities/"+str(ownapiid) - s = requests.get(url,headers=headers) + s = requests.get(url, headers=headers) return s -def createownapiworkoutdata(w): # pragma: no cover + +def createownapiworkoutdata(w): # pragma: no cover filename = w.csvfilename row = rowingdata(csvfile=filename) averagehr = int(row.df[' HRCur (bpm)'].mean()) maxhr = int(row.df[' HRCur (bpm)'].max()) # adding diff, trying to see if this is valid - t = row.df.ix[:,'TimeStamp (sec)'].values-10*row.df.ix[0,'TimeStamp (sec)'] + t = row.df.ix[:, 'TimeStamp (sec)'].values - \ + 10*row.df.ix[0, 'TimeStamp (sec)'] t[0] = t[1] - d = row.df.ix[:,'cum_dist'].values + d = row.df.ix[:, 'cum_dist'].values d[0] = d[1] t = t.astype(int) d = d.astype(int) @@ -204,7 +210,7 @@ def createownapiworkoutdata(w): # pragma: no cover spm[0] = spm[1] hr = row.df[' HRCur (bpm)'].astype(int) - haslatlon=1 + haslatlon = 1 try: lat = row.df[' latitude'].values @@ -219,7 +225,7 @@ def createownapiworkoutdata(w): # pragma: no cover haspower = 0 locdata = [] - hrdata = [] + hrdata = [] spmdata = [] distancedata = [] powerdata = [] @@ -233,17 +239,16 @@ def createownapiworkoutdata(w): # pragma: no cover spmdata.append(spm[i]) if haslatlon: locdata.append(t[i]) - locdata.append([lat[i],lon[i]]) + locdata.append([lat[i], lon[i]]) if haspower: powerdata.append(t[i]) powerdata.append(power[i]) - if haslatlon: data = { "type": "Rowing", "name": w.name, -# "start_time": str(w.date)+"T"+str(w.starttime)+"Z", + # "start_time": str(w.date)+"T"+str(w.starttime)+"Z", "start_time": w.startdatetime.isoformat(), "total_distance": int(w.distance), "duration": int(max(t)), @@ -254,12 +259,12 @@ def createownapiworkoutdata(w): # pragma: no cover "distance": distancedata, "cadence": spmdata, "heartrate": hrdata, - } + } else: data = { "type": "Rowing", "name": w.name, -# "start_time": str(w.date)+"T"+str(w.starttime)+"Z", + # "start_time": str(w.date)+"T"+str(w.starttime)+"Z", "start_time": w.startdatetime.isoformat(), "total_distance": int(w.distance), "duration": int(max(t)), @@ -269,14 +274,15 @@ def createownapiworkoutdata(w): # pragma: no cover "distance": distancedata, "cadence": spmdata, "heartrate": hrdata, - } + } if haspower: data['power'] = powerdata return data -def getidfromresponse(response): # pragma: no cover + +def getidfromresponse(response): # pragma: no cover t = json.loads(response.text) uri = t['uris'][0] id = uri[len(uri)-13:len(uri)-5] diff --git a/rowers/payments.py b/rowers/payments.py index 2a513418..8ccfea36 100644 --- a/rowers/payments.py +++ b/rowers/payments.py @@ -2,10 +2,12 @@ from __future__ import absolute_import from __future__ import division from __future__ import print_function from __future__ import unicode_literals -from rowers.models import Rower,PaidPlan +from rowers.models import Rower, PaidPlan # run once - copies plans to paypal -def planstopaypal(): # pragma: no cover + + +def planstopaypal(): # pragma: no cover plans = PaidPlan.objects.all() for plan in plans: @@ -15,7 +17,7 @@ def planstopaypal(): # pragma: no cover plan.save() -def is_existing_customer(rower): # pragma: no cover +def is_existing_customer(rower): # pragma: no cover if rower.country is not None and rower.customer_id is not None and rower.country != '': if rower.subscription_id is None or rower.subscription_id == '': return False diff --git a/rowers/permissions.py b/rowers/permissions.py index ef7149f6..191c343c 100644 --- a/rowers/permissions.py +++ b/rowers/permissions.py @@ -7,6 +7,7 @@ from __future__ import unicode_literals from rest_framework import permissions from rowers.models import Rower + class IsOwnerOrReadOnly(permissions.BasePermission): """ Custom permission to only allow owners of an object to edit it. @@ -15,35 +16,40 @@ class IsOwnerOrReadOnly(permissions.BasePermission): def has_object_permission(self, request, view, obj): # Read permissions are allowed to any request, # so we'll always allow GET, HEAD or OPTIONS requests. - if request.method in permissions.SAFE_METHODS: # pragma: no cover + if request.method in permissions.SAFE_METHODS: # pragma: no cover return True # Write permissions are only allowed to the owner of the snippet. - return obj.user == request.user # pragma: no cover + return obj.user == request.user # pragma: no cover + class IsOwnerOrNot(permissions.BasePermission): - def has_object_permission(self, request, view, obj): # pragma: no cover + def has_object_permission(self, request, view, obj): # pragma: no cover r = Rower.objects.get(user=request.user) return (obj.user == r) + class IsRowerOrNot(permissions.BasePermission): - def has_object_permission(self, request, view, obj): # pragma: no cover + def has_object_permission(self, request, view, obj): # pragma: no cover r = Rower.objects.get(user=request.user) return (r in obj.rower.all()) + class IsPlanOrHigher(permissions.BasePermission): - def has_object_permission(self, request, view, obj): # pragma: no cover + def has_object_permission(self, request, view, obj): # pragma: no cover r = Rower.objects.get(user=request.user) - return r not in ['basic','pro','freecoach'] + return r not in ['basic', 'pro', 'freecoach'] + class IsCompetitorOrNot(permissions.BasePermission): - def has_object_permission(self, request, view, obj): # pragma: no cover + def has_object_permission(self, request, view, obj): # pragma: no cover return (obj.userid == request.user.id) + class IsManagerOrReadOnly(permissions.BasePermission): - def has_object_permission(self, request, view, obj): # pragma: no cover + def has_object_permission(self, request, view, obj): # pragma: no cover if request.method in permissions.SAFE_METHODS: return True diff --git a/rowers/plannedsessions.py b/rowers/plannedsessions.py index 2c987e3c..4c2f3ea9 100644 --- a/rowers/plannedsessions.py +++ b/rowers/plannedsessions.py @@ -1,4 +1,33 @@ from __future__ import absolute_import +from rowers.emails import htmlstrip, htmlstripnobr +from rowers.models import ( + Rower, Workout, Team, + GeoCourse, TrainingMicroCycle, TrainingMesoCycle, TrainingMacroCycle, + TrainingPlan, PlannedSession, VirtualRaceResult, CourseTestResult, + get_course_timezone, IndoorVirtualRaceResult, VirtualRace, createmacrofillers, + createmesofillers, createmicrofillers, CourseStandard, +) +from rowers.utils import totaltime_sec_to_string +from rowers.tasks import ( + handle_sendemail_raceregistration, handle_sendemail_racesubmission +) +from rowers.tasks import handle_check_race_course +from iso8601 import ParseError +import iso8601 +import rowers.courses as courses +import rowers.datautils as datautils +import rowers.dataprep as dataprep +import numpy as np +import rowers.metrics as metrics +import rowers.mytypes as mytypes +from rowers.courses import get_time_course +from rowers.utils import to_pace +from rowers.opaque import encoder +from rowingdata import rower as rrower +from rowingdata import rowingdata as rrdata +import arrow +import pandas as pd +import json from __future__ import division from __future__ import print_function from __future__ import unicode_literals @@ -15,7 +44,7 @@ import uuid from django.conf import settings import pytz from dateutil import parser -from rowers.utils import myqueue,calculate_age,totaltime_sec_to_string,ps_dict_order +from rowers.utils import myqueue, calculate_age, totaltime_sec_to_string, ps_dict_order from rowers.rows import handle_uploaded_file import collections import re @@ -23,79 +52,47 @@ import django_rq queue = django_rq.get_queue('default') queuelow = django_rq.get_queue('low') queuehigh = django_rq.get_queue('low') -import json - -import pandas as pd -import arrow - -from rowingdata import rowingdata as rrdata -from rowingdata import rower as rrower - -from rowers.opaque import encoder - -from rowers.utils import to_pace def to_time(milliseconds): seconds = milliseconds/1000. hours = int(seconds / 3600) - mins = int((seconds%3600)/60) - sec = int((seconds%3600)%60) + mins = int((seconds % 3600)/60) + sec = int((seconds % 3600) % 60) microsec = int(1e6*(seconds % 1)) - #print(seconds,hours,mins,sec,millisec) + # print(seconds,hours,mins,sec,millisec) - return dt.time(hours,mins,sec,microsec) + return dt.time(hours, mins, sec, microsec) # Wrapper around the rowingdata call to catch some exceptions # Checks for CSV file, then for gzipped CSV file, and if all fails, returns 0 -def rdata(file,rower=rrower()): + + +def rdata(file, rower=rrower()): try: - res = rrdata(csvfile=file,rower=rower) - except (IOError, IndexError, EOFError,FileNotFoundError): # pragma: no cover + res = rrdata(csvfile=file, rower=rower) + except (IOError, IndexError, EOFError, FileNotFoundError): # pragma: no cover try: - res = rrdata(csvfile=file+'.gz',rower=rower) - except (IOError, IndexError, EOFError,FileNotFoundError): + res = rrdata(csvfile=file+'.gz', rower=rower) + except (IOError, IndexError, EOFError, FileNotFoundError): res = 0 return res -from rowers.models import ( - Rower, Workout,Team, - GeoCourse, TrainingMicroCycle,TrainingMesoCycle,TrainingMacroCycle, - TrainingPlan,PlannedSession,VirtualRaceResult,CourseTestResult, - get_course_timezone, IndoorVirtualRaceResult,VirtualRace,createmacrofillers, - createmesofillers,createmicrofillers,CourseStandard, - ) -from rowers.courses import get_time_course - -from rowers.emails import htmlstrip,htmlstripnobr - -import rowers.mytypes as mytypes - -import rowers.metrics as metrics -import numpy as np -import rowers.dataprep as dataprep -import rowers.datautils as datautils -import rowers.courses as 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 ps_dict_get_description(d,short=False): # pragma: no cover - sdict,totalmeters,totalseconds,totalrscore = ps_dict_order(d,short=short,html=False) +def ps_dict_get_description(d, short=False): # pragma: no cover + sdict, totalmeters, totalseconds, totalrscore = ps_dict_order( + d, short=short, html=False) s = '' for item in sdict: s += item['string']+'\n' return s -def ps_dict_get_description_html(d,short=False): - sdict,totalmeters,totalseconds,totalrscore = ps_dict_order(d,short=short) + +def ps_dict_get_description_html(d, short=False): + sdict, totalmeters, totalseconds, totalrscore = ps_dict_order( + d, short=short) s = '
    ' @@ -107,7 +104,7 @@ def ps_dict_get_description_html(d,short=False): return s -def checkscores(r,macrocycles): +def checkscores(r, macrocycles): for m in macrocycles: createmesofillers(m) m.plantime = 0 @@ -119,7 +116,6 @@ def checkscores(r,macrocycles): m.plantrimp = 0 m.actualtrimp = 0 - mesocycles = TrainingMesoCycle.objects.filter( plan=m, type='userdefined').order_by("startdate") @@ -139,14 +135,14 @@ def checkscores(r,macrocycles): type='userdefined').order_by("startdate") for mm in microcycles: - sps = get_sessions(r,startdate=mm.startdate,enddate=mm.enddate) + sps = get_sessions( + r, startdate=mm.startdate, enddate=mm.enddate) # sps = PlannedSession.objects.filter( # rower = r, # startdate__lte=mm.enddate, # enddate__gte=mm.startdate) - mm.plantime = 0 mm.actualtime = 0 mm.plandistance = 0 @@ -156,10 +152,9 @@ def checkscores(r,macrocycles): mm.plantrimp = 0 mm.actualtrimp = 0 - if mm.type == 'userdefined': - for ps in sps: # pragma: no cover - ratio, status, cdate = is_session_complete(r,ps) + for ps in sps: # pragma: no cover + ratio, status, cdate = is_session_complete(r, ps) if ps.sessionmode == 'time': mm.plantime += ps.sessionvalue mm.actualtime += int(ps.sessionvalue*ratio) @@ -196,22 +191,26 @@ def checkscores(r,macrocycles): m.plantrimp += me.plantrimp m.actualtrimp += me.actualtrimp - - if m.type == 'userdefined': m.save() -def get_execution_report(rower,startdate,enddate,plan=None): +def get_execution_report(rower, startdate, enddate, plan=None): if plan: - macros = TrainingMacroCycle.objects.filter(plan=plan).order_by("startdate") - checkscores(rower,macros) - mesos = TrainingMesoCycle.objects.filter(plan__in=macros).order_by("startdate") - micros = TrainingMicroCycle.objects.filter(plan__in=mesos).order_by("startdate") - micros = micros.exclude(enddate__lte=startdate).exclude(startdate__gte=enddate) - else: # pragma: no cover - plans = TrainingPlan.objects.filter(startdate__lte=startdate,enddate__gte=startdate) - plans2 = TrainingPlan.objects.filter(enddate__lte=enddate,startdate__lte=enddate) + macros = TrainingMacroCycle.objects.filter( + plan=plan).order_by("startdate") + checkscores(rower, macros) + mesos = TrainingMesoCycle.objects.filter( + plan__in=macros).order_by("startdate") + micros = TrainingMicroCycle.objects.filter( + plan__in=mesos).order_by("startdate") + micros = micros.exclude(enddate__lte=startdate).exclude( + startdate__gte=enddate) + else: # pragma: no cover + plans = TrainingPlan.objects.filter( + startdate__lte=startdate, enddate__gte=startdate) + plans2 = TrainingPlan.objects.filter( + enddate__lte=enddate, startdate__lte=enddate) plans = plans | plans2 plans = plans.exclude(status=False).order_by("-enddate") @@ -223,18 +222,20 @@ def get_execution_report(rower,startdate,enddate,plan=None): startdate = startdate-timedelta(days=7) micros = [] while startdate <= enddate: - micro = type('micros',(object,), + micro = type('micros', (object,), { - 'startdate':startdate, - 'enddate':startdate+timedelta(days=7) - }) + 'startdate': startdate, + 'enddate': startdate+timedelta(days=7) + }) micros.append(micro) startdate += timedelta(days=7) else: plan = plans[0] - macros = TrainingMacroCycle.objects.filter(plan=plan).order_by("startdate") - checkscores(rower,macros) - mesos = TrainingMesoCycle.objects.filter(plan__in=macros).order_by("startdate") + macros = TrainingMacroCycle.objects.filter( + plan=plan).order_by("startdate") + checkscores(rower, macros) + mesos = TrainingMesoCycle.objects.filter( + plan__in=macros).order_by("startdate") micros = TrainingMicroCycle.objects.filter( plan__in=mesos, enddate__gte=startdate, @@ -249,26 +250,26 @@ def get_execution_report(rower,startdate,enddate,plan=None): for mm in micros: plannedscore = 0 actualscore = 0 - sps = get_sessions(rower,startdate=mm.startdate,enddate=mm.enddate) + sps = get_sessions(rower, startdate=mm.startdate, enddate=mm.enddate) unmatchedworkouts = Workout.objects.filter( user=rower, plannedsession=None, - date__gte=mm.startdate,date__lte=mm.enddate).exclude(duplicate=True) + date__gte=mm.startdate, date__lte=mm.enddate).exclude(duplicate=True) for w in unmatchedworkouts: if w.rscore != 0: actualscore += w.rscore elif w.hrtss != 0: # pragma: no cover actualscore += w.hrtss - else: # pragma: no cover + else: # pragma: no cover minutes = w.duration.hour*60+w.duration.minute actualscore += minutes - for ps in sps: # pragma: no cover - ratio, status, cdate = is_session_complete(rower,ps) + for ps in sps: # pragma: no cover + ratio, status, cdate = is_session_complete(rower, ps) if ps.sessionmode == 'rScore': plannedscore += ps.sessionvalue actualscore += ratio*ps.sessionvalue else: - ws = Workout.objects.filter(user=rower,plannedsession=ps) + ws = Workout.objects.filter(user=rower, plannedsession=ps) if not ws: if ps.sessionmode == 'time': plannedscore += ps.sessionvalue @@ -308,14 +309,14 @@ def get_execution_report(rower,startdate,enddate,plan=None): planned += [plannedscore] executed += [actualscore] - data = pd.DataFrame({ - 'startdate':startdates, - 'planned':planned, - 'executed':executed, + 'startdate': startdates, + 'planned': planned, + 'executed': executed, }) - return(data,'ok') + return(data, 'ok') + def get_indoorraces(workout): races1 = VirtualRace.objects.filter( @@ -325,11 +326,9 @@ def get_indoorraces(workout): sessionmode='distance', sessionvalue=workout.distance) - if workout.duration.second == 0 and workout.duration.microsecond == 0: duration = 60*workout.duration.hour+workout.duration.minute - races2 = VirtualRace.objects.filter( sessiontype='indoorrace', startdate__lte=workout.date, @@ -342,45 +341,41 @@ def get_indoorraces(workout): races = races1 registrations = IndoorVirtualRaceResult.objects.filter( - race__in = races, - boatclass = workout.workouttype, + race__in=races, + boatclass=workout.workouttype, userid=workout.user.id) races = [r.race for r in registrations] - return races -def get_todays_micro(plan,thedate=timezone.now()): + +def get_todays_micro(plan, thedate=timezone.now()): thismicro = None thismacro = TrainingMacroCycle.objects.filter( plan=plan, - startdate__lte = thedate, - enddate__gte = thedate - ) - - + startdate__lte=thedate, + enddate__gte=thedate + ) if thismacro: thismeso = TrainingMesoCycle.objects.filter( plan=thismacro[0], - startdate__lte = thedate, - enddate__gte = thedate + startdate__lte=thedate, + enddate__gte=thedate ) - - if thismeso: thismicro = TrainingMicroCycle.objects.filter( plan=thismeso[0], - startdate__lte = thedate, - enddate__gte = thedate + startdate__lte=thedate, + enddate__gte=thedate ) if thismicro: thismicro = thismicro[0] - else: # pragma: no cover + else: # pragma: no cover mms = TrainingMicroCycle.objects.all() return None @@ -388,60 +383,65 @@ def get_todays_micro(plan,thedate=timezone.now()): return thismicro # Low Level functions - to be called by higher level methods -def add_workouts_plannedsession(ws,ps,r): + + +def add_workouts_plannedsession(ws, ps, r): result = 0 comments = [] errors = [] # check if all sessions have same date dates = [w.date for w in ws] - if (not all(d == dates[0] for d in dates)) and ps.sessiontype not in ['challenge','cycletarget']: # pragma: no cover - errors.append('For tests and training sessions, selected workouts must all be done on the same date') - return result,comments,errors + if (not all(d == dates[0] for d in dates)) and ps.sessiontype not in ['challenge', 'cycletarget']: # pragma: no cover + errors.append( + 'For tests and training sessions, selected workouts must all be done on the same date') + return result, comments, errors - if len(ws)>1 and ps.sessiontype == 'test': # pragma: no cover + if len(ws) > 1 and ps.sessiontype == 'test': # pragma: no cover errors.append('For tests, you can only attach one workout') - return result,comments,errors + return result, comments, errors - - - wold = Workout.objects.filter(plannedsession=ps,user=r) + wold = Workout.objects.filter(plannedsession=ps, user=r) ids = [w.id for w in wold] + [w.id for w in ws] ids = list(set(ids)) - if len(ids)>1 and ps.sessiontype in ['test','coursetest','race','fastest_time','fastest_distance']: # pragma: no cover + if len(ids) > 1 and ps.sessiontype in ['test', 'coursetest', 'race', 'fastest_time', 'fastest_distance']: # pragma: no cover errors.append('For tests, you can only attach one workout') - return result,comments,errors + return result, comments, errors # start adding sessions for w in ws: - if w.date>=ps.startdate and w.date<=ps.enddate: + if w.date >= ps.startdate and w.date <= ps.enddate: w.plannedsession = ps w.save() result += 1 - comments.append('Attached workout %s to session' % encoder.encode_hex(w.id)) - if ps.sessiontype == 'coursetest': # pragma: no cover + comments.append('Attached workout %s to session' % + encoder.encode_hex(w.id)) + if ps.sessiontype == 'coursetest': # pragma: no cover record = CourseTestResult( userid=w.user.id, plannedsession=ps, - duration=dt.time(0,0), + duration=dt.time(0, 0), coursecompleted=False, - ) + ) record.save() - job = myqueue(queue,handle_check_race_course,w.csvfilename, - w.id,ps.course.id,record.id, - w.user.user.email,w.user.user.first_name, + job = myqueue(queue, handle_check_race_course, w.csvfilename, + w.id, ps.course.id, record.id, + w.user.user.email, w.user.user.first_name, mode='coursetest') - if ps.sessiontype == 'fastest_distance': # pragma: no cover - records = CourseTestResult.objects.filter(userid=w.user.id,plannedsession=ps) + if ps.sessiontype == 'fastest_distance': # pragma: no cover + records = CourseTestResult.objects.filter( + userid=w.user.id, plannedsession=ps) for record in records: #w1 = Workout.objects.get(id=record.workoutid) #w1.plannedsession = None - #w1.save() + # w1.save() record.delete() - df = dataprep.getsmallrowdata_db(['time','cumdist'],ids=[w.id]) - fastest_milliseconds,starttime,endtime = datautils.getfastest(df,ps.sessionvalue,mode='distance') + df = dataprep.getsmallrowdata_db( + ['time', 'cumdist'], ids=[w.id]) + fastest_milliseconds, starttime, endtime = datautils.getfastest( + df, ps.sessionvalue, mode='distance') if fastest_milliseconds > 0: w.plannedsession = ps @@ -451,11 +451,11 @@ def add_workouts_plannedsession(ws,ps,r): record = CourseTestResult( userid=w.user.user.id, - plannedsession = ps, - duration = duration, - coursecompleted = True, + plannedsession=ps, + duration=duration, + coursecompleted=True, workoutid=w.id, - distance = ps.sessionvalue, + distance=ps.sessionvalue, startsecond=starttime, endsecond=endtime, ) @@ -463,42 +463,45 @@ def add_workouts_plannedsession(ws,ps,r): record.save() else: errors.append('Could not find a matching interval') - if ps.sessiontype == 'fastest_time': # pragma: no cover - records = CourseTestResult.objects.filter(userid=w.user.id,plannedsession=ps) + if ps.sessiontype == 'fastest_time': # pragma: no cover + records = CourseTestResult.objects.filter( + userid=w.user.id, plannedsession=ps) for record in records: #w1 = Workout.objects.get(id=record.workoutid) #w1.plannedsession = None - #w1.save() + # w1.save() record.delete() - df = dataprep.getsmallrowdata_db(['time','cumdist'],ids=[w.id]) - fastest_meters,starttime,endtime = datautils.getfastest(df,ps.sessionvalue,mode='time') + df = dataprep.getsmallrowdata_db( + ['time', 'cumdist'], ids=[w.id]) + fastest_meters, starttime, endtime = datautils.getfastest( + df, ps.sessionvalue, mode='time') if fastest_meters > 0: w.plannedsession = ps w.save() - duration = dt.time(0,ps.sessionvalue) + duration = dt.time(0, ps.sessionvalue) record = CourseTestResult( userid=w.user.user.id, workoutid=w.id, - plannedsession = ps, - duration = duration, - coursecompleted = True, - distance = fastest_meters, + plannedsession=ps, + duration=duration, + coursecompleted=True, + distance=fastest_meters, startsecond=starttime, endsecond=endtime, ) record.save() else: errors.append('Could not find a matching interval') - else: # pragma: no cover + else: # pragma: no cover errors.append('Workout %i did not match session dates' % w.id) - return result,comments,errors + return result, comments, errors -def remove_workout_plannedsession(w,ps): +def remove_workout_plannedsession(w, ps): if w.plannedsession == ps: w.plannedsession = None w.save() @@ -506,11 +509,13 @@ def remove_workout_plannedsession(w,ps): return 0 -def clone_planned_session(ps): # pragma: no cover + +def clone_planned_session(ps): # pragma: no cover ps.save() - ps.pk = None # creates new instance + ps.pk = None # creates new instance ps.save() + def timefield_to_seconds_duration(t): duration = t.hour*3600. duration += t.minute * 60. @@ -520,14 +525,14 @@ def timefield_to_seconds_duration(t): return duration -def get_virtualrace_times(virtualrace): # pragma: no cover - geocourse = GeoCourse.objects.get(id = virtualrace.course.id) +def get_virtualrace_times(virtualrace): # pragma: no cover + geocourse = GeoCourse.objects.get(id=virtualrace.course.id) timezone_str = get_course_timezone(geocourse) startdatetime = datetime.datetime.combine( - virtualrace.startdate,virtualrace.start_time) + virtualrace.startdate, virtualrace.start_time) enddatetime = datetime.datetime.combine( - virtualrace.enddate,virtualrace.end_time) + virtualrace.enddate, virtualrace.end_time) startdatetime = pytz.timezone(timezone_str).localize( startdatetime @@ -537,11 +542,12 @@ def get_virtualrace_times(virtualrace): # pragma: no cover ) return { - 'startdatetime':startdatetime, - 'enddatetime':enddatetime, - 'evaluation_closure':virtualrace.evaluation_closure, - 'registration_closure':virtualrace.registration_closure, - } + 'startdatetime': startdatetime, + 'enddatetime': enddatetime, + 'evaluation_closure': virtualrace.evaluation_closure, + 'registration_closure': virtualrace.registration_closure, + } + def get_session_metrics(ps): rowers = ps.rower.all() @@ -562,28 +568,27 @@ def get_session_metrics(ps): completedatev = '' statusv = 0 - ws = Workout.objects.filter(user=r,plannedsession=ps).order_by("date") + ws = Workout.objects.filter(user=r, plannedsession=ps).order_by("date") if ws.count() != 0: for w in ws: distancev += w.distance durationv += timefield_to_seconds_duration(w.duration) - thetrimp,hrtss = dataprep.workout_trimp(w) + thetrimp, hrtss = dataprep.workout_trimp(w) trimpv += thetrimp tss = dataprep.workout_rscore(w)[0] if not np.isnan(tss) and tss != 0: rscorev += tss - elif tss == 0: # pragma: no cover + elif tss == 0: # pragma: no cover rscorev += hrtss - ratio,statusv,completiondate = is_session_complete_ws(ws,ps) + ratio, statusv, completiondate = is_session_complete_ws(ws, ps) try: completedatev = completiondate.strftime('%Y-%m-%d') - except AttributeError: # pragma: no cover + except AttributeError: # pragma: no cover completedatev = '' durationv /= 60. - trimp.append(int(trimpv)) duration.append(int(durationv)) distance.append(int(distancev)) @@ -594,18 +599,19 @@ def get_session_metrics(ps): completedate.append(completedatev) thedict = { - 'first_name':firstname, - 'last_name':lastname, - 'duration':duration, - 'distance':distance, - 'rscore':rscore, - 'trimp':trimp, - 'completedate':completedate, - 'status':status, + 'first_name': firstname, + 'last_name': lastname, + 'duration': duration, + 'distance': distance, + 'rscore': rscore, + 'trimp': trimp, + 'completedate': completedate, + 'status': status, } return thedict + cratiocolors = { 'better than nothing': "lightgreen", 'partial': "mediumgreen", @@ -617,34 +623,34 @@ cratiocolors = { 'not done': "white", } -def is_session_complete_ws(ws,ps): + +def is_session_complete_ws(ws, ps): ws = ws.order_by("date") - if ws.count()==0: + if ws.count() == 0: today = timezone.now() if today.date() > ps.enddate: verdict = 'missed' ratio = 0 - return ratio,verdict,None + return ratio, verdict, None else: - return 0,'not done',None + return 0, 'not done', None value = ps.sessionvalue if ps.sessionunit == 'min': value *= 60. - elif ps.sessionunit == 'km': # pragma: no cover + elif ps.sessionunit == 'km': # pragma: no cover value *= 1000. cratiomin = 1 cratiomax = 1 cratios = { - 'better than nothing':0, - 'partial':0.6, - 'on target':0.8, - 'over target':1.2, + 'better than nothing': 0, + 'partial': 0.6, + 'on target': 0.8, + 'over target': 1.2, 'way over target': 1.5 - } - + } if ps.criterium == 'none': if ps.sessiontype == 'session': @@ -656,7 +662,6 @@ def is_session_complete_ws(ws,ps): cratiomin = 0.9167 cratiomax = 1.0833 - score = 0 completiondate = None for w in ws: @@ -666,117 +671,118 @@ def is_session_complete_ws(ws,ps): durationseconds = timefield_to_seconds_duration(w.duration) score += durationseconds elif ps.sessionmode == 'TRIMP': - trimp,hrtss = dataprep.workout_trimp(w) + trimp, hrtss = dataprep.workout_trimp(w) score += trimp elif ps.sessionmode == 'rScore': rscore = dataprep.workout_rscore(w)[0] if not np.isnan(rscore) and rscore != 0: score += rscore - elif rscore == 0: # pragma: no cover - trimp,hrtss = dataprep.workout_trimp(w) + elif rscore == 0: # pragma: no cover + trimp, hrtss = dataprep.workout_trimp(w) score += hrtss - if not completiondate and score>=cratiomin*value: + if not completiondate and score >= cratiomin*value: completiondate = w.date try: ratio = score/float(int(value)) - except ZeroDivisionError: # pragma: no cover + except ZeroDivisionError: # pragma: no cover ratio = 0 verdict = 'better than nothing' - if ps.sessiontype in ['session','cycletarget']: + if ps.sessiontype in ['session', 'cycletarget']: if ps.criterium == 'exact': if ratio == 1.0: - return ratio,'on target',completiondate + return ratio, 'on target', completiondate else: - if not completiondate: # pragma: no cover + if not completiondate: # pragma: no cover completiondate = ws.reverse()[0].date - return ratio,'partial',completiondate - elif ps.criterium == 'minimum': # pragma: no cover + return ratio, 'partial', completiondate + elif ps.criterium == 'minimum': # pragma: no cover if ratio >= 1.0: - return ratio,'on target',completiondate + return ratio, 'on target', completiondate else: if not completiondate: completiondate = ws.reverse()[0].date - return ratio,'partial',completiondate + return ratio, 'partial', completiondate else: thevalue = 0 - for key,value in cratios.items(): - if ratio>value and value>thevalue: + for key, value in cratios.items(): + if ratio > value and value > thevalue: verdict = key thevalue = value completiondate = ws.reverse()[0].date - return ratio,verdict,completiondate + return ratio, verdict, completiondate elif ps.sessiontype == 'test': - if ratio==1.0: - return ratio,'on target',completiondate + if ratio == 1.0: + return ratio, 'on target', completiondate else: - return ratio,'partial',completiondate + return ratio, 'partial', completiondate elif ps.sessiontype == 'challenge': if ps.criterium == 'exact': if ratio == 1.0: - return ratio,'on target',completiondate + return ratio, 'on target', completiondate else: - return ratio,'partial',completiondate - elif ps.criterium == 'minimum': # pragma: no cover + return ratio, 'partial', completiondate + elif ps.criterium == 'minimum': # pragma: no cover if ratio > 1.0: - return ratio,'on target',completiondate + return ratio, 'on target', completiondate else: if not completiondate: completiondate = ws.reverse()[0].date - return ratio,'partial',completiondate + return ratio, 'partial', completiondate else: - if not completiondate: # pragma: no cover + if not completiondate: # pragma: no cover completiondate = ws.reverse()[0].date - return ratio,'partial',completiondate - elif ps.sessiontype == 'race': # pragma: no cover + return ratio, 'partial', completiondate + elif ps.sessiontype == 'race': # pragma: no cover vs = VirtualRaceResult.objects.filter(race=ps) wids = [w.id for w in ws] for record in vs: if record.workoutid in wids: if record.coursecompleted: ratio = record.distance/ps.sessionvalue - return ratio,'on target',completiondate + return ratio, 'on target', completiondate else: ratio = record.distance/ps.sessionvalue - return ratio,'partial',completiondate - return (0,'partial',None) - elif ps.sessiontype in ['fastest_time','fastest_distance']: # pragma: no cover - vs = CourseTestResult.objects.filter(plannedsession=ps,userid=ws[0].user.user.id) + return ratio, 'partial', completiondate + return (0, 'partial', None) + elif ps.sessiontype in ['fastest_time', 'fastest_distance']: # pragma: no cover + vs = CourseTestResult.objects.filter( + plannedsession=ps, userid=ws[0].user.user.id) completiondate = ws.reverse()[0].date wids = [w.id for w in ws] for record in vs: if record.workoutid in wids: if record.coursecompleted: ratio = 1 - return ratio,'on target',completiondate + return ratio, 'on target', completiondate else: - return 0,'partial',completiondate + return 0, 'partial', completiondate if ws: record = CourseTestResult( - userid = ws[0].user.id, - plannedsession = ps, - workoutid = ws[0].id, - duration = dt.time(0,0), - coursecompleted = False + userid=ws[0].user.id, + plannedsession=ps, + workoutid=ws[0].id, + duration=dt.time(0, 0), + coursecompleted=False ) record.save() - return (0,'not done',None) - elif ps.sessiontype == 'coursetest': # pragma: no cover + return (0, 'not done', None) + elif ps.sessiontype == 'coursetest': # pragma: no cover vs = CourseTestResult.objects.filter(plannedsession=ps) wids = [w.id for w in ws] for record in vs: if record.workoutid in wids: if record.coursecompleted: ratio = record.distance/float(ps.sessionvalue) - return ratio,'on target',completiondate + return ratio, 'on target', completiondate else: ratio = record.distance/float(ps.sessionvalue) - return ratio,'partial',completiondate + return ratio, 'partial', completiondate # we're still here - no record, need to create one if ws: @@ -784,46 +790,46 @@ def is_session_complete_ws(ws,ps): userid=ws[0].user.id, plannedsession=ps, workoutid=ws[0].id, - duration=dt.time(0,0), + duration=dt.time(0, 0), coursecompleted=False, ) record.save() - job = myqueue(queue,handle_check_race_course,ws[0].csvfilename, - ws[0].id,ps.course.id,record.id, - ws[0].user.user.email,ws[0].user.user.first_name, + job = myqueue(queue, handle_check_race_course, ws[0].csvfilename, + ws[0].id, ps.course.id, record.id, + ws[0].user.user.email, ws[0].user.user.first_name, mode='coursetest') - return (0,'not done',None) + return (0, 'not done', None) - - else: # pragma: no cover + else: # pragma: no cover if not completiondate: completiondate = ws.reverse()[0].date - return ratio,verdict,completiondate + return ratio, verdict, completiondate -def is_session_complete(r,ps): +def is_session_complete(r, ps): verdict = 'not done' + if r not in ps.rower.all(): # pragma: no cover + return 0, 'not assigned', None - if r not in ps.rower.all(): # pragma: no cover - return 0,'not assigned',None + ws = Workout.objects.filter(user=r, plannedsession=ps) - ws = Workout.objects.filter(user=r,plannedsession=ps) - - return is_session_complete_ws(ws,ps) + return is_session_complete_ws(ws, ps) -def rank_results(ps): # pragma: no cover +def rank_results(ps): # pragma: no cover return 1 -def add_team_session(t,ps): + +def add_team_session(t, ps): ps.team.add(t) ps.save() return 1 -def add_rower_session(r,ps): + +def add_rower_session(r, ps): teams = Team.objects.filter(manager=ps.manager) members = Rower.objects.filter(team__in=teams).distinct() if r in members and r.rowerplan != 'freecoach': @@ -837,22 +843,26 @@ def add_rower_session(r,ps): return 0 -def remove_team_session(t,ps): # pragma: no cover + +def remove_team_session(t, ps): # pragma: no cover ps.team.remove(t) return 1 -def remove_rower_session(r,ps): + +def remove_rower_session(r, ps): ps.rower.remove(r) return 1 + def get_team(request): teamid = request.GET.get('team') return teamid -def get_dates_timeperiod(request,startdatestring='',enddatestring='', - defaulttimeperiod='thisweek',rower=None): + +def get_dates_timeperiod(request, startdatestring='', enddatestring='', + defaulttimeperiod='thisweek', rower=None): # set start end date according timeperiod # should always return datetime.date @@ -868,18 +878,19 @@ def get_dates_timeperiod(request,startdatestring='',enddatestring='', if startdatestring and enddatestring: try: - startdate = dt.datetime.strptime(startdatestring,'%Y-%m-%d').date() - enddate = dt.datetime.strptime(enddatestring,'%Y-%m-%d').date() + startdate = dt.datetime.strptime( + startdatestring, '%Y-%m-%d').date() + enddate = dt.datetime.strptime(enddatestring, '%Y-%m-%d').date() except ValueError: try: - startdate = parser.parse(startdatestring,fuzzy=True).date() + startdate = parser.parse(startdatestring, fuzzy=True).date() enddate = parser.parse(enddatestring, fuzzy=True).date() - except ValueError: # pragma: no cover + except ValueError: # pragma: no cover startdate = timezone.now()-timezone.timedelta(days=5) startdate = startdate.date() enddate = timezone.now().date() - if startdate>enddate: + if startdate > enddate: e = startdate startdate = enddate enddate = e @@ -891,48 +902,50 @@ def get_dates_timeperiod(request,startdatestring='',enddatestring='', startdate = startdate.replace(tzinfo=tz) enddate = enddate.replace(tzinfo=tz) else: - startdate = dt.datetime.combine(startdate,dt.datetime.min.time()) - enddate = dt.datetime.combine(enddate,dt.datetime.min.time()) + startdate = dt.datetime.combine(startdate, dt.datetime.min.time()) + enddate = dt.datetime.combine(enddate, dt.datetime.min.time()) startdate = startdate.astimezone(pytz.utc) enddate = enddate.astimezone(pytz.utc) # set time to 00:00 in local time - startdate = startdate.replace(hour=0,minute=0,second=0,microsecond=0) - enddate = enddate.replace(hour=0,minute=0,second=0,microsecond=0) + startdate = startdate.replace( + hour=0, minute=0, second=0, microsecond=0) + enddate = enddate.replace(hour=0, minute=0, second=0, microsecond=0) - return startdate,enddate + return startdate, enddate daterangetester = re.compile('^(\d+-\d+-\d+)\/(\d+-\d+-\d+)') - - if timeperiod=='today': # pragma: no cover - startdate=timezone.now() - enddate=timezone.now() - elif timeperiod=='last30': + if timeperiod == 'today': # pragma: no cover + startdate = timezone.now() + enddate = timezone.now() + elif timeperiod == 'last30': startdate = timezone.now()-timezone.timedelta(days=30) enddate = timezone.now()+timezone.timedelta(days=1) - elif timeperiod=='tomorrow': # pragma: no cover - startdate=timezone.now()+timezone.timedelta(days=1) - enddate=timezone.now()+timezone.timedelta(days=1) - elif timeperiod=='thisweek': # pragma: no cover + elif timeperiod == 'tomorrow': # pragma: no cover + startdate = timezone.now()+timezone.timedelta(days=1) + enddate = timezone.now()+timezone.timedelta(days=1) + elif timeperiod == 'thisweek': # pragma: no cover today = timezone.now() startdate = timezone.now()-timezone.timedelta(days=today.weekday()) enddate = startdate+timezone.timedelta(days=6) - elif timeperiod=='thismonth': # pragma: no cover + elif timeperiod == 'thismonth': # pragma: no cover today = timezone.now() startdate = today.replace(day=1) enddate = startdate+timezone.timedelta(days=32) enddate = enddate.replace(day=1) enddate = enddate-timezone.timedelta(days=1) - elif timeperiod=='lastweek': # pragma: no cover + elif timeperiod == 'lastweek': # pragma: no cover today = timezone.now() - enddate = today-timezone.timedelta(days=today.weekday())-timezone.timedelta(days=1) + enddate = today - \ + timezone.timedelta(days=today.weekday())-timezone.timedelta(days=1) startdate = enddate-timezone.timedelta(days=6) - elif timeperiod=='nextweek': # pragma: no cover + elif timeperiod == 'nextweek': # pragma: no cover today = timezone.now() - startdate = today-timezone.timedelta(days=today.weekday())+timezone.timedelta(days=7) + startdate = today - \ + timezone.timedelta(days=today.weekday())+timezone.timedelta(days=7) enddate = startdate+timezone.timedelta(days=6) - elif timeperiod=='lastmonth': # pragma: no cover + elif timeperiod == 'lastmonth': # pragma: no cover today = timezone.now() startdate = today.replace(day=1) startdate = startdate-timezone.timedelta(days=3) @@ -940,7 +953,7 @@ def get_dates_timeperiod(request,startdatestring='',enddatestring='', enddate = startdate+timezone.timedelta(days=32) enddate = enddate.replace(day=1) enddate = enddate-timezone.timedelta(days=1) - elif timeperiod=='nextmonth': # pragma: no cover + elif timeperiod == 'nextmonth': # pragma: no cover today = timezone.now() startdate = today.replace(day=1) startdate = startdate+timezone.timedelta(days=32) @@ -948,7 +961,7 @@ def get_dates_timeperiod(request,startdatestring='',enddatestring='', enddate = startdate+timezone.timedelta(days=32) enddate = enddate.replace(day=1) enddate = enddate-timezone.timedelta(days=1) - elif timeperiod=='lastyear': # pragma: no cover + elif timeperiod == 'lastyear': # pragma: no cover today = timezone.now() startdate = today-timezone.timedelta(days=365) enddate = today+timezone.timedelta(days=1) @@ -956,22 +969,22 @@ def get_dates_timeperiod(request,startdatestring='',enddatestring='', tstartdatestring = daterangetester.match(timeperiod).group(1) tenddatestring = daterangetester.match(timeperiod).group(2) try: - startdate = dt.datetime.strptime(tstartdatestring,'%Y-%m-%d').date() - enddate = dt.datetime.strptime(tenddatestring,'%Y-%m-%d').date() - startdate = dt.datetime.combine(startdate,dt.datetime.min.time()) - enddate = dt.datetime.combine(enddate,dt.datetime.min.time()) - if startdate > enddate: # pragma: no cover + startdate = dt.datetime.strptime( + tstartdatestring, '%Y-%m-%d').date() + enddate = dt.datetime.strptime(tenddatestring, '%Y-%m-%d').date() + startdate = dt.datetime.combine(startdate, dt.datetime.min.time()) + enddate = dt.datetime.combine(enddate, dt.datetime.min.time()) + if startdate > enddate: # pragma: no cover startdate2 = enddate enddate = startdate startdate = startdate2 - except ValueError: # pragma: no cover + except ValueError: # pragma: no cover startdate = timezone.now() enddate = timezone.now() else: startdate = timezone.now() enddate = timezone.now() - if startdatestring != '': try: startdate = iso8601.parse_date(startdatestring) @@ -993,14 +1006,15 @@ def get_dates_timeperiod(request,startdatestring='',enddatestring='', enddate = enddate.astimezone(tz) # set time to 00:00 in local time - startdate = startdate.replace(hour=0,minute=0,second=0,microsecond=0) - enddate = enddate.replace(hour=0,minute=0,second=0,microsecond=0) + startdate = startdate.replace(hour=0, minute=0, second=0, microsecond=0) + enddate = enddate.replace(hour=0, minute=0, second=0, microsecond=0) - return startdate,enddate + return startdate, enddate -def get_sessions_manager(m,teamid=0,startdate=timezone.now(), + +def get_sessions_manager(m, teamid=0, startdate=timezone.now(), enddate=timezone.now()+timezone.timedelta(+1000)): - if teamid: # pragma: no cover + if teamid: # pragma: no cover t = Team.objects.get(id=teamid) rs = Rower.objects.filter(team__in=[t]).distinct() sps = PlannedSession.objects.filter( @@ -1009,20 +1023,21 @@ def get_sessions_manager(m,teamid=0,startdate=timezone.now(), startdate__lte=enddate, enddate__gte=startdate, is_template=False, - ).order_by("preferreddate","startdate","enddate").exclude( - sessiontype='race').exclude(sessiontype='indoorrace') + ).order_by("preferreddate", "startdate", "enddate").exclude( + sessiontype='race').exclude(sessiontype='indoorrace') else: sps = PlannedSession.objects.filter( manager=m, startdate__lte=enddate, enddate__gte=startdate, is_template=False, - ).order_by("preferreddate","startdate","enddate").exclude( - sessiontype='race').exclude(sessiontype='indoorrace') + ).order_by("preferreddate", "startdate", "enddate").exclude( + sessiontype='race').exclude(sessiontype='indoorrace') return sps -def get_sessions(r,startdate=timezone.now(), + +def get_sessions(r, startdate=timezone.now(), enddate=timezone.now()+timezone.timedelta(+1000)): sps = PlannedSession.objects.filter( @@ -1030,25 +1045,28 @@ def get_sessions(r,startdate=timezone.now(), startdate__lte=enddate, enddate__gte=startdate, is_template=False, - ).order_by("preferreddate","startdate","enddate").exclude( - sessiontype='race').exclude(sessiontype='indoorrace') + ).order_by("preferreddate", "startdate", "enddate").exclude( + sessiontype='race').exclude(sessiontype='indoorrace') return sps + def get_my_session_ids(r): sps = PlannedSession.objects.filter( rower__in=[r] - ).order_by("preferreddate","startdate","enddate").exclude( - sessiontype='race') + ).order_by("preferreddate", "startdate", "enddate").exclude( + sessiontype='race') return [ps.id for ps in sps] -def get_workouts_session(r,ps): - ws = Workout.objects.filter(user=r,plannedsession=ps) + +def get_workouts_session(r, ps): + ws = Workout.objects.filter(user=r, plannedsession=ps) return ws -def create_sessions_from_json(plansteps,rower,startdate,manager): + +def create_sessions_from_json(plansteps, rower, startdate, manager): trainingdays = plansteps['trainingDays'] planstartdate = startdate for day in trainingdays: @@ -1062,30 +1080,33 @@ def create_sessions_from_json(plansteps,rower,startdate,manager): preferreddate = planstartdate+timedelta(days=day['order']) ps = PlannedSession( - startdate = preferreddate-timedelta(days=preferreddate.weekday()), - enddate = preferreddate+timedelta(days=-preferreddate.weekday()-1,weeks=1), - preferreddate = preferreddate, - sessionsport = sessionsport, # change this - name = workout['workoutName'], - steps = workout, - manager = manager, - sessionmode = 'time', - comment = workout['description'] - ) + startdate=preferreddate - + timedelta(days=preferreddate.weekday()), + enddate=preferreddate + + timedelta(days=-preferreddate.weekday()-1, weeks=1), + preferreddate=preferreddate, + sessionsport=sessionsport, # change this + name=workout['workoutName'], + steps=workout, + manager=manager, + sessionmode='time', + comment=workout['description'] + ) ps.save() - add_rower_session(rower,ps) + add_rower_session(rower, ps) -def update_plannedsession(ps,cd): + +def update_plannedsession(ps, cd): for attr, value in cd.items(): if attr == 'comment': - value.replace("\r\n", " "); - value.replace("\n", " "); + value.replace("\r\n", " ") + value.replace("\n", " ") if attr != 'fitfile': setattr(ps, attr, value) - if cd['fitfile']: # pragma: no cover + if cd['fitfile']: # pragma: no cover f = cd['fitfile'] try: filename, path_and_filename = handle_uploaded_file(f) @@ -1096,22 +1117,23 @@ def update_plannedsession(ps,cd): ps.save() - return 1,'Planned Session Updated' + return 1, 'Planned Session Updated' -def update_indoorvirtualrace(ps,cd): + +def update_indoorvirtualrace(ps, cd): for attr, value in cd.items(): if attr == 'comment': value = htmlstripnobr(value) - value.replace("\r\n", " "); - value.replace("\n", " "); + value.replace("\r\n", " ") + value.replace("\n", " ") setattr(ps, attr, value) timezone_str = cd['timezone'] # correct times - startdatetime = datetime.combine(cd['startdate'],cd['start_time']) - enddatetime = datetime.combine(cd['enddate'],cd['end_time']) + startdatetime = datetime.combine(cd['startdate'], cd['start_time']) + enddatetime = datetime.combine(cd['enddate'], cd['end_time']) startdatetime = pytz.timezone(timezone_str).localize( startdatetime @@ -1125,7 +1147,7 @@ def update_indoorvirtualrace(ps,cd): registration_form = cd['registration_form'] registration_closure = cd['registration_closure'] - if registration_form == 'manual': # pragma: no cover + if registration_form == 'manual': # pragma: no cover try: registration_closure = pytz.timezone( timezone_str @@ -1134,9 +1156,9 @@ def update_indoorvirtualrace(ps,cd): ) except AttributeError: registration_closure = startdatetime - elif registration_form == 'windowstart': # pragma: no cover + elif registration_form == 'windowstart': # pragma: no cover registration_closure = startdatetime - elif registration_form == 'windowend': # pragma: no cover + elif registration_form == 'windowend': # pragma: no cover registration_closure = enddatetime else: registration_closure = ps.evaluation_closure @@ -1145,32 +1167,33 @@ def update_indoorvirtualrace(ps,cd): ps.timezone = timezone_str - if ps.sessiontype == 'fastest_distance': # pragma: no cover + if ps.sessiontype == 'fastest_distance': # pragma: no cover ps.approximate_distance = ps.sessionvalue - if ps.course is not None: # pragma: no cover + if ps.course is not None: # pragma: no cover ps.approximate_distance = ps.course.distance ps.save() - return 1,'Virtual Race Updated' + return 1, 'Virtual Race Updated' -def update_virtualrace(ps,cd): + +def update_virtualrace(ps, cd): for attr, value in cd.items(): if attr == 'comment': value = htmlstripnobr(value) - value.replace("\r\n", " "); - value.replace("\n", " "); + value.replace("\r\n", " ") + value.replace("\n", " ") setattr(ps, attr, value) # correct times course = cd['course'] - geocourse = GeoCourse.objects.get(id= course.id) + geocourse = GeoCourse.objects.get(id=course.id) timezone_str = get_course_timezone(geocourse) - startdatetime = datetime.combine(cd['startdate'],cd['start_time']) - enddatetime = datetime.combine(cd['enddate'],cd['end_time']) + startdatetime = datetime.combine(cd['startdate'], cd['start_time']) + enddatetime = datetime.combine(cd['enddate'], cd['end_time']) startdatetime = pytz.timezone(timezone_str).localize( startdatetime @@ -1184,7 +1207,7 @@ def update_virtualrace(ps,cd): registration_form = cd['registration_form'] registration_closure = cd['registration_closure'] - if registration_form == 'manual': # pragma: no cover + if registration_form == 'manual': # pragma: no cover try: registration_closure = pytz.timezone( timezone_str @@ -1193,9 +1216,9 @@ def update_virtualrace(ps,cd): ) except AttributeError: registration_closure = startdatetime - elif registration_form == 'windowstart': # pragma: no cover + elif registration_form == 'windowstart': # pragma: no cover registration_closure = startdatetime - elif registration_form == 'windowend': # pragma: no cover + elif registration_form == 'windowend': # pragma: no cover registration_closure = enddatetime else: registration_closure = ps.evaluation_closure @@ -1204,7 +1227,7 @@ def update_virtualrace(ps,cd): ps.timezone = timezone_str - if ps.sessiontype == 'fastest_distance': # pragma: no cover + if ps.sessiontype == 'fastest_distance': # pragma: no cover ps.approximate_distance = ps.sessionvalue if ps.course is not None: @@ -1212,9 +1235,10 @@ def update_virtualrace(ps,cd): ps.save() - return 1,'Virtual Race Updated' + return 1, 'Virtual Race Updated' -def race_rower_status(r,race): + +def race_rower_status(r, race): has_registered = False is_complete = False @@ -1224,52 +1248,52 @@ def race_rower_status(r,race): else: resultobj = IndoorVirtualRaceResult - vs = resultobj.objects.filter(userid=r.id,race=race) + vs = resultobj.objects.filter(userid=r.id, race=race) if vs: has_registered = True is_complete = vs[0].coursecompleted - return is_complete,has_registered + return is_complete, has_registered -def race_can_edit(r,race): + +def race_can_edit(r, race): if r.user != race.manager: return False else: start_time = race.start_time start_date = race.startdate - startdatetime = datetime.combine(start_date,start_time) + startdatetime = datetime.combine(start_date, start_time) startdatetime = pytz.timezone(race.timezone).localize( startdatetime ) end_time = race.end_time end_date = race.enddate - enddatetime = datetime.combine(end_date,end_time) + enddatetime = datetime.combine(end_date, end_time) enddatetime = pytz.timezone(race.timezone).localize( enddatetime ) - if timezone.now() startdatetime and timezone.now() < evaluation_closure: - is_complete,has_registered = race_rower_status(r,race) + is_complete, has_registered = race_rower_status(r, race) if is_complete == False: return True else: @@ -1277,98 +1301,93 @@ def race_can_submit(r,race): else: return False - return False # pragma: no cover + return False # pragma: no cover -def race_can_editentry(r,race): + +def race_can_editentry(r, race): start_time = race.start_time start_date = race.startdate - startdatetime = datetime.combine(start_date,start_time) + startdatetime = datetime.combine(start_date, start_time) startdatetime = pytz.timezone(race.timezone).localize( startdatetime - ) + ) evaluation_closure = race.evaluation_closure if timezone.now() < evaluation_closure: - is_complete,has_registered = race_rower_status(r,race) + is_complete, has_registered = race_rower_status(r, race) if is_complete == False: return True - else: # pragma: no cover + else: # pragma: no cover return False - else: # pragma: no cover + else: # pragma: no cover return False - return False # pragma: no cover + return False # pragma: no cover -def race_can_resubmit(r,race): + +def race_can_resubmit(r, race): records = VirtualRaceResult.objects.filter( userid=r.id, race=race) - if not records: return False start_time = race.start_time start_date = race.startdate - startdatetime = datetime.combine(start_date,start_time) + startdatetime = datetime.combine(start_date, start_time) startdatetime = pytz.timezone(race.timezone).localize( startdatetime - ) + ) evaluation_closure = race.evaluation_closure - - if timezone.now() > startdatetime and timezone.now() < evaluation_closure: - is_complete,has_registered = race_rower_status(r,race) + is_complete, has_registered = race_rower_status(r, race) return is_complete - else: # pragma: no cover + else: # pragma: no cover return False - return False # pragma: no cover + return False # pragma: no cover -def race_can_adddiscipline(r,race): - if race.sessiontype not in ['race','fastest_time','fastest_distance']: +def race_can_adddiscipline(r, race): + + if race.sessiontype not in ['race', 'fastest_time', 'fastest_distance']: return False - if race.sessiontype in ['race']: resultobj = VirtualRaceResult - else: # pragma: no cover + else: # pragma: no cover resultobj = IndoorVirtualRaceResult records = resultobj.objects.filter( userid=r.id, race=race) - if not records: return False - - start_time = race.start_time start_date = race.startdate - startdatetime = datetime.combine(start_date,start_time) + startdatetime = datetime.combine(start_date, start_time) startdatetime = pytz.timezone(race.timezone).localize( startdatetime - ) + ) evaluation_closure = race.evaluation_closure - if timezone.now() < evaluation_closure: - is_complete,has_registered = race_rower_status(r,race) + is_complete, has_registered = race_rower_status(r, race) if has_registered: return True - else: # pragma: no cover + else: # pragma: no cover return False - else: # pragma: no cover + else: # pragma: no cover return False - return False # pragma: no cover + return False # pragma: no cover -def race_can_withdraw(r,race): +def race_can_withdraw(r, race): if race.sessiontype in ['race']: recordobj = VirtualRaceResult else: @@ -1377,34 +1396,31 @@ def race_can_withdraw(r,race): records = recordobj.objects.filter( userid=r.id, race=race - ) - - + ) if not records: return False start_time = race.start_time start_date = race.startdate - startdatetime = datetime.combine(start_date,start_time) + startdatetime = datetime.combine(start_date, start_time) startdatetime = pytz.timezone(race.timezone).localize( startdatetime - ) + ) registration_closure = race.registration_closure - if registration_closure is None or registration_closure == '': # pragma: no cover - registration_closure = startdatetime + if registration_closure is None or registration_closure == '': # pragma: no cover + registration_closure = startdatetime - if timezone.now() > registration_closure: # pragma: no cover + if timezone.now() > registration_closure: # pragma: no cover return False elif timezone.now() > startdatetime: return False - return True -def race_can_register(r,race): +def race_can_register(r, race): if race.sessiontype in ['race']: recordobj = VirtualRaceResult else: @@ -1419,68 +1435,71 @@ def race_can_register(r,race): start_time = race.start_time start_date = race.startdate - startdatetime = datetime.combine(start_date,start_time) + startdatetime = datetime.combine(start_date, start_time) startdatetime = pytz.timezone(race.timezone).localize( startdatetime - ) + ) registration_closure = race.registration_closure - if registration_closure is None or registration_closure == '': # pragma: no cover - registration_closure = startdatetime + if registration_closure is None or registration_closure == '': # pragma: no cover + registration_closure = startdatetime - if timezone.now() > registration_closure: # pragma: no cover + if timezone.now() > registration_closure: # pragma: no cover return False return True -def add_rower_race(r,race): + +def add_rower_race(r, race): race.rower.add(r) race.save() return 1 -def remove_rower_race(r,race,recordid=None): + +def remove_rower_race(r, race, recordid=None): race.rower.remove(r) - if race.sessiontype in ['race']: # pragma: no cover + if race.sessiontype in ['race']: # pragma: no cover recordobj = VirtualRaceResult else: recordobj = IndoorVirtualRaceResult - if recordid: # pragma: no cover + if recordid: # pragma: no cover records = recordobj.objects.filter(userid=r.id, - workoutid__isnull=True, - race=race, - id=recordid) + workoutid__isnull=True, + race=race, + id=recordid) else: records = recordobj.objects.filter(userid=r.id, - workoutid__isnull=True, - race=race,) + workoutid__isnull=True, + race=race,) for r in records: r.delete() return 1 -def default_class(r,w,race): + +def default_class(r, w, race): if r.birthdate: age = calculate_age(r.birthdate) - else: # pragma: no cover + else: # pragma: no cover age = 25 sex = r.sex - if sex=='not specified': - sex='male' + if sex == 'not specified': + sex = 'male' - if w is not None: # pragma: no cover + if w is not None: # pragma: no cover boatclass = w.workouttype boattype = w.boattype adaptiveclass = w.adaptiveclass weightclass = w.weightcategory else: - if race.sessiontype in ['race','fastest_time','fastest_distance']: + if race.sessiontype in ['race', 'fastest_time', 'fastest_distance']: boatclass = 'water' - else: # pragma: no cover + else: # pragma: no cover boatclass = 'rower' boattype = '1x' @@ -1489,71 +1508,69 @@ def default_class(r,w,race): if race.coursestandards: standards = CourseStandard.objects.filter( - agemin__lt=age,agemax__gt=age, + agemin__lt=age, agemax__gt=age, boatclass=boatclass, adaptiveclass=adaptiveclass, boattype=boattype, weightclass=weightclass, sex=sex, - standardcollection = race.coursestandards, - ).order_by("agemax","-agemin","boattype","sex") + standardcollection=race.coursestandards, + ).order_by("agemax", "-agemin", "boattype", "sex") - - - if standards.count()==0: # pragma: no cover + if standards.count() == 0: # pragma: no cover # omit weight standards = CourseStandard.objects.filter( - agemin__lt=age,agemax__gt=age, + agemin__lt=age, agemax__gt=age, boatclass=boatclass, adaptiveclass=adaptiveclass, boattype=boattype, sex=sex, - standardcollection = race.coursestandards, - ).order_by( - "agemax","-agemin","boattype","sex","weightclass", - "referencespeed" - ) + standardcollection=race.coursestandards, + ).order_by( + "agemax", "-agemin", "boattype", "sex", "weightclass", + "referencespeed" + ) - if standards.count()==0: + if standards.count() == 0: # omit adaptive class standards = CourseStandard.objects.filter( - agemin__lt=age,agemax__gt=age, - boattype=boattype,sex=sex, - standardcollection = race.coursestandards, - ).order_by( - "agemax","-agemin","boattype","sex", - "weightclass","referencespeed") + agemin__lt=age, agemax__gt=age, + boattype=boattype, sex=sex, + standardcollection=race.coursestandards, + ).order_by( + "agemax", "-agemin", "boattype", "sex", + "weightclass", "referencespeed") - if standards.count()==0: + if standards.count() == 0: # omit boattype standards = CourseStandard.objects.filter( - agemin__lt=age,agemax__gt=age,sex=sex, - standardcollection = race.coursestandards, - ).order_by( - "agemax","-agemin","boattype","sex", - "weightclass","referencespeed") + agemin__lt=age, agemax__gt=age, sex=sex, + standardcollection=race.coursestandards, + ).order_by( + "agemax", "-agemin", "boattype", "sex", + "weightclass", "referencespeed") - if standards.count()==0: + if standards.count() == 0: # omit boattype standards = CourseStandard.objects.filter( - agemin__lt=age,agemax__gt=age,sex='male', - standardcollection = race.coursestandards - ).order_by( - "agemax","-agemin","boattype","sex", - "weightclass","referencespeed") + agemin__lt=age, agemax__gt=age, sex='male', + standardcollection=race.coursestandards + ).order_by( + "agemax", "-agemin", "boattype", "sex", + "weightclass", "referencespeed") - - if standards.count()==0: + if standards.count() == 0: # boolean, boattype, boatclass, adaptiveclass, weightclass, sex, coursestandard, - return False,'1x','water',None,'hwt','male',5.0,None + return False, '1x', 'water', None, 'hwt', 'male', 5.0, None - if standards.count()>0: + if standards.count() > 0: # find optimum standard s = standards[0] - return True,s.boattype,s.boatclass,s.adaptiveclass,s.weightclass,s.sex,s.referencespeed,s + return True, s.boattype, s.boatclass, s.adaptiveclass, s.weightclass, s.sex, s.referencespeed, s # No Course Standard - return True,boattype,boatclass,adaptiveclass,weightclass,sex,5.0,None + return True, boattype, boatclass, adaptiveclass, weightclass, sex, 5.0, None + def add_workout_fastestrace(ws, race, r, recordid=0, doregister=False): result = 0 @@ -1562,63 +1579,61 @@ def add_workout_fastestrace(ws, race, r, recordid=0, doregister=False): start_time = race.start_time start_date = race.startdate - startdatetime = datetime.combine(start_date,start_time) + startdatetime = datetime.combine(start_date, start_time) startdatetime = pytz.timezone(race.timezone).localize( startdatetime - ) + ) end_time = race.end_time end_date = race.enddate - enddatetime = datetime.combine(end_date,end_time) + enddatetime = datetime.combine(end_date, end_time) enddatetime = pytz.timezone(race.timezone).localize( enddatetime - ) + ) ids = [w.id for w in ws] ids = list(set(ids)) - - if len(ids)>1 and race.sessiontype in ['test','coursetest','race','indoorrace','fastest_time','fastest_distance']: # pragma: no cover + if len(ids) > 1 and race.sessiontype in ['test', 'coursetest', 'race', 'indoorrace', 'fastest_time', 'fastest_distance']: # pragma: no cover errors.append('For tests, you can only attach one workout') - return result,comments,errors,0 + return result, comments, errors, 0 username = r.user.first_name+' '+r.user.last_name if r.birthdate: age = calculate_age(r.birthdate) - else: # pragma: no cover - age = None + else: # pragma: no cover + age = None try: record = IndoorVirtualRaceResult.objects.get( userid=r.id, race=race, id=recordid - ) - except IndoorVirtualRaceResult.DoesNotExist: # pragma: no cover + ) + except IndoorVirtualRaceResult.DoesNotExist: # pragma: no cover if doregister: - hasinitial,boattype,boatclass,adaptiveclass,weightclass,sex,referencespeed,initialcategory = default_class(r,ws[0],race) + hasinitial, boattype, boatclass, adaptiveclass, weightclass, sex, referencespeed, initialcategory = default_class( + r, ws[0], race) if hasinitial: record = IndoorVirtualRaceResult( - userid = r.id, - username = r.user.first_name+' '+r.user.last_name, + userid=r.id, + username=r.user.first_name+' '+r.user.last_name, weightcategory=weightclass, adaptiveclass=adaptiveclass, race=race, boatclass=boatclass, sex=sex, - age = age, + age=age, referencespeed=referencespeed, entrycategory=initialcategory, ) record.save() else: errors.append("Unable to find a suitable start category") - return result,comments,errors,0 - else: # pragma: no cover + return result, comments, errors, 0 + else: # pragma: no cover errors.append("Couldn't find this entry") - return result,comments,errors,0 - - + return result, comments, errors, 0 records = IndoorVirtualRaceResult.objects.filter( userid=r.id, @@ -1626,35 +1641,37 @@ def add_workout_fastestrace(ws, race, r, recordid=0, doregister=False): #workoutid = ws[0].id ) - if ws[0].workouttype != record.boatclass: # pragma: no cover - errors.append('Your workout boat class is different than on your race registration') - return 0,comments,errors,0 + if ws[0].workouttype != record.boatclass: # pragma: no cover + errors.append( + 'Your workout boat class is different than on your race registration') + return 0, comments, errors, 0 - if ws[0].workouttype not in mytypes.otwtypes: # pragma: no cover + if ws[0].workouttype not in mytypes.otwtypes: # pragma: no cover errors.append('You must submit a on-the-water rowing workout') - return 0,comments, errors, 0 + return 0, comments, errors, 0 - if record.weightcategory == 'lwt' and ws[0].weightcategory != record.weightcategory: # pragma: no cover - errors.append('Your workout weight category did not match the weight category you registered') - return 0,comments, errors,0 + if record.weightcategory == 'lwt' and ws[0].weightcategory != record.weightcategory: # pragma: no cover + errors.append( + 'Your workout weight category did not match the weight category you registered') + return 0, comments, errors, 0 - if ws[0].adaptiveclass != record.adaptiveclass: # pragma: no cover - errors.append('Your adaptive classification did not match the registration') - return 0,comments, errors, 0 + if ws[0].adaptiveclass != record.adaptiveclass: # pragma: no cover + errors.append( + 'Your adaptive classification did not match the registration') + return 0, comments, errors, 0 # start adding sessions - if ws[0].startdatetime>=startdatetime and ws[0].startdatetime<=enddatetime: + if ws[0].startdatetime >= startdatetime and ws[0].startdatetime <= enddatetime: ws[0].plannedsession = race ws[0].save() result += 1 - else: # pragma: no cover + else: # pragma: no cover errors.append('Workout %i did not match the race window' % ws[0].id) - return result,comments,errors,0 + return result, comments, errors, 0 - - if result>0: - for otherrecord in records: # pragma: no cover + if result > 0: + for otherrecord in records: # pragma: no cover oldworkouts = Workout.objects.filter(plannedsession=race) for oldworkout in oldworkouts: oldworkout.plannedsession = None @@ -1664,13 +1681,15 @@ def add_workout_fastestrace(ws, race, r, recordid=0, doregister=False): otherrecord.coursecompleted = False otherrecord.save() - result, comment, errors = add_workouts_plannedsession(ws,race,r) + result, comment, errors = add_workouts_plannedsession(ws, race, r) if result: record.coursecompleted = True record.workoutid = ws[0].id if race.sessiontype == 'fastest_distance': - df = dataprep.getsmallrowdata_db(['time','cumdist'],ids=[ws[0].id]) - fastest_milliseconds,startsecond,endsecond = datautils.getfastest(df,race.sessionvalue,mode='distance') + df = dataprep.getsmallrowdata_db( + ['time', 'cumdist'], ids=[ws[0].id]) + fastest_milliseconds, startsecond, endsecond = datautils.getfastest( + df, race.sessionvalue, mode='distance') velo = race.sessionvalue/fastest_milliseconds points = 100.*velo/record.referencespeed @@ -1683,14 +1702,16 @@ def add_workout_fastestrace(ws, race, r, recordid=0, doregister=False): record.startsecond = startsecond record.endsecond = endsecond record.save() - if race.sessiontype == 'fastest_time': # pragma: no cover - df = dataprep.getsmallrowdata_db(['time','cumdist'],ids=[ws[0].id]) - fastest_meters, startsecond, endsecond = datautils.getfastest(df,race.sessionvalue,mode='time') + if race.sessiontype == 'fastest_time': # pragma: no cover + df = dataprep.getsmallrowdata_db( + ['time', 'cumdist'], ids=[ws[0].id]) + fastest_meters, startsecond, endsecond = datautils.getfastest( + df, race.sessionvalue, mode='time') velo = fastest_meters/(60.*race.sessionvalue) points = 100.*velo/record.referencespeed if fastest_meters > 0: - duration = dt.time(0,race.sessionvalue) + duration = dt.time(0, race.sessionvalue) record.duration = duration record.distance = fastest_meters record.coursecompleted = True @@ -1699,116 +1720,111 @@ def add_workout_fastestrace(ws, race, r, recordid=0, doregister=False): record.endsecond = endsecond record.save() - - - if ws[0].privacy == 'private': # pragma: no cover + if ws[0].privacy == 'private': # pragma: no cover ws[0].privacy = 'visible' ws[0].save() - comments.append('Workouts submitted to virtual events have to be public. We have changed the workout to a public workout.') + comments.append( + 'Workouts submitted to virtual events have to be public. We have changed the workout to a public workout.') record.save() - else: # pragma: no cover + else: # pragma: no cover errors.append('Could not find a valid interval in this workout') return result, comments, errors, 0 # Low Level functions - to be called by higher level methods -def add_workout_indoorrace(ws,race,r,recordid=0,doregister=False): +def add_workout_indoorrace(ws, race, r, recordid=0, doregister=False): result = 0 comments = [] errors = [] start_time = race.start_time start_date = race.startdate - startdatetime = datetime.combine(start_date,start_time) + startdatetime = datetime.combine(start_date, start_time) startdatetime = pytz.timezone(race.timezone).localize( startdatetime - ) + ) end_time = race.end_time end_date = race.enddate - enddatetime = datetime.combine(end_date,end_time) + enddatetime = datetime.combine(end_date, end_time) enddatetime = pytz.timezone(race.timezone).localize( enddatetime - ) + ) # check if all sessions have same date dates = [w.date for w in ws] - if (not all(d == dates[0] for d in dates)) and race.sessiontype not in ['challenge','cycletarget']: # pragma: no cover - errors.append('For tests and training sessions, selected workouts must all be done on the same date') - return result,comments,errors,0 + if (not all(d == dates[0] for d in dates)) and race.sessiontype not in ['challenge', 'cycletarget']: # pragma: no cover + errors.append( + 'For tests and training sessions, selected workouts must all be done on the same date') + return result, comments, errors, 0 - - if len(ws)>1 and race.sessiontype == 'test': # pragma: no cover + if len(ws) > 1 and race.sessiontype == 'test': # pragma: no cover errors.append('For tests, you can only attach one workout') - return result,comments,errors,0 - - + return result, comments, errors, 0 ids = [w.id for w in ws] ids = list(set(ids)) - if len(ids)>1 and race.sessiontype in ['test','coursetest','race','indoorrace','fastest_time','fastest_distance']: # pragma: no cover + if len(ids) > 1 and race.sessiontype in ['test', 'coursetest', 'race', 'indoorrace', 'fastest_time', 'fastest_distance']: # pragma: no cover errors.append('For tests, you can only attach one workout') - return result,comments,errors,0 - - + return result, comments, errors, 0 username = r.user.first_name+' '+r.user.last_name if r.birthdate: age = calculate_age(r.birthdate) - else: # pragma: no cover - age = None + else: # pragma: no cover + age = None try: record = IndoorVirtualRaceResult.objects.get( userid=r.id, race=race, id=recordid - ) - except IndoorVirtualRaceResult.DoesNotExist: # pragma: no cover + ) + except IndoorVirtualRaceResult.DoesNotExist: # pragma: no cover if doregister: - hasinitial,boattype,boatclass,adaptiveclass,weightclass,sex,referencespeed,initialcategory = default_class(r,ws[0],race) + hasinitial, boattype, boatclass, adaptiveclass, weightclass, sex, referencespeed, initialcategory = default_class( + r, ws[0], race) if hasinitial: record = IndoorVirtualRaceResult( - userid = r.id, - username = r.user.first_name+' '+r.user.last_name, + userid=r.id, + username=r.user.first_name+' '+r.user.last_name, weightcategory=weightclass, adaptiveclass=adaptiveclass, race=race, boatclass=boatclass, sex=sex, - age = age, + age=age, referencespeed=referencespeed, entrycategory=initialcategory, ) record.save() else: errors.append("Unable to find a suitable start category") - return result,comments,errors,0 - else: # pragma: no cover + return result, comments, errors, 0 + else: # pragma: no cover errors.append("Couldn't find this entry") - return result,comments,errors,0 + return result, comments, errors, 0 records = IndoorVirtualRaceResult.objects.filter( userid=r.id, race=race, - workoutid = ws[0].id - ) - + workoutid=ws[0].id + ) if race.sessionmode == 'distance': if ws[0].distance != race.sessionvalue: errors.append('Your workout did not have the correct distance') - return 0,comments, errors, 0 + return 0, comments, errors, 0 else: record.distance = ws[0].distance record.duration = ws[0].duration - else: # pragma: no cover + else: # pragma: no cover t = ws[0].duration seconds = t.second+t.minute*60.+t.hour*3600.+t.microsecond/1.e6 - if seconds != race.sessionvalue*60.: # pragma: no cover + if seconds != race.sessionvalue*60.: # pragma: no cover errors.append('Your workout did not have the correct duration') return 0, comments, errors, 0 else: @@ -1816,33 +1832,36 @@ def add_workout_indoorrace(ws,race,r,recordid=0,doregister=False): record.duration = ws[0].duration if ws[0].workouttype != record.boatclass: - errors.append('Your workout boat class is different than on your race registration') - return 0,comments,errors,0 + errors.append( + 'Your workout boat class is different than on your race registration') + return 0, comments, errors, 0 - if ws[0].workouttype not in mytypes.otetypes: # pragma: no cover + if ws[0].workouttype not in mytypes.otetypes: # pragma: no cover errors.append('You must submit a indoor rowing workout') - return 0,comments, errors, 0 + return 0, comments, errors, 0 - if record.weightcategory == 'lwt' and ws[0].weightcategory != record.weightcategory: # pragma: no cover - errors.append('Your workout weight category did not match the weight category you registered') - return 0,comments, errors,0 + if record.weightcategory == 'lwt' and ws[0].weightcategory != record.weightcategory: # pragma: no cover + errors.append( + 'Your workout weight category did not match the weight category you registered') + return 0, comments, errors, 0 - if ws[0].adaptiveclass != record.adaptiveclass: # pragma: no cover - errors.append('Your adaptive classification did not match the registration') - return 0,comments, errors, 0 + if ws[0].adaptiveclass != record.adaptiveclass: # pragma: no cover + errors.append( + 'Your adaptive classification did not match the registration') + return 0, comments, errors, 0 # start adding sessions - if ws[0].startdatetime>=startdatetime and ws[0].startdatetime<=enddatetime: + if ws[0].startdatetime >= startdatetime and ws[0].startdatetime <= enddatetime: ws[0].plannedsession = race ws[0].save() result += 1 - else: # pragma: no cover + else: # pragma: no cover errors.append('Workout %i did not match the race window' % ws[0].id) - return result,comments,errors,0 + return result, comments, errors, 0 - if result>0: - for otherrecord in records: # pragma: no cover + if result > 0: + for otherrecord in records: # pragma: no cover otherrecord.workoutid = None otherrecord.coursecompleted = False otherrecord.save() @@ -1850,176 +1869,177 @@ def add_workout_indoorrace(ws,race,r,recordid=0,doregister=False): record.coursecompleted = True record.workoutid = ws[0].id - if ws[0].privacy == 'private': # pragma: no cover + if ws[0].privacy == 'private': # pragma: no cover ws[0].privacy = 'visible' ws[0].save() - comments.append('Workouts submitted to virtual events have to be public. We have changed the workout to a public workout.') + comments.append( + 'Workouts submitted to virtual events have to be public. We have changed the workout to a public workout.') record.save() - add_workouts_plannedsession(ws,race,r) + add_workouts_plannedsession(ws, race, r) + + return result, comments, errors, 0 - return result,comments,errors,0 - - -def add_workout_race(ws,race,r,splitsecond=0,recordid=0,doregister=False): +def add_workout_race(ws, race, r, splitsecond=0, recordid=0, doregister=False): result = 0 comments = [] errors = [] start_time = race.start_time start_date = race.startdate - startdatetime = datetime.combine(start_date,start_time) + startdatetime = datetime.combine(start_date, start_time) startdatetime = pytz.timezone(race.timezone).localize( startdatetime - ) + ) end_time = race.end_time end_date = race.enddate - enddatetime = datetime.combine(end_date,end_time) + enddatetime = datetime.combine(end_date, end_time) enddatetime = pytz.timezone(race.timezone).localize( enddatetime - ) + ) # check if all sessions have same date dates = [w.date for w in ws] - if (not all(d == dates[0] for d in dates)) and race.sessiontype not in ['challenge','cycletarget']: # pragma: no cover - errors.append('For tests and training sessions, selected workouts must all be done on the same date') - return result,comments,errors,0 + if (not all(d == dates[0] for d in dates)) and race.sessiontype not in ['challenge', 'cycletarget']: # pragma: no cover + errors.append( + 'For tests and training sessions, selected workouts must all be done on the same date') + return result, comments, errors, 0 - if len(ws)>1 and race.sessiontype == 'test': # pragma: no cover + if len(ws) > 1 and race.sessiontype == 'test': # pragma: no cover errors.append('For tests, you can only attach one workout') - return result,comments,errors,0 - - + return result, comments, errors, 0 ids = [w.id for w in ws] ids = list(set(ids)) - if len(ids)>1 and race.sessiontype in ['test','coursetest','race','fastest_time','fastest_distance']: # pragma: no cover + if len(ids) > 1 and race.sessiontype in ['test', 'coursetest', 'race', 'fastest_time', 'fastest_distance']: # pragma: no cover errors.append('For tests, you can only attach one workout') - return result,comments,errors,0 - - + return result, comments, errors, 0 username = r.user.first_name+' '+r.user.last_name if r.birthdate: age = calculate_age(r.birthdate) - else: # pragma: no cover - age = None + else: # pragma: no cover + age = None try: record = VirtualRaceResult.objects.get( userid=r.id, race=race, id=recordid - ) - except VirtualRaceResult.DoesNotExist: # pragma: no cover + ) + except VirtualRaceResult.DoesNotExist: # pragma: no cover if doregister: - hasinitial,boattype,boatclass,adaptiveclass,weightclass,sex,referencespeed,initialcategory = default_class(r,ws[0],race) + hasinitial, boattype, boatclass, adaptiveclass, weightclass, sex, referencespeed, initialcategory = default_class( + r, ws[0], race) if hasinitial: record = VirtualRaceResult( - userid = r.id, - username = r.user.first_name+' '+r.user.last_name, + userid=r.id, + username=r.user.first_name+' '+r.user.last_name, weightcategory=weightclass, adaptiveclass=adaptiveclass, race=race, boatclass=boatclass, boattype=boattype, sex=sex, - age = age, + age=age, entrycategory=initialcategory, referencespeed=referencespeed, ) record.save() - add_rower_race(r,race) + add_rower_race(r, race) else: errors.append("Unable to find a suitable start category") - return result,comments,errors,0 - else: # pragma: no cover + return result, comments, errors, 0 + else: # pragma: no cover errors.append("Couldn't find this entry") - return result,comments,errors,0 + return result, comments, errors, 0 records = VirtualRaceResult.objects.filter( userid=r.id, race=race, - workoutid = ws[0].id - ) + workoutid=ws[0].id + ) - if not record and not doregister: # pragma: no cover + if not record and not doregister: # pragma: no cover errors.append("Couldn't find this entry") - return result,comments,errors,0 + return result, comments, errors, 0 - #if ws[0].workouttype not in mytypes.otwtypes: + # if ws[0].workouttype not in mytypes.otwtypes: # errors.append('You have to submit a rowing on water workout') # return 0,comments,errors,0 - if ws[0].workouttype != record.boatclass: # pragma: no cover + if ws[0].workouttype != record.boatclass: # pragma: no cover ws[0].workouttype = record.boatclass ws[0].save() #errors.append('Your workout boat class is different than on your race registration') - #return 0,comments,errors,0 + # return 0,comments,errors,0 - if ws[0].boattype != record.boattype: # pragma: no cover - errors.append('Your workout boat type did not match the boat type you registered') - return 0,comments,errors,0 + if ws[0].boattype != record.boattype: # pragma: no cover + errors.append( + 'Your workout boat type did not match the boat type you registered') + return 0, comments, errors, 0 - if record.weightcategory == 'lwt' and ws[0].weightcategory != record.weightcategory: # pragma: no cover - errors.append('Your workout weight category did not match the weight category you registered') - return 0,comments, errors,0 + if record.weightcategory == 'lwt' and ws[0].weightcategory != record.weightcategory: # pragma: no cover + errors.append( + 'Your workout weight category did not match the weight category you registered') + return 0, comments, errors, 0 - if ws[0].adaptiveclass != record.adaptiveclass: # pragma: no cover - errors.append('Your workout adaptive classification did not match the registration') - return 0,comments, errors,0 + if ws[0].adaptiveclass != record.adaptiveclass: # pragma: no cover + errors.append( + 'Your workout adaptive classification did not match the registration') + return 0, comments, errors, 0 # start adding sessions - if ws[0].startdatetime>=startdatetime and ws[0].startdatetime<=enddatetime: + if ws[0].startdatetime >= startdatetime and ws[0].startdatetime <= enddatetime: # convert to gps row = rdata(ws[0].csvfilename) success = row.use_gpsdata() if success: row.write_csv(ws[0].csvfilename) - dataprep.update_strokedata(ws[0].id,row.df) + dataprep.update_strokedata(ws[0].id, row.df) ws[0].impeller = False ws[0].plannedsession = race ws[0].save() result += 1 - else: # pragma: no cover + else: # pragma: no cover errors.append('Workout %i did not match the race window' % ws[0].id) - return result,comments,errors,0 + return result, comments, errors, 0 - if result>0: - for otherrecord in records: # pragma: no cover + if result > 0: + for otherrecord in records: # pragma: no cover otherrecord.workoutid = None otherrecord.coursecompleted = False otherrecord.save() - if ws[0].privacy == 'private': # pragma: no cover + if ws[0].privacy == 'private': # pragma: no cover ws[0].privacy = 'visible' ws[0].save() - comments.append('Workouts submitted to virtual events have to be public. We have changed the workout to a public workout.') + comments.append( + 'Workouts submitted to virtual events have to be public. We have changed the workout to a public workout.') - job = myqueue(queue,handle_check_race_course,ws[0].csvfilename, - ws[0].id,race.course.id,record.id, - ws[0].user.user.email,ws[0].user.user.first_name, + job = myqueue(queue, handle_check_race_course, ws[0].csvfilename, + ws[0].id, race.course.id, record.id, + ws[0].user.user.email, ws[0].user.user.first_name, splitsecond=splitsecond, - referencespeed=record.referencespeed,coursedistance=race.course.distance + referencespeed=record.referencespeed, coursedistance=race.course.distance ) - comments.append('We are now checking adherence to the race course. This may take a few minutes to complete') + comments.append( + 'We are now checking adherence to the race course. This may take a few minutes to complete') + + add_workouts_plannedsession(ws, race, r) + + return result, comments, errors, job.id - - add_workouts_plannedsession(ws,race,r) - - - return result,comments,errors,job.id - -def delete_race_result(workout,race): # pragma: no cover - results = VirtualRaceResult.objects.filter(workoutid=workout.id,race=race) +def delete_race_result(workout, race): # pragma: no cover + results = VirtualRaceResult.objects.filter(workoutid=workout.id, race=race) for r in results: r.workoutid = None r.save() diff --git a/rowers/plots.py b/rowers/plots.py index f4977b57..b241981c 100644 --- a/rowers/plots.py +++ b/rowers/plots.py @@ -3,7 +3,7 @@ from __future__ import division from __future__ import print_function from __future__ import unicode_literals -from matplotlib.ticker import MultipleLocator,FuncFormatter,NullFormatter +from matplotlib.ticker import MultipleLocator, FuncFormatter, NullFormatter import matplotlib.pyplot as plt import numpy as np @@ -11,7 +11,7 @@ from rowers.rows import format_pace_tick, format_pace, format_time, format_time_ # Formatting the distance tick marks -#def format_dist_tick(x,pos=None): +# def format_dist_tick(x,pos=None): # km = x/1000. # template='%6.3f' # return template % (km) @@ -22,21 +22,18 @@ from rowers.rows import format_pace_tick, format_pace, format_time, format_time_ # you can set the slowest paces to fall off the axis. # Useful for OTW rowing where you sometimes stops and pace runs out of # the boundaries -def y_axis_range(ydata,miny=0,padding=.1,ultimate=[-1e9,1e9]): +def y_axis_range(ydata, miny=0, padding=.1, ultimate=[-1e9, 1e9]): # ydata must by a numpy array ymin = np.ma.masked_invalid(ydata).min() ymax = np.ma.masked_invalid(ydata).max() - yrange = ymax-ymin yrangemin = ymin yrangemax = ymax - - - if (yrange == 0): # pragma: no cover + if (yrange == 0): # pragma: no cover if ymin == 0: yrangemin = -padding else: @@ -49,37 +46,37 @@ def y_axis_range(ydata,miny=0,padding=.1,ultimate=[-1e9,1e9]): yrangemin = ymin-padding*yrange yrangemax = ymax+padding*yrange - if (yrangemin < ultimate[0]): # pragma: no cover + if (yrangemin < ultimate[0]): # pragma: no cover yrangemin = ultimate[0] if (yrangemax > ultimate[1]): yrangemax = ultimate[1] - - - return [yrangemin,yrangemax] + return [yrangemin, yrangemax] # Make a plot (this one is only used for testing) -def mkplot(row,title): + + +def mkplot(row, title): df = row.df - t = df.loc[:,' ElapsedTime (sec)'].values - p = df.loc[:,' Stroke500mPace (sec/500m)'].values - hr = df.loc[:,' HRCur (bpm)'].values - end_time = int(df.loc[:,'TimeStamp (sec)'].iloc[df.shape[0]-1]) + t = df.loc[:, ' ElapsedTime (sec)'].values + p = df.loc[:, ' Stroke500mPace (sec/500m)'].values + hr = df.loc[:, ' HRCur (bpm)'].values + end_time = int(df.loc[:, 'TimeStamp (sec)'].iloc[df.shape[0]-1]) - fig, ax1 = plt.subplots(figsize=(5,4)) + fig, ax1 = plt.subplots(figsize=(5, 4)) - ax1.plot(t,p,'b-') + ax1.plot(t, p, 'b-') ax1.set_xlabel('Time (h:m)') ax1.set_ylabel('(sec/500)') - yrange = y_axis_range(df.loc[:,' Stroke500mPace (sec/500m)'], - ultimate = [85,190]) - plt.axis([0,end_time,yrange[1],yrange[0]]) + yrange = y_axis_range(df.loc[:, ' Stroke500mPace (sec/500m)'], + ultimate=[85, 190]) + plt.axis([0, end_time, yrange[1], yrange[0]]) - ax1.set_xticks(range(1000,end_time,1000)) - ax1.set_yticks(range(185,90,-10)) + ax1.set_xticks(range(1000, end_time, 1000)) + ax1.set_yticks(range(185, 90, -10)) ax1.set_title(title) plt.grid(True) majorFormatter = FuncFormatter(format_pace_tick) @@ -92,8 +89,8 @@ def mkplot(row,title): tl.set_color('b') ax2 = ax1.twinx() - ax2.plot(t,hr,'r-') - ax2.set_ylabel('Heart Rate',color='r') + ax2.plot(t, hr, 'r-') + ax2.set_ylabel('Heart Rate', color='r') majorTimeFormatter = FuncFormatter(format_time_tick) majorLocator = (15*60) ax2.xaxis.set_major_formatter(majorTimeFormatter) diff --git a/rowers/polarstuff.py b/rowers/polarstuff.py index f7b040ee..997682f0 100644 --- a/rowers/polarstuff.py +++ b/rowers/polarstuff.py @@ -1,4 +1,20 @@ from __future__ import absolute_import +from rowers.rower_rules import ispromember +from stravalib.exc import ActivityUploadFailed, TimeoutExceeded +from rowers.models import Rower, Workout +import rowers.mytypes as mytypes +from rowers.utils import NoTokenError, custom_exception_handler +from rowers.utils import dologging +from rowsandall_app.settings import ( + POLAR_CLIENT_ID, POLAR_REDIRECT_URI, POLAR_CLIENT_SECRET, UPLOAD_SERVICE_URL +) +import stravalib +from io import StringIO +from rowers.dataprep import columndict +import rowers.dataprep as dataprep +from rowers.tasks import handle_request_post +import pandas as pd +from rowingdata import rowingdata from __future__ import division from __future__ import print_function from __future__ import unicode_literals @@ -18,8 +34,9 @@ import numpy as np from dateutil import parser import time import math -from math import sin,cos,atan2,sqrt -import os,sys +from math import sin, cos, atan2, sqrt +import os +import sys import gzip import base64 import yaml @@ -28,7 +45,7 @@ from requests import ConnectionError from json.decoder import JSONDecodeError # Django -from django.http import HttpResponseRedirect, HttpResponse,JsonResponse +from django.http import HttpResponseRedirect, HttpResponse, JsonResponse from django.conf import settings from django.contrib.auth import authenticate, login, logout from django.contrib.auth.models import User @@ -44,91 +61,73 @@ queuehigh = django_rq.get_queue('high') # Project # from .models import Profile -from rowingdata import rowingdata -import pandas as pd -from rowers.models import Rower,Workout -from rowers.tasks import handle_request_post -import rowers.dataprep as dataprep -from rowers.dataprep import columndict - - -from io import StringIO - -import stravalib -from stravalib.exc import ActivityUploadFailed,TimeoutExceeded - -from rowsandall_app.settings import ( - POLAR_CLIENT_ID, POLAR_REDIRECT_URI, POLAR_CLIENT_SECRET,UPLOAD_SERVICE_URL - ) - -from rowers.utils import dologging #baseurl = 'https://polaraccesslink.com/v3-example' baseurl = 'https://polaraccesslink.com/v3' -from rowers.utils import NoTokenError, custom_exception_handler -import rowers.mytypes as mytypes - # Exchange access code for long-lived access token + def get_token(code): post_data = {"grant_type": "authorization_code", "code": code, - #"redirect_uri": POLAR_REDIRECT_URI, - } + # "redirect_uri": POLAR_REDIRECT_URI, + } auth_string = '{id}:{secret}'.format( - id= POLAR_CLIENT_ID, + id=POLAR_CLIENT_ID, secret=POLAR_CLIENT_SECRET - ) + ) try: - headers = { 'Authorization': 'Basic %s' % base64.b64encode(auth_string) } + headers = {'Authorization': 'Basic %s' % base64.b64encode(auth_string)} except TypeError: - headers = { 'Authorization': 'Basic %s' % base64.b64encode( - bytes(auth_string,'utf-8')).decode('utf-8') } + headers = {'Authorization': 'Basic %s' % base64.b64encode( + bytes(auth_string, 'utf-8')).decode('utf-8')} - dologging('polar.log','Getting token') - dologging('polar.log',post_data) - dologging('polar.log',auth_string) + dologging('polar.log', 'Getting token') + dologging('polar.log', post_data) + dologging('polar.log', auth_string) response = requests.post("https://polarremote.com/v2/oauth2/token", data=post_data, headers=headers) - if response.status_code != 200: # pragma: no cover - dologging('polar.log','Getting token, got:') - dologging('polar.log',response.status_code) - dologging('polar.log',response.reason) - dologging('polar.log',response.text) + if response.status_code != 200: # pragma: no cover + dologging('polar.log', 'Getting token, got:') + dologging('polar.log', response.status_code) + dologging('polar.log', response.reason) + dologging('polar.log', response.text) try: token_json = response.json() thetoken = token_json['access_token'] expires_in = token_json['expires_in'] user_id = token_json['x_user_id'] - dologging('polar.log',response.status_code) + dologging('polar.log', response.status_code) try: - dologging('polar.log',response.text) + dologging('polar.log', response.text) except AttributeError: pass - dologging('polar.log',token_json) - except (KeyError,JSONDecodeError) as e: # pragma: no cover - dologging('polar.log',e) + dologging('polar.log', token_json) + except (KeyError, JSONDecodeError) as e: # pragma: no cover + dologging('polar.log', e) try: - dologging('polar.log',response.text) + dologging('polar.log', response.text) except AttributeError: pass thetoken = 0 expires_in = 0 user_id = 0 - return [thetoken,expires_in,user_id] + return [thetoken, expires_in, user_id] # Make authorization URL including random string -def make_authorization_url(): # pragma: no cover + + +def make_authorization_url(): # pragma: no cover # Generate a random string for the state parameter # Save it for use later to prevent xsrf attacks state = str(uuid4()) @@ -136,72 +135,73 @@ def make_authorization_url(): # pragma: no cover params = {"client_id": POLAR_CLIENT_ID, "response_type": "code", "redirect_uri": POLAR_REDIRECT_URI, - "scope":"write"} + "scope": "write"} import urllib - url = "https://flow.polar.com/oauth2/authorization" +urllib.parse.urlencode(params) - + url = "https://flow.polar.com/oauth2/authorization" + \ + urllib.parse.urlencode(params) return HttpResponseRedirect(url) -def revoke_access(user): # pragma: no cover + +def revoke_access(user): # pragma: no cover headers = { - 'Authorization': 'Bearer {token}'.format(token=user.rower.polartoken) + 'Authorization': 'Bearer {token}'.format(token=user.rower.polartoken) } response = requests.delete('https://www.polaraccesslink.com/v3/users/{userid}'.format( - userid = user.rower.polaruserid - ), headers = headers) + userid=user.rower.polaruserid + ), headers=headers) - dologging('polar.log',response.text) - dologging('polar.log',response.reason) + dologging('polar.log', response.text) + dologging('polar.log', response.reason) return 1 + def get_polar_notifications(): url = baseurl+'/notifications' state = str(uuid4()) auth_string = '{id}:{secret}'.format( - id= POLAR_CLIENT_ID, + id=POLAR_CLIENT_ID, secret=POLAR_CLIENT_SECRET - ) + ) try: - headers = { 'Authorization': 'Basic %s' % base64.b64encode(auth_string) } + headers = {'Authorization': 'Basic %s' % base64.b64encode(auth_string)} except TypeError: - headers = { 'Authorization': 'Basic %s' % base64.b64encode( - bytes(auth_string,'utf-8')).decode('utf-8') } + headers = {'Authorization': 'Basic %s' % base64.b64encode( + bytes(auth_string, 'utf-8')).decode('utf-8')} try: response = requests.get(url, headers=headers) - except ConnectionError: # pragma: no cover + except ConnectionError: # pragma: no cover response = { - 'status_code':400, - } + 'status_code': 400, + } available_data = [] try: if response.status_code == 200: available_data = response.json()['available-user-data'] - dologging('polar.log',available_data) - else: # pragma: no cover - dologging('polar.log',response.status_code) - dologging('polar.log',response.text) - except AttributeError: # pragma: no cover + dologging('polar.log', available_data) + else: # pragma: no cover + dologging('polar.log', response.status_code) + dologging('polar.log', response.text) + except AttributeError: # pragma: no cover try: - dologging('polar.log',response.text) + dologging('polar.log', response.text) except AttributeError: pass pass return available_data -from rowers.rower_rules import ispromember -def get_all_new_workouts(available_data,testing=False): +def get_all_new_workouts(available_data, testing=False): for record in available_data: - dologging('polar.log',str(record)) - if testing: # pragma: no cover + dologging('polar.log', str(record)) + if testing: # pragma: no cover print(record) if record['data-type'] == 'EXERCISE': try: @@ -209,10 +209,10 @@ def get_all_new_workouts(available_data,testing=False): u = r.user if r.polar_auto_import and ispromember(u): exercise_list = get_polar_workouts(u) - dologging('polar.log',exercise_list) - if testing: # pragma: no cover + dologging('polar.log', exercise_list) + if testing: # pragma: no cover print(exercise_list) - except Rower.DoesNotExist: # pragma: no cover + except Rower.DoesNotExist: # pragma: no cover pass return 1 @@ -225,97 +225,96 @@ def get_polar_workouts(user): if (r.polartoken == '') or (r.polartoken is None): s = "Token doesn't exist. Need to authorize" - return custom_exception_handler(401,s) - elif (timezone.now()>r.polartokenexpirydate): # pragma: no cover + return custom_exception_handler(401, s) + elif (timezone.now() > r.polartokenexpirydate): # pragma: no cover s = "Token expired. Needs to refresh" - dologging('polar.log',s) - return custom_exception_handler(401,s) + dologging('polar.log', s) + return custom_exception_handler(401, s) else: authorizationstring = str('Bearer ' + r.polartoken) - headers = {'Authorization':authorizationstring, + headers = {'Authorization': authorizationstring, 'Accept': 'application/json'} headers2 = { - 'Authorization':authorizationstring, + 'Authorization': authorizationstring, } url = baseurl+'/users/{userid}/exercise-transactions'.format( - userid = r.polaruserid - ) - + userid=r.polaruserid + ) response = requests.post(url, headers=headers) - dologging('polar.log',url) - dologging('polar.log',authorizationstring) - dologging('polar.log',str(response.status_code)) - + dologging('polar.log', url) + dologging('polar.log', authorizationstring) + dologging('polar.log', str(response.status_code)) if response.status_code == 201: transactionid = response.json()['transaction-id'] url = baseurl+'/users/{userid}/exercise-transactions/{transactionid}'.format( - transactionid = transactionid, - userid = r.polaruserid - ) + transactionid=transactionid, + userid=r.polaruserid + ) - - dologging('polar.log',url) + dologging('polar.log', url) response = requests.get(url, headers=headers) if response.status_code == 200: exerciseurls = response.json()['exercises'] - dologging('polar.log',exerciseurls) + dologging('polar.log', exerciseurls) for exerciseurl in exerciseurls: - response = requests.get(exerciseurl,headers=headers) + response = requests.get(exerciseurl, headers=headers) if response.status_code == 200: exercise_dict = response.json() tcxuri = exerciseurl+'/tcx' - response = requests.get(tcxuri,headers=headers2) + response = requests.get(tcxuri, headers=headers2) if response.status_code == 200: filename = 'media/mailbox_attachments/{code}_{id}.tcx'.format( - id = exercise_dict['id'], - code = uuid4().hex[:16] - ) - dologging('polar.log',filename) + id=exercise_dict['id'], + code=uuid4().hex[:16] + ) + dologging('polar.log', filename) - with open(filename,'wb') as fop: + with open(filename, 'wb') as fop: fop.write(response.content) workouttype = 'other' try: - workouttype = mytypes.polaraccesslink_sports[exercise_dict['detailed-sport-info']] - except KeyError: # pragma: no cover - dologging('polar.log',exercise_dict['detailed-sport-info']) - dologging('polar.log',workouttype) + workouttype = mytypes.polaraccesslink_sports[ + exercise_dict['detailed-sport-info']] + except KeyError: # pragma: no cover + dologging( + 'polar.log', exercise_dict['detailed-sport-info']) + dologging('polar.log', workouttype) try: - workouttype = mytypes.polarmappinginv[exercise_dict['sport'].lower()] + workouttype = mytypes.polarmappinginv[exercise_dict['sport'].lower( + )] except KeyError: - dologging('polar.log',workouttype) + dologging('polar.log', workouttype) pass - dologging('polar.log',workouttype) - + dologging('polar.log', workouttype) # post file to upload api # TODO: add workouttype uploadoptions = { - 'title':'', - 'workouttype':workouttype, - 'boattype':'1x', - 'user':user.id, - 'secret':settings.UPLOAD_SERVICE_SECRET, - 'file':filename, + 'title': '', + 'workouttype': workouttype, + 'boattype': '1x', + 'user': user.id, + 'secret': settings.UPLOAD_SERVICE_SECRET, + 'file': filename, 'title': '', } #session = requests.session() #newHeaders = {'Content-type': 'application/json', 'Accept': 'text/plain'} - #session.headers.update(newHeaders) + # session.headers.update(newHeaders) url = settings.UPLOAD_SERVICE_URL - dologging('polar.log',uploadoptions) - dologging('polar.log',url) + dologging('polar.log', uploadoptions) + dologging('polar.log', url) #response = session.post(url,json=uploadoptions) job = myqueue( queuehigh, @@ -324,10 +323,10 @@ def get_polar_workouts(user): uploadoptions ) - dologging('polar.log',response.status_code) - if response.status_code != 200: # pragma: no cover + dologging('polar.log', response.status_code) + if response.status_code != 200: # pragma: no cover try: - dologging('polar.log',response.text) + dologging('polar.log', response.text) except: pass try: @@ -336,69 +335,70 @@ def get_polar_workouts(user): pass exercise_dict['filename'] = filename - else: # pragma: no cover + else: # pragma: no cover exercise_dict['filename'] = '' exercise_list.append(exercise_dict) - dologging('polar.log',str(exercise_dict)) + dologging('polar.log', str(exercise_dict)) # commit transaction url = baseurl+'/users/{userid}/exercise-transactions/{transactionid}'.format( - transactionid = transactionid, - userid = r.polaruserid - ) + transactionid=transactionid, + userid=r.polaruserid + ) requests.put(url, headers=headers) - dologging('polar.log','Committed transation at {url}'.format(url=url)) + dologging( + 'polar.log', 'Committed transation at {url}'.format(url=url)) return exercise_list + def register_user(user, token): r = Rower.objects.get(user=user) - if (r.polartoken == '') or (r.polartoken is None): # pragma: no cover + if (r.polartoken == '') or (r.polartoken is None): # pragma: no cover s = "Token doesn't exist. Need to authorize" - return custom_exception_handler(401,s) - elif (timezone.now()>r.polartokenexpirydate): # pragma: no cover + return custom_exception_handler(401, s) + elif (timezone.now() > r.polartokenexpirydate): # pragma: no cover s = "Token expired. Needs to refresh" - return custom_exception_handler(401,s) + return custom_exception_handler(401, s) authorizationstring = 'Bearer {token}'.format(token=token) headers = { 'Content-Type': 'application/xml', - 'Authorization':authorizationstring, + 'Authorization': authorizationstring, 'Accept': 'application/json' } payload = { - "member-id": encoder.encode_hex(user.id) + "member-id": encoder.encode_hex(user.id) } headers = { - 'Content-Type': 'application/json', - 'Accept': 'application/json', - 'Authorization': 'Bearer {token}'.format(token=token) + 'Content-Type': 'application/json', + 'Accept': 'application/json', + 'Authorization': 'Bearer {token}'.format(token=token) } - - dologging('polar.log','Registering user') + dologging('polar.log', 'Registering user') response = requests.post( 'https://www.polaraccesslink.com/v3/users', - json = payload, - headers = headers - ) + json=payload, + headers=headers + ) #url = baseurl+'/users' #response = requests.post(url,params=params,headers=headers) - if response.status_code not in [200,201]: # pragma: no cover - #dologging('polar.log',url) - dologging('polar.log',headers) - dologging('polar.log',payload) - dologging('polar.log',response.status_code) - dologging('polar.log',response.content) + if response.status_code not in [200, 201]: # pragma: no cover + # dologging('polar.log',url) + dologging('polar.log', headers) + dologging('polar.log', payload) + dologging('polar.log', response.status_code) + dologging('polar.log', response.content) try: - dologging('polar.log',response.reason) - dologging('polar.log',response.text) + dologging('polar.log', response.reason) + dologging('polar.log', response.text) except KeyError: pass @@ -408,35 +408,34 @@ def register_user(user, token): return polar_user_data -def get_polar_user_info(user,physical=False): # pragma: no cover + +def get_polar_user_info(user, physical=False): # pragma: no cover r = Rower.objects.get(user=user) if (r.polartoken == '') or (r.polartoken is None): s = "Token doesn't exist. Need to authorize" - return custom_exception_handler(401,s) - elif (timezone.now()>r.polartokenexpirydate): + return custom_exception_handler(401, s) + elif (timezone.now() > r.polartokenexpirydate): s = "Token expired. Needs to refresh" - return custom_exception_handler(401,s) + return custom_exception_handler(401, s) authorizationstring = str('Bearer ' + r.polartoken) headers = { - 'Authorization':authorizationstring, + 'Authorization': authorizationstring, 'Accept': 'application/json' } - params = { 'user-id': r.polaruserid - } + } if not physical: url = baseurl+'/users/{userid}'.format( - userid = r.polaruserid + userid=r.polaruserid ) else: url = 'https://www.polaraccesslink.com/v3/users/{userid}/physical-information-transactions/'.format( - userid = r.polaruserid - ) - + userid=r.polaruserid + ) if physical: response = requests.post(url, headers=headers) @@ -446,70 +445,69 @@ def get_polar_user_info(user,physical=False): # pragma: no cover return response -def get_polar_workout(user,id,transactionid): +def get_polar_workout(user, id, transactionid): r = Rower.objects.get(user=user) - if (r.polartoken == '') or (r.polartoken is None): # pragma: no cover + if (r.polartoken == '') or (r.polartoken is None): # pragma: no cover s = "Token doesn't exist. Need to authorize" - return custom_exception_handler(401,s) - elif (timezone.now()>r.polartokenexpirydate): # pragma: no cover + return custom_exception_handler(401, s) + elif (timezone.now() > r.polartokenexpirydate): # pragma: no cover s = "Token expired. Needs to refresh" - return custom_exception_handler(401,s) + return custom_exception_handler(401, s) else: authorizationstring = str('Bearer ' + r.polartoken) headers = { - 'Authorization':authorizationstring, + 'Authorization': authorizationstring, 'Accept': 'application/json' } - url = baseurl+'/users/{userid}/exercise-transactions'.format( - userid = r.polaruserid - ) - + userid=r.polaruserid + ) response = requests.post(url, headers=headers) if response.status_code == 201: transactionid = response.json()['transaction-id'] url = baseurl+'/users/{userid}/exercise-transactions/{transactionid}'.format( - transactionid = transactionid, - userid = r.polaruserid - ) + transactionid=transactionid, + userid=r.polaruserid + ) response = requests.get(url, headers=headers) if response.status_code == 200: exerciseurls = response.json()['exercises'] for exerciseurl in exerciseurls: - response = requests.get(exerciseurl,headers=headers) + response = requests.get(exerciseurl, headers=headers) if response.status_code == 200: exercise_dict = response.json() thisid = exercise_dict['id'] if thisid == id: url = baseurl+'/users/{userid}/exercise-transactions/{transactionid}/exercises/{exerciseid}/tcx'.format( - userid = r.polaruserid, - transactionid = transactionid, - exerciseid = id - ) + userid=r.polaruserid, + transactionid=transactionid, + exerciseid=id + ) authorizationstring = str('Bearer ' + r.polartoken) headers2 = { - 'Authorization':authorizationstring, - } + 'Authorization': authorizationstring, + } - response = requests.get(url,headers = headers2) + response = requests.get(url, headers=headers2) if response.status_code == 200: result = response.content # commit transaction url = baseurl+'/users/{userid}/exercise-transactions/{transactionid}'.format( - transactionid = transactionid, - userid = r.polaruserid - ) - response = requests.put(url,headers=headers) - dologging('polar.log','Committing transaction on {url}'.format(url=url)) - else: # pragma: no cover + transactionid=transactionid, + userid=r.polaruserid + ) + response = requests.put(url, headers=headers) + dologging( + 'polar.log', 'Committing transaction on {url}'.format(url=url)) + else: # pragma: no cover result = None return result - return None # pragma: no cover + return None # pragma: no cover diff --git a/rowers/rower_rules.py b/rowers/rower_rules.py index f5f3a16d..be51fcc8 100644 --- a/rowers/rower_rules.py +++ b/rowers/rower_rules.py @@ -74,58 +74,68 @@ USER permissions """ # used in can_plan_user + + @rules.predicate def user_is_not_basic(user): if user.rower.rowerplan != 'basic': return True if user.rower.protrialexpires >= timezone.now().date(): - return True # pragma: no cover + return True # pragma: no cover return False + @rules.predicate def user_is_basic(user): return not user_is_not_basic(user) + @rules.predicate def can_start_trial(user): - if user.is_anonymous: # pragma: no cover + if user.is_anonymous: # pragma: no cover return False + return user.rower.protrialexpires == datetime.date(1970, 1, 1) - return user.rower.protrialexpires == datetime.date(1970,1,1) @rules.predicate def can_start_plantrial(user): - if user.is_anonymous: # pragma: no cover + if user.is_anonymous: # pragma: no cover return False - return user.rower.plantrialexpires == datetime.date(1970,1,1) + return user.rower.plantrialexpires == datetime.date(1970, 1, 1) + @rules.predicate -def is_staff(user): # pragma: no cover +def is_staff(user): # pragma: no cover return user.is_staff + @rules.predicate def is_coach(user): - return user.rower.rowerplan in ['coach','freecoach'] + return user.rower.rowerplan in ['coach', 'freecoach'] + @rules.predicate def is_not_freecoach(user): return user.rower.rowerplan != 'freecoach' + def is_paid_coach(user): return user.rower.rowerplan == 'coach' + @rules.predicate def is_planmember(user): try: r = user.rower - except AttributeError: # pragma: no cover + except AttributeError: # pragma: no cover return False - return r.rowerplan in ['coach','plan'] # freecoach? + return r.rowerplan in ['coach', 'plan'] # freecoach? + @rules.predicate def is_promember(user): @@ -134,7 +144,8 @@ def is_promember(user): except AttributeError: return False - return r.rowerplan in ['pro','coach','plan'] # freecoach? + return r.rowerplan in ['pro', 'coach', 'plan'] # freecoach? + @rules.predicate def is_protrial(user): @@ -147,10 +158,9 @@ def is_protrial(user): return r.protrialexpires >= timezone.now().date() if r.rowerplan == 'freecoach': if r.mycoachgroup is not None: - return len(r.mycoachgroup)>=4 - - return False # pragma: no cover + return len(r.mycoachgroup) >= 4 + return False # pragma: no cover ispromember = is_promember | is_protrial @@ -158,6 +168,7 @@ ispromember = is_promember | is_protrial can_have_teams = ispromember | is_coach + @rules.predicate def can_add_team(user): if is_coach(user): @@ -170,56 +181,63 @@ def can_add_team(user): return False + @rules.predicate def can_add_plan(user): return isplanmember(user) or is_coach(user) + @rules.predicate def can_add_workout(user): - if user.is_anonymous: # pragma: no cover + if user.is_anonymous: # pragma: no cover return False return user.rower.rowerplan != 'freecoach' + @rules.predicate def is_plantrial(user): try: r = user.rower - except AttributeError: # pragma: no cover + except AttributeError: # pragma: no cover return False - if r.rowerplan in ['basic','pro']: + if r.rowerplan in ['basic', 'pro']: return r.plantrialexpires >= timezone.now().date() if r.rowerplan == 'freecoach': if r.mycoachgroup is not None: - return len(r.mycoachgroup)>=4 + return len(r.mycoachgroup) >= 4 - return False # pragma: no cover + return False # pragma: no cover isplanmember = is_planmember | is_plantrial + @rules.predicate def can_add_session(user): return isplanmember(user) or is_coach(user) # User / Coach relationships (Rower object) + @rules.predicate def can_plan(user): - if user.is_anonymous: # pragma: no cover + if user.is_anonymous: # pragma: no cover return False - if user.rower.rowerplan in ['plan','coach']: + if user.rower.rowerplan in ['plan', 'coach']: return True - if user.rower.rowerplan in ['basic','pro']: + if user.rower.rowerplan in ['basic', 'pro']: return user.rower.plantrialexpires >= timezone.now().date() - if user.rower.rowerplan == 'freecoach': # pragma: no cover + if user.rower.rowerplan == 'freecoach': # pragma: no cover if user.rower.mycoachgroup is not None: - return len(user.rower.mycoachgroup)>=4 + return len(user.rower.mycoachgroup) >= 4 # checks if rower is coach of user (or is user himself) + + @rules.predicate -def is_coach_user(usercoach,userrower): +def is_coach_user(usercoach, userrower): if usercoach == userrower: return True @@ -242,8 +260,10 @@ def is_coach_user(usercoach,userrower): return False # checks if rower is coach of user (or is user himself) + + @rules.predicate -def is_anonymous_or_coach(usercoach,userrower): # pragma: no cover +def is_anonymous_or_coach(usercoach, userrower): # pragma: no cover if usercoach == userrower: return True @@ -269,12 +289,14 @@ def is_anonymous_or_coach(usercoach,userrower): # pragma: no cover return False # check if rower and user are members of the same team + + @rules.predicate -def is_rower_team_member(user,rower): - if user.rower == rower: # pragma: no cover +def is_rower_team_member(user, rower): + if user.rower == rower: # pragma: no cover return True - if is_coach_user(user,rower.user): + if is_coach_user(user, rower.user): return True teams = rower.team.all() @@ -288,20 +310,23 @@ def is_rower_team_member(user,rower): return False + @rules.predicate -def can_add_workout_member(user,rower): - if not user: # pragma: no cover +def can_add_workout_member(user, rower): + if not user: # pragma: no cover return False - if user.is_anonymous: # pragma: no cover + if user.is_anonymous: # pragma: no cover return False - if user == rower.user: # pragma: no cover + if user == rower.user: # pragma: no cover return True # only below tested - need test user == rower.user return is_coach(user) and user.rower in rower.get_coaches() # check if user can plan for the rower + + @rules.predicate -def can_plan_user(user,rower): +def can_plan_user(user, rower): # user must have planning permission if not can_plan(user): return False @@ -314,7 +339,7 @@ def can_plan_user(user,rower): # free coach, plan etc cannot plan for basic if not is_paid_coach(user) and user_is_not_basic(user): for t in teams: - if rower in t.rower.all(): # pragma: no cover + if rower in t.rower.all(): # pragma: no cover return True # paying coach can plan for all kinds of rowers @@ -325,11 +350,12 @@ def can_plan_user(user,rower): return False -rules.add_perm('rower.add_plan',can_plan_user) # replaces checkaccessplanuser -rules.add_perm('rower.is_coach',is_coach_user) # replaces checkaccessuser -rules.add_perm('rower.is_pro',ispromember) -rules.add_perm('rower.is_staff',is_staff) -rules.add_perm('rower.is_not_freecoach',is_not_freecoach) + +rules.add_perm('rower.add_plan', can_plan_user) # replaces checkaccessplanuser +rules.add_perm('rower.is_coach', is_coach_user) # replaces checkaccessuser +rules.add_perm('rower.is_pro', ispromember) +rules.add_perm('rower.is_staff', is_staff) +rules.add_perm('rower.is_not_freecoach', is_not_freecoach) # WORKOUT permissions @@ -360,51 +386,59 @@ WORKOUT permissions """ # check if user is owner or coach of owner of workout + + @rules.predicate -def is_workout_user(user,workout): +def is_workout_user(user, workout): if user.is_anonymous: return False try: r = user.rower - except AttributeError: # pragma: no cover + except AttributeError: # pragma: no cover return False if workout.user == r: return True - return is_coach_user(user,workout.user.user) + return is_coach_user(user, workout.user.user) # check if user is in same team as owner of workout + + @rules.predicate -def is_workout_team(user,workout): - if user.is_anonymous: # pragma: no cover +def is_workout_team(user, workout): + if user.is_anonymous: # pragma: no cover return False try: r = user.rower - except AttributeError: # pragma: no cover + except AttributeError: # pragma: no cover return False if workout.user == r: return True - return is_rower_team_member(user,workout.user) + return is_rower_team_member(user, workout.user) # check if user can see workout + + @rules.predicate -def can_view_workout(user,workout): +def can_view_workout(user, workout): if workout.privacy != 'private': return True - if user.is_anonymous: # pragma: no cover + if user.is_anonymous: # pragma: no cover return False - return user == workout.user.user # pragma: no cover + return user == workout.user.user # pragma: no cover + can_change_workout = is_workout_user -rules.add_perm('workout.change_workout',can_change_workout) # replaces checkworkoutuser -rules.add_perm('workout.view_workout',can_view_workout) # replaces checkworkoutuserview - +# replaces checkworkoutuser +rules.add_perm('workout.change_workout', can_change_workout) +# replaces checkworkoutuserview +rules.add_perm('workout.view_workout', can_view_workout) # checkviewworkouts @@ -448,151 +482,164 @@ rules.add_perm('workout.view_workout',can_view_workout) # replaces checkworkoutu # Training Target rules # untested can_view_target to can_delete_target + + @rules.predicate -def can_view_target(user,target): # pragma: no cover +def can_view_target(user, target): # pragma: no cover if user.is_anonymous: return False if user == target.manager.user: return True # a target's coach can view as well - if is_coach_user(user,target.manager.user): + if is_coach_user(user, target.manager.user): return True # the object can view as well if user.rower in target.rowers.all(): return True + @rules.predicate -def can_change_target(user,target): # pragma: no cover +def can_change_target(user, target): # pragma: no cover if user.is_anonymous: return False return user == target.manager.user + @rules.predicate -def can_delete_target(user,target): - if user.is_anonymous: # pragma: no cover +def can_delete_target(user, target): + if user.is_anonymous: # pragma: no cover return False return user == target.manager.user -rules.add_perm('target.view_target',can_view_target) -rules.add_perm('target.change_target',can_change_target) -rules.add_perm('target.delete_target',can_delete_target) + +rules.add_perm('target.view_target', can_view_target) +rules.add_perm('target.change_target', can_change_target) +rules.add_perm('target.delete_target', can_delete_target) + @rules.predicate -def can_view_plan(user,plan): - if user.is_anonymous: # pragma: no cover +def can_view_plan(user, plan): + if user.is_anonymous: # pragma: no cover return False if user == plan.manager.user: return True # a plan's coach can view as well # below untested - if is_coach_user(user,plan.manager.user): # pragma: no cover + if is_coach_user(user, plan.manager.user): # pragma: no cover return True # the object can view as well - if user.rower in plan.rowers.all(): # pragma: no cover + if user.rower in plan.rowers.all(): # pragma: no cover return True + @rules.predicate -def can_change_plan(user,plan): - if user.is_anonymous: # pragma: no cover +def can_change_plan(user, plan): + if user.is_anonymous: # pragma: no cover return False - return user == plan.manager.user # pragma: no cover + return user == plan.manager.user # pragma: no cover # below untested + + @rules.predicate -def can_delete_plan(user,plan): - if user.is_anonymous: # pragma: no cover +def can_delete_plan(user, plan): + if user.is_anonymous: # pragma: no cover return False return user == plan.manager.user - -rules.add_perm('plan.view_plan',can_view_plan) -rules.add_perm('plan.change_plan',can_change_plan) -rules.add_perm('plan.delete_plan',can_delete_plan) -rules.add_perm('plan.can_add_plan',can_add_plan) +rules.add_perm('plan.view_plan', can_view_plan) +rules.add_perm('plan.change_plan', can_change_plan) +rules.add_perm('plan.delete_plan', can_delete_plan) +rules.add_perm('plan.can_add_plan', can_add_plan) # untested -@rules.predicate -def can_view_cycle(user,cycle): # pragma: no cover - try: - return can_view_cycle(user,cycle.plan) - except AttributeError: - return can_view_plan(user,cycle.plan) - return False @rules.predicate -def can_change_cycle(user,cycle): # pragma: no cover +def can_view_cycle(user, cycle): # pragma: no cover try: - return can_change_cycle(user,cycle.plan) + return can_view_cycle(user, cycle.plan) except AttributeError: - return can_change_plan(user,cycle.plan) - - return False - -@rules.predicate -def can_delete_cycle(user,cycle): # pragma: no cover - try: - return can_delete_cycle(user,cycle.plan) - except AttributeError: - return can_delete_plan(user,cycle.plan) + return can_view_plan(user, cycle.plan) return False -rules.add_perm('cycle.view_cycle',can_view_cycle) -rules.add_perm('cycle.change_cycle',can_change_cycle) -rules.add_perm('cycle.delete_cycle',can_delete_cycle) +@rules.predicate +def can_change_cycle(user, cycle): # pragma: no cover + try: + return can_change_cycle(user, cycle.plan) + except AttributeError: + return can_change_plan(user, cycle.plan) + + return False + + +@rules.predicate +def can_delete_cycle(user, cycle): # pragma: no cover + try: + return can_delete_cycle(user, cycle.plan) + except AttributeError: + return can_delete_plan(user, cycle.plan) + + return False + + +rules.add_perm('cycle.view_cycle', can_view_cycle) +rules.add_perm('cycle.change_cycle', can_change_cycle) +rules.add_perm('cycle.delete_cycle', can_delete_cycle) # check if user has view access to session @rules.predicate -def can_view_session(user,session): - if session.sessiontype in ['race','indoorrace']: # pragma: no cover +def can_view_session(user, session): + if session.sessiontype in ['race', 'indoorrace']: # pragma: no cover return True - if user.is_anonymous: # pragma: no cover + if user.is_anonymous: # pragma: no cover return False # session manager can view session if user == session.manager: return True # if you're a rower in the session you can view it # below untested - if user.rower in session.rower.all(): # pragma: no cover + if user.rower in session.rower.all(): # pragma: no cover return True # coach users can view sessions created by their team members # below untested - if is_coach(user): # pragma: no cover + if is_coach(user): # pragma: no cover teams = user.rower.get_managed_teams() for t in teams: teamusers = [member.u for member in t.rower.all()] if session.manager in teamusers: return True - return False # pragma: no cover + return False # pragma: no cover + @rules.predicate -def can_change_session(user,session): - if user.is_anonymous: # pragma: no cover +def can_change_session(user, session): + if user.is_anonymous: # pragma: no cover return False # session part of a race should not be changed through the session interface - if session.sessiontype in ['race','indoorrace']: # pragma: no cover + if session.sessiontype in ['race', 'indoorrace']: # pragma: no cover return False if user == session.manager: return True - return False # pragma: no cover + return False # pragma: no cover @rules.predicate -def can_delete_session(user,session): # pragma: no cover +def can_delete_session(user, session): # pragma: no cover if user.is_anonymous: return False - if session.sessiontype in ['race','indoorrace']: + if session.sessiontype in ['race', 'indoorrace']: return False if user == session.manager: @@ -601,11 +648,10 @@ def can_delete_session(user,session): # pragma: no cover return False - -rules.add_perm('plannedsession.add_session',can_add_session) -rules.add_perm('plannedsession.view_session',can_view_session) -rules.add_perm('plannedsession.change_session',can_change_session) -rules.add_perm('plannedsession.delete_session',can_delete_session) +rules.add_perm('plannedsession.add_session', can_add_session) +rules.add_perm('plannedsession.view_session', can_view_session) +rules.add_perm('plannedsession.change_session', can_change_session) +rules.add_perm('plannedsession.delete_session', can_delete_session) # TEAM (group) permissions @@ -635,45 +681,55 @@ rules.add_perm('plannedsession.delete_session',can_delete_session) """ # check if user is manager of the team + + @rules.predicate -def is_team_manager(user,team): +def is_team_manager(user, team): return team.manager == user # check is user is member of team - untested + + @rules.predicate -def is_team_member(user,team): # pragma: no cover +def is_team_member(user, team): # pragma: no cover members = team.rower.all() return user in [member.user for member in members] # check if user can view team + + @rules.predicate -def can_view_team(user,team): +def can_view_team(user, team): # user based - below untested - if user.rower.rowerplan == 'basic' and team.manager.rower.rowerplan != 'coach': # pragma: no cover + if user.rower.rowerplan == 'basic' and team.manager.rower.rowerplan != 'coach': # pragma: no cover return is_plantrial(user) or is_protrial(user) # team is public if team.private == 'open': return True # team is private - below untested - return is_team_member(user,team) | is_team_manager(user,team) # pragma: no cover + return is_team_member(user, team) | is_team_manager(user, team) # pragma: no cover + @rules.predicate -def can_change_team(user,team): - return is_team_manager(user,team) +def can_change_team(user, team): + return is_team_manager(user, team) + @rules.predicate -def can_delete_team(user,team): - return is_team_manager(user,team) +def can_delete_team(user, team): + return is_team_manager(user, team) + @rules.predicate -def can_join_team(user,team): +def can_join_team(user, team): return is_paid_coach(team.manager) or ispromember(user) + # For Team functionality -rules.add_perm('teams.view_team',can_view_team) -rules.add_perm('teams.add_team',user_is_not_basic) -rules.add_perm('teams.change_team',can_change_team) -rules.add_perm('teams.delete_team',can_delete_team) +rules.add_perm('teams.view_team', can_view_team) +rules.add_perm('teams.add_team', user_is_not_basic) +rules.add_perm('teams.change_team', can_change_team) +rules.add_perm('teams.delete_team', can_delete_team) # RACING permissions @@ -685,43 +741,49 @@ rules.add_perm('teams.delete_team',can_delete_team) - RaceLogo """ + @rules.predicate -def can_change_course(user,course): - if user.is_anonymous: # pragma: no cover +def can_change_course(user, course): + if user.is_anonymous: # pragma: no cover return False return course.manager == user.rower # untested + + @rules.predicate -def can_delete_course(user,course): - if user.is_anonymous: # pragma: no cover +def can_delete_course(user, course): + if user.is_anonymous: # pragma: no cover return False return course.manager == user.rower + @rules.predicate -def can_delete_logo(user,logo): - if user.is_anonymous: # pragma: no cover +def can_delete_logo(user, logo): + if user.is_anonymous: # pragma: no cover return False - return logo.user == user # pragma: no cover + return logo.user == user # pragma: no cover + @rules.predicate -def can_change_race(user,race): - if user.is_anonymous: # pragma: no cover +def can_change_race(user, race): + if user.is_anonymous: # pragma: no cover return False return race.manager == user -# everybody can view or add a course -rules.add_perm('course.change_course',can_change_course) -rules.add_perm('course.delete_course',can_delete_course) -rules.add_perm('racelogo.delete_logo',can_delete_logo) +# everybody can view or add a course +rules.add_perm('course.change_course', can_change_course) +rules.add_perm('course.delete_course', can_delete_course) + +rules.add_perm('racelogo.delete_logo', can_delete_logo) # everybody can view a race -rules.add_perm('virtualevent.change_race',can_change_race) +rules.add_perm('virtualevent.change_race', can_change_race) # can races be deleted? diff --git a/rowers/rowing_workout_metrics_pb2.py b/rowers/rowing_workout_metrics_pb2.py index 53d92d53..93c47b37 100644 --- a/rowers/rowing_workout_metrics_pb2.py +++ b/rowers/rowing_workout_metrics_pb2.py @@ -2,159 +2,156 @@ # Generated by the protocol buffer compiler. DO NOT EDIT! # source: rowing-workout-metrics.proto -import sys -_b=sys.version_info[0]<3 and (lambda x:x) or (lambda x:x.encode('latin1')) -from google.protobuf import descriptor as _descriptor -from google.protobuf import message as _message -from google.protobuf import reflection as _reflection from google.protobuf import symbol_database as _symbol_database +from google.protobuf import reflection as _reflection +from google.protobuf import message as _message +from google.protobuf import descriptor as _descriptor +import sys +_b = sys.version_info[0] < 3 and ( + lambda x: x) or (lambda x: x.encode('latin1')) # @@protoc_insertion_point(imports) _sym_db = _symbol_database.Default() - - DESCRIPTOR = _descriptor.FileDescriptor( - name='rowing-workout-metrics.proto', - package='rowing_workout_metrics', - syntax='proto3', - serialized_options=None, - serialized_pb=_b('\n\x1crowing-workout-metrics.proto\x12\x16rowing_workout_metrics\"p\n\x15WorkoutMetricsRequest\x12\x10\n\x08\x66ilename\x18\x01 \x01(\t\x12\x0b\n\x03sex\x18\x02 \x01(\t\x12\x0b\n\x03\x66tp\x18\x03 \x01(\x01\x12\r\n\x05hrftp\x18\x04 \x01(\x01\x12\r\n\x05hrmax\x18\x05 \x01(\x01\x12\r\n\x05hrmin\x18\x06 \x01(\x01\"p\n\x16WorkoutMetricsResponse\x12\x0b\n\x03tss\x18\x01 \x01(\x01\x12\r\n\x05normp\x18\x02 \x01(\x01\x12\r\n\x05trimp\x18\x03 \x01(\x01\x12\r\n\x05hrtss\x18\x04 \x01(\x01\x12\r\n\x05normv\x18\x05 \x01(\x01\x12\r\n\x05normw\x18\x06 \x01(\x01\x32w\n\x07Metrics\x12l\n\x0b\x43\x61lcMetrics\x12-.rowing_workout_metrics.WorkoutMetricsRequest\x1a..rowing_workout_metrics.WorkoutMetricsResponseb\x06proto3') + name='rowing-workout-metrics.proto', + package='rowing_workout_metrics', + syntax='proto3', + serialized_options=None, + serialized_pb=_b('\n\x1crowing-workout-metrics.proto\x12\x16rowing_workout_metrics\"p\n\x15WorkoutMetricsRequest\x12\x10\n\x08\x66ilename\x18\x01 \x01(\t\x12\x0b\n\x03sex\x18\x02 \x01(\t\x12\x0b\n\x03\x66tp\x18\x03 \x01(\x01\x12\r\n\x05hrftp\x18\x04 \x01(\x01\x12\r\n\x05hrmax\x18\x05 \x01(\x01\x12\r\n\x05hrmin\x18\x06 \x01(\x01\"p\n\x16WorkoutMetricsResponse\x12\x0b\n\x03tss\x18\x01 \x01(\x01\x12\r\n\x05normp\x18\x02 \x01(\x01\x12\r\n\x05trimp\x18\x03 \x01(\x01\x12\r\n\x05hrtss\x18\x04 \x01(\x01\x12\r\n\x05normv\x18\x05 \x01(\x01\x12\r\n\x05normw\x18\x06 \x01(\x01\x32w\n\x07Metrics\x12l\n\x0b\x43\x61lcMetrics\x12-.rowing_workout_metrics.WorkoutMetricsRequest\x1a..rowing_workout_metrics.WorkoutMetricsResponseb\x06proto3') ) - - _WORKOUTMETRICSREQUEST = _descriptor.Descriptor( - name='WorkoutMetricsRequest', - full_name='rowing_workout_metrics.WorkoutMetricsRequest', - filename=None, - file=DESCRIPTOR, - containing_type=None, - fields=[ - _descriptor.FieldDescriptor( - name='filename', full_name='rowing_workout_metrics.WorkoutMetricsRequest.filename', index=0, - number=1, type=9, cpp_type=9, label=1, - has_default_value=False, default_value=_b("").decode('utf-8'), - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - serialized_options=None, file=DESCRIPTOR), - _descriptor.FieldDescriptor( - name='sex', full_name='rowing_workout_metrics.WorkoutMetricsRequest.sex', index=1, - number=2, type=9, cpp_type=9, label=1, - has_default_value=False, default_value=_b("").decode('utf-8'), - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - serialized_options=None, file=DESCRIPTOR), - _descriptor.FieldDescriptor( - name='ftp', full_name='rowing_workout_metrics.WorkoutMetricsRequest.ftp', index=2, - number=3, type=1, cpp_type=5, label=1, - has_default_value=False, default_value=float(0), - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - serialized_options=None, file=DESCRIPTOR), - _descriptor.FieldDescriptor( - name='hrftp', full_name='rowing_workout_metrics.WorkoutMetricsRequest.hrftp', index=3, - number=4, type=1, cpp_type=5, label=1, - has_default_value=False, default_value=float(0), - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - serialized_options=None, file=DESCRIPTOR), - _descriptor.FieldDescriptor( - name='hrmax', full_name='rowing_workout_metrics.WorkoutMetricsRequest.hrmax', index=4, - number=5, type=1, cpp_type=5, label=1, - has_default_value=False, default_value=float(0), - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - serialized_options=None, file=DESCRIPTOR), - _descriptor.FieldDescriptor( - name='hrmin', full_name='rowing_workout_metrics.WorkoutMetricsRequest.hrmin', index=5, - number=6, type=1, cpp_type=5, label=1, - has_default_value=False, default_value=float(0), - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - serialized_options=None, file=DESCRIPTOR), - ], - extensions=[ - ], - nested_types=[], - enum_types=[ - ], - serialized_options=None, - is_extendable=False, - syntax='proto3', - extension_ranges=[], - oneofs=[ - ], - serialized_start=56, - serialized_end=168, + name='WorkoutMetricsRequest', + full_name='rowing_workout_metrics.WorkoutMetricsRequest', + filename=None, + file=DESCRIPTOR, + containing_type=None, + fields=[ + _descriptor.FieldDescriptor( + name='filename', full_name='rowing_workout_metrics.WorkoutMetricsRequest.filename', index=0, + number=1, type=9, cpp_type=9, label=1, + has_default_value=False, default_value=_b("").decode('utf-8'), + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, file=DESCRIPTOR), + _descriptor.FieldDescriptor( + name='sex', full_name='rowing_workout_metrics.WorkoutMetricsRequest.sex', index=1, + number=2, type=9, cpp_type=9, label=1, + has_default_value=False, default_value=_b("").decode('utf-8'), + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, file=DESCRIPTOR), + _descriptor.FieldDescriptor( + name='ftp', full_name='rowing_workout_metrics.WorkoutMetricsRequest.ftp', index=2, + number=3, type=1, cpp_type=5, label=1, + has_default_value=False, default_value=float(0), + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, file=DESCRIPTOR), + _descriptor.FieldDescriptor( + name='hrftp', full_name='rowing_workout_metrics.WorkoutMetricsRequest.hrftp', index=3, + number=4, type=1, cpp_type=5, label=1, + has_default_value=False, default_value=float(0), + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, file=DESCRIPTOR), + _descriptor.FieldDescriptor( + name='hrmax', full_name='rowing_workout_metrics.WorkoutMetricsRequest.hrmax', index=4, + number=5, type=1, cpp_type=5, label=1, + has_default_value=False, default_value=float(0), + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, file=DESCRIPTOR), + _descriptor.FieldDescriptor( + name='hrmin', full_name='rowing_workout_metrics.WorkoutMetricsRequest.hrmin', index=5, + number=6, type=1, cpp_type=5, label=1, + has_default_value=False, default_value=float(0), + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, file=DESCRIPTOR), + ], + extensions=[ + ], + nested_types=[], + enum_types=[ + ], + serialized_options=None, + is_extendable=False, + syntax='proto3', + extension_ranges=[], + oneofs=[ + ], + serialized_start=56, + serialized_end=168, ) _WORKOUTMETRICSRESPONSE = _descriptor.Descriptor( - name='WorkoutMetricsResponse', - full_name='rowing_workout_metrics.WorkoutMetricsResponse', - filename=None, - file=DESCRIPTOR, - containing_type=None, - fields=[ - _descriptor.FieldDescriptor( - name='tss', full_name='rowing_workout_metrics.WorkoutMetricsResponse.tss', index=0, - number=1, type=1, cpp_type=5, label=1, - has_default_value=False, default_value=float(0), - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - serialized_options=None, file=DESCRIPTOR), - _descriptor.FieldDescriptor( - name='normp', full_name='rowing_workout_metrics.WorkoutMetricsResponse.normp', index=1, - number=2, type=1, cpp_type=5, label=1, - has_default_value=False, default_value=float(0), - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - serialized_options=None, file=DESCRIPTOR), - _descriptor.FieldDescriptor( - name='trimp', full_name='rowing_workout_metrics.WorkoutMetricsResponse.trimp', index=2, - number=3, type=1, cpp_type=5, label=1, - has_default_value=False, default_value=float(0), - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - serialized_options=None, file=DESCRIPTOR), - _descriptor.FieldDescriptor( - name='hrtss', full_name='rowing_workout_metrics.WorkoutMetricsResponse.hrtss', index=3, - number=4, type=1, cpp_type=5, label=1, - has_default_value=False, default_value=float(0), - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - serialized_options=None, file=DESCRIPTOR), - _descriptor.FieldDescriptor( - name='normv', full_name='rowing_workout_metrics.WorkoutMetricsResponse.normv', index=4, - number=5, type=1, cpp_type=5, label=1, - has_default_value=False, default_value=float(0), - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - serialized_options=None, file=DESCRIPTOR), - _descriptor.FieldDescriptor( - name='normw', full_name='rowing_workout_metrics.WorkoutMetricsResponse.normw', index=5, - number=6, type=1, cpp_type=5, label=1, - has_default_value=False, default_value=float(0), - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - serialized_options=None, file=DESCRIPTOR), - ], - extensions=[ - ], - nested_types=[], - enum_types=[ - ], - serialized_options=None, - is_extendable=False, - syntax='proto3', - extension_ranges=[], - oneofs=[ - ], - serialized_start=170, - serialized_end=282, + name='WorkoutMetricsResponse', + full_name='rowing_workout_metrics.WorkoutMetricsResponse', + filename=None, + file=DESCRIPTOR, + containing_type=None, + fields=[ + _descriptor.FieldDescriptor( + name='tss', full_name='rowing_workout_metrics.WorkoutMetricsResponse.tss', index=0, + number=1, type=1, cpp_type=5, label=1, + has_default_value=False, default_value=float(0), + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, file=DESCRIPTOR), + _descriptor.FieldDescriptor( + name='normp', full_name='rowing_workout_metrics.WorkoutMetricsResponse.normp', index=1, + number=2, type=1, cpp_type=5, label=1, + has_default_value=False, default_value=float(0), + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, file=DESCRIPTOR), + _descriptor.FieldDescriptor( + name='trimp', full_name='rowing_workout_metrics.WorkoutMetricsResponse.trimp', index=2, + number=3, type=1, cpp_type=5, label=1, + has_default_value=False, default_value=float(0), + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, file=DESCRIPTOR), + _descriptor.FieldDescriptor( + name='hrtss', full_name='rowing_workout_metrics.WorkoutMetricsResponse.hrtss', index=3, + number=4, type=1, cpp_type=5, label=1, + has_default_value=False, default_value=float(0), + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, file=DESCRIPTOR), + _descriptor.FieldDescriptor( + name='normv', full_name='rowing_workout_metrics.WorkoutMetricsResponse.normv', index=4, + number=5, type=1, cpp_type=5, label=1, + has_default_value=False, default_value=float(0), + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, file=DESCRIPTOR), + _descriptor.FieldDescriptor( + name='normw', full_name='rowing_workout_metrics.WorkoutMetricsResponse.normw', index=5, + number=6, type=1, cpp_type=5, label=1, + has_default_value=False, default_value=float(0), + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, file=DESCRIPTOR), + ], + extensions=[ + ], + nested_types=[], + enum_types=[ + ], + serialized_options=None, + is_extendable=False, + syntax='proto3', + extension_ranges=[], + oneofs=[ + ], + serialized_start=170, + serialized_end=282, ) DESCRIPTOR.message_types_by_name['WorkoutMetricsRequest'] = _WORKOUTMETRICSREQUEST @@ -162,40 +159,39 @@ DESCRIPTOR.message_types_by_name['WorkoutMetricsResponse'] = _WORKOUTMETRICSRESP _sym_db.RegisterFileDescriptor(DESCRIPTOR) WorkoutMetricsRequest = _reflection.GeneratedProtocolMessageType('WorkoutMetricsRequest', (_message.Message,), { - 'DESCRIPTOR' : _WORKOUTMETRICSREQUEST, - '__module__' : 'rowing_workout_metrics_pb2' - # @@protoc_insertion_point(class_scope:rowing_workout_metrics.WorkoutMetricsRequest) - }) + 'DESCRIPTOR': _WORKOUTMETRICSREQUEST, + '__module__': 'rowing_workout_metrics_pb2' + # @@protoc_insertion_point(class_scope:rowing_workout_metrics.WorkoutMetricsRequest) +}) _sym_db.RegisterMessage(WorkoutMetricsRequest) WorkoutMetricsResponse = _reflection.GeneratedProtocolMessageType('WorkoutMetricsResponse', (_message.Message,), { - 'DESCRIPTOR' : _WORKOUTMETRICSRESPONSE, - '__module__' : 'rowing_workout_metrics_pb2' - # @@protoc_insertion_point(class_scope:rowing_workout_metrics.WorkoutMetricsResponse) - }) + 'DESCRIPTOR': _WORKOUTMETRICSRESPONSE, + '__module__': 'rowing_workout_metrics_pb2' + # @@protoc_insertion_point(class_scope:rowing_workout_metrics.WorkoutMetricsResponse) +}) _sym_db.RegisterMessage(WorkoutMetricsResponse) - _METRICS = _descriptor.ServiceDescriptor( - name='Metrics', - full_name='rowing_workout_metrics.Metrics', - file=DESCRIPTOR, - index=0, - serialized_options=None, - serialized_start=284, - serialized_end=403, - methods=[ - _descriptor.MethodDescriptor( - name='CalcMetrics', - full_name='rowing_workout_metrics.Metrics.CalcMetrics', + name='Metrics', + full_name='rowing_workout_metrics.Metrics', + file=DESCRIPTOR, index=0, - containing_service=None, - input_type=_WORKOUTMETRICSREQUEST, - output_type=_WORKOUTMETRICSRESPONSE, serialized_options=None, - ), -]) + serialized_start=284, + serialized_end=403, + methods=[ + _descriptor.MethodDescriptor( + name='CalcMetrics', + full_name='rowing_workout_metrics.Metrics.CalcMetrics', + index=0, + containing_service=None, + input_type=_WORKOUTMETRICSREQUEST, + output_type=_WORKOUTMETRICSRESPONSE, + serialized_options=None, + ), + ]) _sym_db.RegisterServiceDescriptor(_METRICS) DESCRIPTOR.services_by_name['Metrics'] = _METRICS diff --git a/rowers/rowing_workout_metrics_pb2_grpc.py b/rowers/rowing_workout_metrics_pb2_grpc.py index 773340a5..30aac7b5 100644 --- a/rowers/rowing_workout_metrics_pb2_grpc.py +++ b/rowers/rowing_workout_metrics_pb2_grpc.py @@ -5,42 +5,43 @@ import rowers.rowing_workout_metrics_pb2 as rowing__workout__metrics__pb2 class MetricsStub(object): - """OTW-metrics service definition - """ - - def __init__(self, channel): - """Constructor. - - Args: - channel: A grpc.Channel. + """OTW-metrics service definition """ - self.CalcMetrics = channel.unary_unary( - '/rowing_workout_metrics.Metrics/CalcMetrics', - request_serializer=rowing__workout__metrics__pb2.WorkoutMetricsRequest.SerializeToString, - response_deserializer=rowing__workout__metrics__pb2.WorkoutMetricsResponse.FromString, + + def __init__(self, channel): + """Constructor. + + Args: + channel: A grpc.Channel. + """ + self.CalcMetrics = channel.unary_unary( + '/rowing_workout_metrics.Metrics/CalcMetrics', + request_serializer=rowing__workout__metrics__pb2.WorkoutMetricsRequest.SerializeToString, + response_deserializer=rowing__workout__metrics__pb2.WorkoutMetricsResponse.FromString, ) class MetricsServicer(object): - """OTW-metrics service definition - """ + """OTW-metrics service definition + """ - def CalcMetrics(self, request, context): - # missing associated documentation comment in .proto file - pass - context.set_code(grpc.StatusCode.UNIMPLEMENTED) # pragma: no cover - context.set_details('Method not implemented!') # pragma: no cover - raise NotImplementedError('Method not implemented!') # pragma: no cover + def CalcMetrics(self, request, context): + # missing associated documentation comment in .proto file + pass + context.set_code(grpc.StatusCode.UNIMPLEMENTED) # pragma: no cover + context.set_details('Method not implemented!') # pragma: no cover + raise NotImplementedError( + 'Method not implemented!') # pragma: no cover -def add_MetricsServicer_to_server(servicer, server): # pragma: no cover - rpc_method_handlers = { - 'CalcMetrics': grpc.unary_unary_rpc_method_handler( - servicer.CalcMetrics, - request_deserializer=rowing__workout__metrics__pb2.WorkoutMetricsRequest.FromString, - response_serializer=rowing__workout__metrics__pb2.WorkoutMetricsResponse.SerializeToString, - ), - } - generic_handler = grpc.method_handlers_generic_handler( - 'rowing_workout_metrics.Metrics', rpc_method_handlers) - server.add_generic_rpc_handlers((generic_handler,)) +def add_MetricsServicer_to_server(servicer, server): # pragma: no cover + rpc_method_handlers = { + 'CalcMetrics': grpc.unary_unary_rpc_method_handler( + servicer.CalcMetrics, + request_deserializer=rowing__workout__metrics__pb2.WorkoutMetricsRequest.FromString, + response_serializer=rowing__workout__metrics__pb2.WorkoutMetricsResponse.SerializeToString, + ), + } + generic_handler = grpc.method_handlers_generic_handler( + 'rowing_workout_metrics.Metrics', rpc_method_handlers) + server.add_generic_rpc_handlers((generic_handler,)) diff --git a/rowers/rows.py b/rowers/rows.py index f2d830ec..b84dd6b6 100644 --- a/rowers/rows.py +++ b/rowers/rows.py @@ -14,36 +14,39 @@ import uuid from django.core.exceptions import ValidationError -def format_pace_tick(x,pos=None): # pragma: no cover - minu=int(x/60) - sec=int(x-minu*60.) - sec_str=str(sec).zfill(2) - template='%d:%s' - return template % (minu,sec_str) -def format_time_tick(x,pos=None): # pragma: no cover - hour=int(x/3600) - min=int((x-hour*3600.)/60) - min_str=str(min).zfill(2) - template='%d:%s' - return template % (hour,min_str) +def format_pace_tick(x, pos=None): # pragma: no cover + minu = int(x/60) + sec = int(x-minu*60.) + sec_str = str(sec).zfill(2) + template = '%d:%s' + return template % (minu, sec_str) -def format_pace(x,pos=None): # pragma: no cover + +def format_time_tick(x, pos=None): # pragma: no cover + hour = int(x/3600) + min = int((x-hour*3600.)/60) + min_str = str(min).zfill(2) + template = '%d:%s' + return template % (hour, min_str) + + +def format_pace(x, pos=None): # pragma: no cover if isinf(x) or isnan(x): - x=0 + x = 0 - min=int(x/60) - sec=(x-min*60.) + min = int(x/60) + sec = (x-min*60.) str1 = "{min:0>2}:{sec:0>4.1f}".format( - min = min, - sec = sec - ) + min=min, + sec=sec + ) return str1 -def format_time(x,pos=None): # pragma: no cover +def format_time(x, pos=None): # pragma: no cover min = int(x/60.) sec = int(x-min*60) @@ -51,44 +54,48 @@ def format_time(x,pos=None): # pragma: no cover str1 = "{min:0>2}:{sec:0>4.1f}".format( min=min, sec=sec, - ) + ) return str1 + def validate_image_extension(value): import os ext = os.path.splitext(value.name)[1].lower() - valid_extension = ['.jpg','.jpeg','.png','.gif'] + valid_extension = ['.jpg', '.jpeg', '.png', '.gif'] - if not ext in valid_extension: # pragma: no cover + if not ext in valid_extension: # pragma: no cover raise ValidationError(u'File not supported') + def validate_file_extension(value): - import os - ext = os.path.splitext(value.name)[1] - valid_extensions = ['.tcx','.csv','.TCX','.gpx','.GPX', - '.CSV','.fit','.FIT','.zip','.ZIP', - '.gz','.GZ','.xls', - '.jpg','.jpeg','.tiff','.png','.gif','.bmp'] - if not ext in valid_extensions: # pragma: no cover - raise ValidationError(u'File not supported!') + import os + ext = os.path.splitext(value.name)[1] + valid_extensions = ['.tcx', '.csv', '.TCX', '.gpx', '.GPX', + '.CSV', '.fit', '.FIT', '.zip', '.ZIP', + '.gz', '.GZ', '.xls', + '.jpg', '.jpeg', '.tiff', '.png', '.gif', '.bmp'] + if not ext in valid_extensions: # pragma: no cover + raise ValidationError(u'File not supported!') + def must_be_csv(value): import os ext = os.path.splitext(value.name)[1] - valid_extensions = ['.csv','.CSV'] - if not ext in valid_extensions: # pragma: no cover + valid_extensions = ['.csv', '.CSV'] + if not ext in valid_extensions: # pragma: no cover raise ValidationError(u'File not supported!') + def validate_kml(value): import os ext = os.path.splitext(value.name)[1] - valid_extensions = ['.kml','.KML'] - if not ext in valid_extensions: # pragma: no cover + valid_extensions = ['.kml', '.KML'] + if not ext in valid_extensions: # pragma: no cover raise ValidationError(u'File not supported!') -def handle_uploaded_image(i): # pragma: no cover +def handle_uploaded_image(i): # pragma: no cover from io import StringIO, BytesIO from PIL import Image, ImageOps, ExifTags import os @@ -99,18 +106,17 @@ def handle_uploaded_image(i): # pragma: no cover imagefile = BytesIO(image_str) - image = Image.open(i) try: for orientation in ExifTags.TAGS.keys(): - if ExifTags.TAGS[orientation]=='Orientation': - break - exif=dict(image._getexif().items()) + if ExifTags.TAGS[orientation] == 'Orientation': + break + exif = dict(image._getexif().items()) except (AttributeError, KeyError, IndexError): # cases: image don't have getexif - exif = {'orientation':0} + exif = {'orientation': 0} if image.mode not in ("L", "RGB"): image = image.convert("RGB") @@ -118,35 +124,35 @@ def handle_uploaded_image(i): # pragma: no cover basewidth = 600 wpercent = (basewidth/float(image.size[0])) hsize = int((float(image.size[1])*float(wpercent))) - image = image.resize((basewidth,hsize), Image.ANTIALIAS) + image = image.resize((basewidth, hsize), Image.ANTIALIAS) try: if exif[orientation] == 3: - image=image.rotate(180, expand=True) + image = image.rotate(180, expand=True) elif exif[orientation] == 6: - image=image.rotate(270, expand=True) + image = image.rotate(270, expand=True) elif exif[orientation] == 8: - image=image.rotate(90, expand=True) + image = image.rotate(90, expand=True) except KeyError: pass filename = hashlib.md5(imagefile.getvalue()).hexdigest()+'.jpg' - filename2 = os.path.join('static/plots/',filename) - image.save(filename2,'JPEG') + filename2 = os.path.join('static/plots/', filename) + image.save(filename2, 'JPEG') - return filename,filename2 + return filename, filename2 def handle_uploaded_file(f): fname = f.name ext = fname.split('.')[-1] - fname = '%s.%s' % (uuid.uuid4(),ext) + fname = '%s.%s' % (uuid.uuid4(), ext) #timestr = uuid.uuid4().hex[:10]+'-'+time.strftime("%Y%m%d-%H%M%S") #fname = timestr+'-'+fname fname2 = 'media/'+fname - with open(fname2,'wb+') as destination: + with open(fname2, 'wb+') as destination: for chunk in f.chunks(): destination.write(chunk) - return fname,fname2 + return fname, fname2 diff --git a/rowers/rp3stuff.py b/rowers/rp3stuff.py index 492220ce..2c49fb2d 100644 --- a/rowers/rp3stuff.py +++ b/rowers/rp3stuff.py @@ -1,4 +1,18 @@ from __future__ import absolute_import +from celery import Celery, app +from rowers.rower_rules import is_workout_user +import time +from django_rq import job +from rowers.tasks import handle_rp3_async_workout +from rowsandall_app.settings import ( + C2_CLIENT_ID, C2_REDIRECT_URI, C2_CLIENT_SECRET, + STRAVA_CLIENT_ID, STRAVA_REDIRECT_URI, STRAVA_CLIENT_SECRET, + RP3_CLIENT_ID, RP3_CLIENT_SECRET, + RP3_REDIRECT_URI, RP3_CLIENT_KEY, + RP3_CLIENT_ID, RP3_CLIENT_KEY, RP3_REDIRECT_URI, RP3_CLIENT_SECRET, + UPLOAD_SERVICE_URL, UPLOAD_SERVICE_SECRET +) +from rowers.utils import myqueue from __future__ import division from __future__ import print_function from __future__ import unicode_literals @@ -19,23 +33,7 @@ queue = django_rq.get_queue('default') queuelow = django_rq.get_queue('low') queuehigh = django_rq.get_queue('high') -from rowers.utils import myqueue -from rowsandall_app.settings import ( - C2_CLIENT_ID, C2_REDIRECT_URI, C2_CLIENT_SECRET, - STRAVA_CLIENT_ID, STRAVA_REDIRECT_URI, STRAVA_CLIENT_SECRET, - RP3_CLIENT_ID, RP3_CLIENT_SECRET, - RP3_REDIRECT_URI,RP3_CLIENT_KEY, - RP3_CLIENT_ID, RP3_CLIENT_KEY, RP3_REDIRECT_URI, RP3_CLIENT_SECRET, - UPLOAD_SERVICE_URL, UPLOAD_SERVICE_SECRET - ) - -from rowers.tasks import handle_rp3_async_workout - - -from celery import Celery,app -from django_rq import job -import time #from async_messages import message_user,messages oauth_data = { @@ -44,16 +42,15 @@ oauth_data = { 'redirect_uri': RP3_REDIRECT_URI, 'autorization_uri': "https://rp3rowing-app.com/oauth/authorize?", 'content_type': 'application/x-www-form-urlencoded', -# 'content_type': 'application/json', + # 'content_type': 'application/json', 'tokenname': 'rp3token', 'refreshtokenname': 'rp3refreshtoken', 'expirydatename': 'rp3tokenexpirydate', 'bearer_auth': False, 'base_url': "https://rp3rowing-app.com/oauth/token", - 'scope':'read,write', - } + 'scope': 'read,write', +} -from rowers.rower_rules import is_workout_user graphql_url = "https://rp3rowing-app.com/graphql" @@ -63,29 +60,33 @@ def rp3_open(user): return imports_open(user, oauth_data) # Refresh ST token using refresh token -def do_refresh_token(refreshtoken): # pragma: no cover + + +def do_refresh_token(refreshtoken): # pragma: no cover return imports_do_refresh_token(refreshtoken, oauth_data) # Exchange access code for long-lived access token -def get_token(code): # pragma: no cover - client_auth = requests.auth.HTTPBasicAuth(RP3_CLIENT_KEY, RP3_CLIENT_SECRET) + + +def get_token(code): # pragma: no cover + client_auth = requests.auth.HTTPBasicAuth( + RP3_CLIENT_KEY, RP3_CLIENT_SECRET) post_data = { - "client_id":RP3_CLIENT_KEY, + "client_id": RP3_CLIENT_KEY, "grant_type": "authorization_code", "code": code, - "redirect_uri":RP3_REDIRECT_URI, - "client_secret": RP3_CLIENT_SECRET, + "redirect_uri": RP3_REDIRECT_URI, + "client_secret": RP3_CLIENT_SECRET, } headers = { - 'Content-Type': 'application/x-www-form-urlencoded', + 'Content-Type': 'application/x-www-form-urlencoded', } response = requests.post( - "https://rp3rowing-app.com/oauth/token", - data=post_data,verify=False, + "https://rp3rowing-app.com/oauth/token", + data=post_data, verify=False, ) - try: token_json = response.json() thetoken = token_json['access_token'] @@ -96,10 +97,12 @@ def get_token(code): # pragma: no cover expires_in = 0 refresh_token = 0 - return thetoken,expires_in,refresh_token + return thetoken, expires_in, refresh_token # Make authorization URL including random string -def make_authorization_url(request): # pragma: no cover + + +def make_authorization_url(request): # pragma: no cover return imports_make_authorization_url(oauth_data) @@ -108,7 +111,7 @@ def get_rp3_workout_list(user): auth_token = rp3_open(user) - headers = {'Authorization': 'Bearer ' + auth_token } + headers = {'Authorization': 'Bearer ' + auth_token} get_workouts_list = """{ workouts{ @@ -118,15 +121,15 @@ def get_rp3_workout_list(user): }""" response = requests.post( - url=graphql_url, - headers=headers, - json={'query': get_workouts_list} + url=graphql_url, + headers=headers, + json={'query': get_workouts_list} ) - return response -def get_rp3_workouts(rower,do_async=True): # pragma: no cover + +def get_rp3_workouts(rower, do_async=True): # pragma: no cover try: auth_token = rp3_open(rower.user) except NoTokenError: @@ -138,11 +141,11 @@ def get_rp3_workouts(rower,do_async=True): # pragma: no cover return 0 s = '{d}'.format(d=res.json()) - dologging('rp3_import.log',s) + dologging('rp3_import.log', s) workouts_list = pd.json_normalize(res.json()['data']['workouts']) try: rp3ids = workouts_list['id'].values - workouts_list.set_index('id',inplace=True) + workouts_list.set_index('id', inplace=True) except (KeyError, IndexError): return 0 @@ -153,34 +156,36 @@ def get_rp3_workouts(rower,do_async=True): # pragma: no cover newids = [rp3id for rp3id in rp3ids if not rp3id in knownrp3ids] for id in newids: - startdatetime = workouts_list.loc[id,'executed_at'] - dologging('rp3_import.log',startdatetime) + startdatetime = workouts_list.loc[id, 'executed_at'] + dologging('rp3_import.log', startdatetime) job = myqueue( - queuehigh, - handle_rp3_async_workout, - rower.user.id, - auth_token, - id, - startdatetime, - 10, + queuehigh, + handle_rp3_async_workout, + rower.user.id, + auth_token, + id, + startdatetime, + 10, ) return 1 -def download_rp3_file(url,auth_token,filename): # pragma: no cover - headers = {'Authorization': 'Bearer ' + auth_token } - res = requests.get(url,headers=headers) +def download_rp3_file(url, auth_token, filename): # pragma: no cover + headers = {'Authorization': 'Bearer ' + auth_token} + + res = requests.get(url, headers=headers) if res.status_code == 200: - with open(filename,'wb') as f: + with open(filename, 'wb') as f: f.write(res.content) return res.status_code -def get_rp3_workout_token(workout_id,auth_token,waittime=3,max_attempts=20): # pragma: no cover - headers = {'Authorization': 'Bearer ' + auth_token } + +def get_rp3_workout_token(workout_id, auth_token, waittime=3, max_attempts=20): # pragma: no cover + headers = {'Authorization': 'Bearer ' + auth_token} get_download_link = """{ download(workout_id: """ + str(workout_id) + """, type:csv){ @@ -198,20 +203,21 @@ def get_rp3_workout_token(workout_id,auth_token,waittime=3,max_attempts=20): # p url=graphql_url, headers=headers, json={'query': get_download_link} - ) + ) if response.status_code != 200: have_link = True - workout_download_details = pd.json_normalize(response.json()['data']['download']) + workout_download_details = pd.json_normalize( + response.json()['data']['download']) - if workout_download_details.iat[0,1] == 'ready': - download_url = workout_download_details.iat[0,2] + if workout_download_details.iat[0, 1] == 'ready': + download_url = workout_download_details.iat[0, 2] have_link = True counter += 1 - if counter>max_attempts: + if counter > max_attempts: have_link = True time.sleep(waittime) @@ -219,14 +225,15 @@ def get_rp3_workout_token(workout_id,auth_token,waittime=3,max_attempts=20): # p return download_url -def get_rp3_workout_link(user,workout_id,waittime=3,max_attempts=20): # pragma: no cover +def get_rp3_workout_link(user, workout_id, waittime=3, max_attempts=20): # pragma: no cover r = Rower.objects.get(user=user) auth_token = rp3_open(user) - return get_rp3_workout_token(workout_id,auth_token,waittime=waittime,max_attempts=max_attempts) + return get_rp3_workout_token(workout_id, auth_token, waittime=waittime, max_attempts=max_attempts) -def get_rp3_workout(user,workout_id,startdatetime=None): # pragma: no cover - url = get_rp3_workout_link(user,workout_id) + +def get_rp3_workout(user, workout_id, startdatetime=None): # pragma: no cover + url = get_rp3_workout_link(user, workout_id) filename = 'media/RP3Import_'+str(workout_id)+'.csv' r = Rower.objects.get(user=user) @@ -235,7 +242,6 @@ def get_rp3_workout(user,workout_id,startdatetime=None): # pragma: no cover if not startdatetime: startdatetime = str(timezone.now()) - status_code = download_rp3_file(url, auth_token, filename) if status_code != 200: @@ -247,17 +253,17 @@ def get_rp3_workout(user,workout_id,startdatetime=None): # pragma: no cover 'secret': UPLOAD_SERVICE_SECRET, 'user': userid, 'file': filename, - 'workouttype':'dynamic', - 'boattype':'1x', - 'rp3id':workout_id, - 'startdatetime':startdatetime, + 'workouttype': 'dynamic', + 'boattype': '1x', + 'rp3id': workout_id, + 'startdatetime': startdatetime, } session = requests.session() newHeaders = {'Content-type': 'application/json', 'Accept': 'text/plain'} session.headers.update(newHeaders) - response = session.post(UPLOAD_SERVICE_URL,json=uploadoptions) + response = session.post(UPLOAD_SERVICE_URL, json=uploadoptions) if response.status_code != 200: return 0 diff --git a/rowers/scoring.py b/rowers/scoring.py index 1d255bc0..ab692240 100644 --- a/rowers/scoring.py +++ b/rowers/scoring.py @@ -1,75 +1,80 @@ from rowers.models import ( - StandardCollection,CourseStandard, - VirtualRaceResult,IndoorVirtualRaceResult, - ) + StandardCollection, CourseStandard, + VirtualRaceResult, IndoorVirtualRaceResult, +) import pandas as pd import arrow import datetime -def save_scoring(name,user,filename,id=0,notes=""): - if id==0: - collection = StandardCollection(name=name,manager=user,notes=notes) + +def save_scoring(name, user, filename, id=0, notes=""): + if id == 0: + collection = StandardCollection(name=name, manager=user, notes=notes) collection.save() - standards = CourseStandard.objects.filter(standardcollection=collection) - for standard in standards: # pragma: no cover + standards = CourseStandard.objects.filter( + standardcollection=collection) + for standard in standards: # pragma: no cover standards.delete() - else: # pragma: no cover + else: # pragma: no cover try: collection = StandardCollection.objects.get(id=id) collection.name = name collection.notes = notes collection.save() - standards = CourseStandard.objects.filter(standardcollection=collection) + standards = CourseStandard.objects.filter( + standardcollection=collection) for standard in standards: - records1 = VirtualRaceResult.objects.filter(entrycategory=standard) - records2 = IndoorVirtualRaceResult.objects.filter(entrycategory=standard) + records1 = VirtualRaceResult.objects.filter( + entrycategory=standard) + records2 = IndoorVirtualRaceResult.objects.filter( + entrycategory=standard) if records1.count()+records2.count() == 0: standard.delete() - except StandardCollection.DoesNotExist: return 0 try: df = pd.read_csv(filename) - except: # pragma: no cover + except: # pragma: no cover return 0 df.rename( columns={ - 'name':'Name', - 'agemax':'MaxAge', - 'agemin':'MinAge', - 'adaptiveclass':'AdaptiveClass', - 'coursedistance':'CourseDistance', - 'coursetime':'CourseStandard', - 'boatclass':'BoatClass', - 'boattype':'BoatType', - 'sex':'Gender', - 'weightclass':'WeightClass', - 'skillclass':'SkillClass', - }, - inplace=True) + 'name': 'Name', + 'agemax': 'MaxAge', + 'agemin': 'MinAge', + 'adaptiveclass': 'AdaptiveClass', + 'coursedistance': 'CourseDistance', + 'coursetime': 'CourseStandard', + 'boatclass': 'BoatClass', + 'boattype': 'BoatType', + 'sex': 'Gender', + 'weightclass': 'WeightClass', + 'skillclass': 'SkillClass', + }, + inplace=True) df = df.drop_duplicates(['Name']) for index, row in df.iterrows(): try: name = row['Name'] - except KeyError: # pragma: no cover + except KeyError: # pragma: no cover continue try: coursedistance = row['CourseDistance'] coursetime = row['CourseStandard'] - t = datetime.datetime.strptime(coursetime,'%M:%S.%f') - delta = datetime.timedelta(hours=t.hour, minutes=t.minute, seconds=t.second,microseconds=t.microsecond) + t = datetime.datetime.strptime(coursetime, '%M:%S.%f') + delta = datetime.timedelta( + hours=t.hour, minutes=t.minute, seconds=t.second, microseconds=t.microsecond) seconds = delta.total_seconds() referencespeed = coursedistance/seconds - except KeyError: # pragma: no cover + except KeyError: # pragma: no cover continue try: @@ -77,71 +82,72 @@ def save_scoring(name,user,filename,id=0,notes=""): agemax = row['MaxAge'] agemin = int(agemin) agemax = int(agemax) - except KeyError: # pragma: no cover + except KeyError: # pragma: no cover agemin = 0 agemax = 120 try: boatclass = row['BoatClass'] - if boatclass.lower() in ['standard','olympic','normal','water']: + if boatclass.lower() in ['standard', 'olympic', 'normal', 'water']: boatclass = 'water' - elif boatclass.lower() in ['erg','c2','concept','static','rower']: # pragma: no cover + elif boatclass.lower() in ['erg', 'c2', 'concept', 'static', 'rower']: # pragma: no cover boatclass = 'rower' - elif boatclass.lower() in ['dynamic']: # pragma: no cover + elif boatclass.lower() in ['dynamic']: # pragma: no cover boatclass = 'dynamic' - elif boatclass.lower() in ['slides','slide','slider','sliders']: # pragma: no cover + elif boatclass.lower() in ['slides', 'slide', 'slider', 'sliders']: # pragma: no cover boatclass = 'slides' - elif boatclass.lower() in ['c','c-boat']: # pragma: no cover + elif boatclass.lower() in ['c', 'c-boat']: # pragma: no cover boatclass = 'c-boat' - elif boatclass.lower() in ['coastal','coast']: # pragma: no cover + elif boatclass.lower() in ['coastal', 'coast']: # pragma: no cover boatclass = 'coastal' - elif boatclass.lower() in ['church','churchboat','finnish','finland']: # pragma: no cover + elif boatclass.lower() in ['church', 'churchboat', 'finnish', 'finland']: # pragma: no cover boatclass = 'churchboat' - except KeyError: # pragma: no cover + except KeyError: # pragma: no cover boatclass = 'water' try: boattype = row['BoatType'] - except KeyError: # pragma: no cover + except KeyError: # pragma: no cover boattype = '1x' try: sex = row['Gender'] - if sex.lower() in ['m','men','male','open']: + if sex.lower() in ['m', 'men', 'male', 'open']: sex = 'male' - elif sex.lower() in ['mix','mixed']: + elif sex.lower() in ['mix', 'mixed']: sex = 'mixed' else: sex = 'female' - except KeyError: # pragma: no cover + except KeyError: # pragma: no cover sex = 'female' try: weightclass = row['WeightClass'] - if weightclass.lower() in ['hwt','h','o','heavy','open']: + if weightclass.lower() in ['hwt', 'h', 'o', 'heavy', 'open']: weightclass = 'hwt' - elif weightclass.lower() in ['lwt','l','light','lights','lighties']: + elif weightclass.lower() in ['lwt', 'l', 'light', 'lights', 'lighties']: weightclass = 'lwt' - except KeyError: # pragma: no cover + except KeyError: # pragma: no cover weightclass = 'hwt' adaptiveclass = 'None' try: adaptiveclass = row['AdaptiveClass'] - if adaptiveclass.lower() in ['o','open','none','no']: + if adaptiveclass.lower() in ['o', 'open', 'none', 'no']: adaptiveclass = 'None' - except KeyError: # pragma: no cover + except KeyError: # pragma: no cover adaptiveclass = 'None' try: skillclass = row['SkillClass'] - except KeyError: # pragma: no cover + except KeyError: # pragma: no cover skillclass = 'Open' # finding existing standard - existingstandards = CourseStandard.objects.filter(name=name,standardcollection=collection) - #print(existingstandards,collection) - if existingstandards: # pragma: no cover + existingstandards = CourseStandard.objects.filter( + name=name, standardcollection=collection) + # print(existingstandards,collection) + if existingstandards: # pragma: no cover existingstandards.update( name=name, coursedistance=coursedistance, @@ -155,10 +161,10 @@ def save_scoring(name,user,filename,id=0,notes=""): weightclass=weightclass, adaptiveclass=adaptiveclass, skillclass=skillclass, - standardcollection = collection, + standardcollection=collection, ) else: - #print('not') + # print('not') standard = CourseStandard( name=name, coursedistance=coursedistance, @@ -172,8 +178,8 @@ def save_scoring(name,user,filename,id=0,notes=""): weightclass=weightclass, adaptiveclass=adaptiveclass, skillclass=skillclass, - standardcollection = collection, - ) + standardcollection=collection, + ) standard.save() diff --git a/rowers/serializers.py b/rowers/serializers.py index 52f0e13d..c723e377 100644 --- a/rowers/serializers.py +++ b/rowers/serializers.py @@ -8,16 +8,18 @@ from __future__ import unicode_literals from rest_framework import serializers from rowers.models import ( - Workout,Rower,FavoriteChart,VirtualRaceResult, - VirtualRace,GeoCourse,StandardCollection, CourseStandard, - GeoCourse, GeoPolygon, GeoPoint,PlannedSession, - ) + Workout, Rower, FavoriteChart, VirtualRaceResult, + VirtualRace, GeoCourse, StandardCollection, CourseStandard, + GeoCourse, GeoPolygon, GeoPoint, PlannedSession, +) from django.core.exceptions import PermissionDenied import datetime # Serializers define the API representation. + + class RowerSerializer(serializers.HyperlinkedModelSerializer): class Meta: model = Rower @@ -33,14 +35,15 @@ class RowerSerializer(serializers.HyperlinkedModelSerializer): 'an', 'hrzones', 'powerzones', - 'pw_ut2','pw_ut1','pw_at','pw_tr','pw_an', + 'pw_ut2', 'pw_ut1', 'pw_at', 'pw_tr', 'pw_an', 'ftp', 'hrftp', 'sex', 'defaulttimezone', 'getimportantemails', 'adaptiveclass', - ) + ) + class FavoriteChartSerializer(serializers.ModelSerializer): class Meta: @@ -54,11 +57,7 @@ class FavoriteChartSerializer(serializers.ModelSerializer): 'workouttype', 'reststrokes', 'user' - ) - - - - + ) class CourseStandardSerializer(serializers.ModelSerializer): @@ -81,8 +80,10 @@ class CourseStandardSerializer(serializers.ModelSerializer): 'standardcollection', ) + class StandardCollectionSerializer(serializers.ModelSerializer): - standards = CourseStandardSerializer(many=True,read_only=True) + standards = CourseStandardSerializer(many=True, read_only=True) + class Meta: model = StandardCollection fields = ( @@ -93,8 +94,10 @@ class StandardCollectionSerializer(serializers.ModelSerializer): 'standards' ) + class ShortEntrySerializer(serializers.ModelSerializer): entrycategory = CourseStandardSerializer(read_only=True) + class Meta: model = VirtualRaceResult fields = ( @@ -113,9 +116,11 @@ class ShortEntrySerializer(serializers.ModelSerializer): 'entrycategory', ) + class VirtualRaceSerializer(serializers.ModelSerializer): coursestandards = StandardCollectionSerializer(read_only=True) - entries = ShortEntrySerializer(many=True,read_only=True) + entries = ShortEntrySerializer(many=True, read_only=True) + class Meta: model = VirtualRace fields = ( @@ -134,6 +139,7 @@ class VirtualRaceSerializer(serializers.ModelSerializer): 'coursestandards', ) + class PlannedSessionSerializer(serializers.ModelSerializer): class Meta: model = VirtualRace @@ -151,24 +157,26 @@ class PlannedSessionSerializer(serializers.ModelSerializer): 'course', 'approximate_distance', 'approximate_duration', - #'steps', + # 'steps', 'fitfile' ) - def create(self, validated_data): # pragma: no cover + def create(self, validated_data): # pragma: no cover if self.context['request'].user.is_authenticated: r = Rower.objects.get(user=self.context['request'].user) else: raise PermissionDenied("Not allowed") - ps = PlannedSession.objects.create(manager=r.user,**validated_data) + ps = PlannedSession.objects.create(manager=r.user, **validated_data) ps.rower.add(r) ps.save() return ps + class EntrySerializer(serializers.ModelSerializer): entrycategory = CourseStandardSerializer(read_only=True) race = VirtualRaceSerializer(read_only=True) + class Meta: model = VirtualRaceResult fields = ( @@ -186,6 +194,7 @@ class EntrySerializer(serializers.ModelSerializer): 'entrycategory', ) + class WorkoutSerializer(serializers.ModelSerializer): class Meta: model = Workout @@ -215,7 +224,7 @@ class WorkoutSerializer(serializers.ModelSerializer): 'duplicate', ) - def create(self, validated_data): # pragma: no cover + def create(self, validated_data): # pragma: no cover if self.context['request'].user.is_authenticated: r = Rower.objects.get(user=self.context['request'].user) else: @@ -229,13 +238,12 @@ class WorkoutSerializer(serializers.ModelSerializer): t.minute, t.second) - validated_data['startdatetime'] = rowdatetime validated_data['user'] = r return Workout.objects.create(**validated_data) - def update(self, instance, validated_data): # pragma: no cover + def update(self, instance, validated_data): # pragma: no cover d = validated_data['date'] t = validated_data['starttime'] rowdatetime = datetime.datetime(d.year, @@ -245,34 +253,35 @@ class WorkoutSerializer(serializers.ModelSerializer): t.minute, t.second) - - instance.name=validated_data['name'] - instance.date=validated_data['date'] - instance.workouttype=validated_data['workouttype'] - instance.duration=validated_data['duration'] - instance.distance=validated_data['distance'] - instance.starttime=validated_data['starttime'] - instance.notes=validated_data['notes'] - instance.summary=validated_data['summary'] - instance.averagehr=validated_data['averagehr'] - instance.maxhr=validated_data['maxhr'] - instance.startdatetime=rowdatetime - instance.timezone=validated_data['timezone'] - instance.forceunit=validated_data['forceunit'] - instance.inboard=validated_data['inboard'] - instance.oarlength=validated_data['oarlength'] - instance.privacy=validated_data['privacy'] - instance.rankingpiece=validated_data['rankingpiece'] + instance.name = validated_data['name'] + instance.date = validated_data['date'] + instance.workouttype = validated_data['workouttype'] + instance.duration = validated_data['duration'] + instance.distance = validated_data['distance'] + instance.starttime = validated_data['starttime'] + instance.notes = validated_data['notes'] + instance.summary = validated_data['summary'] + instance.averagehr = validated_data['averagehr'] + instance.maxhr = validated_data['maxhr'] + instance.startdatetime = rowdatetime + instance.timezone = validated_data['timezone'] + instance.forceunit = validated_data['forceunit'] + instance.inboard = validated_data['inboard'] + instance.oarlength = validated_data['oarlength'] + instance.privacy = validated_data['privacy'] + instance.rankingpiece = validated_data['rankingpiece'] instance.save() return instance # This is just a fake one for URL registration purposes + + class StrokeDataSerializer(serializers.Serializer): workoutid = serializers.IntegerField strokedata = serializers.JSONField - def create(self, workoutid, strokedata): # pragma: no cover + def create(self, workoutid, strokedata): # pragma: no cover """ Create and enter a new set of stroke data into the DB """ @@ -280,6 +289,7 @@ class StrokeDataSerializer(serializers.Serializer): # do something return 1 + class GeoPointSerializer(serializers.ModelSerializer): class Meta: model = GeoPoint @@ -291,8 +301,10 @@ class GeoPointSerializer(serializers.ModelSerializer): ) extra_kwargs = {'id': {'read_only': False, 'required': True}} + class GeoPolygonSerializer(serializers.ModelSerializer): points = GeoPointSerializer(many=True) + class Meta: model = GeoPolygon fields = ( @@ -303,8 +315,10 @@ class GeoPolygonSerializer(serializers.ModelSerializer): ) extra_kwargs = {'id': {'read_only': False, 'required': True}} + class GeoCourseSerializer(serializers.ModelSerializer): polygons = GeoPolygonSerializer(many=True) + class Meta: model = GeoCourse fields = ( @@ -316,30 +330,32 @@ class GeoCourseSerializer(serializers.ModelSerializer): 'polygons', ) - def update(self, instance, validated_data): # pragma: no cover - instance.name = validated_data.get('name',instance.name) - instance.country = validated_data.get('country',instance.country) - instance.notes = validated_data.get('notes',instance.notes) + def update(self, instance, validated_data): # pragma: no cover + instance.name = validated_data.get('name', instance.name) + instance.country = validated_data.get('country', instance.country) + instance.notes = validated_data.get('notes', instance.notes) instance.save() polygons = validated_data.get('polygons') for polygon in polygons: - polygon_id = polygon.get('id',None) + polygon_id = polygon.get('id', None) if polygon_id: p = GeoPolygon.objects.get(id=polygon_id) - p.name = polygon.get('name',p.name) - p.order_in_course = polygon.get('order_in_course',p.order_in_course) + p.name = polygon.get('name', p.name) + p.order_in_course = polygon.get( + 'order_in_course', p.order_in_course) p.save() points = polygon.get('points') for point in points: - point_id = point.get('id',None) + point_id = point.get('id', None) if point_id: pt = GeoPoint.objects.get(id=point_id) - pt.latitude = point.get('latitude',pt.latitude) - pt.longitude = point.get('longitude',pt.longitude) - pt.order_in_poly = point.get('order_in_poly',pt.order_in_poly) + pt.latitude = point.get('latitude', pt.latitude) + pt.longitude = point.get('longitude', pt.longitude) + pt.order_in_poly = point.get( + 'order_in_poly', pt.order_in_poly) pt.save() return instance diff --git a/rowers/sporttracksstuff.py b/rowers/sporttracksstuff.py index 8f618d87..6aa3bb5e 100644 --- a/rowers/sporttracksstuff.py +++ b/rowers/sporttracksstuff.py @@ -1,4 +1,16 @@ from __future__ import absolute_import +from rowers.tasks import handle_sporttracks_sync +from rowers.rower_rules import is_workout_user +import rowers.mytypes as mytypes +from rowsandall_app.settings import ( + C2_CLIENT_ID, C2_REDIRECT_URI, C2_CLIENT_SECRET, + STRAVA_CLIENT_ID, STRAVA_REDIRECT_URI, STRAVA_CLIENT_SECRET, + SPORTTRACKS_CLIENT_SECRET, SPORTTRACKS_CLIENT_ID, + SPORTTRACKS_REDIRECT_URI +) +import re +from rowers.imports import * +from rowers.utils import myqueue from __future__ import division from __future__ import print_function from __future__ import unicode_literals @@ -12,20 +24,7 @@ import django_rq queue = django_rq.get_queue('default') queuelow = django_rq.get_queue('low') queuehigh = django_rq.get_queue('low') -from rowers.utils import myqueue -from rowers.imports import * -import re -from rowsandall_app.settings import ( - C2_CLIENT_ID, C2_REDIRECT_URI, C2_CLIENT_SECRET, - STRAVA_CLIENT_ID, STRAVA_REDIRECT_URI, STRAVA_CLIENT_SECRET, - SPORTTRACKS_CLIENT_SECRET, SPORTTRACKS_CLIENT_ID, - SPORTTRACKS_REDIRECT_URI - ) - -import rowers.mytypes as mytypes -from rowers.rower_rules import is_workout_user -from rowers.tasks import handle_sporttracks_sync oauth_data = { 'client_id': SPORTTRACKS_CLIENT_ID, @@ -38,10 +37,12 @@ oauth_data = { 'expirydatename': 'sporttrackstokenexpirydate', 'bearer_auth': False, 'base_url': "https://api.sporttracks.mobi/oauth2/token", - 'scope':'write', - } + 'scope': 'write', +} # Checks if user has SportTracks token, renews them if they are expired + + def sporttracks_open(user): return imports_open(user, oauth_data) @@ -51,15 +52,21 @@ def do_refresh_token(refreshtoken): return imports_do_refresh_token(refreshtoken, oauth_data) # Exchange ST access code for long-lived ST access token + + def get_token(code): - return imports_get_token(code,oauth_data) + return imports_get_token(code, oauth_data) # Make authorization URL including random string -def make_authorization_url(request): # pragma: no cover + + +def make_authorization_url(request): # pragma: no cover return imports_make_authorization_url(oauth_data) # This is token refresh. Looks for tokens in our database, then refreshes -def rower_sporttracks_token_refresh(user): # pragma: no cover + + +def rower_sporttracks_token_refresh(user): # pragma: no cover r = Rower.objects.get(user=user) res = do_refresh_token(r.sporttracksrefreshtoken) access_token = res[0] @@ -77,14 +84,16 @@ def rower_sporttracks_token_refresh(user): # pragma: no cover return r.sporttrackstoken # Get list of workouts available on SportTracks + + def get_sporttracks_workout_list(user): r = Rower.objects.get(user=user) if (r.sporttrackstoken == '') or (r.sporttrackstoken is None): s = "Token doesn't exist. Need to authorize" - return custom_exception_handler(401,s) - elif (timezone.now()>r.sporttrackstokenexpirydate): # pragma: no cover + return custom_exception_handler(401, s) + elif (timezone.now() > r.sporttrackstokenexpirydate): # pragma: no cover s = "Token expired. Needs to refresh." - return custom_exception_handler(401,s) + return custom_exception_handler(401, s) else: # ready to fetch. Hurray authorizationstring = str('Bearer ' + r.sporttrackstoken) @@ -92,37 +101,40 @@ def get_sporttracks_workout_list(user): 'user-agent': 'sanderroosendaal', 'Content-Type': 'application/json'} url = "https://api.sporttracks.mobi/api/v2/fitnessActivities" - s = requests.get(url,headers=headers) + s = requests.get(url, headers=headers) return s # Get workout summary data by SportTracks ID -def get_workout(user,sporttracksid,do_async=False): + + +def get_workout(user, sporttracksid, do_async=False): r = Rower.objects.get(user=user) - if (r.sporttrackstoken == '') or (r.sporttrackstoken is None): # pragma: no cover - return custom_exception_handler(401,s) + if (r.sporttrackstoken == '') or (r.sporttrackstoken is None): # pragma: no cover + return custom_exception_handler(401, s) s = "Token doesn't exist. Need to authorize" - elif (timezone.now()>r.sporttrackstokenexpirydate): # pragma: no cover + elif (timezone.now() > r.sporttrackstokenexpirydate): # pragma: no cover s = "Token expired. Needs to refresh." - return custom_exception_handler(401,s) + return custom_exception_handler(401, s) else: # ready to fetch. Hurray authorizationstring = str('Bearer ' + r.sporttrackstoken) headers = {'Authorization': authorizationstring, 'user-agent': 'sanderroosendaal', 'Content-Type': 'application/json'} - url = "https://api.sporttracks.mobi/api/v2/fitnessActivities/"+str(sporttracksid) - s = requests.get(url,headers=headers) + url = "https://api.sporttracks.mobi/api/v2/fitnessActivities/" + \ + str(sporttracksid) + s = requests.get(url, headers=headers) data = s.json() strokedata = pd.DataFrame.from_dict({ - key: pd.Series(value,dtype='object') for key, value in data.items() + key: pd.Series(value, dtype='object') for key, value in data.items() }) - id,message = add_workout_from_data( + id, message = add_workout_from_data( user, - sporttracksid,data, + sporttracksid, data, strokedata, source='sporttracks', workoutsource='sporttracks') @@ -130,19 +142,21 @@ def get_workout(user,sporttracksid,do_async=False): return id # Create Workout Data for upload to SportTracks + + def createsporttracksworkoutdata(w): timezone = pytz.timezone(w.timezone) filename = w.csvfilename try: row = rowingdata(csvfile=filename) - except: # pragma: no cover + except: # pragma: no cover return 0 try: averagehr = int(row.df[' HRCur (bpm)'].mean()) maxhr = int(row.df[' HRCur (bpm)'].max()) - except KeyError: # pragma: no cover + except KeyError: # pragma: no cover averagehr = 0 maxhr = 0 @@ -151,18 +165,19 @@ def createsporttracksworkoutdata(w): duration += w.duration.minute*60 duration += w.duration.second duration += +1.0e-6*w.duration.microsecond - except AttributeError: # pragma: no cover + except AttributeError: # pragma: no cover return 0 # adding diff, trying to see if this is valid #t = row.df.loc[:,'TimeStamp (sec)'].values-10*row.df.ix[0,'TimeStamp (sec)'] - t = row.df.loc[:,'TimeStamp (sec)'].values-row.df.loc[:,'TimeStamp (sec)'].iloc[0] + t = row.df.loc[:, 'TimeStamp (sec)'].values - \ + row.df.loc[:, 'TimeStamp (sec)'].iloc[0] try: t[0] = t[1] - except IndexError: # pragma: no cover + except IndexError: # pragma: no cover return 0 - d = row.df.loc[:,'cum_dist'].values + d = row.df.loc[:, 'cum_dist'].values d[0] = d[1] t = t.astype(int) d = d.astype(int) @@ -170,25 +185,24 @@ def createsporttracksworkoutdata(w): spm[0] = spm[1] hr = row.df[' HRCur (bpm)'].astype(int).values - haslatlon=1 + haslatlon = 1 try: lat = row.df[' latitude'].values lon = row.df[' longitude'].values - if not lat.std() and not lon.std(): # pragma: no cover + if not lat.std() and not lon.std(): # pragma: no cover haslatlon = 0 except KeyError: haslatlon = 0 - haspower = 1 try: power = row.df[' Power (watts)'].astype(int).values - except KeyError: # pragma: no cover + except KeyError: # pragma: no cover haspower = 0 locdata = [] - hrdata = [] + hrdata = [] spmdata = [] distancedata = [] powerdata = [] @@ -211,12 +225,11 @@ def createsporttracksworkoutdata(w): spmdata.append(spm[i]) if haslatlon: locdata.append(t[i]) - locdata.append([lat[i],lon[i]]) + locdata.append([lat[i], lon[i]]) if haspower: powerdata.append(t[i]) powerdata.append(power[i]) - try: w.notes = w.notes+'\n from '+w.workoutsource+' via rowsandall.com' except TypeError: @@ -239,7 +252,7 @@ def createsporttracksworkoutdata(w): "distance": distancedata, "cadence": spmdata, "heartrate": hrdata, - } + } else: data = { "type": "Rowing", @@ -253,7 +266,7 @@ def createsporttracksworkoutdata(w): "distance": distancedata, "cadence": spmdata, "heartrate": hrdata, - } + } if haspower: data['power'] = powerdata @@ -262,7 +275,9 @@ def createsporttracksworkoutdata(w): # Obtain SportTracks Workout ID from the response returned on successful # upload -def getidfromresponse(response): # pragma: no cover + + +def getidfromresponse(response): # pragma: no cover t = response.json() uri = t['uris'][0] regex = '.*?sporttracks\.mobi\/api\/v2\/fitnessActivities/(\d+)\.json$' @@ -272,13 +287,14 @@ def getidfromresponse(response): # pragma: no cover return int(id) -def default(o): # pragma: no cover - if isinstance(o, numpy.int64): return int(o) + +def default(o): # pragma: no cover + if isinstance(o, numpy.int64): + return int(o) raise TypeError - -def workout_sporttracks_upload(user,w,asynchron=False): # pragma: no cover +def workout_sporttracks_upload(user, w, asynchron=False): # pragma: no cover message = "Uploading to SportTracks" stid = 0 # ready to upload. Hurray @@ -286,12 +302,12 @@ def workout_sporttracks_upload(user,w,asynchron=False): # pragma: no cover thetoken = sporttracks_open(user) - if (is_workout_user(user,w)): + if (is_workout_user(user, w)): data = createsporttracksworkoutdata(w) if not data: message = "Data error" stid = 0 - return message,stid + return message, stid authorizationstring = str('Bearer ' + thetoken) headers = {'Authorization': authorizationstring, @@ -300,45 +316,48 @@ def workout_sporttracks_upload(user,w,asynchron=False): # pragma: no cover url = "https://api.sporttracks.mobi/api/v2/fitnessActivities.json" if asynchron: - job = myqueue(queue,handle_sporttracks_sync, - w.id,url,headers,json.dumps(data,default=default)) - return "Asynchronous sync",0 + job = myqueue(queue, handle_sporttracks_sync, + w.id, url, headers, json.dumps(data, default=default)) + return "Asynchronous sync", 0 - response = requests.post(url,headers=headers,data=json.dumps(data,default=default)) + response = requests.post(url, headers=headers, + data=json.dumps(data, default=default)) # check for duplicate error first - if (response.status_code == 409 ): + if (response.status_code == 409): message = "Duplicate error" w.uploadedtosporttracks = -1 stid = -1 w.save() return message, stid - elif (response.status_code == 201 or response.status_code==200): - s= response.json() + elif (response.status_code == 201 or response.status_code == 200): + s = response.json() stid = getidfromresponse(response) w.uploadedtosporttracks = stid w.save() - return 'Successfully synced to SportTracks',stid + return 'Successfully synced to SportTracks', stid else: s = response message = "Something went wrong in workout_sporttracks_upload_view: %s" % s.reason stid = 0 - return message,stid + return message, stid else: message = "You are not authorized to upload this workout" stid = 0 - return message,stid + return message, stid - return message,stid + return message, stid # Create workout from SportTracks Data, which are slightly different # than Strava or Concept2 data -def add_workout_from_data(user,importid,data,strokedata,source='sporttracks', + + +def add_workout_from_data(user, importid, data, strokedata, source='sporttracks', workoutsource='sporttracks'): try: workouttype = data['type'] - except KeyError: # pragma: no cover + except KeyError: # pragma: no cover workouttype = 'other' if workouttype not in [x[0] for x in Workout.workouttypes]: @@ -348,40 +367,40 @@ def add_workout_from_data(user,importid,data,strokedata,source='sporttracks', except: comments = '' - r = Rower.objects.get(user=user) try: rowdatetime = iso8601.parse_date(data['start_time']) - except iso8601.ParseError: # pragma: no cover + except iso8601.ParseError: # pragma: no cover try: - rowdatetime = datetime.datetime.strptime(data['start_time'],"%Y-%m-%d %H:%M:%S") + rowdatetime = datetime.datetime.strptime( + data['start_time'], "%Y-%m-%d %H:%M:%S") rowdatetime = thetimezone.localize(rowdatetime).astimezone(utc) except: try: rowdatetime = dateutil.parser.parse(data['start_time']) rowdatetime = thetimezone.localize(rowdatetime).astimezone(utc) except: - rowdatetime = datetime.datetime.strptime(data['date'],"%Y-%m-%d %H:%M:%S") + rowdatetime = datetime.datetime.strptime( + data['date'], "%Y-%m-%d %H:%M:%S") rowdatetime = thetimezone.localize(rowdatetime).astimezone(utc) starttimeunix = arrow.get(rowdatetime).timestamp() try: title = data['name'] - except: # pragma: no cover + except: # pragma: no cover title = "Imported data" try: res = splitstdata(data['distance']) distance = res[1] times_distance = res[0] - except KeyError: # pragma: no cover + except KeyError: # pragma: no cover try: res = splitstdata(data['heartrate']) times_distance = res[0] distance = 0*times_distance except KeyError: - return (0,"No distance or heart rate data in the workout") - + return (0, "No distance or heart rate data in the workout") try: l = data['location'] @@ -401,14 +420,14 @@ def add_workout_from_data(user,importid,data,strokedata,source='sporttracks', times_location = times_distance latcoord = np.zeros(len(times_distance)) loncoord = np.zeros(len(times_distance)) - if workouttype in mytypes.otwtypes: # pragma: no cover + if workouttype in mytypes.otwtypes: # pragma: no cover workouttype = 'rower' try: res = splitstdata(data['cadence']) times_spm = res[0] spm = res[1] - except KeyError: # pragma: no cover + except KeyError: # pragma: no cover times_spm = times_distance spm = 0*times_distance @@ -420,30 +439,26 @@ def add_workout_from_data(user,importid,data,strokedata,source='sporttracks', times_hr = times_distance hr = 0*times_distance - # create data series and remove duplicates - distseries = pd.Series(distance,index=times_distance) + distseries = pd.Series(distance, index=times_distance) distseries = distseries.groupby(distseries.index).first() - latseries = pd.Series(latcoord,index=times_location) + latseries = pd.Series(latcoord, index=times_location) latseries = latseries.groupby(latseries.index).first() - lonseries = pd.Series(loncoord,index=times_location) + lonseries = pd.Series(loncoord, index=times_location) lonseries = lonseries.groupby(lonseries.index).first() - spmseries = pd.Series(spm,index=times_spm) + spmseries = pd.Series(spm, index=times_spm) spmseries = spmseries.groupby(spmseries.index).first() - hrseries = pd.Series(hr,index=times_hr) + hrseries = pd.Series(hr, index=times_hr) hrseries = hrseries.groupby(hrseries.index).first() - # Create dicts and big dataframe d = { ' Horizontal (meters)': distseries, ' latitude': latseries, ' longitude': lonseries, ' Cadence (stokes/min)': spmseries, - ' HRCur (bpm)' : hrseries, - } - - + ' HRCur (bpm)': hrseries, + } df = pd.DataFrame(d) @@ -466,42 +481,38 @@ def add_workout_from_data(user,importid,data,strokedata,source='sporttracks', df[' PeakDriveForce (lbs)'] = np.zeros(nr_rows) df[' lapIdx'] = np.zeros(nr_rows) - - unixtime = cum_time+starttimeunix unixtime[0] = starttimeunix df['TimeStamp (sec)'] = unixtime - dt = np.diff(cum_time).mean() wsize = round(5./dt) - velo2 = ewmovingaverage(velo,wsize) + velo2 = ewmovingaverage(velo, wsize) df[' Stroke500mPace (sec/500m)'] = 500./velo2 - df = df.fillna(0) - df.sort_values(by='TimeStamp (sec)',ascending=True) + df.sort_values(by='TimeStamp (sec)', ascending=True) timestr = strftime("%Y%m%d-%H%M%S") # csvfilename ='media/Import_'+str(importid)+'.csv' - csvfilename ='media/{code}_{importid}.csv'.format( + csvfilename = 'media/{code}_{importid}.csv'.format( importid=importid, - code = uuid4().hex[:16] - ) + code=uuid4().hex[:16] + ) - res = df.to_csv(csvfilename+'.gz',index_label='index', - compression='gzip') + res = df.to_csv(csvfilename+'.gz', index_label='index', + compression='gzip') - id,message = dataprep.save_workout_database(csvfilename,r, - workouttype=workouttype, - title=title, - notes=comments, - dosmooth=r.dosmooth, - workoutsource='sporttracks') + id, message = dataprep.save_workout_database(csvfilename, r, + workouttype=workouttype, + title=title, + notes=comments, + dosmooth=r.dosmooth, + workoutsource='sporttracks') - return (id,message) + return (id, message) diff --git a/rowers/stravastuff.py b/rowers/stravastuff.py index 9f597ae7..50043c47 100644 --- a/rowers/stravastuff.py +++ b/rowers/stravastuff.py @@ -1,4 +1,21 @@ from __future__ import absolute_import +from rowers.tasks import handle_strava_sync, fetch_strava_workout +from stravalib.exc import ActivityUploadFailed, TimeoutExceeded +from rowers.rower_rules import is_workout_user, ispromember +from rowers.utils import get_strava_stream +from rowers.utils import dologging +from rowers.imports import * +from rowsandall_app.settings import ( + C2_CLIENT_ID, C2_REDIRECT_URI, C2_CLIENT_SECRET, + STRAVA_CLIENT_ID, STRAVA_REDIRECT_URI, STRAVA_CLIENT_SECRET, + SITE_URL +) +import gzip +import rowers.mytypes as mytypes +from rowers.utils import myqueue +from iso8601 import ParseError +import stravalib +from rowers.dataprep import columndict from __future__ import division from __future__ import print_function from __future__ import unicode_literals @@ -16,34 +33,12 @@ queue = django_rq.get_queue('default') queuelow = django_rq.get_queue('low') queuehigh = django_rq.get_queue('low') -from rowers.dataprep import columndict - -from rowers.rower_rules import is_workout_user,ispromember - -import stravalib -from stravalib.exc import ActivityUploadFailed,TimeoutExceeded - -from iso8601 import ParseError -from rowers.utils import myqueue - -import rowers.mytypes as mytypes -import gzip - -from rowers.tasks import handle_strava_sync,fetch_strava_workout - -from rowsandall_app.settings import ( - C2_CLIENT_ID, C2_REDIRECT_URI, C2_CLIENT_SECRET, - STRAVA_CLIENT_ID, STRAVA_REDIRECT_URI, STRAVA_CLIENT_SECRET, - SITE_URL - ) try: from json.decoder import JSONDecodeError -except ImportError: # pragma: no cover +except ImportError: # pragma: no cover JSONDecodeError = ValueError -from rowers.imports import * -from rowers.utils import dologging webhookverification = "kudos_to_rowing" webhooklink = SITE_URL+'/rowers/strava/webhooks/' @@ -67,18 +62,19 @@ oauth_data = { 'base_url': "https://www.strava.com/oauth/token", 'grant_type': 'refresh_token', 'headers': headers, - 'scope':'activity:write,activity:read_all', - } + 'scope': 'activity:write,activity:read_all', +} # Exchange access code for long-lived access token def get_token(code): return imports_get_token(code, oauth_data) + def strava_open(user): t = time.localtime() timestamp = time.strftime('%b-%d-%Y_%H%M', t) - with open('strava_open.log','a') as f: + with open('strava_open.log', 'a') as f: f.write('\n') f.write(timestamp) f.write(' ') @@ -90,13 +86,15 @@ def strava_open(user): f.write(json.dumps(oauth_data)) f.write('\n') token = imports_open(user, oauth_data) - if user.rower.strava_owner_id == 0: # pragma: no cover + if user.rower.strava_owner_id == 0: # pragma: no cover strava_owner_id = set_strava_athlete_id(user) return token + def do_refresh_token(refreshtoken): return imports_do_refresh_token(refreshtoken, oauth_data) + def rower_strava_token_refresh(user): r = Rower.objects.get(user=user) res = do_refresh_token(r.stravarefreshtoken) @@ -113,10 +111,13 @@ def rower_strava_token_refresh(user): return r.stravatoken # Make authorization URL including random string -def make_authorization_url(request): # pragma: no cover + + +def make_authorization_url(request): # pragma: no cover return imports_make_authorization_url(oauth_data) -def strava_establish_push(): # pragma: no cover + +def strava_establish_push(): # pragma: no cover url = "https://www.strava.com/api/v3/push_subscriptions" post_data = { 'client_id': STRAVA_CLIENT_ID, @@ -128,41 +129,43 @@ def strava_establish_push(): # pragma: no cover 'Accept': 'application/json', 'Content-Type': oauth_data['content_type']} - response = requests.post(url,data=post_data) + response = requests.post(url, data=post_data) return response.status_code -def strava_list_push(): # pragma: no cover + +def strava_list_push(): # pragma: no cover url = "https://www.strava.com/api/v3/push_subscriptions" params = { 'client_id': STRAVA_CLIENT_ID, 'client_secret': STRAVA_CLIENT_SECRET, } - response = requests.get(url,params=params) + response = requests.get(url, params=params) if response.status_code == 200: data = response.json() return [w['id'] for w in data] return [] -def strava_push_delete(id): # pragma: no cover + +def strava_push_delete(id): # pragma: no cover url = "https://www.strava.com/api/v3/push_subscriptions/{id}".format(id=id) params = { 'client_id': STRAVA_CLIENT_ID, 'client_secret': STRAVA_CLIENT_SECRET, } - response = requests.delete(url,json=params) + response = requests.delete(url, json=params) return response.status_code def set_strava_athlete_id(user): r = Rower.objects.get(user=user) - if (r.stravatoken == '') or (r.stravatoken is None): # pragma: no cover + if (r.stravatoken == '') or (r.stravatoken is None): # pragma: no cover s = "Token doesn't exist. Need to authorize" - return custom_exception_handler(401,s) - elif (r.stravatokenexpirydate is None or timezone.now()+timedelta(seconds=3599)>r.stravatokenexpirydate): - token = imports_open(user,oauth_data) + return custom_exception_handler(401, s) + elif (r.stravatokenexpirydate is None or timezone.now()+timedelta(seconds=3599) > r.stravatokenexpirydate): + token = imports_open(user, oauth_data) authorizationstring = str('Bearer ' + r.stravatoken) headers = {'Authorization': authorizationstring, @@ -170,9 +173,9 @@ def set_strava_athlete_id(user): 'Content-Type': 'application/json'} url = "https://www.strava.com/api/v3/athlete" - response = requests.get(url,headers=headers,params={}) + response = requests.get(url, headers=headers, params={}) - if response.status_code == 200: # pragma: no cover + if response.status_code == 200: # pragma: no cover r.strava_owner_id = response.json()['id'] r.save() return response.json()['id'] @@ -181,15 +184,15 @@ def set_strava_athlete_id(user): # Get list of workouts available on Strava -def get_strava_workout_list(user,limit_n=0): +def get_strava_workout_list(user, limit_n=0): r = Rower.objects.get(user=user) - if (r.stravatoken == '') or (r.stravatoken is None): # pragma: no cover + if (r.stravatoken == '') or (r.stravatoken is None): # pragma: no cover s = "Token doesn't exist. Need to authorize" - return custom_exception_handler(401,s) - elif (r.stravatokenexpirydate is None or timezone.now()+timedelta(seconds=3599)>r.stravatokenexpirydate): # pragma: no cover + return custom_exception_handler(401, s) + elif (r.stravatokenexpirydate is None or timezone.now()+timedelta(seconds=3599) > r.stravatokenexpirydate): # pragma: no cover s = "Token expired. Needs to refresh." - return custom_exception_handler(401,s) + return custom_exception_handler(401, s) else: # ready to fetch. Hurray authorizationstring = str('Bearer ' + r.stravatoken) @@ -199,26 +202,24 @@ def get_strava_workout_list(user,limit_n=0): url = "https://www.strava.com/api/v3/athlete/activities" - if limit_n==0: + if limit_n == 0: params = {} - else: # pragma: no cover - params = {'per_page':limit_n} - - s = requests.get(url,headers=headers,params=params) + else: # pragma: no cover + params = {'per_page': limit_n} + s = requests.get(url, headers=headers, params=params) return s -from rowers.utils import get_strava_stream - -def async_get_workout(user,stravaid): +def async_get_workout(user, stravaid): try: token = strava_open(user) - except NoTokenError: # pragma: no cover + except NoTokenError: # pragma: no cover return 0 - csvfilename = 'media/{code}_{stravaid}.csv'.format(code=uuid4().hex[:16],stravaid=stravaid) + csvfilename = 'media/{code}_{stravaid}.csv'.format( + code=uuid4().hex[:16], stravaid=stravaid) job = myqueue(queue, fetch_strava_workout, user.rower.stravatoken, @@ -230,16 +231,18 @@ def async_get_workout(user,stravaid): return job # Get a Strava workout summary data and stroke data by ID -def get_workout(user,stravaid,do_async=True): - return async_get_workout(user,stravaid) + + +def get_workout(user, stravaid, do_async=True): + return async_get_workout(user, stravaid) # Generate Workout data for Strava (a TCX file) -def createstravaworkoutdata(w,dozip=True): +def createstravaworkoutdata(w, dozip=True): filename = w.csvfilename try: row = rowingdata(csvfile=filename) - except IOError: # pragma: no cover + except IOError: # pragma: no cover data = dataprep.read_df_sql(w.id) try: datalength = len(data) @@ -247,16 +250,16 @@ def createstravaworkoutdata(w,dozip=True): datalength = 0 if datalength != 0: - data.rename(columns = columndict,inplace=True) + data.rename(columns=columndict, inplace=True) res = data.to_csv(w.csvfilename+'.gz', index_label='index', compression='gzip') try: row = rowingdata(csvfile=filename) except IOError: - return '','Error - could not find rowing data' + return '', 'Error - could not find rowing data' else: - return '','Error - could not find rowing data' + return '', 'Error - could not find rowing data' tcxfilename = filename[:-4]+'.tcx' try: @@ -264,114 +267,115 @@ def createstravaworkoutdata(w,dozip=True): except TypeError: newnotes = 'from '+w.workoutsource+' via rowsandall.com' - row.exporttotcx(tcxfilename,notes=newnotes) + row.exporttotcx(tcxfilename, notes=newnotes) if dozip: gzfilename = tcxfilename+'.gz' - with open(tcxfilename,'rb') as inF: + with open(tcxfilename, 'rb') as inF: s = inF.read() - with gzip.GzipFile(gzfilename,'wb') as outF: + with gzip.GzipFile(gzfilename, 'wb') as outF: outF.write(s) try: os.remove(tcxfilename) - except WindowError: # pragma: no cover + except WindowError: # pragma: no cover pass - return gzfilename,"" + return gzfilename, "" - - return tcxfilename,"" + return tcxfilename, "" # Upload the TCX file to Strava and set the workout activity type # to rowing on Strava -def handle_stravaexport(f2,workoutname,stravatoken,description='', - activity_type='Rowing',quick=False,asynchron=False): +def handle_stravaexport(f2, workoutname, stravatoken, description='', + activity_type='Rowing', quick=False, asynchron=False): # w = Workout.objects.get(id=workoutid) client = stravalib.Client(access_token=stravatoken) - act = client.upload_activity(f2,'tcx.gz',name=workoutname) + act = client.upload_activity(f2, 'tcx.gz', name=workoutname) try: - if quick: # pragma: no cover + if quick: # pragma: no cover res = act.wait(poll_interval=2.0, timeout=10) message = 'Workout successfully synchronized to Strava' else: - res = act.wait(poll_interval=5.0,timeout=30) + res = act.wait(poll_interval=5.0, timeout=30) message = 'Workout successfully synchronized to Strava' - except: # pragma: no cover + except: # pragma: no cover res = 0 message = 'Strava upload timed out' - # description doesn't work yet. Have to wait for stravalib to update if res: try: - act = client.update_activity(res.id,activity_type=activity_type,description=description,device_name='Rowsandall.com') - except TypeError: # pragma: no cover - act = client.update_activity(res.id,activity_type=activity_type,description=description) - else: # pragma: no cover + act = client.update_activity( + res.id, activity_type=activity_type, description=description, device_name='Rowsandall.com') + except TypeError: # pragma: no cover + act = client.update_activity( + res.id, activity_type=activity_type, description=description) + else: # pragma: no cover message = 'Strava activity update timed out.' - return (0,message) + return (0, message) - return (res.id,message) + return (res.id, message) -def workout_strava_upload(user,w, quick=False,asynchron=True): +def workout_strava_upload(user, w, quick=False, asynchron=True): try: thetoken = strava_open(user) - except NoTokenError: # pragma: no cover - return "Please connect to Strava first",0 + except NoTokenError: # pragma: no cover + return "Please connect to Strava first", 0 message = "Uploading to Strava" - stravaid=-1 + stravaid = -1 r = Rower.objects.get(user=user) res = -1 - if (r.stravatoken == '') or (r.stravatoken is None): # pragma: no cover + if (r.stravatoken == '') or (r.stravatoken is None): # pragma: no cover s = "Token doesn't exist. Need to authorize" raise NoTokenError("Your hovercraft is full of eels") - if (is_workout_user(user,w)): + if (is_workout_user(user, w)): if asynchron: tcxfile, tcxmesg = createstravaworkoutdata(w) - if not tcxfile: # pragma: no cover - return "Failed to create workout data",0 + if not tcxfile: # pragma: no cover + return "Failed to create workout data", 0 activity_type = r.stravaexportas if r.stravaexportas == 'match': try: activity_type = mytypes.stravamapping[w.workouttype] - except KeyError: # pragma: no cover + except KeyError: # pragma: no cover activity_type = 'Rowing' job = myqueue(queue, handle_strava_sync, r.stravatoken, w.id, - tcxfile,w.name,activity_type, + tcxfile, w.name, activity_type, w.notes ) - dologging('strava_export_log.log','Exporting as {t} from {w}'.format(t=activity_type,w=w.workouttype)) - return "Asynchronous sync",-1 + dologging('strava_export_log.log', 'Exporting as {t} from {w}'.format( + t=activity_type, w=w.workouttype)) + return "Asynchronous sync", -1 try: - tcxfile,tcxmesg = createstravaworkoutdata(w) + tcxfile, tcxmesg = createstravaworkoutdata(w) if tcxfile: activity_type = r.stravaexportas if r.stravaexportas == 'match': try: activity_type = mytypes.stravamapping[w.workouttype] - except KeyError: # pragma: no cover + except KeyError: # pragma: no cover activity_type = 'Rowing' - with open(tcxfile,'rb') as f: + with open(tcxfile, 'rb') as f: try: description = w.notes+'\n from '+w.workoutsource+' via rowsandall.com' except TypeError: description = ' via rowsandall.com' - res,mes = handle_stravaexport( - f,w.name, + res, mes = handle_stravaexport( + f, w.name, r.stravatoken, description=description, - activity_type=activity_type,quick=quick,asynchron=asynchron) - if res==0: # pragma: no cover + activity_type=activity_type, quick=quick, asynchron=asynchron) + if res == 0: # pragma: no cover message = mes w.uploadedtostrava = -1 stravaid = -1 @@ -380,29 +384,29 @@ def workout_strava_upload(user,w, quick=False,asynchron=True): os.remove(tcxfile) except WindowsError: pass - return message,stravaid + return message, stravaid w.uploadedtostrava = res w.save() try: os.remove(tcxfile) - except WindowsError: # pragma: no cover + except WindowsError: # pragma: no cover pass message = mes stravaid = res - return message,stravaid - else: # pragma: no cover + return message, stravaid + else: # pragma: no cover message = "Strava TCX data error "+tcxmesg w.uploadedtostrava = -1 stravaid = -1 w.save() return message, stravaid - except ActivityUploadFailed as e: # pragma: no cover + except ActivityUploadFailed as e: # pragma: no cover message = "Strava Upload error: %s" % e w.uploadedtostrava = -1 stravaid = -1 w.save() os.remove(tcxfile) - return message,stravaid - return message,stravaid # pragma: no cover + return message, stravaid + return message, stravaid # pragma: no cover diff --git a/rowers/tasks.py b/rowers/tasks.py index 4cd63ade..3a69de1d 100644 --- a/rowers/tasks.py +++ b/rowers/tasks.py @@ -1,4 +1,21 @@ from __future__ import absolute_import +import math +from rowers.courseutils import ( + coursetime_paths, coursetime_first, time_in_path, + InvalidTrajectoryError +) +from rowers.emails import send_template_email +from rowers.nkimportutils import ( + get_nk_summary, get_nk_allstats, get_nk_intervalstats, getdict, strokeDataToDf, + add_workout_from_data +) +from rowers.utils import get_strava_stream +from stravalib.exc import ActivityUploadFailed +import stravalib +import arrow +import rowers.longtask as longtask +import requests +import rowers.datautils as datautils from __future__ import division from __future__ import print_function from __future__ import unicode_literals @@ -66,21 +83,20 @@ from django_rq import job from django.utils import timezone from django.utils.html import strip_tags -from rowers.utils import deserialize_list,ewmovingaverage,wavg,dologging +from rowers.utils import deserialize_list, ewmovingaverage, wavg, dologging import rowers.utils as utils from rowers.emails import htmlstrip from rowers import mytypes - from rowers.dataprepnodjango import ( update_strokedata, - getsmallrowdata_db, updatecpdata_sql,update_c2id_sql, + getsmallrowdata_db, updatecpdata_sql, update_c2id_sql, update_workout_field_sql, update_agegroup_db, - add_c2_stroke_data_db,totaltime_sec_to_string, - create_c2_stroke_data_db,update_empower, - database_url_debug,database_url,dataprep, + add_c2_stroke_data_db, totaltime_sec_to_string, + create_c2_stroke_data_db, update_empower, + database_url_debug, database_url, dataprep, # create_strava_stroke_data_db ) @@ -89,23 +105,14 @@ from rowers.opaque import encoder from django.core.mail import ( send_mail, - EmailMessage,EmailMultiAlternatives, + EmailMessage, EmailMultiAlternatives, ) from django.template import Context from django.db.utils import OperationalError -from jinja2 import Template,Environment,FileSystemLoader -env = Environment(loader = FileSystemLoader(["rowers/templates"])) +from jinja2 import Template, Environment, FileSystemLoader +env = Environment(loader=FileSystemLoader(["rowers/templates"])) -import rowers.datautils as datautils -import rowers.utils as utils -import requests -import rowers.longtask as longtask -import arrow -import stravalib -from stravalib.exc import ActivityUploadFailed - -from rowers.utils import get_strava_stream def safetimedelta(x): try: @@ -113,25 +120,17 @@ def safetimedelta(x): except ValueError: return timedelta(seconds=0) + siteurl = SITE_URL -from rowers.nkimportutils import ( - get_nk_summary, get_nk_allstats, get_nk_intervalstats,getdict,strokeDataToDf, - add_workout_from_data - ) # testing task -from rowers.emails import send_template_email -from rowers.courseutils import ( - coursetime_paths, coursetime_first, time_in_path, - InvalidTrajectoryError - ) # Concept2 logbook sends over split data for each interval # We use it here to generate a custom summary # Some users complained about small differences -def summaryfromsplitdata(splitdata,data,filename,sep='|',workouttype='rower'): +def summaryfromsplitdata(splitdata, data, filename, sep='|', workouttype='rower'): workouttype = workouttype.lower() totaldist = data['distance'] @@ -142,124 +141,123 @@ def summaryfromsplitdata(splitdata,data,filename,sep='|',workouttype='rower'): spm = 0 try: resttime = data['rest_time']/10. - except KeyError: # pragma: no cover + except KeyError: # pragma: no cover resttime = 0 try: restdistance = data['rest_distance'] - except KeyError: # pragma: no cover + except KeyError: # pragma: no cover restdistance = 0 try: avghr = data['heart_rate']['average'] - except KeyError: # pragma: no cover + except KeyError: # pragma: no cover avghr = 0 try: maxhr = data['heart_rate']['max'] - except KeyError: # pragma: no cover + except KeyError: # pragma: no cover maxhr = 0 try: avgpace = 500.*totaltime/totaldist - except (ZeroDivisionError,OverflowError): # pragma: no cover + except (ZeroDivisionError, OverflowError): # pragma: no cover avgpace = 0. try: restpace = 500.*resttime/restdistance - except (ZeroDivisionError,OverflowError): # pragma: no cover + except (ZeroDivisionError, OverflowError): # pragma: no cover restpace = 0. velo = totaldist/totaltime avgpower = 2.8*velo**(3.0) - if workouttype in ['bike','bikeerg']: # pragma: no cover + if workouttype in ['bike', 'bikeerg']: # pragma: no cover velo = velo/2. avgpower = 2.8*velo**(3.0) velo = velo*2 - try: restvelo = restdistance/resttime - except (ZeroDivisionError,OverflowError): # pragma: no cover + except (ZeroDivisionError, OverflowError): # pragma: no cover restvelo = 0 restpower = 2.8*restvelo**(3.0) - if workouttype in ['bike','bikeerg']: # pragma: no cover + if workouttype in ['bike', 'bikeerg']: # pragma: no cover restvelo = restvelo/2. restpower = 2.8*restvelo**(3.0) restvelo = restvelo*2 try: avgdps = totaldist/data['stroke_count'] - except (ZeroDivisionError,OverflowError,KeyError): + except (ZeroDivisionError, OverflowError, KeyError): avgdps = 0 - from rowingdata import summarystring,workstring,interval_string + from rowingdata import summarystring, workstring, interval_string - - sums = summarystring(totaldist,totaltime,avgpace,spm,avghr,maxhr, - avgdps,avgpower,readFile=filename, + sums = summarystring(totaldist, totaltime, avgpace, spm, avghr, maxhr, + avgdps, avgpower, readFile=filename, separator=sep) - sums += workstring(totaldist,totaltime,avgpace,spm,avghr,maxhr, - avgdps,avgpower,separator=sep,symbol='W') + sums += workstring(totaldist, totaltime, avgpace, spm, avghr, maxhr, + avgdps, avgpower, separator=sep, symbol='W') - sums += workstring(restdistance,resttime,restpace,0,0,0,0,restpower, + sums += workstring(restdistance, resttime, restpace, 0, 0, 0, 0, restpower, separator=sep, symbol='R') sums += '\nWorkout Details\n' sums += '#-{sep}SDist{sep}-Split-{sep}-SPace-{sep}-Pwr-{sep}SPM-{sep}AvgHR{sep}MaxHR{sep}DPS-\n'.format( sep=sep - ) + ) - intervalnr=0 + intervalnr = 0 sa = [] results = [] try: - timebased = data['workout_type'] in ['FixedTimeSplits','FixedTimeInterval'] - except KeyError: # pragma: no cover + timebased = data['workout_type'] in [ + 'FixedTimeSplits', 'FixedTimeInterval'] + except KeyError: # pragma: no cover timebased = False for interval in splitdata: try: idist = interval['distance'] - except KeyError: # pragma: no cover + except KeyError: # pragma: no cover idist = 0 try: itime = interval['time']/10. - except KeyError: # pragma: no cover + except KeyError: # pragma: no cover itime = 0 try: ipace = 500.*itime/idist - except (ZeroDivisionError,OverflowError): # pragma: no cover + except (ZeroDivisionError, OverflowError): # pragma: no cover ipace = 180. try: ispm = interval['stroke_rate'] - except KeyError: # pragma: no cover + except KeyError: # pragma: no cover ispm = 0 try: irest_time = interval['rest_time']/10. - except KeyError: # pragma: no cover + except KeyError: # pragma: no cover irest_time = 0 try: iavghr = interval['heart_rate']['average'] - except KeyError: # pragma: no cover + except KeyError: # pragma: no cover iavghr = 0 try: imaxhr = interval['heart_rate']['average'] - except KeyError: # pragma: no cover + except KeyError: # pragma: no cover imaxhr = 0 # create interval values - iarr = [idist,'meters','work'] + iarr = [idist, 'meters', 'work'] resarr = [itime] - if timebased: # pragma: no cover - iarr = [itime,'seconds','work'] + if timebased: # pragma: no cover + iarr = [itime, 'seconds', 'work'] resarr = [idist] if irest_time > 0: - iarr += [irest_time,'seconds','rest'] + iarr += [irest_time, 'seconds', 'rest'] try: resarr += [interval['rest_distance']] except KeyError: @@ -271,49 +269,51 @@ def summaryfromsplitdata(splitdata,data,filename,sep='|',workouttype='rower'): if itime != 0: ivelo = idist/itime ipower = 2.8*ivelo**(3.0) - if workouttype in ['bike','bikeerg']: # pragma: no cover + if workouttype in ['bike', 'bikeerg']: # pragma: no cover ipower = 2.8*(ivelo/2.)**(3.0) - else: # pragma: no cover + else: # pragma: no cover ivelo = 0 ipower = 0 - sums += interval_string(intervalnr,idist,itime,ipace,ispm, - iavghr,imaxhr,0,ipower,separator=sep) - intervalnr+=1 + sums += interval_string(intervalnr, idist, itime, ipace, ispm, + iavghr, imaxhr, 0, ipower, separator=sep) + intervalnr += 1 - return sums,sa,results + return sums, sa, results @app.task -def handle_request_post(url, data,debug=False, **kwargs): # pragma: no cover - response = requests.post(url,data) - dologging('upload_api.log',data) - dologging('upload_api.log',response.status_code) +def handle_request_post(url, data, debug=False, **kwargs): # pragma: no cover + response = requests.post(url, data) + dologging('upload_api.log', data) + dologging('upload_api.log', response.status_code) return response.status_code @app.task -def add(x, y): # pragma: no cover +def add(x, y): # pragma: no cover return x + y @app.task -def handle_c2_sync(workoutid,url,headers,data,debug=False,**kwargs): - response = requests.post(url,headers=headers,data=data) - if response.status_code not in [200,201]: # pragma: no cover +def handle_c2_sync(workoutid, url, headers, data, debug=False, **kwargs): + response = requests.post(url, headers=headers, data=data) + if response.status_code not in [200, 201]: # pragma: no cover return 0 s = response.json() c2id = s['data']['id'] - res = update_workout_field_sql(workoutid,'uploadedtoc2',c2id,debug=debug) + res = update_workout_field_sql( + workoutid, 'uploadedtoc2', c2id, debug=debug) return res + @app.task -def handle_sporttracks_sync(workoutid,url,headers,data,debug=False,**kwargs): - response = requests.post(url,headers=headers,data=data) - if response.status_code not in [200,201]: # pragma: no cover +def handle_sporttracks_sync(workoutid, url, headers, data, debug=False, **kwargs): + response = requests.post(url, headers=headers, data=data) + if response.status_code not in [200, 201]: # pragma: no cover return 0 t = response.json() @@ -323,114 +323,124 @@ def handle_sporttracks_sync(workoutid,url,headers,data,debug=False,**kwargs): id = int(m) - res = update_workout_field_sql(workoutid,'uploadedtosporttracks',id,debug=debug) + res = update_workout_field_sql( + workoutid, 'uploadedtosporttracks', id, debug=debug) return 1 @app.task -def handle_strava_sync(stravatoken,workoutid,filename,name,activity_type,description,debug=False,**kwargs): +def handle_strava_sync(stravatoken, workoutid, filename, name, activity_type, description, debug=False, **kwargs): client = stravalib.Client(access_token=stravatoken) failed = False try: - with open(filename,'rb') as f: + with open(filename, 'rb') as f: try: - act = client.upload_activity(f,'tcx.gz',name=name) + act = client.upload_activity(f, 'tcx.gz', name=name) try: res = act.wait(poll_interval=1.0, timeout=10) - except stravalib.exc.ActivityUploadFailed: # pragma: no cover - dologging('strava_fail.log','Strava upload failed for Workout {id} ActivityUploadFailed'.format(id=workoutid)) + except stravalib.exc.ActivityUploadFailed: # pragma: no cover + dologging('strava_fail.log', 'Strava upload failed for Workout {id} ActivityUploadFailed'.format( + id=workoutid)) tb = traceback.format_exc() - dologging('strava_fail.log',tb) + dologging('strava_fail.log', tb) failed = True - except JSONDecodeError: # pragma: no cover - dologging('strava_fail.log','Strava upload failed for Workout {id} JSONDecodeError'.format(id=workoutid)) + except JSONDecodeError: # pragma: no cover + dologging('strava_fail.log', 'Strava upload failed for Workout {id} JSONDecodeError'.format( + id=workoutid)) tb = traceback.format_exc() - dologging('strava_fail.log',tb) + dologging('strava_fail.log', tb) failed = True - except stravalib.exc.ObjectNotFound: # pragma: no cover - dologging('strava_fail.log','Strava upload failed for Workout {id} ObjectNotFound'.format(id=workoutid)) + except stravalib.exc.ObjectNotFound: # pragma: no cover + dologging('strava_fail.log', 'Strava upload failed for Workout {id} ObjectNotFound'.format( + id=workoutid)) tb = traceback.format_exc() - dologging('strava_fail.log',tb) + dologging('strava_fail.log', tb) failed = True - except IndexError: # pragma: no cover - dologging('strava_fail.log','Strava upload failed for Workout {id} IndexError'.format(id=workoutid)) + except IndexError: # pragma: no cover + dologging('strava_fail.log', 'Strava upload failed for Workout {id} IndexError'.format( + id=workoutid)) tb = traceback.format_exc() - dologging('strava_fail.log',tb) + dologging('strava_fail.log', tb) failed = True # temporary hack until stravalib is fixed if 'LatLon' in tb: - dologging('strava_fail.log','Trying temporary fix') + dologging('strava_fail.log', 'Trying temporary fix') failed = False - except (ActivityUploadFailed, stravalib.exc.RateLimitExceeded): # pragma: no cover - dologging('strava_fail.log','Strava upload failed for Workout {id}'.format(id=workoutid)) + except (ActivityUploadFailed, stravalib.exc.RateLimitExceeded): # pragma: no cover + dologging( + 'strava_fail.log', 'Strava upload failed for Workout {id}'.format(id=workoutid)) tb = traceback.format_exc() - dologging('strava_fail.log',tb) + dologging('strava_fail.log', tb) failed = True - except FileNotFoundError: # pragma: no cover - dologging('strava_fail.log','Strava upload failed for Workout {id}'.format(id=workoutid)) + except FileNotFoundError: # pragma: no cover + dologging('strava_fail.log', + 'Strava upload failed for Workout {id}'.format(id=workoutid)) failed = True if not failed: - result = update_workout_field_sql(workoutid,'uploadedtostrava',res.id,debug=debug) + result = update_workout_field_sql( + workoutid, 'uploadedtostrava', res.id, debug=debug) try: - act = client.update_activity(res.id,activity_type=activity_type, - description=description,device_name='Rowsandall.com') - dologging('strava_export_log.log','Updating activity {id} to {type}'.format( + act = client.update_activity(res.id, activity_type=activity_type, + description=description, device_name='Rowsandall.com') + dologging('strava_export_log.log', 'Updating activity {id} to {type}'.format( id=workoutid, type=activity_type )) - except TypeError: # pragma: no cover - act = client.update_activity(res.id,activity_type=activity_type, - description=description) - dologging('strava_export_log.log','Updating activity {id} to {type}'.format( + except TypeError: # pragma: no cover + act = client.update_activity(res.id, activity_type=activity_type, + description=description) + dologging('strava_export_log.log', 'Updating activity {id} to {type}'.format( id=workoutid, type=activity_type )) - except: # pragma: no cover + except: # pragma: no cover e = sys.exc_info()[0] - dologging('strava_export_log.log','Update activity failed with error {e} for {id} to {type}'.format( + dologging('strava_export_log.log', 'Update activity failed with error {e} for {id} to {type}'.format( id=workoutid, type=activity_type, e=e )) try: os.remove(filename) - except: # pragma: no cover + except: # pragma: no cover pass return 1 + @app.task def handle_c2_import_stroke_data(c2token, - c2id,workoutid, + c2id, workoutid, starttimeunix, - csvfilename,debug=True,**kwargs): + csvfilename, debug=True, **kwargs): - if 'workouttype' in kwargs: # pragma: no cover + if 'workouttype' in kwargs: # pragma: no cover workouttype = kwargs['workouttype'] else: workouttype = 'rower' authorizationstring = str('Bearer ' + c2token) headers = {'Authorization': authorizationstring, - 'user-agent': 'sanderroosendaal', - 'Content-Type': 'application/json'} + 'user-agent': 'sanderroosendaal', + 'Content-Type': 'application/json'} url = "https://log.concept2.com/api/users/me/results/"+str(c2id)+"/strokes" - s = requests.get(url,headers=headers) + s = requests.get(url, headers=headers) if s.status_code == 200: strokedata = pd.DataFrame.from_dict(s.json()['data']) result = add_c2_stroke_data_db( - strokedata,workoutid,starttimeunix, - csvfilename,debug=debug,workouttype=workouttype + strokedata, workoutid, starttimeunix, + csvfilename, debug=debug, workouttype=workouttype ) return 1 - else: # pragma: no cover - url = "https://log.concept2.com/api/users/me/results/{id}".format(id=c2id) + else: # pragma: no cover + url = "https://log.concept2.com/api/users/me/results/{id}".format( + id=c2id) - s = requests.get(url,headers=headers) + s = requests.get(url, headers=headers) if s.status_code == 200: workoutdata = s.json()['data'] @@ -445,7 +455,8 @@ def handle_c2_import_stroke_data(c2token, weightcategory = 'lwt' totaltime = workoutdata['time']/10. duration = totaltime_sec_to_string(totaltime) - duration = datetime.datetime.strptime(duration,'%H:%M:%S.%f').time() + duration = datetime.datetime.strptime( + duration, '%H:%M:%S.%f').time() try: timezone_str = workoutdata['timezone'] @@ -460,32 +471,32 @@ def handle_c2_import_stroke_data(c2token, ).strftime('%H:%M:%S') result = create_c2_stroke_data_db( - distance,duration,workouttype, - workoutid,starttimeunix, - csvfilename,debug=debug, + distance, duration, workouttype, + workoutid, starttimeunix, + csvfilename, debug=debug, ) - return 1 return 0 - return 0 # pragma: no cover + return 0 # pragma: no cover -def getagegrouprecord(age,sex='male',weightcategory='hwt', - distance=2000,duration=None,indf=pd.DataFrame()): + +def getagegrouprecord(age, sex='male', weightcategory='hwt', + distance=2000, duration=None, indf=pd.DataFrame()): power = 0 if not duration: try: df = indf[indf['distance'] == distance] - except KeyError: # pragma: no cover + except KeyError: # pragma: no cover df = pd.DataFrame() else: duration = 60*int(duration) try: df = indf[indf['duration'] == duration] - except KeyError: # pragma: no cover + except KeyError: # pragma: no cover df = pd.DataFrame() if not df.empty: @@ -493,13 +504,15 @@ def getagegrouprecord(age,sex='male',weightcategory='hwt', powers = df['power'] #poly_coefficients = np.polyfit(ages,powers,6) - fitfunc = lambda pars, x: np.abs(pars[0])*(1-x/max(120,pars[1]))-np.abs(pars[2])*np.exp(-x/np.abs(pars[3]))+np.abs(pars[4])*(np.sin(np.pi*x/max(50,pars[5]))) - errfunc = lambda pars, x,y: fitfunc(pars,x)-y + def fitfunc(pars, x): return np.abs(pars[0])*(1-x/max(120, pars[1]))-np.abs( + pars[2])*np.exp(-x/np.abs(pars[3]))+np.abs(pars[4])*(np.sin(np.pi*x/max(50, pars[5]))) - p0 = [700,120,700,10,100,100] + def errfunc(pars, x, y): return fitfunc(pars, x)-y - p1, success = optimize.leastsq(errfunc,p0[:], - args = (ages,powers)) + p0 = [700, 120, 700, 10, 100, 100] + + p1, success = optimize.leastsq(errfunc, p0[:], + args=(ages, powers)) if success and age is not None: power = fitfunc(p1, float(age)) @@ -507,25 +520,26 @@ def getagegrouprecord(age,sex='male',weightcategory='hwt', #power = np.polyval(poly_coefficients,age) power = 0.5*(np.abs(power)+power) - elif age is not None: # pragma: no cover + elif age is not None: # pragma: no cover new_age = np.range([age]) ww = griddata(ages.values, powers.values, - new_age,method='linear',rescale=True) + new_age, method='linear', rescale=True) power = 0.5*(np.abs(power)+power) - else: # pragma: no cover + else: # pragma: no cover power = 0 return power -def polygon_to_path(polygon,debug=True): + +def polygon_to_path(polygon, debug=True): pid = polygon[0] query = "SELECT id, latitude, longitude FROM rowers_geopoint WHERE polygon_id = {pid} ORDER BY order_in_poly ASC".format( pid=pid - ) + ) if debug: engine = create_engine(database_url_debug, echo=False) - else: # pragma: no cover + else: # pragma: no cover engine = create_engine(database_url, echo=False) with engine.connect() as conn, conn.begin(): @@ -537,42 +551,44 @@ def polygon_to_path(polygon,debug=True): s = [] for point in points: - s.append([point[1],point[2]]) + s.append([point[1], point[2]]) p = path.Path(s[:-1]) return p + @app.task(bind=True) def handle_check_race_course(self, - f1,workoutid,courseid, - recordid,useremail,userfirstname, - **kwargs): # pragma: no cover + f1, workoutid, courseid, + recordid, useremail, userfirstname, + **kwargs): # pragma: no cover - logfile = 'courselog_{workoutid}_{courseid}.log'.format(workoutid=workoutid,courseid=courseid) + logfile = 'courselog_{workoutid}_{courseid}.log'.format( + workoutid=workoutid, courseid=courseid) - if 'debug' in kwargs: # pragma: no cover + if 'debug' in kwargs: # pragma: no cover debug = kwargs['debug'] else: debug = False - if 'splitsecond' in kwargs: # pragma: no cover + if 'splitsecond' in kwargs: # pragma: no cover splitsecond = kwargs['splitsecond'] else: splitsecond = 0 - if 'referencespeed' in kwargs: # pragma: no cover + if 'referencespeed' in kwargs: # pragma: no cover referencespeed = kwargs['referencespeed'] else: referencespeed = 5.0 - if 'coursedistance' in kwargs: # pragma: no cover + if 'coursedistance' in kwargs: # pragma: no cover coursedistance = kwargs['coursedistance'] else: coursedistance = 0 mode = 'race' - if 'mode' in kwargs: # pragma: no cover + if 'mode' in kwargs: # pragma: no cover mode = kwargs['mode'] summary = False @@ -583,23 +599,22 @@ def handle_check_race_course(self, if 'successemail' in kwargs: successemail = kwargs['successemail'] - columns = ['time',' latitude',' longitude','cum_dist'] + columns = ['time', ' latitude', ' longitude', 'cum_dist'] try: row = rdata(csvfile=f1) except IOError: # pragma: no cover try: row = rdata(csvfile=f1 + '.csv') - except IOError: # pragma: no cover + except IOError: # pragma: no cover try: row = rdata(csvfile=f1 + '.gz') - except IOError: # pragma: no cover + except IOError: # pragma: no cover return 0 - row.extend_data() - #row.df.interpolate(inplace=True) + # row.df.interpolate(inplace=True) row.calc_dist_from_gps() rowdata = row.df @@ -607,41 +622,38 @@ def handle_check_race_course(self, try: s = rowdata[' latitude'] - except KeyError: # pragma: no cover + except KeyError: # pragma: no cover return 0 - rowdata.rename(columns = { - ' latitude':'latitude', - ' longitude':'longitude', + rowdata.rename(columns={ + ' latitude': 'latitude', + ' longitude': 'longitude', 'TimeStamp (sec)': 'time', - }, inplace=True) + }, inplace=True) - rowdata.fillna(method='backfill',inplace=True) + rowdata.fillna(method='backfill', inplace=True) - rowdata['time'] = rowdata['time']-rowdata.loc[0,'time'] - rowdata = rowdata[rowdata['time']>splitsecond] + rowdata['time'] = rowdata['time']-rowdata.loc[0, 'time'] + rowdata = rowdata[rowdata['time'] > splitsecond] # we may want to expand the time (interpolate) rowdata['dt'] = rowdata['time'].apply( lambda x: safetimedelta(x) - ) + ) - - rowdata = rowdata.resample('100ms',on='dt').mean() + rowdata = rowdata.resample('100ms', on='dt').mean() rowdata = rowdata.interpolate() # initiate database engine - if debug: # pragma: no cover + if debug: # pragma: no cover engine = create_engine(database_url_debug, echo=False) else: engine = create_engine(database_url, echo=False) - # get polygons query = "SELECT id,name FROM rowers_geopolygon WHERE course_id = {courseid} ORDER BY order_in_course ASC".format( courseid=courseid - ) - + ) with engine.connect() as conn, conn.begin(): result = conn.execute(query) @@ -653,7 +665,7 @@ def handle_check_race_course(self, paths = [] for polygon in polygons: - path = polygon_to_path(polygon,debug=debug) + path = polygon_to_path(polygon, debug=debug) paths.append(path) startsecond = 0 @@ -662,29 +674,31 @@ def handle_check_race_course(self, # check how many times went through start polygon try: try: - entrytimes,entrydistances = time_in_path(rowdata,paths[0],maxmin='max',getall=True, - name=polygons[0].name,logfile=logfile) - except AttributeError: # for testing - entrytimes, entrydistances = time_in_path(rowdata,paths[0],maxmin='max',getall=True, - name='Start',logfile=logfile) - with open(logfile,'ab') as f: + entrytimes, entrydistances = time_in_path(rowdata, paths[0], maxmin='max', getall=True, + name=polygons[0].name, logfile=logfile) + except AttributeError: # for testing + entrytimes, entrydistances = time_in_path(rowdata, paths[0], maxmin='max', getall=True, + name='Start', logfile=logfile) + with open(logfile, 'ab') as f: t = time.localtime() - timestamp = bytes('{t}'.format(t=time.strftime('%b-%d-%Y_%H%M', t)),'utf-8') + timestamp = bytes('{t}'.format( + t=time.strftime('%b-%d-%Y_%H%M', t)), 'utf-8') f.write(b'\n') - f.write(bytes('Course id {n}, Record id {m}'.format(n=courseid,m=recordid),'utf-8')) + f.write(bytes('Course id {n}, Record id {m}'.format( + n=courseid, m=recordid), 'utf-8')) f.write(b'\n') f.write(timestamp) f.write(b' ') - f.write(bytes('Found {n} entrytimes'.format(n=len(entrytimes)),'utf-8')) + f.write(bytes('Found {n} entrytimes'.format( + n=len(entrytimes)), 'utf-8')) - except InvalidTrajectoryError: # pragma: no cover + except InvalidTrajectoryError: # pragma: no cover entrytimes = [] entrydistances = [] coursecompleted = False coursemeters = 0 coursetimeseconds = 0 - cseconds = [] cmeters = [] ccomplete = [] @@ -692,60 +706,60 @@ def handle_check_race_course(self, endseconds = [] for startt in entrytimes: - with open(logfile,'ab') as f: + with open(logfile, 'ab') as f: t = time.localtime() - timestamp = bytes('{t}'.format(t=time.strftime('%b-%d-%Y_%H%M', t)),'utf-8') + timestamp = bytes('{t}'.format( + t=time.strftime('%b-%d-%Y_%H%M', t)), 'utf-8') f.write(b'\n') f.write(timestamp) f.write(b' ') - f.write(bytes('Path starting at {t}'.format(t=startt),'utf-8')) - rowdata2 = rowdata[rowdata['time']>(startt-10.)] + f.write(bytes('Path starting at {t}'.format(t=startt), 'utf-8')) + rowdata2 = rowdata[rowdata['time'] > (startt-10.)] ( coursetimeseconds, coursemeters, coursecompleted, - ) = coursetime_paths(rowdata2,paths,polygons=polygons,logfile=logfile) + ) = coursetime_paths(rowdata2, paths, polygons=polygons, logfile=logfile) ( coursetimefirst, coursemetersfirst, firstcompleted ) = coursetime_first( - rowdata2,paths,polygons=polygons,logfile=logfile) - - + rowdata2, paths, polygons=polygons, logfile=logfile) coursetimesecondsnet = coursetimeseconds-coursetimefirst coursemeters = coursemeters-coursemetersfirst - cseconds.append(coursetimesecondsnet) cmeters.append(coursemeters) ccomplete.append(coursecompleted) endseconds.append(coursetimeseconds) startseconds.append(coursetimefirst) - records = pd.DataFrame({ - 'coursetimeseconds':cseconds, + 'coursetimeseconds': cseconds, 'coursecompleted': ccomplete, 'coursemeters': cmeters, - 'startsecond':startseconds, - 'endsecond':endseconds, - }) + 'startsecond': startseconds, + 'endsecond': endseconds, + }) records = records[records['coursecompleted'] == True] - if len(records): coursecompleted = True mintime = records['coursetimeseconds'].min() - coursetimeseconds = records[records['coursetimeseconds'] == mintime]['coursetimeseconds'].min() - coursemeters = records[records['coursetimeseconds'] == mintime]['coursemeters'].min() - startsecond = records[records['coursetimeseconds'] == mintime]['startsecond'].min() - endsecond = records[records['coursetimeseconds'] == mintime]['endsecond'].min() - else: # pragma: no cover + coursetimeseconds = records[records['coursetimeseconds'] + == mintime]['coursetimeseconds'].min() + coursemeters = records[records['coursetimeseconds'] + == mintime]['coursemeters'].min() + startsecond = records[records['coursetimeseconds'] + == mintime]['startsecond'].min() + endsecond = records[records['coursetimeseconds'] + == mintime]['endsecond'].min() + else: # pragma: no cover coursecompleted = False points = 0 @@ -762,7 +776,7 @@ def handle_check_race_course(self, workoutid=workoutid, startsecond=startsecond, endsecond=endsecond, - ) + ) if mode == 'coursetest': query = 'UPDATE rowers_coursetestresult SET coursecompleted = 1, duration = "{duration}", distance = {distance}, workoutid = {workoutid}, startsecond = {startsecond}, endsecond = {endsecond}, points={points} WHERE id={recordid}'.format( @@ -775,8 +789,6 @@ def handle_check_race_course(self, endsecond=endsecond, ) - - with engine.connect() as conn, conn.begin(): result = conn.execute(query) @@ -786,22 +798,23 @@ def handle_check_race_course(self, except IOError: # pragma: no cover try: row = rdata(csvfile=f1 + '.csv') - except IOError: # pragma: no cover + except IOError: # pragma: no cover try: row = rdata(csvfile=f1 + '.gz') - except IOError: # pragma: no cover + except IOError: # pragma: no cover pass vals, units, typ = row.updateinterval_metric( - ' AverageBoatSpeed (m/s)',0.1,mode='larger', - debug=False,smoothwindow=15., - activewindow=[startsecond,endsecond] - ) + ' AverageBoatSpeed (m/s)', 0.1, mode='larger', + debug=False, smoothwindow=15., + activewindow=[startsecond, endsecond] + ) summary = row.allstats() - row.write_csv(f1,gzip=True) + row.write_csv(f1, gzip=True) - query = "UPDATE `rowers_workout` SET `summary` = '%s' WHERE `id` = %s" % (summary, workoutid) + query = "UPDATE `rowers_workout` SET `summary` = '%s' WHERE `id` = %s" % ( + summary, workoutid) with engine.connect() as conn, conn.begin(): result = conn.execute(query) @@ -818,7 +831,7 @@ def handle_check_race_course(self, return 1 - else: # pragma: no cover + else: # pragma: no cover query = 'UPDATE rowers_virtualraceresult SET coursecompleted = 0, duration = "{duration}", distance = {distance}, workoutid = {workoutid}, startsecond = {startsecond}, endsecond = {endsecond}, points={points} WHERE id={recordid}'.format( recordid=recordid, duration=totaltime_sec_to_string(0), @@ -827,7 +840,7 @@ def handle_check_race_course(self, workoutid=workoutid, startsecond=startsecond, endsecond=endsecond, - ) + ) if mode == 'coursetest': query = 'UPDATE rowers_coursetestresult SET coursecompleted = 0, duration = "{duration}", distance = {distance}, workoutid = {workoutid}, startsecond = {startsecond}, endsecond = {endsecond}, points={points} WHERE id={recordid}'.format( @@ -847,50 +860,48 @@ def handle_check_race_course(self, engine.dispose() # add times for all gates to log file - with open(logfile,'ab') as f: + with open(logfile, 'ab') as f: t = time.localtime() f.write(b'\n') f.write(b' ') f.write(b'--- LOG of all gate times---') - for path,polygon in zip(paths,polygons): - ( secs,meters,completed) = coursetime_paths(rowdata, - [path],polygons=[polygon],logfile=logfile) - with open(logfile,'ab') as f: - line = " time: {t} seconds, distance: {m} meters".format(t=secs,m=meters) - f.write(bytes(line,'utf-8')) + for path, polygon in zip(paths, polygons): + (secs, meters, completed) = coursetime_paths(rowdata, + [path], polygons=[polygon], logfile=logfile) + with open(logfile, 'ab') as f: + line = " time: {t} seconds, distance: {m} meters".format( + t=secs, m=meters) + f.write(bytes(line, 'utf-8')) # send email handle_sendemail_coursefail( - useremail,userfirstname,logfile + useremail, userfirstname, logfile ) os.remove(logfile) return 2 - - - return 0 # pragma: no cover + return 0 # pragma: no cover @app.task(bind=True) def handle_getagegrouprecords(self, df, - distances,durations, - age,sex,weightcategory, + distances, durations, + age, sex, weightcategory, **kwargs): wcdurations = [] wcpower = [] - if 'debug' in kwargs: # pragma: no cover + if 'debug' in kwargs: # pragma: no cover debug = kwargs['debug'] else: debug = False - df = pd.read_json(df) - if sex == 'not specified': # pragma: no cover + if sex == 'not specified': # pragma: no cover return 0 for distance in distances: @@ -898,7 +909,7 @@ def handle_getagegrouprecords(self, age, sex=sex, distance=distance, - weightcategory=weightcategory,indf=df, + weightcategory=weightcategory, indf=df, ) velo = (worldclasspower/2.8)**(1./3.) if not np.isinf(worldclasspower) and not np.isnan(worldclasspower): @@ -906,17 +917,15 @@ def handle_getagegrouprecords(self, duration = distance/velo wcdurations.append(duration) wcpower.append(worldclasspower) - except ZeroDivisionError: # pragma: no cover + except ZeroDivisionError: # pragma: no cover pass - - for duration in durations: worldclasspower = getagegrouprecord( age, sex=sex, duration=duration, - weightcategory=weightcategory,indf=df + weightcategory=weightcategory, indf=df ) if not np.isinf(worldclasspower) and not np.isnan(worldclasspower): try: @@ -924,14 +933,15 @@ def handle_getagegrouprecords(self, distance = int(60*duration*velo) wcdurations.append(60.*duration) wcpower.append(worldclasspower) - except ValueError: # pragma: no cover + except ValueError: # pragma: no cover pass - update_agegroup_db(age,sex,weightcategory,wcdurations,wcpower, + update_agegroup_db(age, sex, weightcategory, wcdurations, wcpower, debug=debug) return 1 + @app.task def handle_get_garmin_file(client_id, client_secret, @@ -943,24 +953,22 @@ def handle_get_garmin_file(client_id, *args, **kwargs): - regex = '.*\?id=(\d+)' - try: # pragma: no cover + try: # pragma: no cover m = re.compile(regex).match(url).group(1) garminid = int(m) except AttributeError: garminid = '' - garmin = OAuth1Session(client_id, - client_secret=client_secret, - resource_owner_key=garmintoken, - resource_owner_secret=garminrefreshtoken, - ) + client_secret=client_secret, + resource_owner_key=garmintoken, + resource_owner_secret=garminrefreshtoken, + ) filename = 'media/{code}_{id}.'.format( - code = uuid4().hex[:16], - id = userid + code=uuid4().hex[:16], + id=userid )+filetype response = garmin.get(url, stream=True) @@ -968,36 +976,36 @@ def handle_get_garmin_file(client_id, with open(filename, 'wb') as out_file: shutil.copyfileobj(response.raw, out_file) - del response uploadoptions = { - 'secret':UPLOAD_SERVICE_SECRET, - 'user':userid, + 'secret': UPLOAD_SERVICE_SECRET, + 'user': userid, 'file': filename, 'title': '', - 'workouttype':'water', - 'boattype':'1x', + 'workouttype': 'water', + 'boattype': '1x', 'garminid': garminid, } session = requests.session() newHeaders = {'Content-type': 'application/json', 'Accept': 'text/plain'} session.headers.update(newHeaders) - response = session.post(UPLOAD_SERVICE_URL,json=uploadoptions) - + response = session.post(UPLOAD_SERVICE_URL, json=uploadoptions) return 1 + @app.task(bind=True) -def long_test_task(self,aantal,debug=False,job=None,session_key=None): # pragma: no cover +def long_test_task(self, aantal, debug=False, job=None, session_key=None): # pragma: no cover job = self.request - return longtask.longtask(aantal,jobid=job.id,debug=debug, + return longtask.longtask(aantal, jobid=job.id, debug=debug, session_key=session_key) + @app.task(bind=True) -def long_test_task2(self,aantal,**kwargs): # pragma: no cover - #debug=False,job=None,jobid='aap'): +def long_test_task2(self, aantal, **kwargs): # pragma: no cover + # debug=False,job=None,jobid='aap'): job = self.request job_id = job.id @@ -1006,11 +1014,7 @@ def long_test_task2(self,aantal,**kwargs): # pragma: no cover kwargs['jobid'] = job_id - return longtask.longtask2(aantal,**kwargs) - - - - + return longtask.longtask2(aantal, **kwargs) # process and update workouts @@ -1019,7 +1023,7 @@ def long_test_task2(self,aantal,**kwargs): # pragma: no cover def handle_update_empower(self, useremail, workoutdicts, - debug=False, **kwargs): # pragma: no cover + debug=False, **kwargs): # pragma: no cover job = self.request job_id = job.id @@ -1072,9 +1076,9 @@ def handle_update_empower(self, progress = 100.*float(counter)/float(aantal) post_data = { - "secret":secret, - "value":progress, - } + "secret": secret, + "value": progress, + } s = requests.post(progressurl, data=post_data) status_code = s.status_code @@ -1097,6 +1101,7 @@ We have updated Power and Work per Stroke data according to the instructions by res = email.send() return 1 + @app.task def handle_calctrimp(id, csvfilename, @@ -1106,7 +1111,7 @@ def handle_calctrimp(id, hrmax, hrmin, debug=False, **kwargs): - if debug: # pragma: no cover + if debug: # pragma: no cover engine = create_engine(database_url_debug, echo=False) else: engine = create_engine(database_url, echo=False) @@ -1121,11 +1126,11 @@ def handle_calctrimp(id, # check what the real file name is if os.path.exists(csvfilename): csvfile = csvfilename - elif os.path.exists(csvfilename+'.csv'): # pragma: no cover + elif os.path.exists(csvfilename+'.csv'): # pragma: no cover csvfile = csvfilename+'.csv' - elif os.path.exists(csvfilename+'.gz'): # pragma: no cover + elif os.path.exists(csvfilename+'.gz'): # pragma: no cover csvfile = csvfilename+'.gz' - else: # pragma: no cover + else: # pragma: no cover return 0 csvfile = os.path.abspath(csvfile) @@ -1135,15 +1140,15 @@ def handle_calctrimp(id, options=[('grpc.lb_policy_name', 'pick_first'), ('grpc.enable_retries', 0), ('grpc.keepalive_timeout_ms', 10000)] - ) as channel: + ) as channel: try: grpc.channel_ready_future(channel).result(timeout=10) - except grpc.FutureTimeoutError: # pragma: no cover + except grpc.FutureTimeoutError: # pragma: no cover return 0 stub = metrics_pb2_grpc.MetricsStub(channel) req = metrics_pb2.WorkoutMetricsRequest( - filename = csvfile, + filename=csvfile, ftp=ftp, sex=sex, hrftp=hrftp, @@ -1151,8 +1156,8 @@ def handle_calctrimp(id, hrmin=hrmin, ) try: - response = stub.CalcMetrics(req,timeout=60) - except: # pragma: no cover + response = stub.CalcMetrics(req, timeout=60) + except: # pragma: no cover return 0 tss = response.tss @@ -1162,52 +1167,51 @@ def handle_calctrimp(id, normw = response.normw hrtss = response.hrtss - if np.isnan(tss): # pragma: no cover + if np.isnan(tss): # pragma: no cover tss = 0 - if np.isnan(normp): # pragma: no cover + if np.isnan(normp): # pragma: no cover normp = 0 - if np.isnan(trimp): # pragma: no cover + if np.isnan(trimp): # pragma: no cover trimp = 0 - if np.isnan(normv): # pragma: no cover + if np.isnan(normv): # pragma: no cover normv = 0 - if np.isnan(normw): # pragma: no cover + if np.isnan(normw): # pragma: no cover normw = 0 - if np.isnan(hrtss): # pragma: no cover + if np.isnan(hrtss): # pragma: no cover hrtss = 0 - if tss > 1000: # pragma: no cover + if tss > 1000: # pragma: no cover tss = 0 - if trimp > 1000: # pragma: no cover + if trimp > 1000: # pragma: no cover trimp = 0 - if normp > 2000: # pragma: no cover + if normp > 2000: # pragma: no cover normp = 0 - if normv > 2000: # pragma: no cover + if normv > 2000: # pragma: no cover normv = 0 - if normw > 10000: # pragma: no cover + if normw > 10000: # pragma: no cover normw = 0 - if hrtss > 1000: # pragma: no cover + if hrtss > 1000: # pragma: no cover hrtss = 0 - query = 'UPDATE rowers_workout SET rscore = {tss}, normp = {normp}, trimp={trimp}, hrtss={hrtss}, normv={normv}, normw={normw} WHERE id={id}'.format( - tss = int(tss), - normp = int(normp), - trimp = int(trimp), - hrtss = int(hrtss), + tss=int(tss), + normp=int(normp), + trimp=int(trimp), + hrtss=int(hrtss), normv=normv, normw=normw, - id = id, - ) + id=id, + ) with engine.connect() as conn, conn.begin(): result = conn.execute(query) @@ -1218,12 +1222,12 @@ def handle_calctrimp(id, @app.task -def handle_updatedps(useremail, workoutids, debug=False,**kwargs): +def handle_updatedps(useremail, workoutids, debug=False, **kwargs): for wid, f1 in workoutids: havedata = 1 try: rowdata = rdata(csvfile=f1) - except IOError: # pragma: no cover + except IOError: # pragma: no cover try: rowdata = rdata(csvfile=f1 + '.csv') except IOError: @@ -1235,7 +1239,6 @@ def handle_updatedps(useremail, workoutids, debug=False,**kwargs): if havedata: update_strokedata(wid, rowdata.df, debug=debug) - subject = "Rowsandall.com Your Distance per Stroke metric has been updated" message = "All your workouts now have Distance per Stroke" @@ -1243,7 +1246,7 @@ def handle_updatedps(useremail, workoutids, debug=False,**kwargs): 'Rowsandall ', [useremail]) - if 'emailbounced' in kwargs: # pragma: no cover + if 'emailbounced' in kwargs: # pragma: no cover emailbounced = kwargs['emailbounced'] else: emailbounced = False @@ -1253,15 +1256,15 @@ def handle_updatedps(useremail, workoutids, debug=False,**kwargs): return 1 -import math -def sigdig(value, digits = 3): + +def sigdig(value, digits=3): try: order = int(math.floor(math.log10(math.fabs(value)))) - except (ValueError,TypeError): # pragma: no cover + except (ValueError, TypeError): # pragma: no cover return value # return integers as is - if value % 1 == 0: # pragma: no cover + if value % 1 == 0: # pragma: no cover return value places = digits - order - 1 @@ -1272,17 +1275,16 @@ def sigdig(value, digits = 3): return fmtstr % (round(value, places)) - @app.task def handle_send_email_alert( useremail, userfirstname, userlastname, rowerfirstname, alertname, stats, **kwargs): - if 'debug' in kwargs: # pragma: no cover + if 'debug' in kwargs: # pragma: no cover debug = kwargs['debug'] else: debug = False - if 'othertexts' in kwargs: # pragma: no cover + if 'othertexts' in kwargs: # pragma: no cover othertexts = kwargs['othertexts'] else: othertexts = None @@ -1290,64 +1292,64 @@ def handle_send_email_alert( report = {} try: report['Percentage'] = int(stats['percentage']) - except KeyError: # pragma: no cover + except KeyError: # pragma: no cover pass try: report['Number of workouts'] = int(stats['workouts']) - except KeyError: # pragma: no cover + except KeyError: # pragma: no cover pass try: report['Data set'] = "{a} strokes out of {b}".format( - a = stats['nr_strokes_qualifying'], - b = stats['nr_strokes'] + a=stats['nr_strokes_qualifying'], + b=stats['nr_strokes'] ) - except KeyError: # pragma: no cover + except KeyError: # pragma: no cover pass - try: report['Median'] = sigdig(stats['median']) - except KeyError: # pragma: no cover + except KeyError: # pragma: no cover pass try: report['Median of qualifying strokes'] = sigdig(stats['median_q']) - except KeyError: # pragma: no cover + except KeyError: # pragma: no cover pass subject = "Rowsandall.com: {alertname} ({startdate} to {enddate})".format( - startdate = stats['startdate'], - enddate = stats['enddate'], + startdate=stats['startdate'], + enddate=stats['enddate'], alertname=alertname, - ) + ) from_email = 'Rowsandall ' d = { - 'report':report, - 'first_name':userfirstname, - 'last_name':userlastname, - 'startdate':stats['startdate'], - 'enddate':stats['enddate'], - 'siteurl':siteurl, - 'rowerfirstname':rowerfirstname, - 'alertname':alertname, - 'othertexts':othertexts, - } + 'report': report, + 'first_name': userfirstname, + 'last_name': userlastname, + 'startdate': stats['startdate'], + 'enddate': stats['enddate'], + 'siteurl': siteurl, + 'rowerfirstname': rowerfirstname, + 'alertname': alertname, + 'othertexts': othertexts, + } - res = send_template_email(from_email,[useremail],subject, + res = send_template_email(from_email, [useremail], subject, 'alertemail.html', - d,**kwargs) + d, **kwargs) return 1 + @app.task def handle_send_email_transaction( username, useremail, amount, **kwargs): - if 'debug' in kwargs: # pragma: no cover + if 'debug' in kwargs: # pragma: no cover debug = kwargs['debug'] else: debug = True @@ -1360,21 +1362,22 @@ def handle_send_email_transaction( 'name': username, 'siteurl': siteurl, 'amount': amount, - } + } - res = send_template_email(from_email,[useremail], + res = send_template_email(from_email, [useremail], subject, 'paymentconfirmationemail.html', d, **kwargs) return 1 + @app.task def handle_send_email_instantplan_notification( - username, useremail, amount, planname,startdate, enddate, **kwargs - ): # pragma: no cover + username, useremail, amount, planname, startdate, enddate, **kwargs +): # pragma: no cover - if 'debug' in kwargs: # pragma: no cover + if 'debug' in kwargs: # pragma: no cover debug = kwargs['debug'] else: debug = True @@ -1388,22 +1391,23 @@ def handle_send_email_instantplan_notification( 'siteurl': siteurl, 'amount': amount, 'planname': planname, - 'startdate':startdate, - 'enddate':enddate, - } + 'startdate': startdate, + 'enddate': enddate, + } - res = send_template_email(from_email,['roosendaalsander@gmail.com'], + res = send_template_email(from_email, ['roosendaalsander@gmail.com'], subject, 'instantplansold.html', d, **kwargs) return 1 + @app.task def handle_send_email_failed_cancel( name, email, username, id, **kwargs): - if 'debug' in kwargs: # pragma: no cover + if 'debug' in kwargs: # pragma: no cover debug = kwargs['debug'] else: debug = True @@ -1418,9 +1422,9 @@ def handle_send_email_failed_cancel( 'email': email, 'username': username, 'id': id, - } + } - res = send_template_email(from_email,["support@rowsandall.com"], + res = send_template_email(from_email, ["support@rowsandall.com"], subject, 'cancel_subscription_fail_email.html', d, **kwargs) @@ -1433,23 +1437,22 @@ def handle_send_email_subscription_update( username, useremail, planname, recurring, price, amount, end_of_billing_period, method, **kwargs): - if 'debug' in kwargs: # pragma: no cover + if 'debug' in kwargs: # pragma: no cover debug = kwargs['debug'] else: debug = True - from_email = 'Rowsandall ' d = { 'name': username, 'siteurl': siteurl, 'amount': amount, - 'price':price, + 'price': price, 'planname': planname, 'recurring': recurring, 'end_of_billing_period': end_of_billing_period, - } + } if method == 'down': template_name = 'subscription_downgrade_email.html' @@ -1460,24 +1463,25 @@ def handle_send_email_subscription_update( notification_template_name = 'subscription_update_notification.html' subject = "Rowsandall Payment Confirmation" - res = send_template_email(from_email,[useremail], + res = send_template_email(from_email, [useremail], subject, template_name, d, **kwargs) - res = send_template_email(from_email,['info@rowsandall.com'], + res = send_template_email(from_email, ['info@rowsandall.com'], 'Subscription Update Notification', notification_template_name, d, **kwargs) return 1 + @app.task def handle_send_email_subscription_create( username, useremail, planname, recurring, price, amount, end_of_billing_period, **kwargs): - if 'debug' in kwargs: # pragma: no cover + if 'debug' in kwargs: # pragma: no cover debug = kwargs['debug'] else: debug = True @@ -1486,62 +1490,63 @@ def handle_send_email_subscription_create( from_email = 'Rowsandall ' - d = { 'name': username, 'siteurl': siteurl, 'amount': amount, - 'price':price, + 'price': price, 'planname': planname, 'end_of_billing_period': end_of_billing_period, 'recurring': recurring, - } + } - res = send_template_email(from_email,[useremail], + res = send_template_email(from_email, [useremail], subject, 'subscription_create_email.html', d, **kwargs) - res = send_template_email(from_email,['info@rowsandall.com'], + res = send_template_email(from_email, ['info@rowsandall.com'], 'Subscription Update Notification', 'subscription_create_notification.html', d, **kwargs) return 1 + @app.task def handle_sendemail_raceregistration( useremail, username, registeredname, racename, raceid, **kwargs): - if 'debug' in kwargs: # pragma: no cover + if 'debug' in kwargs: # pragma: no cover debug = kwargs['debug'] else: debug = True subject = "A new competitor has registered for virtual challenge {n}".format( - n = racename - ) + n=racename + ) from_email = 'Rowsandall ' d = { - 'username':username, - 'registeredname':registeredname, - 'siteurl':siteurl, - 'racename':racename, - 'raceid':raceid, - } + 'username': username, + 'registeredname': registeredname, + 'siteurl': siteurl, + 'racename': racename, + 'raceid': raceid, + } - res = send_template_email(from_email,[useremail], + res = send_template_email(from_email, [useremail], subject, 'raceregisteredemail.html', - d,**kwargs) + d, **kwargs) return 1 + def handle_sendemail_coursesucceed( useremail, username, logfile, workoutid, **kwargs -): # pragma: no cover - if 'debug' in kwargs: # pragma: no cover +): # pragma: no cover + if 'debug' in kwargs: # pragma: no cover debug = kwargs['debug'] else: debug = True @@ -1551,11 +1556,11 @@ def handle_sendemail_coursesucceed( from_email = 'Rowsandall ' d = { - 'username':username, - 'workoutid':encoder.encode_hex(workoutid), + 'username': username, + 'workoutid': encoder.encode_hex(workoutid), } - res = send_template_email(from_email,[useremail], + res = send_template_email(from_email, [useremail], subject, 'trajectorysuccessemail.html', d, @@ -1564,10 +1569,11 @@ def handle_sendemail_coursesucceed( return 1 + def handle_sendemail_coursefail( useremail, username, logfile, **kwargs): - if 'debug' in kwargs: # pragma: no cover + if 'debug' in kwargs: # pragma: no cover debug = kwargs['debug'] else: debug = True @@ -1577,10 +1583,10 @@ def handle_sendemail_coursefail( from_email = 'Rowsandall ' d = { - 'username':username, - } + 'username': username, + } - res = send_template_email(from_email,[useremail], + res = send_template_email(from_email, [useremail], subject, 'trajectoryfailemail.html', d, @@ -1590,34 +1596,35 @@ def handle_sendemail_coursefail( return 1 + @app.task def handle_sendemail_optout( useremail, username, registeredname, racename, raceid, **kwargs): - if 'debug' in kwargs: # pragma: no cover + if 'debug' in kwargs: # pragma: no cover debug = kwargs['debug'] else: debug = True subject = "{name} has opted out from social media posts around challenge {n}".format( - n = racename, - name = registeredname - ) + n=racename, + name=registeredname + ) from_email = 'Rowsandall ' d = { - 'username':username, - 'registeredname':registeredname, - 'siteurl':siteurl, - 'racename':racename, - 'raceid':raceid, - } + 'username': username, + 'registeredname': registeredname, + 'siteurl': siteurl, + 'racename': racename, + 'raceid': raceid, + } - res = send_template_email(from_email,[useremail], + res = send_template_email(from_email, [useremail], subject, 'raceoptoutsocialmedia.html', - d,**kwargs) + d, **kwargs) return 1 @@ -1626,97 +1633,99 @@ def handle_sendemail_optout( def handle_sendemail_racesubmission( useremail, username, registeredname, racename, raceid, **kwargs): - if 'debug' in kwargs: # pragma: no cover + if 'debug' in kwargs: # pragma: no cover debug = kwargs['debug'] else: debug = True subject = "A new result has been submitted for virtual challenge {n}".format( - n = racename - ) + n=racename + ) from_email = 'Rowsandall ' d = { - 'username':username, - 'siteurl':siteurl, - 'registeredname':registeredname, - 'racename':racename, - 'raceid':raceid, - } + 'username': username, + 'siteurl': siteurl, + 'registeredname': registeredname, + 'racename': racename, + 'raceid': raceid, + } - res = send_template_email(from_email,[useremail], + res = send_template_email(from_email, [useremail], subject, 'racesubmissionemail.html', - d,**kwargs) + d, **kwargs) return 1 + @app.task def handle_send_disqualification_email( - useremail,username,reason,message, racename, **kwargs): + useremail, username, reason, message, racename, **kwargs): - if 'debug' in kwargs: # pragma: no cover + if 'debug' in kwargs: # pragma: no cover debug = kwargs['debug'] else: debug = True subject = "Your result for {n} has been disqualified on rowsandall.com".format( - n = racename - ) + n=racename + ) from_email = 'Rowsandall ' d = { - 'username':username, - 'reason':reason, - 'siteurl':siteurl, + 'username': username, + 'reason': reason, + 'siteurl': siteurl, 'message': htmlstrip(message), - 'racename':racename, - } + 'racename': racename, + } - res = send_template_email(from_email,[useremail], + res = send_template_email(from_email, [useremail], subject, 'disqualificationemail.html', - d,**kwargs) + d, **kwargs) return 1 + @app.task def handle_send_withdraw_email( - useremail,username,reason,message, racename, **kwargs): + useremail, username, reason, message, racename, **kwargs): - if 'debug' in kwargs: # pragma: no cover + if 'debug' in kwargs: # pragma: no cover debug = kwargs['debug'] else: debug = True subject = "Your result for {n} has been removed on rowsandall.com".format( - n = racename - ) + n=racename + ) from_email = 'Rowsandall ' d = { - 'username':username, - 'reason':reason, - 'siteurl':siteurl, + 'username': username, + 'reason': reason, + 'siteurl': siteurl, 'message': htmlstrip(message), - 'racename':racename, - } + 'racename': racename, + } - res = send_template_email(from_email,[useremail], + res = send_template_email(from_email, [useremail], subject, 'withdraw_email.html', - d,**kwargs) + d, **kwargs) return 1 @app.task -def handle_sendemail_expired(useremail,userfirstname,userlastname,expireddate, +def handle_sendemail_expired(useremail, userfirstname, userlastname, expireddate, **kwargs): - if 'debug' in kwargs: # pragma: no cover + if 'debug' in kwargs: # pragma: no cover debug = kwargs['debug'] else: debug = False @@ -1725,24 +1734,25 @@ def handle_sendemail_expired(useremail,userfirstname,userlastname,expireddate, from_email = 'Rowsandall ' d = { - 'first_name':userfirstname, - 'last_name':userlastname, - 'siteurl':siteurl, - 'expireddate':expireddate, - } + 'first_name': userfirstname, + 'last_name': userlastname, + 'siteurl': siteurl, + 'expireddate': expireddate, + } - res = send_template_email(from_email,[useremail], - subject,'accountexpiredemail.html', - d,cc=['support@rowsandall.com'],**kwargs) + res = send_template_email(from_email, [useremail], + subject, 'accountexpiredemail.html', + d, cc=['support@rowsandall.com'], **kwargs) return 1 + @app.task def handle_sendemail_breakthrough(workoutid, useremail, userfirstname, userlastname, btvalues=pd.DataFrame().to_json(), **kwargs): - if 'debug' in kwargs: # pragma: no cover + if 'debug' in kwargs: # pragma: no cover debug = kwargs['debug'] else: debug = False @@ -1755,25 +1765,22 @@ def handle_sendemail_breakthrough(workoutid, useremail, 'cpvalue': t.cpvalues, 'pwr': t.pwr } for t in btvalues.itertuples() - ] + ] # send email with attachment subject = "A breakthrough workout on rowsandall.com" from_email = 'Rowsandall ' d = { - 'first_name':userfirstname, - 'siteurl':siteurl, - 'workoutid':encoder.encode_hex(workoutid), - 'btvalues':tablevalues, - } - - - - res = send_template_email(from_email,[useremail], - subject,'breakthroughemail.html', - d,**kwargs) + 'first_name': userfirstname, + 'siteurl': siteurl, + 'workoutid': encoder.encode_hex(workoutid), + 'btvalues': tablevalues, + } + res = send_template_email(from_email, [useremail], + subject, 'breakthroughemail.html', + d, **kwargs) return 1 @@ -1784,14 +1791,13 @@ def handle_sendemail_breakthrough(workoutid, useremail, def handle_sendemail_hard(workoutid, useremail, userfirstname, userlastname, btvalues=pd.DataFrame().to_json(), - debug=False,**kwargs): + debug=False, **kwargs): - if 'debug' in kwargs: # pragma: no cover + if 'debug' in kwargs: # pragma: no cover debug = kwargs['debug'] else: debug = False - btvalues = pd.read_json(btvalues) btvalues.sort_values('delta', axis=0, inplace=True) @@ -1800,22 +1806,21 @@ def handle_sendemail_hard(workoutid, useremail, 'cpvalue': t.cpvalues, 'pwr': t.pwr } for t in btvalues.itertuples() - ] + ] # send email with attachment subject = "That was a pretty hard workout on rowsandall.com" from_email = 'Rowsandall ' d = { - 'first_name':userfirstname, - 'siteurl':siteurl, - 'workoutid':encoder.encode_hex(workoutid), - 'btvalues':tablevalues, - } - - res = send_template_email(from_email,[useremail], - subject,'hardemail.html',d,**kwargs) + 'first_name': userfirstname, + 'siteurl': siteurl, + 'workoutid': encoder.encode_hex(workoutid), + 'btvalues': tablevalues, + } + res = send_template_email(from_email, [useremail], + subject, 'hardemail.html', d, **kwargs) return 1 @@ -1829,12 +1834,12 @@ def handle_sendemail_userdeleted(name, email, debug=False, **kwargs): message += 'The user {name} ({email}) has just deleted his account'.format( name=name, email=email - ) - email = EmailMessage(subject,message, + ) + email = EmailMessage(subject, message, 'Rowsandall ', [fullemail]) - if 'emailbounced' in kwargs: # pragma: no cover + if 'emailbounced' in kwargs: # pragma: no cover emailbounced = kwargs['emailbounced'] else: emailbounced = False @@ -1842,13 +1847,14 @@ def handle_sendemail_userdeleted(name, email, debug=False, **kwargs): if not emailbounced: res = email.send() - return 1 # send email to me when an unrecognized file is uploaded + + @app.task def handle_sendemail_unrecognized(unrecognizedfile, useremail, - debug=False,**kwargs): + debug=False, **kwargs): # send email with attachment fullemail = 'roosendaalsander@gmail.com' @@ -1864,10 +1870,10 @@ def handle_sendemail_unrecognized(unrecognizedfile, useremail, try: email.attach_file(unrecognizedfile) - except IOError: # pragma: no cover + except IOError: # pragma: no cover pass - if 'emailbounced' in kwargs: # pragma: no cover + if 'emailbounced' in kwargs: # pragma: no cover emailbounced = kwargs['emailbounced'] else: emailbounced = False @@ -1875,11 +1881,10 @@ def handle_sendemail_unrecognized(unrecognizedfile, useremail, if not emailbounced: res = email.send() - # remove tcx file try: os.remove(unrecognizedfile) - except: # pragma: no cover + except: # pragma: no cover pass return 1 @@ -1888,96 +1893,92 @@ def handle_sendemail_unrecognized(unrecognizedfile, useremail, # send email to owner when an unrecognized file is uploaded @app.task def handle_sendemail_unrecognizedowner(useremail, userfirstname, - debug=False,**kwargs): + debug=False, **kwargs): # send email with attachment fullemail = useremail subject = "Unrecognized file from Rowsandall.com" - d = { - 'first_name':userfirstname, - 'siteurl':siteurl, + 'first_name': userfirstname, + 'siteurl': siteurl, } from_email = 'Rowsandall ' - res = send_template_email(from_email,[fullemail], - subject,'unrecognizedemail.html',d, + res = send_template_email(from_email, [fullemail], + subject, 'unrecognizedemail.html', d, **kwargs) return 1 + @app.task def handle_sendemailics(first_name, last_name, email, icsfile, **kwargs): # send email with attachment fullemail = first_name + " " + last_name + " " + "<" + email + ">" subject = "Calendar File from Rowsandall.com" - - d = {'first_name':first_name, - 'siteurl':siteurl, - } + d = {'first_name': first_name, + 'siteurl': siteurl, + } from_email = 'Rowsandall ' - - res = send_template_email(from_email,[fullemail], - subject,'icsemail.html',d, - attach_file=icsfile,**kwargs) + res = send_template_email(from_email, [fullemail], + subject, 'icsemail.html', d, + attach_file=icsfile, **kwargs) os.remove(icsfile) return 1 @app.task -def handle_sendemailkml(first_name, last_name, email, kmlfile,**kwargs): +def handle_sendemailkml(first_name, last_name, email, kmlfile, **kwargs): # send email with attachment fullemail = first_name + " " + last_name + " " + "<" + email + ">" subject = "File from Rowsandall.com" - - d = {'first_name':first_name, - 'siteurl':siteurl, - } + d = {'first_name': first_name, + 'siteurl': siteurl, + } from_email = 'Rowsandall ' - - res = send_template_email(from_email,[fullemail], - subject,'kmlemail.html',d, - attach_file=kmlfile,**kwargs) + res = send_template_email(from_email, [fullemail], + subject, 'kmlemail.html', d, + attach_file=kmlfile, **kwargs) os.remove(kmlfile) return 1 # Send email with TCX attachment + + @app.task -def handle_sendemailtcx(first_name, last_name, email, tcxfile,**kwargs): +def handle_sendemailtcx(first_name, last_name, email, tcxfile, **kwargs): # send email with attachment fullemail = first_name + " " + last_name + " " + "<" + email + ">" subject = "File from Rowsandall.com" - - d = {'first_name':first_name, - 'siteurl':siteurl, - } + d = {'first_name': first_name, + 'siteurl': siteurl, + } from_email = 'Rowsandall ' - - res = send_template_email(from_email,[fullemail], - subject,'tcxemail.html',d, - attach_file=tcxfile,**kwargs) + res = send_template_email(from_email, [fullemail], + subject, 'tcxemail.html', d, + attach_file=tcxfile, **kwargs) os.remove(tcxfile) return 1 @app.task -def handle_zip_file(emailfrom, subject, file,**kwargs): # pragma: no cover +def handle_zip_file(emailfrom, subject, file, **kwargs): # pragma: no cover message = "... zip processing ... " try: debug = kwargs['debug'] @@ -1994,10 +1995,8 @@ def handle_zip_file(emailfrom, subject, file,**kwargs): # pragma: no cover if debug: print("attaching") - res = email.send() - if debug: print("sent") time.sleep(60) @@ -2005,83 +2004,79 @@ def handle_zip_file(emailfrom, subject, file,**kwargs): # pragma: no cover # Send email with CSV attachment + @app.task def handle_sendemailsummary(first_name, last_name, email, csvfile, **kwargs): fullemail = first_name + " " + last_name + " " + "<" + email + ">" subject = "File from Rowsandall.com" - - d = {'first_name':first_name, - 'siteurl':siteurl, - } + d = {'first_name': first_name, + 'siteurl': siteurl, + } from_email = 'Rowsandall ' - res = send_template_email(from_email,[fullemail], - subject,'summarymail.html',d, + res = send_template_email(from_email, [fullemail], + subject, 'summarymail.html', d, attach_file=csvfile, **kwargs) try: os.remove(csvfile) - except: # pragma: no cover + except: # pragma: no cover pass return 1 #from rowers.emails import sendemail -@app.task -def handle_sendemailcsv(first_name, last_name, email, csvfile,**kwargs): +@app.task +def handle_sendemailcsv(first_name, last_name, email, csvfile, **kwargs): # send email with attachment fullemail = first_name + " " + last_name + " " + "<" + email + ">" subject = "File from Rowsandall.com" - d = {'first_name':first_name, - 'siteurl':siteurl, - } + d = {'first_name': first_name, + 'siteurl': siteurl, + } from_email = 'Rowsandall ' - - res = send_template_email(from_email,[fullemail], - subject,'csvemail.html',d, - attach_file=csvfile,**kwargs) - + res = send_template_email(from_email, [fullemail], + subject, 'csvemail.html', d, + attach_file=csvfile, **kwargs) return 1 + @app.task def handle_sendemail_ical(first_name, last_name, email, url, icsfile, **kwargs): # send email with attachment fullemail = first_name + " " + last_name + " " + "<" + email + ">" subject = "Calendar File for your sessions from Rowsandall.com" - if 'debug' in kwargs: # pragma: no cover + if 'debug' in kwargs: # pragma: no cover debug = kwargs['debug'] else: debug = False siteurl = SITE_URL - if debug: # pragma: no cover + if debug: # pragma: no cover progressurl = SITE_URL_DEV siteurl = SITE_URL_DEV - - d = {'first_name':first_name, - 'siteurl':siteurl, - 'url':url, - } + d = {'first_name': first_name, + 'siteurl': siteurl, + 'url': url, + } from_email = 'Rowsandall ' - - res = send_template_email(from_email,[fullemail], - subject,'icsemail.html',d, - attach_file=icsfile,**kwargs) - + res = send_template_email(from_email, [fullemail], + subject, 'icsemail.html', d, + attach_file=icsfile, **kwargs) try: os.remove(csvfile) @@ -2092,31 +2087,28 @@ def handle_sendemail_ical(first_name, last_name, email, url, icsfile, **kwargs): @app.task -def handle_sendemailfile(first_name, last_name, email, csvfile,**kwargs): - +def handle_sendemailfile(first_name, last_name, email, csvfile, **kwargs): # send email with attachment fullemail = first_name + " " + last_name + " " + "<" + email + ">" subject = "File from Rowsandall.com" - d = {'first_name':first_name, - 'siteurl':siteurl, - } + d = {'first_name': first_name, + 'siteurl': siteurl, + } from_email = 'Rowsandall ' + res = send_template_email(from_email, [fullemail], + subject, 'fileemail.html', d, + attach_file=csvfile, **kwargs) - res = send_template_email(from_email,[fullemail], - subject,'fileemail.html',d, - attach_file=csvfile,**kwargs) - - - if 'delete' in kwargs: # pragma: no cover + if 'delete' in kwargs: # pragma: no cover dodelete = kwargs['delete'] else: dodelete = False - if dodelete: # pragma: no cover + if dodelete: # pragma: no cover try: os.remove(csvfile) except: @@ -2128,45 +2120,41 @@ def handle_sendemailfile(first_name, last_name, email, csvfile,**kwargs): @app.task(bind=True) -def handle_otwsetpower(self,f1, boattype, boatclass, coastalbrand, weightvalue, +def handle_otwsetpower(self, f1, boattype, boatclass, coastalbrand, weightvalue, first_name, last_name, email, workoutid, **kwargs): job = self.request job_id = job.id - if 'jobkey' in kwargs: job_id = kwargs.pop('jobkey') - if 'ps' in kwargs: # pragma: no cover + if 'ps' in kwargs: # pragma: no cover ps = kwargs['ps'] else: - ps = [1,1,1,1] + ps = [1, 1, 1, 1] - if 'ratio' in kwargs: # pragma: no cover + if 'ratio' in kwargs: # pragma: no cover ratio = kwargs['ratio'] else: ratio = 1.0 - if 'debug' in kwargs: # pragma: no cover + if 'debug' in kwargs: # pragma: no cover debug = kwargs['debug'] else: debug = False kwargs['jobid'] = job_id - - - weightvalue = float(weightvalue) # check what the real file name is if os.path.exists(f1): csvfile = f1 - elif os.path.exists(f1+'.csv'): # pragma: no cover + elif os.path.exists(f1+'.csv'): # pragma: no cover csvfile = f1+'.csv' - elif os.path.exists(f1+'.gz'): # pragma: no cover + elif os.path.exists(f1+'.gz'): # pragma: no cover csvfile = f1+'.gz' - else: # pragma: no cover + else: # pragma: no cover return 0 csvfile = os.path.abspath(csvfile) @@ -2174,7 +2162,7 @@ def handle_otwsetpower(self,f1, boattype, boatclass, coastalbrand, weightvalue, # do something with boat type try: rowdata = rdata(csvfile=csvfile) - except IOError: # pragma: no cover + except IOError: # pragma: no cover try: rowdata = rdata(csvfile=csvfile) except IOError: @@ -2182,7 +2170,7 @@ def handle_otwsetpower(self,f1, boattype, boatclass, coastalbrand, weightvalue, # do calculation, but do not overwrite NK Empower Power data powermeasured = False - try: # pragma: no cover + try: # pragma: no cover w = rowdata.df['wash'] if w.mean() != 0: powermeasured = True @@ -2191,7 +2179,7 @@ def handle_otwsetpower(self,f1, boattype, boatclass, coastalbrand, weightvalue, progressurl = SITE_URL siteurl = SITE_URL - if debug: # pragma: no cover + if debug: # pragma: no cover progressurl = SITE_URL_DEV siteurl = SITE_URL_DEV secret = PROGRESS_CACHE_SECRET @@ -2199,39 +2187,38 @@ def handle_otwsetpower(self,f1, boattype, boatclass, coastalbrand, weightvalue, progressurl += "/rowers/record-progress/" progressurl += job_id+'/' - # do something (this should return from go service) with grpc.insecure_channel( target='localhost:50051', options=[('grpc.lb_policy_name', 'pick_first'), ('grpc.enable_retries', 0), ('grpc.keepalive_timeout_ms', 10000)] - ) as channel: + ) as channel: try: grpc.channel_ready_future(channel).result(timeout=10) - except grpc.FutureTimeoutError: # pragma: no cover + except grpc.FutureTimeoutError: # pragma: no cover return 0 stub = calculator_pb2_grpc.PowerStub(channel) response = stub.CalcPower(calculator_pb2.WorkoutPowerRequest( - filename = csvfile, - boattype = boattype, - coastalbrand = coastalbrand, - crewmass = weightvalue, - powermeasured = powermeasured, - progressurl = progressurl, - secret = secret, - silent = False, - boatclass = boatclass, - ),timeout=1200) + filename=csvfile, + boattype=boattype, + coastalbrand=coastalbrand, + crewmass=weightvalue, + powermeasured=powermeasured, + progressurl=progressurl, + secret=secret, + silent=False, + boatclass=boatclass, + ), timeout=1200) result = response.result - if result == 0: # pragma: no cover + if result == 0: # pragma: no cover # send failure email return 0 # do something with boat type try: rowdata = rdata(csvfile=csvfile) - except IOError: # pragma: no cover + except IOError: # pragma: no cover try: rowdata = rdata(csvfile=csvfile) except IOError: @@ -2243,7 +2230,7 @@ def handle_otwsetpower(self,f1, boattype, boatclass, coastalbrand, weightvalue, ) - rowdata.df['TimeStamp (sec)'].min() try: totaltime = totaltime + rowdata.df.loc[0, ' ElapsedTime (sec)'] - except KeyError: # pragma: no cover + except KeyError: # pragma: no cover pass df = getsmallrowdata_db( ['power', 'workoutid', 'time'], ids=[workoutid], @@ -2257,38 +2244,36 @@ def handle_otwsetpower(self,f1, boattype, boatclass, coastalbrand, weightvalue, #delta,cpvalues,avgpower = datautils.getsinglecp(rowdata.df) res, btvalues, res2 = utils.isbreakthrough( delta, cpvalues, ps[0], ps[1], ps[2], ps[3], ratio) - if res: # pragma: no cover + if res: # pragma: no cover handle_sendemail_breakthrough( workoutid, email, first_name, last_name, btvalues=btvalues.to_json()) - subject = "Your OTW Physics Calculations are ready" from_email = 'Rowsandall ' fullemail = first_name + " " + last_name + " " + "<" + email + ">" - d = { - 'first_name':first_name, - 'siteurl':siteurl, - 'workoutid':encoder.encode_hex(workoutid), - } + 'first_name': first_name, + 'siteurl': siteurl, + 'workoutid': encoder.encode_hex(workoutid), + } - res = send_template_email(from_email,[fullemail], - subject,'otwpoweremail.html',d, - **kwargs) + res = send_template_email(from_email, [fullemail], + subject, 'otwpoweremail.html', d, + **kwargs) return 1 @app.task -def handle_updateergcp(rower_id,workoutfilenames,debug=False,**kwargs): +def handle_updateergcp(rower_id, workoutfilenames, debug=False, **kwargs): therows = [] for f1 in workoutfilenames: try: rowdata = rdata(csvfile=f1) - except IOError: # pragma: no cover + except IOError: # pragma: no cover try: rowdata = rdata(csvfile=f1 + '.csv') except IOError: @@ -2302,20 +2287,19 @@ def handle_updateergcp(rower_id,workoutfilenames,debug=False,**kwargs): cpdata = rowingdata.cumcpdata(therows) cpdata.columns = cpdata.columns.str.lower() - updatecpdata_sql(rower_id,cpdata['delta'],cpdata['cp'], - table='ergcpdata',distance=cpdata['distance'], + updatecpdata_sql(rower_id, cpdata['delta'], cpdata['cp'], + table='ergcpdata', distance=cpdata['distance'], debug=debug) return 1 - @app.task -def handle_updatecp(rower_id,workoutids,debug=False,table='cpdata',**kwargs): - columns = ['power','workoutid','time'] - df = getsmallrowdata_db(columns,ids=workoutids,debug=debug) +def handle_updatecp(rower_id, workoutids, debug=False, table='cpdata', **kwargs): + columns = ['power', 'workoutid', 'time'] + df = getsmallrowdata_db(columns, ids=workoutids, debug=debug) - if df.empty: # pragma: no cover + if df.empty: # pragma: no cover return 0 maxt = 1.05*df['time'].max()/1000. @@ -2324,15 +2308,16 @@ def handle_updatecp(rower_id,workoutids,debug=False,table='cpdata',**kwargs): dfgrouped = df.groupby(['workoutid']) - delta,cpvalue,avgpower = datautils.getcp(dfgrouped,logarr) + delta, cpvalue, avgpower = datautils.getcp(dfgrouped, logarr) - updatecpdata_sql(rower_id,delta,cpvalue,debug=debug,table=table) + updatecpdata_sql(rower_id, delta, cpvalue, debug=debug, table=table) return 1 + @app.task def handle_makeplot(f1, f2, t, hrdata, plotnr, imagename, - debug=False,**kwargs): + debug=False, **kwargs): hrmax = hrdata['hrmax'] hrut2 = hrdata['hrut2'] @@ -2353,7 +2338,7 @@ def handle_makeplot(f1, f2, t, hrdata, plotnr, imagename, hrzones=hrzones) try: row = rdata(csvfile=f2, rower=rr) - except IOError: # pragma: no cover + except IOError: # pragma: no cover row = rdata(csvfile=f2 + '.gz', rower=rr) try: @@ -2361,53 +2346,52 @@ def handle_makeplot(f1, f2, t, hrdata, plotnr, imagename, except (TypeError, KeyError): haspower = False - oterange = kwargs.pop('oterange',[85,240]) - otwrange = kwargs.pop('otwrange',[85,185]) - + oterange = kwargs.pop('oterange', [85, 240]) + otwrange = kwargs.pop('otwrange', [85, 185]) nr_rows = len(row.df) - if (plotnr in [1, 2, 4, 5, 8, 11, 9, 12]) and (nr_rows > 1200): # pragma: no cover + if (plotnr in [1, 2, 4, 5, 8, 11, 9, 12]) and (nr_rows > 1200): # pragma: no cover bin = int(nr_rows / 1200.) df = row.df.groupby(lambda x: x / bin).mean() row.df = df nr_rows = len(row.df) if (plotnr == 1): - fig1 = row.get_timeplot_erg(t,pacerange=oterange,**kwargs) + fig1 = row.get_timeplot_erg(t, pacerange=oterange, **kwargs) elif (plotnr == 2): - fig1 = row.get_metersplot_erg(t,pacerange=oterange,**kwargs) + fig1 = row.get_metersplot_erg(t, pacerange=oterange, **kwargs) elif (plotnr == 3): try: t += ' - Heart Rate Distribution' - except TypeError: # pragma: no cover + except TypeError: # pragma: no cover t = 'Heart Rate Distribution' - fig1 = row.get_piechart(t,**kwargs) + fig1 = row.get_piechart(t, **kwargs) elif (plotnr == 4): - if haspower: # pragma: no cover - fig1 = row.get_timeplot_otwempower(t,pacerange=otwrange,**kwargs) + if haspower: # pragma: no cover + fig1 = row.get_timeplot_otwempower(t, pacerange=otwrange, **kwargs) else: - fig1 = row.get_timeplot_otw(t,pacerange=otwrange,**kwargs) + fig1 = row.get_timeplot_otw(t, pacerange=otwrange, **kwargs) elif (plotnr == 5): - if haspower: # pragma: no cover - fig1 = row.get_metersplot_otwempower(t,pacerange=otwrange,**kwargs) + if haspower: # pragma: no cover + fig1 = row.get_metersplot_otwempower( + t, pacerange=otwrange, **kwargs) else: - fig1 = row.get_metersplot_otw(t,pacerange=otwrange,**kwargs) + fig1 = row.get_metersplot_otw(t, pacerange=otwrange, **kwargs) elif (plotnr == 6): t += ' - Heart Rate Distribution' - fig1 = row.get_piechart(t,**kwargs) + fig1 = row.get_piechart(t, **kwargs) elif (plotnr == 7) or (plotnr == 10): - fig1 = row.get_metersplot_erg2(t,**kwargs) + fig1 = row.get_metersplot_erg2(t, **kwargs) elif (plotnr == 8) or (plotnr == 11): - fig1 = row.get_timeplot_erg2(t,**kwargs) + fig1 = row.get_timeplot_erg2(t, **kwargs) elif (plotnr == 9) or (plotnr == 12): - fig1 = row.get_time_otwpower(t,pacerange=otwrange,**kwargs) + fig1 = row.get_time_otwpower(t, pacerange=otwrange, **kwargs) elif (plotnr == 13) or (plotnr == 16): t += ' - Power Distribution' - fig1 = row.get_power_piechart(t,**kwargs) + fig1 = row.get_power_piechart(t, **kwargs) - if fig1 is None: # pragma: no cover + if fig1 is None: # pragma: no cover return 0 - canvas = FigureCanvas(fig1) canvas.print_figure('static/plots/' + imagename) @@ -2419,170 +2403,182 @@ def handle_makeplot(f1, f2, t, hrdata, plotnr, imagename, # Team related remote tasks + @app.task -def handle_sendemail_coachrequest(email,name,code,coachname, - debug=False,**kwargs): +def handle_sendemail_coachrequest(email, name, code, coachname, + debug=False, **kwargs): fullemail = email subject = 'Invitation to add {n} to your athletes'.format(n=name) from_email = 'Rowsandall ' siteurl = SITE_URL - if debug: # pragma: no cover + if debug: # pragma: no cover siteurl = SITE_URL_DEV d = { - 'name':name, - 'coach':coachname, - 'code':code, - 'siteurl':siteurl - } + 'name': name, + 'coach': coachname, + 'code': code, + 'siteurl': siteurl + } form_email = 'Rowsandall ' - res = send_template_email(from_email,[fullemail], - subject,'coachrequestemail.html',d, + res = send_template_email(from_email, [fullemail], + subject, 'coachrequestemail.html', d, **kwargs) return 1 + @app.task -def handle_sendemail_coachoffer_rejected(coachemail,coachname,name, - debug=False,**kwargs): +def handle_sendemail_coachoffer_rejected(coachemail, coachname, name, + debug=False, **kwargs): fullemail = coachemail - subject = '{n} has rejected your offer to be his coach on rowsandall.com'.format(n=name) + subject = '{n} has rejected your offer to be his coach on rowsandall.com'.format( + n=name) from_email = 'Rowsandall ' siteurl = SITE_URL - if debug: # pragma: no cover + if debug: # pragma: no cover siteurl = SITE_URL_DEV d = { - 'name':name, - 'coach':coachname, - 'siteurl':siteurl, - } + 'name': name, + 'coach': coachname, + 'siteurl': siteurl, + } - res = send_template_email(from_email,[fullemail], - subject,'coachofferrejectedemail.html', + res = send_template_email(from_email, [fullemail], + subject, 'coachofferrejectedemail.html', d, **kwargs) return 1 + @app.task -def handle_sendemail_coachrequest_rejected(email,coachname,name, - debug=False,**kwargs): +def handle_sendemail_coachrequest_rejected(email, coachname, name, + debug=False, **kwargs): fullemail = email - subject = '{n} has rejected your coaching request on rowsandall.com'.format(n=coachname) + subject = '{n} has rejected your coaching request on rowsandall.com'.format( + n=coachname) from_email = 'Rowsandall ' siteurl = SITE_URL - if debug: # pragma: no cover + if debug: # pragma: no cover siteurl = SITE_URL_DEV d = { - 'name':name, - 'coach':coachname, - 'siteurl':siteurl, - } + 'name': name, + 'coach': coachname, + 'siteurl': siteurl, + } - res = send_template_email(from_email,[fullemail], - subject,'coachrequestrejectedemail.html', + res = send_template_email(from_email, [fullemail], + subject, 'coachrequestrejectedemail.html', d, **kwargs) return 1 + @app.task -def handle_sendemail_coachrequest_accepted(email,coachname,name, - debug=False,**kwargs): +def handle_sendemail_coachrequest_accepted(email, coachname, name, + debug=False, **kwargs): fullemail = email - subject = '{n} has accepted your coaching request on rowsandall.com'.format(n=coachname) + subject = '{n} has accepted your coaching request on rowsandall.com'.format( + n=coachname) from_email = 'Rowsandall ' siteurl = SITE_URL - if debug: # pragma: no cover + if debug: # pragma: no cover siteurl = SITE_URL_DEV d = { - 'name':name, - 'coach':coachname, - 'siteurl':siteurl, - } + 'name': name, + 'coach': coachname, + 'siteurl': siteurl, + } - res = send_template_email(from_email,[fullemail], - subject,'coachrequestacceptedemail.html', + res = send_template_email(from_email, [fullemail], + subject, 'coachrequestacceptedemail.html', d, **kwargs) return 1 + @app.task -def handle_sendemail_coachoffer_accepted(coachemail,coachname,name, - debug=False,**kwargs): +def handle_sendemail_coachoffer_accepted(coachemail, coachname, name, + debug=False, **kwargs): fullemail = coachemail - subject = '{n} has accepted your coaching offer on rowsandall.com'.format(n=name) + subject = '{n} has accepted your coaching offer on rowsandall.com'.format( + n=name) from_email = 'Rowsandall ' siteurl = SITE_URL - if debug: # pragma: no cover + if debug: # pragma: no cover siteurl = SITE_URL_DEV d = { - 'name':name, - 'coach':coachname, - 'siteurl':siteurl, - } + 'name': name, + 'coach': coachname, + 'siteurl': siteurl, + } - res = send_template_email(from_email,[fullemail], - subject,'coachofferacceptedemail.html', + res = send_template_email(from_email, [fullemail], + subject, 'coachofferacceptedemail.html', d, **kwargs) return 1 + @app.task -def handle_sendemail_coacheerequest(email,name,code,coachname, - debug=False,**kwargs): +def handle_sendemail_coacheerequest(email, name, code, coachname, + debug=False, **kwargs): fullemail = email - subject = '{n} requests coach access to your data on rowsandall.com'.format(n=coachname) + subject = '{n} requests coach access to your data on rowsandall.com'.format( + n=coachname) from_email = 'Rowsandall ' siteurl = SITE_URL - if debug: # pragma: no cover + if debug: # pragma: no cover siteurl = SITE_URL_DEV d = { - 'name':name, - 'coach':coachname, - 'code':code, - 'siteurl':siteurl - } + 'name': name, + 'coach': coachname, + 'code': code, + 'siteurl': siteurl + } - res = send_template_email(from_email,[fullemail], - subject,'coacheerequestemail.html',d, + res = send_template_email(from_email, [fullemail], + subject, 'coacheerequestemail.html', d, **kwargs) return 1 + @app.task def handle_sendemail_invite(email, name, code, teamname, manager, - debug=False,**kwargs): + debug=False, **kwargs): fullemail = email subject = 'Invitation to join team ' + teamname siteurl = SITE_URL - if debug: # pragma: no cover + if debug: # pragma: no cover siteurl = SITE_URL_DEV d = { - 'name':name, - 'manager':manager, - 'code':code, - 'teamname':teamname, - 'siteurl':siteurl - } + 'name': name, + 'manager': manager, + 'code': code, + 'teamname': teamname, + 'siteurl': siteurl + } from_email = 'Rowsandall ' - res = send_template_email(from_email,[fullemail], - subject,'teaminviteemail.html',d, + res = send_template_email(from_email, [fullemail], + subject, 'teaminviteemail.html', d, **kwargs) return 1 @@ -2595,7 +2591,7 @@ def handle_sendemailnewresponse(first_name, last_name, commenter_last_name, comment, workoutname, workoutid, commentid, - debug=False,**kwargs): + debug=False, **kwargs): fullemail = email from_email = 'Rowsandall ' subject = 'New comment on session ' + workoutname @@ -2603,34 +2599,34 @@ def handle_sendemailnewresponse(first_name, last_name, comment = u''+comment siteurl = SITE_URL - if debug: # pragma: no cover + if debug: # pragma: no cover siteurl = SITE_URL_DEV sessiontype = 'workout' - if 'sessiontype' in kwargs: # pragma: no cover - sessiontype=kwargs.pop('sessiontype') + if 'sessiontype' in kwargs: # pragma: no cover + sessiontype = kwargs.pop('sessiontype') commentlink = '/rowers/workout/{workoutid}/comment/'.format( workoutid=encoder.encode_hex(workoutid)) - if 'commentlink' in kwargs: # pragma: no cover + if 'commentlink' in kwargs: # pragma: no cover commentlink = kwargs.pop('commentlink') d = { - 'first_name':first_name, - 'commenter_first_name':commenter_first_name, - 'commenter_last_name':commenter_last_name, - 'comment':comment, - 'workoutname':workoutname, - 'siteurl':siteurl, - 'workoutid':workoutid, - 'commentid':commentid, - 'sessiontype':sessiontype, - 'commentlink':commentlink, - } + 'first_name': first_name, + 'commenter_first_name': commenter_first_name, + 'commenter_last_name': commenter_last_name, + 'comment': comment, + 'workoutname': workoutname, + 'siteurl': siteurl, + 'workoutid': workoutid, + 'commentid': commentid, + 'sessiontype': sessiontype, + 'commentlink': commentlink, + } res = send_template_email(from_email, [fullemail], - subject,'teamresponseemail.html',d,**kwargs) + subject, 'teamresponseemail.html', d, **kwargs) return 1 @@ -2643,9 +2639,7 @@ def handle_sendemailnewcomment(first_name, commenter_last_name, comment, workoutname, workoutid, - debug=False,**kwargs): - - + debug=False, **kwargs): fullemail = email from_email = 'Rowsandall ' @@ -2658,98 +2652,99 @@ def handle_sendemailnewcomment(first_name, siteurl = SITE_URL_DEV sessiontype = 'workout' - if 'sessiontype' in kwargs: # pragma: no cover - sessiontype=kwargs.pop('sessiontype') + if 'sessiontype' in kwargs: # pragma: no cover + sessiontype = kwargs.pop('sessiontype') commentlink = '/rowers/workout/{workoutid}/comment/'.format( workoutid=encoder.encode_hex(workoutid)) - if 'commentlink' in kwargs: # pragma: no cover + if 'commentlink' in kwargs: # pragma: no cover commentlink = kwargs.pop('commentlink') d = { - 'first_name':first_name, - 'commenter_first_name':commenter_first_name, - 'commenter_last_name':commenter_last_name, - 'comment':comment, - 'workoutname':workoutname, - 'siteurl':siteurl, - 'workoutid':encoder.encode_hex(workoutid), - 'sessiontype':sessiontype, - 'commentlink':commentlink, - } - - res = send_template_email(from_email,[fullemail],subject, - 'teamresponseemail.html',d,**kwargs) + 'first_name': first_name, + 'commenter_first_name': commenter_first_name, + 'commenter_last_name': commenter_last_name, + 'comment': comment, + 'workoutname': workoutname, + 'siteurl': siteurl, + 'workoutid': encoder.encode_hex(workoutid), + 'sessiontype': sessiontype, + 'commentlink': commentlink, + } + res = send_template_email(from_email, [fullemail], subject, + 'teamresponseemail.html', d, **kwargs) return 1 + @app.task -def handle_send_template_email(template,email,fromemail,rowername, - subject,message,debug=False,**kwargs): +def handle_send_template_email(template, email, fromemail, rowername, + subject, message, debug=False, **kwargs): fullemail = [email] d = { - 'message':message, - 'rowername':rowername, - } + 'message': message, + 'rowername': rowername, + } res = send_template_email('Rowsandall ', - ['info@rowsandall.com'],subject, - template,d,cc=[fromemail],bcc=fullemail,**kwargs) + ['info@rowsandall.com'], subject, + template, d, cc=[fromemail], bcc=fullemail, **kwargs) return 1 + @app.task -def handle_sendemail_message(email,fromemail,rowername,message,teamname,managername, - debug=False,**kwargs): +def handle_sendemail_message(email, fromemail, rowername, message, teamname, managername, + debug=False, **kwargs): fullemail = email subject = 'New message from team ' + teamname from_email = 'Rowsandall ' d = { - 'rowername':rowername, - 'teamname':teamname, - 'managername':managername, - 'message':message, - } + 'rowername': rowername, + 'teamname': teamname, + 'managername': managername, + 'message': message, + } - res = send_template_email(from_email,[fullemail],subject, - 'teammessage.html',d,**kwargs) + res = send_template_email(from_email, [fullemail], subject, + 'teammessage.html', d, **kwargs) return 1 + @app.task def handle_sendemail_request(email, name, code, teamname, requestor, id, - debug=False,**kwargs): + debug=False, **kwargs): fullemail = email subject = 'Request to join team ' + teamname from_email = 'Rowsandall ' siteurl = SITE_URL - if debug: # pragma: no cover + if debug: # pragma: no cover siteurl = SITE_URL_DEV d = { - 'requestor':requestor, - 'teamname':teamname, + 'requestor': requestor, + 'teamname': teamname, 'code': code, - 'siteurl':siteurl, - 'id':id, - 'first_name':name, - } - - res = send_template_email(from_email,[fullemail],subject, - 'teamrequestemail.html',d,**kwargs) + 'siteurl': siteurl, + 'id': id, + 'first_name': name, + } + res = send_template_email(from_email, [fullemail], subject, + 'teamrequestemail.html', d, **kwargs) return 1 @app.task def handle_sendemail_request_accept(email, name, teamname, managername, - debug=False,**kwargs): + debug=False, **kwargs): fullemail = email subject = 'Welcome to ' + teamname from_email = 'Rowsandall ' @@ -2759,21 +2754,20 @@ def handle_sendemail_request_accept(email, name, teamname, managername, siteurl = SITE_URL_DEV d = { - 'first_name':name, - 'managername':managername, - 'teamname':teamname, - 'siteurl':siteurl, - } - res = send_template_email(from_email,[fullemail],subject, - 'teamwelcomeemail.html',d,**kwargs) - + 'first_name': name, + 'managername': managername, + 'teamname': teamname, + 'siteurl': siteurl, + } + res = send_template_email(from_email, [fullemail], subject, + 'teamwelcomeemail.html', d, **kwargs) return 1 @app.task def handle_sendemail_request_reject(email, name, teamname, managername, - debug=False,**kwargs): + debug=False, **kwargs): fullemail = email subject = 'Your application to ' + teamname + ' was rejected' from_email = 'Rowsandall ' @@ -2783,20 +2777,20 @@ def handle_sendemail_request_reject(email, name, teamname, managername, siteurl = SITE_URL_DEV d = { - 'first_name':name, - 'managername':managername, - 'teamname':teamname, - 'siteurl':siteurl, - } - res = send_template_email(from_email,[fullemail],subject, - 'teamrejectemail.html',d,**kwargs) + 'first_name': name, + 'managername': managername, + 'teamname': teamname, + 'siteurl': siteurl, + } + res = send_template_email(from_email, [fullemail], subject, + 'teamrejectemail.html', d, **kwargs) return 1 @app.task def handle_sendemail_member_dropped(email, name, teamname, managername, - debug=False,**kwargs): + debug=False, **kwargs): fullemail = email subject = 'You were removed from ' + teamname from_email = 'Rowsandall ' @@ -2806,21 +2800,20 @@ def handle_sendemail_member_dropped(email, name, teamname, managername, siteurl = SITE_URL_DEV d = { - 'first_name':name, - 'managername':managername, - 'teamname':teamname, - 'siteurl':siteurl, + 'first_name': name, + 'managername': managername, + 'teamname': teamname, + 'siteurl': siteurl, } - res = send_template_email(from_email,[fullemail],subject, - 'teamdropemail.html',d,**kwargs) - + res = send_template_email(from_email, [fullemail], subject, + 'teamdropemail.html', d, **kwargs) return 1 @app.task def handle_sendemail_team_removed(email, name, teamname, managername, - debug=False,**kwargs): + debug=False, **kwargs): fullemail = email subject = 'You were removed from ' + teamname @@ -2831,21 +2824,20 @@ def handle_sendemail_team_removed(email, name, teamname, managername, siteurl = SITE_URL_DEV d = { - 'first_name':name, - 'managername':managername, - 'teamname':teamname, - 'siteurl':siteurl, - } - res = send_template_email(from_email,[fullemail],subject, - 'teamremoveemail.html',d,**kwargs) - + 'first_name': name, + 'managername': managername, + 'teamname': teamname, + 'siteurl': siteurl, + } + res = send_template_email(from_email, [fullemail], subject, + 'teamremoveemail.html', d, **kwargs) return 1 @app.task def handle_sendemail_invite_reject(email, name, teamname, managername, - debug=False,**kwargs): + debug=False, **kwargs): fullemail = email subject = 'Your invitation to ' + name + ' was rejected' @@ -2856,19 +2848,19 @@ def handle_sendemail_invite_reject(email, name, teamname, managername, siteurl = SITE_URL_DEV d = { - 'first_name':name, - 'managername':managername, - 'teamname':teamname, - 'siteurl':siteurl, - } - res = send_template_email(from_email,[fullemail],subject, - 'teaminviterejectemail.html',d,**kwargs) + 'first_name': name, + 'managername': managername, + 'teamname': teamname, + 'siteurl': siteurl, + } + res = send_template_email(from_email, [fullemail], subject, + 'teaminviterejectemail.html', d, **kwargs) return 1 @app.task -def handle_setcp(strokesdf,filename,workoutid,debug=False,**kwargs): +def handle_setcp(strokesdf, filename, workoutid, debug=False, **kwargs): try: os.remove(filename) except FileNotFoundError: @@ -2877,14 +2869,13 @@ def handle_setcp(strokesdf,filename,workoutid,debug=False,**kwargs): try: totaltime = strokesdf['time'].max() - except KeyError: # pragma: no cover + except KeyError: # pragma: no cover return 0 try: powermean = strokesdf['power'].mean() - except KeyError: # pragma: no cover + except KeyError: # pragma: no cover powermean = 0 - if powermean != 0: thesecs = totaltime maxt = 1.05 * thesecs @@ -2895,18 +2886,20 @@ def handle_setcp(strokesdf,filename,workoutid,debug=False,**kwargs): delta, cpvalues, avgpower = datautils.getcp(dfgrouped, logarr) df = pd.DataFrame({ - 'delta':delta, - 'cp':cpvalues, - 'id':workoutid, + 'delta': delta, + 'cp': cpvalues, + 'id': workoutid, }) - df.to_parquet(filename,engine='fastparquet',compression='GZIP') + df.to_parquet(filename, engine='fastparquet', + compression='GZIP') return 1 - return 1 # pragma: no cover + return 1 # pragma: no cover + @app.task def handle_sendemail_invite_accept(email, name, teamname, managername, - debug=False,**kwargs): + debug=False, **kwargs): fullemail = email subject = 'Your invitation to ' + name + ' was accepted' @@ -2917,42 +2910,45 @@ def handle_sendemail_invite_accept(email, name, teamname, managername, siteurl = SITE_URL_DEV d = { - 'first_name':name, - 'managername':managername, - 'teamname':teamname, - 'siteurl':siteurl, - } - res = send_template_email(from_email,[fullemail],subject, - 'teaminviteacceptemail.html',d,**kwargs) - + 'first_name': name, + 'managername': managername, + 'teamname': teamname, + 'siteurl': siteurl, + } + res = send_template_email(from_email, [fullemail], subject, + 'teaminviteacceptemail.html', d, **kwargs) return 1 # Another simple task for debugging purposes -def add2(x, y,debug=False,**kwargs): # pragma: no cover +def add2(x, y, debug=False, **kwargs): # pragma: no cover return x + y + graphql_url = "https://rp3rowing-app.com/graphql" + @app.task -def handle_update_wps(rid,types,ids,mode,debug=False,**kwargs): - df = getsmallrowdata_db(['time','driveenergy'],ids=ids) +def handle_update_wps(rid, types, ids, mode, debug=False, **kwargs): + df = getsmallrowdata_db(['time', 'driveenergy'], ids=ids) try: mask = df['driveenergy'] > 100 - except (KeyError, TypeError): # pragma: no cover + except (KeyError, TypeError): # pragma: no cover return 0 try: - wps_median = int(df.loc[mask,'driveenergy'].median()) - except ValueError: # pragma: no cover + wps_median = int(df.loc[mask, 'driveenergy'].median()) + except ValueError: # pragma: no cover return 0 if mode == 'water': - query = "UPDATE `rowers_rower` SET `median_wps` = '%s' WHERE `id` = '%s'" % (wps_median,rid) + query = "UPDATE `rowers_rower` SET `median_wps` = '%s' WHERE `id` = '%s'" % ( + wps_median, rid) else: - query = "UPDATE `rowers_rower` SET `median_wps_erg` = '%s' WHERE `id` = '%s'" % (wps_median,rid) + query = "UPDATE `rowers_rower` SET `median_wps_erg` = '%s' WHERE `id` = '%s'" % ( + wps_median, rid) - if debug: # pragma: no cover + if debug: # pragma: no cover engine = create_engine(database_url_debug, echo=False) else: engine = create_engine(database_url, echo=False) @@ -2965,9 +2961,10 @@ def handle_update_wps(rid,types,ids,mode,debug=False,**kwargs): return wps_median + @app.task -def handle_rp3_async_workout(userid,rp3token,rp3id,startdatetime,max_attempts,debug=False,**kwargs): - headers = {'Authorization': 'Bearer ' + rp3token } +def handle_rp3_async_workout(userid, rp3token, rp3id, startdatetime, max_attempts, debug=False, **kwargs): + headers = {'Authorization': 'Bearer ' + rp3token} get_download_link = """{ download(workout_id: """ + str(rp3id) + """, type:csv){ @@ -2987,86 +2984,85 @@ def handle_rp3_async_workout(userid,rp3token,rp3id,startdatetime,max_attempts,de url=graphql_url, headers=headers, json={'query': get_download_link} - ) + ) - - if response.status_code != 200: # pragma: no cover + if response.status_code != 200: # pragma: no cover have_link = True try: - workout_download_details = pd.json_normalize(response.json()['data']['download']) - except: # pragma: no cover + workout_download_details = pd.json_normalize( + response.json()['data']['download']) + except: # pragma: no cover return 0 - if workout_download_details.iat[0,1] == 'ready': - download_url = workout_download_details.iat[0,2] + if workout_download_details.iat[0, 1] == 'ready': + download_url = workout_download_details.iat[0, 2] have_link = True counter += 1 - if counter>max_attempts: # pragma: no cover + if counter > max_attempts: # pragma: no cover have_link = True time.sleep(waittime) - if download_url == '': # pragma: no cover + if download_url == '': # pragma: no cover return 0 filename = 'media/RP3Import_'+str(rp3id)+'.csv' - res = requests.get(download_url,headers=headers) + res = requests.get(download_url, headers=headers) - - if not startdatetime: # pragma: no cover + if not startdatetime: # pragma: no cover startdatetime = str(timezone.now()) try: startdatetime = str(startdatetime) - except: # pragma: no cover + except: # pragma: no cover pass - if res.status_code != 200: # pragma: no cover + if res.status_code != 200: # pragma: no cover return 0 - - with open(filename,'wb') as f: - #dologging('rp3_import.log',res.text) - dologging('rp3_import.log','Rp3 ID = {id}'.format(id=rp3id)) + with open(filename, 'wb') as f: + # dologging('rp3_import.log',res.text) + dologging('rp3_import.log', 'Rp3 ID = {id}'.format(id=rp3id)) f.write(res.content) uploadoptions = { 'secret': UPLOAD_SERVICE_SECRET, 'user': userid, 'file': filename, - 'workouttype':'dynamic', - 'boattype':'1x', - 'rp3id':int(rp3id), - 'startdatetime':startdatetime, + 'workouttype': 'dynamic', + 'boattype': '1x', + 'rp3id': int(rp3id), + 'startdatetime': startdatetime, } session = requests.session() newHeaders = {'Content-type': 'application/json', 'Accept': 'text/plain'} session.headers.update(newHeaders) - response = session.post(UPLOAD_SERVICE_URL,json=uploadoptions) + response = session.post(UPLOAD_SERVICE_URL, json=uploadoptions) - if response.status_code != 200: # pragma: no cover + if response.status_code != 200: # pragma: no cover return 0 workoutid = response.json()['id'] return workoutid + @app.task -def handle_nk_async_workout(alldata,userid,nktoken,nkid,delaysec,defaulttimezone,debug=False,**kwargs): +def handle_nk_async_workout(alldata, userid, nktoken, nkid, delaysec, defaulttimezone, debug=False, **kwargs): time.sleep(delaysec) s = 'Importing from NK Logbook ID {nkid}'.format(nkid=nkid) - dologging('nklog.log',s) + dologging('nklog.log', s) try: data = alldata[nkid] - except KeyError: # pragma: no cover + except KeyError: # pragma: no cover try: data = alldata[int(nkid)] except KeyError: @@ -3084,18 +3080,19 @@ def handle_nk_async_workout(alldata,userid,nktoken,nkid,delaysec,defaulttimezone # get strokes url = NK_API_LOCATION+"api/v1/sessions/strokes" - response = requests.get(url,headers=headers,params=params) + response = requests.get(url, headers=headers, params=params) - if response.status_code != 200: # pragma: no cover + if response.status_code != 200: # pragma: no cover # error handling and logging - dologging('nklog.log','Response status code {code}'.format(code=response.status_code)) + dologging('nklog.log', 'Response status code {code}'.format( + code=response.status_code)) return 0 jsonData = response.json() strokeData = jsonData[str(nkid)] - dologging('nklog.log',json.dumps(data)) - dologging('nklog.log',json.dumps(strokeData)) + dologging('nklog.log', json.dumps(data)) + dologging('nklog.log', json.dumps(strokeData)) df = strokeDataToDf(strokeData) @@ -3105,21 +3102,22 @@ def handle_nk_async_workout(alldata,userid,nktoken,nkid,delaysec,defaulttimezone csvfilename = 'media/{code}_{nkid}.csv.gz'.format( nkid=nkid, - code = uuid4().hex[:16] + code=uuid4().hex[:16] ) df.to_csv(csvfilename, index_label='index', compression='gzip') - workoutid,error = add_workout_from_data(userid,nkid,data,df) + workoutid, error = add_workout_from_data(userid, nkid, data, df) # dologging('nklog.log','NK Workout ID {id}'.format(id=workoutid)) - if debug: # pragma: no cover + if debug: # pragma: no cover engine = create_engine(database_url_debug, echo=False) else: engine = create_engine(database_url, echo=False) - query = 'SELECT uploadedtonk from rowers_workout WHERE id ={workoutid}'.format(workoutid=workoutid) + query = 'SELECT uploadedtonk from rowers_workout WHERE id ={workoutid}'.format( + workoutid=workoutid) newnkid = 0 with engine.connect() as conn, conn.begin(): @@ -3132,40 +3130,42 @@ def handle_nk_async_workout(alldata,userid,nktoken,nkid,delaysec,defaulttimezone parkedids = [] try: - with open('nkblocked.json','r') as nkblocked: + with open('nkblocked.json', 'r') as nkblocked: jsondata = json.load(nkblocked) parkedids = jsondata['ids'] - except FileNotFoundError: # pragma: no cover + except FileNotFoundError: # pragma: no cover pass newparkedids = [id for id in parkedids if id != newnkid] - with open('nkblocked.json','wt') as nkblocked: - tdata = {'ids':newparkedids} + with open('nkblocked.json', 'wt') as nkblocked: + tdata = {'ids': newparkedids} nkblocked.seek(0) - json.dump(tdata,nkblocked) + json.dump(tdata, nkblocked) # evt update workout summary # return return workoutid + @app.task -def handle_c2_getworkout(userid,c2token,c2id,defaulttimezone,debug=False,**kwargs): +def handle_c2_getworkout(userid, c2token, c2id, defaulttimezone, debug=False, **kwargs): authorizationstring = str('Bearer ' + c2token) headers = {'Authorization': authorizationstring, 'user-agent': 'sanderroosendaal', 'Content-Type': 'application/json'} url = "https://log.concept2.com/api/users/me/results/"+str(c2id) - s = requests.get(url,headers=headers) + s = requests.get(url, headers=headers) - if s.status_code != 200: # pragma: no cover + if s.status_code != 200: # pragma: no cover return 0 data = s.json()['data'] - alldata = {c2id:data} + alldata = {c2id: data} splitdata = None - return handle_c2_async_workout(alldata,userid,c2token,c2id,0,defaulttimezone) + return handle_c2_async_workout(alldata, userid, c2token, c2id, 0, defaulttimezone) + def df_from_summary(data): distance = data['distance'] @@ -3175,23 +3175,24 @@ def df_from_summary(data): weightclass = data['weight_class'] try: title = data['name'] - except KeyError: # pragma: no cover + except KeyError: # pragma: no cover title = "" try: t = data['comments'].split('\n', 1)[0] title += t[:40] - except: # pragma: no cover + except: # pragma: no cover title = '' weightcategory = 'hwt' if weightclass == "L": weightcategory = 'lwt' - startdatetime,starttime,workoutdate,duration,starttimeunix,timezone = utils.get_startdatetime_from_c2data(data) + startdatetime, starttime, workoutdate, duration, starttimeunix, timezone = utils.get_startdatetime_from_c2data( + data) try: splits = data['workout']['splits'] - except (KeyError, TypeError): # pragma: no cover + except (KeyError, TypeError): # pragma: no cover splits = [0] time = starttimeunix elapsed_distance = 0 @@ -3199,27 +3200,27 @@ def df_from_summary(data): distances = [0] try: spms = [splits[0]['stroke_rate']] - except (KeyError, TypeError, IndexError): # pragma: no cover + except (KeyError, TypeError, IndexError): # pragma: no cover spms = [0] try: hrs = [splits[0]['heart_rate']['average']] - except (KeyError, TypeError, IndexError): # pragma: no cover + except (KeyError, TypeError, IndexError): # pragma: no cover hrs = [0] for split in splits: try: time += split['time']/10. times.append(time) - except (KeyError, TypeError): # pragma: no cover + except (KeyError, TypeError): # pragma: no cover times.append(0) try: elapsed_distance += split['distance'] distances.append(elapsed_distance) - except (KeyError, TypeError): # pragma: no cover + except (KeyError, TypeError): # pragma: no cover distances.append(0) try: spms.append(split['stroke_rate']) - except (KeyError, TypeError): # pragma: no cover + except (KeyError, TypeError): # pragma: no cover spms.append(0) try: hrs.append(split['heart_rate']['average']) @@ -3238,20 +3239,19 @@ def df_from_summary(data): return df - @app.task -def handle_c2_async_workout(alldata,userid,c2token,c2id,delaysec,defaulttimezone,debug=False,**kwargs): +def handle_c2_async_workout(alldata, userid, c2token, c2id, delaysec, defaulttimezone, debug=False, **kwargs): time.sleep(delaysec) - with open("c2_auto_import.log","a") as errorlog: + with open("c2_auto_import.log", "a") as errorlog: timestr = time.strftime("%Y%m%d-%H%M%S") errorlog.write(timestr+' '+str(c2id)+' for userid '+str(userid)+'\r\n') data = alldata[c2id] splitdata = None distance = data['distance'] - try: # pragma: no cover + try: # pragma: no cover rest_distance = data['rest_distance'] - rest_time = data['rest_time']/10. + rest_time = data['rest_time']/10. except KeyError: rest_distance = 0 rest_time = 0 @@ -3264,14 +3264,12 @@ def handle_c2_async_workout(alldata,userid,c2token,c2id,delaysec,defaulttimezone try: has_strokedata = data['stroke_data'] - except KeyError: # pragma: no cover + except KeyError: # pragma: no cover has_strokedata = True - - - s = 'User {userid}, C2 ID {c2id}'.format(userid=userid,c2id=c2id) - dologging('debuglog.log',s) - dologging('debuglog.log',json.dumps(data)) + s = 'User {userid}, C2 ID {c2id}'.format(userid=userid, c2id=c2id) + dologging('debuglog.log', s) + dologging('debuglog.log', json.dumps(data)) try: title = data['name'] @@ -3280,7 +3278,7 @@ def handle_c2_async_workout(alldata,userid,c2token,c2id,delaysec,defaulttimezone try: t = data['comments'].split('\n', 1)[0] title += t[:40] - except: # pragma: no cover + except: # pragma: no cover title = '' weightcategory = 'hwt' @@ -3288,186 +3286,184 @@ def handle_c2_async_workout(alldata,userid,c2token,c2id,delaysec,defaulttimezone weightcategory = 'lwt' # Create CSV file name and save data to CSV file - csvfilename ='media/Import_'+str(c2id)+'.csv.gz' - - startdatetime,starttime,workoutdate,duration,starttimeunix,timezone = utils.get_startdatetime_from_c2data(data) + csvfilename = 'media/Import_'+str(c2id)+'.csv.gz' + startdatetime, starttime, workoutdate, duration, starttimeunix, timezone = utils.get_startdatetime_from_c2data( + data) s = 'Time zone {timezone}, startdatetime {startdatetime}, duration {duration}'.format( - timezone=timezone,startdatetime=startdatetime, + timezone=timezone, startdatetime=startdatetime, duration=duration) - dologging('debuglog.log',s) + dologging('debuglog.log', s) try: notes = data['comments'] name = notes[:40] - except (KeyError,TypeError): # pragma: no cover - notes = 'C2 Import Workout from {startdatetime}'.format(startdatetime=startdatetime) + except (KeyError, TypeError): # pragma: no cover + notes = 'C2 Import Workout from {startdatetime}'.format( + startdatetime=startdatetime) name = notes authorizationstring = str('Bearer ' + c2token) headers = {'Authorization': authorizationstring, - 'user-agent': 'sanderroosendaal', - 'Content-Type': 'application/json'} + 'user-agent': 'sanderroosendaal', + 'Content-Type': 'application/json'} url = "https://log.concept2.com/api/users/me/results/"+str(c2id)+"/strokes" try: - s = requests.get(url,headers=headers) - except ConnectionError: # pragma: no cover + s = requests.get(url, headers=headers) + except ConnectionError: # pragma: no cover return 0 - if s.status_code != 200: # pragma: no cover - dologging('debuglog.log','No Stroke Data. Status Code {code}'.format(code=s.status_code)) - dologging('debuglog.log',s.text) + if s.status_code != 200: # pragma: no cover + dologging('debuglog.log', 'No Stroke Data. Status Code {code}'.format( + code=s.status_code)) + dologging('debuglog.log', s.text) has_strokedata = False - - - if not has_strokedata: # pragma: no cover - df = df_from_summary(data) + if not has_strokedata: # pragma: no cover + df = df_from_summary(data) else: - #dologging('debuglog.log',json.dumps(s.json())) + # dologging('debuglog.log',json.dumps(s.json())) try: strokedata = pd.DataFrame.from_dict(s.json()['data']) - except AttributeError: # pragma: no cover - dologging('debuglog.log','No stroke data in stroke data') + except AttributeError: # pragma: no cover + dologging('debuglog.log', 'No stroke data in stroke data') return 0 try: res = make_cumvalues(0.1*strokedata['t']) cum_time = res[0] lapidx = res[1] - except KeyError: # pragma: no cover - dologging('debuglog.log','No time values in stroke data') + except KeyError: # pragma: no cover + dologging('debuglog.log', 'No time values in stroke data') return 0 - unixtime = cum_time+starttimeunix # unixtime[0] = starttimeunix - seconds = 0.1*strokedata.loc[:,'t'] + seconds = 0.1*strokedata.loc[:, 't'] nr_rows = len(unixtime) - try: # pragma: no cover - latcoord = strokedata.loc[:,'lat'] - loncoord = strokedata.loc[:,'lon'] + try: # pragma: no cover + latcoord = strokedata.loc[:, 'lat'] + loncoord = strokedata.loc[:, 'lon'] except: latcoord = np.zeros(nr_rows) loncoord = np.zeros(nr_rows) - try: - strokelength = strokedata.loc[:,'strokelength'] - except: # pragma: no cover + strokelength = strokedata.loc[:, 'strokelength'] + except: # pragma: no cover strokelength = np.zeros(nr_rows) - dist2 = 0.1*strokedata.loc[:,'d'] + dist2 = 0.1*strokedata.loc[:, 'd'] try: - spm = strokedata.loc[:,'spm'] - except KeyError: # pragma: no cover + spm = strokedata.loc[:, 'spm'] + except KeyError: # pragma: no cover spm = 0*dist2 try: - hr = strokedata.loc[:,'hr'] - except KeyError: # pragma: no cover + hr = strokedata.loc[:, 'hr'] + except KeyError: # pragma: no cover hr = 0*spm - pace = strokedata.loc[:,'p']/10. - pace = np.clip(pace,0,1e4) - pace = pace.replace(0,300) + pace = strokedata.loc[:, 'p']/10. + pace = np.clip(pace, 0, 1e4) + pace = pace.replace(0, 300) velo = 500./pace power = 2.8*velo**3 - if workouttype == 'bike': # pragma: no cover + if workouttype == 'bike': # pragma: no cover velo = 1000./pace - dologging('debuglog.log','Unix Time Stamp {s}'.format(s=unixtime[0])) - #dologging('debuglog.log',json.dumps(s.json())) + dologging('debuglog.log', 'Unix Time Stamp {s}'.format(s=unixtime[0])) + # dologging('debuglog.log',json.dumps(s.json())) - df = pd.DataFrame({'TimeStamp (sec)':unixtime, + df = pd.DataFrame({'TimeStamp (sec)': unixtime, ' Horizontal (meters)': dist2, - ' Cadence (stokes/min)':spm, - ' HRCur (bpm)':hr, - ' longitude':loncoord, - ' latitude':latcoord, - ' Stroke500mPace (sec/500m)':pace, - ' Power (watts)':power, - ' DragFactor':np.zeros(nr_rows), - ' DriveLength (meters)':np.zeros(nr_rows), - ' StrokeDistance (meters)':strokelength, - ' DriveTime (ms)':np.zeros(nr_rows), - ' StrokeRecoveryTime (ms)':np.zeros(nr_rows), - ' AverageDriveForce (lbs)':np.zeros(nr_rows), - ' PeakDriveForce (lbs)':np.zeros(nr_rows), - ' lapIdx':lapidx, + ' Cadence (stokes/min)': spm, + ' HRCur (bpm)': hr, + ' longitude': loncoord, + ' latitude': latcoord, + ' Stroke500mPace (sec/500m)': pace, + ' Power (watts)': power, + ' DragFactor': np.zeros(nr_rows), + ' DriveLength (meters)': np.zeros(nr_rows), + ' StrokeDistance (meters)': strokelength, + ' DriveTime (ms)': np.zeros(nr_rows), + ' StrokeRecoveryTime (ms)': np.zeros(nr_rows), + ' AverageDriveForce (lbs)': np.zeros(nr_rows), + ' PeakDriveForce (lbs)': np.zeros(nr_rows), + ' lapIdx': lapidx, ' WorkoutState': 4, - ' ElapsedTime (sec)':seconds, + ' ElapsedTime (sec)': seconds, 'cum_dist': dist2 }) + df.sort_values(by='TimeStamp (sec)', ascending=True) - df.sort_values(by='TimeStamp (sec)',ascending=True) - - res = df.to_csv(csvfilename,index_label='index', - compression='gzip' - ) + res = df.to_csv(csvfilename, index_label='index', + compression='gzip' + ) uploadoptions = { - 'secret':UPLOAD_SERVICE_SECRET, - 'user':userid, + 'secret': UPLOAD_SERVICE_SECRET, + 'user': userid, 'file': csvfilename, 'title': title, - 'workouttype':workouttype, - 'boattype':'1x', - 'c2id':c2id, - 'startdatetime':startdatetime.isoformat(), - 'timezone':str(timezone) + 'workouttype': workouttype, + 'boattype': '1x', + 'c2id': c2id, + 'startdatetime': startdatetime.isoformat(), + 'timezone': str(timezone) } - session = requests.session() newHeaders = {'Content-type': 'application/json', 'Accept': 'text/plain'} session.headers.update(newHeaders) - response = session.post(UPLOAD_SERVICE_URL,json=uploadoptions) + response = session.post(UPLOAD_SERVICE_URL, json=uploadoptions) - if response.status_code != 200: # pragma: no cover + if response.status_code != 200: # pragma: no cover return 0 workoutid = response.json()['id'] - if debug: # pragma: no cover + if debug: # pragma: no cover engine = create_engine(database_url_debug, echo=False) else: engine = create_engine(database_url, echo=False) - query = 'SELECT uploadedtoc2 from rowers_workout WHERE id ={workoutid}'.format(workoutid=workoutid) + query = 'SELECT uploadedtoc2 from rowers_workout WHERE id ={workoutid}'.format( + workoutid=workoutid) newc2id = 0 with engine.connect() as conn, conn.begin(): result = conn.execute(query) tdata = result.fetchall() - if tdata: # pragma: no cover + if tdata: # pragma: no cover newc2id = tdata[0][0] conn.close() parkedids = [] - with open('c2blocked.json','r') as c2blocked: + with open('c2blocked.json', 'r') as c2blocked: try: jsondata = json.load(c2blocked) parkedids = jsondata['ids'] - except JSONDecodeError: # pragma: no cover + except JSONDecodeError: # pragma: no cover parkedids = [] newparkedids = [id for id in parkedids if id != newc2id] - with open('c2blocked.json','wt') as c2blocked: - tdata = {'ids':newparkedids} + with open('c2blocked.json', 'wt') as c2blocked: + tdata = {'ids': newparkedids} c2blocked.seek(0) - json.dump(tdata,c2blocked) + json.dump(tdata, c2blocked) # set distance, time - query = "UPDATE `rowers_workout` SET `distance` = '%s', `duration` = '%s' WHERE `id` = '%s'" % (distance, duration, workoutid) + query = "UPDATE `rowers_workout` SET `distance` = '%s', `duration` = '%s' WHERE `id` = '%s'" % ( + distance, duration, workoutid) with engine.connect() as conn, conn.begin(): result = conn.execute(query) @@ -3477,19 +3473,21 @@ def handle_c2_async_workout(alldata,userid,c2token,c2id,delaysec,defaulttimezone # summary if 'workout' in data: - if 'splits' in data['workout']: # pragma: no cover + if 'splits' in data['workout']: # pragma: no cover splitdata = data['workout']['splits'] - elif 'intervals' in data['workout']: # pragma: no cover + elif 'intervals' in data['workout']: # pragma: no cover splitdata = data['workout']['intervals'] - else: # pragma: no cover + else: # pragma: no cover splitdata = False else: splitdata = False - if splitdata: # pragma: no cover - summary,sa,results = summaryfromsplitdata(splitdata,data,csvfilename,workouttype=workouttype) + if splitdata: # pragma: no cover + summary, sa, results = summaryfromsplitdata( + splitdata, data, csvfilename, workouttype=workouttype) - query = "UPDATE `rowers_workout` SET `summary` = '%s' WHERE `id` = %s" % (summary, workoutid) + query = "UPDATE `rowers_workout` SET `summary` = '%s' WHERE `id` = %s" % ( + summary, workoutid) with engine.connect() as conn, conn.begin(): result = conn.execute(query) @@ -3500,35 +3498,34 @@ def handle_c2_async_workout(alldata,userid,c2token,c2id,delaysec,defaulttimezone from rowingdata.trainingparser import getlist if sa: values = getlist(sa) - units = getlist(sa,sel='unit') - types = getlist(sa,sel='type') + units = getlist(sa, sel='unit') + types = getlist(sa, sel='type') rowdata = rdata(csvfile=csvfilename) if rowdata: rowdata.updateintervaldata(values, - units,types,results) + units, types, results) - rowdata.write_csv(csvfilename,gzip=True) - dataprepnodjango.update_strokedata(w.id,rowdata.df) + rowdata.write_csv(csvfilename, gzip=True) + dataprepnodjango.update_strokedata(w.id, rowdata.df) return workoutid - @app.task -def fetch_strava_workout(stravatoken,oauth_data,stravaid,csvfilename,userid,debug=False,**kwargs): +def fetch_strava_workout(stravatoken, oauth_data, stravaid, csvfilename, userid, debug=False, **kwargs): fetchresolution = 'high' authorizationstring = str('Bearer '+stravatoken) headers = {'Authorization': authorizationstring, 'user-agent': 'sanderroosendaal', 'Content-Type': 'application/json', - 'resolution': 'medium',} + 'resolution': 'medium', } url = "https://www.strava.com/api/v3/activities/"+str(stravaid) - response = requests.get(url,headers=headers) + response = requests.get(url, headers=headers) if response.status_code != 200: # pragma: no cover tstamp = time.localtime() timestamp = time.strftime('%b-%d-%Y_%H%M', tstamp) - with open('strava_webhooks.log','a') as f: + with open('strava_webhooks.log', 'a') as f: f.write('\n') f.write(timestamp) f.write(' ') @@ -3538,7 +3535,8 @@ def fetch_strava_workout(stravatoken,oauth_data,stravaid,csvfilename,userid,debu f.write(' ') f.write(authorizationstring) f.write(' ') - f.write('handle_get_strava_file response code {code}\n'.format(code=response.status_code)) + f.write('handle_get_strava_file response code {code}\n'.format( + code=response.status_code)) try: f.write('Response json {json}\n'.format(json=response.json())) except: @@ -3547,27 +3545,33 @@ def fetch_strava_workout(stravatoken,oauth_data,stravaid,csvfilename,userid,debu return 0 try: - workoutsummary = requests.get(url,headers=headers).json() - except: # pragma: no cover + workoutsummary = requests.get(url, headers=headers).json() + except: # pragma: no cover return 0 - try: startdatetime = workoutsummary['start_date'] - except KeyError: # pragma: no cover + except KeyError: # pragma: no cover startdatetime = timezone.now() - spm = get_strava_stream(None,'cadence',stravaid,authorizationstring=authorizationstring) - hr = get_strava_stream(None,'heartrate',stravaid,authorizationstring=authorizationstring) - t = get_strava_stream(None,'time',stravaid,authorizationstring=authorizationstring) - velo = get_strava_stream(None,'velocity_smooth',stravaid,authorizationstring=authorizationstring) - d = get_strava_stream(None,'distance',stravaid,authorizationstring=authorizationstring) - coords = get_strava_stream(None,'latlng',stravaid,authorizationstring=authorizationstring) - power = get_strava_stream(None,'watts',stravaid,authorizationstring=authorizationstring) + spm = get_strava_stream(None, 'cadence', stravaid, + authorizationstring=authorizationstring) + hr = get_strava_stream(None, 'heartrate', stravaid, + authorizationstring=authorizationstring) + t = get_strava_stream(None, 'time', stravaid, + authorizationstring=authorizationstring) + velo = get_strava_stream(None, 'velocity_smooth', + stravaid, authorizationstring=authorizationstring) + d = get_strava_stream(None, 'distance', stravaid, + authorizationstring=authorizationstring) + coords = get_strava_stream( + None, 'latlng', stravaid, authorizationstring=authorizationstring) + power = get_strava_stream(None, 'watts', stravaid, + authorizationstring=authorizationstring) tstamp = time.localtime() timestamp = time.strftime('%b-%d-%Y_%H%M', tstamp) - with open('strava_webhooks.log','a') as f: + with open('strava_webhooks.log', 'a') as f: f.write('\n') f.write(timestamp) f.write(' ') @@ -3577,7 +3581,7 @@ def fetch_strava_workout(stravatoken,oauth_data,stravaid,csvfilename,userid,debu if t is not None: nr_rows = len(t) - else: # pragma: no cover + else: # pragma: no cover try: duration = int(workoutsummary['elapsed_time']) except KeyError: @@ -3586,78 +3590,72 @@ def fetch_strava_workout(stravatoken,oauth_data,stravaid,csvfilename,userid,debu nr_rows = len(t) - - if nr_rows == 0: # pragma: no cover + if nr_rows == 0: # pragma: no cover return 0 - if d is None: # pragma: no cover + if d is None: # pragma: no cover d = 0*t - if spm is None: # pragma: no cover + if spm is None: # pragma: no cover spm = np.zeros(nr_rows) - if power is None: # pragma: no cover + if power is None: # pragma: no cover power = np.zeros(nr_rows) - if hr is None: # pragma: no cover + if hr is None: # pragma: no cover hr = np.zeros(nr_rows) - if velo is None: # pragma: no cover + if velo is None: # pragma: no cover velo = np.zeros(nr_rows) dt = np.diff(t).mean() wsize = round(5./dt) - velo2 = ewmovingaverage(velo,wsize) + velo2 = ewmovingaverage(velo, wsize) if coords is not None: try: - lat = coords[:,0] - lon = coords[:,1] - except IndexError: # pragma: no cover + lat = coords[:, 0] + lon = coords[:, 1] + except IndexError: # pragma: no cover lat = np.zeros(len(t)) lon = np.zeros(len(t)) - else: # pragma: no cover + else: # pragma: no cover lat = np.zeros(len(t)) lon = np.zeros(len(t)) - - - strokelength = velo*60./(spm) strokelength[np.isinf(strokelength)] = 0.0 - pace = 500./(1.0*velo2) pace[np.isinf(pace)] = 0.0 try: - strokedata = pd.DataFrame({'t':10*t, - 'd':10*d, - 'p':10*pace, - 'spm':spm, - 'hr':hr, - 'lat':lat, - 'lon':lon, - 'power':power, - 'strokelength':strokelength, - }) - except ValueError: # pragma: no cover + strokedata = pd.DataFrame({'t': 10*t, + 'd': 10*d, + 'p': 10*pace, + 'spm': spm, + 'hr': hr, + 'lat': lat, + 'lon': lon, + 'power': power, + 'strokelength': strokelength, + }) + except ValueError: # pragma: no cover return 0 try: workouttype = mytypes.stravamappinginv[workoutsummary['type']] - except KeyError: # pragma: no cover + except KeyError: # pragma: no cover workouttype = 'other' - if workouttype.lower() == 'rowing': # pragma: no cover + if workouttype.lower() == 'rowing': # pragma: no cover workouttype = 'rower' - try: - if 'summary_polyline' in workoutsummary['map'] and workouttype=='rower': # pragma: no cover + if 'summary_polyline' in workoutsummary['map'] and workouttype == 'rower': # pragma: no cover workouttype = 'water' - except KeyError: # pragma: no cover + except KeyError: # pragma: no cover pass try: @@ -3667,17 +3665,16 @@ def fetch_strava_workout(stravatoken,oauth_data,stravaid,csvfilename,userid,debu try: thetimezone = workoutsummary['timezone'] - except: # pragma: no cover + except: # pragma: no cover thetimezone = 'UTC' try: rowdatetime = iso8601.parse_date(workoutsummary['date_utc']) except KeyError: rowdatetime = iso8601.parse_date(workoutsummary['start_date']) - except ParseError: # pragma: no cover + except ParseError: # pragma: no cover rowdatetime = iso8601.parse_date(workoutsummary['date']) - try: intervaltype = workoutsummary['workout_type'] @@ -3686,7 +3683,7 @@ def fetch_strava_workout(stravatoken,oauth_data,stravaid,csvfilename,userid,debu try: title = workoutsummary['name'] - except KeyError: # pragma: no cover + except KeyError: # pragma: no cover title = "" try: t = data['comments'].split('\n', 1)[0] @@ -3701,109 +3698,104 @@ def fetch_strava_workout(stravatoken,oauth_data,stravaid,csvfilename,userid,debu lapidx = res[1] unixtime = cum_time+starttimeunix - seconds = 0.1*strokedata.loc[:,'t'] + seconds = 0.1*strokedata.loc[:, 't'] nr_rows = len(unixtime) try: - latcoord = strokedata.loc[:,'lat'] - loncoord = strokedata.loc[:,'lon'] - if latcoord.std() == 0 and loncoord.std() == 0 and workouttype == 'water': # pragma: no cover + latcoord = strokedata.loc[:, 'lat'] + loncoord = strokedata.loc[:, 'lon'] + if latcoord.std() == 0 and loncoord.std() == 0 and workouttype == 'water': # pragma: no cover workouttype = 'rower' - except: # pragma: no cover + except: # pragma: no cover latcoord = np.zeros(nr_rows) loncoord = np.zeros(nr_rows) if workouttype == 'water': workouttype = 'rower' - - try: - strokelength = strokedata.loc[:,'strokelength'] - except: # pragma: no cover + strokelength = strokedata.loc[:, 'strokelength'] + except: # pragma: no cover strokelength = np.zeros(nr_rows) - dist2 = 0.1*strokedata.loc[:,'d'] + dist2 = 0.1*strokedata.loc[:, 'd'] try: - spm = strokedata.loc[:,'spm'] - except KeyError: # pragma: no cover + spm = strokedata.loc[:, 'spm'] + except KeyError: # pragma: no cover spm = 0*dist2 try: - hr = strokedata.loc[:,'hr'] - except KeyError: # pragma: no cover + hr = strokedata.loc[:, 'hr'] + except KeyError: # pragma: no cover hr = 0*spm - pace = strokedata.loc[:,'p']/10. - pace = np.clip(pace,0,1e4) - pace = pace.replace(0,300) + pace = strokedata.loc[:, 'p']/10. + pace = np.clip(pace, 0, 1e4) + pace = pace.replace(0, 300) velo = 500./pace try: - power = strokedata.loc[:,'power'] - except KeyError: # pragma: no cover + power = strokedata.loc[:, 'power'] + except KeyError: # pragma: no cover power = 2.8*velo**3 - #if power.std() == 0 and power.mean() == 0: + # if power.std() == 0 and power.mean() == 0: # power = 2.8*velo**3 # save csv # Create data frame with all necessary data to write to csv - df = pd.DataFrame({'TimeStamp (sec)':unixtime, + df = pd.DataFrame({'TimeStamp (sec)': unixtime, ' Horizontal (meters)': dist2, - ' Cadence (stokes/min)':spm, - ' HRCur (bpm)':hr, - ' longitude':loncoord, - ' latitude':latcoord, - ' Stroke500mPace (sec/500m)':pace, - ' Power (watts)':power, - ' DragFactor':np.zeros(nr_rows), - ' DriveLength (meters)':np.zeros(nr_rows), - ' StrokeDistance (meters)':strokelength, - ' DriveTime (ms)':np.zeros(nr_rows), - ' StrokeRecoveryTime (ms)':np.zeros(nr_rows), - ' AverageDriveForce (lbs)':np.zeros(nr_rows), - ' PeakDriveForce (lbs)':np.zeros(nr_rows), - ' lapIdx':lapidx, - ' ElapsedTime (sec)':seconds, - 'cum_dist':dist2, + ' Cadence (stokes/min)': spm, + ' HRCur (bpm)': hr, + ' longitude': loncoord, + ' latitude': latcoord, + ' Stroke500mPace (sec/500m)': pace, + ' Power (watts)': power, + ' DragFactor': np.zeros(nr_rows), + ' DriveLength (meters)': np.zeros(nr_rows), + ' StrokeDistance (meters)': strokelength, + ' DriveTime (ms)': np.zeros(nr_rows), + ' StrokeRecoveryTime (ms)': np.zeros(nr_rows), + ' AverageDriveForce (lbs)': np.zeros(nr_rows), + ' PeakDriveForce (lbs)': np.zeros(nr_rows), + ' lapIdx': lapidx, + ' ElapsedTime (sec)': seconds, + 'cum_dist': dist2, }) - - - df.sort_values(by='TimeStamp (sec)',ascending=True) + df.sort_values(by='TimeStamp (sec)', ascending=True) row = rowingdata.rowingdata(df=df) - row.write_csv(csvfilename,gzip=False) + row.write_csv(csvfilename, gzip=False) summary = row.allstats() maxdist = df['cum_dist'].max() duration = row.duration uploadoptions = { - 'secret':UPLOAD_SERVICE_SECRET, - 'user':userid, + 'secret': UPLOAD_SERVICE_SECRET, + 'user': userid, 'file': csvfilename, 'title': title, - 'workouttype':workouttype, - 'boattype':'1x', - 'stravaid':stravaid, + 'workouttype': workouttype, + 'boattype': '1x', + 'stravaid': stravaid, } session = requests.session() newHeaders = {'Content-type': 'application/json', 'Accept': 'text/plain'} session.headers.update(newHeaders) - response = session.post(UPLOAD_SERVICE_URL,json=uploadoptions) + response = session.post(UPLOAD_SERVICE_URL, json=uploadoptions) t = time.localtime() timestamp = time.strftime('%b-%d-%Y_%H%M', t) - with open('strava_webhooks.log','a') as f: + with open('strava_webhooks.log', 'a') as f: f.write('\n') f.write(timestamp) f.write(' ') f.write('fetch_strava_workout posted file with strava id {stravaid} user id {userid}\n'.format( - stravaid=stravaid,userid=userid)) - + stravaid=stravaid, userid=userid)) return 1 diff --git a/rowers/tasks_standalone.py b/rowers/tasks_standalone.py index d2b2375c..d932a91c 100644 --- a/rowers/tasks_standalone.py +++ b/rowers/tasks_standalone.py @@ -1,4 +1,9 @@ from __future__ import absolute_import +from django.contrib.auth.models import User +from rowers.models import Workout +from django_rq import job +from celery import app +import time from __future__ import division from __future__ import print_function from __future__ import unicode_literals @@ -11,21 +16,15 @@ sys.path.append(proj_path) os.environ.setdefault("DJANGO_SETTINGS_MODULE", "rowsandall_app.settings") django.setup() -import time -from celery import app -from django_rq import job -from rowers.models import Workout -from django.contrib.auth.models import User - @app.task -def addcomment2(userid,id,debug=False): - +def addcomment2(userid, id, debug=False): + time.sleep(5) # w = Workout.objects.get(id=id) # w.notes += '\n the task has run' # w.save() u = User.objects.get(id=userid) - messages.info(u,' The task has run') - messages.error(u,' Het is veel te laat') + messages.info(u, ' The task has run') + messages.error(u, ' Het is veel te laat') return 1 diff --git a/rowers/teams.py b/rowers/teams.py index 5b4a23bb..262dd447 100644 --- a/rowers/teams.py +++ b/rowers/teams.py @@ -1,4 +1,23 @@ from __future__ import absolute_import +from rowers.rower_rules import is_team_manager, is_team_member, is_coach, user_is_basic +from rowers.models import ValidationError, PlannedSession +from rowers.tasks import ( + handle_sendemail_invite, + handle_sendemail_request, + handle_sendemail_member_dropped, handle_sendemail_request_accept, + handle_sendemail_request_reject, handle_sendemail_invite_reject, + handle_sendemail_invite_accept, handle_sendemail_team_removed, + handle_sendemail_coachrequest, handle_sendemail_coacheerequest, + handle_sendemail_coachoffer_rejected, + handle_sendemail_coachoffer_accepted, + handle_sendemail_coachrequest_rejected, + handle_sendemail_coachrequest_accepted, + handle_sendemail_message, +) +from rowers.models import ( + Rower, Workout, Team, TeamInvite, User, TeamRequest, CoachRequest, CoachOffer, + CoachingGroup +) from __future__ import division from __future__ import print_function from __future__ import unicode_literals @@ -22,50 +41,31 @@ queue = django_rq.get_queue('default') queuelow = django_rq.get_queue('low') queuehigh = django_rq.get_queue('low') -from rowers.models import ( - Rower, Workout, Team, TeamInvite,User,TeamRequest, CoachRequest, CoachOffer, - CoachingGroup - ) - -from rowers.rower_rules import is_team_manager,is_team_member,is_coach, user_is_basic - -from rowers.tasks import ( - handle_sendemail_invite, - handle_sendemail_request, - handle_sendemail_member_dropped,handle_sendemail_request_accept, - handle_sendemail_request_reject,handle_sendemail_invite_reject, - handle_sendemail_invite_accept,handle_sendemail_team_removed, - handle_sendemail_coachrequest,handle_sendemail_coacheerequest, - handle_sendemail_coachoffer_rejected, - handle_sendemail_coachoffer_accepted, - handle_sendemail_coachrequest_rejected, - handle_sendemail_coachrequest_accepted, - handle_sendemail_message, - ) - -from rowers.models import ValidationError, PlannedSession # Low level functions - to be called by higher level methods -inviteduration = 14 # days +inviteduration = 14 # days -def handle_remove_workouts_team(ws,t): + +def handle_remove_workouts_team(ws, t): for w in ws: w.team.remove(t) return 1 -def handle_add_workouts_team(ws,t): + +def handle_add_workouts_team(ws, t): for w in ws: w.team.add(t) return 1 -def update_team(t,name,manager,private,notes,viewing): - if not is_team_manager(manager,t): - return (0,'You are not the manager of this team') +def update_team(t, name, manager, private, notes, viewing): + + if not is_team_manager(manager, t): + return (0, 'You are not the manager of this team') try: t.name = name t.manager = manager @@ -74,93 +74,100 @@ def update_team(t,name,manager,private,notes,viewing): t.viewing = viewing t.save() except IntegrityError: - return (0,'Team name duplication') - return (1,'Team Updated') + return (0, 'Team name duplication') + return (1, 'Team Updated') -def create_team(name,manager,private='open',notes='',viewing='allmembers'): + +def create_team(name, manager, private='open', notes='', viewing='allmembers'): # needs some error testing - if user_is_basic(manager.rower.user): # pragma: no cover - return (0,'You need to upgrade to a paid plan to establish a team') + if user_is_basic(manager.rower.user): # pragma: no cover + return (0, 'You need to upgrade to a paid plan to establish a team') if not is_coach(manager): ts = Team.objects.filter(manager=manager) - if len(ts)>=1: # pragma: no cover - return (0,'You need to upgrade to the Coach plan to have more than one team') + if len(ts) >= 1: # pragma: no cover + return (0, 'You need to upgrade to the Coach plan to have more than one team') try: - t = Team(name=name,manager=manager,notes=notes, - private=private,viewing=viewing) + t = Team(name=name, manager=manager, notes=notes, + private=private, viewing=viewing) t.save() r = Rower.objects.get(user=manager) - res = add_member(t.id,r) + res = add_member(t.id, r) except IntegrityError: - return (0,'Team name duplication') - return (t.id,'Team created') + return (0, 'Team name duplication') + return (t.id, 'Team created') + def remove_team(id): t = Team.objects.get(id=id) for r in Rower.objects.filter(team=t): - send_team_delete_mail(t,r) + send_team_delete_mail(t, r) return t.delete() - return (1,'Updated rower team expiry') # pragma: no cover + return (1, 'Updated rower team expiry') # pragma: no cover -def add_coach(coach,rower): + +def add_coach(coach, rower): # get coaching group coachgroup = coach.mycoachgroup - if coachgroup is None: # pragma: no cover + if coachgroup is None: # pragma: no cover coachgroup = CoachingGroup(name=coach.user.first_name) coachgroup.save() coach.mycoachgroup = coachgroup coach.save() - if get_coach_club_size(coach)=coach.clubsize: # pragma: no cover - return(0,'You have reached the maximum number of athletes') + return (rekwest.id, 'The request was created') + elif get_coach_club_size(coach) >= coach.clubsize: # pragma: no cover + return(0, 'You have reached the maximum number of athletes') - else: # pragma: no cover - return (0,'You are not a coach') + else: # pragma: no cover + return (0, 'You are not a coach') - - -def create_invite(team,manager,user=None,email=''): +def create_invite(team, manager, user=None, email=''): r = Rower.objects.get(user=manager) - if not is_team_manager(manager,team): - return (0,'Not the team manager') + if not is_team_manager(manager, team): + return (0, 'Not the team manager') if user: try: r2 = Rower.objects.get(user=user) email = r2.user.email - except Rower.DoesNotExist: # pragma: no cover - return (0,'Rower does not exist') + except Rower.DoesNotExist: # pragma: no cover + return (0, 'Rower does not exist') if r2 in Rower.objects.filter(team=team): - return (0,'Already member of that team') - elif email==None or email=='': # pragma: no cover - return (0,'Invalid request - missing email or user') + return (0, 'Already member of that team') + elif email == None or email == '': # pragma: no cover + return (0, 'Invalid request - missing email or user') else: try: r2 = Rower.objects.get(user__email=email) user = User.objects.get(rower=r2) - except Rower.DoesNotExist: # pragma: no cover - user=None - except Rower.MultipleObjectsReturned: # pragma: no cover - return (0,'There is more than one user with that email address') + except Rower.DoesNotExist: # pragma: no cover + user = None + except Rower.MultipleObjectsReturned: # pragma: no cover + return (0, 'There is more than one user with that email address') # if count_club_members(team.manager)+count_invites(team.manager) <= r.clubsize: codes = [i.code for i in TeamInvite.objects.all()] code = uuid.uuid4().hex[:10].upper() # prevent duplicates - while code in codes: # pragma: no cover + while code in codes: # pragma: no cover code = uuid.uuid4().hex[:10].upper() - invite = TeamInvite(team=team,code=code,user=user,email=email) + invite = TeamInvite(team=team, code=code, user=user, email=email) invite.save() - return (invite.id,'Invitation created') + return (invite.id, 'Invitation created') + + return (0, 'Nothing done') # pragma: no cover - return (0,'Nothing done') # pragma: no cover - -def revoke_request(user,id): +def revoke_request(user, id): try: rekwest = TeamRequest.objects.get(id=id) - except TeamRequest.DoesNotExist: # pragma: no cover - return (0,'The request is invalid') + except TeamRequest.DoesNotExist: # pragma: no cover + return (0, 'The request is invalid') t = rekwest.team - if rekwest.user==user: + if rekwest.user == user: rekwest.delete() - return (1,'Request revoked') - else: # pragma: no cover - return (0,'You are not the requestor') + return (1, 'Request revoked') + else: # pragma: no cover + return (0, 'You are not the requestor') -def reject_revoke_coach_offer(user,id): + +def reject_revoke_coach_offer(user, id): try: rekwest = CoachOffer.objects.get(id=id) - except CoachOffer.DoesNotExist: # pragma: no cover - return (0,'The request is invalid') + except CoachOffer.DoesNotExist: # pragma: no cover + return (0, 'The request is invalid') if rekwest.coach.user == user: rekwest.delete() - return (1,'Request removed') + return (1, 'Request removed') elif rekwest.user == user: send_coachoffer_rejected_email(rekwest) rekwest.delete() - return (1,'Request removed') - else: # pragma: no cover - return (0,'Not permitted') + return (1, 'Request removed') + else: # pragma: no cover + return (0, 'Not permitted') -def reject_revoke_coach_request(user,id): + +def reject_revoke_coach_request(user, id): try: rekwest = CoachRequest.objects.get(id=id) - except CoachRequest.DoesNotExist: # pragma: no cover - return (0,'The request is invalid') + except CoachRequest.DoesNotExist: # pragma: no cover + return (0, 'The request is invalid') if rekwest.coach.user == user: send_coachrequest_rejected_email(rekwest) rekwest.delete() - return (1,'Request rejected') + return (1, 'Request rejected') elif rekwest.user == user: rekwest.delete() - return (1,'Request rejected') - else: # pragma: no cover - return (0,'Not permitted') + return (1, 'Request rejected') + else: # pragma: no cover + return (0, 'Not permitted') -def revoke_invite(manager,id): + +def revoke_invite(manager, id): try: invite = TeamInvite.objects.get(id=id) - except TeamInvite.DoesNotExist: # pragma: no cover - return (0,'The invitation is invalid') + except TeamInvite.DoesNotExist: # pragma: no cover + return (0, 'The invitation is invalid') - if is_team_manager(manager,invite.team): + if is_team_manager(manager, invite.team): invite.delete() - return (1,'Invitation revoked') - else: # pragma: no cover - return (0,'You are not the team manager') + return (1, 'Invitation revoked') + else: # pragma: no cover + return (0, 'You are not the team manager') -def reject_request(manager,id): + +def reject_request(manager, id): try: rekwest = TeamRequest.objects.get(id=id) - except TeamRequest.DoesNotExist: # pragma: no cover - return (0,'The request is invalid') + except TeamRequest.DoesNotExist: # pragma: no cover + return (0, 'The request is invalid') - if is_team_manager(manager,rekwest.team): + if is_team_manager(manager, rekwest.team): send_request_reject_email(rekwest) rekwest.delete() - return (1,'Request rejected') - else: # pragma: no cover - return (0,'You are not the manager for this request') + return (1, 'Request rejected') + else: # pragma: no cover + return (0, 'You are not the manager for this request') -def reject_invitation(user,id): +def reject_invitation(user, id): try: invite = TeamInvite.objects.get(id=id) - except TeamInvite.DoesNotExist: # pragma: no cover - return (0,'The invitation is invalid') + except TeamInvite.DoesNotExist: # pragma: no cover + return (0, 'The invitation is invalid') - if invite.user==user: + if invite.user == user: send_invite_reject_email(invite) invite.delete() - return (1,'Invitation rejected') - else: # pragma: no cover - return (0,'This request was not for you') + return (1, 'Invitation rejected') + else: # pragma: no cover + return (0, 'This request was not for you') def send_invite_email(id): try: invitation = TeamInvite.objects.get(id=id) - except TeamInvite.DoesNotExist: # pragma: no cover - return (0,'Invitation doesn not exist') + except TeamInvite.DoesNotExist: # pragma: no cover + return (0, 'Invitation doesn not exist') if invitation.user: email = invitation.user.email name = invitation.user.first_name + " " + invitation.user.last_name - else: # pragma: no cover + else: # pragma: no cover email = invitation.email name = '' code = invitation.code teamname = invitation.team.name - manager = invitation.team.manager.first_name+' '+invitation.team.manager.last_name + manager = invitation.team.manager.first_name + \ + ' '+invitation.team.manager.last_name res = myqueue(queuehigh, handle_sendemail_invite, - email,name,code,teamname,manager) + email, name, code, teamname, manager) - return (1,'Invitation email sent') + return (1, 'Invitation email sent') -def send_team_delete_mail(t,rower): + +def send_team_delete_mail(t, rower): u = rower.user teamname = t.name email = u.email @@ -492,11 +512,12 @@ def send_team_delete_mail(t,rower): manager = t.manager.first_name+' '+t.manager.last_name res = myqueue(queuehigh, handle_sendemail_team_removed, - email,name,teamname,manager) + email, name, teamname, manager) - return (1,'Team delete email sent') + return (1, 'Team delete email sent') -def send_email_member_dropped(teamid,rower): + +def send_email_member_dropped(teamid, rower): t = Team.objects.get(id=teamid) u = rower.user teamname = t.name @@ -504,13 +525,13 @@ def send_email_member_dropped(teamid,rower): name = u.first_name+' '+u.last_name manager = t.manager.first_name+' '+t.manager.last_name - res = myqueue(queuehigh,handle_sendemail_member_dropped, - email,name,teamname,manager) + res = myqueue(queuehigh, handle_sendemail_member_dropped, + email, name, teamname, manager) - return (1,'Member dropped email sent') + return (1, 'Member dropped email sent') -def send_request_accept_email(rekwest): # pragma: no cover +def send_request_accept_email(rekwest): # pragma: no cover id = rekwest.id email = rekwest.user.email teamname = rekwest.team.name @@ -518,9 +539,9 @@ def send_request_accept_email(rekwest): # pragma: no cover manager = rekwest.team.manager.first_name+' '+rekwest.team.manager.last_name res = myqueue(queuehigh, handle_sendemail_request_accept, - email,name,teamname,manager) + email, name, teamname, manager) - return (1,'Invitation email sent') + return (1, 'Invitation email sent') def send_request_reject_email(rekwest): @@ -532,9 +553,9 @@ def send_request_reject_email(rekwest): res = myqueue(queuehigh, handle_sendemail_request_reject, - email,name,teamname,manager) + email, name, teamname, manager) - return (1,'Invitation email sent') + return (1, 'Invitation email sent') def send_invite_reject_email(invitation): @@ -542,20 +563,21 @@ def send_invite_reject_email(invitation): email = invitation.team.manager.email if invitation.user: name = invitation.user.first_name+' '+invitation.user.last_name - else: # pragma: no cover + else: # pragma: no cover name = invitation.email teamname = invitation.team.name - manager = invitation.team.manager.first_name+' '+invitation.team.manager.last_name + manager = invitation.team.manager.first_name + \ + ' '+invitation.team.manager.last_name res = myqueue(queuehigh, handle_sendemail_invite_reject, - email,name,teamname,manager) + email, name, teamname, manager) - return (1,'Invitation email sent') + return (1, 'Invitation email sent') -def send_invite_accept_email(invitation): # pragma: no cover +def send_invite_accept_email(invitation): # pragma: no cover id = invitation.id email = invitation.team.manager.email if invitation.user: @@ -564,15 +586,17 @@ def send_invite_accept_email(invitation): # pragma: no cover name = invitation.email teamname = invitation.team.name - manager = invitation.team.manager.first_name+' '+invitation.team.manager.last_name + manager = invitation.team.manager.first_name + \ + ' '+invitation.team.manager.last_name res = myqueue(queuehigh, handle_sendemail_invite_accept, - email,name,teamname,manager) + email, name, teamname, manager) - return (1,'Invitation email sent') + return (1, 'Invitation email sent') -def send_team_message(team,message): # pragma: no cover + +def send_team_message(team, message): # pragma: no cover rowers = team.rower.all() managername = team.manager.first_name + " " + team.manager.last_name @@ -580,9 +604,10 @@ def send_team_message(team,message): # pragma: no cover rowername = rower.user.first_name + " " + rower.user.last_name res = myqueue(queuehigh, handle_sendemail_message, - rower.user.email,team.manager.email,rowername,message,team.name,managername) + rower.user.email, team.manager.email, rowername, message, team.name, managername) + + return (1, 'message sent') - return (1,'message sent') def send_request_email(rekwest): email = rekwest.team.manager.email @@ -594,82 +619,83 @@ def send_request_email(rekwest): res = myqueue(queuehigh, handle_sendemail_request, - email,name,code,teamname,requestor,id) + email, name, code, teamname, requestor, id) - return (1,'Invitation email sent') + return (1, 'Invitation email sent') -def process_request_code(manager,code): # pragma: no cover + +def process_request_code(manager, code): # pragma: no cover code = code.upper() try: rekwest = TeamRequest.objects.get(code=code) except TeamRequest.DoesNotExist: - return (0,'The request has been revoked or the code is invalid') + return (0, 'The request has been revoked or the code is invalid') r = Rower.objects.get(user=rekwest.user) t = rekwest.team if rekwest.team.manager != manager: - return (0,'You are not the manager of this team') + return (0, 'You are not the manager of this team') - result,comment = add_member(t.id,r) + result, comment = add_member(t.id, r) if not result: - return (result,"The member couldn't be added") - + return (result, "The member couldn't be added") send_request_accept_email(rekwest) - rekwest.delete() - return (result,'The member was added') + return (result, 'The member was added') -def process_invite_code(user,code): # pragma: no cover + +def process_invite_code(user, code): # pragma: no cover code = code.upper() try: invitation = TeamInvite.objects.get(code=code) except TeamInvite.DoesNotExist: - return (0,'The invitation has expired or the code is invalid') + return (0, 'The invitation has expired or the code is invalid') r = Rower.objects.get(user=user) nu = datetime.date(timezone.now()) if nu > invitation.issuedate+timedelta(days=inviteduration): - revoke_invite(invitation.team.manager,invitation.id) - return (0,'The invitation has expired') + revoke_invite(invitation.team.manager, invitation.id) + return (0, 'The invitation has expired') t = invitation.team - result, comment = add_member(t.id,r) + result, comment = add_member(t.id, r) if not result: - return (result,"The member couldn't be added") - + return (result, "The member couldn't be added") send_invite_accept_email(invitation) invitation.delete() - return (result,'You were added to the team') + return (result, 'You were added to the team') -def remove_expired_invites(): # pragma: no cover + +def remove_expired_invites(): # pragma: no cover issuedate = timezone.now()-timedelta(days=inviteduration) issuedate = datetime.date(issuedate) invitations = TeamInvite.objects.filter(issuedate__lt=issuedate) for i in invitations: - revoke_invite(i.team.manager,i.id) + revoke_invite(i.team.manager, i.id) - return (1,'Expired invitations deleted') + return (1, 'Expired invitations deleted') -def process_coachrequest_code(coach,code): + +def process_coachrequest_code(coach, code): code = code.upper() try: rekwest = CoachRequest.objects.get(code=code) - except CoachRequest.DoesNotExist: # pragma: no cover - return (0,'The request has been revoked or is invalid') + except CoachRequest.DoesNotExist: # pragma: no cover + return (0, 'The request has been revoked or is invalid') - if rekwest.coach != coach: # pragma: no cover - return (0,'The request is invalid') + if rekwest.coach != coach: # pragma: no cover + return (0, 'The request is invalid') - result = add_coach(coach,rekwest.user.rower) - if not result: # pragma: no cover + result = add_coach(coach, rekwest.user.rower) + if not result: # pragma: no cover return result else: send_coachrequest_accepted_email(rekwest) @@ -678,19 +704,20 @@ def process_coachrequest_code(coach,code): return result -def process_coachoffer_code(user,code): + +def process_coachoffer_code(user, code): code = code.upper() try: rekwest = CoachOffer.objects.get(code=code) - except CoachOffer.DoesNotExist: # pragma: no cover - return (0,'The request has been revoked or is invalid') + except CoachOffer.DoesNotExist: # pragma: no cover + return (0, 'The request has been revoked or is invalid') - if rekwest.user != user: # pragma: no cover - return (0,'The request is invalid') + if rekwest.user != user: # pragma: no cover + return (0, 'The request is invalid') - result = add_coach(rekwest.coach,rekwest.user.rower) - if not result: # pragma: no cover + result = add_coach(rekwest.coach, rekwest.user.rower) + if not result: # pragma: no cover return result else: send_coachoffer_accepted_email(rekwest) @@ -699,6 +726,7 @@ def process_coachoffer_code(user,code): return result + def send_coachoffer_rejected_email(rekwest): coachname = rekwest.coach.user.first_name + " " + rekwest.coach.user.last_name coachemail = rekwest.coach.user.email @@ -707,7 +735,8 @@ def send_coachoffer_rejected_email(rekwest): res = myqueue(queuehigh, handle_sendemail_coachoffer_rejected, - coachemail,coachname,name) + coachemail, coachname, name) + def send_coachrequest_rejected_email(rekwest): coachname = rekwest.coach.user.first_name + " " + rekwest.coach.user.last_name @@ -717,7 +746,8 @@ def send_coachrequest_rejected_email(rekwest): res = myqueue(queuehigh, handle_sendemail_coachrequest_rejected, - email,coachname,name) + email, coachname, name) + def send_coachrequest_accepted_email(rekwest): coachname = rekwest.coach.user.first_name + " " + rekwest.coach.user.last_name @@ -727,7 +757,7 @@ def send_coachrequest_accepted_email(rekwest): res = myqueue(queuehigh, handle_sendemail_coachrequest_accepted, - email,coachname,name) + email, coachname, name) def send_coachoffer_accepted_email(rekwest): @@ -738,4 +768,4 @@ def send_coachoffer_accepted_email(rekwest): res = myqueue(queuehigh, handle_sendemail_coachoffer_accepted, - coachemail,coachname,name) + coachemail, coachname, name) diff --git a/rowers/tokens.py b/rowers/tokens.py index 457e6cf5..4db5af2e 100644 --- a/rowers/tokens.py +++ b/rowers/tokens.py @@ -1,6 +1,7 @@ from django.contrib.auth.tokens import PasswordResetTokenGenerator import six + class AccountActivationTokenGenerator(PasswordResetTokenGenerator): def _make_hash_value(self, user, timestamp): return ( @@ -8,4 +9,5 @@ class AccountActivationTokenGenerator(PasswordResetTokenGenerator): six.text_type(user.is_active) ) + account_activation_token = AccountActivationTokenGenerator() diff --git a/rowers/tpstuff.py b/rowers/tpstuff.py index c152e6a1..46013337 100644 --- a/rowers/tpstuff.py +++ b/rowers/tpstuff.py @@ -1,4 +1,8 @@ from __future__ import absolute_import +from celery import Celery, app +from rowers.rower_rules import is_workout_user +import time +from django_rq import job from __future__ import division from __future__ import print_function from __future__ import unicode_literals @@ -17,14 +21,11 @@ from rowsandall_app.settings import ( C2_CLIENT_ID, C2_REDIRECT_URI, C2_CLIENT_SECRET, STRAVA_CLIENT_ID, STRAVA_REDIRECT_URI, STRAVA_CLIENT_SECRET, TP_CLIENT_ID, TP_CLIENT_SECRET, - TP_REDIRECT_URI,TP_CLIENT_KEY, - ) + TP_REDIRECT_URI, TP_CLIENT_KEY, +) tpapilocation = "https://api.trainingpeaks.com" -from celery import Celery,app -from django_rq import job -import time #from async_messages import message_user,messages oauth_data = { @@ -33,16 +34,14 @@ oauth_data = { 'redirect_uri': TP_REDIRECT_URI, 'autorization_uri': "https://oauth.trainingpeaks.com/oauth/authorize?", 'content_type': 'application/x-www-form-urlencoded', -# 'content_type': 'application/json', + # 'content_type': 'application/json', 'tokenname': 'tptoken', 'refreshtokenname': 'tprefreshtoken', 'expirydatename': 'tptokenexpirydate', 'bearer_auth': False, 'base_url': "https://oauth.trainingpeaks.com/oauth/token", - 'scope':'write', - } - -from rowers.rower_rules import is_workout_user + 'scope': 'write', +} # Checks if user has UnderArmour token, renews them if they are expired @@ -50,47 +49,52 @@ def tp_open(user): return imports_open(user, oauth_data) # Refresh ST token using refresh token + + def do_refresh_token(refreshtoken): return imports_do_refresh_token(refreshtoken, oauth_data) # Exchange access code for long-lived access token + + def get_token(code): client_auth = requests.auth.HTTPBasicAuth(TP_CLIENT_KEY, TP_CLIENT_SECRET) post_data = { - "client_id":TP_CLIENT_KEY, + "client_id": TP_CLIENT_KEY, "grant_type": "authorization_code", "code": code, - "redirect_uri":TP_REDIRECT_URI, - "client_secret": TP_CLIENT_SECRET, + "redirect_uri": TP_REDIRECT_URI, + "client_secret": TP_CLIENT_SECRET, } headers = { - 'Content-Type': 'application/x-www-form-urlencoded', + 'Content-Type': 'application/x-www-form-urlencoded', } response = requests.post( - "https://oauth.trainingpeaks.com/oauth/token", - data=post_data,verify=False, + "https://oauth.trainingpeaks.com/oauth/token", + data=post_data, verify=False, ) - try: token_json = response.json() thetoken = token_json['access_token'] expires_in = token_json['expires_in'] refresh_token = token_json['refresh_token'] - except KeyError: # pragma: no cover + except KeyError: # pragma: no cover thetoken = 0 expires_in = 0 refresh_token = 0 - return thetoken,expires_in,refresh_token + return thetoken, expires_in, refresh_token # Make authorization URL including random string -def make_authorization_url(request): # pragma: no cover + + +def make_authorization_url(request): # pragma: no cover return imports_make_authorization_url(oauth_data) -def getidfromresponse(response): # pragma: no cover +def getidfromresponse(response): # pragma: no cover t = json.loads(response.text) links = t["_links"] @@ -99,6 +103,7 @@ def getidfromresponse(response): # pragma: no cover return int(id) + def createtpworkoutdata(w): filename = w.csvfilename row = rowingdata(csvfile=filename) @@ -108,64 +113,62 @@ def createtpworkoutdata(w): except TypeError: newnotes = 'from '+w.workoutsource+' via rowsandall.com' - row.exporttotcx(tcxfilename,notes=newnotes) + row.exporttotcx(tcxfilename, notes=newnotes) return tcxfilename -def tp_check(access_token): # pragma: no cover +def tp_check(access_token): # pragma: no cover headers = { "Content-Type": "application/json", - 'Accept': 'application/json', + 'Accept': 'application/json', 'authorization': 'Bearer %s' % access_token } resp = requests.post(tpapilocation+"/v1/info/version", - headers=headers,verify=False) + headers=headers, verify=False) return resp -def uploadactivity(access_token,filename,description='', + +def uploadactivity(access_token, filename, description='', name='Rowsandall.com workout'): data_gz = BytesIO() - with open(filename,'rb') as inF: + with open(filename, 'rb') as inF: s = inF.read() - with gzip.GzipFile(fileobj=data_gz,mode="w") as gzf: + with gzip.GzipFile(fileobj=data_gz, mode="w") as gzf: gzf.write(s) - headers = { 'Content-Type': 'application/json', - 'Accept': 'application/json', + 'Accept': 'application/json', 'Authorization': 'Bearer %s' % access_token } - - data = { "UploadClient": "rowsandall", "Filename": filename, "SetWorkoutPublic": True, - "Title":name, + "Title": name, "Type": "rowing", "Comment": description, "Data": base64.b64encode(data_gz.getvalue()).decode("ascii") - } + } resp = requests.post(tpapilocation+"/v1/file", - data = json.dumps(data), - headers=headers,verify=False) + data=json.dumps(data), + headers=headers, verify=False) - if resp.status_code != 200: # pragma: no cover - return 0,resp.reason,resp.status_code,headers + if resp.status_code != 200: # pragma: no cover + return 0, resp.reason, resp.status_code, headers else: - return resp.json()[0]["Id"],"ok",200,"" + return resp.json()[0]["Id"], "ok", 200, "" - return 0,0,0,0 # pragma: no cover + return 0, 0, 0, 0 # pragma: no cover -def workout_tp_upload(user,w): # pragma: no cover +def workout_tp_upload(user, w): # pragma: no cover message = "Uploading to TrainingPeaks" tpid = 0 r = w.user @@ -174,40 +177,40 @@ def workout_tp_upload(user,w): # pragma: no cover # need some code if token doesn't refresh - - if (is_workout_user(user,w)): + if (is_workout_user(user, w)): tcxfile = createtpworkoutdata(w) if tcxfile: - res,reason,status_code,headers = uploadactivity( - thetoken,tcxfile, + res, reason, status_code, headers = uploadactivity( + thetoken, tcxfile, name=w.name ) if res == 0: - message = "Upload to TrainingPeaks failed with status code "+str(status_code)+": "+reason + message = "Upload to TrainingPeaks failed with status code " + \ + str(status_code)+": "+reason w.tpid = -1 try: os.remove(tcxfile) except WindowsError: pass - return message,tpid + return message, tpid - else: # res != 0 + else: # res != 0 w.uploadedtotp = res tpid = res w.save() os.remove(tcxfile) - return 'Successfully synchronized to TrainingPeaks',tpid + return 'Successfully synchronized to TrainingPeaks', tpid - else: # no tcxfile + else: # no tcxfile message = "Upload to TrainingPeaks failed" w.uploadedtotp = -1 tpid = -1 w.save() - return message,tpid - else: # not allowed to upload + return message, tpid + else: # not allowed to upload message = "You are not allowed to export this workout to TP" tpid = 0 - return message,tpid + return message, tpid - return message,tpid + return message, tpid diff --git a/rowers/traverselinktest.py b/rowers/traverselinktest.py index ebe09a34..ad0c6545 100644 --- a/rowers/traverselinktest.py +++ b/rowers/traverselinktest.py @@ -5,13 +5,13 @@ from __future__ import unicode_literals from __future__ import print_function from bs4 import BeautifulSoup import re -from django.test import TestCase, Client,override_settings +from django.test import TestCase, Client, override_settings from django.core.management import call_command from django.utils.six import StringIO from django.test.client import RequestFactory from .views import c2_open -from rowers.models import Workout, User, Rower, WorkoutForm,RowerForm,GraphImage -from rowers.forms import DocumentsForm,CNsummaryForm,RegistrationFormUniqueEmail +from rowers.models import Workout, User, Rower, WorkoutForm, RowerForm, GraphImage +from rowers.forms import DocumentsForm, CNsummaryForm, RegistrationFormUniqueEmail import rowers.plots as plots import rowers.interactiveplots as iplots import datetime @@ -20,10 +20,10 @@ from rowingdata import rower as rrower from django.utils import timezone from rowers.rows import handle_uploaded_file from django.core.files.uploadedfile import SimpleUploadedFile -from time import strftime,strptime,mktime,time,daylight +from time import strftime, strptime, mktime, time, daylight import os from rowers.tasks import handle_makeplot -from rowers.utils import serialize_list,deserialize_list +from rowers.utils import serialize_list, deserialize_list from shutil import copyfile @@ -34,7 +34,7 @@ import json import numpy as np from rowers import urls -from rowers.views import error500_view,error404_view,error400_view,error403_view +from rowers.views import error500_view, error404_view, error400_view, error403_view from dataprep import delete_strokedata @@ -43,24 +43,26 @@ redis_connection = StrictRedis() VERBOSE = True + class TraverseLinksTest(TestCase): def setUp(self): self.u = User.objects.create_superuser( 'superuser1', - 'superuser1@example.com','pwd') - self.r = Rower.objects.create(user=self.u,gdproptin=True,gdproptindate=timezone.now()) + 'superuser1@example.com', 'pwd') + self.r = Rower.objects.create( + user=self.u, gdproptin=True, gdproptindate=timezone.now()) nu = datetime.datetime.now() - self.w = Workout.objects.create( - name='testworkout',workouttype='On-water', - user=self.r,date=nu.strftime('%Y-%m-%d'), - starttime=nu.strftime('%H:%M:%S'), - duration="0:55:00",distance=8000) - self.w2 = Workout.objects.create( - name='testworkout 2',workouttype='On-water', - user=self.r,date=nu.strftime('%Y-%m-%d'), - starttime=nu.strftime('%H:%M:%S'), - duration="0:55:00",distance=8000) + self.w = Workout.objects.create( + name='testworkout', workouttype='On-water', + user=self.r, date=nu.strftime('%Y-%m-%d'), + starttime=nu.strftime('%H:%M:%S'), + duration="0:55:00", distance=8000) + self.w2 = Workout.objects.create( + name='testworkout 2', workouttype='On-water', + user=self.r, date=nu.strftime('%Y-%m-%d'), + starttime=nu.strftime('%H:%M:%S'), + duration="0:55:00", distance=8000) if self.client.login( username="superuser1", password="pwd"): if VERBOSE: @@ -73,16 +75,15 @@ class TraverseLinksTest(TestCase): # Initialise your database here as needed pass - def test_traverse_urls(self): # Fill these lists as needed with your site specific URLs to check and to avoid to_traverse_list = ['/rowers/list-workouts'] to_avoid_list = ['^/$', '^$', 'javascript:history\.back()', 'javascript:history\.go\(-1\)', '^mailto:.*', '.*github\.io.*', 'javascript:.*', - '.*biorow\.com.*','.*facebook.*', - '.*wordpress.*','.*analytics.*','.*freenet.*', - '.*twitter.*','^blog.*', + '.*biorow\.com.*', '.*facebook.*', + '.*wordpress.*', '.*analytics.*', '.*freenet.*', + '.*twitter.*', '^blog.*', '.*\d+-\d+-\d+.*', '.*flexchart/.*', '.*heroku.*', @@ -104,7 +105,7 @@ class TraverseLinksTest(TestCase): '.*performancephones.*', '.*sporttracks.*', '.*join-select.*', - ] + ] done_list = [] error_list = [] @@ -113,20 +114,26 @@ class TraverseLinksTest(TestCase): source_of_link[link] = 'initial' (to_traverse_list, to_avoid_list, done_list, error_list, source_of_link) = \ - self.recurse_into_path(to_traverse_list, to_avoid_list, done_list, error_list, source_of_link) + self.recurse_into_path( + to_traverse_list, to_avoid_list, done_list, error_list, source_of_link) print('END REACHED\nStats:') - if VERBOSE: print('\nto_traverse_list = ' + str(to_traverse_list)) - if VERBOSE: print('\nto_avoid_list = ' + str(to_avoid_list)) - if VERBOSE: print('\nsource_of_link = ' + str(source_of_link)) - if VERBOSE: print('\ndone_list = ' + str(done_list)) + if VERBOSE: + print('\nto_traverse_list = ' + str(to_traverse_list)) + if VERBOSE: + print('\nto_avoid_list = ' + str(to_avoid_list)) + if VERBOSE: + print('\nsource_of_link = ' + str(source_of_link)) + if VERBOSE: + print('\ndone_list = ' + str(done_list)) print('Followed ' + str(len(done_list)) + ' links successfully') print('Avoided ' + str(len(to_avoid_list)) + ' links') if error_list: print('!! ' + str(len(error_list)) + ' error(s) : ') for error in error_list: - print(str(error) + ' found in page ' + source_of_link[error[0]]) + print(str(error) + ' found in page ' + + source_of_link[error[0]]) print('Errors found traversing links') assert False @@ -142,7 +149,8 @@ class TraverseLinksTest(TestCase): url = to_traverse_list.pop() if not match_any(url, to_avoid_list): - print('Surfing to ' + str(url) + ', discovered in ' + str(source_of_link[url])) + print('Surfing to ' + str(url) + + ', discovered in ' + str(source_of_link[url])) response = self.client.get(url, follow=True) if response.status_code == 200: @@ -152,30 +160,39 @@ class TraverseLinksTest(TestCase): for link in soup.find_all('a'): new_link = link.get('href') - if VERBOSE: print(' Found link: ' + str(new_link)) + if VERBOSE: + print(' Found link: ' + str(new_link)) if match_any(new_link, to_avoid_list): - if VERBOSE: print(' Avoiding it') + if VERBOSE: + print(' Avoiding it') elif new_link in done_list: - if VERBOSE: print(' Already done, ignoring') + if VERBOSE: + print(' Already done, ignoring') elif new_link in to_traverse_list: - if VERBOSE: print(' Already in to traverse list, ignoring') + if VERBOSE: + print(' Already in to traverse list, ignoring') else: - if VERBOSE: print(' New, unknown link: Storing it to traverse later') + if VERBOSE: + print( + ' New, unknown link: Storing it to traverse later') source_of_link[new_link] = url to_traverse_list.append(new_link) done_list.append(url) - if VERBOSE: print('Done') + if VERBOSE: + print('Done') else: error_list.append((url, response.status_code)) to_avoid_list.append(url) - if VERBOSE: print('Diving into next level') + if VERBOSE: + print('Diving into next level') return self.recurse_into_path(to_traverse_list, to_avoid_list, done_list, error_list, source_of_link) else: # Nothing to traverse - if VERBOSE: print('Returning to upper level') + if VERBOSE: + print('Returning to upper level') return to_traverse_list, to_avoid_list, done_list, error_list, source_of_link diff --git a/rowers/uploads.py b/rowers/uploads.py index 447a5e48..60b2a89a 100644 --- a/rowers/uploads.py +++ b/rowers/uploads.py @@ -1,16 +1,22 @@ -from __future__ import absolute_import -from __future__ import division -from __future__ import print_function -from __future__ import unicode_literals +from rowers.mytypes import workouttypes, boattypes, otwtypes, workoutsources, workouttypes_ordered +import rowers.c2stuff as c2stuff +from rowers.rower_rules import is_promember +import rowers.tpstuff as tpstuff +import rowers.sporttracksstuff as sporttracksstuff +import rowers.stravastuff as stravastuff +from rowers.utils import ( + geo_distance, serialize_list, deserialize_list, uniqify, + str2bool, range_to_color_hex, absolute, myqueue, NoTokenError +) # for actions related to uploads from django.conf import settings -from django.utils import timezone,translation +from django.utils import timezone, translation from rowers.tasks import ( - handle_sendemail_unrecognized,handle_sendemailnewcomment, + handle_sendemail_unrecognized, handle_sendemailnewcomment, handle_sendemailnewresponse, handle_updatedps, - handle_makeplot,handle_otwsetpower,handle_sendemailtcx, + handle_makeplot, handle_otwsetpower, handle_sendemailtcx, handle_sendemailcsv - ) +) from rowers.models import GraphImage @@ -35,7 +41,6 @@ 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 @@ -43,18 +48,10 @@ except: from io import StringIO -from rowers.utils import ( - geo_distance,serialize_list,deserialize_list,uniqify, - str2bool,range_to_color_hex,absolute,myqueue,NoTokenError - ) +sources = [s for s, name in workoutsources] -sources = [s for s,name in workoutsources] - - - - -def make_plot(r,w,f1,f2,plottype,title,imagename='',plotnr=0): +def make_plot(r, w, f1, f2, plottype, title, imagename='', plotnr=0): if imagename == '': imagename = f1[:-4]+'.png' fullpathimagename = 'static/plots/'+imagename @@ -62,35 +59,35 @@ def make_plot(r,w,f1,f2,plottype,title,imagename='',plotnr=0): powerperc = 100*np.array([r.pw_ut2, r.pw_ut1, r.pw_at, - r.pw_tr,r.pw_an])/r.ftp + 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), + '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, + 'timeplot': 1, + 'distanceplot': 2, + 'pieplot': 3, + 'None': 0, } axis = r.staticgrids - if axis == None: # pragma: no cover + if axis == None: # pragma: no cover gridtrue = False axis = 'both' else: @@ -99,22 +96,21 @@ def make_plot(r,w,f1,f2,plottype,title,imagename='',plotnr=0): if plotnr == 0: plotnr = plotnrs[plottype] if plotnr == 0: - return 0,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()] + 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) + 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 + width, height = Image.open(fullpathimagename).size except: width = 1200 height = 600 @@ -124,25 +120,16 @@ def make_plot(r,w,f1,f2,plottype,title,imagename='',plotnr=0): i = GraphImage(workout=w, creationdatetime=timezone.now(), filename=fullpathimagename, - width=width,height=height) + 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' + 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.tpstuff as tpstuff - -from rowers.rower_rules import is_promember + return i.id, job.id - -def do_sync(w,options, quick=False): +def do_sync(w, options, quick=False): do_strava_export = w.user.strava_auto_export try: @@ -151,7 +138,7 @@ def do_sync(w,options, quick=False): upload_to_strava = False try: - if options['stravaid'] != 0 and options['stravaid'] != '': # pragma: no cover + if options['stravaid'] != 0 and options['stravaid'] != '': # pragma: no cover w.uploadedtostrava = options['stravaid'] upload_to_strava = False do_strava_export = False @@ -159,29 +146,27 @@ def do_sync(w,options, quick=False): except KeyError: pass - try: - if options['nkid'] != 0 and options['nkid'] != '': # pragma: no cover + 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 + 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 + 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 + if options['garminid'] != 0 and options['garminid'] != '': # pragma: no cover w.uploadedtogarmin = options['garminid'] w.save() except KeyError: @@ -194,7 +179,7 @@ def do_sync(w,options, quick=False): upload_to_c2 = False try: - if options['c2id'] != 0 and options['c2id'] != '': # pragma: no cover + if options['c2id'] != 0 and options['c2id'] != '': # pragma: no cover w.uploadedtoc2 = options['c2id'] upload_to_c2 = False do_c2_export = False @@ -203,7 +188,7 @@ def do_sync(w,options, quick=False): pass try: - if options['rp3id'] != 0 and options['rp3id'] != '': # pragma: no cover + if options['rp3id'] != 0 and options['rp3id'] != '': # pragma: no cover w.uploadedtorp3 = options['rp3id'] w.save() except KeyError: @@ -212,68 +197,66 @@ def do_sync(w,options, quick=False): if w.duplicate: return 0 - if do_c2_export: # pragma: no cover + if do_c2_export: # pragma: no cover try: - message,id = c2stuff.workout_c2_upload(w.user.user,w,asynchron=True) + 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 + except: # pragma: no cover pass - if do_strava_export: # pragma: no cover + if do_strava_export: # pragma: no cover try: - message,id = stravastuff.workout_strava_upload( - w.user.user,w,quick=quick,asynchron=True, + message, id = stravastuff.workout_strava_upload( + w.user.user, w, quick=quick, asynchron=True, ) dologging( 'strava_export_log.log', 'exporting workout {id} as {type}'.format( id=w.id, - type = w.workouttype, - ) + type=w.workouttype, ) - except NoTokenError: # pragma: no cover + ) + 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: + with open('stravalog.log', 'a') as f: f.write('\n') f.write(timestamp) f.write(str(e)) - do_tp_export = w.user.trainingpeaks_auto_export - try: # pragma: no cover + try: # pragma: no cover upload_to_tp = options['upload_to_SportTracks'] or do_tp_export do_tp_export = upload_to_tp except KeyError: upload_to_tp = False - if do_tp_export: # pragma: no cover + if do_tp_export: # pragma: no cover try: - message,id = sporttracksstuff.workout_sporttracks_upload( - w.user.user,w,asynchron=True, + message, id = sporttracksstuff.workout_sporttracks_upload( + w.user.user, w, asynchron=True, ) - with open('st_export.log','a') as logfile: # pragma: no cover + 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: + 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_TrainingPeaks' in options and options['upload_to_TrainingPeaks']) or (w.user.trainingpeaks_auto_export): # pragma: no cover + 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 + message, id = tpstuff.workout_tp_upload( + w.user.user, w ) except NoTokenError: message = "Please connect to TrainingPeaks first" diff --git a/rowers/urls.py b/rowers/urls.py index 84710116..57d3e9f5 100644 --- a/rowers/urls.py +++ b/rowers/urls.py @@ -1,7 +1,4 @@ -from __future__ import absolute_import -from __future__ import division -from __future__ import print_function -from __future__ import unicode_literals +from oauth2_provider.views import base from django.conf import settings from django.conf.urls import url, include from django.urls import path, re_path @@ -10,11 +7,11 @@ from django.contrib.auth.decorators import login_required, permission_required from django.views.decorators.csrf import csrf_exempt from django.utils.decorators import method_decorator from rowers.models import ( - Workout,Rower,FavoriteChart,VirtualRaceResult, - VirtualRace,StandardCollection,CourseStandard,GeoCourse,PlannedSession, - ) + Workout, Rower, FavoriteChart, VirtualRaceResult, + VirtualRace, StandardCollection, CourseStandard, GeoCourse, PlannedSession, +) -from rest_framework import routers, serializers, viewsets,permissions +from rest_framework import routers, serializers, viewsets, permissions from rest_framework.urlpatterns import format_suffix_patterns from rest_framework.permissions import * from rowers import views @@ -23,10 +20,10 @@ from django.views.generic.base import TemplateView from django.utils.decorators import method_decorator from rowers.permissions import ( - IsOwnerOrNot,IsOwnerOrReadOnly, - IsCompetitorOrNot,IsRowerOrNot, + IsOwnerOrNot, IsOwnerOrReadOnly, + IsCompetitorOrNot, IsRowerOrNot, IsPlanOrHigher, - ) +) from rowers.serializers import ( WorkoutSerializer, RowerSerializer, @@ -40,25 +37,26 @@ from rowers.serializers import ( GeoPolygonSerializer, GeoPointSerializer, PlannedSessionSerializer, - ) +) from oauth2_provider.views import ( AuthorizedTokensListView, AuthorizedTokenDeleteView, - ) +) from oauth2_provider.views.base import ( RevokeTokenView - ) +) + class PlannedSessionViewSet(viewsets.ModelViewSet): model = PlannedSession serializer_class = PlannedSessionSerializer - def get_queryset(self): # pragma: no cover + def get_queryset(self): # pragma: no cover try: r = Rower.objects.get(user=self.request.user) - if r.rowerplan not in ['basic','pro']: + if r.rowerplan not in ['basic', 'pro']: return PlannedSession.objects.filter(rower=r).order_by("-preferreddate") else: return [] @@ -70,31 +68,31 @@ class PlannedSessionViewSet(viewsets.ModelViewSet): IsPlanOrHigher, ) + class WorkoutViewSet(viewsets.ModelViewSet): model = Workout #queryset = Workout.objects.all().order_by("-date", "-starttime") serializer_class = WorkoutSerializer - def get_queryset(self): # pragma: no cover + def get_queryset(self): # pragma: no cover try: r = Rower.objects.get(user=self.request.user) - return Workout.objects.filter(user=r).order_by("-date","-starttime") + return Workout.objects.filter(user=r).order_by("-date", "-starttime") except TypeError: return [] - - permission_classes = ( - #DjangoModelPermissions, + # DjangoModelPermissions, IsOwnerOrNot, ) + class RowerViewSet(viewsets.ModelViewSet): model = Rower serializer_class = RowerSerializer #queryset = Rower.objects.all() - def get_queryset(self): # pragma: no cover + def get_queryset(self): # pragma: no cover try: r = Rower.objects.filter(user=self.request.user) return r @@ -103,9 +101,9 @@ class RowerViewSet(viewsets.ModelViewSet): permission_classes = ( IsOwnerOrNot, - ) + ) - http_method_names = ['get','patch','put'] + http_method_names = ['get', 'patch', 'put'] class FavoriteChartViewSet(viewsets.ModelViewSet): @@ -113,7 +111,7 @@ class FavoriteChartViewSet(viewsets.ModelViewSet): serializer_class = FavoriteChartSerializer #queryset = FavoriteChart.objects.all() - def get_queryset(self): # pragma: no cover + def get_queryset(self): # pragma: no cover try: r = Rower.objects.get(user=self.request.user) return FavoriteChart.objects.filter(user=r) @@ -122,31 +120,33 @@ class FavoriteChartViewSet(viewsets.ModelViewSet): permission_classes = ( IsOwnerOrNot, - ) + ) + + http_method_names = ['get', 'put', 'patch', 'delete'] - http_method_names = ['get','put','patch','delete'] class EntryViewSet(viewsets.ModelViewSet): model = VirtualRaceResult serializer_class = EntrySerializer - def get_queryset(self): # pragma: no cover + def get_queryset(self): # pragma: no cover try: return VirtualRaceResult.objects.filter(userid=self.request.user.id) except TypeError: return [] - http_method_names = ['get','post'] + http_method_names = ['get', 'post'] permission_classes = ( IsCompetitorOrNot, ) + class VirtualRaceViewSet(viewsets.ModelViewSet): model = VirtualRace serializer_class = VirtualRaceSerializer - def get_queryset(self): # pragma: no cover + def get_queryset(self): # pragma: no cover try: return VirtualRace.objects.all() except TypeError: @@ -154,11 +154,12 @@ class VirtualRaceViewSet(viewsets.ModelViewSet): http_method_names = ['get'] + class CourseStandardViewSet(viewsets.ModelViewSet): model = CourseStandard serializer_class = CourseStandardSerializer - def get_queryset(self): # pragma: no cover + def get_queryset(self): # pragma: no cover try: return CourseStandard.objects.all() except TypeError: @@ -166,12 +167,13 @@ class CourseStandardViewSet(viewsets.ModelViewSet): http_method_names = ['get'] + class StandardCollectionViewSet(viewsets.ModelViewSet): model = StandardCollection serializer_class = StandardCollectionSerializer - def get_queryset(self): # pragma: no cover + def get_queryset(self): # pragma: no cover try: return StandardCollection.objects.all() except TypeError: @@ -179,674 +181,911 @@ class StandardCollectionViewSet(viewsets.ModelViewSet): http_method_names = ['get'] + class GeoCourseViewSet(viewsets.ModelViewSet): model = GeoCourse, serializer_class = GeoCourseSerializer - def get_queryset(self): # pragma: no cover + def get_queryset(self): # pragma: no cover try: return GeoCourse.objects.all() except TypeError: return [] - http_method_names = ['get','patch'] + http_method_names = ['get', 'patch'] + class StrokeDataViewSet(viewsets.ModelViewSet): serializer_class = StrokeDataSerializer + # Routers provide an easy way of automatically determining the URL conf. router = routers.DefaultRouter() -router.register(r'api/workouts',WorkoutViewSet, 'workout') -router.register(r'api/plannedsessions',PlannedSessionViewSet,'plannedsession') -router.register(r'api/me',RowerViewSet, 'rower') -router.register(r'api/charts',FavoriteChartViewSet, 'charts') -router.register(r'api/entries',EntryViewSet,'entries') -router.register(r'api/challenges',VirtualRaceViewSet,'challenges') -router.register(r'api/standards',CourseStandardViewSet,'standards') -router.register(r'api/standardcollections',StandardCollectionViewSet,'standardcollections') -router.register(r'api/geocourses',GeoCourseViewSet,'geocourses') +router.register(r'api/workouts', WorkoutViewSet, 'workout') +router.register(r'api/plannedsessions', + PlannedSessionViewSet, 'plannedsession') +router.register(r'api/me', RowerViewSet, 'rower') +router.register(r'api/charts', FavoriteChartViewSet, 'charts') +router.register(r'api/entries', EntryViewSet, 'entries') +router.register(r'api/challenges', VirtualRaceViewSet, 'challenges') +router.register(r'api/standards', CourseStandardViewSet, 'standards') +router.register(r'api/standardcollections', + StandardCollectionViewSet, 'standardcollections') +router.register(r'api/geocourses', GeoCourseViewSet, 'geocourses') -def permissiondenied_view(request): # pragma: no cover + +def permissiondenied_view(request): # pragma: no cover raise PermissionDenied - -def filenotfound_view(request): # pragma: no cover +def filenotfound_view(request): # pragma: no cover return rowers.views.error403_view(request) -def response_error_handler(request, exception=None): # pragma: no cover + +def response_error_handler(request, exception=None): # pragma: no cover return HttpResponse('Error handler content', status=403) -def filenotfound_handler(request, exception=None): # pragma: no cover + +def filenotfound_handler(request, exception=None): # pragma: no cover return HttpResponse('Error handler content', status=404) + handler403 = views.error403_view handler404 = views.error404_view handler400 = views.error400_view handler500 = views.error500_view -from oauth2_provider.views import base #app_name = "rowers" urlpatterns = [ re_path(r'^o/authorize/$', base.AuthorizationView.as_view(), name="authorize"), re_path(r'^o/token/$', base.TokenView.as_view(), name="token"), re_path(r'^', include(router.urls)), - re_path(r'^api-docs/$', views.schema_view,name='schema_view'), - re_path(r'^api-auth/', include('rest_framework.urls', namespace='rest_framework')), - re_path(r'^api/workouts/(?P\b[0-9A-Fa-f]+\b)/strokedata/$',views.strokedatajson, + re_path(r'^api-docs/$', views.schema_view, name='schema_view'), + re_path(r'^api-auth/', include('rest_framework.urls', + namespace='rest_framework')), + re_path(r'^api/workouts/(?P\b[0-9A-Fa-f]+\b)/strokedata/$', views.strokedatajson, name='strokedatajson'), - re_path(r'^api/v2/workouts/(?P\b[0-9A-Fa-f]+\b)/strokedata/$',views.strokedatajson_v2, + re_path(r'^api/v2/workouts/(?P\b[0-9A-Fa-f]+\b)/strokedata/$', views.strokedatajson_v2, name='strokedatajson_v2'), - re_path(r'^500v/$',views.error500_view,name='error500_view'), - path('502/', TemplateView.as_view(template_name='502.html'),name='502'), - path('500/', TemplateView.as_view(template_name='500.html'),name='500'), - path('404/', TemplateView.as_view(template_name='404.html'),name='404'), - path('400/', TemplateView.as_view(template_name='400.html'),name='400'), - path('403/', TemplateView.as_view(template_name='403.html'),name='403'), - re_path(r'^exportallworkouts/?/$',views.workouts_summaries_email_view,name='workouts_summaries_email_view'), - path('failedjobs/',views.failed_queue_view,name='failed_queue_view'), - path('failedjobs/empty/',views.failed_queue_empty,name='failed_queue_empty'), - re_path('^failedjobs/(?P\w+.*)/$',views.failed_job_view,name='failed_job_view'), - re_path(r'^update_empower/$',views.rower_update_empower_view,name='rower_update_empower_view'), - re_path(r'^agegroupcp/(?P\d+)/$',views.agegroupcpview,name='agegroupcpview'), - re_path(r'^agegroupcp/(?P\d+)/(?P\d+)/$',views.agegroupcpview,name='agegroupcpview'), + re_path(r'^500v/$', views.error500_view, name='error500_view'), + path('502/', TemplateView.as_view(template_name='502.html'), name='502'), + path('500/', TemplateView.as_view(template_name='500.html'), name='500'), + path('404/', TemplateView.as_view(template_name='404.html'), name='404'), + path('400/', TemplateView.as_view(template_name='400.html'), name='400'), + path('403/', TemplateView.as_view(template_name='403.html'), name='403'), + re_path(r'^exportallworkouts/?/$', views.workouts_summaries_email_view, + name='workouts_summaries_email_view'), + path('failedjobs/', views.failed_queue_view, name='failed_queue_view'), + path('failedjobs/empty/', views.failed_queue_empty, name='failed_queue_empty'), + re_path('^failedjobs/(?P\w+.*)/$', + views.failed_job_view, name='failed_job_view'), + re_path(r'^update_empower/$', views.rower_update_empower_view, + name='rower_update_empower_view'), + re_path(r'^agegroupcp/(?P\d+)/$', + views.agegroupcpview, name='agegroupcpview'), + re_path(r'^agegroupcp/(?P\d+)/(?P\d+)/$', + views.agegroupcpview, name='agegroupcpview'), re_path(r'^ajax_agegroup/(?P\d+)/(?P\w+.*)/(?P\w+.*)/(?P\d+)/$', - views.ajax_agegrouprecords,name='ajax_agegrouprecords'), + views.ajax_agegrouprecords, name='ajax_agegrouprecords'), re_path(r'^agegrouprecords/(?P\w+.*)/(?P\w+.*)/(?P\d+)m/$', - views.agegrouprecordview,name='agegrouprecordview'), + views.agegrouprecordview, name='agegrouprecordview'), re_path(r'^agegrouprecords/(?P\w+.*)/(?P\w+.*)/(?P\d+)min/$', - views.agegrouprecordview,name='agegrouprecordview'), + views.agegrouprecordview, name='agegrouprecordview'), re_path(r'^agegrouprecords/(?P\w+.*)/(?P\w+.*)/$', - views.agegrouprecordview,name='agegrouprecordview'), + views.agegrouprecordview, name='agegrouprecordview'), re_path(r'^agegrouprecords/$', - views.agegrouprecordview,name='agegrouprecordview'), - re_path(r'^list-workouts/team/(?P\d+)/$',views.workouts_view, - name='workouts_view'), - re_path(r'^(?P\d+)/list-workouts/$',views.workouts_view, - name='workouts_view'), - re_path(r'^list-workouts/user/(?P\d+)/$',views.workouts_view, - name='workouts_view'), - re_path(r'^virtualevents/$',views.virtualevents_view,name='virtualevents_view'), - re_path(r'^virtualevent/createchoice/$', TemplateView.as_view(template_name='newchallengechoice.html'),name='newchallengechoice'), - re_path(r'^virtualevent/create/$',views.virtualevent_create_view,name='virtualevent_create_view'), - re_path(r'^virtualevent/createindoor/$',views.indoorvirtualevent_create_view,name='indoorvirtualevent_create_view'), - re_path(r'^virtualevent/createfastest/$',views.fastestvirtualevent_create_view, + views.agegrouprecordview, name='agegrouprecordview'), + re_path(r'^list-workouts/team/(?P\d+)/$', views.workouts_view, + name='workouts_view'), + re_path(r'^(?P\d+)/list-workouts/$', views.workouts_view, + name='workouts_view'), + re_path(r'^list-workouts/user/(?P\d+)/$', views.workouts_view, + name='workouts_view'), + re_path(r'^virtualevents/$', views.virtualevents_view, + name='virtualevents_view'), + re_path(r'^virtualevent/createchoice/$', TemplateView.as_view( + template_name='newchallengechoice.html'), name='newchallengechoice'), + re_path(r'^virtualevent/create/$', views.virtualevent_create_view, + name='virtualevent_create_view'), + re_path(r'^virtualevent/createindoor/$', views.indoorvirtualevent_create_view, + name='indoorvirtualevent_create_view'), + re_path(r'^virtualevent/createfastest/$', views.fastestvirtualevent_create_view, name='fastestvirtualevent_create_view'), re_path(r'^raceregistration/togglenotification/(?P\d+)/$', - views.virtualevent_toggle_email_view,name='virtualevent_toggle_email_view'), + views.virtualevent_toggle_email_view, name='virtualevent_toggle_email_view'), re_path(r'^indoorraceregistration/togglenotification/(?P\d+)/$', - views.indoorvirtualevent_toggle_email_view,name='indoorvirtualevent_toggle_email_view'), - re_path(r'^virtualevent/(?P\d+)/$',views.virtualevent_view,name='virtualevent_view'), - re_path(r'^virtualevent/(?P\d+)/ranking$',views.virtualevent_ranking_view,name='virtualevent_ranking_view'), - re_path(r'^virtualevent/(?P\d+)/edit/$',views.virtualevent_edit_view,name='virtualevent_edit_view'), - re_path(r'^virtualevent/(?P\d+)/editindoor/$',views.indoorvirtualevent_edit_view,name='indoorvirtualevent_edit_view'), - re_path(r'^virtualevent/(?P\d+)/register/$',views.virtualevent_register_view,name='virtualevent_register_view'), - re_path(r'^virtualevent/(?P\d+)/registerindoor/$',views.indoorvirtualevent_register_view,name='indoorvirtualevent_register_view'), - re_path(r'^virtualevent/(?P\d+)/register/edit/(?P\d+)/$',views.virtualevent_entry_edit_view, + views.indoorvirtualevent_toggle_email_view, name='indoorvirtualevent_toggle_email_view'), + re_path(r'^virtualevent/(?P\d+)/$', + views.virtualevent_view, name='virtualevent_view'), + re_path(r'^virtualevent/(?P\d+)/ranking$', + views.virtualevent_ranking_view, name='virtualevent_ranking_view'), + re_path(r'^virtualevent/(?P\d+)/edit/$', + views.virtualevent_edit_view, name='virtualevent_edit_view'), + re_path(r'^virtualevent/(?P\d+)/editindoor/$', + views.indoorvirtualevent_edit_view, name='indoorvirtualevent_edit_view'), + re_path(r'^virtualevent/(?P\d+)/register/$', + views.virtualevent_register_view, name='virtualevent_register_view'), + re_path(r'^virtualevent/(?P\d+)/registerindoor/$', + views.indoorvirtualevent_register_view, name='indoorvirtualevent_register_view'), + re_path(r'^virtualevent/(?P\d+)/register/edit/(?P\d+)/$', views.virtualevent_entry_edit_view, name='virtualevent_entry_edit_view'), - re_path(r'^virtualevent/(?P\d+)/adddiscipline/$',views.virtualevent_addboat_view,name='virtualevent_addboat_view'), - re_path(r'^virtualevent/(?P\d+)/withdraw/(?P\d+)/$',views.virtualevent_withdraw_view,name='virtualevent_withdraw_view'), - re_path(r'^virtualevent/(?P\d+)/withdraw/$',views.virtualevent_withdraw_view,name='virtualevent_withdraw_view'), + re_path(r'^virtualevent/(?P\d+)/adddiscipline/$', + views.virtualevent_addboat_view, name='virtualevent_addboat_view'), + re_path(r'^virtualevent/(?P\d+)/withdraw/(?P\d+)/$', + views.virtualevent_withdraw_view, name='virtualevent_withdraw_view'), + re_path(r'^virtualevent/(?P\d+)/withdraw/$', + views.virtualevent_withdraw_view, name='virtualevent_withdraw_view'), re_path(r'^virtualevent/(?P\b[0-9A-Fa-f]+\b)/submit/$', - views.virtualevent_submit_result_view,name='virtualevent_submit_result_view'), + views.virtualevent_submit_result_view, name='virtualevent_submit_result_view'), re_path(r'^virtualevent/(?P\d+)/submit/(?P\b[0-9A-Fa-f]+\b)/$', - views.virtualevent_submit_result_view,name='virtualevent_submit_result_view'), + views.virtualevent_submit_result_view, name='virtualevent_submit_result_view'), re_path(r'^virtualevent/(?P\d+)/disqualify/(?P\d+)/', - views.virtualevent_disqualify_view,name='virtualevent_disqualify_view'), + views.virtualevent_disqualify_view, name='virtualevent_disqualify_view'), re_path(r'^virtualevent/(?P\d+)/withdrawresult/(?P\d+)/', - views.virtualevent_withdrawresult_view,name='virtualevent_withdrawresult_view'), + views.virtualevent_withdrawresult_view, name='virtualevent_withdrawresult_view'), re_path(r'^virtualevent/(?P\d+)/download/', - views.virtualevent_results_download_view,name='virtualevent_results_download_view'), - re_path(r'^list-workouts/$',views.workouts_view,name='workouts_view'), - re_path(r'^list-courses/$',views.courses_view,name='courses_view'), - re_path(r'^list-standards/$',views.standards_view,name='standards_view'), - re_path(r'^courses/upload/$',views.course_upload_view,name='course_upload_view'), - re_path(r'^courses/(?P\d+)/update/(?P\d+)/',views.course_update_confirm,name='course_update_confirm'), - re_path(r'^courses/(?P\d+)/update/',views.course_upload_replace_view,name='course_upload_replace_view'), - re_path(r'^standards/upload/$',views.standards_upload_view,name='standards_upload_view'), - re_path(r'^standards/upload/(?P\d+)/$',views.standards_upload_view,name='standards_upload_view'), - re_path(r'^workout/addmanual/(?P\d+)/$',views.addmanual_view,name='addmanual_view'), - re_path(r'^workout/addmanual/$',views.addmanual_view,name='addmanual_view'), - re_path(r'^workouts-join/$',views.workouts_join_view,name='workouts_join_view'), - re_path(r'^workouts-join/user/(?P\d+)$',views.workouts_join_view,name='workouts_join_view'), - re_path(r'^workouts-join-select/$',views.workouts_join_select,name='workouts_join_select'), - re_path(r'^workouts-join-select/user/(?P\d+)/$',views.workouts_join_select,name='workouts_join_select'), - re_path(r'^user-analysis-select/(?P\w.*)/team/(?P\d+)/workout/(?P\b[0-9A-Fa-f]+\b)/$',views.analysis_new,name='analysis_new'), - re_path(r'^user-analysis-select/(?P\w.*)/session/(?P\d+)/workout/(?P\b[0-9A-Fa-f]+\b)/$',views.analysis_new,name='analysis_new'), - re_path(r'^user-analysis-select/(?P\w.*)/workout/(?P\b[0-9A-Fa-f]+\b)/$',views.analysis_new,name='analysis_new'), - re_path(r'^user-analysis-select/(?P\w.*)/user/(?P\d+)/$',views.analysis_new,name='analysis_new'), - re_path(r'^user-analysis-select/(?P\w.*)/team/(?P\d+)/$',views.analysis_new,name='analysis_new'), - re_path(r'^user-analysis-select/team/(?P\d+)/workout/(?P\b[0-9A-Fa-f]+\b)/$',views.analysis_new,name='analysis_new'), - re_path(r'^user-analysis-select/user/(?P\d+)/$',views.analysis_new,name='analysis_new'), - re_path(r'^user-analysis-select/team/(?P\d+)/$',views.analysis_new,name='analysis_new'), - re_path(r'^user-analysis-select/(?P\w.*)/$',views.analysis_new,name='analysis_new'), - re_path(r'^user-analysis-select/$',views.analysis_new,name='analysis_new'), - re_path(r'^list-jobs/$',views.session_jobs_view,name='session_jobs_view'), - re_path(r'^jobs-status/$',views.session_jobs_status,name='session_jobs_status'), - re_path(r'^job-kill/(?P.*)/$',views.kill_async_job), - re_path(r'^record-progress/(?P\d+)/(?P.*)/$',views.post_progress,name='post_progress'), - re_path(r'^record-progress/(?P.*)/$',views.post_progress), - re_path(r'^record-progress/$',views.post_progress), - re_path(r'^list-graphs/$',views.graphs_view,name='graphs_view'), - re_path(r'^list-graphs/user/(?P\d+)/$',views.graphs_view,name='graphs_view'), - re_path(r'^createmarkerworkouts/user/(?P\d+)/$',views.create_marker_workouts_view, + views.virtualevent_results_download_view, name='virtualevent_results_download_view'), + re_path(r'^list-workouts/$', views.workouts_view, name='workouts_view'), + re_path(r'^list-courses/$', views.courses_view, name='courses_view'), + re_path(r'^list-standards/$', views.standards_view, name='standards_view'), + re_path(r'^courses/upload/$', views.course_upload_view, + name='course_upload_view'), + re_path(r'^courses/(?P\d+)/update/(?P\d+)/', + views.course_update_confirm, name='course_update_confirm'), + re_path(r'^courses/(?P\d+)/update/', + views.course_upload_replace_view, name='course_upload_replace_view'), + re_path(r'^standards/upload/$', views.standards_upload_view, + name='standards_upload_view'), + re_path(r'^standards/upload/(?P\d+)/$', + views.standards_upload_view, name='standards_upload_view'), + re_path(r'^workout/addmanual/(?P\d+)/$', + views.addmanual_view, name='addmanual_view'), + re_path(r'^workout/addmanual/$', + views.addmanual_view, name='addmanual_view'), + re_path(r'^workouts-join/$', views.workouts_join_view, + name='workouts_join_view'), + re_path(r'^workouts-join/user/(?P\d+)$', + views.workouts_join_view, name='workouts_join_view'), + re_path(r'^workouts-join-select/$', views.workouts_join_select, + name='workouts_join_select'), + re_path(r'^workouts-join-select/user/(?P\d+)/$', + views.workouts_join_select, name='workouts_join_select'), + re_path( + r'^user-analysis-select/(?P\w.*)/team/(?P\d+)/workout/(?P\b[0-9A-Fa-f]+\b)/$', views.analysis_new, name='analysis_new'), + re_path( + r'^user-analysis-select/(?P\w.*)/session/(?P\d+)/workout/(?P\b[0-9A-Fa-f]+\b)/$', views.analysis_new, name='analysis_new'), + re_path( + r'^user-analysis-select/(?P\w.*)/workout/(?P\b[0-9A-Fa-f]+\b)/$', views.analysis_new, name='analysis_new'), + re_path(r'^user-analysis-select/(?P\w.*)/user/(?P\d+)/$', + views.analysis_new, name='analysis_new'), + re_path(r'^user-analysis-select/(?P\w.*)/team/(?P\d+)/$', + views.analysis_new, name='analysis_new'), + re_path( + r'^user-analysis-select/team/(?P\d+)/workout/(?P\b[0-9A-Fa-f]+\b)/$', views.analysis_new, name='analysis_new'), + re_path(r'^user-analysis-select/user/(?P\d+)/$', + views.analysis_new, name='analysis_new'), + re_path(r'^user-analysis-select/team/(?P\d+)/$', + views.analysis_new, name='analysis_new'), + re_path(r'^user-analysis-select/(?P\w.*)/$', + views.analysis_new, name='analysis_new'), + re_path(r'^user-analysis-select/$', + views.analysis_new, name='analysis_new'), + re_path(r'^list-jobs/$', views.session_jobs_view, + name='session_jobs_view'), + re_path(r'^jobs-status/$', views.session_jobs_status, + name='session_jobs_status'), + re_path(r'^job-kill/(?P.*)/$', views.kill_async_job), + re_path(r'^record-progress/(?P\d+)/(?P.*)/$', + views.post_progress, name='post_progress'), + re_path(r'^record-progress/(?P.*)/$', views.post_progress), + re_path(r'^record-progress/$', views.post_progress), + re_path(r'^list-graphs/$', views.graphs_view, name='graphs_view'), + re_path(r'^list-graphs/user/(?P\d+)/$', + views.graphs_view, name='graphs_view'), + re_path(r'^createmarkerworkouts/user/(?P\d+)/$', views.create_marker_workouts_view, name='create_marker_workouts_view'), - re_path(r'^createmarkerworkouts/$',views.create_marker_workouts_view, + re_path(r'^createmarkerworkouts/$', views.create_marker_workouts_view, name='create_marker_workouts_view'), - re_path(r'^goldmedalscores/$',views.goldmedalscores_view,name='goldmedalscores_view'), - re_path(r'^goldmedalscores/user/(?P\d+)/$',views.goldmedalscores_view,name='goldmedalscores_view'), - re_path(r'^goldmedalscores/user/(?P\d+)/(?P\w+.*)/$',views.goldmedalscores_view,name='goldmedalscores_view'), - re_path(r'^performancemanager/$',views.performancemanager_view,name='performancemanager_view'), - re_path(r'^performancemanager/user/(?P\d+)/$',views.performancemanager_view,name='performancemanager_view'), - re_path(r'^performancemanager/user/(?P\d+)/(?P\w+.*)/$',views.performancemanager_view,name='performancemanager_view'), - re_path(r'^trainingzones/$',views.trainingzones_view,name='trainingzones_view'), - re_path(r'^trainingzones/user/(?P\d+)/$',views.trainingzones_view,name='trainingzones_view'), - re_path(r'^trainingzones/user/(?P\d+)/data/$',views.trainingzones_view_data,name="trainingzones_view_data"), - re_path(r'^trainingzones/data/$',views.trainingzones_view_data,name="trainingzones_view_data"), - re_path(r'^ote-bests2/user/(?P\d+)/$',views.rankings_view2,name='rankings_view2'), - re_path(r'^ote-bests2/$',views.rankings_view2,name='rankings_view2'), - re_path(r'^analysisdata/$',views.analysis_view_data,name='analysis_view_data'), - re_path(r'^graph/(?P\d+)/$',views.graph_show_view,name='graph_show_view'), - re_path(r'^graph/(?P\d+)/delete/$',views.GraphDelete.as_view(),name='graph_delete'), - re_path(r'^workout/(?P\b[0-9A-Fa-f]+\b)/get-thumbnails/$',views.get_thumbnails, - name='get_thumbnails'), - re_path(r'^workout/(?P\b[0-9A-Fa-f]+\b)/otwuseimpeller/$',views.otw_use_impeller, - name='otw_use_impeller'), - re_path(r'^workout/(?P\b[0-9A-Fa-f]+\b)/otwusegps/$',views.otw_use_gps, - name='otw_use_gps'), - re_path(r'^workout/(?P\b[0-9A-Fa-f]+\b)/toggle-ranking/$',views.workout_toggle_ranking, - name='workout_toggle_ranking'), - re_path(r'^workout/upload/team/$',views.team_workout_upload_view,name='team_workout_upload_view'), - re_path(r'^workout/upload/(?P\d+)/$',views.workout_upload_view,name='workout_upload_view'), - re_path(r'^workout/upload/$',views.workout_upload_view,name='workout_upload_view'), - re_path(r'^workout/(?P\b[0-9A-Fa-f]+\b)/histo/$',views.workout_histo_view, - name='workout_histo_view'), - re_path(r'^workout/(?P\b[0-9A-Fa-f]+\b)/forcecurve/$',views.workout_forcecurve_view, - name='workout_forcecurve_view'), - re_path(r'^workout/(?P\b[0-9A-Fa-f]+\b)/unsubscribe/$',views.workout_unsubscribe_view, - name='workout_unsubscribe_view'), - re_path(r'^workout/(?P\b[0-9A-Fa-f]+\b)/comment/$',views.workout_comment_view, - name='workout_comment_view'), - re_path(r'^workout/(?P\b[0-9A-Fa-f]+\b)/emailtcx/$',views.workout_tcxemail_view, - name='workout_tcxemail_view'), - re_path(r'^workout/(?P\b[0-9A-Fa-f]+\b)/emailgpx/$',views.workout_gpxemail_view, - name='workout_gpxemail_view'), - re_path(r'^workout/(?P\b[0-9A-Fa-f]+\b)/emailcsv/$',views.workout_csvemail_view, - name='workout_csvemail_view'), - re_path(r'^workout/(?P\b[0-9A-Fa-f]+\b)/csvtoadmin/$',views.workout_csvtoadmin_view, - name='workout_csvtoadmin_view'), - re_path(r'^workout/(?P\b[0-9A-Fa-f]+\b)/edit/$',views.workout_edit_view, - name='workout_edit_view'), - re_path(r'^workout/(?P\b[0-9A-Fa-f]+\b)/map/$',views.workout_map_view,name='workout_map_view'), - re_path(r'^workout/(?P\b[0-9A-Fa-f]+\b)/instroke/(?P\w+.*)/$',views.instroke_chart,name='instroke_chart'), - re_path(r'^workout/(?P\b[0-9A-Fa-f]+\b)/instroke/$',views.instroke_view,name='instroke_view'), - re_path(r'^workout/(?P\b[0-9A-Fa-f]+\b)/stats/$',views.workout_stats_view,name='workout_stats_view'), - re_path(r'^workout/(?P\b[0-9A-Fa-f]+\b)/data/$',views.workout_data_view, - name='workout_data_view'), - re_path(r'^workout/(?P\b[0-9A-Fa-f]+\b)/resample/$',views.workout_resample_view, - name='workout_resample_view'), - re_path(r'^workout/(?P\b[0-9A-Fa-f]+\b)/(?P\w+)/erase/$',views.workout_erase_column_view, + re_path(r'^goldmedalscores/$', views.goldmedalscores_view, + name='goldmedalscores_view'), + re_path(r'^goldmedalscores/user/(?P\d+)/$', + views.goldmedalscores_view, name='goldmedalscores_view'), + re_path(r'^goldmedalscores/user/(?P\d+)/(?P\w+.*)/$', + views.goldmedalscores_view, name='goldmedalscores_view'), + re_path(r'^performancemanager/$', views.performancemanager_view, + name='performancemanager_view'), + re_path(r'^performancemanager/user/(?P\d+)/$', + views.performancemanager_view, name='performancemanager_view'), + re_path(r'^performancemanager/user/(?P\d+)/(?P\w+.*)/$', + views.performancemanager_view, name='performancemanager_view'), + re_path(r'^trainingzones/$', views.trainingzones_view, + name='trainingzones_view'), + re_path(r'^trainingzones/user/(?P\d+)/$', + views.trainingzones_view, name='trainingzones_view'), + re_path(r'^trainingzones/user/(?P\d+)/data/$', + views.trainingzones_view_data, name="trainingzones_view_data"), + re_path(r'^trainingzones/data/$', views.trainingzones_view_data, + name="trainingzones_view_data"), + re_path(r'^ote-bests2/user/(?P\d+)/$', + views.rankings_view2, name='rankings_view2'), + re_path(r'^ote-bests2/$', views.rankings_view2, name='rankings_view2'), + re_path(r'^analysisdata/$', views.analysis_view_data, + name='analysis_view_data'), + re_path(r'^graph/(?P\d+)/$', + views.graph_show_view, name='graph_show_view'), + re_path(r'^graph/(?P\d+)/delete/$', + views.GraphDelete.as_view(), name='graph_delete'), + re_path(r'^workout/(?P\b[0-9A-Fa-f]+\b)/get-thumbnails/$', views.get_thumbnails, + name='get_thumbnails'), + re_path(r'^workout/(?P\b[0-9A-Fa-f]+\b)/otwuseimpeller/$', views.otw_use_impeller, + name='otw_use_impeller'), + re_path(r'^workout/(?P\b[0-9A-Fa-f]+\b)/otwusegps/$', views.otw_use_gps, + name='otw_use_gps'), + re_path(r'^workout/(?P\b[0-9A-Fa-f]+\b)/toggle-ranking/$', views.workout_toggle_ranking, + name='workout_toggle_ranking'), + re_path(r'^workout/upload/team/$', views.team_workout_upload_view, + name='team_workout_upload_view'), + re_path(r'^workout/upload/(?P\d+)/$', + views.workout_upload_view, name='workout_upload_view'), + re_path(r'^workout/upload/$', views.workout_upload_view, + name='workout_upload_view'), + re_path(r'^workout/(?P\b[0-9A-Fa-f]+\b)/histo/$', views.workout_histo_view, + name='workout_histo_view'), + re_path(r'^workout/(?P\b[0-9A-Fa-f]+\b)/forcecurve/$', views.workout_forcecurve_view, + name='workout_forcecurve_view'), + re_path(r'^workout/(?P\b[0-9A-Fa-f]+\b)/unsubscribe/$', views.workout_unsubscribe_view, + name='workout_unsubscribe_view'), + re_path(r'^workout/(?P\b[0-9A-Fa-f]+\b)/comment/$', views.workout_comment_view, + name='workout_comment_view'), + re_path(r'^workout/(?P\b[0-9A-Fa-f]+\b)/emailtcx/$', views.workout_tcxemail_view, + name='workout_tcxemail_view'), + re_path(r'^workout/(?P\b[0-9A-Fa-f]+\b)/emailgpx/$', views.workout_gpxemail_view, + name='workout_gpxemail_view'), + re_path(r'^workout/(?P\b[0-9A-Fa-f]+\b)/emailcsv/$', views.workout_csvemail_view, + name='workout_csvemail_view'), + re_path(r'^workout/(?P\b[0-9A-Fa-f]+\b)/csvtoadmin/$', views.workout_csvtoadmin_view, + name='workout_csvtoadmin_view'), + re_path(r'^workout/(?P\b[0-9A-Fa-f]+\b)/edit/$', views.workout_edit_view, + name='workout_edit_view'), + re_path(r'^workout/(?P\b[0-9A-Fa-f]+\b)/map/$', + views.workout_map_view, name='workout_map_view'), + re_path(r'^workout/(?P\b[0-9A-Fa-f]+\b)/instroke/(?P\w+.*)/$', + views.instroke_chart, name='instroke_chart'), + re_path(r'^workout/(?P\b[0-9A-Fa-f]+\b)/instroke/$', + views.instroke_view, name='instroke_view'), + re_path(r'^workout/(?P\b[0-9A-Fa-f]+\b)/stats/$', + views.workout_stats_view, name='workout_stats_view'), + re_path(r'^workout/(?P\b[0-9A-Fa-f]+\b)/data/$', views.workout_data_view, + name='workout_data_view'), + re_path(r'^workout/(?P\b[0-9A-Fa-f]+\b)/resample/$', views.workout_resample_view, + name='workout_resample_view'), + re_path(r'^workout/(?P\b[0-9A-Fa-f]+\b)/(?P\w+)/erase/$', views.workout_erase_column_view, name='workout_erase_column_view'), - re_path(r'^workout/(?P\b[0-9A-Fa-f]+\b)/zeropower-confirm/$',views.remove_power_confirm_view, + re_path(r'^workout/(?P\b[0-9A-Fa-f]+\b)/zeropower-confirm/$', views.remove_power_confirm_view, name='remove_power_confirm_view'), - re_path(r'^workout/(?P\b[0-9A-Fa-f]+\b)/zeropower/$',views.remove_power_view, + re_path(r'^workout/(?P\b[0-9A-Fa-f]+\b)/zeropower/$', views.remove_power_view, name='remove_power_view'), - re_path(r'^workout/(?P\b[0-9A-Fa-f]+\b)/otwsetpower/$',views.workout_otwsetpower_view,name='workout_otwsetpower_view'), - re_path(r'^workout/(?P\b[0-9A-Fa-f]+\b)/interactiveotwplot/$',views.workout_otwpowerplot_view,name='workout_otwpowerplot_view'), - re_path(r'^workout/(?P\b[0-9A-Fa-f]+\b)/wind/$',views.workout_wind_view,name='workout_wind_view'), - re_path(r'^workout/(?P\b[0-9A-Fa-f]+\b)/image/$',views.workout_uploadimage_view,name='workout_uploadimage_view'), - re_path(r'^virtualevent/(?P\d+)/compare/$',views.virtualevent_compare_view,name='virtualevent_compare_view'), - re_path(r'^courses/(?P\d+)/compare/$',views.course_compare_view,name='course_compare_view'), - re_path(r'^virtualevent/(?P\d+)/mapcompare/$',views.virtualevent_mapcompare_view, + re_path(r'^workout/(?P\b[0-9A-Fa-f]+\b)/otwsetpower/$', + views.workout_otwsetpower_view, name='workout_otwsetpower_view'), + re_path(r'^workout/(?P\b[0-9A-Fa-f]+\b)/interactiveotwplot/$', + views.workout_otwpowerplot_view, name='workout_otwpowerplot_view'), + re_path(r'^workout/(?P\b[0-9A-Fa-f]+\b)/wind/$', + views.workout_wind_view, name='workout_wind_view'), + re_path(r'^workout/(?P\b[0-9A-Fa-f]+\b)/image/$', + views.workout_uploadimage_view, name='workout_uploadimage_view'), + re_path(r'^virtualevent/(?P\d+)/compare/$', + views.virtualevent_compare_view, name='virtualevent_compare_view'), + re_path(r'^courses/(?P\d+)/compare/$', + views.course_compare_view, name='course_compare_view'), + re_path(r'^virtualevent/(?P\d+)/mapcompare/$', views.virtualevent_mapcompare_view, name='virtualevent_mapcompare_view'), - re_path(r'^courses/(?P\d+)/mapcompare/$',views.course_mapcompare_view, + re_path(r'^courses/(?P\d+)/mapcompare/$', views.course_mapcompare_view, name='course_mapcompare_view'), re_path(r'^virtualevent/(?P\d+)/image/$', - views.virtualevent_uploadimage_view,name='virtualevent_uploadimage_view'), + views.virtualevent_uploadimage_view, name='virtualevent_uploadimage_view'), re_path(r'^virtualevent/(?P\d+)/setimage/(?P\d+)/$', - views.virtualevent_setlogo_view,name='virtualevent_setlog_view'), + views.virtualevent_setlogo_view, name='virtualevent_setlog_view'), re_path(r'^virtualevent/(?P\d+)/follow/$', - views.addfollower_view,name='addfollower_view'), + views.addfollower_view, name='addfollower_view'), re_path(r'^logo/(?P\d+)/delete/$', - views.logo_delete_view,name='logo_delete_view'), - re_path(r'^workout/(?P\b[0-9A-Fa-f]+\b)/darkskywind/$',views.workout_downloadwind_view,name='workout_downloadwind_view'), - re_path(r'^workout/(?P\b[0-9A-Fa-f]+\b)/metar/(?P\w+)/$',views.workout_downloadmetar_view,name='workout_downloadmetar_view'), - re_path(r'^workout/(?P\b[0-9A-Fa-f]+\b)/stream/$',views.workout_stream_view,name='workout_stream_view'), - re_path(r'^workout/(?P\b[0-9A-Fa-f]+\b)/editintervals/$',views.workout_summary_edit_view, - name='workout_summary_edit_view'), - re_path(r'^workout/(?P\b[0-9A-Fa-f]+\b)/restore/$',views.workout_summary_restore_view,name='workout_summary_restore_view'), - re_path(r'^workout/(?P\b[0-9A-Fa-f]+\b)/split/$',views.workout_split_view,name='workout_split_view'), - re_path(r'^workout/(?P\b[0-9A-Fa-f]+\b)/view/entry/(?P\d+)/$',views.workout_view,name='workout_view'), - re_path(r'^workout/(?P\b[0-9A-Fa-f]+\b)/view/entry/(?P\d+)/nocourse/$',views.workout_view,name='workout_view'), - re_path(r'^workout/(?P\b[0-9A-Fa-f]+\b)/view/session/(?P\d+)/$',views.workout_view,name='workout_view'), - re_path(r'^workout/(?P\b[0-9A-Fa-f]+\b)/view/$',views.workout_view,name='workout_view'), - re_path(r'^workout/(?P\b[0-9A-Fa-f]+\b)/video/$',views.workout_video_create_view, + views.logo_delete_view, name='logo_delete_view'), + re_path(r'^workout/(?P\b[0-9A-Fa-f]+\b)/darkskywind/$', + views.workout_downloadwind_view, name='workout_downloadwind_view'), + re_path(r'^workout/(?P\b[0-9A-Fa-f]+\b)/metar/(?P\w+)/$', + views.workout_downloadmetar_view, name='workout_downloadmetar_view'), + re_path(r'^workout/(?P\b[0-9A-Fa-f]+\b)/stream/$', + views.workout_stream_view, name='workout_stream_view'), + re_path(r'^workout/(?P\b[0-9A-Fa-f]+\b)/editintervals/$', views.workout_summary_edit_view, + name='workout_summary_edit_view'), + re_path(r'^workout/(?P\b[0-9A-Fa-f]+\b)/restore/$', + views.workout_summary_restore_view, name='workout_summary_restore_view'), + re_path(r'^workout/(?P\b[0-9A-Fa-f]+\b)/split/$', + views.workout_split_view, name='workout_split_view'), + re_path(r'^workout/(?P\b[0-9A-Fa-f]+\b)/view/entry/(?P\d+)/$', + views.workout_view, name='workout_view'), + re_path(r'^workout/(?P\b[0-9A-Fa-f]+\b)/view/entry/(?P\d+)/nocourse/$', + views.workout_view, name='workout_view'), + re_path(r'^workout/(?P\b[0-9A-Fa-f]+\b)/view/session/(?P\d+)/$', + views.workout_view, name='workout_view'), + re_path(r'^workout/(?P\b[0-9A-Fa-f]+\b)/view/$', + views.workout_view, name='workout_view'), + re_path(r'^workout/(?P\b[0-9A-Fa-f]+\b)/video/$', views.workout_video_create_view, name='workout_video_create_view'), - re_path(r'^video/(?P\d+)/delete/$',views.VideoDelete.as_view(),name='video_delete'), - re_path(r'^video/(?P\w.+)/m/$',views.workout_video_view_mini, - name='workout_video_view_mini'), - re_path(r'^video/(?P\w.+)/$',views.workout_video_view, + re_path(r'^video/(?P\d+)/delete/$', + views.VideoDelete.as_view(), name='video_delete'), + re_path(r'^video/(?P\w.+)/m/$', views.workout_video_view_mini, + name='workout_video_view_mini'), + re_path(r'^video/(?P\w.+)/$', views.workout_video_view, name='workout_video_view'), - re_path(r'^videos/$',views.list_videos,name='list_videos'), - re_path(r'^videos/user/(?P\d+)/$',views.list_videos,name='list_videos'), - re_path(r'^add-video/user/(?P\d+)/$',views.video_selectworkout,name='video_selectworkout'), - re_path(r'^add-video/',views.video_selectworkout,name='video_selectworkout'), -# re_path(r'^workout/(?P\d+)/$',views.workout_view,name='workout_view'), - re_path(r'^workout/(?P\b[0-9A-Fa-f]+\b)/$',views.workout_view,name='workout_view'), - re_path(r'^workout/fusion/(?P\b[0-9A-Fa-f]+\b)/(?P\b[0-9A-Fa-f]+\b)/$',views.workout_fusion_view,name='workout_fusion_view'), - re_path(r'^workout/fusion/(?P\b[0-9A-Fa-f]+\b)/$',views.workout_fusion_list,name='workout_fusion_list'), -# re_path(r'^workout/fusion/(?P\b[0-9A-Fa-f]+\b)/(?P\d+-\d+-\d+)/(?P\d+-\d+-\d+)/$',views.workout_fusion_list,name='workout_fusion_list'), - re_path(r'^help/$',TemplateView.as_view( - template_name='help.html'),name='help' - ), - re_path(r'^physics/$',TemplateView.as_view(template_name='physics.html'),name='physics'), - re_path(r'^partners/$',TemplateView.as_view(template_name='partners.html'),name='partners'), + re_path(r'^videos/$', views.list_videos, name='list_videos'), + re_path(r'^videos/user/(?P\d+)/$', + views.list_videos, name='list_videos'), + re_path(r'^add-video/user/(?P\d+)/$', + views.video_selectworkout, name='video_selectworkout'), + re_path(r'^add-video/', views.video_selectworkout, + name='video_selectworkout'), + # re_path(r'^workout/(?P\d+)/$',views.workout_view,name='workout_view'), + re_path(r'^workout/(?P\b[0-9A-Fa-f]+\b)/$', + views.workout_view, name='workout_view'), + re_path(r'^workout/fusion/(?P\b[0-9A-Fa-f]+\b)/(?P\b[0-9A-Fa-f]+\b)/$', + views.workout_fusion_view, name='workout_fusion_view'), + re_path(r'^workout/fusion/(?P\b[0-9A-Fa-f]+\b)/$', + views.workout_fusion_list, name='workout_fusion_list'), + # re_path(r'^workout/fusion/(?P\b[0-9A-Fa-f]+\b)/(?P\d+-\d+-\d+)/(?P\d+-\d+-\d+)/$',views.workout_fusion_list,name='workout_fusion_list'), + re_path(r'^help/$', TemplateView.as_view( + template_name='help.html'), name='help' + ), + re_path(r'^physics/$', + TemplateView.as_view(template_name='physics.html'), name='physics'), + re_path(r'^partners/$', + TemplateView.as_view(template_name='partners.html'), name='partners'), # keeping the old URLs for retrofit re_path(r'^workout/(?P\b[0-9A-Fa-f]+\b)/addtimeplot/$', - views.workout_add_chart_view, - {'plotnr':'1'},name='workout_add_chart_view'), + views.workout_add_chart_view, + {'plotnr': '1'}, name='workout_add_chart_view'), re_path(r'^workout/(?P\b[0-9A-Fa-f]+\b)/adddistanceplot/$', - views.workout_add_chart_view, - {'plotnr':'2'},name='workout_add_chart_view'), + views.workout_add_chart_view, + {'plotnr': '2'}, name='workout_add_chart_view'), re_path(r'^workout/(?P\b[0-9A-Fa-f]+\b)/addpiechart/$', - views.workout_add_chart_view, - {'plotnr':'3'},name='workout_add_chart_view'), + views.workout_add_chart_view, + {'plotnr': '3'}, name='workout_add_chart_view'), re_path(r'^workout/(?P\b[0-9A-Fa-f]+\b)/adddistanceplot2/$', - views.workout_add_chart_view, - {'plotnr':'7'},name='workout_add_chart_view'), + views.workout_add_chart_view, + {'plotnr': '7'}, name='workout_add_chart_view'), re_path(r'^workout/(?P\b[0-9A-Fa-f]+\b)/addtimeplot2/$', - views.workout_add_chart_view, - {'plotnr':'8'},name='workout_add_chart_view'), + views.workout_add_chart_view, + {'plotnr': '8'}, name='workout_add_chart_view'), re_path(r'^workout/(?P\b[0-9A-Fa-f]+\b)/addotwpowerplot/$', - views.workout_add_chart_view, - {'plotnr':'9'},name='workout_add_chart_view'), + views.workout_add_chart_view, + {'plotnr': '9'}, name='workout_add_chart_view'), re_path(r'^workout/(?P\b[0-9A-Fa-f]+\b)/addpowerpiechart/$', - views.workout_add_chart_view, - {'plotnr':'13'},name='workout_add_chart_view'), + views.workout_add_chart_view, + {'plotnr': '13'}, name='workout_add_chart_view'), re_path(r'^workout/(?P\b[0-9A-Fa-f]+\b)/addstatic/(?P\d+)/$', - views.workout_add_chart_view,name='workout_add_chart_view'), - re_path(r'^workout/(?P\b[0-9A-Fa-f]+\b)/addstatic/$',views.workout_add_chart_view,name='workout_add_chart_view'), - re_path(r'^workout/(?P\b[0-9A-Fa-f]+\b)/delete/$',login_required( + views.workout_add_chart_view, name='workout_add_chart_view'), + re_path(r'^workout/(?P\b[0-9A-Fa-f]+\b)/addstatic/$', + views.workout_add_chart_view, name='workout_add_chart_view'), + re_path(r'^workout/(?P\b[0-9A-Fa-f]+\b)/delete/$', login_required( views.WorkoutDelete.as_view()), name='workout_delete'), - re_path(r'^strava/webhooks/',views.strava_webhook_view,name='strava_webhook_view'), - re_path(r'^garmin/summaries/',views.garmin_summaries_view,name='garmin_summaries_view'), - re_path(r'^garmin/files/',views.garmin_newfiles_ping,name='garmin_newfiles_ping'), - re_path(r'^garmin/activities/',views.garmin_details_view,name='garmin_details_view'), - re_path(r'^garmin/deregistration/',views.garmin_deregistration_view,name='garmin_deregistration_view'), - re_path(r'^workout/(?P\b[0-9A-Fa-f]+\b)/smoothenpace/$',views.workout_smoothenpace_view,name='workout_smoothenpace_view'), - re_path(r'^workout/(?P\b[0-9A-Fa-f]+\b)/undosmoothenpace/$',views.workout_undo_smoothenpace_view,name='workout_undo_smoothenpace_view'), - re_path(r'^workout/c2import/$',views.workout_c2import_view,name='workout_c2import_view'), - re_path(r'^workout/c2list/$',views.workout_c2import_view,name='workout_c2import_view'), - re_path(r'^workout/c2list/(?P\d+)/$',views.workout_c2import_view,name='workout_c2import_view'), - re_path(r'^workout/c2list/user/(?P\d+)/$',views.workout_c2import_view,name='workout_c2import_view'), - re_path(r'^workout/c2list/(?P\d+)/user/(?P\d+)/$',views.workout_c2import_view,name='workout_c2import_view'), - re_path(r'^workout/rp3import/$',views.workout_rp3import_view,name='workout_rp3import_view'), - re_path(r'^workout/rp3import/user/(?P\d+)/$',views.workout_rp3import_view,name='workout_rp3import_view'), - re_path(r'^workout/stravaimport/$',views.workout_stravaimport_view,name='workout_stravaimport_view'), - re_path(r'^workout/stravaimport/user/(?P\d+)/$',views.workout_stravaimport_view,name='workout_stravaimport_view'), - re_path(r'^workout/c2import/all/$',views.workout_getc2workout_all,name='workout_getc2workout_all'), - re_path(r'^workout/c2import/all/(?P\d+)/$',views.workout_getc2workout_all,name='workout_getc2workout_all'), - re_path(r'^workout/nkimport/$',views.workout_nkimport_view,name='workout_nkimport_view'), - re_path(r'^workout/nkimport/(?P\d+)/(?P\d+)/$',views.workout_nkimport_view,name='workout_nkimport_view'), - re_path(r'^workout/nkimport/user/(?P\d+)/$',views.workout_nkimport_view,name='workout_nkimport_view'), - re_path(r'^workout/nkimport/all/$',views.workout_getnkworkout_all,name='workout_getnkworkout_all'), - re_path(r'^workout/nkimport/all/(?P\d+-\d+-\d+)/(?P\d+-\d+-\d+)/$',views.workout_getnkworkout_all, + re_path(r'^strava/webhooks/', views.strava_webhook_view, + name='strava_webhook_view'), + re_path(r'^garmin/summaries/', views.garmin_summaries_view, + name='garmin_summaries_view'), + re_path(r'^garmin/files/', views.garmin_newfiles_ping, + name='garmin_newfiles_ping'), + re_path(r'^garmin/activities/', views.garmin_details_view, + name='garmin_details_view'), + re_path(r'^garmin/deregistration/', views.garmin_deregistration_view, + name='garmin_deregistration_view'), + re_path(r'^workout/(?P\b[0-9A-Fa-f]+\b)/smoothenpace/$', + views.workout_smoothenpace_view, name='workout_smoothenpace_view'), + re_path(r'^workout/(?P\b[0-9A-Fa-f]+\b)/undosmoothenpace/$', + views.workout_undo_smoothenpace_view, name='workout_undo_smoothenpace_view'), + re_path(r'^workout/c2import/$', views.workout_c2import_view, + name='workout_c2import_view'), + re_path(r'^workout/c2list/$', views.workout_c2import_view, + name='workout_c2import_view'), + re_path(r'^workout/c2list/(?P\d+)/$', + views.workout_c2import_view, name='workout_c2import_view'), + re_path(r'^workout/c2list/user/(?P\d+)/$', + views.workout_c2import_view, name='workout_c2import_view'), + re_path(r'^workout/c2list/(?P\d+)/user/(?P\d+)/$', + views.workout_c2import_view, name='workout_c2import_view'), + re_path(r'^workout/rp3import/$', views.workout_rp3import_view, + name='workout_rp3import_view'), + re_path(r'^workout/rp3import/user/(?P\d+)/$', + views.workout_rp3import_view, name='workout_rp3import_view'), + re_path(r'^workout/stravaimport/$', views.workout_stravaimport_view, + name='workout_stravaimport_view'), + re_path(r'^workout/stravaimport/user/(?P\d+)/$', + views.workout_stravaimport_view, name='workout_stravaimport_view'), + re_path(r'^workout/c2import/all/$', views.workout_getc2workout_all, + name='workout_getc2workout_all'), + re_path(r'^workout/c2import/all/(?P\d+)/$', + views.workout_getc2workout_all, name='workout_getc2workout_all'), + re_path(r'^workout/nkimport/$', views.workout_nkimport_view, + name='workout_nkimport_view'), + re_path(r'^workout/nkimport/(?P\d+)/(?P\d+)/$', + views.workout_nkimport_view, name='workout_nkimport_view'), + re_path(r'^workout/nkimport/user/(?P\d+)/$', + views.workout_nkimport_view, name='workout_nkimport_view'), + re_path(r'^workout/nkimport/all/$', views.workout_getnkworkout_all, name='workout_getnkworkout_all'), - re_path(r'^workout/rp3import/(?P\d+)/$',views.workout_getrp3importview, + re_path(r'^workout/nkimport/all/(?P\d+-\d+-\d+)/(?P\d+-\d+-\d+)/$', views.workout_getnkworkout_all, + name='workout_getnkworkout_all'), + re_path(r'^workout/rp3import/(?P\d+)/$', views.workout_getrp3importview, name='workout_getrp3importview'), - re_path(r'^workout/rp3import/user/(?P\d+)/$',views.workout_rp3import_view,name='workout_rp3import_view'), - re_path(r'^workout/rp3import/all/$',views.workout_getrp3workout_all,name='workout_getrp3workout_all'), - re_path(r'^workout/(?P\w+.*)import/(?P\d+)/$',views.workout_getimportview,name='workout_getimportview'), - re_path(r'^workout/(?P\w+.*)import/(?P\d+)/async/$',views.workout_getimportview,{'do_async':True},name='workout_getimportview'), -# re_path(r'^workout/stravaimport/all/$',views.workout_getstravaworkout_all,name='workout_getstravaworkout_all'), - re_path(r'^workout/stravaimport/next/$',views.workout_getstravaworkout_next,name='workout_getstravaworkout_next'), - re_path(r'^workout/sporttracksimport/$',views.workout_sporttracksimport_view,name='workout_sporttracksimport_view'), - re_path(r'^workout/sporttracksimport/user/(?P\d+)/$',views.workout_sporttracksimport_view,name='workout_sporttracksimport_view'), - re_path(r'^workout/sporttracksimport/all/$',views.workout_getsporttracksworkout_all,name='workout_getsporttracksworkout_all'), - re_path(r'^workout/polarimport/$',views.workout_polarimport_view,name='workout_polarimport_view'), - re_path(r'^workout/polarimport/user/(?P\d+)/',views.workout_polarimport_view,name='workout_polarimport_view'), - re_path(r'^workout/(?P\b[0-9A-Fa-f]+\b)/c2uploadw/$',views.workout_c2_upload_view,name='workout_c2_upload_view'), - re_path(r'^workout/(?P\b[0-9A-Fa-f]+\b)/stravauploadw/$',views.workout_strava_upload_view,name='workout_strava_upload_view'), - re_path(r'^workout/(?P\b[0-9A-Fa-f]+\b)/recalcsummary/$',views.workout_recalcsummary_view,name='workout_recalcsummary_view'), - re_path(r'^workout/(?P\b[0-9A-Fa-f]+\b)/sporttracksuploadw/$',views.workout_sporttracks_upload_view,name='workout_sporttracks_upload_view'), - re_path(r'^workout/(?P\b[0-9A-Fa-f]+\b)/tpuploadw/$',views.workout_tp_upload_view,name='workout_tp_upload_view'), - re_path(r'^alerts/user/(?P\d+)/$',views.alerts_view,name='alerts_view'), - re_path(r'^alerts/$',views.alerts_view,name='alerts_view'), - re_path(r'^alerts/(?P\d+)/delete/$',views.AlertDelete.as_view(),name='alert_delete_view'), - re_path(r'^alerts/(?P\d+)/edit/user/(?P\d+)/$',views.alert_edit_view,name='alert_edit_view'), - re_path(r'^alerts/(?P\d+)/edit/$',views.alert_edit_view,name='alert_edit_view'), - re_path(r'^alerts/new/user/(?P\d+)/$',views.alert_create_view, name='alert_create_view'), - re_path(r'^alerts/new/$',views.alert_create_view, name='alert_create_view'), - re_path(r'^alerts/(?P\d+)/report/user/(?P\d+)/$',views.alert_report_view,name='alert_report_view'), - re_path(r'^alerts/(?P\d+)/report/(?P\d+)/user/(?P\d+)/$',views.alert_report_view,name='alert_report_view'), - re_path(r'^alerts/(?P\d+)/report/$',views.alert_report_view,name='alert_report_view'), - re_path(r'^me/deactivate/$',views.deactivate_user,name='deactivate_user'), - re_path(r'^me/delete/$',views.remove_user,name='remove_user'), - re_path(r'^survey/$',views.survey,name='survey'), - re_path(r'^me/gdpr-optin-confirm/?/$',views.user_gdpr_confirm,name='user_gdpr_confirm'), - re_path(r'^me/gdpr-optin-confirm/$',views.user_gdpr_confirm,name='user_gdpr_confirm'), - re_path(r'^me/gdpr-optin/?/$',views.user_gdpr_optin,name='user_gdpr_optin'), - re_path(r'^me/gdpr-optin/$',views.user_gdpr_optin,name='user_gdpr_optin'), - re_path(r'^me/teams/$',views.rower_teams_view,name='rower_teams_view'), - re_path(r'^me/calcdps/$',views.rower_calcdps_view,name='rower_calcdps_view'), - re_path(r'^me/exportsettings/$',views.rower_exportsettings_view,name='rower_exportsettings_view'), - re_path(r'^me/exportsettings/user/(?P\d+)/$',views.rower_exportsettings_view,name='rower_exportsettings_view'), - re_path(r'^team/(?P\d+)/$',views.team_view,name='team_view'), - re_path(r'^team/(?P\d+)/memberstats/$',views.team_members_stats_view,name='team_members_stats_view'), - re_path(r'^team/(?P\d+)/edit/$',views.team_edit_view,name='team_edit_view'), - re_path(r'^team/(?P\d+)/leaveconfirm/$',views.team_leaveconfirm_view,name='team_leaveconfirm_view'), - re_path(r'^team/(?P\d+)/leave/$',views.team_leave_view,name='team_leave_view'), - re_path(r'^team/(?P\d+)/deleteconfirm/$',views.team_deleteconfirm_view,name='team_deleteconfirm_view'), - re_path(r'^team/(?P\d+)/requestmembership/(?P\d+)/$',views.team_requestmembership_view,name='team_requestmembership_view'), - re_path(r'^me/coachrequest/(?P\d+)/reject/$',views.reject_revoke_coach_request, - name='reject_revoke_coach_request'), - re_path(r'^coaches/(?P\d+)/dropconfirm/$',views.coach_drop_athlete_confirm_view, - name='coach_drop_athlete_confirm_view'), - re_path(r'^coaches/(?P\d+)/drop/$',views.coach_drop_athlete_view, - name='coach_drop_athlete_view'), - re_path(r'^coaches/(?P\d+)/dropcoachconfirm/$',views.athlete_drop_coach_confirm_view, - name='athlete_drop_coach_confirm_view'), - re_path(r'^coaches/(?P\d+)/dropcoach/$',views.athlete_drop_coach_view, - name='athlete_drop_coach_view'), - re_path(r'^me/coachrequest/(?P\d+)/revoke/$',views.reject_revoke_coach_request, - name='reject_revoke_coach_request'), - re_path(r'^me/coachoffer/(?P\d+)/reject/$',views.reject_revoke_coach_offer, - name='reject_revoke_coach_offer'), - re_path(r'^me/coachoffer/(?P\d+)/revoke/$',views.reject_revoke_coach_offer, - name='reject_revoke_coach_offer'), - re_path(r'^me/coachrequest/(?P\d+)/$',views.request_coaching_view, - name='request_coaching_view'), - re_path(r'^me/coachoffer/(?P\d+)/$',views.offer_coaching_view, - name='offer_coaching_view'), - re_path(r'^me/coachrequest/(?P\w+.*)/accept/$',views.coach_accept_coachrequest_view, - name='coach_accept_coachrequest_view'), - re_path(r'^me/coachoffer/(?P\w+.*)/accept/$',views.rower_accept_coachoffer_view, - name='rower_accept_coachoffer_view'), - re_path(r'^team/(?P\d+)/delete/$',views.team_delete_view,name='team_delete_view'), - re_path(r'^team/create/$',views.team_create_view,name='team_create_view'), - re_path(r'^me/team/(?P\d+)/drop/(?P\d+)/$',views.manager_member_drop_view,name='manager_member_drop_view'), - re_path(r'^me/invitation/(?P\d+)/reject/$',views.invitation_reject_view,name='invitation_reject_view'), - re_path(r'^me/invitation/(?P\d+)/revoke/$',views.invitation_revoke_view,name='invitation_revoke_view'), - re_path(r'^me/invitation/$',views.rower_invitations_view,name='rower_invitations_view'), - re_path(r'^me/raise500/$',views.raise_500,name='raise_500'), - re_path(r'^me/invitation/(\w+.*)/$',views.rower_invitations_view,name='rower_invitations_view'), - re_path(r'^me/request/(?P\d+)/revoke/$',views.request_revoke_view,name='request_revoke_view'), - re_path(r'^me/request/(?P\d+)/reject/$',views.request_reject_view,name='request_reject_view'), - re_path(r'^me/request/(\w+.*)/$',views.manager_requests_view,name='manager_requests_view'), - re_path(r'^me/request/$',views.manager_requests_view,name='manager_requests_view'), - re_path(r'^me/edit/$',views.rower_edit_view,name='rower_edit_view'), - re_path(r'^me/edit/user/(?P\d+)/$',views.rower_edit_view,name='rower_edit_view'), - re_path(r'^me/preferences/$',views.rower_prefs_view,name='rower_prefs_view'), - re_path(r'^me/transactions/$',views.transactions_view,name='transactions_view'), - re_path(r'^me/preferences/user/(?P\d+)/$',views.rower_prefs_view,name='rower_prefs_view'), - re_path(r'^me/edit/(.+.*)/$',views.rower_edit_view,name='rower_edit_view'), - re_path(r'^me/c2authorize/$',views.rower_c2_authorize,name='rower_c2_authorize'), - re_path(r'^me/nkauthorize/$',views.rower_nk_authorize,name='rower_nk_authorize'), - re_path(r'^me/polarauthorize/$',views.rower_polar_authorize,name='rower_polar_authorize'), - re_path(r'^me/revokeapp/(?P\d+)/$',views.rower_revokeapp_view,name='rower_revokeapp_view'), - re_path(r'^me/stravaauthorize/$',views.rower_strava_authorize,name='rower_strava_authorize'), - re_path(r'^me/garminauthorize/$',views.rower_garmin_authorize,name='rower_garmin_authorize'), - re_path(r'^me/sporttracksauthorize/$',views.rower_sporttracks_authorize,name='rower_sporttracks_authorize'), - re_path(r'^me/tpauthorize/$',views.rower_tp_authorize,name='rower_tp_authorize'), - re_path(r'^me/rp3authorize/$',views.rower_rp3_authorize,name='rower_rp3_authorize'), - re_path(r'^me/sporttracksrefresh/$',views.rower_sporttracks_token_refresh,name='rower_sporttracks_token_refresh'), - re_path(r'^me/tprefresh/$',views.rower_tp_token_refresh,name='rower_tp_token_refresh'), - re_path(r'^me/c2refresh/$',views.rower_c2_token_refresh,name='rower_c2_token_refresh'), - re_path(r'^me/favoritecharts/$',views.rower_favoritecharts_view,name='rower_favoritecharts_view'), - re_path(r'^me/favoritecharts/user/(?P\d+)/$',views.rower_favoritecharts_view,name='rower_favoritecharts_view'), -# re_path(r'^me/workflowconfig/$',views.workout_workflow_config_view), - re_path(r'^me/workflowconfig2/$',views.workout_workflow_config2_view,name='workout_workflow_config2_view'), - re_path(r'^me/workflowconfig2/user/(?P\d+)/$',views.workout_workflow_config2_view,name='workout_workflow_config2_view'), - re_path(r'^me/workflowdefault/$',views.workflow_default_view,name='workflow_default_view'), - re_path(r'^email/send/$', views.sendmail,name='sendmail'), - re_path(r'^email/thankyou/$', TemplateView.as_view(template_name='thankyou.html'), name='thankyou'), + re_path(r'^workout/rp3import/user/(?P\d+)/$', + views.workout_rp3import_view, name='workout_rp3import_view'), + re_path(r'^workout/rp3import/all/$', views.workout_getrp3workout_all, + name='workout_getrp3workout_all'), + re_path(r'^workout/(?P\w+.*)import/(?P\d+)/$', + views.workout_getimportview, name='workout_getimportview'), + re_path(r'^workout/(?P\w+.*)import/(?P\d+)/async/$', + views.workout_getimportview, {'do_async': True}, name='workout_getimportview'), + # re_path(r'^workout/stravaimport/all/$',views.workout_getstravaworkout_all,name='workout_getstravaworkout_all'), + re_path(r'^workout/stravaimport/next/$', views.workout_getstravaworkout_next, + name='workout_getstravaworkout_next'), + re_path(r'^workout/sporttracksimport/$', views.workout_sporttracksimport_view, + name='workout_sporttracksimport_view'), + re_path(r'^workout/sporttracksimport/user/(?P\d+)/$', + views.workout_sporttracksimport_view, name='workout_sporttracksimport_view'), + re_path(r'^workout/sporttracksimport/all/$', views.workout_getsporttracksworkout_all, + name='workout_getsporttracksworkout_all'), + re_path(r'^workout/polarimport/$', views.workout_polarimport_view, + name='workout_polarimport_view'), + re_path(r'^workout/polarimport/user/(?P\d+)/', + views.workout_polarimport_view, name='workout_polarimport_view'), + re_path(r'^workout/(?P\b[0-9A-Fa-f]+\b)/c2uploadw/$', + views.workout_c2_upload_view, name='workout_c2_upload_view'), + re_path(r'^workout/(?P\b[0-9A-Fa-f]+\b)/stravauploadw/$', + views.workout_strava_upload_view, name='workout_strava_upload_view'), + re_path(r'^workout/(?P\b[0-9A-Fa-f]+\b)/recalcsummary/$', + views.workout_recalcsummary_view, name='workout_recalcsummary_view'), + re_path(r'^workout/(?P\b[0-9A-Fa-f]+\b)/sporttracksuploadw/$', + views.workout_sporttracks_upload_view, name='workout_sporttracks_upload_view'), + re_path(r'^workout/(?P\b[0-9A-Fa-f]+\b)/tpuploadw/$', + views.workout_tp_upload_view, name='workout_tp_upload_view'), + re_path(r'^alerts/user/(?P\d+)/$', + views.alerts_view, name='alerts_view'), + re_path(r'^alerts/$', views.alerts_view, name='alerts_view'), + re_path(r'^alerts/(?P\d+)/delete/$', + views.AlertDelete.as_view(), name='alert_delete_view'), + re_path(r'^alerts/(?P\d+)/edit/user/(?P\d+)/$', + views.alert_edit_view, name='alert_edit_view'), + re_path(r'^alerts/(?P\d+)/edit/$', + views.alert_edit_view, name='alert_edit_view'), + re_path(r'^alerts/new/user/(?P\d+)/$', + views.alert_create_view, name='alert_create_view'), + re_path(r'^alerts/new/$', views.alert_create_view, + name='alert_create_view'), + re_path(r'^alerts/(?P\d+)/report/user/(?P\d+)/$', + views.alert_report_view, name='alert_report_view'), + re_path(r'^alerts/(?P\d+)/report/(?P\d+)/user/(?P\d+)/$', + views.alert_report_view, name='alert_report_view'), + re_path(r'^alerts/(?P\d+)/report/$', + views.alert_report_view, name='alert_report_view'), + re_path(r'^me/deactivate/$', views.deactivate_user, name='deactivate_user'), + re_path(r'^me/delete/$', views.remove_user, name='remove_user'), + re_path(r'^survey/$', views.survey, name='survey'), + re_path(r'^me/gdpr-optin-confirm/?/$', + views.user_gdpr_confirm, name='user_gdpr_confirm'), + re_path(r'^me/gdpr-optin-confirm/$', + views.user_gdpr_confirm, name='user_gdpr_confirm'), + re_path(r'^me/gdpr-optin/?/$', views.user_gdpr_optin, + name='user_gdpr_optin'), + re_path(r'^me/gdpr-optin/$', views.user_gdpr_optin, name='user_gdpr_optin'), + re_path(r'^me/teams/$', views.rower_teams_view, name='rower_teams_view'), + re_path(r'^me/calcdps/$', views.rower_calcdps_view, + name='rower_calcdps_view'), + re_path(r'^me/exportsettings/$', views.rower_exportsettings_view, + name='rower_exportsettings_view'), + re_path(r'^me/exportsettings/user/(?P\d+)/$', + views.rower_exportsettings_view, name='rower_exportsettings_view'), + re_path(r'^team/(?P\d+)/$', views.team_view, name='team_view'), + re_path(r'^team/(?P\d+)/memberstats/$', + views.team_members_stats_view, name='team_members_stats_view'), + re_path(r'^team/(?P\d+)/edit/$', + views.team_edit_view, name='team_edit_view'), + re_path(r'^team/(?P\d+)/leaveconfirm/$', + views.team_leaveconfirm_view, name='team_leaveconfirm_view'), + re_path(r'^team/(?P\d+)/leave/$', + views.team_leave_view, name='team_leave_view'), + re_path(r'^team/(?P\d+)/deleteconfirm/$', + views.team_deleteconfirm_view, name='team_deleteconfirm_view'), + re_path(r'^team/(?P\d+)/requestmembership/(?P\d+)/$', + views.team_requestmembership_view, name='team_requestmembership_view'), + re_path(r'^me/coachrequest/(?P\d+)/reject/$', views.reject_revoke_coach_request, + name='reject_revoke_coach_request'), + re_path(r'^coaches/(?P\d+)/dropconfirm/$', views.coach_drop_athlete_confirm_view, + name='coach_drop_athlete_confirm_view'), + re_path(r'^coaches/(?P\d+)/drop/$', views.coach_drop_athlete_view, + name='coach_drop_athlete_view'), + re_path(r'^coaches/(?P\d+)/dropcoachconfirm/$', views.athlete_drop_coach_confirm_view, + name='athlete_drop_coach_confirm_view'), + re_path(r'^coaches/(?P\d+)/dropcoach/$', views.athlete_drop_coach_view, + name='athlete_drop_coach_view'), + re_path(r'^me/coachrequest/(?P\d+)/revoke/$', views.reject_revoke_coach_request, + name='reject_revoke_coach_request'), + re_path(r'^me/coachoffer/(?P\d+)/reject/$', views.reject_revoke_coach_offer, + name='reject_revoke_coach_offer'), + re_path(r'^me/coachoffer/(?P\d+)/revoke/$', views.reject_revoke_coach_offer, + name='reject_revoke_coach_offer'), + re_path(r'^me/coachrequest/(?P\d+)/$', views.request_coaching_view, + name='request_coaching_view'), + re_path(r'^me/coachoffer/(?P\d+)/$', views.offer_coaching_view, + name='offer_coaching_view'), + re_path(r'^me/coachrequest/(?P\w+.*)/accept/$', views.coach_accept_coachrequest_view, + name='coach_accept_coachrequest_view'), + re_path(r'^me/coachoffer/(?P\w+.*)/accept/$', views.rower_accept_coachoffer_view, + name='rower_accept_coachoffer_view'), + re_path(r'^team/(?P\d+)/delete/$', + views.team_delete_view, name='team_delete_view'), + re_path(r'^team/create/$', views.team_create_view, name='team_create_view'), + re_path(r'^me/team/(?P\d+)/drop/(?P\d+)/$', + views.manager_member_drop_view, name='manager_member_drop_view'), + re_path(r'^me/invitation/(?P\d+)/reject/$', + views.invitation_reject_view, name='invitation_reject_view'), + re_path(r'^me/invitation/(?P\d+)/revoke/$', + views.invitation_revoke_view, name='invitation_revoke_view'), + re_path(r'^me/invitation/$', views.rower_invitations_view, + name='rower_invitations_view'), + re_path(r'^me/raise500/$', views.raise_500, name='raise_500'), + re_path(r'^me/invitation/(\w+.*)/$', views.rower_invitations_view, + name='rower_invitations_view'), + re_path(r'^me/request/(?P\d+)/revoke/$', + views.request_revoke_view, name='request_revoke_view'), + re_path(r'^me/request/(?P\d+)/reject/$', + views.request_reject_view, name='request_reject_view'), + re_path(r'^me/request/(\w+.*)/$', views.manager_requests_view, + name='manager_requests_view'), + re_path(r'^me/request/$', views.manager_requests_view, + name='manager_requests_view'), + re_path(r'^me/edit/$', views.rower_edit_view, name='rower_edit_view'), + re_path(r'^me/edit/user/(?P\d+)/$', + views.rower_edit_view, name='rower_edit_view'), + re_path(r'^me/preferences/$', views.rower_prefs_view, + name='rower_prefs_view'), + re_path(r'^me/transactions/$', views.transactions_view, + name='transactions_view'), + re_path(r'^me/preferences/user/(?P\d+)/$', + views.rower_prefs_view, name='rower_prefs_view'), + re_path(r'^me/edit/(.+.*)/$', views.rower_edit_view, name='rower_edit_view'), + re_path(r'^me/c2authorize/$', views.rower_c2_authorize, + name='rower_c2_authorize'), + re_path(r'^me/nkauthorize/$', views.rower_nk_authorize, + name='rower_nk_authorize'), + re_path(r'^me/polarauthorize/$', views.rower_polar_authorize, + name='rower_polar_authorize'), + re_path(r'^me/revokeapp/(?P\d+)/$', + views.rower_revokeapp_view, name='rower_revokeapp_view'), + re_path(r'^me/stravaauthorize/$', views.rower_strava_authorize, + name='rower_strava_authorize'), + re_path(r'^me/garminauthorize/$', views.rower_garmin_authorize, + name='rower_garmin_authorize'), + re_path(r'^me/sporttracksauthorize/$', views.rower_sporttracks_authorize, + name='rower_sporttracks_authorize'), + re_path(r'^me/tpauthorize/$', views.rower_tp_authorize, + name='rower_tp_authorize'), + re_path(r'^me/rp3authorize/$', views.rower_rp3_authorize, + name='rower_rp3_authorize'), + re_path(r'^me/sporttracksrefresh/$', views.rower_sporttracks_token_refresh, + name='rower_sporttracks_token_refresh'), + re_path(r'^me/tprefresh/$', views.rower_tp_token_refresh, + name='rower_tp_token_refresh'), + re_path(r'^me/c2refresh/$', views.rower_c2_token_refresh, + name='rower_c2_token_refresh'), + re_path(r'^me/favoritecharts/$', views.rower_favoritecharts_view, + name='rower_favoritecharts_view'), + re_path(r'^me/favoritecharts/user/(?P\d+)/$', + views.rower_favoritecharts_view, name='rower_favoritecharts_view'), + # re_path(r'^me/workflowconfig/$',views.workout_workflow_config_view), + re_path(r'^me/workflowconfig2/$', views.workout_workflow_config2_view, + name='workout_workflow_config2_view'), + re_path(r'^me/workflowconfig2/user/(?P\d+)/$', + views.workout_workflow_config2_view, name='workout_workflow_config2_view'), + re_path(r'^me/workflowdefault/$', views.workflow_default_view, + name='workflow_default_view'), + re_path(r'^email/send/$', views.sendmail, name='sendmail'), + re_path(r'^email/thankyou/$', + TemplateView.as_view(template_name='thankyou.html'), name='thankyou'), re_path(r'^email/$', TemplateView.as_view(template_name='email.html'), name='email'), - re_path(r'^about', TemplateView.as_view(template_name='about_us.html'),name='about'), - re_path(r'^brochure/$',TemplateView.as_view(template_name='brochure.html'), - name='brochure'), - re_path(r'^developers', TemplateView.as_view(template_name='developers.html'),name='about'), - re_path(r'^analysis/user/(?P\d+)/$', views.analysis_view,name='analysis'), - re_path(r'^laboratory/$', views.laboratory_view,name='laboratory_view'), - re_path(r'^laboratory/user/(?P\d+)/$', views.laboratory_view,name='laboratory_view'), - re_path(r'^errormessage/(?P[\w\ ]+.*)/$',views.errormessage_view,name='errormessage_view'), - re_path(r'^analysis/$', views.analysis_view,name='analysis'), - re_path(r'^promembership', TemplateView.as_view(template_name='promembership.html'),name='promembership'), - re_path(r'^checkout/(?P\d+)/$',views.payment_confirm_view,name='payment_confirm_view'), - re_path(r'^upgradecheckout/(?P\d+)/$',views.upgrade_confirm_view,name='upgrade_confirm_view'), - re_path(r'^upgradecheckout/(?P\d+)/$',views.upgrade_confirm_view,name='upgrade_confirm_view'), - re_path(r'^downgradecheckout/(?P\d+)/$',views.downgrade_confirm_view,name='downgrade_confirm_view'), - re_path(r'^billing/$',views.billing_view,name='billing'), - re_path(r'^upgrade/$',views.upgrade_view,name='upgrade'), - re_path(r'^downgrade/$',views.downgrade_view,name='downgrade'), - re_path(r'^paymentcompleted/$',views.payment_completed_view,name='payment_completed_view'), - re_path(r'^downgradecompleted/$',views.downgrade_completed_view,name='downgrade_completed_view'), - re_path(r'^paidplans/$',views.paidplans_view,name='paidplans_view'), - re_path(r'^me/cancelsubscriptions/$',views.plan_stop_view,name='plan_stop_view'), - re_path(r'^me/cancelsubscription/(?P[\w\ ]+.*)/$',views.plan_tobasic_view,name='plan_tobasic_view'), - re_path(r'^checkouts/$',views.checkouts_view,name='checkouts'), - re_path(r'^upgradecheckouts/$',views.upgrade_checkouts_view,name='upgrade_checkouts'), - re_path(r'^downgradecheckouts/$',views.downgrade_checkouts_view,name='downgrade_checkouts'), - re_path(r'^purchasecheckouts/$',views.purchase_checkouts_view,name='purchase_checkouts_view'), - re_path(r'^planrequired/',views.planrequired_view,name='planrequired_view'), - re_path(r'^starttrial/$',views.start_trial_view,name='start_trial_view'), - re_path(r'^startplantrial/$',views.start_plantrial_view,name='start_plantrial_view'), - re_path(r'^legal', TemplateView.as_view(template_name='legal.html'),name='legal'), - re_path(r'^register/$',views.rower_register_view,name='rower_register_view'), - re_path(r'^coachregister/$',views.freecoach_register_view,name='freecoach_register_view'), - path('activate///',views.useractivate, name='useractivate'), - re_path(r'^register/thankyou/$', TemplateView.as_view(template_name='registerthankyou.html'), name='registerthankyou'), - re_path(r'^workout/(?P\b[0-9A-Fa-f]+\b)/workflow/$',views.workout_workflow_view, - name='workout_workflow_view'), - re_path(r'^workout/(?P\b[0-9A-Fa-f]+\b)/courses/$',views.workout_course_view, - name='workout_course_view'), - re_path(r'^workout/(?P\b[0-9A-Fa-f]+\b)/flexchart/(?P[\w\ ]+.*)/(?P[\w\ ]+.*)/(?P[\w\ ]+.*)/(?P\w+)/$',views.workout_flexchart3_view,name='workout_flexchart3_view'), - re_path(r'^workout/(?P\b[0-9A-Fa-f]+\b)/flexchart/(?P\w+.*)/(?P[\w\ ]+.*)/(?P[\w\ ]+.*)/(?P\w+.*)/$',views.workout_flexchart3_view,name='workout_flexchart3_view'), - re_path(r'^workout/(?P\b[0-9A-Fa-f]+\b)/flexchart/(?P\w+.*)/(?P[\w\ ]+.*)/(?P[\w\ ]+.*)/$',views.workout_flexchart3_view,name='workout_flexchart3_view'), - re_path(r'^workout/(?P\b[0-9A-Fa-f]+\b)/flexchart/$',views.workout_flexchart3_view,name='workout_flexchart3_view'), - re_path(r'^workout/(?P\b[0-9A-Fa-f]+\b)/flexchartstacked/$',views.workout_flexchart_stacked_view,name='workout_flexchart_stacked_view'), - re_path(r'^test\_callback',views.rower_process_testcallback,name='rower_process_testcallback'), - re_path(r'^createplan/$',views.rower_create_trainingplan,name='rower_create_trainingplan'), - re_path(r'^createplan/user/(?P\d+)/$',views.rower_create_trainingplan,name='rower_create_trainingplan'), - re_path(r'^plans/$', views.rower_select_instantplan, name='rower_select_instantplan'), + re_path(r'^about', TemplateView.as_view( + template_name='about_us.html'), name='about'), + re_path(r'^brochure/$', TemplateView.as_view(template_name='brochure.html'), + name='brochure'), + re_path(r'^developers', TemplateView.as_view( + template_name='developers.html'), name='about'), + re_path(r'^analysis/user/(?P\d+)/$', + views.analysis_view, name='analysis'), + re_path(r'^laboratory/$', views.laboratory_view, name='laboratory_view'), + re_path(r'^laboratory/user/(?P\d+)/$', + views.laboratory_view, name='laboratory_view'), + re_path(r'^errormessage/(?P[\w\ ]+.*)/$', + views.errormessage_view, name='errormessage_view'), + re_path(r'^analysis/$', views.analysis_view, name='analysis'), + re_path(r'^promembership', TemplateView.as_view( + template_name='promembership.html'), name='promembership'), + re_path(r'^checkout/(?P\d+)/$', + views.payment_confirm_view, name='payment_confirm_view'), + re_path(r'^upgradecheckout/(?P\d+)/$', + views.upgrade_confirm_view, name='upgrade_confirm_view'), + re_path(r'^upgradecheckout/(?P\d+)/$', + views.upgrade_confirm_view, name='upgrade_confirm_view'), + re_path(r'^downgradecheckout/(?P\d+)/$', + views.downgrade_confirm_view, name='downgrade_confirm_view'), + re_path(r'^billing/$', views.billing_view, name='billing'), + re_path(r'^upgrade/$', views.upgrade_view, name='upgrade'), + re_path(r'^downgrade/$', views.downgrade_view, name='downgrade'), + re_path(r'^paymentcompleted/$', views.payment_completed_view, + name='payment_completed_view'), + re_path(r'^downgradecompleted/$', views.downgrade_completed_view, + name='downgrade_completed_view'), + re_path(r'^paidplans/$', views.paidplans_view, name='paidplans_view'), + re_path(r'^me/cancelsubscriptions/$', + views.plan_stop_view, name='plan_stop_view'), + re_path(r'^me/cancelsubscription/(?P[\w\ ]+.*)/$', + views.plan_tobasic_view, name='plan_tobasic_view'), + re_path(r'^checkouts/$', views.checkouts_view, name='checkouts'), + re_path(r'^upgradecheckouts/$', views.upgrade_checkouts_view, + name='upgrade_checkouts'), + re_path(r'^downgradecheckouts/$', views.downgrade_checkouts_view, + name='downgrade_checkouts'), + re_path(r'^purchasecheckouts/$', views.purchase_checkouts_view, + name='purchase_checkouts_view'), + re_path(r'^planrequired/', views.planrequired_view, + name='planrequired_view'), + re_path(r'^starttrial/$', views.start_trial_view, name='start_trial_view'), + re_path(r'^startplantrial/$', views.start_plantrial_view, + name='start_plantrial_view'), + re_path(r'^legal', TemplateView.as_view( + template_name='legal.html'), name='legal'), + re_path(r'^register/$', views.rower_register_view, + name='rower_register_view'), + re_path(r'^coachregister/$', views.freecoach_register_view, + name='freecoach_register_view'), + path('activate///', views.useractivate, name='useractivate'), + re_path(r'^register/thankyou/$', TemplateView.as_view( + template_name='registerthankyou.html'), name='registerthankyou'), + re_path(r'^workout/(?P\b[0-9A-Fa-f]+\b)/workflow/$', views.workout_workflow_view, + name='workout_workflow_view'), + re_path(r'^workout/(?P\b[0-9A-Fa-f]+\b)/courses/$', views.workout_course_view, + name='workout_course_view'), + re_path(r'^workout/(?P\b[0-9A-Fa-f]+\b)/flexchart/(?P[\w\ ]+.*)/(?P[\w\ ]+.*)/(?P[\w\ ]+.*)/(?P\w+)/$', + views.workout_flexchart3_view, name='workout_flexchart3_view'), + re_path(r'^workout/(?P\b[0-9A-Fa-f]+\b)/flexchart/(?P\w+.*)/(?P[\w\ ]+.*)/(?P[\w\ ]+.*)/(?P\w+.*)/$', + views.workout_flexchart3_view, name='workout_flexchart3_view'), + re_path(r'^workout/(?P\b[0-9A-Fa-f]+\b)/flexchart/(?P\w+.*)/(?P[\w\ ]+.*)/(?P[\w\ ]+.*)/$', + views.workout_flexchart3_view, name='workout_flexchart3_view'), + re_path(r'^workout/(?P\b[0-9A-Fa-f]+\b)/flexchart/$', + views.workout_flexchart3_view, name='workout_flexchart3_view'), + re_path(r'^workout/(?P\b[0-9A-Fa-f]+\b)/flexchartstacked/$', + views.workout_flexchart_stacked_view, name='workout_flexchart_stacked_view'), + re_path(r'^test\_callback', views.rower_process_testcallback, + name='rower_process_testcallback'), + re_path(r'^createplan/$', views.rower_create_trainingplan, + name='rower_create_trainingplan'), + re_path(r'^createplan/user/(?P\d+)/$', + views.rower_create_trainingplan, name='rower_create_trainingplan'), + re_path(r'^plans/$', views.rower_select_instantplan, + name='rower_select_instantplan'), re_path(r'^plans/(?P[0-9a-fA-F]{8}\-[0-9a-fA-F]{4}\-[0-9a-fA-F]{4}\-[0-9a-fA-F]{4}\-[0-9a-fA-F]{12})/$', views.rower_view_instantplan, name='rower_view_instantplan'), - re_path(r'^buyplan/(?P\d+)/$',views.buy_trainingplan_view,name='buy_trainingplan_view'), - re_path(r'^confirmpurchaseplan/(?P\d+)/$',views.confirm_trainingplan_purchase_view,name='confirm_trainingplan_purchase_view'), - re_path(r'^addinstantplan/$', views.add_instantplan_view, name='add_instantplan_view'), - re_path(r'^deleteplan/(?P\d+)/$',login_required( - views.TrainingPlanDelete.as_view()),name='trainingplan_delete_view'), - re_path(r'^deletemicrocycle/(?P\d+)/$',login_required( - views.MicroCycleDelete.as_view()),name='microcycle_delete_view'), - re_path(r'^deletemesocycle/(?P\d+)/$',login_required( - views.MesoCycleDelete.as_view()),name='mesocycle_delete_view'), - re_path(r'^deletemacrocycle/(?P\d+)/$',login_required( - views.MacroCycleDelete.as_view()),name='macrocycle_delete_view'), -# re_path(r'^deleteplan/(?P\d+)/$',views.rower_delete_trainingplan), - re_path(r'^plan/(?P\d+)/$',views.rower_trainingplan_view, - name='rower_trainingplan_view'), - re_path(r'^plan/(?P\d+)/user/(?P\d+)/$',views.rower_trainingplan_view, - name='rower_trainingplan_view'), - re_path(r'^plan/(?P\d+)/micro/(?P\d+)/$',views.rower_trainingplan_view, - name='rower_trainingplan_view'), - re_path(r'^plan/(?P\d+)/micro/(?P\d+)/user/(?P\d+)/$',views.rower_trainingplan_view, - name='rower_trainingplan_view'), - re_path(r'^plan/(?P\d+)/meso/(?P\d+)/$',views.rower_trainingplan_view, - name='rower_trainingplan_view'), - re_path(r'^plan/(?P\d+)/meso/(?P\d+)/user/(?P\d+)/$',views.rower_trainingplan_view, - name='rower_trainingplan_view'), - re_path(r'^plan/(?P\d+)/macro/(?P\d+)/$',views.rower_trainingplan_view, - name='rower_trainingplan_view'), - re_path(r'^plan/(?P\d+)/macro/(?P\d+)/user/(?P\d+)/$',views.rower_trainingplan_view, - name='rower_trainingplan_view'), - re_path(r'^plan/(?P\d+)/execution/$',views.rower_trainingplan_execution_view, + re_path(r'^buyplan/(?P\d+)/$', views.buy_trainingplan_view, + name='buy_trainingplan_view'), + re_path(r'^confirmpurchaseplan/(?P\d+)/$', + views.confirm_trainingplan_purchase_view, name='confirm_trainingplan_purchase_view'), + re_path(r'^addinstantplan/$', views.add_instantplan_view, + name='add_instantplan_view'), + re_path(r'^deleteplan/(?P\d+)/$', login_required( + views.TrainingPlanDelete.as_view()), name='trainingplan_delete_view'), + re_path(r'^deletemicrocycle/(?P\d+)/$', login_required( + views.MicroCycleDelete.as_view()), name='microcycle_delete_view'), + re_path(r'^deletemesocycle/(?P\d+)/$', login_required( + views.MesoCycleDelete.as_view()), name='mesocycle_delete_view'), + re_path(r'^deletemacrocycle/(?P\d+)/$', login_required( + views.MacroCycleDelete.as_view()), name='macrocycle_delete_view'), + # re_path(r'^deleteplan/(?P\d+)/$',views.rower_delete_trainingplan), + re_path(r'^plan/(?P\d+)/$', views.rower_trainingplan_view, + name='rower_trainingplan_view'), + re_path(r'^plan/(?P\d+)/user/(?P\d+)/$', views.rower_trainingplan_view, + name='rower_trainingplan_view'), + re_path(r'^plan/(?P\d+)/micro/(?P\d+)/$', views.rower_trainingplan_view, + name='rower_trainingplan_view'), + re_path(r'^plan/(?P\d+)/micro/(?P\d+)/user/(?P\d+)/$', views.rower_trainingplan_view, + name='rower_trainingplan_view'), + re_path(r'^plan/(?P\d+)/meso/(?P\d+)/$', views.rower_trainingplan_view, + name='rower_trainingplan_view'), + re_path(r'^plan/(?P\d+)/meso/(?P\d+)/user/(?P\d+)/$', views.rower_trainingplan_view, + name='rower_trainingplan_view'), + re_path(r'^plan/(?P\d+)/macro/(?P\d+)/$', views.rower_trainingplan_view, + name='rower_trainingplan_view'), + re_path(r'^plan/(?P\d+)/macro/(?P\d+)/user/(?P\d+)/$', views.rower_trainingplan_view, + name='rower_trainingplan_view'), + re_path(r'^plan/(?P\d+)/execution/$', views.rower_trainingplan_execution_view, name='rower_trainingplan_execution_view'), - re_path(r'^plan/(?P\d+)/execution/user/(?P\d+)/$',views.rower_trainingplan_execution_view, + re_path(r'^plan/(?P\d+)/execution/user/(?P\d+)/$', views.rower_trainingplan_execution_view, name='rower_trainingplan_execution_view'), - re_path(r'^macrocycle/(?P\d+)/$',login_required( + re_path(r'^macrocycle/(?P\d+)/$', login_required( views.TrainingMacroCycleUpdate.as_view()), name='macrocycle_update_view'), - re_path(r'^mesocycle/(?P\d+)/$',login_required( + re_path(r'^mesocycle/(?P\d+)/$', login_required( views.TrainingMesoCycleUpdate.as_view()), name='mesocycle_update_view'), - re_path(r'^macrocycle/(?P\d+)/planbymonths/$',login_required( + re_path(r'^macrocycle/(?P\d+)/planbymonths/$', login_required( views.planmacrocyclebymonth)), - re_path(r'^macrocycle/(?P\d+)/planbymonths/user/(?P\d+)/$',views.planmacrocyclebymonth), - re_path(r'^mesocycle/(?P\d+)/planbyweeks/$',views.planmesocyclebyweek), - re_path(r'^mesocycle/(?P\d+)/planbyweeks/user/(?P\d+)/$',views.planmesocyclebyweek), - re_path(r'^microcycle/(?P\d+)/$',login_required( + re_path(r'^macrocycle/(?P\d+)/planbymonths/user/(?P\d+)/$', + views.planmacrocyclebymonth), + re_path(r'^mesocycle/(?P\d+)/planbyweeks/$', + views.planmesocyclebyweek), + re_path(r'^mesocycle/(?P\d+)/planbyweeks/user/(?P\d+)/$', + views.planmesocyclebyweek), + re_path(r'^microcycle/(?P\d+)/$', login_required( views.TrainingMicroCycleUpdate.as_view()), name='microcycle_update_view'), - re_path(r'^deletetarget/(?P\d+)/$',views.rower_delete_trainingtarget, - name='rower_delete_trainingtarget'), - re_path(r'^editplan/(?P\d+)/$',login_required( + re_path(r'^deletetarget/(?P\d+)/$', views.rower_delete_trainingtarget, + name='rower_delete_trainingtarget'), + re_path(r'^editplan/(?P\d+)/$', login_required( views.TrainingPlanUpdate.as_view()), name='trainingplan_update_view'), - re_path(r'^edittarget/(?P\d+)/$',login_required( + re_path(r'^edittarget/(?P\d+)/$', login_required( views.TrainingTargetUpdate.as_view()), name='trainingtarget_update_view'), - re_path(r'^workout/(?P\b[0-9A-Fa-f]+\b)/test\_strokedata/$',views.strokedataform, + re_path(r'^workout/(?P\b[0-9A-Fa-f]+\b)/test\_strokedata/$', views.strokedataform, name='strokedataform'), - re_path(r'^workout/(?P\b[0-9A-Fa-f]+\b)/v2/test\_strokedata/$',views.strokedataform_v2, + re_path(r'^workout/(?P\b[0-9A-Fa-f]+\b)/v2/test\_strokedata/$', views.strokedataform_v2, name='strokedataform_v2'), - re_path(r'^sessions/library/$',views.template_library_view,name="template_library_view"), - re_path(r'^sessions/teamcreate/user/(?P\d+)/$',views.plannedsession_teamcreate_view, - name='plannedsession_teamcreate_view'), + re_path(r'^sessions/library/$', views.template_library_view, + name="template_library_view"), + re_path(r'^sessions/teamcreate/user/(?P\d+)/$', views.plannedsession_teamcreate_view, + name='plannedsession_teamcreate_view'), re_path(r'^sessions/teamcreate/team/(?P\d+)/user/(?P\d+)/$', - views.plannedsession_teamcreate_view, - name='plannedsession_teamcreate_view'), - re_path(r'^sessions/teamcreate/$',views.plannedsession_teamcreate_view, - name='plannedsession_teamcreate_view'), + views.plannedsession_teamcreate_view, + name='plannedsession_teamcreate_view'), + re_path(r'^sessions/teamcreate/$', views.plannedsession_teamcreate_view, + name='plannedsession_teamcreate_view'), re_path(r'^sessions/teamcreate/team/$', - views.plannedsession_teamcreate_view, - name='plannedsession_teamcreate_view'), - re_path(r'^sessions/teamedit/(?P\d+)/$',views.plannedsession_teamedit_view, - name='plannedsession_teamedit_view'), - re_path(r'^sessions/(?P\d+)/share/$',views.template_share_view, + views.plannedsession_teamcreate_view, + name='plannedsession_teamcreate_view'), + re_path(r'^sessions/teamedit/(?P\d+)/$', views.plannedsession_teamedit_view, + name='plannedsession_teamedit_view'), + re_path(r'^sessions/(?P\d+)/share/$', views.template_share_view, name='template_share_view'), - re_path(r'^sessions/(?P\d+)/makeprivate/$',views.template_makeprivate_view, - name='template_makeprivate_view'), - re_path(r'^sessions/(?P\d+)/removeme/$',views.remove_groupsession_view, - name='remove_groupsession_view'), + re_path(r'^sessions/(?P\d+)/makeprivate/$', views.template_makeprivate_view, + name='template_makeprivate_view'), + re_path(r'^sessions/(?P\d+)/removeme/$', views.remove_groupsession_view, + name='remove_groupsession_view'), re_path(r'^sessions/teamedit/(?P\d+)/user/(?P\d+)/$', - views.plannedsession_teamedit_view, - name='plannedsession_teamedit_view'), - re_path(r'^sessions/create/$',views.plannedsession_create_view, - name='plannedsession_create_view'), - re_path(r'^sessions/createtemplate/$',views.plannedsession_createtemplate_view, - name='plannedsession_createtemplate_view'), + views.plannedsession_teamedit_view, + name='plannedsession_teamedit_view'), + re_path(r'^sessions/create/$', views.plannedsession_create_view, + name='plannedsession_create_view'), + re_path(r'^sessions/createtemplate/$', views.plannedsession_createtemplate_view, + name='plannedsession_createtemplate_view'), re_path(r'^sessions/create/user/(?P\d+)/$', - views.plannedsession_create_view, - name='plannedsession_create_view'), - re_path(r'^sessions/multiclone/$',views.plannedsession_multiclone_view,name='plannedsession_multiclone_view'), + views.plannedsession_create_view, + name='plannedsession_create_view'), + re_path(r'^sessions/multiclone/$', views.plannedsession_multiclone_view, + name='plannedsession_multiclone_view'), re_path(r'^sessions/multiclone/user/(?P\d+)/$', - views.plannedsession_multiclone_view, - name='plannedsession_multiclone_view'), - re_path(r'^sessions/multicreate/$',views.plannedsession_multicreate_view, - name='plannedsession_multicreate_view'), + views.plannedsession_multiclone_view, + name='plannedsession_multiclone_view'), + re_path(r'^sessions/multicreate/$', views.plannedsession_multicreate_view, + name='plannedsession_multicreate_view'), re_path(r'^sessions/multicreate/user/(?P\d+)/extra/(?P\d+)/$', - views.plannedsession_multicreate_view, - name='plannedsession_multicreate_view'), + views.plannedsession_multicreate_view, + name='plannedsession_multicreate_view'), re_path(r'^sessions/multicreate/user/(?P\d+)/$', - views.plannedsession_multicreate_view, - name='plannedsession_multicreate_view'), - re_path(r'^sessions/(?P\d+)/edit/$',views.plannedsession_edit_view, + views.plannedsession_multicreate_view, + name='plannedsession_multicreate_view'), + re_path(r'^sessions/(?P\d+)/edit/$', views.plannedsession_edit_view, name='plannedsession_edit_view'), - re_path(r'^sessions/(?P\d+)/templateedit/',views.plannedsession_templateedit_view, + re_path(r'^sessions/(?P\d+)/templateedit/', views.plannedsession_templateedit_view, name='plannedsession_templateedit_view'), - re_path(r'^sessions/(?P\d+)/maketemplate/$',views.plannedsession_totemplate_view, + re_path(r'^sessions/(?P\d+)/maketemplate/$', views.plannedsession_totemplate_view, name='plannedsession_totemplate_view'), - re_path(r'^sessions/(?P\d+)/togarmin/$',views.plannedsession_togarmin_view, + re_path(r'^sessions/(?P\d+)/togarmin/$', views.plannedsession_togarmin_view, name='plannedsession_togarmin_view'), re_path(r'^sessions/(?P\d+)/compare/$', - views.plannedsession_compare_view, - name='plannedsession_compare_view'), + views.plannedsession_compare_view, + name='plannedsession_compare_view'), re_path(r'^sessions/(?P\d+)/compare/user/(?P\d+)/$', - views.plannedsession_compare_view, - name='plannedsession_compare_view'), - re_path(r'^sessions/(?P\d+)/edit/user/(?P\d+)/$',views.plannedsession_edit_view, + views.plannedsession_compare_view, + name='plannedsession_compare_view'), + re_path(r'^sessions/(?P\d+)/edit/user/(?P\d+)/$', views.plannedsession_edit_view, name='plannedsession_edit_view'), - re_path(r'^sessions/(?P\d+)/clone/user/(?P\d+)/$',views.plannedsession_clone_view, + re_path(r'^sessions/(?P\d+)/clone/user/(?P\d+)/$', views.plannedsession_clone_view, name='plannedsession_clone_view'), - re_path(r'^sessions/(?P\d+)/clone/team/$',views.plannedsession_teamclone_view, + re_path(r'^sessions/(?P\d+)/clone/team/$', views.plannedsession_teamclone_view, name='plannedsession_teamclone_view'), - re_path(r'^sessions/(?P\d+)/clone/$',views.plannedsession_clone_view), - re_path(r'^sessions/(?P\d+)/detach/(?P\b[0-9A-Fa-f]+\b)/user/(?P\d+)/$',views.plannedsession_detach_view), - re_path(r'^sessions/(?P\d+)/detach/(?P\b[0-9A-Fa-f]+\b)/$',views.plannedsession_detach_view), - re_path(r'^sessions/(?P\d+)/$',views.plannedsession_view, - name='plannedsession_view'), - re_path(r'^sessions/(?P\d+)/user/(?P\d+)/$',views.plannedsession_view, - name='plannedsession_view'), - re_path(r'^sessions/(?P\d+)/deleteconfirm/$',login_required( + re_path(r'^sessions/(?P\d+)/clone/$', views.plannedsession_clone_view), + re_path( + r'^sessions/(?P\d+)/detach/(?P\b[0-9A-Fa-f]+\b)/user/(?P\d+)/$', views.plannedsession_detach_view), + re_path( + r'^sessions/(?P\d+)/detach/(?P\b[0-9A-Fa-f]+\b)/$', views.plannedsession_detach_view), + re_path(r'^sessions/(?P\d+)/$', views.plannedsession_view, + name='plannedsession_view'), + re_path(r'^sessions/(?P\d+)/user/(?P\d+)/$', views.plannedsession_view, + name='plannedsession_view'), + re_path(r'^sessions/(?P\d+)/deleteconfirm/$', login_required( views.PlannedSessionDelete.as_view())), - re_path(r'^sessions/(?P\d+)/delete/$',login_required( + re_path(r'^sessions/(?P\d+)/delete/$', login_required( views.PlannedSessionDelete.as_view()), name='plannedsession_delete_view'), re_path(r'^sessions/manage/session/(?P\d+)/$', - views.plannedsessions_manage_view, - name='plannedsessions_manage_view'), + views.plannedsessions_manage_view, + name='plannedsessions_manage_view'), re_path(r'^sessions/manage/session/(?P\d+)/user/(?P\d+)/$', - views.plannedsessions_manage_view, - name='plannedsessions_manage_view'), + views.plannedsessions_manage_view, + name='plannedsessions_manage_view'), re_path(r'^sessions/manage/?/$', - views.plannedsessions_manage_view, - name='plannedsessions_manage_view'), + views.plannedsessions_manage_view, + name='plannedsessions_manage_view'), re_path(r'^sessions/manage/user/(?P\d+)/$', - views.plannedsessions_manage_view, - name='plannedsessions_manage_view'), - re_path(r'^sessions/coach/$',views.plannedsessions_coach_view, - name='plannedsessions_coach_view'), - re_path(r'^sessions/coach/user/(?P\d+)/$',views.plannedsessions_coach_view, - name='plannedsessions_coach_view'), + views.plannedsessions_manage_view, + name='plannedsessions_manage_view'), + re_path(r'^sessions/coach/$', views.plannedsessions_coach_view, + name='plannedsessions_coach_view'), + re_path(r'^sessions/coach/user/(?P\d+)/$', views.plannedsessions_coach_view, + name='plannedsessions_coach_view'), re_path(r'^sessions/coach/sendcalendar/user/(?P\d+)/$', views.plannedsessions_coach_icsemail_view, - name='plannedsessions_coach_icsemail_view'), - re_path(r'^sessions/print/?/$',views.plannedsessions_print_view, - name='plannedsessions_print_view'), - re_path(r'^sessions/(?P\d+)/comments/user/(?P\d+)/$',views.plannedsession_comment_view, - name='plannedsession_comment_view'), - re_path(r'^sessions/(?P\d+)/comments/$',views.plannedsession_comment_view, - name='plannedsession_comment_view'), - re_path(r'^sessions/print/user/(?P\d+)/$',views.plannedsessions_print_view, - name='plannedsessions_print_view'), - re_path(r'^sessions/(?P\d+)/message/$',views.plannedsession_message_view, - name='plannedsession_message_view'), - re_path(r'^sessions/print/user/(?P\d+)/(?P\d+-\d+-\d+)/(?P\d+-\d+-\d+)/$',views.plannedsessions_print_view, - name='plannedsessions_print_view'), - re_path(r'^sessions/sendcalendar/$',views.plannedsessions_icsemail_view, - name='plannedsessions_coach_icsemail_view'), - re_path(r'^sessions/sendcalendar/user/(?P\d+)/$',views.plannedsessions_icsemail_view, - name='plannedsessions_icsemail_view'), - re_path(r'^sessions/$',views.plannedsessions_view, - name='plannedsessions_view'), - re_path(r'^sessions/user/(?P\d+)/$',views.plannedsessions_view, - name='plannedsessions_view'), - re_path(r'^courses/(?P\d+)/edit/$',views.course_edit_view, - name='course_edit_view'), - re_path(r'^courses/(?P\d+)/delete/$',views.course_delete_view,name='course_delete_view'), - re_path(r'^courses/(?P\d+)/downloadkml/$',views.course_kmldownload_view, - name='course_kmldownload_view'), - re_path(r'^courses/(?P\d+)/$',views.course_view,name='course_view'), - re_path(r'^standards/(?P\d+)/$',views.standard_view,name='standard_view'), - re_path(r'^standards/(?P\d+)/download/$',views.standards_download_view, + name='plannedsessions_coach_icsemail_view'), + re_path(r'^sessions/print/?/$', views.plannedsessions_print_view, + name='plannedsessions_print_view'), + re_path(r'^sessions/(?P\d+)/comments/user/(?P\d+)/$', views.plannedsession_comment_view, + name='plannedsession_comment_view'), + re_path(r'^sessions/(?P\d+)/comments/$', views.plannedsession_comment_view, + name='plannedsession_comment_view'), + re_path(r'^sessions/print/user/(?P\d+)/$', views.plannedsessions_print_view, + name='plannedsessions_print_view'), + re_path(r'^sessions/(?P\d+)/message/$', views.plannedsession_message_view, + name='plannedsession_message_view'), + re_path(r'^sessions/print/user/(?P\d+)/(?P\d+-\d+-\d+)/(?P\d+-\d+-\d+)/$', views.plannedsessions_print_view, + name='plannedsessions_print_view'), + re_path(r'^sessions/sendcalendar/$', views.plannedsessions_icsemail_view, + name='plannedsessions_coach_icsemail_view'), + re_path(r'^sessions/sendcalendar/user/(?P\d+)/$', views.plannedsessions_icsemail_view, + name='plannedsessions_icsemail_view'), + re_path(r'^sessions/$', views.plannedsessions_view, + name='plannedsessions_view'), + re_path(r'^sessions/user/(?P\d+)/$', views.plannedsessions_view, + name='plannedsessions_view'), + re_path(r'^courses/(?P\d+)/edit/$', views.course_edit_view, + name='course_edit_view'), + re_path(r'^courses/(?P\d+)/delete/$', + views.course_delete_view, name='course_delete_view'), + re_path(r'^courses/(?P\d+)/downloadkml/$', views.course_kmldownload_view, + name='course_kmldownload_view'), + re_path(r'^courses/(?P\d+)/$', views.course_view, name='course_view'), + re_path(r'^standards/(?P\d+)/$', + views.standard_view, name='standard_view'), + re_path(r'^standards/(?P\d+)/download/$', views.standards_download_view, name='standards_download_view'), - re_path(r'^standards/(?P\d+)/deactivate/$',views.standard_deactivate_view, + re_path(r'^standards/(?P\d+)/deactivate/$', views.standard_deactivate_view, name='standard_deactivate_view'), - re_path(r'^courses/(?P\d+)/map/$',views.course_map_view,name='course_map_view'), - re_path(r'^help/$',TemplateView.as_view(template_name='help.html'), name='help'), - re_path(r'^workout/api/upload/',views.workout_upload_api,name='workout_upload_api'), - re_path(r'^access/share/$',views.createShareURL, name="sharedURL"), + re_path(r'^courses/(?P\d+)/map/$', + views.course_map_view, name='course_map_view'), + re_path(r'^help/$', TemplateView.as_view(template_name='help.html'), name='help'), + re_path(r'^workout/api/upload/', views.workout_upload_api, + name='workout_upload_api'), + re_path(r'^access/share/$', views.createShareURL, name="sharedURL"), re_path(r'^access/(?P\w+)/$', views.sharedPage, name="sharedPage"), - re_path(r'^history/user/(?P\d+)/$',views.history_view,name="history_view"), - re_path(r'^history/$',views.history_view,name="history_view"), - re_path(r'^history/user/(?P\d+)/data/$',views.history_view_data,name="history_view_data"), - re_path(r'^history/data/$',views.history_view_data,name="history_view_data"), - re_path(r'^braintree/$',views.braintree_webhook_view,name="braintree_webhook_view"), - ] + re_path(r'^history/user/(?P\d+)/$', + views.history_view, name="history_view"), + re_path(r'^history/$', views.history_view, name="history_view"), + re_path(r'^history/user/(?P\d+)/data/$', + views.history_view_data, name="history_view_data"), + re_path(r'^history/data/$', views.history_view_data, + name="history_view_data"), + re_path(r'^braintree/$', views.braintree_webhook_view, + name="braintree_webhook_view"), +] -if settings.DEBUG: # pragma: no cover +if settings.DEBUG: # pragma: no cover urlpatterns += [ - re_path(r'^c2listug/(?P\d+)/$',views.c2listdebug_view), - re_path(r'^c2listug/$',views.c2listdebug_view), - ] + re_path(r'^c2listug/(?P\d+)/$', views.c2listdebug_view), + re_path(r'^c2listug/$', views.c2listdebug_view), + ] diff --git a/rowers/utils.py b/rowers/utils.py index 8b710eb0..0adc4c3f 100644 --- a/rowers/utils.py +++ b/rowers/utils.py @@ -1,7 +1,6 @@ from __future__ import absolute_import -from __future__ import division -from __future__ import print_function -from __future__ import unicode_literals +from datetime import date +import random from datetime import timedelta from django.utils import timezone @@ -37,40 +36,38 @@ lbstoN = 4.44822 landingpages = ( ('workout_view', 'Workout View'), - ('workout_edit_view','Edit View'), - ('workout_workflow_view','Workflow View'), - ('workout_stats_view','Stats View'), - ('workout_data_view','Data Explore View'), - ('workout_summary_edit_view','Intervals Editor'), - ('workout_flexchart_stacked_view','Workout Stacked Chart'), - ('workout_flexchart3_view','Workout Flex Chart') + ('workout_edit_view', 'Edit View'), + ('workout_workflow_view', 'Workflow View'), + ('workout_stats_view', 'Stats View'), + ('workout_data_view', 'Data Explore View'), + ('workout_summary_edit_view', 'Intervals Editor'), + ('workout_flexchart_stacked_view', 'Workout Stacked Chart'), + ('workout_flexchart3_view', 'Workout Flex Chart') ) landingpages2 = ( ('workout_view', 'Workout View'), - ('workout_edit_view','Edit View'), - ('workout_workflow_view','Workflow View'), - ('workout_stats_view','Stats View'), - ('workout_data_view','Data Explore View'), - ('workout_summary_edit_view','Intervals Editor'), - ('workout_flexchart_stacked_view','Workout Stacked Chart'), - ('workout_flexchart3_view','Workout Flex Chart'), - ('workout_delete','Remove Workout') + ('workout_edit_view', 'Edit View'), + ('workout_workflow_view', 'Workflow View'), + ('workout_stats_view', 'Stats View'), + ('workout_data_view', 'Data Explore View'), + ('workout_summary_edit_view', 'Intervals Editor'), + ('workout_flexchart_stacked_view', 'Workout Stacked Chart'), + ('workout_flexchart3_view', 'Workout Flex Chart'), + ('workout_delete', 'Remove Workout') ) - - workflowmiddlepanel = ( - ('panel_statcharts.html','Static Charts'), - ('flexthumbnails.html','Flex Charts'), - ('panel_summary.html','Summary'), - ('panel_map.html','Map'), - ('panel_comments.html','Basic Info and Links'), - ('panel_notes.html','Workout Notes'), - ('panel_shortcomment.html','Comment Link'), - ('panel_middlesocial.html','Social Media Share Buttons'), - ) + ('panel_statcharts.html', 'Static Charts'), + ('flexthumbnails.html', 'Flex Charts'), + ('panel_summary.html', 'Summary'), + ('panel_map.html', 'Map'), + ('panel_comments.html', 'Basic Info and Links'), + ('panel_notes.html', 'Workout Notes'), + ('panel_shortcomment.html', 'Comment Link'), + ('panel_middlesocial.html', 'Social Media Share Buttons'), +) defaultmiddle = ['panel_middlesocial.html', 'panel_statcharts.html', @@ -79,24 +76,24 @@ defaultmiddle = ['panel_middlesocial.html', 'panel_map.html'] workflowleftpanel = ( - ('panel_navigationheader.html','Navigation Header'), - ('panel_editbuttons.html','Edit Workout Button'), - ('panel_delete.html','Delete Workout Button'), - ('panel_export.html','Export Workout Button'), - ('panel_social.html','Social Media Share Buttons'), - ('panel_advancededit.html','Advanced Workout Edit Button'), - ('panel_editintervals.html','Edit Intervals Button'), - ('panel_stats.html','Workout Statistics Button'), - ('panel_flexchart.html','Flex Chart'), - ('panel_staticchart.html','Create Static Charts Buttons'), - ('panel_uploadimage.html','Attach Image'), - ('panel_geekyheader.html','Geeky Header'), - ('panel_editwind.html','Edit Wind Data'), - ('panel_editstream.html','Edit Stream Data'), - ('panel_otwpower.html','Run OTW Power Calculations'), - ('panel_mapview.html','Map'), - ('panel_ranking.html','Ranking View'), - ) + ('panel_navigationheader.html', 'Navigation Header'), + ('panel_editbuttons.html', 'Edit Workout Button'), + ('panel_delete.html', 'Delete Workout Button'), + ('panel_export.html', 'Export Workout Button'), + ('panel_social.html', 'Social Media Share Buttons'), + ('panel_advancededit.html', 'Advanced Workout Edit Button'), + ('panel_editintervals.html', 'Edit Intervals Button'), + ('panel_stats.html', 'Workout Statistics Button'), + ('panel_flexchart.html', 'Flex Chart'), + ('panel_staticchart.html', 'Create Static Charts Buttons'), + ('panel_uploadimage.html', 'Attach Image'), + ('panel_geekyheader.html', 'Geeky Header'), + ('panel_editwind.html', 'Edit Wind Data'), + ('panel_editstream.html', 'Edit Stream Data'), + ('panel_otwpower.html', 'Run OTW Power Calculations'), + ('panel_mapview.html', 'Map'), + ('panel_ranking.html', 'Ranking View'), +) defaultleft = [ 'panel_navigationheader.html', @@ -118,19 +115,18 @@ coxes_calls = [ "You are clearing the puddles.", "Let's push through now. Get me that open water.", "We're going for the line now. Power ten on the next.", - ] +] info_calls = [ "Please give us a minute to count all those strokes, you've been working hard!", "Please give us a minute to count all your strokes." - ] +] -import random -def dologging(filename,s): +def dologging(filename, s): tstamp = time.localtime() timestamp = time.strftime('%b-%d-%Y %H:%M:%S', tstamp) - with open(filename,'a') as f: + with open(filename, 'a') as f: f.write('\n') f.write(timestamp) f.write(' ') @@ -139,8 +135,9 @@ def dologging(filename,s): except TypeError: f.write(str(s)) -def to_pace(pace): # pragma: no cover - minutes, seconds = divmod(pace,60) + +def to_pace(pace): # pragma: no cover + minutes, seconds = divmod(pace, 60) seconds, rest = divmod(seconds, 1) tenths = int(rest*10) s = '{m:0>2}:{s:0>2}.{t:0>1}'.format( @@ -150,6 +147,7 @@ def to_pace(pace): # pragma: no cover ) return s + def get_call(): call1 = random.choice(coxes_calls) call2 = random.choice(info_calls) @@ -163,102 +161,113 @@ def get_call(): return call + def absolute(request): urls = { 'ABSOLUTE_ROOT': request.build_absolute_uri('/')[:-1].strip("/"), 'ABSOLUTE_ROOT_URL': request.build_absolute_uri('/').strip("/"), - 'PATH':request.build_absolute_uri(), + 'PATH': request.build_absolute_uri(), } return urls -def trcolors(r1,g1,b1,r2,g2,b2): + +def trcolors(r1, g1, b1, r2, g2, b2): r1 = r1/255. r2 = r2/255. g1 = g1/255. g2 = g2/255. b2 = b2/255. b1 = b1/255. - h1,s1,v1 = colorsys.rgb_to_hsv(r1,g1,b1) - h2,s2,v2 = colorsys.rgb_to_hsv(r2,g2,b2) + h1, s1, v1 = colorsys.rgb_to_hsv(r1, g1, b1) + h2, s2, v2 = colorsys.rgb_to_hsv(r2, g2, b2) + return 360*h1, 360*(h2-h1), s1, (s2-s1), v1, (v2-v1) - return 360*h1,360*(h2-h1),s1,(s2-s1),v1,(v2-v1) palettes = { - 'monochrome_blue':(207,-4,0.06,0.89,1.0,-0.38), - 'gold_sunset':(47,-31,.26,-0.12,0.94,-0.5), - 'blue_red':(207,-200,.85,0,.74,-.24), - 'blue_green':(207,-120,.85,0,.75,.25), - 'cyan_green':(192,-50,.08,.65,.98,-.34), - 'cyan_purple':trcolors(237,248,251,136,65,157), - 'green_blue':trcolors(240,249,232,8,104,172), - 'orange_red':trcolors(254,240,217,179,0,0), - 'cyan_blue':trcolors(241,238,246,4,90,141), - 'cyan_green':trcolors(246,239,247,1,108,89), - 'cyan_magenta':trcolors(241,238,246,152,0,67), - 'beige_magenta':trcolors(254,235,226,122,1,119), - 'yellow_green':trcolors(255,255,204,0,104,55), - 'yellow_blue':trcolors(255,255,205,37,52,148), - 'autumn':trcolors(255,255,212,153,52,4), - 'yellow_red':trcolors(255,255,178,189,0,39) + 'monochrome_blue': (207, -4, 0.06, 0.89, 1.0, -0.38), + 'gold_sunset': (47, -31, .26, -0.12, 0.94, -0.5), + 'blue_red': (207, -200, .85, 0, .74, -.24), + 'blue_green': (207, -120, .85, 0, .75, .25), + 'cyan_green': (192, -50, .08, .65, .98, -.34), + 'cyan_purple': trcolors(237, 248, 251, 136, 65, 157), + 'green_blue': trcolors(240, 249, 232, 8, 104, 172), + 'orange_red': trcolors(254, 240, 217, 179, 0, 0), + 'cyan_blue': trcolors(241, 238, 246, 4, 90, 141), + 'cyan_green': trcolors(246, 239, 247, 1, 108, 89), + 'cyan_magenta': trcolors(241, 238, 246, 152, 0, 67), + 'beige_magenta': trcolors(254, 235, 226, 122, 1, 119), + 'yellow_green': trcolors(255, 255, 204, 0, 104, 55), + 'yellow_blue': trcolors(255, 255, 205, 37, 52, 148), + 'autumn': trcolors(255, 255, 212, 153, 52, 4), + 'yellow_red': trcolors(255, 255, 178, 189, 0, 39) } -rankingdistances = [100,500,1000,2000,5000,6000,10000,21097,42195,100000] +rankingdistances = [100, 500, 1000, 2000, + 5000, 6000, 10000, 21097, 42195, 100000] rankingdurations = [] rankingdurations.append(datetime.time(minute=1)) rankingdurations.append(datetime.time(minute=4)) rankingdurations.append(datetime.time(minute=30)) -rankingdurations.append(datetime.time(hour=1,minute=15)) +rankingdurations.append(datetime.time(hour=1, minute=15)) rankingdurations.append(datetime.time(hour=1)) -def range_to_color_hex(groupcols,palette='monochrome_blue'): - try: - plt = palettes[palette] - except KeyError: # pragma: no cover - plt = palettes['monochrome_blue'] +def range_to_color_hex(groupcols, palette='monochrome_blue'): - rgb = [colorsys.hsv_to_rgb((plt[0]+plt[1]*x)/360., - plt[2]+plt[3]*x, - plt[4]+plt[5]*x) for x in groupcols] + try: + plt = palettes[palette] + except KeyError: # pragma: no cover + plt = palettes['monochrome_blue'] - RGB = [(int(255.*r),int(255.*g),int(255.*b)) for (r, g, b) in rgb] - colors = ["#%02x%02x%02x" % (r, g, b) for (r, g, b) in RGB] + rgb = [colorsys.hsv_to_rgb((plt[0]+plt[1]*x)/360., + plt[2]+plt[3]*x, + plt[4]+plt[5]*x) for x in groupcols] - return colors + RGB = [(int(255.*r), int(255.*g), int(255.*b)) for (r, g, b) in rgb] + colors = ["#%02x%02x%02x" % (r, g, b) for (r, g, b) in RGB] + + return colors + + +def str2bool(v): # pragma: no cover + return v.lower() in ("yes", "true", "t", "1") -def str2bool(v): # pragma: no cover - return v.lower() in ("yes", "true", "t", "1") def uniqify(seq, idfun=None): - # order preserving - if idfun is None: - def idfun(x): return x - seen = {} - result = [] - for item in seq: - marker = idfun(item) - # in old Python versions: - # if seen.has_key(marker) - # but in new ones: - if marker in seen: continue - seen[marker] = 1 - result.append(item) - return result + # order preserving + if idfun is None: + def idfun(x): return x + seen = {} + result = [] + for item in seq: + marker = idfun(item) + # in old Python versions: + # if seen.has_key(marker) + # but in new ones: + if marker in seen: + continue + seen[marker] = 1 + result.append(item) + return result -def serialize_list(value,token=','): # pragma: no cover - assert(isinstance(value, list) or isinstance(value, tuple) or isinstance(value,np.ndarray)) + +def serialize_list(value, token=','): # pragma: no cover + assert(isinstance(value, list) or isinstance( + value, tuple) or isinstance(value, np.ndarray)) return token.join([str(s) for s in value]) -def deserialize_list(value,token=','): # pragma: no cover + +def deserialize_list(value, token=','): # pragma: no cover if isinstance(value, list): return value elif isinstance(value, np.ndarray): return value return value.split(token) -def geo_distance(lat1,lon1,lat2,lon2): + +def geo_distance(lat1, lon1, lat2, lon2): """ Approximate distance and bearing between two points defined by lat1,lon1 and lat2,lon2 This is a slight underestimate but is close enough for our purposes, @@ -285,71 +294,66 @@ def geo_distance(lat1,lon1,lat2,lon2): dlon = lon2 - lon1 dlat = lat2 - lat1 - a = math.sin(dlat / 2)**2 + math.cos(lat1) * math.cos(lat2) * math.sin(dlon / 2)**2 + a = math.sin(dlat / 2)**2 + math.cos(lat1) * \ + math.cos(lat2) * math.sin(dlon / 2)**2 c = 2 * math.atan2(math.sqrt(a), math.sqrt(1 - a)) distance = R * c tc1 = math.atan2(math.sin(lon2-lon1)*math.cos(lat2), - math.cos(lat1)*math.sin(lat2)-math.sin(lat1)*math.cos(lat2)*math.cos(lon2-lon1)) + math.cos(lat1)*math.sin(lat2)-math.sin(lat1)*math.cos(lat2)*math.cos(lon2-lon1)) tc1 = tc1 % (2*pi) bearing = math.degrees(tc1) - return [distance,bearing] + return [distance, bearing] +def isbreakthrough(delta, cpvalues, p0, p1, p2, p3, ratio): + pwr = abs(p0)/(1+(delta/abs(p2))) + pwr += abs(p1)/(1+(delta/abs(p3))) -def isbreakthrough(delta,cpvalues,p0,p1,p2,p3,ratio): - pwr = abs(p0)/(1+(delta/abs(p2))) - pwr += abs(p1)/(1+(delta/abs(p3))) + dd = 0.25*(ratio-1) + pwr2 = pwr*(1+dd) - dd = 0.25*(ratio-1) - pwr2 = pwr*(1+dd) + pwr *= ratio - pwr *= ratio + delta = delta.values.astype(int) + cpvalues = cpvalues.values.astype(int) + pwr = pwr.astype(int) + + res = np.sum(cpvalues > pwr) + res2 = np.sum(cpvalues > pwr2) + + btdf = pd.DataFrame( + { + 'delta': delta[cpvalues > pwr], + 'cpvalues': cpvalues[cpvalues > pwr], + 'pwr': pwr[cpvalues > pwr], + } + ) + + btdf.sort_values('delta', axis=0, inplace=True) + + return res >= 1, btdf, res2 >= 1 - delta = delta.values.astype(int) - cpvalues = cpvalues.values.astype(int) - pwr = pwr.astype(int) - - - res = np.sum(cpvalues>pwr) - res2 = np.sum(cpvalues>pwr2) - - - btdf = pd.DataFrame( - { - 'delta':delta[cpvalues>pwr], - 'cpvalues':cpvalues[cpvalues>pwr], - 'pwr':pwr[cpvalues>pwr], - } - ) - - - btdf.sort_values('delta',axis=0,inplace=True) - - - return res>=1,btdf,res2>=1 - - -def myqueue(queue,function,*args,**kwargs): +def myqueue(queue, function, *args, **kwargs): class MockJob: - def __init__(self,*args, **kwargs): + def __init__(self, *args, **kwargs): self.result = 1 self.id = 1 - def revoke(self): # pragma: no cover + def revoke(self): # pragma: no cover return 1 if settings.TESTING: return MockJob() - elif settings.CELERY: # pragma: no cover + elif settings.CELERY: # pragma: no cover kwargs['debug'] = True - job = function.delay(*args,**kwargs) - else: # pragma: no cover + job = function.delay(*args, **kwargs) + else: # pragma: no cover if settings.DEBUG: kwargs['debug'] = True @@ -358,14 +362,12 @@ def myqueue(queue,function,*args,**kwargs): kwargs['jobkey'] = job_id kwargs['timeout'] = 3600 - job = queue.enqueue(function,*args,**kwargs) + job = queue.enqueue(function, *args, **kwargs) - return job # pragma: no cover + return job # pragma: no cover -from datetime import date - -def calculate_age(born,today=None): +def calculate_age(born, today=None): if not today: today = timezone.now() if born: @@ -376,7 +378,8 @@ def calculate_age(born,today=None): else: return None -def my_dict_from_instance(instance,model): + +def my_dict_from_instance(instance, model): thedict = {} thedict['id'] = instance.id @@ -386,23 +389,24 @@ def my_dict_from_instance(instance,model): try: verbosename = f.verbose_name - except: # pragma: no cover + except: # pragma: no cover verbosename = f.name get_choice = 'get_'+fname+'_display' - if hasattr( instance, get_choice): + if hasattr(instance, get_choice): value = getattr(instance, get_choice)() else: try: - value = getattr(instance,fname) - except AttributeError: # pragma: no cover + value = getattr(instance, fname) + except AttributeError: # pragma: no cover value = None if f.editable and value: - thedict[fname] = (verbosename,value) + thedict[fname] = (verbosename, value) return thedict + def wavg(group, avg_name, weight_name): """ http://stackoverflow.com/questions/10951341/pandas-dataframe-aggregate-function-using-multiple-columns In rare instance, we may not have weights, so just return the mean. Customize this if your business case @@ -418,33 +422,34 @@ def wavg(group, avg_name, weight_name): return d.mean() try: return (d * w).sum() / w.sum() - except ZeroDivisionError: # pragma: no cover + except ZeroDivisionError: # pragma: no cover return d.mean() -def totaltime_sec_to_string(totaltime,shorten=False): + +def totaltime_sec_to_string(totaltime, shorten=False): if np.isnan(totaltime): return '' hours = int(totaltime / 3600.) - if hours > 23: # pragma: no cover + if hours > 23: # pragma: no cover message = 'Warning: The workout duration was longer than 23 hours. ' hours = 23 minutes = int((totaltime - 3600. * hours) / 60.) - if minutes > 59: # pragma: no cover + if minutes > 59: # pragma: no cover minutes = 59 - if not message: # pragma: no cover + if not message: # pragma: no cover message = 'Warning: there is something wrong with the workout duration' seconds = int(totaltime - 3600. * hours - 60. * minutes) - if seconds > 59: # pragma: no cover + if seconds > 59: # pragma: no cover seconds = 59 - if not message: # pragma: no cover + if not message: # pragma: no cover message = 'Warning: there is something wrong with the workout duration' tenths = int(10 * (totaltime - 3600. * hours - 60. * minutes - seconds)) - if tenths > 9: # pragma: no cover + if tenths > 9: # pragma: no cover tenths = 9 - if not message: # pragma: no cover + if not message: # pragma: no cover message = 'Warning: there is something wrong with the workout duration' duration = "" @@ -454,27 +459,27 @@ def totaltime_sec_to_string(totaltime,shorten=False): minutes=minutes, seconds=seconds, tenths=tenths - ) + ) else: - if hours != 0: # pragma: no cover + if hours != 0: # pragma: no cover duration = "{hours}:{minutes:02d}:{seconds:02d}".format( hours=hours, minutes=minutes, seconds=seconds, tenths=tenths - ) + ) else: duration = "{minutes}:{seconds:02d}".format( hours=hours, minutes=minutes, seconds=seconds, tenths=tenths - ) + ) return duration -def iscoach(m,r): # pragma: no cover +def iscoach(m, r): # pragma: no cover result = False result = m in r.coaches @@ -483,52 +488,59 @@ def iscoach(m,r): # pragma: no cover # Exponentially weighted moving average # Used for data smoothing of the jagged data obtained by Strava # See bitbucket issue 72 -def ewmovingaverage(interval,window_size): + + +def ewmovingaverage(interval, window_size): # Experimental code using Exponential Weighted moving average try: - intervaldf = pd.DataFrame({'v':interval}) + intervaldf = pd.DataFrame({'v': interval}) idf_ewma1 = intervaldf.ewm(span=window_size) idf_ewma2 = intervaldf[::-1].ewm(span=window_size) - i_ewma1 = idf_ewma1.mean().loc[:,'v'] - i_ewma2 = idf_ewma2.mean().loc[:,'v'] + i_ewma1 = idf_ewma1.mean().loc[:, 'v'] + i_ewma2 = idf_ewma2.mean().loc[:, 'v'] - interval2 = np.vstack((i_ewma1,i_ewma2[::-1])) - interval2 = np.mean( interval2, axis=0) # average - except ValueError: # pragma: no cover + interval2 = np.vstack((i_ewma1, i_ewma2[::-1])) + interval2 = np.mean(interval2, axis=0) # average + except ValueError: # pragma: no cover interval2 = interval return interval2 # Exceptions # Custom error class - to raise a NoTokenError -class NoTokenError(Exception): - def __init__(self,value): - self.value=value - def __str__(self): # pragma: no cover + +class NoTokenError(Exception): + def __init__(self, value): + self.value = value + + def __str__(self): # pragma: no cover return repr(self.value) -class ProcessorCustomerError(Exception): # pragma: no cover + +class ProcessorCustomerError(Exception): # pragma: no cover def __init__(self, value): - self.value=value + self.value = value def __str__(self): return repr(self.value) # Custom exception handler, returns a 401 HTTP message # with exception details in the json data -def custom_exception_handler(exc,message): + + +def custom_exception_handler(exc, message): response = { - "errors": [ - { - "code": str(exc), - "detail": message, - } - ] - } + "errors": [ + { + "code": str(exc), + "detail": message, + } + ] + } res = HttpResponse(message) res.status_code = 401 @@ -536,15 +548,16 @@ def custom_exception_handler(exc,message): return res -def get_strava_stream(r,metric,stravaid,series_type='time',fetchresolution='high',authorizationstring=''): - if r is not None: # pragma: no cover + +def get_strava_stream(r, metric, stravaid, series_type='time', fetchresolution='high', authorizationstring=''): + if r is not None: # pragma: no cover authorizationstring = str('Bearer ' + r.stravatoken) headers = {'Authorization': authorizationstring, - 'user-agent': 'sanderroosendaal', - 'Content-Type': 'application/json', - 'resolution': 'medium',} + 'user-agent': 'sanderroosendaal', + 'Content-Type': 'application/json', + 'resolution': 'medium', } - if metric == 'power': # pragma: no cover + if metric == 'power': # pragma: no cover metric = 'watts' url = "https://www.strava.com/api/v3/activities/{stravaid}/streams/{metric}?resolution={fetchresolution}&series_type={series_type}".format( @@ -552,13 +565,11 @@ def get_strava_stream(r,metric,stravaid,series_type='time',fetchresolution='high fetchresolution=fetchresolution, series_type=series_type, metric=metric - ) + ) + s = requests.get(url, headers=headers) - s = requests.get(url,headers=headers) - - - if metric=='power': # pragma: no cover + if metric == 'power': # pragma: no cover with open('data.txt', 'w') as outfile: json.dump(s.json(), outfile) @@ -567,89 +578,95 @@ def get_strava_stream(r,metric,stravaid,series_type='time',fetchresolution='high y = None try: if data['type'] == metric: - return np.array(data['data']) - except TypeError: # pragma: no cover + return np.array(data['data']) + except TypeError: # pragma: no cover return None - except: # pragma: no cover + except: # pragma: no cover return None - return None # pragma: no cover + return None # pragma: no cover -def allmonths(startdate,enddate): + +def allmonths(startdate, enddate): d = startdate - while d 0: # pragma: no cover + if value < 10 and value > 0: # pragma: no cover targetpower = ftp*0.6 - elif value > 10 and value < 1000: # pragma: no cover + elif value > 10 and value < 1000: # pragma: no cover targetpower = value*ftp/100. elif value > 1000: targetpower = value-1000 avgspeed = ftv*(targetpower/ftp)**(1./3.) distance = avgspeed*seconds avgpower = targetpower - if valuelow != 0 and valuehigh != 0: # pragma: no cover + if valuelow != 0 and valuehigh != 0: # pragma: no cover avgpower = (valuelow+valuehigh)/2. avgspeed = ftv*(avgspeed/ftv)**(1./3.) distance = avgspeed*seconds @@ -682,28 +699,28 @@ def step_to_time_dist(step,avgspeed = 3.2,ftp=200,ftspm=25,ftv=3.7): rscore = 100.*(avgpower/ftp)*seconds/3600. if targettype == 'Cadence': - value = step.get('targetValue',0) - valuelow = step.get('targetValueLow',0) - valuehigh = step.get('targetValueHigh',0) + value = step.get('targetValue', 0) + valuelow = step.get('targetValueLow', 0) + valuehigh = step.get('targetValueHigh', 0) if value != 0: avgpower = ftp*value/ftspm - if valuelow != 0 and valuehigh != 0: # pragma: no cover + if valuelow != 0 and valuehigh != 0: # pragma: no cover avgspm = (valuelow+valuehigh)/2. avgpower = ftp*avgspm/ftspm rscore = 100*(avgpower/ftp)*seconds/3600. - return seconds,distance,rscore + return seconds, distance, rscore elif durationtype == 'Distance': distance = value/100. seconds = distance/avgspeed rscore = 60.*float(seconds)/3600. - if targettype == 'Speed': # pragma: no cover - value = step.get('targetValue',0) - valuelow = step.get('targetValueLow',0) - valuehigh = step.get('targetValueHigh',0) + if targettype == 'Speed': # pragma: no cover + value = step.get('targetValue', 0) + valuelow = step.get('targetValueLow', 0) + valuehigh = step.get('targetValueHigh', 0) velomid = 0 if value != 0: @@ -717,10 +734,10 @@ def step_to_time_dist(step,avgspeed = 3.2,ftp=200,ftspm=25,ftv=3.7): rscoreperhour = 100.*veloratio rscore = rscoreperhour*seconds/3600. - if targettype == 'Power': # pragma: no cover - value = step.get('targetValue',0) - valuelow = step.get('targetValueLow',0) - valuehigh = step.get('targetValueHigh',0) + if targettype == 'Power': # pragma: no cover + value = step.get('targetValue', 0) + valuelow = step.get('targetValueLow', 0) + valuehigh = step.get('targetValueHigh', 0) if value != 0: if value < 10 and value > 0: @@ -739,10 +756,10 @@ def step_to_time_dist(step,avgspeed = 3.2,ftp=200,ftspm=25,ftv=3.7): rscore = 100.*(avgpower/ftp)*seconds/3600. - if targettype == 'Cadence': # pragma: no cover - value = step.get('targetValue',0) - valuelow = step.get('targetValueLow',0) - valuehigh = step.get('targetValueHigh',0) + if targettype == 'Cadence': # pragma: no cover + value = step.get('targetValue', 0) + valuelow = step.get('targetValueLow', 0) + valuehigh = step.get('targetValueHigh', 0) if value != 0: avgpower = ftp*value/ftspm @@ -753,35 +770,37 @@ def step_to_time_dist(step,avgspeed = 3.2,ftp=200,ftspm=25,ftv=3.7): rscore = 100*(avgpower/ftp)*seconds/3600. return seconds, distance, rscore - elif durationtype in ['PowerLessThan','PowerGreaterThan','HrLessThan','HrGreaterThan']: # pragma: no cover + elif durationtype in ['PowerLessThan', 'PowerGreaterThan', 'HrLessThan', 'HrGreaterThan']: # pragma: no cover seconds = 600 distance = seconds*avgspeed veloratio = (avgspeed/ftv)**(3.0) rscoreperhour = 100.*veloratio rscore = rscoreperhour*seconds/3600. - return seconds,distance,rscore + return seconds, distance, rscore - return seconds,distance, rscore + return seconds, distance, rscore -def get_step_type(step): # pragma: no cover + +def get_step_type(step): # pragma: no cover t = 'WorkoutStep' - if step['durationType'] in ['RepeatUntilStepsCmplt','RepeatUntilHrLessThan','RepeatUntilHrGreaterThan']: + if step['durationType'] in ['RepeatUntilStepsCmplt', 'RepeatUntilHrLessThan', 'RepeatUntilHrGreaterThan']: t = 'WorkoutRepeatStep' return t + def peel(l): - if len(l)==0: # pragma: no cover - return None,None - if len(l)==1: - return l[0],None + if len(l) == 0: # pragma: no cover + return None, None + if len(l) == 1: + return l[0], None first = l[0] rest = l[1:] - if first['type'] == 'Step': # pragma: no cover + if first['type'] == 'Step': # pragma: no cover return first, rest # repeatstep theID = -1 @@ -792,18 +811,19 @@ def peel(l): theID = f['stepID'] first['steps'] = list(reversed(lijst)) - return first,rest + return first, rest -def ps_dict_order_dict(d,short=False): +def ps_dict_order_dict(d, short=False): steps = d['steps'] sdicts = [] for step in steps: - sstring, type, stepID, repeatID, repeatValue = step_to_string(step,short=short) - seconds, meters,rscore = step_to_time_dist(step) + sstring, type, stepID, repeatID, repeatValue = step_to_string( + step, short=short) + seconds, meters, rscore = step_to_time_dist(step) sdict = { - 'type':type, + 'type': type, 'stepID': stepID, 'repeatID': repeatID, 'repeatValue': repeatValue, @@ -815,7 +835,8 @@ def ps_dict_order_dict(d,short=False): return sdict2 -def ps_dict_order(d,short=False,rower=None,html=True): + +def ps_dict_order(d, short=False, rower=None, html=True): sdict = collections.OrderedDict({}) steps = d['steps'] @@ -826,12 +847,13 @@ def ps_dict_order(d,short=False,rower=None,html=True): # ftspm = rower.ftspm for step in steps: - sstring, type, stepID, repeatID, repeatValue = step_to_string(step,short=short) - seconds, meters,rscore = step_to_time_dist(step,ftp=ftp) + sstring, type, stepID, repeatID, repeatValue = step_to_string( + step, short=short) + seconds, meters, rscore = step_to_time_dist(step, ftp=ftp) sdict[stepID] = { - 'string':sstring, - 'type':type, + 'string': sstring, + 'type': type, 'stepID': stepID, 'repeatID': repeatID, 'repeatValue': repeatValue, @@ -840,16 +862,16 @@ def ps_dict_order(d,short=False,rower=None,html=True): 'rscore': rscore, } - sdict2 = collections.OrderedDict(reversed(list(sdict.items()))) for step in steps: - sstring, type, stepID, repeatID, repeatValue = step_to_string(step,short=short) + sstring, type, stepID, repeatID, repeatValue = step_to_string( + step, short=short) seconds, meters, rscore = step_to_time_dist(step) sdict[stepID] = { - 'string':sstring, - 'type':type, + 'string': sstring, + 'type': type, 'stepID': stepID, 'repeatID': repeatID, 'repeatValue': repeatValue, @@ -886,7 +908,7 @@ def ps_dict_order(d,short=False,rower=None,html=True): totalmeters += factor*item['meters'] totalseconds += factor*item['seconds'] totalrscore += factor*item['rscore'] - if len(holduntil)>0 and item['stepID'] <= holduntil[-1]: + if len(holduntil) > 0 and item['stepID'] <= holduntil[-1]: if item['stepID'] == holduntil[-1]: sdict3.append(hold.pop()) factor /= multiplier.pop() @@ -895,7 +917,7 @@ def ps_dict_order(d,short=False,rower=None,html=True): else: spaces = spaces[:-3] holduntil.pop() - else: # pragma: no cover + else: # pragma: no cover prevstep = sdict3.pop() prevstep['string'] = prevstep['string'][18:] prevprevstep = sdict3.pop() @@ -914,9 +936,10 @@ def ps_dict_order(d,short=False,rower=None,html=True): sdict = list(reversed(sdict3)) - return sdict,totalmeters,totalseconds,totalrscore + return sdict, totalmeters, totalseconds, totalrscore -def step_to_string(step,short=False): + +def step_to_string(step, short=False): type = 'Step' repeatID = -1 repeatValue = 1 @@ -932,13 +955,13 @@ def step_to_string(step,short=False): durationtype = step['durationType'] if step['durationValue'] == 0: - if durationtype not in ['RepeatUntilStepsCmplt','RepeatUntilHrLessThan','RepeatUntilHrGreaterThan']: # pragma: no cover - return '',type, -1, -1,1 + if durationtype not in ['RepeatUntilStepsCmplt', 'RepeatUntilHrLessThan', 'RepeatUntilHrGreaterThan']: # pragma: no cover + return '', type, -1, -1, 1 if durationtype == 'Time': unit = 'min' value = step['durationValue'] - if value/1000. >= 3600: # pragma: no cover + if value/1000. >= 3600: # pragma: no cover unit = 'h' dd = timedelta(seconds=value/1000.) #duration = humanize.naturaldelta(dd, minimum_unit="seconds") @@ -947,29 +970,31 @@ def step_to_string(step,short=False): unit = 'm' value = step['durationValue']/100. duration = int(value) - elif durationtype == 'HrLessThan': # pragma: no cover + elif durationtype == 'HrLessThan': # pragma: no cover value = step['durationValue'] if value <= 100: - duration = 'until heart rate lower than {v}% of max'.format(v=value) + duration = 'until heart rate lower than {v}% of max'.format( + v=value) if short: duration = 'until HR<{v}% of max'.format(v=value) else: duration = 'until heart rate lower than {v}'.format(v=value-100) if short: duration = 'until HR<{v}'.format(v=value-100) - elif durationtype == 'HrGreaterThan': # pragma: no cover + elif durationtype == 'HrGreaterThan': # pragma: no cover value = step['durationValue'] if value <= 100: - duration = 'until heart rate greater than {v}% of max'.format(v=value) + duration = 'until heart rate greater than {v}% of max'.format( + v=value) if short: duration = 'until R>{v}% of max'.format(v=value) else: duration = 'until heart rate greater than {v}'.format(v=value-100) if short: duration = 'until HR>{v}'.format(v=value/100) - elif durationtype == 'PowerLessThan': # pragma: no cover + elif durationtype == 'PowerLessThan': # pragma: no cover value = step['durationValue'] - targetvalue = step.get('targetValue',0) + targetvalue = step.get('targetValue', 0) if value <= 1000: duration = 'Repeat until Power is less than {targetvalue} % of FTP'.format( targetvalue=targetvalue @@ -982,61 +1007,66 @@ def step_to_string(step,short=False): ) if short: 'until < {targetvalue} W'.format(targetvalue=targetvalue-1000) - elif durationtype == 'PowerGreaterThan': # pragma: no cover + elif durationtype == 'PowerGreaterThan': # pragma: no cover value = step['durationValue'] - targetvalue = step.get('targetValue',0) + targetvalue = step.get('targetValue', 0) if value <= 1000: duration = 'Repeat until Power is greater than {targetvalue} % of FTP'.format( targetvalue=targetvalue ) if short: - duration = 'until > {targetvalue}% of FTP'.format(targetvalue=targetvalue) + duration = 'until > {targetvalue}% of FTP'.format( + targetvalue=targetvalue) else: duration = 'Repeat until Power is greater than {targetvalue} Watt'.format( targetvalue=targetvalue-1000 ) if short: - duration = 'until > {targetvalue} W'.format(targetvalue=targetvalue) - elif durationtype == 'RepeatUntilStepsCmplt': # pragma: no cover + duration = 'until > {targetvalue} W'.format( + targetvalue=targetvalue) + elif durationtype == 'RepeatUntilStepsCmplt': # pragma: no cover type = 'RepeatStep' ntimes = ': {v}x'.format(v=step['targetValue']) repeatID = step['durationValue'] - duration =ntimes - repeatValue = step.get('targetValue',0) - elif durationtype == 'RepeatUntilHrGreaterThan': # pragma: no cover + duration = ntimes + repeatValue = step.get('targetValue', 0) + elif durationtype == 'RepeatUntilHrGreaterThan': # pragma: no cover type = 'RepeatStep' - targetvalue = step.get('targetValue',0) + targetvalue = step.get('targetValue', 0) if targetvalue <= 100: duration = 'Repeat until Heart Rate is greater than {targetvalue} % of max'.format( targetvalue=targetvalue - ) + ) if short: - duration = ': until HR>{targetvalue} % of max'.format(targetvalue=targetvalue) + duration = ': until HR>{targetvalue} % of max'.format( + targetvalue=targetvalue) else: duration = 'Repeat until Heart Rate is greater than {targetvalue}:'.format( targetvalue=targetvalue-100. ) if short: - duration = ': untl HR>{targetvalue}'.format(targetvalue=targetvalue-100) + duration = ': untl HR>{targetvalue}'.format( + targetvalue=targetvalue-100) repeatID = step['durationValue'] - elif durationtype == 'RepeatUntilHrLessThan': # pragma: no cover + elif durationtype == 'RepeatUntilHrLessThan': # pragma: no cover type = 'RepeatStep' - targetvalue = step.get('targetValue',0) + targetvalue = step.get('targetValue', 0) if targetvalue <= 100: duration = 'Repeat until Heart Rate is less than {targetvalue} % of max'.format( targetvalue=targetvalue - ) + ) if short: - duration = ': until HR<{targetvalue}% of max'.format(targetvalue=targetvalue) + duration = ': until HR<{targetvalue}% of max'.format( + targetvalue=targetvalue) else: duration = 'Repeat until Heart Rate is less than {targetvalue}:'.format( targetvalue=targetvalue-100. ) if short: - duration = ': until HR<{targetvalue}'.format(targetvalue=targetvalue-100) + duration = ': until HR<{targetvalue}'.format( + targetvalue=targetvalue-100) repeatID = step['durationValue'] - # try: @@ -1044,30 +1074,30 @@ def step_to_string(step,short=False): except KeyError: targettype = None - if targettype == 'HeartRate': # pragma: no cover - value = step.get('targetValue',0) - valuelow = step.get('targetValueLow',0) - valuehigh = step.get('targetValueHigh',0) + if targettype == 'HeartRate': # pragma: no cover + value = step.get('targetValue', 0) + valuelow = step.get('targetValueLow', 0) + valuehigh = step.get('targetValueHigh', 0) - if value < 10 and value>0: + if value < 10 and value > 0: target = '@ HR zone {v}'.format(v=value) else: if valuelow < 100: target = '@ HR {l} - {h} % of max'.format( - l = valuelow, - h = valuehigh, - ) + l=valuelow, + h=valuehigh, + ) else: target = '@ HR {l} - {h}'.format( - l = valuelow - 100, - h = valuehigh - 100, - ) - elif targettype == 'Power': # pragma: no cover - value = step.get('targetValue',0) - valuelow = step.get('targetValueLow',0) - valuehigh = step.get('targetValueHigh',0) + l=valuelow - 100, + h=valuehigh - 100, + ) + elif targettype == 'Power': # pragma: no cover + value = step.get('targetValue', 0) + valuelow = step.get('targetValueLow', 0) + valuehigh = step.get('targetValueHigh', 0) - if value < 10 and value>0: + if value < 10 and value > 0: target = '@ Power zone {v}'.format(v=value) elif value > 10 and value < 1000: target = '@ {v}% of FTP'.format(v=value) @@ -1076,19 +1106,19 @@ def step_to_string(step,short=False): elif valuelow > 0 and valuehigh > 0: if valuelow < 1000: target = '@ {l} - {h} % of FTP'.format( - l = valuelow, - h = valuehigh, - ) + l=valuelow, + h=valuehigh, + ) else: target = '@ {l} - {h} W'.format( - l = valuelow-1000, - h = valuehigh-1000, - ) - elif targettype == 'Speed': # pragma: no cover + l=valuelow-1000, + h=valuehigh-1000, + ) + elif targettype == 'Speed': # pragma: no cover - value = step.get('targetValue',0) - valuelow = step.get('targetValueLow',0) - valuehigh = step.get('targetValueHigh',0) + value = step.get('targetValue', 0) + valuelow = step.get('targetValueLow', 0) + valuehigh = step.get('targetValueHigh', 0) if value != 0: v = value/1000. @@ -1097,9 +1127,9 @@ def step_to_string(step,short=False): target = '@ {v} m/s {p}, per 500m'.format( v=value/1000., p=pacestring) - if short: # pragma: no cover + if short: # pragma: no cover target = '@ {p}'.format(p=pacestring) - elif valuelow != 0 and valuehigh != 0: # pragma: no cover + elif valuelow != 0 and valuehigh != 0: # pragma: no cover v = valuelow/1000. pace = 500./v pacestringlow = to_pace(pace) @@ -1109,29 +1139,28 @@ def step_to_string(step,short=False): pacestringhigh = to_pace(pace) target = '@ {l:1.2f} and {h:1.2f} m/s, ({ph} to {pl} per 500m)'.format( - l = valuelow/1000., - h = valuehigh/1000., - pl = pacestringlow, - ph = pacestringhigh, + l=valuelow/1000., + h=valuehigh/1000., + pl=pacestringlow, + ph=pacestringhigh, ) if short: target = '@ {pl} - {ph}'.format( - pl = pacestringlow, - ph = pacestringhigh, + pl=pacestringlow, + ph=pacestringhigh, ) - elif targettype == 'Cadence': # pragma: no cover - value = step.get('targetValue',0) - valuelow = step.get('targetValueLow',0) - valuehigh = step.get('targetValueHigh',0) + elif targettype == 'Cadence': # pragma: no cover + value = step.get('targetValue', 0) + valuelow = step.get('targetValueLow', 0) + valuehigh = step.get('targetValueHigh', 0) if value != 0: target = '@ {v} SPM'.format(v=value) elif valuelow != 0 and valuehigh != 0: target = '@ {l} - {h} SPM'.format( - l = valuelow, - h = valuehigh - ) - + l=valuelow, + h=valuehigh + ) nr = step['stepId'] @@ -1139,7 +1168,7 @@ def step_to_string(step,short=False): notes = '' try: - if len(step['description']): # pragma: no cover + if len(step['description']): # pragma: no cover notes = ' - '+step['description'] except KeyError: notes = '' @@ -1150,48 +1179,49 @@ def step_to_string(step,short=False): intensity = 0 s = '{duration} {unit} {target} {repeat} {notes}'.format( - nr = nr, - name = name, + nr=nr, + name=name, unit=unit, - intensity = intensity, - duration = duration, + intensity=intensity, + duration=duration, target=target, - repeat = repeat, + repeat=repeat, notes=notes, ) if short: s = '{duration} {unit} {target} {repeat}'.format( - duration = duration, - target = target, - repeat = repeat, - unit = unit, + duration=duration, + target=target, + repeat=repeat, + unit=unit, ) - if short and intensity in ['Warmup','Cooldown','Rest']: + if short and intensity in ['Warmup', 'Cooldown', 'Rest']: s = '{intensity} {duration} {unit} {target} {repeat}'.format( - intensity = intensity, - duration = duration, - target = target, - repeat = repeat, - unit = unit + intensity=intensity, + duration=duration, + target=target, + repeat=repeat, + unit=unit ) - elif intensity in ['Warmup','Cooldown','Rest']: + elif intensity in ['Warmup', 'Cooldown', 'Rest']: s = '{intensity} {duration} {unit} {target} {repeat} {notes}'.format( - intensity = intensity, - duration = duration, - target = target, - repeat = repeat, - unit = unit, - notes = notes, + intensity=intensity, + duration=duration, + target=target, + repeat=repeat, + unit=unit, + notes=notes, ) if type == 'RepeatStep': s = 'Repeat {duration}'.format(duration=duration) - return s,type, nr, repeatID, repeatValue + return s, type, nr, repeatID, repeatValue -def strfdelta(tdelta): # pragma: no cover + +def strfdelta(tdelta): # pragma: no cover try: minutes, seconds = divmod(tdelta.seconds, 60) tenths = int(tdelta.microseconds / 1e5) @@ -1207,34 +1237,36 @@ def strfdelta(tdelta): # pragma: no cover return res + def request_is_ajax(request): is_ajax = request.META.get('HTTP_X_REQUESTED_WITH') == 'XMLHttpRequest' - #if settings.TESTING: + # if settings.TESTING: # is_ajax = True return is_ajax + def intervals_to_string(vals, units, typ): - if vals is None or units is None or typ is None: # pragma: no cover + if vals is None or units is None or typ is None: # pragma: no cover return '' - if len(vals) != len(units) or len(vals) != len(typ): # pragma: no cover + if len(vals) != len(units) or len(vals) != len(typ): # pragma: no cover return '' s = '' previous = 'rest' for i in range(len(vals)): if typ[i] == 'rest' and previous == 'rest': - if units[i] == 'min': # pragma: no cover + if units[i] == 'min': # pragma: no cover val = int(vals[i])*60 unit = 'sec' else: val = int(vals[i]) - if units[i] == 'meters': # pragma: no cover + if units[i] == 'meters': # pragma: no cover unit = 'm' if units[i] == 'seconds': unit = 'sec' - s += '+0min/{val}{unit}'.format(val=val,unit=unit) - elif typ[i] == 'rest': # pragma: no cover + s += '+0min/{val}{unit}'.format(val=val, unit=unit) + elif typ[i] == 'rest': # pragma: no cover if units[i] == 'min': val = int(vals[i])*60 unit = 'sec' @@ -1244,7 +1276,7 @@ def intervals_to_string(vals, units, typ): unit = 'm' if units[i] == 'seconds': unit = 'sec' - s += '/{val}{unit}'.format(val=val,unit=unit) + s += '/{val}{unit}'.format(val=val, unit=unit) previous = 'rest' else: # pragma: no cover # work interval if units[i] == 'min': @@ -1256,7 +1288,7 @@ def intervals_to_string(vals, units, typ): unit = 'm' if units[i] == 'seconds': unit = 'sec' - s += '+{val}{unit}'.format(val=val,unit=unit) + s += '+{val}{unit}'.format(val=val, unit=unit) previous = 'work' if s[0] == '+': @@ -1264,13 +1296,14 @@ def intervals_to_string(vals, units, typ): return s + def get_timezone_from_c2data(data): try: timezone = pytz.timezone(data['timezone']) except UnknownTimeZoneError: timezone = pytz.utc - except KeyError: # pragma: no cover + except KeyError: # pragma: no cover timezone = pytz.utc return timezone @@ -1280,7 +1313,7 @@ def get_startdatetime_from_c2data(data): timezone = get_timezone_from_c2data(data) try: startdatetime = iso8601.parse_date(data['date_utc']) - except: # pragma: no cover + except: # pragma: no cover startdatetime = iso8601.parse_date(data['date']) totaltime = data['time']/10. @@ -1302,4 +1335,4 @@ def get_startdatetime_from_c2data(data): timezone ).strftime('%H:%M:%S') - return startdatetime,starttime,workoutdate,duration,starttimeunix,timezone + return startdatetime, starttime, workoutdate, duration, starttimeunix, timezone diff --git a/rowers/validator.py b/rowers/validator.py index ee4c327d..e017c46e 100644 --- a/rowers/validator.py +++ b/rowers/validator.py @@ -1,20 +1,21 @@ from django.core.exceptions import ValidationError from django.utils.translation import gettext as _ + class LettersAndDigitsValidator: def validate(self, password, user=None): vals = { - 'Password must contain an uppercase letter.': lambda s: any(x.isupper() for x in s), - 'Password must contain a lowercase letter.': lambda s: any(x.islower() for x in s), - 'Password must contain a digit.': lambda s: any(x.isdigit() for x in s), - 'Password cannot contain white spaces.': lambda s: not any(x.isspace() for x in s) + 'Password must contain an uppercase letter.': lambda s: any(x.isupper() for x in s), + 'Password must contain a lowercase letter.': lambda s: any(x.islower() for x in s), + 'Password must contain a digit.': lambda s: any(x.isdigit() for x in s), + 'Password cannot contain white spaces.': lambda s: not any(x.isspace() for x in s) } valid = None for n, val in vals.items(): - if not val(password): - valid = False - raise ValidationError(n) + if not val(password): + valid = False + raise ValidationError(n) return valid def get_help_text(self): diff --git a/rowers/views/analysisviews.py b/rowers/views/analysisviews.py index 1a1f3cd8..0569917e 100644 --- a/rowers/views/analysisviews.py +++ b/rowers/views/analysisviews.py @@ -1,4 +1,7 @@ from __future__ import absolute_import +import time +from rowers.forms import analysischoices +from django.contrib.staticfiles import finders from __future__ import division from __future__ import print_function from __future__ import unicode_literals @@ -10,32 +13,30 @@ import simplejson from jinja2 import Template, Environment, FileSystemLoader from rowers.rower_rules import can_view_session -def floatformat(x,prec=2): # pragma: no cover - return '{x}'.format(x=round(x,prec)) -import time -env = Environment(loader = FileSystemLoader(["rowers/templates"])) +def floatformat(x, prec=2): # pragma: no cover + return '{x}'.format(x=round(x, prec)) + + +env = Environment(loader=FileSystemLoader(["rowers/templates"])) env.filters['floatformat'] = floatformat -from django.contrib.staticfiles import finders -from rowers.forms import analysischoices - # generic Analysis view - defaultoptions = { 'includereststrokes': False, - 'workouttypes':['rower','dynamic','slides'], + 'workouttypes': ['rower', 'dynamic', 'slides'], 'waterboattype': mytypes.waterboattype, - 'function':'boxplot' + 'function': 'boxplot' } @user_passes_test(ispromember, login_url="/rowers/paidplans", message="This functionality requires a Pro plan or higher. If you are already a Pro user, please log in to access this functionality", redirect_field_name=None) -@permission_required('rower.is_coach',fn=get_user_by_userid,raise_exception=True) -def analysis_new(request,userid=0,function='boxplot',teamid=0,id='',session=0): +@permission_required('rower.is_coach', fn=get_user_by_userid, raise_exception=True) +def analysis_new(request, userid=0, function='boxplot', teamid=0, id='', session=0): r = getrequestrower(request, userid=userid) user = r.user userid = user.id @@ -44,7 +45,7 @@ def analysis_new(request,userid=0,function='boxplot',teamid=0,id='',session=0): firstworkout = None if id: firstworkout = get_workout(id) - if not is_workout_team(request.user,firstworkout): # pragma: no cover + if not is_workout_team(request.user, firstworkout): # pragma: no cover raise PermissionDenied("You are not allowed to use this workout") firstworkoutquery = Workout.objects.filter(id=encoder.decode_hex(id)) @@ -55,22 +56,21 @@ def analysis_new(request,userid=0,function='boxplot',teamid=0,id='',session=0): try: thesession = PlannedSession.objects.get(id=session) - if not can_view_session(user,thesession): + if not can_view_session(user, thesession): raise PermissionDenied("you cannot view this session") except PlannedSession.DoesNotExist: thesession = None - if 'options' in request.session: options = request.session['options'] else: - options=defaultoptions + options = defaultoptions options['userid'] = userid try: workouttypes = options['workouttypes'] - except KeyError: # pragma: no cover - workouttypes = ['rower','dynamic','slides'] + except KeyError: # pragma: no cover + workouttypes = ['rower', 'dynamic', 'slides'] try: modalities = options['modalities'] @@ -84,18 +84,17 @@ def analysis_new(request,userid=0,function='boxplot',teamid=0,id='',session=0): except KeyError: worldclass = False - try: includereststrokes = options['includereststrokes'] - except KeyError: # pragma: no cover + except KeyError: # pragma: no cover includereststrokes = False if 'startdate' in request.session: startdate = iso8601.parse_date(request.session['startdate']) else: - startdate=timezone.now()-datetime.timedelta(days=42) + startdate = timezone.now()-datetime.timedelta(days=42) - if function not in [c[0] for c in analysischoices]: # pragma: no cover + if function not in [c[0] for c in analysischoices]: # pragma: no cover function = 'boxplot' if 'enddate' in request.session: @@ -126,7 +125,7 @@ def analysis_new(request,userid=0,function='boxplot',teamid=0,id='',session=0): waterboattype = optionsform.cleaned_data['waterboattype'] if modality == 'all': modalities = [m[0] for m in mytypes.workouttypes] - else: # pragma: no cover + else: # pragma: no cover modalities = [modality] if modality != 'water': waterboattype = [b[0] for b in mytypes.boattypes] @@ -139,41 +138,36 @@ def analysis_new(request,userid=0,function='boxplot',teamid=0,id='',session=0): worldclass = False options['cpoverlay'] = worldclass - chartform = AnalysisChoiceForm(request.POST) if chartform.is_valid(): for key, value in chartform.cleaned_data.items(): options[key] = value - form = WorkoutMultipleCompareForm(request.POST) if form.is_valid(): cd = form.cleaned_data selectedworkouts = cd['workouts'] ids = [int(w.id) for w in selectedworkouts] options['ids'] = ids - else: # pragma: no cover + else: # pragma: no cover ids = [] options['ids'] = ids else: thediv = '' dateform = DateRangeForm(initial={ - 'startdate':startdate, - 'enddate':enddate, - }) - - + 'startdate': startdate, + 'enddate': enddate, + }) negtypes = [] for b in mytypes.boattypes: - if b[0] not in waterboattype: # pragma: no cover + if b[0] not in waterboattype: # pragma: no cover negtypes.append(b[0]) + startdate = datetime.datetime.combine(startdate, datetime.time()) + enddate = datetime.datetime.combine(enddate, datetime.time(23, 59, 59)) - startdate = datetime.datetime.combine(startdate,datetime.time()) - enddate = datetime.datetime.combine(enddate,datetime.time(23,59,59)) - - if enddate < startdate: # pragma: no cover + if enddate < startdate: # pragma: no cover s = enddate enddate = startdate startdate = s @@ -181,59 +175,55 @@ def analysis_new(request,userid=0,function='boxplot',teamid=0,id='',session=0): # make sure the dates are not naive try: startdate = pytz.utc.localize(startdate) - except (ValueError, AttributeError): # pragma: no cover + except (ValueError, AttributeError): # pragma: no cover pass try: enddate = pytz.utc.localize(enddate) - except (ValueError, AttributeError): # pragma: no cover + except (ValueError, AttributeError): # pragma: no cover pass negtypes = [] for b in mytypes.boattypes: - if b[0] not in waterboattype: # pragma: no cover + if b[0] not in waterboattype: # pragma: no cover negtypes.append(b[0]) - - - if theteam is not None and (theteam.viewing == 'allmembers' or theteam.manager == request.user): # pragma: no cover + if theteam is not None and (theteam.viewing == 'allmembers' or theteam.manager == request.user): # pragma: no cover workouts = Workout.objects.filter(team=theteam, startdatetime__gte=startdate, startdatetime__lte=enddate, workouttype__in=modalities, - ) - elif theteam is not None and theteam.viewing == 'coachonly': # pragma: no cover - workouts = Workout.objects.filter(team=theteam,user=r, + ) + elif theteam is not None and theteam.viewing == 'coachonly': # pragma: no cover + workouts = Workout.objects.filter(team=theteam, user=r, startdatetime__gte=startdate, startdatetime__lte=enddate, workouttype__in=modalities, - ) + ) elif thesession is not None: - workouts = get_workouts_session(r,thesession) + workouts = get_workouts_session(r, thesession) else: workouts = Workout.objects.filter(user=r, startdatetime__gte=startdate, startdatetime__lte=enddate, workouttype__in=modalities, - ) + ) if firstworkout: workouts = firstworkoutquery | workouts workouts = workouts.order_by( - "-date", "-starttime" - ).exclude(boattype__in=negtypes) - - + "-date", "-starttime" + ).exclude(boattype__in=negtypes) query = request.POST.get('q') - if query: # pragma: no cover + if query: # pragma: no cover query_list = query.split() try: workouts = workouts.filter( reduce(operator.and_, - (Q(name__icontains=q) for q in query_list)) | + (Q(name__icontains=q) for q in query_list)) | reduce(operator.and_, - (Q(notes__icontains=q) for q in query_list)) - ) - searchform = SearchForm(initial={'q':query}) + (Q(notes__icontains=q) for q in query_list)) + ) + searchform = SearchForm(initial={'q': query}) except TypeError: searchform = SearchForm() else: @@ -243,7 +233,7 @@ def analysis_new(request,userid=0,function='boxplot',teamid=0,id='',session=0): form = WorkoutMultipleCompareForm() if id: form.fields["workouts"].initial = [firstworkout] - chartform = AnalysisChoiceForm(initial={'function':function}) + chartform = AnalysisChoiceForm(initial={'function': function}) selectedworkouts = Workout.objects.none() else: selectedworkouts = Workout.objects.filter(id__in=ids) @@ -251,10 +241,9 @@ def analysis_new(request,userid=0,function='boxplot',teamid=0,id='',session=0): form.fields["workouts"].queryset = workouts | selectedworkouts optionsform = AnalysisOptionsForm(initial={ - 'modality':modality, - 'waterboattype':waterboattype, - }) - + 'modality': modality, + 'waterboattype': waterboattype, + }) if r.birthdate: age = calculate_age(r.birthdate) @@ -267,45 +256,45 @@ def analysis_new(request,userid=0,function='boxplot',teamid=0,id='',session=0): request.session['enddate'] = enddatestring request.session['options'] = options - breadcrumbs = [ { - 'url':'/rowers/analysis', - 'name':'Analysis' + 'url': '/rowers/analysis', + 'name': 'Analysis' }, { - 'url':reverse('analysis_new',kwargs={'userid':userid}), + 'url': reverse('analysis_new', kwargs={'userid': userid}), 'name': 'Analysis Select' }, ] return render(request, 'user_analysis_select.html', {'workouts': workouts, - 'dateform':dateform, - 'startdate':startdate, - 'enddate':enddate, - 'rower':r, - 'breadcrumbs':breadcrumbs, - 'theuser':user, - 'the_div':thediv, - 'form':form, - 'active':'nav-analysis', - 'chartform':chartform, - 'searchform':searchform, - 'optionsform':optionsform, - 'worldclass':worldclass, - 'age':age, - 'sex':r.sex, - 'weightcategory':r.weightcategory, - 'teams':get_my_teams(request.user), + 'dateform': dateform, + 'startdate': startdate, + 'enddate': enddate, + 'rower': r, + 'breadcrumbs': breadcrumbs, + 'theuser': user, + 'the_div': thediv, + 'form': form, + 'active': 'nav-analysis', + 'chartform': chartform, + 'searchform': searchform, + 'optionsform': optionsform, + 'worldclass': worldclass, + 'age': age, + 'sex': r.sex, + 'weightcategory': r.weightcategory, + 'teams': get_my_teams(request.user), }) -def trendflexdata(workouts, options,userid=0): + +def trendflexdata(workouts, options, userid=0): includereststrokes = options['includereststrokes'] palette = options['palette'] groupby = options['groupby'] binsize = options['binsize'] - xparam = options['xparam'] + xparam = options['xparam'] yparam = options['yparam'] spmmin = options['spmmin'] spmmax = options['spmmax'] @@ -321,66 +310,62 @@ def trendflexdata(workouts, options,userid=0): int(w.id): w.__str__() for w in workouts } - fieldlist,fielddict = dataprep.getstatsfields() - fieldlist = [xparam,yparam,groupby, - 'workoutid','spm','driveenergy', + fieldlist, fielddict = dataprep.getstatsfields() + fieldlist = [xparam, yparam, groupby, + 'workoutid', 'spm', 'driveenergy', 'workoutstate'] # prepare data frame - datadf,extracols = dataprep.read_cols_df_sql(ids,fieldlist) + datadf, extracols = dataprep.read_cols_df_sql(ids, fieldlist) - if xparam == groupby: # pragma: no cover + if xparam == groupby: # pragma: no cover datadf['groupby'] = datadf[xparam] groupy = 'groupby' - datadf = dataprep.clean_df_stats(datadf,workstrokesonly=workstrokesonly) + datadf = dataprep.clean_df_stats(datadf, workstrokesonly=workstrokesonly) - - datadf = dataprep.filter_df(datadf,'spm',spmmin, + datadf = dataprep.filter_df(datadf, 'spm', spmmin, largerthan=True) - datadf = dataprep.filter_df(datadf,'spm',spmmax, + datadf = dataprep.filter_df(datadf, 'spm', spmmax, largerthan=False) - datadf = dataprep.filter_df(datadf,'driveenergy',workmin, + datadf = dataprep.filter_df(datadf, 'driveenergy', workmin, largerthan=True) - datadf = dataprep.filter_df(datadf,'driveneergy',workmax, + datadf = dataprep.filter_df(datadf, 'driveneergy', workmax, largerthan=False) - - datadf.dropna(axis=0,how='any',inplace=True) - + datadf.dropna(axis=0, how='any', inplace=True) datemapping = { - w.id:w.date for w in workouts + w.id: w.date for w in workouts } datadf['date'] = datadf['workoutid'] - datadf['date'].replace(datemapping,inplace=True) + datadf['date'].replace(datemapping, inplace=True) today = timezone.now() try: - datadf['days ago'] = list(map(lambda x : x.days, datadf.date - today)) + datadf['days ago'] = list(map(lambda x: x.days, datadf.date - today)) except TypeError: datadf['days ago'] = 0 - if groupby != 'date': try: bins = np.arange(datadf[groupby].min()-binsize, datadf[groupby].max()+binsize, binsize) - groups = datadf.groupby(pd.cut(datadf[groupby],bins,labels=False)) - except (ValueError, AttributeError): # pragma: no cover - return ('','Error: not enough data') - else: # pragma: no cover + groups = datadf.groupby( + pd.cut(datadf[groupby], bins, labels=False)) + except (ValueError, AttributeError): # pragma: no cover + return ('', 'Error: not enough data') + else: # pragma: no cover bins = np.arange(datadf['days ago'].min()-binsize, datadf['days ago'].max()+binsize, binsize, - ) + ) groups = datadf.groupby(pd.cut(datadf['days ago'], bins, - labels=False)) - + labels=False)) xvalues = groups.mean()[xparam] yvalues = groups.mean()[yparam] @@ -388,7 +373,7 @@ def trendflexdata(workouts, options,userid=0): yerror = groups.std()[yparam] groupsize = groups.count()[xparam] - mask = groupsize <= min([0.01*groupsize.sum(),0.2*groupsize.mean()]) + mask = groupsize <= min([0.01*groupsize.sum(), 0.2*groupsize.mean()]) xvalues.loc[mask] = np.nan yvalues.loc[mask] = np.nan @@ -402,7 +387,7 @@ def trendflexdata(workouts, options,userid=0): yerror.dropna(inplace=True) groupsize.dropna(inplace=True) - if len(groupsize) == 0: # pragma: no cover + if len(groupsize) == 0: # pragma: no cover messages.error('No data in selection') url = reverse(user_multiflex_select) return HttpResponseRedirect(url) @@ -410,16 +395,15 @@ def trendflexdata(workouts, options,userid=0): groupsize = 30.*np.sqrt(groupsize/float(groupsize.max())) df = pd.DataFrame({ - xparam:xvalues, - yparam:yvalues, - 'x':xvalues, - 'y':yvalues, - 'xerror':xerror, - 'yerror':yerror, - 'groupsize':groupsize, + xparam: xvalues, + yparam: yvalues, + 'x': xvalues, + 'y': yvalues, + 'xerror': xerror, + 'yerror': yerror, + 'groupsize': groupsize, }) - if yparam == 'pace': df['y'] = dataprep.paceformatsecs(df['y']/1.0e3) @@ -428,18 +412,18 @@ def trendflexdata(workouts, options,userid=0): if groupby != 'date': try: df['groupval'] = groups.mean()[groupby] - df.loc[mask,'groupval'] = np.nan + df.loc[mask, 'groupval'] = np.nan groupcols = df['groupval'] - except (ValueError, AttributeError): # pragma: no cover - df['groupval'] = groups.mean()[groupby].fillna(value=0) - df.loc[mask,'groupval'] = np.nan + except (ValueError, AttributeError): # pragma: no cover + df['groupval'] = groups.mean()[groupby].fillna(value=0) + df.loc[mask, 'groupval'] = np.nan groupcols = df['groupval'] - except KeyError: # pragma: no cover - messages.error(request,'Data selection error') + except KeyError: # pragma: no cover + messages.error(request, 'Data selection error') url = reverse(user_multiflex_select) return HttpResponseRedirect(url) - else: # pragma: no cover + else: # pragma: no cover try: dates = groups.min()[groupby] dates.loc[mask] = np.nan @@ -451,57 +435,52 @@ def trendflexdata(workouts, options,userid=0): df['groupval'] = groups.mean()['days ago'].fillna(value=0) groupcols = 100.*np.arange(aantal)/float(aantal) - groupcols = (groupcols-groupcols.min())/(groupcols.max()-groupcols.min()) - if aantal == 1: # pragma: no cover + if aantal == 1: # pragma: no cover groupcols = np.array([1.]) - - colors = range_to_color_hex(groupcols,palette=palette) + colors = range_to_color_hex(groupcols, palette=palette) df['color'] = colors - clegendx = np.arange(0,1.2,.2) - legcolors = range_to_color_hex(clegendx,palette=palette) + clegendx = np.arange(0, 1.2, .2) + legcolors = range_to_color_hex(clegendx, palette=palette) if groupby != 'date': - clegendy = df['groupval'].min()+clegendx*(df['groupval'].max()-df['groupval'].min()) - else: # pragma: no cover + clegendy = df['groupval'].min()+clegendx * \ + (df['groupval'].max()-df['groupval'].min()) + else: # pragma: no cover clegendy = df.index.min()+clegendx*(df.index.max()-df.index.min()) - - - colorlegend = zip(range(6),clegendy,legcolors) - + colorlegend = zip(range(6), clegendy, legcolors) if userid == 0: extratitle = '' - else: # pragma: no cover + else: # pragma: no cover u = User.objects.get(id=userid) extratitle = ' '+u.first_name+' '+u.last_name + script, div = interactive_multiflex(df, xparam, yparam, + groupby, + extratitle=extratitle, + ploterrorbars=ploterrorbars, + binsize=binsize, + colorlegend=colorlegend, + spmmin=spmmin, spmmax=spmmax, + workmin=workmin, workmax=workmax) - - script,div = interactive_multiflex(df,xparam,yparam, - groupby, - extratitle=extratitle, - ploterrorbars=ploterrorbars, - binsize=binsize, - colorlegend=colorlegend, - spmmin=spmmin,spmmax=spmmax, - workmin=workmin,workmax=workmax) - - scripta= script.split('\n')[2:-1] + scripta = script.split('\n')[2:-1] script = ''.join(scripta) - return(script,div) + return(script, div) + def flexalldata(workouts, options): includereststrokes = options['includereststrokes'] xparam = options['xaxis'] yparam1 = options['yaxis1'] yparam2 = options['yaxis2'] - promember=True + promember = True workstrokesonly = not includereststrokes @@ -517,7 +496,8 @@ def flexalldata(workouts, options): scripta = script.split('\n')[2:-1] script = ''.join(scripta) - return(script,div) + return(script, div) + def histodata(workouts, options): includereststrokes = options['includereststrokes'] @@ -528,17 +508,16 @@ def histodata(workouts, options): workmin = options['workmin'] workmax = options['workmax'] - workstrokesonly = not includereststrokes - script, div = interactive_histoall(workouts,plotfield,includereststrokes, - spmmin=spmmin,spmmax=spmmax,workmin=workmin,workmax=workmax) - + script, div = interactive_histoall(workouts, plotfield, includereststrokes, + spmmin=spmmin, spmmax=spmmax, workmin=workmin, workmax=workmax) scripta = script.split('\n')[2:-1] script = ''.join(scripta) - return(script,div) + return(script, div) + def cpdata(workouts, options): userid = options['userid'] @@ -548,64 +527,62 @@ def cpdata(workouts, options): u = User.objects.get(id=userid) r = u.rower - ids = [w.id for w in workouts] - delta, cpvalue, avgpower,workoutnames,urls = dataprep.fetchcp_new(r,workouts) + delta, cpvalue, avgpower, workoutnames, urls = dataprep.fetchcp_new( + r, workouts) powerdf = pd.DataFrame({ - 'Delta':delta, - 'CP':cpvalue, - 'workout':workoutnames, - 'url':urls, + 'Delta': delta, + 'CP': cpvalue, + 'workout': workoutnames, + 'url': urls, }) + if powerdf.empty: # pragma: no cover + return('', '

    No valid data found

    ') - - if powerdf.empty: # pragma: no cover - return('','

    No valid data found

    ') - - powerdf = powerdf[powerdf['CP']>0] - powerdf.dropna(axis=0,inplace=True) - powerdf.sort_values(['Delta','CP'],ascending=[1,0],inplace=True) - powerdf.drop_duplicates(subset='Delta',keep='first',inplace=True) + powerdf = powerdf[powerdf['CP'] > 0] + powerdf.dropna(axis=0, inplace=True) + powerdf.sort_values(['Delta', 'CP'], ascending=[1, 0], inplace=True) + powerdf.drop_duplicates(subset='Delta', keep='first', inplace=True) rowername = r.user.first_name+" "+r.user.last_name wcdurations = [] wcpower = [] - if len(powerdf) !=0 : + if len(powerdf) != 0: datefirst = pd.Series(w.date for w in workouts).min() datelast = pd.Series(w.date for w in workouts).max() title = 'CP chart for {name}, from {d1} to {d2}'.format( - name = rowername, - d1 = datefirst, - d2 = datelast, + name=rowername, + d1=datefirst, + d2=datelast, ) wtype = 'water' - if workouts[0].workouttype in mytypes.otetypes: # pragma: no cover + if workouts[0].workouttype in mytypes.otetypes: # pragma: no cover wtype = 'erg' - if workouts[0].workouttype == 'bikeerg': # pragma: no cover + if workouts[0].workouttype == 'bikeerg': # pragma: no cover # for Mike wtype = 'erg' if cpoverlay: if r.birthdate: age = calculate_age(r.birthdate) - else: # pragma: no cover + else: # pragma: no cover worldclasspower = None age = 0 agerecords = CalcAgePerformance.objects.filter( - age = age, - sex = r.sex, - weightcategory = r.weightcategory + age=age, + sex=r.sex, + weightcategory=r.weightcategory ) if len(agerecords) == 0: wcpower = [] wcdurations = [] - else: # pragma: no cover + else: # pragma: no cover wcdurations = [] wcpower = [] for record in agerecords: @@ -616,10 +593,10 @@ def cpdata(workouts, options): wcdurations.append(record.duration) wcpower.append(recordpower) - res = interactive_otwcpchart(powerdf,promember=True,rowername=rowername,r=r, - cpfit=cpfit,title=title,type=wtype, + res = interactive_otwcpchart(powerdf, promember=True, rowername=rowername, r=r, + cpfit=cpfit, title=title, type=wtype, cpoverlay=cpoverlay, - wcdurations=wcdurations,wcpower=wcpower) + wcdurations=wcdurations, wcpower=wcpower) script = res[0] div = res[1] @@ -628,12 +605,12 @@ def cpdata(workouts, options): paulslope = 1 paulintercept = 1 message = res[4] - else: # pragma: no cover + else: # pragma: no cover script = '' div = '

    No ranking pieces found.

    ' paulslope = 1 paulintercept = 1 - p1 = [1,1,1,1] + p1 = [1, 1, 1, 1] ratio = 1 message = "" @@ -644,62 +621,64 @@ def cpdata(workouts, options): if minutes != 0: # minutes = 77 try: - hourvalue,tvalue = divmod(minutes,60) - except: # pragma: no cover - hourvalue = 0 + hourvalue, tvalue = divmod(minutes, 60) + except: # pragma: no cover + hourvalue = 0 tvalue = minutes # hourvalue = 1, tvalue = 17 try: hourvalue = int(hourvalue) - except TypeError: # pragma: no cover + except TypeError: # pragma: no cover hourvalue = 0 try: minutevalue = int(tvalue) - except TypeError: # pragma: no cover + except TypeError: # pragma: no cover minutevalue = 0 tvalue = int(60*(tvalue-minutevalue)) - if hourvalue >= 24: # pragma: no cover + if hourvalue >= 24: # pragma: no cover hourvalue = 23 pieceduration = datetime.time( - minute = minutevalue, - hour = hourvalue, - second = tvalue, + minute=minutevalue, + hour=hourvalue, + second=tvalue, ) - pieceseconds = 3600.*pieceduration.hour+60.*pieceduration.minute+pieceduration.second + pieceseconds = 3600.*pieceduration.hour+60. * \ + pieceduration.minute+pieceduration.second # CP model pwr = p1[0]/(1+pieceseconds/p1[2]) pwr += p1[1]/(1+pieceseconds/p1[3]) - if pwr <= 0: # pragma: no cover + if pwr <= 0: # pragma: no cover pwr = 50. - if not np.isnan(pwr): + if not np.isnan(pwr): try: pwr2 = pwr*ratio - except: # pragma: no cover + except: # pragma: no cover pwr2 = pwr duration = timedeltaconv(pieceseconds) power = int(pwr) upper = int(pwr2) - else: # pragma: no cover + else: # pragma: no cover duration = timedeltaconv(0) power = 0 upper = 0 htmly = env.get_template('otwcp.html') html_content = htmly.render({ - 'script':script, - 'the_div':div, - 'duration':duration, - 'power':power, - 'upper':upper, + 'script': script, + 'the_div': div, + 'duration': duration, + 'power': power, + 'upper': upper, }) - return (script,html_content) + return (script, html_content) + def statsdata(workouts, options): includereststrokes = options['includereststrokes'] @@ -717,19 +696,18 @@ def statsdata(workouts, options): ids = [w.id for w in workouts] datamapping = { - w.id:w.date for w in workouts - } + w.id: w.date for w in workouts + } - fieldlist,fielddict = dataprep.getstatsfields() + fieldlist, fielddict = dataprep.getstatsfields() # prepare data frame - datadf,extracols = dataprep.read_cols_df_sql(ids,fieldlist) + datadf, extracols = dataprep.read_cols_df_sql(ids, fieldlist) - - datadf = dataprep.clean_df_stats(datadf,workstrokesonly=workstrokesonly) + datadf = dataprep.clean_df_stats(datadf, workstrokesonly=workstrokesonly) try: datadf['pace'] = datadf['pace']/1000. - except KeyError: # pragma: no cover + except KeyError: # pragma: no cover pass # Create stats @@ -737,53 +715,53 @@ def statsdata(workouts, options): # fielddict.pop('workoutstate') # fielddict.pop('workoutid') - - for field,verbosename in fielddict.items(): + for field, verbosename in fielddict.items(): try: thedict = { - 'mean':datadf[field].mean(), + 'mean': datadf[field].mean(), 'min': datadf[field].min(), 'std': datadf[field].std(), 'max': datadf[field].max(), 'median': datadf[field].median(), - 'firstq':datadf[field].quantile(q=0.25), - 'thirdq':datadf[field].quantile(q=0.75), - 'verbosename':verbosename, - } + 'firstq': datadf[field].quantile(q=0.25), + 'thirdq': datadf[field].quantile(q=0.75), + 'verbosename': verbosename, + } stats[field] = thedict - except KeyError: # pragma: no cover + except KeyError: # pragma: no cover pass # Create a dict with correlation values cor = datadf.corr(method='spearman') - cor.fillna(value=0,inplace=True) + cor.fillna(value=0, inplace=True) cordict = {} - for field1,verbosename1 in fielddict.items(): + for field1, verbosename1 in fielddict.items(): thedict = {} - for field2,verbosename2 in fielddict.items(): + for field2, verbosename2 in fielddict.items(): try: - thedict[verbosename2] = cor.loc[field1,field2] - except KeyError: # pragma: no cover + thedict[verbosename2] = cor.loc[field1, field2] + except KeyError: # pragma: no cover thedict[verbosename2] = 0 cordict[verbosename1] = thedict context = { - 'stats':stats, - 'cordict':cordict, - } + 'stats': stats, + 'cordict': cordict, + } htmly = env.get_template('statsdiv.html') html_content = htmly.render(context) - return('',html_content) + return('', html_content) -def comparisondata(workouts,options): + +def comparisondata(workouts, options): includereststrokes = options['includereststrokes'] xparam = options['xaxis'] yparam1 = options['yaxis1'] plottype = options['plottype'] - promember=True + promember = True workstrokesonly = not includereststrokes @@ -791,13 +769,13 @@ def comparisondata(workouts,options): labeldict = { int(w.id): w.__str__() for w in workouts - } + } - res = interactive_multiple_compare_chart(ids,xparam,yparam1, - promember=promember, - plottype=plottype, - workstrokesonly=workstrokesonly, - labeldict=labeldict) + res = interactive_multiple_compare_chart(ids, xparam, yparam1, + promember=promember, + plottype=plottype, + workstrokesonly=workstrokesonly, + labeldict=labeldict) script = res[0] div = res[1] @@ -805,10 +783,10 @@ def comparisondata(workouts,options): scripta = script.split('\n')[2:-1] script = ''.join(scripta) - return(script,div) + return(script, div) -def boxplotdata(workouts,options): +def boxplotdata(workouts, options): includereststrokes = options['includereststrokes'] spmmin = options['spmmin'] @@ -825,134 +803,125 @@ def boxplotdata(workouts,options): int(w.id): w.__str__() for w in workouts } - datemapping = { - w.id:w.date for w in workouts + w.id: w.date for w in workouts } - - - fieldlist,fielddict = dataprep.getstatsfields() - fieldlist = [plotfield,'workoutid','spm','driveenergy', + fieldlist, fielddict = dataprep.getstatsfields() + fieldlist = [plotfield, 'workoutid', 'spm', 'driveenergy', 'workoutstate'] ids = [w.id for w in workouts] # prepare data frame - datadf,extracols = dataprep.read_cols_df_sql(ids,fieldlist) + datadf, extracols = dataprep.read_cols_df_sql(ids, fieldlist) - datadf = dataprep.clean_df_stats(datadf,workstrokesonly=workstrokesonly) + datadf = dataprep.clean_df_stats(datadf, workstrokesonly=workstrokesonly) - datadf = dataprep.filter_df(datadf,'spm',spmmin, + datadf = dataprep.filter_df(datadf, 'spm', spmmin, largerthan=True) - datadf = dataprep.filter_df(datadf,'spm',spmmax, + datadf = dataprep.filter_df(datadf, 'spm', spmmax, largerthan=False) - datadf = dataprep.filter_df(datadf,'driveenergy',workmin, + datadf = dataprep.filter_df(datadf, 'driveenergy', workmin, largerthan=True) - datadf = dataprep.filter_df(datadf,'driveneergy',workmax, + datadf = dataprep.filter_df(datadf, 'driveneergy', workmax, largerthan=False) - datadf.dropna(axis=0,how='any',inplace=True) + datadf.dropna(axis=0, how='any', inplace=True) - - datadf['workoutid'].replace(datemapping,inplace=True) - datadf.rename(columns={"workoutid":"date"},inplace=True) - datadf['date'] = pd.to_datetime(datadf['date'],errors='coerce') + datadf['workoutid'].replace(datemapping, inplace=True) + datadf.rename(columns={"workoutid": "date"}, inplace=True) + datadf['date'] = pd.to_datetime(datadf['date'], errors='coerce') datadf = datadf.dropna(subset=['date']) datadf = datadf.sort_values(['date']) - if userid == 0: # pragma: no cover + if userid == 0: # pragma: no cover extratitle = '' else: u = User.objects.get(id=userid) extratitle = ' '+u.first_name+' '+u.last_name - - - script,div = interactive_boxchart(datadf,plotfield, - extratitle=extratitle, - spmmin=spmmin,spmmax=spmmax,workmin=workmin,workmax=workmax) + script, div = interactive_boxchart(datadf, plotfield, + extratitle=extratitle, + spmmin=spmmin, spmmax=spmmax, workmin=workmin, workmax=workmax) scripta = script.split('\n')[2:-1] script = ''.join(scripta) - return(script,div) + return(script, div) -@user_passes_test(ispromember,login_url="/rowers/paidplans", + +@user_passes_test(ispromember, login_url="/rowers/paidplans", message="This functionality requires a Pro plan or higher. If you are already a Pro user, please log in to access this functionality", redirect_field_name=None) -@permission_required('rower.is_coach',fn=get_user_by_userid,raise_exception=True) -def analysis_view_data(request,userid=0): +@permission_required('rower.is_coach', fn=get_user_by_userid, raise_exception=True) +def analysis_view_data(request, userid=0): is_ajax = request_is_ajax(request) if settings.TESTING: is_ajax = True - if not is_ajax: # pragma: no cover + if not is_ajax: # pragma: no cover url = reverse('analysis_new') return HttpResponseRedirect(url) if 'options' in request.session: options = request.session['options'] - else: # pragma: no cover + else: # pragma: no cover options = defaultoptions - - if userid==0: + if userid == 0: userid = request.user.id workouts = [] try: ids = options['ids'] - except KeyError: # pragma: no cover + except KeyError: # pragma: no cover return JSONResponse({ - "script":'', - "div":'No data found' + "script": '', + "div": 'No data found' }) function = options['function'] - if not ids: # pragma: no cover + if not ids: # pragma: no cover return JSONResponse({ - "script":'', - "div":'No data found' + "script": '', + "div": 'No data found' }) - - for id in ids: try: workouts.append(Workout.objects.get(id=id)) - except Workout.DoesNotExist: # pragma: no cover + except Workout.DoesNotExist: # pragma: no cover pass if function == 'boxplot': - script, div = boxplotdata(workouts,options) - elif function == 'trendflex': # pragma: no cover - script, div = trendflexdata(workouts, options,userid=userid) - elif function == 'histo': # pragma: no cover + script, div = boxplotdata(workouts, options) + elif function == 'trendflex': # pragma: no cover + script, div = trendflexdata(workouts, options, userid=userid) + elif function == 'histo': # pragma: no cover script, div = histodata(workouts, options) - elif function == 'flexall': # pragma: no cover - script,div = flexalldata(workouts,options) - elif function == 'stats': # pragma: no cover - script,div = statsdata(workouts,options) - elif function == 'compare': # pragma: no cover - script,div = comparisondata(workouts,options) - elif function == 'cp': # pragma: no cover + elif function == 'flexall': # pragma: no cover + script, div = flexalldata(workouts, options) + elif function == 'stats': # pragma: no cover + script, div = statsdata(workouts, options) + elif function == 'compare': # pragma: no cover + script, div = comparisondata(workouts, options) + elif function == 'cp': # pragma: no cover script, div = cpdata(workouts, options) - else: # pragma: no cover + else: # pragma: no cover script = '' div = 'Unknown analysis functions' return JSONResponse({ - "script":script, - "div":div, - }) - - + "script": script, + "div": div, + }) def planrequired_view(request): - messages.info(request,"This functionality requires Coach or Self-Coach membership") + messages.info( + request, "This functionality requires Coach or Self-Coach membership") return HttpResponseRedirect(reverse('paidplans_view')) @@ -960,13 +929,13 @@ def planrequired_view(request): @user_passes_test(ispromember, login_url="/rowers/paidplans", message="This functionality requires a Pro plan or higher. If you are already a Pro user, please log in to access this functionality", redirect_field_name=None) -def create_marker_workouts_view(request,userid=0, - startdate=timezone.now()-timezone.timedelta(days=42), - enddate=timezone.now()): - therower = getrequestrower(request,userid=userid) +def create_marker_workouts_view(request, userid=0, + startdate=timezone.now()-timezone.timedelta(days=42), + enddate=timezone.now()): + therower = getrequestrower(request, userid=userid) theuser = therower.user - workouts = Workout.objects.filter(user=theuser.rower,date__gte=startdate, + workouts = Workout.objects.filter(user=theuser.rower, date__gte=startdate, date__lte=enddate, workouttype__in=mytypes.rowtypes, duplicate=False).order_by('date') @@ -976,84 +945,84 @@ def create_marker_workouts_view(request,userid=0, url = reverse('goldmedalscores_view', kwargs={ - 'userid':request.user.id, - }) + 'userid': request.user.id, + }) return HttpResponseRedirect(url) + @user_passes_test(ispromember, login_url="/rowers/paidplans", message="This functionality requires a Pro plan or higher. If you are already a Pro user, please log in to access this functionality", redirect_field_name=None) -def goldmedalscores_view(request,userid=0, +def goldmedalscores_view(request, userid=0, startdate=timezone.now()-timezone.timedelta(days=365), enddate=timezone.now()): is_ajax = request_is_ajax(request) - therower = getrequestrower(request,userid=userid) + therower = getrequestrower(request, userid=userid) theuser = therower.user - if request.method == 'POST': form = DateRangeForm(request.POST) if form.is_valid(): startdate = form.cleaned_data['startdate'] enddate = form.cleaned_data['enddate'] - if startdate > enddate: # pragma: no cover + if startdate > enddate: # pragma: no cover s = enddate enddate = startdate startdate = s else: form = DateRangeForm(initial={ - 'startdate':startdate, - 'enddate':enddate, + 'startdate': startdate, + 'enddate': enddate, }) script, div, ids = goldmedalscorechart( - theuser,startdate=startdate,enddate=enddate, + theuser, startdate=startdate, enddate=enddate, ) bestworkouts = Workout.objects.filter(id__in=ids).order_by('-date') breadcrumbs = [ { - 'url':'/rower/analysis', - 'name':'Analysis', + 'url': '/rower/analysis', + 'name': 'Analysis', }, { - 'url':reverse(goldmedalscores_view), + 'url': reverse(goldmedalscores_view), 'name': 'Gold Medal Scores' } ] - if is_ajax: # pragma: no cover + if is_ajax: # pragma: no cover response = json.dumps({ - 'script':script, - 'div':div, + 'script': script, + 'div': div, }) - return(HttpResponse(response,content_type='application/json')) + return(HttpResponse(response, content_type='application/json')) - return render(request,'goldmedalscores.html', + return render(request, 'goldmedalscores.html', { - 'rower':therower, - 'active':'nav-analysis', - 'chartscript':script, - 'breadcrumbs':breadcrumbs, - 'the_div':div, - 'form':form, - 'bestworkouts':bestworkouts, + 'rower': therower, + 'active': 'nav-analysis', + 'chartscript': script, + 'breadcrumbs': breadcrumbs, + 'the_div': div, + 'form': form, + 'bestworkouts': bestworkouts, }) + @user_passes_test(ispromember, login_url="/rowers/paidplans", message="This functionality requires a Pro plan or higher. If you are already a Pro user, please log in to access this functionality", redirect_field_name=None) -@permission_required('rower.is_coach',fn=get_user_by_userid,raise_exception=True) -def trainingzones_view(request,userid=0): +@permission_required('rower.is_coach', fn=get_user_by_userid, raise_exception=True) +def trainingzones_view(request, userid=0): is_ajax = request_is_ajax(request) - r = getrequestrower(request,userid=userid) - + r = getrequestrower(request, userid=userid) enddate = timezone.now() startdate = enddate-datetime.timedelta(days=42) @@ -1061,17 +1030,15 @@ def trainingzones_view(request,userid=0): date_agg = 'week' yaxis = 'percentage' - form = TrainingZonesForm({ - 'startdate':startdate, - 'enddate':enddate, - 'zones':zones, - 'dates':date_agg, - 'yaxis':yaxis, + 'startdate': startdate, + 'enddate': enddate, + 'zones': zones, + 'dates': date_agg, + 'yaxis': yaxis, }) - - if request.method == 'POST': # pragma: no cover + if request.method == 'POST': # pragma: no cover form = TrainingZonesForm(request.POST) if form.is_valid(): @@ -1082,16 +1049,16 @@ def trainingzones_view(request,userid=0): yaxis = form.cleaned_data['yaxis'] if date_agg == 'week': - startdate = startdate - datetime.timedelta(days = startdate.weekday()) - else: # pragma: no cover - startdate = startdate - datetime.timedelta(days = (startdate.day-1)) + startdate = startdate - datetime.timedelta(days=startdate.weekday()) + else: # pragma: no cover + startdate = startdate - datetime.timedelta(days=(startdate.day-1)) form = TrainingZonesForm(initial={ - 'startdate':startdate, - 'enddate':enddate, - 'zones':zones, - 'dates':date_agg, - 'yaxis':yaxis, + 'startdate': startdate, + 'enddate': enddate, + 'zones': zones, + 'dates': date_agg, + 'yaxis': yaxis, }) script = '' @@ -1099,34 +1066,35 @@ def trainingzones_view(request,userid=0): breadcrumbs = [ { - 'url':'/rowers/analysis', - 'name':'Analysis' + 'url': '/rowers/analysis', + 'name': 'Analysis' }, { - 'url':reverse('trainingzones_view'), + 'url': reverse('trainingzones_view'), 'name': 'Training Zones' } ] - return render(request,'trainingzones.html', + return render(request, 'trainingzones.html', { - 'active':'nav-analysis', - 'breadcrumbs':breadcrumbs, - 'rower':r, - 'the_script':script, - 'the_div':div, - 'form':form, - 'startdate':startdate, - 'enddate':enddate, - 'zones':zones, + 'active': 'nav-analysis', + 'breadcrumbs': breadcrumbs, + 'rower': r, + 'the_script': script, + 'the_div': div, + 'form': form, + 'startdate': startdate, + 'enddate': enddate, + 'zones': zones, 'dates': date_agg, 'yaxis': yaxis, } ) + @login_required() -def trainingzones_view_data(request,userid=0): - r = getrequestrower(request,userid=userid) +def trainingzones_view_data(request, userid=0): + r = getrequestrower(request, userid=userid) startdate = timezone.now()-datetime.timedelta(days=365) enddate = timezone.now() @@ -1134,24 +1102,27 @@ def trainingzones_view_data(request,userid=0): date_agg = 'week' yaxis = 'percentage' - zones = request.GET.get('zones',zones) + zones = request.GET.get('zones', zones) - date_agg = request.GET.get('dates',date_agg) + date_agg = request.GET.get('dates', date_agg) - yaxis = request.GET.get('yaxis',yaxis) + yaxis = request.GET.get('yaxis', yaxis) if request.GET.get('startdate'): - startdate = datetime.datetime.strptime(request.GET.get('startdate'),"%Y-%m-%d") + startdate = datetime.datetime.strptime( + request.GET.get('startdate'), "%Y-%m-%d") startdate = arrow.get(startdate).datetime if request.GET.get('enddate'): - enddate = datetime.datetime.strptime(request.GET.get('enddate'),"%Y-%m-%d") + enddate = datetime.datetime.strptime( + request.GET.get('enddate'), "%Y-%m-%d") enddate = arrow.get(enddate).datetime + data = get_zones_report(r, startdate, enddate, + trainingzones=zones, date_agg=date_agg, yaxis=yaxis) - data = get_zones_report(r,startdate,enddate,trainingzones=zones,date_agg=date_agg,yaxis=yaxis) - - script, div = interactive_zoneschart(r,data,startdate,enddate,trainingzones=zones,date_agg=date_agg,yaxis=yaxis) + script, div = interactive_zoneschart( + r, data, startdate, enddate, trainingzones=zones, date_agg=date_agg, yaxis=yaxis) return JSONResponse({ 'script': script, @@ -1159,19 +1130,17 @@ def trainingzones_view_data(request,userid=0): }) - @user_passes_test(ispromember, login_url="/rowers/paidplans", message="This functionality requires a Pro plan or higher. If you are already a Pro user, please log in to access this functionality", redirect_field_name=None) -@permission_required('rower.is_coach',fn=get_user_by_userid,raise_exception=True) -def performancemanager_view(request,userid=0,mode='rower', +@permission_required('rower.is_coach', fn=get_user_by_userid, raise_exception=True) +def performancemanager_view(request, userid=0, mode='rower', startdate=timezone.now()-timezone.timedelta(days=365), enddate=timezone.now()): is_ajax = request_is_ajax(request) - - therower = getrequestrower(request,userid=userid) + therower = getrequestrower(request, userid=userid) theuser = therower.user kfitness = therower.kfit @@ -1196,64 +1165,60 @@ def performancemanager_view(request,userid=0,mode='rower', therower.save() else: form = PerformanceManagerForm(initial={ - 'doform':doform, - 'dofatigue':dofatigue, - }) - + 'doform': doform, + 'dofatigue': dofatigue, + }) script, thediv, endfitness, endfatigue, endform, ids = performance_chart( - theuser,startdate=startdate,enddate=enddate, - kfitness = kfitness, - kfatigue = kfatigue, - metricchoice = metricchoice, - doform = doform, - dofatigue = dofatigue, - showtests = True, - ) + theuser, startdate=startdate, enddate=enddate, + kfitness=kfitness, + kfatigue=kfatigue, + metricchoice=metricchoice, + doform=doform, + dofatigue=dofatigue, + showtests=True, + ) - ids = pd.Series(ids,dtype='int').dropna().values + ids = pd.Series(ids, dtype='int').dropna().values bestworkouts = Workout.objects.filter(id__in=ids).order_by('-date') - breadcrumbs = [ { - 'url':'/rowers/analysis', - 'name':'Analysis' + 'url': '/rowers/analysis', + 'name': 'Analysis' }, { - 'url':reverse('performancemanager_view'), + 'url': reverse('performancemanager_view'), 'name': 'Performance Manager' } ] - if is_ajax: # pragma: no cover + if is_ajax: # pragma: no cover response = json.dumps({ - 'script':script, - 'div':thediv, - 'endform':int(endform), + 'script': script, + 'div': thediv, + 'endform': int(endform), 'endfitness': int(endfitness), 'endfatigue': int(endfatigue), }) - return(HttpResponse(response,content_type='application/json')) + return(HttpResponse(response, content_type='application/json')) - - return render(request,'performancemanager.html', + return render(request, 'performancemanager.html', { - 'rower':therower, - 'active':'nav-analysis', - 'chartscript':script, - 'breadcrumbs':breadcrumbs, - 'the_div':thediv, - 'mode':mode, - 'form':form, - 'endfitness':int(endfitness), - 'endfatigue':int(endfatigue), - 'endform':int(endform), - 'bestworkouts':bestworkouts, - }) - + 'rower': therower, + 'active': 'nav-analysis', + 'chartscript': script, + 'breadcrumbs': breadcrumbs, + 'the_div': thediv, + 'mode': mode, + 'form': form, + 'endfitness': int(endfitness), + 'endfatigue': int(endfatigue), + 'endform': int(endform), + 'bestworkouts': bestworkouts, + }) @login_required() @@ -1265,8 +1230,8 @@ def ajax_agegrouprecords(request, wcdurations = [] wcpower = [] - durations = [1,4,30,60] - distances = [100,500,1000,2000,5000,6000,10000,21097,42195] + durations = [1, 4, 30, 60] + distances = [100, 500, 1000, 2000, 5000, 6000, 10000, 21097, 42195] df = pd.DataFrame( list( @@ -1281,38 +1246,36 @@ def ajax_agegrouprecords(request, job = myqueue(queue, handle_getagegrouprecords, - jsondf,distances,durations,age,sex,weightcategory, - ) - - + jsondf, distances, durations, age, sex, weightcategory, + ) return JSONResponse( { - 'job':job.id - } - ) + 'job': job.id + } + ) # Show ranking distances including predicted paces @login_required() -@permission_required('rower.is_coach',fn=get_user_by_userid,raise_exception=True) -def rankings_view2(request,userid=0, - startdate=timezone.now()-datetime.timedelta(days=365), - enddate=timezone.now(), - deltadays=-1, - startdatestring="", - enddatestring=""): +@permission_required('rower.is_coach', fn=get_user_by_userid, raise_exception=True) +def rankings_view2(request, userid=0, + startdate=timezone.now()-datetime.timedelta(days=365), + enddate=timezone.now(), + deltadays=-1, + startdatestring="", + enddatestring=""): - if deltadays>0: # pragma: no cover + if deltadays > 0: # pragma: no cover startdate = enddate-datetime.timedelta(days=int(deltadays)) - if startdatestring != "": # pragma: no cover + if startdatestring != "": # pragma: no cover startdate = iso8601.parse_date(startdatestring) - if enddatestring != "": # pragma: no cover + if enddatestring != "": # pragma: no cover enddate = iso8601.parse_date(enddatestring) - if enddate < startdate: # pragma: no cover + if enddate < startdate: # pragma: no cover s = enddate enddate = startdate startdate = s @@ -1322,9 +1285,8 @@ def rankings_view2(request,userid=0, else: lastupdated = "1900-01-01" - - promember=0 - r = getrequestrower(request,userid=userid) + promember = 0 + r = getrequestrower(request, userid=userid) theuser = r.user wcdurations = [] @@ -1342,13 +1304,11 @@ def rankings_view2(request,userid=0, pass try: userid = options['userid'] - except KeyError: # pragma: no cover + except KeyError: # pragma: no cover userid = 0 else: options = {} - - lastupdatedtime = arrow.get(lastupdated).timestamp() current_time = arrow.utcnow().timestamp() @@ -1357,7 +1317,7 @@ def rankings_view2(request,userid=0, if str(userid) != str(theuser) or deltatime_seconds > 3600: recalc = True options['lastupdated'] = arrow.utcnow().isoformat() - else: # pragma: no cover + else: # pragma: no cover recalc = False options['userid'] = theuser.id @@ -1369,9 +1329,9 @@ def rankings_view2(request,userid=0, age = 0 agerecords = CalcAgePerformance.objects.filter( - age = age, - sex = r.sex, - weightcategory = r.weightcategory) + age=age, + sex=r.sex, + weightcategory=r.weightcategory) if len(agerecords) == 0: recalc = True @@ -1391,10 +1351,9 @@ def rankings_view2(request,userid=0, request.session['options'] = options - result = request.user.is_authenticated and ispromember(request.user) if result: - promember=1 + promember = 1 # get all indoor rows in date range @@ -1405,11 +1364,11 @@ def rankings_view2(request,userid=0, if dateform.is_valid(): startdate = dateform.cleaned_data['startdate'] enddate = dateform.cleaned_data['enddate'] - if startdate > enddate: # pragma: no cover + if startdate > enddate: # pragma: no cover s = enddate enddate = startdate startdate = s - elif request.method == 'POST' and "datedelta" in request.POST: # pragma: no cover + elif request.method == 'POST' and "datedelta" in request.POST: # pragma: no cover deltaform = DeltaDaysForm(request.POST) if deltaform.is_valid(): deltadays = deltaform.cleaned_data['deltadays'] @@ -1423,7 +1382,7 @@ def rankings_view2(request,userid=0, dateform = DateRangeForm(initial={ 'startdate': startdate, 'enddate': enddate, - }) + }) else: dateform = DateRangeForm() deltaform = DeltaDaysForm() @@ -1432,23 +1391,21 @@ def rankings_view2(request,userid=0, dateform = DateRangeForm(initial={ 'startdate': startdate, 'enddate': enddate, - }) + }) deltaform = DeltaDaysForm() # get all 2k (if any) - this rower, in date range try: r = getrower(theuser) - except Rower.DoesNotExist: # pragma: no cover + except Rower.DoesNotExist: # pragma: no cover allergworkouts = [] - r=0 - + r = 0 uu = theuser - # test to fix bug - startdate = datetime.datetime.combine(startdate,datetime.time()) - enddate = datetime.datetime.combine(enddate,datetime.time(23,59,59)) + startdate = datetime.datetime.combine(startdate, datetime.time()) + enddate = datetime.datetime.combine(enddate, datetime.time(23, 59, 59)) #enddate = enddate+datetime.timedelta(days=1) startdate = arrow.get(startdate).datetime enddate = arrow.get(enddate).datetime @@ -1457,16 +1414,14 @@ def rankings_view2(request,userid=0, theworkouts = [] thesecs = [] - - rankingdistances.sort() rankingdurations.sort() for rankingdistance in rankingdistances: workouts = Workout.objects.filter( - user=r,distance=rankingdistance, - workouttype__in=['rower','dynamic','slides'], + user=r, distance=rankingdistance, + workouttype__in=['rower', 'dynamic', 'slides'], rankingpiece=True, startdatetime__gte=startdate, startdatetime__lte=enddate).order_by('duration') @@ -1484,7 +1439,7 @@ def rankings_view2(request,userid=0, for rankingduration in rankingdurations: workouts = Workout.objects.filter( - user=r,duration=rankingduration, + user=r, duration=rankingduration, workouttype='rower', rankingpiece=True, startdatetime__gte=startdate, @@ -1506,13 +1461,12 @@ def rankings_view2(request,userid=0, thevelos = thedistances/thesecs theavpower = 2.8*(thevelos**3) - # create interactive plot - if len(thedistances) !=0 : + if len(thedistances) != 0: res = interactive_cpchart( - r,thedistances,thesecs,theavpower, - theworkouts,promember=promember, - wcdurations=wcdurations,wcpower=wcpower + r, thedistances, thesecs, theavpower, + theworkouts, promember=promember, + wcdurations=wcdurations, wcpower=wcpower ) script = res[0] div = res[1] @@ -1521,8 +1475,8 @@ def rankings_view2(request,userid=0, p1 = res[4] message = res[5] try: - testcalc = pd.Series(res[6],dtype='float')*3 - except TypeError: # pragma: no cover + testcalc = pd.Series(res[6], dtype='float')*3 + except TypeError: # pragma: no cover age = 0 else: @@ -1530,33 +1484,31 @@ def rankings_view2(request,userid=0, div = '

    No ranking pieces found.

    ' paulslope = 1 paulintercept = 1 - p1 = [1,1,1,1] + p1 = [1, 1, 1, 1] message = "" - - if request.method == 'POST' and "piece" in request.POST: # pragma: no cover + if request.method == 'POST' and "piece" in request.POST: # pragma: no cover form = PredictedPieceForm(request.POST) if form.is_valid(): value = form.cleaned_data['value'] - hourvalue,value = divmod(value,60) + hourvalue, value = divmod(value, 60) if hourvalue >= 24: hourvalue = 23 pieceunit = form.cleaned_data['pieceunit'] if pieceunit == 'd': rankingdistances.append(value) else: - rankingdurations.append(datetime.time(minute=int(value),hour=int(hourvalue))) + rankingdurations.append(datetime.time( + minute=int(value), hour=int(hourvalue))) else: form = PredictedPieceForm() rankingdistances.sort() rankingdurations.sort() - predictions = [] cpredictions = [] - for rankingdistance in rankingdistances: # Paul's model p = paulslope*np.log10(rankingdistance)+paulintercept @@ -1565,25 +1517,25 @@ def rankings_view2(request,userid=0, pwr = 2.8*(velo**3) try: pwr = int(pwr) - except (ValueError, AttributeError): # pragma: no cover + except (ValueError, AttributeError): # pragma: no cover pwr = 0 - a = {'distance':rankingdistance, - 'duration':timedeltaconv(t), - 'pace':timedeltaconv(p), - 'power':int(pwr)} + a = {'distance': rankingdistance, + 'duration': timedeltaconv(t), + 'pace': timedeltaconv(p), + 'power': int(pwr)} predictions.append(a) # CP model - pwr2 = p1[0]/(1+t/p1[2]) pwr2 += p1[1]/(1+t/p1[3]) - if pwr2 <= 0: # pragma: no cover + if pwr2 <= 0: # pragma: no cover pwr2 = 50. velo2 = (pwr2/2.8)**(1./3.) - if np.isnan(velo2) or velo2 <= 0: # pragma: no cover + if np.isnan(velo2) or velo2 <= 0: # pragma: no cover velo2 = 1.0 t2 = rankingdistance/velo2 @@ -1591,25 +1543,22 @@ def rankings_view2(request,userid=0, pwr3 = p1[0]/(1+t2/p1[2]) pwr3 += p1[1]/(1+t2/p1[3]) - if pwr3 <= 0: # pragma: no cover + if pwr3 <= 0: # pragma: no cover pwr3 = 50. velo3 = (pwr3/2.8)**(1./3.) - if np.isnan(velo3) or velo3 <= 0: # pragma: no cover + if np.isnan(velo3) or velo3 <= 0: # pragma: no cover velo3 = 1.0 t3 = rankingdistance/velo3 p3 = 500./velo3 - a = {'distance':rankingdistance, - 'duration':timedeltaconv(t3), - 'pace':timedeltaconv(p3), - 'power':int(pwr3)} + a = {'distance': rankingdistance, + 'duration': timedeltaconv(t3), + 'pace': timedeltaconv(p3), + 'power': int(pwr3)} cpredictions.append(a) - - - for rankingduration in rankingdurations: t = 3600.*rankingduration.hour t += 60.*rankingduration.minute @@ -1629,39 +1578,39 @@ def rankings_view2(request,userid=0, p = 500./velo pwr = 2.8*(velo**3) try: - a = {'distance':int(d), - 'duration':timedeltaconv(t), - 'pace':timedeltaconv(p), - 'power':int(pwr)} + a = {'distance': int(d), + 'duration': timedeltaconv(t), + 'pace': timedeltaconv(p), + 'power': int(pwr)} predictions.append(a) - except: # pragma: no cover + except: # pragma: no cover pass # CP model pwr = p1[0]/(1+t/p1[2]) pwr += p1[1]/(1+t/p1[3]) - if pwr <= 0: # pragma: no cover + if pwr <= 0: # pragma: no cover pwr = 50. velo = (pwr/2.8)**(1./3.) - if np.isnan(velo) or velo <=0: # pragma: no cover + if np.isnan(velo) or velo <= 0: # pragma: no cover velo = 1.0 d = t*velo p = 500./velo - a = {'distance':int(d), - 'duration':timedeltaconv(t), - 'pace':timedeltaconv(p), - 'power':int(pwr)} + a = {'distance': int(d), + 'duration': timedeltaconv(t), + 'pace': timedeltaconv(p), + 'power': int(pwr)} cpredictions.append(a) if recalc: wcdurations = [] wcpower = [] - durations = [1,4,30,60] - distances = [100,500,1000,2000,5000,6000,10000,21097,42195] + durations = [1, 4, 30, 60] + distances = [100, 500, 1000, 2000, 5000, 6000, 10000, 21097, 42195] df = pd.DataFrame( list( @@ -1676,71 +1625,68 @@ def rankings_view2(request,userid=0, job = myqueue(queue, handle_getagegrouprecords, - jsondf,distances,durations,age,r.sex,r.weightcategory) + jsondf, distances, durations, age, r.sex, r.weightcategory) try: - request.session['async_tasks'] += [(job.id,'agegrouprecords')] + request.session['async_tasks'] += [(job.id, 'agegrouprecords')] except KeyError: - request.session['async_tasks'] = [(job.id,'agegrouprecords')] + request.session['async_tasks'] = [(job.id, 'agegrouprecords')] - - - messages.error(request,message) + messages.error(request, message) return render(request, 'rankings.html', - {'rankingworkouts':theworkouts, - 'interactiveplot':script, - 'the_div':div, - 'predictions':predictions, - 'cpredictions':cpredictions, - 'nrdata':len(thedistances), - 'form':form, - 'dateform':dateform, - 'deltaform':deltaform, + {'rankingworkouts': theworkouts, + 'interactiveplot': script, + 'the_div': div, + 'predictions': predictions, + 'cpredictions': cpredictions, + 'nrdata': len(thedistances), + 'form': form, + 'dateform': dateform, + 'deltaform': deltaform, 'id': theuser, - 'theuser':uu, - 'rower':r, - 'active':'nav-analysis', - 'age':age, - 'sex':r.sex, - 'recalc':recalc, - 'weightcategory':r.weightcategory, - 'startdate':startdate, - 'enddate':enddate, - 'teams':get_my_teams(request.user), + 'theuser': uu, + 'rower': r, + 'active': 'nav-analysis', + 'age': age, + 'sex': r.sex, + 'recalc': recalc, + 'weightcategory': r.weightcategory, + 'startdate': startdate, + 'enddate': enddate, + 'teams': get_my_teams(request.user), }) - @login_required() -def otecp_toadmin_view(request,theuser=0, +def otecp_toadmin_view(request, theuser=0, startdate=timezone.now()-datetime.timedelta(days=365), enddate=timezone.now(), startdatestring="", enddatestring="", - ): # pragma: no cover + ): # pragma: no cover - if startdatestring != "": # pragma: no cover + if startdatestring != "": # pragma: no cover try: startdate = iso8601.parse_date(startdatestring) except ParseError: pass - if enddatestring != "": # pragma: no cover + if enddatestring != "": # pragma: no cover try: enddate = iso8601.parse_date(enddatestring) except ParseError: pass - if theuser == 0: # pragma: no cover + if theuser == 0: # pragma: no cover theuser = request.user.id u = User.objects.get(id=theuser) r = Rower.objects.get(user=u) - startdate = datetime.datetime.combine(startdate,datetime.time()) - enddate = datetime.datetime.combine(enddate,datetime.time(23,59,59)) + startdate = datetime.datetime.combine(startdate, datetime.time()) + enddate = datetime.datetime.combine(enddate, datetime.time(23, 59, 59)) theworkouts = Workout.objects.filter( - user=r,rankingpiece=True, + user=r, rankingpiece=True, workouttype__in=[ 'rower', 'dynamic', @@ -1750,24 +1696,23 @@ def otecp_toadmin_view(request,theuser=0, startdatetime__lte=enddate ).order_by("-startdatetime") - - delta,cpvalue,avgpower = dataprep.fetchcp( - r,theworkouts,table='cpergdata' + delta, cpvalue, avgpower = dataprep.fetchcp( + r, theworkouts, table='cpergdata' ) powerdf = pd.DataFrame({ - 'Delta':delta, - 'CP':cpvalue, - }) + 'Delta': delta, + 'CP': cpvalue, + }) csvfilename = 'CP_data_user_{id}.csv'.format( - id = theuser - ) + id=theuser + ) - powerdf = powerdf[powerdf['CP']>0] - powerdf.dropna(axis=0,inplace=True) - powerdf.sort_values(['Delta','CP'],ascending=[1,0],inplace=True) - powerdf.drop_duplicates(subset='Delta',keep='first',inplace=True) + powerdf = powerdf[powerdf['CP'] > 0] + powerdf.dropna(axis=0, inplace=True) + powerdf.sort_values(['Delta', 'CP'], ascending=[1, 0], inplace=True) + powerdf.drop_duplicates(subset='Delta', keep='first', inplace=True) powerdf.to_csv(csvfilename) res = myqueue(queuehigh, @@ -1779,18 +1724,19 @@ def otecp_toadmin_view(request,theuser=0, delete=True) successmessage = "The CSV file was sent to the site admin per email" - messages.info(request,successmessage) + messages.info(request, successmessage) response = HttpResponseRedirect('/rowers/list-workouts/') return response + @login_required() -def otwcp_toadmin_view(request,theuser=0, +def otwcp_toadmin_view(request, theuser=0, startdate=timezone.now()-datetime.timedelta(days=365), enddate=timezone.now(), startdatestring="", enddatestring="", - ): # pragma: no cover + ): # pragma: no cover if startdatestring != "": try: @@ -1810,34 +1756,33 @@ def otwcp_toadmin_view(request,theuser=0, u = User.objects.get(id=theuser) r = Rower.objects.get(user=u) - startdate = datetime.datetime.combine(startdate,datetime.time()) - enddate = datetime.datetime.combine(enddate,datetime.time(23,59,59)) + startdate = datetime.datetime.combine(startdate, datetime.time()) + enddate = datetime.datetime.combine(enddate, datetime.time(23, 59, 59)) theworkouts = Workout.objects.filter( - user=r,rankingpiece=True, + user=r, rankingpiece=True, workouttype='water', startdatetime__gte=startdate, startdatetime__lte=enddate ).order_by("-startdatetime") - - delta,cpvalue,avgpower = dataprep.fetchcp( - r,theworkouts,table='cpdata' + delta, cpvalue, avgpower = dataprep.fetchcp( + r, theworkouts, table='cpdata' ) powerdf = pd.DataFrame({ - 'Delta':delta, - 'CP':cpvalue, - }) + 'Delta': delta, + 'CP': cpvalue, + }) csvfilename = 'CP_data_user_{id}.csv'.format( - id = theuser - ) + id=theuser + ) - powerdf = powerdf[powerdf['CP']>0] - powerdf.dropna(axis=0,inplace=True) - powerdf.sort_values(['Delta','CP'],ascending=[1,0],inplace=True) - powerdf.drop_duplicates(subset='Delta',keep='first',inplace=True) + powerdf = powerdf[powerdf['CP'] > 0] + powerdf.dropna(axis=0, inplace=True) + powerdf.sort_values(['Delta', 'CP'], ascending=[1, 0], inplace=True) + powerdf.drop_duplicates(subset='Delta', keep='first', inplace=True) powerdf.to_csv(csvfilename) res = myqueue(queuehigh, @@ -1849,27 +1794,28 @@ def otwcp_toadmin_view(request,theuser=0, delete=True) successmessage = "The CSV file was sent to the site admin per email" - messages.info(request,successmessage) + messages.info(request, successmessage) response = HttpResponseRedirect('/rowers/list-workouts/') return response -def agegroupcpview(request,age,normalize=0): - script,div = interactive_agegroupcpchart(age,normalized=normalize) +def agegroupcpview(request, age, normalize=0): + script, div = interactive_agegroupcpchart(age, normalized=normalize) - response = render(request,'agegroupcp.html', + response = render(request, 'agegroupcp.html', { 'active': 'nav-analysis', - 'interactiveplot':script, - 'the_div':div, - } + 'interactiveplot': script, + 'the_div': div, + } ) return response -def agegrouprecordview(request,sex='male',weightcategory='hwt', - distance=2000,duration=None): + +def agegrouprecordview(request, sex='male', weightcategory='hwt', + distance=2000, duration=None): if not duration: df = pd.DataFrame( list( @@ -1892,38 +1838,38 @@ def agegrouprecordview(request,sex='male',weightcategory='hwt', ) ) - - script,div = interactive_agegroup_plot(df,sex=sex,distance=distance, - duration=duration, - weightcategory=weightcategory) + script, div = interactive_agegroup_plot(df, sex=sex, distance=distance, + duration=duration, + weightcategory=weightcategory) return render(request, 'agegroupchart.html', { - 'interactiveplot':script, - 'active':'nav-analysis', - 'the_div':div, - }) + 'interactiveplot': script, + 'active': 'nav-analysis', + 'the_div': div, + }) # alert overview view -#@user_passes_test(ispromember, login_url="/rowers/paidplans", +# @user_passes_test(ispromember, login_url="/rowers/paidplans", # message="This functionality requires a Pro plan or higher. If you are already a Pro user, please log in to access this functionality", # redirect_field_name=None) + + @login_required -@permission_required('rower.is_coach',fn=get_user_by_userid,raise_exception=True) -def alerts_view(request,userid=0): - r = getrequestrower(request,userid=userid) +@permission_required('rower.is_coach', fn=get_user_by_userid, raise_exception=True) +def alerts_view(request, userid=0): + r = getrequestrower(request, userid=userid) alerts = Alert.objects.filter(rower=r).order_by('next_run') stats = [] - for alert in alerts: # pragma: no cover + for alert in alerts: # pragma: no cover stats.append(alert_get_stats(alert)) - breadcrumbs = [ { - 'url':'/rowers/analysis', + 'url': '/rowers/analysis', 'name': 'Analysis' }, { @@ -1932,22 +1878,25 @@ def alerts_view(request,userid=0): }, ] - return render(request,'alerts.html', + return render(request, 'alerts.html', { - 'breadcrumbs':breadcrumbs, - 'alerts':alerts, - 'rower':r, - 'stats':stats, + 'breadcrumbs': breadcrumbs, + 'alerts': alerts, + 'rower': r, + 'stats': stats, }) # alert create view + + @user_passes_test(ispromember, login_url="/rowers/paidplans", message="This functionality requires a Pro plan or higher. If you are already a Pro user, please log in to access this functionality", redirect_field_name=None) -@permission_required('rower.is_coach',fn=get_user_by_userid,raise_exception=True) -def alert_create_view(request,userid=0): - r = getrequestrower(request,userid=userid) - FilterFormSet = formset_factory(ConditionEditForm, formset=BaseConditionFormSet,extra=1) +@permission_required('rower.is_coach', fn=get_user_by_userid, raise_exception=True) +def alert_create_view(request, userid=0): + r = getrequestrower(request, userid=userid) + FilterFormSet = formset_factory( + ConditionEditForm, formset=BaseConditionFormSet, extra=1) filter_formset = FilterFormSet() if request.method == 'POST': @@ -1975,23 +1924,23 @@ def alert_create_view(request,userid=0): filters.append( { - 'metric':metric, - 'condition':condition, - 'value1':value1, - 'value2':value2, - } - ) + 'metric': metric, + 'condition': condition, + 'value1': value1, + 'value2': value2, + } + ) - result,message = create_alert(request.user,r,measured,period=period,emailalert=emailalert, - reststrokes=reststrokes,workouttype=workouttype, - boattype=boattype, - filter = filters, - name=name) + result, message = create_alert(request.user, r, measured, period=period, emailalert=emailalert, + reststrokes=reststrokes, workouttype=workouttype, + boattype=boattype, + filter=filters, + name=name) if result: - messages.info(request,message) + messages.info(request, message) - url = reverse('alert_edit_view',kwargs={'id':result}) + url = reverse('alert_edit_view', kwargs={'id': result}) return HttpResponseRedirect(url) else: form = AlertEditForm() @@ -1999,7 +1948,7 @@ def alert_create_view(request,userid=0): breadcrumbs = [ { - 'url':'/rowers/analysis', + 'url': '/rowers/analysis', 'name': 'Analysis' }, { @@ -2012,21 +1961,23 @@ def alert_create_view(request,userid=0): } ] - return render(request,'alert_create.html', + return render(request, 'alert_create.html', { - 'breadcrumbs':breadcrumbs, + 'breadcrumbs': breadcrumbs, 'formset': filter_formset, - 'rower':r, - 'form':form, - 'measuredform':measuredform, - }) + 'rower': r, + 'form': form, + 'measuredform': measuredform, + }) # alert report view + + @login_required() -@permission_required('rower.is_coach',fn=get_user_by_userid,raise_exception=True) -def alert_report_view(request,id=0,userid=0,nperiod=0): - r = getrequestrower(request,userid=userid) - if userid == 0: # pragma: no cover +@permission_required('rower.is_coach', fn=get_user_by_userid, raise_exception=True) +def alert_report_view(request, id=0, userid=0, nperiod=0): + r = getrequestrower(request, userid=userid) + if userid == 0: # pragma: no cover userid = request.user.id alert = Alert.objects.get(id=id) @@ -2034,75 +1985,77 @@ def alert_report_view(request,id=0,userid=0,nperiod=0): try: alert = Alert.objects.get(id=id) - except Alert.DoesNotExist: # pragma: no cover + except Alert.DoesNotExist: # pragma: no cover raise Http404("This alert doesn't exist") - - if not checkalertowner(alert,request.user): # pragma: no cover + if not checkalertowner(alert, request.user): # pragma: no cover raise PermissionDenied('You are not allowed to edit this Alert') - stats = alert_get_stats(alert,nperiod=nperiod) + stats = alert_get_stats(alert, nperiod=nperiod) is_ajax = request_is_ajax(request) if not is_ajax: return JSONResponse({ - "stats":stats, - }) + "stats": stats, + }) breadcrumbs = [ { - 'url':'/rowers/analysis', + 'url': '/rowers/analysis', 'name': 'Analysis' }, { - 'url':reverse('alerts_view'), - 'name':'Alerts', + 'url': reverse('alerts_view'), + 'name': 'Alerts', }, { 'url': reverse('alert_edit_view', - kwargs={'userid':userid,'id':alert.id}), + kwargs={'userid': userid, 'id': alert.id}), 'name': alert.name, }, { 'url': reverse('alert_report_view', - kwargs={'userid':userid,'id':alert.id}), + kwargs={'userid': userid, 'id': alert.id}), 'name': 'Report', }, - ] # pragma: no cover - return render(request,'alert_stats.html', + ] # pragma: no cover + return render(request, 'alert_stats.html', { - 'breadcrumbs':breadcrumbs, - 'stats':stats, - 'rower':r, - 'alert':alert, - 'nperiod':nperiod, - }) # pragma: no cover + 'breadcrumbs': breadcrumbs, + 'stats': stats, + 'rower': r, + 'alert': alert, + 'nperiod': nperiod, + }) # pragma: no cover # alert edit view + + @user_passes_test(ispromember, login_url="/rowers/paidplans", message="This functionality requires a Pro plan or higher. If you are already a Pro user, please log in to access this functionality", redirect_field_name=None) -@permission_required('rower.is_coach',fn=get_user_by_userid,raise_exception=True) -def alert_edit_view(request,id=0,userid=0): - r = getrequestrower(request,userid=userid) +@permission_required('rower.is_coach', fn=get_user_by_userid, raise_exception=True) +def alert_edit_view(request, id=0, userid=0): + r = getrequestrower(request, userid=userid) try: alert = Alert.objects.get(id=id) - except Alert.DoesNotExist: # pragma: no cover + except Alert.DoesNotExist: # pragma: no cover raise Http404("This alert doesn't exist") - - if alert.manager != request.user: # pragma: no cover + if alert.manager != request.user: # pragma: no cover raise PermissionDenied('You are not allowed to edit this Alert') - FilterFormSet = formset_factory(ConditionEditForm, formset=BaseConditionFormSet,extra=0) - if len(alert.filter.all()) == 0: # pragma: no cover - FilterFormSet = formset_factory(ConditionEditForm, formset=BaseConditionFormSet, extra=1) + FilterFormSet = formset_factory( + ConditionEditForm, formset=BaseConditionFormSet, extra=0) + if len(alert.filter.all()) == 0: # pragma: no cover + FilterFormSet = formset_factory( + ConditionEditForm, formset=BaseConditionFormSet, extra=1) - filter_data = [{'metric':m.metric, - 'value1':m.value1, - 'value2':m.value2, - 'condition':m.condition} + filter_data = [{'metric': m.metric, + 'value1': m.value1, + 'value2': m.value2, + 'condition': m.condition} for m in alert.filter.all()] if request.method == 'POST': @@ -2145,52 +2098,50 @@ def alert_edit_view(request,id=0,userid=0): filters.append( { - 'metric':metric, - 'condition':condition, - 'value1':value1, - 'value2':value2, - } - ) + 'metric': metric, + 'condition': condition, + 'value1': value1, + 'value2': value2, + } + ) res = alert_add_filters(alert, filters) - messages.info(request,'Alert was changed') + messages.info(request, 'Alert was changed') else: form = AlertEditForm(instance=alert) measuredform = ConditionEditForm(instance=alert.measured) filter_formset = FilterFormSet(initial=filter_data) - - breadcrumbs = [ { - 'url':'/rowers/analysis', + 'url': '/rowers/analysis', 'name': 'Analysis' }, { - 'url':reverse('alerts_view'), - 'name':'Alerts', + 'url': reverse('alerts_view'), + 'name': 'Alerts', }, { 'url': reverse('alert_edit_view', - kwargs={'userid':userid,'id':alert.id}), + kwargs={'userid': userid, 'id': alert.id}), 'name': alert.name, }, ] - - return render(request,'alert_edit.html', + return render(request, 'alert_edit.html', { - 'breadcrumbs':breadcrumbs, - 'rower':r, - 'form':form, - 'measuredform':measuredform, - 'formset':filter_formset, - 'alert':alert, - }) + 'breadcrumbs': breadcrumbs, + 'rower': r, + 'form': form, + 'measuredform': measuredform, + 'formset': filter_formset, + 'alert': alert, + }) # alert delete view + class AlertDelete(DeleteView): login_required = True model = Alert @@ -2200,33 +2151,33 @@ class AlertDelete(DeleteView): def get_context_data(self, **kwargs): context = super(AlertDelete, self).get_context_data(**kwargs) - if 'userid' in kwargs: # pragma: no cover + if 'userid' in kwargs: # pragma: no cover userid = kwargs['userid'] else: userid = 0 - context['rower'] = getrequestrower(self.request,userid=userid) + context['rower'] = getrequestrower(self.request, userid=userid) context['alert'] = self.object breadcrumbs = [ { - 'url':'/rowers/analysis', + 'url': '/rowers/analysis', 'name': 'Analysis' - }, + }, { - 'url':reverse('alerts_view'), - 'name':'Alerts', - }, + 'url': reverse('alerts_view'), + 'name': 'Alerts', + }, { 'url': reverse('alert_edit_view', - kwargs={'userid':userid,'id':self.object.pk}), + kwargs={'userid': userid, 'id': self.object.pk}), 'name': 'Alert', - }, + }, { - 'url': reverse('alert_delete_view',kwargs={'pk':self.object.pk}), + 'url': reverse('alert_delete_view', kwargs={'pk': self.object.pk}), 'name': 'Delete' - } - ] + } + ] context['breadcrumbs'] = breadcrumbs @@ -2238,59 +2189,59 @@ class AlertDelete(DeleteView): def get_object(self, *args, **kwargs): obj = super(AlertDelete, self).get_object(*args, **kwargs) - if obj.manager != self.request.user: # pragma: no cover + if obj.manager != self.request.user: # pragma: no cover raise PermissionDenied("You are not allowed to delete this Alert") # some checks return obj + @user_passes_test(ispromember, login_url="/rowers/paidplans", message="This functionality requires a Pro plan or higher. If you are already a Pro user, please log in to access this functionality", redirect_field_name=None) -@permission_required('rower.is_coach',fn=get_user_by_userid,raise_exception=True) -def history_view(request,userid=0): - r = getrequestrower(request,userid=userid) +@permission_required('rower.is_coach', fn=get_user_by_userid, raise_exception=True) +def history_view(request, userid=0): + r = getrequestrower(request, userid=userid) usertimezone = pytz.timezone(r.defaulttimezone) - time_min = datetime.time(hour=0,minute=0,second=0) - time_max = datetime.time(hour=23,minute=59,second=59) - + time_min = datetime.time(hour=0, minute=0, second=0) + time_max = datetime.time(hour=23, minute=59, second=59) if request.GET.get('startdate'): - startdate,enddate = get_dates_timeperiod(request) + startdate, enddate = get_dates_timeperiod(request) sstartdate = startdate senddate = enddate - activity_startdate = usertimezone.localize(timezone.datetime.combine(startdate,time_min)) - activity_enddate = usertimezone.localize(timezone.datetime.combine(enddate,time_max)) + activity_startdate = usertimezone.localize( + timezone.datetime.combine(startdate, time_min)) + activity_enddate = usertimezone.localize( + timezone.datetime.combine(enddate, time_max)) else: activity_enddate = timezone.now() - activity_enddate = usertimezone.localize(timezone.datetime.combine(activity_enddate.date(),time_max)) + activity_enddate = usertimezone.localize( + timezone.datetime.combine(activity_enddate.date(), time_max)) startdate = timezone.now()-datetime.timedelta(days=14) - activity_startdate = usertimezone.localize(timezone.datetime.combine(startdate.date(),time_min)) + activity_startdate = usertimezone.localize( + timezone.datetime.combine(startdate.date(), time_min)) sstartdate = activity_startdate.date senddate = activity_enddate.date startdate = sstartdate enddate = senddate - - typeselect = 'All' if request.GET.get('workouttype'): typeselect = request.GET.get('workouttype') - yaxis = request.GET.get('yaxis','duration') + yaxis = request.GET.get('yaxis', 'duration') if typeselect not in mytypes.checktypes: typeselect = 'All' - form = HistorySelectForm(initial={'startdate':activity_startdate, - 'enddate':activity_enddate, - 'workouttype':typeselect, - 'yaxis':yaxis}) - - + form = HistorySelectForm(initial={'startdate': activity_startdate, + 'enddate': activity_enddate, + 'workouttype': typeselect, + 'yaxis': yaxis}) g_workouts = Workout.objects.filter( user=r, @@ -2300,22 +2251,22 @@ def history_view(request,userid=0): privacy='visible' ).order_by("-startdatetime") - ids = [w.id for w in g_workouts] - columns = ['hr','power','time'] + columns = ['hr', 'power', 'time'] - tscript,tdiv = interactive_workouttype_piechart(g_workouts) + tscript, tdiv = interactive_workouttype_piechart(g_workouts) - totalmeters,totalhours, totalminutes, totalseconds = get_totals(g_workouts) + totalmeters, totalhours, totalminutes, totalseconds = get_totals( + g_workouts) # meters, duration per workout type wtypes = list(set([w.workouttype for w in g_workouts])) - typechoices = [("All","All")] + typechoices = [("All", "All")] for wtype in wtypes: if wtype in mytypes.checktypes: - typechoices.append((wtype,mytypes.workouttypes_ordered[wtype])) + typechoices.append((wtype, mytypes.workouttypes_ordered[wtype])) form.fields['workouttype'].choices = typechoices @@ -2323,12 +2274,12 @@ def history_view(request,userid=0): for wtype in wtypes: a_workouts = g_workouts.filter(workouttype=wtype) - wmeters, whours, wminutes,wseconds = get_totals(a_workouts) + wmeters, whours, wminutes, wseconds = get_totals(a_workouts) ddict = {} ddict['id'] = wtype try: ddict['wtype'] = mytypes.workouttypes_ordered[wtype] - except KeyError: # pragma: no cover + except KeyError: # pragma: no cover ddict['wtype'] = wtype ddict['distance'] = wmeters ddict['duration'] = "{whours}:{wminutes:02d}:{wseconds:02d}".format( @@ -2340,7 +2291,6 @@ def history_view(request,userid=0): ddict['nrworkouts'] = a_workouts.count() listofdicts.append(ddict) - # interactive hr pie chart totalscript = '' totaldiv = get_call() @@ -2354,19 +2304,18 @@ def history_view(request,userid=0): totalsdict['distance'] = totalmeters - - totalsdict['nrworkouts'] = g_workouts.count() + totalsdict['nrworkouts'] = g_workouts.count() breadcrumbs = [ { - 'url':'/rowers/analysis', - 'name':'Analysis', + 'url': '/rowers/analysis', + 'name': 'Analysis', }, { - 'url':reverse('history_view'), + 'url': reverse('history_view'), 'name': 'History', }, - ] + ] lastseven = timezone.now()-datetime.timedelta(days=7) lastfourteen = timezone.now()-datetime.timedelta(days=14) @@ -2374,61 +2323,67 @@ def history_view(request,userid=0): today = timezone.now() - lastyear = datetime.datetime(year=today.year-1,month=today.month,day=today.day) + lastyear = datetime.datetime( + year=today.year-1, month=today.month, day=today.day) - firstmay = datetime.datetime(year=today.year,month=5,day=1).astimezone(usertimezone) - if firstmay>today: # pragma: no cover - firstmay = datetime.datetime(year=today.year-1,month=5,day=1) + firstmay = datetime.datetime( + year=today.year, month=5, day=1).astimezone(usertimezone) + if firstmay > today: # pragma: no cover + firstmay = datetime.datetime(year=today.year-1, month=5, day=1) - - return render(request,'history.html', + return render(request, 'history.html', { - 'tscript':tscript, - 'tdiv':tdiv, - 'rower':r, - 'breadcrumbs':breadcrumbs, - 'active':'nav-analysis', - 'totalsdict':totalsdict, - 'typedicts':listofdicts, - 'totalscript':totalscript, - 'totaldiv':totaldiv, - 'form':form, - 'startdate':activity_startdate, - 'enddate':activity_enddate, - 'lastseven':lastseven, - 'lastfourteen':lastfourteen, - 'last28':last28, - 'lastyear':lastyear, - 'today':today, - 'workouttype':typeselect, - 'yaxis':yaxis, - 'firstmay':firstmay, - 'sstartdate':sstartdate, - 'senddate':senddate, + 'tscript': tscript, + 'tdiv': tdiv, + 'rower': r, + 'breadcrumbs': breadcrumbs, + 'active': 'nav-analysis', + 'totalsdict': totalsdict, + 'typedicts': listofdicts, + 'totalscript': totalscript, + 'totaldiv': totaldiv, + 'form': form, + 'startdate': activity_startdate, + 'enddate': activity_enddate, + 'lastseven': lastseven, + 'lastfourteen': lastfourteen, + 'last28': last28, + 'lastyear': lastyear, + 'today': today, + 'workouttype': typeselect, + 'yaxis': yaxis, + 'firstmay': firstmay, + 'sstartdate': sstartdate, + 'senddate': senddate, }) + @login_required() -def history_view_data(request,userid=0): - r = getrequestrower(request,userid=userid) +def history_view_data(request, userid=0): + r = getrequestrower(request, userid=userid) usertimezone = pytz.timezone(r.defaulttimezone) - time_min = datetime.time(hour=0,minute=0,second=0) - time_max = datetime.time(hour=23,minute=59,second=59) + time_min = datetime.time(hour=0, minute=0, second=0) + time_max = datetime.time(hour=23, minute=59, second=59) startdate = timezone.now()-datetime.timedelta(days=14) enddate = timezone.now() - activity_enddate = usertimezone.localize(timezone.datetime.combine(enddate.date(),time_max)) - activity_startdate = usertimezone.localize(timezone.datetime.combine(startdate.date(),time_min)) - + activity_enddate = usertimezone.localize( + timezone.datetime.combine(enddate.date(), time_max)) + activity_startdate = usertimezone.localize( + timezone.datetime.combine(startdate.date(), time_min)) if request.GET.get('startdate'): - startdate = datetime.datetime.strptime(request.GET.get('startdate'),"%Y-%m-%d") - activity_startdate = usertimezone.localize(timezone.datetime.combine(startdate,time_min)) + startdate = datetime.datetime.strptime( + request.GET.get('startdate'), "%Y-%m-%d") + activity_startdate = usertimezone.localize( + timezone.datetime.combine(startdate, time_min)) if request.GET.get('enddate'): - enddate = datetime.datetime.strptime(request.GET.get('enddate'),"%Y-%m-%d") - activity_enddate = usertimezone.localize(timezone.datetime.combine(enddate,time_max)) - + enddate = datetime.datetime.strptime( + request.GET.get('enddate'), "%Y-%m-%d") + activity_enddate = usertimezone.localize( + timezone.datetime.combine(enddate, time_max)) typeselect = 'All' @@ -2438,13 +2393,11 @@ def history_view_data(request,userid=0): if typeselect not in mytypes.checktypes: typeselect = 'All' - yaxis = request.GET.get('yaxis','duration') + yaxis = request.GET.get('yaxis', 'duration') - - if yaxis.lower() not in ['duration','rscore','trimp','distance']: # pragma: no cover + if yaxis.lower() not in ['duration', 'rscore', 'trimp', 'distance']: # pragma: no cover yaxis = 'duration' - g_workouts = Workout.objects.filter( user=r, startdatetime__gte=activity_startdate, @@ -2455,60 +2408,59 @@ def history_view_data(request,userid=0): ids = [w.id for w in g_workouts] - columns = ['hr','power','time'] + columns = ['hr', 'power', 'time'] - df = getsmallrowdata_db(columns,ids=ids) + df = getsmallrowdata_db(columns, ids=ids) try: df['deltat'] = df['time'].diff().clip(lower=0) except KeyError: pass - df = dataprep.clean_df_stats(df,workstrokesonly=True, - ignoreadvanced=True,ignorehr=False) + df = dataprep.clean_df_stats(df, workstrokesonly=True, + ignoreadvanced=True, ignorehr=False) - totalmeters,totalhours, totalminutes,totalseconds = get_totals(g_workouts) + totalmeters, totalhours, totalminutes, totalseconds = get_totals( + g_workouts) # meters, duration per workout type wtypes = list(set([w.workouttype for w in g_workouts])) - typechoices = [("All","All")] + typechoices = [("All", "All")] for wtype in wtypes: if wtype in mytypes.checktypes: - typechoices.append((wtype,mytypes.workouttypes_ordered[wtype])) + typechoices.append((wtype, mytypes.workouttypes_ordered[wtype])) listofdicts = [] for wtype in wtypes: a_workouts = g_workouts.filter(workouttype=wtype) - wmeters, whours, wminutes,wseconds = get_totals(a_workouts) + wmeters, whours, wminutes, wseconds = get_totals(a_workouts) ddict = {} - try: # pragma: no cover + try: # pragma: no cover ddict['wtype'] = mytypes.workouttypes_ordered[wtype] - except KeyError: # pragma: no cover + except KeyError: # pragma: no cover ddict['wtype'] = wtype ddict['id'] = wtype ddict['distance'] = wmeters ddict['duration'] = "{whours}:{wminutes:02d}:{wseconds:02d}".format( whours=whours, - wminutes=wminutes,wseconds=wseconds, + wminutes=wminutes, wseconds=wseconds, ) - ddf = getsmallrowdata_db(columns,ids=[w.id for w in a_workouts]) + ddf = getsmallrowdata_db(columns, ids=[w.id for w in a_workouts]) try: ddf['deltat'] = ddf['time'].diff().clip(lower=0) - except KeyError: # pragma: no cover + except KeyError: # pragma: no cover pass + ddf = dataprep.clean_df_stats(ddf, workstrokesonly=False, + ignoreadvanced=True) - ddf = dataprep.clean_df_stats(ddf,workstrokesonly=False, - ignoreadvanced=True) - - - ddict['hrmean'] = int(wavg(ddf,'hr','deltat')) + ddict['hrmean'] = int(wavg(ddf, 'hr', 'deltat')) try: ddict['hrmax'] = ddf['hr'].max().astype(int) - except (KeyError, ValueError, AttributeError): # pragma: no cover + except (KeyError, ValueError, AttributeError): # pragma: no cover ddict['hrmax'] = 0 - ddict['powermean'] = int(wavg(ddf,'power','deltat')) + ddict['powermean'] = int(wavg(ddf, 'power', 'deltat')) try: ddict['powermax'] = ddf['power'].max().astype(int) except KeyError: @@ -2524,54 +2476,52 @@ def history_view_data(request,userid=0): totalsdict['distance'] = totalmeters try: - totalsdict['powermean'] = int(wavg(df,'power','deltat')) + totalsdict['powermean'] = int(wavg(df, 'power', 'deltat')) totalsdict['powermax'] = df['power'].max().astype(int) except KeyError: totalsdict['powermean'] = 0 totalsdict['powermax'] = 0 try: - totalsdict['hrmean'] = int(wavg(df,'hr','deltat')) + totalsdict['hrmean'] = int(wavg(df, 'hr', 'deltat')) totalsdict['hrmax'] = df['hr'].max().astype(int) - except KeyError: # pragma: no cover + except KeyError: # pragma: no cover totalsdict['hrmean'] = 0 totalsdict['hrmax'] = 0 - totalsdict['nrworkouts'] = g_workouts.count() - + totalsdict['nrworkouts'] = g_workouts.count() # activity chart - activity_script, activity_div = interactive_activitychart2(g_workouts,startdate,enddate,yaxis=yaxis) - + activity_script, activity_div = interactive_activitychart2( + g_workouts, startdate, enddate, yaxis=yaxis) # interactive hr pie chart if typeselect == 'All': totalseconds = 3600*totalhours+60*totalminutes+totalseconds - totalscript,totaldiv = interactive_hr_piechart(df,r,'All Workouts', - totalseconds=totalseconds) + totalscript, totaldiv = interactive_hr_piechart(df, r, 'All Workouts', + totalseconds=totalseconds) else: a_workouts = g_workouts.filter(workouttype=typeselect) - meters, hours,minutes,seconds = get_totals(a_workouts) + meters, hours, minutes, seconds = get_totals(a_workouts) totalseconds = 3600*hours+60*minutes+seconds - ddf = getsmallrowdata_db(columns,ids=[w.id for w in a_workouts]) + ddf = getsmallrowdata_db(columns, ids=[w.id for w in a_workouts]) try: ddf['deltat'] = ddf['time'].diff().clip(lower=0) except KeyError: pass - - ddf = dataprep.clean_df_stats(ddf,workstrokesonly=True, - ignoreadvanced=True) + ddf = dataprep.clean_df_stats(ddf, workstrokesonly=True, + ignoreadvanced=True) totalscript, totaldiv = interactive_hr_piechart( - ddf,r,mytypes.workouttypes_ordered[typeselect], + ddf, r, mytypes.workouttypes_ordered[typeselect], totalseconds=totalseconds) return JSONResponse({ - 'script':totalscript, - 'div':totaldiv, - 'totalsdict':totalsdict, - 'listofdicts':listofdicts, - 'activities_script':activity_script, - 'activities_chart':activity_div, + 'script': totalscript, + 'div': totaldiv, + 'totalsdict': totalsdict, + 'listofdicts': listofdicts, + 'activities_script': activity_script, + 'activities_chart': activity_div, }) diff --git a/rowers/views/apiviews.py b/rowers/views/apiviews.py index ab78a6f7..af789c55 100644 --- a/rowers/views/apiviews.py +++ b/rowers/views/apiviews.py @@ -1,4 +1,5 @@ from __future__ import absolute_import +from rest_framework_swagger.renderers import OpenAPIRenderer, SwaggerUIRenderer from __future__ import division from __future__ import print_function from __future__ import unicode_literals @@ -11,77 +12,79 @@ import sys import arrow # Stroke data form to test API upload + + @login_required() -def strokedataform(request,id=0): +def strokedataform(request, id=0): id = encoder.decode_hex(id) try: w = Workout.objects.get(id=id) - except Workout.DoesNotExist: # pragma: no cover + except Workout.DoesNotExist: # pragma: no cover raise Http404("Workout doesn't exist") if request.method == 'GET': form = StrokeDataForm() return render(request, 'strokedata_form.html', { - 'form':form, - 'teams':get_my_teams(request.user), - 'id':id, - 'workout':w, + 'form': form, + 'teams': get_my_teams(request.user), + 'id': id, + 'workout': w, }) - elif request.method == 'POST': # pragma: no cover + elif request.method == 'POST': # pragma: no cover form = StrokeDataForm() return render(request, 'strokedata_form.html', { - 'form':form, - 'teams':get_my_teams(request.user), - 'id':id, - 'workout':w, - }) # pragma: no cover + 'form': form, + 'teams': get_my_teams(request.user), + 'id': id, + 'workout': w, + }) # pragma: no cover + @login_required() -def strokedataform_v2(request,id=0): +def strokedataform_v2(request, id=0): id = encoder.decode_hex(id) - try: w = Workout.objects.get(id=id) - except Workout.DoesNotExist: # pragma: no cover + except Workout.DoesNotExist: # pragma: no cover raise Http404("Workout doesn't exist") if request.method == 'GET': form = StrokeDataForm() return render(request, 'strokedata_form_v2.html', { - 'form':form, - 'teams':get_my_teams(request.user), - 'id':id, - 'workout':w, + 'form': form, + 'teams': get_my_teams(request.user), + 'id': id, + 'workout': w, }) - elif request.method == 'POST': # pragma: no cover + elif request.method == 'POST': # pragma: no cover form = StrokeDataForm() return render(request, 'strokedata_form_v2.html', { - 'form':form, - 'teams':get_my_teams(request.user), - 'id':id, - 'workout':w, - }) # pragma: no cover + 'form': form, + 'teams': get_my_teams(request.user), + 'id': id, + 'workout': w, + }) # pragma: no cover # Process the POSTed stroke data according to the API definition # Return the GET stroke data according to the API definition -from rest_framework_swagger.renderers import OpenAPIRenderer, SwaggerUIRenderer + @csrf_exempt @login_required() -@api_view(["GET","POST"]) +@api_view(["GET", "POST"]) @permission_classes([IsAuthenticated]) -def strokedatajson_v2(request,id): +def strokedatajson_v2(request, id): """ POST: Add Stroke data to workout GET: Get stroke data of workout @@ -89,59 +92,58 @@ def strokedatajson_v2(request,id): {"data": [{"hr": 110, "p": 3600, "spm": 53, "d": 6, "t": 12}, {"hr": 111, "p": 3600, "spm": 53, "d": 6, "t": 12}, {"hr": 111, "p": 3600, "spm": 64, "d": 6, "t": 22}, {"hr": 110, "p": 3600, "spm": 16, "d": 14, "t": 55}, {"hr": 110, "p": 3600, "spm": 16, "d": 14, "t": 82}, {"hr": 107, "p": 3600, "spm": 12, "d": 22, "t": 109}, {"hr": 107, "p": 3600, "spm": 12, "d": 22, "t": 133}, {"hr": 108, "p": 3600, "spm": 12, "d": 32, "t": 157}, {"hr": 108, "p": 3577, "spm": 12, "d": 32, "t": 157}, {"hr": 108, "p": 3411, "spm": 12, "d": 32, "t": 157}, {"hr": 108, "p": 2649, "spm": 12, "d": 32, "t": 157}, {"hr": 108, "p": 3099, "spm": 12, "d": 32, "t": 157}, {"hr": 108, "p": 3600, "spm": 12, "d": 32, "t": 157}, {"hr": 100, "p": 3600, "spm": 44, "d": 115, "t": 292}, {"hr": 99, "p": 3600, "spm": 27, "d": 129, "t": 305}, {"hr": 97, "p": 3600, "spm": 34, "d": 161, "t": 330}, {"hr": 96, "p": 3600, "spm": 25, "d": 177, "t": 344}, {"hr": 96, "p": 3494, "spm": 43, "d": 196, "t": 357}, {"hr": 98, "p": 2927, "spm": 26, "d": 235, "t": 377}, {"hr": 102, "p": 2718, "spm": 27, "d": 380, "t": 455}, {"hr": 102, "p": 2753, "spm": 9, "d": 398, "t": 472}, {"hr": 102, "p": 2864, "spm": 61, "d": 406, "t": 477}, {"hr": 101, "p": 2780, "spm": 15, "d": 484, "t": 515}, {"hr": 101, "p": 2365, "spm": 16, "d": 583, "t": 554}, {"hr": 103, "p": 1965, "spm": 16, "d": 681, "t": 592}, {"hr": 104, " """ - row = get_object_or_404(Workout,pk=id) - if row.user != request.user.rower: # pragma: no cover - return HttpResponse("You do not have permission to perform this action",status=403) + row = get_object_or_404(Workout, pk=id) + if row.user != request.user.rower: # pragma: no cover + return HttpResponse("You do not have permission to perform this action", status=403) try: id = int(id) - except ValueError: # pragma: no cover - return HttpResponse("Not a valid workout number",status=404) + except ValueError: # pragma: no cover + return HttpResponse("Not a valid workout number", status=404) if request.method == 'GET': - columns = ['spm','time','hr','pace','power','distance'] - datadf = dataprep.getsmallrowdata_db(columns,ids=[id]) - with open('apilog.log','a') as logfile: + columns = ['spm', 'time', 'hr', 'pace', 'power', 'distance'] + datadf = dataprep.getsmallrowdata_db(columns, ids=[id]) + with open('apilog.log', 'a') as logfile: logfile.write(str(timezone.now())+": ") logfile.write(request.user.username+"(strokedatajson_v2 GET) \n") data = datadf.to_json(orient='records') data2 = json.loads(data) - data2 = {"data":data2} + data2 = {"data": data2} return JSONResponse(data2) if request.method == 'POST': - with open('apilog.log','a') as logfile: + with open('apilog.log', 'a') as logfile: logfile.write(str(timezone.now())+": ") logfile.write(request.user.username+" (strokedatajson_v2 POST) \n") try: for d in request.data['data']: logfile.write(json.dumps(d)) logfile.write("\n") - except KeyError: # pragma: no cover + except KeyError: # pragma: no cover try: for d in request.data['strokedata']: logfile.write(json.dumps(d)) logfile.write("\n") except KeyError: logfile.write("No data in request.data\n") - except (AttributeError,TypeError): # pragma: no cover + except (AttributeError, TypeError): # pragma: no cover logfile.write("No data in request\n") checkdata, r = dataprep.getrowdata_db(id=row.id) - if not checkdata.empty: # pragma: no cover - return HttpResponse("Duplicate Error",status=409) + if not checkdata.empty: # pragma: no cover + return HttpResponse("Duplicate Error", status=409) df = pd.DataFrame() try: df = pd.DataFrame(request.data['data']) - except KeyError: # pragma: no cover + except KeyError: # pragma: no cover try: df = pd.DataFrame(request.data['strokedata']) except: - return HttpResponse("No JSON object could be decoded",status=400) - + return HttpResponse("No JSON object could be decoded", status=400) df.index = df.index.astype(int) df.sort_index(inplace=True) @@ -149,38 +151,36 @@ def strokedatajson_v2(request,id): #time, pace, distance,spm try: time = df['time']/1.e3 - except KeyError: # pragma: no cover + except KeyError: # pragma: no cover try: time = df['t']/10. except KeyError: - return HttpResponse("Missing time",status=400) + return HttpResponse("Missing time", status=400) try: spm = df['spm'] - except KeyError: # pragma: no cover - return HttpResponse("Missing spm",status=400) + except KeyError: # pragma: no cover + return HttpResponse("Missing spm", status=400) try: distance = df['distance'] - except KeyError: # pragma: no cover + except KeyError: # pragma: no cover try: distance = df['d']/10. except KeyError: - return HttpResponse("Missing distance",status=400) + return HttpResponse("Missing distance", status=400) try: pace = df['pace']/1.e3 - except KeyError: # pragma: no cover + except KeyError: # pragma: no cover try: pace = df['p']/10. except KeyError: - return HttpResponse("Missing pace",status=400) - - + return HttpResponse("Missing pace", status=400) try: power = df['power'] - except KeyError: # pragma: no cover + except KeyError: # pragma: no cover power = 0*time try: drivelength = df['drivelength'] @@ -240,7 +240,7 @@ def strokedatajson_v2(request,id): lapidx = 0*time try: hr = df['hr'] - except KeyError: # pragma: no cover + except KeyError: # pragma: no cover hr = 0*df['time'] try: @@ -253,257 +253,258 @@ def strokedatajson_v2(request,id): except KeyError: longitude = 0*df['time'] - starttime = totimestamp(row.startdatetime)+time[0] unixtime = starttime+time - with open('apilog.log','a') as logfile: + with open('apilog.log', 'a') as logfile: logfile.write(str(arrow.get(starttime).datetime)+": ") - logfile.write(request.user.username+"(strokedatajson_v2 POST - data parsed) \r\n") + logfile.write(request.user.username + + "(strokedatajson_v2 POST - data parsed) \r\n") - - data = pd.DataFrame({'TimeStamp (sec)':unixtime, + data = pd.DataFrame({'TimeStamp (sec)': unixtime, ' Horizontal (meters)': distance, - ' Cadence (stokes/min)':spm, - ' HRCur (bpm)':hr, - ' DragFactor':dragfactor, - ' Stroke500mPace (sec/500m)':pace, - ' Power (watts)':power, - ' DriveLength (meters)':drivelength, - ' DriveTime (ms)':drivetime, - ' StrokeRecoveryTime (ms)':strokerecoverytime, - ' AverageDriveForce (lbs)':averagedriveforce, - ' PeakDriveForce (lbs)':peakdriveforce, - ' lapIdx':lapidx, - ' ElapsedTime (sec)':time, - 'catch':catch, - 'slip':slip, - 'finish':finish, - 'wash':wash, - 'driveenergy':driveenergy, - 'peakforceangle':peakforceangle, + ' Cadence (stokes/min)': spm, + ' HRCur (bpm)': hr, + ' DragFactor': dragfactor, + ' Stroke500mPace (sec/500m)': pace, + ' Power (watts)': power, + ' DriveLength (meters)': drivelength, + ' DriveTime (ms)': drivetime, + ' StrokeRecoveryTime (ms)': strokerecoverytime, + ' AverageDriveForce (lbs)': averagedriveforce, + ' PeakDriveForce (lbs)': peakdriveforce, + ' lapIdx': lapidx, + ' ElapsedTime (sec)': time, + 'catch': catch, + 'slip': slip, + 'finish': finish, + 'wash': wash, + 'driveenergy': driveenergy, + 'peakforceangle': peakforceangle, ' latitude': latitude, ' longitude': longitude, - }) + }) r = getrower(request.user) timestr = row.startdatetime.strftime("%Y%m%d-%H%M%S") - csvfilename ='media/Import_'+timestr+'.csv' + csvfilename = 'media/Import_'+timestr+'.csv' workoutdate = row.date workoutstartdatetime = row.startdatetime - workoutenddatetime = workoutstartdatetime+datetime.timedelta(seconds=data[' ElapsedTime (sec)'].max()) + workoutenddatetime = workoutstartdatetime + \ + datetime.timedelta(seconds=data[' ElapsedTime (sec)'].max()) - duplicate = dataprep.checkduplicates(r,workoutdate,workoutstartdatetime,workoutenddatetime) + duplicate = dataprep.checkduplicates( + r, workoutdate, workoutstartdatetime, workoutenddatetime) if duplicate: row.duplicate = True row.save() - res = data.to_csv(csvfilename+'.gz',index_label='index', - compression='gzip') + res = data.to_csv(csvfilename+'.gz', index_label='index', + compression='gzip') row.csvfilename = csvfilename row.save() powerperc = 100*np.array([r.pw_ut2, r.pw_ut1, r.pw_at, - r.pw_tr,r.pw_an])/r.ftp + r.pw_tr, r.pw_an])/r.ftp ftp = float(r.ftp) if row.workouttype in mytypes.otwtypes: ftp = ftp*(100.-r.otwslack)/100. - rr = rrower(hrmax=r.max,hrut2=r.ut2, - hrut1=r.ut1,hrat=r.at, - hrtr=r.tr,hran=r.an,ftp=ftp, - powerperc=powerperc,powerzones=r.powerzones) - rowdata = rdata(csvfile=row.csvfilename,rower=rr).df + rr = rrower(hrmax=r.max, hrut2=r.ut2, + hrut1=r.ut1, hrat=r.at, + hrtr=r.tr, hran=r.an, ftp=ftp, + powerperc=powerperc, powerzones=r.powerzones) + rowdata = rdata(csvfile=row.csvfilename, rower=rr).df - datadf = dataprep.dataprep(rowdata,id=row.id,bands=True,barchart=True,otwpower=True,empower=True) + datadf = dataprep.dataprep( + rowdata, id=row.id, bands=True, barchart=True, otwpower=True, empower=True) - job = myqueue(queuehigh, handle_calctrimp, row.id, row.csvfilename, r.ftp,r.sex,r.hrftp, r.max, r.rest) + job = myqueue(queuehigh, handle_calctrimp, row.id, + row.csvfilename, r.ftp, r.sex, r.hrftp, r.max, r.rest) isbreakthrough, ishard = dataprep.checkbreakthrough(row, r) - if r.getemailnotifications and not r.emailbounced: # pragma: no cover + if r.getemailnotifications and not r.emailbounced: # pragma: no cover link = settings.SITE_URL+reverse( - r.defaultlandingpage, - kwargs = { - 'id':encoder.encode_hex(row.id), + r.defaultlandingpage, + kwargs={ + 'id': encoder.encode_hex(row.id), } ) email_sent = send_confirm(r.user, row.name, link, '') - result = uploads.do_sync(row,{},quick=True) + result = uploads.do_sync(row, {}, quick=True) - - with open('apilog.log','a') as logfile: + with open('apilog.log', 'a') as logfile: logfile.write(str(timezone.now())+": ") - logfile.write(request.user.username+" (strokedatajson_v2 POST completed successfully) \n") + logfile.write(request.user.username + + " (strokedatajson_v2 POST completed successfully) \n") return(JsonResponse( - {"workout public id":encoder.encode_hex(row.id), - "workout private id":row.id, - "status":"success", + {"workout public id": encoder.encode_hex(row.id), + "workout private id": row.id, + "status": "success", })) - #return(HttpResponse(encoder.encode_hex(row.id),status=201)) - - return HttpResponseNotAllowed("Method not supported") # pragma: no cover + # return(HttpResponse(encoder.encode_hex(row.id),status=201)) + return HttpResponseNotAllowed("Method not supported") # pragma: no cover @csrf_exempt @login_required() -@api_view(['GET','POST']) +@api_view(['GET', 'POST']) @permission_classes([IsAuthenticated]) -def strokedatajson(request,id=0): +def strokedatajson(request, id=0): """ POST: Add Stroke data to workout GET: Get stroke data of workout """ - row = get_object_or_404(Workout,pk=id) - if row.user != request.user.rower: # pragma: no cover + row = get_object_or_404(Workout, pk=id) + if row.user != request.user.rower: # pragma: no cover raise PermissionDenied("You have no access to this workout") try: id = int(id) - except ValueError: # pragma: no cover - return HttpResponse("Not a valid workout number",status=403) - + except ValueError: # pragma: no cover + return HttpResponse("Not a valid workout number", status=403) if request.method == 'GET': # currently only returns a subset. - columns = ['spm','time','hr','pace','power','distance'] - datadf = dataprep.getsmallrowdata_db(columns,ids=[id]) - with open('apilog.log','a') as logfile: + columns = ['spm', 'time', 'hr', 'pace', 'power', 'distance'] + datadf = dataprep.getsmallrowdata_db(columns, ids=[id]) + with open('apilog.log', 'a') as logfile: logfile.write(str(timezone.now())+": ") logfile.write(request.user.username+"(strokedatajson GET) \n") return JSONResponse(datadf) if request.method == 'POST': - with open('apilog.log','a') as logfile: + with open('apilog.log', 'a') as logfile: logfile.write(str(timezone.now())+": ") logfile.write(request.user.username+"(strokedatajson POST) \n") - checkdata,r = dataprep.getrowdata_db(id=row.id) - if not checkdata.empty: # pragma: no cover - return HttpResponse("Duplicate Error",status=409) + checkdata, r = dataprep.getrowdata_db(id=row.id) + if not checkdata.empty: # pragma: no cover + return HttpResponse("Duplicate Error", status=409) # strokedata = request.POST['strokedata'] # checking/validating and cleaning try: strokedata = json.loads(request.data)['strokedata'] - except: # pragma: no cover + except: # pragma: no cover try: s = json.dumps(request.data) strokedata = json.loads(s)['strokedata'] - except: # pragma: no cover - return HttpResponse("No JSON object could be decoded",status=400) + except: # pragma: no cover + return HttpResponse("No JSON object could be decoded", status=400) try: df = pd.DataFrame(strokedata) - except ValueError: # pragma: no cover - return HttpResponse("Arrays must all be same length",status=400) + except ValueError: # pragma: no cover + return HttpResponse("Arrays must all be same length", status=400) df.index = df.index.astype(int) df.sort_index(inplace=True) # time, hr, pace, spm, power, drivelength, distance, drivespeed, dragfactor, strokerecoverytime, averagedriveforce, peakdriveforce, lapidx try: time = df['time']/1.e3 - except KeyError: # pragma: no cover - return HttpResponse("There must be time values",status=400) + except KeyError: # pragma: no cover + return HttpResponse("There must be time values", status=400) aantal = len(time) pace = df['pace']/1.e3 - if len(pace) != aantal: # pragma: no cover - return HttpResponse("Pace array has incorrect length",status=400) + if len(pace) != aantal: # pragma: no cover + return HttpResponse("Pace array has incorrect length", status=400) distance = df['distance'] - if len(distance) != aantal: # pragma: no cover - return HttpResponse("Distance array has incorrect length",status=400) + if len(distance) != aantal: # pragma: no cover + return HttpResponse("Distance array has incorrect length", status=400) spm = df['spm'] - if len(spm) != aantal: # pragma: no cover - return HttpResponse("SPM array has incorrect length",status=400) + if len(spm) != aantal: # pragma: no cover + return HttpResponse("SPM array has incorrect length", status=400) - res = dataprep.testdata(time,distance,pace,spm) - if not res: # pragma: no cover - return HttpResponse("Data are not numerical",status=400) + res = dataprep.testdata(time, distance, pace, spm) + if not res: # pragma: no cover + return HttpResponse("Data are not numerical", status=400) - power = trydf(df,aantal,'power') - drivelength = trydf(df,aantal,'drivelength') - drivespeed = trydf(df,aantal,'drivespeed') - dragfactor = trydf(df,aantal,'dragfactor') - drivetime = trydf(df,aantal,'drivetime') - strokerecoverytime = trydf(df,aantal,'strokerecoverytime') - averagedriveforce = trydf(df,aantal,'averagedriveforce') - peakdriveforce = trydf(df,aantal,'peakdriveforce') - wash = trydf(df,aantal,'wash') - catch = trydf(df,aantal,'catch') - finish = trydf(df,aantal,'finish') - peakforceangle = trydf(df,aantal,'peakforceangle') - driveenergy = trydf(df,aantal,'driveenergy') - slip = trydf(df,aantal,'slip') - lapidx = trydf(df,aantal,'lapidx') - hr = trydf(df,aantal,'hr') + power = trydf(df, aantal, 'power') + drivelength = trydf(df, aantal, 'drivelength') + drivespeed = trydf(df, aantal, 'drivespeed') + dragfactor = trydf(df, aantal, 'dragfactor') + drivetime = trydf(df, aantal, 'drivetime') + strokerecoverytime = trydf(df, aantal, 'strokerecoverytime') + averagedriveforce = trydf(df, aantal, 'averagedriveforce') + peakdriveforce = trydf(df, aantal, 'peakdriveforce') + wash = trydf(df, aantal, 'wash') + catch = trydf(df, aantal, 'catch') + finish = trydf(df, aantal, 'finish') + peakforceangle = trydf(df, aantal, 'peakforceangle') + driveenergy = trydf(df, aantal, 'driveenergy') + slip = trydf(df, aantal, 'slip') + lapidx = trydf(df, aantal, 'lapidx') + hr = trydf(df, aantal, 'hr') starttime = totimestamp(row.startdatetime)+time[0] unixtime = starttime+time - with open('apilog.log','a') as logfile: + with open('apilog.log', 'a') as logfile: logfile.write(str(starttime)+": ") logfile.write(request.user.username+"(POST) \r\n") - data = pd.DataFrame({'TimeStamp (sec)':unixtime, + data = pd.DataFrame({'TimeStamp (sec)': unixtime, ' Horizontal (meters)': distance, - ' Cadence (stokes/min)':spm, - ' HRCur (bpm)':hr, - ' DragFactor':dragfactor, - ' Stroke500mPace (sec/500m)':pace, - ' Power (watts)':power, - ' DriveLength (meters)':drivelength, - ' DriveTime (ms)':drivetime, - ' StrokeRecoveryTime (ms)':strokerecoverytime, - ' AverageDriveForce (lbs)':averagedriveforce, - ' PeakDriveForce (lbs)':peakdriveforce, - ' lapIdx':lapidx, - ' ElapsedTime (sec)':time, - 'catch':catch, - 'slip':slip, - 'finish':finish, - 'wash':wash, - 'driveenergy':driveenergy, - 'peakforceangle':peakforceangle, - }) - + ' Cadence (stokes/min)': spm, + ' HRCur (bpm)': hr, + ' DragFactor': dragfactor, + ' Stroke500mPace (sec/500m)': pace, + ' Power (watts)': power, + ' DriveLength (meters)': drivelength, + ' DriveTime (ms)': drivetime, + ' StrokeRecoveryTime (ms)': strokerecoverytime, + ' AverageDriveForce (lbs)': averagedriveforce, + ' PeakDriveForce (lbs)': peakdriveforce, + ' lapIdx': lapidx, + ' ElapsedTime (sec)': time, + 'catch': catch, + 'slip': slip, + 'finish': finish, + 'wash': wash, + 'driveenergy': driveenergy, + 'peakforceangle': peakforceangle, + }) r = getrower(request.user) timestr = row.startdatetime.strftime("%Y%m%d-%H%M%S") - csvfilename ='media/Import_'+timestr+'.csv' + csvfilename = 'media/Import_'+timestr+'.csv' - res = data.to_csv(csvfilename+'.gz',index_label='index', - compression='gzip') + res = data.to_csv(csvfilename+'.gz', index_label='index', + compression='gzip') row.csvfilename = csvfilename row.save() powerperc = 100*np.array([r.pw_ut2, r.pw_ut1, r.pw_at, - r.pw_tr,r.pw_an])/r.ftp + r.pw_tr, r.pw_an])/r.ftp ftp = float(r.ftp) if row.workouttype in mytypes.otwtypes: ftp = ftp*(100.-r.otwslack)/100. - rr = rrower(hrmax=r.max,hrut2=r.ut2, - hrut1=r.ut1,hrat=r.at, - hrtr=r.tr,hran=r.an,ftp=ftp, - powerperc=powerperc,powerzones=r.powerzones) - rowdata = rdata(csvfile=row.csvfilename,rower=rr).df + rr = rrower(hrmax=r.max, hrut2=r.ut2, + hrut1=r.ut1, hrat=r.at, + hrtr=r.tr, hran=r.an, ftp=ftp, + powerperc=powerperc, powerzones=r.powerzones) + rowdata = rdata(csvfile=row.csvfilename, rower=rr).df - datadf = dataprep.dataprep(rowdata,id=row.id,bands=True,barchart=True,otwpower=True,empower=True) + datadf = dataprep.dataprep( + rowdata, id=row.id, bands=True, barchart=True, otwpower=True, empower=True) # mangling # - return HttpResponse(encoder.encode_hex(row.id),status=201) + return HttpResponse(encoder.encode_hex(row.id), status=201) - #Method not supported - return HttpResponseNotAllowed("Method not supported") # pragma: no cover + # Method not supported + return HttpResponseNotAllowed("Method not supported") # pragma: no cover diff --git a/rowers/views/errorviews.py b/rowers/views/errorviews.py index fba266b8..23f5068c 100644 --- a/rowers/views/errorviews.py +++ b/rowers/views/errorviews.py @@ -11,33 +11,38 @@ from django.test import SimpleTestCase, override_settings from django.urls import path -def servererror_view(request): # pragma: no cover +def servererror_view(request): # pragma: no cover raise ValueError # Custom error pages with Rowsandall headers + + def error500_view(request): - response = render(request,'500.html', {},status=500) + response = render(request, '500.html', {}, status=500) # context_instance = RequestContext(request)) response.status_code = 500 return response + def error404_view(request, exception): - response = render(request,'404.html', {},status=404) + response = render(request, '404.html', {}, status=404) # context_instance = RequestContext(request)) response.status_code = 404 return response + def error400_view(request, exception): - response = render(request,'400.html', {},status=400) + response = render(request, '400.html', {}, status=400) # context_instance = RequestContext(request)) response.status_code = 400 return response -def error403_view(request,*args, **kwargs): # pragma: no cover - response = render(request,'403.html', {},status=403) + +def error403_view(request, *args, **kwargs): # pragma: no cover + response = render(request, '403.html', {}, status=403) # context_instance = RequestContext(request)) response.status_code = 403 diff --git a/rowers/views/exportviews.py b/rowers/views/exportviews.py index ba0bf528..25319bc7 100644 --- a/rowers/views/exportviews.py +++ b/rowers/views/exportviews.py @@ -1,14 +1,13 @@ from rowers.views.statements import * # Export workout to TCX and send to user's email address -@permission_required('workout.change_workout',fn=get_workout_by_opaqueid,raise_exception=True) -def workout_tcxemail_view(request,id=0): + + +@permission_required('workout.change_workout', fn=get_workout_by_opaqueid, raise_exception=True) +def workout_tcxemail_view(request, id=0): r = getrower(request.user) w = get_workout(id) - - - row = rdata(csvfile=w.csvfilename) code = str(uuid4()) @@ -16,7 +15,7 @@ def workout_tcxemail_view(request,id=0): row.exporttotcx(tcxfilename) - with open(tcxfilename,'r') as f: + with open(tcxfilename, 'r') as f: response = HttpResponse(f) response['Content-Disposition'] = 'attachment; filename="%s"' % tcxfilename response['Content-Type'] = 'application/octet-stream' @@ -25,131 +24,129 @@ def workout_tcxemail_view(request,id=0): return response - - - @login_required() -def plannedsessions_icsemail_view(request,userid=0): - r = getrequestrower(request,userid=userid) - startdate,enddate = get_dates_timeperiod(request) +def plannedsessions_icsemail_view(request, userid=0): + r = getrequestrower(request, userid=userid) + startdate, enddate = get_dates_timeperiod(request) startdate = startdate.date() enddate = enddate.date() - sps = get_sessions(r,startdate=startdate,enddate=enddate) + sps = get_sessions(r, startdate=startdate, enddate=enddate) cal = Calendar() - cal.add('prodid','rowsandall') - cal.add('version','1.0') + cal.add('prodid', 'rowsandall') + cal.add('version', '1.0') for ps in sps: event = Event() comment = '{d} {u} {c}'.format( d=ps.sessionvalue, - u = ps.sessionunit, - c = ps.criterium) - event.add('summary',ps.name) - event.add('dtstart',ps.preferreddate) - event.add('dtend',ps.preferreddate) + u=ps.sessionunit, + c=ps.criterium) + event.add('summary', ps.name) + event.add('dtstart', ps.preferreddate) + event.add('dtend', ps.preferreddate) event['uid'] = 'plannedsession_'+str(ps.id) - event.add('description',ps.comment) - event.add('comment',comment) + event.add('description', ps.comment) + event.add('comment', comment) cal.add_component(event) - response = HttpResponse(cal.to_ical()) response['Content-Disposition'] = 'attachment; filename="training_plan_{u}_{d1}_{d2}.ics"'.format( - u = request.user.username, - d1 = startdate.strftime("%Y%m%d"), - d2 = enddate.strftime("%Y%m%d"), - ) + u=request.user.username, + d1=startdate.strftime("%Y%m%d"), + d2=enddate.strftime("%Y%m%d"), + ) response['Content-Type'] = 'application/octet-stream' return response + @login_required() -def plannedsessions_coach_icsemail_view(request,userid=0): - therower = getrequestplanrower(request,userid=userid) - startdate,enddate = get_dates_timeperiod(request) +def plannedsessions_coach_icsemail_view(request, userid=0): + therower = getrequestplanrower(request, userid=userid) + startdate, enddate = get_dates_timeperiod(request) startdate = startdate.date() enddate = enddate.date() if 'coach' in request.user.rower.rowerplan: - sps = get_sessions_manager(request.user,teamid=0, + sps = get_sessions_manager(request.user, teamid=0, enddate=enddate, startdate=startdate) - else: # pragma: no cover + else: # pragma: no cover rteams = therower.team.filter(viewing='allmembers') - sps = get_sessions(therower,startdate=startdate,enddate=enddate) + sps = get_sessions(therower, startdate=startdate, enddate=enddate) if therower.rowerplan != 'freecoach': rowers = [therower] - else: # pragma: no cover + else: # pragma: no cover rowers = [] - for ps in sps: # pragma: no cover + for ps in sps: # pragma: no cover if 'coach' in request.user.rower.rowerplan: rowers += ps.rower.all().exclude(rowerplan='freecoach') else: - rowers += ps.rower.filter(team__in=rteams).exclude(rowerplan='freecoach') + rowers += ps.rower.filter( + team__in=rteams).exclude(rowerplan='freecoach') rowers = list(set(rowers)) cal = Calendar() - cal.add('prodid','rowsandall') - cal.add('version','1.0') + cal.add('prodid', 'rowsandall') + cal.add('version', '1.0') - for ps in sps: # pragma: no cover + for ps in sps: # pragma: no cover event = Event() comment = '{d} {u} {c}'.format( d=ps.sessionvalue, - u = ps.sessionunit, - c = ps.criterium) - event.add('summary',ps.name) - event.add('dtstart',ps.preferreddate) - event.add('dtend',ps.preferreddate) + u=ps.sessionunit, + c=ps.criterium) + event.add('summary', ps.name) + event.add('dtstart', ps.preferreddate) + event.add('dtend', ps.preferreddate) event['uid'] = 'plannedsession_'+str(ps.id) - event.add('description',ps.comment) - event.add('comment',comment) + event.add('description', ps.comment) + event.add('comment', comment) cal.add_component(event) - icalstring = cal.to_ical() fname = "media/training_plan_{d1}_{d2}".format( - d1 = startdate.strftime("%Y%m%d"), - d2 = enddate.strftime("%Y%m%d"), - ) + d1=startdate.strftime("%Y%m%d"), + d2=enddate.strftime("%Y%m%d"), + ) url = reverse('plannedsessions_coach_view')+'?when={d1}/{d2}'.format( - d1 = startdate.strftime("%Y-%m-%d"), - d2 = enddate.strftime("%Y-%m-%d"), - ) + d1=startdate.strftime("%Y-%m-%d"), + d2=enddate.strftime("%Y-%m-%d"), + ) for rower in rowers: fname2 = fname+"_{u}.ics".format(u=rower.id) - with open(fname2,'wb') as fop: + with open(fname2, 'wb') as fop: fop.write(icalstring) - job = myqueue(queue,handle_sendemail_ical, + job = myqueue(queue, handle_sendemail_ical, rower.user.first_name, rower.user.last_name, rower.user.email, url, - fname2,debug=False) + fname2, debug=False) return HttpResponseRedirect(url) + @login_required() -def course_kmldownload_view(request,id=0): +def course_kmldownload_view(request, id=0): r = getrower(request.user) - if r.emailbounced: # pragma: no cover + if r.emailbounced: # pragma: no cover message = "Please check your email address first. Email to this address bounced." - messages.error(request,message) + messages.error(request, message) return HttpResponseRedirect( reverse('course_view', - kwargs = { - 'id':str(id), - }) - ) + kwargs={ + 'id': str(id), + }) + ) course = GeoCourse.objects.get(id=id) @@ -158,22 +155,19 @@ def course_kmldownload_view(request,id=0): kmlfilename = 'course_{id}.kml'.format(id=id) response = HttpResponse(kmlstring) - response['Content-Disposition'] = 'attachment; filename="{filename}"'.format(filename=kmlfilename) + response['Content-Disposition'] = 'attachment; filename="{filename}"'.format( + filename=kmlfilename) response['Content-Type'] = 'application/octet-stream' return response - # Export workout to GPX and send to user's email address -@permission_required('workout.change_workout',fn=get_workout_by_opaqueid,raise_exception=True) -def workout_gpxemail_view(request,id=0): +@permission_required('workout.change_workout', fn=get_workout_by_opaqueid, raise_exception=True) +def workout_gpxemail_view(request, id=0): r = getrower(request.user) w = get_workout(id) - - - row = rdata(csvfile=w.csvfilename) code = str(uuid4()) @@ -181,7 +175,7 @@ def workout_gpxemail_view(request,id=0): row.exporttogpx(gpxfilename) - with open(gpxfilename,'r') as f: + with open(gpxfilename, 'r') as f: response = HttpResponse(f) response['Content-Disposition'] = 'attachment; filename="%s"' % gpxfilename response['Content-Type'] = 'application/octet-stream' @@ -190,17 +184,19 @@ def workout_gpxemail_view(request,id=0): return response # Get Workout summary CSV file + + @login_required() def workouts_summaries_email_view(request): r = getrower(request.user) - if r.emailbounced: # pragma: no cover + if r.emailbounced: # pragma: no cover message = "Please check your email address first. Email to this address bounced." messages.error(request, message) return HttpResponseRedirect( reverse(r.defaultlandingpage, - kwargs = { - 'id':str(w.id), - }) + kwargs={ + 'id': str(w.id), + }) ) if request.method == 'POST': @@ -211,29 +207,31 @@ def workouts_summaries_email_view(request): filename = 'rowsandall_workouts_{first}_{last}.csv'.format( first=startdate, last=enddate - ) - df = dataprep.workout_summary_to_df(r,startdate=startdate,enddate=enddate) - df.to_csv(filename,encoding='utf-8') - res = myqueue(queuehigh,handle_sendemailsummary, + ) + df = dataprep.workout_summary_to_df( + r, startdate=startdate, enddate=enddate) + df.to_csv(filename, encoding='utf-8') + res = myqueue(queuehigh, handle_sendemailsummary, r.user.first_name, r.user.last_name, r.user.email, filename, - emailbounced = r.emailbounced - ) - messages.info(request,'The summary CSV file was sent to you per email') + emailbounced=r.emailbounced + ) + messages.info( + request, 'The summary CSV file was sent to you per email') else: form = DateRangeForm() - return render(request,"export_workouts.html", + return render(request, "export_workouts.html", { - 'form':form - }) + 'form': form + }) # Get Workout CSV file and send it to user's email address -@permission_required('workout.change_workout',fn=get_workout_by_opaqueid,raise_exception=True) -def workout_csvemail_view(request,id=0): +@permission_required('workout.change_workout', fn=get_workout_by_opaqueid, raise_exception=True) +def workout_csvemail_view(request, id=0): r = getrower(request.user) w = get_workout(id) @@ -261,13 +259,12 @@ def workout_csvemail_view(request,id=0): # Get Workout CSV file and send it to user's email address @login_required() -@permission_required('rower.is_staff',fn=get_user_by_userid,raise_exception=True) -def workout_csvtoadmin_view(request,id=0): # pragma: no cover +@permission_required('rower.is_staff', fn=get_user_by_userid, raise_exception=True) +def workout_csvtoadmin_view(request, id=0): # pragma: no cover message = "" r = getrower(request.user) w = get_workout(id) - csvfile = w.csvfilename res = myqueue(queuehigh, handle_sendemailcsv, @@ -277,11 +274,11 @@ def workout_csvtoadmin_view(request,id=0): # pragma: no cover csvfile) successmessage = "The CSV file was sent to the site admin per email" - messages.info(request,successmessage) + messages.info(request, successmessage) url = reverse('workout_view', - kwargs = { - 'id':encoder.encode_hex(w.id), - }) + kwargs={ + 'id': encoder.encode_hex(w.id), + }) response = HttpResponseRedirect(url) return response diff --git a/rowers/views/importviews.py b/rowers/views/importviews.py index b891dc2f..960688f5 100644 --- a/rowers/views/importviews.py +++ b/rowers/views/importviews.py @@ -1,4 +1,5 @@ from __future__ import absolute_import +from rowsandall_app.settings import NK_OAUTH_LOCATION from __future__ import division from __future__ import print_function from __future__ import unicode_literals @@ -9,59 +10,60 @@ from rowers.tasks import fetch_strava_workout import numpy -def default(o): # pragma: no cover - if isinstance(o, numpy.int64): return int(o) + +def default(o): # pragma: no cover + if isinstance(o, numpy.int64): + return int(o) raise TypeError -from rowsandall_app.settings import NK_OAUTH_LOCATION # Send workout to TP -@permission_required('workout.change_workout',fn=get_workout_by_opaqueid,raise_exception=True) -def workout_tp_upload_view(request,id=0): +@permission_required('workout.change_workout', fn=get_workout_by_opaqueid, raise_exception=True) +def workout_tp_upload_view(request, id=0): message = "" r = getrower(request.user) res = -1 try: thetoken = tp_open(r.user) - except NoTokenError: # pragma: no cover + except NoTokenError: # pragma: no cover return HttpResponseRedirect("/rowers/me/tpauthorize/") # ready to upload. Hurray - w = get_workout_by_opaqueid(request,id) + w = get_workout_by_opaqueid(request, id) r = w.user - tcxfile = tpstuff.createtpworkoutdata(w) if tcxfile: - res,reason,status_code,headers = tpstuff.uploadactivity( - r.tptoken,tcxfile, + res, reason, status_code, headers = tpstuff.uploadactivity( + r.tptoken, tcxfile, name=w.name ) - if res == 0: # pragma: no cover - message = "Upload to TrainingPeaks failed with status code "+str(status_code)+": "+reason + if res == 0: # pragma: no cover + message = "Upload to TrainingPeaks failed with status code " + \ + str(status_code)+": "+reason try: os.remove(tcxfile) except WindowsError: pass - messages.error(request,message) + messages.error(request, message) - else: # res != 0 + else: # res != 0 w.uploadedtotp = res w.save() os.remove(tcxfile) - messages.info(request,'Uploaded to TrainingPeaks') + messages.info(request, 'Uploaded to TrainingPeaks') else: # pragma: no cover # no tcxfile message = "Upload to TrainingPeaks failed" w.uploadedtotp = -1 w.save() - messages.error(request,message) + messages.error(request, message) url = reverse(r.defaultlandingpage, - kwargs = { - 'id':encoder.encode_hex(w.id), + kwargs={ + 'id': encoder.encode_hex(w.id), }) return HttpResponseRedirect(url) @@ -69,77 +71,83 @@ def workout_tp_upload_view(request,id=0): # Send workout to Strava # abundance of error logging here because there were/are some bugs -@permission_required('workout.change_workout',fn=get_workout_by_opaqueid,raise_exception=True) -def workout_strava_upload_view(request,id=0): +@permission_required('workout.change_workout', fn=get_workout_by_opaqueid, raise_exception=True) +def workout_strava_upload_view(request, id=0): message = "" r = getrower(request.user) - w = get_workout_by_opaqueid(request,id) + w = get_workout_by_opaqueid(request, id) result = -1 - comment, result = stravastuff.workout_strava_upload(r.user,w,asynchron=True) - messages.info(request,'Your workout will be synchronized to Strava in the background') + comment, result = stravastuff.workout_strava_upload( + r.user, w, asynchron=True) + messages.info( + request, 'Your workout will be synchronized to Strava in the background') url = reverse(r.defaultlandingpage, - kwargs = { - 'id':encoder.encode_hex(w.id), - } + kwargs={ + 'id': encoder.encode_hex(w.id), + } ) return HttpResponseRedirect(url) # Upload workout to Concept2 logbook + + @login_required() -def workout_c2_upload_view(request,id=0): +def workout_c2_upload_view(request, id=0): message = "" # ready to upload. Hurray w = get_workout(id) r = w.user s = 'C2 Upload Workout starttime {startdatetime} timezone {timezone} user id {userid}'.format( - startdatetime = w.startdatetime, - timezone = w.timezone, - userid = w.user.user.id + startdatetime=w.startdatetime, + timezone=w.timezone, + userid=w.user.user.id ) - dologging('c2_log.log',s) + dologging('c2_log.log', s) try: - message,c2id = c2stuff.workout_c2_upload(request.user,w,asynchron=True) - except NoTokenError: # pragma: no cover + message, c2id = c2stuff.workout_c2_upload( + request.user, w, asynchron=True) + except NoTokenError: # pragma: no cover return HttpResponseRedirect("/rowers/me/c2authorize/") - messages.info(request,'Your workout will be synchronized to the Concept2 Logbook in the background') + messages.info( + request, 'Your workout will be synchronized to the Concept2 Logbook in the background') url = reverse(r.defaultlandingpage, - kwargs = { - 'id':encoder.encode_hex(w.id) - }) - + kwargs={ + 'id': encoder.encode_hex(w.id) + }) return HttpResponseRedirect(url) - # Upload workout to SportTracks -@permission_required('workout.change_workout',fn=get_workout_by_opaqueid) -def workout_sporttracks_upload_view(request,id=0): +@permission_required('workout.change_workout', fn=get_workout_by_opaqueid) +def workout_sporttracks_upload_view(request, id=0): message = "" # ready to upload. Hurray w = get_workout(id) r = w.user - message, res = sporttracksstuff.workout_sporttracks_upload(r.user,w,asynchron=True) + message, res = sporttracksstuff.workout_sporttracks_upload( + r.user, w, asynchron=True) - messages.info(request,'Your workout will be synchronized with SportTracks in the background') + messages.info( + request, 'Your workout will be synchronized with SportTracks in the background') url = reverse(r.defaultlandingpage, - kwargs = { - 'id':encoder.encode_hex(w.id), - }) # pragma: no cover + kwargs={ + 'id': encoder.encode_hex(w.id), + }) # pragma: no cover - return HttpResponseRedirect(url) # pragma: no cover + return HttpResponseRedirect(url) # pragma: no cover # NK Logbook authorization @login_required() -def rower_nk_authorize(request): # pragma: no cover +def rower_nk_authorize(request): # pragma: no cover state = str(uuid4()) scope = "read" params = { @@ -156,10 +164,9 @@ def rower_nk_authorize(request): # pragma: no cover return HttpResponseRedirect(url) - # Concept2 authorization @login_required() -def rower_c2_authorize(request): # pragma: no cover +def rower_c2_authorize(request): # pragma: no cover # Generate a random string for the state parameter # Save it for use later to prevent xsrf attacks @@ -168,22 +175,27 @@ def rower_c2_authorize(request): # pragma: no cover params = {"client_id": C2_CLIENT_ID, "response_type": "code", "redirect_uri": C2_REDIRECT_URI} - url = "http://log.concept2.com/oauth/authorize?"+ urllib.parse.urlencode(params) + url = "http://log.concept2.com/oauth/authorize?" + \ + urllib.parse.urlencode(params) url += "&scope="+scope return HttpResponseRedirect(url) # Garmin authorization + + @login_required() -def rower_garmin_authorize(request): # pragma: no cover - authorization_url,token,secret = garmin_stuff.garmin_authorize() +def rower_garmin_authorize(request): # pragma: no cover + authorization_url, token, secret = garmin_stuff.garmin_authorize() request.session['garmin_owner_key'] = token request.session['garmin_owner_secret'] = secret return HttpResponseRedirect(authorization_url) # Strava Authorization + + @login_required() -def rower_strava_authorize(request): # pragma: no cover +def rower_strava_authorize(request): # pragma: no cover # Generate a random string for the state parameter # Save it for use later to prevent xsrf attacks @@ -194,35 +206,37 @@ def rower_strava_authorize(request): # pragma: no cover "redirect_uri": STRAVA_REDIRECT_URI, "scope": "activity:write,activity:read_all"} - url = "https://www.strava.com/oauth/authorize?"+ urllib.parse.urlencode(params) + url = "https://www.strava.com/oauth/authorize?" + \ + urllib.parse.urlencode(params) return HttpResponseRedirect(url) # Polar Authorization + + @login_required() -def rower_polar_authorize(request): # pragma: no cover +def rower_polar_authorize(request): # pragma: no cover state = str(uuid4()) params = {"client_id": POLAR_CLIENT_ID, "response_type": "code", - #"redirect_uri": POLAR_REDIRECT_URI, + # "redirect_uri": POLAR_REDIRECT_URI, "state": state, -# "scope":"accesslink.read_all" - } - url = "https://flow.polar.com/oauth2/authorization?" +urllib.parse.urlencode(params) - dologging('polar.log','Authorizing') - dologging('polar.log',url) - dologging('polar.log',params) + # "scope":"accesslink.read_all" + } + url = "https://flow.polar.com/oauth2/authorization?" + \ + urllib.parse.urlencode(params) + dologging('polar.log', 'Authorizing') + dologging('polar.log', url) + dologging('polar.log', params) return HttpResponseRedirect(url) - - # SportTracks Authorization @login_required() -def rower_sporttracks_authorize(request): # pragma: no cover +def rower_sporttracks_authorize(request): # pragma: no cover # Generate a random string for the state parameter # Save it for use later to prevent xsrf attacks @@ -233,15 +247,15 @@ def rower_sporttracks_authorize(request): # pragma: no cover "state": state, "redirect_uri": SPORTTRACKS_REDIRECT_URI} - url = "https://api.sporttracks.mobi/oauth2/authorize?"+ urllib.parse.urlencode(params) - + url = "https://api.sporttracks.mobi/oauth2/authorize?" + \ + urllib.parse.urlencode(params) return HttpResponseRedirect(url) # RP3 Authorization @login_required() -def rower_rp3_authorize(request): # pragma: no cover +def rower_rp3_authorize(request): # pragma: no cover # Generate a random string for the state parameter # Save it for use later to prevent xsrf attacks @@ -250,13 +264,16 @@ def rower_rp3_authorize(request): # pragma: no cover "response_type": "code", "redirect_uri": RP3_REDIRECT_URI, } - url = "https://rp3rowing-app.com/oauth/authorize/?" +urllib.parse.urlencode(params) + url = "https://rp3rowing-app.com/oauth/authorize/?" + \ + urllib.parse.urlencode(params) return HttpResponseRedirect(url) # TrainingPeaks Authorization + + @login_required() -def rower_tp_authorize(request): # pragma: no cover +def rower_tp_authorize(request): # pragma: no cover # Generate a random string for the state parameter # Save it for use later to prevent xsrf attacks @@ -266,7 +283,8 @@ def rower_tp_authorize(request): # pragma: no cover "redirect_uri": TP_REDIRECT_URI, "scope": "file:write", } - url = "https://oauth.trainingpeaks.com/oauth/authorize/?" +urllib.parse.urlencode(params) + url = "https://oauth.trainingpeaks.com/oauth/authorize/?" + \ + urllib.parse.urlencode(params) return HttpResponseRedirect(url) @@ -290,18 +308,16 @@ def rower_c2_token_refresh(request): r.save() successmessage = "Tokens refreshed. Good to go" - messages.info(request,successmessage) - else: # pragma: no cover + messages.info(request, successmessage) + else: # pragma: no cover message = "Something went wrong (refreshing tokens). Please reauthorize:" - messages.error(request,message) + messages.error(request, message) url = reverse('workouts_view') return HttpResponseRedirect(url) - - # TrainingPeaks token refresh. URL for manual refresh. Not visible to users @login_required() def rower_tp_token_refresh(request): @@ -322,22 +338,20 @@ def rower_tp_token_refresh(request): r.save() successmessage = "Tokens refreshed. Good to go" - messages.info(request,successmessage) + messages.info(request, successmessage) url = reverse('workouts_view') return HttpResponseRedirect(url) - - # SportTracks token refresh. URL for manual refresh. Not visible to users @login_required() def rower_sporttracks_token_refresh(request): r = getrower(request.user) res = sporttracksstuff.do_refresh_token( r.sporttracksrefreshtoken, - ) + ) access_token = res[0] expires_in = res[1] refresh_token = res[2] @@ -351,39 +365,37 @@ def rower_sporttracks_token_refresh(request): r.save() successmessage = "Tokens refreshed. Good to go" - messages.info(request,successmessage) + messages.info(request, successmessage) url = reverse('workouts_view') return HttpResponseRedirect(url) - # Concept2 Callback @login_required() def rower_process_callback(request): try: code = request.GET['code'] res = c2stuff.get_token(code) - except MultiValueDictKeyError: # pragma: no cover + except MultiValueDictKeyError: # pragma: no cover message = "The resource owner or authorization server denied the request" - messages.error(request,message) + messages.error(request, message) url = reverse('rower_exportsettings_view') return HttpResponseRedirect(url) access_token = res[0] - if access_token == 0: # pragma: no cover + if access_token == 0: # pragma: no cover message = res[1] message += ' Contact info@rowsandall.com if this behavior persists.' - messages.error(request,message) + messages.error(request, message) url = reverse('rower_exportsettings_view') return HttpResponseRedirect(url) - expires_in = res[1] refresh_token = res[2] expirydatetime = timezone.now()+datetime.timedelta(seconds=expires_in) @@ -396,38 +408,39 @@ def rower_process_callback(request): r.save() successmessage = "Tokens stored. Good to go. Please check your import/export settings" - messages.info(request,successmessage) + messages.info(request, successmessage) url = reverse('rower_exportsettings_view') return HttpResponseRedirect(url) - # dummy @login_required() -def rower_process_twittercallback(request): # pragma: no cover +def rower_process_twittercallback(request): # pragma: no cover return "dummy" # Process Polar Callback + + @login_required() def rower_process_polarcallback(request): - error = request.GET.get('error','no error') - dologging('polar.log','Callback: {error}'.format(error=error)) - if error != 'no error': # pragma: no cover - messages.error(request,error) + error = request.GET.get('error', 'no error') + dologging('polar.log', 'Callback: {error}'.format(error=error)) + if error != 'no error': # pragma: no cover + messages.error(request, error) url = reverse('rower_exportsettings_view') return HttpResponseRedirect(url) try: code = request.GET['code'] - dologging('polar.log',code) - except MultiValueDictKeyError: # pragma: no cover + dologging('polar.log', code) + except MultiValueDictKeyError: # pragma: no cover try: message = request.GET['error'] except MultiValueDictKeyError: message = "access error" - messages.error(request,message) + messages.error(request, message) url = reverse('workouts_view') return HttpResponseRedirect(url) @@ -443,19 +456,18 @@ def rower_process_polarcallback(request): if user_id: polar_user_data = polarstuff.register_user(request.user, access_token) - else: # pragma: no cover - messages.error(request,'Polar Flow Authorization Failed') + else: # pragma: no cover + messages.error(request, 'Polar Flow Authorization Failed') url = reverse('rower_exportsettings_view') return HttpResponseRedirect(url) - try: user_id2 = polar_user_data['polar-user-id'] - except KeyError: # pragma: no cover + except KeyError: # pragma: no cover user_id2 = 0 - if user_id2 != user_id: # pragma: no cover - messages.error(request,'Polar User ID error') + if user_id2 != user_id: # pragma: no cover + messages.error(request, 'Polar User ID error') #expirydatetime = timezone.now()+datetime.timedelta(seconds=expires_in) @@ -464,20 +476,21 @@ def rower_process_polarcallback(request): #r.polartokenexpirydate = expirydatetime #r.polaruserid = user_id - #r.save() + # r.save() if user_id2 == user_id: successmessage = "Tokens stored. Good to go. Please check your import/export settings" - messages.info(request,successmessage) - else: # pragma: no cover - messages.error(request,"Please contact support@rowsandall.com for help.") + messages.info(request, successmessage) + else: # pragma: no cover + messages.error( + request, "Please contact support@rowsandall.com for help.") url = reverse('rower_exportsettings_view') return HttpResponseRedirect(url) # process Garmin callback @login_required() -def rower_process_garmincallback(request): # pragma: no cover +def rower_process_garmincallback(request): # pragma: no cover r = getrower(request.user) absoluteurl = request.build_absolute_uri() @@ -486,26 +499,29 @@ def rower_process_garmincallback(request): # pragma: no cover secret = request.session['garmin_owner_secret'] except KeyError: authorization_url, key, secret = garmin_stuff.garmin_authorize() - garmintoken,garminrefreshtoken = garmin_stuff.garmin_processcallback(absoluteurl,key,secret) + garmintoken, garminrefreshtoken = garmin_stuff.garmin_processcallback( + absoluteurl, key, secret) r.garmintoken = garmintoken r.garminrefreshtoken = garminrefreshtoken r.save() successmessage = "Tokens stored. Good to go. Please check your import/export settings" - messages.info(request,successmessage) + messages.info(request, successmessage) url = reverse('rower_exportsettings_view') return HttpResponseRedirect(url) # Process NK Callback + + @login_required() -def rower_process_nkcallback(request): # pragma: no cover +def rower_process_nkcallback(request): # pragma: no cover # do stuff try: - code = request.GET.get('code',None) + code = request.GET.get('code', None) res = nkstuff.get_token(code) except MultiValueDictKeyError: message = "The resource owner or authorization server denied the request" - messages.error(request,message) + messages.error(request, message) url = reverse('rower_exportsettings_view') return HttpResponseRedirect(url) @@ -514,7 +530,7 @@ def rower_process_nkcallback(request): # pragma: no cover if access_token == 0: message = res[1] message += ' Contact support@rowsandall.com if this behavior persists' - messages.error(request,message) + messages.error(request, message) url = reverse('rower_exportsettings_view') @@ -534,13 +550,15 @@ def rower_process_nkcallback(request): # pragma: no cover r.save() successmessage = "Tokens stored. Good to go. Please check your import/export settings" - messages.info(request,successmessage) + messages.info(request, successmessage) url = reverse('rower_exportsettings_view') return HttpResponseRedirect(url) + @login_required() -def workout_getnkworkout_all(request,startdatestring='',enddatestring=''): - startdate,enddate = get_dates_timeperiod(request,startdatestring=startdatestring,enddatestring=enddatestring) +def workout_getnkworkout_all(request, startdatestring='', enddatestring=''): + startdate, enddate = get_dates_timeperiod( + request, startdatestring=startdatestring, enddatestring=enddatestring) startdate = startdate.date() enddate = enddate.date() @@ -552,51 +570,58 @@ def workout_getnkworkout_all(request,startdatestring='',enddatestring=''): try: thetoken = nk_open(request.user) - except NoTokenError: # pragma: no cover + except NoTokenError: # pragma: no cover return HttpResponseRedirect("rower_nk_authorize") r = getrequestrower(request) - result = nkstuff.get_nk_workouts(r,do_async=True,before=before,after=after) + result = nkstuff.get_nk_workouts( + r, do_async=True, before=before, after=after) if result: - messages.info(request,"Your NK workouts will be imported in the coming few minutes") - else: # pragma: no cover - messages.error(request,"Your NK workouts import failed") + messages.info( + request, "Your NK workouts will be imported in the coming few minutes") + else: # pragma: no cover + messages.error(request, "Your NK workouts import failed") url = reverse('workouts_view') return HttpResponseRedirect(url) + @login_required() -@permission_required('rower.is_coach',fn=get_user_by_userid, raise_exception=True) -@permission_required('rower.is_not_freecoach',fn=get_user_by_userid, raise_exception=True) -def workout_nkimport_view(request,userid=0,after=0,before=0): - startdate,enddate = get_dates_timeperiod(request,defaulttimeperiod='last30') +@permission_required('rower.is_coach', fn=get_user_by_userid, raise_exception=True) +@permission_required('rower.is_not_freecoach', fn=get_user_by_userid, raise_exception=True) +def workout_nkimport_view(request, userid=0, after=0, before=0): + startdate, enddate = get_dates_timeperiod( + request, defaulttimeperiod='last30') startdate = startdate.date() enddate = enddate.date() - r = getrequestrower(request,userid=userid) - if r.user != request.user: # pragma: no cover - messages.error(request,'You can only access your own workouts on the NK Logbook, not those of your athletes') - url = reverse('workout_nkimport_view',kwargs={'userid':request.user.id}) + r = getrequestrower(request, userid=userid) + if r.user != request.user: # pragma: no cover + messages.error( + request, 'You can only access your own workouts on the NK Logbook, not those of your athletes') + url = reverse('workout_nkimport_view', kwargs={ + 'userid': request.user.id}) return HttpResponseRedirect(url) try: thetoken = nk_open(request.user) - except NoTokenError: # pragma: no cover + except NoTokenError: # pragma: no cover return HttpResponseRedirect("/rowers/me/nkauthorize/") - if request.method == 'POST': # pragma: no cover + if request.method == 'POST': # pragma: no cover dateform = DateRangeForm(request.POST) if dateform.is_valid(): startdate = dateform.cleaned_data['startdate'] - enddate = dateform.cleaned_data['enddate']+datetime.timedelta(days=1) + enddate = dateform.cleaned_data['enddate'] + \ + datetime.timedelta(days=1) else: dateform = DateRangeForm(initial={ - 'startdate':startdate, - 'enddate':enddate, + 'startdate': startdate, + 'enddate': enddate, }) - if enddate < startdate: # pragma: no cover + if enddate < startdate: # pragma: no cover s = enddate enddate = startdate startdate = s @@ -613,16 +638,16 @@ def workout_nkimport_view(request,userid=0,after=0,before=0): after = arrow.get(startdate) after = str(int(after.timestamp()*1000)) - res = nkstuff.get_nk_workout_list(request.user,before=before,after=after) + res = nkstuff.get_nk_workout_list(request.user, before=before, after=after) - if (res.status_code != 200): # pragma: no cover + if (res.status_code != 200): # pragma: no cover if (res.status_code == 401): r = getrower(request.user) if (r.stravatoken == '') or (r.stravatoken is None): s = "Token doesn't exist. Need to authorize" return HttpResponseRedirect("/rowers/me/nkauthorize/") message = "Something went wrong in workout_nkimport_view" - messages.error(request,message) + messages.error(request, message) url = reverse('workouts_view') return HttpResponseRedirect(url) @@ -636,46 +661,47 @@ def workout_nkimport_view(request,userid=0,after=0,before=0): ] parkedids = [] try: - with open('nkblocked.json','r') as nkblocked: + with open('nkblocked.json', 'r') as nkblocked: jsondata = json.load(nkblocked) parkedids = jsondata['ids'] - except FileNotFoundError: # pragma: no cover + except FileNotFoundError: # pragma: no cover pass knownnkids = uniqify(knownnkids+tombstones+parkedids) newids = [nkid for nkid in nkids if not nkid in knownnkids] - nkdata = [{ - 'id':int(item['id']), + 'id': int(item['id']), 'elapsed_time':item['elapsedTime'], 'start_date':arrow.get(item['startTime']), } for item in res.json()] - #for item in res.json(): + # for item in res.json(): # print(item['startTime'],arrow.get(item['startTime']),item['name']) workouts = [] for item in res.json(): - d = int(float(item['totalDistanceGps'])) # could also be Impeller + d = int(float(item['totalDistanceGps'])) # could also be Impeller i = item['id'] n = item['name'] if i in knownnkids: nnn = '' - else: # pragma: no cover + else: # pragma: no cover nnn = 'NEW' - ttot = str(datetime.timedelta(seconds=int(float(item['elapsedTime'])/1000.))) - s = arrow.get(item['startTime'],tzinfo=r.defaulttimezone).format(arrow.FORMAT_RFC850) + ttot = str(datetime.timedelta( + seconds=int(float(item['elapsedTime'])/1000.))) + s = arrow.get(item['startTime'], tzinfo=r.defaulttimezone).format( + arrow.FORMAT_RFC850) #s = arrow.get(item['startTime']).to(r.defaulttimezone).isoformat() - keys = ['id','distance','duration','starttime','name','new'] - values = [i,d,ttot,s,n,nnn] + keys = ['id', 'distance', 'duration', 'starttime', 'name', 'new'] + values = [i, d, ttot, s, n, nnn] rs = dict(zip(keys, values)) workouts.append(rs) workouts = workouts[::-1] - if request.method == 'POST': # pragma: no cover + if request.method == 'POST': # pragma: no cover try: tdict = dict(request.POST.lists()) ids = tdict['workoutid'] @@ -686,7 +712,8 @@ def workout_nkimport_view(request,userid=0,after=0,before=0): alldata[item['id']] = item counter = 0 for nkid in nkids: - csvfilename = 'media/{code}_{nkid}.csv'.format(code=uuid4().hex[:16],nkid=nkid) + csvfilename = 'media/{code}_{nkid}.csv'.format( + code=uuid4().hex[:16], nkid=nkid) result = myqueue( queue, handle_nk_async_workout, @@ -698,7 +725,8 @@ def workout_nkimport_view(request,userid=0,after=0,before=0): r.defaulttimezone ) counter = counter+1 - messages.info(request,'Your NK logbook workouts will be imported in the background. It may take a few minutes before it appears.') + messages.info( + request, 'Your NK logbook workouts will be imported in the background. It may take a few minutes before it appears.') url = reverse('workouts_view') return HttpResponseRedirect(url) except KeyError: @@ -706,48 +734,49 @@ def workout_nkimport_view(request,userid=0,after=0,before=0): breadcrumbs = [ { - 'url':'/rowers/list-workouts/', - 'name':'Workouts' + 'url': '/rowers/list-workouts/', + 'name': 'Workouts' }, { - 'url':reverse('workout_nkimport_view'), - 'name':'NK Logbook' - }, - ] + 'url': reverse('workout_nkimport_view'), + 'name': 'NK Logbook' + }, + ] - checknew = request.GET.get('selectallnew',False) + checknew = request.GET.get('selectallnew', False) - return render(request,'nk_list_import.html', + return render(request, 'nk_list_import.html', { - 'workouts':workouts, - 'rower':r, - 'dateform':dateform, - 'startdate':startdate, - 'enddate':enddate, - 'active':'nav-workouts', - 'breadcrumbs':breadcrumbs, - 'teams':get_my_teams(request.user), - 'checknew':checknew, + 'workouts': workouts, + 'rower': r, + 'dateform': dateform, + 'startdate': startdate, + 'enddate': enddate, + 'active': 'nav-workouts', + 'breadcrumbs': breadcrumbs, + 'teams': get_my_teams(request.user), + 'checknew': checknew, }) # Process Strava Callback + + @login_required() def rower_process_stravacallback(request): try: code = request.GET['code'] scope = request.GET['scope'] - except MultiValueDictKeyError:# pragma: no cover + except MultiValueDictKeyError: # pragma: no cover try: message = request.GET['error'] - except MultiValueDictKeyError:# pragma: no cover + except MultiValueDictKeyError: # pragma: no cover message = "access error" - messages.error(request,message) + messages.error(request, message) url = reverse('workouts_view') return HttpResponseRedirect(url) - res = stravastuff.get_token(code) if res[0]: @@ -766,32 +795,29 @@ def rower_process_stravacallback(request): id = stravastuff.set_strava_athlete_id(r.user) successmessage = "Tokens stored. Good to go. Please check your import/export settings" - messages.info(request,successmessage) + messages.info(request, successmessage) url = reverse('rower_exportsettings_view') return HttpResponseRedirect(url) - else:# pragma: no cover + else: # pragma: no cover message = "Something went wrong with the Strava authorization" - messages.error(request,message) + messages.error(request, message) url = reverse('rower_exportsettings_view') return HttpResponseRedirect(url) - - # Process SportTracks callback @login_required() def rower_process_sporttrackscallback(request): try: code = request.GET['code'] - except:# pragma: no cover - messages.error(request,"Sorry, something went wrong.") + except: # pragma: no cover + messages.error(request, "Sorry, something went wrong.") url = reverse('rower_exportsettings_view') return HttpResponseRedirect(url) res = sporttracksstuff.get_token(code) - access_token = res[0] expires_in = res[1] refresh_token = res[2] @@ -806,23 +832,21 @@ def rower_process_sporttrackscallback(request): r.save() successmessage = "Tokens stored. Good to go. Please check your import/export settings" - messages.info(request,successmessage) + messages.info(request, successmessage) url = reverse('rower_exportsettings_view') return HttpResponseRedirect(url) - - # Process RP3 callback @login_required() -def rower_process_rp3callback(request): # pragma: no cover +def rower_process_rp3callback(request): # pragma: no cover try: code = request.GET['code'] except MultiValueDictKeyError: - messages.error(request,"There was an error with the callback") + messages.error(request, "There was an error with the callback") try: errormessage = request.GET['error'] - messages.error(request,errormessage) + messages.error(request, errormessage) except MultiValueDictKeyError: pass url = reverse('rower_exportsettings_view') @@ -843,20 +867,22 @@ def rower_process_rp3callback(request): # pragma: no cover r.save() successmessage = "Tokens stored. Good to go. Please check your import/export settings" - messages.info(request,successmessage) + messages.info(request, successmessage) url = reverse('rower_exportsettings_view') return HttpResponseRedirect(url) # Process TrainingPeaks callback + + @login_required() def rower_process_tpcallback(request): try: code = request.GET['code'] - except MultiValueDictKeyError: # pragma: no cover - messages.error(request,"There was an error with the callback") + except MultiValueDictKeyError: # pragma: no cover + messages.error(request, "There was an error with the callback") try: errormessage = request.GET['error'] - messages.error(request,errormessage) + messages.error(request, errormessage) except MultiValueDictKeyError: pass url = reverse('rower_exportsettings_view') @@ -877,19 +903,17 @@ def rower_process_tpcallback(request): r.save() successmessage = "Tokens stored. Good to go. Please check your import/export settings" - messages.info(request,successmessage) + messages.info(request, successmessage) url = reverse('rower_exportsettings_view') return HttpResponseRedirect(url) - # Process Own API callback - for API testing purposes @login_required() -def rower_process_testcallback(request): # pragma: no cover +def rower_process_testcallback(request): # pragma: no cover code = request.GET['code'] res = ownapistuff.get_token(code) - access_token = res[0] expires_in = res[1] refresh_token = res[2] @@ -903,27 +927,28 @@ def rower_process_testcallback(request): # pragma: no cover return HttpResponse(text) + @login_required() -@permission_required('rower.is_coach',fn=get_user_by_userid,raise_exception=True) -def workout_rp3import_view(request,userid=0): - r = getrequestrower(request,userid=userid) +@permission_required('rower.is_coach', fn=get_user_by_userid, raise_exception=True) +def workout_rp3import_view(request, userid=0): + r = getrequestrower(request, userid=userid) try: thetoken = rp3stuff.rp3_open(request.user) - except NoTokenError: # pragma: no cover + except NoTokenError: # pragma: no cover url = reverse('rower_rp3_authorize') return HttpResponseRedirect(url) res = rp3stuff.get_rp3_workout_list(request.user) - if (res.status_code != 200): # pragma: no cover + if (res.status_code != 200): # pragma: no cover if (res.status_code == 401): r = getrower(request.user) if (r.stravatoken == '') or (r.stravatoken is None): s = "Token doesn't exist. Need to authorize" return HttpResponseRedirect("/rowers/me/stravaauthorize/") message = "Something went wrong in workout_rp3import_view" - messages.error(request,message) + messages.error(request, message) url = reverse('workouts_view') return HttpResponseRedirect(url) @@ -931,7 +956,7 @@ def workout_rp3import_view(request,userid=0): try: rp3ids = workouts_list['id'].values - except KeyError: # pragma: no cover + except KeyError: # pragma: no cover rp3ids = [] knownrp3ids = uniqify([ @@ -942,80 +967,79 @@ def workout_rp3import_view(request,userid=0): workouts = [] - for key,data in workouts_list.iterrows(): + for key, data in workouts_list.iterrows(): try: i = data['id'] - except KeyError: # pragma: no cover + except KeyError: # pragma: no cover i = 0 - if i in knownrp3ids: # pragma: no cover + if i in knownrp3ids: # pragma: no cover nnn = '' else: nnn = 'NEW' try: s = data['executed_at'] - except KeyError: # pragma: no cover + except KeyError: # pragma: no cover s = '' - keys = ['id','starttime','new'] - values = [i,s,nnn] + keys = ['id', 'starttime', 'new'] + values = [i, s, nnn] - res = dict(zip(keys,values)) + res = dict(zip(keys, values)) workouts.append(res) breadcrumbs = [ { - 'url':'/rowers/list-workouts/', - 'name':'Workouts' + 'url': '/rowers/list-workouts/', + 'name': 'Workouts' }, { - 'url':reverse('workout_rp3import_view'), - 'name':'RP3' - }, - ] + 'url': reverse('workout_rp3import_view'), + 'name': 'RP3' + }, + ] - return render(request,'rp3_list_import.html', + return render(request, 'rp3_list_import.html', { - 'workouts':workouts, - 'rower':r, - 'active':'nav-workouts', - 'breadcrumbs':breadcrumbs, - 'teams':get_my_teams(request.user) - }) + 'workouts': workouts, + 'rower': r, + 'active': 'nav-workouts', + 'breadcrumbs': breadcrumbs, + 'teams': get_my_teams(request.user) + }) # The page where you select which Strava workout to import @login_required() -@permission_required('rower.is_coach',fn=get_user_by_userid,raise_exception=True) -@permission_required('rower.is_not_freecoach',fn=get_user_by_userid, raise_exception=True) -def workout_stravaimport_view(request,message="",userid=0): +@permission_required('rower.is_coach', fn=get_user_by_userid, raise_exception=True) +@permission_required('rower.is_not_freecoach', fn=get_user_by_userid, raise_exception=True) +def workout_stravaimport_view(request, message="", userid=0): - r = getrequestrower(request,userid=userid) + r = getrequestrower(request, userid=userid) if r.user != request.user: - messages.error(request,'You can only access your own workouts on the NK Logbook, not those of your athletes') - url = reverse('workout_stravaimport_view',kwargs={'userid':request.user.id}) + messages.error( + request, 'You can only access your own workouts on the NK Logbook, not those of your athletes') + url = reverse('workout_stravaimport_view', + kwargs={'userid': request.user.id}) return HttpResponseRedirect(url) - #if r.user != request.user: + # if r.user != request.user: # messages.info(request,"You cannot import other people's workouts from Strava") try: thetoken = strava_open(request.user) - except NoTokenError: # pragma: no cover + except NoTokenError: # pragma: no cover return HttpResponseRedirect("/rowers/me/stravaauthorize/") - res = stravastuff.get_strava_workout_list(request.user) - - - if (res.status_code != 200): # pragma: no cover + if (res.status_code != 200): # pragma: no cover if (res.status_code == 401): r = getrower(request.user) if (r.stravatoken == '') or (r.stravatoken is None): s = "Token doesn't exist. Need to authorize" return HttpResponseRedirect("/rowers/me/stravaauthorize/") message = "Something went wrong in workout_stravaimport_view" - messages.error(request,message) + messages.error(request, message) url = reverse('workouts_view') return HttpResponseRedirect(url) else: @@ -1024,14 +1048,14 @@ def workout_stravaimport_view(request,message="",userid=0): rower = r stravaids = [int(item['id']) for item in res.json()] stravadata = [{ - 'id':int(item['id']), + 'id': int(item['id']), 'elapsed_time':item['elapsed_time'], 'start_date':item['start_date'], - } for item in res.json()] + } for item in res.json()] - wfailed = Workout.objects.filter(user=r,uploadedtostrava=-1) + wfailed = Workout.objects.filter(user=r, uploadedtostrava=-1) - for w in wfailed: # pragma: no cover + for w in wfailed: # pragma: no cover for item in stravadata: elapsed_time = item['elapsed_time'] start_date = item['start_date'] @@ -1041,35 +1065,37 @@ def workout_stravaimport_view(request,message="",userid=0): elapsed_time = datetime.datetime.strptime( str(elapsed_td), "%H:%M:%S" - ) + ) if str(elapsed_time)[-7:] == str(w.duration)[-7:]: w.uploadedtostrava = int(stravaid) w.save() - knownstravaids = uniqify([ w.uploadedtostrava for w in Workout.objects.filter(user=r) - ]) - newids = [stravaid for stravaid in stravaids if not stravaid in knownstravaids] + ]) + newids = [ + stravaid for stravaid in stravaids if not stravaid in knownstravaids] for item in res.json(): d = int(float(item['distance'])) i = item['id'] - if i in knownstravaids: # pragma: no cover + if i in knownstravaids: # pragma: no cover nnn = '' else: nnn = 'NEW' n = item['name'] - ttot = str(datetime.timedelta(seconds=int(float(item['elapsed_time'])))) + ttot = str(datetime.timedelta( + seconds=int(float(item['elapsed_time'])))) s = item['start_date'] r = item['type'] - keys = ['id','distance','duration','starttime','type','name','new'] - values = [i,d,ttot,s,r,n,nnn] - res2 = dict(zip(keys,values)) + keys = ['id', 'distance', 'duration', + 'starttime', 'type', 'name', 'new'] + values = [i, d, ttot, s, r, n, nnn] + res2 = dict(zip(keys, values)) workouts.append(res2) if request.method == "POST": - try: # pragma: no cover + try: # pragma: no cover tdict = dict(request.POST.lists()) ids = tdict['workoutid'] stravaids = [int(id) for id in ids] @@ -1077,7 +1103,8 @@ def workout_stravaimport_view(request,message="",userid=0): for item in res.json(): alldata[item['id']] = item for stravaid in stravaids: - csvfilename = 'media/{code}_{stravaid}.csv'.format(code=uuid4().hex[:16],stravaid=stravaid) + csvfilename = 'media/{code}_{stravaid}.csv'.format( + code=uuid4().hex[:16], stravaid=stravaid) result = myqueue( queue, fetch_strava_workout, @@ -1088,51 +1115,54 @@ def workout_stravaimport_view(request,message="",userid=0): rower.user.id ) # done, redirect to workouts list - messages.info(request,'Your Strava workouts will be imported in the background. It may take a few minutes before it appears.'.format(stravaid=stravaid)) + messages.info(request, 'Your Strava workouts will be imported in the background. It may take a few minutes before it appears.'.format( + stravaid=stravaid)) url = reverse('workouts_view') return HttpResponseRedirect(url) - except KeyError: # pragma: no cover + except KeyError: # pragma: no cover pass breadcrumbs = [ { - 'url':'/rowers/list-workouts/', - 'name':'Workouts' + 'url': '/rowers/list-workouts/', + 'name': 'Workouts' }, { - 'url':reverse('workout_stravaimport_view'), - 'name':'Strava' - }, - ] + 'url': reverse('workout_stravaimport_view'), + 'name': 'Strava' + }, + ] - checknew = request.GET.get('selectallnew',False) + checknew = request.GET.get('selectallnew', False) - return render(request,'strava_list_import.html', - {'workouts':workouts, - 'rower':rower, - 'active':'nav-workouts', - 'breadcrumbs':breadcrumbs, - 'teams':get_my_teams(request.user), - 'checknew':checknew, + return render(request, 'strava_list_import.html', + {'workouts': workouts, + 'rower': rower, + 'active': 'nav-workouts', + 'breadcrumbs': breadcrumbs, + 'teams': get_my_teams(request.user), + 'checknew': checknew, }) - return HttpResponse(res) # pragma: no cover + return HttpResponse(res) # pragma: no cover # for Strava webhook request validation + + @csrf_exempt def strava_webhook_view(request): if request.method == 'GET': challenge = request.GET.get('hub.challenge') verificationtoken = request.GET.get('hub.verify_token') - if verificationtoken != stravastuff.webhookverification: # pragma: no cover + if verificationtoken != stravastuff.webhookverification: # pragma: no cover return HttpResponse(status=403) - data = {"hub.challenge":challenge} + data = {"hub.challenge": challenge} return JSONResponse(data) # logging t = time.localtime() timestamp = time.strftime('%b-%d-%Y_%H%M', t) - with open('strava_webhooks.log','a') as f: + with open('strava_webhooks.log', 'a') as f: f.write('\n') f.write(timestamp) f.write(' ') @@ -1149,9 +1179,9 @@ def strava_webhook_view(request): object_type = data['object_type'] strava_owner = data['owner_id'] starttimeunix = data['event_time'] - except KeyError: # pragma: no cover + except KeyError: # pragma: no cover timestamp = time.strftime('%b-%d-%Y_%H%M', t) - with open('strava_webhooks.log','a') as f: + with open('strava_webhooks.log', 'a') as f: f.write('\n') f.write(timestamp) f.write(' ') @@ -1162,50 +1192,52 @@ def strava_webhook_view(request): if aspect_type == 'create': try: stravaid = data['object_id'] - except KeyError: # pragma: no cover - dologging('strava_webhooks.log','KeyError line 1105') + except KeyError: # pragma: no cover + dologging('strava_webhooks.log', 'KeyError line 1105') return HttpResponse(status=200) try: r = Rower.objects.get(strava_owner_id=strava_owner) - except Rower.DoesNotExist: # pragma: no cover - dologging('strava_webhooks.log','Rower not found') + except Rower.DoesNotExist: # pragma: no cover + dologging('strava_webhooks.log', 'Rower not found') return HttpResponse(status=200) - except MultipleObjectsReturned: # pragma: no cover - s = 'Multiple rowers found for strava ID {id}'.format(id=strava_owner) - dologging('strava_webhooks.log',s) + except MultipleObjectsReturned: # pragma: no cover + s = 'Multiple rowers found for strava ID {id}'.format( + id=strava_owner) + dologging('strava_webhooks.log', s) rs = Rower.objects.filter(strava_owner_id=strava_owner) r = rs[0] ws = Workout.objects.filter(uploadedtostrava=stravaid) - if ws.count()==0 and r.strava_auto_import: - job = stravastuff.async_get_workout(r.user,stravaid) - if job == 0: # pragma: no cover - dologging('strava_webhooks.log','Strava strava_open yielded NoTokenError') - else: # pragma: no cover - dologging('strava_webhooks.log','Workouts already existing') + if ws.count() == 0 and r.strava_auto_import: + job = stravastuff.async_get_workout(r.user, stravaid) + if job == 0: # pragma: no cover + dologging('strava_webhooks.log', + 'Strava strava_open yielded NoTokenError') + else: # pragma: no cover + dologging('strava_webhooks.log', 'Workouts already existing') for w in ws: - dologging('strava_webhooks.log',str(w)) + dologging('strava_webhooks.log', str(w)) elif aspect_type == 'delete': try: stravaid = data['object_id'] - except KeyError: # pragma: no cover - dologging('strava_webhooks.log','KeyError line 1132') + except KeyError: # pragma: no cover + dologging('strava_webhooks.log', 'KeyError line 1132') try: ws = Workout.objects.filter(uploadedtostrava=stravaid) if ws.count() == 0: return HttpResponse(status=200) - except Workout.DoesNotExist: # pragma: no cover + except Workout.DoesNotExist: # pragma: no cover return HttpResponse(status=200) - try: # pragma: no cover + try: # pragma: no cover r = Rower.objects.get(strava_owner_id=strava_owner) - except Rower.DoesNotExist: # pragma: no cover - dologging('strava_webhooks.log','Rower not found') + except Rower.DoesNotExist: # pragma: no cover + dologging('strava_webhooks.log', 'Rower not found') return HttpResponse(status=200) - except MultipleObjectsReturned: # pragma: no cover + except MultipleObjectsReturned: # pragma: no cover rs = Rower.objects.filter(strava_owner_id=strava_owner) r = rs[0] - if r.strava_auto_delete: # pragma: no cover + if r.strava_auto_delete: # pragma: no cover for w in ws: if w.user == r: w.delete() @@ -1213,8 +1245,8 @@ def strava_webhook_view(request): try: updates = data['updates'] stravaid = data['object_id'] - except KeyError: # pragma: no cover - with open('strava_webhooks.log','a') as f: + except KeyError: # pragma: no cover + with open('strava_webhooks.log', 'a') as f: f.write('\n') f.write(timestamp) f.write(' ') @@ -1222,11 +1254,11 @@ def strava_webhook_view(request): return HttpResponse(status=200) try: ws = Workout.objects.filter(uploadedtostrava=stravaid) - if ws.count() == 0: # pragma: no cover + if ws.count() == 0: # pragma: no cover return HttpResponse(status=200) - except Workout.DoesNotExist: # pragma: no cover + except Workout.DoesNotExist: # pragma: no cover timestamp = time.strftime('%b-%d-%Y_%H%M', t) - with open('strava_webhooks.log','a') as f: + with open('strava_webhooks.log', 'a') as f: f.write('\n') f.write(timestamp) f.write(' ') @@ -1234,15 +1266,15 @@ def strava_webhook_view(request): return HttpResponse(status=200) try: r = Rower.objects.get(strava_owner_id=strava_owner) - except Rower.DoesNotExist: # pragma: no cover + except Rower.DoesNotExist: # pragma: no cover timestamp = time.strftime('%b-%d-%Y_%H%M', t) - with open('strava_webhooks.log','a') as f: + with open('strava_webhooks.log', 'a') as f: f.write('\n') f.write(timestamp) f.write(' ') f.write('Rower not found') return HttpResponse(status=200) - except MultipleObjectsReturned: # pragma: no cover + except MultipleObjectsReturned: # pragma: no cover rs = Rower.objects.filter(strava_owner_id=strava_owner) r = rs[0] @@ -1255,8 +1287,8 @@ def strava_webhook_view(request): try: w.workouttype = mytypes.stravamappinginv[value] w.save() - except KeyError: # pragma: no cover - with open('strava_webhooks.log','a') as f: + except KeyError: # pragma: no cover + with open('strava_webhooks.log', 'a') as f: f.write('\n') f.write(timestamp) f.write(' ') @@ -1266,14 +1298,16 @@ def strava_webhook_view(request): return HttpResponse(status=200) # For push notifications from Garmin + + @csrf_exempt -def garmin_summaries_view(request): # pragma: no cover +def garmin_summaries_view(request): # pragma: no cover if request.method != 'POST': return HttpResponse(status=200) t = time.localtime() timestamp = time.strftime('%b-%d-%Y_%H%M', t) - with open('garminlog.log','a') as f: + with open('garminlog.log', 'a') as f: f.write('\n') f.write(timestamp) f.write(' ') @@ -1289,12 +1323,12 @@ def garmin_summaries_view(request): # pragma: no cover return HttpResponse(status=200) + @csrf_exempt -def garmin_newfiles_ping(request): # pragma: no cover +def garmin_newfiles_ping(request): # pragma: no cover t = time.localtime() timestamp = time.strftime('%b-%d-%Y_%H%M', t) - if request.method != 'POST': return HttpResponse(status=200) @@ -1308,17 +1342,19 @@ def garmin_newfiles_ping(request): # pragma: no cover callbackURL = file['callbackURL'] starttime = file['startTimeInSeconds'] fileType = file['fileType'] - job = garmin_stuff.get_garmin_file(r,callbackURL,starttime,fileType) + job = garmin_stuff.get_garmin_file( + r, callbackURL, starttime, fileType) except Rower.DoesNotExist: pass except KeyError: pass - return HttpResponse(status=200) # pragma: no cover + return HttpResponse(status=200) # pragma: no cover + @csrf_exempt def garmin_deregistration_view(request): - if request.method != 'POST': # pragma: no cover + if request.method != 'POST': # pragma: no cover return HttpResponse(status=200) data = json.loads(request.body) @@ -1330,16 +1366,17 @@ def garmin_deregistration_view(request): r = Rower.objects.get(garmintoken=garmintoken) r.garmintoken = '' r.save() - except Rower.DoesNotExist: # pragma: no cover + except Rower.DoesNotExist: # pragma: no cover pass - except KeyError: # pragma: no cover + except KeyError: # pragma: no cover pass return HttpResponse(status=200) + @csrf_exempt def garmin_details_view(request): - if request.method != 'POST': # pragma: no cover + if request.method != 'POST': # pragma: no cover return HttpResponse(status=200) t = time.localtime() @@ -1352,23 +1389,22 @@ def garmin_details_view(request): return HttpResponse(status=200) - - # the page where you select which Polar workout to Import @login_required() -@permission_required('rower.is_coach',fn=get_user_by_userid,raise_exception=True) -@permission_required('rower.is_not_freecoach',fn=get_user_by_userid, raise_exception=True) -def workout_polarimport_view(request,userid=0): # pragma: no cover +@permission_required('rower.is_coach', fn=get_user_by_userid, raise_exception=True) +@permission_required('rower.is_not_freecoach', fn=get_user_by_userid, raise_exception=True) +def workout_polarimport_view(request, userid=0): # pragma: no cover exercises = polarstuff.get_polar_workouts(request.user) workouts = [] try: a = exercises.status_code if a == 401: - messages.error(request,'Not authorized. You need to connect to Polar first') + messages.error( + request, 'Not authorized. You need to connect to Polar first') url = reverse('workouts_view') return HttpResponseRedirect(url) - except: # pragma: no cover + except: # pragma: no cover exercises = [] pass @@ -1384,45 +1420,46 @@ def workout_polarimport_view(request,userid=0): # pragma: no cover rowtype = exercise['sport'] durationstring = exercise['duration'] duration = isodate.parse_duration(durationstring) - keys = ['id','distance','duration','starttime','type','transactionid'] - values = [i,d,duration,starttime,rowtype,transactionid] - res = dict(zip(keys,values)) + keys = ['id', 'distance', 'duration', + 'starttime', 'type', 'transactionid'] + values = [i, d, duration, starttime, rowtype, transactionid] + res = dict(zip(keys, values)) workouts.append(res) breadcrumbs = [ { - 'url':'/rowers/list-workouts/', - 'name':'Workouts' + 'url': '/rowers/list-workouts/', + 'name': 'Workouts' }, { - 'url':reverse('workout_polarimport_view'), - 'name':'Polar' + 'url': reverse('workout_polarimport_view'), + 'name': 'Polar' }, - ] + ] r = getrower(request.user) return render(request, 'polar_list_import.html', { - 'workouts':workouts, - 'active':'nav-workouts', - 'rower':r, - 'breadcrumbs':breadcrumbs, - 'teams':get_my_teams(request.user), - }) - - + 'workouts': workouts, + 'active': 'nav-workouts', + 'rower': r, + 'breadcrumbs': breadcrumbs, + 'teams': get_my_teams(request.user), + }) # The page where you select which SportTracks workout to import @login_required() -@permission_required('rower.is_coach',fn=get_user_by_userid,raise_exception=True) -@permission_required('rower.is_not_freecoach',fn=get_user_by_userid, raise_exception=True) -def workout_sporttracksimport_view(request,message="",userid=0): - r = getrequestrower(request,userid=userid) +@permission_required('rower.is_coach', fn=get_user_by_userid, raise_exception=True) +@permission_required('rower.is_not_freecoach', fn=get_user_by_userid, raise_exception=True) +def workout_sporttracksimport_view(request, message="", userid=0): + r = getrequestrower(request, userid=userid) if r.user != request.user: - messages.error(request,'You can only access your own workouts on the NK Logbook, not those of your athletes') - url = reverse('workout_sporttracksimport_view',kwargs={'userid':request.user.id}) + messages.error( + request, 'You can only access your own workouts on the NK Logbook, not those of your athletes') + url = reverse('workout_sporttracksimport_view', + kwargs={'userid': request.user.id}) return HttpResponseRedirect(url) res = sporttracksstuff.get_sporttracks_workout_list(request.user) @@ -1432,13 +1469,13 @@ def workout_sporttracksimport_view(request,message="",userid=0): if (r.sporttrackstoken == '') or (r.sporttrackstoken is None): s = "Token doesn't exist. Need to authorize" return HttpResponseRedirect("/rowers/me/sporttracksauthorize/") - else: # pragma: no cover + else: # pragma: no cover return HttpResponseRedirect("/rowers/me/sporttracksrefresh/") - message = "Something went wrong in workout_sporttracksimport_view" # pragma: no cover - messages.error(request,message) # pragma: no cover - if settings.DEBUG: # pragma: no cover + message = "Something went wrong in workout_sporttracksimport_view" # pragma: no cover + messages.error(request, message) # pragma: no cover + if settings.DEBUG: # pragma: no cover return HttpResponse(res) - else: # pragma: no cover + else: # pragma: no cover url = reverse('workouts_view') return HttpResponseRedirect(url) @@ -1452,7 +1489,7 @@ def workout_sporttracksimport_view(request,message="",userid=0): for item in res.json()['items']: d = int(float(item['total_distance'])) i = int(getidfromuri(item['uri'])) - if i in knownstids: # pragma: no cover + if i in knownstids: # pragma: no cover nnn = '' else: nnn = 'NEW' @@ -1460,49 +1497,52 @@ def workout_sporttracksimport_view(request,message="",userid=0): ttot = str(datetime.timedelta(seconds=int(float(item['duration'])))) s = item['start_time'] r = item['type'] - keys = ['id','distance','duration','starttime','type','name','new'] - values = [i,d,ttot,s,r,n,nnn] - res = dict(zip(keys,values)) + keys = ['id', 'distance', 'duration', + 'starttime', 'type', 'name', 'new'] + values = [i, d, ttot, s, r, n, nnn] + res = dict(zip(keys, values)) workouts.append(res) r = getrower(request.user) breadcrumbs = [ { - 'url':'/rowers/list-workouts/', - 'name':'Workouts' + 'url': '/rowers/list-workouts/', + 'name': 'Workouts' }, { - 'url':reverse('workout_sporttracksimport_view'), - 'name':'SportTracks' + 'url': reverse('workout_sporttracksimport_view'), + 'name': 'SportTracks' }, ] - return render(request,'sporttracks_list_import.html', - {'workouts':workouts, - 'breadcrumbs':breadcrumbs, - 'active':'nav-workouts', - 'rower':r, - 'teams':get_my_teams(request.user), - }) + return render(request, 'sporttracks_list_import.html', + {'workouts': workouts, + 'breadcrumbs': breadcrumbs, + 'active': 'nav-workouts', + 'rower': r, + 'teams': get_my_teams(request.user), + }) - return HttpResponse(res) # pragma: no cover + return HttpResponse(res) # pragma: no cover # List of workouts on Concept2 logbook. This view only used for debugging + + @login_required() -def c2listdebug_view(request,page=1,message=""): # pragma: no cover +def c2listdebug_view(request, page=1, message=""): # pragma: no cover try: thetoken = c2_open(request.user) - except NoTokenError: # pragma: no cover + except NoTokenError: # pragma: no cover return HttpResponseRedirect("/rowers/me/c2authorize/") r = getrower(request.user) - res = c2stuff.get_c2_workout_list(request.user,page=page) + res = c2stuff.get_c2_workout_list(request.user, page=page) if (res.status_code != 200): message = "Something went wrong in workout_c2import_view (C2 token renewal)" - messages.error(request,message) + messages.error(request, message) if settings.DEBUG: return HttpResponse(res) else: @@ -1519,79 +1559,88 @@ def c2listdebug_view(request,page=1,message=""): # pragma: no cover r = item['type'] s2 = item['source'] c = item['comments'] - keys = ['id','distance','duration','starttime','rowtype','source','comment'] - values = [i,d,ttot,s,r,s2,c] - res = dict(zip(keys,values)) + keys = ['id', 'distance', 'duration', + 'starttime', 'rowtype', 'source', 'comment'] + values = [i, d, ttot, s, r, s2, c] + res = dict(zip(keys, values)) workouts.append(res) - return render(request, 'c2_list_import2.html', - {'workouts':workouts, - 'teams':get_my_teams(request.user), - }) + {'workouts': workouts, + 'teams': get_my_teams(request.user), + }) # Import all unknown workouts available on Concept2 logbook + + @login_required() -def workout_getc2workout_all(request,page=1,message=""): # pragma: no cover +def workout_getc2workout_all(request, page=1, message=""): # pragma: no cover try: thetoken = c2_open(request.user) - except NoTokenError: # pragma: no cover + except NoTokenError: # pragma: no cover return HttpResponseRedirect("/rowers/me/c2authorize/") r = getrequestrower(request) - result = c2stuff.get_c2_workouts(r,page=page,do_async=True) + result = c2stuff.get_c2_workouts(r, page=page, do_async=True) if result: - messages.info(request,'Your C2 workouts will be imported in the coming few minutes') + messages.info( + request, 'Your C2 workouts will be imported in the coming few minutes') else: - messages.error(request,'Your C2 workouts import failed') + messages.error(request, 'Your C2 workouts import failed') url = reverse('workouts_view') return HttpResponseRedirect(url) + @login_required() -def workout_getrp3workout_all(request): # pragma: no cover +def workout_getrp3workout_all(request): # pragma: no cover try: thetoken = rp3_open(request.user) - except NoTokenError: # pragma: no cover + except NoTokenError: # pragma: no cover return HttpResponseRedirect("/rowers/me/rp3authorize/") r = getrequestrower(request) - result = rp3stuff.get_rp3_workouts(r,do_async=True) + result = rp3stuff.get_rp3_workouts(r, do_async=True) if result: - messages.info(request,'Your RP3 workouts will be imported in the coming few minutes') + messages.info( + request, 'Your RP3 workouts will be imported in the coming few minutes') else: - messages.error(request,'Your RP3 workouts import failed') + messages.error(request, 'Your RP3 workouts import failed') url = reverse('workouts_view') return HttpResponseRedirect(url) # List of workouts available on Concept2 logbook - for import -@login_required() -@permission_required('rower.is_coach',fn=get_user_by_userid,raise_exception=True) -@permission_required('rower.is_not_freecoach',fn=get_user_by_userid, raise_exception=True) -def workout_c2import_view(request,page=1,userid=0,message=""): - rower = getrequestrower(request,userid=userid) + +@login_required() +@permission_required('rower.is_coach', fn=get_user_by_userid, raise_exception=True) +@permission_required('rower.is_not_freecoach', fn=get_user_by_userid, raise_exception=True) +def workout_c2import_view(request, page=1, userid=0, message=""): + + rower = getrequestrower(request, userid=userid) if rower.user != request.user: - messages.error(request,'You can only access your own workouts on the NK Logbook, not those of your athletes') - url = reverse('workout_c2import_view',kwargs={'userid':request.user.id}) + messages.error( + request, 'You can only access your own workouts on the NK Logbook, not those of your athletes') + url = reverse('workout_c2import_view', kwargs={ + 'userid': request.user.id}) return HttpResponseRedirect(url) try: thetoken = c2_open(request.user) - except NoTokenError: # pragma: no cover + except NoTokenError: # pragma: no cover return HttpResponseRedirect("/rowers/me/c2authorize/") - res = c2stuff.get_c2_workout_list(request.user,page=page) + res = c2stuff.get_c2_workout_list(request.user, page=page) - if (res.status_code != 200): # pragma: no cover + if (res.status_code != 200): # pragma: no cover message = "Something went wrong in workout_c2import_view (C2 token refresh)" - messages.error(request,message) + messages.error(request, message) url = reverse('workouts_view') return HttpResponseRedirect(url) @@ -1605,10 +1654,10 @@ def workout_c2import_view(request,page=1,userid=0,message=""): ] parkedids = [] try: - with open('c2blocked.json','r') as c2blocked: + with open('c2blocked.json', 'r') as c2blocked: jsondata = json.load(c2blocked) parkedids = jsondata['ids'] - except: # pragma: no cover + except: # pragma: no cover pass knownc2ids = uniqify(knownc2ids+tombstones+parkedids) @@ -1624,15 +1673,16 @@ def workout_c2import_view(request,page=1,userid=0,message=""): c = item['comments'] if i in knownc2ids: nnn = '' - else: # pragma: no cover + else: # pragma: no cover nnn = 'NEW' - keys = ['id','distance','duration','starttime','rowtype','source','comment','new'] - values = [i,d,ttot,s,r,s2,c,nnn] - ress = dict(zip(keys,values)) + keys = ['id', 'distance', 'duration', 'starttime', + 'rowtype', 'source', 'comment', 'new'] + values = [i, d, ttot, s, r, s2, c, nnn] + ress = dict(zip(keys, values)) workouts.append(ress) if request.method == "POST": - try: # pragma: no cover + try: # pragma: no cover tdict = dict(request.POST.lists()) ids = tdict['workoutid'] c2ids = [int(id) for id in ids] @@ -1642,7 +1692,8 @@ def workout_c2import_view(request,page=1,userid=0,message=""): alldata[item['id']] = item counter = 0 for c2id in c2ids: - csvfilename = 'media/{code}_{c2id}.csv'.format(code=uuid4().hex[:16],c2id=c2id) + csvfilename = 'media/{code}_{c2id}.csv'.format( + code=uuid4().hex[:16], c2id=c2id) result = myqueue( queue, handle_c2_async_workout, @@ -1655,80 +1706,84 @@ def workout_c2import_view(request,page=1,userid=0,message=""): ) counter = counter+1 # done, redirect to workouts list - messages.info(request,'Your Concept2 workouts will be imported in the background. It may take a few minutes before it appears.'.format(c2id=c2id)) + messages.info( + request, 'Your Concept2 workouts will be imported in the background. It may take a few minutes before it appears.'.format(c2id=c2id)) url = reverse('workouts_view') return HttpResponseRedirect(url) - except KeyError: # pragma: no cover + except KeyError: # pragma: no cover pass - breadcrumbs = [ { - 'url':'/rowers/list-workouts/', - 'name':'Workouts' + 'url': '/rowers/list-workouts/', + 'name': 'Workouts' }, { - 'url':reverse('workout_c2import_view'), - 'name':'Concept2' + 'url': reverse('workout_c2import_view'), + 'name': 'Concept2' }, { - 'url':reverse('workout_c2import_view',kwargs={'page':page}), - 'name':'Page '+str(page) + 'url': reverse('workout_c2import_view', kwargs={'page': page}), + 'name': 'Page '+str(page) } ] rower = getrower(request.user) - checknew = request.GET.get('selectallnew',False) + checknew = request.GET.get('selectallnew', False) return render(request, 'c2_list_import2.html', - {'workouts':workouts, - 'rower':rower, - 'active':'nav-workouts', - 'breadcrumbs':breadcrumbs, - 'teams':get_my_teams(request.user), - 'page':page, - 'checknew':checknew, - }) + {'workouts': workouts, + 'rower': rower, + 'active': 'nav-workouts', + 'breadcrumbs': breadcrumbs, + 'teams': get_my_teams(request.user), + 'page': page, + 'checknew': checknew, + }) + importlistviews = { - 'c2':'workout_c2import_view', - 'strava':'workout_stravaimport_view', - 'polar':'workout_polarimport_view', - 'ownapi':'workout_view', - 'sporttracks':'workout_sporttracksimport_view', - 'trainingpeaks':'workout_view', - 'nk':'workout_nkimport_view', - } + 'c2': 'workout_c2import_view', + 'strava': 'workout_stravaimport_view', + 'polar': 'workout_polarimport_view', + 'ownapi': 'workout_view', + 'sporttracks': 'workout_sporttracksimport_view', + 'trainingpeaks': 'workout_view', + 'nk': 'workout_nkimport_view', +} importauthorizeviews = { - 'c2':'rower_c2_authorize', - 'strava':'rower_strava_authorize', - 'polar':'rower_polar_authorize', - 'ownapi':'workout_view', - 'sporttracks':'rower_sporttracks_authorize', - 'trainingpeaks':'rower_tp_authorize', - 'nk':'rower_nk_authorize', + 'c2': 'rower_c2_authorize', + 'strava': 'rower_strava_authorize', + 'polar': 'rower_polar_authorize', + 'ownapi': 'workout_view', + 'sporttracks': 'rower_sporttracks_authorize', + 'trainingpeaks': 'rower_tp_authorize', + 'nk': 'rower_nk_authorize', } importsources = { - 'c2':c2stuff, - 'strava':stravastuff, - 'polar':polarstuff, - 'ownapi':ownapistuff, - 'sporttracks':sporttracksstuff, - 'trainingpeaks':tpstuff, - 'nk':nkstuff, + 'c2': c2stuff, + 'strava': stravastuff, + 'polar': polarstuff, + 'ownapi': ownapistuff, + 'sporttracks': sporttracksstuff, + 'trainingpeaks': tpstuff, + 'nk': nkstuff, } + @login_required() -@permission_required('rower.is_not_freecoach',fn=get_user_by_userid, raise_exception=True) -def workout_getrp3importview(request,externalid): +@permission_required('rower.is_not_freecoach', fn=get_user_by_userid, raise_exception=True) +def workout_getrp3importview(request, externalid): r = getrequestrower(request) - if r.user != request.user: # pragma: no cover - messages.error(request,'You can only access your own workouts on the NK Logbook, not those of your athletes') - url = reverse('workout_rp3import_view',kwargs={'userid':request.user.id}) + if r.user != request.user: # pragma: no cover + messages.error( + request, 'You can only access your own workouts on the NK Logbook, not those of your athletes') + url = reverse('workout_rp3import_view', kwargs={ + 'userid': request.user.id}) return HttpResponseRedirect(url) token = rp3stuff.rp3_open(r.user) startdatetime = request.GET.get('startdatetime') @@ -1744,37 +1799,39 @@ def workout_getrp3importview(request,externalid): #id = rp3stuff.get_rp3_workout(r.user,externalid,startdatetime=startdatetime) - messages.info(request,'The workout will be imported in the background') + messages.info(request, 'The workout will be imported in the background') url = reverse('workout_rp3import_view') return HttpResponseRedirect(url) + @login_required() -def workout_getimportview(request,externalid,source = 'c2',do_async=True): - if 'startdate' in request.session and source == 'nk': # pragma: no cover +def workout_getimportview(request, externalid, source='c2', do_async=True): + if 'startdate' in request.session and source == 'nk': # pragma: no cover startdate = request.session.get('startdate') enddate = request.session.get('enddate') try: - result = importsources[source].get_workout(request.user,externalid,do_async=do_async, - startdate=startdate,enddate=enddate) + result = importsources[source].get_workout(request.user, externalid, do_async=do_async, + startdate=startdate, enddate=enddate) except NoTokenError: return HttpResponseRedirect(reverse(importauthorizeviews[source])) url = reverse(importlistviews[source]) return HttpResponseRedirect(url) try: - result = importsources[source].get_workout(request.user,externalid, - do_async=do_async) + result = importsources[source].get_workout(request.user, externalid, + do_async=do_async) except NoTokenError: return HttpResponseRedirect(reverse(importauthorizeviews[source])) - if result: # pragma: no cover - messages.info(request,"Your workout will be imported in the background") + if result: # pragma: no cover + messages.info( + request, "Your workout will be imported in the background") # this should return to the respective import list page - else: # pragma: no cover - messages.error(request,'Error getting the workout') + else: # pragma: no cover + messages.error(request, 'Error getting the workout') url = reverse(importlistviews[source]) return HttpResponseRedirect(url) @@ -1786,32 +1843,32 @@ def workout_getsporttracksworkout_all(request): res = sporttracksstuff.get_sporttracks_workout_list(request.user) if (res.status_code == 200): r = getrower(request.user) - stids = [int(getidfromuri(item['uri'])) for item in res.json()['items']] + stids = [int(getidfromuri(item['uri'])) + for item in res.json()['items']] knownstids = uniqify([ w.uploadedtosporttracks for w in Workout.objects.filter(user=r) - ]) + ]) newids = [stid for stid in stids if not stid in knownstids] for sporttracksid in newids: id = sporttracksstuff.get_workout( - request.user,sporttracksid) + request.user, sporttracksid) - if id==0: # pragma: no cover - messages.error(request,"Something went wrong with workout {id}".format(id=sporttracksid)) + if id == 0: # pragma: no cover + messages.error( + request, "Something went wrong with workout {id}".format(id=sporttracksid)) else: w = Workout.objects.get(id=id) - w.uploadedtosporttracks=sporttracksid + w.uploadedtosporttracks = sporttracksid w.save() url = reverse('workouts_view') return HttpResponseRedirect(url) - - # Imports all new workouts from SportTracks @login_required() -def workout_getstravaworkout_next(request): # pragma: no cover +def workout_getstravaworkout_next(request): # pragma: no cover r = Rower.objects.get(user=request.user) @@ -1828,14 +1885,14 @@ def workout_getstravaworkout_next(request): # pragma: no cover knownstravaids = uniqify([ w.uploadedtostrava for w in Workout.objects.filter(user=r) - ]) - newids = [stravaid for stravaid in stravaids if not stravaid in knownstravaids] + ]) + newids = [ + stravaid for stravaid in stravaids if not stravaid in knownstravaids] theid = newids[0] - workoutid = stravastuff.create_async_workout(alldata,r.user,stravaid,debug=True) - - + workoutid = stravastuff.create_async_workout( + alldata, r.user, stravaid, debug=True) url = reverse('workouts_view') return HttpResponseRedirect(url) diff --git a/rowers/views/otherviews.py b/rowers/views/otherviews.py index 30f60ecb..14ee6df6 100644 --- a/rowers/views/otherviews.py +++ b/rowers/views/otherviews.py @@ -9,14 +9,14 @@ from rq import Queue from redis import Redis from rq.job import Job + @login_required() -def download_fit(request,filename=''): +def download_fit(request, filename=''): r = getrower(request.user) pss = PlannedSession.objects.filter(fitfile=filename) - - if len(pss) != 1: # pragma: no cover + if len(pss) != 1: # pragma: no cover raise Http404("Could not find the required file") ps = pss[0] @@ -24,53 +24,52 @@ def download_fit(request,filename=''): if ps.manager == request.user or request.user.rower in ps.rower.all(): owns = True - if not owns: # pragma: no cover + if not owns: # pragma: no cover raise PermissionDenied("You are not allowed to download this file") fitfile = ps.fitfile try: response = HttpResponse(fitfile) except FileNotFoundError: - raise Http404("File not found") + raise Http404("File not found") - response['Content-Disposition'] = 'attachment; filename="%s"' % filename # pragma: no cover - response['Content-Type'] = 'application/octet-stream' # pragma: no cover + response['Content-Disposition'] = 'attachment; filename="%s"' % filename # pragma: no cover + response['Content-Type'] = 'application/octet-stream' # pragma: no cover + + return response # pragma: no cover - return response # pragma: no cover @login_required() def failed_queue_view(request): - if not request.user.is_staff: # pragma: no cover + if not request.user.is_staff: # pragma: no cover raise PermissionDenied("Not Allowed") q = Queue('failed', connection=Redis()) resultslist = [] - for job in q.jobs: # pragma: no cover + for job in q.jobs: # pragma: no cover traceback = str(job.exc_info) - info = { - 'id':job.id, + 'id': job.id, 'started_at': job.started_at, 'traceback1': traceback, - } - + } resultslist += [info] - return render(request, "failed_jobs.html", { - 'resultslist':resultslist, - } + 'resultslist': resultslist, + } ) + @login_required() def failed_queue_empty(request): - if not request.user.is_staff: # pragma: no cover + if not request.user.is_staff: # pragma: no cover raise PermissionDenied("Not Allowed") q = Queue('failed', connection=Redis()) @@ -81,8 +80,8 @@ def failed_queue_empty(request): @login_required() -def failed_job_view(request,id=0): # pragma: no cover - if not request.user.is_staff: +def failed_job_view(request, id=0): # pragma: no cover + if not request.user.is_staff: raise PermissionDenied("Not Allowed") q = Queue('failed', connection=Redis()) @@ -94,38 +93,40 @@ def failed_job_view(request,id=0): # pragma: no cover @login_required() -def errormessage_view(request,errormessage='aap'): # pragma: no cover - if (errormessage=='3dsecure'): +def errormessage_view(request, errormessage='aap'): # pragma: no cover + if (errormessage == '3dsecure'): errormessage = '3D Secure Card Verification Error. Please check your card details.' - messages.error(request,errormessage) + messages.error(request, errormessage) return JSONResponse({ - "result":1, - }) + "result": 1, + }) # Shows analysis page @login_required() -@permission_required('rower.is_coach',fn=get_user_by_userid,raise_exception=True) -def analysis_view(request,userid=0): - r = getrequestrower(request,userid=userid) +@permission_required('rower.is_coach', fn=get_user_by_userid, raise_exception=True) +def analysis_view(request, userid=0): + r = getrequestrower(request, userid=userid) return render(request, "analysis.html", { - 'active':'nav-analysis', - 'rower':r, + 'active': 'nav-analysis', + 'rower': r, } ) # Shows laboratory page + + @login_required() -@permission_required('rower.is_coach',fn=get_user_by_userid,raise_exception=True) -def laboratory_view(request,userid=0): - r = getrequestrower(request,userid=userid) +@permission_required('rower.is_coach', fn=get_user_by_userid, raise_exception=True) +def laboratory_view(request, userid=0): + r = getrequestrower(request, userid=userid) return render(request, "laboratory.html", { - 'active':'nav-analysis', - 'rower':r, + 'active': 'nav-analysis', + 'rower': r, } ) diff --git a/rowers/views/paymentviews.py b/rowers/views/paymentviews.py index 268240cb..4fa2b841 100644 --- a/rowers/views/paymentviews.py +++ b/rowers/views/paymentviews.py @@ -1,4 +1,8 @@ from __future__ import absolute_import +from django.utils.encoding import force_bytes, force_text +from rowers.tokens import account_activation_token +from django.contrib.sites.shortcuts import get_current_site +from django.utils.http import urlsafe_base64_encode, urlsafe_base64_decode from __future__ import division from __future__ import print_function from __future__ import unicode_literals @@ -8,52 +12,54 @@ from django.core.mail import EmailMessage from rowers import credits + @csrf_exempt def braintree_webhook_view(request): - with open('braintreewebhooks.log','a') as f: + with open('braintreewebhooks.log', 'a') as f: t = time.localtime() timestamp = time.strftime('%b-%d-%Y_%H%M', t) f.write('\n') f.write(timestamp+' /rowers/braintree/\n') if request.method == 'POST': result = braintreestuff.webhook(request) - if result == 4: # pragma: no cover + if result == 4: # pragma: no cover raise PermissionDenied("Not allowed") return HttpResponse('') + def paidplans_view(request): if not request.user.is_anonymous: r = request.user.rower if r.paymentprocessor != 'braintree' and r.paymenttype == 'recurring': # pragma: no cover - messages.error(request,'Automated payment processing is currently only available through BrainTree (by PayPal). You are currently on a recurring payment plan with PayPal. Contact the site administrator at support@rowsandall.com before you proceed') + messages.error(request, 'Automated payment processing is currently only available through BrainTree (by PayPal). You are currently on a recurring payment plan with PayPal. Contact the site administrator at support@rowsandall.com before you proceed') else: r = None - - return render(request, 'paidplans.html', - {'rower':r}) + {'rower': r}) + @login_required() def billing_view(request): - if not PAYMENT_PROCESSING_ON: # pragma: no cover # pragma: no cover + if not PAYMENT_PROCESSING_ON: # pragma: no cover # pragma: no cover url = reverse('promembership') return HttpResponseRedirect(url) r = request.user.rower - if r.paymentprocessor != 'braintree' and r.paymenttype == 'recurring': # pragma: no cover - messages.error(request,'Automated payment processing is currently only available through BrainTree (by PayPal). You are currently on a recurring payment plan with PayPal. Contact the site administrator at support@rowsandall.com before you proceed') + if r.paymentprocessor != 'braintree' and r.paymenttype == 'recurring': # pragma: no cover + messages.error(request, 'Automated payment processing is currently only available through BrainTree (by PayPal). You are currently on a recurring payment plan with PayPal. Contact the site administrator at support@rowsandall.com before you proceed') - if payments.is_existing_customer(r): # pragma: no cover + if payments.is_existing_customer(r): # pragma: no cover url = reverse(upgrade_view) return HttpResponseRedirect(url) if request.method == 'POST': billingaddressform = RowerBillingAddressForm(request.POST) - planselectform = PlanSelectForm(request.POST,paymentprocessor='braintree') + planselectform = PlanSelectForm( + request.POST, paymentprocessor='braintree') if billingaddressform.is_valid(): cd = billingaddressform.cleaned_data for attr, value in cd.items(): @@ -65,30 +71,31 @@ def billing_view(request): plan = planselectform.cleaned_data['plan'] try: customer_id = braintreestuff.create_customer(r) - except ProcessorCustomerError: # pragma: no cover - messages.error(request,"Something went wrong registering you as a customer.") + except ProcessorCustomerError: # pragma: no cover + messages.error( + request, "Something went wrong registering you as a customer.") url = reverse(billing_view) return HttpResponseRedirect(url) url = reverse(payment_confirm_view, kwargs={ - 'planid':plan.id + 'planid': plan.id }) return HttpResponseRedirect(url) - else: billingaddressform = RowerBillingAddressForm(instance=r) planselectform = PlanSelectForm(paymentprocessor='braintree') return render(request, 'billing.html', - {'rower':r, - 'billingaddressform':billingaddressform, - 'planselectform':planselectform, + {'rower': r, + 'billingaddressform': billingaddressform, + 'planselectform': planselectform, }) -def buy_trainingplan_view(request,id=0): - if not PAYMENT_PROCESSING_ON: # pragma: no cover # pragma: no cover + +def buy_trainingplan_view(request, id=0): + if not PAYMENT_PROCESSING_ON: # pragma: no cover # pragma: no cover url = reverse('promembership') return HttpResponseRedirect(url) @@ -99,23 +106,24 @@ def buy_trainingplan_view(request,id=0): date__gte=timezone.now(), ).order_by("-date") - plan = get_object_or_404(InstantPlan,pk=id) + plan = get_object_or_404(InstantPlan, pk=id) - if r.paymentprocessor != 'braintree': # pragma: no cover - messages.error(request,"This purchase is currently only available through BrainTree (by PayPal)") + if r.paymentprocessor != 'braintree': # pragma: no cover + messages.error( + request, "This purchase is currently only available through BrainTree (by PayPal)") - if id == 0 or id is None: # pragma: no cover - messages.error(request,"There was an error accessing this plan") - url = reverse('rower_view_instantplan',kwargs={ - 'id':plan.uuid, + if id == 0 or id is None: # pragma: no cover + messages.error(request, "There was an error accessing this plan") + url = reverse('rower_view_instantplan', kwargs={ + 'id': plan.uuid, }) return HttpResponseRedirect(url) if request.method == 'POST': billingaddressform = RowerBillingAddressForm(instance=r) - form = InstantPlanSelectForm(request.POST,targets=targets) - if billingaddressform.is_valid(): # pragma: no cover + form = InstantPlanSelectForm(request.POST, targets=targets) + if billingaddressform.is_valid(): # pragma: no cover cd = billingaddressform.cleaned_data for attr, value in cd.items(): setattr(r, attr, value) @@ -132,36 +140,37 @@ def buy_trainingplan_view(request,id=0): status = True # get target and set enddate - try: # pragma: no cover + try: # pragma: no cover targetid = cd['target'] target = TrainingTarget.objects.get(id=int(targetid)) - except (KeyError,ValueError): + except (KeyError, ValueError): try: targetid = request.POST['target'] - if targetid != '': # pragma: no cover + if targetid != '': # pragma: no cover target = TrainingTarget.objects.get(id=int(targetid)) - else: # pragma: no cover + else: # pragma: no cover target = None except KeyError: target = None - if target and datechoice == 'target': # pragma: no cover + if target and datechoice == 'target': # pragma: no cover enddate = target.date - elif datechoice == 'startdate': # pragma: no cover + elif datechoice == 'startdate': # pragma: no cover enddate = startdate+datetime.timedelta(days=plan.duration) else: startdate = enddate-datetime.timedelta(days=plan.duration) pars = { - 'name':cd['name'], - 'enddate':enddate, - 'notes':notes, - 'status':status, - 'rower':r.id, + 'name': cd['name'], + 'enddate': enddate, + 'notes': notes, + 'status': status, + 'rower': r.id, } params = urllib.parse.urlencode(pars) - url = reverse('confirm_trainingplan_purchase_view',kwargs={'id':plan.id}) + url = reverse('confirm_trainingplan_purchase_view', + kwargs={'id': plan.id}) url = url + "?%s" % params return HttpResponseRedirect(url) @@ -172,30 +181,31 @@ def buy_trainingplan_view(request,id=0): return render(request, 'buy_trainingplan.html', { - 'rower':r, - 'plan':plan, - 'billingaddressform':billingaddressform, - 'form':form, + 'rower': r, + 'plan': plan, + 'billingaddressform': billingaddressform, + 'form': form, }) def purchase_checkouts_view(request): - if not PAYMENT_PROCESSING_ON: # pragma: no cover # pragma: no cover + if not PAYMENT_PROCESSING_ON: # pragma: no cover # pragma: no cover url = reverse('promembership') return HttpResponseRedirect(url) r = request.user.rower - if request.method != 'POST': # pragma: no cover - url = reverse('rower_view_instantplan',kwargs={ - 'id':plan.uuid, + if request.method != 'POST': # pragma: no cover + url = reverse('rower_view_instantplan', kwargs={ + 'id': plan.uuid, }) return HttpResponseRedirect(url) - if r.rowerplan == 'freecoach': # pragma: no cover - messages.error(request,'You cannot purchase this training plan as a free coach member') - url = reverse('rower_view_instantplan',kwargs={ - 'id':plan.uuid, + if r.rowerplan == 'freecoach': # pragma: no cover + messages.error( + request, 'You cannot purchase this training plan as a free coach member') + url = reverse('rower_view_instantplan', kwargs={ + 'id': plan.uuid, }) return HttpResponseRedirect(url) @@ -207,21 +217,21 @@ def purchase_checkouts_view(request): plan = InstantPlan.objects.get(id=data['plan']) authorizationstring = 'Bearer '+settings.WORKOUTS_FIT_TOKEN url = settings.WORKOUTS_FIT_URL+"/trainingplan/"+str(plan.uuid) - headers = {'Authorization':authorizationstring} - response = requests.get(url=url,headers=headers) - if response.status_code != 200: # pragma: no cover - messages.error(request,"Could not connect to the training plan server") + headers = {'Authorization': authorizationstring} + response = requests.get(url=url, headers=headers) + if response.status_code != 200: # pragma: no cover + messages.error( + request, "Could not connect to the training plan server") return HttpResponseRedirect(reverse('rower_select_instantplan')) - amount, success = braintreestuff.make_payment(r,data) + amount, success = braintreestuff.make_payment(r, data) diff = plan.price - int(amount) - eurocredits = credits.withdraw(diff,r) - - + eurocredits = credits.withdraw(diff, r) if success: - messages.info(request,"Your payment was completed and the sessions are copied to your calendar") + messages.info( + request, "Your payment was completed and the sessions are copied to your calendar") plansteps = response.json() name = data['name'] enddate = data['enddate'] @@ -230,18 +240,19 @@ def purchase_checkouts_view(request): startdate = enddate-datetime.timedelta(days=plan.duration) # upgrade rower - if r.rowerplan == 'basic': # pragma: no cover - messages.info(request,'You have been upgraded to the Self-Coach plan for the duration of the plan') + if r.rowerplan == 'basic': # pragma: no cover + messages.info( + request, 'You have been upgraded to the Self-Coach plan for the duration of the plan') r.rowerplan = 'plan' r.planexpires = enddate r.save() p = TrainingPlan( name=name, - #target=target, + # target=target, manager=r, startdate=startdate, - enddate=enddate,status=status, + enddate=enddate, status=status, notes=notes, ) @@ -249,9 +260,9 @@ def purchase_checkouts_view(request): p.rowers.add(r) - create_sessions_from_json(plansteps,r,startdate,r.user) + create_sessions_from_json(plansteps, r, startdate, r.user) - job = myqueue(queuehigh,handle_send_email_instantplan_notification, + job = myqueue(queuehigh, handle_send_email_instantplan_notification, r.user.username, r.user.email, plan.price, @@ -260,99 +271,104 @@ def purchase_checkouts_view(request): enddate) url = reverse('plannedsessions_view') - timeperiod = startdate.strftime('%Y-%m-%d')+'/'+enddate.strftime('%Y-%m-%d') + timeperiod = startdate.strftime( + '%Y-%m-%d')+'/'+enddate.strftime('%Y-%m-%d') url = url+'?when='+timeperiod return HttpResponseRedirect(url) - else: # pragma: no cover - messages.error(request,"There was a problem with your payment") - url = reverse('rower_view_instantplan',kwargs={ - 'id':plan.uuid, + else: # pragma: no cover + messages.error(request, "There was a problem with your payment") + url = reverse('rower_view_instantplan', kwargs={ + 'id': plan.uuid, }) return HttpResponseRedirect(url) - elif 'tac' not in request.POST: # pragma: no cover + elif 'tac' not in request.POST: # pragma: no cover try: - planid=int(request.POST['plan']) + planid = int(request.POST['plan']) enddate = request.POST['enddate'] rower = r.id # incomplete except IndexError: - messages.error(request,"There was an error in the payment form") + messages.error(request, "There was an error in the payment form") url = reverse("purchase_checkouts_view") return HttpResponseRedirect(url) - else: # pragma: no cover - messages.error(request,"There was an error in the payment form") + else: # pragma: no cover + messages.error(request, "There was an error in the payment form") - url = reverse('rower_select_instantplan') # pragma: no cover - if 'plan' in request.POST: # pragma: no cover + url = reverse('rower_select_instantplan') # pragma: no cover + if 'plan' in request.POST: # pragma: no cover plan = plan = InstantPlan.objects.get(id=request.POST['plan']) - url = reverse('rower_view_instantplan',kwargs={ - 'id':plan.uuid, - }) - return HttpResponseRedirect(url) # pragma: no cover + url = reverse('rower_view_instantplan', kwargs={ + 'id': plan.uuid, + }) + return HttpResponseRedirect(url) # pragma: no cover -def confirm_trainingplan_purchase_view(request,id = 0): - if not PAYMENT_PROCESSING_ON: # pragma: no cover # pragma: no cover + +def confirm_trainingplan_purchase_view(request, id=0): + if not PAYMENT_PROCESSING_ON: # pragma: no cover # pragma: no cover url = reverse('promembership') return HttpResponseRedirect(url) r = request.user.rower - plan = get_object_or_404(InstantPlan,pk=id) + plan = get_object_or_404(InstantPlan, pk=id) - if r.paymentprocessor != 'braintree': # pragma: no cover - messages.error(request,"This purchase is currently only available through BrainTree (by PayPal)") + if r.paymentprocessor != 'braintree': # pragma: no cover + messages.error( + request, "This purchase is currently only available through BrainTree (by PayPal)") - if id == 0 or id is None: # pragma: no cover - messages.error(request,"There was an error accessing this plan") - url = reverse('rower_view_instantplan',kwargs={ - 'id':plan.uuid, + if id == 0 or id is None: # pragma: no cover + messages.error(request, "There was an error accessing this plan") + url = reverse('rower_view_instantplan', kwargs={ + 'id': plan.uuid, }) return HttpResponseRedirect(url) client_token = braintreestuff.get_client_token(r) - enddate = request.GET.get('enddate',None) - name = request.GET.get('name','') - status = request.GET.get('status',True) - notes = request.GET.get('notes','') - if enddate is None: # pragma: no cover - messages.error(request,"There was an error accessing this plan") - url = reverse('rower_view_instantplan',kwargs={ - 'id':plan.uuid, + enddate = request.GET.get('enddate', None) + name = request.GET.get('name', '') + status = request.GET.get('status', True) + notes = request.GET.get('notes', '') + if enddate is None: # pragma: no cover + messages.error(request, "There was an error accessing this plan") + url = reverse('rower_view_instantplan', kwargs={ + 'id': plan.uuid, }) return render(request, 'confirm_trainingplan.html', { - 'plan':plan, - 'client_token':client_token, - 'rower':r, - 'enddate':enddate, - 'status':status, - 'name':name, - 'notes':notes, + 'plan': plan, + 'client_token': client_token, + 'rower': r, + 'enddate': enddate, + 'status': status, + 'name': name, + 'notes': notes, }) + @login_required() def upgrade_view(request): - if not PAYMENT_PROCESSING_ON: # pragma: no cover + if not PAYMENT_PROCESSING_ON: # pragma: no cover url = reverse('promembership') return HttpResponseRedirect(url) r = request.user.rower - if r.paymentprocessor != 'braintree' and r.paymenttype == 'recurring': # pragma: no cover - messages.error(request,'Automated payment processing is currently only available through BrainTree (by PayPal). You are currently on a recurring payment plan with PayPal. Contact the site administrator at support@rowsandall.com before you proceed') + if r.paymentprocessor != 'braintree' and r.paymenttype == 'recurring': # pragma: no cover + messages.error(request, 'Automated payment processing is currently only available through BrainTree (by PayPal). You are currently on a recurring payment plan with PayPal. Contact the site administrator at support@rowsandall.com before you proceed') - if r.subscription_id is None or r.subscription_id == '': # pragma: no cover + if r.subscription_id is None or r.subscription_id == '': # pragma: no cover url = reverse(billing_view) return HttpResponseRedirect(url) if request.method == 'POST': billingaddressform = RowerBillingAddressForm(request.POST) - planselectform = PlanSelectForm(request.POST,paymentprocessor='braintree') + planselectform = PlanSelectForm( + request.POST, paymentprocessor='braintree') if billingaddressform.is_valid(): cd = billingaddressform.cleaned_data for attr, value in cd.items(): @@ -364,7 +380,7 @@ def upgrade_view(request): if billingaddressform.is_valid(): url = reverse(upgrade_confirm_view, kwargs={ - 'planid':plan.id + 'planid': plan.id }) return HttpResponseRedirect(url) @@ -375,43 +391,44 @@ def upgrade_view(request): return render(request, 'upgrade.html', - {'rower':r, - 'billingaddressform':billingaddressform, - 'planselectform':planselectform, + {'rower': r, + 'billingaddressform': billingaddressform, + 'planselectform': planselectform, }) + @login_required() def downgrade_view(request): - if not PAYMENT_PROCESSING_ON: # pragma: no cover + if not PAYMENT_PROCESSING_ON: # pragma: no cover url = reverse('promembership') return HttpResponseRedirect(url) r = request.user.rower - if r.paymentprocessor != 'braintree' and r.paymenttype == 'recurring': # pragma: no cover - messages.error(request,'Automated payment processing is currently only available through BrainTree (by PayPal). You are currently on a recurring payment plan with PayPal. Contact the site administrator at support@rowsandall.com before you proceed') + if r.paymentprocessor != 'braintree' and r.paymenttype == 'recurring': # pragma: no cover + messages.error(request, 'Automated payment processing is currently only available through BrainTree (by PayPal). You are currently on a recurring payment plan with PayPal. Contact the site administrator at support@rowsandall.com before you proceed') - if r.subscription_id is None or r.subscription_id == '': # pragma: no cover + if r.subscription_id is None or r.subscription_id == '': # pragma: no cover url = reverse(billing_view) return HttpResponseRedirect(url) if request.method == 'POST': billingaddressform = RowerBillingAddressForm(request.POST) - planselectform = PlanSelectForm(request.POST,paymentprocessor='braintree') + planselectform = PlanSelectForm( + request.POST, paymentprocessor='braintree') if billingaddressform.is_valid(): cd = billingaddressform.cleaned_data for attr, value in cd.items(): setattr(r, attr, value) r.save() - if planselectform.is_valid(): plan = planselectform.cleaned_data['plan'] - if plan.price > r.paidplan.price: # pragma: no cover + if plan.price > r.paidplan.price: # pragma: no cover nextview = upgrade_confirm_view - elif plan.price == r.paidplan.price: # pragma: no cover - messages.info(request,'You did not select a new plan') + elif plan.price == r.paidplan.price: # pragma: no cover + messages.info(request, 'You did not select a new plan') url = reverse(downgrade_view) return HttpResponseRedirect(url) else: @@ -420,7 +437,7 @@ def downgrade_view(request): if billingaddressform.is_valid(): url = reverse(nextview, kwargs={ - 'planid':plan.id + 'planid': plan.id }) return HttpResponseRedirect(url) @@ -428,18 +445,19 @@ def downgrade_view(request): else: billingaddressform = RowerBillingAddressForm(instance=r) planselectform = PlanSelectForm(paymentprocessor='braintree', - rower=r,includeall=True, initial={'plan':r.paidplan}) + rower=r, includeall=True, initial={'plan': r.paidplan}) return render(request, 'downgrade.html', - {'rower':r, - 'billingaddressform':billingaddressform, - 'planselectform':planselectform, + {'rower': r, + 'billingaddressform': billingaddressform, + 'planselectform': planselectform, }) + @login_required() def plan_stop_view(request): - if not PAYMENT_PROCESSING_ON: # pragma: no cover + if not PAYMENT_PROCESSING_ON: # pragma: no cover url = reverse('promembership') return HttpResponseRedirect(url) @@ -447,84 +465,84 @@ def plan_stop_view(request): subscriptions = [] - if r.paymentprocessor != 'braintree' and r.paymenttype == 'recurring': # pragma: no cover - messages.error(request,'Automated payment processing is currently only available through BrainTree (by PayPal). You are currently on a recurring payment plan with PayPal. Contact the site administrator at support@rowsandall.com before you proceed') + if r.paymentprocessor != 'braintree' and r.paymenttype == 'recurring': # pragma: no cover + messages.error(request, 'Automated payment processing is currently only available through BrainTree (by PayPal). You are currently on a recurring payment plan with PayPal. Contact the site administrator at support@rowsandall.com before you proceed') if r.paidplan is not None and r.paidplan.paymentprocessor == 'braintree': try: subscriptions = braintreestuff.find_subscriptions(r) - except ProcessorCustomerError: # pragma: no cover + except ProcessorCustomerError: # pragma: no cover r.paymentprocessor = None r.save() - - return render(request, 'subscriptions_cancel.html', - {'rower':r, - 'subscriptions':subscriptions + {'rower': r, + 'subscriptions': subscriptions }) + @login_required() -def plan_tobasic_view(request,id=0): - if not PAYMENT_PROCESSING_ON: # pragma: no cover +def plan_tobasic_view(request, id=0): + if not PAYMENT_PROCESSING_ON: # pragma: no cover url = reverse('promembership') return HttpResponseRedirect(url) r = request.user.rower - if r.paidplan.paymentprocessor == 'braintree': # pragma: no cover - success, themessages,errormessages = braintreestuff.cancel_subscription(r,id) + if r.paidplan.paymentprocessor == 'braintree': # pragma: no cover + success, themessages, errormessages = braintreestuff.cancel_subscription( + r, id) for message in themessages: - messages.info(request,message) + messages.info(request, message) for message in errormessages: - messages.error(request,message) + messages.error(request, message) url = reverse(plan_stop_view) return HttpResponseRedirect(url) - @login_required() -def upgrade_confirm_view(request,planid = 0): - if not PAYMENT_PROCESSING_ON: # pragma: no cover +def upgrade_confirm_view(request, planid=0): + if not PAYMENT_PROCESSING_ON: # pragma: no cover url = reverse('promembership') return HttpResponseRedirect(url) try: plan = PaidPlan.objects.get(id=planid) - except PaidPlan.DoesNotExist: # pragma: no cover - messages.error(request,"Something went wrong. Please try again.") + except PaidPlan.DoesNotExist: # pragma: no cover + messages.error(request, "Something went wrong. Please try again.") url = reverse(billing_view) return HttpResponseRedirect(url) r = request.user.rower - if r.paymentprocessor != 'braintree' and r.paymenttype == 'recurring': # pragma: no cover - messages.error(request,'Automated payment processing is currently only available through BrainTree (by PayPal). You are currently on a recurring payment plan with PayPal. Contact the site administrator at support@rowsandall.com before you proceed') + if r.paymentprocessor != 'braintree' and r.paymenttype == 'recurring': # pragma: no cover + messages.error(request, 'Automated payment processing is currently only available through BrainTree (by PayPal). You are currently on a recurring payment plan with PayPal. Contact the site administrator at support@rowsandall.com before you proceed') client_token = braintreestuff.get_client_token(r) return render(request, "upgradeconfirm.html", { - 'plan':plan, - 'client_token':client_token, - 'rower':r, + 'plan': plan, + 'client_token': client_token, + 'rower': r, }) + @login_required() -def downgrade_confirm_view(request,planid = 0): - if not PAYMENT_PROCESSING_ON: # pragma: no cover +def downgrade_confirm_view(request, planid=0): + if not PAYMENT_PROCESSING_ON: # pragma: no cover url = reverse('promembership') return HttpResponseRedirect(url) try: plan = PaidPlan.objects.get(id=planid) - except PaidPlan.DoesNotExist: # pragma: no cover - messages.error(request,"Something went wrong. Please try again.") + except PaidPlan.DoesNotExist: # pragma: no cover + messages.error(request, "Something went wrong. Please try again.") url = reverse(billing_view) return HttpResponseRedirect(url) @@ -535,201 +553,206 @@ def downgrade_confirm_view(request,planid = 0): return render(request, "downgradeconfirm.html", { - 'plan':plan, - 'client_token':client_token, - 'rower':r, + 'plan': plan, + 'client_token': client_token, + 'rower': r, }) @login_required() -def payment_confirm_view(request,planid = 0): - if not PAYMENT_PROCESSING_ON: # pragma: no cover +def payment_confirm_view(request, planid=0): + if not PAYMENT_PROCESSING_ON: # pragma: no cover url = reverse('promembership') return HttpResponseRedirect(url) try: plan = PaidPlan.objects.get(id=planid) - except PaidPlan.DoesNotExist: # pragma: no cover - messages.error(request,"Something went wrong. Please try again.") + except PaidPlan.DoesNotExist: # pragma: no cover + messages.error(request, "Something went wrong. Please try again.") url = reverse(billing_view) return HttpResponseRedirect(url) r = request.user.rower - if r.paymentprocessor != 'braintree' and r.paymenttype == 'recurring': # pragma: no cover - messages.error(request,'Automated payment processing is currently only available through BrainTree (by PayPal). You are currently on a recurring payment plan with PayPal. Contact the site administrator at support@rowsandall.com before you proceed') + if r.paymentprocessor != 'braintree' and r.paymenttype == 'recurring': # pragma: no cover + messages.error(request, 'Automated payment processing is currently only available through BrainTree (by PayPal). You are currently on a recurring payment plan with PayPal. Contact the site administrator at support@rowsandall.com before you proceed') client_token = braintreestuff.get_client_token(r) return render(request, "paymentconfirm.html", { - 'plan':plan, - 'client_token':client_token, - 'rower':r, + 'plan': plan, + 'client_token': client_token, + 'rower': r, }) @login_required() def checkouts_view(request): - if not PAYMENT_PROCESSING_ON: # pragma: no cover + if not PAYMENT_PROCESSING_ON: # pragma: no cover url = reverse('promembership') return HttpResponseRedirect(url) - r = request.user.rower - if r.paymentprocessor != 'braintree' and r.paymenttype == 'recurring': # pragma: no cover - messages.error(request,'Automated payment processing is currently only available through BrainTree (by PayPal). You are currently on a recurring payment plan with PayPal. Contact the site administrator at support@rowsandall.com before you proceed') + if r.paymentprocessor != 'braintree' and r.paymenttype == 'recurring': # pragma: no cover + messages.error(request, 'Automated payment processing is currently only available through BrainTree (by PayPal). You are currently on a recurring payment plan with PayPal. Contact the site administrator at support@rowsandall.com before you proceed') - if request.method != 'POST': # pragma: no cover + if request.method != 'POST': # pragma: no cover url = reverse(paidplans_view) return HttpResponseRedirect(url) form = BillingForm(request.POST) if form.is_valid(): data = form.cleaned_data - success,amount = braintreestuff.create_subscription(r,data) + success, amount = braintreestuff.create_subscription(r, data) if success: - messages.info(request,"Your payment has succeeded and your plan has been updated") + messages.info( + request, "Your payment has succeeded and your plan has been updated") url = "{baseurl}?amount={amount:.2f}".format( - baseurl = reverse(payment_completed_view), - amount = amount) + baseurl=reverse(payment_completed_view), + amount=amount) return HttpResponseRedirect(url) - else: # pragma: no cover - messages.error(request,"There was a problem with your payment") + else: # pragma: no cover + messages.error(request, "There was a problem with your payment") url = reverse(billing_view) return HttpResponseRedirect(url) - elif 'tac' not in request.POST: # pragma: no cover + elif 'tac' not in request.POST: # pragma: no cover try: planid = int(request.POST['plan']) - url = reverse('payment_confirm_view',kwargs={'planid':planid}) - messages.error(request,"You must review and acknowledge the terms and conditions") + url = reverse('payment_confirm_view', kwargs={'planid': planid}) + messages.error( + request, "You must review and acknowledge the terms and conditions") return HttpResponseRedirect(url) except IndexError: - messages.error(request,"There was an error in the payment form") + messages.error(request, "There was an error in the payment form") url = reverse('billing_view') return HttpResponseRedirect(url) - else: # pragma: no cover - messages.error(request,"There was an error in the payment form") + else: # pragma: no cover + messages.error(request, "There was an error in the payment form") url = reverse(billing_view) return HttpResponseRedirect(url) - url = reverse(paidplans_view) # pragma: no cover - return HttpResponseRedirect(url) # pragma: no cover + url = reverse(paidplans_view) # pragma: no cover + return HttpResponseRedirect(url) # pragma: no cover + @login_required() def upgrade_checkouts_view(request): - if not PAYMENT_PROCESSING_ON: # pragma: no cover + if not PAYMENT_PROCESSING_ON: # pragma: no cover url = reverse('promembership') return HttpResponseRedirect(url) - r = request.user.rower - if request.method != 'POST': # pragma: no cover + if request.method != 'POST': # pragma: no cover url = reverse(paidplans_view) return HttpResponseRedirect(url) form = BillingForm(request.POST) if form.is_valid(): data = form.cleaned_data - success,amount = braintreestuff.update_subscription(r,data) + success, amount = braintreestuff.update_subscription(r, data) if success: - messages.info(request,"Your payment has succeeded and your plan has been updated") + messages.info( + request, "Your payment has succeeded and your plan has been updated") url = "{baseurl}?amount={amount:.2f}".format( - baseurl = reverse(payment_completed_view), - amount = amount) + baseurl=reverse(payment_completed_view), + amount=amount) return HttpResponseRedirect(url) - else: # pragma: no cover - messages.error(request,"There was a problem with your payment") + else: # pragma: no cover + messages.error(request, "There was a problem with your payment") url = reverse(upgrade_view) return HttpResponseRedirect(url) - elif 'tac' not in request.POST: # pragma: no cover + elif 'tac' not in request.POST: # pragma: no cover try: planid = int(request.POST['plan']) - url = reverse('upgrade_confirm_view',kwargs={'planid':planid}) - messages.error(request,"You must review and acknowledge the terms and conditions") + url = reverse('upgrade_confirm_view', kwargs={'planid': planid}) + messages.error( + request, "You must review and acknowledge the terms and conditions") return HttpResponseRedirect(url) except IndexError: - messages.error(request,"There was an error in the payment form") + messages.error(request, "There was an error in the payment form") url = reverse('billing_view') return HttpResponseRedirect(url) - else: # pragma: no cover - messages.error(request,"There was an error in the payment form") + else: # pragma: no cover + messages.error(request, "There was an error in the payment form") url = reverse(upgrade_view) return HttpResponseRedirect(url) - url = reverse(paidplans_view) # pragma: no cover - return HttpResponseRedirect(url) # pragma: no cover + url = reverse(paidplans_view) # pragma: no cover + return HttpResponseRedirect(url) # pragma: no cover + @login_required() def downgrade_checkouts_view(request): - if not PAYMENT_PROCESSING_ON: # pragma: no cover + if not PAYMENT_PROCESSING_ON: # pragma: no cover url = reverse('promembership') return HttpResponseRedirect(url) - r = request.user.rower - if request.method != 'POST': # pragma: no cover + if request.method != 'POST': # pragma: no cover url = reverse(paidplans_view) return HttpResponseRedirect(url) form = BillingForm(request.POST) if form.is_valid(): data = form.cleaned_data - success = braintreestuff.update_subscription(r,data,method='down') + success = braintreestuff.update_subscription(r, data, method='down') if success: - messages.info(request,"Your plan has been updated") + messages.info(request, "Your plan has been updated") url = reverse(downgrade_completed_view) return HttpResponseRedirect(url) - else: # pragma: no cover - messages.error(request,"There was a problem with your transaction") + else: # pragma: no cover + messages.error( + request, "There was a problem with your transaction") url = reverse(upgrade_view) return HttpResponseRedirect(url) - elif 'tac' not in request.POST: # pragma: no cover + elif 'tac' not in request.POST: # pragma: no cover try: planid = int(request.POST['plan']) - url = reverse('downgrade_confirm_view',kwargs={'planid':planid}) - messages.error(request,"You must review and acknowledge the terms and conditions") + url = reverse('downgrade_confirm_view', kwargs={'planid': planid}) + messages.error( + request, "You must review and acknowledge the terms and conditions") return HttpResponseRedirect(url) except IndexError: - messages.error(request,"There was an error in the payment form") + messages.error(request, "There was an error in the payment form") url = reverse('billing_view') return HttpResponseRedirect(url) - else: # pragma: no cover - messages.error(request,"There was an error in the payment form") + else: # pragma: no cover + messages.error(request, "There was an error in the payment form") url = reverse(upgrade_view) return HttpResponseRedirect(url) - url = reverse(paidplans_view) # pragma: no cover - return HttpResponseRedirect(url) # pragma: no cover + url = reverse(paidplans_view) # pragma: no cover + return HttpResponseRedirect(url) # pragma: no cover @login_required() def payment_completed_view(request): - if not PAYMENT_PROCESSING_ON: # pragma: no cover + if not PAYMENT_PROCESSING_ON: # pragma: no cover url = reverse('promembership') return HttpResponseRedirect(url) - amount = request.GET.get('amount',0) - + amount = request.GET.get('amount', 0) r = request.user.rower return render(request, "payment_completed.html", { - 'rower':r, - 'amount':amount, - }) + 'rower': r, + 'amount': amount, + }) + @login_required() def downgrade_completed_view(request): - if not PAYMENT_PROCESSING_ON: # pragma: no cover + if not PAYMENT_PROCESSING_ON: # pragma: no cover url = reverse('promembership') return HttpResponseRedirect(url) @@ -738,15 +761,14 @@ def downgrade_completed_view(request): return render(request, "downgrade_completed.html", { - 'rower':r - }) + 'rower': r + }) + -from django.utils.encoding import force_bytes, force_text -from django.utils.http import urlsafe_base64_encode, urlsafe_base64_decode -from django.contrib.sites.shortcuts import get_current_site -from rowers.tokens import account_activation_token # Email activation -def useractivate(request, uidb64, token): # pragma: no cover + + +def useractivate(request, uidb64, token): # pragma: no cover try: uid = force_text(urlsafe_base64_decode(uidb64)) user = User.objects.get(id=uid) @@ -760,15 +782,14 @@ def useractivate(request, uidb64, token): # pragma: no cover subject = "Thank you for registering on rowsandall.com" from_address = 'Sander Roosendaal ' - d = {'first_name':user.first_name} + d = {'first_name': user.first_name} template = 'registeremail.html' if user.rower.rowerplan == 'freecoach': template = 'coachregisteremail.html' - send_template_email(from_address,[fullemail], - subject,'registeremail.html',d) - + send_template_email(from_address, [fullemail], + subject, 'registeremail.html', d) subject2 = "New User" message2 = "New user registered.\n" @@ -778,30 +799,28 @@ def useractivate(request, uidb64, token): # pragma: no cover if user.rower.rowerplan == 'freecoach': subject2 = "New Free Coach User" - send_mail(subject2, message2, 'Rowsandall Server ', ['roosendaalsander@gmail.com']) - - messages.info(request,'Thank you for your email confirmation. YOu are now signed in to your account.') + messages.info( + request, 'Thank you for your email confirmation. YOu are now signed in to your account.') login(request, user, backend=settings.AUTHENTICATION_BACKENDS[0]) url = reverse('workouts_view') - #if user.rower.rowerplan == 'freecoach': + # if user.rower.rowerplan == 'freecoach': # url+='?next=/rowers/me/teams' return HttpResponseRedirect(url) else: return render(request, "invalid_activation.html", - {} ) - + {}) # User registration def rower_register_view(request): - nextpage = request.GET.get('next','/rowers/list-workouts/') - if nextpage == '': # pragma: no cover + nextpage = request.GET.get('next', '/rowers/list-workouts/') + if nextpage == '': # pragma: no cover nextpage = '/rowers/list-workouts/' if request.method == 'POST': @@ -818,7 +837,7 @@ def rower_register_view(request): weightcategory = form.cleaned_data['weightcategory'] adaptiveclass = form.cleaned_data['adaptiveclass'] nextpage = request.POST['next'] - theuser = User.objects.create_user(username,password=password) + theuser = User.objects.create_user(username, password=password) theuser.first_name = first_name theuser.last_name = last_name theuser.email = email @@ -827,7 +846,7 @@ def rower_register_view(request): birthdate = birthdate.replace(tzinfo=None) - therower = Rower(user=theuser,sex=sex,birthdate=birthdate, + therower = Rower(user=theuser, sex=sex, birthdate=birthdate, weightcategory=weightcategory, adaptiveclass=adaptiveclass) @@ -840,9 +859,9 @@ def rower_register_view(request): f = 'media/testdata.csv.gz' timestr = strftime("%Y%m%d-%H%M%S") f2 = f[:-7]+timestr+'.csv.gz' - copyfile(f,f2) + copyfile(f, f2) - response = dataprep.new_workout_from_file(therower,f2, + response = dataprep.new_workout_from_file(therower, f2, title='New User Sample Data', notes='This is an example workout to get you started') newworkoutid = response[0] @@ -873,31 +892,32 @@ def rower_register_view(request): mail_subject, message, to=[to_email] ) email.send() - return render(request,'confirmemailpage.html',{'address':to_email}) - + return render(request, 'confirmemailpage.html', {'address': to_email}) # login(request,theuser) # return HttpResponseRedirect(nextpage) # '/rowers/register/thankyou/') - else: # pragma: no cover + else: # pragma: no cover return render(request, "registration_form.html", - {'form':form, - 'next':nextpage,}) + {'form': form, + 'next': nextpage, }) else: - form = RegistrationFormSex() + form = RegistrationFormSex() return render(request, "registration_form.html", - {'form':form, - 'next':nextpage,}) + {'form': form, + 'next': nextpage, }) # User registration -def freecoach_register_view(request): # pragma: no cover - nextpage = request.GET.get('next','/rowers/me/teams/') - if nextpage == '': # pragma: no cover + +def freecoach_register_view(request): # pragma: no cover + + nextpage = request.GET.get('next', '/rowers/me/teams/') + if nextpage == '': # pragma: no cover nextpage = '/rowers/me/teams/' if request.method == 'POST': @@ -914,7 +934,7 @@ def freecoach_register_view(request): # pragma: no cover weightcategory = form.cleaned_data['weightcategory'] adaptiveclass = form.cleaned_data['adaptiveclass'] nextpage = request.POST['next'] - theuser = User.objects.create_user(username,password=password) + theuser = User.objects.create_user(username, password=password) theuser.first_name = first_name theuser.last_name = last_name theuser.email = email @@ -922,10 +942,10 @@ def freecoach_register_view(request): # pragma: no cover birthdate = birthdate.replace(tzinfo=None) - therower = Rower(user=theuser,sex=sex,birthdate=birthdate, + therower = Rower(user=theuser, sex=sex, birthdate=birthdate, weightcategory=weightcategory, adaptiveclass=adaptiveclass, - rowerplan='freecoach',clubsize=10) + rowerplan='freecoach', clubsize=10) therower.save() @@ -957,24 +977,25 @@ def freecoach_register_view(request): # pragma: no cover return HttpResponseRedirect(nextpage) - else: # pragma: no cover + else: # pragma: no cover return render(request, "freecoach_registration_form.html", - {'form':form, - 'next':nextpage,}) + {'form': form, + 'next': nextpage, }) else: - form = RegistrationFormSex() + form = RegistrationFormSex() form.fields.pop('sex') form.fields.pop('weightcategory') form.fields.pop('adaptiveclass') return render(request, "freecoach_registration_form.html", - {'form':form, - 'next':nextpage,}) + {'form': form, + 'next': nextpage, }) + @login_required() -@permission_required('rower.is_staff',fn=get_user_by_userid,raise_exception=True) -def transactions_view(request): # pragma: no cover +@permission_required('rower.is_staff', fn=get_user_by_userid, raise_exception=True) +def transactions_view(request): # pragma: no cover if not request.user.is_staff: raise PermissionDenied("Not Allowed") @@ -984,8 +1005,9 @@ def transactions_view(request): # pragma: no cover startdate = dateform.cleaned_data['startdate'] enddate = dateform.cleaned_data['enddate'] - df = braintreestuff.get_transactions(startdate,enddate) - filename="transactions_{s}_{e}.csv".format(s = startdate, e = enddate) + df = braintreestuff.get_transactions(startdate, enddate) + filename = "transactions_{s}_{e}.csv".format( + s=startdate, e=enddate) response = HttpResponse(df.to_csv()) response['Content-Disposition'] = 'attachment; filename="%s"' % filename response['Content-Type'] = 'application/octet-stream' @@ -998,5 +1020,5 @@ def transactions_view(request): # pragma: no cover return render(request, 'transactions.html', { - 'dateform':dateform + 'dateform': dateform }) diff --git a/rowers/views/planviews.py b/rowers/views/planviews.py index a600c45a..7f22554c 100644 --- a/rowers/views/planviews.py +++ b/rowers/views/planviews.py @@ -1,4 +1,7 @@ from __future__ import absolute_import +from rowers.plannedsessions import cratiocolors, checkscores +from rowers.utils import allmonths +from rowers.utils import allsundays from __future__ import division from __future__ import print_function from __future__ import unicode_literals @@ -11,13 +14,15 @@ from taggit.models import Tag import rowers.garmin_stuff as gs from rowers import credits -@login_required -@permission_required('plannedsession.view_session',fn=get_session_by_pk,raise_exception=True) -def plannedsession_comment_view(request,id=0,userid=0): - r = getrequestplanrower(request,userid=userid) - ps = get_object_or_404(PlannedSession,pk=id) - comments = PlannedSessionComment.objects.filter(plannedsession=ps).order_by("created") +@login_required +@permission_required('plannedsession.view_session', fn=get_session_by_pk, raise_exception=True) +def plannedsession_comment_view(request, id=0, userid=0): + r = getrequestplanrower(request, userid=userid) + ps = get_object_or_404(PlannedSession, pk=id) + + comments = PlannedSessionComment.objects.filter( + plannedsession=ps).order_by("created") if request.method == 'POST': manager = ps.manager @@ -27,71 +32,71 @@ def plannedsession_comment_view(request,id=0,userid=0): comment = cd['comment'] comment = bleach.clean(comment) try: - if isinstance(comment,unicode): # pragma: no cover + if isinstance(comment, unicode): # pragma: no cover comment = comment.encode('utf8') - elif isinstance(comment, str): # pragma: no cover + elif isinstance(comment, str): # pragma: no cover comment = comment.decode('utf8') except: pass notification = cd['notification'] - c = PlannedSessionComment(plannedsession=ps,user=request.user,comment=comment, - notification=notification) + c = PlannedSessionComment(plannedsession=ps, user=request.user, comment=comment, + notification=notification) c.save() url = reverse('plannedsession_comment_view', kwargs={ - 'id':id - }) + 'id': id + }) message = '{name} says: {comment}'.format( - name = request.user.first_name, - comment = comment, - url = url, + name=request.user.first_name, + comment=comment, + url=url, ) - if request.user != manager: # pragma: no cover - a_messages.info(r.user,message.encode('ascii','ignore')) + if request.user != manager: # pragma: no cover + a_messages.info(r.user, message.encode('ascii', 'ignore')) sessiontype = 'training session' - if ps.sessiontype == 'race': # pragma: no cover + if ps.sessiontype == 'race': # pragma: no cover sessiontype = 'online virtual race' - elif ps.sessiontype == 'indoorrace': # pragma: no cover + elif ps.sessiontype == 'indoorrace': # pragma: no cover sessiontype = 'indoor online virtual race' res = myqueue(queuehigh, - handle_sendemailnewcomment,r.user.first_name, - r.user.last_name, - r.user.email, + handle_sendemailnewcomment, r.user.first_name, + r.user.last_name, + r.user.email, request.user.first_name, request.user.last_name, - comment,ps.name,ps.id, - emailbounced = r.emailbounced, - sessiontype = sessiontype, - commentlink = url - ) + comment, ps.name, ps.id, + emailbounced=r.emailbounced, + sessiontype=sessiontype, + commentlink=url + ) commenters = {oc.user for oc in comments if oc.notification} - if ps.sessiontype=='race': # pragma: no cover + if ps.sessiontype == 'race': # pragma: no cover registrations = VirtualRaceResult.objects.filter( race__id=ps.id, emailnotifications=True) ids = [rg.userid for rg in registrations] - rwrs = Rower.objects.filter(id__in= ids) + rwrs = Rower.objects.filter(id__in=ids) rowers = {u.user for u in rwrs} - elif ps.sessiontype=='indoorrace': # pragma: no cover + elif ps.sessiontype == 'indoorrace': # pragma: no cover registrations = IndoorVirtualRaceResult.objects.filter( race__id=ps.id, emailnotifications=True) ids = [rg.userid for rg in registrations] - rwrs = Rower.objects.filter(id__in= ids) + rwrs = Rower.objects.filter(id__in=ids) rowers = {u.user for u in rwrs} else: rowers = {r.user for r in ps.rower.all()} commenters = set(list(commenters)+list(rowers)) for u in commenters: try: - a_messages.info(u,message) - except ValueError: # pragma: no cover + a_messages.info(u, message) + except ValueError: # pragma: no cover pass - if u != request.user and u != r.user: # pragma: no cover + if u != request.user and u != r.user: # pragma: no cover ocr = Rower.objects.get(user=u) res = myqueue(queue, handle_sendemailnewresponse, @@ -104,11 +109,11 @@ def plannedsession_comment_view(request,id=0,userid=0): ps.name, ps.id, c.id, - emailbounced = ocr.emailbounced, - sessiontype = sessiontype, - commentlink = url + emailbounced=ocr.emailbounced, + sessiontype=sessiontype, + commentlink=url ) - if ps.sessiontype in ['race','indoorrace']: # pragma: no cover + if ps.sessiontype in ['race', 'indoorrace']: # pragma: no cover followers = VirtualRaceFollower.objects.filter(race__id=ps.id) for follower in followers: othername = '' @@ -116,81 +121,81 @@ def plannedsession_comment_view(request,id=0,userid=0): othername = follower.user.first_name+' '+follower.user.last_name email = follower.emailaddress res = myqueue(queue, - handle_sendemailnewresponse, - othername,'',email, - request.user.first_name, - request.user.last_name, - comment, - ps.name,ps.id,c.id, - emailbounced = False, - sessiontype = sessiontype, - commentlink = url, - ) + handle_sendemailnewresponse, + othername, '', email, + request.user.first_name, + request.user.last_name, + comment, + ps.name, ps.id, c.id, + emailbounced=False, + sessiontype=sessiontype, + commentlink=url, + ) - url = reverse('plannedsession_comment_view',kwargs={'id':ps.id}) + url = reverse('plannedsession_comment_view', kwargs={'id': ps.id}) return HttpResponseRedirect(url) - form = WorkoutCommentForm() rower = getrower(request.user) - if ps.sessiontype in ['race','indoorrace']: # pragma: no cover + if ps.sessiontype in ['race', 'indoorrace']: # pragma: no cover breadcrumbs = [ { - 'url':reverse('virtualevents_view'), + 'url': reverse('virtualevents_view'), 'name': 'Races' - }, + }, { - 'url': reverse('virtualevent_view',kwargs={'id':ps.id}), + 'url': reverse('virtualevent_view', kwargs={'id': ps.id}), 'name': ps.name - }, + }, { - 'url':reverse('plannedsession_comment_view',kwargs={'id':ps.id}), + 'url': reverse('plannedsession_comment_view', kwargs={'id': ps.id}), 'name': 'Comments' - } - ] + } + ] active = 'nav-racing' else: breadcrumbs = [ { - 'url':reverse('plannedsessions_view'), + 'url': reverse('plannedsessions_view'), 'name': 'Sessions' - }, + }, { - 'url': reverse('plannedsession_view',kwargs={'id':ps.id}), + 'url': reverse('plannedsession_view', kwargs={'id': ps.id}), 'name': ps.name - }, + }, { - 'url':reverse('plannedsession_comment_view',kwargs={'id':ps.id}), + 'url': reverse('plannedsession_comment_view', kwargs={'id': ps.id}), 'name': 'Comments' - } - ] + } + ] active = 'nav-plan' return render(request, - 'plannedsession_comments.html', - {'plannedsession':ps, - 'rower':rower, - 'breadcrumbs':breadcrumbs, - 'active':active, - 'comments':comments, - 'form':form, - }) + 'plannedsession_comments.html', + {'plannedsession': ps, + 'rower': rower, + 'breadcrumbs': breadcrumbs, + 'active': active, + 'comments': comments, + 'form': form, + }) + @login_required -@permission_required('virtualevent.change_race',fn=get_session_by_pk,raise_exception=True) -def plannedsession_message_view(request,id=0,userid=0): - r = getrequestplanrower(request,userid=userid) - ps = get_object_or_404(PlannedSession,pk=id) +@permission_required('virtualevent.change_race', fn=get_session_by_pk, raise_exception=True) +def plannedsession_message_view(request, id=0, userid=0): + r = getrequestplanrower(request, userid=userid) + ps = get_object_or_404(PlannedSession, pk=id) userform = VirtualRaceAthleteForm(instance=ps) if request.method == 'POST': - userform = VirtualRaceAthleteForm(request.POST,instance=ps) + userform = VirtualRaceAthleteForm(request.POST, instance=ps) if userform.is_valid(): subject = userform.cleaned_data['subject'] message = userform.cleaned_data['message'] @@ -211,76 +216,74 @@ def plannedsession_message_view(request,id=0,userid=0): message, ) - url = reverse('plannedsession_view',kwargs={'id':ps.id}) - if ps.sessiontype in ['race','indoorrace']: # pragma: no cover - url = reverse('virtualevent_view',kwargs={'id':ps.id}) + url = reverse('plannedsession_view', kwargs={'id': ps.id}) + if ps.sessiontype in ['race', 'indoorrace']: # pragma: no cover + url = reverse('virtualevent_view', kwargs={'id': ps.id}) return HttpResponseRedirect(url) - - if ps.sessiontype in ['race','indoorrace']: # pragma: no cover + if ps.sessiontype in ['race', 'indoorrace']: # pragma: no cover breadcrumbs = [ { - 'url':reverse('virtualevents_view'), + 'url': reverse('virtualevents_view'), 'name': 'Races' - }, + }, { - 'url': reverse('virtualevent_view',kwargs={'id':ps.id}), + 'url': reverse('virtualevent_view', kwargs={'id': ps.id}), 'name': ps.name - }, + }, { - 'url':reverse('plannedsession_comment_view',kwargs={'id':ps.id}), + 'url': reverse('plannedsession_comment_view', kwargs={'id': ps.id}), 'name': 'Comments' - } - ] + } + ] active = 'nav-racing' else: breadcrumbs = [ { - 'url':reverse('plannedsessions_view'), + 'url': reverse('plannedsessions_view'), 'name': 'Sessions' - }, + }, { - 'url': reverse('plannedsession_view',kwargs={'id':ps.id}), + 'url': reverse('plannedsession_view', kwargs={'id': ps.id}), 'name': ps.name - }, + }, { - 'url':reverse('plannedsession_comment_view',kwargs={'id':ps.id}), + 'url': reverse('plannedsession_comment_view', kwargs={'id': ps.id}), 'name': 'Comments' - } - ] + } + ] active = 'nav-plan' return render(request, - 'plannedsession_message.html', - {'plannedsession':ps, - 'rower':r, - 'breadcrumbs':breadcrumbs, - 'active':active, - 'userform':userform, - }) + 'plannedsession_message.html', + {'plannedsession': ps, + 'rower': r, + 'breadcrumbs': breadcrumbs, + 'active': active, + 'userform': userform, + }) # Cloning sessions -@user_passes_test(can_plan,login_url="/rowers/paidplans/", +@user_passes_test(can_plan, login_url="/rowers/paidplans/", message="This functionality requires a Coach or Self-Coach plan", redirect_field_name=None) def plannedsession_multiclone_view( request, userid=0,): - r = getrequestplanrower(request,userid=userid) + r = getrequestplanrower(request, userid=userid) - - startdate,enddate = get_dates_timeperiod(request) + startdate, enddate = get_dates_timeperiod(request) startdate = startdate.date() enddate = enddate.date() teamid = get_team(request) - if request.method == 'POST' and 'daterange' in request.POST: # pragma: no cover + if request.method == 'POST' and 'daterange' in request.POST: # pragma: no cover dateform = DateRangeForm(request.POST) if dateform.is_valid(): startdate = dateform.cleaned_data['startdate'] @@ -291,9 +294,9 @@ def plannedsession_multiclone_view( request.session['enddate'] = enddatestring else: dateform = DateRangeForm(initial={ - 'startdate':startdate, - 'enddate':enddate, - }) + 'startdate': startdate, + 'enddate': enddate, + }) if request.method == 'POST' and 'plannedsessions' in request.POST: form = PlannedSessionMultipleCloneForm(request.POST) @@ -317,20 +320,19 @@ def plannedsession_multiclone_view( ps.fitfile = None ps.save() for rower in rowers: - add_rower_session(rower,ps) + add_rower_session(rower, ps) for team in teams: - add_team_session(team,ps) + add_team_session(team, ps) startdatestring = shiftstartdate.strftime('%Y-%m-%d') enddatestring = lastdate.strftime('%Y-%m-%d') url = reverse(plannedsessions_view, - kwargs = { - 'userid':r.user.id, - }) + kwargs={ + 'userid': r.user.id, + }) - - url+='?when='+startdatestring+'/'+enddatestring + url += '?when='+startdatestring+'/'+enddatestring return HttpResponseRedirect(url) @@ -340,10 +342,10 @@ def plannedsession_multiclone_view( is_template=False, startdate__lte=enddate, enddate__gte=startdate).order_by( - "startdate","preferreddate","enddate").exclude( + "startdate", "preferreddate", "enddate").exclude( sessiontype='race') - if teamid: # pragma: no cover + if teamid: # pragma: no cover sps = sps.filter(team__in=[teamid]) try: team = Team.objects.get(id=teamid) @@ -355,14 +357,14 @@ def plannedsession_multiclone_view( team = None query = request.GET.get('q') - if query: # pragma: no cover + if query: # pragma: no cover query_list = query.split() sps = sps.filter( reduce(operator.and_, (Q(name__icontains=q) for q in query_list)) | reduce(operator.and_, (Q(comment__icontains=q) for q in query_list)) - ) + ) form = PlannedSessionMultipleCloneForm() form.fields["plannedsessions"].queryset = sps @@ -371,75 +373,79 @@ def plannedsession_multiclone_view( try: trainingplan = TrainingPlan.objects.filter( - startdate__lte = startdate, - rowers = r, - enddate__gte = enddate)[0] + startdate__lte=startdate, + rowers=r, + enddate__gte=enddate)[0] except IndexError: trainingplan = None - timeperiod = startdate.strftime('%Y-%m-%d')+'/'+enddate.strftime('%Y-%m-%d') + timeperiod = startdate.strftime( + '%Y-%m-%d')+'/'+enddate.strftime('%Y-%m-%d') breadcrumbs = [ { 'url': reverse(plannedsessions_view), 'name': 'Planned Sessions' - }, + }, { 'url': reverse(plannedsession_multiclone_view), 'name': 'Clone Multiple Sessions' - } - ] + } + ] dateform = DateRangeForm(initial={ - 'startdate':startdate, - 'enddate':enddate, - }) + 'startdate': startdate, + 'enddate': enddate, + }) - if Team.objects.filter(manager=request.user).count()>=1: + if Team.objects.filter(manager=request.user).count() >= 1: teamform = RowerTeamForm(request.user) - if teamid: # pragma: no cover - teamform = RowerTeamForm(request.user,initial={'team':teamid}) + if teamid: # pragma: no cover + teamform = RowerTeamForm(request.user, initial={'team': teamid}) else: teamform = None return render(request, 'plannedsessions_multiclone_select.html', - {'plannedsessions':sps, - 'breadcrumbs':breadcrumbs, - 'plan':trainingplan, - 'dateform':dateform, - 'startdate':startdate, - 'enddate':enddate, - 'form':form, - 'dateshiftform':dateshiftform, - 'rower':r, - 'active':'nav-plan', - 'timeperiod':timeperiod, - 'team':team, - 'teamform':teamform, + {'plannedsessions': sps, + 'breadcrumbs': breadcrumbs, + 'plan': trainingplan, + 'dateform': dateform, + 'startdate': startdate, + 'enddate': enddate, + 'form': form, + 'dateshiftform': dateshiftform, + 'rower': r, + 'active': 'nav-plan', + 'timeperiod': timeperiod, + 'team': team, + 'teamform': teamform, } ) -@permission_required('plannedsession.change_session',fn=get_session_by_pk,raise_exception=True) -@user_passes_test(can_plan,login_url="/rowers/paidplans/", + +@permission_required('plannedsession.change_session', fn=get_session_by_pk, raise_exception=True) +@user_passes_test(can_plan, login_url="/rowers/paidplans/", message="This functionality requires a Coach or Self-Coach plan", redirect_field_name=None) -def template_share_view(request,id=0,userid=0): - r = getrequestplanrower(request,userid=userid) - ps = get_object_or_404(PlannedSession,pk=id) +def template_share_view(request, id=0, userid=0): + r = getrequestplanrower(request, userid=userid) + ps = get_object_or_404(PlannedSession, pk=id) if ps.can_be_shared: ps.is_public = True ps.save() - else: # pragma: no cover - messages.error(request,'This planned session comes from a third party and cannot be shared') + else: # pragma: no cover + messages.error( + request, 'This planned session comes from a third party and cannot be shared') return HttpResponseRedirect(reverse(template_library_view)) -@permission_required('plannedsession.change_session',fn=get_session_by_pk,raise_exception=True) -@user_passes_test(can_plan,login_url="/rowers/paidplans/", + +@permission_required('plannedsession.change_session', fn=get_session_by_pk, raise_exception=True) +@user_passes_test(can_plan, login_url="/rowers/paidplans/", message="This functionality requires a Coach or Self-Coach plan", redirect_field_name=None) -def template_makeprivate_view(request,id=0,userid=0): - r = getrequestplanrower(request,userid=userid) - ps = get_object_or_404(PlannedSession,pk=id) +def template_makeprivate_view(request, id=0, userid=0): + r = getrequestplanrower(request, userid=userid) + ps = get_object_or_404(PlannedSession, pk=id) ps.is_public = False ps.save() @@ -447,29 +453,30 @@ def template_makeprivate_view(request,id=0,userid=0): # Manage Template sessions (library) -@user_passes_test(can_plan,login_url="/rowers/paidplans/", +@user_passes_test(can_plan, login_url="/rowers/paidplans/", message="This functionality requires a Coach or Self-Coach plan", redirect_field_name=None) -def template_library_view(request,userid=0): - r = getrequestplanrower(request,userid=userid) - templates = PlannedSession.objects.filter(manager=request.user,is_template=True) - templates2 = PlannedSession.objects.filter(is_template=True,is_public=True) +def template_library_view(request, userid=0): + r = getrequestplanrower(request, userid=userid) + templates = PlannedSession.objects.filter( + manager=request.user, is_template=True) + templates2 = PlannedSession.objects.filter( + is_template=True, is_public=True) templates = templates | templates2 - startdate,enddate = get_dates_timeperiod(request) + startdate, enddate = get_dates_timeperiod(request) startdate = startdate.date() enddate = enddate.date() try: trainingplan = TrainingPlan.objects.filter( - startdate__lte = startdate, - rowers = r, - enddate__gte = enddate)[0] + startdate__lte=startdate, + rowers=r, + enddate__gte=enddate)[0] except IndexError: trainingplan = None - alltags = [] for t in templates: tags = t.tags.all() @@ -479,7 +486,7 @@ def template_library_view(request,userid=0): alltags = uniqify(alltags) tag = request.GET.get('tag') - if tag: # pragma: no cover + if tag: # pragma: no cover tags = [tag] templates = templates.filter(tags__name__in=tags).distinct() @@ -487,28 +494,28 @@ def template_library_view(request,userid=0): { 'url': reverse(plannedsessions_view), 'name': 'Planned Sessions' - }, + }, { 'url': reverse(template_library_view), 'name': 'Session Library', } - ] + ] - return render(request,'templatelibrary.html', + return render(request, 'templatelibrary.html', { - 'teams':get_my_teams(request.user), - 'breadcrumbs': breadcrumbs, - 'templates':templates, - 'plan': trainingplan, - 'rower':r, - 'active':'nav-plan', - 'alltags':alltags, - } + 'teams': get_my_teams(request.user), + 'breadcrumbs': breadcrumbs, + 'templates': templates, + 'plan': trainingplan, + 'rower': r, + 'active': 'nav-plan', + 'alltags': alltags, + } ) # Individual user creates training for himself -@user_passes_test(can_plan,login_url="/rowers/paidplans/", +@user_passes_test(can_plan, login_url="/rowers/paidplans/", message="This functionality requires a Coach or Self-Coach plan", redirect_field_name=None) def plannedsession_create_view(request, @@ -516,16 +523,15 @@ def plannedsession_create_view(request, startdatestring='', enddatestring=''): - r = getrequestplanrower(request,userid=userid) + r = getrequestplanrower(request, userid=userid) - startdate,enddate = get_dates_timeperiod(request,startdatestring=startdatestring, - enddatestring=enddatestring) + startdate, enddate = get_dates_timeperiod(request, startdatestring=startdatestring, + enddatestring=enddatestring) startdate = startdate.date() enddate = enddate.date() - timeperiod = startdate.strftime('%Y-%m-%d')+'/'+enddate.strftime('%Y-%m-%d') - - + timeperiod = startdate.strftime( + '%Y-%m-%d')+'/'+enddate.strftime('%Y-%m-%d') if request.method == 'POST': sessioncreateform = PlannedSessionForm(request.POST, request.FILES) @@ -534,34 +540,30 @@ def plannedsession_create_view(request, ps.manager = request.user ps.save() - - - - add_rower_session(r,ps) + add_rower_session(r, ps) request.session['fstartdate'] = str(arrow.get(ps.startdate)) request.session['fenddate'] = str(arrow.get(ps.enddate)) request.session['fprefdate'] = str(arrow.get(ps.preferreddate)) - - url = reverse(plannedsessions_view,kwargs={ - 'userid':userid, + url = reverse(plannedsessions_view, kwargs={ + 'userid': userid, }) - - if '_continue' in request.POST: # pragma: no cover - url = reverse('plannedsession_edit_view',kwargs={ - 'id':ps.id, + if '_continue' in request.POST: # pragma: no cover + url = reverse('plannedsession_edit_view', kwargs={ + 'id': ps.id, }) - elif '_addanother' in request.POST: # pragma: no cover - url = reverse('plannedsession_create_view',kwargs={'userid':userid}) + elif '_addanother' in request.POST: # pragma: no cover + url = reverse('plannedsession_create_view', + kwargs={'userid': userid}) url = url+'?when='+timeperiod return HttpResponseRedirect(url) else: - if 'fstartdate' in request.session: # pragma: no cover + if 'fstartdate' in request.session: # pragma: no cover try: fstartdate = arrow.get(request.session['fstartdate']).date() except KeyError: @@ -585,18 +587,17 @@ def plannedsession_create_view(request, if fprefdate > enddate: fprefdate = enddate - forminitial = { - 'startdate':fstartdate, - 'enddate':fenddate, - 'preferreddate':fprefdate - } + 'startdate': fstartdate, + 'enddate': fenddate, + 'preferreddate': fprefdate + } else: try: preferreddate = startdate.date() except AttributeError: preferreddate = startdate - if preferreddate < timezone.now().date(): # pragma: no cover + if preferreddate < timezone.now().date(): # pragma: no cover preferreddate = timezone.now().date() try: @@ -604,13 +605,13 @@ def plannedsession_create_view(request, except AttributeError: pass - if preferreddate > enddate: # pragma: no cover + if preferreddate > enddate: # pragma: no cover preferreddate = enddate forminitial = { - 'startdate':startdate, - 'enddate':enddate, - 'preferreddate':preferreddate, + 'startdate': startdate, + 'enddate': enddate, + 'preferreddate': preferreddate, } sessioncreateform = PlannedSessionForm(initial=forminitial) @@ -620,16 +621,15 @@ def plannedsession_create_view(request, startdate = startdate.date() enddate = enddate.date() - - sps = get_sessions(r,startdate=startdate,enddate=enddate).exclude( - sessiontype='race') + sps = get_sessions(r, startdate=startdate, enddate=enddate).exclude( + sessiontype='race') sessiontemplates = PlannedSession.objects.filter( manager=request.user, is_template=True).order_by("name") sessiontemplates2 = PlannedSession.objects.filter( - is_template=True,is_public=True + is_template=True, is_public=True ).order_by("name") sessiontemplates = sessiontemplates | sessiontemplates2 @@ -637,7 +637,7 @@ def plannedsession_create_view(request, sessiontemplates = sessiontemplates.order_by("name") alltags = [] - for t in sessiontemplates: # pragma: no cover + for t in sessiontemplates: # pragma: no cover tags = t.tags.all() for tag in tags: alltags.append(tag) @@ -645,24 +645,23 @@ def plannedsession_create_view(request, alltags = uniqify(alltags) tag = request.GET.get('tag') - if tag: # pragma: no cover + if tag: # pragma: no cover tags = [tag] - sessiontemplates = sessiontemplates.filter(tags__name__in=tags).distinct() + sessiontemplates = sessiontemplates.filter( + tags__name__in=tags).distinct() try: trainingplan = TrainingPlan.objects.filter( - startdate__lte = startdate, - rowers = r, - enddate__gte = enddate)[0] + startdate__lte=startdate, + rowers=r, + enddate__gte=enddate)[0] except IndexError: trainingplan = None - - dateform = DateRangeForm(initial={ - 'startdate':startdate, - 'enddate':enddate, - }) + 'startdate': startdate, + 'enddate': enddate, + }) breadcrumbs = [ { @@ -679,37 +678,38 @@ def plannedsession_create_view(request, } ] - return render(request,'plannedsessioncreate.html', + return render(request, 'plannedsessioncreate.html', { - 'teams':get_my_teams(request.user), - 'plan':trainingplan, - 'dateform':dateform, - 'breadcrumbs':breadcrumbs, - 'form':sessioncreateform, - 'active':'nav-plan', - 'plannedsessions':sps, - 'sessiontemplates':sessiontemplates, - 'rower':r, - 'timeperiod':timeperiod, - 'alltags':alltags, + 'teams': get_my_teams(request.user), + 'plan': trainingplan, + 'dateform': dateform, + 'breadcrumbs': breadcrumbs, + 'form': sessioncreateform, + 'active': 'nav-plan', + 'plannedsessions': sps, + 'sessiontemplates': sessiontemplates, + 'rower': r, + 'timeperiod': timeperiod, + 'alltags': alltags, }) -@user_passes_test(can_plan,login_url="/rowers/paidplans/", + +@user_passes_test(can_plan, login_url="/rowers/paidplans/", message="This functionality requires a Coach or Self-Coach plan", redirect_field_name=None) def plannedsession_createtemplate_view(request, - userid=0, - ): + userid=0, + ): - r = getrequestplanrower(request,userid=userid) + r = getrequestplanrower(request, userid=userid) - startdate,enddate = get_dates_timeperiod(request) + startdate, enddate = get_dates_timeperiod(request) startdate = startdate.date() enddate = enddate.date() - if request.method == 'POST': - sessioncreateform = PlannedSessionTemplateForm(request.POST, request.FILES) + sessioncreateform = PlannedSessionTemplateForm( + request.POST, request.FILES) if sessioncreateform.is_valid(): ps = sessioncreateform.save(commit=False) ps.manager = request.user @@ -717,15 +717,15 @@ def plannedsession_createtemplate_view(request, ps.save() sessioncreateform.save_m2m() - add_rower_session(r,ps) + add_rower_session(r, ps) url = reverse('template_library_view') - if '_continue' in request.POST: # pragma: no cover - url = reverse('plannedsession_templateedit_view',kwargs={ - 'id':ps.id, + if '_continue' in request.POST: # pragma: no cover + url = reverse('plannedsession_templateedit_view', kwargs={ + 'id': ps.id, }) - elif '_addanother' in request.POST: # pragma: no cover + elif '_addanother' in request.POST: # pragma: no cover url = reverse('plannedsession_createtemplate_view') return HttpResponseRedirect(url) @@ -737,7 +737,7 @@ def plannedsession_createtemplate_view(request, is_template=True).order_by("name") sessiontemplates2 = PlannedSession.objects.filter( - is_template=True,is_public=True + is_template=True, is_public=True ).order_by("name") sessiontemplates = sessiontemplates | sessiontemplates2 @@ -745,7 +745,7 @@ def plannedsession_createtemplate_view(request, sessiontemplates = sessiontemplates.order_by("name") alltags = [] - for t in sessiontemplates: # pragma: no cover + for t in sessiontemplates: # pragma: no cover tags = t.tags.all() for tag in tags: alltags.append(tag) @@ -754,59 +754,58 @@ def plannedsession_createtemplate_view(request, try: trainingplan = TrainingPlan.objects.filter( - startdate__lte = startdate, - rowers = r, - enddate__gte = enddate)[0] + startdate__lte=startdate, + rowers=r, + enddate__gte=enddate)[0] except IndexError: trainingplan = None breadcrumbs = [ { - 'url': reverse(plannedsessions_view), - 'name': 'Sessions' + 'url': reverse(plannedsessions_view), + 'name': 'Sessions' }, { - 'url':reverse('template_library_view'), + 'url': reverse('template_library_view'), 'name': 'Session Library' - }, + }, { - 'url':reverse(plannedsession_createtemplate_view), - 'name': 'Create Library Session' + 'url': reverse(plannedsession_createtemplate_view), + 'name': 'Create Library Session' } - ] + ] - return render(request,'plannedsessiontemplatecreate.html', + return render(request, 'plannedsessiontemplatecreate.html', { - 'teams':get_my_teams(request.user), - 'plan':trainingplan, - 'form':sessioncreateform, - 'breadcrumbs':breadcrumbs, - 'active':'nav-plan', - 'rower':r, - 'alltags':alltags, + 'teams': get_my_teams(request.user), + 'plan': trainingplan, + 'form': sessioncreateform, + 'breadcrumbs': breadcrumbs, + 'active': 'nav-plan', + 'rower': r, + 'alltags': alltags, }) -@user_passes_test(can_plan,login_url="/rowers/paidplans/", +@user_passes_test(can_plan, login_url="/rowers/paidplans/", message="This functionality requires a Coach or Self-Coach plan", redirect_field_name=None) def plannedsession_multicreate_view(request, - teamid=0,userid=0,extrasessions=0): + teamid=0, userid=0, extrasessions=0): - extrasessions=int(extrasessions) + extrasessions = int(extrasessions) - r = getrequestplanrower(request,userid=userid) + r = getrequestplanrower(request, userid=userid) - - startdate,enddate = get_dates_timeperiod(request) + startdate, enddate = get_dates_timeperiod(request) startdate = startdate.date() enddate = enddate.date() teamid = get_team(request) try: trainingplan = TrainingPlan.objects.filter( - startdate__lte = startdate, - rowers = r, - enddate__gte = enddate)[0] + startdate__lte=startdate, + rowers=r, + enddate__gte=enddate)[0] except IndexError: trainingplan = None @@ -814,13 +813,13 @@ def plannedsession_multicreate_view(request, qset = PlannedSession.objects.filter( rower__in=[r], - manager = request.user, + manager=request.user, startdate__lte=enddate, enddate__gte=startdate, - ).order_by("startdate","preferreddate","enddate").exclude( + ).order_by("startdate", "preferreddate", "enddate").exclude( sessiontype='race') - if teamid: # pragma: no cover + if teamid: # pragma: no cover qset = qset.filter(team__in=[teamid]) try: team = Team.objects.get(id=teamid) @@ -831,17 +830,14 @@ def plannedsession_multicreate_view(request, else: team = None - initial = { - 'startdate':startdate, - 'enddate':enddate, - 'sessionvalue':60, - 'manager':request.user, + 'startdate': startdate, + 'enddate': enddate, + 'sessionvalue': 60, + 'manager': request.user, 'name': 'NEW SESSION' } - - initials = [initial for i in range(extrasessions)] PlannedSessionFormSet = modelformset_factory( @@ -851,124 +847,129 @@ def plannedsession_multicreate_view(request, extra=extrasessions, ) if request.method == "POST": - ps_formset = PlannedSessionFormSet(queryset = qset, - data = request.POST) - if ps_formset.is_valid(): # pragma: no cover + ps_formset = PlannedSessionFormSet(queryset=qset, + data=request.POST) + if ps_formset.is_valid(): # pragma: no cover instances = ps_formset.save(commit=False) for ps in instances: ps.save() - add_rower_session(r,ps) - if team: # pragma: no cover - add_team_session(team,ps) - messages.info(request,"Saved changes for Planned Session "+str(ps)) - for obj in ps_formset.deleted_objects: # pragma: no cover - messages.info(request,"Deleted Planned Session "+str(obj)) + add_rower_session(r, ps) + if team: # pragma: no cover + add_team_session(team, ps) + messages.info( + request, "Saved changes for Planned Session "+str(ps)) + for obj in ps_formset.deleted_objects: # pragma: no cover + messages.info(request, "Deleted Planned Session "+str(obj)) obj.delete() url = reverse(plannedsession_multicreate_view, - kwargs = { - 'userid':r.user.id, - } + kwargs={ + 'userid': r.user.id, + } ) - startdatestring = startdate.strftime('%Y-%m-%d') enddatestring = enddate.strftime('%Y-%m-%d') url += '?when='+startdatestring+'/'+enddatestring - if team: # pragma: no cover + if team: # pragma: no cover url += '&team={teamid}'.format(teamid=team.id) return HttpResponseRedirect(url) - ps_formset = PlannedSessionFormSet(queryset = qset, + ps_formset = PlannedSessionFormSet(queryset=qset, initial=initials) - timeperiod = startdate.strftime('%Y-%m-%d')+'/'+enddate.strftime('%Y-%m-%d') + timeperiod = startdate.strftime( + '%Y-%m-%d')+'/'+enddate.strftime('%Y-%m-%d') breadcrumbs = [ { 'url': reverse(plannedsessions_view), 'name': 'Planned Sessions' - }, + }, { 'url': reverse(plannedsession_multicreate_view), 'name': 'Plan MicroCycle' - } - ] + } + ] dateform = DateRangeForm(initial={ - 'startdate':startdate, - 'enddate':enddate - }) + 'startdate': startdate, + 'enddate': enddate + }) - if Team.objects.filter(manager=request.user).count()>=1: + if Team.objects.filter(manager=request.user).count() >= 1: teamform = RowerTeamForm(request.user) - if teamid: # pragma: no cover - teamform = RowerTeamForm(request.user,initial={'team':teamid}) + if teamid: # pragma: no cover + teamform = RowerTeamForm(request.user, initial={'team': teamid}) else: teamform = None context = { - 'ps_formset':ps_formset, - 'breadcrumbs':breadcrumbs, - 'rower':r, - 'active':'nav-plan', - 'dateform':dateform, - 'teamform':teamform, - 'plan':trainingplan, - 'timeperiod':timeperiod, - 'teams':get_my_teams(request.user), + 'ps_formset': ps_formset, + 'breadcrumbs': breadcrumbs, + 'rower': r, + 'active': 'nav-plan', + 'dateform': dateform, + 'teamform': teamform, + 'plan': trainingplan, + 'timeperiod': timeperiod, + 'teams': get_my_teams(request.user), 'extrasessions': extrasessions+1, - 'team':team, - } + 'team': team, + } - - return render(request,'plannedsession_multicreate.html',context) + return render(request, 'plannedsession_multicreate.html', context) # Manager creates sessions for entire team -@user_passes_test(can_plan,login_url="/rowers/paidplans/", + + +@user_passes_test(can_plan, login_url="/rowers/paidplans/", message="This functionality requires a Coach or Self-Coach plan", redirect_field_name=None) def plannedsession_teamcreate_view(request, - teamid=0,userid=0): + teamid=0, userid=0): - therower = getrequestplanrower(request,userid=userid) + therower = getrequestplanrower(request, userid=userid) - startdate,enddate = get_dates_timeperiod(request) + startdate, enddate = get_dates_timeperiod(request) startdate = startdate.date() enddate = enddate.date() - timeperiod = startdate.strftime('%Y-%m-%d')+'/'+enddate.strftime('%Y-%m-%d') + timeperiod = startdate.strftime( + '%Y-%m-%d')+'/'+enddate.strftime('%Y-%m-%d') teams = Team.objects.filter(manager=request.user) - if teams.count()>0: + if teams.count() > 0: teamchoices = [(team.id, team.name) for team in teams] teaminitial = [str(teams[0].id)] else: - messages.info(request,"You have no teams established yet. We are redirecting you to the Team Management page.") + messages.info( + request, "You have no teams established yet. We are redirecting you to the Team Management page.") url = reverse('rower_teams_view') return HttpResponseRedirect(url) - - trainingplan = None sps = [] for team in teams: - res = get_sessions_manager(request.user,startdate=startdate,enddate=enddate) + res = get_sessions_manager( + request.user, startdate=startdate, enddate=enddate) sps += res sps = list(set(sps)) ids = [ps.id for ps in sps] sps = PlannedSession.objects.filter(id__in=ids).order_by( - "preferreddate","startdate","enddate") + "preferreddate", "startdate", "enddate") - sessiontemplates = PlannedSession.objects.filter(manager=request.user,is_template=True) - sessiontemplates2 = PlannedSession.objects.filter(is_template=True,is_public=True) + sessiontemplates = PlannedSession.objects.filter( + manager=request.user, is_template=True) + sessiontemplates2 = PlannedSession.objects.filter( + is_template=True, is_public=True) sessiontemplates = sessiontemplates | sessiontemplates2 if request.method == 'POST': sessioncreateform = PlannedSessionForm(request.POST, request.FILES) sessionteamselectform = PlannedSessionTeamForm( - request.user,request.POST + request.user, request.POST ) if sessioncreateform.is_valid() and sessionteamselectform.is_valid(): @@ -977,32 +978,32 @@ def plannedsession_teamcreate_view(request, ps.manager = request.user ps.save() - cd = sessionteamselectform.cleaned_data teams = cd['team'] request.session['teams'] = [team.id for team in teams] for team in teams: - add_team_session(team,ps) - rs = Rower.objects.filter(team__in=[team]).exclude(rowerplan='freecoach') + add_team_session(team, ps) + rs = Rower.objects.filter( + team__in=[team]).exclude(rowerplan='freecoach') for r in rs: - add_rower_session(r,ps) + add_rower_session(r, ps) - url = reverse(plannedsessions_view,kwargs={ - 'userid':userid, + url = reverse(plannedsessions_view, kwargs={ + 'userid': userid, }) - if '_continue' in request.POST: # pragma: no cover - url = reverse('plannedsession_edit_view',kwargs={ - 'id':ps.id, + if '_continue' in request.POST: # pragma: no cover + url = reverse('plannedsession_edit_view', kwargs={ + 'id': ps.id, }) - elif '_addanother' in request.POST: # pragma: no cover - url = reverse('plannedsession_teamcreate_view',kwargs={'userid':userid}) + elif '_addanother' in request.POST: # pragma: no cover + url = reverse('plannedsession_teamcreate_view', + kwargs={'userid': userid}) url = url+'?when='+timeperiod return HttpResponseRedirect(url) - #url = reverse(plannedsession_teamcreate_view) #startdatestring = startdate.strftime('%Y-%m-%d') #enddatestring = enddate.strftime('%Y-%m-%d') @@ -1010,17 +1011,18 @@ def plannedsession_teamcreate_view(request, #next = request.GET.get('next', url) - #return HttpResponseRedirect(next) - else: # pragma: no cover - timeperiod = startdate.strftime('%Y-%m-%d')+'/'+enddate.strftime('%Y-%m-%d') + # return HttpResponseRedirect(next) + else: # pragma: no cover + timeperiod = startdate.strftime( + '%Y-%m-%d')+'/'+enddate.strftime('%Y-%m-%d') breadcrumbs = [ { 'url': reverse(plannedsessions_view), 'name': 'Planned Sessions' }, { - 'url': reverse(plannedsessions_view)+'?when='+timeperiod, - 'name': timeperiod, + 'url': reverse(plannedsessions_view)+'?when='+timeperiod, + 'name': timeperiod, }, { 'url': reverse(plannedsession_teamcreate_view), @@ -1028,152 +1030,156 @@ def plannedsession_teamcreate_view(request, } ] - timeperiod = startdate.strftime('%Y-%m-%d')+'/'+enddate.strftime('%Y-%m-%d') + timeperiod = startdate.strftime( + '%Y-%m-%d')+'/'+enddate.strftime('%Y-%m-%d') - return render(request,'plannedsessionteamcreate.html', + return render(request, 'plannedsessionteamcreate.html', { - 'teams':get_my_teams(request.user), - 'plan':trainingplan, - 'breadcrumbs':breadcrumbs, - 'form':sessioncreateform, - 'sessiontemplates':sessiontemplates, - 'teamform':sessionteamselectform, - 'timeperiod':timeperiod, - 'plannedsessions':sps, - 'rower':therower, - 'active':'nav-plan' + 'teams': get_my_teams(request.user), + 'plan': trainingplan, + 'breadcrumbs': breadcrumbs, + 'form': sessioncreateform, + 'sessiontemplates': sessiontemplates, + 'teamform': sessionteamselectform, + 'timeperiod': timeperiod, + 'plannedsessions': sps, + 'rower': therower, + 'active': 'nav-plan' }) else: initial = { - 'startdate':startdate, - 'enddate':enddate, - 'preferreddate':startdate, - } + 'startdate': startdate, + 'enddate': enddate, + 'preferreddate': startdate, + } - if 'teams' in request.session: # pragma: no cover + if 'teams' in request.session: # pragma: no cover teams = request.session['teams'] theteams = Team.objects.filter(id__in=teams) initialteam = { - 'team':theteams - } + 'team': theteams + } else: initialteam = {} sessioncreateform = PlannedSessionForm(initial=initial) sessionteamselectform = PlannedSessionTeamForm( - request.user,initial=initialteam + request.user, initial=initialteam ) - timeperiod = startdate.strftime('%Y-%m-%d')+'/'+enddate.strftime('%Y-%m-%d') + timeperiod = startdate.strftime( + '%Y-%m-%d')+'/'+enddate.strftime('%Y-%m-%d') breadcrumbs = [ - { - 'url': reverse(plannedsessions_view), - 'name': 'Planned Sessions' + { + 'url': reverse(plannedsessions_view), + 'name': 'Planned Sessions' }, - { - 'url': reverse(plannedsessions_view)+'?when='+timeperiod, - 'name': timeperiod, - }, - { - 'url': reverse(plannedsession_teamcreate_view), - 'name': 'Add Team Session' + { + 'url': reverse(plannedsessions_view)+'?when='+timeperiod, + 'name': timeperiod, + }, + { + 'url': reverse(plannedsession_teamcreate_view), + 'name': 'Add Team Session' } ] dateform = DateRangeForm(initial={ - 'startdate':startdate, - 'enddate':enddate, - }) + 'startdate': startdate, + 'enddate': enddate, + }) - return render(request,'plannedsessionteamcreate.html', - { - 'teams':get_my_teams(request.user), - 'plan':trainingplan, - 'dateform':dateform, - 'breadcrumbs':breadcrumbs, - 'form':sessioncreateform, - 'sessiontemplates':sessiontemplates, - 'teamform':sessionteamselectform, - 'timeperiod':timeperiod, - 'plannedsessions':sps, - 'rower':therower, - 'active':'nav-plan' - }) + return render(request, 'plannedsessionteamcreate.html', + { + 'teams': get_my_teams(request.user), + 'plan': trainingplan, + 'dateform': dateform, + 'breadcrumbs': breadcrumbs, + 'form': sessioncreateform, + 'sessiontemplates': sessiontemplates, + 'teamform': sessionteamselectform, + 'timeperiod': timeperiod, + 'plannedsessions': sps, + 'rower': therower, + 'active': 'nav-plan' + }) # Manager edits sessions for entire team -@user_passes_test(can_plan,login_url="/rowers/paidplans/", + + +@user_passes_test(can_plan, login_url="/rowers/paidplans/", message="This functionality requires a Coach or Self-Coach plan", redirect_field_name=None) -@permission_required('plannedsession.change_session',fn=get_session_by_pk,raise_exception=True) +@permission_required('plannedsession.change_session', fn=get_session_by_pk, raise_exception=True) def plannedsession_teamedit_view(request, - id=0,userid=0): + id=0, userid=0): - r = getrequestplanrower(request,userid=userid) + r = getrequestplanrower(request, userid=userid) - ps = get_object_or_404(PlannedSession,pk=id) + ps = get_object_or_404(PlannedSession, pk=id) - startdate,enddate = get_dates_timeperiod(request) + startdate, enddate = get_dates_timeperiod(request) startdate = startdate.date() enddate = enddate.date() - timeperiod = startdate.strftime('%Y-%m-%d')+'/'+enddate.strftime('%Y-%m-%d') + timeperiod = startdate.strftime( + '%Y-%m-%d')+'/'+enddate.strftime('%Y-%m-%d') teams = Team.objects.filter(manager=request.user) teamchoices = [(team.id, team.name) for team in teams] teaminitial = ps.team.all() - startdate,enddate = get_dates_timeperiod(request) + startdate, enddate = get_dates_timeperiod(request) startdate = startdate.date() enddate = enddate.date() try: trainingplan = TrainingPlan.objects.filter( - startdate__lte = startdate, - rowers = r, - enddate__gte = enddate)[0] - except IndexError: # pragma: no cover + startdate__lte=startdate, + rowers=r, + enddate__gte=enddate)[0] + except IndexError: # pragma: no cover trainingplan = None sps = [] rowers = [] for team in teams: - res = get_sessions_manager(request.user,startdate=startdate,enddate=enddate) + res = get_sessions_manager( + request.user, startdate=startdate, enddate=enddate) sps += res rowers += Rower.objects.filter(team__in=[team]) rowers = list(set(rowers)) - sps = list(set(sps)) ids = [pps.id for pps in sps] sps = PlannedSession.objects.filter(id__in=ids).order_by( - "preferreddate","startdate","enddate") + "preferreddate", "startdate", "enddate") if request.method == 'POST': - sessioncreateform = PlannedSessionForm(request.POST,request.FILES,instance=ps) + sessioncreateform = PlannedSessionForm( + request.POST, request.FILES, instance=ps) sessionteamselectform = PlannedSessionTeamForm( - request.user,request.POST + request.user, request.POST ) - sessionrowerform = PlannedSessionTeamMemberForm(ps,request.POST) + sessionrowerform = PlannedSessionTeamMemberForm(ps, request.POST) if sessioncreateform.is_valid(): cd = sessioncreateform.cleaned_data - if cd['sessionunit'] == 'min': # pragma: no cover + if cd['sessionunit'] == 'min': # pragma: no cover cd['sessionmode'] = 'time' - elif cd['sessionunit'] in ['km','m']: + elif cd['sessionunit'] in ['km', 'm']: cd['sessionmode'] = 'distance' - - res,message = update_plannedsession(ps,cd) + res, message = update_plannedsession(ps, cd) if res: - messages.info(request,message) - else: # pragma: no cover - messages.error(request,message) - + messages.info(request, message) + else: # pragma: no cover + messages.error(request, message) # some logic when to add all selected rowers if sessionteamselectform.is_valid(): @@ -1181,43 +1187,37 @@ def plannedsession_teamedit_view(request, selectedteams = cd['team'] for team in teams: if team in selectedteams: - add_team_session(team,ps) + add_team_session(team, ps) rs = Rower.objects.filter(team__in=[team]) for r in rs: - add_rower_session(r,ps) - else: # pragma: no cover - remove_team_session(team,ps) - else: # pragma: no cover + add_rower_session(r, ps) + else: # pragma: no cover + remove_team_session(team, ps) + else: # pragma: no cover selectedteams = [] for team in teams: - remove_team_session(team,ps) - - + remove_team_session(team, ps) if sessionrowerform.is_valid(): cd = sessionrowerform.cleaned_data selectedrowers = cd['members'] for r in rowers: if r in selectedrowers: - add_rower_session(r,ps) + add_rower_session(r, ps) else: - remove_rower_session(r,ps) + remove_rower_session(r, ps) for t in selectedteams: if t in r.team.all(): - add_rower_session(r,ps) - + add_rower_session(r, ps) url = reverse('plannedsessions_view') - - if "_continue" in request.POST: # pragma: no cover + if "_continue" in request.POST: # pragma: no cover url = reverse(plannedsession_edit_view, - kwargs={ - 'id':int(ps.id), - 'userid':r.user.id, - }) - - + kwargs={ + 'id': int(ps.id), + 'userid': r.user.id, + }) url = url+'?when='+timeperiod return HttpResponseRedirect(url) @@ -1229,12 +1229,12 @@ def plannedsession_teamedit_view(request, sessionteamselectform.fields['team'].initial = teaminitial sessionrowerform = PlannedSessionTeamMemberForm( ps - ) - + ) sessionrowerform.fields['members'].initial = ps.rower.all() - timeperiod = startdate.strftime('%Y-%m-%d')+'/'+enddate.strftime('%Y-%m-%d') + timeperiod = startdate.strftime( + '%Y-%m-%d')+'/'+enddate.strftime('%Y-%m-%d') breadcrumbs = [ { 'url': reverse(plannedsessions_view), @@ -1245,62 +1245,62 @@ def plannedsession_teamedit_view(request, 'name': timeperiod, }, { - 'url':reverse(plannedsession_view, - kwargs={ - 'userid':userid, - 'id':id, - } - ), + 'url': reverse(plannedsession_view, + kwargs={ + 'userid': userid, + 'id': id, + } + ), 'name': ps.id - }, + }, { 'url': reverse(plannedsession_teamedit_view, kwargs={ - 'userid':userid, - 'id':id, + 'userid': userid, + 'id': id, }), 'name': 'Edit' } ] dateform = DateRangeForm(initial={ - 'startdate':startdate, - 'enddate':enddate, - }) + 'startdate': startdate, + 'enddate': enddate, + }) - return render(request,'plannedsessionteamedit.html', + return render(request, 'plannedsessionteamedit.html', { - 'plannedsession':ps, - 'plan':trainingplan, - 'dateform':dateform, - 'breadcrumbs':breadcrumbs, - 'rower':r, - 'active':'nav-plan', - 'teams':get_my_teams(request.user), - 'form':sessioncreateform, - 'teamform':sessionteamselectform, - 'rowersform':sessionrowerform, - 'timeperiod':timeperiod, - 'plannedsessions':sps, + 'plannedsession': ps, + 'plan': trainingplan, + 'dateform': dateform, + 'breadcrumbs': breadcrumbs, + 'rower': r, + 'active': 'nav-plan', + 'teams': get_my_teams(request.user), + 'form': sessioncreateform, + 'teamform': sessionteamselectform, + 'rowersform': sessionrowerform, + 'timeperiod': timeperiod, + 'plannedsessions': sps, }) -#@user_passes_test(iscoachmember,login_url="/rowers/paidplans/", +# @user_passes_test(iscoachmember,login_url="/rowers/paidplans/", # redirect_field_name=None) + + @login_required() def plannedsessions_coach_view(request, - teamid=0,userid=0): + teamid=0, userid=0): + therower = getrequestplanrower(request, userid=userid) - therower = getrequestplanrower(request,userid=userid) - - - startdate,enddate = get_dates_timeperiod(request) + startdate, enddate = get_dates_timeperiod(request) startdate = startdate.date() enddate = enddate.date() trainingplan = None - if teamid != 0:# pragma: no cover + if teamid != 0: # pragma: no cover try: theteam = Team.objects.get(id=teamid) except Team.DoesNotExist: @@ -1309,23 +1309,24 @@ def plannedsessions_coach_view(request, theteam = False if 'coach' in request.user.rower.rowerplan: - sps = get_sessions_manager(request.user,teamid=teamid, + sps = get_sessions_manager(request.user, teamid=teamid, enddate=enddate, startdate=startdate) else: rteams = therower.team.filter(viewing='allmembers') - sps = get_sessions(therower,startdate=startdate,enddate=enddate) + sps = get_sessions(therower, startdate=startdate, enddate=enddate) if therower.rowerplan != 'freecoach': rowers = [therower] - else:# pragma: no cover + else: # pragma: no cover rowers = [] for ps in sps: if 'coach' in request.user.rower.rowerplan: rowers += ps.rower.all().exclude(rowerplan='freecoach') - else: # pragma: no cover - rowers += ps.rower.filter(team__in=rteams).exclude(rowerplan='freecoach') + else: # pragma: no cover + rowers += ps.rower.filter( + team__in=rteams).exclude(rowerplan='freecoach') rowers = list(set(rowers)) @@ -1335,97 +1336,92 @@ def plannedsessions_coach_view(request, rowerstatus = {} rowercolor = {} for r in rowers: - ratio, status,completiondate = is_session_complete(r,ps) + ratio, status, completiondate = is_session_complete(r, ps) rowerstatus[r.id] = status rowercolor[r.id] = cratiocolors[status] sessiondict = { 'id': ps.id, - 'results':rowerstatus, + 'results': rowerstatus, 'name': ps.name, 'startdate': ps.startdate, 'color': rowercolor, 'preferreddate': ps.preferreddate, 'enddate': ps.enddate, - } + } statusdict.append(sessiondict) unmatchedworkouts = [] for r in rowers: unmatchedworkouts += Workout.objects.filter( - user=r, - plannedsession=None, - date__gte=startdate,date__lte=enddate) - + user=r, + plannedsession=None, + date__gte=startdate, date__lte=enddate) myteams = Team.objects.filter(manager=request.user) - timeperiod = startdate.strftime('%Y-%m-%d')+'/'+enddate.strftime('%Y-%m-%d') + timeperiod = startdate.strftime( + '%Y-%m-%d')+'/'+enddate.strftime('%Y-%m-%d') breadcrumbs = [ { 'url': reverse(plannedsessions_view), 'name': 'Planned Sessions' - }, + }, { 'url': reverse(plannedsessions_coach_view), 'name': 'Coach View' - } - ] + } + ] dateform = DateRangeForm(initial={ - 'startdate':startdate, - 'enddate':enddate, - }) + 'startdate': startdate, + 'enddate': enddate, + }) ttemplate = 'plannedsessionscoach.html' - if len(rowers) > 5 and len(rowers) > len(sps): # pragma: no cover + if len(rowers) > 5 and len(rowers) > len(sps): # pragma: no cover ttemplate = 'plannedsessionscoach2.html' - - return render(request,ttemplate, + return render(request, ttemplate, { - 'myteams':myteams, - 'plannedsessions':sps, - 'breadcrumbs':breadcrumbs, - 'plan':trainingplan, - 'statusdict':statusdict, - 'dateform':dateform, - 'timeperiod':timeperiod, - 'rowers':rowers, - 'rower':therower, - 'active':'nav-plan', - 'theteam':theteam, - 'unmatchedworkouts':unmatchedworkouts, - 'rower':getrower(request.user) - } + 'myteams': myteams, + 'plannedsessions': sps, + 'breadcrumbs': breadcrumbs, + 'plan': trainingplan, + 'statusdict': statusdict, + 'dateform': dateform, + 'timeperiod': timeperiod, + 'rowers': rowers, + 'rower': therower, + 'active': 'nav-plan', + 'theteam': theteam, + 'unmatchedworkouts': unmatchedworkouts, + 'rower': getrower(request.user) + } ) -from rowers.plannedsessions import cratiocolors,checkscores @login_required() def plannedsessions_view(request, - userid=0,startdatestring='',enddatestring=''): - + userid=0, startdatestring='', enddatestring=''): try: - r = getrequestplanrower(request,userid=userid) - except PermissionDenied: # pragma: no cover + r = getrequestplanrower(request, userid=userid) + except PermissionDenied: # pragma: no cover r = request.user.rower - if startdatestring: # pragma: no cover + if startdatestring: # pragma: no cover try: startdate = iso8601.parse_date(startdatestring) except ParseError: pass - if enddatestring: # pragma: no cover + if enddatestring: # pragma: no cover try: enddate = iso8601.parse_date(enddatestring) except ParseError: pass - - - startdate,enddate = get_dates_timeperiod( + startdate, enddate = get_dates_timeperiod( request, startdatestring=startdatestring, enddatestring=enddatestring, @@ -1435,14 +1431,14 @@ def plannedsessions_view(request, try: trainingplan = TrainingPlan.objects.filter( - startdate__lte = startdate, - rowers = r, - enddate__gte = enddate - )[0] + startdate__lte=startdate, + rowers=r, + enddate__gte=enddate + )[0] except IndexError: trainingplan = None - sps = get_sessions(r,startdate=startdate,enddate=enddate) + sps = get_sessions(r, startdate=startdate, enddate=enddate) completeness = {} actualvalue = {} @@ -1450,28 +1446,28 @@ def plannedsessions_view(request, sessioncolor = {} totals = { - 'trimp':0, - 'rscore':0, - 'distance':0, - 'time':0, - 'plannedtime':0, - 'planneddistance':0, - 'plannedtrimp':0, - 'plannedrscore':0, - 'actualtime':0, - 'actualdistance':0, - 'actualtrimp':0, - 'actualrscore':0, - } + 'trimp': 0, + 'rscore': 0, + 'distance': 0, + 'time': 0, + 'plannedtime': 0, + 'planneddistance': 0, + 'plannedtrimp': 0, + 'plannedrscore': 0, + 'actualtime': 0, + 'actualdistance': 0, + 'actualtrimp': 0, + 'actualrscore': 0, + } ws = Workout.objects.filter( user=r, date__gte=startdate, date__lte=enddate, - ) + ) for w in ws: - thetrimp,hrtss = dataprep.workout_trimp(w) + thetrimp, hrtss = dataprep.workout_trimp(w) totals['trimp'] += thetrimp tss = dataprep.workout_rscore(w)[0] if not np.isnan(tss) and tss != 0: @@ -1481,29 +1477,31 @@ def plannedsessions_view(request, tss = hrtss totals['distance'] += w.distance totals['time'] += timefield_to_seconds_duration(w.duration) - if w.plannedsession: # pragma: no cover + if w.plannedsession: # pragma: no cover if w.plannedsession.sessionmode == 'distance': totals['actualdistance'] += w.distance elif w.plannedsession.sessionmode == 'time': - totals['actualtime'] += timefield_to_seconds_duration(w.duration) + totals['actualtime'] += timefield_to_seconds_duration( + w.duration) elif w.plannedsession.sessionmode == 'rScore': totals['actualrscore'] += tss elif w.plannedsession.sessionmode == 'TRIMP': totals['actualtrimp'] += thetrimp - if not sps and request.user.rower.rowerplan == 'basic': # pragma: no cover + if not sps and request.user.rower.rowerplan == 'basic': # pragma: no cover messages.error(request, "You must purchase Coach or Self-coach plans or be part of a team to get planned sessions") for ps in sps: - ratio,status,cdate = is_session_complete(r,ps) + ratio, status, cdate = is_session_complete(r, ps) actualvalue[ps.id] = int(ps.sessionvalue*ratio) completeness[ps.id] = status sessioncolor[ps.id] = cratiocolors[status] - ws = Workout.objects.filter(user=r,plannedsession=ps) + ws = Workout.objects.filter(user=r, plannedsession=ps) completiondate[ps.id] = cdate if ps.steps: - sdict, totalmeters, totalseconds, totalrscore = ps_dict_order(ps.steps,rower=r) + sdict, totalmeters, totalseconds, totalrscore = ps_dict_order( + ps.steps, rower=r) totals['planneddistance'] += int(totalmeters) totals['plannedtime'] += int(totalseconds)/60. totals['plannedrscore'] += int(totalrscore) @@ -1517,11 +1515,11 @@ def plannedsessions_view(request, totals['plannedtime'] += ps.sessionvalue else: totals['plannedtime'] += ps.approximate_duration - if ps.sessionmode == 'rScore': # pragma: no cover + if ps.sessionmode == 'rScore': # pragma: no cover totals['plannedrscore'] += ps.sessionvalue else: totals['plannedrscore'] += ps.approximate_rscore - if ps.sessionmode == 'TRIMP': # pragma: no cover + if ps.sessionmode == 'TRIMP': # pragma: no cover totals['plannedtrimp'] += ps.sessionvalue else: totals['plannedtrimp'] += ps.approximate_rscore*2 @@ -1535,110 +1533,110 @@ def plannedsessions_view(request, plannedsession=None, date__gte=startdate, date__lte=enddate - ) + ) - timeperiod = startdate.strftime('%Y-%m-%d')+'/'+enddate.strftime('%Y-%m-%d') + timeperiod = startdate.strftime( + '%Y-%m-%d')+'/'+enddate.strftime('%Y-%m-%d') breadcrumbs = [ { 'url': reverse(plannedsessions_view), 'name': 'Planned Sessions' - }, - ] + }, + ] initial = { - 'startdate':startdate, - 'enddate':enddate, - } + 'startdate': startdate, + 'enddate': enddate, + } dateform = DateRangeForm(initial=initial) - - return render(request,'plannedsessions.html', + return render(request, 'plannedsessions.html', { - 'teams':get_my_teams(request.user), - 'breadcrumbs':breadcrumbs, - 'plannedsessions':sps, - 'plan':trainingplan, + 'teams': get_my_teams(request.user), + 'breadcrumbs': breadcrumbs, + 'plannedsessions': sps, + 'plan': trainingplan, 'active': 'nav-plan', - 'dateform':dateform, - 'totals':totals, - 'rower':r, - 'timeperiod':timeperiod, - 'completeness':completeness, - 'sessioncolor':sessioncolor, - 'actualvalue':actualvalue, - 'completiondate':completiondate, - 'unmatchedworkouts':unmatchedworkouts, + 'dateform': dateform, + 'totals': totals, + 'rower': r, + 'timeperiod': timeperiod, + 'completeness': completeness, + 'sessioncolor': sessioncolor, + 'actualvalue': actualvalue, + 'completiondate': completiondate, + 'unmatchedworkouts': unmatchedworkouts, }) + @allow_shares -#@login_required() -def plannedsessions_print_view(request,userid=0,startdatestring='',enddatestring=''): +# @login_required() +def plannedsessions_print_view(request, userid=0, startdatestring='', enddatestring=''): - r = getrequestplanrower(request,userid=userid) + r = getrequestplanrower(request, userid=userid) - - - startdate,enddate = get_dates_timeperiod(request,startdatestring=startdatestring, - enddatestring=enddatestring) + startdate, enddate = get_dates_timeperiod(request, startdatestring=startdatestring, + enddatestring=enddatestring) startdate = startdate.date() enddate = enddate.date() try: trainingplan = TrainingPlan.objects.filter( - startdate__lte = startdate, - rowers = r, - enddate__gte = enddate)[0] + startdate__lte=startdate, + rowers=r, + enddate__gte=enddate)[0] except IndexError: trainingplan = None - sps = get_sessions(r,startdate=startdate,enddate=enddate) + sps = get_sessions(r, startdate=startdate, enddate=enddate) completeness = {} actualvalue = {} completiondate = {} - timeperiod = startdate.strftime('%Y-%m-%d')+'/'+enddate.strftime('%Y-%m-%d') - return render(request,'plannedsessions_print.html', + timeperiod = startdate.strftime( + '%Y-%m-%d')+'/'+enddate.strftime('%Y-%m-%d') + return render(request, 'plannedsessions_print.html', { - 'teams':get_my_teams(request.user), - 'plan':trainingplan, - 'plannedsessions':sps, - 'rower':r, - 'active':'nav-plan', - 'startdate':startdate, - 'enddate':enddate, - 'timeperiod':timeperiod, + 'teams': get_my_teams(request.user), + 'plan': trainingplan, + 'plannedsessions': sps, + 'rower': r, + 'active': 'nav-plan', + 'startdate': startdate, + 'enddate': enddate, + 'timeperiod': timeperiod, }) @login_required() -@permission_required('rower.is_coach',fn=get_user_by_userid,raise_exception=True) -def plannedsessions_manage_view(request,userid=0, +@permission_required('rower.is_coach', fn=get_user_by_userid, raise_exception=True) +def plannedsessions_manage_view(request, userid=0, initialsession=0): is_ajax = request.META.get('HTTP_X_REQUESTED_WITH') == 'XMLHttpRequest' - r = getrequestrower(request,userid=userid) + r = getrequestrower(request, userid=userid) - startdate,enddate = get_dates_timeperiod(request) + startdate, enddate = get_dates_timeperiod(request) startdate = startdate.date() enddate = enddate.date() try: trainingplan = TrainingPlan.objects.filter( - startdate__lte = startdate, - rowers = r, - enddate__gte = enddate)[0] + startdate__lte=startdate, + rowers=r, + enddate__gte=enddate)[0] except IndexError: trainingplan = None - sps = get_sessions(r,startdate=startdate,enddate=enddate) - if initialsession==0: + sps = get_sessions(r, startdate=startdate, enddate=enddate) + if initialsession == 0: try: - initialsession=sps[0].id + initialsession = sps[0].id except IndexError: - initialsession=0 + initialsession = 0 if initialsession: try: @@ -1649,24 +1647,24 @@ def plannedsessions_manage_view(request,userid=0, ps0 = None ws = Workout.objects.filter( - user=r,date__gte=startdate, + user=r, date__gte=startdate, date__lte=enddate ).order_by( - "-date","-startdatetime","-id" - ) + "-date", "-startdatetime", "-id" + ) - - initialworkouts = [w.id for w in Workout.objects.filter(user=r,plannedsession=ps0)] + initialworkouts = [w.id for w in Workout.objects.filter( + user=r, plannedsession=ps0)] linkedworkouts = [] for w in ws: - if w.plannedsession is not None: # pragma: no cover + if w.plannedsession is not None: # pragma: no cover linkedworkouts.append(w.id) plannedsessionstuple = [] for ps in sps: - sessiontpl = (ps.id,ps.__str__()) + sessiontpl = (ps.id, ps.__str__()) plannedsessionstuple.append(sessiontpl) plannedsessionstuple = tuple(plannedsessionstuple) @@ -1685,119 +1683,117 @@ def plannedsessions_manage_view(request,userid=0, workoutdata['choices'] = tuple(choices) if request.method == 'POST': - ps_form = PlannedSessionSelectForm(plannedsessionstuple,request.POST) - w_form = WorkoutSessionSelectForm(workoutdata,request.POST) + ps_form = PlannedSessionSelectForm(plannedsessionstuple, request.POST) + w_form = WorkoutSessionSelectForm(workoutdata, request.POST) if ps_form.is_valid(): - ps = PlannedSession.objects.get(id=ps_form.cleaned_data['plannedsession']) + ps = PlannedSession.objects.get( + id=ps_form.cleaned_data['plannedsession']) if w_form.is_valid(): selectedworkouts = w_form.cleaned_data['workouts'] - else: # pragma: no cover + else: # pragma: no cover selectedworkouts = [] - if len(selectedworkouts)==0: # pragma: no cover + if len(selectedworkouts) == 0: # pragma: no cover for w in ws: - remove_workout_plannedsession(w,ps) + remove_workout_plannedsession(w, ps) if selectedworkouts: - workouts = Workout.objects.filter(user=r,id__in=selectedworkouts) + workouts = Workout.objects.filter( + user=r, id__in=selectedworkouts) for w in ws: if w.id not in selectedworkouts: - remove_workout_plannedsession(w,ps) + remove_workout_plannedsession(w, ps) - result,comments,errors = add_workouts_plannedsession(workouts,ps,r) + result, comments, errors = add_workouts_plannedsession( + workouts, ps, r) for c in comments: - messages.info(request,c) - for er in errors: # pragma: no cover - messages.error(request,er) - - + messages.info(request, c) + for er in errors: # pragma: no cover + messages.error(request, er) ps_form = PlannedSessionSelectForm(plannedsessionstuple, initialsession=initialsession) w_form = WorkoutSessionSelectForm(workoutdata=workoutdata) - - if is_ajax: # pragma: no cover + if is_ajax: # pragma: no cover ajax_workouts = [] - for id,name in workoutdata['choices']: + for id, name in workoutdata['choices']: ininitial = id in initialworkouts inlinked = id in linkedworkouts - ajax_workouts.append((id,name,ininitial,inlinked)) + ajax_workouts.append((id, name, ininitial, inlinked)) ajax_response = { - 'workouts':ajax_workouts, - 'plannedsessionstuple':plannedsessionstuple, + 'workouts': ajax_workouts, + 'plannedsessionstuple': plannedsessionstuple, } - return JSONResponse(ajax_response) - breadcrumbs = [ { - 'url':reverse(plannedsessions_view, - kwargs={'userid':userid}), + 'url': reverse(plannedsessions_view, + kwargs={'userid': userid}), 'name': 'Plan' }, { - 'url':reverse(plannedsessions_manage_view, - kwargs={ - 'userid':userid, - 'initialsession':initialsession, - } - ), + 'url': reverse(plannedsessions_manage_view, + kwargs={ + 'userid': userid, + 'initialsession': initialsession, + } + ), 'name': 'Link Sessions to Workouts' - }, + }, ] - timeperiod = startdate.strftime('%Y-%m-%d')+'/'+enddate.strftime('%Y-%m-%d') + timeperiod = startdate.strftime( + '%Y-%m-%d')+'/'+enddate.strftime('%Y-%m-%d') dateform = DateRangeForm(initial={ - 'startdate':startdate, - 'enddate':enddate, - }) + 'startdate': startdate, + 'enddate': enddate, + }) - return render(request,'plannedsessionsmanage.html', + return render(request, 'plannedsessionsmanage.html', { - 'teams':get_my_teams(request.user), - 'plan':trainingplan, - 'dateform':dateform, - 'plannedsessions':sps, - 'workouts':ws, - 'active':'nav-plan', - 'breadcrumbs':breadcrumbs, - 'timeperiod':timeperiod, - 'rower':r, - 'ps_form':ps_form, - 'w_form':w_form, + 'teams': get_my_teams(request.user), + 'plan': trainingplan, + 'dateform': dateform, + 'plannedsessions': sps, + 'workouts': ws, + 'active': 'nav-plan', + 'breadcrumbs': breadcrumbs, + 'timeperiod': timeperiod, + 'rower': r, + 'ps_form': ps_form, + 'w_form': w_form, }) # Clone an existing planned session # need clarity on cloning behavior time shift -@permission_required('plannedsession.change_session',fn=get_session_by_pk,raise_exception=True) -@user_passes_test(can_plan,login_url="/rowers/paidplans/", +@permission_required('plannedsession.change_session', fn=get_session_by_pk, raise_exception=True) +@user_passes_test(can_plan, login_url="/rowers/paidplans/", message="This functionality requires a Coach or Self-Coach plan", redirect_field_name=None) -def plannedsession_clone_view(request,id=0,userid=0): +def plannedsession_clone_view(request, id=0, userid=0): - r = getrequestplanrower(request,userid=userid) + r = getrequestplanrower(request, userid=userid) - - startdate,enddate = get_dates_timeperiod(request) + startdate, enddate = get_dates_timeperiod(request) startdate = startdate.date() enddate = enddate.date() try: trainingplan = TrainingPlan.objects.filter( - startdate__lte = startdate, - rowers = r, - enddate__gte = enddate)[0] - except IndexError: # pragma: no cover + startdate__lte=startdate, + rowers=r, + enddate__gte=enddate)[0] + except IndexError: # pragma: no cover trainingplan = None - ps = get_object_or_404(PlannedSession,pk=id) + ps = get_object_or_404(PlannedSession, pk=id) rowers = ps.rower.all() teams = ps.team.all() @@ -1815,23 +1811,22 @@ def plannedsession_clone_view(request,id=0,userid=0): ps.preferreddate = ps.startdate+deltadays ps.fitfile = None - ps.save() if rowers: for rower in rowers: - add_rower_session(rower,ps) - else: # pragma: no cover - add_rower_session(r,ps) + add_rower_session(rower, ps) + else: # pragma: no cover + add_rower_session(r, ps) for team in teams: - add_team_session(team,ps) + add_team_session(team, ps) url = reverse(plannedsession_edit_view, - kwargs = { - 'id':ps.id, - 'userid':r.user.id, - } - ) + kwargs={ + 'id': ps.id, + 'userid': r.user.id, + } + ) startdatestring = startdate.strftime('%Y-%m-%d') enddatestring = enddate.strftime('%Y-%m-%d') @@ -1842,38 +1837,39 @@ def plannedsession_clone_view(request,id=0,userid=0): # Clone an existing planned session # need clarity on cloning behavior time shift -@permission_required('plannedsession.change_session',fn=get_session_by_pk,raise_exception=True) -@user_passes_test(can_plan,login_url="/rowers/paidplans/", + + +@permission_required('plannedsession.change_session', fn=get_session_by_pk, raise_exception=True) +@user_passes_test(can_plan, login_url="/rowers/paidplans/", message="This functionality requires a Coach or Self-Coach plan", redirect_field_name=None) -def plannedsession_teamclone_view(request,id=0): +def plannedsession_teamclone_view(request, id=0): r = getrequestplanrower(request) teams = Team.objects.filter(manager=request.user) - if teams.count()>0: # pragma: no cover + if teams.count() > 0: # pragma: no cover teamchoices = [(team.id, team.name) for team in teams] teaminitial = [str(teams[0].id)] - else: # pragma: no cover - messages.info(request,"You have no teams established yet. We are redirecting you to the Team Management page.") + else: # pragma: no cover + messages.info( + request, "You have no teams established yet. We are redirecting you to the Team Management page.") url = reverse('rower_teams_view') return HttpResponseRedirect(url) - - startdate,enddate = get_dates_timeperiod(request) + startdate, enddate = get_dates_timeperiod(request) startdate = startdate.date() enddate = enddate.date() try: trainingplan = TrainingPlan.objects.filter( - startdate__lte = startdate, - rowers = r, - enddate__gte = enddate)[0] - except IndexError: # pragma: no cover + startdate__lte=startdate, + rowers=r, + enddate__gte=enddate)[0] + except IndexError: # pragma: no cover trainingplan = None - ps = get_object_or_404(PlannedSession,pk=id) - + ps = get_object_or_404(PlannedSession, pk=id) ps.pk = None ps.id = None @@ -1891,11 +1887,11 @@ def plannedsession_teamclone_view(request,id=0): ps.save() url = reverse(plannedsession_teamedit_view, - kwargs = { - 'id':ps.id, - 'userid':r.user.id, - } - ) + kwargs={ + 'id': ps.id, + 'userid': r.user.id, + } + ) startdatestring = startdate.strftime('%Y-%m-%d') enddatestring = enddate.strftime('%Y-%m-%d') @@ -1904,40 +1900,43 @@ def plannedsession_teamclone_view(request,id=0): next = request.GET.get('next', url) return HttpResponseRedirect(next) -@permission_required('plannedsession.change_session',fn=get_session_by_pk,raise_exception=True) + +@permission_required('plannedsession.change_session', fn=get_session_by_pk, raise_exception=True) @user_passes_test(can_plan, login_url="/rowers/paidplans/", -message="This functionality requires a Coach or Self-Coach plan", -redirect_field_name=None) -def plannedsession_templateedit_view(request,id=0): + message="This functionality requires a Coach or Self-Coach plan", + redirect_field_name=None) +def plannedsession_templateedit_view(request, id=0): r = getrequestrower(request) - startdate,enddate = get_dates_timeperiod(request) + startdate, enddate = get_dates_timeperiod(request) startdate = startdate.date() enddate = enddate.date() try: trainingplan = TrainingPlan.objects.filter( - startdate__lte = startdate, - rowers = r, - enddate__gte = enddate)[0] + startdate__lte=startdate, + rowers=r, + enddate__gte=enddate)[0] except IndexError: trainingplan = None try: ps = PlannedSession.objects.get(id=id) - except PlannedSession.DoesNotExist: # pragma: no cover + except PlannedSession.DoesNotExist: # pragma: no cover raise Http404("Planned Session does not exist") - if ps.manager != request.user: # pragma: no cover - raise PermissionDenied("You are not allowed to edit this planned session") + if ps.manager != request.user: # pragma: no cover + raise PermissionDenied( + "You are not allowed to edit this planned session") - if ps.sessiontype in ['race','indoorrace']: # pragma: no cover - raise PermissionDenied("You are not allowed to edit this planned session because it is a race") + if ps.sessiontype in ['race', 'indoorrace']: # pragma: no cover + raise PermissionDenied( + "You are not allowed to edit this planned session because it is a race") - if not ps.is_template: # pragma: no cover + if not ps.is_template: # pragma: no cover ps.pk = None ps.id = None ps.is_template = True - ps.startdate = datetime.date(1970,1,1) - ps.enddate = datetime.date(1970,1,1) + ps.startdate = datetime.date(1970, 1, 1) + ps.enddate = datetime.date(1970, 1, 1) ps.team.clear() ps.fitfile = None ps.save() @@ -1945,80 +1944,81 @@ def plannedsession_templateedit_view(request,id=0): sessioncreateform = PlannedSessionTemplateForm(instance=ps) if request.method == 'POST': - sessioncreateform = PlannedSessionTemplateForm(request.POST,instance=ps) + sessioncreateform = PlannedSessionTemplateForm( + request.POST, instance=ps) if sessioncreateform.is_valid(): cd = sessioncreateform.cleaned_data if cd['sessionunit'] == 'min': cd['sessionmode'] = 'time' - elif cd['sessionunit'] in ['km','m']: # pragma: no cover + elif cd['sessionunit'] in ['km', 'm']: # pragma: no cover cd['sessionmode'] = 'distance' obj = sessioncreateform.save(commit=False) obj.save() sessioncreateform.save_m2m() - res, message = update_plannedsession(ps,cd) - #sessioncreateform.save_m2m() + res, message = update_plannedsession(ps, cd) + # sessioncreateform.save_m2m() if res: - messages.info(request,message) - else: # pragma: no cover - messages.error(request,message) + messages.info(request, message) + else: # pragma: no cover + messages.error(request, message) url = reverse('template_library_view') - if '_continue' in request.POST: # pragma: no cover - url = reverse('plannedsession_templateedit_view',kwargs={ - 'id':ps.id, - }) - + if '_continue' in request.POST: # pragma: no cover + url = reverse('plannedsession_templateedit_view', kwargs={ + 'id': ps.id, + }) return HttpResponseRedirect(url) breadcrumbs = [ { - 'url': reverse(plannedsessions_view), - 'name': 'Sessions' + 'url': reverse(plannedsessions_view), + 'name': 'Sessions' }, { 'url': reverse('template_library_view'), 'name': 'Library', }, { - 'url':reverse(plannedsession_templateedit_view, - kwargs={ - 'id':id, + 'url': reverse(plannedsession_templateedit_view, + kwargs={ + 'id': id, + } + ), + 'name': 'Edit Session' } - ), - 'name': 'Edit Session' - } - ] + ] - sessiontemplates = PlannedSession.objects.filter(manager=request.user,is_template=True) + sessiontemplates = PlannedSession.objects.filter( + manager=request.user, is_template=True) sessiontemplates2 = PlannedSession.objects.filter( - is_template=True,is_public=True + is_template=True, is_public=True ).order_by("name") sessiontemplates = sessiontemplates | sessiontemplates2 - return render(request,'plannedsessiontemplateedit.html', + return render(request, 'plannedsessiontemplateedit.html', { - 'teams':get_my_teams(request.user), + 'teams': get_my_teams(request.user), 'plan': trainingplan, 'breadcrumbs': breadcrumbs, 'form': sessioncreateform, - 'active':'nav-plan', + 'active': 'nav-plan', 'thesession': ps, 'sessiontemplates': sessiontemplates, 'rower': r, }) -@permission_required('plannedsession.change_session',fn=get_session_by_pk,raise_exception=True) +@permission_required('plannedsession.change_session', fn=get_session_by_pk, raise_exception=True) @user_passes_test(can_plan, login_url="/rowers/paidplans/", message="This functionality requires a Coach or Self-Coach plan", redirect_field_name=None) -def plannedsession_togarmin_view(request,id=0): +def plannedsession_togarmin_view(request, id=0): r = getrequestplanrower(request) @@ -2026,17 +2026,19 @@ def plannedsession_togarmin_view(request,id=0): startdate = startdate.date() enddate = enddate.date() - ps = get_object_or_404(PlannedSession,pk=id) + ps = get_object_or_404(PlannedSession, pk=id) - result = gs.garmin_session_create(ps,r.user) + result = gs.garmin_session_create(ps, r.user) - if not result: # pragma: no cover - messages.error(request,'You failed to export your session to Garmin Connect') # pragma: no cover - else: # pragma: no cover - messages.info(request,'Session is now on Garmin Connect. Sync your Garmin watch') + if not result: # pragma: no cover + messages.error( + request, 'You failed to export your session to Garmin Connect') # pragma: no cover + else: # pragma: no cover + messages.info( + request, 'Session is now on Garmin Connect. Sync your Garmin watch') - url = reverse(plannedsession_view,kwargs={'userid':r.user.id, - 'id':ps.id,}) + url = reverse(plannedsession_view, kwargs={'userid': r.user.id, + 'id': ps.id, }) startdatestring = startdate.strftime('%Y-%m-%d') enddatestring = enddate.strftime('%Y-%m-%d') @@ -2047,11 +2049,11 @@ def plannedsession_togarmin_view(request,id=0): return HttpResponseRedirect(next) -@permission_required('plannedsession.change_session',fn=get_session_by_pk,raise_exception=True) +@permission_required('plannedsession.change_session', fn=get_session_by_pk, raise_exception=True) @user_passes_test(can_plan, login_url="/rowers/paidplans/", message="This functionality requires a Coach or Self-Coach plan", redirect_field_name=None) -def plannedsession_totemplate_view(request,id=0): +def plannedsession_totemplate_view(request, id=0): r = getrequestplanrower(request) @@ -2059,17 +2061,17 @@ def plannedsession_totemplate_view(request,id=0): startdate = startdate.date() enddate = enddate.date() - ps = get_object_or_404(PlannedSession,pk=id) + ps = get_object_or_404(PlannedSession, pk=id) ps.pk = None ps.id = None ps.is_template = True ps.fitfile = None - ps.startdate = datetime.date(1970,1,1) - ps.enddate = datetime.date(1970,1,1) + ps.startdate = datetime.date(1970, 1, 1) + ps.enddate = datetime.date(1970, 1, 1) ps.save() - url = reverse(plannedsession_create_view,kwargs={'userid':r.user.id}) + url = reverse(plannedsession_create_view, kwargs={'userid': r.user.id}) startdatestring = startdate.strftime('%Y-%m-%d') enddatestring = enddate.strftime('%Y-%m-%d') @@ -2080,66 +2082,67 @@ def plannedsession_totemplate_view(request,id=0): return HttpResponseRedirect(next) # Edit an existing planned session -@permission_required('plannedsession.change_session',fn=get_session_by_pk,raise_exception=True) -@user_passes_test(can_plan,login_url="/rowers/paidplans/", + + +@permission_required('plannedsession.change_session', fn=get_session_by_pk, raise_exception=True) +@user_passes_test(can_plan, login_url="/rowers/paidplans/", message="This functionality requires a Coach or Self-Coach plan", redirect_field_name=None) -def plannedsession_edit_view(request,id=0,userid=0): +def plannedsession_edit_view(request, id=0, userid=0): - r = getrequestplanrower(request,userid=userid) + r = getrequestplanrower(request, userid=userid) - startdate,enddate = get_dates_timeperiod(request) + startdate, enddate = get_dates_timeperiod(request) startdate = startdate.date() enddate = enddate.date() - timeperiod = startdate.strftime('%Y-%m-%d')+'/'+enddate.strftime('%Y-%m-%d') - + timeperiod = startdate.strftime( + '%Y-%m-%d')+'/'+enddate.strftime('%Y-%m-%d') try: trainingplan = TrainingPlan.objects.filter( - startdate__lte = startdate, - rowers = r, - enddate__gte = enddate)[0] - except IndexError: # pragma: no cover + startdate__lte=startdate, + rowers=r, + enddate__gte=enddate)[0] + except IndexError: # pragma: no cover trainingplan = None - ps = get_object_or_404(PlannedSession,pk=id) + ps = get_object_or_404(PlannedSession, pk=id) - if ps.team.all() or ps.rower.all().count()>1: + if ps.team.all() or ps.rower.all().count() > 1: url = reverse(plannedsession_teamedit_view, kwargs={ - 'id':id, - }) + 'id': id, + }) url = url+"?when="+timeperiod return HttpResponseRedirect(url) if request.method == 'POST': - sessioncreateform = PlannedSessionForm(request.POST, request.FILES,instance=ps) + sessioncreateform = PlannedSessionForm( + request.POST, request.FILES, instance=ps) if sessioncreateform.is_valid(): cd = sessioncreateform.cleaned_data - if cd['sessionunit'] == 'min': # pragma: no cover + if cd['sessionunit'] == 'min': # pragma: no cover cd['sessionmode'] = 'time' - elif cd['sessionunit'] in ['km','m']: + elif cd['sessionunit'] in ['km', 'm']: cd['sessionmode'] = 'distance' - - - res,message = update_plannedsession(ps,cd) + res, message = update_plannedsession(ps, cd) if res: - messages.info(request,message) - else: # pragma: no cover - messages.error(request,message) + messages.info(request, message) + else: # pragma: no cover + messages.error(request, message) url = reverse('plannedsessions_view') - if "_continue" in request.POST: # pragma: no cover + if "_continue" in request.POST: # pragma: no cover url = reverse(plannedsession_edit_view, - kwargs={ - 'id':int(ps.id), - 'userid':r.user.id, - }) + kwargs={ + 'id': int(ps.id), + 'userid': r.user.id, + }) startdatestring = startdate.strftime('%Y-%m-%d') enddatestring = enddate.strftime('%Y-%m-%d') @@ -2150,110 +2153,109 @@ def plannedsession_edit_view(request,id=0,userid=0): else: sessioncreateform = PlannedSessionForm(instance=ps) - sps = get_sessions(r,startdate=startdate,enddate=enddate) + sps = get_sessions(r, startdate=startdate, enddate=enddate) breadcrumbs = [ { 'url': reverse(plannedsessions_view, - kwargs={'userid':userid}), + kwargs={'userid': userid}), 'name': 'Sessions' - }, + }, { 'url': reverse(plannedsessions_view)+'?when='+timeperiod, 'name': timeperiod, }, { - 'url':reverse(plannedsession_view, - kwargs={ - 'userid':userid, - 'id':id, - } - ), + 'url': reverse(plannedsession_view, + kwargs={ + 'userid': userid, + 'id': id, + } + ), 'name': ps.id - }, + }, { - 'url':reverse(plannedsession_edit_view, - kwargs={ - 'userid':userid, - 'id':id, - } - ), + 'url': reverse(plannedsession_edit_view, + kwargs={ + 'userid': userid, + 'id': id, + } + ), 'name': 'Edit' - } - ] + } + ] - - sessiontemplates = PlannedSession.objects.filter(manager=request.user,is_template=True) + sessiontemplates = PlannedSession.objects.filter( + manager=request.user, is_template=True) sessiontemplates2 = PlannedSession.objects.filter( - is_template=True,is_public=True + is_template=True, is_public=True ).order_by("name") sessiontemplates = sessiontemplates | sessiontemplates2 dateform = DateRangeForm(initial={ - 'startdate':startdate, - 'enddate':enddate, - }) + 'startdate': startdate, + 'enddate': enddate, + }) - timeperiod = startdate.strftime('%Y-%m-%d')+'/'+enddate.strftime('%Y-%m-%d') + timeperiod = startdate.strftime( + '%Y-%m-%d')+'/'+enddate.strftime('%Y-%m-%d') - return render(request,'plannedsessionedit.html', + return render(request, 'plannedsessionedit.html', { - 'teams':get_my_teams(request.user), - 'plan':trainingplan, - 'breadcrumbs':breadcrumbs, - 'form':sessioncreateform, - 'active':'nav-plan', - 'plannedsessions':sps, - 'thesession':ps, - 'dateform':dateform, - 'sessiontemplates':sessiontemplates, - 'rower':r, - 'timeperiod':timeperiod, + 'teams': get_my_teams(request.user), + 'plan': trainingplan, + 'breadcrumbs': breadcrumbs, + 'form': sessioncreateform, + 'active': 'nav-plan', + 'plannedsessions': sps, + 'thesession': ps, + 'dateform': dateform, + 'sessiontemplates': sessiontemplates, + 'rower': r, + 'timeperiod': timeperiod, }) -@permission_required('workout.change_workout',fn=get_workout_by_opaqueid,raise_exception=True) -def plannedsession_detach_view(request,id=0,psid=0): +@permission_required('workout.change_workout', fn=get_workout_by_opaqueid, raise_exception=True) +def plannedsession_detach_view(request, id=0, psid=0): r = getrequestrower(request) - - ps = get_object_or_404(PlannedSession,pk=psid) + ps = get_object_or_404(PlannedSession, pk=psid) w = get_workout(id) - remove_workout_plannedsession(w,ps) + remove_workout_plannedsession(w, ps) - url = reverse(plannedsession_view,kwargs={'id':psid}) + url = reverse(plannedsession_view, kwargs={'id': psid}) next = request.GET.get('next', url) return HttpResponseRedirect(next) + @login_required() -@permission_required('plannedsession.view_session',fn=get_session_by_pk,raise_exception=True) -def plannedsession_view(request,id=0,userid=0): +@permission_required('plannedsession.view_session', fn=get_session_by_pk, raise_exception=True) +def plannedsession_view(request, id=0, userid=0): + r = getrequestplanrower(request, userid=userid) - r = getrequestplanrower(request,userid=userid) - - ps = get_object_or_404(PlannedSession,pk=id) - if ps.steps is not None: # pragma: no cover + ps = get_object_or_404(PlannedSession, pk=id) + if ps.steps is not None: # pragma: no cover jsons = ps.steps else: jsons = {} - - try: # pragma: no cover + try: # pragma: no cover r = VirtualRace.objects.get(id=ps.id) url = reverse('virtualevent_view', - kwargs={'id':ps.id} - ) + kwargs={'id': ps.id} + ) return HttpResponseRedirect(url) except VirtualRace.DoesNotExist: pass - if ps.course: # pragma: no cover - coursescript,coursediv = course_map(ps.course) + if ps.course: # pragma: no cover + coursescript, coursediv = course_map(ps.course) else: coursescript = '' coursediv = '' @@ -2264,18 +2266,18 @@ def plannedsession_view(request,id=0,userid=0): resultsdict = get_session_metrics(ps) resultsdict = pd.DataFrame(resultsdict).transpose().to_dict() - psdict = my_dict_from_instance(ps,PlannedSession) + psdict = my_dict_from_instance(ps, PlannedSession) - ws = get_workouts_session(r,ps) + ws = get_workouts_session(r, ps) - ratio,status,completiondate = is_session_complete(r,ps) + ratio, status, completiondate = is_session_complete(r, ps) ratio = int(100.*ratio) # ranking for test ranking = [] - if ps.sessiontype in ['test','coursetest']: + if ps.sessiontype in ['test', 'coursetest']: if ps.sessionmode == 'distance': rankws = Workout.objects.filter( plannedsession=ps).order_by("duration") @@ -2294,11 +2296,11 @@ def plannedsession_view(request,id=0,userid=0): 'distance': w.distance, 'time': dddelta, 'type': w.workouttype, - 'coursecompleted':True, - 'sessionresult':0, - 'workoutid':w.id, + 'coursecompleted': True, + 'sessionresult': 0, + 'workoutid': w.id, } - if ps.sessiontype == 'coursetest': # pragma: no cover + if ps.sessiontype == 'coursetest': # pragma: no cover vs = CourseTestResult.objects.filter(plannedsession=ps, workoutid=w.id) @@ -2324,12 +2326,12 @@ def plannedsession_view(request,id=0,userid=0): plannedsession=ps, duration=w.duration, coursecompleted=False, - ) + ) record.save() - job = myqueue(queue,handle_check_race_course, - w.csvfilename,w.id,ps.course.id, + job = myqueue(queue, handle_check_race_course, + w.csvfilename, w.id, ps.course.id, record.id, - w.user.user.email,w.user.user.first_name, + w.user.user.email, w.user.user.first_name, mode='coursetest') intsecs = 0 @@ -2340,14 +2342,14 @@ def plannedsession_view(request,id=0,userid=0): hours=w.duration.hour, minutes=w.duration.minute, seconds=w.duration.second, - ) + ) wdict['distance'] = ps.course.distance wdict['coursecompleted'] = False ranking.append(wdict) - if ps.sessiontype == 'coursetest': # pragma: no cover + if ps.sessiontype == 'coursetest': # pragma: no cover ranking = sorted(ranking, key=lambda k: k['time']) - if ps.sessiontype == 'fastest_distance': # pragma: no cover + if ps.sessiontype == 'fastest_distance': # pragma: no cover vs = CourseTestResult.objects.filter(plannedsession=ps) if vs: @@ -2361,9 +2363,9 @@ def plannedsession_view(request,id=0,userid=0): 'date': w.date, 'distance': record.distance, 'type': w.workouttype, - 'workoutid':w.id, - 'coursecompleted':True, - 'sessionresult':record.id + 'workoutid': w.id, + 'coursecompleted': True, + 'sessionresult': record.id } coursecompleted = record.coursecompleted @@ -2375,7 +2377,6 @@ def plannedsession_view(request,id=0,userid=0): microseconds=t.microsecond ) - wdict['coursecompleted'] = coursecompleted ranking.append(wdict) @@ -2383,7 +2384,7 @@ def plannedsession_view(request,id=0,userid=0): pass ranking = sorted(ranking, key=lambda k: k['time']) - if ps.sessiontype == 'fastest_time': # pragma: no cover + if ps.sessiontype == 'fastest_time': # pragma: no cover vs = CourseTestResult.objects.filter(plannedsession=ps) if vs: @@ -2396,9 +2397,9 @@ def plannedsession_view(request,id=0,userid=0): 'date': w.date, 'distance': record.distance, 'type': w.workouttype, - 'workoutid':w.id, - 'coursecompleted':True, - 'sessionresult':record.id, + 'workoutid': w.id, + 'coursecompleted': True, + 'sessionresult': record.id, } coursecompleted = record.coursecompleted @@ -2410,7 +2411,6 @@ def plannedsession_view(request,id=0,userid=0): microseconds=t.microsecond ) - wdict['coursecompleted'] = coursecompleted ranking.append(wdict) @@ -2418,130 +2418,131 @@ def plannedsession_view(request,id=0,userid=0): # if coursetest, need to reorder the ranking - startdate,enddate = get_dates_timeperiod(request) + startdate, enddate = get_dates_timeperiod(request) startdate = startdate.date() enddate = enddate.date() try: trainingplan = TrainingPlan.objects.filter( - startdate__lte = startdate, - rowers = r, - enddate__gte = enddate)[0] + startdate__lte=startdate, + rowers=r, + enddate__gte=enddate)[0] except IndexError: trainingplan = None - timeperiod = startdate.strftime('%Y-%m-%d')+'/'+enddate.strftime('%Y-%m-%d') + timeperiod = startdate.strftime( + '%Y-%m-%d')+'/'+enddate.strftime('%Y-%m-%d') breadcrumbs = [ { - 'url':reverse(plannedsessions_view, - kwargs={'userid':userid}), + 'url': reverse(plannedsessions_view, + kwargs={'userid': userid}), 'name': 'Plan' }, { 'url': reverse(plannedsessions_view, - kwargs={'userid':userid}), + kwargs={'userid': userid}), 'name': 'Sessions' - }, + }, { - 'url':reverse(plannedsession_view, - kwargs={ - 'userid':userid, - 'id':id, - } - ), + 'url': reverse(plannedsession_view, + kwargs={ + 'userid': userid, + 'id': id, + } + ), 'name': ps.id - } - ] + } + ] - comments = PlannedSessionComment.objects.filter(plannedsession=ps).order_by("created") + comments = PlannedSessionComment.objects.filter( + plannedsession=ps).order_by("created") steps = '' - if ps.steps: # pragma: no cover + if ps.steps: # pragma: no cover d = ps.steps - steps = ps_dict_get_description_html(d,short=False) + steps = ps_dict_get_description_html(d, short=False) - - - return render(request,'plannedsessionview.html', + return render(request, 'plannedsessionview.html', { 'psdict': psdict, - 'attrs':[ - 'name','startdate','enddate','preferreddate', + 'attrs': [ + 'name', 'startdate', 'enddate', 'preferreddate', 'sessionsport', 'sessiontype', - 'sessionmode','criterium', - 'sessionvalue','sessionunit', - 'approximate_distance','approximate_duration', + 'sessionmode', 'criterium', + 'sessionvalue', 'sessionunit', + 'approximate_distance', 'approximate_duration', 'approximate_rscore', 'comment', ], 'workouts': ws, - 'active':'nav-plan', - 'breadcrumbs':breadcrumbs, - 'manager':mm, - 'rower':r, - 'ratio':ratio, - 'plan':trainingplan, - 'status':status, - 'results':resultsdict, - 'plannedsession':ps, - 'timeperiod':timeperiod, - 'ranking':ranking, + 'active': 'nav-plan', + 'breadcrumbs': breadcrumbs, + 'manager': mm, + 'rower': r, + 'ratio': ratio, + 'plan': trainingplan, + 'status': status, + 'results': resultsdict, + 'plannedsession': ps, + 'timeperiod': timeperiod, + 'ranking': ranking, 'coursescript': coursescript, 'coursediv': coursediv, 'comments': comments, - 'steps':steps, - } + 'steps': steps, + } ) + class PlannedSessionDelete(DeleteView): model = PlannedSession template_name = 'plannedsessiondeleteconfirm.html' # extra parameters def get_context_data(self, **kwargs): - context = super(PlannedSessionDelete,self).get_context_data(**kwargs) + context = super(PlannedSessionDelete, self).get_context_data(**kwargs) - if 'userid' in kwargs: # pragma: no cover + if 'userid' in kwargs: # pragma: no cover userid = kwargs['userid'] else: userid = 0 - context['active']= 'nav-plan' - context['rower'] = getrequestrower(self.request,userid=userid) + context['active'] = 'nav-plan' + context['rower'] = getrequestrower(self.request, userid=userid) context['ps'] = self.object - psdict = my_dict_from_instance(self.object,PlannedSession) + psdict = my_dict_from_instance(self.object, PlannedSession) context['psdict'] = psdict - context['attrs'] = ['name','startdate','enddate','sessiontype'] + context['attrs'] = ['name', 'startdate', 'enddate', 'sessiontype'] breadcrumbs = [ { - 'url':reverse(plannedsessions_view, - kwargs={'userid':userid}), + 'url': reverse(plannedsessions_view, + kwargs={'userid': userid}), 'name': 'Plan' }, { 'url': reverse(plannedsessions_view, - kwargs={'userid':userid}), + kwargs={'userid': userid}), 'name': 'Sessions' }, { - 'url':reverse(plannedsession_view, - kwargs={ - 'userid':userid, - 'id':self.object.pk, - } - ), + 'url': reverse(plannedsession_view, + kwargs={ + 'userid': userid, + 'id': self.object.pk, + } + ), 'name': self.object.pk }, { - 'url':reverse('plannedsession_delete_view', - kwargs={'pk':self.object.pk}), + 'url': reverse('plannedsession_delete_view', + kwargs={'pk': self.object.pk}), 'name': 'Delete' - } + } ] context['breadcrumbs'] = breadcrumbs @@ -2550,42 +2551,43 @@ class PlannedSessionDelete(DeleteView): def get_success_url(self): ws = Workout.objects.filter(plannedsession=self.object) - for w in ws: # pragma: no cover + for w in ws: # pragma: no cover w.plannedsession = None w.save() - - url = reverse(plannedsessions_view) - next = self.request.GET.get('next',url) + next = self.request.GET.get('next', url) return next def get_object(self, *args, **kwargs): obj = super(PlannedSessionDelete, self).get_object(*args, **kwargs) - if not can_delete_session(self.request.user,obj): # pragma: no cover - raise PermissionDenied('You are not allowed to delete this planned session') + if not can_delete_session(self.request.user, obj): # pragma: no cover + raise PermissionDenied( + 'You are not allowed to delete this planned session') return obj -def rower_view_instantplan(request,id='',userid=0): - r = getrequestrower(request,userid=userid) - if not id: # pragma: no cover + +def rower_view_instantplan(request, id='', userid=0): + r = getrequestrower(request, userid=userid) + if not id: # pragma: no cover raise Http404("Plan does not exist") plan = InstantPlan.objects.get(uuid=id) try: - discountedprice = credits.discounted(plan.price,r) - except AttributeError: # pragma: no cover + discountedprice = credits.discounted(plan.price, r) + except AttributeError: # pragma: no cover discountedprice = plan.price authorizationstring = 'Bearer '+settings.WORKOUTS_FIT_TOKEN url = settings.WORKOUTS_FIT_URL+"/trainingplan/"+id - headers = {'Authorization':authorizationstring} + headers = {'Authorization': authorizationstring} - response = requests.get(url=url,headers=headers) - if response.status_code != 200: # pragma: no cover - messages.error(request,"Could not connect to the training plan server") + response = requests.get(url=url, headers=headers) + if response.status_code != 200: # pragma: no cover + messages.error( + request, "Could not connect to the training plan server") return HttpResponseRedirect(reverse('rower_select_instantplan')) plansteps = response.json() @@ -2596,7 +2598,7 @@ def rower_view_instantplan(request,id='',userid=0): nextday = trainingdays.pop(0) for i in range(plansteps['duration']): if nextday['order'] == i+1: - nextday['week'] = (divmod(i,7)[0])+1 + nextday['week'] = (divmod(i, 7)[0])+1 trainingdays2.append(nextday) try: nextday = trainingdays.pop(0) @@ -2605,11 +2607,11 @@ def rower_view_instantplan(request,id='',userid=0): else: trainingdays2.append( { - 'order':i+1, - 'week': (divmod(i,7)[0])+1, - 'workouts':[] - } - ) + 'order': i+1, + 'week': (divmod(i, 7)[0])+1, + 'workouts': [] + } + ) targets = TrainingTarget.objects.filter( rowers=r, @@ -2617,39 +2619,39 @@ def rower_view_instantplan(request,id='',userid=0): ).order_by("-date") if request.method == 'POST' and not request.user.is_anonymous: - if not can_plan(request.user) and plan.price == 0: # pragma: no cover - messages.error(request,'You must be on a paid plan to use free training plans') - url = reverse('rower_view_instantplan',kwargs={ - 'id':id, - }) + if not can_plan(request.user) and plan.price == 0: # pragma: no cover + messages.error( + request, 'You must be on a paid plan to use free training plans') + url = reverse('rower_view_instantplan', kwargs={ + 'id': id, + }) return HttpResponseRedirect(url) # check if plan is free or credits are sufficient - if plan.price > 0: # pragma: no cover + if plan.price > 0: # pragma: no cover if plan.price > r.eurocredits: - messages.error(request,'You did not have enough credit to purchase this plan') - url = reverse('rower_view_instantplan',kwargs={ - 'id':id, - }) + messages.error( + request, 'You did not have enough credit to purchase this plan') + url = reverse('rower_view_instantplan', kwargs={ + 'id': id, + }) return HttpResponseRedirect(url) - - form = InstantPlanSelectForm(request.POST,targets=targets) - + form = InstantPlanSelectForm(request.POST, targets=targets) if form.is_valid(): - if plan.price > 0: # pragma: no cover - eurocredits = credits.withdraw(plan.price,r) + if plan.price > 0: # pragma: no cover + eurocredits = credits.withdraw(plan.price, r) plansteps = response.json() name = form.cleaned_data['name'] try: targetid = form.cleaned_data['target'] - if targetid != '': # pragma: no cover + if targetid != '': # pragma: no cover target = TrainingTarget.objects.get(id=int(targetid)) else: target = None - except KeyError: # pragma: no cover + except KeyError: # pragma: no cover try: targetid = request.POST['target'] if targetid != '': @@ -2664,110 +2666,113 @@ def rower_view_instantplan(request,id='',userid=0): datechoice = form.cleaned_data['datechoice'] status = True - if target and datechoice == 'target': # pragma: no cover + if target and datechoice == 'target': # pragma: no cover enddate = target.date startdate = enddate-datetime.timedelta(days=plan.duration) elif datechoice == 'startdate': enddate = startdate+datetime.timedelta(days=plan.duration) - else: # pragma: no cover + else: # pragma: no cover startdate = enddate-datetime.timedelta(days=plan.duration) - p = TrainingPlan( name=name, target=target, manager=r, startdate=startdate, - enddate=enddate,status=status, + enddate=enddate, status=status, notes=notes, ) p.save() p.rowers.add(r) - create_sessions_from_json(plansteps,r,startdate,r.user) + create_sessions_from_json(plansteps, r, startdate, r.user) - messages.info(request,'Your Sessions have been added') + messages.info(request, 'Your Sessions have been added') url = reverse('plannedsessions_view') - timeperiod = startdate.strftime('%Y-%m-%d')+'/'+enddate.strftime('%Y-%m-%d') + timeperiod = startdate.strftime( + '%Y-%m-%d')+'/'+enddate.strftime('%Y-%m-%d') url = url+'?when='+timeperiod return HttpResponseRedirect(url) - elif not request.user.is_anonymous: tz = pytz.timezone(r.defaulttimezone) nowdate = timezone.now().astimezone(tz) initial = { - 'datechoice':'startdate', + 'datechoice': 'startdate', 'startdate': nowdate, 'enddate': nowdate+datetime.timedelta(days=21) } - form = InstantPlanSelectForm(targets=targets,instantplan=plan,initial=initial) - else: # pragma: no cover + form = InstantPlanSelectForm( + targets=targets, instantplan=plan, initial=initial) + else: # pragma: no cover form = None breadcrumbs = [ { - 'url':reverse('plannedsessions_view'), + 'url': reverse('plannedsessions_view'), 'name': 'Sessions' - }, + }, { - 'url':reverse(rower_create_trainingplan, - kwargs={'id':userid}), + 'url': reverse(rower_create_trainingplan, + kwargs={'id': userid}), 'name': 'Manage Plans and Targets' - }, + }, { - 'url':reverse('rower_select_instantplan'), + 'url': reverse('rower_select_instantplan'), 'name': 'Select Existing Plans' }, { - 'url':reverse('rower_view_instantplan',kwargs={ - 'id':id, -# 'userid':userid, + 'url': reverse('rower_view_instantplan', kwargs={ + 'id': id, + # 'userid':userid, }), - 'name':plan.name + 'name': plan.name } - ] + ] return render(request, 'instantplan.html', { - 'rower':r, - 'active':'nav-plan', + 'rower': r, + 'active': 'nav-plan', 'plan': plan, - 'trainingdays':trainingdays2, - 'breadcrumbs':breadcrumbs, - 'form':form, + 'trainingdays': trainingdays2, + 'breadcrumbs': breadcrumbs, + 'form': form, }) + @login_required() -def remove_groupsession_view(request,id=0): +def remove_groupsession_view(request, id=0): r = getrequestrower(request) - ps = get_object_or_404(PlannedSession,pk=id) + ps = get_object_or_404(PlannedSession, pk=id) - res = remove_rower_session(r,ps) + res = remove_rower_session(r, ps) if res: - messages.info(request,"We have removed you from this group session") - else: # pragma: no cover - messages.error(request,"For some reason we could not remove you from this group session") + messages.info(request, "We have removed you from this group session") + else: # pragma: no cover + messages.error( + request, "For some reason we could not remove you from this group session") url = reverse('plannedsessions_view') return HttpResponseRedirect(url) + @login_required() def add_instantplan_view(request): - if not request.user.is_staff: # pragma: no cover + if not request.user.is_staff: # pragma: no cover raise PermissionDenied("Not Allowed") r = request.user.rower - if request.method == 'POST': # pragma: no cover - form = InstantPlanForm(request.POST,request.FILES) + if request.method == 'POST': # pragma: no cover + form = InstantPlanForm(request.POST, request.FILES) if form.is_valid(): ip = form.save(commit=False) ip.owner = r.user @@ -2781,75 +2786,77 @@ def add_instantplan_view(request): breadcrumbs = [ { - 'url':reverse('plannedsessions_view'), + 'url': reverse('plannedsessions_view'), 'name': 'Sessions' - }, + }, { - 'url':reverse(rower_create_trainingplan), + 'url': reverse(rower_create_trainingplan), 'name': 'Manage Plans and Targets' - }, + }, { - 'url':reverse('rower_select_instantplan'), + 'url': reverse('rower_select_instantplan'), 'name': 'Select Existing Plans' }, { 'url': reverse(add_instantplan_view), 'name': 'Add New Instant Plan' } - ] + ] - return render(request,'add_instantplan.html', + return render(request, 'add_instantplan.html', { - 'form':form, - 'rower':r, - 'active':'nav-plan', - 'breadcrumbs':breadcrumbs, + 'form': form, + 'rower': r, + 'active': 'nav-plan', + 'breadcrumbs': breadcrumbs, } ) -def rower_select_instantplan(request,id=0): - r = getrequestrower(request,userid=id) + +def rower_select_instantplan(request, id=0): + r = getrequestrower(request, userid=id) themanager = getrower(request.user) # get and present available plans - ips = InstantPlan.objects.all().order_by("name","sessionsperweek","hoursperweek","duration","id") + ips = InstantPlan.objects.all().order_by( + "name", "sessionsperweek", "hoursperweek", "duration", "id") breadcrumbs = [ { - 'url':reverse('plannedsessions_view'), + 'url': reverse('plannedsessions_view'), 'name': 'Sessions' - }, + }, { - 'url':reverse(rower_create_trainingplan, - kwargs={'id':id}), + 'url': reverse(rower_create_trainingplan, + kwargs={'id': id}), 'name': 'Manage Plans and Targets' - }, + }, { - 'url':reverse('rower_select_instantplan'), + 'url': reverse('rower_select_instantplan'), 'name': 'Select Existing Plans' } - ] - + ] return render(request, 'instantplans.html', { - 'rower':r, - 'active':'nav-plan', - 'plans':ips, + 'rower': r, + 'active': 'nav-plan', + 'plans': ips, }) -@user_passes_test(can_plan,login_url="/rowers/paidplans", + +@user_passes_test(can_plan, login_url="/rowers/paidplans", message="This functionality requires a Coach or Self-Coach plan", redirect_field_name=None) -def rower_create_trainingplan(request,id=0): - therower = getrequestrower(request,userid=id) +def rower_create_trainingplan(request, id=0): + therower = getrequestrower(request, userid=id) theuser = therower.user themanager = getrower(request.user) if request.method == 'POST' and 'date' in request.POST: - targetform = TrainingTargetForm(request.POST,user=request.user) + targetform = TrainingTargetForm(request.POST, user=request.user) if targetform.is_valid(): name = targetform.cleaned_data['name'] date = targetform.cleaned_data['date'] @@ -2865,15 +2872,13 @@ def rower_create_trainingplan(request,id=0): manager=themanager, notes=notes) - t.save() for athlete in rowers: t.rowers.add(athlete) t.save() elif request.method == 'POST' and 'startdate' in request.POST: - form = TrainingPlanForm(request.POST,user=request.user) - + form = TrainingPlanForm(request.POST, user=request.user) if form.is_valid(): name = form.cleaned_data['name'] @@ -2884,9 +2889,9 @@ def rower_create_trainingplan(request,id=0): targetid = request.POST['target'] if targetid != '': target = TrainingTarget.objects.get(id=int(targetid)) - else: # pragma: no cover + else: # pragma: no cover target = None - except KeyError: # pragma: no cover + except KeyError: # pragma: no cover target = None startdate = form.cleaned_data['startdate'] status = form.cleaned_data['status'] @@ -2903,17 +2908,16 @@ def rower_create_trainingplan(request,id=0): target=target, manager=themanager, startdate=startdate, - enddate=enddate,status=status, + enddate=enddate, status=status, notes=notes, ) p.save() for athlete in athletes: - if can_plan_user(request.user,athlete): + if can_plan_user(request.user, athlete): p.rowers.add(athlete) - targets = TrainingTarget.objects.filter( rowers=therower, date__gte=timezone.now(), @@ -2922,7 +2926,7 @@ def rower_create_trainingplan(request,id=0): old_targets = TrainingTarget.objects.filter( rowers=therower, date__lt=timezone.now(), - ).order_by("-date") + ).order_by("-date") targetform = TrainingTargetForm(user=request.user) @@ -2934,48 +2938,47 @@ def rower_create_trainingplan(request,id=0): status=True, ).order_by("-startdate") - for p in plans_to_deactivate: # pragma: no cover + for p in plans_to_deactivate: # pragma: no cover p.status = False p.save() - form = TrainingPlanForm(targets=targets, - initial={'status':False,'rowers':[therower]}, + initial={'status': False, 'rowers': [therower]}, user=request.user) breadcrumbs = [ { - 'url':reverse(plannedsessions_view, - kwargs={'userid':id}), + 'url': reverse(plannedsessions_view, + kwargs={'userid': id}), 'name': 'Plan' }, { - 'url':reverse(rower_create_trainingplan, - kwargs={'id':id}), + 'url': reverse(rower_create_trainingplan, + kwargs={'id': id}), 'name': 'Manage Plans and Targets' - } - ] + } + ] - - return render(request,'trainingplan_create.html', + return render(request, 'trainingplan_create.html', { - 'form':form, - 'rower':therower, - 'breadcrumbs':breadcrumbs, - 'plans':plans, - 'active':'nav-plan', - 'targets':targets, - 'targetform':targetform, - 'old_targets':old_targets, - }) + 'form': form, + 'rower': therower, + 'breadcrumbs': breadcrumbs, + 'plans': plans, + 'active': 'nav-plan', + 'targets': targets, + 'targetform': targetform, + 'old_targets': old_targets, + }) -@user_passes_test(can_plan,login_url="/rowers/paidplans", + +@user_passes_test(can_plan, login_url="/rowers/paidplans", message="This functionality requires a Coach or Self-Coach plan", redirect_field_name=None) -@permission_required('target.delete_target',fn=get_target_by_pk,raise_exception=True) -def rower_delete_trainingtarget(request,id=0): +@permission_required('target.delete_target', fn=get_target_by_pk, raise_exception=True) +def rower_delete_trainingtarget(request, id=0): - target = get_object_or_404(TrainingTarget,pk=id) + target = get_object_or_404(TrainingTarget, pk=id) target.delete() url = reverse(rower_create_trainingplan) @@ -2990,12 +2993,14 @@ class TrainingPlanDelete(DeleteView): def get_object(self, *args, **kwargs): obj = super(TrainingPlanDelete, self).get_object(*args, **kwargs) - if not can_delete_plan(self.request.user,obj): # pragma: no cover - raise PermissionDenied('You are not allowed to delete this training plan') + if not can_delete_plan(self.request.user, obj): # pragma: no cover + raise PermissionDenied( + 'You are not allowed to delete this training plan') return obj -class MicroCycleDelete(DeleteView): # pragma: no cover + +class MicroCycleDelete(DeleteView): # pragma: no cover model = TrainingMicroCycle template_name = 'trainingplan_delete.html' @@ -3006,41 +3011,41 @@ class MicroCycleDelete(DeleteView): # pragma: no cover if 'userid' in kwargs: userid = kwargs['userid'] else: - userid=0 + userid = 0 breadcrumbs = [ { - 'url':reverse(plannedsessions_view, - kwargs={'userid':userid}), + 'url': reverse(plannedsessions_view, + kwargs={'userid': userid}), 'name': 'Plan' }, { - 'url':reverse(rower_trainingplan_view, - kwargs={'userid':userid, - 'id':self.object.plan.plan.plan.id}), + 'url': reverse(rower_trainingplan_view, + kwargs={'userid': userid, + 'id': self.object.plan.plan.plan.id}), 'name': self.object.plan.plan.plan.name }, { - 'url':reverse('macrocycle_update_view', - kwargs={'pk':self.object.plan.plan.pk}), + 'url': reverse('macrocycle_update_view', + kwargs={'pk': self.object.plan.plan.pk}), 'name': self.object.plan.plan.name - }, + }, { - 'url':reverse('mesocycle_update_view', - kwargs={'pk':self.object.plan.pk}), + 'url': reverse('mesocycle_update_view', + kwargs={'pk': self.object.plan.pk}), 'name': self.object.plan.name - }, + }, { - 'url':reverse('microcycle_update_view', - kwargs={'pk':self.object.pk}), + 'url': reverse('microcycle_update_view', + kwargs={'pk': self.object.pk}), 'name': self.object.name - } + } ] context['active'] = 'nav-plan' context['breadcrumbs'] = breadcrumbs - context['rower'] = getrequestrower(self.request,userid=userid) + context['rower'] = getrequestrower(self.request, userid=userid) return context @@ -3049,19 +3054,20 @@ class MicroCycleDelete(DeleteView): # pragma: no cover createmacrofillers(plan) thismesoid = self.object.plan.pk return reverse(rower_trainingplan_view, - kwargs = { - 'id':plan.id, - 'thismesoid':thismesoid - }) + kwargs={ + 'id': plan.id, + 'thismesoid': thismesoid + }) def get_object(self, *args, **kwargs): obj = super(MicroCycleDelete, self).get_object(*args, **kwargs) - if not can_delete_cycle(self.request.user,obj): - raise PermissionDenied('You are not allowed to delete this training plan cycle') + if not can_delete_cycle(self.request.user, obj): + raise PermissionDenied( + 'You are not allowed to delete this training plan cycle') return obj -class MesoCycleDelete(DeleteView): # pragma: no cover +class MesoCycleDelete(DeleteView): # pragma: no cover model = TrainingMesoCycle template_name = 'trainingplan_delete.html' @@ -3072,36 +3078,36 @@ class MesoCycleDelete(DeleteView): # pragma: no cover if 'userid' in kwargs: userid = kwargs['userid'] else: - userid=0 + userid = 0 breadcrumbs = [ { - 'url':reverse(plannedsessions_view, - kwargs={'userid':userid}), + 'url': reverse(plannedsessions_view, + kwargs={'userid': userid}), 'name': 'Plan' }, { - 'url':reverse(rower_trainingplan_view, - kwargs={'userid':userid, - 'id':self.object.plan.plan.id}), + 'url': reverse(rower_trainingplan_view, + kwargs={'userid': userid, + 'id': self.object.plan.plan.id}), 'name': self.object.plan.plan.name }, { - 'url':reverse('macrocycle_update_view', - kwargs={'pk':self.object.plan.pk}), + 'url': reverse('macrocycle_update_view', + kwargs={'pk': self.object.plan.pk}), 'name': self.object.plan.name - }, + }, { - 'url':reverse('mesocycle_update_view', - kwargs={'pk':self.object.pk}), + 'url': reverse('mesocycle_update_view', + kwargs={'pk': self.object.pk}), 'name': self.object.name - } + } ] context['active'] = 'nav-plan' context['breadcrumbs'] = breadcrumbs - context['rower'] = getrequestrower(self.request,userid=userid) + context['rower'] = getrequestrower(self.request, userid=userid) return context @@ -3110,21 +3116,22 @@ class MesoCycleDelete(DeleteView): # pragma: no cover thismacroid = self.object.plan.pk createmacrofillers(plan) return reverse(rower_trainingplan_view, - kwargs = { - 'id':plan.id, - 'thismacroid':thismacroid, - }) + kwargs={ + 'id': plan.id, + 'thismacroid': thismacroid, + }) def get_object(self, *args, **kwargs): obj = super(MesoCycleDelete, self).get_object(*args, **kwargs) - if not can_delete_cycle(self.request.user,obj): - raise PermissionDenied('You are not allowed to delete this training plan cycle') + if not can_delete_cycle(self.request.user, obj): + raise PermissionDenied( + 'You are not allowed to delete this training plan cycle') return obj -class MacroCycleDelete(DeleteView): # pragma: no cover +class MacroCycleDelete(DeleteView): # pragma: no cover model = TrainingMacroCycle template_name = 'trainingplan_delete.html' @@ -3135,30 +3142,30 @@ class MacroCycleDelete(DeleteView): # pragma: no cover if 'userid' in kwargs: userid = kwargs['userid'] else: - userid=0 + userid = 0 breadcrumbs = [ { - 'url':reverse(plannedsessions_view, - kwargs={'userid':userid}), + 'url': reverse(plannedsessions_view, + kwargs={'userid': userid}), 'name': 'Plan' }, { - 'url':reverse(rower_trainingplan_view, - kwargs={'userid':userid, - 'id':self.object.plan.id}), + 'url': reverse(rower_trainingplan_view, + kwargs={'userid': userid, + 'id': self.object.plan.id}), 'name': self.object.plan.name }, { - 'url':reverse('macrocycle_update_view', - kwargs={'pk':self.object.pk}), + 'url': reverse('macrocycle_update_view', + kwargs={'pk': self.object.pk}), 'name': self.object.name - } + } ] context['active'] = 'nav-plan' context['breadcrumbs'] = breadcrumbs - context['rower'] = getrequestrower(self.request,userid=userid) + context['rower'] = getrequestrower(self.request, userid=userid) return context @@ -3166,117 +3173,117 @@ class MacroCycleDelete(DeleteView): # pragma: no cover plan = self.object.plan createmacrofillers(plan) return reverse(rower_trainingplan_view, - kwargs = { - 'id':plan.id - }) + kwargs={ + 'id': plan.id + }) def get_object(self, *args, **kwargs): obj = super(MacroCycleDelete, self).get_object(*args, **kwargs) - if not can_delete_cycle(self.request.user,obj): - raise PermissionDenied('You are not allowed to delete this training plan cycle') + if not can_delete_cycle(self.request.user, obj): + raise PermissionDenied( + 'You are not allowed to delete this training plan cycle') return obj -@user_passes_test(can_plan,login_url="/rowers/paidplans", +@user_passes_test(can_plan, login_url="/rowers/paidplans", message="This functionality requires a Coach or Self-Coach plan", redirect_field_name=None) def rower_trainingplan_execution_view(request, id=0, userid=0): - startdate,enddate = get_dates_timeperiod(request) + startdate, enddate = get_dates_timeperiod(request) startdate = startdate.date() enddate = enddate.date() - r = getrequestrower(request,userid=userid) + r = getrequestrower(request, userid=userid) - - if int(id)>0: + if int(id) > 0: try: plan = TrainingPlan.objects.get(id=id) - except TrainingPlan.DoesNotExist: # pragma: no cover + except TrainingPlan.DoesNotExist: # pragma: no cover raise Http404("Training Plan Does Not Exist") - if not is_coach_user(request.user,plan.manager.user): # pragma: no cover + if not is_coach_user(request.user, plan.manager.user): # pragma: no cover if request.user.rower not in plan.rowers.all(): raise PermissionDenied("Access denied") - if not startdate or not enddate: # pragma: no cover - if int(id)>0: + if not startdate or not enddate: # pragma: no cover + if int(id) > 0: startdate = plan.startdate enddate = plan.enddate else: - startdate,enddate = get_dates_timeperiod(request) + startdate, enddate = get_dates_timeperiod(request) else: - startdate,enddate = get_dates_timeperiod(request) + startdate, enddate = get_dates_timeperiod(request) startdate = startdate.date() enddate = enddate.date() - if int(id)>0: - data,message = get_execution_report(r,startdate,enddate,plan=plan) - else: # pragma: no cover - data,message = get_execution_report(r,startdate,enddate) + if int(id) > 0: + data, message = get_execution_report(r, startdate, enddate, plan=plan) + else: # pragma: no cover + data, message = get_execution_report(r, startdate, enddate) if not data.empty: - script, div = interactive_planchart(data,startdate,enddate) - else: # pragma: no cover + script, div = interactive_planchart(data, startdate, enddate) + else: # pragma: no cover script = '' div = '' - messages.error(request,'The plan does not cover this time range') + messages.error(request, 'The plan does not cover this time range') if int(id): breadcrumbs = [ { - 'url':reverse(plannedsessions_view, - kwargs={'userid':userid}), + 'url': reverse(plannedsessions_view, + kwargs={'userid': userid}), 'name': 'Plan' }, { - 'url':reverse(rower_trainingplan_view, - kwargs={'userid':userid, - 'id':id}), + 'url': reverse(rower_trainingplan_view, + kwargs={'userid': userid, + 'id': id}), 'name': plan.name }, { - 'url':reverse(rower_trainingplan_execution_view, - kwargs={'userid':userid, - 'id':id}), + 'url': reverse(rower_trainingplan_execution_view, + kwargs={'userid': userid, + 'id': id}), 'name': 'Execution' } ] - else: # pragma: no cover + else: # pragma: no cover breadcrumbs = [ { - 'url':reverse(plannedsessions_view, - kwargs={'userid':userid}), + 'url': reverse(plannedsessions_view, + kwargs={'userid': userid}), 'name': 'Plan' }, { - 'url':reverse(rower_trainingplan_execution_view, - kwargs={'userid':userid, - 'id':id}), + 'url': reverse(rower_trainingplan_execution_view, + kwargs={'userid': userid, + 'id': id}), 'name': 'Execution' } ] - return render(request,'trainingplan_chart.html', + return render(request, 'trainingplan_chart.html', { 'todays_date': timezone.now().date(), 'active': 'nav-plan', - 'breadcrumbs':breadcrumbs, - 'rower':r, - 'the_script':script, - 'the_div':div - } + 'breadcrumbs': breadcrumbs, + 'rower': r, + 'the_script': script, + 'the_div': div + } ) -#@user_passes_test(can_plan,login_url="/rowers/paidplans", +# @user_passes_test(can_plan,login_url="/rowers/paidplans", # message="This functionality requires a Coach or Self-Coach plan", # redirect_field_name=None) @login_required() -@permission_required('plan.view_plan',fn=get_plan_by_pk,raise_exception=True) +@permission_required('plan.view_plan', fn=get_plan_by_pk, raise_exception=True) def rower_trainingplan_view(request, id=0, userid=0, @@ -3284,114 +3291,117 @@ def rower_trainingplan_view(request, thismacroid=0, thismesoid=0): - - startdate,enddate = get_dates_timeperiod(request) + startdate, enddate = get_dates_timeperiod(request) startdate = startdate.date() enddate = enddate.date() - plan = get_object_or_404(TrainingPlan,pk=id) - r = getrequestrower(request,userid=userid) + plan = get_object_or_404(TrainingPlan, pk=id) + r = getrequestrower(request, userid=userid) createmacrofillers(plan) macrocycles = TrainingMacroCycle.objects.filter( plan=plan, type='userdefined').order_by("startdate") - checkscores(r,macrocycles) + checkscores(r, macrocycles) createmacrofillers(plan) - macrocycles = TrainingMacroCycle.objects.filter(plan=plan).order_by("startdate") + macrocycles = TrainingMacroCycle.objects.filter( + plan=plan).order_by("startdate") count = 0 cycles = {} for m in macrocycles: createmesofillers(m) - mesocycles = TrainingMesoCycle.objects.filter(plan=m).order_by("startdate") + mesocycles = TrainingMesoCycle.objects.filter( + plan=m).order_by("startdate") mesos = {} count2 = 0 for me in mesocycles: createmicrofillers(me) - microcycles = TrainingMicroCycle.objects.filter(plan=me).order_by("startdate") + microcycles = TrainingMicroCycle.objects.filter( + plan=me).order_by("startdate") mesos[count2] = (me, microcycles) count2 += 1 - cycles[count] = (m,mesos) + cycles[count] = (m, mesos) count += 1 breadcrumbs = [ { - 'url':reverse(plannedsessions_view, - kwargs={'userid':userid}), + 'url': reverse(plannedsessions_view, + kwargs={'userid': userid}), 'name': 'Plan' }, { - 'url':reverse(rower_trainingplan_view, - kwargs={'userid':userid, - 'id':id}), + 'url': reverse(rower_trainingplan_view, + kwargs={'userid': userid, + 'id': id}), 'name': plan.name - } - ] + } + ] if not thismicroid and not thismacroid and not thismesoid: try: - thismicro = get_todays_micro(plan,thedate=startdate) + thismicro = get_todays_micro(plan, thedate=startdate) thismicroid = thismicro.pk - except AttributeError: # pragma: no cover + except AttributeError: # pragma: no cover thismicroid = None - - return render(request,'trainingplan.html', + return render(request, 'trainingplan.html', { - 'plan':plan, + 'plan': plan, 'todays_date': timezone.now().date(), - 'active':'nav-plan', - 'breadcrumbs':breadcrumbs, - 'rower':r, - 'cycles':cycles, - 'thismicroid':thismicroid, - 'thismacroid':thismacroid, - 'thismesoid':thismesoid, - } + 'active': 'nav-plan', + 'breadcrumbs': breadcrumbs, + 'rower': r, + 'cycles': cycles, + 'thismicroid': thismicroid, + 'thismacroid': thismacroid, + 'thismesoid': thismesoid, + } ) -class TrainingMacroCycleUpdate(UpdateView): # pragma: no cover + +class TrainingMacroCycleUpdate(UpdateView): # pragma: no cover model = TrainingMacroCycle template_name = 'trainingplan_edit.html' form_class = TrainingMacroCycleForm # extra parameters def get_context_data(self, **kwargs): - context = super(TrainingMacroCycleUpdate, self).get_context_data(**kwargs) + context = super(TrainingMacroCycleUpdate, + self).get_context_data(**kwargs) if 'userid' in kwargs: userid = kwargs['userid'] else: - userid=0 + userid = 0 breadcrumbs = [ { - 'url':reverse(plannedsessions_view, - kwargs={'userid':userid}), + 'url': reverse(plannedsessions_view, + kwargs={'userid': userid}), 'name': 'Plan' }, { - 'url':reverse(rower_trainingplan_view, - kwargs={'userid':userid, - 'id':self.object.plan.id}), + 'url': reverse(rower_trainingplan_view, + kwargs={'userid': userid, + 'id': self.object.plan.id}), 'name': self.object.plan.name }, { - 'url':reverse('macrocycle_update_view', - kwargs={'pk':self.object.pk}), + 'url': reverse('macrocycle_update_view', + kwargs={'pk': self.object.pk}), 'name': self.object.name - } + } ] context['active'] = 'nav-plan' context['breadcrumbs'] = breadcrumbs - context['rower'] = getrequestrower(self.request,userid=userid) + context['rower'] = getrequestrower(self.request, userid=userid) return context @@ -3399,9 +3409,9 @@ class TrainingMacroCycleUpdate(UpdateView): # pragma: no cover plan = self.object.plan createmacrofillers(plan) return reverse(rower_trainingplan_view, - kwargs = { - 'id':plan.id, - 'thismacroid':self.object.id, + kwargs={ + 'id': plan.id, + 'thismacroid': self.object.id, } ) @@ -3414,55 +3424,58 @@ class TrainingMacroCycleUpdate(UpdateView): # pragma: no cover def get_object(self, *args, **kwargs): obj = super(TrainingMacroCycleUpdate, self).get_object(*args, **kwargs) - if not can_change_cycle(self.request.user,obj): - raise PermissionDenied('You are not allowed to edit this training plan cycle') + if not can_change_cycle(self.request.user, obj): + raise PermissionDenied( + 'You are not allowed to edit this training plan cycle') else: obj.type = 'userdefined' obj.save() return obj -class TrainingMesoCycleUpdate(UpdateView): # pragma: no cover + +class TrainingMesoCycleUpdate(UpdateView): # pragma: no cover model = TrainingMesoCycle template_name = 'trainingplan_edit.html' form_class = TrainingMesoCycleForm # extra parameters def get_context_data(self, **kwargs): - context = super(TrainingMesoCycleUpdate, self).get_context_data(**kwargs) + context = super(TrainingMesoCycleUpdate, + self).get_context_data(**kwargs) if 'userid' in kwargs: userid = kwargs['userid'] else: - userid=0 + userid = 0 breadcrumbs = [ { - 'url':reverse(plannedsessions_view, - kwargs={'userid':userid}), + 'url': reverse(plannedsessions_view, + kwargs={'userid': userid}), 'name': 'Plan' }, { - 'url':reverse(rower_trainingplan_view, - kwargs={'userid':userid, - 'id':self.object.plan.plan.id}), + 'url': reverse(rower_trainingplan_view, + kwargs={'userid': userid, + 'id': self.object.plan.plan.id}), 'name': self.object.plan.plan.name }, { - 'url':reverse('macrocycle_update_view', - kwargs={'pk':self.object.plan.pk}), + 'url': reverse('macrocycle_update_view', + kwargs={'pk': self.object.plan.pk}), 'name': self.object.plan.name - }, + }, { - 'url':reverse('mesocycle_update_view', - kwargs={'pk':self.object.pk}), + 'url': reverse('mesocycle_update_view', + kwargs={'pk': self.object.pk}), 'name': self.object.name - } + } ] context['active'] = 'nav-plan' context['breadcrumbs'] = breadcrumbs - context['rower'] = getrequestrower(self.request,userid=userid) + context['rower'] = getrequestrower(self.request, userid=userid) return context @@ -3470,9 +3483,9 @@ class TrainingMesoCycleUpdate(UpdateView): # pragma: no cover plan = self.object.plan createmesofillers(plan) return reverse(rower_trainingplan_view, - kwargs = { - 'id':plan.plan.id, - 'thismesoid':self.object.id, + kwargs={ + 'id': plan.plan.id, + 'thismesoid': self.object.id, } ) @@ -3485,8 +3498,9 @@ class TrainingMesoCycleUpdate(UpdateView): # pragma: no cover def get_object(self, *args, **kwargs): obj = super(TrainingMesoCycleUpdate, self).get_object(*args, **kwargs) - if not can_change_cycle(self.request.user,obj): - raise PermissionDenied('You are not allowed to edit this training plan cycle') + if not can_change_cycle(self.request.user, obj): + raise PermissionDenied( + 'You are not allowed to edit this training plan cycle') else: obj.type = 'userdefined' @@ -3495,53 +3509,55 @@ class TrainingMesoCycleUpdate(UpdateView): # pragma: no cover obj.plan.save() return obj -class TrainingMicroCycleUpdate(UpdateView): # pragma: no cover + +class TrainingMicroCycleUpdate(UpdateView): # pragma: no cover model = TrainingMicroCycle template_name = 'trainingplan_edit.html' form_class = TrainingMicroCycleForm # extra parameters def get_context_data(self, **kwargs): - context = super(TrainingMicroCycleUpdate, self).get_context_data(**kwargs) + context = super(TrainingMicroCycleUpdate, + self).get_context_data(**kwargs) if 'userid' in kwargs: userid = kwargs['userid'] else: - userid=0 + userid = 0 breadcrumbs = [ { - 'url':reverse(plannedsessions_view, - kwargs={'userid':userid}), + 'url': reverse(plannedsessions_view, + kwargs={'userid': userid}), 'name': 'Plan' }, { - 'url':reverse(rower_trainingplan_view, - kwargs={'userid':userid, - 'id':self.object.plan.plan.plan.id}), + 'url': reverse(rower_trainingplan_view, + kwargs={'userid': userid, + 'id': self.object.plan.plan.plan.id}), 'name': self.object.plan.plan.plan.name }, { - 'url':reverse('macrocycle_update_view', - kwargs={'pk':self.object.plan.plan.pk}), + 'url': reverse('macrocycle_update_view', + kwargs={'pk': self.object.plan.plan.pk}), 'name': self.object.plan.plan.name - }, + }, { - 'url':reverse('mesocycle_update_view', - kwargs={'pk':self.object.plan.pk}), + 'url': reverse('mesocycle_update_view', + kwargs={'pk': self.object.plan.pk}), 'name': self.object.plan.name - }, + }, { - 'url':reverse('microcycle_update_view', - kwargs={'pk':self.object.pk}), + 'url': reverse('microcycle_update_view', + kwargs={'pk': self.object.pk}), 'name': self.object.name - } + } ] context['active'] = 'nav-plan' context['breadcrumbs'] = breadcrumbs - context['rower'] = getrequestrower(self.request,userid=userid) + context['rower'] = getrequestrower(self.request, userid=userid) return context @@ -3549,11 +3565,12 @@ class TrainingMicroCycleUpdate(UpdateView): # pragma: no cover plan = self.object.plan createmicrofillers(plan) return reverse(rower_trainingplan_view, - kwargs = { - 'id':plan.plan.plan.id, - 'thismicroid':self.object.pk + kwargs={ + 'id': plan.plan.plan.id, + 'thismicroid': self.object.pk } ) + def form_valid(self, form): form.instance.user = self.request.user form.instance.post_date = datetime.datetime.now() @@ -3563,9 +3580,9 @@ class TrainingMicroCycleUpdate(UpdateView): # pragma: no cover def get_object(self, *args, **kwargs): obj = super(TrainingMicroCycleUpdate, self).get_object(*args, **kwargs) - if not can_change_cycle(self.request.user,obj): - raise PermissionDenied('You are not allowed to edit this training plan cycle') - + if not can_change_cycle(self.request.user, obj): + raise PermissionDenied( + 'You are not allowed to edit this training plan cycle') else: obj.type = 'userdefined' @@ -3574,7 +3591,8 @@ class TrainingMicroCycleUpdate(UpdateView): # pragma: no cover obj.plan.save() return obj -class TrainingPlanUpdate(UpdateView): # pragma: no cover + +class TrainingPlanUpdate(UpdateView): # pragma: no cover model = TrainingPlan template_name = 'trainingplan_edit.html' form_class = TrainingPlanForm @@ -3586,31 +3604,31 @@ class TrainingPlanUpdate(UpdateView): # pragma: no cover if 'userid' in kwargs: userid = kwargs['userid'] else: - userid=0 + userid = 0 breadcrumbs = [ { - 'url':reverse(plannedsessions_view, - kwargs={'userid':userid}), + 'url': reverse(plannedsessions_view, + kwargs={'userid': userid}), 'name': 'Plan' }, { - 'url':reverse(rower_trainingplan_view, - kwargs={'userid':userid, - 'id':self.object.id}), + 'url': reverse(rower_trainingplan_view, + kwargs={'userid': userid, + 'id': self.object.id}), 'name': self.object.name }, { - 'url':reverse('trainingplan_update_view', - kwargs={'pk':self.object.pk}), + 'url': reverse('trainingplan_update_view', + kwargs={'pk': self.object.pk}), 'name': 'Edit' - } + } ] context['active'] = 'nav-plan' context['breadcrumbs'] = breadcrumbs - context['rower'] = getrequestrower(self.request,userid=userid) + context['rower'] = getrequestrower(self.request, userid=userid) return context @@ -3628,12 +3646,14 @@ class TrainingPlanUpdate(UpdateView): # pragma: no cover def get_object(self, *args, **kwargs): obj = super(TrainingPlanUpdate, self).get_object(*args, **kwargs) - if not can_change_plan(self.request.user,obj): - raise PermissionDenied('You are not allowed to edit this training plan cycle') + if not can_change_plan(self.request.user, obj): + raise PermissionDenied( + 'You are not allowed to edit this training plan cycle') return obj -class TrainingTargetUpdate(UpdateView): # pragma: no cover + +class TrainingTargetUpdate(UpdateView): # pragma: no cover model = TrainingTarget template_name = 'trainingplan_edit.html' form_class = TrainingTargetForm @@ -3645,25 +3665,25 @@ class TrainingTargetUpdate(UpdateView): # pragma: no cover if 'userid' in kwargs: userid = kwargs['userid'] else: - userid=0 + userid = 0 breadcrumbs = [ { - 'url':reverse(plannedsessions_view, - kwargs={'userid':userid}), + 'url': reverse(plannedsessions_view, + kwargs={'userid': userid}), 'name': 'Plan' }, { - 'url':reverse('trainingtarget_update_view', - kwargs={'pk':self.object.pk}), + 'url': reverse('trainingtarget_update_view', + kwargs={'pk': self.object.pk}), 'name': 'Edit' - } + } ] context['active'] = 'nav-plan' context['breadcrumbs'] = breadcrumbs - context['rower'] = getrequestrower(self.request,userid=userid) + context['rower'] = getrequestrower(self.request, userid=userid) return context @@ -3678,19 +3698,19 @@ class TrainingTargetUpdate(UpdateView): # pragma: no cover def get_object(self, *args, **kwargs): obj = super(TrainingTargetUpdate, self).get_object(*args, **kwargs) - if not can_change_target(self.request.user,obj): - raise PermissionDenied('You are not allowed to edit this training plan cycle') + if not can_change_target(self.request.user, obj): + raise PermissionDenied( + 'You are not allowed to edit this training plan cycle') return obj -from rowers.utils import allsundays -@user_passes_test(can_plan,login_url="/rowers/paidplans", +@user_passes_test(can_plan, login_url="/rowers/paidplans", message="This functionality requires a Coach or Self-Coach plan", redirect_field_name=None) -@permission_required('cycle.change_cycle',fn=get_meso_by_pk,raise_exception=True) -def planmesocyclebyweek(request,id=0,userid=0): # pragma: no cover - cycle = get_object_or_404(TrainingMesoCycle,pk=id) +@permission_required('cycle.change_cycle', fn=get_meso_by_pk, raise_exception=True) +def planmesocyclebyweek(request, id=0, userid=0): # pragma: no cover + cycle = get_object_or_404(TrainingMesoCycle, pk=id) micros = TrainingMicroCycle.objects.filter(plan=cycle) for m in micros: @@ -3699,8 +3719,8 @@ def planmesocyclebyweek(request,id=0,userid=0): # pragma: no cover cycle.type = 'userdefined' cycle.save() - #we're still here. We have permission - sundays = [s for s in allsundays(cycle.startdate,cycle.enddate)] + # we're still here. We have permission + sundays = [s for s in allsundays(cycle.startdate, cycle.enddate)] if sundays and sundays[-1] < cycle.enddate: sundays = sundays+[cycle.enddate] @@ -3708,7 +3728,7 @@ def planmesocyclebyweek(request,id=0,userid=0): # pragma: no cover sundays = [cycle.enddate] for i in range(len(sundays)): - if i==0: + if i == 0: monday = cycle.startdate else: monday = sundays[i]-timedelta(days=6) @@ -3717,30 +3737,29 @@ def planmesocyclebyweek(request,id=0,userid=0): # pragma: no cover nextsunday = sundays[i] - micro = TrainingMicroCycle(startdate = monday, - enddate = nextsunday, - plan = cycle, - name = 'Week %s' % monday.isocalendar()[1], - type = 'userdefined') + micro = TrainingMicroCycle(startdate=monday, + enddate=nextsunday, + plan=cycle, + name='Week %s' % monday.isocalendar()[1], + type='userdefined') micro.save() micros = TrainingMicroCycle.objects.filter(plan=cycle) url = reverse(rower_trainingplan_view, - kwargs = {'userid':str(userid), - 'id':str(cycle.plan.plan.id), - 'thismicroid':str(micros[0].id)}) + kwargs={'userid': str(userid), + 'id': str(cycle.plan.plan.id), + 'thismicroid': str(micros[0].id)}) return HttpResponseRedirect(url) -from rowers.utils import allmonths -@user_passes_test(can_plan,login_url="/rowers/paidplans", +@user_passes_test(can_plan, login_url="/rowers/paidplans", message="This functionality requires a Coach or Self-Coach plan", redirect_field_name=None) -@permission_required('cycle.change_cycle',fn=get_macro_by_pk,raise_exception=True) -def planmacrocyclebymonth(request,id=0,userid=0): # pragma: no cover - cycle = get_object_or_404(TrainingMacroCycle,pk=id) +@permission_required('cycle.change_cycle', fn=get_macro_by_pk, raise_exception=True) +def planmacrocyclebymonth(request, id=0, userid=0): # pragma: no cover + cycle = get_object_or_404(TrainingMacroCycle, pk=id) mesos = TrainingMesoCycle.objects.filter(plan=cycle) for m in mesos: @@ -3749,8 +3768,8 @@ def planmacrocyclebymonth(request,id=0,userid=0): # pragma: no cover cycle.type = 'userdefined' cycle.save() - #we're still here. We have permission - monthstarts = [d for d in allmonths(cycle.startdate,cycle.enddate)] + # we're still here. We have permission + monthstarts = [d for d in allmonths(cycle.startdate, cycle.enddate)] monthstarts.append(cycle.enddate) for i in range(len(monthstarts)-1): firstday = monthstarts[i] @@ -3758,19 +3777,18 @@ def planmacrocyclebymonth(request,id=0,userid=0): # pragma: no cover if lastday < cycle.enddate and i == len(monthstarts)-2: lastday = cycle.enddate - - meso = TrainingMesoCycle(startdate = firstday, - enddate = lastday, - plan = cycle, - name = '%s' % firstday.strftime("%B"), - type = 'userdefined') + meso = TrainingMesoCycle(startdate=firstday, + enddate=lastday, + plan=cycle, + name='%s' % firstday.strftime("%B"), + type='userdefined') meso.save() mesos = TrainingMesoCycle.objects.filter(plan=cycle) url = reverse(rower_trainingplan_view, - kwargs = {'userid':str(userid), - 'id':str(cycle.plan.id), - 'thismesoid':str(mesos[0].id)}) + kwargs={'userid': str(userid), + 'id': str(cycle.plan.id), + 'thismesoid': str(mesos[0].id)}) return HttpResponseRedirect(url) diff --git a/rowers/views/racesviews.py b/rowers/views/racesviews.py index 67947454..db95160a 100644 --- a/rowers/views/racesviews.py +++ b/rowers/views/racesviews.py @@ -14,15 +14,17 @@ from rowers.plannedsessions import timefield_to_seconds_duration from rowers.courses import getnearestraces, getnearestcourses # List Courses + + def courses_view(request): r = getrower(request.user) g = GeoIP2() - ip = request.META.get('HTTP_X_REAL_IP','1.1.1.1') + ip = request.META.get('HTTP_X_REAL_IP', '1.1.1.1') try: lat_lon = g.lat_lon(ip) city = g.city(ip) - except: # pragma: no cover + except: # pragma: no cover lat_lon = None city = { 'city': '', @@ -30,18 +32,15 @@ def courses_view(request): 'time_zone': '', } - - courses = GeoCourse.objects.all().order_by("country","name","distance") + courses = GeoCourse.objects.all().order_by("country", "name", "distance") nearby = request.GET.get('nearby') - if nearby and lat_lon is not None: # pragma: no cover - courses = getnearestcourses(lat_lon,courses) - - + if nearby and lat_lon is not None: # pragma: no cover + courses = getnearestcourses(lat_lon, courses) # add search processing query = request.GET.get('q') - if query: # pragma: no cover + if query: # pragma: no cover query_list = query.split() courses = GeoCourse.objects.filter( reduce(operator.and_, @@ -50,23 +49,25 @@ def courses_view(request): (Q(country__icontains=q) for q in query_list)) | reduce(operator.and_, (Q(notes__icontains=q) for q in query_list)) - ) - searchform = SearchForm(initial={'q':query}) + ) + searchform = SearchForm(initial={'q': query}) else: searchform = SearchForm() - return render(request,'list_courses.html', - {'courses':courses, - 'active':'nav-racing', - 'searchform':searchform, - 'rower':r, - 'location':lat_lon, - 'city':city['city'], + return render(request, 'list_courses.html', + {'courses': courses, + 'active': 'nav-racing', + 'searchform': searchform, + 'rower': r, + 'location': lat_lon, + 'city': city['city'], 'country_name': city['country_name'], - 'time_zone':city['time_zone'], + 'time_zone': city['time_zone'], }) # List Courses + + def standards_view(request): r = getrower(request.user) @@ -74,36 +75,34 @@ def standards_view(request): # add search processing query = request.GET.get('q') - if query: # pragma: no cover + if query: # pragma: no cover query_list = query.split() standards = StandardCollection.objects.filter( reduce(operator.and_, (Q(name__icontains=q) for q in query_list)) | reduce(operator.and_, (Q(notes__icontains=q) for q in query_list)) - ) - searchform = SearchForm(initial={'q':query}) + ) + searchform = SearchForm(initial={'q': query}) else: searchform = SearchForm() - - return render(request,'list_standards.html', - {'standards':standards, - 'active':'nav-racing', - 'searchform':searchform, - 'rower':r, + return render(request, 'list_standards.html', + {'standards': standards, + 'active': 'nav-racing', + 'searchform': searchform, + 'rower': r, }) - # for ajax calls -def course_map_view(request,id=0): +def course_map_view(request, id=0): try: course = GeoCourse.objects.get(id=id) - except GeoCourse.DoesNotExist: # pragma: no cover # pragma: no cover + except GeoCourse.DoesNotExist: # pragma: no cover # pragma: no cover raise Http404("Course doesn't exist") - script,div = course_map(course) + script, div = course_map(course) breadcrumbs = [ { @@ -115,35 +114,33 @@ def course_map_view(request,id=0): 'name': 'Courses' }, { - 'url': reverse(course_view,kwargs={'id':course.id}), + 'url': reverse(course_view, kwargs={'id': course.id}), 'name': course.name }, { - 'url': reverse(course_map_view,kwargs={'id':course.id}), + 'url': reverse(course_map_view, kwargs={'id': course.id}), 'name': 'Map' } ] r = getrower(request.user) - return render(request, 'coursemap.html', { - 'mapdiv':div, - 'course':course, - 'mapscript':script, - 'active':'nav-racing', - 'rower':r, - 'breadcrumbs':breadcrumbs, + 'mapdiv': div, + 'course': course, + 'mapscript': script, + 'active': 'nav-racing', + 'rower': r, + 'breadcrumbs': breadcrumbs, }) - @login_required() -@permission_required('course.delete_course',fn=get_course_by_pk,raise_exception=True) -def course_delete_view(request,id=0): - course = get_object_or_404(GeoCourse,pk=id) +@permission_required('course.delete_course', fn=get_course_by_pk, raise_exception=True) +def course_delete_view(request, id=0): + course = get_object_or_404(GeoCourse, pk=id) r = getrower(request.user) @@ -157,17 +154,18 @@ def course_delete_view(request,id=0): return HttpResponseRedirect(url) + @login_required() -@permission_required('course.change_course',fn=get_course_by_pk,raise_exception=True) -def course_edit_view(request,id=0): - course = get_object_or_404(GeoCourse,pk=id) +@permission_required('course.change_course', fn=get_course_by_pk, raise_exception=True) +def course_edit_view(request, id=0): + course = get_object_or_404(GeoCourse, pk=id) r = getrower(request.user) ps = PlannedSession.objects.filter(course=course) nosessions = len(ps) == 0 - script,div = course_map(course) + script, div = course_map(course) if request.method == 'POST': form = GeoCourseEditForm(request.POST) @@ -193,74 +191,77 @@ def course_edit_view(request,id=0): 'name': 'Courses' }, { - 'url': reverse(course_view,kwargs={'id':course.id}), + 'url': reverse(course_view, kwargs={'id': course.id}), 'name': course.name }, { - 'url': reverse(course_edit_view,kwargs={'id':course.id}), + 'url': reverse(course_edit_view, kwargs={'id': course.id}), 'name': 'Edit' - } - ] + } + ] return render(request, 'course_edit_view.html', { - 'course':course, - 'active':'nav-racing', - 'breadcrumbs':breadcrumbs, - 'mapscript':script, - 'mapdiv':div, - 'nosessions':nosessions, - 'rower':r, - 'form':form, - } + 'course': course, + 'active': 'nav-racing', + 'breadcrumbs': breadcrumbs, + 'mapscript': script, + 'mapdiv': div, + 'nosessions': nosessions, + 'rower': r, + 'form': form, + } ) -#@login_required() -def course_view(request,id=0): +# @login_required() + + +def course_view(request, id=0): try: course = GeoCourse.objects.get(id=id) - except GeoCourse.DoesNotExist: # pragma: no cover + except GeoCourse.DoesNotExist: # pragma: no cover raise Http404("Course doesn't exist") r = getrower(request.user) - script,div = course_map(course) + script, div = course_map(course) # get results records = VirtualRaceResult.objects.filter( course=course, workoutid__isnull=False, - coursecompleted=True).order_by("duration","-distance") + coursecompleted=True).order_by("duration", "-distance") if request.user.is_authenticated: - notsharing = Rower.objects.filter(share_course_results=False).exclude(id=r.id) - else: # pragma: no cover + notsharing = Rower.objects.filter( + share_course_results=False).exclude(id=r.id) + else: # pragma: no cover notsharing = Rower.objects.filter(share_course_results=False) notsharing_ids = [o.user.id for o in notsharing] records = records.exclude(userid__in=notsharing_ids) - if 'onlyme' in request.GET: # pragma: no cover - onlyme = request.GET.get('onlyme',False) + if 'onlyme' in request.GET: # pragma: no cover + onlyme = request.GET.get('onlyme', False) if onlyme == 'true': onlyme = True if onlyme and request.user.is_authenticated: records = records.filter(userid=r.user.id) else: - onlyme=False + onlyme = False - - form = RaceResultFilterForm(records=records,groups=False) - if request.method == 'POST': # pragma: no cover - form = RaceResultFilterForm(request.POST,records=records,groups=False) + form = RaceResultFilterForm(records=records, groups=False) + if request.method == 'POST': # pragma: no cover + form = RaceResultFilterForm( + request.POST, records=records, groups=False) if form.is_valid(): cd = form.cleaned_data try: sex = cd['sex'] except KeyError: - sex = ['female','male','mixed'] + sex = ['female', 'male', 'mixed'] try: boattype = cd['boattype'] @@ -278,12 +279,12 @@ def course_view(request,id=0): try: weightcategory = cd['weightcategory'] except KeyError: - weightcategory = ['hwt','lwt'] + weightcategory = ['hwt', 'lwt'] try: adaptiveclass = cd['adaptiveclass'] except KeyError: - adaptiveclass = ['None','PR1','PR2','PR3','FES'] + adaptiveclass = ['None', 'PR1', 'PR2', 'PR3', 'FES'] records = VirtualRaceResult.objects.filter( course=course, @@ -296,8 +297,7 @@ def course_view(request,id=0): boatclass__in=boatclass, boattype__in=boattype, adaptiveclass__in=adaptiveclass, - ).order_by("duration","-distance") - + ).order_by("duration", "-distance") breadcrumbs = [ { @@ -309,49 +309,50 @@ def course_view(request,id=0): 'name': 'Courses' }, { - 'url': reverse(course_view,kwargs={'id':course.id}), + 'url': reverse(course_view, kwargs={'id': course.id}), 'name': course.name }, - ] + ] return render(request, 'course_view.html', { - 'active':'nav-racing', - 'breadcrumbs':breadcrumbs, - 'course':course, - 'mapscript':script, - 'mapdiv':div, - 'nosessions':False, - 'records':records, - 'rower':r, - 'form':form, - 'onlyme':onlyme, - } + 'active': 'nav-racing', + 'breadcrumbs': breadcrumbs, + 'course': course, + 'mapscript': script, + 'mapdiv': div, + 'nosessions': False, + 'records': records, + 'rower': r, + 'form': form, + 'onlyme': onlyme, + } ) -def standard_view(request,id=0): + +def standard_view(request, id=0): try: collection = StandardCollection.objects.get(id=id) - except StandardCollection.DoesNotExist: # pragma: no cover + except StandardCollection.DoesNotExist: # pragma: no cover raise Http404("Standard Collection does not exist") r = getrower(request.user) - allowed = ["-referencespeed","agemax","agemin","sex","name", - "referencespeed","-agemax","-agemin","-sex","-name"] + allowed = ["-referencespeed", "agemax", "agemin", "sex", "name", + "referencespeed", "-agemax", "-agemin", "-sex", "-name"] orderby = request.GET.get('order_by') if orderby not in allowed: orderby = None - if orderby is not None: # pragma: no cover + if orderby is not None: # pragma: no cover standards = CourseStandard.objects.filter( standardcollection=collection - ).order_by(orderby,"-referencespeed","agemax","agemin","sex","name") + ).order_by(orderby, "-referencespeed", "agemax", "agemin", "sex", "name") else: standards = CourseStandard.objects.filter( standardcollection=collection - ).order_by("-referencespeed","agemax","agemin","sex","name") + ).order_by("-referencespeed", "agemax", "agemin", "sex", "name") breadcrumbs = [ { @@ -363,86 +364,89 @@ def standard_view(request,id=0): 'name': 'Standards' }, { - 'url': reverse(standard_view,kwargs={'id':collection.id}), + 'url': reverse(standard_view, kwargs={'id': collection.id}), 'name': collection.name }, - ] + ] return render(request, 'standard_view.html', { - 'active':'nav-racing', - 'breadcrumbs':breadcrumbs, - 'collection':collection, - 'standards':standards, - 'rower':r, - } + 'active': 'nav-racing', + 'breadcrumbs': breadcrumbs, + 'collection': collection, + 'standards': standards, + 'rower': r, + } ) -@login_required() -@permission_required('racelogo.delete_logo',fn=get_logo_by_pk,raise_exception=True) -def logo_delete_view(request,id=0): # pragma: no cover - logo = get_object_or_404(RaceLogo,pk=id) +@login_required() +@permission_required('racelogo.delete_logo', fn=get_logo_by_pk, raise_exception=True) +def logo_delete_view(request, id=0): # pragma: no cover + logo = get_object_or_404(RaceLogo, pk=id) if logo.user == request.user: logo.delete() - messages.info(request,"Logo Deleted") + messages.info(request, "Logo Deleted") url = reverse('virtualevents_view') return HttpResponseRedirect(url) + @login_required() -@permission_required('virtualevent.change_race',fn=get_virtualevent_by_pk,raise_exception=True) -def virtualevent_setlogo_view(request,id=0,logoid=0): # pragma: no cover - race = get_object_or_404(VirtualRace,pk=id) - logo = get_object_or_404(RaceLogo,pk=logoid) +@permission_required('virtualevent.change_race', fn=get_virtualevent_by_pk, raise_exception=True) +def virtualevent_setlogo_view(request, id=0, logoid=0): # pragma: no cover + race = get_object_or_404(VirtualRace, pk=id) + logo = get_object_or_404(RaceLogo, pk=logoid) otherlogos = race.logos.all() for otherlogo in otherlogos: otherlogo.race.remove(race) - logo.race.add(race) logo.save() url = reverse('virtualevent_view', - kwargs={'id':id}) + kwargs={'id': id}) return HttpResponseRedirect(url) # Image upload to virtual event + + @login_required() -def virtualevent_uploadimage_view(request,id=0): # pragma: no cover +def virtualevent_uploadimage_view(request, id=0): # pragma: no cover is_ajax = request.META.get('HTTP_X_REQUESTED_WITH') == 'XMLHttpRequest' r = getrower(request.user) - race = get_object_or_404(VirtualRace,pk=id) + race = get_object_or_404(VirtualRace, pk=id) - logos = RaceLogo.objects.filter(user=request.user).order_by("-creationdatetime") + logos = RaceLogo.objects.filter( + user=request.user).order_by("-creationdatetime") breadcrumbs = [ { 'url': reverse('virtualevents_view'), 'name': 'Challenges' - }, + }, { - 'url': reverse('virtualevent_view',kwargs={'id':id}), + 'url': reverse('virtualevent_view', kwargs={'id': id}), 'name': race.name - }, + }, { 'url': reverse(virtualevent_uploadimage_view, - kwargs={'id':id}), + kwargs={'id': id}), 'name': 'Add Image' - } - ] + } + ] if request.method == 'POST': if len(logos) >= 6: - messages.error(request,"You cannot have more than 6 logos") + messages.error(request, "You cannot have more than 6 logos") url = reverse(virtualevent_imageupload_view, - kwargs={'id':id}) + kwargs={'id': id}) return HttpResponseRedirect(url) @@ -456,13 +460,13 @@ def virtualevent_uploadimage_view(request,id=0): # pragma: no cover width, height = Image.open(path_and_filename).size except: message = "Not a valid image" - messages.error(request,message) + messages.error(request, message) os.remove(path_and_filename) url = reverse(virtualevent_image_view, - kwargs={'id':id}) + kwargs={'id': id}) if is_ajax: - return JSONResponse({'result':0, 'url':0}) + return JSONResponse({'result': 0, 'url': 0}) else: return HttpResponseRedirect(url) @@ -470,62 +474,59 @@ def virtualevent_uploadimage_view(request,id=0): # pragma: no cover for logo in otherlogos: logo.race.remove(race) - logo = RaceLogo(user = request.user, + logo = RaceLogo(user=request.user, creationdatetime=timezone.now(), - filename = path_and_filename, + filename=path_and_filename, width=width, height=height) logo.save() logo.race.add(race) logo.save() - url = reverse('virtualevent_view', - kwargs={'id':id}) + kwargs={'id': id}) if is_ajax: - return JSONResponse({'result':1, 'url':url}) + return JSONResponse({'result': 1, 'url': url}) else: return HttpResponseRedirect(url) else: - messages.error(request,"Something went wrong - no file attached") + messages.error( + request, "Something went wrong - no file attached") url = reverse(virtualevent_uploadimage_view, - kwargs = {'id':id}) + kwargs={'id': id}) if is_ajax: - return JSONResponse({'result':0,'url':1}) + return JSONResponse({'result': 0, 'url': 1}) else: return HttpResponseRedirect(url) else: form = ImageForm() - if is_ajax: - return {'result':0} + return {'result': 0} - - return render(request,'logo_form.html', - {'form':form, - 'rower':r, - 'logos':logos, - 'active':'nav-racing', - 'breadcrumbs':breadcrumbs, - 'race':race, - }) + return render(request, 'logo_form.html', + {'form': form, + 'rower': r, + 'logos': logos, + 'active': 'nav-racing', + 'breadcrumbs': breadcrumbs, + 'race': race, + }) @login_required() -@permission_required('course.change_course',fn=get_course_by_pk,raise_exception=True) -def course_upload_replace_view(request,id=0): +@permission_required('course.change_course', fn=get_course_by_pk, raise_exception=True) +def course_upload_replace_view(request, id=0): is_ajax = request.META.get('HTTP_X_REQUESTED_WITH') == 'XMLHttpRequest' r = getrower(request.user) - course = get_object_or_404(GeoCourse,pk=id) - + course = get_object_or_404(GeoCourse, pk=id) if request.method == 'POST': - form = CourseForm(request.POST,request.FILES) + form = CourseForm(request.POST, request.FILES) if form.is_valid(): f = form.cleaned_data['file'] @@ -536,81 +537,83 @@ def course_upload_replace_view(request,id=0): cs = courses.kmltocourse(path_and_filename) os.remove(path_and_filename) - if cs and len(cs) > 1: # pragma: no cover - messages.info(request,'File contained multiple courses. We use the first one.') + if cs and len(cs) > 1: # pragma: no cover + messages.info( + request, 'File contained multiple courses. We use the first one.') if cs: course = cs[0] cname = course['name'] cnotes = notes+'\n\n'+course['description'] polygons = course['polygons'] - course = courses.createcourse(r,cname,polygons,notes=cnotes) - if course.country == 'unknown': # pragma: no cover + course = courses.createcourse( + r, cname, polygons, notes=cnotes) + if course.country == 'unknown': # pragma: no cover course.country = country course.save() url = reverse(course_update_confirm, - kwargs = { - 'newid':course.id, - 'id':id, + kwargs={ + 'newid': course.id, + 'id': id, } - ) - if is_ajax: # pragma: no cover - return JSONResponse({'result':1,'url':url}) + ) + if is_ajax: # pragma: no cover + return JSONResponse({'result': 1, 'url': url}) else: return HttpResponseRedirect(url) - else: # pragma: no cover - messages.error(request,"File does not contain a course") - else: # pragma: no cover - messages.error(request,"No file attached") - else: # pragma: no cover - messages.error(request,"Form is not valid") + else: # pragma: no cover + messages.error(request, "File does not contain a course") + else: # pragma: no cover + messages.error(request, "No file attached") + else: # pragma: no cover + messages.error(request, "Form is not valid") else: form = CourseForm() form.fields['name'].widget = forms.HiddenInput() if not is_ajax: - return render(request,'course_form_update.html', - {'form':form, - 'course':course, - 'active':'nav-racing', + return render(request, 'course_form_update.html', + {'form': form, + 'course': course, + 'active': 'nav-racing', }) - else: # pragma: no cover - return {'result':0} + else: # pragma: no cover + return {'result': 0} @login_required() -@permission_required('course.change_course',fn=get_course_by_pk,raise_exception=True) -def course_update_confirm(request,id=0,newid=0): - course = get_object_or_404(GeoCourse,pk=id) - course2 = get_object_or_404(GeoCourse,pk=newid) +@permission_required('course.change_course', fn=get_course_by_pk, raise_exception=True) +def course_update_confirm(request, id=0, newid=0): + course = get_object_or_404(GeoCourse, pk=id) + course2 = get_object_or_404(GeoCourse, pk=newid) r = getrower(request.user) if request.method == 'POST': form = CourseConfirmForm(request.POST) if form.is_valid(): doupdate = form.cleaned_data['doupdate'] if doupdate: - res = courses.replacecourse(course,course2) - messages.info(request,'All challenges with this course are updated') + res = courses.replacecourse(course, course2) + messages.info( + request, 'All challenges with this course are updated') url = reverse(course_view, - kwargs = { - 'id':course2.id, - }) + kwargs={ + 'id': course2.id, + }) return HttpResponseRedirect(url) - else: # pragma: no cover + else: # pragma: no cover course2.delete() url = reverse(course_view, - kwargs = { - 'id':course.id, - }) + kwargs={ + 'id': course.id, + }) return HttpResponseRedirect(url) else: form = CourseConfirmForm() # GET call or invalid form script, div = course_map(course2) - breadcrumbs = [ { 'url': reverse('virtualevents_view'), @@ -621,24 +624,24 @@ def course_update_confirm(request,id=0,newid=0): 'name': 'Courses' }, { - 'url': reverse(course_view,kwargs={'id':course.id}), + 'url': reverse(course_view, kwargs={'id': course.id}), 'name': course.name }, { - 'url': reverse(course_upload_replace_view,kwargs={'id':course.id}), + 'url': reverse(course_upload_replace_view, kwargs={'id': course.id}), 'name': 'Replace Markers' - } - ] + } + ] return render(request, 'course_replace_confirm.html', - {'course':course, - 'form':form, - 'active':'nav-racing', - 'breadcrumbs':breadcrumbs, - 'rower':r, - 'mapdiv':div, - 'mapscript':script, + {'course': course, + 'form': form, + 'active': 'nav-racing', + 'breadcrumbs': breadcrumbs, + 'rower': r, + 'mapdiv': div, + 'mapscript': script, }) @@ -650,8 +653,7 @@ def course_upload_view(request): r = getrower(request.user) if request.method == 'POST': - form = CourseForm(request.POST,request.FILES) - + form = CourseForm(request.POST, request.FILES) if form.is_valid(): f = form.cleaned_data['file'] @@ -660,7 +662,7 @@ def course_upload_view(request): country = form.cleaned_data['country'] if f is not None: - filename,path_and_filename = handle_uploaded_file(f) + filename, path_and_filename = handle_uploaded_file(f) cs = courses.kmltocourse(path_and_filename) @@ -669,56 +671,60 @@ def course_upload_view(request): cnotes = notes+'\n\n'+course['description'] polygons = course['polygons'] - course = courses.createcourse(r,cname,polygons,notes=cnotes) + course = courses.createcourse( + r, cname, polygons, notes=cnotes) - if course.country == 'unknown': # pragma: no cover + if course.country == 'unknown': # pragma: no cover course.country = country course.save() os.remove(path_and_filename) url = reverse(courses_view) - if is_ajax: # pragma: no cover - return JSONResponse({'result':1,'url':url}) + if is_ajax: # pragma: no cover + return JSONResponse({'result': 1, 'url': url}) else: return HttpResponseRedirect(url) - else: # pragma: no cover - messages.error(request,'Something went wrong - no file attached') + else: # pragma: no cover + messages.error( + request, 'Something went wrong - no file attached') url = reverse(course_upload_view) if is_ajax: - return JSONResponse({'result':0,'url':0}) + return JSONResponse({'result': 0, 'url': 0}) else: return HttpResponseRedirect(url) - else: # pragma: no cover - messages.error(request,'Form is not valid') - return render(request,'course_form.html', - {'form':form, + else: # pragma: no cover + messages.error(request, 'Form is not valid') + return render(request, 'course_form.html', + {'form': form, }) else: if not is_ajax: form = CourseForm() - return render(request,'course_form.html', - {'form':form, - 'active':'nav-racing', + return render(request, 'course_form.html', + {'form': form, + 'active': 'nav-racing', }) - else: # pragma: no cover - return {'result':0} + else: # pragma: no cover + return {'result': 0} # Standards deactivate + + @login_required() -def standard_deactivate_view(request,id=0): +def standard_deactivate_view(request, id=0): is_ajax = request.META.get('HTTP_X_REQUESTED_WITH') == 'XMLHttpRequest' r = getrower(request.user) try: collection = StandardCollection.objects.get(id=id) - except StandardCollection.DoesNotExist: # pragma: no cover + except StandardCollection.DoesNotExist: # pragma: no cover raise Http404("Does not exist") - if request.user != collection.manager: # pragma: no cover + if request.user != collection.manager: # pragma: no cover raise PermissionDenied("You cannot change this set of time standards") collection.active = False @@ -728,17 +734,18 @@ def standard_deactivate_view(request,id=0): return HttpResponseRedirect(url) -def standards_download_view(request,id=0): + +def standards_download_view(request, id=0): try: collection = StandardCollection.objects.get(id=id) - except StandardCollection.DoesNotExist: # pragma: no cover + except StandardCollection.DoesNotExist: # pragma: no cover raise Http404("Does not exist") filename = 'Standard Times {name} {id} {date}.csv'.format( id=id, name=collection.name, date=timezone.now().strftime("%Y-%m-%d %H:%M:%S %Z") - ) + ) standards = CourseStandard.objects.filter(standardcollection=collection) df = pd.DataFrame.from_records(standards.values()) @@ -753,84 +760,83 @@ def standards_download_view(request,id=0): # Standards upload @login_required() -def standards_upload_view(request,id=0): +def standards_upload_view(request, id=0): is_ajax = request.META.get('HTTP_X_REQUESTED_WITH') == 'XMLHttpRequest' r = getrower(request.user) - if id != 0: # pragma: no cover + if id != 0: # pragma: no cover collection = StandardCollection.objects.get(id=id) if request.user != collection.manager: - raise PermissionDenied("You cannot change this set of time standards") - + raise PermissionDenied( + "You cannot change this set of time standards") if request.method == 'POST': - form = StandardsForm(request.POST,request.FILES) + form = StandardsForm(request.POST, request.FILES) if form.is_valid(): f = form.cleaned_data['file'] name = form.cleaned_data['name'] notes = form.cleaned_data['notes'] if f is not None: - filename,path_and_filename = handle_uploaded_file(f) - - id = save_scoring(name,request.user,path_and_filename,notes=notes,id=id) + filename, path_and_filename = handle_uploaded_file(f) + id = save_scoring(name, request.user, + path_and_filename, notes=notes, id=id) os.remove(path_and_filename) - - - if id==0: # pragma: no cover + if id == 0: # pragma: no cover url = reverse(standards_view) else: - url = reverse(standard_view,kwargs={'id':id}) + url = reverse(standard_view, kwargs={'id': id}) - if is_ajax: # pragma: no cover - return JSONResponse({'result':1,'url':url}) + if is_ajax: # pragma: no cover + return JSONResponse({'result': 1, 'url': url}) return HttpResponseRedirect(url) - else: # pragma: no cover - messages.error(request,'Something went wrong - no file attached') + else: # pragma: no cover + messages.error( + request, 'Something went wrong - no file attached') url = reverse(standards_upload_view) if is_ajax: - return JSONResponse({'result':0,'url':0}) + return JSONResponse({'result': 0, 'url': 0}) return HttpResponseRedirect(url) - else: # pragma: no cover - messages.error(request,'Form is not valid') - return render(request,'standard_form.html', - {'form':form, - 'active':'nav-racing', - 'id':id, + else: # pragma: no cover + messages.error(request, 'Form is not valid') + return render(request, 'standard_form.html', + {'form': form, + 'active': 'nav-racing', + 'id': id, }) else: if not is_ajax: form = StandardsForm() - if id != 0: # pragma: no cover + if id != 0: # pragma: no cover collection = StandardCollection.objects.get(id=id) form = StandardsForm(initial={ - 'name':collection.name, + 'name': collection.name, 'notes': collection.notes, }) - return render(request,'standard_form.html', - {'form':form, - 'active':'nav-racing', - 'id':id, + return render(request, 'standard_form.html', + {'form': form, + 'active': 'nav-racing', + 'id': id, }) - return {'result':0} # pragma: no cover + return {'result': 0} # pragma: no cover def virtualevents_view(request): is_ajax = request.META.get('HTTP_X_REQUESTED_WITH') == 'XMLHttpRequest' g = GeoIP2() - ip = request.META.get('HTTP_X_REAL_IP','1.1.1.1') + ip = request.META.get('HTTP_X_REAL_IP', '1.1.1.1') try: lat_lon = g.lat_lon(ip) city = g.city(ip) - except: # pragma: no cover + except: # pragma: no cover lat_lon = None city = { 'city': '', @@ -847,10 +853,7 @@ def virtualevents_view(request): evaluation_closure__gte=timezone.now()-datetime.timedelta(days=3), ) - - - - races = (races1 | races2).order_by("startdate","start_time") + races = (races1 | races2).order_by("startdate", "start_time") if len(races) == 0: races = VirtualRace.objects.all().order_by("-startdate") @@ -868,8 +871,9 @@ def virtualevents_view(request): country = cd['country'] regattatype = cd['regattatype'] if country == 'All': - countries = VirtualRace.objects.order_by('country').values_list('country').distinct() - else: # pragma: no cover + countries = VirtualRace.objects.order_by( + 'country').values_list('country').distinct() + else: # pragma: no cover countries = [country, 'Indoor'] @@ -882,97 +886,95 @@ def virtualevents_view(request): startdate__lte=timezone.now(), evaluation_closure__gte=timezone.now(), country__in=countries - ) - - - races = (races1 | races2).order_by("startdate","start_time") + ) + races = (races1 | races2).order_by("startdate", "start_time") elif regattatype == 'previous': races = VirtualRace.objects.filter( evaluation_closure__lt=timezone.now(), country__in=countries - ).order_by("-startdate","-start_time") + ).order_by("-startdate", "-start_time") elif regattatype == 'ongoing': races = VirtualRace.objects.filter( startdate__lte=timezone.now(), evaluation_closure__gte=timezone.now(), country__in=countries - ).order_by("startdate","start_time") - elif regattatype == 'my': # pragma: no cover + ).order_by("startdate", "start_time") + elif regattatype == 'my': # pragma: no cover mysessions = get_my_session_ids(r) races = VirtualRace.objects.filter( id__in=mysessions, country__in=countries - ).order_by("startdate","start_time") - elif regattatype == 'all': # pragma: no cover + ).order_by("startdate", "start_time") + elif regattatype == 'all': # pragma: no cover races = VirtualRace.objects.filter( country__in=countries - ).order_by("startdate","start_time") + ).order_by("startdate", "start_time") else: form = VirtualRaceSelectForm() nearby = request.GET.get('nearby') - if nearby and lat_lon is not None: # pragma: no cover - races = getnearestraces(lat_lon,races) + if nearby and lat_lon is not None: # pragma: no cover + races = getnearestraces(lat_lon, races) - - if is_ajax: # pragma: no cover - return render(request,'racelist.html', - { 'races':races, - 'rower':r, - 'location': lat_lon, - 'city':city['city'], - 'country_name': city['country_name'], - 'time_zone':city['time_zone'], - }) + if is_ajax: # pragma: no cover + return render(request, 'racelist.html', + {'races': races, + 'rower': r, + 'location': lat_lon, + 'city': city['city'], + 'country_name': city['country_name'], + 'time_zone': city['time_zone'], + }) breadcrumbs = [ { - 'url':reverse('virtualevents_view'), + 'url': reverse('virtualevents_view'), 'name': 'Challenges' - }, - ] + }, + ] + + return render(request, 'virtualevents.html', + {'races': races, + 'form': form, + 'breadcrumbs': breadcrumbs, + 'active': 'nav-racing', + 'rower': r, + 'location': lat_lon, + 'city': city['city'], + 'country_name': city['country_name'], + 'time_zone': city['time_zone'], + } + ) - return render(request,'virtualevents.html', - { 'races':races, - 'form':form, - 'breadcrumbs':breadcrumbs, - 'active':'nav-racing', - 'rower':r, - 'location': lat_lon, - 'city':city['city'], - 'country_name': city['country_name'], - 'time_zone':city['time_zone'], - } - ) @login_required() -@permission_required('virtualevent.change_race',fn=get_virtualevent_by_pk,raise_exception=True) -def virtualevent_disqualify_view(request,id=0,recordid=0): +@permission_required('virtualevent.change_race', fn=get_virtualevent_by_pk, raise_exception=True) +def virtualevent_disqualify_view(request, id=0, recordid=0): r = getrower(request.user) - race = get_object_or_404(VirtualRace,pk=id) + race = get_object_or_404(VirtualRace, pk=id) raceid = race.id - if race.sessiontype == 'race': recordobj = VirtualRaceResult - else: # pragma: no cover + else: # pragma: no cover recordobj = IndoorVirtualRaceResult # datum moet voor race evaluation date zijn (ook in template controleren) try: record = recordobj.objects.get(id=recordid) - except recordobj.DoesNotExist: # pragma: no cover - messages.error(request,"We couldn't find the record") + except recordobj.DoesNotExist: # pragma: no cover + messages.error(request, "We couldn't find the record") - if timezone.now() > race.evaluation_closure+datetime.timedelta(hours=1): # pragma: no cover - messages.error(request,"The evaluation is already closed and the results are official") - url = reverse('virtualevent_view',kwargs={'id':raceid}) + if timezone.now() > race.evaluation_closure+datetime.timedelta(hours=1): # pragma: no cover + messages.error( + request, "The evaluation is already closed and the results are official") + url = reverse('virtualevent_view', kwargs={'id': raceid}) return HttpResponseRedirect(url) @@ -986,18 +988,19 @@ def virtualevent_disqualify_view(request,id=0,recordid=0): r = Rower.objects.get(id=record.userid) name = record.username - job = myqueue(queue,handle_send_disqualification_email, + job = myqueue(queue, handle_send_disqualification_email, r.user.email, name, - disqualifier,message,race.name) + disqualifier, message, race.name) - messages.info(request,"We have invalidated the result for: "+str(record)) + messages.info( + request, "We have invalidated the result for: "+str(record)) record.coursecompleted = False record.startsecond = 0 record.endsecond = 0 record.save() - url = reverse('virtualevent_view',kwargs={'id':raceid}) + url = reverse('virtualevent_view', kwargs={'id': raceid}) return HttpResponseRedirect(url) @@ -1006,7 +1009,7 @@ def virtualevent_disqualify_view(request,id=0,recordid=0): try: workout = Workout.objects.get(id=record.workoutid) - except Workout.DoesNotExist: # pragma: no cover + except Workout.DoesNotExist: # pragma: no cover workout = None script = '' @@ -1015,10 +1018,11 @@ def virtualevent_disqualify_view(request,id=0,recordid=0): mapscript = '' mapdiv = '' if workout is not None: - g = GraphImage.objects.filter(workout=workout).order_by("-creationdatetime") - for i in g: # pragma: no cover + g = GraphImage.objects.filter( + workout=workout).order_by("-creationdatetime") + for i in g: # pragma: no cover try: - width,height = Image.open(i.filename).size + width, height = Image.open(i.filename).size i.width = width i.height = height i.save() @@ -1033,80 +1037,80 @@ def virtualevent_disqualify_view(request,id=0,recordid=0): if rowdata != 0: try: latitude = rowdata.df[' latitude'] - if not latitude.std(): # pragma: no cover + if not latitude.std(): # pragma: no cover hascoordinates = 0 - except (KeyError, AttributeError): # pragma: no cover + except (KeyError, AttributeError): # pragma: no cover hascoordinates = 0 - else: # pragma: no cover + else: # pragma: no cover hascoordinates = 0 if hascoordinates: mapscript, mapdiv = leaflet_chart(rowdata.df[' latitude'], - rowdata.df[' longitude'], - workout.name) - else: # pragma: no cover + rowdata.df[' longitude'], + workout.name) + else: # pragma: no cover mapscript = "" mapdiv = "" breadcrumbs = [ { - 'url':reverse('virtualevents_view'), + 'url': reverse('virtualevents_view'), 'name': 'Challenges' - }, + }, { - 'url':reverse('virtualevent_view', - kwargs={'id':race.id}), + 'url': reverse('virtualevent_view', + kwargs={'id': race.id}), 'name': race.name - }, + }, { - 'url':reverse(virtualevent_disqualify_view, - kwargs={'id':id, - 'recordid':recordid}), + 'url': reverse(virtualevent_disqualify_view, + kwargs={'id': id, + 'recordid': recordid}), 'name': 'Disqualify Entry' - }, - ] + }, + ] buttons = [] if not request.user.is_anonymous: - if race_can_register(r,race): # pragma: no cover + if race_can_register(r, race): # pragma: no cover buttons += ['registerbutton'] - if race_can_adddiscipline(r,race): + if race_can_adddiscipline(r, race): buttons += ['adddisciplinebutton'] - if race_can_submit(r,race): + if race_can_submit(r, race): buttons += ['submitbutton'] - if race_can_resubmit(r,race): # pragma: no cover + if race_can_resubmit(r, race): # pragma: no cover buttons += ['resubmitbutton'] - if race_can_withdraw(r,race): # pragma: no cover + if race_can_withdraw(r, race): # pragma: no cover buttons += ['withdrawbutton'] - if race_can_edit(r,race): + if race_can_edit(r, race): buttons += ['editbutton'] - return render(request,"disqualification_view.html", - {'workout':workout, - 'active':'nav-racing', - 'graphs':g, - 'buttons':buttons, - 'interactiveplot':script, - 'the_div':div, - 'mapscript':mapscript, - 'mapdiv':mapdiv, - 'form':form, - 'race':race, - 'record':record, + return render(request, "disqualification_view.html", + {'workout': workout, + 'active': 'nav-racing', + 'graphs': g, + 'buttons': buttons, + 'interactiveplot': script, + 'the_div': div, + 'mapscript': mapscript, + 'mapdiv': mapdiv, + 'form': form, + 'race': race, + 'record': record, }) + @login_required() -def virtualevent_withdrawresult_view(request,id=0,recordid=0): +def virtualevent_withdrawresult_view(request, id=0, recordid=0): r = getrower(request.user) - race = get_object_or_404(VirtualRace,pk=id) - + race = get_object_or_404(VirtualRace, pk=id) if race.sessiontype == 'race': recordobj = VirtualRaceResult @@ -1117,15 +1121,16 @@ def virtualevent_withdrawresult_view(request,id=0,recordid=0): try: record = recordobj.objects.get(id=recordid) - except recordobj.DoesNotExist: # pragma: no cover - messages.error(request,"We couldn't find the record") + except recordobj.DoesNotExist: # pragma: no cover + messages.error(request, "We couldn't find the record") - if r.id != record.userid: # pragma: no cover + if r.id != record.userid: # pragma: no cover raise PermissionDenied("You are not the owner of this result") - if timezone.now() > race.evaluation_closure+datetime.timedelta(hours=1): # pragma: no cover - messages.error(request,"The evaluation is already closed and the results are official") - url = reverse('virtualevent_view',kwargs={'id':raceid}) + if timezone.now() > race.evaluation_closure+datetime.timedelta(hours=1): # pragma: no cover + messages.error( + request, "The evaluation is already closed and the results are official") + url = reverse('virtualevent_view', kwargs={'id': raceid}) return HttpResponseRedirect(url) @@ -1139,30 +1144,32 @@ def virtualevent_withdrawresult_view(request,id=0,recordid=0): r = Rower.objects.get(id=record.userid) name = record.username - job = myqueue(queue,handle_send_withdraw_email, + job = myqueue(queue, handle_send_withdraw_email, r.user.email, name, - disqualifier,message,race.name) + disqualifier, message, race.name) - messages.info(request,"We have invalidated the result for: "+str(record)) + messages.info( + request, "We have invalidated the result for: "+str(record)) record.coursecompleted = False record.startsecond = 0 record.endsecond = 0 record.save() - url = reverse('virtualevent_view',kwargs={'id':id}) + url = reverse('virtualevent_view', kwargs={'id': id}) return HttpResponseRedirect(url) else: form = DisqualificationForm(request.POST) - try: # pragma: no cover + try: # pragma: no cover workout = Workout.objects.get(id=record.workoutid) - g = GraphImage.objects.filter(workout=workout).order_by("-creationdatetime") + g = GraphImage.objects.filter( + workout=workout).order_by("-creationdatetime") for i in g: try: - width,height = Image.open(i.filename).size + width, height = Image.open(i.filename).size i.width = width i.height = height i.save() @@ -1191,7 +1198,7 @@ def virtualevent_withdrawresult_view(request,id=0,recordid=0): else: mapscript = "" mapdiv = "" - except Workout.DoesNotExist: # pragma: no cover + except Workout.DoesNotExist: # pragma: no cover mapscript = "" mapdiv = "" workout = None @@ -1199,79 +1206,76 @@ def virtualevent_withdrawresult_view(request,id=0,recordid=0): div = "" g = None - - breadcrumbs = [ { - 'url':reverse('virtualevents_view'), + 'url': reverse('virtualevents_view'), 'name': 'Challenges' - }, + }, { - 'url':reverse('virtualevent_view', - kwargs={'id':race.id}), + 'url': reverse('virtualevent_view', + kwargs={'id': race.id}), 'name': race.name - }, + }, { - 'url':reverse(virtualevent_disqualify_view, - kwargs={'id':id, - 'recordid':recordid}), + 'url': reverse(virtualevent_disqualify_view, + kwargs={'id': id, + 'recordid': recordid}), 'name': 'Disqualify Entry' - }, - ] + }, + ] buttons = [] if not request.user.is_anonymous: - if race_can_register(r,race): # pragma: no cover + if race_can_register(r, race): # pragma: no cover buttons += ['registerbutton'] - if race_can_adddiscipline(r,race): + if race_can_adddiscipline(r, race): buttons += ['adddisciplinebutton'] - if race_can_submit(r,race): + if race_can_submit(r, race): buttons += ['submitbutton'] - if race_can_resubmit(r,race): # pragma: no cover + if race_can_resubmit(r, race): # pragma: no cover buttons += ['resubmitbutton'] - if race_can_withdraw(r,race): # pragma: no cover + if race_can_withdraw(r, race): # pragma: no cover buttons += ['withdrawbutton'] - if race_can_edit(r,race): + if race_can_edit(r, race): buttons += ['editbutton'] - return render(request,"withdraw_view.html", - {'workout':workout, - 'active':'nav-racing', - 'graphs':g, - 'buttons':buttons, - 'interactiveplot':script, - 'the_div':div, - 'mapscript':mapscript, - 'mapdiv':mapdiv, - 'form':form, - 'race':race, - 'record':record, + return render(request, "withdraw_view.html", + {'workout': workout, + 'active': 'nav-racing', + 'graphs': g, + 'buttons': buttons, + 'interactiveplot': script, + 'the_div': div, + 'mapscript': mapscript, + 'mapdiv': mapdiv, + 'form': form, + 'race': race, + 'record': record, }) -def virtualevent_view(request,id=0): +def virtualevent_view(request, id=0): results = [] if not request.user.is_anonymous: r = getrower(request.user) - else: # pragma: no cover + else: # pragma: no cover r = None - try: race = VirtualRace.objects.get(id=id) - except VirtualRace.DoesNotExist: # pragma: no cover + except VirtualRace.DoesNotExist: # pragma: no cover raise Http404("Virtual Challenge does not exist") if race.sessiontype == 'race': - script,div = course_map(race.course) + script, div = course_map(race.course) resultobj = VirtualRaceResult else: script = '' @@ -1284,7 +1288,7 @@ def virtualevent_view(request,id=0): # to-do - add DNS dns = [] - if timezone.now() > race.evaluation_closure: # pragma: no cover + if timezone.now() > race.evaluation_closure: # pragma: no cover dns = resultobj.objects.filter( race=race, workoutid__isnull=True, @@ -1296,34 +1300,33 @@ def virtualevent_view(request,id=0): coursecompleted=False, ) - if not request.user.is_anonymous: - if race_can_register(r,race): + if race_can_register(r, race): buttons += ['registerbutton'] - if race_can_adddiscipline(r,race): + if race_can_adddiscipline(r, race): buttons += ['adddisciplinebutton'] - if race_can_submit(r,race): + if race_can_submit(r, race): buttons += ['submitbutton'] - if race_can_resubmit(r,race): # pragma: no cover + if race_can_resubmit(r, race): # pragma: no cover buttons += ['resubmitbutton'] - if race_can_withdraw(r,race): + if race_can_withdraw(r, race): buttons += ['withdrawbutton'] - if race_can_edit(r,race): + if race_can_edit(r, race): buttons += ['editbutton'] - if request.method == 'POST': # pragma: no cover - form = RaceResultFilterForm(request.POST,records=records) + if request.method == 'POST': # pragma: no cover + form = RaceResultFilterForm(request.POST, records=records) if form.is_valid(): cd = form.cleaned_data try: sex = cd['sex'] except KeyError: - sex = ['female','male','mixed'] + sex = ['female', 'male', 'mixed'] try: boattype = cd['boattype'] @@ -1344,12 +1347,12 @@ def virtualevent_view(request,id=0): try: weightcategory = cd['weightcategory'] except KeyError: - weightcategory = ['hwt','lwt'] + weightcategory = ['hwt', 'lwt'] try: adaptiveclass = cd['adaptiveclass'] except KeyError: - adaptiveclass = ['None','PR1','PR2','PR3','FES'] + adaptiveclass = ['None', 'PR1', 'PR2', 'PR3', 'FES'] try: entrycategory = cd['entrycategory'] @@ -1380,7 +1383,7 @@ def virtualevent_view(request,id=0): age__gte=age_min, age__lte=age_max, coursecompleted=True, - ).order_by("duration","-distance") + ).order_by("duration", "-distance") if entrycategory is not None: results = results.filter(entrycategory__in=entrycategory) @@ -1403,9 +1406,7 @@ def virtualevent_view(request,id=0): race=race, workoutid__isnull=False, coursecompleted=True, - ).order_by("duration","-distance") - - + ).order_by("duration", "-distance") if results: form = RaceResultFilterForm(records=records) @@ -1414,23 +1415,24 @@ def virtualevent_view(request,id=0): breadcrumbs = [ { - 'url':reverse('virtualevents_view'), + 'url': reverse('virtualevents_view'), 'name': 'Challenges' - }, + }, { - 'url':reverse('virtualevent_view', - kwargs={'id':race.id} - ), + 'url': reverse('virtualevent_view', + kwargs={'id': race.id} + ), 'name': race.name - } - ] + } + ] - allowed = ["duration","distance","-distance","points","-points","-duration","-distance"] + allowed = ["duration", "distance", "-distance", + "points", "-points", "-duration", "-distance"] orderby = request.GET.get('order_by') if orderby not in allowed: orderby = None - if orderby is not None: # pragma: no cover + if orderby is not None: # pragma: no cover try: results = results.order_by(orderby) except AttributeError: @@ -1438,30 +1440,32 @@ def virtualevent_view(request,id=0): racelogos = race.logos.all() - if racelogos: # pragma: no cover + if racelogos: # pragma: no cover racelogo = racelogos[0] else: racelogo = None - comments = PlannedSessionComment.objects.filter(plannedsession=race).order_by("created") + comments = PlannedSessionComment.objects.filter( + plannedsession=race).order_by("created") - return render(request,'virtualevent.html', + return render(request, 'virtualevent.html', { - 'coursescript':script, - 'coursediv':div, - 'breadcrumbs':breadcrumbs, - 'race':race, - 'rower':r, - 'results':results, - 'buttons':buttons, - 'dns':dns, - 'dnf':dnf, - 'records':records, - 'racelogo':racelogo, - 'form':form, - 'active':'nav-racing', - 'comments':comments - }) + 'coursescript': script, + 'coursediv': div, + 'breadcrumbs': breadcrumbs, + 'race': race, + 'rower': r, + 'results': results, + 'buttons': buttons, + 'dns': dns, + 'dnf': dnf, + 'records': records, + 'racelogo': racelogo, + 'form': form, + 'active': 'nav-racing', + 'comments': comments + }) + def do_encode(x): try: @@ -1469,19 +1473,20 @@ def do_encode(x): except: return x + @login_required() -@permission_required('virtualevent.change_race',fn=get_virtualevent_by_pk,raise_exception=True) -def virtualevent_results_download_view(request,id=0): +@permission_required('virtualevent.change_race', fn=get_virtualevent_by_pk, raise_exception=True) +def virtualevent_results_download_view(request, id=0): r = getrower(request.user) try: race = VirtualRace.objects.get(id=id) - except VirtualRace.DoesNotExist: # pragma: no cover + except VirtualRace.DoesNotExist: # pragma: no cover raise Http404("Virtual Challenge does not exist") if race.sessiontype == 'race': resultobj = VirtualRaceResult - else: # pragma: no cover + else: # pragma: no cover resultobj = IndoorVirtualRaceResult records = resultobj.objects.filter(race=race) @@ -1490,10 +1495,10 @@ def virtualevent_results_download_view(request,id=0): id=id, name=race.name, date=timezone.now().strftime("%Y-%m-%d %H:%M:%S %Z") - ) + ) df = pd.DataFrame.from_records(records.values()) - df['workoutid'] = df['workoutid'].apply(lambda x:do_encode(x)) + df['workoutid'] = df['workoutid'].apply(lambda x: do_encode(x)) response = HttpResponse(df.to_csv()) @@ -1503,23 +1508,22 @@ def virtualevent_results_download_view(request,id=0): return response -def virtualevent_ranking_view(request,id=0): +def virtualevent_ranking_view(request, id=0): results = [] if not request.user.is_anonymous: r = getrower(request.user) - else: # pragma: no cover + else: # pragma: no cover r = None - try: race = VirtualRace.objects.get(id=id) - except VirtualRace.DoesNotExist: # pragma: no cover + except VirtualRace.DoesNotExist: # pragma: no cover raise Http404("Virtual Challenge does not exist") if race.sessiontype == 'race': - script,div = course_map(race.course) + script, div = course_map(race.course) resultobj = VirtualRaceResult else: script = '' @@ -1528,12 +1532,11 @@ def virtualevent_ranking_view(request,id=0): records = resultobj.objects.filter(race=race) - buttons = [] # to-do - add DNS dns = [] - if timezone.now() > race.evaluation_closure: # pragma: no cover + if timezone.now() > race.evaluation_closure: # pragma: no cover dns = resultobj.objects.filter( race=race, workoutid__isnull=True, @@ -1545,34 +1548,33 @@ def virtualevent_ranking_view(request,id=0): coursecompleted=False, ) - if not request.user.is_anonymous: - if race_can_register(r,race): # pragma: no cover + if race_can_register(r, race): # pragma: no cover buttons += ['registerbutton'] - if race_can_adddiscipline(r,race): + if race_can_adddiscipline(r, race): buttons += ['adddisciplinebutton'] - if race_can_submit(r,race): + if race_can_submit(r, race): buttons += ['submitbutton'] - if race_can_resubmit(r,race): # pragma: no cover + if race_can_resubmit(r, race): # pragma: no cover buttons += ['resubmitbutton'] - if race_can_withdraw(r,race): # pragma: no cover + if race_can_withdraw(r, race): # pragma: no cover buttons += ['withdrawbutton'] - if race_can_edit(r,race): + if race_can_edit(r, race): buttons += ['editbutton'] - if request.method == 'POST': # pragma: no cover - form = RaceResultFilterForm(request.POST,records=records) + if request.method == 'POST': # pragma: no cover + form = RaceResultFilterForm(request.POST, records=records) if form.is_valid(): cd = form.cleaned_data try: sex = cd['sex'] except KeyError: - sex = ['female','male','mixed'] + sex = ['female', 'male', 'mixed'] try: boattype = cd['boattype'] @@ -1593,12 +1595,12 @@ def virtualevent_ranking_view(request,id=0): try: weightcategory = cd['weightcategory'] except KeyError: - weightcategory = ['hwt','lwt'] + weightcategory = ['hwt', 'lwt'] try: adaptiveclass = cd['adaptiveclass'] except KeyError: - adaptiveclass = ['None','PR1','PR2','PR3','FES'] + adaptiveclass = ['None', 'PR1', 'PR2', 'PR3', 'FES'] if race.sessiontype == 'race': results = resultobj.objects.filter( @@ -1622,8 +1624,7 @@ def virtualevent_ranking_view(request,id=0): adaptiveclass__in=adaptiveclass, age__gte=age_min, age__lte=age_max - ).order_by("duration","-distance") - + ).order_by("duration", "-distance") # to-do - add DNS dns = [] @@ -1643,120 +1644,118 @@ def virtualevent_ranking_view(request,id=0): race=race, workoutid__isnull=False, coursecompleted=True, - ).order_by("duration","-distance") + ).order_by("duration", "-distance") if results: form = RaceResultFilterForm(records=records) - else: # pragma: no cover + else: # pragma: no cover form = None - - breadcrumbs = [ { - 'url':reverse('virtualevents_view'), + 'url': reverse('virtualevents_view'), 'name': 'Challenges' - }, + }, { - 'url':reverse('virtualevent_view', - kwargs={'id':race.id} - ), + 'url': reverse('virtualevent_view', + kwargs={'id': race.id} + ), 'name': race.name - } - ] + } + ] racelogos = race.logos.all() - if racelogos: # pragma: no cover + if racelogos: # pragma: no cover racelogo = racelogos[0] else: racelogo = None - return render(request,'virtualeventranking.html', + return render(request, 'virtualeventranking.html', { - 'coursescript':script, - 'coursediv':div, - 'breadcrumbs':breadcrumbs, - 'race':race, - 'rower':r, - 'results':results, - 'buttons':buttons, - 'dns':dns, - 'records':records, - 'racelogo':racelogo, - 'form':form, - 'active':'nav-racing', - }) + 'coursescript': script, + 'coursediv': div, + 'breadcrumbs': breadcrumbs, + 'race': race, + 'rower': r, + 'results': results, + 'buttons': buttons, + 'dns': dns, + 'records': records, + 'racelogo': racelogo, + 'form': form, + 'active': 'nav-racing', + }) @login_required() -def virtualevent_withdraw_view(request,id=0,recordid=None): +def virtualevent_withdraw_view(request, id=0, recordid=None): r = getrower(request.user) try: race = VirtualRace.objects.get(id=id) - except VirtualRace.DoesNotExist: # pragma: no cover + except VirtualRace.DoesNotExist: # pragma: no cover raise Http404("Virtual Challenge does not exist") - if race_can_withdraw(r,race): - remove_rower_race(r,race,recordid=recordid) + if race_can_withdraw(r, race): + remove_rower_race(r, race, recordid=recordid) messages.info(request, "You have successfully withdrawn from this race.") - else: # pragma: no cover - messages.error(request,"You cannot withdraw from this race") + else: # pragma: no cover + messages.error(request, "You cannot withdraw from this race") url = reverse('virtualevent_view', - kwargs = { - 'id':race.id - }) + kwargs={ + 'id': race.id + }) return HttpResponseRedirect(url) + @login_required() -def virtualevent_addboat_view(request,id=0): +def virtualevent_addboat_view(request, id=0): r = getrower(request.user) try: race = VirtualRace.objects.get(id=id) - except VirtualRace.DoesNotExist: # pragma: no cover + except VirtualRace.DoesNotExist: # pragma: no cover raise Http404("Virtual Challenge does not exist") if race.sessiontype in ['race']: resultobj = VirtualRaceResult - else: # pragma: no cover + else: # pragma: no cover resultobj = IndoorVirtualRaceResult categories = None - hasinitial,boattype,boatclass,adaptiveclass,weightclass,sex,referencespeed,initialcategory = default_class(r,None,race) + hasinitial, boattype, boatclass, adaptiveclass, weightclass, sex, referencespeed, initialcategory = default_class( + r, None, race) if race.coursestandards is not None: categories = CourseStandard.objects.filter( standardcollection=race.coursestandards).order_by("name") - - if not race_can_adddiscipline(r,race): # pragma: no cover - messages.error(request,"You cannot register for this race") + if not race_can_adddiscipline(r, race): # pragma: no cover + messages.error(request, "You cannot register for this race") url = reverse('virtualevent_view', - kwargs = { - 'id':race.id + kwargs={ + 'id': race.id }) return HttpResponseRedirect(url) records = resultobj.objects.filter( - userid = r.id, - race = race - ) - + userid=r.id, + race=race + ) boattypes = [record.boattype for record in records] boatclasses = [record.boatclass for record in records] - allowedboats = tuple([ type for type in mytypes.boattypes if type[0] not in boattypes] ) - + allowedboats = tuple( + [type for type in mytypes.boattypes if type[0] not in boattypes]) # we're still here if request.method == 'POST': # process form - form = VirtualRaceResultForm(request.POST,categories=categories) + form = VirtualRaceResultForm(request.POST, categories=categories) if form.is_valid(): cd = form.cleaned_data teamname = cd['teamname'] @@ -1777,11 +1776,10 @@ def virtualevent_addboat_view(request,id=0): if sex == 'not specified': sex = 'male' - if mix: # pragma: no cover + if mix: # pragma: no cover sex = 'mixed' - - if boattype in boattypes and boatclass in boatclasses and race.coursestandards is None: # pragma: no cover + if boattype in boattypes and boatclass in boatclasses and race.coursestandards is None: # pragma: no cover # check if different sexes therecords = records.filter( boattype=boattype, @@ -1795,10 +1793,10 @@ def virtualevent_addboat_view(request,id=0): "You have already registered in that boat class/type" ) url = reverse('virtualevent_view', - kwargs = { + kwargs={ 'id': race.id } - ) + ) return HttpResponseRedirect(url) @@ -1811,13 +1809,14 @@ def virtualevent_addboat_view(request,id=0): thegroups = [record.entrycategory for record in records] - if coursestandard in thegroups: # pragma: no cover - messages.error(request,"You have already registered in that group") + if coursestandard in thegroups: # pragma: no cover + messages.error( + request, "You have already registered in that group") url = reverse('virtualevent_view', - kwargs = { + kwargs={ 'id': race.id } - ) + ) return HttpResponseRedirect(url) @@ -1829,36 +1828,39 @@ def virtualevent_addboat_view(request,id=0): skillclass = coursestandard.skillclass returnurl = reverse(virtualevent_register_view, - kwargs={'id':race.id}) + kwargs={'id': race.id}) - if age < coursestandard.agemin: # pragma: no cover - messages.error(request,'You are younger than the minimum age for this group') + if age < coursestandard.agemin: # pragma: no cover + messages.error( + request, 'You are younger than the minimum age for this group') return HttpResponseRedirect(returnurl) - if age > coursestandard.agemax: # pragma: no cover - messages.error(request,'You are older than the maximum age for this group') + if age > coursestandard.agemax: # pragma: no cover + messages.error( + request, 'You are older than the maximum age for this group') return HttpResponseRedirect(returnurl) - if sex == 'male' and coursestandard.sex != 'male': # pragma: no cover - messages.error(request,'Men are not allowed to enter this category') + if sex == 'male' and coursestandard.sex != 'male': # pragma: no cover + messages.error( + request, 'Men are not allowed to enter this category') return HttpResponseRedirect(returnurl) - if sex == 'mixed' and coursestandard.sex not in ['mixed','male']: # pragma: no cover - messages.error(request,'Mixed crews are not allowed to enter this category') + if sex == 'mixed' and coursestandard.sex not in ['mixed', 'male']: # pragma: no cover + messages.error( + request, 'Mixed crews are not allowed to enter this category') return HttpResponseRedirect(returnurl) - record = resultobj( userid=r.id, teamname=teamname, race=race, - username = u'{f} {l}'.format( - f = r.user.first_name, - l = r.user.last_name + username=u'{f} {l}'.format( + f=r.user.first_name, + l=r.user.last_name ), weightcategory=weightcategory, adaptiveclass=adaptiveclass, - duration=datetime.time(0,0), + duration=datetime.time(0, 0), boattype=boattype, boatclass=boatclass, coursecompleted=False, @@ -1867,30 +1869,29 @@ def virtualevent_addboat_view(request,id=0): sex=sex, age=age, acceptsocialmedia=acceptsocialmedia, - ) + ) record.save() - add_rower_race(r,race) + add_rower_race(r, race) # send email about opt out if not acceptsocialmedia: job = myqueue( queue, handle_sendemail_optout, - race.manager.email,race.manager.first_name, + race.manager.email, race.manager.first_name, r.user.first_name+' '+r.user.last_name, - race.name,race.id, + race.name, race.id, ) - messages.info( request, "You have successfully registered for this race. Good luck!" ) otherrecords = resultobj.objects.filter( - race = race).exclude(userid = r.id) + race=race).exclude(userid=r.id) for otherrecord in otherrecords: try: @@ -1906,13 +1907,12 @@ def virtualevent_addboat_view(request,id=0): race.name, race.id ) - except Rower.DoesNotExist: # pragma: no cover + except Rower.DoesNotExist: # pragma: no cover pass + followers = VirtualRaceFollower.objects.filter(race=race) - followers = VirtualRaceFollower.objects.filter(race = race) - - for follower in followers: # pragma: no cover + for follower in followers: # pragma: no cover othername = '' if follower.user: othername = follower.user.first_name+' '+follower.user.last_name @@ -1923,12 +1923,12 @@ def virtualevent_addboat_view(request,id=0): queue, handle_sendemail_raceregistration, email, othername, - registeredname,race.name,race.id, - ) + registeredname, race.name, race.id, + ) url = reverse('virtualevent_view', - kwargs = { - 'id':race.id + kwargs={ + 'id': race.id }) return HttpResponseRedirect(url) @@ -1937,114 +1937,113 @@ def virtualevent_addboat_view(request,id=0): if hasinitial: initial = { 'age': calculate_age(r.birthdate), - 'boattype':boattype, - 'boatclass':boatclass, - 'adaptiveclass':adaptiveclass, - 'weightclass':weightclass, - 'sex':sex, - 'entrycategory':initialcategory, + 'boattype': boattype, + 'boatclass': boatclass, + 'adaptiveclass': adaptiveclass, + 'weightclass': weightclass, + 'sex': sex, + 'entrycategory': initialcategory, } - else: # pragma: no cover + else: # pragma: no cover initial = { 'age': calculate_age(r.birthdate), 'weightcategory': r.weightcategory, 'adaptiveclass': r.adaptiveclass, - } + } categories = None if race.coursestandards is not None: categories = CourseStandard.objects.filter( standardcollection=race.coursestandards).order_by("name") - - form = VirtualRaceResultForm(initial=initial,categories=categories) + form = VirtualRaceResultForm(initial=initial, categories=categories) breadcrumbs = [ { - 'url':reverse('virtualevents_view'), + 'url': reverse('virtualevents_view'), 'name': 'Challenges' - }, + }, { - 'url':reverse('virtualevent_view', - kwargs={'id':race.id} - ), + 'url': reverse('virtualevent_view', + kwargs={'id': race.id} + ), 'name': race.name - }, + }, { 'url': reverse('virtualevent_addboat_view', - kwargs = {'id':race.id} + kwargs={'id': race.id} ), 'name': 'Add Discipline' - } - ] - + } + ] buttons = [] if not request.user.is_anonymous: - if race_can_register(r,race): # pragma: no cover + if race_can_register(r, race): # pragma: no cover buttons += ['registerbutton'] - if race_can_adddiscipline(r,race): + if race_can_adddiscipline(r, race): buttons += ['adddisciplinebutton'] - if race_can_submit(r,race): + if race_can_submit(r, race): buttons += ['submitbutton'] - if race_can_resubmit(r,race): # pragma: no cover + if race_can_resubmit(r, race): # pragma: no cover buttons += ['resubmitbutton'] - if race_can_withdraw(r,race): + if race_can_withdraw(r, race): buttons += ['withdrawbutton'] - if race_can_edit(r,race): + if race_can_edit(r, race): buttons += ['editbutton'] - return render(request,'virtualeventregister.html', + return render(request, 'virtualeventregister.html', { - 'form':form, - 'buttons':buttons, - 'breadcrumbs':breadcrumbs, - 'race':race, - 'userid':r.user.id, + 'form': form, + 'buttons': buttons, + 'breadcrumbs': breadcrumbs, + 'race': race, + 'userid': r.user.id, 'active': 'nav-racing', }) + @login_required() -def virtualevent_register_view(request,id=0): +def virtualevent_register_view(request, id=0): r = getrower(request.user) try: race = VirtualRace.objects.get(id=id) - except VirtualRace.DoesNotExist: # pragma: no cover + except VirtualRace.DoesNotExist: # pragma: no cover raise Http404("Virtual Challenge does not exist") - categories = None - hasinitial,boattype,boatclass,adaptiveclass,weightclass,sex,referencespeed,initialcategory = default_class(r,None,race) + hasinitial, boattype, boatclass, adaptiveclass, weightclass, sex, referencespeed, initialcategory = default_class( + r, None, race) if race.coursestandards is not None: categories = CourseStandard.objects.filter( standardcollection=race.coursestandards).order_by("name") - if not race_can_register(r,race): - messages.error(request,"You cannot register for this race") + if not race_can_register(r, race): + messages.error(request, "You cannot register for this race") url = reverse('virtualevent_view', - kwargs = { - 'id':race.id + kwargs={ + 'id': race.id }) return HttpResponseRedirect(url) if race.sessiontype == 'race': resultobj = VirtualRaceResult - else: # pragma: no cover + else: # pragma: no cover resultobj = IndoorVirtualRaceResult # we're still here if request.method == 'POST': # process form - form = VirtualRaceResultForm(request.POST,categories=categories) + form = VirtualRaceResultForm(request.POST, categories=categories) if form.is_valid(): cd = form.cleaned_data @@ -2059,7 +2058,6 @@ def virtualevent_register_view(request,id=0): sex = r.sex - if boattype == '1x' and r.birthdate: age = calculate_age(r.birthdate) sex = r.sex @@ -2067,10 +2065,9 @@ def virtualevent_register_view(request,id=0): if sex == 'not specified': sex = 'male' - if mix: # pragma: no cover + if mix: # pragma: no cover sex = 'mixed' - coursestandard = None referencespeed = 5.0 @@ -2084,37 +2081,39 @@ def virtualevent_register_view(request,id=0): skillclass = coursestandard.skillclass returnurl = reverse(virtualevent_register_view, - kwargs={'id':race.id}) + kwargs={'id': race.id}) - if age < coursestandard.agemin: # pragma: no cover - messages.error(request,'You are younger than the minimum age for this group') + if age < coursestandard.agemin: # pragma: no cover + messages.error( + request, 'You are younger than the minimum age for this group') return HttpResponseRedirect(returnurl) - if age > coursestandard.agemax: # pragma: no cover - messages.error(request,'You are older than the maximum age for this group') + if age > coursestandard.agemax: # pragma: no cover + messages.error( + request, 'You are older than the maximum age for this group') return HttpResponseRedirect(returnurl) - if sex == 'male' and coursestandard.sex != 'male': # pragma: no cover - messages.error(request,'Men are not allowed to enter this category') + if sex == 'male' and coursestandard.sex != 'male': # pragma: no cover + messages.error( + request, 'Men are not allowed to enter this category') return HttpResponseRedirect(returnurl) - if sex == 'mixed' and coursestandard.sex not in ['mixed','male']: # pragma: no cover - messages.error(request,'Mixed crews are not allowed to enter this category') + if sex == 'mixed' and coursestandard.sex not in ['mixed', 'male']: # pragma: no cover + messages.error( + request, 'Mixed crews are not allowed to enter this category') return HttpResponseRedirect(returnurl) - - record = resultobj( userid=r.id, teamname=teamname, race=race, - username = u'{f} {l}'.format( - f = r.user.first_name, - l = r.user.last_name + username=u'{f} {l}'.format( + f=r.user.first_name, + l=r.user.last_name ), weightcategory=weightcategory, adaptiveclass=adaptiveclass, - duration=datetime.time(0,0), + duration=datetime.time(0, 0), boatclass=boatclass, boattype=boattype, coursecompleted=False, @@ -2122,30 +2121,30 @@ def virtualevent_register_view(request,id=0): age=age, entrycategory=coursestandard, referencespeed=referencespeed, - acceptsocialmedia = acceptsocialmedia, - ) + acceptsocialmedia=acceptsocialmedia, + ) record.save() - - add_rower_race(r,race) + add_rower_race(r, race) # send email about opt out if not acceptsocialmedia: job = myqueue( queue, handle_sendemail_optout, - race.manager.email,race.manager.first_name, + race.manager.email, race.manager.first_name, r.user.first_name+' '+r.user.last_name, - race.name,race.id, + race.name, race.id, ) # remove followers - myfollows = VirtualRaceFollower.objects.filter(user=r.user,race=race) - for f in myfollows: # pragma: no cover + myfollows = VirtualRaceFollower.objects.filter( + user=r.user, race=race) + for f in myfollows: # pragma: no cover f.delete() otherrecords = resultobj.objects.filter( - race = race).exclude(userid = r.id) + race=race).exclude(userid=r.id) for otherrecord in otherrecords: try: @@ -2160,13 +2159,13 @@ def virtualevent_register_view(request,id=0): registeredname, race.name, race.id - ) - except Rower.DoesNotExist: # pragma: no cover + ) + except Rower.DoesNotExist: # pragma: no cover pass - followers = VirtualRaceFollower.objects.filter(race = race) + followers = VirtualRaceFollower.objects.filter(race=race) - for follower in followers: # pragma: no cover + for follower in followers: # pragma: no cover othername = '' if follower.user: othername = follower.user.first_name+' '+follower.user.last_name @@ -2177,20 +2176,17 @@ def virtualevent_register_view(request,id=0): queue, handle_sendemail_raceregistration, email, othername, - registeredname,race.name,race.id, - ) - + registeredname, race.name, race.id, + ) messages.info( request, "You have successfully registered for this race. Good luck!" ) - - url = reverse('virtualevent_view', - kwargs = { - 'id':race.id + kwargs={ + 'id': race.id }) return HttpResponseRedirect(url) @@ -2199,86 +2195,87 @@ def virtualevent_register_view(request,id=0): if hasinitial: initial = { 'age': calculate_age(r.birthdate), - 'boattype':boattype, - 'boatclass':boatclass, - 'adaptiveclass':adaptiveclass, - 'weightclass':weightclass, - 'sex':sex, - 'entrycategory':initialcategory, + 'boattype': boattype, + 'boatclass': boatclass, + 'adaptiveclass': adaptiveclass, + 'weightclass': weightclass, + 'sex': sex, + 'entrycategory': initialcategory, } - else: # pragma: no cover + else: # pragma: no cover initial = { 'age': calculate_age(r.birthdate), 'weightcategory': r.weightcategory, 'adaptiveclass': r.adaptiveclass, - } + } categories = None if race.coursestandards is not None: categories = CourseStandard.objects.filter( standardcollection=race.coursestandards).order_by("name") - form = VirtualRaceResultForm(initial=initial,categories=categories) + form = VirtualRaceResultForm(initial=initial, categories=categories) breadcrumbs = [ { - 'url':reverse('virtualevents_view'), + 'url': reverse('virtualevents_view'), 'name': 'Challenges' - }, + }, { - 'url':reverse('virtualevent_view', - kwargs={'id':race.id} - ), + 'url': reverse('virtualevent_view', + kwargs={'id': race.id} + ), 'name': race.name - }, + }, { 'url': reverse(virtualevent_register_view, - kwargs = {'id':race.id} + kwargs={'id': race.id} ), 'name': 'Register' - } - ] + } + ] buttons = [] if not request.user.is_anonymous: - if race_can_register(r,race): + if race_can_register(r, race): buttons += ['registerbutton'] - if race_can_adddiscipline(r,race): # pragma: no cover + if race_can_adddiscipline(r, race): # pragma: no cover buttons += ['adddisciplinebutton'] - if race_can_submit(r,race): + if race_can_submit(r, race): buttons += ['submitbutton'] - if race_can_resubmit(r,race): # pragma: no cover + if race_can_resubmit(r, race): # pragma: no cover buttons += ['resubmitbutton'] - if race_can_withdraw(r,race): # pragma: no cover + if race_can_withdraw(r, race): # pragma: no cover buttons += ['withdrawbutton'] - if race_can_edit(r,race): + if race_can_edit(r, race): buttons += ['editbutton'] - return render(request,'virtualeventregister.html', + return render(request, 'virtualeventregister.html', { - 'form':form, - 'buttons':buttons, - 'breadcrumbs':breadcrumbs, - 'race':race, - 'userid':r.user.id, + 'form': form, + 'buttons': buttons, + 'breadcrumbs': breadcrumbs, + 'race': race, + 'userid': r.user.id, }) + @login_required() -def virtualevent_toggle_email_view(request,id=0): +def virtualevent_toggle_email_view(request, id=0): r = getrower(request.user) race = VirtualRace.objects.get(id=id) - records = VirtualRaceResult.objects.filter(userid=r.id,race=race) + records = VirtualRaceResult.objects.filter(userid=r.id, race=race) if True in [record.emailnotifications for record in records]: newsetting = False - else: # pragma: no cover + else: # pragma: no cover newsetting = True for record in records: @@ -2286,12 +2283,13 @@ def virtualevent_toggle_email_view(request,id=0): record.save() url = reverse('virtualevent_view', - kwargs={'id':race.id}) + kwargs={'id': race.id}) return HttpResponseRedirect(url) + @login_required() -def indoorvirtualevent_toggle_email_view(request,id=0): +def indoorvirtualevent_toggle_email_view(request, id=0): r = getrower(request.user) race = VirtualRace.objects.get(id=id) @@ -2300,7 +2298,7 @@ def indoorvirtualevent_toggle_email_view(request,id=0): if True in [record.emailnotifications for record in records]: newsetting = False - else: # pragma: no cover + else: # pragma: no cover newsetting = True for record in records: @@ -2308,29 +2306,30 @@ def indoorvirtualevent_toggle_email_view(request,id=0): record.save() url = reverse('virtualevent_view', - kwargs={'id':race.id}) + kwargs={'id': race.id}) return HttpResponseRedirect(url) + @login_required() -def indoorvirtualevent_register_view(request,id=0): +def indoorvirtualevent_register_view(request, id=0): r = getrower(request.user) try: race = VirtualRace.objects.get(id=id) - except VirtualRace.DoesNotExist: # pragma: no cover + except VirtualRace.DoesNotExist: # pragma: no cover raise Http404("Virtual Challenge does not exist") categories = None - if race.coursestandards is not None: # pragma: no cover + if race.coursestandards is not None: # pragma: no cover categories = CourseStandard.objects.filter( standardcollection=race.coursestandards).order_by("name") - if not race_can_register(r,race): - messages.error(request,"You cannot register for this race") + if not race_can_register(r, race): + messages.error(request, "You cannot register for this race") url = reverse('virtualevent_view', - kwargs = { - 'id':race.id + kwargs={ + 'id': race.id }) return HttpResponseRedirect(url) @@ -2338,7 +2337,7 @@ def indoorvirtualevent_register_view(request,id=0): # we're still here if request.method == 'POST': # process form - form = IndoorVirtualRaceResultForm(request.POST,categories=categories) + form = IndoorVirtualRaceResultForm(request.POST, categories=categories) if form.is_valid(): cd = form.cleaned_data teamname = cd['teamname'] @@ -2360,7 +2359,7 @@ def indoorvirtualevent_register_view(request,id=0): coursestandard = None referencespeed = 5.0 - if race.coursestandards is not None: # pragma: no cover + if race.coursestandards is not None: # pragma: no cover coursestandard = cd['entrycategory'] referencespeed = coursestandard.referencespeed boatclass = coursestandard.boatclass @@ -2369,37 +2368,39 @@ def indoorvirtualevent_register_view(request,id=0): skillclass = coursestandard.skillclass returnurl = reverse(virtualevent_register_view, - kwargs={'id':race.id}) + kwargs={'id': race.id}) if age < coursestandard.agemin: - messages.error(request,'You are younger than the minimum age for this group') + messages.error( + request, 'You are younger than the minimum age for this group') return HttpResponseRedirect(returnurl) if age > coursestandard.agemax: - messages.error(request,'You are older than the maximum age for this group') + messages.error( + request, 'You are older than the maximum age for this group') return HttpResponseRedirect(returnurl) if sex == 'male' and coursestandard.sex != 'male': - messages.error(request,'Men are not allowed to enter this category') + messages.error( + request, 'Men are not allowed to enter this category') return HttpResponseRedirect(returnurl) - if sex == 'mixed' and coursestandard.sex not in ['mixed','male']: - messages.error(request,'Mixed crews are not allowed to enter this category') + if sex == 'mixed' and coursestandard.sex not in ['mixed', 'male']: + messages.error( + request, 'Mixed crews are not allowed to enter this category') return HttpResponseRedirect(returnurl) - - record = IndoorVirtualRaceResult( userid=r.id, teamname=teamname, race=race, - username = u'{f} {l}'.format( - f = r.user.first_name, - l = r.user.last_name + username=u'{f} {l}'.format( + f=r.user.first_name, + l=r.user.last_name ), weightcategory=weightcategory, adaptiveclass=adaptiveclass, - duration=datetime.time(0,0), + duration=datetime.time(0, 0), boatclass=boatclass, coursecompleted=False, sex=sex, @@ -2407,29 +2408,30 @@ def indoorvirtualevent_register_view(request,id=0): entrycategory=coursestandard, referencespeed=referencespeed, acceptsocialmedia=acceptsocialmedia, - ) + ) record.save() - add_rower_race(r,race) + add_rower_race(r, race) # send email about opt out if not acceptsocialmedia: job = myqueue( queue, handle_sendemail_optout, - race.manager.email,race.manager.first_name, + race.manager.email, race.manager.first_name, r.user.first_name+' '+r.user.last_name, - race.name,race.id, + race.name, race.id, ) # remove followers - myfollows = VirtualRaceFollower.objects.filter(user=r.user,race=race) - for f in myfollows: # pragma: no cover + myfollows = VirtualRaceFollower.objects.filter( + user=r.user, race=race) + for f in myfollows: # pragma: no cover f.delete() otherrecords = IndoorVirtualRaceResult.objects.filter( - race = race).exclude(userid = r.id) + race=race).exclude(userid=r.id) for otherrecord in otherrecords: try: @@ -2444,13 +2446,13 @@ def indoorvirtualevent_register_view(request,id=0): registeredname, race.name, race.id - ) - except Rower.DoesNotExist: # pragma: no cover + ) + except Rower.DoesNotExist: # pragma: no cover pass - followers = VirtualRaceFollower.objects.filter(race = race) + followers = VirtualRaceFollower.objects.filter(race=race) - for follower in followers: # pragma: no cover + for follower in followers: # pragma: no cover othername = '' if follower.user: othername = follower.user.first_name+' '+follower.user.last_name @@ -2461,9 +2463,8 @@ def indoorvirtualevent_register_view(request,id=0): queue, handle_sendemail_raceregistration, email, othername, - registeredname,race.name,race.id, - ) - + registeredname, race.name, race.id, + ) messages.info( request, @@ -2471,8 +2472,8 @@ def indoorvirtualevent_register_view(request,id=0): ) url = reverse('virtualevent_view', - kwargs = { - 'id':race.id + kwargs={ + 'id': race.id }) return HttpResponseRedirect(url) @@ -2482,65 +2483,67 @@ def indoorvirtualevent_register_view(request,id=0): 'age': calculate_age(r.birthdate), 'weightcategory': r.weightcategory, 'adaptiveclass': r.adaptiveclass, - } + } categories = None - if race.coursestandards is not None: # pragma: no cover + if race.coursestandards is not None: # pragma: no cover categories = CourseStandard.objects.filter( standardcollection=race.coursestandards).order_by("name") - form = IndoorVirtualRaceResultForm(initial=initial,categories=categories) + form = IndoorVirtualRaceResultForm( + initial=initial, categories=categories) breadcrumbs = [ { - 'url':reverse('virtualevents_view'), + 'url': reverse('virtualevents_view'), 'name': 'Challenges' - }, + }, { - 'url':reverse('virtualevent_view', - kwargs={'id':race.id} - ), + 'url': reverse('virtualevent_view', + kwargs={'id': race.id} + ), 'name': race.name - }, + }, { 'url': reverse(indoorvirtualevent_register_view, - kwargs = {'id':race.id} + kwargs={'id': race.id} ), 'name': 'Register' - } - ] + } + ] buttons = [] if not request.user.is_anonymous: - if race_can_register(r,race): + if race_can_register(r, race): buttons += ['registerbutton'] - if race_can_adddiscipline(r,race): # pragma: no cover + if race_can_adddiscipline(r, race): # pragma: no cover buttons += ['adddisciplinebutton'] - if race_can_submit(r,race): + if race_can_submit(r, race): buttons += ['submitbutton'] - if race_can_resubmit(r,race): # pragma: no cover + if race_can_resubmit(r, race): # pragma: no cover buttons += ['resubmitbutton'] - if race_can_withdraw(r,race): # pragma: no cover + if race_can_withdraw(r, race): # pragma: no cover buttons += ['withdrawbutton'] - if race_can_edit(r,race): + if race_can_edit(r, race): buttons += ['editbutton'] - return render(request,'virtualeventregister.html', + return render(request, 'virtualeventregister.html', { - 'form':form, - 'buttons':buttons, - 'race':race, - 'breadcrumbs':breadcrumbs, - 'userid':r.user.id, + 'form': form, + 'buttons': buttons, + 'race': race, + 'breadcrumbs': breadcrumbs, + 'userid': r.user.id, }) + @login_required() def indoorvirtualevent_create_view(request): r = getrower(request.user) @@ -2568,9 +2571,8 @@ def indoorvirtualevent_create_view(request): timezone_str = cd['timezone'] - startdatetime = datetime.datetime.combine(startdate,start_time) - enddatetime = datetime.datetime.combine(enddate,end_time) - + startdatetime = datetime.datetime.combine(startdate, start_time) + enddatetime = datetime.datetime.combine(enddate, end_time) startdatetime = pytz.timezone(timezone_str).localize( startdatetime @@ -2582,7 +2584,7 @@ def indoorvirtualevent_create_view(request): evaluation_closure.replace(tzinfo=None) ) - if registration_form == 'manual': # pragma: no cover + if registration_form == 'manual': # pragma: no cover try: registration_closure = pytz.timezone( timezone_str @@ -2591,14 +2593,14 @@ def indoorvirtualevent_create_view(request): ) except AttributeError: registration_closure = startdatetime - elif registration_form == 'windowstart': # pragma: no cover + elif registration_form == 'windowstart': # pragma: no cover registration_closure = startdatetime - elif registration_form == 'windowend': # pragma: no cover + elif registration_form == 'windowend': # pragma: no cover registration_closure = enddatetime else: registration_closure = evaluation_closure - if sessionunit == 'min': # pragma: no cover + if sessionunit == 'min': # pragma: no cover sessionmode = 'time' else: sessionmode = 'distance' @@ -2606,15 +2608,15 @@ def indoorvirtualevent_create_view(request): vs = VirtualRace( name=name, startdate=startdate, - preferreddate = startdate, - start_time = start_time, + preferreddate=startdate, + start_time=start_time, enddate=enddate, end_time=end_time, comment=comment, - sessiontype = 'indoorrace', - sessionunit = sessionunit, - sessionmode = sessionmode, - sessionvalue = sessionvalue, + sessiontype='indoorrace', + sessionunit=sessionunit, + sessionmode=sessionmode, + sessionvalue=sessionvalue, course=None, timezone=timezone_str, coursestandards=coursestandards, @@ -2622,7 +2624,7 @@ def indoorvirtualevent_create_view(request): registration_closure=registration_closure, contact_phone=contact_phone, contact_email=contact_email, - country = 'Indoor', + country='Indoor', manager=request.user, ) @@ -2631,27 +2633,26 @@ def indoorvirtualevent_create_view(request): # create Site Announcement & Tweet if settings.DEBUG or settings.TESTING: dotweet = False - elif 'dev' in settings.SITE_URL: # pragma: no cover + elif 'dev' in settings.SITE_URL: # pragma: no cover dotweet = False - else: # pragma: no cover + else: # pragma: no cover dotweet = True announcementshort = "New Virtual Indoor Challenge on rowsandall.com: {name}".format( - name = name, + name=name, ) announcement = announcementshort + " {siteurl}/rowers/virtualevent/{raceid}/".format( - siteurl = SITE_URL, - raceid = vs.id + siteurl=SITE_URL, + raceid=vs.id ) - - if len(announcement)>250: # pragma: no cover + if len(announcement) > 250: # pragma: no cover announcement = announcementshort sa = SiteAnnouncement( - announcement = announcement, - dotweet = dotweet + announcement=announcement, + dotweet=dotweet ) sa.save() @@ -2662,28 +2663,28 @@ def indoorvirtualevent_create_view(request): racecreateform = IndoorVirtualRaceForm(timezone=r.defaulttimezone) - breadcrumbs = [ { - 'url':reverse('virtualevents_view'), + 'url': reverse('virtualevents_view'), 'name': 'Challenges' - }, + }, { - 'url':reverse('indoorvirtualevent_create_view', - ), + 'url': reverse('indoorvirtualevent_create_view', + ), 'name': 'New Indoor Virtual Regatta' - }, - ] + }, + ] - return render(request,'indoorvirtualeventcreate.html', + return render(request, 'indoorvirtualeventcreate.html', { - 'form':racecreateform, - 'breadcrumbs':breadcrumbs, - 'rower':r, - 'active':'nav-racing', + 'form': racecreateform, + 'breadcrumbs': breadcrumbs, + 'rower': r, + 'active': 'nav-racing', }) + @login_required() def fastestvirtualevent_create_view(request): r = getrower(request.user) @@ -2711,9 +2712,8 @@ def fastestvirtualevent_create_view(request): timezone_str = cd['timezone'] - startdatetime = datetime.datetime.combine(startdate,start_time) - enddatetime = datetime.datetime.combine(enddate,end_time) - + startdatetime = datetime.datetime.combine(startdate, start_time) + enddatetime = datetime.datetime.combine(enddate, end_time) startdatetime = pytz.timezone(timezone_str).localize( startdatetime @@ -2725,7 +2725,7 @@ def fastestvirtualevent_create_view(request): evaluation_closure.replace(tzinfo=None) ) - if registration_form == 'manual': # pragma: no cover + if registration_form == 'manual': # pragma: no cover try: registration_closure = pytz.timezone( timezone_str @@ -2734,33 +2734,32 @@ def fastestvirtualevent_create_view(request): ) except AttributeError: registration_closure = startdatetime - elif registration_form == 'windowstart': # pragma: no cover + elif registration_form == 'windowstart': # pragma: no cover registration_closure = startdatetime - elif registration_form == 'windowend': # pragma: no cover + elif registration_form == 'windowend': # pragma: no cover registration_closure = enddatetime else: registration_closure = evaluation_closure - if sessionunit == 'min': # pragma: no cover + if sessionunit == 'min': # pragma: no cover sessionmode = 'time' sessiontype = 'fastest_time' else: sessionmode = 'distance' sessiontype = 'fastest_distance' - vs = VirtualRace( name=name, startdate=startdate, - preferreddate = startdate, - start_time = start_time, + preferreddate=startdate, + start_time=start_time, enddate=enddate, end_time=end_time, comment=comment, - sessiontype = sessiontype, - sessionunit = sessionunit, - sessionmode = sessionmode, - sessionvalue = sessionvalue, + sessiontype=sessiontype, + sessionunit=sessionunit, + sessionmode=sessionmode, + sessionvalue=sessionvalue, course=None, timezone=timezone_str, coursestandards=coursestandards, @@ -2768,7 +2767,7 @@ def fastestvirtualevent_create_view(request): registration_closure=registration_closure, contact_phone=contact_phone, contact_email=contact_email, - country = 'Indoor', + country='Indoor', manager=request.user, ) @@ -2782,27 +2781,26 @@ def fastestvirtualevent_create_view(request): # create Site Announcement & Tweet if settings.DEBUG or settings.TESTING: dotweet = False - elif 'dev' in settings.SITE_URL: # pragma: no cover + elif 'dev' in settings.SITE_URL: # pragma: no cover dotweet = False - else: # pragma: no cover + else: # pragma: no cover dotweet = True announcementshort = "New Virtual Challenge on rowsandall.com: {name}".format( - name = name, + name=name, ) announcement = announcementshort + " {siteurl}/rowers/virtualevent/{raceid}/".format( - siteurl = SITE_URL, - raceid = vs.id + siteurl=SITE_URL, + raceid=vs.id ) - - if len(announcement)>250: # pragma: no cover + if len(announcement) > 250: # pragma: no cover announcement = announcementshort sa = SiteAnnouncement( - announcement = announcement, - dotweet = dotweet + announcement=announcement, + dotweet=dotweet ) sa.save() @@ -2813,25 +2811,24 @@ def fastestvirtualevent_create_view(request): racecreateform = IndoorVirtualRaceForm(timezone=r.defaulttimezone) - breadcrumbs = [ { - 'url':reverse('virtualevents_view'), + 'url': reverse('virtualevents_view'), 'name': 'Challenges' - }, + }, { - 'url':reverse('indoorvirtualevent_create_view', - ), + 'url': reverse('indoorvirtualevent_create_view', + ), 'name': 'New Indoor Virtual Regatta' - }, - ] + }, + ] - return render(request,'fastestvirtualeventcreate.html', + return render(request, 'fastestvirtualeventcreate.html', { - 'form':racecreateform, - 'breadcrumbs':breadcrumbs, - 'rower':r, - 'active':'nav-racing', + 'form': racecreateform, + 'breadcrumbs': breadcrumbs, + 'rower': r, + 'active': 'nav-racing', }) @@ -2860,12 +2857,11 @@ def virtualevent_create_view(request): # correct times - geocourse = GeoCourse.objects.get(id= course.id) + geocourse = GeoCourse.objects.get(id=course.id) timezone_str = get_course_timezone(geocourse) - startdatetime = datetime.datetime.combine(startdate,start_time) - enddatetime = datetime.datetime.combine(enddate,end_time) - + startdatetime = datetime.datetime.combine(startdate, start_time) + enddatetime = datetime.datetime.combine(enddate, end_time) startdatetime = pytz.timezone(timezone_str).localize( startdatetime @@ -2877,7 +2873,7 @@ def virtualevent_create_view(request): evaluation_closure.replace(tzinfo=None) ) - if registration_form == 'manual': # pragma: no cover + if registration_form == 'manual': # pragma: no cover try: registration_closure = pytz.timezone( timezone_str @@ -2886,31 +2882,30 @@ def virtualevent_create_view(request): ) except AttributeError: registration_closure = startdatetime - elif registration_form == 'windowstart': # pragma: no cover + elif registration_form == 'windowstart': # pragma: no cover registration_closure = startdatetime - elif registration_form == 'windowend': # pragma: no cover + elif registration_form == 'windowend': # pragma: no cover registration_closure = enddatetime else: registration_closure = evaluation_closure - vs = VirtualRace( name=name, startdate=startdate, - preferreddate = startdate, - start_time = start_time, + preferreddate=startdate, + start_time=start_time, enddate=enddate, end_time=end_time, course=geocourse, comment=comment, - sessiontype = 'race', + sessiontype='race', timezone=timezone_str, evaluation_closure=evaluation_closure, registration_closure=registration_closure, contact_phone=contact_phone, coursestandards=coursestandards, contact_email=contact_email, - country = course.country, + country=course.country, manager=request.user, ) @@ -2919,28 +2914,27 @@ def virtualevent_create_view(request): # create Site Announcement & Tweet if settings.DEBUG or settings.TESTING: dotweet = False - elif 'dev' in settings.SITE_URL: # pragma: no cover + elif 'dev' in settings.SITE_URL: # pragma: no cover dotweet = False - else: # pragma: no cover + else: # pragma: no cover dotweet = True announcementshort = "New Virtual Challenge on rowsandall.com: {name} on course {course}".format( - name = name, - course = course.name, + name=name, + course=course.name, ) announcement = announcementshort + " {siteurl}/rowers/virtualevent/{raceid}/".format( - siteurl = SITE_URL, - raceid = vs.id + siteurl=SITE_URL, + raceid=vs.id ) - - if len(announcement)>250: # pragma: no cover + if len(announcement) > 250: # pragma: no cover announcement = announcementshort sa = SiteAnnouncement( - announcement = announcement, - dotweet = dotweet + announcement=announcement, + dotweet=dotweet ) sa.save() @@ -2950,71 +2944,70 @@ def virtualevent_create_view(request): racecreateform = VirtualRaceForm() - breadcrumbs = [ { - 'url':reverse('virtualevents_view'), + 'url': reverse('virtualevents_view'), 'name': 'Challenges' - }, + }, { - 'url':reverse(virtualevent_create_view, - ), + 'url': reverse(virtualevent_create_view, + ), 'name': 'New Virtual Regatta' - }, - ] - return render(request,'virtualeventcreate.html', + }, + ] + return render(request, 'virtualeventcreate.html', { - 'form':racecreateform, - 'breadcrumbs':breadcrumbs, - 'rower':r, - 'active':'nav-racing', + 'form': racecreateform, + 'breadcrumbs': breadcrumbs, + 'rower': r, + 'active': 'nav-racing', }) + @login_required() -@permission_required('virtualevent.change_race',fn=get_virtualevent_by_pk,raise_exception=True) -def virtualevent_edit_view(request,id=0): +@permission_required('virtualevent.change_race', fn=get_virtualevent_by_pk, raise_exception=True) +def virtualevent_edit_view(request, id=0): r = getrower(request.user) - race = get_object_or_404(VirtualRace,pk=id) - - + race = get_object_or_404(VirtualRace, pk=id) start_time = race.start_time start_date = race.startdate - startdatetime = datetime.datetime.combine(start_date,start_time) + startdatetime = datetime.datetime.combine(start_date, start_time) startdatetime = pytz.timezone(race.timezone).localize( startdatetime - ) + ) end_time = race.end_time end_date = race.enddate - enddatetime = datetime.datetime.combine(end_date,end_time) + enddatetime = datetime.datetime.combine(end_date, end_time) enddatetime = pytz.timezone(race.timezone).localize( enddatetime - ) + ) - if timezone.now() > enddatetime: # pragma: no cover - messages.error(request,"You cannot edit a race after the end of the race window") + if timezone.now() > enddatetime: # pragma: no cover + messages.error( + request, "You cannot edit a race after the end of the race window") url = reverse('virtualevent_view', kwargs={ - 'id':race.id, - }) + 'id': race.id, + }) if request.method == 'POST': - racecreateform = VirtualRaceForm(request.POST,instance=race) + racecreateform = VirtualRaceForm(request.POST, instance=race) if racecreateform.is_valid(): cd = racecreateform.cleaned_data - res, message = update_virtualrace(race,cd) + res, message = update_virtualrace(race, cd) if res: - messages.info(request,message) - else: # pragma: no cover - messages.error(request,message) + messages.info(request, message) + else: # pragma: no cover + messages.error(request, message) url = reverse('virtualevent_view', - kwargs = { - 'id':race.id - }) + kwargs={ + 'id': race.id + }) return HttpResponseRedirect(url) @@ -3024,97 +3017,99 @@ def virtualevent_edit_view(request,id=0): breadcrumbs = [ { - 'url':reverse('virtualevents_view'), + 'url': reverse('virtualevents_view'), 'name': 'Challenges' - }, + }, { - 'url':reverse('virtualevent_view', - kwargs={'id':race.id} - ), + 'url': reverse('virtualevent_view', + kwargs={'id': race.id} + ), 'name': race.name - }, + }, { 'url': reverse(virtualevent_edit_view, - kwargs = {'id':race.id} + kwargs={'id': race.id} ), 'name': 'Edit' - } - ] + } + ] buttons = [] if not request.user.is_anonymous: - if race_can_register(r,race): + if race_can_register(r, race): buttons += ['registerbutton'] - if race_can_adddiscipline(r,race): # pragma: no cover + if race_can_adddiscipline(r, race): # pragma: no cover buttons += ['adddisciplinebutton'] - if race_can_submit(r,race): # pragma: no cover + if race_can_submit(r, race): # pragma: no cover buttons += ['submitbutton'] - if race_can_resubmit(r,race): # pragma: no cover + if race_can_resubmit(r, race): # pragma: no cover buttons += ['resubmitbutton'] - if race_can_withdraw(r,race): # pragma: no cover + if race_can_withdraw(r, race): # pragma: no cover buttons += ['withdrawbutton'] - if race_can_edit(r,race): + if race_can_edit(r, race): buttons += ['editbutton'] - return render(request,'virtualeventedit.html', + return render(request, 'virtualeventedit.html', { - 'form':racecreateform, - 'breadcrumbs':breadcrumbs, - 'buttons':buttons, - 'rower':r, - 'race':race, + 'form': racecreateform, + 'breadcrumbs': breadcrumbs, + 'buttons': buttons, + 'rower': r, + 'race': race, }) + @login_required() -@permission_required('virtualevent.change_race',fn=get_virtualevent_by_pk,raise_exception=True) -def indoorvirtualevent_edit_view(request,id=0): +@permission_required('virtualevent.change_race', fn=get_virtualevent_by_pk, raise_exception=True) +def indoorvirtualevent_edit_view(request, id=0): r = getrower(request.user) - race = get_object_or_404(VirtualRace,pk=id) + race = get_object_or_404(VirtualRace, pk=id) start_time = race.start_time start_date = race.startdate - startdatetime = datetime.datetime.combine(start_date,start_time) + startdatetime = datetime.datetime.combine(start_date, start_time) startdatetime = pytz.timezone(race.timezone).localize( startdatetime - ) + ) end_time = race.end_time end_date = race.enddate - enddatetime = datetime.datetime.combine(end_date,end_time) + enddatetime = datetime.datetime.combine(end_date, end_time) enddatetime = pytz.timezone(race.timezone).localize( enddatetime - ) + ) - if timezone.now() > enddatetime: # pragma: no cover - messages.error(request,"You cannot edit a race after the end of the race window") + if timezone.now() > enddatetime: # pragma: no cover + messages.error( + request, "You cannot edit a race after the end of the race window") url = reverse('virtualevent_view', kwargs={ - 'id':race.id, - }) + 'id': race.id, + }) if request.method == 'POST': - racecreateform = IndoorVirtualRaceForm(request.POST,instance=race) + racecreateform = IndoorVirtualRaceForm(request.POST, instance=race) if racecreateform.is_valid(): cd = racecreateform.cleaned_data - res, message = update_indoorvirtualrace(race,cd) + res, message = update_indoorvirtualrace(race, cd) if res: - messages.info(request,message) - else: # pragma: no cover - messages.error(request,message) + messages.info(request, message) + else: # pragma: no cover + messages.error(request, message) url = reverse('virtualevent_view', - kwargs = { - 'id':race.id - }) + kwargs={ + 'id': race.id + }) return HttpResponseRedirect(url) @@ -3122,67 +3117,65 @@ def indoorvirtualevent_edit_view(request,id=0): racecreateform = IndoorVirtualRaceForm(instance=race) - breadcrumbs = [ { - 'url':reverse('virtualevents_view'), + 'url': reverse('virtualevents_view'), 'name': 'Challenges' - }, + }, { - 'url':reverse('virtualevent_view', - kwargs={'id':race.id} - ), + 'url': reverse('virtualevent_view', + kwargs={'id': race.id} + ), 'name': race.name - }, + }, { 'url': reverse(indoorvirtualevent_edit_view, - kwargs = {'id':race.id} + kwargs={'id': race.id} ), 'name': 'Edit' - } - ] + } + ] buttons = [] if not request.user.is_anonymous: - if race_can_register(r,race): + if race_can_register(r, race): buttons += ['registerbutton'] - if race_can_adddiscipline(r,race): # pragma: no cover + if race_can_adddiscipline(r, race): # pragma: no cover buttons += ['adddisciplinebutton'] - if race_can_submit(r,race): # pragma: no cover + if race_can_submit(r, race): # pragma: no cover buttons += ['submitbutton'] - if race_can_resubmit(r,race): # pragma: no cover + if race_can_resubmit(r, race): # pragma: no cover buttons += ['resubmitbutton'] - if race_can_withdraw(r,race): # pragma: no cover + if race_can_withdraw(r, race): # pragma: no cover buttons += ['withdrawbutton'] - if race_can_edit(r,race): + if race_can_edit(r, race): buttons += ['editbutton'] - - return render(request,'virtualeventedit.html', + return render(request, 'virtualeventedit.html', { - 'form':racecreateform, - 'buttons':buttons, - 'breadcrumbs':breadcrumbs, - 'rower':r, - 'race':race, + 'form': racecreateform, + 'buttons': buttons, + 'breadcrumbs': breadcrumbs, + 'rower': r, + 'race': race, }) @login_required() -def virtualevent_submit_result_view(request,id=0,workoutid=0): +def virtualevent_submit_result_view(request, id=0, workoutid=0): r = getrower(request.user) try: race = VirtualRace.objects.get(id=id) - except VirtualRace.DoesNotExist: # pragma: no cover + except VirtualRace.DoesNotExist: # pragma: no cover raise Http404("Virtual Challenge does not exist") start_time = race.start_time @@ -3195,7 +3188,7 @@ def virtualevent_submit_result_view(request,id=0,workoutid=0): enddatetime = datetime.datetime.combine(end_date, end_time) enddatetime = pytz.timezone(race.timezone).localize(enddatetime) - can_submit = race_can_submit(r,race) or race_can_resubmit(r,race) + can_submit = race_can_submit(r, race) or race_can_resubmit(r, race) if race.sessiontype == 'race': resultobj = VirtualRaceResult @@ -3203,22 +3196,23 @@ def virtualevent_submit_result_view(request,id=0,workoutid=0): resultobj = IndoorVirtualRaceResult records = resultobj.objects.filter( - userid = r.id, + userid=r.id, race=race - ) + ) - if records.count() == 0: # pragma: no cover - hasinitial,boattype,boatclass,adaptiveclass,weightclass,sex,referencespeed,initialcategory = default_class(r,None,race) + if records.count() == 0: # pragma: no cover + hasinitial, boattype, boatclass, adaptiveclass, weightclass, sex, referencespeed, initialcategory = default_class( + r, None, race) if not hasinitial: - messages.error(request,"Sorry, you have to register first") + messages.error(request, "Sorry, you have to register first") url = reverse('virtualevent_view', - kwargs = { - 'id':id, + kwargs={ + 'id': id, }) return HttpResponseRedirect(url) record = resultobj( - userid = r.id, - username = r.user.first_name+' '+r.user.last_name, + userid=r.id, + username=r.user.first_name+' '+r.user.last_name, weightcategory=weightclass, adaptiveclass=adaptiveclass, race=race, @@ -3231,7 +3225,6 @@ def virtualevent_submit_result_view(request,id=0,workoutid=0): record.save() records = [record] - entrychoices = [] for record in records: @@ -3242,39 +3235,38 @@ def virtualevent_submit_result_view(request,id=0,workoutid=0): entries['choices'] = entrychoices entries['initial'] = [records[0].id] - if not can_submit: # pragma: no cover - messages.error(request,'You cannot submit a result to this race') + if not can_submit: # pragma: no cover + messages.error(request, 'You cannot submit a result to this race') url = reverse('virtualevent_view', - kwargs = { - 'id':id - } + kwargs={ + 'id': id + } ) return HttpResponseRedirect(url) ws = Workout.objects.filter( user=r, - #workouttype__in=mytypes.rowtypes, + # workouttype__in=mytypes.rowtypes, startdatetime__gte=startdatetime, startdatetime__lte=enddatetime, distance__gte=race.approximate_distance, - ).order_by("-date","-startdatetime","id") + ).order_by("-date", "-startdatetime", "id") - if not ws: # pragma: no cover + if not ws: # pragma: no cover messages.info( request, 'You have no workouts executed during the race window. Please upload a result or enter it manually.' ) url = reverse('virtualevent_view', - kwargs = { - 'id':id - }) + kwargs={ + 'id': id + }) return HttpResponseRedirect(url) - initialworkouts = [w.id for w in Workout.objects.filter( - user=r,plannedsession=race + user=r, plannedsession=race )] workoutdata = {} @@ -3285,13 +3277,13 @@ def virtualevent_submit_result_view(request,id=0,workoutid=0): for w in ws: wtpl = (w.id, w.__str__()) choices.append(wtpl) - if w.id in initialworkouts: # pragma: no cover + if w.id in initialworkouts: # pragma: no cover workoutdata['initial'].append(w.id) workoutdata['choices'] = tuple(choices) if request.method == 'POST': - w_form = WorkoutRaceSelectForm(workoutdata,entries,request.POST) + w_form = WorkoutRaceSelectForm(workoutdata, entries, request.POST) if w_form.is_valid(): selectedworkout = w_form.cleaned_data['workouts'] splitsecond = 0 @@ -3299,41 +3291,40 @@ def virtualevent_submit_result_view(request,id=0,workoutid=0): else: selectedworkout = None - if selectedworkout is not None: workouts = Workout.objects.filter(id=selectedworkout) if race.sessiontype == 'race': - result,comments,errors,jobid = add_workout_race( - workouts,race,r, - splitsecond=splitsecond,recordid=recordid) - elif race.sessiontype in ['fastest_time','fastest_distance']: # pragma: no cover + result, comments, errors, jobid = add_workout_race( + workouts, race, r, + splitsecond=splitsecond, recordid=recordid) + elif race.sessiontype in ['fastest_time', 'fastest_distance']: # pragma: no cover result, comments, errors, jobid = add_workout_fastestrace( - workouts,race,r,recordid=recordid + workouts, race, r, recordid=recordid ) else: - result,comments,errors,jobid = add_workout_indoorrace( - workouts,race,r,recordid=recordid) - + result, comments, errors, jobid = add_workout_indoorrace( + workouts, race, r, recordid=recordid) for c in comments: - messages.info(request,c) + messages.info(request, c) for er in errors: - messages.error(request,er) + messages.error(request, er) if jobid: try: - request.session['async_tasks'] += [(jobid,'submit_race')] + request.session['async_tasks'] += [(jobid, 'submit_race')] except KeyError: - request.session['async_tasks'] = [(jobid,'submit_race')] + request.session['async_tasks'] = [(jobid, 'submit_race')] - messages.info(request,"We are evaluating your result. The page will reload when we're done. Your result will show up if you adhered to the course") + messages.info( + request, "We are evaluating your result. The page will reload when we're done. Your result will show up if you adhered to the course") if result: - otherrecords = resultobj.objects.filter(race = race).exclude(userid = r.id) + otherrecords = resultobj.objects.filter( + race=race).exclude(userid=r.id) if not jobid: - messages.info(request,"Result submitted successfully.") - + messages.info(request, "Result submitted successfully.") for otherrecord in otherrecords: try: @@ -3348,13 +3339,13 @@ def virtualevent_submit_result_view(request,id=0,workoutid=0): registeredname, race.name, race.id - ) - except Rower.DoesNotExist: # pragma: no cover + ) + except Rower.DoesNotExist: # pragma: no cover pass - followers = VirtualRaceFollower.objects.filter(race = race) + followers = VirtualRaceFollower.objects.filter(race=race) - for follower in followers: # pragma: no cover + for follower in followers: # pragma: no cover othername = '' if follower.user: othername = follower.user.first_name+' '+follower.user.last_name @@ -3365,14 +3356,13 @@ def virtualevent_submit_result_view(request,id=0,workoutid=0): queue, handle_sendemail_racesubmission, email, othername, - registeredname,race.name,race.id, - ) - + registeredname, race.name, race.id, + ) # redirect to race page url = reverse('virtualevent_view', - kwargs = { - 'id':race.id + kwargs={ + 'id': race.id }) return HttpResponseRedirect(url) @@ -3380,142 +3370,144 @@ def virtualevent_submit_result_view(request,id=0,workoutid=0): else: if workoutid: workoutdata['initial'] = encoder.decode_hex(workoutid) - w_form = WorkoutRaceSelectForm(workoutdata,entries) - - + w_form = WorkoutRaceSelectForm(workoutdata, entries) breadcrumbs = [ { - 'url':reverse('virtualevents_view'), + 'url': reverse('virtualevents_view'), 'name': 'Challenges' - }, + }, { - 'url':reverse('virtualevent_view', - kwargs={'id':race.id} - ), + 'url': reverse('virtualevent_view', + kwargs={'id': race.id} + ), 'name': race.name - }, + }, { 'url': reverse(virtualevent_submit_result_view, - kwargs = {'id':race.id} + kwargs={'id': race.id} ), 'name': 'Submit Result' - } - ] + } + ] buttons = [] if not request.user.is_anonymous: - if race_can_register(r,race): # pragma: no cover + if race_can_register(r, race): # pragma: no cover buttons += ['registerbutton'] - if race_can_adddiscipline(r,race): + if race_can_adddiscipline(r, race): buttons += ['adddisciplinebutton'] - if race_can_submit(r,race): + if race_can_submit(r, race): buttons += ['submitbutton'] - if race_can_resubmit(r,race): # pragma: no cover + if race_can_resubmit(r, race): # pragma: no cover buttons += ['resubmitbutton'] - if race_can_withdraw(r,race): # pragma: no cover + if race_can_withdraw(r, race): # pragma: no cover buttons += ['withdrawbutton'] - if race_can_edit(r,race): + if race_can_edit(r, race): buttons += ['editbutton'] - - return render(request,'race_submit.html', + return render(request, 'race_submit.html', { - 'race':race, - 'buttons':buttons, - 'workouts':ws, - 'breadcrumbs':breadcrumbs, - 'active':'nav-racing', - 'rower':r, - 'w_form':w_form, - }) + 'race': race, + 'buttons': buttons, + 'workouts': ws, + 'breadcrumbs': breadcrumbs, + 'active': 'nav-racing', + 'rower': r, + 'w_form': w_form, + }) -def addfollower_view(request,id=0): + +def addfollower_view(request, id=0): try: race = VirtualRace.objects.get(id=id) - except VirtualRace.DoesNotExist: # pragma: no cover + except VirtualRace.DoesNotExist: # pragma: no cover raise Http404("Virtual Challenge does not exist") if not request.user.is_anonymous: follower = VirtualRaceFollower( user=request.user, race=race, - emailaddress = request.user.email + emailaddress=request.user.email ) follower.save() - messages.info(request,"You will receive challenge notifications per email") + messages.info( + request, "You will receive challenge notifications per email") url = reverse(virtualevent_view, - kwargs={'id':id}) + kwargs={'id': id}) return HttpResponseRedirect(url) # Anonymous - if request.method == 'POST': # pragma: no cover + if request.method == 'POST': # pragma: no cover form = FollowerForm(request.POST) if form.is_valid(): email = form.cleaned_data['emailaddress'] follower = VirtualRaceFollower( race=race, - emailaddress = email + emailaddress=email ) follower.save() - messages.info(request,"You will receive challenge notifications per email") + messages.info( + request, "You will receive challenge notifications per email") url = reverse(virtualevent_view, - kwargs={'id':id}) + kwargs={'id': id}) return HttpResponseRedirect(url) - else: # pragma: no cover + else: # pragma: no cover form = FollowerForm() breadcrumbs = [ { - 'url':reverse('virtualevents_view'), + 'url': reverse('virtualevents_view'), 'name': 'Challenges' - }, + }, { - 'url':reverse('virtualevent_view', - kwargs={'id':race.id} - ), + 'url': reverse('virtualevent_view', + kwargs={'id': race.id} + ), 'name': race.name - }, + }, { 'url': reverse(addfollower_view, - kwargs = {'id':race.id} + kwargs={'id': race.id} ), 'name': 'Follow' - } - ] # pragma: no cover + } + ] # pragma: no cover - return render(request,'followerform.html', + return render(request, 'followerform.html', { - 'form':form, - 'active':'nav-racing', - 'breadcrumbs':breadcrumbs, + 'form': form, + 'active': 'nav-racing', + 'breadcrumbs': breadcrumbs, } - ) # pragma: no cover + ) # pragma: no cover + @login_required() -def virtualevent_entry_edit_view(request,id=0,entryid=0): +def virtualevent_entry_edit_view(request, id=0, entryid=0): r = getrower(request.user) try: race = VirtualRace.objects.get(id=id) - except VirtualRace.DoesNotExist: # pragma: no cover + except VirtualRace.DoesNotExist: # pragma: no cover raise Http404("Virtual Challenge does not exist") - if not race_can_editentry(r,race): # pragma: no cover - messages.error(request,'You cannot change your entries for this challenge') + if not race_can_editentry(r, race): # pragma: no cover + messages.error( + request, 'You cannot change your entries for this challenge') url = reverse('virtualevent_view', - kwargs={'id':race.id} + kwargs={'id': race.id} ) return HttpResponseRedirect(url) @@ -3527,33 +3519,32 @@ def virtualevent_entry_edit_view(request,id=0,entryid=0): if race.sessiontype == 'race': resultobj = VirtualRaceResult formobj = VirtualRaceResultForm - elif race.sessiontype in ['fastest_distance','fastest_time']: # pragma: no cover + elif race.sessiontype in ['fastest_distance', 'fastest_time']: # pragma: no cover resultobj = IndoorVirtualRaceResult formobj = VirtualRaceResultForm - else: # pragma: no cover + else: # pragma: no cover resultobj = IndoorVirtualRaceResult formobj = IndoorVirtualRaceResultForm records = resultobj.objects.filter( - userid = r.id, + userid=r.id, race=race - ).exclude(id=entryid) + ).exclude(id=entryid) try: record = resultobj.objects.get(id=entryid) - except resultobj.DoesNotExist: # pragma: no cover + except resultobj.DoesNotExist: # pragma: no cover raise Http404("Could not find your entry") if request.method == 'POST': - form = formobj(request.POST,categories=categories) + form = formobj(request.POST, categories=categories) if form.is_valid(): cd = form.cleaned_data - teamname = cd['teamname'] try: boattype = cd['boattype'] - except KeyError: # pragma: no cover + except KeyError: # pragma: no cover boattype = None boatclass = cd['boatclass'] weightcategory = cd['weightcategory'] @@ -3561,13 +3552,13 @@ def virtualevent_entry_edit_view(request,id=0,entryid=0): age = cd['age'] try: mix = cd['mix'] - except KeyError: # pragma: no cover + except KeyError: # pragma: no cover mix = None acceptsocialmedia = cd['acceptsocialmedia'] sex = r.sex - if mix: # pragma: no cover + if mix: # pragma: no cover sex = 'mixed' if boattype == '1x' and r.birthdate: @@ -3576,13 +3567,12 @@ def virtualevent_entry_edit_view(request,id=0,entryid=0): if sex == 'not specified': sex = 'male' - coursestandard = None referencespeed = 5.0 returnurl = reverse(virtualevent_entry_edit_view, - kwargs={'id':race.id, - 'entryid':record.id}) + kwargs={'id': race.id, + 'entryid': record.id}) if race.coursestandards is not None: coursestandard = cd['entrycategory'] @@ -3593,85 +3583,97 @@ def virtualevent_entry_edit_view(request,id=0,entryid=0): adaptiveclass = coursestandard.adaptiveclass skillclass = coursestandard.skillclass - if age < coursestandard.agemin: # pragma: no cover - messages.error(request,'You are younger than the minimum age for this group') + if age < coursestandard.agemin: # pragma: no cover + messages.error( + request, 'You are younger than the minimum age for this group') return HttpResponseRedirect(returnurl) - if age > coursestandard.agemax: # pragma: no cover - messages.error(request,'You are older than the maximum age for this group') + if age > coursestandard.agemax: # pragma: no cover + messages.error( + request, 'You are older than the maximum age for this group') return HttpResponseRedirect(returnurl) - if sex == 'male' and coursestandard.sex != 'male': # pragma: no cover - messages.error(request,'Men are not allowed to enter this category') + if sex == 'male' and coursestandard.sex != 'male': # pragma: no cover + messages.error( + request, 'Men are not allowed to enter this category') return HttpResponseRedirect(returnurl) - if sex == 'mixed' and coursestandard.sex not in ['mixed','male']: # pragma: no cover - messages.error(request,'Mixed crews are not allowed to enter this category') + if sex == 'mixed' and coursestandard.sex not in ['mixed', 'male']: # pragma: no cover + messages.error( + request, 'Mixed crews are not allowed to enter this category') return HttpResponseRedirect(returnurl) - if record.workoutid: # pragma: no cover + if record.workoutid: # pragma: no cover try: w = Workout.objects.get(id=record.workoutid) - except Workout.DoesNotExist: # pragma: no cover + except Workout.DoesNotExist: # pragma: no cover w = None if w is not None: if boattype != w.boattype: - messages.error(request,'You cannot change boat type to a different one than your row') + messages.error( + request, 'You cannot change boat type to a different one than your row') return HttpResponseRedirect(returnurl) if boatclass != w.workouttype: - messages.error(request,'You cannot change the class to a different one than your row') + messages.error( + request, 'You cannot change the class to a different one than your row') return HttpResponseRedirect(returnurl) if weightcategory != w.weightcategory: - messages.error(request,'You cannot change weight class to a different one than your row') + messages.error( + request, 'You cannot change weight class to a different one than your row') return HttpResponseRedirect(returnurl) if adaptiveclass != w.adaptiveclass: - messages.error(request,'You cannot change adaptive class to a different one than your row') + messages.error( + request, 'You cannot change adaptive class to a different one than your row') return HttpResponseRedirect(returnurl) else: if boattype != record.boattype: - messages.error(request,'You cannot change boat type to a different one ') + messages.error( + request, 'You cannot change boat type to a different one ') return HttpResponseRedirect(returnurl) if boatclass != record.boatclass: - messages.error(request,'You cannot change the class to a different one ') + messages.error( + request, 'You cannot change the class to a different one ') return HttpResponseRedirect(returnurl) if weightcategory != record.weightcategory: - messages.error(request,'You cannot change weight class to a different one ') + messages.error( + request, 'You cannot change weight class to a different one ') return HttpResponseRedirect(returnurl) if adaptiveclass != record.adaptiveclass: - messages.error(request,'You cannot change adaptive class to a different one ') + messages.error( + request, 'You cannot change adaptive class to a different one ') return HttpResponseRedirect(returnurl) - if record.points != 0: # pragma: no cover + if record.points != 0: # pragma: no cover if race.sessiontype == 'race': coursedistance = race.course.distance else: coursedistance = record.distance - v = coursedistance/timefield_to_seconds_duration(record.duration) + v = coursedistance / \ + timefield_to_seconds_duration(record.duration) points = 100.*(2-(referencespeed/v)) record.points = points - record.teamname = teamname - record.weightcategory=weightcategory - record.adaptiveclass=adaptiveclass - record.boatclass=boatclass + record.weightcategory = weightcategory + record.adaptiveclass = adaptiveclass + record.boatclass = boatclass if race.sessiontype == 'race': record.boattype = boattype record.mix = mix - record.sex=sex - record.age=age - record.entrycategory=coursestandard - record.referencespeed=referencespeed - record.acceptsocialmedia=acceptsocialmedia + record.sex = sex + record.age = age + record.entrycategory = coursestandard + record.referencespeed = referencespeed + record.acceptsocialmedia = acceptsocialmedia duplicates = False for otherrecord in records: - if record.isduplicate(otherrecord): # pragma: no cover + if record.isduplicate(otherrecord): # pragma: no cover duplicates = True - if duplicates: # pragma: no cover - messages.error(request,"You have already entered this group") + if duplicates: # pragma: no cover + messages.error(request, "You have already entered this group") return HttpResponseRedirect(returnurl) else: record.save() @@ -3682,60 +3684,59 @@ def virtualevent_entry_edit_view(request,id=0,entryid=0): ) url = reverse('virtualevent_view', - kwargs = { - 'id':race.id + kwargs={ + 'id': race.id }) return HttpResponseRedirect(url) else: - form = formobj(instance=record,categories=categories) - + form = formobj(instance=record, categories=categories) breadcrumbs = [ { - 'url':reverse('virtualevents_view'), + 'url': reverse('virtualevents_view'), 'name': 'Challenges' - }, + }, { - 'url':reverse('virtualevent_view', - kwargs={'id':race.id} - ), + 'url': reverse('virtualevent_view', + kwargs={'id': race.id} + ), 'name': race.name - }, + }, { 'url': reverse(virtualevent_entry_edit_view, - kwargs = {'id':race.id, - 'entryid':entryid} + kwargs={'id': race.id, + 'entryid': entryid} ), 'name': 'Follow' - } - ] + } + ] buttons = [] if not request.user.is_anonymous: - if race_can_register(r,race): # pragma: no cover + if race_can_register(r, race): # pragma: no cover buttons += ['registerbutton'] - if race_can_adddiscipline(r,race): + if race_can_adddiscipline(r, race): buttons += ['adddisciplinebutton'] - if race_can_submit(r,race): + if race_can_submit(r, race): buttons += ['submitbutton'] - if race_can_resubmit(r,race): # pragma: no cover + if race_can_resubmit(r, race): # pragma: no cover buttons += ['resubmitbutton'] - if race_can_withdraw(r,race): # pragma: no cover + if race_can_withdraw(r, race): # pragma: no cover buttons += ['withdrawbutton'] - if race_can_edit(r,race): + if race_can_edit(r, race): buttons += ['editbutton'] - return render(request,'entryedit.html', + return render(request, 'entryedit.html', { - 'form':form, - 'active':'nav-racing', - 'breadcrumbs':breadcrumbs, - 'userid':r.user.id, - 'race':race, + 'form': form, + 'active': 'nav-racing', + 'breadcrumbs': breadcrumbs, + 'userid': r.user.id, + 'race': race, }) diff --git a/rowers/views/statements.py b/rowers/views/statements.py index 649c0e13..b27191c1 100644 --- a/rowers/views/statements.py +++ b/rowers/views/statements.py @@ -1,4 +1,38 @@ +import rowers.teams as teams +from rowers.serializers import RowerSerializer, WorkoutSerializer +from rq import Queue, cancel_job +from redis import StrictRedis, Redis +from rowers.models import C2WorldClassAgePerformance +from rowers.utils import ProcessorCustomerError +import rowers.datautils as datautils +from rowers.utils import ( + geo_distance, serialize_list, deserialize_list, uniqify, + str2bool, range_to_color_hex, absolute, myqueue, get_call, + calculate_age, rankingdistances, rankingdurations, + my_dict_from_instance, wavg, NoTokenError, + request_is_ajax, dologging +) +from rowers.celery import result as celery_result +from rowers.interactiveplots import * +from scipy.interpolate import griddata +from rowers.dataprep import getsmallrowdata_db +from rowers.dataprep import timedeltaconv +from scipy.special import lambertw +from io import BytesIO +import rowers.plots as plots +from rowers.permissions import IsOwnerOrNot, IsCompetitorOrNot +from rest_framework.permissions import IsAuthenticated +from rest_framework.decorators import api_view, renderer_classes, permission_classes +from rest_framework.response import Response +from rq.job import Job +from rules.contrib.views import permission_required, objectgetter +from django.core.cache import cache +from django.utils.crypto import get_random_string +from rq.registry import StartedJobRegistry +from rq.exceptions import NoSuchJobError +import threading +import redis import time import colorsys @@ -39,47 +73,47 @@ import rowers.payments as payments from rowers.opaque import encoder from rowers.rower_rules import ( - ispromember,is_coach_user,is_team_member,is_rower_team_member, - is_workout_user,isplanmember,can_delete_session, - can_view_target,can_change_target,can_delete_target, - can_view_plan,can_change_plan,can_delete_plan, - can_view_cycle,can_change_cycle,can_delete_cycle, - can_add_workout_member,can_plan_user,is_paid_coach, - can_start_trial, can_start_plantrial,can_plan,is_workout_team, + ispromember, is_coach_user, is_team_member, is_rower_team_member, + is_workout_user, isplanmember, can_delete_session, + can_view_target, can_change_target, can_delete_target, + can_view_plan, can_change_plan, can_delete_plan, + can_view_cycle, can_change_cycle, can_delete_cycle, + can_add_workout_member, can_plan_user, is_paid_coach, + can_start_trial, can_start_plantrial, can_plan, is_workout_team, is_promember, - ) +) from django.shortcuts import render from django.template.loader import render_to_string -from django.views.generic.edit import UpdateView,DeleteView +from django.views.generic.edit import UpdateView, DeleteView from django.http import ( HttpResponse, HttpResponseRedirect, JsonResponse, HttpResponseForbidden, HttpResponseNotAllowed, - HttpResponseNotFound,Http404 - ) + HttpResponseNotFound, Http404 +) from django.contrib.auth import authenticate, login, logout from rowers.forms import ( - ForceCurveOptionsForm,HistoForm,TeamMessageForm, - LoginForm,DocumentsForm,UploadOptionsForm,ImageForm,CourseForm, - CourseConfirmForm,ResampleForm, - TeamUploadOptionsForm,WorkFlowLeftPanelForm,WorkFlowMiddlePanelForm, - WorkFlowLeftPanelElement,WorkFlowMiddlePanelElement, - LandingPageForm,PlannedSessionSelectForm,WorkoutSessionSelectForm, - PlannedSessionTeamForm,PlannedSessionTeamMemberForm, - VirtualRaceSelectForm,WorkoutRaceSelectForm,CourseSelectForm, - RaceResultFilterForm,PowerIntervalUpdateForm,FlexAxesForm, - FlexOptionsForm,DataFrameColumnsForm,OteWorkoutTypeForm, - MetricsForm,DisqualificationForm,disqualificationreasons, - disqualifiers,SearchForm,BillingForm,PlanSelectForm, - VideoAnalysisCreateForm,WorkoutSingleSelectForm, - VideoAnalysisMetricsForm,SurveyForm,HistorySelectForm, - StravaChartForm,FitnessFitForm,PerformanceManagerForm, - TrainingPlanBillingForm,InstantPlanSelectForm, + ForceCurveOptionsForm, HistoForm, TeamMessageForm, + LoginForm, DocumentsForm, UploadOptionsForm, ImageForm, CourseForm, + CourseConfirmForm, ResampleForm, + TeamUploadOptionsForm, WorkFlowLeftPanelForm, WorkFlowMiddlePanelForm, + WorkFlowLeftPanelElement, WorkFlowMiddlePanelElement, + LandingPageForm, PlannedSessionSelectForm, WorkoutSessionSelectForm, + PlannedSessionTeamForm, PlannedSessionTeamMemberForm, + VirtualRaceSelectForm, WorkoutRaceSelectForm, CourseSelectForm, + RaceResultFilterForm, PowerIntervalUpdateForm, FlexAxesForm, + FlexOptionsForm, DataFrameColumnsForm, OteWorkoutTypeForm, + MetricsForm, DisqualificationForm, disqualificationreasons, + disqualifiers, SearchForm, BillingForm, PlanSelectForm, + VideoAnalysisCreateForm, WorkoutSingleSelectForm, + VideoAnalysisMetricsForm, SurveyForm, HistorySelectForm, + StravaChartForm, FitnessFitForm, PerformanceManagerForm, + TrainingPlanBillingForm, InstantPlanSelectForm, TrainingZonesForm, - ) +) from django.urls import reverse, reverse_lazy @@ -89,69 +123,70 @@ from django.core.paginator import Paginator, EmptyPage, PageNotAnInteger from django.conf import settings from django.urls import resolve from django.utils.datastructures import MultiValueDictKeyError -from django.utils import timezone,translation +from django.utils import timezone, translation from django.core.mail import send_mail, BadHeaderError from rowers.forms import ( - SummaryStringForm,StrokeDataForm, - StatsOptionsForm,PredictedPieceForm,DateRangeForm,DeltaDaysForm, - FitnessMetricForm,PredictedPieceFormNoDistance, + SummaryStringForm, StrokeDataForm, + StatsOptionsForm, PredictedPieceForm, DateRangeForm, DeltaDaysForm, + FitnessMetricForm, PredictedPieceFormNoDistance, EmailForm, RegistrationForm, RegistrationFormTermsOfService, - RegistrationFormUniqueEmail,RegistrationFormSex, - CNsummaryForm,UpdateWindForm, + RegistrationFormUniqueEmail, RegistrationFormSex, + CNsummaryForm, UpdateWindForm, StandardsForm, - UpdateStreamForm,WorkoutMultipleCompareForm,ChartParamChoiceForm, - FusionMetricChoiceForm,BoxPlotChoiceForm,MultiFlexChoiceForm, - TrendFlexModalForm,WorkoutSplitForm,WorkoutJoinParamForm, + UpdateStreamForm, WorkoutMultipleCompareForm, ChartParamChoiceForm, + FusionMetricChoiceForm, BoxPlotChoiceForm, MultiFlexChoiceForm, + TrendFlexModalForm, WorkoutSplitForm, WorkoutJoinParamForm, AnalysisOptionsForm, AnalysisChoiceForm, - PlannedSessionMultipleCloneForm,SessionDateShiftForm,RowerTeamForm, - ) + PlannedSessionMultipleCloneForm, SessionDateShiftForm, RowerTeamForm, +) from rowers.models import ( - Workout, User, Rower, WorkoutForm,FavoriteChart, - PlannedSession, DeactivateUserForm,DeleteUserForm, - TrainingPlan,TrainingPlanForm,TrainingTarget,TrainingTargetForm, - TrainingMacroCycle,TrainingMesoCycle,TrainingMicroCycle, - TrainingTarget,TrainingTargetForm, - TrainingMacroCycleForm,createmacrofillers, + Workout, User, Rower, WorkoutForm, FavoriteChart, + PlannedSession, DeactivateUserForm, DeleteUserForm, + TrainingPlan, TrainingPlanForm, TrainingTarget, TrainingTargetForm, + TrainingMacroCycle, TrainingMesoCycle, TrainingMicroCycle, + TrainingTarget, TrainingTargetForm, + TrainingMacroCycleForm, createmacrofillers, createmicrofillers, createmesofillers, - microcyclecheckdates,mesocyclecheckdates,macrocyclecheckdates, + microcyclecheckdates, mesocyclecheckdates, macrocyclecheckdates, TrainingMesoCycleForm, TrainingMicroCycleForm, - RaceLogo,RowerBillingAddressForm,PaidPlan, + RaceLogo, RowerBillingAddressForm, PaidPlan, AlertEditForm, ConditionEditForm, - PlannedSessionComment,CoachRequest,CoachOffer, - VideoAnalysis,ShareKey, - StandardCollection,CourseStandard, - VirtualRaceFollower,TombStone, InstantPlan - ) + PlannedSessionComment, CoachRequest, CoachOffer, + VideoAnalysis, ShareKey, + StandardCollection, CourseStandard, + VirtualRaceFollower, TombStone, InstantPlan +) from rowers.models import ( - RowerPowerForm,RowerHRZonesForm,RowerForm,RowerCPForm,GraphImage,AdvancedWorkoutForm, - RowerPowerZonesForm,AccountRowerForm,UserForm, - Team,TeamForm,TeamInviteForm,TeamInvite,TeamRequest, - WorkoutComment,WorkoutCommentForm,RowerExportForm, + RowerPowerForm, RowerHRZonesForm, RowerForm, RowerCPForm, GraphImage, AdvancedWorkoutForm, + RowerPowerZonesForm, AccountRowerForm, UserForm, + Team, TeamForm, TeamInviteForm, TeamInvite, TeamRequest, + WorkoutComment, WorkoutCommentForm, RowerExportForm, CalcAgePerformance, - PowerTimeFitnessMetric,BlogPost, - PlannedSessionForm,PlannedSessionTemplateForm, - PlannedSessionFormSmall,GeoCourseEditForm,VirtualRace, - VirtualRaceForm,VirtualRaceResultForm,RowerImportExportForm, - IndoorVirtualRaceResultForm,IndoorVirtualRaceResult, - IndoorVirtualRaceForm,PlannedSessionCommentForm, + PowerTimeFitnessMetric, BlogPost, + PlannedSessionForm, PlannedSessionTemplateForm, + PlannedSessionFormSmall, GeoCourseEditForm, VirtualRace, + VirtualRaceForm, VirtualRaceResultForm, RowerImportExportForm, + IndoorVirtualRaceResultForm, IndoorVirtualRaceResult, + IndoorVirtualRaceForm, PlannedSessionCommentForm, Alert, Condition, StaticChartRowerForm, - FollowerForm,VirtualRaceAthleteForm,InstantPlanForm,DataRowerForm - ) + FollowerForm, VirtualRaceAthleteForm, InstantPlanForm, DataRowerForm +) from rowers.models import ( - FavoriteForm,BaseFavoriteFormSet,SiteAnnouncement,BasePlannedSessionFormSet, - get_course_timezone,BaseConditionFormSet, - ) -from rowers.metrics import rowingmetrics,defaultfavoritecharts,nometrics,metricsgroups + FavoriteForm, BaseFavoriteFormSet, SiteAnnouncement, BasePlannedSessionFormSet, + get_course_timezone, BaseConditionFormSet, +) +from rowers.metrics import rowingmetrics, defaultfavoritecharts, nometrics, metricsgroups from rowers import metrics as metrics from rowers import courses as courses import rowers.uploads as uploads from django.forms.formsets import formset_factory from django.forms import modelformset_factory -from django.contrib.auth.decorators import login_required #,user_passes_test +from django.contrib.auth.decorators import login_required # ,user_passes_test from rowers.decorators import user_passes_test -from time import strftime,strptime,mktime,time,daylight -import os,sys +from time import strftime, strptime, mktime, time, daylight +import os +import sys import datetime import iso8601 import rowers.c2stuff as c2stuff @@ -179,30 +214,30 @@ from rowsandall_app.settings import ( POLAR_CLIENT_ID, POLAR_REDIRECT_URI, POLAR_CLIENT_SECRET, SPORTTRACKS_CLIENT_ID, SPORTTRACKS_REDIRECT_URI, SPORTTRACKS_CLIENT_SECRET, - TP_CLIENT_ID,TP_REDIRECT_URI,TP_CLIENT_KEY,TP_CLIENT_SECRET, - RP3_CLIENT_ID,RP3_REDIRECT_URI,RP3_CLIENT_KEY,RP3_CLIENT_SECRET, - BRAINTREE_MERCHANT_ID,BRAINTREE_PUBLIC_KEY,BRAINTREE_PRIVATE_KEY, + TP_CLIENT_ID, TP_REDIRECT_URI, TP_CLIENT_KEY, TP_CLIENT_SECRET, + RP3_CLIENT_ID, RP3_REDIRECT_URI, RP3_CLIENT_KEY, RP3_CLIENT_SECRET, + BRAINTREE_MERCHANT_ID, BRAINTREE_PUBLIC_KEY, BRAINTREE_PRIVATE_KEY, PAYMENT_PROCESSING_ON, RECAPTCHA_SITE_KEY, RECAPTCHA_SITE_SECRET, NK_REDIRECT_URI, NK_CLIENT_ID, NK_CLIENT_SECRET - ) +) #from rowers.tasks_standalone import addcomment2 from django.contrib import messages from async_messages import messages as a_messages -from django.contrib.admin.widgets import AdminDateWidget,AdminTimeWidget,AdminSplitDateTime +from django.contrib.admin.widgets import AdminDateWidget, AdminTimeWidget, AdminSplitDateTime import requests import json from rest_framework.renderers import JSONRenderer from rest_framework.parsers import JSONParser -from rowers.rows import handle_uploaded_file,handle_uploaded_image +from rowers.rows import handle_uploaded_file, handle_uploaded_image from rowers.plannedsessions import * -from rowers.tasks import handle_makeplot,handle_otwsetpower,handle_sendemailtcx,handle_sendemailcsv +from rowers.tasks import handle_makeplot, handle_otwsetpower, handle_sendemailtcx, handle_sendemailcsv from rowers.tasks import ( - handle_sendemail_unrecognized,handle_sendemailnewcomment, + handle_sendemail_unrecognized, handle_sendemailnewcomment, handle_request_post, handle_sendemailsummary, handle_rp3_async_workout, @@ -212,8 +247,8 @@ from rowers.tasks import ( handle_sendemailfile, handle_sendemailkml, handle_sendemailnewresponse, handle_updatedps, - handle_updatecp,long_test_task,long_test_task2, - handle_zip_file,handle_getagegrouprecords, + handle_updatecp, long_test_task, long_test_task2, + handle_zip_file, handle_getagegrouprecords, handle_update_empower, handle_sendemailics, handle_sendemail_userdeleted, @@ -224,7 +259,7 @@ from rowers.tasks import ( handle_c2_async_workout, handle_send_email_instantplan_notification, handle_nk_async_workout, - ) +) from scipy.signal import savgol_filter #from django.shortcuts import render_to_response @@ -232,7 +267,7 @@ try: from Cookie import SimpleCookie except ModuleNotFoundError: from http.cookies import SimpleCookie -from shutil import copyfile,move +from shutil import copyfile, move import rowers.mytypes as mytypes from rowingdata import rower as rrower from rowingdata import main as rmain @@ -243,37 +278,25 @@ import pandas as pd import numpy as np import matplotlib.pyplot as plt -from rowers.emails import send_template_email,htmlstrip, send_confirm +from rowers.emails import send_template_email, htmlstrip, send_confirm from rowers.alerts import * -from pytz import timezone as tz,utc +from pytz import timezone as tz, utc from timezonefinder import TimezoneFinder import dateutil import mpld3 from mpld3 import plugins import stravalib -from stravalib.exc import ActivityUploadFailed,TimeoutExceeded -from rowers.weather import get_wind_data,get_airport_code,get_metar_data +from stravalib.exc import ActivityUploadFailed, TimeoutExceeded +from rowers.weather import get_wind_data, get_airport_code, get_metar_data -from oauth2_provider.models import Application,Grant,AccessToken +from oauth2_provider.models import Application, Grant, AccessToken import django_rq queue = django_rq.get_queue('default') queuelow = django_rq.get_queue('low') queuehigh = django_rq.get_queue('high') -import redis -import threading -from redis import StrictRedis,Redis -from rq.exceptions import NoSuchJobError -from rq.registry import StartedJobRegistry -from rq import Queue,cancel_job - -from django.utils.crypto import get_random_string - -from django.core.cache import cache - -from rules.contrib.views import permission_required, objectgetter def get_totals(workouts): totalseconds = 0 @@ -281,78 +304,90 @@ def get_totals(workouts): for w in workouts: totalmeters += w.distance - totalseconds += 60*(w.duration.hour*60+w.duration.minute)+w.duration.second + totalseconds += 60*(w.duration.hour*60 + + w.duration.minute)+w.duration.second - hours, remainder = divmod(totalseconds,3600) - minutes, seconds = divmod(remainder,60) + hours, remainder = divmod(totalseconds, 3600) + minutes, seconds = divmod(remainder, 60) - return totalmeters,hours, minutes, seconds + return totalmeters, hours, minutes, seconds # creating shareable views + + def allow_shares(view_func): def sharify(request, *args, **kwargs): shared = kwargs.get('__shared', None) - if shared is not None: # pragma: no cover + if shared is not None: # pragma: no cover del kwargs["__shared"] request.session['shared'] = True return view_func(request, *args, **kwargs) - else: return login_required(view_func)(request, *args, **kwargs) + else: + return login_required(view_func)(request, *args, **kwargs) return sharify + class SharifyError(Exception): pass + def sharedPage(request, key): try: try: shareKey = ShareKey.objects.get(pk=key) - except: # pragma: no cover + except: # pragma: no cover raise SharifyError - if shareKey.expired: # pragma: no cover + if shareKey.expired: # pragma: no cover raise SharifyError func, args, kwargs = resolve(shareKey.location) kwargs["__shared"] = True return func(request, *args, **kwargs) - except SharifyError: # pragma: no cover - raise Http404 # or add a more detailed error page. This either means that the key doesn’t exist or is expired. + except SharifyError: # pragma: no cover + # or add a more detailed error page. This either means that the key doesn’t exist or is expired. + raise Http404 + def createShareURL(request): if request.method == 'POST': url = request.POST['url'] ndays = int(request.POST['ndays']) key = ShareKey.objects.create(pk=get_random_string(40), - expiration_seconds=60*60*24*ndays, - location = url) + expiration_seconds=60*60*24*ndays, + location=url) key.save() - return render(request, 'share.html', {"key":key}) - else: # pragma: no cover + return render(request, 'share.html', {"key": key}) + else: # pragma: no cover raise Http404 -def createShareModel(request, model_id): # pragma: no cover + +def createShareModel(request, model_id): # pragma: no cover task = MyModel.objects.get(pk=model_id) key = ShareKey.objects.create(pk=get_random_string(40), - expiration_seconds=60*60*24, # 1 day - location = task.get_absolute_url(), + expiration_seconds=60*60*24, # 1 day + location=task.get_absolute_url(), ) key.save() - return render(request, 'share.html', {"key":key}) + return render(request, 'share.html', {"key": key}) # Utility to get stroke data in a JSON response + + class JSONResponse(HttpResponse): def __init__(self, data, **kwargs): content = JSONRenderer().render(data) kwargs['content_type'] = 'application/json' super(JSONResponse, self).__init__(content, **kwargs) -def getfavorites(r,row): + +def getfavorites(r, row): workouttype = 'ote' if row.workouttype in mytypes.otwtypes: workouttype = 'otw' - matchworkouttypes = [workouttype,'all'] + matchworkouttypes = [workouttype, 'all'] workoutsource = row.workoutsource - if 'speedcoach2' in row.workoutsource: # pragma: no cover + if 'speedcoach2' in row.workoutsource: # pragma: no cover workoutsource = 'speedcoach2' favorites = FavoriteChart.objects.filter(user=r, @@ -360,69 +395,79 @@ def getfavorites(r,row): favorites2 = FavoriteChart.objects.filter(user=r, workouttype__in=[workoutsource]).order_by("id") - - favorites = favorites | favorites2 maxfav = len(favorites)-1 + return favorites, maxfav - return favorites,maxfav -def get_logo_by_pk(request,*args,**kwargs): # pragma: no cover +def get_logo_by_pk(request, *args, **kwargs): # pragma: no cover id = kwargs['id'] - return get_object_or_404(RaceLogo,pk=id) + return get_object_or_404(RaceLogo, pk=id) -def get_virtualevent_by_pk(request,*args,**kwargs): + +def get_virtualevent_by_pk(request, *args, **kwargs): id = kwargs['id'] - return get_object_or_404(VirtualRace,pk=id) + return get_object_or_404(VirtualRace, pk=id) -def get_promember(request,*args,**kwargs): # pragma: no cover + +def get_promember(request, *args, **kwargs): # pragma: no cover return request.user -def get_course_by_pk(request,*args,**kwargs): - id = kwargs['id'] - return get_object_or_404(GeoCourse,pk=id) -def get_workout_by_opaqueid(request,id,**kwargs): +def get_course_by_pk(request, *args, **kwargs): + id = kwargs['id'] + return get_object_or_404(GeoCourse, pk=id) + + +def get_workout_by_opaqueid(request, id, **kwargs): pk = encoder.decode_hex(id) - return get_object_or_404(Workout,pk=pk) + return get_object_or_404(Workout, pk=pk) -def get_session_by_pk(request,*args,**kwargs): + +def get_session_by_pk(request, *args, **kwargs): id = kwargs['id'] - return get_object_or_404(PlannedSession,pk=id) + return get_object_or_404(PlannedSession, pk=id) -def get_target_by_pk(request,*args,**kwargs): + +def get_target_by_pk(request, *args, **kwargs): id = kwargs['id'] - return get_object_or_404(TrainingTarget,pk=id) + return get_object_or_404(TrainingTarget, pk=id) -def get_plan_by_pk(request,*args,**kwargs): + +def get_plan_by_pk(request, *args, **kwargs): id = kwargs['id'] - return get_object_or_404(TrainingPlan,pk=id) + return get_object_or_404(TrainingPlan, pk=id) -def get_macro_by_pk(request,*args,**kwargs): # pragma: no cover + +def get_macro_by_pk(request, *args, **kwargs): # pragma: no cover id = kwargs['id'] - return get_object_or_404(TrainingMacroCycle,pk=id) + return get_object_or_404(TrainingMacroCycle, pk=id) -def get_meso_by_pk(request,*args,**kwargs): # pragma: no cover + +def get_meso_by_pk(request, *args, **kwargs): # pragma: no cover id = kwargs['id'] - return get_object_or_404(TrainingMesoCycle,pk=id) + return get_object_or_404(TrainingMesoCycle, pk=id) -def get_micro_by_pk(request,*args,**kwargs): # pragma: no cover + +def get_micro_by_pk(request, *args, **kwargs): # pragma: no cover id = kwargs['id'] - return get_object_or_404(TrainingMicroCycle,pk=id) + return get_object_or_404(TrainingMicroCycle, pk=id) -def get_workout_default_page(request,id): + +def get_workout_default_page(request, id): if request.user.is_anonymous: - return reverse('workout_view',kwargs={'id':id}) + return reverse('workout_view', kwargs={'id': id}) else: r = Rower.objects.get(user=request.user) if r.defaultlandingpage == 'workout_edit_view': - return reverse('workout_edit_view',kwargs={'id':id}) - else: # pragma: no cover - return reverse('workout_workflow_view',kwargs={'id':id}) + return reverse('workout_edit_view', kwargs={'id': id}) + else: # pragma: no cover + return reverse('workout_workflow_view', kwargs={'id': id}) -def get_user_by_userid(*args,**kwargs): + +def get_user_by_userid(*args, **kwargs): request = args[0] try: id = kwargs['userid'] @@ -432,10 +477,11 @@ def get_user_by_userid(*args,**kwargs): if id is not None and int(id) == 0: id = request.user.id - u = get_object_or_404(User,pk=id) + u = get_object_or_404(User, pk=id) return u -def get_user_by_id(*args,**kwargs): # pragma: no cover + +def get_user_by_id(*args, **kwargs): # pragma: no cover request = args[0] try: id = args[1] @@ -445,17 +491,19 @@ def get_user_by_id(*args,**kwargs): # pragma: no cover except KeyError: id = request.user.id - return get_object_or_404(User,pk=id) + return get_object_or_404(User, pk=id) -def get_rower_by_id(request,id): # pragma: no cover + +def get_rower_by_id(request, id): # pragma: no cover u = User.objects.get(id=id) return u.rower -def getrequestrower(request,rowerid=0,userid=0,notpermanent=False): + +def getrequestrower(request, rowerid=0, userid=0, notpermanent=False): userid = int(userid) rowerid = int(rowerid) - #if userid == 0: + # if userid == 0: # userid = request.user.id if notpermanent == False: @@ -473,21 +521,20 @@ def getrequestrower(request,rowerid=0,userid=0,notpermanent=False): elif userid != 0: u = User.objects.get(id=userid) r = getrower(u) - elif request.user.is_anonymous: # pragma: no cover + elif request.user.is_anonymous: # pragma: no cover return None else: r = getrower(request.user) u = r.user - - except Rower.DoesNotExist: # pragma: no cover: # pragma: no cover + except Rower.DoesNotExist: # pragma: no cover: # pragma: no cover raise Http404("Rower doesn't exist") if r.user == request.user: request.session['rowerid'] = r.id return r - if userid != 0 and not is_rower_team_member(request.user,u.rower): + if userid != 0 and not is_rower_team_member(request.user, u.rower): request.session['rowerid'] = request.user.rower.id raise PermissionDenied("You have no access to this user") @@ -497,11 +544,12 @@ def getrequestrower(request,rowerid=0,userid=0,notpermanent=False): request.session['rowerid'] = r.id return r -def getrequestrowercoachee(request,rowerid=0,userid=0,notpermanent=False): + +def getrequestrowercoachee(request, rowerid=0, userid=0, notpermanent=False): userid = int(userid) rowerid = int(rowerid) - #if userid == 0: + # if userid == 0: # userid = request.user.id if notpermanent == False: @@ -519,21 +567,20 @@ def getrequestrowercoachee(request,rowerid=0,userid=0,notpermanent=False): elif userid != 0: u = User.objects.get(id=userid) r = getrower(u) - elif request.user.is_anonymous: # pragma: no cover + elif request.user.is_anonymous: # pragma: no cover return None else: r = getrower(request.user) u = r.user - - except Rower.DoesNotExist: # pragma: no cover: # pragma: no cover + except Rower.DoesNotExist: # pragma: no cover: # pragma: no cover raise Http404("Rower doesn't exist") if r.user == request.user: request.session['rowerid'] = r.id return r - if userid != 0 and not is_coach_user(request.user,u): # pragma: no cover + if userid != 0 and not is_coach_user(request.user, u): # pragma: no cover request.session['rowerid'] = request.user.rower.id raise PermissionDenied("You have no access to this user") @@ -543,7 +590,8 @@ def getrequestrowercoachee(request,rowerid=0,userid=0,notpermanent=False): request.session['rowerid'] = r.id return r -def getrequestplanrower(request,rowerid=0,userid=0,notpermanent=False): + +def getrequestplanrower(request, rowerid=0, userid=0, notpermanent=False): userid = int(userid) rowerid = int(rowerid) @@ -562,19 +610,19 @@ def getrequestplanrower(request,rowerid=0,userid=0,notpermanent=False): elif userid != 0: try: u = User.objects.get(id=userid) - except User.DoesNotExist: # pragma: no cover: # pragma: no cover + except User.DoesNotExist: # pragma: no cover: # pragma: no cover raise Http404("User does not exist") r = getrower(u) else: r = getrower(request.user) - except Rower.DoesNotExist: # pragma: no cover: # pragma: no cover + except Rower.DoesNotExist: # pragma: no cover: # pragma: no cover raise Http404("Rower doesn't exist") - if 'shared' in request.session and request.session['shared']: # pragma: no cover + if 'shared' in request.session and request.session['shared']: # pragma: no cover return r - if r.user != request.user and not can_plan_user(request.user,r ): + if r.user != request.user and not can_plan_user(request.user, r): request.session['rowerid'] = r.id raise PermissionDenied("You have no access to this user") @@ -588,12 +636,12 @@ def getrower(user): try: if user is None or user.is_anonymous: return None - except AttributeError: # pragma: no cover + except AttributeError: # pragma: no cover if User.objects.get(id=user).is_anonymous: return None try: r = Rower.objects.get(user=user) - except Rower.DoesNotExist: # pragma: no cover: + except Rower.DoesNotExist: # pragma: no cover: r = Rower(user=user) r.save() @@ -604,24 +652,26 @@ def get_workout(id): try: id = encoder.decode_hex(id) w = Workout.objects.get(id=id) - except Workout.DoesNotExist: # pragma: no cover: + except Workout.DoesNotExist: # pragma: no cover: raise Http404("Workout doesn't exist") return w -def get_workoutuser(id,request): + +def get_workoutuser(id, request): try: id = encoder.decode_hex(id) w = Workout.objects.get(id=id) - except Workout.DoesNotExist: # pragma: no cover: + except Workout.DoesNotExist: # pragma: no cover: raise Http404("Workout doesn't exist") - if not is_workout_user(request.user,w): # pragma: no cover + if not is_workout_user(request.user, w): # pragma: no cover raise PermissionDenied return w -def getvalue(data): # pragma: no cover + +def getvalue(data): # pragma: no cover perc = 0 total = 1 done = 0 @@ -637,9 +687,10 @@ def getvalue(data): # pragma: no cover if i[0] == 'session_key': session_key = i[1] - return total,done,id,session_key + return total, done, id, session_key -class SessionTaskListener(threading.Thread): # pragma: no cover + +class SessionTaskListener(threading.Thread): # pragma: no cover def __init__(self, r, channels): threading.Thread.__init__(self) self.redis = r @@ -649,10 +700,10 @@ class SessionTaskListener(threading.Thread): # pragma: no cover def work(self, item): try: - data = json.loads(item['data']) - total,done,id,session_key = getvalue(data) + data = json.loads(item['data']) + total, done, id, session_key = getvalue(data) perc = int(100.*done/total) - cache.set(id,perc,3600) + cache.set(id, perc, 3600) except TypeError: pass @@ -666,66 +717,45 @@ class SessionTaskListener(threading.Thread): # pragma: no cover self.work(item) -queuefailed = Queue("failed",connection=Redis()) +queuefailed = Queue("failed", connection=Redis()) redis_connection = StrictRedis() r = Redis() # this doesn't yet work on production -#if settings.DEBUG: +# if settings.DEBUG: # client = SessionTaskListener(r,['tasks']) # client.start() -rq_registry = StartedJobRegistry(queue.name,connection=redis_connection) -rq_registryhigh = StartedJobRegistry(queuehigh.name,connection=redis_connection) -rq_registrylow = StartedJobRegistry(queuelow.name,connection=redis_connection) +rq_registry = StartedJobRegistry(queue.name, connection=redis_connection) +rq_registryhigh = StartedJobRegistry( + queuehigh.name, connection=redis_connection) +rq_registrylow = StartedJobRegistry(queuelow.name, connection=redis_connection) -from rq.job import Job try: from rest_framework_swagger.views import get_swagger_view -except ImportError: # pragma: no cover +except ImportError: # pragma: no cover pass -from rest_framework.renderers import JSONRenderer -from rest_framework.parsers import JSONParser -from rest_framework.response import Response -from rowers.serializers import RowerSerializer,WorkoutSerializer try: - from rest_framework import status,permissions,generics -except ImportError: # pragma: no cover + from rest_framework import status, permissions, generics +except ImportError: # pragma: no cover pass -from rest_framework.decorators import api_view, renderer_classes, permission_classes -from rest_framework.permissions import IsAuthenticated - - -from rowers.permissions import IsOwnerOrNot, IsCompetitorOrNot - -import rowers.plots as plots - - -from io import BytesIO -from scipy.special import lambertw - -from rowers.dataprep import timedeltaconv -from rowers.dataprep import getsmallrowdata_db - -from scipy.interpolate import griddata #LOCALTIMEZONE = tz('Etc/UTC') USER_LANGUAGE = 'en-US' -from rowers.interactiveplots import * -from rowers.celery import result as celery_result # Define the API documentation try: schema_view = get_swagger_view(title='Rowsandall API') -except NameError: # pragma: no cover +except NameError: # pragma: no cover pass -def remove_asynctask(request,id): # pragma: no cover + +def remove_asynctask(request, id): # pragma: no cover try: oldtasks = request.session['async_tasks'] except KeyError: @@ -734,11 +764,12 @@ def remove_asynctask(request,id): # pragma: no cover newtasks = [] for task in oldtasks: if id not in task[0]: - newtasks += [(task[0],task[1])] + newtasks += [(task[0], task[1])] request.session['async_tasks'] = newtasks -def get_job_result(jobid): # pragma: no cover + +def get_job_result(jobid): # pragma: no cover if settings.TESTING: return None elif settings.CELERY: @@ -751,13 +782,14 @@ def get_job_result(jobid): # pragma: no cover else: # job is ready try: - job = Job.fetch(jobid,connection=redis_connection) + job = Job.fetch(jobid, connection=redis_connection) result = job.result except NoSuchJobError: return None return result + verbose_job_status = { 'updatecp': 'Critical Power Calculation for Ergometer Workouts', 'updatecpwater': 'Critical Power Calculation for OTW Workouts', @@ -769,23 +801,23 @@ verbose_job_status = { 'update_empower': 'Correct Empower Inflated Power Bug', 'submit_race': 'Checking Race Course Result', 'check_race_course': 'Checking Course Result', - } +} -def get_job_status(jobid): # pragma: no cover + +def get_job_status(jobid): # pragma: no cover if settings.TESTING: summary = { 'status': 'failed', 'result': 0, 'finished': True, 'failed': True, - 'started_at':None, - } + 'started_at': None, + } return summary elif settings.CELERY: job = celery_result.AsyncResult(jobid) jobresult = job.result - if 'fail' in job.status.lower(): jobresult = '0' summary = { @@ -795,22 +827,22 @@ def get_job_status(jobid): # pragma: no cover } else: try: - job = Job.fetch(jobid,connection=redis_connection) + job = Job.fetch(jobid, connection=redis_connection) try: status = job.status except AttributeError: status = job.get_status() summary = { - 'status':status, - 'result':job.result, - 'started_at':job.started_at + 'status': status, + 'result': job.result, + 'started_at': job.started_at } except NoSuchJobError: summary = { 'status': 'success', 'result': 1, - 'started_at':None, + 'started_at': None, } try: @@ -831,36 +863,38 @@ def get_job_status(jobid): # pragma: no cover 'result': 0, 'finished': True, 'failed': True, - 'started_at':None, - } + 'started_at': None, + } return summary -def kill_async_job(request,id='aap'): # pragma: no cover + +def kill_async_job(request, id='aap'): # pragma: no cover if settings.CELERY: job = celery_result.AsyncResult(id) job.revoke() else: try: - cancel_job(id,connection=redis_connection) + cancel_job(id, connection=redis_connection) except NoSuchJobError: pass - remove_asynctask(request,id) + remove_asynctask(request, id) cache.delete(id) url = reverse(session_jobs_status) return HttpResponseRedirect(url) + @login_required() -def raise_500(request): # pragma: no cover +def raise_500(request): # pragma: no cover if request.user.is_superuser: raise ValueError else: return HttpResponse("invalid") -#@login_required() -#def test_job_view(request,aantal=100): +# @login_required() +# def test_job_view(request,aantal=100): # # session_key = request.session._session_key # @@ -877,8 +911,8 @@ def raise_500(request): # pragma: no cover # # return HttpResponseRedirect(url) -#@login_required() -#def test_job_view2(request,aantal=100): +# @login_required() +# def test_job_view2(request,aantal=100): # # # job = myqueue(queuehigh,long_test_task2,int(aantal), @@ -894,74 +928,76 @@ def raise_500(request): # pragma: no cover # # return HttpResponseRedirect(url) + @csrf_exempt -def post_progress(request,id=None,value=0): # pragma: no cover +def post_progress(request, id=None, value=0): # pragma: no cover if request.method == 'POST': try: secret = request.POST['secret'] except KeyError: - return HttpResponse('Access Denied',status=401) + return HttpResponse('Access Denied', status=401) if secret == settings.PROGRESS_CACHE_SECRET: if not id: try: id = request.POST['id'] except KeyError: - return HttpResponse('Invalid request',400) + return HttpResponse('Invalid request', 400) try: value = request.POST['value'] except KeyError: pass - cache.set(id,value,3600) + cache.set(id, value, 3600) # test result = cache.get(id) return HttpResponse('progress cached '+str(result), status=201) - else: # secret not given - return HttpResponse('access denied',status=401) + else: # secret not given + return HttpResponse('access denied', status=401) - else: # request method is not POST - return HttpResponse('GET method not allowed',status=405) + else: # request method is not POST + return HttpResponse('GET method not allowed', status=405) -def get_all_queued_jobs(userid=0): # pragma: no cover + +def get_all_queued_jobs(userid=0): # pragma: no cover r = StrictRedis() jobs = [] celerykeys = r.keys('celery*') for key in celerykeys: - id= key[17:] + id = key[17:] job = celery_result.AsyncResult(id) jobresult = job.result if 'fail' in job.status.lower(): jobresult = '0' jobs.append( - (id,{ - 'status':job.status, - 'result':jobresult, - 'function':'', - 'meta':job.info, - })) + (id, { + 'status': job.status, + 'result': jobresult, + 'function': '', + 'meta': job.info, + })) ids = [j.id for j in queue.jobs] ids += [j.id for j in queuehigh.jobs] ids += [j.id for j in queuelow.jobs] ids += [j.id for j in queuefailed.jobs] - for id in ids: - job = Job.fetch(id,connection=redis_connection) + job = Job.fetch(id, connection=redis_connection) jobs.append( - (id,{ - 'status':job.get_status(), - 'result':job.result, - 'function':job.func_name, - 'meta':job.meta, - })) + (id, { + 'status': job.get_status(), + 'result': job.result, + 'function': job.func_name, + 'meta': job.meta, + })) return jobs + def get_stored_tasks_status(request): try: taskids = request.session['async_tasks'] @@ -969,7 +1005,7 @@ def get_stored_tasks_status(request): taskids = [] taskstatus = [] - for id,func_name in reversed(taskids): # pragma: no cover + for id, func_name in reversed(taskids): # pragma: no cover progress = 0 try: cached_progress = cache.get(id) @@ -981,59 +1017,58 @@ def get_stored_tasks_status(request): cached_progress = 0 finished = get_job_status(id)['finished'] if finished: - cache.set(id,100) + cache.set(id, 100) progress = 100 - elif cached_progress is not None and cached_progress>0: + elif cached_progress is not None and cached_progress > 0: progress = cached_progress else: progress = 0 this_task_status = { - 'id':id, - 'status':get_job_status(id)['status'], - 'failed':get_job_status(id)['failed'], - 'finished':get_job_status(id)['finished'], - 'func_name':func_name, + 'id': id, + 'status': get_job_status(id)['status'], + 'failed': get_job_status(id)['failed'], + 'finished': get_job_status(id)['finished'], + 'func_name': func_name, 'verbose': verbose_job_status[func_name], 'progress': progress, } taskstatus.append(this_task_status) - return taskstatus -@permission_required('workout.change_workout',fn=get_workout_by_opaqueid,raise_exception=True) -def get_thumbnails(request,id): - row = get_workout_by_opaqueid(request,id) +@permission_required('workout.change_workout', fn=get_workout_by_opaqueid, raise_exception=True) +def get_thumbnails(request, id): + row = get_workout_by_opaqueid(request, id) r = getrower(request.user) result = request.user.is_authenticated and ispromember(request.user) if result: - promember=1 + promember = 1 if request.user == row.user.user: - mayedit=1 + mayedit = 1 comments = WorkoutComment.objects.filter(workout=row) aantalcomments = len(comments) - favorites,maxfav = getfavorites(r,row) + favorites, maxfav = getfavorites(r, row) charts = [] - charts = thumbnails_set(r,encoder.decode_hex(id),favorites) + charts = thumbnails_set(r, encoder.decode_hex(id), favorites) try: if charts[0]['script'] == '': charts = [] - except IndexError: # pragma: no cover + except IndexError: # pragma: no cover charts = [] - return JSONResponse(charts) -def get_blog_posts(request): # pragma: no cover + +def get_blog_posts(request): # pragma: no cover blogposts = BlogPost.objects.all().order_by("-date") jsondata = [] @@ -1041,15 +1076,16 @@ def get_blog_posts(request): # pragma: no cover if blogposts: for blogpost in blogposts[0:3]: thedict = { - 'title':blogpost.title, - 'link':blogpost.link, + 'title': blogpost.title, + 'link': blogpost.link, } jsondata.append(thedict) return JSONResponse(jsondata) -def get_blog_posts_old(request): # pragma: no cover + +def get_blog_posts_old(request): # pragma: no cover try: response = requests.get( 'https://analytics.rowsandall.com/wp-json/wp/v2/posts?per_page=3') @@ -1064,47 +1100,47 @@ def get_blog_posts_old(request): # pragma: no cover blogposts = [] - for postdata in blogs_json[0:3]: try: title = postdata['title']['rendered'].encode( - 'ascii','xmlcharrefreplace') + 'ascii', 'xmlcharrefreplace') except TypeError: title = postdata['title']['rendered'].encode( - 'ascii','xmlcharrefreplace').decode('utf-8') - + 'ascii', 'xmlcharrefreplace').decode('utf-8') thedict = { 'title': title, -# 'author': '', -# 'image': image_url, -# 'excerpt': excerpt_first, + # 'author': '', + # 'image': image_url, + # 'excerpt': excerpt_first, 'link': postdata['link'], - } + } blogposts.append(thedict) return JSONResponse(blogposts) - @login_required() def session_jobs_view(request): taskstatus = get_stored_tasks_status(request) return HttpResponse(json.dumps(taskstatus)) + @login_required() def session_jobs_status(request): taskstatus = get_stored_tasks_status(request) return render(request, 'async_tasks.html', - {'taskstatus':taskstatus}) + {'taskstatus': taskstatus}) # Test if row data include candidates + + def rowhascoordinates(row): # create interactive plot f1 = row.csvfilename @@ -1116,12 +1152,12 @@ def rowhascoordinates(row): try: latitude = rowdata.df[' latitude'] - if not latitude.std(): # pragma: no cover + if not latitude.std(): # pragma: no cover hascoordinates = 0 - except (KeyError,AttributeError): + except (KeyError, AttributeError): hascoordinates = 0 - else: # pragma: no cover + else: # pragma: no cover hascoordinates = 0 return hascoordinates @@ -1129,28 +1165,30 @@ def rowhascoordinates(row): # Wrapper around the rowingdata call to catch some exceptions # Checks for CSV file, then for gzipped CSV file, and if all fails, returns 0 -def rdata(csvfile=None,rower=rrower()): - if csvfile is None: # pragma: no cover +def rdata(csvfile=None, rower=rrower()): + if csvfile is None: # pragma: no cover return 0 try: - res = rrdata(csvfile=csvfile,rower=rower) - except pd.errors.EmptyDataError: # pragma: no cover + res = rrdata(csvfile=csvfile, rower=rower) + except pd.errors.EmptyDataError: # pragma: no cover res = 0 - except (IOError, IndexError, EOFError,FileNotFoundError): # pragma: no cover + except (IOError, IndexError, EOFError, FileNotFoundError): # pragma: no cover try: - res = rrdata(csvfile=file+'.gz',rower=rower) - except (IOError, IndexError, EOFError,FileNotFoundError): + res = rrdata(csvfile=file+'.gz', rower=rower) + except (IOError, IndexError, EOFError, FileNotFoundError): res = 0 return res # Query to get teams managed and member of + + def get_my_teams(user): try: therower = Rower.objects.get(user=user) try: teams1 = therower.team.all() - except AttributeError: # pragma: no cover + except AttributeError: # pragma: no cover teams1 = [] teams2 = Team.objects.filter(manager=user) @@ -1161,67 +1199,56 @@ def get_my_teams(user): return teams # Used for the interval editor - translates seconds to a time object -def get_time(second): # pragma: no cover - if (second<=0) or (second>1e9): + + +def get_time(second): # pragma: no cover + if (second <= 0) or (second > 1e9): hours = 0 - minutes=0 - sec=0 + minutes = 0 + sec = 0 microsecond = 0 - elif math.isnan(second): # pragma: no cover + elif math.isnan(second): # pragma: no cover hours = 0 - minutes=0 - sec=0 + minutes = 0 + sec = 0 microsecond = 0 else: days = int(second/(24.*3600.)) % (24*3600) hours = int((second-24.*3600.*days)/3600.) % 24 minutes = int((second-3600.*(hours+24.*days))/60.) % 60 sec = int(second-3600.*(hours+24.*days)-60.*minutes) % 60 - microsecond = int(1.0e6*(second-3600.*(hours+24.*days)-60.*minutes-sec)) - return datetime.time(hours,minutes,sec,microsecond) + microsecond = int( + 1.0e6*(second-3600.*(hours+24.*days)-60.*minutes-sec)) + return datetime.time(hours, minutes, sec, microsecond) # get the workout ID from the SportTracks URI -def getidfromsturi(uri,length=8): # pragma: no cover +def getidfromsturi(uri, length=8): # pragma: no cover return uri[len(uri)-length:] -import re -def getidfromuri(uri): # pragma: no cover - m = re.search('/(\w.*)\/(\d+)',uri) +def getidfromuri(uri): # pragma: no cover + m = re.search('/(\w.*)\/(\d+)', uri) return m.group(2) - -from rowers.utils import ( - geo_distance,serialize_list,deserialize_list,uniqify, - str2bool,range_to_color_hex,absolute,myqueue,get_call, - calculate_age,rankingdistances,rankingdurations, - my_dict_from_instance,wavg,NoTokenError, - request_is_ajax,dologging - ) - -import rowers.datautils as datautils - # Check if a user is a Coach member + def iscoachmember(user): if not user.is_anonymous: try: r = Rower.objects.get(user=user) - except Rower.DoesNotExist: # pragma: no cover: + except Rower.DoesNotExist: # pragma: no cover: r = Rower(user=user) r.save() result = user.is_authenticated and ('coach' in r.rowerplan) - else: # pragma: no cover + else: # pragma: no cover result = False return result -from rowers.utils import ProcessorCustomerError - - # More User/Rower utils def add_defaultfavorites(r): for c in defaultfavoritecharts: @@ -1247,16 +1274,16 @@ def sendmail(request): recaptcha_secret = RECAPTCHA_SITE_SECRET url = 'https://www.google.com/recaptcha/api/siteverify' data = { - 'secret':recaptcha_secret, + 'secret': recaptcha_secret, 'response': response_string, } - response = requests.post(url,data=data,verify=True) + response = requests.post(url, data=data, verify=True) success = False if response.status_code == 200: success = response.json().get('success') form = EmailForm(request.POST) - if form.is_valid() and success: # pragma: no cover + if form.is_valid() and success: # pragma: no cover firstname = form.cleaned_data['firstname'] lastname = form.cleaned_data['lastname'] email = form.cleaned_data['email'] @@ -1266,19 +1293,21 @@ def sendmail(request): try: send_mail(subject, message, fullemail, ['info@rowsandall.com']) except: - messages.error(request,"Something went wrong trying to send the email") + messages.error( + request, "Something went wrong trying to send the email") return HttpResponseRedirect('/rowers/email/thankyou/') else: if not success: - messages.error(request,'Bots are not welcome') - else: # pragma: no cover - messages.error(request,'Something went wrong. Please try again') + messages.error(request, 'Bots are not welcome') + else: # pragma: no cover + messages.error( + request, 'Something went wrong. Please try again') return HttpResponseRedirect('/rowers/email/') else: return HttpResponseRedirect('/rowers/email/') -def keyvalue_get_default(key,options,def_options): # pragma: no cover +def keyvalue_get_default(key, options, def_options): # pragma: no cover try: return options[key] @@ -1286,27 +1315,24 @@ def keyvalue_get_default(key,options,def_options): # pragma: no cover return def_options[key] - - # Creates unix time stamp from a datetime object -def totimestamp(dt, epoch=datetime.datetime(1970,1,1,tzinfo=tz('UTC'))): # pragma: no cover +def totimestamp(dt, epoch=datetime.datetime(1970, 1, 1, tzinfo=tz('UTC'))): # pragma: no cover td = dt - epoch # return td.total_seconds() return (td.microseconds + (td.seconds + td.days * 86400) * 10**6) / 10**6 # Check if a column of a dataframe has the required (aantal) # number of elements. Also checks if the column is a numerical type # Replaces any faulty columns with zeros -def trydf(df,aantal,column): # pragma: no cover + + +def trydf(df, aantal, column): # pragma: no cover try: s = df[column] if len(s) != aantal: return np.zeros(aantal) - if not np.issubdtype(s,np.number): + if not np.issubdtype(s, np.number): return np.zeros(aantal) except KeyError: s = np.zeros(aantal) return s - -import rowers.teams as teams -from rowers.models import C2WorldClassAgePerformance diff --git a/rowers/views/teamviews.py b/rowers/views/teamviews.py index a989b59a..a882032e 100644 --- a/rowers/views/teamviews.py +++ b/rowers/views/teamviews.py @@ -1,4 +1,5 @@ from __future__ import absolute_import +from rowers.forms import TeamInviteCodeForm from __future__ import division from __future__ import print_function from __future__ import unicode_literals @@ -7,30 +8,31 @@ from rowers.views.statements import * from rowers.rower_rules import * -def get_team_by_pk(request,team_id): - return get_object_or_404(Team,pk=team_id) +def get_team_by_pk(request, team_id): + return get_object_or_404(Team, pk=team_id) @login_required() -@permission_required('teams.view_team',fn=get_team_by_pk,raise_exception=True) -def team_view(request,team_id=0,userid=0): +@permission_required('teams.view_team', fn=get_team_by_pk, raise_exception=True) +def team_view(request, team_id=0, userid=0): ismember = 0 hasrequested = 0 - r = getrequestrower(request,userid=userid) + r = getrequestrower(request, userid=userid) myteams, memberteams, otherteams = get_teams(request) teams.remove_expired_invites() - t = get_object_or_404(Team,pk=team_id) + t = get_object_or_404(Team, pk=team_id) - #if r.rowerplan == 'basic' and t.manager.rower.rowerplan != 'coach': + # if r.rowerplan == 'basic' and t.manager.rower.rowerplan != 'coach': # raise PermissionDenied("You need to be on a Paid Plan to see or join this team") - - q = User.objects.filter(rower__isnull=False,rower__team__in=myteams).distinct().exclude(rower__team__name=t.name) + q = User.objects.filter(rower__isnull=False, rower__team__in=myteams).distinct( + ).exclude(rower__team__name=t.name) mygroups = [request.user.rower.mycoachgroup] - q2 = User.objects.filter(rower__isnull=False,rower__coachinggroups__in=mygroups).distinct().exclude(rower__team__name=t.name) + q2 = User.objects.filter(rower__isnull=False, rower__coachinggroups__in=mygroups).distinct( + ).exclude(rower__team__name=t.name) q = q | q2 @@ -42,24 +44,24 @@ def team_view(request,team_id=0,userid=0): cd = inviteform.cleaned_data newmember = cd['user'] email = cd['email'] - inviteid,text = teams.create_invite(t,t.manager, - user=newmember, - email=email) + inviteid, text = teams.create_invite(t, t.manager, + user=newmember, + email=email) if inviteid: teams.send_invite_email(inviteid) successmessage = text - messages.info(request,successmessage) - else: # pragma: no cover + messages.info(request, successmessage) + else: # pragma: no cover message = text - messages.error(request,message) + messages.error(request, message) groupmessageform = TeamMessageForm() - elif request.method == 'POST' and request.user == t.manager and 'message' in request.POST: # pragma: no cover + elif request.method == 'POST' and request.user == t.manager and 'message' in request.POST: # pragma: no cover groupmessageform = TeamMessageForm(request.POST) inviteform = TeamInviteForm() if groupmessageform.is_valid(): message = groupmessageform.cleaned_data['message'] - teams.send_team_message(t,message) - messages.info(request,'Message was sent to all team members') + teams.send_team_message(t, message) + messages.info(request, 'Message was sent to all team members') groupmessageform = TeamMessageForm() elif request.user == t.manager: @@ -70,9 +72,9 @@ def team_view(request,team_id=0,userid=0): inviteform = '' groupmessageform = '' - - members = Rower.objects.filter(team=t).order_by('user__last_name','user__first_name') - thisteammyrequests = TeamRequest.objects.filter(team=t,user=request.user) + members = Rower.objects.filter(team=t).order_by( + 'user__last_name', 'user__first_name') + thisteammyrequests = TeamRequest.objects.filter(team=t, user=request.user) if len(thisteammyrequests): hasrequested = 1 @@ -81,88 +83,90 @@ def team_view(request,team_id=0,userid=0): breadcrumbs = [ { - 'url':reverse(rower_teams_view), + 'url': reverse(rower_teams_view), 'name': 'Groups' - }, + }, { - 'url':reverse(team_view,kwargs={'team_id':team_id}), + 'url': reverse(team_view, kwargs={'team_id': team_id}), 'name': t.name - } - ] - + } + ] return render(request, 'team.html', { - 'team':t, - 'teams':get_my_teams(request.user), - 'myteams':myteams, - 'memberteams':memberteams, + 'team': t, + 'teams': get_my_teams(request.user), + 'myteams': myteams, + 'memberteams': memberteams, 'groupmessageform': groupmessageform, - 'members':members, - 'breadcrumbs':breadcrumbs, - 'active':'nav-teams', - 'inviteform':inviteform, - 'ismember':ismember, - 'hasrequested':hasrequested, - }) + 'members': members, + 'breadcrumbs': breadcrumbs, + 'active': 'nav-teams', + 'inviteform': inviteform, + 'ismember': ismember, + 'hasrequested': hasrequested, + }) + @login_required() -def team_leaveconfirm_view(request,id=0): +def team_leaveconfirm_view(request, id=0): try: t = Team.objects.get(id=id) - except Team.DoesNotExist: # pragma: no cover # pragma: no cover + except Team.DoesNotExist: # pragma: no cover # pragma: no cover raise Http404("Team doesn't exist") myteams, memberteams, otherteams = get_teams(request) breadcrumbs = [ { - 'url':reverse(rower_teams_view), + 'url': reverse(rower_teams_view), 'name': 'Groups' - }, + }, { - 'url':reverse(team_view,kwargs={'team_id':id}), + 'url': reverse(team_view, kwargs={'team_id': id}), 'name': t.name - }, + }, { - 'url':reverse(team_leaveconfirm_view,kwargs={'id':id}), + 'url': reverse(team_leaveconfirm_view, kwargs={'id': id}), 'name': 'Leave' - } - ] - return render(request,'teamleaveconfirm.html', + } + ] + return render(request, 'teamleaveconfirm.html', { - 'team':t, - 'teams':get_my_teams(request.user), - 'myteams':myteams, - 'memberteams':memberteams, - 'otherteams':otherteams, - 'active':'nav-teams', - 'breadcrumbs':breadcrumbs, - }) + 'team': t, + 'teams': get_my_teams(request.user), + 'myteams': myteams, + 'memberteams': memberteams, + 'otherteams': otherteams, + 'active': 'nav-teams', + 'breadcrumbs': breadcrumbs, + }) + @login_required() def rower_calcdps_view(request): r = getrower(request.user) - ws = [(w.id,w.csvfilename) for w in Workout.objects.filter(user=r)] - res = myqueue(queue,handle_updatedps,r.user.email,ws,debug=False, + ws = [(w.id, w.csvfilename) for w in Workout.objects.filter(user=r)] + res = myqueue(queue, handle_updatedps, r.user.email, ws, debug=False, emailbounced=r.emailbounced) - messages.info(request,"Your workouts are being updated in the background. You will receive email when this is done.") + messages.info( + request, "Your workouts are being updated in the background. You will receive email when this is done.") url = reverse('workouts_view') return HttpResponseRedirect(url) + @login_required() -def team_leave_view(request,id=0): +def team_leave_view(request, id=0): r = getrower(request.user) - teams.remove_member(id,r) + teams.remove_member(id, r) url = reverse(rower_teams_view) response = HttpResponseRedirect(url) return response -from rowers.forms import TeamInviteCodeForm def get_teams(request): r = Rower.objects.get(user=request.user) @@ -180,24 +184,24 @@ def get_teams(request): return myteams, memberteams, otherteams + @login_required() -def rower_teams_view(request): # pragma: no cover +def rower_teams_view(request): # pragma: no cover if request.method == 'POST': form = TeamInviteCodeForm(request.POST) if form.is_valid(): code = form.cleaned_data['code'] - res,text = teams.process_invite_code(request.user,code) + res, text = teams.process_invite_code(request.user, code) if res: - messages.info(request,text) + messages.info(request, text) else: - messages.error(request,text) + messages.error(request, text) else: form = TeamInviteCodeForm() r = getrower(request.user) ts = Team.objects.filter(rower=r) - myteams, memberteams, otherteams = get_teams(request) teams.remove_expired_invites() @@ -217,19 +221,20 @@ def rower_teams_view(request): # pragma: no cover coachrequests = CoachRequest.objects.filter(coach=r) invitedathletes = [rekwest.user for rekwest in mycoachoffers] - invitedcoaches = [rekwest.coach for rekwest in mycoachrequests ] + invitedcoaches = [rekwest.coach for rekwest in mycoachrequests] invitedcoaches += [rekwest.coach for rekwest in coachoffers] invitingathletes = [rekwest.user for rekwest in coachrequests] coaches = teams.rower_get_coaches(r) - potentialcoaches = [t.manager for t in memberteams if t.manager not in coaches and 'coach' in t.manager.rower.rowerplan] + potentialcoaches = [ + t.manager for t in memberteams if t.manager not in coaches and 'coach' in t.manager.rower.rowerplan] offercoaches = [ r.user for r in Rower.objects.all().exclude(user=request.user) if r.can_coach() ] potentialcoaches = list(set(potentialcoaches+offercoaches)) - potentialcoaches = [c for c in potentialcoaches if c.rower not in invitedcoaches+coaches] - + potentialcoaches = [ + c for c in potentialcoaches if c.rower not in invitedcoaches+coaches] coachees = teams.coach_getcoachees(request.user.rower) @@ -238,362 +243,376 @@ def rower_teams_view(request): # pragma: no cover team__in=myteams).exclude( user__in=invitedathletes).exclude( user=request.user - ).exclude(coachinggroups__in=[request.user.rower.mycoachgroup]) - elif request.user.rower.rowerplan == 'freecoach': # pragma: no cover + ).exclude(coachinggroups__in=[request.user.rower.mycoachgroup]) + elif request.user.rower.rowerplan == 'freecoach': # pragma: no cover potentialathletes = Rower.objects.filter( team__in=myteams).exclude( user__in=invitedathletes).exclude( user=request.user - ).exclude( + ).exclude( coachinggroups__in=[request.user.rower.mycoachgroup] - ).exclude( - rowerplan__in=['basic','freecoach'] - ) + ).exclude( + rowerplan__in=['basic', 'freecoach'] + ) else: potentialathletes = [] - potentialathletes = [a for a in potentialathletes if a.user not in invitingathletes] + potentialathletes = [ + a for a in potentialathletes if a.user not in invitingathletes] # clubsize = teams.count_invites(request.user)+teams.count_club_members(request.user) # max_clubsize = r.clubsize - breadcrumbs = [ { - 'url':reverse(rower_teams_view), + 'url': reverse(rower_teams_view), 'name': 'Groups' - } - ] - + } + ] return render(request, 'teams.html', - { - 'teams':ts, - 'active':'nav-teams', - 'breadcrumbs':breadcrumbs, - 'myteams':myteams, - 'memberteams':memberteams, - 'invites':invites, - 'otherteams':otherteams, - 'requests':requests, - 'myrequests':myrequests, - 'form':form, - 'myinvites':myinvites, - 'mycoachrequests':mycoachrequests, - 'mycoachoffers':mycoachoffers, - 'coachrequests':coachrequests, - 'coachoffers':coachoffers, - 'coaches':coaches, - 'potentialcoaches':potentialcoaches, - 'coachees':coachees, - 'potentialathletes':potentialathletes, - }) + { + 'teams': ts, + 'active': 'nav-teams', + 'breadcrumbs': breadcrumbs, + 'myteams': myteams, + 'memberteams': memberteams, + 'invites': invites, + 'otherteams': otherteams, + 'requests': requests, + 'myrequests': myrequests, + 'form': form, + 'myinvites': myinvites, + 'mycoachrequests': mycoachrequests, + 'mycoachoffers': mycoachoffers, + 'coachrequests': coachrequests, + 'coachoffers': coachoffers, + 'coaches': coaches, + 'potentialcoaches': potentialcoaches, + 'coachees': coachees, + 'potentialathletes': potentialathletes, + }) + @login_required() -def invitation_revoke_view(request,id): - res,text = teams.revoke_invite(request.user,id) +def invitation_revoke_view(request, id): + res, text = teams.revoke_invite(request.user, id) if res: - messages.info(request,text) + messages.info(request, text) successmessage = text - else: # pragma: no cover + else: # pragma: no cover message = text - messages.error(request,text) + messages.error(request, text) url = reverse(rower_teams_view) return HttpResponseRedirect(url) + @login_required() -def manager_member_drop_view(request,teamid,userid, - message='',successmessage=''): +def manager_member_drop_view(request, teamid, userid, + message='', successmessage=''): rower = Rower.objects.get(user__id=userid) - res, text = teams.mgr_remove_member(teamid,request.user,rower) + res, text = teams.mgr_remove_member(teamid, request.user, rower) if res: - messages.info(request,text) - else: # pragma: no cover - messages.error(request,text) + messages.info(request, text) + else: # pragma: no cover + messages.error(request, text) url = reverse(rower_teams_view) return HttpResponseRedirect(url) -@login_required() -def manager_requests_view(request,code=None): - if code: - res,text = teams.process_request_code(request.user,code) - if res: # pragma: no cover - messages.info(request,text) - else: - messages.error(request,text) - url = reverse(rower_teams_view,kwargs={ +@login_required() +def manager_requests_view(request, code=None): + if code: + res, text = teams.process_request_code(request.user, code) + if res: # pragma: no cover + messages.info(request, text) + else: + messages.error(request, text) + + url = reverse(rower_teams_view, kwargs={ }) return HttpResponseRedirect(url) + @login_required() -def athlete_drop_coach_confirm_view(request,id): +def athlete_drop_coach_confirm_view(request, id): r = getrower(request.user) try: coach = Rower.objects.get(id=id) - except Rower.DoesNotExist: # pragma: no cover # pragma: no cover + except Rower.DoesNotExist: # pragma: no cover # pragma: no cover raise Http404("This rower doesn't exist") - if coach not in teams.rower_get_coaches(r): # pragma: no cover + if coach not in teams.rower_get_coaches(r): # pragma: no cover raise PermissionDenied("You are not allowed to do this") breadcrumbs = [ { - 'url':reverse('rower_teams_view'), + 'url': reverse('rower_teams_view'), 'name': 'Groups' - }, + }, { - 'url':reverse('athlete_drop_coach_confirm_view',kwargs={'id':id}), + 'url': reverse('athlete_drop_coach_confirm_view', kwargs={'id': id}), 'name': 'Confirm drop athlete' - } - ] + } + ] - return render(request,'dropcoachconfirm.html', + return render(request, 'dropcoachconfirm.html', { - 'rower':r, - 'coach':coach - }) + 'rower': r, + 'coach': coach + }) + @login_required() -def coach_drop_athlete_confirm_view(request,id): +def coach_drop_athlete_confirm_view(request, id): r = getrower(request.user) try: rower = Rower.objects.get(id=id) - except Rower.DoesNotExist: # pragma: no cover # pragma: no cover + except Rower.DoesNotExist: # pragma: no cover # pragma: no cover raise Http404("This rower doesn't exist") - if rower not in teams.coach_getcoachees(r): # pragma: no cover + if rower not in teams.coach_getcoachees(r): # pragma: no cover raise PermissionDenied("You are not allowed to do this") breadcrumbs = [ { - 'url':reverse('rower_teams_view'), + 'url': reverse('rower_teams_view'), 'name': 'Groups' - }, + }, { - 'url':reverse('coach_drop_athlete_confirm_view',kwargs={'id':id}), + 'url': reverse('coach_drop_athlete_confirm_view', kwargs={'id': id}), 'name': 'Confirm drop athlete' - } - ] + } + ] - return render(request,'dropathleteconfirm.html', + return render(request, 'dropathleteconfirm.html', { - 'rower':r, - 'athlete':rower - }) + 'rower': r, + 'athlete': rower + }) + @login_required() -def coach_drop_athlete_view(request,id): +def coach_drop_athlete_view(request, id): r = getrower(request.user) try: rower = Rower.objects.get(id=id) - except Rower.DoesNotExist: # pragma: no cover + except Rower.DoesNotExist: # pragma: no cover raise Http404("This rower doesn't exist") - if rower not in teams.coach_getcoachees(r): # pragma: no cover + if rower not in teams.coach_getcoachees(r): # pragma: no cover raise PermissionDenied("You are not allowed to do this") - res,text = teams.coach_remove_athlete(r,rower) + res, text = teams.coach_remove_athlete(r, rower) if res: - messages.info(request,'You are not coaching this athlete any more') - else: # pragma: no cover - messages.error(request,'There was an error dropping the athlete from your list') + messages.info(request, 'You are not coaching this athlete any more') + else: # pragma: no cover + messages.error( + request, 'There was an error dropping the athlete from your list') url = reverse('rower_teams_view') return HttpResponseRedirect(url) + @login_required() -def athlete_drop_coach_view(request,id): +def athlete_drop_coach_view(request, id): r = getrower(request.user) try: coach = Rower.objects.get(id=id) - except Rower.DoesNotExist: # pragma: no cover + except Rower.DoesNotExist: # pragma: no cover raise Http404("This coach doesn't exist") - if coach not in teams.rower_get_coaches(r): # pragma: no cover + if coach not in teams.rower_get_coaches(r): # pragma: no cover raise PermissionDenied("You are not allowed to do this") - res,text = teams.coach_remove_athlete(coach,r) + res, text = teams.coach_remove_athlete(coach, r) if res: - messages.info(request,'Removal successful') - else: # pragma: no cover - messages.error(request,'There was an error dropping the coach from your list') + messages.info(request, 'Removal successful') + else: # pragma: no cover + messages.error( + request, 'There was an error dropping the coach from your list') url = reverse('rower_teams_view') return HttpResponseRedirect(url) + @login_required() -def team_requestmembership_view(request,teamid,userid): +def team_requestmembership_view(request, teamid, userid): try: t = Team.objects.get(id=teamid) - except Team.DoesNotExist: # pragma: no cover + except Team.DoesNotExist: # pragma: no cover raise Http404("Team doesn't exist") - r = getrequestrower(request,userid=userid) + r = getrequestrower(request, userid=userid) - #if t.manager.rower.rowerplan in ['plan','pro'] and r.rowerplan == 'basic': - if not can_join_team(r.user,t): + # if t.manager.rower.rowerplan in ['plan','pro'] and r.rowerplan == 'basic': + if not can_join_team(r.user, t): messages.error(request, "You have to be on a paid plan (Pro or higher) to join this team. As a basic user you can only join teams managed by users on the Coach plan.") url = reverse('paidplans_view') return HttpResponseRedirect(url) - res,text = teams.create_request(t,userid) + res, text = teams.create_request(t, userid) if res: - messages.info(request,text) - else: # pragma: no cover - messages.error(request,text) - - - url = reverse('team_view',kwargs={ - 'team_id':int(teamid), - }) + messages.info(request, text) + else: # pragma: no cover + messages.error(request, text) + url = reverse('team_view', kwargs={ + 'team_id': int(teamid), + }) return HttpResponseRedirect(url) + @login_required() -def request_coaching_view(request,coachid): +def request_coaching_view(request, coachid): r = getrequestrower(request) coach = User.objects.get(id=coachid).rower if 'coach' in coach.rowerplan: - res,text = teams.create_coaching_request(coach,request.user) + res, text = teams.create_coaching_request(coach, request.user) if res: - messages.info(request,text) - else: # pragma: no cover - messages.error(request,text) - else: # pragma: no cover - messages.error(request,'That person is not a coach') + messages.info(request, text) + else: # pragma: no cover + messages.error(request, text) + else: # pragma: no cover + messages.error(request, 'That person is not a coach') url = reverse('rower_teams_view') return HttpResponseRedirect(url) -@user_passes_test(iscoachmember,login_url="/rowers/paidplans",redirect_field_name=None) -def offer_coaching_view(request,userid): + +@user_passes_test(iscoachmember, login_url="/rowers/paidplans", redirect_field_name=None) +def offer_coaching_view(request, userid): try: u = User.objects.get(id=userid) - except User.DoesNotExist: # pragma: no cover + except User.DoesNotExist: # pragma: no cover raise Http404("This user doesn't exist") coach = getrequestrower(request) - res,text = teams.create_coaching_offer(coach,u) + res, text = teams.create_coaching_offer(coach, u) if res: - messages.info(request,text) - else: # pragma: no cover - messages.error(request,text) + messages.info(request, text) + else: # pragma: no cover + messages.error(request, text) url = reverse('rower_teams_view') return HttpResponseRedirect(url) + @login_required() -def reject_revoke_coach_request(request,id=0): - res, text = teams.reject_revoke_coach_request(request.user,id) +def reject_revoke_coach_request(request, id=0): + res, text = teams.reject_revoke_coach_request(request.user, id) if res: - messages.info(request,text) - else: # pragma: no cover - messages.error(request,text) + messages.info(request, text) + else: # pragma: no cover + messages.error(request, text) url = reverse('rower_teams_view') return HttpResponseRedirect(url) + @login_required() -def reject_revoke_coach_offer(request,id=0): - res, text = teams.reject_revoke_coach_offer(request.user,id) +def reject_revoke_coach_offer(request, id=0): + res, text = teams.reject_revoke_coach_offer(request.user, id) if res: - messages.info(request,text) - else: # pragma: no cover - messages.error(request,text) + messages.info(request, text) + else: # pragma: no cover + messages.error(request, text) url = reverse('rower_teams_view') return HttpResponseRedirect(url) + @login_required() -def request_revoke_view(request,id=0): - res,text = teams.revoke_request(request.user,id) +def request_revoke_view(request, id=0): + res, text = teams.revoke_request(request.user, id) if res: - messages.info(request,text) + messages.info(request, text) - else: # pragma: no cover - messages.error(request,text) + else: # pragma: no cover + messages.error(request, text) url = reverse(rower_teams_view) return HttpResponseRedirect(url) + @login_required() -def request_reject_view(request,id=0): - res,text = teams.reject_request(request.user,id) +def request_reject_view(request, id=0): + res, text = teams.reject_request(request.user, id) if res: - messages.info(request,text) - else: # pragma: no cover - messages.error(request,text) + messages.info(request, text) + else: # pragma: no cover + messages.error(request, text) url = reverse(rower_teams_view) return HttpResponseRedirect(url) + @login_required() -def invitation_reject_view(request,id=0): - res,text = teams.reject_invitation(request.user,id) +def invitation_reject_view(request, id=0): + res, text = teams.reject_invitation(request.user, id) if res: - messages.info(request,text) - else: # pragma: no cover - messages.error(request,text) + messages.info(request, text) + else: # pragma: no cover + messages.error(request, text) url = reverse(rower_teams_view) return HttpResponseRedirect(url) + @login_required() -def rower_invitations_view(request,code=None,message='',successmessage=''): +def rower_invitations_view(request, code=None, message='', successmessage=''): if code: teams.remove_expired_invites() - - res,text = teams.process_invite_code(request.user,code) - if res: # pragma: no cover - messages.info(request,text) - teamid=res - url = reverse(team_view,kwargs={ - 'team_id':teamid, + res, text = teams.process_invite_code(request.user, code) + if res: # pragma: no cover + messages.info(request, text) + teamid = res + url = reverse(team_view, kwargs={ + 'team_id': teamid, }) else: - messages.error(request,text) + messages.error(request, text) url = reverse(rower_teams_view) return HttpResponseRedirect(url) - url = reverse(rower_teams_view,kwargs={ + url = reverse(rower_teams_view, kwargs={ }) return HttpResponseRedirect(url) + @login_required() -@permission_required('teams.change_team',fn=get_team_by_pk,raise_exception=True) +@permission_required('teams.change_team', fn=get_team_by_pk, raise_exception=True) def team_edit_view(request, team_id=0): - t = get_object_or_404(Team,pk=team_id) + t = get_object_or_404(Team, pk=team_id) - - if request.method == 'POST': # pragma: no cover - teamcreateform = TeamForm(request.POST,instance=t) + if request.method == 'POST': # pragma: no cover + teamcreateform = TeamForm(request.POST, instance=t) if teamcreateform.is_valid(): cd = teamcreateform.cleaned_data name = cd['name'] @@ -601,18 +620,18 @@ def team_edit_view(request, team_id=0): manager = request.user private = cd['private'] viewing = cd['viewing'] - res,message=teams.update_team(t,name,manager,private,notes, - viewing) + res, message = teams.update_team(t, name, manager, private, notes, + viewing) if res: - messages.info(request,message) + messages.info(request, message) else: - messages.error(request,message) + messages.error(request, message) url = reverse(team_view, kwargs={ - 'team_id':int(team_id), + 'team_id': int(team_id), } - ) + ) response = HttpResponseRedirect(url) return response @@ -624,33 +643,34 @@ def team_edit_view(request, team_id=0): breadcrumbs = [ { - 'url':reverse(rower_teams_view), + 'url': reverse(rower_teams_view), 'name': 'Groups' - }, + }, { - 'url':reverse(team_view,kwargs={'team_id':team_id}), + 'url': reverse(team_view, kwargs={'team_id': team_id}), 'name': t.name - }, + }, { - 'url':reverse(team_edit_view,kwargs={'team_id':team_id}), + 'url': reverse(team_edit_view, kwargs={'team_id': team_id}), 'name': 'Edit' - } - ] + } + ] - return render(request,'teamedit.html', + return render(request, 'teamedit.html', { - 'form':teamcreateform, - 'teams':get_my_teams(request.user), - 'myteams':myteams, - 'memberteams':memberteams, - 'otherteams':otherteams, - 'active':'nav-teams', - 'breadcrumbs':breadcrumbs, - 'team':t, - }) + 'form': teamcreateform, + 'teams': get_my_teams(request.user), + 'myteams': myteams, + 'memberteams': memberteams, + 'otherteams': otherteams, + 'active': 'nav-teams', + 'breadcrumbs': breadcrumbs, + 'team': t, + }) + @login_required() -@user_passes_test(can_add_team,login_url="/rowers/paidplans",redirect_field_name=None, +@user_passes_test(can_add_team, login_url="/rowers/paidplans", redirect_field_name=None, message="You need to upgrade or log in to access this functionality") def team_create_view(request): r = getrequestrower(request) @@ -664,11 +684,11 @@ def team_create_view(request): manager = request.user private = cd['private'] viewing = cd['viewing'] - res,message=teams.create_team(name,manager,private,notes, - viewing) + res, message = teams.create_team(name, manager, private, notes, + viewing) - if not res: # pragma: no cover - messages.error(request,message) + if not res: # pragma: no cover + messages.error(request, message) url = reverse('paidplans_view') return HttpResponseRedirect(url) @@ -683,62 +703,65 @@ def team_create_view(request): breadcrumbs = [ { - 'url':reverse(rower_teams_view), + 'url': reverse(rower_teams_view), 'name': 'Groups' - }, + }, { - 'url':reverse(team_create_view), + 'url': reverse(team_create_view), 'name': "New Team" - }, - ] - return render(request,'teamcreate.html', + }, + ] + return render(request, 'teamcreate.html', { - 'teams':get_my_teams(request.user), - 'form':teamcreateform, - 'myteams':myteams, - 'memberteams':memberteams, - 'otherteams':otherteams, - 'active':'nav-teams', - 'breadcrumbs':breadcrumbs, - }) + 'teams': get_my_teams(request.user), + 'form': teamcreateform, + 'myteams': myteams, + 'memberteams': memberteams, + 'otherteams': otherteams, + 'active': 'nav-teams', + 'breadcrumbs': breadcrumbs, + }) -#@login_required() -@permission_required('teams.delete_team',fn=get_team_by_pk,raise_exception=True) -def team_deleteconfirm_view(request,team_id): +# @login_required() + + +@permission_required('teams.delete_team', fn=get_team_by_pk, raise_exception=True) +def team_deleteconfirm_view(request, team_id): r = getrower(request.user) - t = get_object_or_404(Team,pk=team_id) + t = get_object_or_404(Team, pk=team_id) myteams, memberteams, otherteams = get_teams(request) breadcrumbs = [ { - 'url':reverse(rower_teams_view), + 'url': reverse(rower_teams_view), 'name': 'Groups' - }, + }, { - 'url':reverse(team_view,kwargs={'team_id':team_id}), + 'url': reverse(team_view, kwargs={'team_id': team_id}), 'name': t.name - }, + }, { - 'url':reverse(team_deleteconfirm_view,kwargs={'team_id':team_id}), + 'url': reverse(team_deleteconfirm_view, kwargs={'team_id': team_id}), 'name': 'Leave' - } - ] - return render(request,'teamdeleteconfirm.html', + } + ] + return render(request, 'teamdeleteconfirm.html', { - 'teams':get_my_teams(request.user), - 'team':t, - 'myteams':myteams, - 'memberteams':memberteams, - 'otherteams':otherteams, - 'active':'nav-teams', + 'teams': get_my_teams(request.user), + 'team': t, + 'myteams': myteams, + 'memberteams': memberteams, + 'otherteams': otherteams, + 'active': 'nav-teams', }) + @login_required() -@permission_required('teams.delete_team',fn=get_team_by_pk,raise_exception=True) -def team_delete_view(request,team_id): +@permission_required('teams.delete_team', fn=get_team_by_pk, raise_exception=True) +def team_delete_view(request, team_id): r = getrower(request.user) - t = get_object_or_404(Team,pk=team_id) + t = get_object_or_404(Team, pk=team_id) teams.remove_team(t.id) @@ -746,13 +769,15 @@ def team_delete_view(request,team_id): response = HttpResponseRedirect(url) return response -@login_required() -@permission_required('teams.change_team',fn=get_team_by_pk,raise_exception=True) -def team_members_stats_view(request,team_id): - r = getrower(request.user) - t = get_object_or_404(Team,pk=team_id) - members = Rower.objects.filter(team=t).order_by("user__last_name","user__first_name") +@login_required() +@permission_required('teams.change_team', fn=get_team_by_pk, raise_exception=True) +def team_members_stats_view(request, team_id): + r = getrower(request.user) + t = get_object_or_404(Team, pk=team_id) + + members = Rower.objects.filter(team=t).order_by( + "user__last_name", "user__first_name") theusers = [member.user for member in members] @@ -760,53 +785,55 @@ def team_members_stats_view(request,team_id): breadcrumbs = [ { - 'url':reverse(rower_teams_view), + 'url': reverse(rower_teams_view), 'name': 'Groups' - }, + }, { - 'url':reverse(team_view,kwargs={'team_id':team_id}), + 'url': reverse(team_view, kwargs={'team_id': team_id}), 'name': t.name - }, + }, { - 'url':reverse(team_members_stats_view,kwargs={'team_id':team_id}), + 'url': reverse(team_members_stats_view, kwargs={'team_id': team_id}), 'name': 'Members Stats' - } - ] + } + ] - response = render(request,'teamstats.html', - { - 'teams':get_my_teams(request.user), - 'myteams':myteams, - 'memberteams':memberteams, - 'otherteams':otherteams, - 'active':'nav-teams', - 'breadcrumbs':breadcrumbs, - 'team':t, - 'theusers':theusers, - }) + response = render(request, 'teamstats.html', + { + 'teams': get_my_teams(request.user), + 'myteams': myteams, + 'memberteams': memberteams, + 'otherteams': otherteams, + 'active': 'nav-teams', + 'breadcrumbs': breadcrumbs, + 'team': t, + 'theusers': theusers, + }) return response + @login_required() -def rower_accept_coachoffer_view(request,code=None): +def rower_accept_coachoffer_view(request, code=None): if code: - res, text = teams.process_coachoffer_code(request.user,code) + res, text = teams.process_coachoffer_code(request.user, code) if res: - messages.info(request,text) - else: # pragma: no cover - messages.error(request,text) + messages.info(request, text) + else: # pragma: no cover + messages.error(request, text) url = reverse('rower_teams_view') return HttpResponseRedirect(url) + @login_required() -def coach_accept_coachrequest_view(request,code=None): +def coach_accept_coachrequest_view(request, code=None): if code: - res, text = teams.process_coachrequest_code(request.user.rower,code) + res, text = teams.process_coachrequest_code(request.user.rower, code) if res: - messages.info(request,text) - else: # pragma: no cover - messages.error(request,text) + messages.info(request, text) + else: # pragma: no cover + messages.error(request, text) url = reverse('rower_teams_view') return HttpResponseRedirect(url) diff --git a/rowers/views/userviews.py b/rowers/views/userviews.py index a7306d34..24718c14 100644 --- a/rowers/views/userviews.py +++ b/rowers/views/userviews.py @@ -5,6 +5,7 @@ from __future__ import unicode_literals from rowers.views.statements import * + @login_required() def deactivate_user(request): pk = request.user.id @@ -16,13 +17,15 @@ def deactivate_user(request): if user_form.is_valid(): if not user_form.cleaned_data['is_active']: r = Rower.objects.get(user=user) - if r.paidplan is not None and r.paidplan.paymentprocessor == 'braintree': # pragma: no cover + if r.paidplan is not None and r.paidplan.paymentprocessor == 'braintree': # pragma: no cover try: - subscriptions = braintreestuff.find_subscriptions(r) + subscriptions = braintreestuff.find_subscriptions( + r) for subscription in subscriptions: - success, themessages,errormessages = braintreestuff.cancel_subscription(r,id) + success, themessages, errormessages = braintreestuff.cancel_subscription( + r, id) for message in themessages: - messages.info(request,message) + messages.info(request, message) except ProcessorCustomerError: pass @@ -44,22 +47,24 @@ def deactivate_user(request): return render(request, "userprofile_deactivate.html", { "user_form": user_form, }) - else: # pragma: no cover + else: # pragma: no cover raise PermissionDenied + @login_required() def user_gdpr_optin(request): r = getrower(request.user) r.gdproptin = False r.gdproptindate = None r.save() - nexturl = request.GET.get('next','/rowers/list-workouts/') - if r.gdproptin: # pragma: no cover + nexturl = request.GET.get('next', '/rowers/list-workouts/') + if r.gdproptin: # pragma: no cover return HttpResponseRedirect(nexturl) - return render(request,'gdpr_optin.html',{ + return render(request, 'gdpr_optin.html', { "next": nexturl - }) + }) + @login_required() def user_gdpr_confirm(request): @@ -68,12 +73,11 @@ def user_gdpr_confirm(request): r.gdproptindate = timezone.now() r.save() - nexturl = request.GET.get('next','/rowers/list-workouts/') + nexturl = request.GET.get('next', '/rowers/list-workouts/') return HttpResponseRedirect(nexturl) - @login_required() def remove_user(request): pk = request.user.id @@ -81,20 +85,21 @@ def remove_user(request): user_form = DeleteUserForm(instance=user) if request.user.is_authenticated and request.user.id == user.id: if request.method == "POST": - user_form = DeleteUserForm(request.POST,instance=user) + user_form = DeleteUserForm(request.POST, instance=user) if user_form.is_valid(): cd = user_form.cleaned_data name = user.first_name+' '+user.last_name email = user.email r = Rower.objects.get(user=user) - if r.paidplan is not None and r.paidplan.paymentprocessor == 'braintree': # pragma: no cover + if r.paidplan is not None and r.paidplan.paymentprocessor == 'braintree': # pragma: no cover try: subscriptions = braintreestuff.find_subscriptions(r) for subscription in subscriptions: - success, themessages,errormessages = braintreestuff.cancel_subscription(r,id) + success, themessages, errormessages = braintreestuff.cancel_subscription( + r, id) for message in themessages: - messages.info(request,message) + messages.info(request, message) except: pass @@ -114,13 +119,13 @@ def remove_user(request): return HttpResponseRedirect(url) return render(request, "userprofile_delete.html", { "user_form": user_form, - }) - else: # pragma: no cover + }) + else: # pragma: no cover raise PermissionDenied @login_required() -def survey(request): # pragma: no cover +def survey(request): # pragma: no cover r = getrower(request.user) @@ -136,20 +141,20 @@ def survey(request): # pragma: no cover return HttpResponseRedirect(nexturl) context = { - 'teams':get_my_teams(request.user), - 'rower':r, - 'form':surveyform, - } + 'teams': get_my_teams(request.user), + 'rower': r, + 'form': surveyform, + } + return render(request, 'survey.html', context) - return render(request,'survey.html',context) @login_required() def start_trial_view(request): r = getrower(request.user) - if not can_start_trial(request.user): # pragma: no cover - messages.error(request,'You do not qualify for a trial') + if not can_start_trial(request.user): # pragma: no cover + messages.error(request, 'You do not qualify for a trial') url = '/rowers/paidplans' return HttpResponseRedirect(url) @@ -158,7 +163,7 @@ def start_trial_view(request): url = reverse('workouts_view') - messages.info(request,'We have started your 14 day trial period') + messages.info(request, 'We have started your 14 day trial period') subject2 = "User started Pro Trial" message2 = "User Started Pro Trial.\n" @@ -173,17 +178,18 @@ def start_trial_view(request): [r.user.email], 'Welcome to the Rowsandall Pro Trial', 'protrialewelcome.html', - {'first_name':r.user.first_name, - 'last_name':r.user.last_name}) + {'first_name': r.user.first_name, + 'last_name': r.user.last_name}) return HttpResponseRedirect(url) + @login_required() def start_plantrial_view(request): r = getrower(request.user) - if not can_start_plantrial(request.user): # pragma: no cover - messages.error(request,'You do not qualify for a trial') + if not can_start_plantrial(request.user): # pragma: no cover + messages.error(request, 'You do not qualify for a trial') url = '/rowers/paidplans' return HttpResponseRedirect(url) @@ -193,7 +199,7 @@ def start_plantrial_view(request): url = reverse('workouts_view') - messages.info(request,'We have started your 14 day trial period') + messages.info(request, 'We have started your 14 day trial period') subject2 = "User started Plan Trial" message2 = "User Started Plan Trial.\n" @@ -208,72 +214,77 @@ def start_plantrial_view(request): [r.user.email], 'Welcome to the Rowsandall Self-Coach Trial', 'plantrialwelcome.html', - {'first_name':r.user.first_name, - 'last_name':r.user.last_name}) + {'first_name': r.user.first_name, + 'last_name': r.user.last_name}) return HttpResponseRedirect(url) # Page where user can manage his favorite charts @login_required() -@permission_required('rower.is_coach',fn=get_user_by_userid,raise_exception=True) -def rower_favoritecharts_view(request,userid=0): +@permission_required('rower.is_coach', fn=get_user_by_userid, raise_exception=True) +def rower_favoritecharts_view(request, userid=0): message = '' successmessage = '' - r = getrequestrowercoachee(request,userid=userid,notpermanent=True) + r = getrequestrowercoachee(request, userid=userid, notpermanent=True) staticchartform = StaticChartRowerForm(instance=r) datasettingsform = DataRowerForm(instance=r) favorites = FavoriteChart.objects.filter(user=r).order_by('id') aantal = len(favorites) - favorites_data = [{'yparam1':f.yparam1, - 'yparam2':f.yparam2, - 'xparam':f.xparam, - 'plottype':f.plottype, - 'workouttype':f.workouttype, - 'reststrokes':f.reststrokes, - 'notes':f.notes,} - for f in favorites] - FavoriteChartFormSet = formset_factory(FavoriteForm,formset=BaseFavoriteFormSet,extra=0) - if aantal==0: - FavoriteChartFormSet = formset_factory(FavoriteForm,formset=BaseFavoriteFormSet,extra=1) + favorites_data = [{'yparam1': f.yparam1, + 'yparam2': f.yparam2, + 'xparam': f.xparam, + 'plottype': f.plottype, + 'workouttype': f.workouttype, + 'reststrokes': f.reststrokes, + 'notes': f.notes, } + for f in favorites] + FavoriteChartFormSet = formset_factory( + FavoriteForm, formset=BaseFavoriteFormSet, extra=0) + if aantal == 0: + FavoriteChartFormSet = formset_factory( + FavoriteForm, formset=BaseFavoriteFormSet, extra=1) - if request.method == 'POST' and 'staticgrids' in request.POST: # pragma: no cover - staticchartform = StaticChartRowerForm(request.POST,instance=r) + if request.method == 'POST' and 'staticgrids' in request.POST: # pragma: no cover + staticchartform = StaticChartRowerForm(request.POST, instance=r) if staticchartform.is_valid(): r.staticgrids = staticchartform.cleaned_data.get('staticgrids') r.slowpaceerg = staticchartform.cleaned_data.get('slowpaceerg') r.fastpaceerg = staticchartform.cleaned_data.get('fastpaceerg') r.slowpaceotw = staticchartform.cleaned_data.get('slowpaceotw') r.fastpaceotw = staticchartform.cleaned_data.get('fastpaceotw') - r.staticchartonupload = staticchartform.cleaned_data.get('staticchartonupload') + r.staticchartonupload = staticchartform.cleaned_data.get( + 'staticchartonupload') r.fav_analysis = staticchartform.cleaned_data.get('fav_analysis') r.usersmooth = staticchartform.cleaned_data.get('usersmooth') r.save() - if request.method == 'POST' and 'save_data' in request.POST: # pragma: no cover - datasettingsform = DataRowerForm(request.POST,instance=r) + if request.method == 'POST' and 'save_data' in request.POST: # pragma: no cover + datasettingsform = DataRowerForm(request.POST, instance=r) if datasettingsform.is_valid(): cd = datasettingsform.cleaned_data r.autojoin = cd.get('autojoin') r.dosmooth = cd.get('dosmooth') r.erg_recalculatepower = cd.get('erg_recalculatepower') r.save() - messages.info(request,"We have updated your data settings") + messages.info(request, "We have updated your data settings") - if request.method == 'POST' and 'defaults_data' in request.POST: # pragma: no cover + if request.method == 'POST' and 'defaults_data' in request.POST: # pragma: no cover defaultsmooth = Rower._meta.get_field('dosmooth').get_default() defaultautojoin = Rower._meta.get_field('autojoin').get_default() - defaultergcalcpower = Rower._meta.get_field('erg_recalculatepower').get_default() + defaultergcalcpower = Rower._meta.get_field( + 'erg_recalculatepower').get_default() r.dosmooth = defaultsmooth r.autojoin = defaultautojoin r.erg_recalculatepower = defaultergcalcpower r.save() datasettingsform = DataRowerForm(instance=r) - messages.info(request,"We have reset your data settings to the default values") + messages.info( + request, "We have reset your data settings to the default values") - if request.method == 'POST' and 'form-TOTAL_FORMS' in request.POST: # pragma: no cover + if request.method == 'POST' and 'form-TOTAL_FORMS' in request.POST: # pragma: no cover favorites_formset = FavoriteChartFormSet(request.POST) if favorites_formset.is_valid(): new_instances = [] @@ -298,53 +309,54 @@ def rower_favoritecharts_view(request,userid=0): FavoriteChart.objects.filter(user=r).delete() FavoriteChart.objects.bulk_create(new_instances) successmessage = "You have updated your favorites" - messages.info(request,message) - if len(new_instances)==0: - FavoriteChartFormSet=formset_factory(FavoriteForm,formset=BaseFavoriteFormSet,extra=1) + messages.info(request, message) + if len(new_instances) == 0: + FavoriteChartFormSet = formset_factory( + FavoriteForm, formset=BaseFavoriteFormSet, extra=1) favorites_formset = FavoriteChartFormSet() except IntegrityError: message = "something went wrong" - messages.error(request,message) + messages.error(request, message) else: favorites_formset = FavoriteChartFormSet(initial=favorites_data) - context = { - 'favorites_formset':favorites_formset, - 'teams':get_my_teams(request.user), - 'rower':r, - 'staticchartform':staticchartform, - 'datasettingsform':datasettingsform, - } + 'favorites_formset': favorites_formset, + 'teams': get_my_teams(request.user), + 'rower': r, + 'staticchartform': staticchartform, + 'datasettingsform': datasettingsform, + } - - - return render(request,'favoritecharts.html',context) + return render(request, 'favoritecharts.html', context) # page where user sets his export settings + + @login_required() -@permission_required('rower.is_coach',fn=get_user_by_userid,raise_exception=True) -def rower_exportsettings_view(request,userid=0): +@permission_required('rower.is_coach', fn=get_user_by_userid, raise_exception=True) +def rower_exportsettings_view(request, userid=0): attrtokens = { - 'polar_auto_import':'polartoken', - 'c2_auto_export':'c2token', - 'c2_auto_import':'c2token', - 'runkeeper_auto_export':'runkeepertoken', - 'sporttracks_auto_export':'sporttrackstoken', - 'strava_auto_export':'stravatoken', - 'strava_auto_import':'stravatoken', - 'strava_auto_delete':'stravatoken', - 'trainingpeaks_auto_export':'tptoken', - 'rp3_auto_import':'rp3token', - 'nk_auto_import':'nktoken' + 'polar_auto_import': 'polartoken', + 'c2_auto_export': 'c2token', + 'c2_auto_import': 'c2token', + 'runkeeper_auto_export': 'runkeepertoken', + 'sporttracks_auto_export': 'sporttrackstoken', + 'strava_auto_export': 'stravatoken', + 'strava_auto_import': 'stravatoken', + 'strava_auto_delete': 'stravatoken', + 'trainingpeaks_auto_export': 'tptoken', + 'rp3_auto_import': 'rp3token', + 'nk_auto_import': 'nktoken' } - r = getrequestrowercoachee(request,userid=userid) + r = getrequestrowercoachee(request, userid=userid) if request.method == 'POST': form = RowerExportForm(request.POST) if form.is_valid(): cd = form.cleaned_data - if r.rowerplan == 'basic': # pragma: no cover - messages.error(request,'These settings can only be set if you are a user on one of the paid plans.') + if r.rowerplan == 'basic': # pragma: no cover + messages.error( + request, 'These settings can only be set if you are a user on one of the paid plans.') for attr, value in cd.items(): doset = True @@ -354,50 +366,52 @@ def rower_exportsettings_view(request,userid=0): doset = False except KeyError: doset = True - if r.rowerplan == 'basic': # pragma: no cover + if r.rowerplan == 'basic': # pragma: no cover doset = False if not doset: - before = getattr(r,attr) + before = getattr(r, attr) if before == value: doset = True if doset: setattr(r, attr, value) else: if r.rowerplan != 'basic': - messages.error(request,'Could not set '+attr+'. You need to create the connection first.') + messages.error( + request, 'Could not set '+attr+'. You need to create the connection first.') r.save() - messages.info(request,'Settings saved') + messages.info(request, 'Settings saved') else: form = RowerExportForm(instance=r) breadcrumbs = [ { - 'url':'/rowers/me/edit/', + 'url': '/rowers/me/edit/', 'name': 'Profile' - }, + }, { 'url': reverse('rower_exportsettings_view'), 'name': 'Export Settings' - } - ] + } + ] return render(request, 'rower_exportsettings.html', - {'form':form, - 'rower':r, + {'form': form, + 'rower': r, 'breadcrumbs': breadcrumbs, - }) + }) # Page where user can set his details # Add email address to form so user can change his email address @login_required() -@permission_required('rower.is_coach',fn=get_user_by_userid,raise_exception=True) -def rower_edit_view(request,rowerid=0,userid=0,message=""): - r = getrequestrowercoachee(request,rowerid=rowerid,userid=userid,notpermanent=True) +@permission_required('rower.is_coach', fn=get_user_by_userid, raise_exception=True) +def rower_edit_view(request, rowerid=0, userid=0, message=""): + r = getrequestrowercoachee( + request, rowerid=rowerid, userid=userid, notpermanent=True) - if 'courseshare' in request.GET: # pragma: no cover - courseshare = request.GET.get('courseshare',"ok") + if 'courseshare' in request.GET: # pragma: no cover + courseshare = request.GET.get('courseshare', "ok") if courseshare == 'true': r.share_course_results = True r.save() @@ -409,19 +423,18 @@ def rower_edit_view(request,rowerid=0,userid=0,message=""): breadcrumbs = [ { - 'url':'/rowers/me/edit/', + 'url': '/rowers/me/edit/', 'name': 'Profile' - }, + }, { 'url': reverse('rower_edit_view'), 'name': 'Account Settings' - } - ] - + } + ] if request.method == 'POST': accountform = AccountRowerForm(request.POST, instance=r) - userform = UserForm(request.POST,instance=r.user) + userform = UserForm(request.POST, instance=r.user) if accountform.is_valid() and userform.is_valid(): # process @@ -434,7 +447,7 @@ def rower_edit_view(request,rowerid=0,userid=0,message=""): sex = cd['sex'] try: offercoaching = cd['offercoaching'] - except KeyError: # pragma: no cover + except KeyError: # pragma: no cover offercoaching = False autojoin = cd['autojoin'] adaptiveclass = cd['adaptiveclass'] @@ -445,25 +458,25 @@ def rower_edit_view(request,rowerid=0,userid=0,message=""): getemailnotifications = cd['getemailnotifications'] getimportantemails = cd['getimportantemails'] share_course_results = cd['share_course_results'] - defaulttimezone=cd['defaulttimezone'] + defaulttimezone = cd['defaulttimezone'] fav_analysis = cd['fav_analysis'] usersmooth = cd['usersmooth'] u = r.user - if u.email != email and len(email): # pragma: no cover + if u.email != email and len(email): # pragma: no cover resetbounce = True else: resetbounce = False if len(first_name): u.first_name = first_name u.last_name = last_name - if len(email): ## and check_email_freeforuse(u,email): + if len(email): # and check_email_freeforuse(u,email): u.email = email resetbounce = True emailalternatives = cd['emailalternatives'] u.save() - r.defaulttimezone=defaulttimezone + r.defaulttimezone = defaulttimezone r.weightcategory = weightcategory r.adaptiveclass = adaptiveclass r.getemailnotifications = getemailnotifications @@ -479,51 +492,49 @@ def rower_edit_view(request,rowerid=0,userid=0,message=""): r.fav_analysis = fav_analysis r.usersmooth = usersmooth - - if resetbounce and r.emailbounced: # pragma: no cover + if resetbounce and r.emailbounced: # pragma: no cover r.emailbounced = False r.save() accountform = AccountRowerForm(instance=r) userform = UserForm(instance=u) successmessage = 'Account Information changed' - messages.info(request,successmessage) + messages.info(request, successmessage) else: accountform = AccountRowerForm(instance=r) userform = UserForm(instance=r.user) - grants = AccessToken.objects.filter(user=request.user) return render(request, 'rower_form.html', { - 'teams':get_my_teams(request.user), - 'breadcrumbs':breadcrumbs, - 'grants':grants, - 'userform':userform, - 'accountform':accountform, - 'rower':r, + 'teams': get_my_teams(request.user), + 'breadcrumbs': breadcrumbs, + 'grants': grants, + 'userform': userform, + 'accountform': accountform, + 'rower': r, }) # Page where user can set his details # Add email address to form so user can change his email address @login_required() -@permission_required('rower.is_coach',fn=get_user_by_userid,raise_exception=True) -def rower_prefs_view(request,userid=0,message=""): - r = getrequestrowercoachee(request,userid=userid,notpermanent=True) +@permission_required('rower.is_coach', fn=get_user_by_userid, raise_exception=True) +def rower_prefs_view(request, userid=0, message=""): + r = getrequestrowercoachee(request, userid=userid, notpermanent=True) rowerid = r.id breadcrumbs = [ { - 'url':'/rowers/me/edit/', + 'url': '/rowers/me/edit/', 'name': 'Profile' - }, + }, { 'url': reverse('rower_prefs_view'), 'name': 'Zones' - } - ] + } + ] form = RowerHRZonesForm(instance=r) powerform = RowerPowerForm(instance=r) @@ -550,26 +561,26 @@ def rower_prefs_view(request,userid=0,message=""): hrtrname = cd['hrtrname'] hranname = cd['hranname'] hrmaxname = cd['hrmaxname'] - hrzones = [hrrestname,hrut2name,hrut1name,hratname,hrtrname,hranname,hrmaxname] + hrzones = [hrrestname, hrut2name, hrut1name, + hratname, hrtrname, hranname, hrmaxname] - - r.max = max(min(hrmax,250),10) - r.ut2 = max(min(ut2,250),10) - r.ut1 = max(min(ut1,250),10) - r.at = max(min(at,250),10) - r.tr = max(min(tr,250),10) - r.an = max(min(an,250),10) - r.rest = max(min(rest,250),10) + r.max = max(min(hrmax, 250), 10) + r.ut2 = max(min(ut2, 250), 10) + r.ut1 = max(min(ut1, 250), 10) + r.at = max(min(at, 250), 10) + r.tr = max(min(tr, 250), 10) + r.an = max(min(an, 250), 10) + r.rest = max(min(rest, 250), 10) r.hrzones = hrzones r.save() successmessage = "Your Heart Rate data were changed" - messages.info(request,successmessage) + messages.info(request, successmessage) elif request.method == 'POST' and "ftp" in request.POST: powerform = RowerPowerForm(request.POST) if powerform.is_valid(): cd = powerform.cleaned_data hrftp = cd['hrftp'] - if hrftp == 0: # pragma: no cover + if hrftp == 0: # pragma: no cover hrftp = int((r.an+r.tr)/2.) ftp = cd['ftp'] otwslack = cd['otwslack'] @@ -577,10 +588,10 @@ def rower_prefs_view(request,userid=0,message=""): powerfrac = 100*np.array([r.pw_ut2, r.pw_ut1, r.pw_at, - r.pw_tr,r.pw_an])/r.ftp - r.ftp = max(min(ftp,650),50) - r.otwslack = max(min(otwslack,50),0) - ut2,ut1,at,tr,an = (r.ftp*powerfrac/100.).astype(int) + r.pw_tr, r.pw_an])/r.ftp + r.ftp = max(min(ftp, 650), 50) + r.otwslack = max(min(otwslack, 50), 0) + ut2, ut1, at, tr, an = (r.ftp*powerfrac/100.).astype(int) r.pw_ut2 = ut2 r.pw_ut1 = ut1 r.pw_at = at @@ -589,7 +600,7 @@ def rower_prefs_view(request,userid=0,message=""): r.hrftp = hrftp r.save() message = "FTP and/or OTW slack values changed." - messages.info(request,message) + messages.info(request, message) elif request.method == 'POST' and "ut3name" in request.POST: powerzonesform = RowerPowerZonesForm(request.POST) @@ -606,7 +617,7 @@ def rower_prefs_view(request,userid=0,message=""): atname = cd['atname'] trname = cd['trname'] anname = cd['anname'] - powerzones = [ut3name,ut2name,ut1name,atname,trname,anname] + powerzones = [ut3name, ut2name, ut1name, atname, trname, anname] r.pw_ut2 = pw_ut2 r.pw_ut1 = pw_ut1 @@ -616,8 +627,8 @@ def rower_prefs_view(request,userid=0,message=""): r.powerzones = powerzones r.save() successmessage = "Your Power Zone data were changed" - messages.info(request,successmessage) - elif request.method == 'POST' and 'cprange' in request.POST: # pragma: no cover + messages.info(request, successmessage) + elif request.method == 'POST' and 'cprange' in request.POST: # pragma: no cover cpform = RowerCPForm(request.POST) if cpform.is_valid(): cd = cpform.cleaned_data @@ -628,19 +639,19 @@ def rower_prefs_view(request,userid=0,message=""): r.kfit = kfit r.kfatigue = kfatigue r.save() - messages.info(request,'Updated CP range and time decay constants') - success = dataprep.update_rolling_cp(r,mytypes.otwtypes,'water') - success = dataprep.update_rolling_cp(r,mytypes.otetypes,'erg') + messages.info(request, 'Updated CP range and time decay constants') + success = dataprep.update_rolling_cp(r, mytypes.otwtypes, 'water') + success = dataprep.update_rolling_cp(r, mytypes.otetypes, 'erg') return render(request, 'rower_preferences.html', { - 'form':form, - 'teams':get_my_teams(request.user), - 'powerform':powerform, - 'powerzonesform':powerzonesform, - 'breadcrumbs':breadcrumbs, - 'cpform':cpform, - 'rower':r, + 'form': form, + 'teams': get_my_teams(request.user), + 'powerform': powerform, + 'powerzonesform': powerzonesform, + 'breadcrumbs': breadcrumbs, + 'cpform': cpform, + 'rower': r, }) @@ -648,10 +659,11 @@ def rower_prefs_view(request,userid=0,message=""): # this views is called when you press a button on the User edit page # the button is only there when you have granted access to an app @login_required() -def rower_revokeapp_view(request,id=0): # pragma: no cover +def rower_revokeapp_view(request, id=0): # pragma: no cover try: - tokens = AccessToken.objects.filter(user=request.user,application=id) - refreshtokens = AccessToken.objects.filter(user=request.user,application=id) + tokens = AccessToken.objects.filter(user=request.user, application=id) + refreshtokens = AccessToken.objects.filter( + user=request.user, application=id) for token in tokens: token.revoke() for token in refreshtokens: @@ -672,7 +684,7 @@ def rower_update_empower_view( request, startdate=timezone.now()-datetime.timedelta(days=365), enddate=timezone.now() -): # pragma: no cover +): # pragma: no cover try: r = getrower(request.user) except Rower.DoesNotExist: @@ -689,10 +701,9 @@ def rower_update_empower_view( request.session['enddate'] = enddatestring else: dateform = DateRangeForm(initial={ - 'startdate':startdate, - 'enddate':enddate, - }) - + 'startdate': startdate, + 'enddate': enddate, + }) if request.method == 'POST' and 'workouts' in request.POST: form = WorkoutMultipleCompareForm(request.POST) @@ -704,25 +715,27 @@ def rower_update_empower_view( for w in workouts: if w.user != r: message = "You can only alter your own workouts" - messages.error(request,message) + messages.error(request, message) if 'x' in w.boattype and w.oarlength is not None and w.oarlength > 3.30: - message = "Oarlength and boat type mismatch for workout "+str(w.id)+". Skipping workout" - messages.error(request,message) + message = "Oarlength and boat type mismatch for workout " + \ + str(w.id)+". Skipping workout" + messages.error(request, message) elif 'x' not in w.boattype and w.oarlength is not None and w.oarlength <= 3.30: - message = "Oarlength and boat type mismatch for workout "+str(w.id)+". Skipping workout" - messages.error(request,message) + message = "Oarlength and boat type mismatch for workout " + \ + str(w.id)+". Skipping workout" + messages.error(request, message) elif w.oarlength is None: - message = "Incorrect oarlength in workout "+str(w.id)+". Skipping workout" - messages.error(request,message) + message = "Incorrect oarlength in workout " + \ + str(w.id)+". Skipping workout" + messages.error(request, message) else: - workoutdict = { - 'id':w.id, - 'boattype':w.boattype, - 'filename':w.csvfilename, - 'inboard':w.inboard, - 'oarlength':w.oarlength + 'id': w.id, + 'boattype': w.boattype, + 'filename': w.csvfilename, + 'inboard': w.inboard, + 'oarlength': w.oarlength } workoutdicts.append(workoutdict) @@ -730,20 +743,19 @@ def rower_update_empower_view( w.workoutsource = 'speedcoach2corrected' w.save() - - job = myqueue(queuelow,handle_update_empower, - request.user.email,workoutdicts, + job = myqueue(queuelow, handle_update_empower, + request.user.email, workoutdicts, debug=False, emailbounced=r.emailbounced) try: - request.session['async_tasks'] += [(job.id,'update_empower')] + request.session['async_tasks'] += [(job.id, 'update_empower')] except KeyError: - request.session['async_tasks'] = [(job.id,'update_empower')] + request.session['async_tasks'] = [(job.id, 'update_empower')] successmessage = 'Your workouts are being updated in the background. You will receive email when this is done. You can check the status of your calculations here' - messages.info(request,successmessage) + messages.info(request, successmessage) url = reverse('workouts_view') return HttpResponseRedirect(url) @@ -754,16 +766,16 @@ def rower_update_empower_view( startdatetime__lte=enddate, workoutsource='speedcoach2', user=r, - ).order_by("-date","-starttime") + ).order_by("-date", "-starttime") form = WorkoutMultipleCompareForm() form.fields["workouts"].queryset = workouts # GET request = prepare form return render(request, 'empower_fix.html', - {'workouts':workouts, + {'workouts': workouts, 'active': 'nav-workouts', - 'dateform':dateform, - 'form':form, - 'rower':r + 'dateform': dateform, + 'form': form, + 'rower': r }) diff --git a/rowers/views/workoutviews.py b/rowers/views/workoutviews.py index 44829b37..2b9341f3 100644 --- a/rowers/views/workoutviews.py +++ b/rowers/views/workoutviews.py @@ -20,11 +20,15 @@ from json.decoder import JSONDecodeError import ruptures as rpt from rowers.courses import getnearestraces, getnearestcourses -def default(o): # pragma: no cover - if isinstance(o, numpy.int64): return int(o) - if isinstance(o, numpy.int32): return int(o) + +def default(o): # pragma: no cover + if isinstance(o, numpy.int64): + return int(o) + if isinstance(o, numpy.int32): + return int(o) raise TypeError + def get_video_id(url): """Returns Video_ID extracting from the given url of Youtube @@ -43,7 +47,7 @@ def get_video_id(url): if url.startswith(('youtu', 'www')): url = 'http://' + url - elif 'http' not in url: # pragma: no cover + elif 'http' not in url: # pragma: no cover # not sure if this is a valid case at all return url @@ -56,15 +60,17 @@ def get_video_id(url): return query.path.split('/')[2] elif 'youtu.be' in query.hostname: return query.path[1:] - else: # pragma: no cover + else: # pragma: no cover raise ValueError # Show a video compared with data -def workout_video_view_mini(request,id=''): + + +def workout_video_view_mini(request, id=''): try: id = encoder.decode_hex(id) analysis = VideoAnalysis.objects.get(id=id) - except (VideoAnalysis.DoesNotExist,ValueError): # pragma: no cover + except (VideoAnalysis.DoesNotExist, ValueError): # pragma: no cover raise Http404("Video Analysis does not exist") w = analysis.workout @@ -75,10 +81,9 @@ def workout_video_view_mini(request,id=''): else: mode = 'erg' - - if request.user.is_authenticated: - mayedit = is_workout_user(request.user,w) and is_promember(request.user) + mayedit = is_workout_user( + request.user, w) and is_promember(request.user) rower = request.user.rower else: mayedit = False @@ -87,12 +92,12 @@ def workout_video_view_mini(request,id=''): # get video ID and offset if mayedit and request.method == 'POST': form = VideoAnalysisCreateForm(request.POST) - metricsform = VideoAnalysisMetricsForm(request.POST,mode=mode) + metricsform = VideoAnalysisMetricsForm(request.POST, mode=mode) if form.is_valid() and metricsform.is_valid(): video_id = form.cleaned_data['url'] try: video_id = get_video_id(form.cleaned_data['url']) - except (TypeError,ValueError): # pragma: no cover + except (TypeError, ValueError): # pragma: no cover pass delay = form.cleaned_data['delay'] metricsgroups = metricsform.cleaned_data['groups'] @@ -102,19 +107,19 @@ def workout_video_view_mini(request,id=''): analysis.delay = delay analysis.metricsgroups = metricsgroups analysis.save() - else: # pragma: no cover + else: # pragma: no cover # invalid forms video_id = id delay = 0 elif mayedit: form = VideoAnalysisCreateForm( - initial = { - 'name':analysis.name, + initial={ + 'name': analysis.name, 'delay': analysis.delay, 'url': analysis.video_id, } ) - metricsform = VideoAnalysisMetricsForm(initial={'groups':analysis.metricsgroups}, + metricsform = VideoAnalysisMetricsForm(initial={'groups': analysis.metricsgroups}, mode=mode) metricsgroups = analysis.metricsgroups video_id = analysis.video_id @@ -123,62 +128,61 @@ def workout_video_view_mini(request,id=''): metricsform = None metricsgroups = analysis.metricsgroups - data, metrics, maxtime = dataprep.get_video_data(w,groups=metricsgroups,mode=mode) + data, metrics, maxtime = dataprep.get_video_data( + w, groups=metricsgroups, mode=mode) hascoordinates = pd.Series(data['latitude']).std() > 0 # create map - if hascoordinates and mode=='water': - mapscript, mapdiv = leaflet_chart_video(data['latitude'],data['longitude'], + if hascoordinates and mode == 'water': + mapscript, mapdiv = leaflet_chart_video(data['latitude'], data['longitude'], w.name) else: mapscript, mapdiv = interactive_chart_video(data) data['longitude'] = data['spm'] data['latitude'] = list(range(len(data['spm']))) - breadcrumbs = [ - { - 'url':'/rowers/list-workouts/', - 'name':'Workouts' - }, - { - 'url':get_workout_default_page(request,encoder.encode_hex(w.id)), - 'name': w.name - }, - { - 'url':reverse('workout_video_view_mini',kwargs={'id':encoder.encode_hex(analysis.id)}), - 'name': 'Video Analysis' - } - - ] + { + 'url': '/rowers/list-workouts/', + 'name': 'Workouts' + }, + { + 'url': get_workout_default_page(request, encoder.encode_hex(w.id)), + 'name': w.name + }, + { + 'url': reverse('workout_video_view_mini', kwargs={'id': encoder.encode_hex(analysis.id)}), + 'name': 'Video Analysis' + } + ] return render(request, 'embedded_video_mini.html', { - 'workout':w, - 'rower':rower, - 'data': json.dumps(data,default=default), + 'workout': w, + 'rower': rower, + 'data': json.dumps(data, default=default), 'mapscript': mapscript, 'mapdiv': mapdiv, 'video_id': analysis.video_id, - 'form':form, - 'breadcrumbs':breadcrumbs, - 'analysis':analysis, - 'maxtime':maxtime, - 'metrics':metrics, + 'form': form, + 'breadcrumbs': breadcrumbs, + 'analysis': analysis, + 'maxtime': maxtime, + 'metrics': metrics, 'locked': True, - 'metricsform':metricsform, + 'metricsform': metricsform, 'metricsgroups': metricsgroups, 'siteurl': settings.SITE_URL, - }) + }) # Show a video compared with data -def workout_video_view(request,id=''): +def workout_video_view(request, id=''): try: id = encoder.decode_hex(id) analysis = VideoAnalysis.objects.get(id=id) - except (VideoAnalysis.DoesNotExist,ValueError): # pragma: no cover + except (VideoAnalysis.DoesNotExist, ValueError): # pragma: no cover raise Http404("Video Analysis does not exist") w = analysis.workout @@ -190,7 +194,8 @@ def workout_video_view(request,id=''): mode = 'erg' if request.user.is_authenticated: - mayedit = is_promember(request.user) and is_workout_user(request.user,w) + mayedit = is_promember( + request.user) and is_workout_user(request.user, w) rower = request.user.rower else: mayedit = False @@ -199,12 +204,12 @@ def workout_video_view(request,id=''): # get video ID and offset if mayedit and request.method == 'POST': form = VideoAnalysisCreateForm(request.POST) - metricsform = VideoAnalysisMetricsForm(request.POST,mode=mode) + metricsform = VideoAnalysisMetricsForm(request.POST, mode=mode) if form.is_valid() and metricsform.is_valid(): video_id = form.cleaned_data['url'] try: video_id = get_video_id(form.cleaned_data['url']) - except (TypeError,ValueError): # pragma: no cover + except (TypeError, ValueError): # pragma: no cover pass delay = form.cleaned_data['delay'] metricsgroups = metricsform.cleaned_data['groups'] @@ -214,18 +219,18 @@ def workout_video_view(request,id=''): analysis.delay = delay analysis.metricsgroups = metricsgroups analysis.save() - else: # pragma: no cover + else: # pragma: no cover video_id = id delay = 0 elif mayedit: form = VideoAnalysisCreateForm( - initial = { - 'name':analysis.name, + initial={ + 'name': analysis.name, 'delay': analysis.delay, 'url': analysis.video_id, } ) - metricsform = VideoAnalysisMetricsForm(initial={'groups':analysis.metricsgroups}, + metricsform = VideoAnalysisMetricsForm(initial={'groups': analysis.metricsgroups}, mode=mode) metricsgroups = analysis.metricsgroups video_id = analysis.video_id @@ -234,64 +239,63 @@ def workout_video_view(request,id=''): metricsform = None metricsgroups = analysis.metricsgroups - data, metrics, maxtime = dataprep.get_video_data(w,groups=metricsgroups,mode=mode) + data, metrics, maxtime = dataprep.get_video_data( + w, groups=metricsgroups, mode=mode) hascoordinates = pd.Series(data['latitude']).std() > 0 # create map - if hascoordinates and mode=='water': - mapscript, mapdiv = leaflet_chart_video(data['latitude'],data['longitude'], + if hascoordinates and mode == 'water': + mapscript, mapdiv = leaflet_chart_video(data['latitude'], data['longitude'], w.name) else: mapscript, mapdiv = interactive_chart_video(data) data['longitude'] = data['spm'] data['latitude'] = list(range(len(data['spm']))) - breadcrumbs = [ - { - 'url':'/rowers/list-workouts/', - 'name':'Workouts' - }, - { - 'url':get_workout_default_page(request,encoder.encode_hex(w.id)), - 'name': w.name - }, - { - 'url':reverse('workout_video_view',kwargs={'id':encoder.encode_hex(analysis.id)}), - 'name': 'Video Analysis' - } - - ] + { + 'url': '/rowers/list-workouts/', + 'name': 'Workouts' + }, + { + 'url': get_workout_default_page(request, encoder.encode_hex(w.id)), + 'name': w.name + }, + { + 'url': reverse('workout_video_view', kwargs={'id': encoder.encode_hex(analysis.id)}), + 'name': 'Video Analysis' + } + ] return render(request, 'embedded_video.html', { - 'workout':w, - 'rower':rower, - 'data': json.dumps(data,default=default), + 'workout': w, + 'rower': rower, + 'data': json.dumps(data, default=default), 'mapscript': mapscript, 'mapdiv': mapdiv, 'video_id': analysis.video_id, - 'form':form, - 'breadcrumbs':breadcrumbs, - 'analysis':analysis, - 'maxtime':maxtime, - 'metrics':metrics, + 'form': form, + 'breadcrumbs': breadcrumbs, + 'analysis': analysis, + 'maxtime': maxtime, + 'metrics': metrics, 'locked': True, - 'metricsform':metricsform, + 'metricsform': metricsform, 'metricsgroups': metricsgroups, 'siteurl': settings.SITE_URL, - }) + }) # Create a video compared with data -@permission_required('workout.change_workout',fn=get_workout_by_opaqueid,raise_exception=True) -@user_passes_test(ispromember,login_url="/rowers/paidplans/", +@permission_required('workout.change_workout', fn=get_workout_by_opaqueid, raise_exception=True) +@user_passes_test(ispromember, login_url="/rowers/paidplans/", message="This functionality requires a Pro plan or higher", redirect_field_name=None) -def workout_video_create_view(request,id=0): +def workout_video_create_view(request, id=0): # get workout - w = get_workout_by_opaqueid(request,id) + w = get_workout_by_opaqueid(request, id) if w.workouttype in mytypes.otwtypes: mode = 'water' else: @@ -300,57 +304,61 @@ def workout_video_create_view(request,id=0): # get video ID and offset if request.method == 'POST': form = VideoAnalysisCreateForm(request.POST) - metricsform = VideoAnalysisMetricsForm(request.POST,mode=mode) + metricsform = VideoAnalysisMetricsForm(request.POST, mode=mode) if form.is_valid() and metricsform.is_valid(): url = form.cleaned_data['url'] delay = form.cleaned_data['delay'] metricsgroups = metricsform.cleaned_data['groups'] try: video_id = get_video_id(url) - except ValueError: # pragma: no cover - messages.error(request,"Not a valid YouTube video link") + except ValueError: # pragma: no cover + messages.error(request, "Not a valid YouTube video link") video_id = None if 'save_button' in request.POST: analysis = VideoAnalysis( workout=w, name=form.cleaned_data['name'], - video_id = video_id, + video_id=video_id, delay=delay, metricsgroups=metricsgroups ) try: analysis.save() url = reverse('workout_video_view', - kwargs={'id':encoder.encode_hex(analysis.id)}) + kwargs={'id': encoder.encode_hex(analysis.id)}) return HttpResponseRedirect(url) - except IntegrityError: # pragma: no cover - messages.error(request,'You cannot save two video analysis with the same YouTube video and Workout. Redirecting to your existing analysis') - analysis = VideoAnalysis.objects.filter(workout=w,video_id=video_id) + except IntegrityError: # pragma: no cover + messages.error( + request, 'You cannot save two video analysis with the same YouTube video and Workout. Redirecting to your existing analysis') + analysis = VideoAnalysis.objects.filter( + workout=w, video_id=video_id) if analysis: url = reverse('workout_video_view', - kwargs={'id':encoder.encode_hex(analysis[0].id)}) + kwargs={'id': encoder.encode_hex(analysis[0].id)}) else: url = reverse('workout_video_create_view', - kwargs={'id':encoder.encode_hex(w.id)}) + kwargs={'id': encoder.encode_hex(w.id)}) return HttpResponseRedirect(url) - else: # pragma: no cover + else: # pragma: no cover video_id = None delay = 0 metricsgroups = ['basic'] else: form = VideoAnalysisCreateForm() - metricsform = VideoAnalysisMetricsForm(initial={'groups':['basic']},mode=mode) + metricsform = VideoAnalysisMetricsForm( + initial={'groups': ['basic']}, mode=mode) video_id = None delay = 0 metricsgroups = ['basic'] # get data - data, metrics, maxtime = dataprep.get_video_data(w,groups=metricsgroups,mode=mode) + data, metrics, maxtime = dataprep.get_video_data( + w, groups=metricsgroups, mode=mode) hascoordinates = pd.Series(data['latitude']).std() > 0 # create map - if hascoordinates and mode=='water': - mapscript, mapdiv = leaflet_chart_video(data['latitude'],data['longitude'], + if hascoordinates and mode == 'water': + mapscript, mapdiv = leaflet_chart_video(data['latitude'], data['longitude'], w.name) else: mapscript, mapdiv = interactive_chart_video(data) @@ -358,56 +366,57 @@ def workout_video_create_view(request,id=0): data['latitude'] = list(range(len(data['spm']))) breadcrumbs = [ - { - 'url':'/rowers/list-workouts/', - 'name':'Workouts' - }, - { - 'url':get_workout_default_page(request,encoder.encode_hex(w.id)), - 'name': w.name - }, - { - 'url':reverse('workout_video_create_view',kwargs={'id':encoder.encode_hex(w.id)}), - 'name': 'Video Analysis' - } + { + 'url': '/rowers/list-workouts/', + 'name': 'Workouts' + }, + { + 'url': get_workout_default_page(request, encoder.encode_hex(w.id)), + 'name': w.name + }, + { + 'url': reverse('workout_video_create_view', kwargs={'id': encoder.encode_hex(w.id)}), + 'name': 'Video Analysis' + } - ] + ] - analysis = {'delay':delay} + analysis = {'delay': delay} template = 'embedded_video.html' - return render(request, template, { - 'workout':w, - 'rower':request.user.rower, - 'data': json.dumps(data,default=default), + 'workout': w, + 'rower': request.user.rower, + 'data': json.dumps(data, default=default), 'mapscript': mapscript, 'mapdiv': mapdiv, 'video_id': video_id, - 'form':form, - 'metricsform':metricsform, - 'analysis':analysis, - 'breadcrumbs':breadcrumbs, - 'maxtime':maxtime, - 'metrics':metrics, + 'form': form, + 'metricsform': metricsform, + 'analysis': analysis, + 'breadcrumbs': breadcrumbs, + 'maxtime': maxtime, + 'metrics': metrics, 'metricsgroups': metricsgroups, 'locked': False, 'siteurl': settings.SITE_URL, - }) + }) # Show the EMpower Oarlock generated Stroke Profile -@user_passes_test(ispromember,login_url="/rowers/paidplans/", + + +@user_passes_test(ispromember, login_url="/rowers/paidplans/", message="This functionality requires a Pro plan or higher. If you are already a Pro user, please log in to access this functionality. If you are already a Pro user, please log in to access this functionality", redirect_field_name=None) -@permission_required('workout.change_workout',fn=get_workout_by_opaqueid,raise_exception=True) -def workout_forcecurve_view(request,id=0,workstrokesonly=False): +@permission_required('workout.change_workout', fn=get_workout_by_opaqueid, raise_exception=True) +def workout_forcecurve_view(request, id=0, workstrokesonly=False): row = get_workoutuser(id, request) - promember=0 - mayedit=0 + promember = 0 + mayedit = 0 r = getrequestrower(request) promember = 1 @@ -421,165 +430,162 @@ def workout_forcecurve_view(request,id=0,workstrokesonly=False): includereststrokes = form.cleaned_data['includereststrokes'] plottype = form.cleaned_data['plottype'] workstrokesonly = not includereststrokes - else: # pragma: no cover + else: # pragma: no cover workstrokesonly = True plottype = 'line' else: form = ForceCurveOptionsForm() plottype = 'line' - script,div,js_resources,css_resources = interactive_forcecurve( + script, div, js_resources, css_resources = interactive_forcecurve( [row], workstrokesonly=workstrokesonly, plottype=plottype, ) breadcrumbs = [ - { - 'url':'/rowers/list-workouts/', - 'name':'Workouts' - }, - { - 'url':get_workout_default_page(request,id), - 'name': row.name - }, - { - 'url':reverse('workout_forcecurve_view',kwargs={'id':id}), - 'name': 'Empower Force Curve' - } + { + 'url': '/rowers/list-workouts/', + 'name': 'Workouts' + }, + { + 'url': get_workout_default_page(request, id), + 'name': row.name + }, + { + 'url': reverse('workout_forcecurve_view', kwargs={'id': id}), + 'name': 'Empower Force Curve' + } - ] + ] r = getrower(request.user) return render(request, 'forcecurve_single.html', { - 'the_script':script, - 'rower':r, - 'form':form, - 'workout':row, - 'breadcrumbs':breadcrumbs, - 'active':'nav-workouts', - 'the_div':div, + 'the_script': script, + 'rower': r, + 'form': form, + 'workout': row, + 'breadcrumbs': breadcrumbs, + 'active': 'nav-workouts', + 'the_div': div, 'js_res': js_resources, - 'css_res':css_resources, - 'id':id, - 'mayedit':mayedit, - 'teams':get_my_teams(request.user), + 'css_res': css_resources, + 'id': id, + 'mayedit': mayedit, + 'teams': get_my_teams(request.user), }) # Switch from GPS to Impeller (only for SpeedCoach 2, if impeller data) -@permission_required('workout.change_workout',fn=get_workout_by_opaqueid,raise_exception=True) -def otw_use_impeller(request,id=0): - w = get_workoutuser(id, request) +@permission_required('workout.change_workout', fn=get_workout_by_opaqueid, raise_exception=True) +def otw_use_impeller(request, id=0): + w = get_workoutuser(id, request) + row = rdata(csvfile=w.csvfilename) success = row.use_impellerdata() if success: row.write_csv(w.csvfilename) - dataprep.update_strokedata(w.id,row.df) + dataprep.update_strokedata(w.id, row.df) w.impeller = True w.save() - messages.info(request,'The distance and speed data are now based on Impeller data') + messages.info( + request, 'The distance and speed data are now based on Impeller data') else: - messages.error(request,'No impeller data found') + messages.error(request, 'No impeller data found') - url = reverse('workout_edit_view',kwargs={'id':encoder.encode_hex(w.id)}) + url = reverse('workout_edit_view', kwargs={'id': encoder.encode_hex(w.id)}) return HttpResponseRedirect(url) # Switch from Impeller to GPS (only for SpeedCoach 2, if impeller data) -@permission_required('workout.change_workout',fn=get_workout_by_opaqueid,raise_exception=True) -def otw_use_gps(request,id=0): +@permission_required('workout.change_workout', fn=get_workout_by_opaqueid, raise_exception=True) +def otw_use_gps(request, id=0): w = get_workoutuser(id, request) - row = rdata(csvfile=w.csvfilename) success = row.use_gpsdata() if success: row.write_csv(w.csvfilename) - dataprep.update_strokedata(w.id,row.df) + dataprep.update_strokedata(w.id, row.df) w.impeller = False w.save() - messages.info(request,'The distance and speed data are now based on GPS data') + messages.info( + request, 'The distance and speed data are now based on GPS data') else: - messages.error(request,'No GPS data found') + messages.error(request, 'No GPS data found') - url = reverse('workout_edit_view',kwargs={'id':encoder.encode_hex(w.id)}) + url = reverse('workout_edit_view', kwargs={'id': encoder.encode_hex(w.id)}) return HttpResponseRedirect(url) - # Show Stroke power histogram for a workout @login_required() -@permission_required('workout.change_workout',fn=get_workout_by_opaqueid,raise_exception=True) -def workout_histo_view(request,id=0): +@permission_required('workout.change_workout', fn=get_workout_by_opaqueid, raise_exception=True) +def workout_histo_view(request, id=0): w = get_workoutuser(id, request) r = getrequestrower(request) promember = 1 - mayedit=0 + mayedit = 0 if w.user == r: mayedit = 1 - res = interactive_histoall([w],'power',False) + res = interactive_histoall([w], 'power', False) script = res[0] div = res[1] breadcrumbs = [ - { - 'url':'/rowers/list-workouts/', - 'name':'Workouts' - }, - { - 'url':get_workout_default_page(request,id), - 'name': w.name - }, - { - 'url':reverse('workout_histo_view',kwargs={'id':id}), - 'name': 'Histogram' - } - - ] + { + 'url': '/rowers/list-workouts/', + 'name': 'Workouts' + }, + { + 'url': get_workout_default_page(request, id), + 'name': w.name + }, + { + 'url': reverse('workout_histo_view', kwargs={'id': id}), + 'name': 'Histogram' + } + ] return render(request, 'histo_single.html', - {'interactiveplot':script, - 'breadcrumbs':breadcrumbs, - 'active':'nav-workouts', - 'workout':w, - 'rower':r, - 'the_div':div, - 'id':id, - 'mayedit':mayedit, - 'teams':get_my_teams(request.user), + {'interactiveplot': script, + 'breadcrumbs': breadcrumbs, + 'active': 'nav-workouts', + 'workout': w, + 'rower': r, + 'the_div': div, + 'id': id, + 'mayedit': mayedit, + 'teams': get_my_teams(request.user), }) - - # add a workout manually @login_required() -def addmanual_view(request,raceid=0): +def addmanual_view(request, raceid=0): r = Rower.objects.get(user=request.user) breadcrumbs = [ { - 'url':'/rowers/list-workouts/', - 'name':'Workouts' + 'url': '/rowers/list-workouts/', + 'name': 'Workouts' }, { - 'url':reverse('addmanual_view'), + 'url': reverse('addmanual_view'), 'name': 'Add Manual Entry' - }, + }, ] - if request.method == 'POST': # Form was submitted form = WorkoutForm(request.POST) @@ -588,7 +594,7 @@ def addmanual_view(request,raceid=0): if form.is_valid() and metricsform.is_valid(): # Get values from form name = form.cleaned_data['name'] - if name == '': # pragma: no cover + if name == '': # pragma: no cover name = 'Manual Entry' date = form.cleaned_data['date'] starttime = form.cleaned_data['starttime'] @@ -601,7 +607,7 @@ def addmanual_view(request,raceid=0): rpe = form.cleaned_data['rpe'] if not rpe: rpe = -1 - except KeyError: # pragma: no cover + except KeyError: # pragma: no cover rpe = -1 notes = form.cleaned_data['notes'] thetimezone = form.cleaned_data['timezone'] @@ -611,12 +617,12 @@ def addmanual_view(request,raceid=0): avgspm = metricsform.cleaned_data['avgspm'] #ps = form.cleaned_data.get('plannedsession',None) - boattype = form.cleaned_data.get('boattype','1x') - privacy = form.cleaned_data.get('privacy','visible') - rankingpiece = form.cleaned_data.get('rankingpiece',False) - duplicate = form.cleaned_data.get('duplicate',False) + boattype = form.cleaned_data.get('boattype', '1x') + privacy = form.cleaned_data.get('privacy', 'visible') + rankingpiece = form.cleaned_data.get('rankingpiece', False) + duplicate = form.cleaned_data.get('duplicate', False) - if private: # pragma: no cover + if private: # pragma: no cover privacy = 'private' else: privacy = 'visible' @@ -629,26 +635,23 @@ def addmanual_view(request,raceid=0): pytz.timezone(thetimezone) ) - id,message = dataprep.create_row_df(r, - distance, - duration,startdatetime, - rpe=rpe, - weightcategory=weightcategory, - adaptiveclass=adaptiveclass, - avghr=avghr, - rankingpiece=rankingpiece, - avgpwr=avgpwr, - duplicate=duplicate, - avgspm=avgspm, - title = name, - notes=notes, - workouttype=workouttype) - - - - if message: # pragma: no cover - messages.error(request,message) + id, message = dataprep.create_row_df(r, + distance, + duration, startdatetime, + rpe=rpe, + weightcategory=weightcategory, + adaptiveclass=adaptiveclass, + avghr=avghr, + rankingpiece=rankingpiece, + avgpwr=avgpwr, + duplicate=duplicate, + avgspm=avgspm, + title=name, + notes=notes, + workouttype=workouttype) + if message: # pragma: no cover + messages.error(request, message) if id: w = Workout.objects.get(id=id) @@ -665,129 +668,131 @@ def addmanual_view(request,raceid=0): w.distance = distance w.duration = duration w.save() - #if ps: + # if ps: # add_workouts_plannedsession([w],ps,w.user) - messages.info(request,'New workout created') + messages.info(request, 'New workout created') + iform = ImageForm(request.POST, request.FILES) - iform = ImageForm(request.POST,request.FILES) - - if iform.is_valid(): # this works but cannot get the tests to work + if iform.is_valid(): # this works but cannot get the tests to work f = iform.cleaned_data['file'] - if f is not None: # pragma: no cover - filename,path_and_filename = handle_uploaded_image(f) + if f is not None: # pragma: no cover + filename, path_and_filename = handle_uploaded_image(f) try: width, height = Image.open(path_and_filename).size except: message = "Not a valid image" - messages.error(request,message) + messages.error(request, message) os.remove(path_and_filename) i = GraphImage(workout=w, creationdatetime=timezone.now(), - filename = path_and_filename, - width=width,height=height) + filename=path_and_filename, + width=width, height=height) i.save() if raceid != 0: try: race = VirtualRace.objects.get(id=raceid) - except VirtualRace.DoesNotExist: # pragma: no cover - messages.error(request,"Race does not exist") + except VirtualRace.DoesNotExist: # pragma: no cover + messages.error(request, "Race does not exist") url = reverse('workout_edit_view', - kwargs = {'id':encoder.encode_hex(id)}) + kwargs={'id': encoder.encode_hex(id)}) return HttpResponseRedirect(url) - can_submit = race_can_submit(r,race) or race_can_resubmit(r,race) + can_submit = race_can_submit( + r, race) or race_can_resubmit(r, race) can_submit = can_submit and race.sessiontype == 'indoorrace' - if not can_submit: # pragma: no cover - messages.error(request,'You cannot submit a result for this race') + if not can_submit: # pragma: no cover + messages.error( + request, 'You cannot submit a result for this race') if can_submit: - records = IndoorVirtualRaceResult.objects.filter(race=race,userid=r.id) - if not records: # pragma: no cover - messages.error(request,'You have to register for the race first') - url = reverse('virtualevent_view',kwargs = {'id':race.id}) + records = IndoorVirtualRaceResult.objects.filter( + race=race, userid=r.id) + if not records: # pragma: no cover + messages.error( + request, 'You have to register for the race first') + url = reverse('virtualevent_view', + kwargs={'id': race.id}) return HttpResponseRedirect(url) recordid = records[0].id result, comments, errors, jobid = add_workout_indoorrace( - [w],race,r,recordid=recordid + [w], race, r, recordid=recordid ) - for c in comments: # pragma: no cover - messages.info(request,c) - for er in errors: # pragma: no cover - messages.error(request,er) + for c in comments: # pragma: no cover + messages.info(request, c) + for er in errors: # pragma: no cover + messages.error(request, er) if result: otherrecords = IndoorVirtualRaceResult.objects.filter( - race = race + race=race ).exclude(userid=r.id) for otherrecord in otherrecords: - try: # pragma: no cover - otheruser = Rower.objects.get(id=otherrecord.userid) + try: # pragma: no cover + 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 + queue, + handle_sendemail_racesubmission, + otheruser.user.email, othername, + registeredname, + race.name, + race.id ) - except Rower.DoesNotExist: # pragma: no cover + except Rower.DoesNotExist: # pragma: no cover pass - url = reverse('virtualevent_view',kwargs = {'id':race.id}) + url = reverse('virtualevent_view', kwargs={'id': race.id}) return HttpResponseRedirect(url) url = reverse( 'workout_edit_view', - kwargs={'id':encoder.encode_hex(id)} - ) + kwargs={'id': encoder.encode_hex(id)} + ) return HttpResponseRedirect(url) - else: # pragma: no cover + else: # pragma: no cover iform = ImageForm() - return render(request,'manualadd.html', - {'form':form, - 'iform':iform, - 'metricsform':metricsform, - 'breadcrumbs':breadcrumbs, - 'active':'nav-workouts', + return render(request, 'manualadd.html', + {'form': form, + 'iform': iform, + 'metricsform': metricsform, + 'breadcrumbs': breadcrumbs, + 'active': 'nav-workouts', }) initial = { - 'workouttype':'rower', - 'date':timezone.now(), - 'starttime':timezone.now(), - 'timezone':r.defaulttimezone, - 'duration':datetime.timedelta(minutes=2), - 'distance':500, + 'workouttype': 'rower', + 'date': timezone.now(), + 'starttime': timezone.now(), + 'timezone': r.defaulttimezone, + 'duration': datetime.timedelta(minutes=2), + 'distance': 500, } form = WorkoutForm(initial=initial) iform = ImageForm() metricsform = MetricsForm() - return render(request,'manualadd.html', - {'form':form, - 'iform':iform, - 'metricsform':metricsform, - 'breadcrumbs':breadcrumbs, - 'active':'nav-workouts', + return render(request, 'manualadd.html', + {'form': form, + 'iform': iform, + 'metricsform': metricsform, + 'breadcrumbs': breadcrumbs, + 'active': 'nav-workouts', }) - - - # Reload the workout and calculate the summary from the stroke data (lapIDx) -@permission_required('workout.change_workout',fn=get_workout_by_opaqueid,raise_exception=True) -def workout_recalcsummary_view(request,id=0): +@permission_required('workout.change_workout', fn=get_workout_by_opaqueid, raise_exception=True) +def workout_recalcsummary_view(request, id=0): row = get_workoutuser(id, request) filename = row.csvfilename @@ -796,37 +801,36 @@ def workout_recalcsummary_view(request,id=0): row.summary = rowdata.allstats() row.save() successmessage = "Summary Updated" - messages.info(request,successmessage) + messages.info(request, successmessage) url = reverse('workout_edit_view', - kwargs = { - 'id':id, - }) + kwargs={ + 'id': id, + }) else: message = "Something went wrong. Could not update summary" - messages.error(request,message) + messages.error(request, message) url = reverse('workout_edit_view', - kwargs = { - 'id':id, - }) + kwargs={ + 'id': id, + }) return HttpResponseRedirect(url) # Joining workout -@user_passes_test(ispromember,login_url="/rowers/paidplans", +@user_passes_test(ispromember, login_url="/rowers/paidplans", message="This functionality requires a Pro plan or higher. If you are already a Pro user, please log in to access this functionality. If you are already a Pro user, please log in to access this functionality", redirect_field_name=None) -@permission_required('rower.is_coach',fn=get_user_by_userid,raise_exception=True) -def workouts_join_view(request,userid=0): - promember=0 +@permission_required('rower.is_coach', fn=get_user_by_userid, raise_exception=True) +def workouts_join_view(request, userid=0): + promember = 0 - r = getrequestrower(request,userid=userid) + r = getrequestrower(request, userid=userid) if not request.user.is_anonymous: r = getrower(request.user) result = request.user.is_authenticated and ispromember(request.user) if result: - promember=1 - + promember = 1 if request.method == 'POST' and 'workouts' in request.POST: form = WorkoutMultipleCompareForm(request.POST) @@ -841,91 +845,88 @@ def workouts_join_view(request,userid=0): ids = [int(w.id) for w in workouts] request.session['ids'] = ids + id, message = dataprep.join_workouts(r, ids, + title=workout_name, + setprivate=set_private, + killparents=killparents) - id,message = dataprep.join_workouts(r,ids, - title=workout_name, - setprivate=set_private, - killparents=killparents) - - if message: # pragma: no cover - messages.error(request,message) + if message: # pragma: no cover + messages.error(request, message) url = reverse(r.defaultlandingpage, - kwargs = { - 'id':encoder.encode_hex(id), + kwargs={ + 'id': encoder.encode_hex(id), }) return HttpResponseRedirect(url) - else: # pragma: no cover + else: # pragma: no cover messages.error("Form is not valid") url = reverse('workouts_join_select') return HttpResponseRedirect(url) + defaultoptions = { 'includereststrokes': False, - 'workouttypes':['rower','dynamic','slides'], + 'workouttypes': ['rower', 'dynamic', 'slides'], 'waterboattype': mytypes.waterboattype, - 'function':'boxplot' + 'function': 'boxplot' } + @user_passes_test(ispromember, login_url="/rowers/paidplans", message="This functionality requires a Pro plan or higher", redirect_field_name=None) -@permission_required('rower.is_coach',fn=get_user_by_userid,raise_exception=True) -def video_selectworkout(request,userid=0): +@permission_required('rower.is_coach', fn=get_user_by_userid, raise_exception=True) +def video_selectworkout(request, userid=0): r = getrequestrower(request, userid=userid) user = r.user userid = user.id workouts = Workout.objects.filter(user=r).order_by('-date') - if 'options' in request.session: options = request.session['options'] else: - options=defaultoptions + options = defaultoptions options['userid'] = userid try: workouttypes = options['workouttypes'] - except KeyError: # pragma: no cover - workouttypes = ['rower','dynamic','slides'] + except KeyError: # pragma: no cover + workouttypes = ['rower', 'dynamic', 'slides'] - try: # pragma: no cover + try: # pragma: no cover modalities = options['modalities'] modality = modalities[0] - except KeyError: # pragma: no cover + except KeyError: # pragma: no cover modalities = [m[0] for m in mytypes.workouttypes_ordered.items()] modality = 'all' - query = request.GET.get('q') - if query: # pragma: no cover + if query: # pragma: no cover query_list = query.split() workouts = workouts.filter( reduce(operator.and_, (Q(name__icontains=q) for q in query_list)) | reduce(operator.and_, (Q(notes__icontains=q) for q in query_list)) - ) - searchform = SearchForm(initial={'q':query}) + ) + searchform = SearchForm(initial={'q': query}) else: searchform = SearchForm() - if 'startdate' in request.session: startdate = iso8601.parse_date(request.session['startdate']) else: - startdate=timezone.now()-datetime.timedelta(days=42) + startdate = timezone.now()-datetime.timedelta(days=42) if 'enddate' in request.session: enddate = iso8601.parse_date(request.session['enddate']) else: enddate = timezone.now() - - workouts = workouts.filter(date__gte=startdate,date__lte=enddate) + workouts = workouts.filter(date__gte=startdate, date__lte=enddate) waterboattype = mytypes.waterboattype @@ -945,45 +946,40 @@ def video_selectworkout(request,userid=0): cd = form.cleaned_data selectedworkout = cd['workout'] url = reverse('workout_video_create_view', - kwargs={'id':encoder.encode_hex(selectedworkout.id)}) + kwargs={'id': encoder.encode_hex(selectedworkout.id)}) return HttpResponseRedirect(url) - else: # pragma: no cover + else: # pragma: no cover id = 0 else: form = WorkoutSingleSelectForm(workouts=workouts) thediv = '' dateform = DateRangeForm(initial={ - 'startdate':startdate, - 'enddate':enddate, - }) - - + 'startdate': startdate, + 'enddate': enddate, + }) negtypes = [] for b in mytypes.boattypes: - if b[0] not in waterboattype: # pragma: no cover + if b[0] not in waterboattype: # pragma: no cover negtypes.append(b[0]) - - startdate = datetime.datetime.combine(startdate,datetime.time()) - enddate = datetime.datetime.combine(enddate,datetime.time(23,59,59)) + startdate = datetime.datetime.combine(startdate, datetime.time()) + enddate = datetime.datetime.combine(enddate, datetime.time(23, 59, 59)) # make sure the dates are not naive startdate = pytz.utc.localize(startdate) enddate = pytz.utc.localize(enddate) - if enddate < startdate: # pragma: no cover + if enddate < startdate: # pragma: no cover s = enddate enddate = startdate startdate = s negtypes = [] for b in mytypes.boattypes: - if b[0] not in waterboattype: # pragma: no cover + if b[0] not in waterboattype: # pragma: no cover negtypes.append(b[0]) - - workouts = Workout.objects.filter(user=r, startdatetime__gte=startdate, startdatetime__lte=enddate, @@ -991,9 +987,8 @@ def video_selectworkout(request,userid=0): ) workouts = workouts.order_by( - "-date", "-starttime" - ).exclude(boattype__in=negtypes) - + "-date", "-starttime" + ).exclude(boattype__in=negtypes) startdatestring = startdate.strftime('%Y-%m-%d') enddatestring = enddate.strftime('%Y-%m-%d') @@ -1001,35 +996,34 @@ def video_selectworkout(request,userid=0): request.session['enddate'] = enddatestring request.session['options'] = options - breadcrumbs = [ { - 'url':'/rowers/analysis', - 'name':'Analysis' + 'url': '/rowers/analysis', + 'name': 'Analysis' }, { - 'url':reverse('analysis_new',kwargs={'userid':userid}), + 'url': reverse('analysis_new', kwargs={'userid': userid}), 'name': 'Analysis Select' }, ] return render(request, 'video_selectworkout.html', {'workouts': workouts, - 'dateform':dateform, - 'startdate':startdate, - 'enddate':enddate, - 'rower':r, - 'breadcrumbs':breadcrumbs, - 'theuser':user, - 'the_div':thediv, - 'form':form, - 'active':'nav-analysis', - 'searchform':searchform, - 'teams':get_my_teams(request.user), + 'dateform': dateform, + 'startdate': startdate, + 'enddate': enddate, + 'rower': r, + 'breadcrumbs': breadcrumbs, + 'theuser': user, + 'the_div': thediv, + 'form': form, + 'active': 'nav-analysis', + 'searchform': searchform, + 'teams': get_my_teams(request.user), }) -@permission_required('rower.is_coach',fn=get_user_by_userid,raise_exception=True) -@user_passes_test(ispromember,login_url="/rowers/paidplans", +@permission_required('rower.is_coach', fn=get_user_by_userid, raise_exception=True) +@user_passes_test(ispromember, login_url="/rowers/paidplans", message="This functionality requires a Pro plan or higher. If you are already a Pro user, please log in to access this functionality. If you are already a Pro user, please log in to access this functionality", redirect_field_name=None) def workouts_join_select(request, @@ -1042,19 +1036,16 @@ def workouts_join_select(request, enddate=timezone.now()+datetime.timedelta(days=1), ): - - - r = getrequestrower(request,userid=userid) + r = getrequestrower(request, userid=userid) if 'waterboattype' in request.session: waterboattype = request.session['waterboattype'] else: waterboattype = mytypes.waterboattype - if 'modalities' in request.session: modalities = request.session['modalities'] - if len(modalities) > 1: # pragma: no cover + if len(modalities) > 1: # pragma: no cover modality = 'all' else: modality = modalities[0] @@ -1075,32 +1066,29 @@ def workouts_join_select(request, if modalityform.is_valid(): modality = modalityform.cleaned_data['modality'] waterboattype = modalityform.cleaned_data['waterboattype'] - if modality == 'all': # pragma: no cover + if modality == 'all': # pragma: no cover modalities = [m[0] for m in mytypes.workouttypes] else: modalities = [modality] - if modality != 'water': # pragma: no cover + if modality != 'water': # pragma: no cover waterboattype = [b[0] for b in mytypes.boattypes] - request.session['modalities'] = modalities request.session['waterboattype'] = waterboattype else: dateform = DateRangeForm(initial={ - 'startdate':startdate, - 'enddate':enddate, - }) - - + 'startdate': startdate, + 'enddate': enddate, + }) negtypes = [] for b in mytypes.boattypes: if b[0] not in waterboattype: negtypes.append(b[0]) - startdate = datetime.datetime.combine(startdate,datetime.time()) - enddate = datetime.datetime.combine(enddate,datetime.time(23,59,59)) + startdate = datetime.datetime.combine(startdate, datetime.time()) + enddate = datetime.datetime.combine(enddate, datetime.time(23, 59, 59)) #enddate = enddate+datetime.timedelta(days=1) if startdatestring: @@ -1108,7 +1096,7 @@ def workouts_join_select(request, if enddatestring: enddate = iso8601.parse_date(enddatestring) - if enddate < startdate: # pragma: no cover + if enddate < startdate: # pragma: no cover s = enddate enddate = startdate startdate = s @@ -1123,130 +1111,126 @@ def workouts_join_select(request, except ValueError: pass - workouts = Workout.objects.filter(user=r, - startdatetime__gte=startdate, + startdatetime__gte=startdate, startdatetime__lte=enddate, workouttype__in=modalities).order_by("-date", "-starttime").exclude(boattype__in=negtypes) query = request.GET.get('q') - if query: # pragma: no cover + if query: # pragma: no cover query_list = query.split() workouts = workouts.filter( reduce(operator.and_, (Q(name__icontains=q) for q in query_list)) | reduce(operator.and_, (Q(notes__icontains=q) for q in query_list)) - ) - searchform = SearchForm(initial={'q':query}) + ) + searchform = SearchForm(initial={'q': query}) else: searchform = SearchForm() form = WorkoutMultipleCompareForm() form.fields["workouts"].queryset = workouts - joinparamform = WorkoutJoinParamForm() modalityform = TrendFlexModalForm(initial={ - 'modality':modality, - 'waterboattype':waterboattype + 'modality': modality, + 'waterboattype': waterboattype }) - - messages.info(request,successmessage) - messages.error(request,message) + messages.info(request, successmessage) + messages.error(request, message) return render(request, 'workout_join_select.html', {'workouts': workouts, - 'dateform':dateform, - 'searchform':searchform, - 'startdate':startdate, - 'enddate':enddate, - 'active':'nav-workouts', - 'form':form, - 'joinparamform':joinparamform, - 'modalityform':modalityform, - 'teams':get_my_teams(request.user), + 'dateform': dateform, + 'searchform': searchform, + 'startdate': startdate, + 'enddate': enddate, + 'active': 'nav-workouts', + 'form': form, + 'joinparamform': joinparamform, + 'modalityform': modalityform, + 'teams': get_my_teams(request.user), }) + @login_required() -def remove_power_confirm_view(request,id=0): +def remove_power_confirm_view(request, id=0): r = getrower(request.user) - workout = get_workout_by_opaqueid(request,id) + workout = get_workout_by_opaqueid(request, id) breadcrumbs = [ { - 'url':'/rowers/list-workouts/', - 'name':'Workouts' - }, + 'url': '/rowers/list-workouts/', + 'name': 'Workouts' + }, { - 'url':get_workout_default_page(request,encoder.encode_hex(workout.id)), + 'url': get_workout_default_page(request, encoder.encode_hex(workout.id)), 'name': encoder.encode_hex(workout.id) - }, - { 'url':reverse('remove_power_confirm_view', - kwargs={'id':encoder.encode_hex(workout.id)}), - 'name': 'Delete' - } + }, + {'url': reverse('remove_power_confirm_view', + kwargs={'id': encoder.encode_hex(workout.id)}), + 'name': 'Delete' + } - ] + ] return render(request, 'workout_remove_power_confirm.html', { - 'workout':workout, - 'rower':r, - 'breadcrumbs':breadcrumbs, + 'workout': workout, + 'rower': r, + 'breadcrumbs': breadcrumbs, }) @login_required() -def remove_power_view(request,id=0): +def remove_power_view(request, id=0): r = getrower(request.user) - workout = get_workout_by_opaqueid(request,id) + workout = get_workout_by_opaqueid(request, id) try: os.remove('media/cpdata_{id}.parquet.gz'.format(id=workout.id)) - except OSError: # pragma: no cover + except OSError: # pragma: no cover pass f = workout.csvfilename powerperc = 100*np.array([r.pw_ut2, r.pw_ut1, r.pw_at, - r.pw_tr,r.pw_an])/r.ftp + r.pw_tr, r.pw_an])/r.ftp rr = rrower(hrmax=r.max, hrut2=r.ut2, hrut1=r.ut1, hrat=r.at, hrtr=r.tr, hran=r.an, ftp=r.ftp, powerperc=powerperc, powerzones=r.powerzones, hrzones=r.hrzones) - row = rdata(csvfile=f,rower=rr) + row = rdata(csvfile=f, rower=rr) row.df[' Power (watts)'] = 0 row.write_csv(f) res = dataprep.dataprep(row.df, id=workout.id) - cpdf,delta,cpvalues = dataprep.setcp(workout) + cpdf, delta, cpvalues = dataprep.setcp(workout) workout.normp = 0 workout.rscore = 0 workout.save() - dataprep.initiate_cp(r) - url = reverse(r.defaultlandingpage, - kwargs = { - 'id':id, - } + kwargs={ + 'id': id, + } ) return HttpResponseRedirect(url) # Team comparison -@user_passes_test(ispromember,login_url='/rowers/paidplans/', + + +@user_passes_test(ispromember, login_url='/rowers/paidplans/', message="This functionality requires a Pro plan or higher. If you are already a Pro user, please log in to access this functionality. If you are already a Pro user, please log in to access this functionality", redirect_field_name=None) - - -def course_mapcompare_view(request,id=0): +def course_mapcompare_view(request, id=0): results = [] r = None @@ -1255,30 +1239,29 @@ def course_mapcompare_view(request,id=0): try: course = GeoCourse.objects.get(id=id) - except GeoCourse.DoesNotExist: # pragma: no cover + except GeoCourse.DoesNotExist: # pragma: no cover raise Http404("Course does not exist") - results = VirtualRaceResult.objects.filter( course=course, workoutid__isnull=False, coursecompleted=True, - ).order_by("distance","duration") + ).order_by("distance", "duration") workoutids = [result.workoutid for result in results] startenddict = {} for result in results: - startenddict[result.workoutid] = (result.startsecond,result.endsecond) + startenddict[result.workoutid] = (result.startsecond, result.endsecond) - if len(workoutids) == 0: # pragma: no cover + if len(workoutids) == 0: # pragma: no cover url = reverse('course_view', kwargs={ - 'id':course.id, + 'id': course.id, }) - messages.info(request,'There are no results to display') + messages.info(request, 'There are no results to display') return HttpResponseRedirect(url) @@ -1286,14 +1269,14 @@ def course_mapcompare_view(request,id=0): for id in workoutids: try: workouts.append(Workout.objects.get(id=id)) - except Workout.DoesNotExist: # pragma: no cover + except Workout.DoesNotExist: # pragma: no cover pass labeldict = { int(w.id): w.__str__() for w in workouts - } + } - script,div = leaflet_chart_compare(course,workoutids, + script, div = leaflet_chart_compare(course, workoutids, labeldict=labeldict, startenddict=startenddict) @@ -1303,38 +1286,37 @@ def course_mapcompare_view(request,id=0): 'name': 'Courses' }, { - 'url':reverse('course_view', - kwargs={ - 'id':course.id, - } - ), + 'url': reverse('course_view', + kwargs={ + 'id': course.id, + } + ), 'name': course.name }, { - 'url':reverse('course_mapcompare_view', - kwargs={ - 'id':course.id, - } - ), + 'url': reverse('course_mapcompare_view', + kwargs={ + 'id': course.id, + } + ), 'name': 'Course Compare' } ] - - return render(request,'mapcompare.html', - {'mapscript':script, - 'mapdiv':div, - 'breadcrumbs':breadcrumbs, - 'rower':r, - 'course':course, - 'results':results, - 'active':'nav-racing', - 'teamid':0, - 'teams':[] + return render(request, 'mapcompare.html', + {'mapscript': script, + 'mapdiv': div, + 'breadcrumbs': breadcrumbs, + 'rower': r, + 'course': course, + 'results': results, + 'active': 'nav-racing', + 'teamid': 0, + 'teams': [] }) -def virtualevent_mapcompare_view(request,id=0): +def virtualevent_mapcompare_view(request, id=0): results = [] r = None @@ -1343,33 +1325,34 @@ def virtualevent_mapcompare_view(request,id=0): try: race = VirtualRace.objects.get(id=id) - except VirtualRace.DoesNotExist: # pragma: no cover + except VirtualRace.DoesNotExist: # pragma: no cover raise Http404("Virtual Challenge does not exist") - if race.sessiontype != 'race': # pragma: no cover - url = reverse(virtualevent_view,kwargs={'id':id}) - messages.error(request,"This challenge doesn't have map data") + if race.sessiontype != 'race': # pragma: no cover + url = reverse(virtualevent_view, kwargs={'id': id}) + messages.error(request, "This challenge doesn't have map data") return HttpResponseRedirect(request) results = VirtualRaceResult.objects.filter( race=race, workoutid__isnull=False, - ).order_by("distance","duration") + ).order_by("distance", "duration") workoutids = [result.workoutid for result in results] startenddict = {} if race.sessiontype == 'race': for result in results: - startenddict[result.workoutid] = (result.startsecond,result.endsecond) + startenddict[result.workoutid] = ( + result.startsecond, result.endsecond) - if len(workoutids) == 0: # pragma: no cover + if len(workoutids) == 0: # pragma: no cover url = reverse('virtualevent_view', kwargs={ - 'id':race.id, + 'id': race.id, }) - messages.info(request,'There are no results to display') + messages.info(request, 'There are no results to display') return HttpResponseRedirect(url) @@ -1377,14 +1360,14 @@ def virtualevent_mapcompare_view(request,id=0): for id in workoutids: try: workouts.append(Workout.objects.get(id=id)) - except Workout.DoesNotExist: # pragma: no cover + except Workout.DoesNotExist: # pragma: no cover pass labeldict = { int(w.id): w.__str__() for w in workouts - } + } - script,div = leaflet_chart_compare(race.course,workoutids, + script, div = leaflet_chart_compare(race.course, workoutids, labeldict=labeldict, startenddict=startenddict) @@ -1394,38 +1377,37 @@ def virtualevent_mapcompare_view(request,id=0): 'name': 'Challenges' }, { - 'url':reverse('virtualevent_view', - kwargs={ - 'id':race.id, - } - ), + 'url': reverse('virtualevent_view', + kwargs={ + 'id': race.id, + } + ), 'name': race.name }, { - 'url':reverse('virtualevent_mapcompare_view', - kwargs={ - 'id':race.id, - } - ), + 'url': reverse('virtualevent_mapcompare_view', + kwargs={ + 'id': race.id, + } + ), 'name': 'Course Compare' } ] - - return render(request,'mapcompare.html', - {'mapscript':script, - 'mapdiv':div, - 'breadcrumbs':breadcrumbs, - 'rower':r, - 'race':race, - 'results':results, - 'active':'nav-racing', - 'teamid':0, - 'teams':[] + return render(request, 'mapcompare.html', + {'mapscript': script, + 'mapdiv': div, + 'breadcrumbs': breadcrumbs, + 'rower': r, + 'race': race, + 'results': results, + 'active': 'nav-racing', + 'teamid': 0, + 'teams': [] }) -def course_compare_view(request,id=0): +def course_compare_view(request, id=0): results = [] promember = 0 @@ -1433,39 +1415,38 @@ def course_compare_view(request,id=0): r = getrower(request.user) result = request.user.is_authenticated and ispromember(request.user) if result: - promember=1 - else: # pragma: no cover + promember = 1 + else: # pragma: no cover r = None try: course = GeoCourse.objects.get(id=id) - except GeoCourse.DoesNotExist: # pragma: no cover + except GeoCourse.DoesNotExist: # pragma: no cover raise Http404("Course does not exist") - - script,div = course_map(course) + script, div = course_map(course) resultobj = VirtualRaceResult results = resultobj.objects.filter( course=course, workoutid__isnull=False, coursecompleted=True, - ).order_by("duration","-distance") + ).order_by("duration", "-distance") workoutids = [result.workoutid for result in results] startenddict = {} for result in results: - startenddict[result.workoutid] = (result.startsecond,result.endsecond) + startenddict[result.workoutid] = (result.startsecond, result.endsecond) - if len(workoutids) == 0: # pragma: no cover + if len(workoutids) == 0: # pragma: no cover url = reverse('course_view', kwargs={ - 'id':course.id, + 'id': course.id, }) - messages.info(request,'There are no results to display') + messages.info(request, 'There are no results to display') return HttpResponseRedirect(url) @@ -1480,7 +1461,6 @@ def course_compare_view(request,id=0): request.session['xparam'] = xparam request.session['yparam'] = yparam - workouts = [] for id in workoutids: try: @@ -1489,27 +1469,27 @@ def course_compare_view(request,id=0): pass form = WorkoutMultipleCompareForm() - form.fields["workouts"].queryset = Workout.objects.filter(id__in=workoutids) - form.fields["workouts"].initial = Workout.objects.filter(id__in=workoutids) - + form.fields["workouts"].queryset = Workout.objects.filter( + id__in=workoutids) + form.fields["workouts"].initial = Workout.objects.filter( + id__in=workoutids) labeldict = { int(w.id): w.__str__() for w in workouts - } - - + } chartform = ChartParamChoiceForm( - initial = { - 'xparam':xparam, - 'yparam':yparam, - 'plottype':plottype, - 'teamid':0 - } - ) - if request.method == 'POST' and 'workouts' in request.POST: # pragma: no cover + initial={ + 'xparam': xparam, + 'yparam': yparam, + 'plottype': plottype, + 'teamid': 0 + } + ) + if request.method == 'POST' and 'workouts' in request.POST: # pragma: no cover form = WorkoutMultipleCompareForm(request.POST) - form.fields["workouts"].queryset = Workout.objects.filter(id__in=workoutids) + form.fields["workouts"].queryset = Workout.objects.filter( + id__in=workoutids) chartform = ChartParamChoiceForm(request.POST) if form.is_valid() and chartform.is_valid(): cd = form.cleaned_data @@ -1521,12 +1501,12 @@ def course_compare_view(request,id=0): teamid = chartform.cleaned_data['teamid'] ids = [int(w.id) for w in workouts] request.session['ids'] = ids - elif request.method == 'POST': # pragma: no cover + elif request.method == 'POST': # pragma: no cover form = WorkoutMultipleCompareForm() - form.fields["workouts"].queryset = Workout.objects.filter(id__in=workoutids) + form.fields["workouts"].queryset = Workout.objects.filter( + id__in=workoutids) request.session['ids'] = workoutids - chartform = ChartParamChoiceForm(request.POST) if chartform.is_valid(): xparam = chartform.cleaned_data['xparam'] @@ -1538,7 +1518,6 @@ def course_compare_view(request,id=0): except KeyError: # pragma: no cover pass - workouts = [] for id in workoutids: try: @@ -1548,19 +1527,18 @@ def course_compare_view(request,id=0): pass labeldict = { - int(w.id): w.__str__() for w in workouts - } + int(w.id): w.__str__() for w in workouts + } - - res = interactive_multiple_compare_chart(workoutids,xparam,yparam, + res = interactive_multiple_compare_chart(workoutids, xparam, yparam, promember=promember, plottype=plottype, - labeldict=labeldict,startenddict=startenddict) + labeldict=labeldict, startenddict=startenddict) script = res[0] div = res[1] errormessage = res[3] - if errormessage != '': # pragma: no cover - messages.error(request,errormessage) + if errormessage != '': # pragma: no cover + messages.error(request, errormessage) breadcrumbs = [ { @@ -1568,41 +1546,40 @@ def course_compare_view(request,id=0): 'name': 'Courses' }, { - 'url':reverse('course_view', - kwargs={ - 'id':course.id, - } - ), + 'url': reverse('course_view', + kwargs={ + 'id': course.id, + } + ), 'name': course.name }, { - 'url':reverse('virtualevent_compare_view', - kwargs={ - 'id':course.id, - } - ), + 'url': reverse('virtualevent_compare_view', + kwargs={ + 'id': course.id, + } + ), 'name': 'Compare' } ] - - return render(request,'multicompare.html', - {'interactiveplot':script, - 'the_div':div, - 'breadcrumbs':breadcrumbs, - 'rower':r, - 'course':course, - 'results':results, - 'active':'nav-racing', - 'promember':promember, - 'teamid':0, - 'chartform':chartform, - 'form':form, - 'teams':[] + return render(request, 'multicompare.html', + {'interactiveplot': script, + 'the_div': div, + 'breadcrumbs': breadcrumbs, + 'rower': r, + 'course': course, + 'results': results, + 'active': 'nav-racing', + 'promember': promember, + 'teamid': 0, + 'chartform': chartform, + 'form': form, + 'teams': [] }) -def virtualevent_compare_view(request,id=0): +def virtualevent_compare_view(request, id=0): results = [] promember = 0 @@ -1610,19 +1587,19 @@ def virtualevent_compare_view(request,id=0): r = getrower(request.user) result = request.user.is_authenticated and ispromember(request.user) if result: - promember=1 - else: # pragma: no cover + promember = 1 + else: # pragma: no cover r = None try: race = VirtualRace.objects.get(id=id) - except VirtualRace.DoesNotExist: # pragma: no cover + except VirtualRace.DoesNotExist: # pragma: no cover raise Http404("Virtual Challenge does not exist") if race.sessiontype == 'race': - script,div = course_map(race.course) + script, div = course_map(race.course) resultobj = VirtualRaceResult - else: # pragma: no cover + else: # pragma: no cover script = '' div = '' resultobj = IndoorVirtualRaceResult @@ -1631,22 +1608,23 @@ def virtualevent_compare_view(request,id=0): race=race, workoutid__isnull=False, coursecompleted=True, - ).order_by("duration","-distance") + ).order_by("duration", "-distance") workoutids = [result.workoutid for result in results] startenddict = {} if race.sessiontype == 'race': for result in results: - startenddict[result.workoutid] = (result.startsecond,result.endsecond) + startenddict[result.workoutid] = ( + result.startsecond, result.endsecond) - if len(workoutids) == 0: # pragma: no cover + if len(workoutids) == 0: # pragma: no cover url = reverse('virtualevent_view', kwargs={ - 'id':race.id, + 'id': race.id, }) - messages.info(request,'There are no results to display') + messages.info(request, 'There are no results to display') return HttpResponseRedirect(url) @@ -1655,18 +1633,14 @@ def virtualevent_compare_view(request,id=0): if race.sessionmode == 'distance': xparam = 'cumdist' - yparam = 'pace' plottype = 'line' - - request.session['ids'] = workoutids request.session['plottype'] = plottype request.session['xparam'] = xparam request.session['yparam'] = yparam - workouts = [] for id in workoutids: try: @@ -1675,27 +1649,27 @@ def virtualevent_compare_view(request,id=0): pass form = WorkoutMultipleCompareForm() - form.fields["workouts"].queryset = Workout.objects.filter(id__in=workoutids) - form.fields["workouts"].initial = Workout.objects.filter(id__in=workoutids) - + form.fields["workouts"].queryset = Workout.objects.filter( + id__in=workoutids) + form.fields["workouts"].initial = Workout.objects.filter( + id__in=workoutids) labeldict = { int(w.id): w.__str__() for w in workouts - } - - + } chartform = ChartParamChoiceForm( - initial = { - 'xparam':xparam, - 'yparam':yparam, - 'plottype':plottype, - 'teamid':0 - } - ) - if request.method == 'POST' and 'workouts' in request.POST: # pragma: no cover + initial={ + 'xparam': xparam, + 'yparam': yparam, + 'plottype': plottype, + 'teamid': 0 + } + ) + if request.method == 'POST' and 'workouts' in request.POST: # pragma: no cover form = WorkoutMultipleCompareForm(request.POST) - form.fields["workouts"].queryset = Workout.objects.filter(id__in=workoutids) + form.fields["workouts"].queryset = Workout.objects.filter( + id__in=workoutids) chartform = ChartParamChoiceForm(request.POST) if form.is_valid() and chartform.is_valid(): cd = form.cleaned_data @@ -1709,10 +1683,10 @@ def virtualevent_compare_view(request,id=0): request.session['ids'] = ids elif request.method == 'POST': form = WorkoutMultipleCompareForm() - form.fields["workouts"].queryset = Workout.objects.filter(id__in=workoutids) + form.fields["workouts"].queryset = Workout.objects.filter( + id__in=workoutids) request.session['ids'] = workoutids - chartform = ChartParamChoiceForm(request.POST) if chartform.is_valid(): xparam = chartform.cleaned_data['xparam'] @@ -1724,7 +1698,6 @@ def virtualevent_compare_view(request,id=0): except KeyError: # pragma: no cover pass - workouts = [] for id in workoutids: try: @@ -1734,19 +1707,18 @@ def virtualevent_compare_view(request,id=0): pass labeldict = { - int(w.id): w.__str__() for w in workouts - } + int(w.id): w.__str__() for w in workouts + } - - res = interactive_multiple_compare_chart(workoutids,xparam,yparam, + res = interactive_multiple_compare_chart(workoutids, xparam, yparam, promember=promember, plottype=plottype, - labeldict=labeldict,startenddict=startenddict) + labeldict=labeldict, startenddict=startenddict) script = res[0] div = res[1] errormessage = res[3] - if errormessage != '': # pragma: no cover - messages.error(request,errormessage) + if errormessage != '': # pragma: no cover + messages.error(request, errormessage) breadcrumbs = [ { @@ -1754,58 +1726,56 @@ def virtualevent_compare_view(request,id=0): 'name': 'Challenges' }, { - 'url':reverse('virtualevent_view', - kwargs={ - 'id':race.id, - } - ), + 'url': reverse('virtualevent_view', + kwargs={ + 'id': race.id, + } + ), 'name': race.name }, { - 'url':reverse('virtualevent_compare_view', - kwargs={ - 'id':race.id, - } - ), + 'url': reverse('virtualevent_compare_view', + kwargs={ + 'id': race.id, + } + ), 'name': 'Compare' } ] - - return render(request,'multicompare.html', - {'interactiveplot':script, - 'the_div':div, - 'breadcrumbs':breadcrumbs, - 'rower':r, - 'race':race, - 'results':results, - 'active':'nav-racing', - 'promember':promember, - 'teamid':0, - 'chartform':chartform, - 'form':form, - 'teams':[] + return render(request, 'multicompare.html', + {'interactiveplot': script, + 'the_div': div, + 'breadcrumbs': breadcrumbs, + 'rower': r, + 'race': race, + 'results': results, + 'active': 'nav-racing', + 'promember': promember, + 'teamid': 0, + 'chartform': chartform, + 'form': form, + 'teams': [] }) @permission_required('plannedsession.view_session', - fn=get_session_by_pk,raise_exception=True) -def plannedsession_compare_view(request,id=0,userid=0): - r = getrequestrower(request,userid=userid) + fn=get_session_by_pk, raise_exception=True) +def plannedsession_compare_view(request, id=0, userid=0): + r = getrequestrower(request, userid=userid) try: ps = PlannedSession.objects.get(id=id) - except PlannedSession.DoesNotExist: # pragma: no cover + except PlannedSession.DoesNotExist: # pragma: no cover raise Http404("Planned session does not exist") - workouts = Workout.objects.filter(plannedsession=ps) ids = [int(w.id) for w in workouts] labeldict = { int(w.id): w.__str__() for w in workouts - } + } xparam = 'time' yparam = 'hr' @@ -1822,57 +1792,57 @@ def plannedsession_compare_view(request,id=0,userid=0): if ids: url = reverse('analysis_new', kwargs={ - 'session':ps.id, - 'id':encoder.encode_hex(ids[0]), - 'function':'compare'}) + 'session': ps.id, + 'id': encoder.encode_hex(ids[0]), + 'function': 'compare'}) else: - url = reverse('plannedsession_view',kwargs={'id':ps.id}) + url = reverse('plannedsession_view', kwargs={'id': ps.id}) return HttpResponseRedirect(url) # List Workouts @login_required() -def workouts_view(request,message='',successmessage='', - teamid=0,rowerid=0,userid=0): +def workouts_view(request, message='', successmessage='', + teamid=0, rowerid=0, userid=0): - startdate,enddate = get_dates_timeperiod(request,defaulttimeperiod='lastyear') + startdate, enddate = get_dates_timeperiod( + request, defaulttimeperiod='lastyear') request.session['referer'] = absolute(request)['PATH'] - r = getrequestrower(request,rowerid=rowerid,userid=userid) + r = getrequestrower(request, rowerid=rowerid, userid=userid) # check if access is allowed - - startdate = datetime.datetime.combine(startdate,datetime.time()) - enddate = datetime.datetime.combine(enddate,datetime.time(23,59,59)) + startdate = datetime.datetime.combine(startdate, datetime.time()) + enddate = datetime.datetime.combine(enddate, datetime.time(23, 59, 59)) query = None if request.method == 'POST': dateform = DateRangeForm(request.POST) searchform = SearchForm(request.POST) - if dateform.is_valid(): # pragma: no cover + if dateform.is_valid(): # pragma: no cover startdate = dateform.cleaned_data['startdate'] enddate = dateform.cleaned_data['enddate'] if searchform.is_valid(): query = searchform.cleaned_data['q'] else: dateform = DateRangeForm(initial={ - 'startdate':startdate, - 'enddate':enddate, - }) + 'startdate': startdate, + 'enddate': enddate, + }) usertimezone = pytz.timezone(r.defaulttimezone) - startdate = datetime.datetime.combine(startdate,datetime.time()).astimezone(usertimezone) - enddate = datetime.datetime.combine(enddate,datetime.time(23,59,59)).astimezone(usertimezone) + startdate = datetime.datetime.combine( + startdate, datetime.time()).astimezone(usertimezone) + enddate = datetime.datetime.combine( + enddate, datetime.time(23, 59, 59)).astimezone(usertimezone) #enddate = enddate+datetime.timedelta(days=1) - - if enddate < startdate: # pragma: no cover + if enddate < startdate: # pragma: no cover s = enddate enddate = startdate startdate = s - startdatestring = startdate.strftime('%Y-%m-%d') enddatestring = enddate.strftime('%Y-%m-%d') @@ -1882,22 +1852,23 @@ def workouts_view(request,message='',successmessage='', try: if enddate > timezone.now(): activity_enddate = timezone.now() - activity_enddate = activity_enddate.replace(hour=23,minute=59,second=59).astimezone(usertimezone) + activity_enddate = activity_enddate.replace( + hour=23, minute=59, second=59).astimezone(usertimezone) activity_startdate = activity_enddate-datetime.timedelta(days=15) - activity_startdate = activity_startdate.replace(hour=0,minute=0,second=0) - else: # pragma: no cover + activity_startdate = activity_startdate.replace( + hour=0, minute=0, second=0) + else: # pragma: no cover activity_enddate = enddate - except (ValueError, AttributeError): # pragma: no cover + except (ValueError, AttributeError): # pragma: no cover activity_enddate = enddate g_startdate = activity_startdate g_enddate = activity_enddate - if teamid: try: theteam = Team.objects.get(id=teamid) - except Team.DoesNotExist: # pragma: no cover + except Team.DoesNotExist: # pragma: no cover raise Http404("Team doesn't exist") if theteam.viewing == 'allmembers' or theteam.manager == request.user: @@ -1905,27 +1876,26 @@ def workouts_view(request,message='',successmessage='', team=theteam, startdatetime__gte=startdate, startdatetime__lte=enddate, - privacy='visible').order_by("-date","-starttime") + privacy='visible').order_by("-date", "-starttime") g_workouts = Workout.objects.filter( team=theteam, startdatetime__gte=activity_startdate, startdatetime__lte=activity_enddate, duplicate=False, privacy='visible').order_by("-date", "-starttime") - elif theteam.viewing == 'coachonly': # pragma: no cover + elif theteam.viewing == 'coachonly': # pragma: no cover workouts = Workout.objects.filter( - team=theteam,user=r, + team=theteam, user=r, startdatetime__gte=startdate, startdatetime__lte=enddate, privacy='visible').order_by("-startdatetime") g_workouts = Workout.objects.filter( - team=theteam,user=r, + team=theteam, user=r, startdatetime__gte=activity_startdate, startdatetime__lte=activity_enddate, duplicate=False, privacy='visible').order_by("-startdatetime") - elif request.user != r.user: theteam = None workouts = Workout.objects.filter( @@ -1958,35 +1928,32 @@ def workouts_view(request,message='',successmessage='', g_enddate = timezone.now() g_startdate = (timezone.now()-timedelta(days=15)) - - workoutsnohr = workouts.exclude(averagehr__isnull=False) - for w in workoutsnohr: # pragma: no cover + for w in workoutsnohr: # pragma: no cover res = dataprep.workout_trimp(w) # ids = [w.id for w in workouts] # df = dataprep.getsmallrowdata_db(['time','power'],ids=ids) # polarization = dataprep.polarization_index(df,r) - - if query: # pragma: no cover + if query: # pragma: no cover query_list = query.split() workouts = workouts.filter( reduce(operator.and_, (Q(name__icontains=q) for q in query_list)) | reduce(operator.and_, (Q(notes__icontains=q) for q in query_list)) - ) - searchform = SearchForm(initial={'q':query}) + ) + searchform = SearchForm(initial={'q': query}) else: searchform = SearchForm() - paginator = Paginator(workouts,12) # show 25 workouts per page - page = request.GET.get('page',1) + paginator = Paginator(workouts, 12) # show 25 workouts per page + page = request.GET.get('page', 1) try: workouts = paginator.page(page) - except EmptyPage: # pragma: no cover + except EmptyPage: # pragma: no cover workouts = paginator.page(paginator.num_pages) today = timezone.now() @@ -1998,88 +1965,87 @@ def workouts_view(request,message='',successmessage='', ) if theteam: - stack='rower' + stack = 'rower' else: - stack='type' + stack = 'type' - yaxis = request.GET.get('yaxis','duration') - if yaxis not in ['duration','trimp','rscore']: # pragma: no cover + yaxis = request.GET.get('yaxis', 'duration') + if yaxis not in ['duration', 'trimp', 'rscore']: # pragma: no cover yaxis = 'duration' - script,div = interactive_activitychart(g_workouts, - g_startdate, - g_enddate, - stack=stack, - yaxis=yaxis) + script, div = interactive_activitychart(g_workouts, + g_startdate, + g_enddate, + stack=stack, + yaxis=yaxis) - totalmeters,totalhours, totalminutes,total_seconds = get_totals(g_workouts) + totalmeters, totalhours, totalminutes, total_seconds = get_totals( + g_workouts) totalminutes = '{totalminutes:02d}'.format(totalminutes=totalminutes) - - messages.info(request,successmessage) - messages.error(request,message) + messages.info(request, successmessage) + messages.error(request, message) breadcrumbs = [ - { - 'url':'/rowers/list-workouts/', - 'name':'Workouts' - }, - ] - timeperiod = startdate.strftime('%Y-%m-%d')+'/'+enddate.strftime('%Y-%m-%d') + { + 'url': '/rowers/list-workouts/', + 'name': 'Workouts' + }, + ] + timeperiod = startdate.strftime( + '%Y-%m-%d')+'/'+enddate.strftime('%Y-%m-%d') return render(request, 'list_workouts.html', {'workouts': workouts, 'active': 'nav-workouts', - 'rower':r, - 'searchform':searchform, - 'breadcrumbs':breadcrumbs, - 'dateform':dateform, - 'startdate':startdate, - 'enddate':enddate, - 'announcements':announcements[0:4], - 'team':theteam, - 'teams':get_my_teams(request.user), - 'interactiveplot':script, - 'the_div':div, - 'timeperiod':timeperiod, - 'totalmeters':totalmeters, - 'totalminutes':totalminutes, - 'totalhours':totalhours, + 'rower': r, + 'searchform': searchform, + 'breadcrumbs': breadcrumbs, + 'dateform': dateform, + 'startdate': startdate, + 'enddate': enddate, + 'announcements': announcements[0:4], + 'team': theteam, + 'teams': get_my_teams(request.user), + 'interactiveplot': script, + 'the_div': div, + 'timeperiod': timeperiod, + 'totalmeters': totalmeters, + 'totalminutes': totalminutes, + 'totalhours': totalhours, }) - - # List of workouts to compare a selected workout to -@user_passes_test(ispromember,login_url="/rowers/paidplans", +@user_passes_test(ispromember, login_url="/rowers/paidplans", message="This functionality requires a Pro plan or higher. If you are already a Pro user, please log in to access this functionality. If you are already a Pro user, please log in to access this functionality", redirect_field_name=None) -@permission_required('workout.change_workout',fn=get_workout_by_opaqueid,raise_exception=True) -def workout_fusion_list(request,id=0, - startdate=timezone.now()-datetime.timedelta(days=365), - enddate=timezone.now()): +@permission_required('workout.change_workout', fn=get_workout_by_opaqueid, raise_exception=True) +def workout_fusion_list(request, id=0, + startdate=timezone.now()-datetime.timedelta(days=365), + enddate=timezone.now()): r = getrequestrower(request) u = r.user - if request.method == 'POST': # pragma: no cover + if request.method == 'POST': # pragma: no cover dateform = DateRangeForm(request.POST) if dateform.is_valid(): startdate = dateform.cleaned_data['startdate'] enddate = dateform.cleaned_data['enddate'] else: dateform = DateRangeForm(initial={ - 'startdate':startdate, - 'enddate':enddate, + 'startdate': startdate, + 'enddate': enddate, }) - startdate = datetime.datetime.combine(startdate,datetime.time()) - enddate = datetime.datetime.combine(enddate,datetime.time(23,59,59)) + startdate = datetime.datetime.combine(startdate, datetime.time()) + enddate = datetime.datetime.combine(enddate, datetime.time(23, 59, 59)) #enddate = enddate+datetime.timedelta(days=1) startdate = arrow.get(startdate).datetime enddate = arrow.get(enddate).datetime - if enddate < startdate: # pragma: no cover + if enddate < startdate: # pragma: no cover s = enddate enddate = startdate startdate = s @@ -2094,63 +2060,64 @@ def workout_fusion_list(request,id=0, startdatetime__lte=enddate).order_by("-date", "-starttime").exclude(id=theid) query = request.GET.get('q') - if query: # pragma: no cover + if query: # pragma: no cover query_list = query.split() workouts = workouts.filter( reduce(operator.and_, (Q(name__icontains=q) for q in query_list)) | reduce(operator.and_, (Q(notes__icontains=q) for q in query_list)) - ) - searchform = SearchForm(initial={'q':query}) + ) + searchform = SearchForm(initial={'q': query}) else: searchform = SearchForm() - paginator = Paginator(workouts,15) # show 25 workouts per page - page = request.GET.get('page',1) + paginator = Paginator(workouts, 15) # show 25 workouts per page + page = request.GET.get('page', 1) try: workouts = paginator.page(page) - except EmptyPage: # pragma: no cover + except EmptyPage: # pragma: no cover workouts = paginator.page(paginator.num_pages) row = get_workoutuser(id, request) - breadcrumbs = [ { - 'url':'/rowers/list-workouts/', - 'name':'Workouts' + 'url': '/rowers/list-workouts/', + 'name': 'Workouts' }, { - 'url':get_workout_default_page(request,encoder.encode_hex(row.id)), + 'url': get_workout_default_page(request, encoder.encode_hex(row.id)), 'name': row.name }, { - 'url':reverse('workout_fusion_list',kwargs={'id':id}), + 'url': reverse('workout_fusion_list', kwargs={'id': id}), 'name': 'Sensor Fusion' } ] return render(request, 'fusion_list.html', - {'id':id, - 'workout':row, - 'rower':r, - 'searchform':searchform, - 'active':'nav-workouts', - 'breadcrumbs':breadcrumbs, + {'id': id, + 'workout': row, + 'rower': r, + 'searchform': searchform, + 'active': 'nav-workouts', + 'breadcrumbs': breadcrumbs, 'workouts': workouts, - 'last_name':u.last_name, - 'first_name':u.first_name, - 'dateform':dateform, - 'startdate':startdate, - 'enddate':enddate, - 'teams':get_my_teams(request.user), - }) + 'last_name': u.last_name, + 'first_name': u.first_name, + 'dateform': dateform, + 'startdate': startdate, + 'enddate': enddate, + 'teams': get_my_teams(request.user), + }) # Basic view of workout -@permission_required('workout.view_workout',fn=get_workout_by_opaqueid,raise_exception=True) -def workout_view(request,id=0,raceresult=0,sessionresult=0,nocourseraceresult=0): + + +@permission_required('workout.view_workout', fn=get_workout_by_opaqueid, raise_exception=True) +def workout_view(request, id=0, raceresult=0, sessionresult=0, nocourseraceresult=0): request.session['referer'] = absolute(request)['PATH'] if not request.user.is_anonymous: @@ -2159,7 +2126,7 @@ def workout_view(request,id=0,raceresult=0,sessionresult=0,nocourseraceresult=0) rower = None # get row - row = get_workout_by_opaqueid(request,id) + row = get_workout_by_opaqueid(request, id) f1 = row.csvfilename rowdata = rdata(csvfile=f1) summary = row.summary @@ -2168,11 +2135,10 @@ def workout_view(request,id=0,raceresult=0,sessionresult=0,nocourseraceresult=0) aantalcomments = len(comments) - g = GraphImage.objects.filter(workout=row).order_by("-creationdatetime") - for i in g: # pragma: no cover + for i in g: # pragma: no cover try: - width,height = Image.open(i.filename).size + width, height = Image.open(i.filename).size i.width = width i.height = height i.save() @@ -2181,65 +2147,64 @@ def workout_view(request,id=0,raceresult=0,sessionresult=0,nocourseraceresult=0) # get raceresult or session result intervaldata = {} - if sessionresult != 0: # pragma: no cover + if sessionresult != 0: # pragma: no cover try: result = CourseTestResult.objects.get(id=sessionresult) startsecond = result.startsecond endsecond = result.endsecond duration = row.duration durationsecs = duration.hour*3600+duration.minute*60+duration.second - itime = [startsecond,endsecond-startsecond] - itype = [3,4] + itime = [startsecond, endsecond-startsecond] + itype = [3, 4] intervaldata['itime'] = itime intervaldata['itype'] = itype - vals, units, typ = rowdata.updateinterval_metric(' AverageBoatSpeed (m/s)',0.1,mode='larger', - debug=False,smoothwindow=15., - activewindow = [startsecond,endsecond]) + vals, units, typ = rowdata.updateinterval_metric(' AverageBoatSpeed (m/s)', 0.1, mode='larger', + debug=False, smoothwindow=15., + activewindow=[startsecond, endsecond]) summary = rowdata.allstats() except CourseTestResult.DoesNotExist: pass - if nocourseraceresult != 0: # pragma: no cover + if nocourseraceresult != 0: # pragma: no cover try: result = IndoorVirtualRaceResult.objects.get(id=nocourseraceresult) startsecond = result.startsecond endsecond = result.endsecond duration = row.duration durationsecs = duration.hour*3600+duration.minute*60+duration.second - itime = [startsecond,endsecond-startsecond] - itype = [3,4] + itime = [startsecond, endsecond-startsecond] + itype = [3, 4] intervaldata['itime'] = itime intervaldata['itype'] = itype - vals, units, typ = rowdata.updateinterval_metric(' AverageBoatSpeed (m/s)',0.1,mode='larger', - debug=False,smoothwindow=15., - activewindow = [startsecond,endsecond]) + vals, units, typ = rowdata.updateinterval_metric(' AverageBoatSpeed (m/s)', 0.1, mode='larger', + debug=False, smoothwindow=15., + activewindow=[startsecond, endsecond]) summary = rowdata.allstats() except CourseTestResult.DoesNotExist: pass - - if raceresult != 0: # pragma: no cover + if raceresult != 0: # pragma: no cover try: result = VirtualRaceResult.objects.get(id=raceresult) startsecond = result.startsecond endsecond = result.endsecond duration = row.duration durationsecs = duration.hour*3600+duration.minute*60+duration.second - itime = [startsecond,endsecond-startsecond] - itype = [3,4] + itime = [startsecond, endsecond-startsecond] + itype = [3, 4] intervaldata['itime'] = itime intervaldata['itype'] = itype - vals, units, typ = rowdata.updateinterval_metric(' AverageBoatSpeed (m/s)',0.1,mode='larger', - debug=False,smoothwindow=15., - activewindow = [startsecond,endsecond]) + vals, units, typ = rowdata.updateinterval_metric(' AverageBoatSpeed (m/s)', 0.1, mode='larger', + debug=False, smoothwindow=15., + activewindow=[startsecond, endsecond]) summary = rowdata.allstats() except VirtualRaceResult.DoesNotExist: pass # create interactive plot - res = interactive_chart(encoder.decode_hex(id),intervaldata=intervaldata) + res = interactive_chart(encoder.decode_hex(id), intervaldata=intervaldata) script = res[0] div = res[1] @@ -2248,41 +2213,44 @@ def workout_view(request,id=0,raceresult=0,sessionresult=0,nocourseraceresult=0) if rowdata != 0: try: latitude = rowdata.df[' latitude'] - if not latitude.std(): # pragma: no cover + if not latitude.std(): # pragma: no cover hascoordinates = 0 - except (KeyError,AttributeError): + except (KeyError, AttributeError): hascoordinates = 0 - else: # pragma: no cover + else: # pragma: no cover hascoordinates = 0 courses = [] if hascoordinates: - if intervaldata: # pragma: no cover - rowdata.df['reltime'] = rowdata.df['TimeStamp (sec)']-rowdata.df.loc[0,'TimeStamp (sec)'] - mask = (rowdata.df['reltime']>startsecond) & (rowdata.df['reltime'] startsecond) & ( + rowdata.df['reltime'] < endsecond) + latitudes = rowdata.df.loc[mask, ' latitude'] + longitudes = rowdata.df.loc[mask, ' longitude'] else: latitudes = rowdata.df[' latitude'] longitudes = rowdata.df[' longitude'] - mapscript,mapdiv = leaflet_chart(latitudes,longitudes,row.name,raceresult=raceresult) - records = VirtualRaceResult.objects.filter(workoutid=row.id,userid=row.user.user.id,coursecompleted=True) - if records.count()>0: # pragma: no cover + mapscript, mapdiv = leaflet_chart( + latitudes, longitudes, row.name, raceresult=raceresult) + records = VirtualRaceResult.objects.filter( + workoutid=row.id, userid=row.user.user.id, coursecompleted=True) + if records.count() > 0: # pragma: no cover courses = list(set([record.course for record in records])) - else: mapscript = "" mapdiv = "" breadcrumbs = [ { - 'url':'/rowers/list-workouts/', - 'name':'Workouts' + 'url': '/rowers/list-workouts/', + 'name': 'Workouts' }, { - 'url':reverse('workout_view',kwargs={'id':id}), + 'url': reverse('workout_view', kwargs={'id': id}), 'name': row.name, } @@ -2290,100 +2258,98 @@ def workout_view(request,id=0,raceresult=0,sessionresult=0,nocourseraceresult=0) u = row.user.user - recordsindoor = IndoorVirtualRaceResult.objects.filter(workoutid= row.id) - records = VirtualRaceResult.objects.filter(workoutid= row.id) + recordsindoor = IndoorVirtualRaceResult.objects.filter(workoutid=row.id) + records = VirtualRaceResult.objects.filter(workoutid=row.id) return render(request, 'workout_view.html', - {'workout':row, - 'rower':rower, - 'breadcrumbs':breadcrumbs, - 'active':'nav-workouts', - 'graphs':g, - 'last_name':u.last_name, - 'records':records, - 'summary':summary, - 'recordsindoor':recordsindoor, - 'first_name':u.first_name, - 'interactiveplot':script, - 'aantalcomments':aantalcomments, - 'mapscript':mapscript, - 'mapdiv':mapdiv, - 'teams':get_my_teams(request.user), - 'courses':courses, - 'the_div':div}) + {'workout': row, + 'rower': rower, + 'breadcrumbs': breadcrumbs, + 'active': 'nav-workouts', + 'graphs': g, + 'last_name': u.last_name, + 'records': records, + 'summary': summary, + 'recordsindoor': recordsindoor, + 'first_name': u.first_name, + 'interactiveplot': script, + 'aantalcomments': aantalcomments, + 'mapscript': mapscript, + 'mapdiv': mapdiv, + 'teams': get_my_teams(request.user), + 'courses': courses, + 'the_div': div}) # Resets stroke data to raw data (pace) -@permission_required('workout.change_workout',fn=get_workout_by_opaqueid,raise_exception=True) -@user_passes_test(ispromember,login_url="/rowers/paidplans", +@permission_required('workout.change_workout', fn=get_workout_by_opaqueid, raise_exception=True) +@user_passes_test(ispromember, login_url="/rowers/paidplans", message="This functionality requires a Pro plan or higher. If you are already a Pro user, please log in to access this functionality. If you are already a Pro user, please log in to access this functionality", redirect_field_name=None) def workout_undo_smoothenpace_view( - request,id=0,message="",successmessage="" + request, id=0, message="", successmessage="" ): row = get_workoutuser(id, request) r = getrower(request.user) filename = row.csvfilename row = rdata(csvfile=filename) - if row == 0: # pragma: no cover + if row == 0: # pragma: no cover return HttpResponse("Error: CSV Data File Not Found") if 'originalvelo' in row.df: velo = row.df['originalvelo'].values row.df[' Stroke500mPace (sec/500m)'] = 500./velo - row.write_csv(filename,gzip=True) - dataprep.update_strokedata(encoder.decode_hex(id),row.df) + row.write_csv(filename, gzip=True) + dataprep.update_strokedata(encoder.decode_hex(id), row.df) url = reverse(r.defaultlandingpage, - kwargs = { - 'id':id, - } + kwargs={ + 'id': id, + } ) - return HttpResponseRedirect(url) # Data smoothing of pace data -@permission_required('workout.change_workout',fn=get_workout_by_opaqueid,raise_exception=True) -@user_passes_test(ispromember,login_url="/rowers/paidplans", +@permission_required('workout.change_workout', fn=get_workout_by_opaqueid, raise_exception=True) +@user_passes_test(ispromember, login_url="/rowers/paidplans", message="This functionality requires a Pro plan or higher. If you are already a Pro user, please log in to access this functionality. If you are already a Pro user, please log in to access this functionality", redirect_field_name=None) -def workout_smoothenpace_view(request,id=0,message="",successmessage=""): +def workout_smoothenpace_view(request, id=0, message="", successmessage=""): row = get_workoutuser(id, request) previousurl = request.META.get('HTTP_REFERER') r = getrower(request.user) - filename = row.csvfilename row = rdata(csvfile=filename) - if row == 0: # pragma: no cover + if row == 0: # pragma: no cover return HttpResponse("Error: CSV Data File Not Found") try: pace = row.df[' Stroke500mPace (sec/500m)'].values velo = 500./pace except KeyError: - messages.error(request,'Unable to find the data') - if previousurl: # pragma: no cover + messages.error(request, 'Unable to find the data') + if previousurl: # pragma: no cover url = previousurl else: url = reverse(r.defaultlandingpage, - kwargs = { - 'id':id, + kwargs={ + 'id': id, } - ) + ) return HttpResponseRedirect(url) if not 'originalvelo' in row.df: row.df['originalvelo'] = velo - velo2 = stravastuff.ewmovingaverage(velo,5) + velo2 = stravastuff.ewmovingaverage(velo, 5) pace2 = 500./abs(velo2) @@ -2391,134 +2357,136 @@ def workout_smoothenpace_view(request,id=0,message="",successmessage=""): row.df = row.df.fillna(0) - row.write_csv(filename,gzip=True) - dataprep.update_strokedata(encoder.decode_hex(id),row.df) + row.write_csv(filename, gzip=True) + dataprep.update_strokedata(encoder.decode_hex(id), row.df) - messages.info(request,'A smoothening filter was applied to your pace data') + messages.info( + request, 'A smoothening filter was applied to your pace data') - if previousurl: # pragma: no cover + if previousurl: # pragma: no cover url = previousurl else: url = reverse(r.defaultlandingpage, - kwargs = { - 'id':id, + kwargs={ + 'id': id, } - ) + ) return HttpResponseRedirect(url) # Get weather for given location and date/time -@permission_required('workout.change_workout',fn=get_workout_by_opaqueid,raise_exception=True) -@user_passes_test(ispromember,login_url="/rowers/paidplans", +@permission_required('workout.change_workout', fn=get_workout_by_opaqueid, raise_exception=True) +@user_passes_test(ispromember, login_url="/rowers/paidplans", message="This functionality requires a Pro plan or higher. If you are already a Pro user, please log in to access this functionality", redirect_field_name=None) -def workout_downloadwind_view(request,id=0, +def workout_downloadwind_view(request, id=0, airportcode=None, - message="",successmessage=""): + message="", successmessage=""): row = get_workoutuser(id, request) f1 = row.csvfilename # create bearing rowdata = rdata(csvfile=f1) - if rowdata == 0: # pragma: no cover + if rowdata == 0: # pragma: no cover return HttpResponse("Error: CSV Data File Not Found") try: - bearing = rowdata.df.loc[:,'bearing'].values + bearing = rowdata.df.loc[:, 'bearing'].values except KeyError: rowdata.add_bearing() - rowdata.write_csv(f1,gzip=True) + rowdata.write_csv(f1, gzip=True) # get wind try: avglat = rowdata.df[' latitude'].mean() avglon = rowdata.df[' longitude'].mean() - avgtime = int(rowdata.df['TimeStamp (sec)'].mean()-rowdata.df.loc[:,'TimeStamp (sec)'].iloc[0]) + avgtime = int(rowdata.df['TimeStamp (sec)'].mean( + )-rowdata.df.loc[:, 'TimeStamp (sec)'].iloc[0]) startdatetime = dateutil.parser.parse("{}, {}".format(row.date, row.starttime)) starttimeunix = int(arrow.get(row.startdatetime).timestamp()) #starttimeunix = int(mktime(startdatetime.utctimetuple())) avgtime = starttimeunix+avgtime - winddata = get_wind_data(avglat,avglon,avgtime) + winddata = get_wind_data(avglat, avglon, avgtime) windspeed = winddata[0] windbearing = winddata[1] message = winddata[2] if message is not None: try: row.notes += "\n"+message - except TypeError: # pragma: no cover + except TypeError: # pragma: no cover if message is not None and row.notes is not None: row.notes += message row.save() - rowdata.add_wind(windspeed,windbearing) - rowdata.write_csv(f1,gzip=True) + rowdata.add_wind(windspeed, windbearing) + rowdata.write_csv(f1, gzip=True) - messages.info(request,message) + messages.info(request, message) kwargs = { - 'id':id} + 'id': id} - url = reverse('workout_wind_view',kwargs=kwargs) + url = reverse('workout_wind_view', kwargs=kwargs) response = HttpResponseRedirect(url) except KeyError: message = "No latitude/longitude data" - messages.error(request,message) + messages.error(request, message) kwargs = { - 'id':id + 'id': id } - url = reverse('workout_wind_view',kwargs=kwargs) + url = reverse('workout_wind_view', kwargs=kwargs) response = HttpResponseRedirect(url) - - return response # Get weather for given location and date/time -@permission_required('workout.change_workout',fn=get_workout_by_opaqueid,raise_exception=True) -@user_passes_test(ispromember,login_url="/rowers/paidplans",message="This functionality requires a Pro plan or higher. If you are already a Pro user, please log in to access this functionality",redirect_field_name=None) -def workout_downloadmetar_view(request,id=0, - airportcode=None, - message="",successmessage=""): + + +@permission_required('workout.change_workout', fn=get_workout_by_opaqueid, raise_exception=True) +@user_passes_test(ispromember, login_url="/rowers/paidplans", message="This functionality requires a Pro plan or higher. If you are already a Pro user, please log in to access this functionality", redirect_field_name=None) +def workout_downloadmetar_view(request, id=0, + airportcode=None, + message="", successmessage=""): row = get_workoutuser(id, request) f1 = row.csvfilename - # create bearing rowdata = rdata(csvfile=f1) - if rowdata == 0: # pragma: no cover + if rowdata == 0: # pragma: no cover return HttpResponse("Error: CSV Data File Not Found") try: - bearing = rowdata.df.loc[:,'bearing'].values + bearing = rowdata.df.loc[:, 'bearing'].values except KeyError: rowdata.add_bearing() - rowdata.write_csv(f1,gzip=True) + rowdata.write_csv(f1, gzip=True) # get wind try: avglat = rowdata.df[' latitude'].mean() avglon = rowdata.df[' longitude'].mean() - airportcode = get_airport_code(avglat,avglon)[0] - avgtime = int(rowdata.df['TimeStamp (sec)'].mean()-rowdata.df.loc[:,'TimeStamp (sec)'].iloc[0]) + airportcode = get_airport_code(avglat, avglon)[0] + avgtime = int(rowdata.df['TimeStamp (sec)'].mean( + )-rowdata.df.loc[:, 'TimeStamp (sec)'].iloc[0]) startdatetime = dateutil.parser.parse("{}, {}".format(row.date, row.starttime)) starttimeunix = arrow.get(row.startdatetime).timestamp() #starttimeunix = int(mktime(startdatetime.utctimetuple())) - avgtime = starttimeunix +avgtime - winddata = get_metar_data(airportcode,avgtime) + avgtime = starttimeunix + avgtime + winddata = get_metar_data(airportcode, avgtime) windspeed = winddata[0] windbearing = winddata[1] message = winddata[2] try: row.notes += "\n"+message - except TypeError: # pragma: no cover + except TypeError: # pragma: no cover if message is not None: try: row.notes += message @@ -2526,51 +2494,48 @@ def workout_downloadmetar_view(request,id=0, pass row.save() - rowdata.add_wind(windspeed,windbearing) - rowdata.write_csv(f1,gzip=True) - messages.info(request,message) + rowdata.add_wind(windspeed, windbearing) + rowdata.write_csv(f1, gzip=True) + messages.info(request, message) kwargs = { - 'id':id} + 'id': id} - url = reverse('workout_wind_view',kwargs=kwargs) + url = reverse('workout_wind_view', kwargs=kwargs) response = HttpResponseRedirect(url) except KeyError: message = "No latitude/longitude data" - messages.error(request,message) + messages.error(request, message) kwargs = { - 'id':id + 'id': id } - url = reverse('workout_wind_view',kwargs=kwargs) + url = reverse('workout_wind_view', kwargs=kwargs) response = HttpResponseRedirect(url) - - return response # Show form to update wind data -@permission_required('workout.change_workout',fn=get_workout_by_opaqueid,raise_exception=True) -@user_passes_test(ispromember,login_url="/rowers/paidplans",message="This functionality requires a Pro plan or higher. If you are already a Pro user, please log in to access this functionality",redirect_field_name=None) -def workout_wind_view(request,id=0,message="",successmessage=""): +@permission_required('workout.change_workout', fn=get_workout_by_opaqueid, raise_exception=True) +@user_passes_test(ispromember, login_url="/rowers/paidplans", message="This functionality requires a Pro plan or higher. If you are already a Pro user, please log in to access this functionality", redirect_field_name=None) +def workout_wind_view(request, id=0, message="", successmessage=""): row = get_workoutuser(id, request) r = getrower(request.user) breadcrumbs = [ - { - 'url':'/rowers/list-workouts/', - 'name':'Workouts' - }, - { - 'url':get_workout_default_page(request,id), - 'name': row.name - }, - { - 'url':reverse('workout_wind_view',kwargs={'id':id}), - 'name': 'Wind' - } - - ] + { + 'url': '/rowers/list-workouts/', + 'name': 'Workouts' + }, + { + 'url': get_workout_default_page(request, id), + 'name': row.name + }, + { + 'url': reverse('workout_wind_view', kwargs={'id': id}), + 'name': 'Wind' + } + ] # get data f1 = row.csvfilename @@ -2579,37 +2544,35 @@ def workout_wind_view(request,id=0,message="",successmessage=""): # create bearing rowdata = rdata(csvfile=f1) - if row == 0: # pragma: no cover + if row == 0: # pragma: no cover return HttpResponse("Error: CSV Data File Not Found") - hascoordinates = 1 try: - latitude = rowdata.df.loc[:,' latitude'] + latitude = rowdata.df.loc[:, ' latitude'] except KeyError: hascoordinates = 0 - if hascoordinates and not latitude.std(): # pragma: no cover + if hascoordinates and not latitude.std(): # pragma: no cover hascoordinates = 0 try: - bearing = rowdata.df.loc[:,'bearing'].values + bearing = rowdata.df.loc[:, 'bearing'].values except KeyError: rowdata.add_bearing() - rowdata.write_csv(f1,gzip=True) - + rowdata.write_csv(f1, gzip=True) if hascoordinates: avglat = rowdata.df[' latitude'].mean() avglon = rowdata.df[' longitude'].mean() - airportcode,newlat,newlon,airportdistance = get_airport_code(avglat,avglon) + airportcode, newlat, newlon, airportdistance = get_airport_code( + avglat, avglon) airportcode = airportcode.upper() airportdistance = airportdistance[0] else: airportcode = 'UNKNOWN' airportdistance = 0 - if request.method == 'POST': # process form form = UpdateWindForm(request.POST) @@ -2624,34 +2587,33 @@ def workout_wind_view(request,id=0,message="",successmessage=""): winddirection2 = form.cleaned_data['winddirection2'] windunit = form.cleaned_data['windunit'] - rowdata.update_wind(vwind1,vwind2, + rowdata.update_wind(vwind1, vwind2, winddirection1, winddirection2, - dist1,dist2, + dist1, dist2, units=windunit) - rowdata.write_csv(f1,gzip=True) + rowdata.write_csv(f1, gzip=True) - - else: # pragma: no cover + else: # pragma: no cover message = "Invalid Form" - messages.error(request,message) + messages.error(request, message) kwargs = { - 'id':id + 'id': id } - url = reverse('workout_wind_view',kwargs=kwargs) + url = reverse('workout_wind_view', kwargs=kwargs) response = HttpResponseRedirect(url) else: form = UpdateWindForm() # create interactive plot - res = interactive_windchart(encoder.decode_hex(id),promember=1) + res = interactive_windchart(encoder.decode_hex(id), promember=1) script = res[0] div = res[1] if hascoordinates: - gmscript,gmdiv = leaflet_chart( + gmscript, gmdiv = leaflet_chart( rowdata.df[' latitude'], rowdata.df[' longitude'], row.name) @@ -2659,45 +2621,42 @@ def workout_wind_view(request,id=0,message="",successmessage=""): gmscript = "" gmdiv = "No GPS data available" - - messages.info(request,successmessage) - messages.error(request,message) + messages.info(request, successmessage) + messages.error(request, message) return render(request, 'windedit.html', - {'workout':row, - 'rower':r, - 'breadcrumbs':breadcrumbs, - 'active':'nav-workouts', - 'teams':get_my_teams(request.user), - 'interactiveplot':script, - 'form':form, - 'airport':airportcode, - 'airportdistance':airportdistance, - 'the_div':div, - 'gmap':gmscript, - 'gmapdiv':gmdiv}) + {'workout': row, + 'rower': r, + 'breadcrumbs': breadcrumbs, + 'active': 'nav-workouts', + 'teams': get_my_teams(request.user), + 'interactiveplot': script, + 'form': form, + 'airport': airportcode, + 'airportdistance': airportdistance, + 'the_div': div, + 'gmap': gmscript, + 'gmapdiv': gmdiv}) # Show form to update River stream data (for river dwellers) -@permission_required('workout.change_workout',fn=get_workout_by_opaqueid,raise_exception=True) -@user_passes_test(ispromember,login_url="/rowers/paidplans",message="This functionality requires a Pro plan or higher. If you are already a Pro user, please log in to access this functionality",redirect_field_name=None) -def workout_stream_view(request,id=0,message="",successmessage=""): +@permission_required('workout.change_workout', fn=get_workout_by_opaqueid, raise_exception=True) +@user_passes_test(ispromember, login_url="/rowers/paidplans", message="This functionality requires a Pro plan or higher. If you are already a Pro user, please log in to access this functionality", redirect_field_name=None) +def workout_stream_view(request, id=0, message="", successmessage=""): row = get_workoutuser(id, request) r = getrower(request.user) - - - # create interactive plot f1 = row.csvfilename u = row.user.user r = getrower(u) rowdata = rdata(csvfile=f1) - if rowdata == 0: # pragma: no cover - messages.info(request,"Error: CSV data file not found") - url = reverse('workout_edit_view',kwargs={'id':encoder.encode_hex(row.id)}) + if rowdata == 0: # pragma: no cover + messages.info(request, "Error: CSV data file not found") + url = reverse('workout_edit_view', kwargs={ + 'id': encoder.encode_hex(row.id)}) return HttpResponseRedirect(url) if request.method == 'POST': @@ -2712,69 +2671,67 @@ def workout_stream_view(request,id=0,message="",successmessage=""): stream2 = form.cleaned_data['stream2'] streamunit = form.cleaned_data['streamunit'] - rowdata.update_stream(stream1,stream2,dist1,dist2, - units=streamunit) + rowdata.update_stream(stream1, stream2, dist1, dist2, + units=streamunit) - rowdata.write_csv(f1,gzip=True) + rowdata.write_csv(f1, gzip=True) - - else: # pragma: no cover + else: # pragma: no cover message = "Invalid Form" - messages.error(request,message) + messages.error(request, message) kwargs = { - 'id':id} - url = reverse('workout_wind_view',kwargs=kwargs) + 'id': id} + url = reverse('workout_wind_view', kwargs=kwargs) response = HttpResponseRedirect(url) else: form = UpdateStreamForm() # create interactive plot - res = interactive_streamchart(encoder.decode_hex(id),promember=1) + res = interactive_streamchart(encoder.decode_hex(id), promember=1) script = res[0] div = res[1] breadcrumbs = [ - { - 'url':'/rowers/list-workouts/', - 'name':'Workouts' - }, - { - 'url':get_workout_default_page(request,id), - 'name': row.name - }, - { - 'url':reverse('workout_stream_view',kwargs={'id':id}), - 'name': 'Stream' - } + { + 'url': '/rowers/list-workouts/', + 'name': 'Workouts' + }, + { + 'url': get_workout_default_page(request, id), + 'name': row.name + }, + { + 'url': reverse('workout_stream_view', kwargs={'id': id}), + 'name': 'Stream' + } - ] + ] - messages.info(request,successmessage) - messages.error(request,message) + messages.info(request, successmessage) + messages.error(request, message) return render(request, 'streamedit.html', - {'workout':row, - 'rower':r, - 'breadcrumbs':breadcrumbs, - 'active':'nav-workouts', - 'teams':get_my_teams(request.user), - 'interactiveplot':script, - 'form':form, - 'the_div':div}) + {'workout': row, + 'rower': r, + 'breadcrumbs': breadcrumbs, + 'active': 'nav-workouts', + 'teams': get_my_teams(request.user), + 'interactiveplot': script, + 'form': form, + 'the_div': div}) # Form to set average crew weight and boat type, then run power calcs -@permission_required('workout.change_workout',fn=get_workout_by_opaqueid,raise_exception=True) -@user_passes_test(ispromember, login_url="/rowers/paidplans",redirect_field_name=None) -def workout_otwsetpower_view(request,id=0,message="",successmessage=""): + + +@permission_required('workout.change_workout', fn=get_workout_by_opaqueid, raise_exception=True) +@user_passes_test(ispromember, login_url="/rowers/paidplans", redirect_field_name=None) +def workout_otwsetpower_view(request, id=0, message="", successmessage=""): w = get_workoutuser(id, request) r = getrower(request.user) mayedit = 1 - - - if request.method == 'POST': # process form form = AdvancedWorkoutForm(request.POST) @@ -2791,29 +2748,28 @@ def workout_otwsetpower_view(request,id=0,message="",successmessage=""): w.boatbrand = coastalbrand w.save() - # load row data & create power/wind/bearing columns if not set f1 = w.csvfilename rowdata = rdata(csvfile=f1) - if rowdata == 0: # pragma: no cover + if rowdata == 0: # pragma: no cover return HttpResponse("Error: CSV Data File Not Found") try: vstream = rowdata.df['vstream'] except KeyError: rowdata.add_stream(0) - rowdata.write_csv(f1,gzip=True) + rowdata.write_csv(f1, gzip=True) try: bearing = rowdata.df['bearing'] except KeyError: rowdata.add_bearing() - rowdata.write_csv(f1,gzip=True) + rowdata.write_csv(f1, gzip=True) try: vwind = rowdata.df['vwind'] except KeyError: - rowdata.add_wind(0,0) - rowdata.write_csv(f1,gzip=True) + rowdata.add_wind(0, 0) + rowdata.write_csv(f1, gzip=True) # do power calculation (asynchronous) r = w.user @@ -2824,134 +2780,129 @@ def workout_otwsetpower_view(request,id=0,message="",successmessage=""): emailaddress = u.email job = myqueue(queue, - handle_otwsetpower,f1,boattype,boatclass,coastalbrand, + handle_otwsetpower, f1, boattype, boatclass, coastalbrand, weightvalue, - first_name,last_name,emailaddress,encoder.decode_hex(id), - ps=[r.p0,r.p1,r.p2,r.p3], + first_name, last_name, emailaddress, encoder.decode_hex( + id), + ps=[r.p0, r.p1, r.p2, r.p3], ratio=r.cpratio, - # quick_calc = quick_calc, - # go_service=go_service, - emailbounced = r.emailbounced - ) + # quick_calc = quick_calc, + # go_service=go_service, + emailbounced=r.emailbounced + ) try: - request.session['async_tasks'] += [(job.id,'otwsetpower')] + request.session['async_tasks'] += [(job.id, 'otwsetpower')] except KeyError: - request.session['async_tasks'] = [(job.id,'otwsetpower')] + request.session['async_tasks'] = [(job.id, 'otwsetpower')] successmessage = 'Your calculations have been submitted. You will receive an email when they are done. You can check the status of your calculations here' - messages.info(request,successmessage) + messages.info(request, successmessage) kwargs = { - 'id':id} + 'id': id} try: url = request.session['referer'] except KeyError: - url = reverse('workout_edit_view',kwargs=kwargs) + url = reverse('workout_edit_view', kwargs=kwargs) response = HttpResponseRedirect(url) return response - else: # pragma: no cover + else: # pragma: no cover message = "Invalid Form" - messages.error(request,message) + messages.error(request, message) kwargs = { - 'id':id} - url = reverse('workout_otwsetpower_view',kwargs=kwargs) + 'id': id} + url = reverse('workout_otwsetpower_view', kwargs=kwargs) response = HttpResponseRedirect(url) else: form = AdvancedWorkoutForm(instance=w) breadcrumbs = [ - { - 'url':'/rowers/list-workouts/', - 'name':'Workouts' - }, - { - 'url':get_workout_default_page(request,id), - 'name': w.name - }, - { - 'url':reverse('workout_otwsetpower_view',kwargs={'id':id}), - 'name': 'OTW Power' - } + { + 'url': '/rowers/list-workouts/', + 'name': 'Workouts' + }, + { + 'url': get_workout_default_page(request, id), + 'name': w.name + }, + { + 'url': reverse('workout_otwsetpower_view', kwargs={'id': id}), + 'name': 'OTW Power' + } - ] + ] - - messages.error(request,message) - messages.info(request,successmessage) + messages.error(request, message) + messages.info(request, successmessage) return render(request, 'otwsetpower.html', - {'workout':w, - 'rower':w, - 'mayedit':mayedit, - 'active':'nav-workouts', - 'breadcrumbs':breadcrumbs, - 'teams':get_my_teams(request.user), - 'form':form, + {'workout': w, + 'rower': w, + 'mayedit': mayedit, + 'active': 'nav-workouts', + 'breadcrumbs': breadcrumbs, + 'teams': get_my_teams(request.user), + 'form': form, }) -@permission_required('workout.change_workout',fn=get_workout_by_opaqueid,raise_exception=True) -def instroke_view(request,id=0): + +@permission_required('workout.change_workout', fn=get_workout_by_opaqueid, raise_exception=True) +def instroke_view(request, id=0): w = get_workoutuser(id, request) r = getrower(request.user) mayedit = 1 breadcrumbs = [ - { - 'url':'/rowers/list-workouts/', - 'name':'Workouts' - }, - { - 'url':get_workout_default_page(request,id), - 'name': w.name - }, - { - 'url':reverse('instroke_view',kwargs={'id':id}), - 'name': 'In-Stroke Metrics' - } + { + 'url': '/rowers/list-workouts/', + 'name': 'Workouts' + }, + { + 'url': get_workout_default_page(request, id), + 'name': w.name + }, + { + 'url': reverse('instroke_view', kwargs={'id': id}), + 'name': 'In-Stroke Metrics' + } - ] + ] # form = WorkoutForm(instance=row) g = GraphImage.objects.filter(workout=w).order_by("-creationdatetime") # check if user is owner of this workout - - rowdata = rrdata(csvfile=w.csvfilename) try: instrokemetrics = rowdata.get_instroke_columns() instrokemetrics = [m for m in instrokemetrics if not m in nometrics] - except AttributeError: # pragma: no cover + except AttributeError: # pragma: no cover instrokemetrics = [] - return render(request, 'instroke.html', - {'workout':w, - 'rower':r, - 'active':'nav-workouts', - 'breadcrumbs':breadcrumbs, - 'mayedit':mayedit, - 'teams':get_my_teams(request.user), - 'instrokemetrics':instrokemetrics, + {'workout': w, + 'rower': r, + 'active': 'nav-workouts', + 'breadcrumbs': breadcrumbs, + 'mayedit': mayedit, + 'teams': get_my_teams(request.user), + 'instrokemetrics': instrokemetrics, }) # generate instroke chart -@permission_required('workout.change_workout',fn=get_workout_by_opaqueid,raise_exception=True) -def instroke_chart(request,id=0,metric=''): # pragma: no cover +@permission_required('workout.change_workout', fn=get_workout_by_opaqueid, raise_exception=True) +def instroke_chart(request, id=0, metric=''): # pragma: no cover w = get_workoutuser(id, request) - - rowdata = rrdata(csvfile=w.csvfilename) instrokemetrics = rowdata.get_instroke_columns() - if metric in instrokemetrics: f1 = w.csvfilename[6:-4] timestr = strftime("%Y%m%d-%H%M%S") @@ -2968,7 +2919,7 @@ def instroke_chart(request,id=0,metric=''): # pragma: no cover gc.collect() try: - width,height = Image.open(fullpathimagename).size + width, height = Image.open(fullpathimagename).size except: width = 1200 height = 600 @@ -2978,59 +2929,60 @@ def instroke_chart(request,id=0,metric=''): # pragma: no cover i = GraphImage(workout=w, creationdatetime=timezone.now(), filename=fullpathimagename, - width=width,height=height) + width=width, height=height) i.save() else: - messages.error(request,'You have reached the maximum number of static images for this workout. Delete an image first') - + messages.error( + request, 'You have reached the maximum number of static images for this workout. Delete an image first') r = getrower(request.user) url = reverse(r.defaultlandingpage, - kwargs = { - 'id':id, - }) + kwargs={ + 'id': id, + }) return HttpResponseRedirect(url) # erase column -@permission_required('workout.change_workout',fn=get_workout_by_opaqueid,raise_exception=True) -def workout_erase_column_view(request, id=0,column=''): - r = getrower(request.user) - w = get_workoutuser(id,request) - protected = ['time','pace','velo','cumdist','ftime','fpace',] - if column in protected: # pragma: no cover - messages.error(request,'You cannot erase this protected column') - url = reverse('workout_data_view',kwargs={ - 'id':encoder.encode_hex(w.id) + +@permission_required('workout.change_workout', fn=get_workout_by_opaqueid, raise_exception=True) +def workout_erase_column_view(request, id=0, column=''): + r = getrower(request.user) + w = get_workoutuser(id, request) + + protected = ['time', 'pace', 'velo', 'cumdist', 'ftime', 'fpace', ] + if column in protected: # pragma: no cover + messages.error(request, 'You cannot erase this protected column') + url = reverse('workout_data_view', kwargs={ + 'id': encoder.encode_hex(w.id) }) return HttpResponseRedirect(url) try: - data = dataprep.getsmallrowdata_db([column],ids=[w.id]) - except: # pragma: no cover - messages.error(request,'Invalid column') - url = reverse('workout_data_view',kwargs={ - 'id':encoder.encode_hex(w.id) + data = dataprep.getsmallrowdata_db([column], ids=[w.id]) + except: # pragma: no cover + messages.error(request, 'Invalid column') + url = reverse('workout_data_view', kwargs={ + 'id': encoder.encode_hex(w.id) }) return HttpResponseRedirect(url) try: cdata = data[column] - except KeyError: # pragma: no cover - url = reverse('workout_data_view',kwargs={ - 'id':encoder.encode_hex(w.id) + except KeyError: # pragma: no cover + url = reverse('workout_data_view', kwargs={ + 'id': encoder.encode_hex(w.id) }) return HttpResponseRedirect(url) - - if not column: # pragma: no cover - url = reverse('workout_data_view',kwargs={ - 'id':encoder.encode_hex(w.id) + if not column: # pragma: no cover + url = reverse('workout_data_view', kwargs={ + 'id': encoder.encode_hex(w.id) }) return HttpResponseRedirect(url) @@ -3045,9 +2997,10 @@ def workout_erase_column_view(request, id=0,column=''): defaultvalue = mms[column]['default'] except KeyError: if not mms[column]['null']: # pragma: no cover - messages.error(request,'You cannot erase this protected column') - url = reverse('workout_data_view',kwargs={ - 'id':encoder.encode_hex(w.id) + messages.error( + request, 'You cannot erase this protected column') + url = reverse('workout_data_view', kwargs={ + 'id': encoder.encode_hex(w.id) }) return HttpResponseRedirect(url) @@ -3055,84 +3008,77 @@ def workout_erase_column_view(request, id=0,column=''): try: columnl = dataprep.columndict[column] - except KeyError: # pragma: no cover - messages.error(request,'You cannot erase this column') - url = reverse('workout_data_view',kwargs={ - 'id':encoder.encode_hex(w.id) + except KeyError: # pragma: no cover + messages.error(request, 'You cannot erase this column') + url = reverse('workout_data_view', kwargs={ + 'id': encoder.encode_hex(w.id) }) return HttpResponseRedirect(url) - - row,workout = dataprep.getrowdata(id=w.id) + row, workout = dataprep.getrowdata(id=w.id) row.df[columnl] = defaultvalue os.remove(w.csvfilename+'.gz') + row.write_csv(w.csvfilename, gzip=True) - row.write_csv(w.csvfilename,gzip=True) - - - row,workout = dataprep.getrowdata(id=w.id) - datadf = dataprep.dataprep(row.df,id=w.id) + row, workout = dataprep.getrowdata(id=w.id) + datadf = dataprep.dataprep(row.df, id=w.id) if column == 'hr': w.hrtss = 0 w.trimp = 0 w.save() - if column == 'power': # pragma: no cover + if column == 'power': # pragma: no cover w.rscore = 0 w.normp = 0 w.goldmedalstandard = -1 w.goldmedalseconds = 0 w.save() - trimp,hrtss = dataprep.workout_trimp(w,reset=True) - rscore,normp = dataprep.workout_rscore(w,reset=True) - goldstandard,goldstandardduration = dataprep.workout_goldmedalstandard(w,reset=True) + trimp, hrtss = dataprep.workout_trimp(w, reset=True) + rscore, normp = dataprep.workout_rscore(w, reset=True) + goldstandard, goldstandardduration = dataprep.workout_goldmedalstandard( + w, reset=True) - - messages.info(request,'Data for column '+column+' have been erased') - url = reverse('workout_data_view',kwargs={ - 'id':encoder.encode_hex(w.id) + messages.info(request, 'Data for column '+column+' have been erased') + url = reverse('workout_data_view', kwargs={ + 'id': encoder.encode_hex(w.id) }) return HttpResponseRedirect(url) - breadcrumbs = [ - { - 'url':'/rowers/list-workouts/', - 'name':'Workouts' - }, - { - 'url':get_workout_default_page(request,id), - 'name': w.name - }, - { - 'url':reverse('workout_data_view',kwargs={'id':id}), - 'name': 'Data Explorer' - } + { + 'url': '/rowers/list-workouts/', + 'name': 'Workouts' + }, + { + 'url': get_workout_default_page(request, id), + 'name': w.name + }, + { + 'url': reverse('workout_data_view', kwargs={'id': id}), + 'name': 'Data Explorer' + } - ] + ] return render(request, 'workout_erase_column.html', { - 'column':column, - 'teams':get_my_teams(request.user), + 'column': column, + 'teams': get_my_teams(request.user), 'workout': w, 'breadcrumbs': breadcrumbs, - } + } ) - - - # resample to 1s intervals -@permission_required('workout.change_workout',fn=get_workout_by_opaqueid,raise_exception=True) +@permission_required('workout.change_workout', fn=get_workout_by_opaqueid, raise_exception=True) def workout_resample_view(request, id=0): r = getrower(request.user) w = get_workoutuser(id, request) @@ -3143,103 +3089,103 @@ def workout_resample_view(request, id=0): form = ResampleForm(request.POST) if form.is_valid(): overwrite = form.cleaned_data['resamplechoice'] - datadf,id, msgs = dataprep.resample(encoder.decode_hex(id),r,w,overwrite=overwrite) + datadf, id, msgs = dataprep.resample( + encoder.decode_hex(id), r, w, overwrite=overwrite) for message in msgs: - messages.info(request,message) + messages.info(request, message) + url = get_workout_default_page(request, encoder.encode_hex(id)) - url = get_workout_default_page(request,encoder.encode_hex(id)) - - messages.info(request,'The workout has been resampled: here'.format(url=url)) + messages.info( + request, 'The workout has been resampled: here'.format(url=url)) breadcrumbs = [ - { - 'url':'/rowers/list-workouts/', - 'name':'Workouts' - }, - { - 'url':get_workout_default_page(request,id), - 'name': w.name - }, - { - 'url':reverse('workout_resample_view',kwargs={'id':id}), - 'name': 'Resample Data' - } + { + 'url': '/rowers/list-workouts/', + 'name': 'Workouts' + }, + { + 'url': get_workout_default_page(request, id), + 'name': w.name + }, + { + 'url': reverse('workout_resample_view', kwargs={'id': id}), + 'name': 'Resample Data' + } - ] + ] return render(request, 'workout_resample.html', { - 'form':form, - 'teams':get_my_teams(request.user), + 'form': form, + 'teams': get_my_teams(request.user), 'workout': w, 'breadcrumbs': breadcrumbs, - } + } ) # data explorer -@permission_required('workout.change_workout',fn=get_workout_by_opaqueid,raise_exception=True) + + +@permission_required('workout.change_workout', fn=get_workout_by_opaqueid, raise_exception=True) def workout_data_view(request, id=0): r = getrower(request.user) w = get_workoutuser(id, request) - breadcrumbs = [ - { - 'url':'/rowers/list-workouts/', - 'name':'Workouts' - }, - { - 'url':get_workout_default_page(request,id), - 'name': w.name - }, - { - 'url':reverse('workout_data_view',kwargs={'id':id}), - 'name': 'Data Explorer' - } + { + 'url': '/rowers/list-workouts/', + 'name': 'Workouts' + }, + { + 'url': get_workout_default_page(request, id), + 'name': w.name + }, + { + 'url': reverse('workout_data_view', kwargs={'id': id}), + 'name': 'Data Explorer' + } - ] + ] - - datadf,row = dataprep.getrowdata_db(id=encoder.decode_hex(id)) + datadf, row = dataprep.getrowdata_db(id=encoder.decode_hex(id)) try: - datadf.sort_values(['ftime'],inplace=True) + datadf.sort_values(['ftime'], inplace=True) except KeyError: pass columns = datadf.columns.values to_be_dropped = [ - 'id','time','hr_an','hr_at','hr_bottom','hr_max', - 'hr_tr','hr_ut1','hr_ut2','x_right', + 'id', 'time', 'hr_an', 'hr_at', 'hr_bottom', 'hr_max', + 'hr_tr', 'hr_ut1', 'hr_ut2', 'x_right', ] to_be_dropped = [c for c in to_be_dropped if c in columns] - datadf.drop(labels=to_be_dropped,inplace=True,axis=1) + datadf.drop(labels=to_be_dropped, inplace=True, axis=1) - - cols = ['ftime','cumdist','fpace','spm', - 'hr','power','driveenergy','drivelength','averageforce', - 'peakforce','distance','drivespeed','workoutstate', - 'catch','finish','peakforceangle','wash','slip','rhythm', - 'effectiveangle','totalangle','distanceperstroke','velo'] + cols = ['ftime', 'cumdist', 'fpace', 'spm', + 'hr', 'power', 'driveenergy', 'drivelength', 'averageforce', + 'peakforce', 'distance', 'drivespeed', 'workoutstate', + 'catch', 'finish', 'peakforceangle', 'wash', 'slip', 'rhythm', + 'effectiveangle', 'totalangle', 'distanceperstroke', 'velo'] cols = [c for c in cols if c in datadf.columns] - tcols = ['ftime','cumdist','fpace','spm','hr','power'] + tcols = ['ftime', 'cumdist', 'fpace', 'spm', 'hr', 'power'] datadf = datadf[cols] try: - datadf.loc[:,'hr'] = datadf['hr'].astype('int') - datadf.loc[:,'power'] = datadf['power'].astype('int') - datadf.loc[:,'distance'] = datadf['distance'].astype('int') - datadf.loc[:,'spm'] = 10*datadf['spm'].astype('int')/10. + datadf.loc[:, 'hr'] = datadf['hr'].astype('int') + datadf.loc[:, 'power'] = datadf['power'].astype('int') + datadf.loc[:, 'distance'] = datadf['distance'].astype('int') + datadf.loc[:, 'spm'] = 10*datadf['spm'].astype('int')/10. except KeyError: pass @@ -3249,7 +3195,7 @@ def workout_data_view(request, id=0): tcols = form.cleaned_data['cols'] else: - form = DataFrameColumnsForm(initial = {'cols':tcols}) + form = DataFrameColumnsForm(initial={'cols': tcols}) try: datadf = datadf[tcols] @@ -3264,8 +3210,8 @@ def workout_data_view(request, id=0): for col in cols: try: if datadf[col].mean() == 0 and datadf[col].std() == 0: - datadf.drop(labels=[col],axis=1,inplace=True) - except (TypeError,KeyError): + datadf.drop(labels=[col], axis=1, inplace=True) + except (TypeError, KeyError): pass # pd.set_option('display.width', 1000) @@ -3273,121 +3219,115 @@ def workout_data_view(request, id=0): htmltable = datadf.to_html( bold_rows=True, - show_dimensions=True,border=1, - classes=['pandastable'],justify='justify' + show_dimensions=True, border=1, + classes=['pandastable'], justify='justify' ) return render(request, 'workout_data.html', { 'htmltable': htmltable, - 'data':datadf, - 'cols':datadf.columns, - 'form':form, - 'teams':get_my_teams(request.user), + 'data': datadf, + 'cols': datadf.columns, + 'form': form, + 'teams': get_my_teams(request.user), 'workout': w, 'breadcrumbs': breadcrumbs, - } + } ) # Stats page -@permission_required('workout.view_workout',fn=get_workout_by_opaqueid,raise_exception=True) -def workout_stats_view(request,id=0,message="",successmessage=""): +@permission_required('workout.view_workout', fn=get_workout_by_opaqueid, raise_exception=True) +def workout_stats_view(request, id=0, message="", successmessage=""): r = getrower(request.user) w = get_workout(id) mayedit = 0 if request.user == w.user.user: - mayedit=1 - if is_workout_user(request.user,w): - mayedit=1 + mayedit = 1 + if is_workout_user(request.user, w): + mayedit = 1 breadcrumbs = [ - { - 'url':'/rowers/list-workouts/', - 'name':'Workouts' - }, - { - 'url':get_workout_default_page(request,id), - 'name': w.name - }, - { - 'url':reverse('workout_stats_view',kwargs={'id':id}), - 'name': 'Stats' - } + { + 'url': '/rowers/list-workouts/', + 'name': 'Workouts' + }, + { + 'url': get_workout_default_page(request, id), + 'name': w.name + }, + { + 'url': reverse('workout_stats_view', kwargs={'id': id}), + 'name': 'Stats' + } - ] + ] workstrokesonly = True - if request.method == 'POST' and 'workstrokesonly' in request.POST: # pragma: no cover + if request.method == 'POST' and 'workstrokesonly' in request.POST: # pragma: no cover workstrokesonly = str2bool(request.POST['workstrokesonly']) - # prepare data frame - datadf,row = dataprep.getrowdata_db(id=encoder.decode_hex(id)) + datadf, row = dataprep.getrowdata_db(id=encoder.decode_hex(id)) - - - datadf = dataprep.clean_df_stats(datadf,workstrokesonly=workstrokesonly, + datadf = dataprep.clean_df_stats(datadf, workstrokesonly=workstrokesonly, ignoreadvanced=False) - if datadf.empty: - datadf,row = dataprep.getrowdata_db(id=encoder.decode_hex(id)) - datadf = dataprep.clean_df_stats(datadf,workstrokesonly=False, + datadf, row = dataprep.getrowdata_db(id=encoder.decode_hex(id)) + datadf = dataprep.clean_df_stats(datadf, workstrokesonly=False, ignoreadvanced=True) - workstrokesonly=False + workstrokesonly = False if datadf.empty: return HttpResponse("CSV data file not found") #datadf['deltat'] = datadf['time'].diff() - - workoutstateswork = [1,4,5,8,9,6,7] + workoutstateswork = [1, 4, 5, 8, 9, 6, 7] workoutstatesrest = [3] - workoutstatetransition = [0,2,10,11,12,13] - + workoutstatetransition = [0, 2, 10, 11, 12, 13] # Create stats stats = {} - fieldlist,fielddict = dataprep.getstatsfields() + fieldlist, fielddict = dataprep.getstatsfields() try: fielddict.pop('pace') - except KeyError: # pragma: no cover + except KeyError: # pragma: no cover pass - for field,verbosename in fielddict.items(): + for field, verbosename in fielddict.items(): try: thedict = { - 'mean':datadf[field].mean(), + 'mean': datadf[field].mean(), 'wmean': wavg(datadf, field, 'deltat'), 'min': datadf[field].min(), 'std': datadf[field].std(), 'max': datadf[field].max(), 'median': datadf[field].median(), - 'firstq':datadf[field].quantile(q=0.25), - 'thirdq':datadf[field].quantile(q=0.75), - 'verbosename':verbosename, + 'firstq': datadf[field].quantile(q=0.25), + 'thirdq': datadf[field].quantile(q=0.75), + 'verbosename': verbosename, } stats[field] = thedict - except KeyError: # pragma: no cover + except KeyError: # pragma: no cover pass # Create a dict with correlation values cor = datadf.corr(method='spearman') - cor.fillna(value=0,inplace=True) + cor.fillna(value=0, inplace=True) cordict = {} - for field1,verbosename1 in fielddict.items(): + for field1, verbosename1 in fielddict.items(): thedict = {} - for field2,verbosename2 in fielddict.items(): + for field2, verbosename2 in fielddict.items(): try: - thedict[verbosename2] = cor.loc[field1,field2] - except KeyError: # pragma: no cover + thedict[verbosename2] = cor.loc[field1, field2] + except KeyError: # pragma: no cover thedict[verbosename2] = 0 cordict[verbosename1] = thedict @@ -3396,9 +3336,8 @@ def workout_stats_view(request,id=0,message="",successmessage=""): otherstats = {} # Normalized power & TSS - tss,normp = dataprep.workout_rscore(w) - goldmedalstandard,goldmedalseconds = dataprep.workout_goldmedalstandard(w) - + tss, normp = dataprep.workout_rscore(w) + goldmedalstandard, goldmedalseconds = dataprep.workout_goldmedalstandard(w) if not np.isnan(goldmedalstandard) and goldmedalstandard > 0: otherstats['goldmedalstandard'] = { @@ -3410,23 +3349,22 @@ def workout_stats_view(request,id=0,message="",successmessage=""): if not np.isnan(goldmedalseconds) and goldmedalseconds > 0: otherstats['goldmedalseconds'] = { 'verbose_name': 'Gold Medal Standard Duration', - 'value': utils.totaltime_sec_to_string(goldmedalseconds,shorten=True), + 'value': utils.totaltime_sec_to_string(goldmedalseconds, shorten=True), 'unit': '', } - if not np.isnan(tss) and tss != 0: otherstats['tss'] = { - 'verbose_name':'rScore', - 'value':int(tss), - 'unit':'' + 'verbose_name': 'rScore', + 'value': int(tss), + 'unit': '' } if not np.isnan(normp): otherstats['np'] = { - 'verbose_name':'rPower', - 'value':int(10*normp)/10., - 'unit':'Watt' + 'verbose_name': 'rPower', + 'value': int(10*normp)/10., + 'unit': 'Watt' } # HR Drift @@ -3436,11 +3374,11 @@ def workout_stats_view(request,id=0,message="",successmessage=""): mask1 = datadf['time'] < thalf mask2 = datadf['time'] > thalf - hr1 = datadf.loc[mask1,'hr'].mean() - hr2 = datadf.loc[mask2,'hr'].mean() + hr1 = datadf.loc[mask1, 'hr'].mean() + hr2 = datadf.loc[mask2, 'hr'].mean() - pwr1 = datadf.loc[mask1,'power'].mean() - pwr2 = datadf.loc[mask2,'power'].mean() + pwr1 = datadf.loc[mask1, 'power'].mean() + pwr2 = datadf.loc[mask2, 'power'].mean() try: hrdrift = ((pwr1/hr1)-(pwr2/hr2))/(pwr1/hr1) @@ -3448,18 +3386,18 @@ def workout_stats_view(request,id=0,message="",successmessage=""): if not np.isnan(hrdrift): try: hrdrift = int(100*hrdrift)/100. - except: # pragma: no cover + except: # pragma: no cover hrdrift = 0 otherstats['hrdrift'] = { 'verbose_name': 'Heart Rate Drift', 'value': hrdrift, 'unit': '%', } - except (ZeroDivisionError,ValueError): # pragma: no cover + except (ZeroDivisionError, ValueError): # pragma: no cover pass # TRIMP - trimp,hrtss = dataprep.workout_trimp(w) + trimp, hrtss = dataprep.workout_trimp(w) otherstats['trimp'] = { 'verbose_name': 'TRIMP', @@ -3470,33 +3408,32 @@ def workout_stats_view(request,id=0,message="",successmessage=""): otherstats['hrScore'] = { 'verbose_name': 'rScore (HR)', 'value': hrtss, - 'unit':'' - } + 'unit': '' + } return render(request, 'workoutstats.html', { - 'stats':stats, - 'teams':get_my_teams(request.user), - 'workout':w, - 'rower':r, - 'mayedit':mayedit, - 'breadcrumbs':breadcrumbs, - 'active':'nav-workouts', - 'workstrokesonly':workstrokesonly, - 'cordict':cordict, - 'otherstats':otherstats, + 'stats': stats, + 'teams': get_my_teams(request.user), + 'workout': w, + 'rower': r, + 'mayedit': mayedit, + 'breadcrumbs': breadcrumbs, + 'active': 'nav-workouts', + 'workstrokesonly': workstrokesonly, + 'cordict': cordict, + 'otherstats': otherstats, }) - # Change default landing page @login_required() def workflow_default_view(request): r = getrower(request.user) if r.defaultlandingpage == 'workout_edit_view': r.defaultlandingpage = 'workout_workflow_view' - else: # pragma: no cover + else: # pragma: no cover r.defaultlandingpage = 'workout_edit_view' r.save() @@ -3508,7 +3445,7 @@ def workflow_default_view(request): # Workflow configuration @login_required() -def workout_workflow_config2_view(request,userid=0): +def workout_workflow_config2_view(request, userid=0): request.session['referer'] = absolute(request)['PATH'] request.session[translation.LANGUAGE_SESSION_KEY] = USER_LANGUAGE try: @@ -3516,11 +3453,9 @@ def workout_workflow_config2_view(request,userid=0): except KeyError: workoutid = 0 + r = getrequestrower(request, userid=userid, notpermanent=True) - r = getrequestrower(request,userid=userid,notpermanent=True) - - MiddlePanelFormSet = formset_factory(WorkFlowMiddlePanelElement,extra=1) - + MiddlePanelFormSet = formset_factory(WorkFlowMiddlePanelElement, extra=1) if request.method == 'POST': middlepanel_formset = MiddlePanelFormSet(request.POST, @@ -3532,77 +3467,70 @@ def workout_workflow_config2_view(request,userid=0): if value != 'None': newmiddlepanel.append(value) - newmiddlepanel = [i for i in newmiddlepanel if i != None] r.workflowmiddlepanel = newmiddlepanel try: r.save() - except IntegrityError: # pragma: no cover - messages.error(request,'Something went wrong') + except IntegrityError: # pragma: no cover + messages.error(request, 'Something went wrong') - middlepanelform_data = [{'panel':panel} - for panel in r.workflowmiddlepanel] + middlepanelform_data = [{'panel': panel} + for panel in r.workflowmiddlepanel] middlepanel_formset = MiddlePanelFormSet(initial=middlepanelform_data, prefix='middlepanel') - tmplt = 'workflowconfig2.html' - return render(request,tmplt, + return render(request, tmplt, { - 'rower':r, - 'middlepanel_formset':middlepanel_formset, + 'rower': r, + 'middlepanel_formset': middlepanel_formset, 'workoutid': workoutid, - }) - + }) # Workflow View @login_required() -@permission_required('workout.view_workout',fn=get_workout_by_opaqueid,raise_exception=True) -def workout_workflow_view(request,id): +@permission_required('workout.view_workout', fn=get_workout_by_opaqueid, raise_exception=True) +def workout_workflow_view(request, id): request.session['referer'] = absolute(request)['PATH'] request.session['lastworkout'] = id request.session[translation.LANGUAGE_SESSION_KEY] = USER_LANGUAGE - row = get_workout_by_opaqueid(request,id) + row = get_workout_by_opaqueid(request, id) r = getrower(request.user) result = request.user.is_authenticated and ispromember(request.user) if result: - promember=1 + promember = 1 if request.user == row.user.user: - mayedit=1 + mayedit = 1 comments = WorkoutComment.objects.filter(workout=row) aantalcomments = len(comments) - favorites,maxfav = getfavorites(r,row) + favorites, maxfav = getfavorites(r, row) charts = get_call() - if 'panel_map.html' in r.workflowmiddlepanel and rowhascoordinates(row): rowdata = rdata(csvfile=row.csvfilename) - mapscript,mapdiv = leaflet_chart2(rowdata.df[' latitude'], - rowdata.df[' longitude'], - row.name) + mapscript, mapdiv = leaflet_chart2(rowdata.df[' latitude'], + rowdata.df[' longitude'], + row.name) else: mapscript = '' mapdiv = '' - - statcharts = GraphImage.objects.filter(workout=row) - middleTemplates = [] for t in r.workflowmiddlepanel: try: template.loader.get_template(t) middleTemplates.append(t) - except template.TemplateDoesNotExist: # pragma: no cover + except template.TemplateDoesNotExist: # pragma: no cover pass leftTemplates = [] @@ -3610,53 +3538,54 @@ def workout_workflow_view(request,id): try: template.loader.get_template(t) leftTemplates.append(t) - except template.TemplateDoesNotExist: # pragma: no cover + except template.TemplateDoesNotExist: # pragma: no cover pass - breadcrumbs = [ - { - 'url':'/rowers/list-workouts/', - 'name':'Workouts' - }, - { - 'url':get_workout_default_page(request,id), - 'name': row.name - }, - { - 'url':reverse('workout_workflow_view',kwargs={'id':id}), - 'name': 'View' - } + { + 'url': '/rowers/list-workouts/', + 'name': 'Workouts' + }, + { + 'url': get_workout_default_page(request, id), + 'name': row.name + }, + { + 'url': reverse('workout_workflow_view', kwargs={'id': id}), + 'name': 'View' + } - ] + ] return render(request, 'workflow.html', { - 'middleTemplates':middleTemplates, - 'leftTemplates':leftTemplates, - 'active':'nav-workouts', - 'breadcrumbs':breadcrumbs, - 'charts':charts, - 'workout':row, - 'mapscript':mapscript, - 'mapdiv':mapdiv, - 'statcharts':statcharts, - 'rower':r, - 'aantalcomments':aantalcomments, + 'middleTemplates': middleTemplates, + 'leftTemplates': leftTemplates, + 'active': 'nav-workouts', + 'breadcrumbs': breadcrumbs, + 'charts': charts, + 'workout': row, + 'mapscript': mapscript, + 'mapdiv': mapdiv, + 'statcharts': statcharts, + 'rower': r, + 'aantalcomments': aantalcomments, }) # The famous flex chart + + @login_required() -@permission_required('workout.view_workout',fn=get_workout_by_opaqueid,raise_exception=True) -def workout_flexchart3_view(request,*args,**kwargs): +@permission_required('workout.view_workout', fn=get_workout_by_opaqueid, raise_exception=True) +def workout_flexchart3_view(request, *args, **kwargs): try: id = kwargs['id'] - except KeyError: # pragma: no cover + except KeyError: # pragma: no cover raise Http404("Invalid workout number") - if 'promember' in kwargs: # pragma: no cover + if 'promember' in kwargs: # pragma: no cover promember = kwargs['promember'] else: promember = 0 @@ -3669,31 +3598,31 @@ def workout_flexchart3_view(request,*args,**kwargs): row = get_workout(id) r = getrequestrower(request) - promember=0 - mayedit=0 + promember = 0 + mayedit = 0 if not request.user.is_anonymous: result = ispromember(request.user) if result: - promember=1 + promember = 1 if request.user == row.user.user: - mayedit=1 - if is_workout_user(request.user,row): - mayedit=1 + mayedit = 1 + if is_workout_user(request.user, row): + mayedit = 1 workouttype = 'ote' if row.workouttype in mytypes.otwtypes: workouttype = 'otw' - favorites,maxfav = getfavorites(r,row) + favorites, maxfav = getfavorites(r, row) # check if favoritenr is not out of range if favorites: try: t = favorites[favoritenr].xparam - except IndexError: # pragma: no cover - favoritenr=0 + except IndexError: # pragma: no cover + favoritenr = 0 except AssertionError: - favoritenr=0 + favoritenr = 0 if 'xparam' in kwargs: xparam = kwargs['xparam'] @@ -3713,7 +3642,7 @@ def workout_flexchart3_view(request,*args,**kwargs): if 'yparam2' in kwargs: yparam2 = kwargs['yparam2'] - if yparam2 == '': # pragma: no cover + if yparam2 == '': # pragma: no cover yparam2 = 'None' else: if favorites: @@ -3725,18 +3654,18 @@ def workout_flexchart3_view(request,*args,**kwargs): if not request.user.is_anonymous: r = getrower(request.user) - if favoritenr>=0 and r.showfavoritechartnotes: + if favoritenr >= 0 and r.showfavoritechartnotes: try: favoritechartnotes = favorites[favoritenr].notes - except IndexError: # pragma: no cover + except IndexError: # pragma: no cover favoritechartnotes = '' else: favoritechartnotes = '' - else: # pragma: no cover + else: # pragma: no cover favoritechartnotes = '' favoritenr = 0 - if 'plottype' in kwargs: # pragma: no cover + if 'plottype' in kwargs: # pragma: no cover plottype = kwargs['plottype'] else: if favorites: @@ -3744,7 +3673,7 @@ def workout_flexchart3_view(request,*args,**kwargs): else: plottype = 'line' - if 'workstrokesonly' in kwargs: # pragma: no cover + if 'workstrokesonly' in kwargs: # pragma: no cover workstrokesonly = kwargs['workstrokesonly'] else: if favorites: @@ -3763,14 +3692,15 @@ def workout_flexchart3_view(request,*args,**kwargs): range = metrics.yaxmaxima[yparam1] if yparam2 is not None: range = metrics.yaxmaxima[yparam2] - f = FavoriteChart(user=r,xparam=xparam, - yparam1=yparam1,yparam2=yparam2, - plottype=plottype,workouttype=workouttype, - reststrokes=reststrokes) + f = FavoriteChart(user=r, xparam=xparam, + yparam1=yparam1, yparam2=yparam2, + plottype=plottype, workouttype=workouttype, + reststrokes=reststrokes) f.save() - except KeyError: # pragma: no cover - messages.error(request,'We cannot save the ad hoc metrics in a favorite chart') + except KeyError: # pragma: no cover + messages.error( + request, 'We cannot save the ad hoc metrics in a favorite chart') if request.method == 'POST' and 'xaxis' in request.POST: flexoptionsform = FlexOptionsForm(request.POST) @@ -3781,7 +3711,7 @@ def workout_flexchart3_view(request,*args,**kwargs): workstrokesonly = not includereststrokes - flexaxesform = FlexAxesForm(request,request.POST) + flexaxesform = FlexAxesForm(request, request.POST) if flexaxesform.is_valid(): cd = flexaxesform.cleaned_data @@ -3791,19 +3721,21 @@ def workout_flexchart3_view(request,*args,**kwargs): else: pass - if not promember: - for name,d in rowingmetrics: + for name, d in rowingmetrics: if d['type'] != 'basic': - if xparam == name: # pragma: no cover + if xparam == name: # pragma: no cover xparam = 'time' - messages.info(request,'To use '+d['verbose_name']+', you have to be Pro member') - if yparam1 == name: # pragma: no cover + messages.info( + request, 'To use '+d['verbose_name']+', you have to be Pro member') + if yparam1 == name: # pragma: no cover yparam1 = 'pace' - messages.info(request,'To use '+d['verbose_name']+', you have to be Pro member') - if yparam2 == name: # pragma: no cover + messages.info( + request, 'To use '+d['verbose_name']+', you have to be Pro member') + if yparam2 == name: # pragma: no cover yparam2 = 'spm' - messages.info(request,'To use '+d['verbose_name']+', you have to be Pro member') + messages.info( + request, 'To use '+d['verbose_name']+', you have to be Pro member') # bring back slashes # yparam1 = yparam1.replace('_slsh_','/') @@ -3814,25 +3746,25 @@ def workout_flexchart3_view(request,*args,**kwargs): ( script, div, js_resources, css_resources, workstrokesonly ) = interactive_flex_chart2( - encoder.decode_hex(id),request.user.rower, - xparam=xparam,yparam1=yparam1, - yparam2=yparam2, - promember=promember,plottype=plottype, - workstrokesonly=workstrokesonly,mode=row.workouttype - ) + encoder.decode_hex(id), request.user.rower, + xparam=xparam, yparam1=yparam1, + yparam2=yparam2, + promember=promember, plottype=plottype, + workstrokesonly=workstrokesonly, mode=row.workouttype + ) - axchoicesbasic = {ax[0]:ax[1] for ax in axes if ax[4]=='basic'} - axchoicespro = {ax[0]:ax[1] for ax in axes if ax[4]=='pro'} - noylist = ["time","distance"] + axchoicesbasic = {ax[0]: ax[1] for ax in axes if ax[4] == 'basic'} + axchoicespro = {ax[0]: ax[1] for ax in axes if ax[4] == 'pro'} + noylist = ["time", "distance"] axchoicesbasic.pop("cumdist") if row.workouttype in mytypes.otwtypes: - for name,d in rowingmetrics: + for name, d in rowingmetrics: if d['mode'] == 'erg': axchoicespro.pop(name) else: - for name,d in rowingmetrics: + for name, d in rowingmetrics: if d['mode'] == 'water': axchoicespro.pop(name) @@ -3841,88 +3773,88 @@ def workout_flexchart3_view(request,*args,**kwargs): rowdata = rdata(csvfile=row.csvfilename) try: rowdata.set_instroke_metrics() - except (AttributeError,TypeError): # pragma: no cover + except (AttributeError, TypeError): # pragma: no cover pass try: additionalmetrics = rowdata.get_additional_metrics() - additionalmetrics = [m for m in additionalmetrics if not m in nometrics] - except AttributeError: # pragma: no cover + additionalmetrics = [ + m for m in additionalmetrics if not m in nometrics] + except AttributeError: # pragma: no cover additionalmetrics = [] - # extrametrics = {m.replace('/','_slsh_'):m for m in additionalmetrics} extrametrics = additionalmetrics initial = { - 'xaxis':xparam, - 'yaxis1':yparam1, - 'yaxis2':yparam2, + 'xaxis': xparam, + 'yaxis1': yparam1, + 'yaxis2': yparam2, } - flexaxesform = FlexAxesForm(request,initial=initial, + flexaxesform = FlexAxesForm(request, initial=initial, extrametrics=extrametrics) initial = { 'includereststrokes': not workstrokesonly, - 'plottype':plottype - } + 'plottype': plottype + } flexoptionsform = FlexOptionsForm(initial=initial) row = Workout.objects.get(id=encoder.decode_hex(id)) breadcrumbs = [ - { - 'url':'/rowers/list-workouts/', - 'name':'Workouts' - }, - { - 'url':get_workout_default_page(request,id), - 'name': row.name - }, - { - 'url':reverse('workout_flexchart3_view',kwargs=kwargs), - 'name': 'Flex Chart' - } - - ] + { + 'url': '/rowers/list-workouts/', + 'name': 'Workouts' + }, + { + 'url': get_workout_default_page(request, id), + 'name': row.name + }, + { + 'url': reverse('workout_flexchart3_view', kwargs=kwargs), + 'name': 'Flex Chart' + } + ] return render(request, 'flexchart3otw.html', - {'the_script':script, - 'the_div':div, - 'breadcrumbs':breadcrumbs, - 'rower':r, - 'active':'nav-workouts', - 'workout':row, - 'chartform':flexaxesform, - 'optionsform':flexoptionsform, + {'the_script': script, + 'the_div': div, + 'breadcrumbs': breadcrumbs, + 'rower': r, + 'active': 'nav-workouts', + 'workout': row, + 'chartform': flexaxesform, + 'optionsform': flexoptionsform, 'js_res': js_resources, - 'css_res':css_resources, - 'teams':get_my_teams(request.user), - 'id':id, - 'xparam':xparam, - 'yparam1':yparam1, - 'yparam2':yparam2, - 'plottype':plottype, - 'axchoicesbasic':axchoicesbasic, - 'axchoicespro':axchoicespro, - 'extrametrics':extrametrics, - 'favoritechartnotes':favoritechartnotes, - 'noylist':noylist, - 'mayedit':mayedit, - 'promember':promember, + 'css_res': css_resources, + 'teams': get_my_teams(request.user), + 'id': id, + 'xparam': xparam, + 'yparam1': yparam1, + 'yparam2': yparam2, + 'plottype': plottype, + 'axchoicesbasic': axchoicesbasic, + 'axchoicespro': axchoicespro, + 'extrametrics': extrametrics, + 'favoritechartnotes': favoritechartnotes, + 'noylist': noylist, + 'mayedit': mayedit, + 'promember': promember, 'workstrokesonly': not workstrokesonly, - 'favoritenr':favoritenr, - 'maxfav':maxfav, - }) + 'favoritenr': favoritenr, + 'maxfav': maxfav, + }) + @login_required() -@permission_required('workout.view_workout',fn=get_workout_by_opaqueid,raise_exception=True) -def workout_flexchart_stacked_view(request,*args,**kwargs): +@permission_required('workout.view_workout', fn=get_workout_by_opaqueid, raise_exception=True) +def workout_flexchart_stacked_view(request, *args, **kwargs): try: id = kwargs['id'] - except KeyError: # pragma: no cover + except KeyError: # pragma: no cover raise Http404("Invalid workout number") workout = get_workout(id) @@ -3935,7 +3867,7 @@ def workout_flexchart_stacked_view(request,*args,**kwargs): yparam4 = 'spm' if request.method == 'POST': - flexaxesform = StravaChartForm(request,request.POST) + flexaxesform = StravaChartForm(request, request.POST) if flexaxesform.is_valid(): cd = flexaxesform.cleaned_data xparam = cd['xaxis'] @@ -3947,7 +3879,7 @@ def workout_flexchart_stacked_view(request,*args,**kwargs): ( script, div, js_resources, css_resources, comment ) = interactive_flexchart_stacked( - encoder.decode_hex(id),r,xparam=xparam, + encoder.decode_hex(id), r, xparam=xparam, yparam1=yparam1, yparam2=yparam2, yparam3=yparam3, @@ -3955,72 +3887,74 @@ def workout_flexchart_stacked_view(request,*args,**kwargs): mode=workout.workouttype, ) - if comment is not None: # pragma: no cover - messages.error(request,comment) + if comment is not None: # pragma: no cover + messages.error(request, comment) initial = { - 'xaxis':xparam, - 'yaxis1':yparam1, - 'yaxis2':yparam2, - 'yaxis3':yparam3, - 'yaxis4':yparam4, + 'xaxis': xparam, + 'yaxis1': yparam1, + 'yaxis2': yparam2, + 'yaxis3': yparam3, + 'yaxis4': yparam4, } - flexaxesform = StravaChartForm(request,initial=initial, - ) + flexaxesform = StravaChartForm(request, initial=initial, + ) breadcrumbs = [ - { - 'url':'/rowers/list-workouts/', - 'name':'Workouts' - }, - { - 'url':get_workout_default_page(request,id), - 'name': workout.name - }, - { - 'url':reverse('workout_flexchart_stacked_view',kwargs=kwargs), - 'name': 'Stacked Flex Chart' - } + { + 'url': '/rowers/list-workouts/', + 'name': 'Workouts' + }, + { + 'url': get_workout_default_page(request, id), + 'name': workout.name + }, + { + 'url': reverse('workout_flexchart_stacked_view', kwargs=kwargs), + 'name': 'Stacked Flex Chart' + } - ] + ] return render(request, 'flexchartstacked.html', { - 'the_script':script, - 'the_div':div, - 'breadcrumbs':breadcrumbs, - 'rower':r, - 'active':'nav-workouts', - 'workout':workout, - 'chartform':flexaxesform, - 'js_res':js_resources, - 'css_res':css_resources, - 'id':id, - 'xparam':xparam, - 'yparam1':yparam1, - 'yparam2':yparam2, - 'yparam3':yparam3, - 'yparam4':yparam4, + 'the_script': script, + 'the_div': div, + 'breadcrumbs': breadcrumbs, + 'rower': r, + 'active': 'nav-workouts', + 'workout': workout, + 'chartform': flexaxesform, + 'js_res': js_resources, + 'css_res': css_resources, + 'id': id, + 'xparam': xparam, + 'yparam1': yparam1, + 'yparam2': yparam2, + 'yparam3': yparam3, + 'yparam4': yparam4, } ) # The interactive plot with wind corrected pace for OTW outings -def workout_otwpowerplot_view(request,id=0,message="",successmessage=""): + + +def workout_otwpowerplot_view(request, id=0, message="", successmessage=""): w = get_workout(id) r = getrower(request.user) breadcrumbs = [ { - 'url':'/rowers/list-workouts/', - 'name':'Workouts' + 'url': '/rowers/list-workouts/', + 'name': 'Workouts' }, { - 'url':get_workout_default_page(request,id), + 'url': get_workout_default_page(request, id), 'name': w.name }, { - 'url':reverse('workout_otwpowerplot_view',kwargs={'id':id}), + 'url': reverse('workout_otwpowerplot_view', kwargs={'id': id}), 'name': 'Interactive OTW Power Plot' } @@ -4028,46 +3962,46 @@ def workout_otwpowerplot_view(request,id=0,message="",successmessage=""): # check if user is owner of this workout - # create interactive plot f1 = w.csvfilename u = w.user.user # r = getrower(u) - promember=0 + promember = 0 mayedit = 0 result = request.user.is_authenticated and ispromember(request.user) if result: - promember=1 + promember = 1 if request.user == w.user.user: - mayedit=1 + mayedit = 1 # create interactive plot - res = interactive_otw_advanced_pace_chart(encoder.decode_hex(id),promember=promember) + res = interactive_otw_advanced_pace_chart( + encoder.decode_hex(id), promember=promember) script = res[0] div = res[1] - messages.error(request,message) - messages.info(request,successmessage) + messages.error(request, message) + messages.info(request, successmessage) return render(request, 'otwinteractive.html', - {'workout':w, - 'rower':r, - 'active':'nav-workouts', - 'breadcrumbs':breadcrumbs, - 'teams':get_my_teams(request.user), - 'interactiveplot':script, - 'the_div':div, - 'mayedit':mayedit}) + {'workout': w, + 'rower': r, + 'active': 'nav-workouts', + 'breadcrumbs': breadcrumbs, + 'teams': get_my_teams(request.user), + 'interactiveplot': script, + 'the_div': div, + 'mayedit': mayedit}) # @login_required() -def workout_unsubscribe_view(request,id=0): +def workout_unsubscribe_view(request, id=0): w = get_workout(id) - if w.privacy == 'private' and w.user.user != request.user: # pragma: no cover + if w.privacy == 'private' and w.user.user != request.user: # pragma: no cover return HttpResponseForbidden("Permission error") comments = WorkoutComment.objects.filter(workout=w, @@ -4081,23 +4015,23 @@ def workout_unsubscribe_view(request,id=0): successmessage = 'You have been unsubscribed from new comment notifications for this workout' - messages.info(request,successmessage) + messages.info(request, successmessage) return render(request, 'workout_comments.html', - {'workout':w, - 'teams':get_my_teams(request.user), - 'comments':comments, - 'form':form, + {'workout': w, + 'teams': get_my_teams(request.user), + 'comments': comments, + 'form': form, }) # list of comments to a workout @login_required() -def workout_comment_view(request,id=0): +def workout_comment_view(request, id=0): w = get_workout(id) - if w.privacy == 'private' and w.user.user != request.user: # pragma: no cover + if w.privacy == 'private' and w.user.user != request.user: # pragma: no cover return HttpResponseForbidden("Permission error") comments = WorkoutComment.objects.filter(workout=w).order_by("created") @@ -4110,44 +4044,43 @@ def workout_comment_view(request,id=0): cd = form.cleaned_data comment = cd['comment'] comment = bleach.clean(comment) - try: # pragma: no cover - if isinstance(comment,unicode): + try: # pragma: no cover + if isinstance(comment, unicode): comment = comment.encode('utf8') elif isinstance(comment, str): comment = comment.decode('utf8') except: pass - notification = cd['notification'] - c = WorkoutComment(workout=w,user=request.user,comment=comment, + c = WorkoutComment(workout=w, user=request.user, comment=comment, notification=notification) c.save() url = reverse('workout_comment_view', kwargs={ - 'id':id, - }) + 'id': id, + }) message = '{name} says: {comment}'.format( - name = request.user.first_name, - comment = comment, - url = url, + name=request.user.first_name, + comment=comment, + url=url, ) - if request.user != r.user: # pragma: no cover - a_messages.info(r.user,message.encode('ascii','ignore')) + if request.user != r.user: # pragma: no cover + a_messages.info(r.user, message.encode('ascii', 'ignore')) res = myqueue(queuehigh, - handle_sendemailnewcomment,r.user.first_name, + handle_sendemailnewcomment, r.user.first_name, r.user.last_name, r.user.email, request.user.first_name, request.user.last_name, - comment,w.name,w.id, - emailbounced = r.emailbounced - ) + comment, w.name, w.id, + emailbounced=r.emailbounced + ) commenters = {oc.user for oc in comments if oc.notification} - for u in commenters: # pragma: no cover - a_messages.info(u,message) + for u in commenters: # pragma: no cover + a_messages.info(u, message) if u != request.user and u != r.user: ocr = Rower.objects.get(user=u) res = myqueue(queue, @@ -4161,20 +4094,20 @@ def workout_comment_view(request,id=0): w.name, w.id, c.id, - emailbounced = ocr.emailbounced - ) + emailbounced=ocr.emailbounced + ) url = reverse('workout_comment_view', - kwargs = { - 'id':id}) + kwargs={ + 'id': id}) return HttpResponseRedirect(url) form = WorkoutCommentForm() g = GraphImage.objects.filter(workout=w).order_by("-creationdatetime") - for i in g: # pragma: no cover + for i in g: # pragma: no cover try: - width,height = Image.open(i.filename).size + width, height = Image.open(i.filename).size i.width = width i.height = height i.save() @@ -4184,76 +4117,74 @@ def workout_comment_view(request,id=0): rower = getrower(request.user) breadcrumbs = [ - { - 'url':'/rowers/list-workouts/', - 'name':'Workouts' - }, - { - 'url':get_workout_default_page(request,id), - 'name': w.name - }, - { - 'url':reverse('workout_comment_view',kwargs={'id':id}), - 'name': 'Comments' - } - - ] + { + 'url': '/rowers/list-workouts/', + 'name': 'Workouts' + }, + { + 'url': get_workout_default_page(request, id), + 'name': w.name + }, + { + 'url': reverse('workout_comment_view', kwargs={'id': id}), + 'name': 'Comments' + } + ] return render(request, 'workout_comments.html', - {'workout':w, - 'rower':rower, - 'breadcrumbs':breadcrumbs, - 'active':'nav-workouts', - 'teams':get_my_teams(request.user), - 'graphs':g, - 'comments':comments, - 'form':form, - }) - + {'workout': w, + 'rower': rower, + 'breadcrumbs': breadcrumbs, + 'active': 'nav-workouts', + 'teams': get_my_teams(request.user), + 'graphs': g, + 'comments': comments, + 'form': form, + }) # The basic edit page @login_required() -@permission_required('workout.change_workout',fn=get_workout_by_opaqueid,raise_exception=True) -def workout_edit_view(request,id=0,message="",successmessage=""): +@permission_required('workout.change_workout', fn=get_workout_by_opaqueid, raise_exception=True) +def workout_edit_view(request, id=0, message="", successmessage=""): request.session[translation.LANGUAGE_SESSION_KEY] = USER_LANGUAGE request.session['referer'] = absolute(request)['PATH'] - - row = get_workoutuser(id,request) + row = get_workoutuser(id, request) if request.user.rower.rowerplan == 'basic' and 'speedcoach2' in row.workoutsource: # pragma: no cover - data = getsmallrowdata_db(['wash'],ids=[encoder.decode_hex(id)]) + data = getsmallrowdata_db(['wash'], ids=[encoder.decode_hex(id)]) try: if data['wash'].std() != 0: url = reverse('paidplans_view') messages.info( request, - 'Some Empower Oarlock data are only available to users with a paid plan'.format(u=url) + 'Some Empower Oarlock data are only available to users with a paid plan'.format( + u=url) ) except: pass if request.user.rower.rowerplan == 'basic' and 'nklinklogbook' in row.workoutsource: # pragma: no cover - data = getsmallrowdata_db(['wash'],ids=[encoder.decode_hex(id)]) + data = getsmallrowdata_db(['wash'], ids=[encoder.decode_hex(id)]) try: if data['wash'].std() != 0: url = reverse('paidplans_view') messages.info( request, - 'Some Empower Oarlock data are only available to users with a paid plan'.format(u=url) + 'Some Empower Oarlock data are only available to users with a paid plan'.format( + u=url) ) except: pass - form = WorkoutForm(instance=row) if request.method == 'POST': # Form was submitted - form = WorkoutForm(request.POST,instance=row) + form = WorkoutForm(request.POST, instance=row) if form.is_valid(): # Get values from form name = form.cleaned_data['name'] @@ -4272,51 +4203,56 @@ def workout_edit_view(request,id=0,message="",successmessage=""): rpe = form.cleaned_data['rpe'] if not rpe: # pragma: no cover rpe = -1 - except KeyError: # pragma: no cover + except KeyError: # pragma: no cover rpe = -1 - ps = form.cleaned_data.get('plannedsession',None) + ps = form.cleaned_data.get('plannedsession', None) - boattype = request.POST.get('boattype',Workout.objects.get(id=encoder.decode_hex(id)).boattype) - privacy = request.POST.get('privacy',Workout.objects.get(id=encoder.decode_hex(id)).privacy) - rankingpiece = form.cleaned_data.get('rankingpiece',Workout.objects.get(id=row.id).rankingpiece) - duplicate = form.cleaned_data.get('duplicate',Workout.objects.get(id=row.id).duplicate) + boattype = request.POST.get('boattype', Workout.objects.get( + id=encoder.decode_hex(id)).boattype) + privacy = request.POST.get('privacy', Workout.objects.get( + id=encoder.decode_hex(id)).privacy) + rankingpiece = form.cleaned_data.get( + 'rankingpiece', Workout.objects.get(id=row.id).rankingpiece) + duplicate = form.cleaned_data.get( + 'duplicate', Workout.objects.get(id=row.id).duplicate) if private: privacy = 'private' - else: # pragma: no cover + else: # pragma: no cover privacy = 'visible' try: startdatetime = datetime.datetime.combine( - date,starttime + date, starttime ) - except TypeError: # pragma: no cover + except TypeError: # pragma: no cover startdatetime = datetime.datetime.combine( - date,datetime.datetime.min.time() - ) + date, datetime.datetime.min.time() + ) try: startdatetime = pytz.timezone(thetimezone).localize( startdatetime ) - except UnknownTimeZoneError: # pragma: no cover + except UnknownTimeZoneError: # pragma: no cover pass try: # aware object can be in any timezone out = startdatetime.astimezone(pytz.utc) - except (ValueError, TypeError): # pragma: no cover + except (ValueError, TypeError): # pragma: no cover startdatetime = timezone.make_aware(startdatetime) try: - startdatetime = startdatetime.astimezone(pytz.timezone(thetimezone)) - except UnknownTimeZoneError: # pragma: no cover + startdatetime = startdatetime.astimezone( + pytz.timezone(thetimezone)) + except UnknownTimeZoneError: # pragma: no cover thetimezone = 'UTC' timechanged = (startdatetime != row.startdatetime) - row.name = name + row.name = name row.date = date row.starttime = starttime row.startdatetime = startdatetime @@ -4335,17 +4271,17 @@ def workout_edit_view(request,id=0,message="",successmessage=""): row.plannedsession = ps dragchanged = False - if newdragfactor != row.dragfactor: # pragma: no cover + if newdragfactor != row.dragfactor: # pragma: no cover row.dragfactor = newdragfactor dragchanged = True try: row.save() - except IntegrityError: # pragma: no cover + except IntegrityError: # pragma: no cover pass - if ps: # pragma: no cover - add_workouts_plannedsession([row],ps,row.user) + if ps: # pragma: no cover + add_workouts_plannedsession([row], ps, row.user) # change data in csv file datachanged = (dragchanged or timechanged) @@ -4354,25 +4290,25 @@ def workout_edit_view(request,id=0,message="",successmessage=""): if dragchanged: try: # pragma: no cover r.change_drag(newdragfactor) - except AttributeError: # pragma: no cover + except AttributeError: # pragma: no cover pass - if r == 0: # pragma: no cover + if r == 0: # pragma: no cover return HttpResponse("Error: CSV Data File Not Found") r.rowdatetime = startdatetime - r.write_csv(row.csvfilename,gzip=True) - dataprep.update_strokedata(encoder.decode_hex(id),r.df) + r.write_csv(row.csvfilename, gzip=True) + dataprep.update_strokedata(encoder.decode_hex(id), r.df) successmessage = "Changes saved" - messages.info(request,successmessage) + messages.info(request, successmessage) else: form = WorkoutForm(instance=row) g = GraphImage.objects.filter(workout=row).order_by("-creationdatetime") - for i in g: # pragma: no cover + for i in g: # pragma: no cover try: - width,height = Image.open(i.filename).size + width, height = Image.open(i.filename).size i.width = width i.height = height i.save() @@ -4398,98 +4334,91 @@ def workout_edit_view(request,id=0,message="",successmessage=""): hascoordinates = 0 if not longitude.std(): hascoordinates = 0 - except (KeyError,AttributeError): + except (KeyError, AttributeError): hascoordinates = 0 - - else: # pragma: no cover hascoordinates = 0 - mapscript = "" mapdiv = "" if hascoordinates: try: - mapscript,mapdiv = leaflet_chart( + mapscript, mapdiv = leaflet_chart( rowdata.df[' latitude'], rowdata.df[' longitude'], row.name) except KeyError: # pragma: no cover pass - records = VirtualRaceResult.objects.filter(workoutid=row.id,userid=row.user.user.id,coursecompleted=True) - if records.count()>0: # pragma: no cover + records = VirtualRaceResult.objects.filter( + workoutid=row.id, userid=row.user.user.id, coursecompleted=True) + if records.count() > 0: # pragma: no cover courses = list(set([record.course for record in records])) - breadcrumbs = [ - { - 'url':'/rowers/list-workouts/', - 'name':'Workouts' - }, - { - 'url':get_workout_default_page(request,encoder.encode_hex(row.id)), - 'name': row.name - }, - { - 'url':reverse('workout_edit_view',kwargs={'id':encoder.encode_hex(row.id)}), - 'name': 'Edit' - } + { + 'url': '/rowers/list-workouts/', + 'name': 'Workouts' + }, + { + 'url': get_workout_default_page(request, encoder.encode_hex(row.id)), + 'name': row.name + }, + { + 'url': reverse('workout_edit_view', kwargs={'id': encoder.encode_hex(row.id)}), + 'name': 'Edit' + } - ] + ] if row.workouttype in mytypes.otetypes: indoorraces = get_indoorraces(row) else: indoorraces = [] - r = row.user # render page return render(request, 'workout_form.html', - {'form':form, - 'workout':row, - 'teams':get_my_teams(request.user), - 'graphs':g, - 'videos':videos, - 'breadcrumbs':breadcrumbs, - 'rower':r, - 'indoorraces':indoorraces, - 'active':'nav-workouts', - 'mapscript':mapscript, - 'mapdiv':mapdiv, - 'rower':r, - 'courses':courses, - }) - - + {'form': form, + 'workout': row, + 'teams': get_my_teams(request.user), + 'graphs': g, + 'videos': videos, + 'breadcrumbs': breadcrumbs, + 'rower': r, + 'indoorraces': indoorraces, + 'active': 'nav-workouts', + 'mapscript': mapscript, + 'mapdiv': mapdiv, + 'rower': r, + 'courses': courses, + }) @login_required() -def workout_map_view(request,id=0): +def workout_map_view(request, id=0): request.session[translation.LANGUAGE_SESSION_KEY] = USER_LANGUAGE request.session['referer'] = absolute(request)['PATH'] w = get_workout(id) breadcrumbs = [ - { - 'url':'/rowers/list-workouts/', - 'name':'Workouts' - }, - { - 'url':get_workout_default_page(request,id), - 'name': w.name - }, - { - 'url':reverse('workout_map_view',kwargs={'id':id}), - 'name': 'Map' - } - - ] + { + 'url': '/rowers/list-workouts/', + 'name': 'Workouts' + }, + { + 'url': get_workout_default_page(request, id), + 'name': w.name + }, + { + 'url': reverse('workout_map_view', kwargs={'id': id}), + 'name': 'Map' + } + ] # create interactive plot f1 = w.csvfilename @@ -4500,127 +4429,120 @@ def workout_map_view(request,id=0): if rowdata != 0: try: latitude = rowdata.df[' latitude'] - if not latitude.std(): # pragma: no cover + if not latitude.std(): # pragma: no cover hascoordinates = 0 - except (KeyError,AttributeError): + except (KeyError, AttributeError): hascoordinates = 0 - else: # pragma: no cover + else: # pragma: no cover hascoordinates = 0 - if hascoordinates: - mapscript,mapdiv = leaflet_chart2(rowdata.df[' latitude'], - rowdata.df[' longitude'], - w.name) + mapscript, mapdiv = leaflet_chart2(rowdata.df[' latitude'], + rowdata.df[' longitude'], + w.name) else: mapscript = "" mapdiv = "" - mayedit=0 + mayedit = 0 if not request.user.is_anonymous: r = getrower(request.user) result = request.user.is_authenticated and ispromember(request.user) if result: - promember=1 + promember = 1 if request.user == w.user.user: - mayedit=1 + mayedit = 1 return render(request, 'map_view.html', - {'mapscript':mapscript, - 'workout':w, - 'rower':r, - 'breadcrumbs':breadcrumbs, - 'active':'nav-workouts', - 'mapdiv':mapdiv, - 'mayedit':mayedit, + {'mapscript': mapscript, + 'workout': w, + 'rower': r, + 'breadcrumbs': breadcrumbs, + 'active': 'nav-workouts', + 'mapdiv': mapdiv, + 'mayedit': mayedit, }) - - # Image upload -@permission_required('workout.change_workout',fn=get_workout_by_opaqueid,raise_exception=True) -def workout_uploadimage_view(request,id): # pragma: no cover +@permission_required('workout.change_workout', fn=get_workout_by_opaqueid, raise_exception=True) +def workout_uploadimage_view(request, id): # pragma: no cover is_ajax = request.META.get('HTTP_X_REQUESTED_WITH') == 'XMLHttpRequest' - r = getrower(request.user) w = get_workoutuser(id, request) breadcrumbs = [ - { - 'url':'/rowers/list-workouts/', - 'name':'Workouts' - }, - { - 'url':get_workout_default_page(request,id), - 'name': w.name - }, - { - 'url':reverse('workout_uploadimage_view',kwargs={'id':id}), - 'name': 'Upload Image' - } - - ] - + { + 'url': '/rowers/list-workouts/', + 'name': 'Workouts' + }, + { + 'url': get_workout_default_page(request, id), + 'name': w.name + }, + { + 'url': reverse('workout_uploadimage_view', kwargs={'id': id}), + 'name': 'Upload Image' + } + ] images = GraphImage.objects.filter(workout=w) - - if images.count() >= 6: # pragma: no cover + if images.count() >= 6: # pragma: no cover message = "You have reached the maximum number of static images for this workout" - messages.error(request,message) + messages.error(request, message) url = reverse(r.defaultlandingpage, - kwargs = { - 'id':id, - }) + kwargs={ + 'id': id, + }) return HttpResponseRedirect(url) - if request.method == 'POST': - form = ImageForm(request.POST,request.FILES) + form = ImageForm(request.POST, request.FILES) if form.is_valid(): f = form.cleaned_data['file'] if f is not None: - filename,path_and_filename = handle_uploaded_image(f) + filename, path_and_filename = handle_uploaded_image(f) try: - width,height = Image.open(path_and_filename).size + width, height = Image.open(path_and_filename).size except: message = "Not a valid image" - messages.error(request,message) + messages.error(request, message) os.remove(path_and_filename) url = reverse('workout_uploadimage_view', - kwargs = {'id':id}) + kwargs={'id': id}) if is_ajax: - return JSONResponse({'result':0,'url':0}) + return JSONResponse({'result': 0, 'url': 0}) else: return HttpResponseRedirect(url) i = GraphImage(workout=w, creationdatetime=timezone.now(), - filename = path_and_filename, - width=width,height=height) + filename=path_and_filename, + width=width, height=height) i.save() url = reverse(r.defaultlandingpage, - kwargs = {'id':id}) + kwargs={'id': id}) if is_ajax: - return JSONResponse({'result':1,'url':url}) + return JSONResponse({'result': 1, 'url': url}) else: return HttpResponseRedirect(url) else: - messages.error(request,'Something went wrong - no file attached') + messages.error( + request, 'Something went wrong - no file attached') url = reverse('workout_uploadimage_view', - kwargs = {'id':id}) + kwargs={'id': id}) if is_ajax: - return JSONResponse({'result':0,'url':0}) + return JSONResponse({'result': 0, 'url': 0}) else: return HttpResponseRedirect(url) else: @@ -4629,69 +4551,63 @@ def workout_uploadimage_view(request,id): # pragma: no cover else: if not is_ajax: form = ImageForm() - return render(request,'image_form.html', - {'form':form, - 'rower':r, - 'active':'nav-workouts', - 'breadcrumbs':breadcrumbs, - 'teams':get_my_teams(request.user), + return render(request, 'image_form.html', + {'form': form, + 'rower': r, + 'active': 'nav-workouts', + 'breadcrumbs': breadcrumbs, + 'teams': get_my_teams(request.user), 'workout': w, }) else: - return {'result':0} - + return {'result': 0} # Generic chart creation -@permission_required('workout.change_workout',fn=get_workout_by_opaqueid,raise_exception=True) -def workout_add_chart_view(request,id,plotnr=1): +@permission_required('workout.change_workout', fn=get_workout_by_opaqueid, raise_exception=True) +def workout_add_chart_view(request, id, plotnr=1): w = get_workoutuser(id, request) r = getrower(request.user) plotnr = int(plotnr) - f1 = w.csvfilename[6:-4] timestr = strftime("%Y%m%d-%H%M%S") imagename = f1+timestr+'.png' u = w.user.user r = getrower(u) title = w.name - res,jobid = uploads.make_plot( - r,w,f1,w.csvfilename,'timeplot',title,plotnr=plotnr, + res, jobid = uploads.make_plot( + r, w, f1, w.csvfilename, 'timeplot', title, plotnr=plotnr, imagename=imagename ) - if res == 0: # pragma: no cover - messages.error(request,jobid) + if res == 0: # pragma: no cover + messages.error(request, jobid) else: try: - request.session['async_tasks'] += [(jobid,'make_plot')] + request.session['async_tasks'] += [(jobid, 'make_plot')] except KeyError: - request.session['async_tasks'] = [(jobid,'make_plot')] + request.session['async_tasks'] = [(jobid, 'make_plot')] - - url = reverse(r.defaultlandingpage,kwargs={'id':encoder.encode_hex(w.id)}) + url = reverse(r.defaultlandingpage, kwargs={ + 'id': encoder.encode_hex(w.id)}) return HttpResponseRedirect(url) - - - @login_required -@permission_required('workout.change_workout',fn=get_workout_by_opaqueid,raise_exception=True) -def workout_toggle_ranking(request,id=0): +@permission_required('workout.change_workout', fn=get_workout_by_opaqueid, raise_exception=True) +def workout_toggle_ranking(request, id=0): is_ajax = request.META.get('HTTP_X_REQUESTED_WITH') == 'XMLHttpRequest' - - row = get_workout_by_opaqueid(request,id) + row = get_workout_by_opaqueid(request, id) row.rankingpiece = not row.rankingpiece row.save() - if is_ajax: # pragma: no cover - response = JSONResponse({'result':row.rankingpiece}, + if is_ajax: # pragma: no cover + response = JSONResponse({'result': row.rankingpiece}, content_type='application/json') return response @@ -4702,38 +4618,42 @@ def workout_toggle_ranking(request,id=0): return response # simple POST API for files on local (e.g. in mailbox) + + @csrf_exempt def workout_upload_api(request): stravaid = '' - if request.method != 'POST': # pragma: no cover - message = {'status':'false','message':'this view cannot be accessed through GET'} - return JSONResponse(status=403,data=message) + if request.method != 'POST': # pragma: no cover + message = {'status': 'false', + 'message': 'this view cannot be accessed through GET'} + return JSONResponse(status=403, data=message) # test if JSON try: json_data = json.loads(request.body) secret = json_data['secret'] post_data = json_data - except (KeyError,JSONDecodeError): + except (KeyError, JSONDecodeError): q = request.POST - post_data = {k: q.getlist(k) if len(q.getlist(k))>1 else v for k, v in q.items()} - + post_data = {k: q.getlist(k) if len( + q.getlist(k)) > 1 else v for k, v in q.items()} # only allow local host hostt = request.get_host().split(':') - if hostt[0] not in ['localhost','127.0.0.1','dev.rowsandall.com','rowsandall.com']: - message = {'status':'false','message':'permission denied for host '+hostt[0]} - return JSONResponse(status=403,data=message) + if hostt[0] not in ['localhost', '127.0.0.1', 'dev.rowsandall.com', 'rowsandall.com']: + message = {'status': 'false', + 'message': 'permission denied for host '+hostt[0]} + return JSONResponse(status=403, data=message) # check credentials here try: secret = post_data['secret'] except KeyError: - message = {'status': 'false', 'message':'missing credentials'} - return JSONResponse(status=400,data=message) + message = {'status': 'false', 'message': 'missing credentials'} + return JSONResponse(status=400, data=message) if secret != settings.UPLOAD_SERVICE_SECRET: - message = {'status':'false','message':'invalid credentials'} - return JSONResponse(status=403,data=message) + message = {'status': 'false', 'message': 'invalid credentials'} + return JSONResponse(status=403, data=message) form = DocumentsForm(post_data) optionsform = TeamUploadOptionsForm(post_data) @@ -4742,36 +4662,36 @@ def workout_upload_api(request): try: fstr = post_data['file'] nn, ext = os.path.splitext(fstr) - if ext== '.gz': # pragma: no cover + if ext == '.gz': # pragma: no cover nn, ext2 = os.path.splitext(nn) ext = ext2+ext f1 = uuid.uuid4().hex[:10]+'-'+time.strftime("%Y%m%d-%H%M%S")+ext f2 = 'media/'+f1 - copyfile(fstr,f2) + copyfile(fstr, f2) except KeyError: - message = {'status':'false','message':'no filename given'} - return JSONResponse(status=400,data=message) + message = {'status': 'false', 'message': 'no filename given'} + return JSONResponse(status=400, data=message) except FileNotFoundError: - message = {'status':'false','message':'could not find file'} - return JSONResponse(status=400,data=message) + message = {'status': 'false', 'message': 'could not find file'} + return JSONResponse(status=400, data=message) # sync related IDs - stravaid = post_data.get('stravaid','') - c2id = post_data.get('c2id','') - nkid = post_data.get('nkid','') - rp3id = post_data.get('rp3id','') - garminid = post_data.get('garminid',0) + stravaid = post_data.get('stravaid', '') + c2id = post_data.get('c2id', '') + nkid = post_data.get('nkid', '') + rp3id = post_data.get('rp3id', '') + garminid = post_data.get('garminid', 0) - startdatetime = post_data.get('startdatetime','') - oarlockfirmware = post_data.get('oarlockfirmware',None) - inboard = post_data.get('inboard',None) - oarlength = post_data.get('oarlength',None) - useImpeller = post_data.get('useImpeller',False) + startdatetime = post_data.get('startdatetime', '') + oarlockfirmware = post_data.get('oarlockfirmware', None) + inboard = post_data.get('inboard', None) + oarlength = post_data.get('oarlength', None) + useImpeller = post_data.get('useImpeller', False) - totalDistance = post_data.get('totalDistance',None) - elapsedTime = post_data.get('elapsedTime',None) - summary = post_data.get('summary',None) - timezone = post_data.get('timezone',None) + totalDistance = post_data.get('totalDistance', None) + elapsedTime = post_data.get('elapsedTime', None) + summary = post_data.get('summary', None) + timezone = post_data.get('timezone', None) s = 'Posting c2id {c2id} to Rowsandall. Startdatetime {startdatetime}, time zone {timezone}'.format( c2id=c2id, @@ -4779,7 +4699,7 @@ def workout_upload_api(request): timezone=timezone, ) - dologging('debuglog.log',s) + dologging('debuglog.log', s) r = None if form.is_valid(): @@ -4790,18 +4710,17 @@ def workout_upload_api(request): rpe = form.cleaned_data['rpe'] try: rpe = int(rpe) - except ValueError: # pragma: no cover + except ValueError: # pragma: no cover rpe = 0 - except KeyError: # pragma: no cover + except KeyError: # pragma: no cover rpe = -1 if rowerform.is_valid(): u = rowerform.cleaned_data['user'] r = getrower(u) - if 'useremail' in post_data: us = User.objects.filter(email=post_data['useremail']) - if len(us): # pragma: no cover + if len(us): # pragma: no cover u = us[0] r = getrower(u) else: @@ -4812,16 +4731,14 @@ def workout_upload_api(request): r = rwr break if r is not None and r.emailalternatives is not None: - if post_data['useremail'] not in r.emailalternatives: # pragma: no cover - message = {'status':'false','message':'could not find user'} - return JSONResponse(status=400,data=message) - - - if r is None: # pragma: no cover - message = {'status':'false','message':'invalid user'} - return JSONResponse(status=400,data=message) - + if post_data['useremail'] not in r.emailalternatives: # pragma: no cover + message = {'status': 'false', + 'message': 'could not find user'} + return JSONResponse(status=400, data=message) + if r is None: # pragma: no cover + message = {'status': 'false', 'message': 'invalid user'} + return JSONResponse(status=400, data=message) notes = form.cleaned_data['notes'] if optionsform.is_valid(): @@ -4835,19 +4752,19 @@ def workout_upload_api(request): makeprivate = optionsform.cleaned_data['makeprivate'] else: # pragma: no cover message = optionsform.errors - return JSONResponse(status=400,data=message) + return JSONResponse(status=400, data=message) - if r is None: # pragma: no cover - message = {'status':'false','message':'something went wrong'} - return JSONResponse(status=400,data=message) + if r is None: # pragma: no cover + message = {'status': 'false', 'message': 'something went wrong'} + return JSONResponse(status=400, data=message) id, message, f2 = dataprep.new_workout_from_file( - r,f2, + r, f2, workouttype=workouttype, workoutsource=None, boattype=boattype, makeprivate=makeprivate, - title = t, + title=t, rpe=rpe, notes=notes, uploadoptions=post_data, @@ -4858,97 +4775,103 @@ def workout_upload_api(request): impeller=useImpeller, ) - if id == 0: # pragma: no cover + if id == 0: # pragma: no cover if message is not None: - message = {'status':'false','message':'unable to process file: '+message} + message = {'status': 'false', + 'message': 'unable to process file: '+message} else: - message = {'status': 'false', 'message': 'unable to process file'} + message = {'status': 'false', + 'message': 'unable to process file'} - return JSONResponse(status=400,data=message) - if id == -1: # pragma: no cover - message = {'status': 'true', 'message':message} - return JSONResponse(status=200,data=message) + return JSONResponse(status=400, data=message) + if id == -1: # pragma: no cover + message = {'status': 'true', 'message': message} + return JSONResponse(status=200, data=message) w = Workout.objects.get(id=id) - if timezone is not None: # pragma: no cover + if timezone is not None: # pragma: no cover - w.startdatetime = w.startdatetime.astimezone(pytz.timezone(timezone)) + w.startdatetime = w.startdatetime.astimezone( + pytz.timezone(timezone)) w.workoutdate = w.startdatetime.strftime('%Y-%m-%d') w.starttime = w.startdatetime.strftime('%H:%M:%S') w.timezone = timezone - dologging('debuglog.log','Start Date Time set to {s}'.format(s=w.startdatetime)) - dologging('debuglog.log','Workout Date set to {s}'.format(s=w.workoutdate)) - dologging('debuglog.log','Time zone set to {zone}'.format(zone=w.timezone)) + dologging('debuglog.log', 'Start Date Time set to {s}'.format( + s=w.startdatetime)) + dologging('debuglog.log', + 'Workout Date set to {s}'.format(s=w.workoutdate)) + dologging('debuglog.log', + 'Time zone set to {zone}'.format(zone=w.timezone)) w.save() - if make_plot: # pragma: no cover - res, jobid = uploads.make_plot(r,w,f1,f2,plottype,t) - elif r.staticchartonupload != 'None': # pragma: no cover + if make_plot: # pragma: no cover + res, jobid = uploads.make_plot(r, w, f1, f2, plottype, t) + elif r.staticchartonupload != 'None': # pragma: no cover plottype = r.staticchartonupload - res, jobid = uploads.make_plot(r,w,f1,f2,plottype,t) + res, jobid = uploads.make_plot(r, w, f1, f2, plottype, t) - if inboard: # pragma: no cover + if inboard: # pragma: no cover w.inboard = inboard w.save() - if oarlength: # pragma: no cover + if oarlength: # pragma: no cover w.oarlength = oarlength w.save() - if totalDistance: # pragma: no cover + if totalDistance: # pragma: no cover w.distance = totalDistance w.save() - if elapsedTime: # pragma: no cover + if elapsedTime: # pragma: no cover w.duration = totaltime_sec_to_string(elapsedTime) - if summary: # pragma: no cover + if summary: # pragma: no cover w.summary = summary w.save() - uploads.do_sync(w,post_data,quick=True) - + uploads.do_sync(w, post_data, quick=True) else: # pragma: no cover # form invalid if fstr: os.remove(fstr) message = form.errors - return JSONResponse(status=400,data=message) + return JSONResponse(status=400, data=message) - message = {'status': 'true','id':w.id} + message = {'status': 'true', 'id': w.id} statuscode = 200 if fstr: try: os.remove(fstr) - except FileNotFoundError: # pragma: no cover - message = {'status': 'true', 'id':w.id,'message': 'Error deleting temporary file'} + except FileNotFoundError: # pragma: no cover + message = {'status': 'true', 'id': w.id, + 'message': 'Error deleting temporary file'} statuscode = 500 - if r.getemailnotifications and not r.emailbounced: # pragma: no cover + if r.getemailnotifications and not r.emailbounced: # pragma: no cover link = settings.SITE_URL+reverse( - r.defaultlandingpage, - kwargs = { - 'id':encoder.encode_hex(w.id), + r.defaultlandingpage, + kwargs={ + 'id': encoder.encode_hex(w.id), } ) email_sent = send_confirm(r.user, t, link, '') - return JSONResponse(status=statuscode,data=message) + return JSONResponse(status=statuscode, data=message) # This is the main view for processing uploaded files @login_required() def workout_upload_view(request, uploadoptions={ - 'makeprivate':False, - 'make_plot':False, - 'upload_to_C2':False, - 'plottype':'timeplot', - 'landingpage':'workout_edit_view', + 'makeprivate': False, + 'make_plot': False, + 'upload_to_C2': False, + 'plottype': 'timeplot', + 'landingpage': 'workout_edit_view', }, docformoptions={ - 'workouttype':'rower', + 'workouttype': 'rower', }, raceid=0): @@ -4957,26 +4880,26 @@ def workout_upload_view(request, is_ajax = False r = getrower(request.user) - if r.rowerplan == 'freecoach': # pragma: no cover + if r.rowerplan == 'freecoach': # pragma: no cover url = reverse('team_workout_upload_view') return HttpResponseRedirect(url) breadcrumbs = [ { - 'url':'/rowers/list-workouts/', - 'name':'Workouts' + 'url': '/rowers/list-workouts/', + 'name': 'Workouts' }, { 'url': reverse('workout_upload_view'), 'name': 'Upload' - } - ] + } + ] if 'uploadoptions' in request.session: uploadoptions = request.session['uploadoptions'] try: defaultlandingpage = uploadoptions['landingpage'] - except KeyError: # pragma: no cover + except KeyError: # pragma: no cover uploadoptions['landingpage'] = r.defaultlandingpage defaultlandingpage = r.defaultlandingpage else: @@ -4987,49 +4910,48 @@ def workout_upload_view(request, else: request.session['docformoptions'] = docformoptions - makeprivate = uploadoptions.get('makeprivate',False) - make_plot = uploadoptions.get('make_plot',False) - workouttype = uploadoptions.get('WorkoutType','rower') - boattype = docformoptions.get('boattype','1x') - + makeprivate = uploadoptions.get('makeprivate', False) + make_plot = uploadoptions.get('make_plot', False) + workouttype = uploadoptions.get('WorkoutType', 'rower') + boattype = docformoptions.get('boattype', '1x') try: rpe = docformoptions['rpe'] - try: # pragma: no cover + try: # pragma: no cover rpe = int(rpe) except ValueError: # pragma: no cover rpe = 0 - if not rpe: # pragma: no cover + if not rpe: # pragma: no cover rpe = -1 except KeyError: rpe = -1 - notes = docformoptions.get('notes','') - workoutsource = uploadoptions.get('workoutsource',None) - plottype = uploadoptions.get('plottype','timeplot') - landingpage = uploadoptions.get('landingpage',r.defaultlandingpage) - upload_to_c2 = uploadoptions.get('upload_to_C2',False) - upload_to_strava = uploadoptions.get('upload_to_Strava',False) - upload_to_st = uploadoptions.get('upload_to_SportTracks',False) - upload_to_tp = uploadoptions.get('upload_to_TrainingPeaks',False) + notes = docformoptions.get('notes', '') + workoutsource = uploadoptions.get('workoutsource', None) + plottype = uploadoptions.get('plottype', 'timeplot') + landingpage = uploadoptions.get('landingpage', r.defaultlandingpage) + upload_to_c2 = uploadoptions.get('upload_to_C2', False) + upload_to_strava = uploadoptions.get('upload_to_Strava', False) + upload_to_st = uploadoptions.get('upload_to_SportTracks', False) + upload_to_tp = uploadoptions.get('upload_to_TrainingPeaks', False) response = {} if request.method == 'POST': - form = DocumentsForm(request.POST,request.FILES) - optionsform = UploadOptionsForm(request.POST,request=request) + form = DocumentsForm(request.POST, request.FILES) + optionsform = UploadOptionsForm(request.POST, request=request) if form.is_valid(): -# f = request.FILES['file'] + # f = request.FILES['file'] f = form.cleaned_data['file'] if f is not None: res = handle_uploaded_file(f) - else: # pragma: no cover + else: # pragma: no cover messages.error(request, "Something went wrong - no file attached") url = reverse('workout_upload_view') if is_ajax: - return JSONResponse({'result':0,'url':0}) + return JSONResponse({'result': 0, 'url': 0}) else: return HttpResponseRedirect(url) @@ -5042,13 +4964,13 @@ def workout_upload_view(request, rpe = int(rpe) except ValueError: rpe = 0 - except KeyError: # pragma: no cover + except KeyError: # pragma: no cover rpe = -1 request.session['docformoptions'] = { - 'workouttype':workouttype, + 'workouttype': workouttype, 'boattype': boattype, - } + } notes = form.cleaned_data['notes'] offline = form.cleaned_data['offline'] @@ -5071,34 +4993,33 @@ def workout_upload_view(request, registrationid = 0 uploadoptions = { - 'makeprivate':makeprivate, - 'make_plot':make_plot, - 'plottype':plottype, - 'upload_to_C2':upload_to_c2, - 'upload_to_Strava':upload_to_strava, - 'upload_to_SportTracks':upload_to_st, - 'upload_to_TrainingPeaks':upload_to_tp, - 'landingpage':landingpage, + 'makeprivate': makeprivate, + 'make_plot': make_plot, + 'plottype': plottype, + 'upload_to_C2': upload_to_c2, + 'upload_to_Strava': upload_to_strava, + 'upload_to_SportTracks': upload_to_st, + 'upload_to_TrainingPeaks': upload_to_tp, + 'landingpage': landingpage, 'boattype': boattype, - 'rpe':rpe, + 'rpe': rpe, 'workouttype': workouttype, } - request.session['uploadoptions'] = uploadoptions - f1 = res[0] # file name - f2 = res[1] # file name incl media directory + f1 = res[0] # file name + f2 = res[1] # file name incl media directory if not offline: - id,message,f2 = dataprep.new_workout_from_file( - r,f2, + id, message, f2 = dataprep.new_workout_from_file( + r, f2, workouttype=workouttype, workoutsource=workoutsource, boattype=boattype, rpe=rpe, makeprivate=makeprivate, - title = t, + title=t, notes=notes, ) else: @@ -5110,127 +5031,126 @@ def workout_upload_view(request, url = settings.UPLOAD_SERVICE_URL job = myqueue(queuehigh, - handle_request_post, - url, - uploadoptions - ) + handle_request_post, + url, + uploadoptions + ) messages.info( request, "The file was too large to process in real time. It will be processed in a background process. You will receive an email when it is ready") url = reverse('workout_upload_view') - if is_ajax: # pragma: no cover - return JSONResponse({'result':1,'url':url}) + if is_ajax: # pragma: no cover + return JSONResponse({'result': 1, 'url': url}) else: response = HttpResponseRedirect(url) return response - if not id: # pragma: no cover - messages.error(request,message) + if not id: # pragma: no cover + messages.error(request, message) url = reverse('workout_upload_view') - if is_ajax: # pragma: no cover - return JSONResponse({'result':0,'url':url}) + if is_ajax: # pragma: no cover + return JSONResponse({'result': 0, 'url': url}) else: response = HttpResponseRedirect(url) return response - elif id == -1: # pragma: no cover + elif id == -1: # pragma: no cover message = 'The zip archive will be processed in the background. The files in the archive will only be uploaded without the extra actions. You will receive email when the workouts are ready.' - messages.info(request,message) + messages.info(request, message) url = reverse('workout_upload_view') if is_ajax: - return JSONResponse({'result':1,'url':url}) + return JSONResponse({'result': 1, 'url': url}) else: response = HttpResponseRedirect(url) return response else: - if message: # pragma: no cover - messages.error(request,message) + if message: # pragma: no cover + messages.error(request, message) w = Workout.objects.get(id=id) url = reverse('workout_edit_view', - kwargs = { - 'id':encoder.encode_hex(w.id), + kwargs={ + 'id': encoder.encode_hex(w.id), }) - if is_ajax: # pragma: no cover - response = {'result': 1,'url':url} + if is_ajax: # pragma: no cover + response = {'result': 1, 'url': url} else: response = HttpResponseRedirect(url) - r = getrower(request.user) - if (make_plot): # pragma: no cover - res,jobid = uploads.make_plot(r,w,f1,f2,plottype,t) + if (make_plot): # pragma: no cover + res, jobid = uploads.make_plot(r, w, f1, f2, plottype, t) if res == 0: - messages.error(request,jobid) + messages.error(request, jobid) else: try: - request.session['async_tasks'] += [(jobid,'make_plot')] + request.session['async_tasks'] += [ + (jobid, 'make_plot')] except KeyError: - request.session['async_tasks'] = [(jobid,'make_plot')] + request.session['async_tasks'] = [(jobid, 'make_plot')] elif r.staticchartonupload != None: plottype = r.staticchartonupload - res, jobid = uploads.make_plot(r,w,f1,f2,plottype,t) + res, jobid = uploads.make_plot(r, w, f1, f2, plottype, t) # upload to C2 - if (upload_to_c2): # pragma: no cover + if (upload_to_c2): # pragma: no cover try: - message,id = c2stuff.workout_c2_upload(request.user,w) + message, id = c2stuff.workout_c2_upload(request.user, w) except NoTokenError: id = 0 message = "Something went wrong with the Concept2 sync" - if id>1: - messages.info(request,message) + if id > 1: + messages.info(request, message) else: - messages.error(request,message) + messages.error(request, message) - if (upload_to_strava): # pragma: no cover + if (upload_to_strava): # pragma: no cover try: - message,id = stravastuff.workout_strava_upload( - request.user,w, + message, id = stravastuff.workout_strava_upload( + request.user, w, ) except NoTokenError: id = 0 message = "Please connect to Strava first" - if id>1: - messages.info(request,message) + if id > 1: + messages.info(request, message) else: - messages.error(request,message) + messages.error(request, message) - if (upload_to_st): # pragma: no cover + if (upload_to_st): # pragma: no cover try: - message,id = sporttracksstuff.workout_sporttracks_upload( - request.user,w + message, id = sporttracksstuff.workout_sporttracks_upload( + request.user, w ) except NoTokenError: message = "Please connect to SportTracks first" id = 0 - if id>1: - messages.info(request,message) + if id > 1: + messages.info(request, message) else: - messages.error(request,message) + messages.error(request, message) - - if (upload_to_tp): # pragma: no cover + if (upload_to_tp): # pragma: no cover try: - message,id = tpstuff.workout_tp_upload( - request.user,w - ) + message, id = tpstuff.workout_tp_upload( + request.user, w + ) except NoTokenError: message = "Please connect to TrainingPeaks first" id = 0 - if id>1: - messages.info(request,message) + if id > 1: + messages.info(request, message) else: - messages.error(request,message) + messages.error(request, message) if int(registrationid) < 0: # pragma: no cover race = VirtualRace.objects.get(id=-int(registrationid)) if race.sessiontype == 'race': - result,comments,errors,jobid = add_workout_race( - [w], race,r,doregister=True, + result, comments, errors, jobid = add_workout_race( + [w], race, r, doregister=True, ) if result: messages.info( @@ -5238,13 +5158,13 @@ def workout_upload_view(request, "We have submitted your workout to the race") for c in comments: - messages.info(request,c) + messages.info(request, c) for er in errors: - messages.error(request,er) + messages.error(request, er) elif race.sessiontype == 'indoorrace': - result,comments,errors,jobid = add_workout_indoorrace( - [w],race,r,doregister=True, - ) + result, comments, errors, jobid = add_workout_indoorrace( + [w], race, r, doregister=True, + ) if result: messages.info( @@ -5252,21 +5172,22 @@ def workout_upload_view(request, "We have submitted your workout to the race") for c in comments: - messages.info(request,c) + messages.info(request, c) for er in errors: - messages.error(request,er) - elif race.sessiontype in ['fastest_time','fastest_distance']: - result,comments,errors,jobid = add_workout_fastestrace( - [w], race,r,doregister=True, + messages.error(request, er) + elif race.sessiontype in ['fastest_time', 'fastest_distance']: + result, comments, errors, jobid = add_workout_fastestrace( + [w], race, r, doregister=True, ) if result: - messages.info(request,"We have submitted your workout to the race") + messages.info( + request, "We have submitted your workout to the race") for c in comments: - messages.info(request,c) + messages.info(request, c) for er in errors: - messages.error(request,er) + messages.error(request, er) - if int(registrationid)>0: # pragma: no cover + if int(registrationid) > 0: # pragma: no cover races = VirtualRace.objects.filter( registration_closure__gt=timezone.now() ) @@ -5277,28 +5198,28 @@ def workout_upload_view(request, ) registrations = IndoorVirtualRaceResult.objects.filter( - race__in = races, + race__in=races, id=registrationid, - userid = r.id, + userid=r.id, ) registrations2 = VirtualRaceResult.objects.filter( - race__in = races, + race__in=races, id=registrationid, userid=r.id, ) - if int(registrationid) in [r.id for r in registrations]: # pragma: no cover + if int(registrationid) in [r.id for r in registrations]: # pragma: no cover # indoor race registrations = registrations.filter(id=registrationid) if registrations: race = registrations[0].race if race.sessiontype == 'indoorrace': - result,comments,errors,jobid = add_workout_indoorrace( - [w],race,r,recordid=registrations[0].id - ) - elif race.sessiontype in ['fastest_time','fastest_distance']: - result,comments, errors,jobid = add_workout_fastestrace( - [w],race,r,recordid=registrations[0].id + result, comments, errors, jobid = add_workout_indoorrace( + [w], race, r, recordid=registrations[0].id + ) + elif race.sessiontype in ['fastest_time', 'fastest_distance']: + result, comments, errors, jobid = add_workout_fastestrace( + [w], race, r, recordid=registrations[0].id ) if result: @@ -5307,23 +5228,22 @@ def workout_upload_view(request, "We have submitted your workout to the race") for c in comments: - messages.info(request,c) + messages.info(request, c) for er in errors: - messages.error(request,er) + messages.error(request, er) - - if int(registrationid) in [r.id for r in registrations2]: # pragma: no cover + if int(registrationid) in [r.id for r in registrations2]: # pragma: no cover # race registrations = registrations2.filter(id=registrationid) if registrations: race = registrations[0].race if race.sessiontype == 'race': - result,comments,errors,jobid = add_workout_race( - [w], race,r,recordid=registrations[0].id - ) - elif race.sessiontype in ['fastest_time','fastest_distance']: + result, comments, errors, jobid = add_workout_race( + [w], race, r, recordid=registrations[0].id + ) + elif race.sessiontype in ['fastest_time', 'fastest_distance']: result, comments, errors, jobid = add_workout_fastestrace( - [w],race,r,recordid=registrations[0].id + [w], race, r, recordid=registrations[0].id ) if result: messages.info( @@ -5331,90 +5251,86 @@ def workout_upload_view(request, "We have submitted your workout to the race") for c in comments: - messages.info(request,c) + messages.info(request, c) for er in errors: - messages.error(request,er) + messages.error(request, er) - - - if registrationid != 0: # pragma: no cover + if registrationid != 0: # pragma: no cover try: url = reverse('virtualevent_view', - kwargs = { - 'id':race.id, - }) + kwargs={ + 'id': race.id, + }) except UnboundLocalError: if landingpage != 'workout_upload_view': url = reverse(landingpage, - kwargs = { - 'id':encoder.encode_hex(w.id), - }) - else: # pragma: no cover + kwargs={ + 'id': encoder.encode_hex(w.id), + }) + else: # pragma: no cover url = reverse(landingpage) - elif landingpage != 'workout_upload_view': # pragma: no cover + elif landingpage != 'workout_upload_view': # pragma: no cover url = reverse(landingpage, - kwargs = { - 'id':encoder.encode_hex(w.id), + kwargs={ + 'id': encoder.encode_hex(w.id), }) - else: # pragma: no cover + else: # pragma: no cover url = reverse(landingpage) - if is_ajax: # pragma: no cover - response = {'result':1,'url':url} + if is_ajax: # pragma: no cover + response = {'result': 1, 'url': url} else: response = HttpResponseRedirect(url) else: - if not is_ajax: # pragma: no cover + if not is_ajax: # pragma: no cover response = render(request, 'document_form.html', - {'form':form, - 'teams':get_my_teams(request.user), + {'form': form, + 'teams': get_my_teams(request.user), 'optionsform': optionsform, - }) + }) - - if is_ajax: # pragma: no cover + if is_ajax: # pragma: no cover return JSONResponse(response) else: return response else: if not is_ajax: - if r.c2_auto_export and ispromember(r.user): # pragma: no cover + if r.c2_auto_export and ispromember(r.user): # pragma: no cover uploadoptions['upload_to_C2'] = True - if r.strava_auto_export and ispromember(r.user): # pragma: no cover + if r.strava_auto_export and ispromember(r.user): # pragma: no cover uploadoptions['upload_to_Strava'] = True - if r.sporttracks_auto_export and ispromember(r.user): # pragma: no cover + if r.sporttracks_auto_export and ispromember(r.user): # pragma: no cover uploadoptions['upload_to_SportTracks'] = True - if r.trainingpeaks_auto_export and ispromember(r.user): # pragma: no cover + if r.trainingpeaks_auto_export and ispromember(r.user): # pragma: no cover uploadoptions['upload_to_TrainingPeaks'] = True - form = DocumentsForm(initial=docformoptions) optionsform = UploadOptionsForm(initial=uploadoptions, - request=request,raceid=raceid) + request=request, raceid=raceid) return render(request, 'document_form.html', - {'form':form, - 'active':'nav-workouts', - 'breadcrumbs':breadcrumbs, - 'teams':get_my_teams(request.user), + {'form': form, + 'active': 'nav-workouts', + 'breadcrumbs': breadcrumbs, + 'teams': get_my_teams(request.user), 'optionsform': optionsform, - }) - else: # pragma: no cover - return {'result':0} + }) + else: # pragma: no cover + return {'result': 0} # This is the main view for processing uploaded files -@user_passes_test(iscoachmember,login_url="/rowers/paidplans",redirect_field_name=None, +@user_passes_test(iscoachmember, login_url="/rowers/paidplans", redirect_field_name=None, message="This functionality requires a Coach plan or higher") -def team_workout_upload_view(request,message="", +def team_workout_upload_view(request, message="", successmessage="", uploadoptions={ - 'make_plot':False, - 'plottype':'timeplot', + 'make_plot': False, + 'plottype': 'timeplot', }): if 'uploadoptions' in request.session: @@ -5424,15 +5340,14 @@ def team_workout_upload_view(request,message="", breadcrumbs = [ { - 'url':'/rowers/list-workouts/', - 'name':'Workouts' + 'url': '/rowers/list-workouts/', + 'name': 'Workouts' }, { 'url': reverse('team_workout_upload_view'), 'name': 'Team Upload' - } - ] - + } + ] myteams = Team.objects.filter(manager=request.user) @@ -5441,7 +5356,7 @@ def team_workout_upload_view(request,message="", r = getrower(request.user) if request.method == 'POST': - form = DocumentsForm(request.POST,request.FILES) + form = DocumentsForm(request.POST, request.FILES) optionsform = TeamUploadOptionsForm(request.POST) rowerform = TeamInviteForm(request.POST) @@ -5451,23 +5366,24 @@ def team_workout_upload_view(request,message="", ).exclude( rowerplan='freecoach' ).distinct() - if r.rowerplan == 'freecoach': # pragma: no cover + if r.rowerplan == 'freecoach': # pragma: no cover rowers = rowers.exclude(rowerplan='basic') - rowerform.fields['user'].queryset = User.objects.filter(rower__in=rowers).distinct() + rowerform.fields['user'].queryset = User.objects.filter( + rower__in=rowers).distinct() rowerform.fields['user'].required = True if form.is_valid() and rowerform.is_valid(): - f = request.FILES.get('file',False) + f = request.FILES.get('file', False) if f: res = handle_uploaded_file(f) - else: # pragma: no cover - messages.error(request,'No file attached') + else: # pragma: no cover + messages.error(request, 'No file attached') response = render(request, 'team_document_form.html', - {'form':form, - 'teams':get_my_teams(request.user), - 'optionsform':optionsform, - 'rowerform':rowerform, + {'form': form, + 'teams': get_my_teams(request.user), + 'optionsform': optionsform, + 'rowerform': rowerform, }) return response @@ -5478,17 +5394,17 @@ def team_workout_upload_view(request,message="", if rowerform.is_valid(): u = rowerform.cleaned_data['user'] r = getrower(u) - if not can_add_workout_member(request.user,r): # pragma: no cover + if not can_add_workout_member(request.user, r): # pragma: no cover message = 'Please select a rower' - messages.error(request,message) - messages.info(request,successmessage) + messages.error(request, message) + messages.info(request, successmessage) response = render(request, - 'team_document_form.html', - {'form':form, - 'teams':get_my_teams(request.user), - 'optionsform': optionsform, - 'rowerform': rowerform, - }) + 'team_document_form.html', + {'form': form, + 'teams': get_my_teams(request.user), + 'optionsform': optionsform, + 'rowerform': rowerform, + }) return response @@ -5501,36 +5417,34 @@ def team_workout_upload_view(request,message="", plottype = optionsform.cleaned_data['plottype'] uploadoptions = { - 'makeprivate':False, - 'make_plot':make_plot, - 'plottype':plottype, - 'upload_to_C2':False, + 'makeprivate': False, + 'make_plot': make_plot, + 'plottype': plottype, + 'upload_to_C2': False, } - request.session['uploadoptions'] = uploadoptions - f1 = res[0] # file name - f2 = res[1] # file name incl media directory - + f1 = res[0] # file name + f2 = res[1] # file name incl media directory if not offline: - id,message,f2 = dataprep.new_workout_from_file( - r,f2, + id, message, f2 = dataprep.new_workout_from_file( + r, f2, workouttype=workouttype, boattype=boattype, makeprivate=False, - title = t, + title=t, notes='' ) - else: # pragma: no cover + else: # pragma: no cover job = myqueue( queuehigh, handle_zip_file, r.user.email, t, f2, - emailbounced = r.emailbounced + emailbounced=r.emailbounced ) messages.info( @@ -5538,27 +5452,25 @@ def team_workout_upload_view(request,message="", "The file was too large to process in real time. It will be processed in a background process. The user will receive an email when it is ready" ) - url = reverse('team_workout_upload_view') response = HttpResponseRedirect(url) return response - - if not id: # pragma: no cover - messages.error(request,message) + if not id: # pragma: no cover + messages.error(request, message) url = reverse('team_workout_upload_view') response = HttpResponseRedirect(url) return response - elif id == -1: # pragma: no cover + elif id == -1: # pragma: no cover message = 'The zip archive will be processed in the background. The files in the archive will only be uploaded without the extra actions. You will receive email when the workouts are ready.' - messages.info(request,message) + messages.info(request, message) url = reverse('team_workout_upload_view') response = HttpResponseRedirect(url) return response else: successmessage = "The workout was added to the user's account" - messages.info(request,successmessage) + messages.info(request, successmessage) url = reverse('team_workout_upload_view') @@ -5566,26 +5478,23 @@ def team_workout_upload_view(request,message="", w = Workout.objects.get(id=id) r = getrower(request.user) - if (make_plot): # pragma: no cover - id,jobid = uploads.make_plot(r,w,f1,f2,plottype,t) + if (make_plot): # pragma: no cover + id, jobid = uploads.make_plot(r, w, f1, f2, plottype, t) elif r.staticchartonupload: plottype = r.staticchartonupload - id,jobid = uploads.make_plot(r,w,f1,f2,plottype,t) - - - + id, jobid = uploads.make_plot(r, w, f1, f2, plottype, t) else: response = render(request, 'team_document_form.html', - {'form':form, - 'teams':get_my_teams(request.user), - 'active': 'nav-workouts', - 'breadcrumbs':breadcrumbs, - 'optionsform': optionsform, - 'rowerform': rowerform, - }) + {'form': form, + 'teams': get_my_teams(request.user), + 'active': 'nav-workouts', + 'breadcrumbs': breadcrumbs, + 'optionsform': optionsform, + 'rowerform': rowerform, + }) return response else: @@ -5599,33 +5508,30 @@ def team_workout_upload_view(request,message="", ).exclude( rowerplan='freecoach' ).distinct() - if r.rowerplan == 'freecoach': # pragma: no cover + if r.rowerplan == 'freecoach': # pragma: no cover rowers = rowers.exclude(rowerplan='basic') - rowerform.fields['user'].queryset = User.objects.filter(rower__in=rowers).distinct() - + rowerform.fields['user'].queryset = User.objects.filter( + rower__in=rowers).distinct() return render(request, 'team_document_form.html', - {'form':form, -# 'teams':get_my_teams(request.user), + {'form': form, + # 'teams':get_my_teams(request.user), 'optionsform': optionsform, 'active': 'nav-workouts', - 'breadcrumbs':breadcrumbs, -# 'rower':r, - 'rowerform':rowerform, - }) - - - + 'breadcrumbs': breadcrumbs, + # 'rower':r, + 'rowerform': rowerform, + }) # A page with all the recent graphs (searchable on workout name) @login_required() -def list_videos(request,userid=0): - r = getrequestrower(request,userid=userid) +def list_videos(request, userid=0): + r = getrequestrower(request, userid=userid) workouts = Workout.objects.filter(user=r).order_by("-date", "-starttime") query = request.GET.get('q') - if query: # pragma: no cover + if query: # pragma: no cover query_list = query.split() if query: query_list = query.split() @@ -5634,77 +5540,77 @@ def list_videos(request,userid=0): (Q(name__icontains=q) for q in query_list)) | reduce(operator.and_, (Q(notes__icontains=q) for q in query_list)) - ) - searchform = SearchForm(initial={'q':query}) + ) + searchform = SearchForm(initial={'q': query}) else: searchform = SearchForm() - g = VideoAnalysis.objects.filter(workout__in=workouts).order_by("-workout__date") + g = VideoAnalysis.objects.filter( + workout__in=workouts).order_by("-workout__date") - - paginator = Paginator(g,8) - page = request.GET.get('page',1) + paginator = Paginator(g, 8) + page = request.GET.get('page', 1) try: g = paginator.page(page) - except EmptyPage: # pragma: no cover + except EmptyPage: # pragma: no cover g = paginator.page(paginator.num_pages) return render(request, 'list_videos.html', {'analyses': g, - 'searchform':searchform, - 'active':'nav-analysis', - 'rower':r, - 'teams':get_my_teams(request.user), + 'searchform': searchform, + 'active': 'nav-analysis', + 'rower': r, + 'teams': get_my_teams(request.user), }) + @login_required() -def graphs_view(request,userid=0): +def graphs_view(request, userid=0): request.session['referer'] = reverse('graphs_view') - r = getrequestrower(request,userid=userid) + r = getrequestrower(request, userid=userid) workouts = Workout.objects.filter(user=r).order_by("-date", "-starttime") query = request.GET.get('q') - if query: # pragma: no cover + if query: # pragma: no cover query_list = query.split() workouts = workouts.filter( reduce(operator.and_, (Q(name__icontains=q) for q in query_list)) | reduce(operator.and_, (Q(notes__icontains=q) for q in query_list)) - ) - searchform = SearchForm(initial={'q':query}) + ) + searchform = SearchForm(initial={'q': query}) else: searchform = SearchForm() - g = GraphImage.objects.filter(workout__in=workouts).order_by("-creationdatetime") + g = GraphImage.objects.filter( + workout__in=workouts).order_by("-creationdatetime") - - paginator = Paginator(g,8) + paginator = Paginator(g, 8) page = request.GET.get('page') try: g = paginator.page(page) except PageNotAnInteger: g = paginator.page(1) - except EmptyPage: # pragma: no cover + except EmptyPage: # pragma: no cover g = paginator.page(paginator.num_pages) return render(request, 'list_graphs.html', - {'graphs': g, - 'searchform':searchform, - 'active':'nav-workouts', - 'teams':get_my_teams(request.user), - 'rower':r, - }) - + {'graphs': g, + 'searchform': searchform, + 'active': 'nav-workouts', + 'teams': get_my_teams(request.user), + 'rower': r, + }) # Show the chart (png image) -def graph_show_view(request,id): +def graph_show_view(request, id): try: g = GraphImage.objects.get(id=id) - try: # pragma: no cover - width,height = Image.open(g.filename).size + try: # pragma: no cover + width, height = Image.open(g.filename).size g.width = width g.height = height g.save() @@ -5716,38 +5622,39 @@ def graph_show_view(request,id): breadcrumbs = [ { - 'url':'/rowers/list-workouts/', - 'name':'Workouts' - }, + 'url': '/rowers/list-workouts/', + 'name': 'Workouts' + }, { - 'url':get_workout_default_page(request,encoder.encode_hex(w.id)), + 'url': get_workout_default_page(request, encoder.encode_hex(w.id)), 'name': w.name - }, + }, { - 'url':reverse('graph_show_view',kwargs={'id':id}), + 'url': reverse('graph_show_view', kwargs={'id': id}), 'name': 'Chart' - } + } - ] + ] + return render(request, 'show_graph.html', + {'graph': g, + 'teams': get_my_teams(request.user), + 'workout': w, + 'breadcrumbs': breadcrumbs, + 'active': 'nav-workouts', + 'rower': r, }) - return render(request,'show_graph.html', - {'graph':g, - 'teams':get_my_teams(request.user), - 'workout':w, - 'breadcrumbs':breadcrumbs, - 'active':'nav-workouts', - 'rower':r,}) - - except GraphImage.DoesNotExist: # pragma: no cover + except GraphImage.DoesNotExist: # pragma: no cover raise Http404("This graph doesn't exist") - except Workout.DoesNotExist: # pragma: no cover + except Workout.DoesNotExist: # pragma: no cover raise Http404("This workout doesn't exist") # Restore original stroke data and summary -@permission_required('workout.change_workout',fn=get_workout_by_opaqueid,raise_exception=True) -def workout_summary_restore_view(request,id,message="",successmessage=""): - row = get_workout_by_opaqueid(request,id) + + +@permission_required('workout.change_workout', fn=get_workout_by_opaqueid, raise_exception=True) +def workout_summary_restore_view(request, id, message="", successmessage=""): + row = get_workout_by_opaqueid(request, id) s = "" # still here - this is a workout we may edit @@ -5757,66 +5664,67 @@ def workout_summary_restore_view(request,id,message="",successmessage=""): powerperc = 100*np.array([r.pw_ut2, r.pw_ut1, r.pw_at, - r.pw_tr,r.pw_an])/r.ftp + r.pw_tr, r.pw_an])/r.ftp ftp = float(r.ftp) if row.workouttype in mytypes.otwtypes: ftp = ftp*(100.-r.otwslack)/100. - rr = rrower(hrmax=r.max,hrut2=r.ut2, - hrut1=r.ut1,hrat=r.at, - hrtr=r.tr,hran=r.an,ftp=ftp, - powerperc=powerperc,powerzones=r.powerzones, + rr = rrower(hrmax=r.max, hrut2=r.ut2, + hrut1=r.ut1, hrat=r.at, + hrtr=r.tr, hran=r.an, ftp=ftp, + powerperc=powerperc, powerzones=r.powerzones, hrzones=r.hrzones) - rowdata = rdata(csvfile=f1,rower=rr) - if rowdata == 0: # pragma: no cover + rowdata = rdata(csvfile=f1, rower=rr) + if rowdata == 0: # pragma: no cover raise Http404("Error: CSV Data File Not Found") rowdata.restoreintervaldata() - rowdata.write_csv(f1,gzip=True) - dataprep.update_strokedata(encoder.decode_hex(id),rowdata.df) + rowdata.write_csv(f1, gzip=True) + dataprep.update_strokedata(encoder.decode_hex(id), rowdata.df) intervalstats = rowdata.allstats() row.summary = intervalstats row.save() # create interactive plot try: - res = interactive_chart(encoder.decode_hex(id),promember=1) + res = interactive_chart(encoder.decode_hex(id), promember=1) script = res[0] div = res[1] - except ValueError: # pragma: no cover + except ValueError: # pragma: no cover pass - - messages.info(request,'Original Interval Data Restored') + messages.info(request, 'Original Interval Data Restored') url = reverse('workout_summary_edit_view', kwargs={ - 'id':encoder.encode_hex(row.id), + 'id': encoder.encode_hex(row.id), } - ) + ) return HttpResponseRedirect(url) # Split a workout -@permission_required('workout.change_workout',fn=get_workout_by_opaqueid,raise_exception=True) -@user_passes_test(ispromember,login_url="/rowers/paidplans", + + +@permission_required('workout.change_workout', fn=get_workout_by_opaqueid, raise_exception=True) +@user_passes_test(ispromember, login_url="/rowers/paidplans", message="This functionality requires a Pro plan or higher. If you are already a Pro user, please log in to access this functionality", redirect_field_name=None) -def workout_split_view(request,id=0): - row = get_workout_by_opaqueid(request,id) +def workout_split_view(request, id=0): + row = get_workout_by_opaqueid(request, id) r = row.user breadcrumbs = [ { - 'url':'/rowers/list-workouts/', - 'name':'Workouts' + 'url': '/rowers/list-workouts/', + 'name': 'Workouts' }, { - 'url':get_workout_default_page(request,id), + 'url': get_workout_default_page(request, id), 'name': row.name }, { - 'url':reverse('workout_split_view', - kwargs={'id':id}), + 'url': reverse('workout_split_view', + kwargs={'id': id}), 'name': 'Chart' } @@ -5831,43 +5739,43 @@ def workout_split_view(request,id=0): splitsecond += splittime.microsecond/1.e6 splitmode = form.cleaned_data['splitmode'] try: - ids,mesgs = dataprep.split_workout( - r,row,splitsecond,splitmode + ids, mesgs = dataprep.split_workout( + r, row, splitsecond, splitmode ) for message in mesgs: - messages.info(request,message) - except IndexError: # pragma: no cover - messages.error(request,"Something went wrong in Split") - + messages.info(request, message) + except IndexError: # pragma: no cover + messages.error(request, "Something went wrong in Split") if request.user == r: # pragma: no cover url = reverse('workouts_view') else: - mgrids = [team.id for team in Team.objects.filter(manager=request.user)] + mgrids = [team.id for team in Team.objects.filter( + manager=request.user)] rwrids = [team.id for team in r.team.all()] teamids = list(set(mgrids) & set(rwrids)) - if len(teamids) > 0: # pragma: no cover + if len(teamids) > 0: # pragma: no cover teamid = teamids[0] url = reverse('workouts_view', kwargs={ - 'teamid':int(teamid), + 'teamid': int(teamid), } - ) + ) else: url = reverse('workouts_view') rowname = row.name try: - if isinstance(rowname,unicode): # pragma: no cover + if isinstance(rowname, unicode): # pragma: no cover rowname = rowname.encode('utf8') - elif isinstance(rowname, str): # pragma: no cover + elif isinstance(rowname, str): # pragma: no cover rowname = rowname.decode('utf8') except: pass - qdict = {'q':rowname} - url+='?'+urllib.parse.urlencode(qdict) + qdict = {'q': rowname} + url += '?'+urllib.parse.urlencode(qdict) return HttpResponseRedirect(url) @@ -5875,31 +5783,31 @@ def workout_split_view(request,id=0): # create interactive plot try: - res = interactive_chart(encoder.decode_hex(id),promember=1) + res = interactive_chart(encoder.decode_hex(id), promember=1) script = res[0] div = res[1] except ValueError: # pragma: no cover pass return render(request, 'splitworkout.html', - {'form':form, - 'rower':r, - 'breadcrumbs':breadcrumbs, - 'active':'nav-workouts', - 'workout':row, - 'thediv':script, - 'thescript':div, - }) + {'form': form, + 'rower': r, + 'breadcrumbs': breadcrumbs, + 'active': 'nav-workouts', + 'workout': row, + 'thediv': script, + 'thescript': div, + }) # Fuse two workouts -@user_passes_test(ispromember,login_url="/rowers/paidplans",message="This functionality requires a Pro plan or higher. If you are already a Pro user, please log in to access this functionality",redirect_field_name=None) -def workout_fusion_view(request,id1=0,id2=1): +@user_passes_test(ispromember, login_url="/rowers/paidplans", message="This functionality requires a Pro plan or higher. If you are already a Pro user, please log in to access this functionality", redirect_field_name=None) +def workout_fusion_view(request, id1=0, id2=1): try: id1 = encoder.decode_hex(id1) id2 = encoder.decode_hex(id2) - except: # pragma: no cover + except: # pragma: no cover pass r = getrower(request.user) @@ -5908,39 +5816,38 @@ def workout_fusion_view(request,id1=0,id2=1): w1 = Workout.objects.get(id=id1) w2 = Workout.objects.get(id=id2) r = w1.user - if (is_workout_user(request.user,w1)==False) or \ - (is_workout_user(request.user,w2)==False): # pragma: no cover + if (is_workout_user(request.user, w1) == False) or \ + (is_workout_user(request.user, w2) == False): # pragma: no cover raise PermissionDenied("You are not allowed to use these workouts") - except Workout.DoesNotExist: # pragma: no cover + except Workout.DoesNotExist: # pragma: no cover raise Http404("One of the workouts doesn't exist") if request.method == 'POST': - form = FusionMetricChoiceForm(request.POST,instance=w2) + form = FusionMetricChoiceForm(request.POST, instance=w2) if form.is_valid(): cd = form.cleaned_data columns = cd['columns'] timeoffset = cd['offset'] posneg = cd['posneg'] - if posneg == 'neg': # pragma: no cover + if posneg == 'neg': # pragma: no cover timeoffset = -timeoffset # Create DataFrame - df,forceunit = dataprep.datafusion(id1,id2,columns,timeoffset) + df, forceunit = dataprep.datafusion(id1, id2, columns, timeoffset) - - idnew,message = dataprep.new_workout_from_df(r,df, - title='Fused data', - parent=w1, - forceunit=forceunit) - if message != None: # pragma: no cover - messages.error(request,message) + idnew, message = dataprep.new_workout_from_df(r, df, + title='Fused data', + parent=w1, + forceunit=forceunit) + if message != None: # pragma: no cover + messages.error(request, message) else: successmessage = 'Data fused' - messages.info(request,message) + messages.info(request, message) url = reverse('workout_edit_view', kwargs={ - 'id':encoder.encode_hex(idnew), + 'id': encoder.encode_hex(idnew), }) return HttpResponseRedirect(url) @@ -5949,57 +5856,59 @@ def workout_fusion_view(request,id1=0,id2=1): breadcrumbs = [ { - 'url':'/rowers/list-workouts/', - 'name':'Workouts' + 'url': '/rowers/list-workouts/', + 'name': 'Workouts' }, { - 'url':get_workout_default_page(request,encoder.encode_hex(w1.id)), + 'url': get_workout_default_page(request, encoder.encode_hex(w1.id)), 'name': encoder.encode_hex(w1.id) }, { - 'url':reverse('workout_fusion_list', - kwargs={'id':encoder.encode_hex(id1)}), + 'url': reverse('workout_fusion_list', + kwargs={'id': encoder.encode_hex(id1)}), 'name': 'Sensor Fusion' }, { - 'url':reverse('workout_fusion_view', - kwargs={ - 'id1':encoder.encode_hex(id1), - 'id2':encoder.encode_hex(id2) - }), + 'url': reverse('workout_fusion_view', + kwargs={ + 'id1': encoder.encode_hex(id1), + 'id2': encoder.encode_hex(id2) + }), 'name': encoder.encode_hex(w2.id) } ] return render(request, 'fusion.html', - {'form':form, - 'teams':get_my_teams(request.user), - 'workout':w1, - 'rower':r, - 'breadcrumbs':breadcrumbs, - 'active':'nav-workouts', - 'workout1':w1, - 'workout2':w2, - }) + {'form': form, + 'teams': get_my_teams(request.user), + 'workout': w1, + 'rower': r, + 'breadcrumbs': breadcrumbs, + 'active': 'nav-workouts', + 'workout1': w1, + 'workout2': w2, + }) # See attached courses + + @login_required() -@permission_required('workout.change_workout',fn=get_workout_by_opaqueid, raise_exception=True) +@permission_required('workout.change_workout', fn=get_workout_by_opaqueid, raise_exception=True) def workout_course_view(request, id): - row = get_workout_by_opaqueid(request,id) + row = get_workout_by_opaqueid(request, id) r = getrower(request.user) breadcrumbs = [ { - 'url':'/rowers/list-workouts/', - 'name':'Workouts' + 'url': '/rowers/list-workouts/', + 'name': 'Workouts' }, { - 'url':get_workout_default_page(request,encoder.encode_hex(row.id)), + 'url': get_workout_default_page(request, encoder.encode_hex(row.id)), 'name': row.name }, { - 'url':reverse('workout_course_view',kwargs={'id':id}), + 'url': reverse('workout_course_view', kwargs={'id': id}), 'name': 'Measured Courses' } @@ -6007,16 +5916,14 @@ def workout_course_view(request, id): courses = [] courseselectform = CourseSelectForm() - has_latlon,lat_mean,lon_mean = dataprep.workout_has_latlon(row.id) + has_latlon, lat_mean, lon_mean = dataprep.workout_has_latlon(row.id) if has_latlon: - courses = getnearestcourses([lat_mean,lon_mean],GeoCourse.objects.all(),whatisnear=25, + courses = getnearestcourses([lat_mean, lon_mean], GeoCourse.objects.all(), whatisnear=25, strict=True) courseselectform = CourseSelectForm(choices=courses) - - if request.method == 'POST': - courseselectform = CourseSelectForm(request.POST,choices=courses) + courseselectform = CourseSelectForm(request.POST, choices=courses) if courseselectform.is_valid(): course = courseselectform.cleaned_data['course'] # get or create a record @@ -6024,23 +5931,23 @@ def workout_course_view(request, id): userid=r.id, course=course, workoutid=row.id - ) + ) if records: record = records[0] - else: # pragma: no cover + else: # pragma: no cover # create record record = VirtualRaceResult( - userid = r.id, - username = r.user.first_name+' '+r.user.last_name, - workoutid = row.id, - weightcategory = r.weightcategory, - adaptiveclass = r.adaptiveclass, - course = course, - distance = course.distance, - boatclass = row.workouttype, - boattype = row.boattype, - sex = r.sex, - age = calculate_age(r.birthdate), + userid=r.id, + username=r.user.first_name+' '+r.user.last_name, + workoutid=row.id, + weightcategory=r.weightcategory, + adaptiveclass=r.adaptiveclass, + course=course, + distance=course.distance, + boatclass=row.workouttype, + boattype=row.boattype, + sex=r.sex, + age=calculate_age(r.birthdate), ) record.save() @@ -6055,52 +5962,54 @@ def workout_course_view(request, id): r.user.first_name, summary=True, successemail=True, - ) + ) try: - request.session['async_tasks'] += [(job.id,'check_race_course')] - except KeyError: # pragma: no cover - request.session['async_tasks'] = [(job.id,'check_race_course')] + request.session['async_tasks'] += [ + (job.id, 'check_race_course')] + except KeyError: # pragma: no cover + request.session['async_tasks'] = [ + (job.id, 'check_race_course')] - messages.info(request,'We are checking your time on the course in the background. You will receive an email when the check is complete. You can check the status here') + messages.info(request, 'We are checking your time on the course in the background. You will receive an email when the check is complete. You can check the status here') # get results records = VirtualRaceResult.objects.filter( course__isnull=False, workoutid=row.id, - coursecompleted=True).order_by("duration","-distance") + coursecompleted=True).order_by("duration", "-distance") return render(request, 'workout_courses.html', - {'workout':row, - 'rower':r, - 'breadcrumbs':breadcrumbs, - 'active':'nav-workouts', - 'teams':get_my_teams(request.user), - 'courses':courses, - 'courseselectform':courseselectform, - 'records':records, + {'workout': row, + 'rower': r, + 'breadcrumbs': breadcrumbs, + 'active': 'nav-workouts', + 'teams': get_my_teams(request.user), + 'courses': courses, + 'courseselectform': courseselectform, + 'records': records, }) # Edit the splits/summary @login_required() -@permission_required('workout.change_workout',fn=get_workout_by_opaqueid,raise_exception=True) -def workout_summary_edit_view(request,id,message="",successmessage="" +@permission_required('workout.change_workout', fn=get_workout_by_opaqueid, raise_exception=True) +def workout_summary_edit_view(request, id, message="", successmessage="" ): - row = get_workout_by_opaqueid(request,id) + row = get_workout_by_opaqueid(request, id) r = getrower(request.user) breadcrumbs = [ { - 'url':'/rowers/list-workouts/', - 'name':'Workouts' + 'url': '/rowers/list-workouts/', + 'name': 'Workouts' }, { - 'url':get_workout_default_page(request,encoder.encode_hex(row.id)), + 'url': get_workout_default_page(request, encoder.encode_hex(row.id)), 'name': row.name }, { - 'url':reverse('workout_summary_edit_view',kwargs={'id':id}), + 'url': reverse('workout_summary_edit_view', kwargs={'id': id}), 'name': 'Edit Intervals' } @@ -6114,24 +6023,24 @@ def workout_summary_edit_view(request,id,message="",successmessage="" powerperc = 100*np.array([r.pw_ut2, r.pw_ut1, r.pw_at, - r.pw_tr,r.pw_an])/r.ftp + r.pw_tr, r.pw_an])/r.ftp ftp = float(r.ftp) if row.workouttype in mytypes.otwtypes: ftp = ftp*(100.-r.otwslack)/100. - rr = rrower(hrmax=r.max,hrut2=r.ut2, - hrut1=r.ut1,hrat=r.at, - hrtr=r.tr,hran=r.an,ftp=ftp, - powerperc=powerperc,powerzones=r.powerzones, + rr = rrower(hrmax=r.max, hrut2=r.ut2, + hrut1=r.ut1, hrat=r.at, + hrtr=r.tr, hran=r.an, ftp=ftp, + powerperc=powerperc, powerzones=r.powerzones, hrzones=r.hrzones) - rowdata = rdata(csvfile=f1,rower=rr) - if rowdata == 0: # pragma: no cover + rowdata = rdata(csvfile=f1, rower=rr) + if rowdata == 0: # pragma: no cover return HttpResponse("Error: CSV Data File Not Found") #intervalstats = rowdata.allstats() intervalstats = row.summary try: - itime,idist,itype = rowdata.intervalstats_values() + itime, idist, itype = rowdata.intervalstats_values() except TypeError: # pragma: no cover return HttpResponse("Error: CSV Data File Not Found") nrintervals = len(idist) @@ -6154,8 +6063,8 @@ def workout_summary_edit_view(request,id,message="",successmessage="" normspm = 0 if tss == -1: - tss,normp = dataprep.workout_rscore(row) - normv,normw = dataprep.workout_normv(row,pp=8.0) + tss, normp = dataprep.workout_rscore(row) + normv, normw = dataprep.workout_normv(row, pp=8.0) work = int(normw) power = int(normp) @@ -6176,12 +6085,12 @@ def workout_summary_edit_view(request,id,message="",successmessage="" try: normw = int(normw) - except ValueError: # pragma: no cover + except ValueError: # pragma: no cover normw = 100 try: normp = int(normp) - except ValueError: # pragma: no cover + except ValueError: # pragma: no cover normp = 100 data = { @@ -6192,7 +6101,7 @@ def workout_summary_edit_view(request,id,message="",successmessage="" 'spm': normspm, 'activeminutesmin': 0, 'activeminutesmax': activeminutesmax, - } + } powerorpace = 'power' @@ -6203,17 +6112,16 @@ def workout_summary_edit_view(request,id,message="",successmessage="" # looking for courses courses = [] courseselectform = CourseSelectForm() - has_latlon,lat_mean,lon_mean = dataprep.workout_has_latlon(row.id) + has_latlon, lat_mean, lon_mean = dataprep.workout_has_latlon(row.id) if has_latlon: - courses = getnearestcourses([lat_mean,lon_mean],GeoCourse.objects.all(),whatisnear=25, + courses = getnearestcourses([lat_mean, lon_mean], GeoCourse.objects.all(), whatisnear=25, strict=True) courseselectform = CourseSelectForm(choices=courses) - powerupdateform = PowerIntervalUpdateForm(initial=data) if request.method == 'POST' and "course" in request.POST: - courseselectform = CourseSelectForm(request.POST,choices=courses) + courseselectform = CourseSelectForm(request.POST, choices=courses) if courseselectform.is_valid(): course = courseselectform.cleaned_data['course'] # get or create a record @@ -6221,23 +6129,23 @@ def workout_summary_edit_view(request,id,message="",successmessage="" userid=r.id, course=course, workoutid=row.id - ) - if records: # pragma: no cover + ) + if records: # pragma: no cover record = records[0] else: # create record record = VirtualRaceResult( - userid = r.id, - username = r.user.first_name+' '+r.user.last_name, - workoutid = row.id, - weightcategory = r.weightcategory, - adaptiveclass = r.adaptiveclass, - course = course, - distance = course.distance, - boatclass = row.workouttype, - boattype = row.boattype, - sex = r.sex, - age = calculate_age(r.birthdate), + userid=r.id, + username=r.user.first_name+' '+r.user.last_name, + workoutid=row.id, + weightcategory=r.weightcategory, + adaptiveclass=r.adaptiveclass, + course=course, + distance=course.distance, + boatclass=row.workouttype, + boattype=row.boattype, + sex=r.sex, + age=calculate_age(r.birthdate), ) record.save() @@ -6252,54 +6160,53 @@ def workout_summary_edit_view(request,id,message="",successmessage="" r.user.first_name, summary=True, successemail=True, - ) + ) try: - request.session['async_tasks'] += [(job.id,'check_race_course')] + request.session['async_tasks'] += [ + (job.id, 'check_race_course')] except KeyError: - request.session['async_tasks'] = [(job.id,'check_race_course')] + request.session['async_tasks'] = [ + (job.id, 'check_race_course')] - messages.info(request,'We are checking your time on the course in the background. You will receive an email when the check is complete. You can check the status here') + messages.info(request, 'We are checking your time on the course in the background. You will receive an email when the check is complete. You can check the status here') vals = None # feeling lucky / ruptures if request.method == 'POST' and "ruptures" in request.POST: df = pd.DataFrame({ - 'spm':rowdata.df[' Cadence (stokes/min)'], - 'power':rowdata.df[' Power (watts)'], - 'v':rowdata.df[' AverageBoatSpeed (m/s)'] + 'spm': rowdata.df[' Cadence (stokes/min)'], + 'power': rowdata.df[' Power (watts)'], + 'v': rowdata.df[' AverageBoatSpeed (m/s)'] }) algo = rpt.Pelt(model="rbf").fit(df.values) result = algo.predict(pen=10) - - df['time'] = rowdata.df['TimeStamp (sec)'].values + df['time'] = rowdata.df['TimeStamp (sec)'].values try: timeprev = int(df['time'].values[0]) timenext = int(df['time'].values[result[0]]) s = '{delta}sec'.format(delta=timenext-timeprev) - except IndexError: # pragma: no cover + except IndexError: # pragma: no cover s = '0sec' - for i in range(len(result)-1): timeprev = int(df['time'].values[result[i]-1]) timenext = int(df['time'].values[result[i+1]-1]) - interval = '+{delta}sec'.format(delta=timenext-timeprev) + interval = '+{delta}sec'.format(delta=timenext-timeprev) s += interval try: rowdata.updateinterval_string(s) - except: # pragma: no cover - messages.error(request,"Nope, you were not lucky") + except: # pragma: no cover + messages.error(request, "Nope, you were not lucky") intervalstats = rowdata.allstats() itime, idist, itype = rowdata.intervalstats_values() nrintervals = len(idist) savebutton = 'savestringform' intervalString = s - form = SummaryStringForm(initial={'intervalstring':intervalString}) - + form = SummaryStringForm(initial={'intervalstring': intervalString}) # We have submitted the mini language interpreter if request.method == 'POST' and "intervalstring" in request.POST: @@ -6309,57 +6216,57 @@ def workout_summary_edit_view(request,id,message="",successmessage="" s = cd["intervalstring"] try: rowdata.updateinterval_string(s) - except: # pragma: no cover - messages.error(request,'Parsing error') + except: # pragma: no cover + messages.error(request, 'Parsing error') intervalstats = rowdata.allstats() - itime,idist,itype = rowdata.intervalstats_values() + itime, idist, itype = rowdata.intervalstats_values() nrintervals = len(idist) savebutton = 'savestringform' powerupdateform = PowerIntervalUpdateForm(initial=data) # we are saving the results obtained from the split by power/pace interpreter elif request.method == 'POST' and "savepowerpaceform" in request.POST: - powerorpace = request.POST.get('powerorpace','pace') - value_pace = request.POST.get('value_pace',avpace) - value_power = request.POST.get('value_power',int(normp)) - value_work = request.POST.get('value_work',int(normw)) - value_spm = request.POST.get('value_spm',int(normspm)) - activeminutesmin = request.POST.get('activeminutesmin',0) + powerorpace = request.POST.get('powerorpace', 'pace') + value_pace = request.POST.get('value_pace', avpace) + value_power = request.POST.get('value_power', int(normp)) + value_work = request.POST.get('value_work', int(normw)) + value_spm = request.POST.get('value_spm', int(normspm)) + activeminutesmin = request.POST.get('activeminutesmin', 0) try: activeminutesmax = request.POST['activeminutesmax'] - except: # pragma: no cover + except: # pragma: no cover pass try: activesecondsmin = 60.*float(activeminutesmin) activesecondsmax = 60.*float(activeminutesmax) - except ValueError: # pragma: no cover + except ValueError: # pragma: no cover activesecondsmin = 0 activeminutesmin = 0 activeminutesmax = maxminutes activesecondsmax = rowdata.duration - if abs(rowdata.duration-activesecondsmax) < 60.: # pragma: no cover + if abs(rowdata.duration-activesecondsmax) < 60.: # pragma: no cover activesecondsmax = rowdata.duration if powerorpace == 'power': try: power = int(value_power) - except ValueError: # pragma: no cover + except ValueError: # pragma: no cover int(normp) - elif powerorpace == 'pace': # pragma: no cover + elif powerorpace == 'pace': # pragma: no cover try: pace_secs = float(value_pace) except ValueError: try: - pace_secs = float(value_pace.replace(',','.')) + pace_secs = float(value_pace.replace(',', '.')) except ValueError: - pace_secs = int(500./normv) - elif powerorpace == 'work': # pragma: no cover + pace_secs = int(500./normv) + elif powerorpace == 'work': # pragma: no cover try: work = int(value_work) except ValueError: work = int(normw) - elif powerorpace == 'spm': # pragma: no cover + elif powerorpace == 'spm': # pragma: no cover try: spm = int(value_spm) except ValueError: @@ -6368,56 +6275,56 @@ def workout_summary_edit_view(request,id,message="",successmessage="" if powerorpace == 'power' and power is not None: try: vals, units, typ = rowdata.updateinterval_metric( - ' Power (watts)',power,mode='larger', - debug=False,smoothwindow=15., - activewindow=[activesecondsmin,activesecondsmax], - ) - except: # pragma: no cover - messages.error(request,'Error updating power') - elif powerorpace == 'pace': # pragma: no cover + ' Power (watts)', power, mode='larger', + debug=False, smoothwindow=15., + activewindow=[activesecondsmin, activesecondsmax], + ) + except: # pragma: no cover + messages.error(request, 'Error updating power') + elif powerorpace == 'pace': # pragma: no cover try: velo = 500./pace_secs vals, units, typ = rowdata.updateinterval_metric( - ' AverageBoatSpeed (m/s)',velo,mode='larger', - debug=False,smoothwindow=15., - activewindow=[activesecondsmin,activesecondsmax], - ) + ' AverageBoatSpeed (m/s)', velo, mode='larger', + debug=False, smoothwindow=15., + activewindow=[activesecondsmin, activesecondsmax], + ) except: - messages.error(request,'Error updating pace') - elif powerorpace == 'work': # pragma: no cover + messages.error(request, 'Error updating pace') + elif powerorpace == 'work': # pragma: no cover try: vals, units, typ = rowdata.updateinterval_metric( - 'driveenergy',work,mode='larger', - debug=False,smoothwindow=15., - activewindow=[activesecondsmin,activesecondsmax], - ) + 'driveenergy', work, mode='larger', + debug=False, smoothwindow=15., + activewindow=[activesecondsmin, activesecondsmax], + ) except: - messages.error(request,'Error updating Work per Stroke') - elif powerorpace == 'spm': # pragma: no cover + messages.error(request, 'Error updating Work per Stroke') + elif powerorpace == 'spm': # pragma: no cover try: vals, units, typ = rowdata.updateinterval_metric( - ' Cadence (stokes/min)',spm,mode='larger', - debug=False,smoothwindow=2., - activewindow=[activesecondsmin,activesecondsmax],) + ' Cadence (stokes/min)', spm, mode='larger', + debug=False, smoothwindow=2., + activewindow=[activesecondsmin, activesecondsmax],) except: - messages.error(request,'Error updating SPM') + messages.error(request, 'Error updating SPM') intervalString = '' if vals is not None: intervalString = intervals_to_string(vals, units, typ) intervalstats = rowdata.allstats() - itime,idist,itype = rowdata.intervalstats_values() + itime, idist, itype = rowdata.intervalstats_values() nrintervals = len(idist) row.summary = intervalstats row.save() - rowdata.write_csv(f1,gzip=True) + rowdata.write_csv(f1, gzip=True) - dataprep.update_strokedata(encoder.decode_hex(id),rowdata.df) + dataprep.update_strokedata(encoder.decode_hex(id), rowdata.df) - messages.info(request,"Updated interval data saved") + messages.info(request, "Updated interval data saved") data = { 'power': power, 'pace': datetime.timedelta(seconds=int(pace_secs)), @@ -6427,44 +6334,43 @@ def workout_summary_edit_view(request,id,message="",successmessage="" 'activeminutesmin': activeminutesmin, 'activeminutesmax': activeminutesmax, } - form = SummaryStringForm(initial={'intervalstring':intervalString}) + form = SummaryStringForm(initial={'intervalstring': intervalString}) powerupdateform = PowerIntervalUpdateForm(initial=data) savebutton = 'savepowerpaceform' formvalues = { 'powerorpace': powerorpace, 'value_power': power, 'value_pace': pace_secs, - 'value_work':work, - } - + 'value_work': work, + } # we are saving the results obtained from the mini language interpreter elif request.method == 'POST' and "savestringform" in request.POST: s = request.POST["savestringform"] try: rowdata.updateinterval_string(s) - except (ParseException,err): # pragma: no cover - messages.error(request,'Parsing error in column '+str(err.col)) + except (ParseException, err): # pragma: no cover + messages.error(request, 'Parsing error in column '+str(err.col)) intervalstats = rowdata.allstats() - itime,idist,itype = rowdata.intervalstats_values() + itime, idist, itype = rowdata.intervalstats_values() nrintervals = len(idist) row.summary = intervalstats #intervalstats = rowdata.allstats() if s: try: row.notes = u'{n} \n {s}'.format( - n = row.notes, - s = s + n=row.notes, + s=s ) - except TypeError: # pragma: no cover + except TypeError: # pragma: no cover pass row.save() - rowdata.write_csv(f1,gzip=True) - dataprep.update_strokedata(encoder.decode_hex(id),rowdata.df) - messages.info(request,"Updated interval data saved") - data = {'intervalstring':s} + rowdata.write_csv(f1, gzip=True) + dataprep.update_strokedata(encoder.decode_hex(id), rowdata.df) + messages.info(request, "Updated interval data saved") + data = {'intervalstring': s} form = SummaryStringForm(initial=data) powerupdateform = PowerIntervalUpdateForm(initial={ 'power': int(normp), @@ -6477,7 +6383,6 @@ def workout_summary_edit_view(request,id,message="",successmessage="" }) savebutton = 'savestringform' - # we are saving the results obtained from the power update form elif request.method == 'POST' and 'selector' in request.POST: powerupdateform = PowerIntervalUpdateForm(request.POST) @@ -6492,53 +6397,54 @@ def workout_summary_edit_view(request,id,message="",successmessage="" activeminutesmax = cd['activeminutesmax'] activesecondsmin = 60.*activeminutesmin activesecondsmax = 60.*activeminutesmax - if abs(rowdata.duration-activesecondsmax) < 60.: # pragma: no cover + if abs(rowdata.duration-activesecondsmax) < 60.: # pragma: no cover activesecondsmax = rowdata.duration try: pace_secs = pace.seconds+pace.microseconds/1.0e6 - except AttributeError: # pragma: no cover + except AttributeError: # pragma: no cover pace_secs = 120. if powerorpace == 'power' and power is not None: - vals, units, typ = rowdata.updateinterval_metric(' Power (watts)',power,mode='larger', - debug=False,smoothwindow=15, - activewindow=[activesecondsmin,activesecondsmax], - ) + vals, units, typ = rowdata.updateinterval_metric(' Power (watts)', power, mode='larger', + debug=False, smoothwindow=15, + activewindow=[ + activesecondsmin, activesecondsmax], + ) - elif powerorpace == 'pace': # pragma: no cover + elif powerorpace == 'pace': # pragma: no cover try: velo = 500./pace_secs - vals, units, typ = rowdata.updateinterval_metric(' AverageBoatSpeed (m/s)',velo,mode='larger', - debug=False,smoothwindow=15, - activewindow=[activesecondsmin,activesecondsmax], - ) + vals, units, typ = rowdata.updateinterval_metric(' AverageBoatSpeed (m/s)', velo, mode='larger', + debug=False, smoothwindow=15, + activewindow=[ + activesecondsmin, activesecondsmax], + ) except: - messages.error(request,'Error updating pace') - elif powerorpace == 'work': # pragma: no cover + messages.error(request, 'Error updating pace') + elif powerorpace == 'work': # pragma: no cover try: vals, units, typ = rowdata.updateinterval_metric( - 'driveenergy',work,mode='larger', - debug=False,smoothwindow=15., - activewindow=[activesecondsmin,activesecondsmax],) + 'driveenergy', work, mode='larger', + debug=False, smoothwindow=15., + activewindow=[activesecondsmin, activesecondsmax],) except: - messages.error(request,'Error updating Work per Stroke') - elif powerorpace == 'spm': # pragma: no cover + messages.error(request, 'Error updating Work per Stroke') + elif powerorpace == 'spm': # pragma: no cover try: - vals, units, typ = rowdata.updateinterval_metric(' Cadence (stokes/min)',spm,mode='larger', - debug=False,smoothwindow=2., - activewindow=[activesecondsmin,activesecondsmax], - ) + vals, units, typ = rowdata.updateinterval_metric(' Cadence (stokes/min)', spm, mode='larger', + debug=False, smoothwindow=2., + activewindow=[ + activesecondsmin, activesecondsmax], + ) except: - messages.error(request,'Error updating SPM') - + messages.error(request, 'Error updating SPM') intervalString = '' if vals is not None: intervalString = intervals_to_string(vals, units, typ) - intervalstats = rowdata.allstats() - itime,idist,itype = rowdata.intervalstats_values() + itime, idist, itype = rowdata.intervalstats_values() nrintervals = len(idist) savebutton = 'savepowerpaceform' formvalues = { @@ -6549,53 +6455,53 @@ def workout_summary_edit_view(request,id,message="",successmessage="" 'value_spm': spm, 'activeminutesmin': activeminutesmin, 'activeminutesmax': activeminutesmax, - } + } powerupdateform = PowerIntervalUpdateForm(initial=cd) - form = SummaryStringForm(initial={'intervalstring':intervalString}) - - - + form = SummaryStringForm( + initial={'intervalstring': intervalString}) # create interactive plot try: intervaldata = { - 'itime':itime, - 'idist':idist, - 'itype':itype, + 'itime': itime, + 'idist': idist, + 'itype': itype, 'selector': powerorpace, 'normp': normp, 'normv': normv, - } - res = interactive_chart(encoder.decode_hex(id),promember=1,intervaldata=intervaldata) + } + res = interactive_chart(encoder.decode_hex( + id), promember=1, intervaldata=intervaldata) script = res[0] div = res[1] - except ValueError: # pragma: no cover + except ValueError: # pragma: no cover script = '' div = '' # render page return render(request, 'summary_edit.html', - {'form':form, - 'activeminutesmax':activeminutesmax, - 'activeminutesmin':activeminutesmin, + {'form': form, + 'activeminutesmax': activeminutesmax, + 'activeminutesmin': activeminutesmin, 'maxminutes': maxminutes, - 'powerupdateform':powerupdateform, - 'workout':row, - 'rower':r, - 'breadcrumbs':breadcrumbs, - 'active':'nav-workouts', - 'teams':get_my_teams(request.user), - 'intervalstats':intervalstats, - 'nrintervals':nrintervals, - 'interactiveplot':script, - 'the_div':div, - 'intervalstring':s, - 'savebutton':savebutton, - 'formvalues':formvalues, - 'courses':courses, - 'courseselectform':courseselectform, + 'powerupdateform': powerupdateform, + 'workout': row, + 'rower': r, + 'breadcrumbs': breadcrumbs, + 'active': 'nav-workouts', + 'teams': get_my_teams(request.user), + 'intervalstats': intervalstats, + 'nrintervals': nrintervals, + 'interactiveplot': script, + 'the_div': div, + 'intervalstring': s, + 'savebutton': savebutton, + 'formvalues': formvalues, + 'courses': courses, + 'courseselectform': courseselectform, }) + class VideoDelete(DeleteView): login_required = True model = VideoAnalysis @@ -6607,24 +6513,24 @@ class VideoDelete(DeleteView): breadcrumbs = [ { - 'url':'/rowers/list-workouts/', - 'name':'Workouts' - }, + 'url': '/rowers/list-workouts/', + 'name': 'Workouts' + }, { - 'url':get_workout_default_page( + 'url': get_workout_default_page( self.request, encoder.encode_hex(self.object.workout.id)), 'name': self.object.workout.name - }, + }, { - 'url':reverse('workout_video_view',kwargs={'id':encoder.encode_hex(self.object.id)}), + 'url': reverse('workout_video_view', kwargs={'id': encoder.encode_hex(self.object.id)}), 'name': 'Video Analysis' - }, - { 'url':reverse('video_delete',kwargs={'pk':str(self.object.pk)}), - 'name': 'Delete' - } + }, + {'url': reverse('video_delete', kwargs={'pk': str(self.object.pk)}), + 'name': 'Delete' + } - ] + ] context['active'] = 'nav-workouts' context['rower'] = getrower(self.request.user) @@ -6632,23 +6538,24 @@ class VideoDelete(DeleteView): return context - def get_success_url(self): w = self.object.workout try: w = Workout.objects.get(id=w.id) - except Workout.DoesNotExist: # pragma: no cover + except Workout.DoesNotExist: # pragma: no cover return reverse('workouts_view') - return reverse('workout_edit_view',kwargs={'id':encoder.encode_hex(w.id)}) + return reverse('workout_edit_view', kwargs={'id': encoder.encode_hex(w.id)}) def get_object(self, *args, **kwargs): obj = super(VideoDelete, self).get_object(*args, **kwargs) - if not is_coach_user(self.request.user,obj.workout.user.user): # pragma: no cover - raise PermissionDenied('You are not allowed to delete this analysis') + if not is_coach_user(self.request.user, obj.workout.user.user): # pragma: no cover + raise PermissionDenied( + 'You are not allowed to delete this analysis') return obj + class GraphDelete(DeleteView): login_required = True model = GraphImage @@ -6660,24 +6567,24 @@ class GraphDelete(DeleteView): breadcrumbs = [ { - 'url':'/rowers/list-workouts/', - 'name':'Workouts' - }, + 'url': '/rowers/list-workouts/', + 'name': 'Workouts' + }, { - 'url':get_workout_default_page( + 'url': get_workout_default_page( self.request, encoder.encode_hex(self.object.workout.id)), 'name': self.object.workout.name - }, + }, { - 'url':reverse('graph_show_view',kwargs={'id':self.object.pk}), + 'url': reverse('graph_show_view', kwargs={'id': self.object.pk}), 'name': 'Chart' - }, - { 'url':reverse('graph_delete',kwargs={'pk':str(self.object.pk)}), - 'name': 'Delete' - } + }, + {'url': reverse('graph_delete', kwargs={'pk': str(self.object.pk)}), + 'name': 'Delete' + } - ] + ] context['active'] = 'nav-workouts' context['rower'] = getrower(self.request.user) @@ -6685,26 +6592,25 @@ class GraphDelete(DeleteView): return context - def get_success_url(self): w = self.object.workout try: w = Workout.objects.get(id=w.id) - except Workout.DoesNotExist: # pragma: no cover + except Workout.DoesNotExist: # pragma: no cover return reverse('workouts_view') - return reverse('workout_edit_view',kwargs={'id':encoder.encode_hex(w.id)}) + return reverse('workout_edit_view', kwargs={'id': encoder.encode_hex(w.id)}) def get_object(self, *args, **kwargs): obj = super(GraphDelete, self).get_object(*args, **kwargs) - if not is_workout_user(self.request.user,obj.workout): # pragma: no cover + if not is_workout_user(self.request.user, obj.workout): # pragma: no cover raise PermissionDenied('You are not allowed to delete this chart') return obj -class WorkoutDelete(PermissionRequiredMixin,DeleteView): +class WorkoutDelete(PermissionRequiredMixin, DeleteView): login_required = True model = Workout template_name = 'workout_delete_confirm.html' @@ -6716,28 +6622,29 @@ class WorkoutDelete(PermissionRequiredMixin,DeleteView): breadcrumbs = [ { - 'url':'/rowers/list-workouts/', - 'name':'Workouts' - }, + 'url': '/rowers/list-workouts/', + 'name': 'Workouts' + }, { - 'url':get_workout_default_page(self.request,encoder.encode_hex(self.object.id)), + 'url': get_workout_default_page(self.request, encoder.encode_hex(self.object.id)), 'name': encoder.encode_hex(self.object.id) - }, - { 'url':reverse('workout_delete',kwargs={'pk':str(self.object.pk)}), - 'name': 'Delete' - } + }, + {'url': reverse('workout_delete', kwargs={'pk': str(self.object.pk)}), + 'name': 'Delete' + } - ] + ] - mayedit=0 - promember=0 + mayedit = 0 + promember = 0 if not self.request.user.is_anonymous: r = getrower(self.request.user) - result = self.request.user.is_authenticated and ispromember(self.request.user) + result = self.request.user.is_authenticated and ispromember( + self.request.user) if result: - promember=1 + promember = 1 if self.request.user == self.object.user.user: - mayedit=1 + mayedit = 1 context['active'] = 'nav-workouts' context['rower'] = getrower(self.request.user) @@ -6748,7 +6655,6 @@ class WorkoutDelete(PermissionRequiredMixin,DeleteView): return context - def get_success_url(self): return reverse('workouts_view') @@ -6756,13 +6662,12 @@ class WorkoutDelete(PermissionRequiredMixin,DeleteView): workout_pk = self.kwargs['pk'] try: obj = Workout.objects.get(pk=workout_pk) - except (ValueError,Workout.DoesNotExist): + except (ValueError, Workout.DoesNotExist): workout_pk = encoder.decode_hex(workout_pk) try: obj = Workout.objects.get(pk=workout_pk) - except Workout.DoesNotExist: # pragma: no cover + except Workout.DoesNotExist: # pragma: no cover raise Http404("One of the workouts doesn't exist") # obj = super(WorkoutDelete, self).get_object(*args, **kwargs) - return obj diff --git a/rowers/weather.py b/rowers/weather.py index 65d93954..6974c762 100644 --- a/rowers/weather.py +++ b/rowers/weather.py @@ -1,16 +1,11 @@ -from __future__ import absolute_import -from __future__ import division -from __future__ import print_function -from __future__ import unicode_literals -from __future__ import unicode_literals, absolute_import import requests from requests.exceptions import ConnectionError import json -from lxml import objectify,etree +from lxml import objectify, etree import xml.etree.ElementTree as ET import time from datetime import datetime -from rowingdata import rowingdata,geo_distance +from rowingdata import rowingdata, geo_distance import arrow import pandas as pd from rowers.models import Rower, Workout @@ -18,33 +13,39 @@ from rowers.models import Rower, Workout from rowsandall_app.settings import FORECAST_IO_KEY # Find closest airport -def get_airport_code(lat,lon): + + +def get_airport_code(lat, lon): metardata = pd.read_csv('rowers/data/metarlatlon.csv') deltasq = (lat-metardata.lat)**2+(lon-metardata.lon)**2 a = metardata[deltasq == deltasq.min()] airport_code = a.iloc[0]['icao'] newlat = a.iloc[0]['lat'] newlon = a.iloc[0]['lon'] - distance = geo_distance(lat,lon,newlat,newlon) - return airport_code,newlat,newlon,distance + distance = geo_distance(lat, lon, newlat, newlon) + return airport_code, newlat, newlon, distance # Get weather data from the DarkSky API -def get_weather_data(long,lat,unixtime): + + +def get_weather_data(long, lat, unixtime): url = "https://api.darksky.net/forecast/"+FORECAST_IO_KEY+"/" url += str(long)+","+str(lat)+","+str(unixtime) try: s = requests.get(url) - except ConnectionError: # pragma: no cover + except ConnectionError: # pragma: no cover return 0 if s.ok: return s.json() - else: # pragma: no cover + else: # pragma: no cover return 0 # Get Metar data -def get_metar_data(airportcode,unixtime): + + +def get_metar_data(airportcode, unixtime): timestamp = arrow.get(unixtime).isoformat() @@ -56,17 +57,16 @@ def get_metar_data(airportcode,unixtime): try: s = requests.get(url) - except: # pragma: no cover + except: # pragma: no cover message = 'Failed to download METAR data' - return [0,0,message,'',''] + return [0, 0, message, '', ''] - - if s.ok: # pragma: no cover + if s.ok: # pragma: no cover try: doc = etree.fromstring(s.content) except AttributeError: message = 'METAR data content error' - return [0,0,message,'',0] + return [0, 0, message, '', 0] lengte = len(doc.xpath('data/METAR/station_id')) idnr = int(lengte/2) try: @@ -78,8 +78,7 @@ def get_metar_data(airportcode,unixtime): rawtext = doc.xpath('data/METAR/raw_text')[idnr].text except IndexError: message = 'Failed to download METAR data' - return [0,0,message,'',timestamp] - + return [0, 0, message, '', timestamp] windbearing = float(wind_dir) wind_knots = float(wind_speed) @@ -93,22 +92,21 @@ def get_metar_data(airportcode,unixtime): message = 'Summary for your location at '+timestamp+': ' message += 'Temperature '+temp_c+'C/'+temp_f+'F' message += '. Wind: '+str(wind_ms)+' m/s ('+str(wind_knots)+' kt)' - message +='. Wind Bearing: '+str(windbearing)+' degrees' + message += '. Wind Bearing: '+str(windbearing)+' degrees' # message +='\n'+rawtext - return [wind_ms,windbearing,message,rawtext,timestamp] + return [wind_ms, windbearing, message, rawtext, timestamp] - - message = 'Failed to download METAR data' # pragma: no cover - return [0,0,message,'',timestamp] # pragma: no cover + message = 'Failed to download METAR data' # pragma: no cover + return [0, 0, message, '', timestamp] # pragma: no cover # Get wind data (and translate from knots to m/s) -def get_wind_data(lat,long,unixtime): - data = get_weather_data(lat,long,unixtime) +def get_wind_data(lat, long, unixtime): + data = get_weather_data(lat, long, unixtime) summary = '' temperature = 20 - if data: # pragma: no cover + if data: # pragma: no cover try: # we are getting wind in mph windspeed = data['currently']['windSpeed']*0.44704 @@ -117,13 +115,11 @@ def get_wind_data(lat,long,unixtime): windspeed = 0 windbearing = 0 - try: airports = data['flags']['madis-stations'] except KeyError: airports = ['unknown'] - try: temperature = data['currently']['temperature'] # Temp is given in Fahrenheit, so convert to Celsius for Europeans @@ -133,7 +129,6 @@ def get_wind_data(lat,long,unixtime): temperature = 'unknown' temperaturec = 'unknown' - try: summary = data['currently']['summary'] except KeyError: @@ -157,8 +152,8 @@ def get_wind_data(lat,long,unixtime): message = 'Summary for your location at '+timestamp+': '+summary message += '. Temperature '+str(temperature)+'F/'+str(temperaturec)+'C' - if data: # pragma: no cover - message += '. Wind: '+str(windspeed)+' m/s. Wind Bearing: '+str(windbearing)+' degrees' + if data: # pragma: no cover + message += '. Wind: ' + \ + str(windspeed)+' m/s. Wind Bearing: '+str(windbearing)+' degrees' - - return [windspeed,windbearing,message,airports,timestamp] + return [windspeed, windbearing, message, airports, timestamp]