from rowers.utils import rankingdistances, rankingdurations from rowers.utils import ( workflowleftpanel, workflowmiddlepanel, defaultleft, defaultmiddle, workout_name_element, ) from rowers.utils import palettes from time import strftime from django import forms from django.contrib.admin.widgets import FilteredSelectMultiple from rowers.models import ( Workout, Rower, Team, PlannedSession, GeoCourse, VirtualRace, VirtualRaceResult, IndoorVirtualRaceResult, PaidPlan, InStrokeAnalysis, ForceCurveAnalysis ) 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_recaptcha.fields import ReCaptchaField from django_recaptcha.widgets import ReCaptchaV3 from django.core.exceptions import ValidationError 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 from rowers import models formaxlabels = axlabels.copy() formaxlabels.pop('None') parchoices = list(sorted(formaxlabels.items(), key=lambda x: x[1])) class DeepWaterLoginForm(forms.Form): username = forms.CharField( max_length=150, label='Username', widget=forms.TextInput(attrs={'placeholder': 'Username'})) password = forms.CharField( max_length=128, label='Password', widget=forms.PasswordInput(attrs={'placeholder': 'Password'})) def clean(self): cleaned_data = super(DeepWaterLoginForm, self).clean() if not cleaned_data.get('username') or not cleaned_data.get('password'): raise forms.ValidationError("Please enter both username and password.") return cleaned_data 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: # pragma: no cover pass if value: if comma_index > dot_index: # pragma: no cover value = value.replace('.', '').replace(',', '.') return super(FlexibleDecimalField, self).to_python(value) class ResampleForm(forms.Form): # add resamplechoice field, the result is a True or False boolean, labels are "overwrite" and "create copy" resamplechoice = forms.ChoiceField( required=True, choices=((True, 'overwrite'), (False, 'create copy')), label='Resample choice', widget=forms.RadioSelect) class TrainingZonesForm(forms.Form): startdate = forms.DateField( initial=timezone.now()-datetime.timedelta(days=42), widget=AdminDateWidget(), # format='%Y-%m-%d'), label='Start Date') enddate = forms.DateField( initial=timezone.now(), # widget=SelectDateWidget(years=range(1990,2050)), widget=AdminDateWidget(), # format='%Y-%m-%d'), label='End Date') class InstantPlanSelectForm(forms.Form): datechoices = ( ('startdate', 'start date'), ('enddate', 'end date'), ('target', 'target') ) name = forms.CharField(max_length=255, label='Plan Name', required=False) startdate = forms.DateField( initial=timezone.now(), # widget=SelectDateWidget(years=range(1990,2050)), widget=AdminDateWidget(), # format='%Y-%m-%d'), label='Start Date') enddate = forms.DateField( initial=timezone.now()+datetime.timedelta(days=21), widget=AdminDateWidget(), # format='%Y-%m-%d'), label='End Date') plan_past_days = forms.BooleanField(initial=False, required=False, label='Insert sessions for the past') target = forms.ChoiceField(required=False) datechoice = forms.ChoiceField(choices=datechoices, initial='enddate', label='Plan by target, start or end date', widget=forms.RadioSelect) notes = forms.CharField(required=False, max_length=200, label='Plan Notes', widget=forms.Textarea) def __init__(self, *args, **kwargs): targets = kwargs.pop('targets', None) instantplan = kwargs.pop('instantplan', None) super(InstantPlanSelectForm, self).__init__(*args, **kwargs) if targets: targetchoices = [(x.id, x) for x in targets] targetchoices.append((None, '---')) self.fields['target'].choices = targetchoices else: datechoices = ( ('startdate', 'start date'), ('enddate', 'end date'), ) self.fields['datechoice'].choices = datechoices self.fields['datechoice'].label = 'Plan by start or end date' self.fields.pop('target') if instantplan: self.fields['enddate'].initial = timezone.now( )+datetime.timedelta(days=instantplan.duration) # Instroke Metrics interactive chart form class InstrokeForm(forms.Form): name = forms.CharField(initial="", max_length=200,required=False) metric = forms.ChoiceField(label='metric',choices=(('a','a'),('b','b'))) individual_curves = forms.BooleanField(label='individual curves',initial=False, required=False) spm_min = forms.FloatField(initial=15,label='SPM Min',widget=HiddenInput) spm_max = forms.FloatField(initial=45,label='SPM Max',widget=HiddenInput) activeminutesmin = forms.FloatField( required=False, initial=0, widget=forms.HiddenInput()) activeminutesmax = forms.FloatField( required=False, initial=0, widget=forms.HiddenInput()) notes = forms.CharField(required=False, max_length=200, label='Notes', widget=forms.Textarea) def __init__(self, *args, **kwargs): # pragma: no cover choices = kwargs.pop('choices', []) super(InstrokeForm, self).__init__(*args, **kwargs) if len(choices) > 0: choices = [(choice, choice) for choice in choices] self.fields['metric'].choices = choices self.fields['metric'].initial = choices[0] # 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) # TrainingPlanBillingForm form class TrainingPlanBillingForm(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) enddate = forms.DateField(widget=forms.HiddenInput) name = forms.CharField(max_length=255, required=False) notes = forms.CharField(max_length=255, required=False) status = forms.CharField(max_length=255, required=True) tac = forms.BooleanField(required=True, initial=False) # 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, label="First Name") lastname = forms.CharField(max_length=255, required=False, label="Last Name") email = forms.EmailField() subject = forms.CharField(max_length=255) message = forms.CharField(widget=forms.Textarea()) captcha = ReCaptchaField(widget=ReCaptchaV3( attrs={ 'required_score': 0.85, } )) 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"), ("distance", "distance") ) 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 = models.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+mytypes.ergtypes, 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() # 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): # pragma: no cover 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): # pragma: no cover 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', ) class WorkoutNameTemplateElement(forms.Form): templatechoices = tuple(list(workout_name_element)+[('None', 'None')]) element = forms.ChoiceField( label='', choices=templatechoices, 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') upload_to_Intervals = forms.BooleanField(initial=False, required=False, label='Export to Intervals') # 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()) # set upload_to_X based on r.X_auto_export for field in ['C2', 'Strava', 'SportTracks', 'TrainingPeaks', 'Intervals']: if getattr(r, field.lower()+'_auto_export') and r.rowerplan in ['pro', 'plan','coach']: self.fields['upload_to_'+field].initial = True 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' ): # pragma: no cover rs = VirtualRaceResult.objects.filter(race=ra, userid=r.id) if rs.count() == 0: noregistrations.append((-ra.id, ra.name)) for ra in VirtualRace.objects.filter( registration_closure__gt=timezone.now(), sessiontype='indoorrace' ): # pragma: no cover rs = IndoorVirtualRaceResult.objects.filter(race=ra, userid=r.id) if rs.count() == 0: noregistrations.append((-ra.id, ra.name)) choices = choices3+choices1+choices2+noregistrations if int(raceid) in [r.id for r in races]: # pragma: no cover 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: # pragma: no cover 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') upload_to_Intervals = 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 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') # 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 PlanByRscoreForm(forms.Form): byrscore = forms.BooleanField(initial=False, required=False, label='plan by rScore') 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"}) DISPOSABLE_DOMAINS = ["powerscrews.com"] 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. """ email = self.cleaned_data['email'] domain = email.split('@')[-1].lower() if domain in DISPOSABLE_DOMAINS: raise ValidationError("Registration using this domain is not allowed") if User.objects.filter(email__iexact=self.cleaned_data['email']): # pragma: no cover raise forms.ValidationError( "This email address is already in use. Please supply a different email address.") return self.cleaned_data['email'] class HoneypotField(forms.CharField): """ A honeypot field that should be left empty by humans """ def __init__(self, *args, **kwargs): kwargs.setdefault('required', False) kwargs.setdefault('widget', forms.TextInput(attrs={ 'style': 'display:none !important', 'tabindex': '-1', 'autocomplete': 'off', 'aria-hidden': 'true', })) super().__init__(*args, **kwargs) def clean(self, value): if value: # If the field has a value, it's likely a bot raise ValidationError("Please leave this field empty.") return value 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)) hp_field = HoneypotField(label="URL") timestamp = forms.CharField(widget=forms.HiddenInput(), required=False) def clean_birthdate(self): dob = self.cleaned_data['birthdate'] age = (timezone.now() - dob).days/365 if age < 16: # pragma: no cover raise forms.ValidationError( 'Must be at least 16 years old to register') 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) captcha = ReCaptchaField(widget=ReCaptchaV3( attrs={ 'required_score': 0.85, })) def clean(self): cleaned_data = super().clean() timestamp = cleaned_data.get('timestamp') if timestamp: submission_time = timezone.now() form_render_time = timezone.datetime.fromtimestamp(float(timestamp), tz=timezone.get_current_timezone()) if (submission_time - form_render_time).seconds < 3: raise forms.ValidationError("Form submitted too quickly. Are you a bot?") return cleaned_data 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()) boattypes = mytypes.boattypes ergtypes = mytypes.ergtypes workouttypes = mytypes.workouttypes ww = list(workouttypes) ww.append(tuple(('all', 'All'))) workouttypes = tuple(ww) class DataFrameColumnsForm(forms.Form): cols = ['ftime', 'cumdist', 'fpace', 'spm', 'hr', 'grpower', '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+ergtypes, label='Water Boat Type', initial=mytypes.waterboattype) ranking = forms.BooleanField(label='Ranking Workouts Only', initial=False,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+ergtypes, label='Water Boat Type', initial=mytypes.waterboattype) # This form sets options for the summary stats page class StatsOptionsForm(forms.Form): includereststrokes = forms.BooleanField( initial=False, label='Include Rest Strokes', required=False) water = forms.BooleanField(initial=False, required=False) waterboattype = forms.MultipleChoiceField(choices=boattypes+ergtypes, label='Water Boat Type', widget=forms.CheckboxSelectMultiple(), initial=mytypes.waterboattype) def __init__(self, *args, **kwargs): # pragma: no cover super(StatsOptionsForm, self).__init__(*args, **kwargs) for type in mytypes.checktypes: self.fields[type] = forms.BooleanField( initial=True, required=False) 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): # pragma: no cover course = kwargs.pop('course', None) manager = kwargs.pop('manager', None) choices = kwargs.pop('choices', []) super(CourseSelectForm, self).__init__(*args, **kwargs) if len(choices) > 0: self.fields['course'].queryset = GeoCourse.objects.filter( id__in=[c.id for c in choices]) if course is not None: d_min = 0.5*course.distance d_max = 2*course.distance country = course.country countries = ['unknown', country] 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) 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 InStrokeMultipleCompareForm(forms.Form): analyses = forms.ModelMultipleChoiceField( queryset=InStrokeAnalysis.objects.all(), widget=forms.CheckboxSelectMultiple() ) class ForceCurveMultipleCompareForm(forms.Form): analyses = forms.ModelMultipleChoiceField( queryset=ForceCurveAnalysis.objects.all(), widget=forms.CheckboxSelectMultiple() ) bulkactions = ( ('remove','remove'), ('export','export'), ('rower assign','rower assign'), ('set commute','set commute'), ('set private','set private'), ('set public','set public'), ('unset commute','unset commute'), ) destinations = ( ('c2','concept2'), ('strava','strava'), ('sporttracks','sporttracks'), ('trainingpeaks','trainingpeaks'), ('intervals','intervals.icu') ) class WorkoutBulkActions(forms.Form): action = forms.ChoiceField( choices=bulkactions, label='Action', required=True, initial='remove') class ExportChoices(forms.Form): destination = forms.ChoiceField( choices=destinations, label='Destination', required=False, initial='strava' ) class AssignChoices(forms.Form): remove_workout = forms.BooleanField(initial=False, required=False) rowers = forms.ModelMultipleChoiceField(label='Rowers', required=False, queryset=Rower.objects.filter(), widget=forms.CheckboxSelectMultiple()) def __init__(self, *args, **kwargs): super(AssignChoices, self).__init__(*args, **kwargs) self.fields['rowers'].queryset = Rower.objects.filter() 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])) 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'), ('cr', 'CR 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') ) crfitchoices = ( ('data', 'Fit to Selected Workouts'), ('automatic', 'Critical Stroke Rate Rolling Data') ) cpfit = forms.ChoiceField(choices=cpfitchoices, label='Model Fit', initial='data', required=False) crfit = forms.ChoiceField(choices=crfitchoices, 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') trendline = forms.BooleanField(initial=False, required=False, label='Trend Line') savedata = forms.BooleanField(initial=False, required=False, label='Export data as CSV') def __init__(self, *args, **kwargs): super(AnalysisChoiceForm, self).__init__(*args, **kwargs) if 'initial' in kwargs and kwargs['initial']['function'] == 'compare': self.fields['xaxis'].initial = 'time' self.fields['yaxis1'].initial = 'hr' self.fields['plottype'].initial = 'line' 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: try: if df.loc[:, label].std() == 0: try: formaxlabels2.pop(label) except KeyError: # pragma: no cover pass except KeyError: # pragma: no cover formaxlabels2.pop(label) 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(), required=False, ) def __init__(self, *args, **kwargs): records = kwargs.pop('records', None) groups = kwargs.pop('groups', None) super(RaceResultFilterForm, self).__init__(*args, **kwargs) if records: # group if groups: # pragma: no cover 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] else: del self.fields['entrycategory'] # 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: # pragma: no cover 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: # pragma: no cover 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: # pragma: no cover 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: # pragma: no cover adaptivecategorychoices = [] for choice in self.fields['adaptivecategory'].choices: if choice[0] in theadaptivecategoryes: adaptivecategorychoices.append(choice) self.fields['adaptivecategory'].choices = adaptivecategorychoices class WorkoutRaceSelectForm(forms.Form): # evaluate_after = forms.TimeField( # input_formats=['%H:%M:%S.%f', # '%H:%M:%S', # '%H:%M:%S', # '%M:%S.%f', # '%M:%S', # '%M'], # label = 'Only Evaluate After:', # required=False) def __init__(self, workoutdata, entries, *args, **kwargs): 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): # pragma: no cover 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() def get_countries(): try: countries = VirtualRace.objects.order_by( 'country').values_list('country').distinct() countries = tuple([(c[0], c[0]) for c in countries]) countries = countries+(('All', 'All'),) except: # pragma: no cover countries = (('All', 'All')) 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') trendline = forms.BooleanField(initial=False, required=False, label='Add Trend Line') class ForceCurveOptionsForm(forms.Form): includereststrokes = forms.BooleanField(initial=False, required=False, label='Include Rest Strokes', widget=forms.CheckboxInput(attrs={'class':'hidden'})) spm_min = forms.FloatField(initial=15.0,label='SPM Min',widget=HiddenInput,required=False) spm_max = forms.FloatField(initial=55.0,label='SPM Max',widget=HiddenInput,required=False) dist_min = forms.IntegerField(initial=0,label='Dist Min',widget=HiddenInput,required=False) dist_max = forms.IntegerField(initial=150000,label='Dist Max',widget=HiddenInput,required=False) work_min = forms.IntegerField(initial=0,label='Work Min',widget=HiddenInput,required=False) work_max = forms.IntegerField(initial=1500,label='Work Max',widget=HiddenInput,required=False) notes = forms.CharField(initial="", label='notes', widget=HiddenInput, required=False) name = forms.CharField(initial="", label='Name',required=False) plotcircles = forms.BooleanField(initial=False, widget=forms.CheckboxInput(attrs={'class':'hidden'}), required=False) plotlines = forms.BooleanField(initial=False, widget=forms.CheckboxInput(attrs={'class':'hidden'}), required=False) 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): 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): 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