예제 #1
0
def test_clean_invalid_value():
    field = RecurrenceField()
    value = "RRULE:FREQS=WEEKLY"

    with pytest.raises(forms.ValidationError) as e:
        field.clean(value)
    assert e.value.messages[0] == "bad parameter: FREQS"
예제 #2
0
def test_none_fieldvalue():

    field = RecurrenceField()
    value = None
    return_obj = field.clean(value)

    assert isinstance(return_obj, Recurrence) or return_obj is None
def test_clean_invalid_value():
    field = RecurrenceField()
    value = "RRULE:FREQS=WEEKLY"

    with pytest.raises(forms.ValidationError) as e:
        field.clean(value)
    assert e.value.messages[0] == "bad parameter: FREQS"
예제 #4
0
    def test_dt_start_and_dtend_converts_to_utc(self):
        """Convert the values for dtstart and dtend to UTC."""
        tz = pytz.timezone('America/Adak')

        limits = Recurrence(
            dtstart=datetime(2014, 1, 1, 0, 0, 0, tzinfo=tz),
            dtend=datetime(2014, 2, 3, 0, 0, 0, tzinfo=tz),
        )

        value = recurrence.serialize(limits)

        field = RecurrenceField()
        cleaned_value = field.clean(value)
        assert cleaned_value.dtstart == datetime(2014,
                                                 1,
                                                 1,
                                                 0,
                                                 0,
                                                 0,
                                                 tzinfo=tz).astimezone(
                                                     pytz.utc)
        assert cleaned_value.dtend == datetime(2014, 2, 3, 0, 0, 0,
                                               tzinfo=tz).astimezone(pytz.utc)
        assert cleaned_value.dtstart.tzname() == 'UTC'
        assert cleaned_value.dtend.tzname() == 'UTC'
    def test_strip_dtstart_and_dtend_if_required(self):
        """Test that naive datetimes will get converted to UTC and returned as UTC."""
        rule = Rule(
            recurrence.WEEKLY
        )

        limits = Recurrence(
            dtstart=datetime(2014, 1, 1, 0, 0, 0),
            dtend=datetime(2014, 2, 3, 0, 0, 0),
            rrules=[rule]
        )

        value = recurrence.serialize(limits)

        field = RecurrenceField()
        cleaned_value = field.clean(value)
        assert cleaned_value.rrules == [rule]
        assert cleaned_value.dtstart == datetime(2014, 1, 1, 0, 0, 0, tzinfo=pytz.utc)
        assert cleaned_value.dtend == datetime(2014, 2, 3, 0, 0, 0, tzinfo=pytz.utc)

        field = RecurrenceField(accept_dtstart=False, accept_dtend=False)
        cleaned_value = field.clean(value)
        assert cleaned_value != limits
        assert cleaned_value.dtstart is None
        assert cleaned_value.dtend is None
예제 #6
0
def test_check_allowable_frequencies():
    rule = Rule(
        recurrence.WEEKLY
    )

    limits = Recurrence(
        rrules=[rule]
    )

    value = recurrence.serialize(limits)

    field = RecurrenceField(frequencies=[
        recurrence.WEEKLY
    ])
    field.clean(value)

    field = RecurrenceField(frequencies=[
        recurrence.YEARLY
    ])
    with pytest.raises(forms.ValidationError) as e:
        field.clean(value)
    assert e.value.messages[0] == "Invalid frequency."

    limits = Recurrence(
        exrules=[rule]
    )

    value = recurrence.serialize(limits)

    with pytest.raises(forms.ValidationError) as e:
        field.clean(value)
    assert e.value.messages[0] == "Invalid frequency."
def test_clean_normal_value():
    field = RecurrenceField()
    value = "RRULE:FREQ=WEEKLY;BYDAY=TU"

    obj = field.clean(value)

    assert len(obj.rrules) == 1
    assert obj.rrules[0].to_text() == "weekly, each Tuesday"
예제 #8
0
def test_clean_normal_value():
    field = RecurrenceField()
    value = "RRULE:FREQ=WEEKLY;BYDAY=TU"

    obj = field.clean(value)

    assert len(obj.rrules) == 1
    assert obj.rrules[0].to_text() == "weekly, each Tuesday"
