From c52eb0f327315ca3a412a94ad28f7b3973ec7056 Mon Sep 17 00:00:00 2001 From: Ryan Petrello Date: Wed, 9 May 2018 15:11:02 -0400 Subject: [PATCH] provide a naive UNTIL= datestamp for schedules for UI convenience --- awx/api/serializers.py | 7 +- awx/main/models/schedules.py | 11 +++ .../tests/functional/models/test_schedule.py | 68 +++++++++++++++++++ 3 files changed, 85 insertions(+), 1 deletion(-) diff --git a/awx/api/serializers.py b/awx/api/serializers.py index b1e62b8613..8e8b9930c5 100644 --- a/awx/api/serializers.py +++ b/awx/api/serializers.py @@ -4512,14 +4512,19 @@ class ScheduleSerializer(LaunchConfigurationBaseSerializer, SchedulePreviewSeria show_capabilities = ['edit', 'delete'] timezone = serializers.SerializerMethodField() + until = serializers.SerializerMethodField() class Meta: model = Schedule - fields = ('*', 'unified_job_template', 'enabled', 'dtstart', 'dtend', 'rrule', 'next_run', 'timezone',) + fields = ('*', 'unified_job_template', 'enabled', 'dtstart', 'dtend', 'rrule', 'next_run', 'timezone', + 'until') def get_timezone(self, obj): return obj.timezone + def get_until(self, obj): + return obj.until + def get_related(self, obj): res = super(ScheduleSerializer, self).get_related(obj) res.update(dict( diff --git a/awx/main/models/schedules.py b/awx/main/models/schedules.py index dabf7fba10..13048dcf7d 100644 --- a/awx/main/models/schedules.py +++ b/awx/main/models/schedules.py @@ -122,6 +122,17 @@ class Schedule(CommonModel, LaunchTimeConfig): logger.warn('Could not detect valid zoneinfo for {}'.format(self.rrule)) return '' + @property + def until(self): + # The UNTIL= datestamp (if any) coerced from UTC to the local naive time + # of the DTSTART + for r in Schedule.rrulestr(self.rrule)._rrule: + if r._until: + local_until = r._until.astimezone(r._dtstart.tzinfo) + naive_until = local_until.replace(tzinfo=None) + return naive_until.isoformat() + return '' + @classmethod def coerce_naive_until(cls, rrule): # diff --git a/awx/main/tests/functional/models/test_schedule.py b/awx/main/tests/functional/models/test_schedule.py index e89dbf017d..d18e848d97 100644 --- a/awx/main/tests/functional/models/test_schedule.py +++ b/awx/main/tests/functional/models/test_schedule.py @@ -206,3 +206,71 @@ def test_timezone_property(job_template, rrule, tz): unified_job_template=job_template ) assert s.timezone == tz + + +@pytest.mark.django_db +def test_utc_until_property(job_template): + rrule = 'DTSTART:20380601T120000Z RRULE:FREQ=HOURLY;INTERVAL=1;UNTIL=20380601T170000Z' + s = Schedule( + name='Some Schedule', + rrule=rrule, + unified_job_template=job_template + ) + s.save() + + assert s.rrule.endswith('20380601T170000Z') + assert s.until == '2038-06-01T17:00:00' + + +@pytest.mark.django_db +def test_localized_until_property(job_template): + rrule = 'DTSTART;TZID=America/New_York:20380601T120000 RRULE:FREQ=HOURLY;INTERVAL=1;UNTIL=20380601T220000Z' + s = Schedule( + name='Some Schedule', + rrule=rrule, + unified_job_template=job_template + ) + s.save() + + assert s.rrule.endswith('20380601T220000Z') + assert s.until == '2038-06-01T17:00:00' + + +@pytest.mark.django_db +def test_utc_naive_coercion(job_template): + rrule = 'DTSTART:20380601T120000Z RRULE:FREQ=HOURLY;INTERVAL=1;UNTIL=20380601T170000' + s = Schedule( + name='Some Schedule', + rrule=rrule, + unified_job_template=job_template + ) + s.save() + + assert s.rrule.endswith('20380601T170000Z') + assert s.until == '2038-06-01T17:00:00' + + +@pytest.mark.django_db +def test_est_naive_coercion(job_template): + rrule = 'DTSTART;TZID=America/New_York:20380601T120000 RRULE:FREQ=HOURLY;INTERVAL=1;UNTIL=20380601T170000' + s = Schedule( + name='Some Schedule', + rrule=rrule, + unified_job_template=job_template + ) + s.save() + + assert s.rrule.endswith('20380601T220000Z') # 5PM EDT = 10PM UTC + assert s.until == '2038-06-01T17:00:00' + + +@pytest.mark.django_db +def test_empty_until_property(job_template): + rrule = 'DTSTART;TZID=America/New_York:20380601T120000 RRULE:FREQ=HOURLY;INTERVAL=1' + s = Schedule( + name='Some Schedule', + rrule=rrule, + unified_job_template=job_template + ) + s.save() + assert s.until == ''