From fbe2391b86fb3d41498d699f61ea3f7a25de3600 Mon Sep 17 00:00:00 2001 From: Ryan Petrello Date: Wed, 2 May 2018 15:23:57 -0400 Subject: [PATCH] provide the timezone so that the UI doesn't have to this will also ensure that UI doesn't use a different front end library that will yield different results than the underlying Python code --- awx/api/serializers.py | 7 ++++- awx/api/views.py | 6 +--- awx/main/models/schedules.py | 31 ++++++++++++++++++- .../tests/functional/models/test_schedule.py | 15 +++++++++ 4 files changed, 52 insertions(+), 7 deletions(-) diff --git a/awx/api/serializers.py b/awx/api/serializers.py index 64b709d056..b1e62b8613 100644 --- a/awx/api/serializers.py +++ b/awx/api/serializers.py @@ -4511,9 +4511,14 @@ class SchedulePreviewSerializer(BaseSerializer): class ScheduleSerializer(LaunchConfigurationBaseSerializer, SchedulePreviewSerializer): show_capabilities = ['edit', 'delete'] + timezone = serializers.SerializerMethodField() + class Meta: model = Schedule - fields = ('*', 'unified_job_template', 'enabled', 'dtstart', 'dtend', 'rrule', 'next_run',) + fields = ('*', 'unified_job_template', 'enabled', 'dtstart', 'dtend', 'rrule', 'next_run', 'timezone',) + + def get_timezone(self, obj): + return obj.timezone def get_related(self, obj): res = super(ScheduleSerializer, self).get_related(obj) diff --git a/awx/api/views.py b/awx/api/views.py index 5e080ccea9..f1098ffce0 100644 --- a/awx/api/views.py +++ b/awx/api/views.py @@ -745,11 +745,7 @@ class ScheduleZoneInfo(APIView): swagger_topic = 'System Configuration' def get(self, request): - from dateutil.zoneinfo import get_zonefile_instance - return Response([ - {'name': zone} - for zone in sorted(get_zonefile_instance().zones) - ]) + return Response(Schedule.get_zoneinfo()) class LaunchConfigCredentialsBase(SubListAttachDetachAPIView): diff --git a/awx/main/models/schedules.py b/awx/main/models/schedules.py index 71efa702c6..af75b8e8a3 100644 --- a/awx/main/models/schedules.py +++ b/awx/main/models/schedules.py @@ -4,7 +4,9 @@ import logging import datetime import dateutil.rrule -from dateutil.tz import datetime_exists +from operator import itemgetter +import dateutil.parser +from dateutil.tz import datetime_exists, tzutc # Django from django.db import models @@ -94,6 +96,33 @@ class Schedule(CommonModel, LaunchTimeConfig): help_text=_("The next time that the scheduled action will run.") ) + @classmethod + def get_zoneinfo(self): + from dateutil.zoneinfo import get_zonefile_instance + return [ + {'name': zone} + for zone in sorted(get_zonefile_instance().zones) + ] + + @property + def timezone(self): + utc = tzutc() + _rrule = dateutil.rrule.rrulestr( + self.rrule, + tzinfos={x: utc for x in dateutil.parser.parserinfo().UTCZONE} + ) + tzinfo = _rrule._dtstart.tzinfo + if tzinfo == utc: + return 'UTC' + fname = tzinfo._filename + all_zones = map(itemgetter('name'), Schedule.get_zoneinfo()) + all_zones.sort(key = lambda x: -len(x)) + for zone in all_zones: + if fname.endswith(zone): + return zone + logger.warn('Could not detect valid zoneinfo for {}'.format(self.rrule)) + return '' + @classmethod def rrulestr(cls, rrule, **kwargs): """ diff --git a/awx/main/tests/functional/models/test_schedule.py b/awx/main/tests/functional/models/test_schedule.py index 101afa8b99..cb3dcd34ed 100644 --- a/awx/main/tests/functional/models/test_schedule.py +++ b/awx/main/tests/functional/models/test_schedule.py @@ -203,3 +203,18 @@ def test_beginning_of_time(job_template): ) with pytest.raises(ValueError): s.save() + + +@pytest.mark.django_db +@pytest.mark.parametrize('rrule, tz', [ + ['DTSTART:20300112T210000Z RRULE:FREQ=DAILY;INTERVAL=1', 'UTC'], + ['DTSTART;TZID=America/New_York:20300112T210000 RRULE:FREQ=DAILY;INTERVAL=1', 'America/New_York'] +]) +def test_timezone_property(job_template, rrule, tz): + # ensure that really large generators don't have performance issues + s = Schedule( + name='Some Schedule', + rrule=rrule, + unified_job_template=job_template + ) + assert s.timezone == tz