예제 #9
0
 def test_aware_until_gets_converted_to_utc(self):
     tz = pytz.timezone('America/Adak')
     until = datetime(2014, 1, 1, 0, 0, 0, tzinfo=tz)
     recurs = Recurrence(rrules=[Rule(recurrence.DAILY, until=until)], )
     value = recurrence.serialize(recurs)
     field = RecurrenceField()
     cleaned_value = field.clean(value)
     assert cleaned_value.rrules[0].until == until.astimezone(pytz.utc)
예제 #10
0
def test_check_max_rrules():
    rule = Rule(recurrence.WEEKLY)

    limits = Recurrence(rrules=[rule])

    value = recurrence.serialize(limits)

    field = RecurrenceField(max_rrules=0)
    with pytest.raises(forms.ValidationError) as e:
        field.clean(value)
    assert e.value.messages[0] == "Max rules exceeded. The limit is 0"
예제 #11
0
 def test_naive_until_gets_converted_to_utc(self):
     recurs = Recurrence(
         rrules=[Rule(
             recurrence.DAILY,
             until=datetime(2014, 1, 1, 0, 0, 0))
         ],
     )
     value = recurrence.serialize(recurs)
     field = RecurrenceField()
     cleaned_value = field.clean(value)
     assert cleaned_value.rrules[0].until == datetime(2014, 1, 1, 0, 0, 0, tzinfo=pytz.utc)
예제 #12
0
 def test_aware_until_gets_converted_to_utc(self):
     tz = pytz.timezone('America/Adak')
     until = datetime(2014, 1, 1, 0, 0, 0, tzinfo=tz)
     recurs = Recurrence(
         rrules=[Rule(
             recurrence.DAILY,
             until=until)
         ],
     )
     value = recurrence.serialize(recurs)
     field = RecurrenceField()
     cleaned_value = field.clean(value)
     assert cleaned_value.rrules[0].until == until.astimezone(pytz.utc)
def test_check_allowable_frequencies():
    rule = Rule(
        recurrence.WEEKLY
    )

    limits = Recurrence(
        rrules=[rule]
    )

    value = recurrence.serialize(limits)

    field = RecurrenceField(frequencies=[
        recurrence.WEEKLY
    ])
    field.clean(value)

    field = RecurrenceField(frequencies=[
        recurrence.YEARLY
    ])
    with pytest.raises(forms.ValidationError) as e:
        field.clean(value)
    assert e.value.messages[0] == "Invalid frequency."

    limits = Recurrence(
        exrules=[rule]
    )

    value = recurrence.serialize(limits)

    with pytest.raises(forms.ValidationError) as e:
        field.clean(value)
    assert e.value.messages[0] == "Invalid frequency."
예제 #14
0
 def test_naive_until_gets_converted_to_utc(self):
     recurs = Recurrence(rrules=[
         Rule(recurrence.DAILY, until=datetime(2014, 1, 1, 0, 0, 0))
     ], )
     value = recurrence.serialize(recurs)
     field = RecurrenceField()
     cleaned_value = field.clean(value)
     assert cleaned_value.rrules[0].until == datetime(2014,
                                                      1,
                                                      1,
                                                      0,
                                                      0,
                                                      0,
                                                      tzinfo=pytz.utc)
def test_check_max_rrules():
    rule = Rule(
        recurrence.WEEKLY
    )

    limits = Recurrence(
        rrules=[rule]
    )

    value = recurrence.serialize(limits)

    field = RecurrenceField(max_rrules=0)
    with pytest.raises(forms.ValidationError) as e:
        field.clean(value)
    assert e.value.messages[0] == "Max rules exceeded. The limit is 0"
예제 #16
0
    class Form(forms.Form):
        name = forms.CharField()
        description = forms.CharField(required=False,
                                      initial='',
                                      widget=forms.Textarea)

        recurrence_enabled = forms.BooleanField(required=False, initial=True)
        recurrence_start = forms.DateTimeField(
            initial=timezone.now,
            help_text=
            _('The recurrence defined below is applied from this date and time onwards.<br/>'
              'Eg. for daily recurrence the actions would be scheduled for the time set here.<br/>'
              'The set time zone is %s.<br/>'
              'Note that this date is <em>always</em> included in the schedule, even if it '
              'doesn\'t match the criteria.') % settings.TIME_ZONE)
        recurrence = RecurrenceField(required=False)

        def __init__(self, data, *args, **kwargs):
            super().__init__(data, *args, **kwargs)
            if 'recurrence' in self.initial:
                self.initial['recurrence_start'] = self.initial[
                    'recurrence'].dtstart

        def clean(self):
            super().clean()
            if not self.errors:
                dtstart = self.cleaned_data.pop('recurrence_start')
                self.cleaned_data['recurrence'].dtstart = dtstart
            return self.cleaned_data
