From ba9d0823c74f0cd5a3bec08cfb5678da4934901d Mon Sep 17 00:00:00 2001 From: Mira Weller Date: Tue, 14 Jan 2025 13:29:31 +0100 Subject: [PATCH] use namedtuples for the parts of RelDate[Time]{Field,Widget} --- src/pretix/base/reldate.py | 175 +++++++++++------- .../pretixbase/forms/widgets/reldate.html | 4 +- .../pretixbase/forms/widgets/reldatetime.html | 6 +- 3 files changed, 113 insertions(+), 72 deletions(-) diff --git a/src/pretix/base/reldate.py b/src/pretix/base/reldate.py index d2e6451a67..681e025a3d 100644 --- a/src/pretix/base/reldate.py +++ b/src/pretix/base/reldate.py @@ -185,48 +185,72 @@ BEFORE_AFTER_CHOICE = ( ) +reldatetimeparts = namedtuple('reldatetimeparts', ( + "status", # 0 + "absolute", # 1 + "rel_days_number", # 2 + "rel_mins_relationto", # 3 + "rel_days_timeofday", # 4 + "rel_mins_number", # 5 + "rel_days_relationto", # 6 + "rel_mins_relation", # 7 + "rel_days_relation" # 8 +)) +reldatetimeparts.indizes = reldatetimeparts(*range(9)) + + class RelativeDateTimeWidget(forms.MultiWidget): template_name = 'pretixbase/forms/widgets/reldatetime.html' - + parts = reldatetimeparts def __init__(self, *args, **kwargs): self.status_choices = kwargs.pop('status_choices') base_choices = kwargs.pop('base_choices') - widgets = ( - forms.RadioSelect(choices=self.status_choices), - forms.DateTimeInput( + widgets = reldatetimeparts( + status=forms.RadioSelect(choices=self.status_choices), + absolute=forms.DateTimeInput( attrs={'class': 'datetimepicker'} ), - forms.NumberInput(), - forms.Select(choices=base_choices), - forms.TimeInput(attrs={'placeholder': _('Time'), 'class': 'timepickerfield'}), - forms.NumberInput(), - forms.Select(choices=base_choices), - forms.Select(choices=BEFORE_AFTER_CHOICE), - forms.Select(choices=BEFORE_AFTER_CHOICE), + rel_days_number=forms.NumberInput(), + rel_mins_relationto=forms.Select(choices=base_choices), + rel_days_timeofday=forms.TimeInput(attrs={'placeholder': _('Time'), 'class': 'timepickerfield'}), + rel_mins_number=forms.NumberInput(), + rel_days_relationto=forms.Select(choices=base_choices), + rel_mins_relation=forms.Select(choices=BEFORE_AFTER_CHOICE), + rel_days_relation=forms.Select(choices=BEFORE_AFTER_CHOICE), ) super().__init__(widgets=widgets, *args, **kwargs) def decompress(self, value): if isinstance(value, str): value = RelativeDateWrapper.from_string(value) + if isinstance(value, reldatetimeparts): + return value if not value: - return ['unset', None, 1, 'date_from', None, 0, "date_from", "before", "before"] + return reldatetimeparts(status='unset', absolute=None, rel_days_number=1, rel_mins_relationto='date_from', + rel_days_timeofday=None, rel_mins_number=0, rel_days_relationto="date_from", + rel_mins_relation="before", rel_days_relation="before") elif isinstance(value.data, (datetime.datetime, datetime.date)): - return ['absolute', value.data, 1, 'date_from', None, 0, "date_from", "before", "before"] + return reldatetimeparts(status='absolute', absolute=value.data, rel_days_number=1, rel_mins_relationto='date_from', + rel_days_timeofday=None, rel_mins_number=0, rel_days_relationto="date_from", + rel_mins_relation="before", rel_days_relation="before") elif value.data.minutes is not None: - return ['relative_minutes', None, None, value.data.base_date_name, None, value.data.minutes, value.data.base_date_name, - "after" if value.data.is_after else "before", "after" if value.data.is_after else "before"] - return ['relative', None, value.data.days, value.data.base_date_name, value.data.time, 0, value.data.base_date_name, - "after" if value.data.is_after else "before", "after" if value.data.is_after else "before"] + return reldatetimeparts(status='relative_minutes', absolute=None, rel_days_number=None, rel_mins_relationto=value.data.base_date_name, + rel_days_timeofday=None, rel_mins_number=value.data.minutes, rel_days_relationto=value.data.base_date_name, + rel_mins_relation="after" if value.data.is_after else "before", + rel_days_relation="after" if value.data.is_after else "before") + return reldatetimeparts(status='relative', absolute=None, rel_days_number=value.data.days, rel_mins_relationto=value.data.base_date_name, + rel_days_timeofday=value.data.time, rel_mins_number=0, rel_days_relationto=value.data.base_date_name, + rel_mins_relation="after" if value.data.is_after else "before", + rel_days_relation="after" if value.data.is_after else "before") def get_context(self, name, value, attrs): ctx = super().get_context(name, value, attrs) ctx['required'] = self.status_choices[0][0] == 'unset' - ctx['rendered_subwidgets'] = [ + ctx['rendered_subwidgets'] = self.parts(*( self._render(w['template_name'], {**ctx, 'widget': w}) for w in ctx['widget']['subwidgets'] - ] + ))._asdict() return ctx @@ -245,36 +269,36 @@ class RelativeDateTimeField(forms.MultiValueField): choices = BASE_CHOICES if not kwargs.get('required', True): status_choices.insert(0, ('unset', _('Not set'))) - fields = ( - forms.ChoiceField( + fields = reldatetimeparts( + status=forms.ChoiceField( choices=status_choices, required=True ), - forms.DateTimeField( + absolute=forms.DateTimeField( required=False ), - forms.IntegerField( + rel_days_number=forms.IntegerField( required=False ), - forms.ChoiceField( + rel_mins_relationto=forms.ChoiceField( choices=choices, required=False ), - forms.TimeField( + rel_days_timeofday=forms.TimeField( required=False, ), - forms.IntegerField( + rel_mins_number=forms.IntegerField( required=False ), - forms.ChoiceField( + rel_days_relationto=forms.ChoiceField( choices=choices, required=False ), - forms.ChoiceField( + rel_mins_relation=forms.ChoiceField( choices=BEFORE_AFTER_CHOICE, required=False ), - forms.ChoiceField( + rel_days_relation=forms.ChoiceField( choices=BEFORE_AFTER_CHOICE, required=False ), @@ -288,32 +312,33 @@ class RelativeDateTimeField(forms.MultiValueField): ) def set_event(self, event): - self.widget.widgets[3].choices = [ + self.widget.widgets[reldatetimeparts.indizes.rel_mins_relationto].choices = [ (k, v) for k, v in BASE_CHOICES if getattr(event, k, None) ] def compress(self, data_list): if not data_list: return None - if data_list[0] == 'absolute': - return RelativeDateWrapper(data_list[1]) - elif data_list[0] == 'unset': + data = reldatetimeparts(*data_list) + if data.status == 'absolute': + return RelativeDateWrapper(data.absolute) + elif data.status == 'unset': return None - elif data_list[0] == 'relative_minutes': + elif data.status == 'relative_minutes': return RelativeDateWrapper(RelativeDate( days=0, - base_date_name=data_list[3], + base_date_name=data.rel_mins_relationto, time=None, - minutes=data_list[5], - is_after=data_list[7] == "after", + minutes=data.rel_mins_number, + is_after=data.rel_mins_relation == "after", )) else: return RelativeDateWrapper(RelativeDate( - days=data_list[2], - base_date_name=data_list[6], - time=data_list[4], + days=data.rel_days_number, + base_date_name=data.rel_days_relationto, + time=data.rel_days_timeofday, minutes=None, - is_after=data_list[8] == "after", + is_after=data.rel_days_relation == "after", )) def has_changed(self, initial, data): @@ -322,29 +347,41 @@ class RelativeDateTimeField(forms.MultiValueField): return super().has_changed(initial, data) def clean(self, value): - if value[0] == 'absolute' and not value[1]: + data = reldatetimeparts(*value) + if data.status == 'absolute' and not data.absolute: raise ValidationError(self.error_messages['incomplete']) - elif value[0] == 'relative' and (value[2] is None or not value[6]): + elif data.status == 'relative' and (data.rel_days_number is None or not data.rel_days_relationto): raise ValidationError(self.error_messages['incomplete']) - elif value[0] == 'relative_minutes' and (value[5] is None or not value[3]): + elif data.status == 'relative_minutes' and (data.rel_mins_number is None or not data.rel_mins_relationto): raise ValidationError(self.error_messages['incomplete']) return super().clean(value) +reldateparts = namedtuple('reldateparts', ( + "status", # 0 + "absolute", # 1 + "rel_days_number", # 2 + "rel_days_relationto", # 3 + "rel_days_relation", # 4 +)) +reldateparts.indizes = reldateparts(*range(5)) + + class RelativeDateWidget(RelativeDateTimeWidget): template_name = 'pretixbase/forms/widgets/reldate.html' + parts = reldateparts def __init__(self, *args, **kwargs): self.status_choices = kwargs.pop('status_choices') - widgets = ( - forms.RadioSelect(choices=self.status_choices), - forms.DateInput( + widgets = reldateparts( + status=forms.RadioSelect(choices=self.status_choices), + absolute=forms.DateInput( attrs={'class': 'datepickerfield'} ), - forms.NumberInput(), - forms.Select(choices=kwargs.pop('base_choices')), - forms.Select(choices=BEFORE_AFTER_CHOICE), + rel_days_number=forms.NumberInput(), + rel_days_relationto=forms.Select(choices=kwargs.pop('base_choices')), + rel_days_relation=forms.Select(choices=BEFORE_AFTER_CHOICE), ) forms.MultiWidget.__init__(self, widgets=widgets, *args, **kwargs) @@ -352,10 +389,12 @@ class RelativeDateWidget(RelativeDateTimeWidget): if isinstance(value, str): value = RelativeDateWrapper.from_string(value) if not value: - return ['unset', None, 1, 'date_from', 'before'] + return reldateparts(status='unset', absolute=None, rel_days_number=1, rel_days_relationto='date_from', rel_days_relation='before') + if isinstance(value, reldateparts): + return value elif isinstance(value.data, (datetime.datetime, datetime.date)): - return ['absolute', value.data, 1, 'date_from', 'before'] - return ['relative', None, value.data.days, value.data.base_date_name, "after" if value.data.is_after else "before"] + return reldateparts(status='absolute', absolute=value.data, rel_days_number=1, rel_days_relationto='date_from', rel_days_relation='before') + return reldateparts(status='relative', absolute=None, rel_days_number=value.data.days, rel_days_relationto=value.data.base_date_name, rel_days_relation="after" if value.data.is_after else "before") class RelativeDateField(RelativeDateTimeField): @@ -367,22 +406,22 @@ class RelativeDateField(RelativeDateTimeField): ] if not kwargs.get('required', True): status_choices.insert(0, ('unset', _('Not set'))) - fields = ( - forms.ChoiceField( + fields = reldateparts( + status=forms.ChoiceField( choices=status_choices, required=True ), - forms.DateField( + absolute=forms.DateField( required=False ), - forms.IntegerField( + rel_days_number=forms.IntegerField( required=False ), - forms.ChoiceField( + rel_days_relationto=forms.ChoiceField( choices=BASE_CHOICES, required=False ), - forms.ChoiceField( + rel_days_relation=forms.ChoiceField( choices=BEFORE_AFTER_CHOICE, required=False ), @@ -396,22 +435,24 @@ class RelativeDateField(RelativeDateTimeField): def compress(self, data_list): if not data_list: return None - if data_list[0] == 'absolute': - return RelativeDateWrapper(data_list[1]) - elif data_list[0] == 'unset': + data = reldateparts(*data_list) + if data.status == 'absolute': + return RelativeDateWrapper(data.absolute) + elif data.status == 'unset': return None else: return RelativeDateWrapper(RelativeDate( - days=data_list[2], - base_date_name=data_list[3], + days=data.rel_days_number, + base_date_name=data.rel_days_relationto, time=None, minutes=None, - is_after=data_list[4] == "after" + is_after=data.rel_days_relation == "after" )) def clean(self, value): - if value[0] == 'absolute' and not value[1]: + data = reldateparts(*value) + if data.status == 'absolute' and not data.absolute: raise ValidationError(self.error_messages['incomplete']) - elif value[0] == 'relative' and (value[2] is None or not value[3]): + elif data.status == 'relative' and (data.rel_days_number is None or not data.rel_days_relationto): raise ValidationError(self.error_messages['incomplete']) return forms.MultiValueField.clean(self, value) diff --git a/src/pretix/base/templates/pretixbase/forms/widgets/reldate.html b/src/pretix/base/templates/pretixbase/forms/widgets/reldate.html index 2cf24d8e58..73b8ff7c3d 100644 --- a/src/pretix/base/templates/pretixbase/forms/widgets/reldate.html +++ b/src/pretix/base/templates/pretixbase/forms/widgets/reldate.html @@ -9,9 +9,9 @@ {{ selopt.label }} {% if selopt.value == "absolute" %} - {% include widget.subwidgets.1.template_name with widget=widget.subwidgets.1 %} + {{ rendered_subwidgets.absolute }} {% elif selopt.value == "relative" %} - {% blocktrans trimmed with number=rendered_subwidgets.2 relation=rendered_subwidgets.4 relation_to=rendered_subwidgets.3 %} + {% blocktrans trimmed with number=rendered_subwidgets.rel_days_number relation=rendered_subwidgets.rel_days_relation relation_to=rendered_subwidgets.rel_days_relationto %} {{ number }} days {{ relation }} {{ relation_to }} {% endblocktrans %} {% endif %} diff --git a/src/pretix/base/templates/pretixbase/forms/widgets/reldatetime.html b/src/pretix/base/templates/pretixbase/forms/widgets/reldatetime.html index 49879ae81e..3e618d0910 100644 --- a/src/pretix/base/templates/pretixbase/forms/widgets/reldatetime.html +++ b/src/pretix/base/templates/pretixbase/forms/widgets/reldatetime.html @@ -9,13 +9,13 @@ {{ selopt.label }} {% if selopt.value == "absolute" %} - {% include widget.subwidgets.1.template_name with widget=widget.subwidgets.1 %} + {{ rendered_subwidgets.absolute }} {% elif selopt.value == "relative_minutes" %} - {% blocktrans trimmed with number=rendered_subwidgets.5 relation=rendered_subwidgets.7 relation_to=rendered_subwidgets.3 %} + {% blocktrans trimmed with number=rendered_subwidgets.rel_mins_number relation=rendered_subwidgets.rel_mins_relation relation_to=rendered_subwidgets.rel_mins_relationto %} {{ number }} minutes {{ relation }} {{ relation_to }} {% endblocktrans %} {% elif selopt.value == "relative" %} - {% blocktrans trimmed with number=rendered_subwidgets.2 relation=rendered_subwidgets.8 relation_to=rendered_subwidgets.6 time_of_day=rendered_subwidgets.4 %} + {% blocktrans trimmed with number=rendered_subwidgets.rel_days_number relation=rendered_subwidgets.rel_days_relation relation_to=rendered_subwidgets.rel_days_relationto time_of_day=rendered_subwidgets.rel_days_timeofday %} {{ number }} days {{ relation }} {{ relation_to }} at {{ time_of_day }} {% endblocktrans %} {% endif %}