Private
Public Access
1
0
This commit is contained in:
Sander Roosendaal
2019-02-25 08:05:20 +01:00
64 changed files with 4181 additions and 4057 deletions
+1
View File
@@ -26,6 +26,7 @@ conftest.py
# temporary test files
/rowers/tests/testdata/temp
/rowers/tests/testdata/testdata.csv.gz
rowers/tests/testdata/testdata.csv.gz
/rowers/tests/testdata/testdata.tcx
+4 -4
View File
@@ -1,4 +1,4 @@
# -*- coding: utf-8 -*-
# -*- Coding: utf-8 -*-
from __future__ import unicode_literals
from django.db import models
@@ -44,7 +44,7 @@ class Member(models.Model):
verbose_name_plural = _("members")
ordering = ['user']
user = models.OneToOneField(User)
user = models.OneToOneField(User,on_delete=models.CASCADE)
contributions = models.BooleanField(default=True)
status = models.CharField(default="member",
choices=statuses,
@@ -150,8 +150,8 @@ class Outing(models.Model):
("completed", _("completed")),
("race", _("race")),
)
boat = models.ForeignKey(Boat)
rower = models.ForeignKey(Member)
boat = models.ForeignKey(Boat,on_delete=models.CASCADE)
rower = models.ForeignKey(Member,on_delete=models.CASCADE)
race = models.ManyToManyField(Races,blank=True)
otherrowers = models.ManyToManyField(Member,related_name="otherrowers")
status = models.CharField(_("Status"),default="active",
+1 -1
View File
@@ -1,4 +1,4 @@
from celery import Celery,app
from .celery import Celery,app
import os
import time
import gc
+3 -3
View File
@@ -1,5 +1,5 @@
from __future__ import absolute_import
from .tasks import app as celery_app
from __future__ import absolute_import, unicode_literals
from rowers.celery import app as celery_app
__all__ = ('celery_app',)
+3 -2
View File
@@ -1,3 +1,4 @@
from __future__ import unicode_literals, absolute_import
# The interactions with the Concept2 logbook API
# All C2 related functions should be defined here
# (There is still some stuff defined directly in views.py. Need to
@@ -6,7 +7,7 @@
from rowers.imports import *
import datetime
from requests import Request, Session
import mytypes
import rowers.mytypes as mytypes
from rowers.mytypes import otwtypes
from iso8601 import ParseError
@@ -19,7 +20,7 @@ import django_rq
queue = django_rq.get_queue('default')
queuelow = django_rq.get_queue('low')
queuehigh = django_rq.get_queue('low')
from utils import myqueue
from rowers.utils import myqueue
oauth_data = {
'client_id': C2_CLIENT_ID,
+3 -2
View File
@@ -1,3 +1,4 @@
from __future__ import unicode_literals, absolute_import
# All the Courses related methods
# Python
@@ -28,7 +29,7 @@ import pandas as pd
import numpy as np
from timezonefinder import TimezoneFinder
import dataprep
import rowers.dataprep as dataprep
from rowers.utils import geo_distance
ns = {'opengis': 'http://www.opengis.net/kml/2.2'}
@@ -47,7 +48,7 @@ from rowers.models import (
polygon_to_path,coordinate_in_path
)
from utils import geo_distance
from rowers.courseutils import coursetime_paths, coursetime_first,time_in_path
+15 -14
View File
@@ -1,5 +1,6 @@
# All the data preparation, data cleaning and data mangling should
# be defined here
from __future__ import unicode_literals, absolute_import
from rowers.models import Workout, StrokeData,Team
import pytz
@@ -55,7 +56,7 @@ import pandas as pd
import numpy as np
import itertools
import math
from tasks import (
from rowers.tasks import (
handle_sendemail_unrecognized, handle_sendemail_breakthrough,
handle_sendemail_hard, handle_updatecp,handle_updateergcp,
handle_calctrimp,
@@ -65,9 +66,9 @@ from django.conf import settings
from sqlalchemy import create_engine
import sqlalchemy as sa
import sys
import utils
import datautils
from utils import lbstoN,myqueue,is_ranking_piece,wavg
import rowers.utils as utils
import rowers.datautils as datautils
from rowers.utils import lbstoN,myqueue,is_ranking_piece,wavg
from timezonefinder import TimezoneFinder
@@ -638,7 +639,7 @@ def deletecpdata_sql(rower_id,table='cpdata'):
try:
result = conn.execute(query)
except:
print "Database locked"
print("Database locked")
conn.close()
engine.dispose()
@@ -855,7 +856,7 @@ def create_row_df(r,distance,duration,startdatetime,workouttype='rower',
return (id, message)
from utils import totaltime_sec_to_string
from rowers.utils import totaltime_sec_to_string
# Processes painsled CSV file to database
def save_workout_database(f2, r, dosmooth=True, workouttype='rower',
@@ -1564,7 +1565,7 @@ def compare_data(id):
res = conn.execute(query)
l2 = res.fetchall()[0][0]
except:
print "Database Locked"
print("Database Locked")
conn.close()
engine.dispose()
lfile = l1
@@ -1583,13 +1584,13 @@ def repair_data(verbose=False):
test, ldb, lfile = compare_data(w.id)
if not test:
if verbose:
print w.id, lfile, ldb
print(w.id, lfile, ldb)
try:
rowdata = rdata(w.csvfilename)
if rowdata and len(rowdata.df):
update_strokedata(w.id, rowdata.df)
except IOError, AttributeError:
except (IOError, AttributeError):
pass
if lfile == 0:
@@ -1618,10 +1619,10 @@ def repair_data(verbose=False):
def rdata(file, rower=rrower()):
try:
res = rrdata(csvfile=file, rower=rower)
except IOError, IndexError:
except (IOError, IndexError):
try:
res = rrdata(csvfile=file + '.gz', rower=rower)
except IOError, IndexError:
except (IOError, IndexError):
res = rrdata()
except:
res = rrdata()
@@ -1640,7 +1641,7 @@ def delete_strokedata(id):
try:
result = conn.execute(query)
except:
print "Database Locked"
print("Database Locked")
conn.close()
engine.dispose()
@@ -1792,7 +1793,7 @@ def prepmultipledata(ids, verbose=False):
for id in res:
rowdata, row = getrowdata(id=id)
if verbose:
print id
print(id)
if rowdata and len(rowdata.df):
data = dataprep(rowdata.df, id=id, bands=True,
barchart=True, otwpower=True)
@@ -2029,7 +2030,7 @@ def fix_newtons(id=0, limit=3000):
peakforce = rowdata['peakforce']
if peakforce.mean() > limit:
w = Workout.objects.get(id=id)
print "fixing ", id
print("fixing ", id)
rowdata = rdata(w.csvfilename)
if rowdata and len(rowdata.df):
update_strokedata(w.id, rowdata.df)
+13 -13
View File
@@ -18,7 +18,7 @@ import sqlalchemy as sa
from rowsandall_app.settings import DATABASES
from rowsandall_app.settings_dev import DATABASES as DEV_DATABASES
from utils import lbstoN
from rowers.utils import lbstoN
try:
@@ -133,7 +133,7 @@ def rdata(file,rower=rrower()):
return res
from utils import totaltime_sec_to_string
from rowers.utils import totaltime_sec_to_string
# Creates C2 stroke data
@@ -635,14 +635,14 @@ def delete_strokedata(id,debug=False):
try:
result = conn.execute(query)
except:
print "Database Locked"
print("Database Locked")
conn.close()
engine.dispose()
def update_strokedata(id,df,debug=False):
delete_strokedata(id,debug=debug)
if debug:
print "updating ",id
print("updating ",id)
rowdata = dataprep(df,id=id,bands=True,barchart=True,otwpower=True,
debug=debug)
@@ -676,11 +676,11 @@ def update_empower(id, inboard, oarlength, boattype, df, f1, debug=False):
if success:
delete_strokedata(id,debug=debug)
if debug:
print "updated ",id
print "correction ",corr_factor
print("updated ",id)
print("correction ",corr_factor)
else:
if debug:
print "not updated ",id
print("not updated ",id)
rowdata = dataprep(df,id=id,bands=True,barchart=True,otwpower=True,
@@ -784,8 +784,8 @@ def read_cols_df_sql(ids,columns,debug=False):
def read_df_sql(id,debug=False):
if debug:
engine = create_engine(database_url_debug, echo=False)
print "read_df",id
print database_url_debug
print("read_df",id)
print(database_url_debug)
else:
engine = create_engine(database_url, echo=False)
@@ -826,7 +826,7 @@ def deletecpdata_sql(rower_id,table='cpdata',debug=False):
try:
result = conn.execute(query)
except:
print "Database locked"
print("Database locked")
conn.close()
engine.dispose()
@@ -846,7 +846,7 @@ def delete_agegroup_db(age,sex,weightcategory,debug=False):
try:
result = conn.execute(query)
except:
print "Database locked"
print("Database locked")
conn.close()
engine.dispose()
@@ -956,11 +956,11 @@ def dataprep(rowdatadf,id=0,bands=True,barchart=True,otwpower=True,
if rowdatadf.empty:
if debug:
print "empty"
print("empty")
return 0
if debug:
print "dataprep",id
print("dataprep",id)
rowdatadf.set_index([range(len(rowdatadf))],inplace=True)
t = rowdatadf.ix[:,'TimeStamp (sec)']
+9 -7
View File
@@ -1,3 +1,4 @@
from __future__ import unicode_literals, absolute_import
from django import forms
from django.contrib.admin.widgets import FilteredSelectMultiple
from rowers.models import (
@@ -9,15 +10,16 @@ from rowers.rows import validate_file_extension,must_be_csv,validate_image_exten
from django.contrib.auth.forms import UserCreationForm
from django.contrib.auth.models import User
from django.contrib.admin.widgets import AdminDateWidget
from django.forms.extras.widgets import SelectDateWidget
from django.forms.widgets import SelectDateWidget
#from django.forms.extras.widgets import SelectDateWidget
from django.utils import timezone,translation
from django.forms import ModelForm, Select
import dataprep
import mytypes
import rowers.dataprep as dataprep
import rowers.mytypes as mytypes
import datetime
from django.forms import formset_factory
from utils import landingpages
from metrics import axes
from rowers.utils import landingpages
from rowers.metrics import axes
class FlexibleDecimalField(forms.DecimalField):
@@ -162,7 +164,7 @@ class DocumentsForm(forms.Form):
super(DocumentsForm, self).__init__(*args, **kwargs)
# self.fields['offline'].widget = HiddenInput()
from utils import (
from rowers.utils import (
workflowleftpanel,workflowmiddlepanel,
defaultleft,defaultmiddle
)
@@ -824,7 +826,7 @@ formaxlabelsmultiflex.pop('distance')
formaxlabelsmultiflex['workoutid'] = 'Workout'
parchoicesmultiflex = list(sorted(formaxlabelsmultiflex.items(), key = lambda x:x[1]))
from utils import palettes
from rowers.utils import palettes
palettechoices = tuple((p,p) for p in palettes.keys())
+4 -4
View File
@@ -1,3 +1,4 @@
from __future__ import unicode_literals, absolute_import
# All the functionality to connect to SportTracks
# Python
@@ -16,7 +17,7 @@ from dateutil import parser
import time
from time import strftime
import dataprep
import rowers.dataprep as dataprep
import math
from math import sin,cos,atan2,sqrt
import os,sys
@@ -38,7 +39,7 @@ from django.contrib.auth.decorators import login_required
from rowingdata import rowingdata, make_cumvalues
import pandas as pd
from rowers.models import Rower,Workout,checkworkoutuser
from rowers import mytypes
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,
@@ -46,7 +47,7 @@ from rowsandall_app.settings import (
SPORTTRACKS_CLIENT_ID, SPORTTRACKS_REDIRECT_URI
)
from utils import (
from rowers.utils import (
NoTokenError, custom_exception_handler, ewmovingaverage,
geo_distance,isprorower,uniqify
)
@@ -103,7 +104,6 @@ def imports_open(user,oauth_data):
oauth_data,
)
elif tokenexpirydate is None and expirydatename is not None and 'strava' in expirydatename:
print 'noot'
token = imports_token_refresh(
user,
tokenname,
+5 -5
View File
@@ -38,7 +38,7 @@ from bokeh.core.properties import value
from collections import OrderedDict
from django.conf import settings
from courses import (
from rowers.courses import (
course_coord_center,course_coord_maxmin,
polygon_coord_center
)
@@ -62,7 +62,7 @@ from scipy.signal import savgol_filter
from scipy.interpolate import griddata
import stravastuff
import rowers.stravastuff as stravastuff
from rowers.dataprep import rdata
import rowers.dataprep as dataprep
@@ -70,9 +70,9 @@ import rowers.metrics as metrics
from rowers.metrics import axes,axlabels,yaxminima,yaxmaxima
from utils import lbstoN
from datautils import p0
import datautils
from rowers.utils import lbstoN
from rowers.datautils import p0
import rowers.datautils as datautils
watermarkurl = "/static/img/logo7.png"
watermarksource = ColumnDataSource(dict(
+3 -3
View File
@@ -72,7 +72,7 @@ def longtask2(aantal,jobid=None,debug=False,secret=''):
counter = 0
progress = int(100.*i/aantal)
if debug:
print progress
print(progress)
if jobid != None:
if debug:
url = SITE_URL_DEV
@@ -83,8 +83,8 @@ def longtask2(aantal,jobid=None,debug=False,secret=''):
post_data = {"secret":secret}
s = requests.post(url, data=post_data)
if debug:
print url
print s
print(url)
print(s)
return 1
+1 -1
View File
@@ -92,7 +92,7 @@ def make_new_workout_from_email(rower, datafile, name, cntr=0,testing=False):
fileformat = fileformat[2]
if testing:
print 'Fileformat = ',fileformat
print('Fileformat = ',fileformat)
if fileformat == 'unknown':
# extension = datafilename[-4:].lower()
+2 -2
View File
@@ -188,7 +188,7 @@ class Command(BaseCommand):
res = polarstuff.get_all_new_workouts(polar_available)
# Concept2
rowers = Rower.objects.filter(c2_auto_import=True)
rowers = Rower.objects.filter(c2_auto_import=True).exclude(rowerplan='basic')
for r in rowers:
c2stuff.get_c2_workouts(r)
@@ -287,7 +287,7 @@ class Command(BaseCommand):
message.delete()
# Strava
rowers = Rower.objects.filter(strava_auto_import=True)
rowers = Rower.objects.filter(strava_auto_import=True).exclude(rowerplan='basic')
for r in rowers:
stravastuff.get_strava_workouts(r)
+3 -2
View File
@@ -1,6 +1,7 @@
from utils import lbstoN
from __future__ import absolute_import
from rowers.utils import lbstoN
import numpy as np
from models import C2WorldClassAgePerformance
from rowers.models import C2WorldClassAgePerformance
import pandas as pd
from scipy import optimize
from django.utils import timezone
+68 -49
View File
@@ -1,4 +1,4 @@
from __future__ import unicode_literals
from __future__ import unicode_literals, absolute_import
from django.db import models,IntegrityError
from django.contrib.auth.models import User
@@ -7,8 +7,8 @@ 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
from django.forms.extras.widgets import SelectDateWidget
from django.forms.widgets import SplitDateTimeWidget,SelectDateWidget
#from django.forms.extras.widgets import SelectDateWidget
from django.forms.formsets import BaseFormSet
from django.contrib.admin.widgets import AdminDateWidget,AdminTimeWidget,AdminSplitDateTime
from datetimewidget.widgets import DateTimeWidget
@@ -35,7 +35,7 @@ from rowers.rows import validate_file_extension
from collections import OrderedDict
from timezonefinder import TimezoneFinder
import mytypes
import rowers.mytypes as mytypes
from matplotlib import path
from rowsandall_app.settings import (
@@ -108,7 +108,7 @@ class TemplateListField(models.TextField):
def get_db_prep_value(self, value, connection, prepared=False):
if not value: return
assert(isinstance(value, list) or isinstance(value, tuple))
return self.token.join([unicode(s) for s in value])
return self.token.join([str(s) for s in value])
def value_to_string(self, obj):
value = self._get_val_from_obj(obj)
@@ -151,7 +151,7 @@ class PowerZonesField(models.TextField):
def get_db_prep_value(self, value, connection, prepared=False):
if not value: return
assert(isinstance(value, list) or isinstance(value, tuple))
return self.token.join([unicode(s) for s in value])
return self.token.join([str(s) for s in value])
def value_to_string(self, obj):
value = self._get_val_from_obj(obj)
@@ -216,7 +216,7 @@ def update_records(url=c2url,verbose=True):
)
try:
if verbose:
print record
print(record)
record.save()
except:
if verbose:
@@ -261,7 +261,7 @@ class PowerTimeFitnessMetric(models.Model):
date = models.DateField(default=current_day)
last_workout = models.IntegerField(default=0)
user = models.ForeignKey(User)
user = models.ForeignKey(User,on_delete=models.CASCADE)
PowerFourMin = models.FloatField(default=0)
PowerTwoK = models.FloatField(default=0)
PowerOneHour = models.FloatField(default=0)
@@ -334,7 +334,7 @@ class Team(models.Model):
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,) # validators=[is_not_basic])
manager = models.ForeignKey(User, null=True,on_delete=models.CASCADE) # validators=[is_not_basic])
private = models.CharField(max_length=30,choices=choices,default='open',
verbose_name='Team Type')
@@ -371,8 +371,8 @@ class TeamForm(ModelForm):
}
class TeamInvite(models.Model):
team = models.ForeignKey(Team)
user = models.ForeignKey(User,null=True)
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)
@@ -388,17 +388,17 @@ class TeamInviteForm(ModelForm):
class TeamRequest(models.Model):
team = models.ForeignKey(Team)
user = models.ForeignKey(User,null=True)
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)
from utils import (
from rowers.utils import (
workflowleftpanel,workflowmiddlepanel,
defaultleft,defaultmiddle,landingpages
)
from utils import geo_distance
from rowers.utils import geo_distance
def polygon_coord_center(polygon):
@@ -675,7 +675,7 @@ class Rower(models.Model):
('Workout','Workout'),
('Yoga','Yoga'),
)
user = models.OneToOneField(User)
user = models.OneToOneField(User,on_delete=models.CASCADE)
#billing details
country = CountryField(default=None, null=True, blank=True)
@@ -699,7 +699,7 @@ class Rower(models.Model):
null=True,blank=True,
default='braintree')
paidplan = models.ForeignKey(PaidPlan,null=True,default=None)
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)
@@ -840,7 +840,7 @@ class Rower(models.Model):
# Friends/Team
friends = models.ManyToManyField("self",blank=True)
mycoachgroup = models.ForeignKey(CoachingGroup,related_name='coachingrole',null=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)
@@ -881,15 +881,15 @@ class DeleteUserForm(forms.ModelForm):
# requestor is user
class CoachRequest(models.Model):
coach = models.ForeignKey(Rower)
user = models.ForeignKey(User,null=True)
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)
# requestor is coach
class CoachOffer(models.Model):
coach = models.ForeignKey(Rower)
user = models.ForeignKey(User,null=True)
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)
@@ -956,7 +956,7 @@ class FavoriteChart(models.Model):
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)
user = models.ForeignKey(Rower,on_delete=models.CASCADE)
class FavoriteForm(ModelForm):
@@ -1024,13 +1024,32 @@ def checkworkoutuser(user,workout):
except Rower.DoesNotExist:
return False
# Check if workout may be viewed by this user
def checkworkoutuserview(user,workout):
if user.is_anonymous():
return False
try:
r = Rower.objects.get(user=user)
if workout.user == r:
return True
teams = workout.user.team.all()
for team in teams:
if team in r.team.all():
return True
return False
except Rower.DoesNotExist:
return False
return False
def checkviewworkouts(user,rower):
try:
r = user.rower
if rower == r:
return True
teams = Team.objects.filter(manager=user)
print Rower.objects.filter(team__in=teams)
print(Rower.objects.filter(team__in=teams))
if rower in Rower.objects.filter(team__in=teams):
return True
except Rower.DoesNotExist:
@@ -1077,7 +1096,7 @@ timezones = (
# models related to geo data (points, polygon, courses)
class GeoCourse(models.Model):
manager = models.ForeignKey(Rower)
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)
@@ -1108,7 +1127,7 @@ class GeoCourseEditForm(ModelForm):
class GeoPolygon(models.Model):
name = models.CharField(max_length=150,blank=True)
course = models.ForeignKey(GeoCourse, blank=True)
course = models.ForeignKey(GeoCourse, blank=True,on_delete=models.CASCADE)
order_in_course = models.IntegerField(default=0)
def __unicode__(self):
@@ -1127,7 +1146,7 @@ class GeoPolygon(models.Model):
class GeoPoint(models.Model):
latitude = models.FloatField(default=0)
longitude = models.FloatField(default=0)
polygon = models.ForeignKey(GeoPolygon,blank=True)
polygon = models.ForeignKey(GeoPolygon,blank=True,on_delete=models.CASCADE)
order_in_poly = models.IntegerField(default=0)
# need error checking to "insert" new point into existing polygon? This affects order_in_poly
@@ -1140,7 +1159,7 @@ class GeoPoint(models.Model):
class TrainingTarget(models.Model):
rowers = models.ManyToManyField(Rower, related_name='targetathletes',
verbose_name='Athletes')
manager = models.ForeignKey(Rower,related_name='targetmanager',null=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)
@@ -1208,10 +1227,10 @@ class TrainingPlan(models.Model):
rowers = models.ManyToManyField(Rower,related_name='planathletes',
verbose_name='Athletes')
manager = models.ForeignKey(Rower,related_name='planmanager',null=True)
manager = models.ForeignKey(Rower,related_name='planmanager',null=True,on_delete=models.SET_NULL)
name = models.CharField(max_length=150,blank=True)
status = models.BooleanField(default=True,verbose_name='Active')
target = models.ForeignKey(TrainingTarget,blank=True,null=True)
target = models.ForeignKey(TrainingTarget,blank=True,null=True,on_delete=models.SET_NULL)
startdate = models.DateField(default=current_day)
enddate = models.DateField(
default=half_year_from_now)
@@ -1581,7 +1600,7 @@ def macrocyclecheckdates(plan):
cycles = cycles[1:]
class TrainingMacroCycle(models.Model):
plan = models.ForeignKey(TrainingPlan)
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(
@@ -1669,7 +1688,7 @@ class TrainingMacroCycleForm(ModelForm):
}
class TrainingMesoCycle(models.Model):
plan = models.ForeignKey(TrainingMacroCycle)
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(
@@ -1745,7 +1764,7 @@ class TrainingMesoCycle(models.Model):
class TrainingMicroCycle(models.Model):
plan = models.ForeignKey(TrainingMesoCycle)
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(
@@ -1884,9 +1903,9 @@ class PlannedSession(models.Model):
('None',None),
)
manager = models.ForeignKey(User)
manager = models.ForeignKey(User,on_delete=models.PROTECT)
course = models.ForeignKey(GeoCourse,blank=True,null=True,
verbose_name='OTW Course')
verbose_name='OTW Course',on_delete=models.SET_NULL)
name = models.CharField(max_length=150,blank=True,
verbose_name='Name')
@@ -2092,7 +2111,7 @@ class VirtualRace(PlannedSession):
class RaceLogo(models.Model):
filename = models.CharField(default='',max_length=150)
creationdatetime = models.DateTimeField()
user = models.ForeignKey(User)
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')
@@ -2102,7 +2121,7 @@ class RaceLogo(models.Model):
def delete(self, *args, **kwargs):
os.remove(self.filename)
print 'file deleted'
print('file deleted')
super(RaceLogo,self).delete(*args, **kwargs)
# Date input utility
@@ -2473,10 +2492,10 @@ class Workout(models.Model):
privacychoices = mytypes.privacychoices
adaptivetypes = mytypes.adaptivetypes
user = models.ForeignKey(Rower)
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')
verbose_name='Session',on_delete=models.SET_NULL)
name = models.CharField(max_length=150,blank=True,null=True)
date = models.DateField()
workouttype = models.CharField(choices=workouttypes,max_length=50,
@@ -2630,7 +2649,7 @@ def auto_delete_strokedata_on_delete(sender, instance, **kwargs):
try:
result = conn.execute(query)
except:
print "Database Locked"
print("Database Locked")
conn.close()
engine.dispose()
@@ -2648,7 +2667,7 @@ class VirtualRaceResult(models.Model):
adaptiveclass = models.CharField(default="None",max_length=50,
choices=mytypes.adaptivetypes,
verbose_name="Adaptive Class")
race = models.ForeignKey(VirtualRace)
race = models.ForeignKey(VirtualRace,on_delete=models.CASCADE)
duration = models.TimeField(default=datetime.time(1,0))
distance = models.IntegerField(default=0)
boatclass = models.CharField(choices=boatclasses,
@@ -2707,7 +2726,7 @@ class IndoorVirtualRaceResult(models.Model):
adaptiveclass = models.CharField(default="None",max_length=50,
choices=mytypes.adaptivetypes,
verbose_name="Adaptive Class")
race = models.ForeignKey(VirtualRace)
race = models.ForeignKey(VirtualRace,on_delete=models.CASCADE)
duration = models.TimeField(default=datetime.time(1,0))
distance = models.IntegerField(default=0)
boatclass = models.CharField(choices=boatclasses,
@@ -2750,7 +2769,7 @@ class IndoorVirtualRaceResult(models.Model):
class CourseTestResult(models.Model):
userid = models.IntegerField(default=0)
workoutid = models.IntegerField(null=True)
plannedsession = models.ForeignKey(PlannedSession)
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)
@@ -2884,7 +2903,7 @@ class ergcpdata(models.Model):
class GraphImage(models.Model):
filename = models.CharField(default='',max_length=150,blank=True,null=True)
creationdatetime = models.DateTimeField()
workout = models.ForeignKey(Workout)
workout = models.ForeignKey(Workout,on_delete=models.CASCADE)
width = models.IntegerField(default=1200)
height = models.IntegerField(default=600)
@@ -2901,7 +2920,7 @@ def auto_delete_image_on_delete(sender,instance, **kwargs):
if len(others) == 0:
os.remove(instance.filename)
else:
print "couldn't find the file "+instance.filename
print("couldn't find the file "+instance.filename)
# Form to update Workout data
@@ -3431,8 +3450,8 @@ class WorkoutComment(models.Model):
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)
workout = models.ForeignKey(Workout)
user = models.ForeignKey(User,on_delete=models.PROTECT)
workout = models.ForeignKey(Workout,on_delete=models.CASCADE)
def __unicode__(self):
return u'Comment to: {w} by {u1} {u2}'.format(
@@ -3456,8 +3475,8 @@ class PlannedSessionComment(models.Model):
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)
plannedsession = models.ForeignKey(PlannedSession)
user = models.ForeignKey(User,on_delete=models.PROTECT)
plannedsession = models.ForeignKey(PlannedSession,on_delete=models.CASCADE)
def __unicode__(self):
return u'Comment to: {w} by {u1} {u2}'.format(
+7 -5
View File
@@ -1,3 +1,5 @@
from six import iteritems
workouttypes = (
('water','Standard Racing Shell'),
('rower','Indoor Rower'),
@@ -208,15 +210,15 @@ c2mapping = {
}
c2mappinginv = {value:key for key,value in c2mapping.iteritems() if value is not None}
c2mappinginv = {value:key for key,value in iteritems(c2mapping) if value is not None}
stravamappinginv = {value:key for key,value in stravamapping.iteritems() if value is not None}
stravamappinginv = {value:key for key,value in iteritems(stravamapping) if value is not None}
stmappinginv = {value:key for key,value in stmapping.iteritems() if value is not None}
stmappinginv = {value:key for key,value in iteritems(stmapping) if value is not None}
rkmappinginv = {value:key for key,value in rkmapping.iteritems() if value is not None}
rkmappinginv = {value:key for key,value in iteritems(rkmapping) if value is not None}
polarmappinginv = {value:key for key,value in polarmapping.iteritems() if value is not None}
polarmappinginv = {value:key for key,value in iteritems(polarmapping) if value is not None}
otwtypes = (
'water',
+2 -1
View File
@@ -1,3 +1,4 @@
from __future__ import unicode_literals, absolute_import
# Interactions with Rowsandall.com API. Not fully complete.
# Python
@@ -15,7 +16,7 @@ import math
from math import sin,cos,atan2,sqrt
import urllib
import c2stuff
import rowers.c2stuff as c2stuff
# Django
from django.shortcuts import render_to_response
+12 -6
View File
@@ -1,3 +1,4 @@
from __future__ import unicode_literals, absolute_import
# Python
from django.utils import timezone
from datetime import datetime
@@ -10,7 +11,7 @@ import uuid
from django.conf import settings
import pytz
from dateutil import parser
from utils import myqueue,calculate_age,totaltime_sec_to_string
from rowers.utils import myqueue,calculate_age,totaltime_sec_to_string
import re
import django_rq
queue = django_rq.get_queue('default')
@@ -30,10 +31,10 @@ from rowers.emails import htmlstrip,htmlstripnobr
import rowers.mytypes as mytypes
import metrics
import rowers.metrics as metrics
import numpy as np
import dataprep
import courses
import rowers.dataprep as dataprep
import rowers.courses as courses
import iso8601
from iso8601 import ParseError
from rowers.tasks import handle_check_race_course
@@ -473,13 +474,14 @@ def remove_rower_session(r,ps):
return 1
def get_dates_timeperiod(request,startdatestring='',enddatestring=''):
def get_dates_timeperiod(request,startdatestring='',enddatestring='',
defaulttimeperiod='thisweek'):
# set start end date according timeperiod
timeperiod = request.GET.get('when')
if not timeperiod:
timeperiod = 'thisweek'
timeperiod = defaulttimeperiod
startdatestring = request.GET.get('startdate')
enddatestring = request.GET.get('enddate')
@@ -536,6 +538,10 @@ 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':
today = date.today()
startdate = today-timezone.timedelta(days=365)
enddate = today+timezone.timedelta(days=1)
elif daterangetester.match(timeperiod):
tstartdatestring = daterangetester.match(timeperiod).group(1)
tenddatestring = daterangetester.match(timeperiod).group(2)
+1 -1
View File
@@ -2,7 +2,7 @@ from matplotlib.ticker import MultipleLocator,FuncFormatter,NullFormatter
import matplotlib.pyplot as plt
import numpy as np
from rows import format_pace_tick, format_pace, format_time, format_time_tick
from rowers.rows import format_pace_tick, format_pace, format_time, format_time_tick
# Formatting the distance tick marks
+7 -8
View File
@@ -1,3 +1,4 @@
from __future__ import unicode_literals, absolute_import
# All the functionality needed to connect to Strava
# Python
@@ -33,8 +34,8 @@ from rowingdata import rowingdata
import pandas as pd
from rowers.models import Rower,Workout
from rowers.models import checkworkoutuser
import dataprep
from dataprep import columndict
import rowers.dataprep as dataprep
from rowers.dataprep import columndict
from io import StringIO
@@ -51,8 +52,8 @@ from rowsandall_app.settings import (
baseurl = 'https://polaraccesslink.com/v3'
from utils import NoTokenError, custom_exception_handler
import mytypes
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):
@@ -126,7 +127,7 @@ from rowers.utils import isprorower
def get_all_new_workouts(available_data,testing=False):
for record in available_data:
if testing:
print record
print(record)
if record['data-type'] == 'EXERCISE':
try:
r = Rower.objects.get(polaruserid=record['user-id'])
@@ -134,7 +135,7 @@ def get_all_new_workouts(available_data,testing=False):
if r.polar_auto_import and isprorower(r):
exercise_list = get_polar_workouts(u)
if testing:
print exercise_list
print(exercise_list)
except Rower.DoesNotExist:
pass
@@ -313,8 +314,6 @@ def get_polar_workout(user,id,transactionid):
response = requests.get(url,headers = headers2)
if response.status_code == 200:
print response.text
result = response.text
# commit transaction
response = requests.put(url,headers=headers)
+1
View File
@@ -1,3 +1,4 @@
from __future__ import unicode_literals, absolute_import
# All the functionality needed to connect to Runkeeper
from rowers.imports import *
import re
-1
View File
@@ -140,7 +140,6 @@ class StrokeDataSerializer(serializers.Serializer):
"""
# do something
print "fake serializer"
return 1
+2 -1
View File
@@ -1,3 +1,4 @@
from __future__ import unicode_literals, absolute_import
# All the functionality to connect to SportTracks
from rowers.imports import *
@@ -9,7 +10,7 @@ from rowsandall_app.settings import (
SPORTTRACKS_REDIRECT_URI
)
import mytypes
import rowers.mytypes as mytypes
oauth_data = {
'client_id': SPORTTRACKS_CLIENT_ID,
+6 -4
View File
@@ -1,3 +1,5 @@
from __future__ import unicode_literals, absolute_import
# All the functionality needed to connect to Strava
from scipy import optimize
from scipy.signal import savgol_filter
@@ -10,15 +12,15 @@ queue = django_rq.get_queue('default')
queuelow = django_rq.get_queue('low')
queuehigh = django_rq.get_queue('low')
from dataprep import columndict
from rowers.dataprep import columndict
import stravalib
from stravalib.exc import ActivityUploadFailed,TimeoutExceeded
from iso8601 import ParseError
from utils import myqueue
from rowers.utils import myqueue
import mytypes
import rowers.mytypes as mytypes
import gzip
from rowsandall_app.settings import (
@@ -259,7 +261,7 @@ def create_async_workout(alldata,user,stravaid,debug=False):
return 1
from utils import get_strava_stream
from rowers.utils import get_strava_stream
+24 -17
View File
@@ -1,4 +1,5 @@
""" Background tasks done by Celery (develop) or QR (production) """
from __future__ import absolute_import
import os
import time
import gc
@@ -16,7 +17,10 @@ from rowingdata import rowingdata as rdata
from datetime import timedelta
from sqlalchemy import create_engine
from celery import app
#from celery import app
from rowers.celery import app
from celery import shared_task
import datetime
import pytz
import iso8601
@@ -39,10 +43,11 @@ from django_rq import job
from django.utils import timezone
from django.utils.html import strip_tags
from utils import deserialize_list,ewmovingaverage,wavg
from emails import htmlstrip
from rowers.utils import deserialize_list,ewmovingaverage,wavg
from rowers.emails import htmlstrip
from HTMLParser import HTMLParser
#from HTMLParser import HTMLParser
from html.parser import HTMLParser
class MLStripper(HTMLParser):
def __init__(self):
self.reset()
@@ -68,6 +73,7 @@ from rowers.dataprepnodjango import (
# create_strava_stroke_data_db
)
from rowers.opaque import encoder
from django.core.mail import (
send_mail,
@@ -79,13 +85,13 @@ from django.db.utils import OperationalError
from jinja2 import Template,Environment,FileSystemLoader
env = Environment(loader = FileSystemLoader(["rowers/templates"]))
import datautils
import utils
import rowers.datautils as datautils
import rowers.utils as utils
import requests
import longtask
import rowers.longtask as longtask
import arrow
from utils import get_strava_stream
from rowers.utils import get_strava_stream
siteurl = SITE_URL
@@ -127,7 +133,7 @@ def handle_c2_import_stroke_data(c2token,
return 1
else:
url = "https://log.concept2.com/api/users/me/results/"+str(c2id)
url = "https://log.concept2.com/api/users/me/results/{id}".format(id=c2id)
s = requests.get(url,headers=headers)
@@ -1237,21 +1243,21 @@ def handle_zip_file(emailfrom, subject, file,**kwargs):
debug = False
if debug:
print message
print(message)
email = EmailMessage(subject, message,
emailfrom,
['workouts@rowsandall.com'])
email.attach_file(file)
if debug:
print "attaching"
print("attaching")
res = email.send()
if debug:
print "sent"
print("sent")
time.sleep(60)
return 1
@@ -1489,7 +1495,6 @@ def handle_updateergcp(rower_id,workoutfilenames,debug=False,**kwargs):
rowdata = rdata(f1 + '.gz')
except IOError:
rowdata = 0
if rowdata != 0:
therows.append(rowdata)
@@ -1822,7 +1827,7 @@ def handle_sendemail_coacheerequest(email,name,code,coachname,
debug=False,**kwargs):
fullemail = email
subject = '{n} asks 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 <info@rowsandall.com>'
siteurl = SITE_URL
if debug:
@@ -1890,7 +1895,8 @@ def handle_sendemailnewresponse(first_name, last_name,
if 'sessiontype' in kwargs:
sessiontype=kwargs.pop('sessiontype')
commentlink = '/rowers/workout/{workoutid}/comment/'.format(workoutid=workoutid)
commentlink = '/rowers/workout/{workoutid}/comment/'.format(
workoutid=encoder.encode_hex(workoutid))
if 'commentlink' in kwargs:
commentlink = kwargs.pop('commentlink')
@@ -1940,7 +1946,8 @@ def handle_sendemailnewcomment(first_name,
if 'sessiontype' in kwargs:
sessiontype=kwargs.pop('sessiontype')
commentlink = '/rowers/workout/{workoutid}/comment/'.format(workoutid=workoutid)
commentlink = '/rowers/workout/{workoutid}/comment/'.format(
workoutid=encoder.encode_hex(workoutid))
if 'commentlink' in kwargs:
commentlink = kwargs.pop('commentlink')
@@ -1951,7 +1958,7 @@ def handle_sendemailnewcomment(first_name,
'comment':comment,
'workoutname':workoutname,
'siteurl':siteurl,
'workoutid':workoutid,
'workoutid':encoder.encode_hex(workoutid),
'sessiontype':sessiontype,
'commentlink':commentlink,
}
+1 -1
View File
@@ -10,7 +10,7 @@ from django.db import IntegrityError
import uuid
from django.conf import settings
from utils import myqueue
from rowers.utils import myqueue
import django_rq
queue = django_rq.get_queue('default')
+2 -2
View File
@@ -4,10 +4,10 @@
<p>Dear <strong>{{ name }}</strong>,</p>
<p>
{{ coachname }} is offering to become your coach on rowsandall.com.
{{ coach }} is offering to become your coach on rowsandall.com.
</p>
<p>
By accepting the invite, {{ coachname }} will have access to your
By accepting the invite, {{ coach }} will have access to your
data and will be able to upload workouts and run analysis on rowsandall.com
on behalf of you.
</p>
+4 -8
View File
@@ -129,7 +129,7 @@
{% if workouts.has_next %}
{% if request.GET.q %}
<a href="/rowers/list-workouts/{{ startdate|date:"Y-m-d" }}/{{ enddate|date:"Y-m-d" }}/?page={{ workouts.next_page_number }}&q={{ request.GET.q }}">
<a href="{{ request.path }}?page={{ workouts.next_page_number }}&q={{ request.GET.q }}&when={{ timeperiod }}">
<i class="fas fa-arrow-alt-right"></i>
</a>
<a
@@ -137,7 +137,7 @@
<i class="fas fa-arrow-alt-to-right"></i>
</a>
{% else %}
<a href="/rowers/list-workouts/{{ startdate|date:"Y-m-d" }}/{{ enddate|date:"Y-m-d" }}/?page={{ workouts.next_page_number }}">
<a href="{{ request.path }}?page={{ workouts.next_page_number }}&when={{ timeperiod }}">
<i class="fas fa-arrow-alt-right"></i>
</a>
<a href="?page={{ workouts.paginator.num_pages }}">
@@ -216,7 +216,7 @@
{% if team %}
<td colspan="2">
{% if workout|may_edit:request %}
<a class="small" href="/rowers/{{ workout.user.id }}/list-workouts/">
<a class="small" href="/rowers/list-workouts/user/{{ workout.user.user.id }}/">
{{ workout.user.user.first_name }}
{{ workout.user.user.last_name }}
</a>
@@ -245,18 +245,14 @@
</a>
</td>
<td>
{% if workout|may_edit:request %}
<a class="small"
href="/rowers/workout/{{ workout.id|encode }}/stats/"
title="Stats">
<i class="fal fa-table fa-fw"></i>
</a>
{% else %}
&nbsp;
{% endif %}
</td>
<td>
{% if workout.user.user == user or user == team.manager %}
{% if workout|may_edit:request %}
<a class="small" href="/rowers/workout/{{ workout.id|encode }}/delete/"
title="Delete">
<i class="fas fa-trash-alt fa-fw"></i>
+1 -1
View File
@@ -63,7 +63,7 @@
</li>
<li id="plan-teamsession">
<a href="/rowers/sessions/teamcreate/?when={{ timeperiod }}">
<i class="fas fa-whistle fa-fw"></i>&nbsp;Add Team Session
<i class="fas fa-whistle fa-fw"></i>&nbsp;Add Group Session
</a>
</li>
<li id="plan-microcycle">
+1 -1
View File
@@ -41,7 +41,7 @@
</a>
</li>
<li id="compare">
<a href="/rowers/multi-compare/workout/{{ workout.id|encode }}/">
<a href="/rowers/team-compare-select/workout/{{ workout.id|encode }}/">
<i class="fas fa-balance-scale fa-fw"></i>&nbsp;Compare
</a>
</li>
+19 -5
View File
@@ -94,7 +94,14 @@
<td>&#10004;</td>
</tr>
<tr>
<td>Create Training plans, tests and challenges for yourself. Track your performance
<td>Create and manage groups.</td>
<td>&nbsp;</td>
<td>&#10004;</td>
<td>&#10004;</td>
<td>&#10004;</td>
</tr>
<tr>
<td>Create Training plans, tests and challenges for yourself and your training group. Track your performance
against plan.</td>
<td>&nbsp;</td>
<td>&nbsp;</td>
@@ -110,14 +117,21 @@
<td>&#10004;</td>
</tr>
<tr>
<td>Create and manage teams.</td>
<td>Manage your athlete's workouts</td>
<td>&nbsp;</td>
<td>&nbsp;</td>
<td>&nbsp;</td>
<td>&#10004;</td>
</tr>
<tr>
<td>Manage your athlete's workouts</td>
<td>Run analytics for your athletes</td>
<td>&nbsp;</td>
<td>&nbsp;</td>
<td>&nbsp;</td>
<td>&#10004;</td>
</tr>
<tr>
<td>Change zone intensities and other workout related settings for your athletes</td>
<td>&nbsp;</td>
<td>&nbsp;</td>
<td>&nbsp;</td>
@@ -126,8 +140,8 @@
<tr>
<td>Pricing</td>
<td>FREE</td>
<td nowrap="nowrap">From 15&euro;/year</td>
<td nowrap="nowrap">From 65&euro;/year</td>
<td nowrap="nowrap">From 25&euro;/year</td>
<td nowrap="nowrap">From 75&euro;/year</td>
<td nowrap="nowrap">From 90&euro;/year</td>
</tr>
<tr>
+1 -1
View File
@@ -90,7 +90,7 @@
</li>
<li class="grid_2">
{% csrf_token %}
<input class="button green" type="submit" value="Submit">
<input type="submit" value="Submit">
</li>
</ul>
+4
View File
@@ -96,6 +96,10 @@
{% endfor %}
</tbody>
</table>
<p>
<a href="/rowers/sessions/manage/session/{{ psdict.id.1 }}/">
Add, remove or change workouts for this session</a>
</p>
</li>
<li class="grid_2">
<h2>{{ rower.user.first_name }} {{ rower.user.last_name }}</h2>
@@ -4,6 +4,11 @@
{% block main %}
<h1>Import and Export Settings for {{ rower.user.first_name }} {{ rower.user.last_name }}</h1>
{% if user.rower.rowerplan == 'basic' %}
The auto import and export settings only work on <a href="/rowers/paidplans/">a paid plan</a>.
{% endif %}
{% if form.errors %}
<p style="color: red;">
Please correct the error{{ form.errors|pluralize }} below.
+3 -2
View File
@@ -470,7 +470,7 @@ def userurl(path,member):
userstring = 'user/%s/' % member.id
# remove team
tpattern = re.compile('\/team\/\d+/')
tpattern = re.compile('team\/\d+/')
if tpattern.search(path) is not None:
path = tpattern.sub('',path)
@@ -489,7 +489,7 @@ def teamurl(path,team):
# remove user
upattern = re.compile('\/user\/\d+/')
if upattern.search(path) is not None:
path = upattern.sub('',path)
path = upattern.sub('/',path)
if pattern.search(path) is not None:
@@ -497,6 +497,7 @@ def teamurl(path,team):
else:
replaced = path+teamstring
return replaced
@register.filter
+2 -2
View File
@@ -145,8 +145,8 @@ class UserFactory(factory.DjangoModelFactory):
email = factory.LazyAttribute(lambda _: faker.email())
username = factory.LazyAttribute(lambda _: faker.profile()['username'])
first_name = faker.name().split(' ')[0]
last_name = faker.name().split(' ')[0]
first_name = factory.LazyAttribute(lambda _:faker.name().split(' ')[0])
last_name = factory.LazyAttribute(lambda _:faker.name().split(' ')[0])
class RaceFactory(factory.DjangoModelFactory):
class Meta:
+26 -2
View File
@@ -959,6 +959,31 @@ class PermissionsViewTests(TestCase):
response = self.c.get(url)
self.assertEqual(response.status_code,200)
# stats
url = reverse('workout_view',
kwargs={'id':encoder.encode_hex(self.uplan2_workouts[0].id)}
)
response = self.c.get(url)
self.assertEqual(response.status_code,200)
# workflow
url = reverse('workout_workflow_view',
kwargs={'id':encoder.encode_hex(self.uplan2_workouts[0].id)}
)
response = self.c.get(url)
self.assertEqual(response.status_code,200)
# stats
url = reverse('workout_stats_view',
kwargs={'id':encoder.encode_hex(self.uplan2_workouts[0].id)}
)
response = self.c.get(url)
self.assertEqual(response.status_code,200)
## Pro users (and higher) can join group led by other Pro (or higher) user
def test_team_member_request_pro_pro(self):
login = self.c.login(username=self.upro.username,password=self.upropassword)
@@ -1416,8 +1441,7 @@ class PermissionsCoachingTests(TestCase):
## Basic users can subscribe to any race
# group related
## group members can see but not edit each other's workouts and charts
###
## group members can see but not edit each other's plans
+1 -6
View File
@@ -1,3 +1,4 @@
from __future__ import unicode_literals, absolute_import
# All the functionality needed to connect to Runkeeper
from rowers.imports import *
@@ -149,12 +150,6 @@ def uploadactivity(access_token,filename,description='',
headers=headers)
if resp.status_code != 200:
if settings.DEBUG:
print resp.status_code
print resp.reason
print ""
print headers
print ""
return 0,resp.reason,resp.status_code,headers
else:
return resp.json()[0]["Id"],"ok",200,""
+2 -1
View File
@@ -1,6 +1,7 @@
from __future__ import unicode_literals, absolute_import
from rowers.imports import *
import mytypes
import rowers.mytypes as mytypes
from rowers.mytypes import otwtypes
from rowsandall_app.settings import (
+9 -4
View File
@@ -26,12 +26,13 @@ queue = django_rq.get_queue('default')
queuelow = django_rq.get_queue('low')
queuehigh = django_rq.get_queue('low')
from mytypes import workouttypes,boattypes,otwtypes,workoutsources
from rowers.mytypes import workouttypes,boattypes,otwtypes,workoutsources
try:
from cStringIO import StringIO
except:
from StringIO import StringIO
from io import StringIO
from rowers.utils import (
geo_distance,serialize_list,deserialize_list,uniqify,
@@ -451,8 +452,12 @@ def make_plot(r,w,f1,f2,plottype,title,imagename='',plotnr=0):
return i.id,job.id
import c2stuff,stravastuff,sporttracksstuff,runkeeperstuff
import underarmourstuff,tpstuff
import rowers.c2stuff as c2stuff
import rowers.stravastuff as stravastuff
import rowers.sporttracksstuff as sporttracksstuff
import rowers.runkeeperstuff as runkeeperstuff
import rowers.underarmourstuff as underarmourstuff
import rowers.tpstuff as tpstuff
def set_workouttype(w,options):
try:
+1 -1
View File
@@ -2,7 +2,7 @@ from django.conf import settings
from django.conf.urls import url, include
from django.contrib.auth.models import User
from django.contrib.auth.decorators import login_required, permission_required
from models import Workout,Rower,StrokeData,FavoriteChart
from rowers.models import Workout,Rower,StrokeData,FavoriteChart
from rest_framework import routers, serializers, viewsets,permissions
from rest_framework.urlpatterns import format_suffix_patterns
+2 -1
View File
@@ -1,4 +1,5 @@
from statements import *
from __future__ import unicode_literals, absolute_import
from rowers.views.statements import *
# Histogram for a date/time range
@user_passes_test(ispromember,login_url="/rowers/paidplans",
+1 -1
View File
@@ -1,4 +1,4 @@
from statements import *
from rowers.views.statements import *
# Stroke data form to test API upload
+1 -1
View File
@@ -1,4 +1,4 @@
from statements import *
from rowers.views.statements import *
# Custom error pages with Rowsandall headers
def error500_view(request):
+1 -1
View File
@@ -1,4 +1,4 @@
from statements import *
from rowers.views.statements import *
+1 -1
View File
@@ -1,4 +1,4 @@
from statements import *
from rowers.views.statements import *
# Send workout to TP
+1 -1
View File
@@ -1,4 +1,4 @@
from statements import *
from rowers.views.statements import *
@login_required()
+1 -1
View File
@@ -1,5 +1,5 @@
from statements import *
from rowers.views.statements import *
def paidplans_view(request):
if not request.user.is_anonymous():
+2 -4
View File
@@ -1,4 +1,4 @@
from statements import *
from rowers.views.statements import *
@login_required()
def plannedsession_comment_view(request,id=0,userid=0):
@@ -496,8 +496,6 @@ def plannedsession_multicreate_view(request,
for obj in ps_formset.deleted_objects:
messages.info(request,"Deleted Planned Session "+str(obj))
obj.delete()
else:
print ps_formset.errors
url = reverse(plannedsession_multicreate_view,
kwargs = {
@@ -716,7 +714,7 @@ def plannedsession_teamcreate_view(request,
})
# Manager edits sessions for entire team
@user_passes_test(iscoachmember,login_url="/rowers/paidplans/",
@user_passes_test(hasplannedsessions,login_url="/rowers/paidplans/",
message="This functionality requires a Coach or Self-Coach plan",
redirect_field_name=None)
def plannedsession_teamedit_view(request,
+2 -2
View File
@@ -1,4 +1,4 @@
from statements import *
from rowers.views.statements import *
# List Courses
@@ -653,7 +653,7 @@ def virtualevent_disqualify_view(request,raceid=0,recordid=0):
latitude = rowdata.df[' latitude']
if not latitude.std():
hascoordinates = 0
except KeyError, AttributeError:
except (KeyError, AttributeError):
hascoordinates = 0
else:
hascoordinates = 0
+35 -6
View File
@@ -1,7 +1,7 @@
import time
import colorsys
import timestring
import zipfile
import bleach
import arrow
@@ -56,7 +56,11 @@ from rowers.forms import (
MetricsForm,DisqualificationForm,disqualificationreasons,
disqualifiers,SearchForm,BillingForm,PlanSelectForm
)
try:
from django.core.urlresolvers import reverse, reverse_lazy
except ModuleNotFoundError:
from django.urls import reverse, reverse_lazy
from django.core.exceptions import PermissionDenied
from django.template import RequestContext
@@ -181,7 +185,10 @@ from rowers.tasks import (
from scipy.signal import savgol_filter
from django.shortcuts import render_to_response
try:
from Cookie import SimpleCookie
except ModuleNotFoundError:
from http.cookies import SimpleCookie
from shutil import copyfile,move
import rowers.mytypes as mytypes
from rowingdata import rower as rrower
@@ -367,6 +374,14 @@ def get_workout_permitted(user,id):
return w
def get_workout_permittedview(user,id):
w = get_workout(id)
if (checkworkoutuserview(user,w)==False):
raise PermissionDenied("Access denied")
return w
def getvalue(data):
perc = 0
total = 1
@@ -407,7 +422,7 @@ class SessionTaskListener(threading.Thread):
for item in self.pubsub.listen():
if item['data'] == "KILL":
self.pubsub.unsubscribe()
print self, "unsubscribed and finished"
print(self, "unsubscribed and finished")
break
else:
self.work(item)
@@ -429,14 +444,23 @@ 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:
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:
pass
from rest_framework.decorators import api_view, renderer_classes
from rowers.permissions import IsOwnerOrNot
import rowers.plots as plots
@@ -457,7 +481,10 @@ 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:
pass
def remove_asynctask(request,id):
try:
@@ -864,7 +891,7 @@ def rowhascoordinates(row):
if not latitude.std():
hascoordinates = 0
except KeyError,AttributeError:
except (KeyError,AttributeError):
hascoordinates = 0
else:
@@ -878,10 +905,10 @@ def rowhascoordinates(row):
def rdata(file,rower=rrower()):
try:
res = rrdata(csvfile=file,rower=rower)
except IOError, IndexError:
except (IOError, IndexError):
try:
res = rrdata(csvfile=file+'.gz',rower=rower)
except IOError, IndexError:
except (IOError, IndexError):
res = 0
return res
@@ -944,7 +971,9 @@ from rowers.utils import (
import rowers.datautils as datautils
from rowers.models import checkworkoutuser,checkaccessuser,checkviewworkouts
from rowers.models import (
checkworkoutuser,checkaccessuser,checkviewworkouts,checkworkoutuserview
)
# Check if a user is a Coach member
def iscoachmember(user):
+2 -2
View File
@@ -1,4 +1,4 @@
from statements import *
from rowers.views.statements import *
@login_required()
@@ -205,7 +205,7 @@ def rower_teams_view(request,message='',successmessage=''):
potentialcoaches = [c for c in potentialcoaches if teams.get_coach_club_size(c.rower)<c.rower.clubsize]
coachees = teams.coach_getcoachees(r)
coachees = teams.coach_getcoachees(request.user.rower)
if request.user.rower.rowerplan == 'coach':
potentialathletes = Rower.objects.filter(
+1 -1
View File
@@ -1,4 +1,4 @@
from statements import *
from rowers.views.statements import *
@login_required()
def start_trial_view(request):
+23 -31
View File
@@ -1,4 +1,4 @@
from statements import *
from rowers.views.statements import *
import rowers.teams as teams
# Show the EMpower Oarlock generated Stroke Profile
@@ -590,7 +590,9 @@ def workouts_join_select(request,
})
# Team comparison
@login_required()
@user_passes_test(ispromember,login_url='/rowers/paidplans/',
message="This functionality requires a Pro plan or higher",
redirect_field_name=None)
def team_comparison_select(request,
startdatestring="",
enddatestring="",
@@ -695,11 +697,6 @@ def team_comparison_select(request,
except Team.DoesNotExist:
theteam = 0
if requestrower.rowerplan == 'basic' and theteam==0:
if requestrower.protrialexpires is None or requestrower.protrialexpires<datetime.date.today():
raise PermissionDenied("Access denied")
if theteam and (theteam.viewing == 'allmembers' or theteam.manager == request.user):
workouts = Workout.objects.filter(team=theteam,
startdatetime__gte=startdate,
@@ -737,8 +734,8 @@ def team_comparison_select(request,
if id:
firstworkout = get_workout(id)
if not checkworkoutuser(request.user,firstworkout):
raise PermissionDenied("You are not allowed to sue this workout")
if not checkworkoutuserview(request.user,firstworkout):
raise PermissionDenied("You are not allowed to use this workout")
firstworkoutquery = Workout.objects.filter(id=encoder.decode_hex(id))
workouts = firstworkoutquery | workouts
@@ -1160,26 +1157,17 @@ def multi_compare_view(request,id=0,userid=0):
# List Workouts
@login_required()
def workouts_view(request,message='',successmessage='',
startdatestring='',
enddatestring='',
teamid=0,rankingonly=False,rowerid=0,userid=0):
startdate,enddate = get_dates_timeperiod(request,defaulttimeperiod='lastyear')
request.session['referer'] = absolute(request)['PATH']
r = getrequestrower(request,rowerid=rowerid,userid=userid)
# check if access is allowed
if not checkviewworkouts(request.user,r):
raise PermissionDenied("Access denied")
if startdatestring:
startdate = iso8601.parse_date(startdatestring)
else:
startdate = datetime.date.today()-datetime.timedelta(days=365)
if enddatestring:
enddate = iso8601.parse_date(enddatestring)
else:
enddate = datetime.date.today()
startdate = datetime.datetime.combine(startdate,datetime.time())
@@ -1352,7 +1340,7 @@ def workouts_view(request,message='',successmessage='',
'name':'Workouts'
},
]
timeperiod = startdate.strftime('%Y-%m-%d')+'/'+enddate.strftime('%Y-%m-%d')
return render(request, 'list_workouts.html',
{'workouts': workouts,
'active': 'nav-workouts',
@@ -1368,6 +1356,7 @@ def workouts_view(request,message='',successmessage='',
'teams':get_my_teams(request.user),
'interactiveplot':script,
'the_div':div,
'timeperiod':timeperiod,
})
@@ -1527,7 +1516,7 @@ def workout_view(request,id=0):
latitude = rowdata.df[' latitude']
if not latitude.std():
hascoordinates = 0
except KeyError,AttributeError:
except (KeyError,AttributeError):
hascoordinates = 0
else:
@@ -2489,7 +2478,7 @@ def workout_stats_view(request,id=0,message="",successmessage=""):
# prepare data frame
datadf,row = dataprep.getrowdata_db(id=encoder.decode_hex(id))
if (checkworkoutuser(request.user,row)==False):
if (checkworkoutuserview(request.user,row)==False):
raise PermissionDenied('Access Denied')
datadf = dataprep.clean_df_stats(datadf,workstrokesonly=workstrokesonly)
@@ -2588,7 +2577,7 @@ def workout_stats_view(request,id=0,message="",successmessage=""):
'value': hrdrift,
'unit': '%',
}
except ZeroDivisionError,ValueError:
except (ZeroDivisionError,ValueError):
pass
# TRIMP
@@ -2725,7 +2714,7 @@ 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_permitted(request.user,id)
row = get_workout_permittedview(request.user,id)
r = getrower(request.user)
result = request.user.is_authenticated() and ispromember(request.user)
@@ -3571,7 +3560,7 @@ def workout_map_view(request,id=0):
latitude = rowdata.df[' latitude']
if not latitude.std():
hascoordinates = 0
except KeyError,AttributeError:
except (KeyError,AttributeError):
hascoordinates = 0
else:
@@ -4209,7 +4198,8 @@ def workout_upload_view(request,
# 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="",
successmessage="",
uploadoptions={
@@ -4450,7 +4440,7 @@ def graph_show_view(request,id):
'name':'Workouts'
},
{
'url':get_workout_default_page(request,w.id),
'url':get_workout_default_page(request,encoder.encode_hex(w.id)),
'name': w.name
},
{
@@ -4525,7 +4515,9 @@ def workout_summary_restore_view(request,id,message="",successmessage=""):
return HttpResponseRedirect(url)
# Split a workout
@user_passes_test(ispromember,login_url="/rowers/paidplans",message="This functionality requires a Pro plan or higher",redirect_field_name=None)
@user_passes_test(ispromember,login_url="/rowers/paidplans",
message="This functionality requires a Pro plan or higher",
redirect_field_name=None)
def workout_split_view(request,id=0):
row = get_workout_permitted(request.user,id)
@@ -4804,7 +4796,7 @@ def workout_summary_edit_view(request,id,message="",successmessage=""
s = cd["intervalstring"]
try:
rowdata.updateinterval_string(s)
except ParseException,err:
except (ParseException,err):
messages.error(request,'Parsing error in column '+str(err.col))
intervalstats = rowdata.allstats()
itime,idist,itype = rowdata.intervalstats_values()
@@ -4885,7 +4877,7 @@ def workout_summary_edit_view(request,id,message="",successmessage=""
s = request.POST["savestringform"]
try:
rowdata.updateinterval_string(s)
except ParseException,err:
except (ParseException,err):
messages.error(request,'Parsing error in column '+str(err.col))
intervalstats = rowdata.allstats()
+1
View File
@@ -1,3 +1,4 @@
from __future__ import unicode_literals, absolute_import
import requests
import json
from lxml import objectify,etree
+3 -1
View File
@@ -59,7 +59,7 @@ INSTALLED_APPS = [
'django_mailbox',
'rest_framework',
'datetimewidget',
'rest_framework_swagger',
# 'rest_framework_swagger',
'oauth2_provider',
'corsheaders',
'analytical',
@@ -379,8 +379,10 @@ REST_FRAMEWORK = {
'rest_framework.authentication.BasicAuthentication',
'rest_framework.authentication.SessionAuthentication',
'oauth2_provider.ext.rest_framework.OAuth2Authentication',
# 'oauth2_provider.contrib.rest_framework.OAuth2Authentication',
),
'PAGE_SIZE': 20,
'DEFAULT_PAGINATION_CLASS':'rest_framework.pagination.LimitOffsetPagination',
}
SWAGGER_SETTINGS = {
+1 -1
View File
@@ -9,7 +9,7 @@ https://docs.djangoproject.com/en/1.9/topics/settings/
For the full list of settings and their values, see
https://docs.djangoproject.com/en/1.9/ref/settings/
"""
from settings import *
from .settings import *
DATABASES = {
'default': {
+1 -1
View File
@@ -12,7 +12,7 @@ https://docs.djangoproject.com/en/1.9/ref/settings/
import os
from settings import *
from .settings import *
DATABASES = {
'default': {
+18 -6
View File
@@ -36,18 +36,25 @@ urlpatterns = [
url('^', include('django.contrib.auth.urls')),
url(r'^django-rq/',include('django_rq.urls')),
url(r'^password_change_done/$',auth_views.password_change_done,name='password_change_done'),
# url(r'^password_change_done/$',auth_views.PasswordChangeDoneView,name='password_change_done'),
url(r'^password_change/$',auth_views.password_change),
# url(r'^password_change/$',auth_views.PasswordChangeView,name='password_change'),
url(r'^password_reset/$',
auth_views.password_reset,
# auth_views.PasswordResetView,
{'template_name': 'rowers/templates/registration/password_reset.html'},
name='password_reset'),
url(r'^password_reset/done/$',
auth_views.password_reset_done,
# auth_views.PasswordResetDoneView,
name='password_reset_done'),
url(r'^reset/(?P<uidb64>[0-9A-Za-z_\-]+)/(?P<token>.+)/$',
auth_views.password_reset_confirm,
# auth_views.PasswordResetConfirmView,
name='password_reset_confirm'),
url(r'^reset/done/$', auth_views.password_reset_complete,
url(r'^reset/done/$',
auth_views.password_reset_complete,
# auth_views.PasswordResetCompleteView,
name='password_reset_complete'),
]
@@ -55,11 +62,16 @@ urlpatterns += [
url(r'^robots\.txt$', TemplateView.as_view(template_name='robots.txt',
content_type='text/plain')),
url(r'^admin/', admin.site.urls),
url(r'^api-auth/', include('rest_framework.urls', namespace='rest_framework')),
url(r'^api-auth/', include('rest_framework.urls', namespace='rest_framework2')),
url(r'^$',rootview),
url(r'^getblogs/$',rowersviews.get_blog_posts),
url(r'^login/',auth_views.login, name='login'),
url(r'^logout/$',auth_views.logout,
url(r'^login/',
auth_views.login,
# auth_views.LoginView,
name='login'),
url(r'^logout/$',
auth_views.logout,
# auth_views.LogoutView,
{'next_page': '/'},
name='logout',),
url(r'^rowers/',include('rowers.urls')),
@@ -75,7 +87,7 @@ urlpatterns += [
url(r'^twitter\_callback',rowersviews.rower_process_twittercallback),
url(r'^i18n/', include('django.conf.urls.i18n')),
url(r'^tz_detect/', include('tz_detect.urls')),
url(r'^jsi18n/', 'django.views.i18n.javascript_catalog',name='jsi18n'),
# url(r'^jsi18n/', 'django.views.i18n.javascript_catalog',name='jsi18n'),
]
@@ -83,7 +95,7 @@ if settings.DEBUG:
import debug_toolbar
import django
urlpatterns += [
url(r'^__debug__/',include(debug_toolbar.urls)),
# url(r'^__debug__/','debug_toolbar.urls'),
url(r'^static/(?P<path>.*)$',
django.views.static.serve,
kwargs={'document_root': settings.STATIC_ROOT,}