예제 #17
0
    def test_naive_rdates_converted_to_utc(self):
        limits = Recurrence(rdates=[
            datetime(2014, 1, 1, 0, 0, 0),
            datetime(2014, 1, 2, 0, 0, 0),
        ], )

        value = recurrence.serialize(limits)

        field = RecurrenceField()
        cleaned_value = field.clean(value)
        assert cleaned_value.rdates == [
            datetime(2014, 1, 1, 0, 0, 0, tzinfo=pytz.utc),
            datetime(2014, 1, 2, 0, 0, 0, tzinfo=pytz.utc),
        ]
        for rdate in cleaned_value.rdates:
            assert rdate.tzname() == 'UTC'
예제 #18
0
    def test_dt_start_and_dtend_converts_to_utc(self):
        """Convert the values for dtstart and dtend to UTC."""
        tz = pytz.timezone('America/Adak')

        limits = Recurrence(
            dtstart=datetime(2014, 1, 1, 0, 0, 0, tzinfo=tz),
            dtend=datetime(2014, 2, 3, 0, 0, 0, tzinfo=tz),
        )

        value = recurrence.serialize(limits)

        field = RecurrenceField(required=False)
        cleaned_value = field.clean(value)
        assert cleaned_value.dtstart == datetime(2014, 1, 1, 0, 0, 0, tzinfo=tz).astimezone(pytz.utc)
        assert cleaned_value.dtend == datetime(2014, 2, 3, 0, 0, 0, tzinfo=tz).astimezone(pytz.utc)
        assert cleaned_value.dtstart.tzname() == 'UTC'
        assert cleaned_value.dtend.tzname() == 'UTC'
예제 #19
0
    def test_aware_rdates_converted_to_utc(self):
        tz = pytz.timezone('America/Adak')
        limits = Recurrence(rdates=[
            datetime(2014, 1, 1, 0, 0, 0, tzinfo=tz),
            datetime(2014, 1, 2, 0, 0, 0, tzinfo=tz),
        ], )

        value = recurrence.serialize(limits)

        field = RecurrenceField()
        cleaned_value = field.clean(value)
        assert cleaned_value.rdates == [
            datetime(2014, 1, 1, 0, 0, 0, tzinfo=tz).astimezone(pytz.utc),
            datetime(2014, 1, 2, 0, 0, 0, tzinfo=tz).astimezone(pytz.utc),
        ]
        for rdate in cleaned_value.rdates:
            assert rdate.tzname() == 'UTC'
예제 #20
0
class GeneratePermanenceForm(forms.Form):
    # repeat_counter = forms.IntegerField(label=_("Number of permanence(s)"), min_value=0, max_value=54)
    # repeat_step = forms.IntegerField(label=_("Number of week(s) between two permanences"), min_value=0, max_value=12)
    recurrences = RecurrenceField()

    def __init__(self, *args, **kwargs):
        self.request = kwargs.pop("request", None)
        super(GeneratePermanenceForm, self).__init__(*args, **kwargs)
예제 #21
0
    def test_naive_rdates_converted_to_utc(self):
        limits = Recurrence(
            rdates=[
                datetime(2014, 1, 1, 0, 0, 0),
                datetime(2014, 1, 2, 0, 0, 0),
            ],
        )

        value = recurrence.serialize(limits)

        field = RecurrenceField()
        cleaned_value = field.clean(value)
        assert cleaned_value.rdates == [
            datetime(2014, 1, 1, 0, 0, 0, tzinfo=pytz.utc),
            datetime(2014, 1, 2, 0, 0, 0, tzinfo=pytz.utc),
        ]
        for rdate in cleaned_value.rdates:
            assert rdate.tzname() == 'UTC'
예제 #22
0
class EventDuplicationForm(forms.Form):
    start_date = forms.DateField(
        widget=CustomDateInput(format="%Y-%m-%d"),
        initial=date.today(),
        help_text=
        _("This date will be used as the start date for recurring events that you create below, e.g. daily events will be created from this date onwards."
          ),
    )
    recurrence = RecurrenceField(required=False)
