def clean_level(self): """ Remove extra whitespace from the level, strip if not WS. """ program = self.cleaned_data.get('program') program_enum = enums.Program(program) if program else None if program_enum and not program_enum.winter_rules_apply(): return None return self.cleaned_data.get('level', '').strip()
def get(self, context, **response_kwargs): try: program_enum = enums.Program(self.kwargs['program']) except ValueError: return JsonResponse({}, status=404) return JsonResponse({ 'leaders': sorted(self.describe_leaders(program_enum), key=lambda par: par['name']) })
def get_context_data(self, **kwargs): context = super().get_context_data(**kwargs) context['is_currently_iap'] = is_currently_iap() # There is separate logic for determining if we allow choosing the WS program. # Rather than duplicate that logic here, just see if it's a selectable choice. form: forms.TripForm = context['form'] context['can_select_ws_program'] = any( enums.Program(value) == enums.Program.WINTER_SCHOOL for category, choices in form.fields['program'].choices for value, label in choices) return context
def __init__(self, *args, **kwargs): allowed_programs = kwargs.pop("allowed_programs", None) super().__init__(*args, **kwargs) trip = self.instance if trip.pk is None: # New trips must have *all* dates/times in the future now = local_now() # (the `datetime-local` inputs don't take timezones at all) # We specify only to the minutes' place so that we don't display seconds naive_iso_now = now.replace(tzinfo=None).isoformat( timespec='minutes') self.fields['trip_date'].widget.attrs['min'] = now.date( ).isoformat() # There is *extra* dynamic logic that (open < close at <= trip date) # However, we can at minimum enforce that times occur in the future self.fields['signups_open_at'].widget.attrs['min'] = naive_iso_now self.fields['signups_close_at'].widget.attrs['min'] = naive_iso_now # Use the participant queryset to cover an edge case: # editing an old trip where one of the leaders is no longer a leader! self.fields[ 'leaders'].queryset = models.Participant.objects.get_queryset() self.fields['leaders'].help_text = None # Disable "Hold command..." # We'll dynamically hide the level widget on GET if it's not a winter trip # On POST, we only want this field required for winter trips program = self.data.get('program') program_enum = enums.Program(program) if program else None self.fields['level'].required = (program_enum and program_enum.winter_rules_apply()) initial_program: Optional[ enums.Program] = trip.pk and trip.program_enum if allowed_programs is not None: self.fields['program'].choices = list( self._allowed_program_choices(allowed_programs)) # If it's currently WS, the WS program is almost certainly what's desired for new trips. if (enums.Program.WINTER_SCHOOL in allowed_programs and is_currently_iap() and not trip.pk): initial_program = enums.Program.WINTER_SCHOOL self._init_wimp() # (No need for `ng-init`, we have a custom directive) self.fields['leaders'].widget.attrs['data-ng-model'] = 'leaders' _bind_input(self, 'program', initial=initial_program and initial_program.value) _bind_input(self, 'algorithm', initial=trip and trip.algorithm)
def _allowed_program_choices(self, allowed_program_enums): # If editing an existing trip, the old program can persist. if self.instance and self.instance.program_enum not in allowed_program_enums: allowed_program_enums = [ self.instance.program_enum, *allowed_program_enums ] for category, choices in enums.Program.choices(): assert isinstance(category, str) and isinstance(choices, list) valid_choices = [(value, label) for (value, label) in choices if enums.Program(value) in allowed_program_enums] if valid_choices: yield (category, valid_choices)
def __init__(self, *args, **kwargs): allowed_programs = kwargs.pop("allowed_programs", None) super().__init__(*args, **kwargs) # Use the participant queryset to cover an edge case: # editing an old trip where one of the leaders is no longer a leader! self.fields[ 'leaders'].queryset = models.Participant.objects.get_queryset() self.fields['leaders'].help_text = None # Disable "Hold command..." # We'll dynamically hide the level widget on GET if it's not a winter trip # On POST, we only want this field required for winter trips program = self.data.get('program') program_enum = enums.Program(program) if program else None self.fields['level'].required = (program_enum and program_enum.winter_rules_apply()) if allowed_programs is not None: self.fields['program'].choices = list( self._allowed_program_choices(allowed_programs)) self._init_wimp()
def clean(self): """ Ensure that all leaders can lead the trip. """ super().clean() if 'program' not in self.cleaned_data or 'leaders' not in self.cleaned_data: return self.cleaned_data leaders = self.cleaned_data['leaders'] program_enum = enums.Program(self.cleaned_data['program']) # To allow editing old trips with lapsed leaders, only check new additions trip = self.instance if trip.pk: leaders = leaders.exclude(pk__in=trip.leaders.all()) lacking_privs = [ par for par in leaders if not par.can_lead(program_enum) ] if lacking_privs: names = ', '.join(leader.name for leader in lacking_privs) self.add_error('leaders', f"{names} can't lead {program_enum.label} trips") return self.cleaned_data