1767 lines
64 KiB
Python
1767 lines
64 KiB
Python
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 import forms
|
|
from django.contrib.admin.widgets import FilteredSelectMultiple
|
|
from rowers.models import (
|
|
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 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.extras.widgets import SelectDateWidget
|
|
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 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]))
|
|
|
|
class SurveyForm(forms.Form):
|
|
surveydone = forms.ChoiceField(
|
|
required=True,
|
|
choices=(('YES','YES'),('NO','NO')),
|
|
label='Will you take a 2 minute survey to help improve rowsandall?',
|
|
widget = forms.RadioSelect)
|
|
|
|
class FlexibleDecimalField(forms.DecimalField):
|
|
|
|
def to_python(self, value):
|
|
# check decimal symbol
|
|
comma_index = 0
|
|
dot_index = 0
|
|
try:
|
|
comma_index = value.index(',')
|
|
except ValueError:
|
|
pass
|
|
try:
|
|
dot_index = value.index('.')
|
|
except ValueError:
|
|
pass
|
|
if value:
|
|
if comma_index > dot_index:
|
|
value = value.replace('.', '').replace(',', '.')
|
|
return super(FlexibleDecimalField, self).to_python(value)
|
|
|
|
# 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)')
|
|
|
|
def get_metricschoices(mode='water'):
|
|
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]+')'
|
|
|
|
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'),
|
|
widget=forms.CheckboxSelectMultiple,)
|
|
|
|
class Meta:
|
|
mode = 'water'
|
|
|
|
def __init__(self, *args, **kwargs):
|
|
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)
|
|
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)
|
|
|
|
|
|
# login form
|
|
class LoginForm(forms.Form):
|
|
username = forms.CharField()
|
|
password = forms.CharField(widget=forms.PasswordInput())
|
|
|
|
# search form
|
|
class SearchForm(forms.Form):
|
|
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)
|
|
lastname = forms.CharField(max_length=255)
|
|
email = forms.EmailField()
|
|
subject = forms.CharField(max_length=255)
|
|
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'),
|
|
)
|
|
|
|
disqualifiers = {}
|
|
for key, value in disqualificationreasons:
|
|
disqualifiers[key] = value
|
|
|
|
class DisqualificationForm(forms.Form):
|
|
|
|
reason = forms.ChoiceField(required=True,
|
|
choices=disqualificationreasons,
|
|
widget = forms.RadioSelect,)
|
|
|
|
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))
|
|
startdate = forms.DateField(
|
|
initial=timezone.now()-datetime.timedelta(days=15),
|
|
# widget=SelectDateWidget(years=range(1990,2050)),
|
|
widget=AdminDateWidget(), #format='%Y-%m-%d'),
|
|
label='Start Date')
|
|
enddate = forms.DateField(
|
|
initial=timezone.now(),
|
|
widget=AdminDateWidget(), #format='%Y-%m-%d'),
|
|
label='End Date')
|
|
|
|
workouttype = forms.ChoiceField(initial='All',choices=typeselectchoices)
|
|
|
|
metricchoices = (
|
|
("time","duration"),
|
|
("TRIMP","trimp"),
|
|
("rScore","rscore")
|
|
)
|
|
|
|
yaxis = forms.ChoiceField(initial='time',choices=metricchoices,label="Measure by")
|
|
|
|
class Meta:
|
|
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')
|
|
|
|
# Upload the CrewNerd Summary CSV
|
|
class CNsummaryForm(forms.Form):
|
|
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')
|
|
|
|
# little window to type a Team invitation code
|
|
class TeamInviteCodeForm(forms.Form):
|
|
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)
|
|
|
|
# The form used for uploading images
|
|
class ImageForm(forms.Form):
|
|
file = forms.FileField(required=False,
|
|
validators=[validate_image_extension])
|
|
|
|
def __init__(self, *args, **kwargs):
|
|
from django.forms.widgets import HiddenInput
|
|
super(ImageForm, self).__init__(*args, **kwargs)
|
|
|
|
|
|
# The form used for uploading images
|
|
class CourseForm(forms.Form):
|
|
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',
|
|
widget=forms.Textarea)
|
|
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,
|
|
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')
|
|
file = forms.FileField(required=False,
|
|
validators=[must_be_csv])
|
|
notes = forms.CharField(required=False,
|
|
max_length=200,label='Course Notes',
|
|
widget=forms.Textarea)
|
|
|
|
def __init__(self, *args, **kwargs):
|
|
from django.forms.widgets import HiddenInput
|
|
super(StandardsForm, self).__init__(*args, **kwargs)
|
|
|
|
# The form used for uploading files
|
|
class DocumentsForm(forms.Form):
|
|
rpechoices = Workout.rpechoices
|
|
rpechoices = tuple([(-1,'---')]+list(rpechoices))
|
|
title = forms.CharField(required=False)
|
|
file = forms.FileField(required=False,
|
|
validators=[validate_file_extension])
|
|
|
|
workouttype = forms.ChoiceField(required=True,
|
|
choices=Workout.workouttypes,
|
|
label='Workout Type')
|
|
|
|
boattype = forms.ChoiceField(required=True,
|
|
choices=mytypes.boattypes,
|
|
label = "Boat Type")
|
|
|
|
rpe = forms.ChoiceField(required=False,
|
|
choices=rpechoices,
|
|
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')
|
|
class Meta:
|
|
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):
|
|
panels = defaultleft
|
|
|
|
leftpanel = forms.MultipleChoiceField(
|
|
label='',
|
|
choices=workflowleftpanel,
|
|
initial=panels,
|
|
widget=FilteredSelectMultiple(
|
|
('elements'),
|
|
False
|
|
)
|
|
)
|
|
|
|
class Media:
|
|
css = {
|
|
'all':['admin/css/widgets.css',]
|
|
# 'css/uid-manage-form.css'],
|
|
}
|
|
js = ['/admin/jsi18n/']
|
|
|
|
|
|
def __init__(self, *args, **kwargs):
|
|
if 'instance' in kwargs:
|
|
r = kwargs.pop('instance')
|
|
panels = r.workflowleftpanel
|
|
self.base_fields['leftpanel'].initial=panels
|
|
else:
|
|
panels = defaultleft
|
|
|
|
|
|
super(WorkFlowLeftPanelForm,self).__init__(*args, **kwargs)
|
|
|
|
|
|
class WorkFlowMiddlePanelForm(forms.Form):
|
|
panels = defaultmiddle
|
|
|
|
|
|
middlepanel = forms.MultipleChoiceField(
|
|
label='',
|
|
choices=workflowmiddlepanel,
|
|
initial=panels,
|
|
widget=FilteredSelectMultiple(
|
|
('elements'),
|
|
False
|
|
)
|
|
)
|
|
|
|
class Media:
|
|
css = {
|
|
'all':['admin/css/widgets.css',]
|
|
# 'css/uid-manage-form.css'],
|
|
}
|
|
js = ['/admin/jsi18n/']
|
|
|
|
def __init__(self, *args, **kwargs):
|
|
if 'instance' in kwargs:
|
|
r = kwargs.pop('instance')
|
|
panels = r.workflowmiddlepanel
|
|
self.base_fields['middlepanel'].initial=panels
|
|
else:
|
|
panels = defaultmiddle
|
|
|
|
super(WorkFlowMiddlePanelForm,self).__init__(*args, **kwargs)
|
|
|
|
class WorkFlowLeftPanelElement(forms.Form):
|
|
panelchoices = tuple(list(workflowleftpanel)+[('None','None')])
|
|
|
|
panel = forms.ChoiceField(
|
|
label='',
|
|
choices=panelchoices,
|
|
initial='None',
|
|
)
|
|
|
|
class WorkFlowMiddlePanelElement(forms.Form):
|
|
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 = 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)
|
|
plottype = forms.ChoiceField(required=False,
|
|
choices=plotchoices,
|
|
initial='timeplot',
|
|
label='Plot Type')
|
|
upload_to_C2 = forms.BooleanField(initial=False,required=False,
|
|
label='Export to Concept2 logbook')
|
|
upload_to_Strava = forms.BooleanField(initial=False,required=False,
|
|
label='Export to Strava')
|
|
upload_to_SportTracks = forms.BooleanField(initial=False,required=False,
|
|
label='Export to SportTracks')
|
|
upload_to_RunKeeper = forms.BooleanField(initial=False,required=False,
|
|
label='Export to RunKeeper')
|
|
upload_to_MapMyFitness = forms.BooleanField(initial=False,
|
|
required=False,
|
|
label='Export to MapMyFitness')
|
|
upload_to_TrainingPeaks = forms.BooleanField(initial=False,
|
|
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,
|
|
label='Make Workout Private')
|
|
|
|
submitrace = forms.ChoiceField(
|
|
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())
|
|
|
|
class Meta:
|
|
fields = ['make_plot','plottype','upload_toc2','makeprivate']
|
|
|
|
def __init__(self, *args, **kwargs):
|
|
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)
|
|
|
|
registrations2 = VirtualRaceResult.objects.filter(
|
|
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,'---')]
|
|
|
|
noregistrations = []
|
|
for ra in VirtualRace.objects.filter(registration_closure__gt=timezone.now(),sessiontype='race'):
|
|
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'):
|
|
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]:
|
|
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)
|
|
else:
|
|
registrations = IndoorVirtualRaceResult.objects.filter(race=therace,userid=r.id)
|
|
|
|
if registrations.count()==0:
|
|
race = VirtualRace.objects.get(id=raceid)
|
|
choices = [(-int(raceid),race.name)]
|
|
else:
|
|
choices = [(r.id,str(r)) for r in registrations]
|
|
choices = choices+[(0,'---')]
|
|
|
|
if races:
|
|
self.fields['submitrace'].choices = choices
|
|
else:
|
|
del self.fields['submitrace']
|
|
|
|
|
|
# The form to indicate additional actions to be performed immediately
|
|
# after a successful upload. This version allows the Team manager to select
|
|
# 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)
|
|
plottype = forms.ChoiceField(required=False,
|
|
choices=plotchoices,
|
|
initial='timeplot',
|
|
label='Plot Type')
|
|
|
|
upload_to_C2 = forms.BooleanField(initial=False,required=False,
|
|
label='Export to Concept2 logbook')
|
|
upload_to_Strava = forms.BooleanField(initial=False,required=False,
|
|
label='Export to Strava')
|
|
upload_to_SportTracks = forms.BooleanField(initial=False,required=False,
|
|
label='Export to SportTracks')
|
|
upload_to_RunKeeper = forms.BooleanField(initial=False,required=False,
|
|
label='Export to RunKeeper')
|
|
upload_to_MapMyFitness = forms.BooleanField(initial=False,
|
|
required=False,
|
|
label='Export to MapMyFitness')
|
|
upload_to_TrainingPeaks = forms.BooleanField(initial=False,
|
|
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,
|
|
label='Make Workout Private')
|
|
|
|
|
|
|
|
class Meta:
|
|
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'),
|
|
)
|
|
splittime = forms.TimeField(input_formats=['%H:%M:%S.%f',
|
|
'%H:%M:%S',
|
|
'%H:%M:%S',
|
|
'%M:%S.%f',
|
|
'%M:%S',
|
|
'%M'],
|
|
label = 'Split Time')
|
|
splitmode = forms.MultipleChoiceField(
|
|
initial=['keep original',
|
|
'keep first',
|
|
'keep second',
|
|
'firstprivate',
|
|
'secondprivate'],
|
|
label='Split Mode',
|
|
choices=splitchoices,
|
|
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'),
|
|
)
|
|
|
|
workouttypes = forms.MultipleChoiceField(
|
|
required=True,
|
|
choices=choices,
|
|
label='Workout Types',
|
|
initial = [a for a,b in choices],
|
|
)
|
|
|
|
class PredictedPieceForm(forms.Form):
|
|
unitchoices = (
|
|
('t','minutes'),
|
|
('d','meters'),
|
|
)
|
|
|
|
rankingdistancechoices = []
|
|
rankingdurationchoices = []
|
|
for d in rankingdistances:
|
|
thetuple = (d,str(d)+' m')
|
|
rankingdistancechoices.append(thetuple)
|
|
|
|
for d in rankingdurations:
|
|
timestr = d.strftime("%H:%M:%S")
|
|
thetuple = (timestr,timestr)
|
|
rankingdurationchoices.append(thetuple)
|
|
|
|
trankingdistances = forms.MultipleChoiceField(
|
|
required=True,
|
|
choices=rankingdistancechoices,initial=rankingdistances,
|
|
label='Ranking Distances'
|
|
)
|
|
|
|
trankingdurations = forms.MultipleChoiceField(
|
|
required=True,
|
|
choices=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')
|
|
|
|
class Meta:
|
|
fields = ['value','pieceunit']
|
|
|
|
class PredictedPieceFormNoDistance(forms.Form):
|
|
|
|
rankingdurationchoices = []
|
|
|
|
for d in rankingdurations:
|
|
timestr = d.strftime("%H:%M:%S")
|
|
thetuple = (timestr,timestr)
|
|
rankingdurationchoices.append(thetuple)
|
|
|
|
|
|
trankingdurations = forms.MultipleChoiceField(
|
|
required=True,
|
|
choices=rankingdurationchoices,
|
|
initial=[a for a,b in rankingdurationchoices],
|
|
label='Ranking Durations'
|
|
)
|
|
|
|
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')
|
|
streamunit = forms.ChoiceField(required=True,
|
|
choices=unitchoices,
|
|
initial='m',
|
|
label='Unit')
|
|
|
|
class Meta:
|
|
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')
|
|
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')
|
|
|
|
class Meta:
|
|
fields = ['dist1','dist2',
|
|
'vwind1','vwind2',
|
|
'windunit',
|
|
'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'),
|
|
label='Start Date')
|
|
enddate = forms.DateField(
|
|
initial=timezone.now(),
|
|
widget=AdminDateWidget(), #format='%Y-%m-%d'),
|
|
label='End Date')
|
|
|
|
class Meta:
|
|
fields = ['startdate','enddate']
|
|
input_formats=("%Y-%m-%d")
|
|
dateTimeOptions = {
|
|
'format': '%Y-%m-%d',
|
|
'autoclose': True,
|
|
}
|
|
|
|
class FitnessMetricForm(forms.Form):
|
|
startdate = forms.DateField(
|
|
initial=timezone.now()-datetime.timedelta(days=365),
|
|
# widget=SelectDateWidget(years=range(1990,2050)),
|
|
widget=AdminDateWidget(),
|
|
label='Start Date')
|
|
enddate = forms.DateField(
|
|
initial=timezone.now(),
|
|
widget=AdminDateWidget(),
|
|
label='End Date')
|
|
|
|
modechoices = (
|
|
('rower','indoor rower'),
|
|
('water','on the water')
|
|
)
|
|
|
|
mode = forms.ChoiceField(required=True,
|
|
choices=modechoices,
|
|
initial='rower',
|
|
label='Workout Mode'
|
|
)
|
|
|
|
class Meta:
|
|
fields = ['startdate','enddate','mode']
|
|
|
|
class PerformanceManagerForm(forms.Form):
|
|
startdate = forms.DateField(
|
|
initial=timezone.now()-datetime.timedelta(days=365),
|
|
# widget=SelectDateWidget(years=range(1990,2050)),
|
|
widget=AdminDateWidget(),
|
|
label='Start Date')
|
|
enddate = forms.DateField(
|
|
initial=timezone.now(),
|
|
widget=AdminDateWidget(),
|
|
label='End Date')
|
|
|
|
metricchoices = (
|
|
('hrtss','Use Heart Rate Data'),
|
|
('rscore','Use Power and Heart Rate Data'),
|
|
)
|
|
|
|
metricchoice = forms.ChoiceField(
|
|
required=True,
|
|
choices=metricchoices,
|
|
initial='hrtss',
|
|
label='Data',
|
|
widget=forms.RadioSelect
|
|
)
|
|
|
|
dofatigue = forms.BooleanField(required=False,initial=False,
|
|
label='Fatigue')
|
|
|
|
doform = forms.BooleanField(required=False,initial=False,
|
|
label='Freshness')
|
|
|
|
|
|
class FitnessFitForm(forms.Form):
|
|
startdate = forms.DateField(
|
|
initial=timezone.now()-datetime.timedelta(days=365),
|
|
# widget=SelectDateWidget(years=range(1990,2050)),
|
|
widget=AdminDateWidget(),
|
|
label='Start Date')
|
|
enddate = forms.DateField(
|
|
initial=timezone.now(),
|
|
widget=AdminDateWidget(),
|
|
label='End Date')
|
|
|
|
modechoices = (
|
|
('rower','indoor rower'),
|
|
('water','on the water')
|
|
)
|
|
|
|
metricchoices = (
|
|
('trimp','TRIMP'),
|
|
('rscore','rScore')
|
|
)
|
|
|
|
modelchoices = (
|
|
('banister','Banister Impulse-Response model'),
|
|
('tsb','Coggan Training Stress Balance model'),
|
|
)
|
|
|
|
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')
|
|
|
|
kfitness = forms.IntegerField(initial=42,required=True,
|
|
label='Fitness 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')
|
|
|
|
modelchoice = forms.ChoiceField(required=True,
|
|
choices=modelchoices,
|
|
initial='tsb',
|
|
label='Model to use')
|
|
|
|
# temporary
|
|
k1 = forms.FloatField(required=True,initial=1.0,
|
|
label='k1')
|
|
k2 = forms.FloatField(required=True,initial=1.0,
|
|
label='k2')
|
|
p0 = forms.IntegerField(required=True,initial=100,label='Unfit Performance')
|
|
|
|
mode = forms.ChoiceField(required=True,
|
|
choices=modechoices,
|
|
initial='rower',
|
|
label='Workout Mode'
|
|
)
|
|
|
|
class Meta:
|
|
fields = ['startdate','enddate','mode','fitnesstest',
|
|
'kfitness','kfatigue','metricchoice',
|
|
'k1','k2','p0']
|
|
|
|
class SessionDateShiftForm(forms.Form):
|
|
shiftstartdate = forms.DateField(
|
|
initial=timezone.now(),
|
|
widget=AdminDateWidget(),
|
|
label='Shift to start on')
|
|
|
|
class Meta:
|
|
fields = ['shiftstartdate']
|
|
|
|
# Form used to select workouts for the past N days
|
|
class DeltaDaysForm(forms.Form):
|
|
deltadays = forms.IntegerField(initial=7,required=False,label='')
|
|
|
|
class RegistrationForm(UserCreationForm):
|
|
"""
|
|
Form for registering a new user account.
|
|
Validates that the requested username is not already in use, and
|
|
requires the password to be entered twice to catch typos.
|
|
Subclasses should feel free to add any additional validation they
|
|
need, but should avoid defining a ``save()`` method -- the actual
|
|
saving of collected user data is delegated to the active
|
|
registration backend.
|
|
"""
|
|
required_css_class = 'required'
|
|
email = forms.EmailField(label="E-mail")
|
|
|
|
class Meta:
|
|
model = User
|
|
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'}),
|
|
}
|
|
|
|
class RegistrationFormTermsOfService(RegistrationForm):
|
|
"""
|
|
Subclass of ``RegistrationForm`` which adds a required checkbox
|
|
for agreeing to a site's Terms of Service.
|
|
"""
|
|
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):
|
|
"""
|
|
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']):
|
|
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'),
|
|
)
|
|
|
|
adaptivecategories = mytypes.adaptivetypes
|
|
|
|
thisyear = timezone.now().year
|
|
|
|
birthdate = forms.DateTimeField(
|
|
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:
|
|
raise forms.ValidationError('Must be at least 16 years old to register')
|
|
return self.cleaned_data['birthdate']
|
|
|
|
sex = forms.ChoiceField(required=False,
|
|
choices=sexcategories,
|
|
initial='not specified',
|
|
label='Sex')
|
|
|
|
weightcategory = forms.ChoiceField(label='Weight Category',
|
|
choices=weightcategories,initial='hwt',required=False)
|
|
|
|
adaptiveclass = forms.ChoiceField(label='Adaptive Classification',
|
|
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):
|
|
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')
|
|
)
|
|
|
|
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())
|
|
|
|
# Form used to update interval stats
|
|
class IntervalUpdateForm(forms.Form):
|
|
|
|
def __init__(self, *args, **kwargs):
|
|
typechoices = (
|
|
(1,'single time'),
|
|
(2,'single distance'),
|
|
(3,'rest (time based)'),
|
|
(3,'rest (distance based)'),
|
|
(4,'work (time based)'),
|
|
(5,'work (distance based)'),
|
|
)
|
|
aantal = int(kwargs.pop('aantal'))
|
|
super(IntervalUpdateForm, self).__init__(*args, **kwargs)
|
|
|
|
for i in range(aantal):
|
|
self.fields['intervalt_%s' % i] = forms.DurationField(label='Time '+str(i+1))
|
|
self.fields['intervald_%s' % i] = forms.IntegerField(label='Distance '+str(i+1))
|
|
self.fields['type_%s' % i] = forms.ChoiceField(choices=typechoices,
|
|
required=True,
|
|
initial=4,
|
|
label = 'Type '+str(i+1))
|
|
self.fields['intervalt_%s' % i].widget.attrs['style'] = 'width:76px; height: 16px;'
|
|
self.fields['intervald_%s' % i].widget.attrs['style'] = 'width:76px; height: 16px;'
|
|
self.fields['type_%s' % i].widget.attrs['style'] = 'width:156px; height: 22px;'
|
|
self.fields['intervald_%s' % i].widget = forms.TimeInput(format='%H:%M:%S.%f')
|
|
|
|
boattypes = mytypes.boattypes
|
|
workouttypes = mytypes.workouttypes
|
|
ww = list(workouttypes)
|
|
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']
|
|
|
|
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')
|
|
|
|
class AnalysisOptionsForm(forms.Form):
|
|
modality = forms.ChoiceField(choices=workouttypes,
|
|
label='Workout Type',
|
|
initial='all')
|
|
waterboattype = forms.MultipleChoiceField(choices=boattypes,
|
|
label='Water Boat Type',
|
|
initial = mytypes.waterboattype)
|
|
rankingonly = forms.BooleanField(initial=False,
|
|
label='Only Ranking Pieces',
|
|
required=False)
|
|
|
|
|
|
# form to select modality and boat type for trend flex
|
|
class TrendFlexModalForm(forms.Form):
|
|
modality = forms.ChoiceField(choices=workouttypes,
|
|
label='Workout Type',
|
|
initial='all')
|
|
waterboattype = forms.MultipleChoiceField(choices=boattypes,
|
|
label='Water Boat Type',
|
|
initial = mytypes.waterboattype)
|
|
rankingonly = forms.BooleanField(initial=False,
|
|
label='Only Ranking Pieces',
|
|
required=False)
|
|
|
|
|
|
|
|
# This form sets options for the summary stats page
|
|
class StatsOptionsForm(forms.Form):
|
|
includereststrokes = forms.BooleanField(initial=False,label='Include Rest Strokes',required=False)
|
|
rankingonly = forms.BooleanField(initial=False,
|
|
label='Only Ranking Pieces',required=False)
|
|
water = forms.BooleanField(initial=False,required=False)
|
|
waterboattype = forms.MultipleChoiceField(choices=boattypes,
|
|
label='Water Boat Type',
|
|
widget=forms.CheckboxSelectMultiple(),
|
|
initial = mytypes.waterboattype)
|
|
|
|
|
|
|
|
def __init__(self, *args, **kwargs):
|
|
super(StatsOptionsForm, self).__init__(*args,**kwargs)
|
|
|
|
for type in mytypes.checktypes:
|
|
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)
|
|
|
|
def __init__(self, *args, **kwargs):
|
|
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:
|
|
self.fields['plan'].queryset = PaidPlan.objects.filter(
|
|
paymentprocessor=paymentprocessor,
|
|
active=True
|
|
).exclude(
|
|
shortname="basic"
|
|
).order_by(
|
|
"price","shortname"
|
|
)
|
|
if rower and not includeall:
|
|
try:
|
|
amount = rower.paidplan.price
|
|
except AttributeError:
|
|
amount = 0
|
|
self.fields['plan'].queryset = PaidPlan.objects.filter(
|
|
paymentprocessor=rower.paymentprocessor,
|
|
active=True
|
|
).exclude(
|
|
price__lte=amount
|
|
).order_by(
|
|
"price","shortname"
|
|
)
|
|
|
|
|
|
class CourseSelectForm(forms.Form):
|
|
course = forms.ModelChoiceField(queryset=GeoCourse.objects.filter())
|
|
|
|
def __init__(self, *args, **kwargs):
|
|
course = kwargs.pop('course',None)
|
|
manager = kwargs.pop('manager',None)
|
|
super(CourseSelectForm,self).__init__(*args,**kwargs)
|
|
if course is not None:
|
|
print('aap',course)
|
|
d_min = 0.5*course.distance
|
|
d_max = 2*course.distance
|
|
country = course.country
|
|
countries = ['unknown',country]
|
|
print(countries)
|
|
self.fields['course'].queryset = self.fields['course'].queryset.filter(
|
|
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)
|
|
print(self.fields['course'].queryset)
|
|
|
|
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
|
|
|
|
class WorkoutMultipleCompareForm(forms.Form):
|
|
workouts = forms.ModelMultipleChoiceField(
|
|
queryset=Workout.objects.filter(),
|
|
widget=forms.CheckboxSelectMultiple())
|
|
|
|
def __init__(self, *args, **kwargs):
|
|
super(WorkoutMultipleCompareForm,self).__init__(*args,**kwargs)
|
|
self.fields['workouts'].queryset = Workout.objects.filter()
|
|
|
|
|
|
class PlannedSessionMultipleCloneForm(forms.Form):
|
|
plannedsessions = forms.ModelMultipleChoiceField(
|
|
queryset=PlannedSession.objects.all(),
|
|
widget=forms.CheckboxSelectMultiple(),
|
|
label='Planned Sessions'
|
|
)
|
|
|
|
|
|
grouplabels = axlabels.copy()
|
|
grouplabels['date'] = 'Date'
|
|
grouplabels['workoutid'] = 'Workout'
|
|
grouplabels.pop('None')
|
|
grouplabels.pop('time')
|
|
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]))
|
|
|
|
from rowers.utils import palettes
|
|
|
|
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'),
|
|
)
|
|
|
|
|
|
|
|
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]))
|
|
|
|
|
|
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'),
|
|
)
|
|
|
|
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]))
|
|
|
|
function = forms.ChoiceField(choices=analysischoices,initial='boxplot',
|
|
label='Analysis')
|
|
xaxis = forms.ChoiceField(
|
|
choices=axchoices,label='X-Axis',required=True,initial='spm')
|
|
yaxis1 = forms.ChoiceField(
|
|
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')
|
|
|
|
plotfield = forms.ChoiceField(choices=parchoices,initial='spm',
|
|
label='Metric')
|
|
xparam = forms.ChoiceField(choices=parchoicesmultiflex,
|
|
initial='hr',
|
|
label='X axis')
|
|
yparam = forms.ChoiceField(choices=parchoicesmultiflex,
|
|
initial='pace',
|
|
label='Y axis')
|
|
|
|
groupby = forms.ChoiceField(choices=groupchoices,initial='spm',
|
|
label='Group By')
|
|
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',
|
|
initial='monochrome_blue')
|
|
|
|
spmmin = forms.FloatField(initial=15,
|
|
required=False,label = 'Min SPM')
|
|
spmmax = forms.FloatField(initial=55,
|
|
required=False,label = 'Max SPM')
|
|
workmin = forms.FloatField(initial=0,
|
|
required=False,label = 'Min Work per Stroke')
|
|
workmax = forms.FloatField(initial=1500,
|
|
required=False,label = 'Max Work per Stroke')
|
|
|
|
cpfitchoices = (
|
|
('data','Fit to Selected Workouts'),
|
|
('automatic','Critical Power Rolling Data')
|
|
)
|
|
|
|
cpfit = forms.ChoiceField(choices=cpfitchoices,
|
|
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)
|
|
|
|
includereststrokes = forms.BooleanField(initial=False,
|
|
required=False,
|
|
label='Include Rest Strokes')
|
|
|
|
|
|
|
|
class BoxPlotChoiceForm(forms.Form):
|
|
yparam = forms.ChoiceField(choices=parchoices,initial='spm',
|
|
label='Metric')
|
|
spmmin = forms.FloatField(initial=15,
|
|
required=False,label = 'Min SPM')
|
|
spmmax = forms.FloatField(initial=55,
|
|
required=False,label = 'Max SPM')
|
|
workmin = forms.FloatField(initial=0,
|
|
required=False,label = 'Min Work per Stroke')
|
|
workmax = forms.FloatField(initial=1500,
|
|
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',
|
|
label='X axis')
|
|
yparam = forms.ChoiceField(choices=parchoicesmultiflex,
|
|
initial='pace',
|
|
label='Y axis')
|
|
groupby = forms.ChoiceField(choices=groupchoices,initial='spm',
|
|
label='Group By')
|
|
binsize = forms.FloatField(initial=1,required=False,label = 'Bin Size')
|
|
spmmin = forms.FloatField(initial=15,
|
|
required=False,label = 'Min SPM')
|
|
spmmax = forms.FloatField(initial=55,
|
|
required=False,label = 'Max SPM')
|
|
workmin = forms.FloatField(initial=0,
|
|
required=False,label = 'Min Work per Stroke')
|
|
workmax = forms.FloatField(initial=1500,
|
|
required=False,label = 'Max Work per Stroke')
|
|
ploterrorbars = forms.BooleanField(initial=False,
|
|
required=False,
|
|
label='Plot Error Bars')
|
|
includereststrokes = forms.BooleanField(initial=False,
|
|
required=False,
|
|
label='Include Rest Strokes')
|
|
palette = forms.ChoiceField(choices=palettechoices,
|
|
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')
|
|
teamid = forms.IntegerField(widget=forms.HiddenInput())
|
|
|
|
formaxlabels.pop('time')
|
|
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,
|
|
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'),
|
|
)
|
|
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())
|
|
|
|
def __init__(self, *args, **kwargs):
|
|
super(FusionMetricChoiceForm, self).__init__(*args, **kwargs)
|
|
formaxlabels2 = formaxlabels.copy()
|
|
# need to add code to remove "empty" fields
|
|
|
|
if self.instance.id is not None:
|
|
id = self.instance.id
|
|
df = dataprep.getrowdata_db(id=id)[0]
|
|
|
|
labeldict = {key:value for key,value in self.fields['columns'].choices}
|
|
|
|
for label in labeldict:
|
|
if df.loc[:,label].std() == 0:
|
|
try:
|
|
formaxlabels2.pop(label)
|
|
except KeyError:
|
|
pass
|
|
|
|
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)
|
|
|
|
self.fields['plannedsession'] = forms.ChoiceField(
|
|
label='Sessions',
|
|
choices = sessionchoices,
|
|
widget = forms.RadioSelect,
|
|
initial=initialsession
|
|
)
|
|
|
|
|
|
class WorkoutSessionSelectForm(forms.Form):
|
|
|
|
def __init__(self, workoutdata, *args, **kwargs):
|
|
|
|
super(WorkoutSessionSelectForm, self).__init__(*args, **kwargs)
|
|
|
|
self.fields['workouts'] = forms.MultipleChoiceField(
|
|
label='Workouts',
|
|
choices = workoutdata['choices'],
|
|
initial=workoutdata['initial'],
|
|
widget = forms.CheckboxSelectMultiple,
|
|
)
|
|
|
|
|
|
|
|
class RaceResultFilterForm(forms.Form):
|
|
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'),
|
|
)
|
|
|
|
weightcategories = (
|
|
('hwt','heavy-weight'),
|
|
('lwt','light-weight'),
|
|
)
|
|
|
|
adaptivecategories = mytypes.adaptivetypes
|
|
|
|
sex = forms.MultipleChoiceField(
|
|
choices=sexchoices,
|
|
initial=['male','female','mixed'],
|
|
label='Gender',
|
|
widget=forms.CheckboxSelectMultiple())
|
|
|
|
boatclass = forms.MultipleChoiceField(
|
|
choices=boatclasses,
|
|
initial=boatclassinitial,
|
|
label='Boat/Erg Class',
|
|
widget=forms.CheckboxSelectMultiple())
|
|
|
|
boattype = forms.MultipleChoiceField(
|
|
choices=boattypes,
|
|
label='Boat Type',
|
|
initial=mytypes.waterboattype,
|
|
widget=forms.CheckboxSelectMultiple())
|
|
|
|
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'],
|
|
widget=forms.CheckboxSelectMultiple())
|
|
|
|
adaptivecategory = forms.MultipleChoiceField(
|
|
choices=adaptivecategories,
|
|
label='Adaptive Class',
|
|
initial=['None','PR1','PR2','PR3','FES'],
|
|
widget=forms.CheckboxSelectMultiple())
|
|
|
|
entrycategory = forms.MultipleChoiceField(
|
|
choices = [],
|
|
label = 'Groups',
|
|
widget=forms.CheckboxSelectMultiple()
|
|
)
|
|
|
|
def __init__(self, *args, **kwargs):
|
|
if 'records' in kwargs:
|
|
records = kwargs.pop('records',None)
|
|
|
|
super(RaceResultFilterForm,self).__init__(*args,**kwargs)
|
|
|
|
if records:
|
|
# group
|
|
thecategories = [record.entrycategory for record in records]
|
|
thecategories = list(set(thecategories))
|
|
if len(thecategories) <= 1:
|
|
del self.fields['entrycategory']
|
|
else:
|
|
categorychoices = []
|
|
for category in thecategories:
|
|
if category is not None:
|
|
categorychoices.append(
|
|
(category.id,category)
|
|
)
|
|
self.fields['entrycategory'].choices = categorychoices
|
|
self.fields['entrycategory'].initial = [cat[0] for cat in categorychoices]
|
|
|
|
# sex
|
|
thesexes = [record.sex for record in records]
|
|
thesexes = list(set(thesexes))
|
|
|
|
if len(thesexes)<= 1:
|
|
del self.fields['sex']
|
|
else:
|
|
sexchoices = []
|
|
for choice in self.fields['sex'].choices:
|
|
if choice[0] in thesexes:
|
|
sexchoices.append(choice)
|
|
self.fields['sex'].choices = sexchoices
|
|
|
|
# boatclass
|
|
theboatclasses = [record.boatclass for record in records]
|
|
theboatclasses = list(set(theboatclasses))
|
|
|
|
if len(theboatclasses)<= 1:
|
|
del self.fields['boatclass']
|
|
else:
|
|
boatclasschoices = []
|
|
for choice in self.fields['boatclass'].choices:
|
|
if choice[0] in theboatclasses:
|
|
boatclasschoices.append(choice)
|
|
self.fields['boatclass'].choices = boatclasschoices
|
|
|
|
# boattype
|
|
try:
|
|
theboattypees = [record.boattype for record in records]
|
|
theboattypees = list(set(theboattypees))
|
|
except AttributeError:
|
|
theboattypees = []
|
|
|
|
if len(theboattypees)<= 1:
|
|
del self.fields['boattype']
|
|
else:
|
|
boattypechoices = []
|
|
for choice in self.fields['boattype'].choices:
|
|
if choice[0] in theboattypees:
|
|
boattypechoices.append(choice)
|
|
self.fields['boattype'].choices = boattypechoices
|
|
|
|
# weightcategory
|
|
theweightcategoryes = [record.weightcategory for record in records]
|
|
theweightcategoryes = list(set(theweightcategoryes))
|
|
|
|
if len(theweightcategoryes)<= 1:
|
|
del self.fields['weightcategory']
|
|
else:
|
|
weightcategorychoices = []
|
|
for choice in self.fields['weightcategory'].choices:
|
|
if choice[0] in theweightcategoryes:
|
|
weightcategorychoices.append(choice)
|
|
self.fields['weightcategory'].choices = weightcategorychoices
|
|
|
|
# adaptivecategory
|
|
theadaptivecategoryes = [record.adaptiveclass for record in records]
|
|
theadaptivecategoryes = list(set(theadaptivecategoryes))
|
|
|
|
if len(theadaptivecategoryes)<= 1:
|
|
del self.fields['adaptivecategory']
|
|
else:
|
|
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):
|
|
|
|
super(WorkoutRaceSelectForm, self).__init__(*args, **kwargs)
|
|
|
|
self.fields['workouts'] = forms.ChoiceField(
|
|
label='Workouts',
|
|
choices = workoutdata['choices'],
|
|
initial=workoutdata['initial'],
|
|
widget=forms.RadioSelect,
|
|
)
|
|
|
|
self.fields['record'] = forms.ChoiceField(
|
|
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='',
|
|
widget=forms.Textarea()
|
|
)
|
|
|
|
# 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)
|
|
|
|
self.fields['team'].queryset = Team.objects.filter(manager=user)
|
|
|
|
|
|
class PlannedSessionTeamForm(forms.Form):
|
|
team = forms.ModelMultipleChoiceField(
|
|
queryset=Team.objects.all(),
|
|
required=False,
|
|
widget=forms.CheckboxSelectMultiple())
|
|
|
|
def __init__(self, user, *args, **kwargs):
|
|
super(PlannedSessionTeamForm,self).__init__(*args, **kwargs)
|
|
|
|
self.fields['team'].queryset = Team.objects.filter(manager=user)
|
|
|
|
def clean(self):
|
|
if any(self.errors):
|
|
return
|
|
|
|
cd = self.cleaned_data
|
|
if not cd['team']:
|
|
raise forms.ValidationError(
|
|
'You must select at least one team'
|
|
)
|
|
|
|
return cd
|
|
|
|
|
|
class PlannedSessionTeamMemberForm(forms.Form):
|
|
members = forms.ModelMultipleChoiceField(
|
|
queryset=Rower.objects.all(),
|
|
widget=forms.CheckboxSelectMultiple())
|
|
|
|
def __init__(self, thesession, *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:
|
|
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'),
|
|
)
|
|
|
|
regattatype = forms.ChoiceField(
|
|
label='Type',
|
|
choices = regattatypechoices,
|
|
initial = 'upcoming',
|
|
)
|
|
|
|
country = forms.ChoiceField(
|
|
label='Country',
|
|
choices = get_countries()
|
|
)
|
|
|
|
|
|
def __init__(self, *args, **kwargs):
|
|
super(VirtualRaceSelectForm, self).__init__(*args, **kwargs)
|
|
self.fields['country'] = forms.ChoiceField(
|
|
choices = get_countries(),initial='All'
|
|
)
|
|
|
|
|
|
class FlexOptionsForm(forms.Form):
|
|
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',
|
|
label='Chart Type')
|
|
|
|
class ForceCurveOptionsForm(forms.Form):
|
|
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',
|
|
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]))
|
|
|
|
|
|
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]))
|
|
|
|
class StravaChartForm(forms.Form):
|
|
xaxischoices = (
|
|
('cumdist','Distance'),
|
|
('time','Time')
|
|
)
|
|
|
|
xaxis = forms.ChoiceField(
|
|
choices = xaxischoices,label='X-Axis',required=True)
|
|
|
|
|
|
yaxis1 = forms.ChoiceField(
|
|
choices=yaxchoices,label='First Chart',required=True)
|
|
yaxis2 = forms.ChoiceField(
|
|
choices=yaxchoices2,label='Second Chart',required=True)
|
|
|
|
yaxis3 = forms.ChoiceField(
|
|
choices=yaxchoices,label='Third Chart',required=True)
|
|
yaxis4 = forms.ChoiceField(
|
|
choices=yaxchoices2,label='Fourth Chart',required=True)
|
|
|
|
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
|
|
)
|
|
|
|
axchoicesbasicx = []
|
|
axchoicesbasicy = []
|
|
|
|
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]))
|
|
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)'))
|
|
|
|
|
|
if not user_is_not_basic(rower.user):
|
|
self.fields['xaxis'].choices = axchoicesbasicx
|
|
self.fields['yaxis1'].choices = axchoicesbasicy
|
|
self.fields['yaxis2'].choices = axchoicesbasicy
|
|
self.fields['yaxis3'].choices = axchoicesbasicy
|
|
self.fields['yaxis4'].choices = axchoicesbasicy
|
|
|
|
|
|
class FlexAxesForm(forms.Form):
|
|
|
|
xaxis = forms.ChoiceField(
|
|
choices=axchoices,label='X-Axis',required=True)
|
|
yaxis1 = forms.ChoiceField(
|
|
choices=yaxchoices,label='Left Axis',required=True)
|
|
yaxis2 = forms.ChoiceField(
|
|
choices=yaxchoices2,label='Right Axis',required=True)
|
|
|
|
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
|
|
)
|
|
|
|
axchoicesbasicx = []
|
|
axchoicesbasicy = []
|
|
|
|
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]))
|
|
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)'))
|
|
|
|
|
|
if not user_is_not_basic(rower.user):
|
|
self.fields['xaxis'].choices = axchoicesbasicx
|
|
self.fields['yaxis1'].choices = axchoicesbasicy
|
|
self.fields['yaxis2'].choices = axchoicesbasicy
|