예제 #23
0
    def test_aware_rdates_converted_to_utc(self):
        tz = pytz.timezone('America/Adak')
        limits = Recurrence(
            rdates=[
                datetime(2014, 1, 1, 0, 0, 0, tzinfo=tz),
                datetime(2014, 1, 2, 0, 0, 0, tzinfo=tz),
            ],
        )

        value = recurrence.serialize(limits)

        field = RecurrenceField()
        cleaned_value = field.clean(value)
        assert cleaned_value.rdates == [
            datetime(2014, 1, 1, 0, 0, 0, tzinfo=tz).astimezone(pytz.utc),
            datetime(2014, 1, 2, 0, 0, 0, tzinfo=tz).astimezone(pytz.utc),
        ]
        for rdate in cleaned_value.rdates:
            assert rdate.tzname() == 'UTC'
예제 #24
0
    def test_strip_dtstart_and_dtend_if_required(self):
        """Test that naive datetimes will get converted to UTC and returned as UTC."""
        rule = Rule(recurrence.WEEKLY)

        limits = Recurrence(dtstart=datetime(2014, 1, 1, 0, 0, 0),
                            dtend=datetime(2014, 2, 3, 0, 0, 0),
                            rrules=[rule])

        value = recurrence.serialize(limits)

        field = RecurrenceField()
        cleaned_value = field.clean(value)
        assert cleaned_value.rrules == [rule]
        assert cleaned_value.dtstart == datetime(2014,
                                                 1,
                                                 1,
                                                 0,
                                                 0,
                                                 0,
                                                 tzinfo=pytz.utc)
        assert cleaned_value.dtend == datetime(2014,
                                               2,
                                               3,
                                               0,
                                               0,
                                               0,
                                               tzinfo=pytz.utc)

        field = RecurrenceField(accept_dtstart=False, accept_dtend=False)
        cleaned_value = field.clean(value)
        assert cleaned_value != limits
        assert cleaned_value.dtstart is None
        assert cleaned_value.dtend is None
예제 #25
0
파일: event.py 프로젝트: garinm90/ephios
 def post(self, *args, **kwargs):
     try:
         recurrence = RecurrenceField().clean(
             self.request.POST["recurrence_string"])
         return HttpResponse(
             json.dumps(
                 recurrence.between(
                     datetime.now() - timedelta(days=1),
                     datetime.now() + timedelta(
                         days=730
                     ),  # allow dates up to two years in the future
                     inc=True,
                     dtstart=datetime.combine(
                         DateField().to_python(
                             self.request.POST["dtstart"]),
                         datetime.min.time()),
                 ),
                 default=lambda obj: date_format(
                     obj, format="SHORT_DATE_FORMAT"),
             ))
     except (TypeError, KeyError, ValidationError):
         return HttpResponse()
예제 #26
0
def test_check_max_rdates():
    limits = Recurrence(rdates=[
        datetime(2014, 1, 1, 0, 0, 0),
        datetime(2014, 1, 2, 0, 0, 0),
    ])

    value = recurrence.serialize(limits)

    field = RecurrenceField(max_rdates=2)
    field.clean(value)

    field = RecurrenceField(max_rdates=1)
    with pytest.raises(forms.ValidationError) as e:
        field.clean(value)
    assert e.value.messages[0] == "Max dates exceeded. The limit is 1"
def test_strip_dtstart_and_dtend_if_required():
    rule = Rule(
        recurrence.WEEKLY
    )

    limits = Recurrence(
        dtstart=datetime(2014, 1, 1, 0, 0, 0),
        dtend=datetime(2014, 2, 3, 0, 0, 0),
        rrules=[rule]
    )

    value = recurrence.serialize(limits)

    field = RecurrenceField()
    cleaned_value = field.clean(value)
    assert cleaned_value == limits
    assert cleaned_value.dtstart == datetime(2014, 1, 1, 0, 0, 0)
    assert cleaned_value.dtend == datetime(2014, 2, 3, 0, 0, 0)

    field = RecurrenceField(accept_dtstart=False, accept_dtend=False)
    cleaned_value = field.clean(value)
    assert cleaned_value != limits
    assert cleaned_value.dtstart is None
    assert cleaned_value.dtend is None
