diff --git a/rowers/dataprep.py b/rowers/dataprep.py index 7f7fac93..d7c6e6a1 100644 --- a/rowers/dataprep.py +++ b/rowers/dataprep.py @@ -32,7 +32,7 @@ from rowingdata import ( MysteryParser, BoatCoachOTWParser,QuiskeParser, painsledDesktopParser, speedcoachParser, ErgStickParser, SpeedCoach2Parser, FITParser, fitsummarydata, - make_cumvalues,cumcpdata, + make_cumvalues,cumcpdata,ExcelTemplate, summarydata, get_file_type, ) @@ -996,6 +996,11 @@ def save_workout_database(f2, r, dosmooth=True, workouttype='rower', def parsenonpainsled(fileformat,f2,summary): # handle RowPro: + if (fileformat == 'xls'): + row = ExcelTemplate(f2) + hasrecognized = True + + if (fileformat == 'rp'): row = RowProParser(f2) hasrecognized = True diff --git a/rowers/forms.py b/rowers/forms.py index aa8cf8ed..f029285d 100644 --- a/rowers/forms.py +++ b/rowers/forms.py @@ -354,6 +354,7 @@ class RegistrationFormTermsOfService(RegistrationForm): tos = forms.BooleanField(widget=forms.CheckboxInput, label='I have read and agree to the Terms of Service', error_messages={'required': "You must agree to the terms to register"}) + class RegistrationFormUniqueEmail(RegistrationFormTermsOfService): @@ -370,6 +371,35 @@ class RegistrationFormUniqueEmail(RegistrationFormTermsOfService): 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'), + ) + + weightcategories = ( + ('hwt','heavy-weight'), + ('lwt','light-weight'), + ) + + birthdate = forms.DateTimeField(widget=SelectDateWidget( + years=range(timezone.now().year-100,timezone.now().year-10)), + initial = datetime.date(year=1970, + month=4, + day=15)) + + sex = forms.ChoiceField(required=True, + choices=sexcategories, + initial='not specified', + label='Sex') + + weightcategory = forms.ChoiceField(label='Weight Category', + choices=weightcategories) + +# def __init__(self, *args, **kwargs): +# self.fields['sex'].initial = 'not specified' + # Time field supporting microseconds. Not used, I believe. class MyTimeField(forms.TimeField): diff --git a/rowers/metrics.py b/rowers/metrics.py index 9c1fcfa0..bf1e790b 100644 --- a/rowers/metrics.py +++ b/rowers/metrics.py @@ -1,4 +1,5 @@ from utils import lbstoN +import numpy as np rowingmetrics = ( ('time',{ @@ -301,3 +302,17 @@ dropping to 8m for race pace in the single.""", This value should be fairly constant across all stroke rates.""", }, ) + +def calc_trimp(df,sex,hrmax,hrmin): + if sex == 'male': + f = 1.92 + else: + f = 1.67 + + dt = df['time'].diff()/6.e4 + + hrr = (df['hr']-hrmin)/(hrmax-hrmin) + trimpdata = dt*hrr*0.64*np.exp(f*hrr) + trimp = trimpdata.sum() + + return trimp diff --git a/rowers/models.py b/rowers/models.py index 7c67f0f1..28993bd1 100644 --- a/rowers/models.py +++ b/rowers/models.py @@ -8,6 +8,7 @@ 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.formsets import BaseFormSet from datetimewidget.widgets import DateTimeWidget from django.core.validators import validate_email @@ -210,6 +211,12 @@ class Rower(models.Model): ('lwt','light-weight'), ) + sexcategories = ( + ('male','male'), + ('female','female'), + ('not specified','not specified'), + ) + stravatypes = ( ('Ride','Ride'), ('Kitesurf','Kitesurf'), @@ -257,6 +264,11 @@ class Rower(models.Model): max_length=30, choices=weightcategories) + sex = models.CharField(default="not specified", + max_length=30, + choices=sexcategories) + + birthdate = models.DateField(null=True,blank=True) # Power Zone Data ftp = models.IntegerField(default=226,verbose_name="Functional Threshold Power") @@ -939,10 +951,17 @@ class RowerPowerZonesForm(ModelForm): class AccountRowerForm(ModelForm): class Meta: model = Rower - fields = ['weightcategory','getemailnotifications', + fields = ['sex','birthdate','weightcategory','getemailnotifications', 'defaulttimezone','showfavoritechartnotes', 'defaultlandingpage'] + widgets = { + 'birthdate': SelectDateWidget( + years=range( + timezone.now().year-100,timezone.now().year-10)), + } + + def clean_email(self): email = self.cleaned_data.get('email') diff --git a/rowers/rows.py b/rowers/rows.py index acf7c27a..51de9769 100644 --- a/rowers/rows.py +++ b/rowers/rows.py @@ -50,7 +50,7 @@ def validate_file_extension(value): ext = os.path.splitext(value.name)[1] valid_extensions = ['.tcx','.csv','.TCX', '.CSV','.fit','.FIT','.zip','.ZIP', - '.gz','.GZ'] + '.gz','.GZ','.xls'] if not ext in valid_extensions: raise ValidationError(u'File not supported!') diff --git a/rowers/tests.py b/rowers/tests.py index fb8a67fd..ce4ca872 100644 --- a/rowers/tests.py +++ b/rowers/tests.py @@ -307,6 +307,9 @@ class NewUserRegistrationTest(TestCase): 'password1':'aapindewei2', 'password2':'aapindewei2', 'tos':True, + 'weightcategory':'hwt', + 'sex':'male', + 'birthdate':datetime.datetime(year=1970,month=4,day=2) } form = RegistrationFormUniqueEmail(form_data) diff --git a/rowers/views.py b/rowers/views.py index 52cb236b..8903a9d6 100644 --- a/rowers/views.py +++ b/rowers/views.py @@ -44,7 +44,8 @@ from rowers.forms import ( SummaryStringForm,IntervalUpdateForm,StrokeDataForm, StatsOptionsForm,PredictedPieceForm,DateRangeForm,DeltaDaysForm, EmailForm, RegistrationForm, RegistrationFormTermsOfService, - RegistrationFormUniqueEmail,CNsummaryForm,UpdateWindForm, + RegistrationFormUniqueEmail,RegistrationFormSex, + CNsummaryForm,UpdateWindForm, UpdateStreamForm,WorkoutMultipleCompareForm,ChartParamChoiceForm, FusionMetricChoiceForm,BoxPlotChoiceForm,MultiFlexChoiceForm, TrendFlexModalForm,WorkoutSplitForm,WorkoutJoinParamForm, @@ -58,6 +59,7 @@ from rowers.models import ( ) from rowers.models import FavoriteForm,BaseFavoriteFormSet,SiteAnnouncement from rowers.metrics import rowingmetrics,defaultfavoritecharts +from rowers import metrics import rowers.uploads as uploads from django.forms.formsets import formset_factory import StringIO @@ -772,20 +774,25 @@ def add_defaultfavorites(r): # User registration def rower_register_view(request): if request.method == 'POST': - form = RegistrationFormUniqueEmail(request.POST) + #form = RegistrationFormUniqueEmail(request.POST) + form = RegistrationFormSex(request.POST) if form.is_valid(): first_name = form.cleaned_data['first_name'] last_name = form.cleaned_data['last_name'] email = form.cleaned_data['email'] password = form.cleaned_data['password1'] username = form.cleaned_data['username'] + sex = form.cleaned_data['sex'] + birthdate = form.cleaned_data['birthdate'] + weightcategory = form.cleaned_data['weightcategory'] theuser = User.objects.create_user(username,password=password) theuser.first_name = first_name theuser.last_name = last_name theuser.email = email theuser.save() - therower = Rower(user=theuser) + therower = Rower(user=theuser,sex=sex,birthdate=birthdate, + weightcategory=weightcategory) therower.save() @@ -843,7 +850,7 @@ Oh, one more thing. The site is currently in beta and is developing fast. Bear w "registration_form.html", {'form':form}) else: - form = RegistrationFormUniqueEmail() + form = RegistrationFormSex() return render(request, "registration_form.html", {'form':form,}) @@ -6879,8 +6886,14 @@ def workout_stats_view(request,id=0,message="",successmessage=""): if datadf.empty: - return HttpResponse("CSV data file not found") + datadf,row = dataprep.getrowdata_db(id=id) + datadf = dataprep.clean_df_stats(datadf,workstrokesonly=False) + workstrokesonly=False + if datadf.empty: + return HttpResponse("CSV data file not found") + + workoutstateswork = [1,4,5,8,9,6,7] workoutstatesrest = [3] workoutstatetransition = [0,2,10,11,12,13] @@ -6977,6 +6990,15 @@ def workout_stats_view(request,id=0,message="",successmessage=""): except ZeroDivisionError,ValueError: pass + # TRIMP + if datadf['hr'].mean() > 0 and r.sex != 'not specified': + trimp = metrics.calc_trimp(datadf,r.sex,r.max,r.rest) + trimp = int(trimp) + otherstats['trimp'] = { + 'verbose_name': 'TRIMP', + 'value': trimp, + 'unit': 'minutes' + } return render(request, 'workoutstats.html', @@ -7802,7 +7824,7 @@ def workout_comment_view(request,id=0): url = url, ) if request.user != r.user: - a_messages.info(r.user,message) + a_messages.info(r.user,message.encode('ascii','ignore')) res = myqueue(queuehigh, handle_sendemailnewcomment,r.user.first_name, @@ -10222,8 +10244,10 @@ def rower_edit_view(request,message=""): first_name = ucd['first_name'] last_name = ucd['last_name'] email = ucd['email'] + sex = cd['sex'] defaultlandingpage = cd['defaultlandingpage'] weightcategory = cd['weightcategory'] + birthdate = cd['birthdate'] showfavoritechartnotes = cd['showfavoritechartnotes'] getemailnotifications = cd['getemailnotifications'] defaulttimezone=cd['defaulttimezone'] @@ -10242,6 +10266,8 @@ def rower_edit_view(request,message=""): r.getemailnotifications = getemailnotifications r.defaultlandingpage = defaultlandingpage r.showfavoritechartnotes = showfavoritechartnotes + r.sex = sex + r.birthdate = birthdate r.save() form = RowerForm(instance=r) powerform = RowerPowerForm(instance=r)