def test_check_max_rdates():
    limits = Recurrence(
        rdates=[
            datetime(2014, 1, 1, 0, 0, 0),
            datetime(2014, 1, 2, 0, 0, 0),
        ]
    )

    value = recurrence.serialize(limits)

    field = RecurrenceField(max_rdates=2)
    field.clean(value)

    field = RecurrenceField(max_rdates=1)
    with pytest.raises(forms.ValidationError) as e:
        field.clean(value)
    assert e.value.messages[0] == "Max dates exceeded. The limit is 1"
예제 #29
0
def test_strip_dtstart_and_dtend_if_required():
    rule = Rule(recurrence.WEEKLY)

    limits = Recurrence(dtstart=datetime(2014, 1, 1, 0, 0, 0),
                        dtend=datetime(2014, 2, 3, 0, 0, 0),
                        rrules=[rule])

    value = recurrence.serialize(limits)

    field = RecurrenceField()
    cleaned_value = field.clean(value)
    assert cleaned_value == limits
    assert cleaned_value.dtstart == datetime(2014, 1, 1, 0, 0, 0)
    assert cleaned_value.dtend == datetime(2014, 2, 3, 0, 0, 0)

    field = RecurrenceField(accept_dtstart=False, accept_dtend=False)
    cleaned_value = field.clean(value)
    assert cleaned_value != limits
    assert cleaned_value.dtstart is None
    assert cleaned_value.dtend is None
예제 #30
0
class ResourceSearchForm(SearchForm):
    """
    Main search form.
    
    Ordering of results is computed according to the following:
    
    """
    def __init__(self, *args, **kwargs):
        kwargs['auto_id'] = True
        kwargs['label_suffix'] = ''
        super(ResourceSearchForm, self).__init__(*args, **kwargs)

    #q = forms.CharField(label=__(u'Quoi?'), initial='',required=True)
    q = AutoCompleteField('resource',
                          help_text='',
                          label=__(u'Quoi?'),
                          initial='',
                          required=True)
    a = AutoCompleteField('location',
                          help_text='',
                          label=__(u'Où?'),
                          initial='',
                          required=False)
    t = RecurrenceField(label=__(u'Quand?'),
                        initial='',
                        required=False,
                        max_rdates=0,
                        max_exdates=0)
    d = forms.CharField(label=__(u'Quand?'), initial='', required=False)

    def search(self):
        # First, store the SearchQuerySet received from other processing.
        sqs = super(ResourceSearchForm, self).search()
        address = self.cleaned_data.get('a')
        if (address):
            g = geocoders.Nominatim(
            )  # this should be changed to openstreetmap
            try:
                place, (lat, lng) = g.geocode(address)
                print "address: %s, lat : %g, lng : %g" % (address, lat, lng)
                loc = Point(lng, lat)
                max_dist = D(km=10)
                #sqs = sqs.dwithin('location',loc,max_dist).distance('location',loc)
            except geopy.exc.GeocoderServiceError:
                pass
        time = self.cleaned_data.get('t')
        if (time):
            # extract serialized events from search query set index
            events = sqs.exclude(event='toto')
            excluded = list()
            for e in events:
                if e.event:
                    # we only check if we can go to the next upcoming occurrence
                    # checking all occurrences would be too costly
                    ev = deserialize(e.event).after(datetime.now())
                    if not ev in time.occurrences(dtstart=ev):
                        excluded.append(e.pk)
            if (excluded):
                sqs = sqs.exclude(id__in=excluded)
        return sqs

    def no_query_found(self):
        return self.searchqueryset.all()
예제 #31
0
class ActionTriggerForm(forms.ModelForm):
    """A form for creating a default trigger while creating an action."""
    recurrences = RecurrenceField(
        help_text="Select the rules to define how this reminder should repeat."
    )

    class Meta:
        model = Trigger
        fields = [
            'time_of_day',
            'frequency',
            'start_when_selected',
            'stop_on_complete',
            'time',
            'trigger_date',
            'relative_value',
            'relative_units',
            'recurrences',
        ]
        widgets = {
            "time": TimeSelectWidget(include_empty=True),
            "trigger_date": forms.TextInput(attrs={'class': 'datepicker'}),
        }
        labels = {
            "time": "Reminder Time",
            "trigger_date": "Reminder Date",
        }

    class Media:
        js = (
            "foundation/js/vendor/jquery.js",
            "js/action_trigger_form.js",
        )

    def __init__(self, *args, **kwargs):
        super().__init__(*args, **kwargs)
        # If there's a trigger instance, include a button to disable it. This
        # should be handled in the template.
        try:
            if self.instance.action_default:
                disable_button = HTML(
                    '<button type="button" id="disable-trigger-button" '
                    ' class="button info tiny pull-right">'
                    '<i class="fa fa-bell-slash"></i> Remove Trigger</button><hr/>'
                )
        except ObjectDoesNotExist:
            disable_button = HTML('')

        self.fields['time'].help_text = (
            "Set the time at which the notification should be sent")
        self.fields['trigger_date'].help_text = (
            "The date on which notifications should start")

        # Better label names for the Relative Reminders
        self.fields['relative_value'].label = 'Relative Reminder'
        self.fields['relative_units'].label = '&nbsp;'

        self.helper = FormHelper()
        self.helper.form_tag = False
        self.helper.layout = Layout(
            Fieldset(
                _("Reminder Details"),
                disable_button,
                'time_of_day',
                'frequency',
                # NOTE: advanced options are hidden by default and may
                #       override the above dynamic/automatic notifications.
                # -------------------------------------------------------------
                HTML('<hr/><strong>Advanced Options</strong>'),
                HTML('<a class="button tiny secondary right" id="advanced" '
                     'data-status="hidden"><i class="fa fa-chevron-right"></i>'
                     ' Show</a>'),
                Div(HTML("<hr/><p class='alert-box warning'>"
                         "<i class='fa fa-warning'></i> These options may "
                         "override any options set above</p>"),
                    Div(
                        Div('time', css_class="large-6 columns"),
                        Div('trigger_date', css_class="large-6 columns"),
                    ),
                    Div(
                        Field('stop_on_complete',
                              data_tooltip=None,
                              aria_haspopup="true",
                              title=(
                                  "Reminders will cease to fire once the user "
                                  "has completed this action."),
                              css_class="has-tip tip-top"), ),
                    Field(
                        'start_when_selected',
                        data_tooltip=None,
                        aria_haspopup="true",
                        title=
                        ("If selected the reminder date will be automatically "
                         "be set when the user adds this action."),
                        css_class="has-tip tip-top"),
                    Div(Div('relative_value', css_class="large-6 columns"),
                        Div('relative_units', css_class="large-6 columns"),
                        css_class="row"),
                    Div(Div(HTML(
                        '<div class="hint">Relative reminders will fire '
                        'based on when the user adopts an action. Select '
                        'the amount time after the user adds the action '
                        'that this reminder should start</div>'),
                            css_class="large-12 columns"),
                        css_class="row"),
                    'recurrences',
                    css_class="trigger-form-advanced")))

    def save(self, *args, **kwargs):
        obj = super().save(*args, **kwargs)
        obj.name = date_hash()
        return obj

    def clean(self):
        data = super().clean()
        recurrences = data.get('recurrences')
        date = data.get('trigger_date')
        rel_value = data.get('relative_value')
        rel_units = data.get('relative_units')
        start_when_selected = data.get('start_when_selected', False)

        # Require BOTH relative_value and relative_units
        if (rel_value or rel_units) and not all([rel_value, rel_units]):
            self.add_error(
                'relative_value',
                ValidationError(
                    "Both a value and units are required for Relative Reminders",
                    code="required_for_relative_reminders"))

        if not start_when_selected and not date:
            # Intervals (e.g. every other day) need a starting date.
            if recurrences and 'INTERVAL' in serialize_recurrences(
                    recurrences):
                self.add_error(
                    'trigger_date',
                    ValidationError(
                        "A Trigger Date is required for recurrences that contain an "
                        "interval (such as every 2 days)",
                        code="required_for_intervals"))
            elif recurrences and 'COUNT' in serialize_recurrences(recurrences):
                self.add_error(
                    'trigger_date',
                    ValidationError(
                        "A Trigger Date is required for recurrences that occur a set "
                        "number of times",
                        code="required_for_count"))
        return data
def test_none_fieldvalue():
    field = RecurrenceField()
    value = None
    return_obj = field.clean(value)

    assert isinstance(return_obj, Recurrence) or return_obj is None