From 82ea659ed478f6b72de607213c305b44a265b9ec Mon Sep 17 00:00:00 2001 From: Jennifer Richards Date: Wed, 23 Feb 2022 20:51:18 +0000 Subject: [PATCH] Use correct UTC time when creating Meetecho conferences. Fixes #3565. Commit ready for merge. - Legacy-Id: 19969 --- ietf/meeting/helpers.py | 2 +- ietf/meeting/tests_helpers.py | 20 +++++------ ietf/utils/meetecho.py | 7 ++-- ietf/utils/tests_meetecho.py | 66 ++++++++++++++++++++--------------- 4 files changed, 54 insertions(+), 41 deletions(-) diff --git a/ietf/meeting/helpers.py b/ietf/meeting/helpers.py index 531a94c86..12bdabac5 100644 --- a/ietf/meeting/helpers.py +++ b/ietf/meeting/helpers.py @@ -1099,7 +1099,7 @@ def create_interim_session_conferences(sessions): confs = meetecho_manager.create( group=session.group, description=str(session), - start_time=ts.time, + start_time=ts.utc_start_time(), duration=ts.duration, ) except Exception as err: diff --git a/ietf/meeting/tests_helpers.py b/ietf/meeting/tests_helpers.py index 76f43c63e..a8158e462 100644 --- a/ietf/meeting/tests_helpers.py +++ b/ietf/meeting/tests_helpers.py @@ -456,8 +456,8 @@ class InterimTests(TestCase): def test_create_interim_session_conferences(self, mock): mock_conf_mgr = mock.return_value # "instance" seen by the internals sessions = [ - SessionFactory(meeting__type_id='interim', remote_instructions='junk'), - SessionFactory(meeting__type_id='interim', remote_instructions=''), + SessionFactory(meeting__type_id='interim', meeting__time_zone='america/halifax', remote_instructions='junk'), + SessionFactory(meeting__type_id='interim', meeting__time_zone='asia/kuala_lumpur', remote_instructions=''), ] timeslots = [ session.official_timeslotassignment().timeslot for session in sessions @@ -482,18 +482,18 @@ class InterimTests(TestCase): mock_conf_mgr.create.return_value = [ Conference( manager=mock_conf_mgr, id=1, public_id='some-uuid', description='desc', - start_time=timeslots[0].time, duration=timeslots[0].duration, url='fake-meetecho-url', + start_time=timeslots[0].utc_start_time(), duration=timeslots[0].duration, url='fake-meetecho-url', deletion_token='please-delete-me', ), ] create_interim_session_conferences([sessions[0]]) self.assertTrue(mock_conf_mgr.create.called) - self.assertCountEqual( + self.assertEqual( mock_conf_mgr.create.call_args[1], { 'group': sessions[0].group, 'description': str(sessions[0]), - 'start_time': timeslots[0].time, + 'start_time': timeslots[0].utc_start_time(), 'duration': timeslots[0].duration, } ) @@ -507,30 +507,30 @@ class InterimTests(TestCase): mock_conf_mgr.create.side_effect = [ [Conference( manager=mock_conf_mgr, id=1, public_id='some-uuid', description='desc', - start_time=timeslots[0].time, duration=timeslots[0].duration, url='different-fake-meetecho-url', + start_time=timeslots[0].utc_start_time(), duration=timeslots[0].duration, url='different-fake-meetecho-url', deletion_token='please-delete-me', )], [Conference( manager=mock_conf_mgr, id=2, public_id='another-uuid', description='desc', - start_time=timeslots[1].time, duration=timeslots[1].duration, url='another-fake-meetecho-url', + start_time=timeslots[1].utc_start_time(), duration=timeslots[1].duration, url='another-fake-meetecho-url', deletion_token='please-delete-me-too', )], ] create_interim_session_conferences([sessions[0], sessions[1]]) self.assertTrue(mock_conf_mgr.create.called) - self.assertCountEqual( + self.assertEqual( mock_conf_mgr.create.call_args_list, [ ({ 'group': sessions[0].group, 'description': str(sessions[0]), - 'start_time': timeslots[0].time, + 'start_time': timeslots[0].utc_start_time(), 'duration': timeslots[0].duration, },), ({ 'group': sessions[1].group, 'description': str(sessions[1]), - 'start_time': timeslots[1].time, + 'start_time': timeslots[1].utc_start_time(), 'duration': timeslots[1].duration, },), ] diff --git a/ietf/utils/meetecho.py b/ietf/utils/meetecho.py index 76b68d124..26ae93f03 100644 --- a/ietf/utils/meetecho.py +++ b/ietf/utils/meetecho.py @@ -15,11 +15,14 @@ import debug # pyflakes: ignore from datetime import datetime, timedelta from json import JSONDecodeError +from pytz import utc from typing import Dict, Sequence, Union from urllib.parse import urljoin class MeetechoAPI: + timezone = utc + def __init__(self, api_base: str, client_id: str, client_secret: str, request_timeout=3.01): self.client_id = client_id self.client_secret = client_secret @@ -57,10 +60,10 @@ class MeetechoAPI: return None def _deserialize_time(self, s: str) -> datetime: - return datetime.strptime(s, '%Y-%m-%d %H:%M:%S') + return self.timezone.localize(datetime.strptime(s, '%Y-%m-%d %H:%M:%S')) def _serialize_time(self, dt: datetime) -> str: - return dt.strftime('%Y-%m-%d %H:%M:%S') + return dt.astimezone(self.timezone).strftime('%Y-%m-%d %H:%M:%S') def _deserialize_duration(self, minutes: int) -> timedelta: return timedelta(minutes=minutes) diff --git a/ietf/utils/tests_meetecho.py b/ietf/utils/tests_meetecho.py index 82c0a7fa9..d40e013f8 100644 --- a/ietf/utils/tests_meetecho.py +++ b/ietf/utils/tests_meetecho.py @@ -4,6 +4,7 @@ import datetime import requests import requests_mock +from pytz import timezone, utc from unittest.mock import patch from urllib.parse import urljoin @@ -91,7 +92,7 @@ class APITests(TestCase): api = MeetechoAPI(API_BASE, CLIENT_ID, CLIENT_SECRET) api_response = api.schedule_meeting( wg_token='my-token', - start_time=datetime.datetime(2021, 9, 14, 10, 0, 0), + start_time=utc.localize(datetime.datetime(2021, 9, 14, 10, 0, 0)), duration=datetime.timedelta(minutes=130), description='interim-2021-wgname-01', extrainfo='message for staff', @@ -117,23 +118,32 @@ class APITests(TestCase): }, 'Incorrect request content' ) - self.assertEqual( - api_response, - { - 'rooms': { - '3d55bce0-535e-4ba8-bb8e-734911cf3c32': { - 'room': { - 'id': 18, - 'start_time': datetime.datetime(2021, 9, 14, 10, 0, 0), - 'duration': datetime.timedelta(minutes=130), - 'description': 'interim-2021-wgname-01', + # same time in different time zones + for start_time in [ + utc.localize(datetime.datetime(2021, 9, 14, 10, 0, 0)), + timezone('america/halifax').localize(datetime.datetime(2021, 9, 14, 7, 0, 0)), + timezone('europe/kiev').localize(datetime.datetime(2021, 9, 14, 13, 0, 0)), + timezone('pacific/easter').localize(datetime.datetime(2021, 9, 14, 5, 0, 0)), + timezone('africa/porto-novo').localize(datetime.datetime(2021, 9, 14, 11, 0, 0)), + ]: + self.assertEqual( + api_response, + { + 'rooms': { + '3d55bce0-535e-4ba8-bb8e-734911cf3c32': { + 'room': { + 'id': 18, + 'start_time': start_time, + 'duration': datetime.timedelta(minutes=130), + 'description': 'interim-2021-wgname-01', + }, + 'url': 'https://meetings.conf.meetecho.com/interim/?short=3d55bce0-535e-4ba8-bb8e-734911cf3c32', + 'deletion_token': 'session-deletion-token', }, - 'url': 'https://meetings.conf.meetecho.com/interim/?short=3d55bce0-535e-4ba8-bb8e-734911cf3c32', - 'deletion_token': 'session-deletion-token', - }, - } - }, - ) + } + }, + f'Incorrect time conversion for {start_time.tzinfo.zone}', + ) def test_fetch_meetings(self): self.maxDiff = 2048 @@ -181,7 +191,7 @@ class APITests(TestCase): '3d55bce0-535e-4ba8-bb8e-734911cf3c32': { 'room': { 'id': 18, - 'start_time': datetime.datetime(2021, 9, 14, 10, 0, 0), + 'start_time': utc.localize(datetime.datetime(2021, 9, 14, 10, 0, 0)), 'duration': datetime.timedelta(minutes=130), 'description': 'interim-2021-wgname-01', }, @@ -191,7 +201,7 @@ class APITests(TestCase): 'e68e96d4-d38f-475b-9073-ecab46ca96a5': { 'room': { 'id': 23, - 'start_time': datetime.datetime(2021, 9, 15, 14, 30, 0), + 'start_time': utc.localize(datetime.datetime(2021, 9, 15, 14, 30, 0)), 'duration': datetime.timedelta(minutes=30), 'description': 'interim-2021-wgname-02', }, @@ -239,7 +249,7 @@ class APITests(TestCase): def test_time_serialization(self): """Time de/serialization should be consistent""" - time = datetime.datetime.now().replace(microsecond=0) # cut off to 0 microseconds + time = datetime.datetime.now(utc).replace(microsecond=0) # cut off to 0 microseconds api = MeetechoAPI(API_BASE, CLIENT_ID, CLIENT_SECRET) self.assertEqual(api._deserialize_time(api._serialize_time(time)), time) @@ -253,7 +263,7 @@ class ConferenceManagerTests(TestCase): 'session-1-uuid': { 'room': { 'id': 1, - 'start_time': datetime.datetime(2022,2,4,1,2,3), + 'start_time': utc.localize(datetime.datetime(2022,2,4,1,2,3)), 'duration': datetime.timedelta(minutes=45), 'description': 'some-description', }, @@ -263,7 +273,7 @@ class ConferenceManagerTests(TestCase): 'session-2-uuid': { 'room': { 'id': 2, - 'start_time': datetime.datetime(2022,2,5,4,5,6), + 'start_time': utc.localize(datetime.datetime(2022,2,5,4,5,6)), 'duration': datetime.timedelta(minutes=90), 'description': 'another-description', }, @@ -280,7 +290,7 @@ class ConferenceManagerTests(TestCase): id=1, public_id='session-1-uuid', description='some-description', - start_time=datetime.datetime(2022,2,4,1,2,3), + start_time=utc.localize(datetime.datetime(2022, 2, 4, 1, 2, 3)), duration=datetime.timedelta(minutes=45), url='https://example.com/some/url', deletion_token='delete-me', @@ -290,7 +300,7 @@ class ConferenceManagerTests(TestCase): id=2, public_id='session-2-uuid', description='another-description', - start_time=datetime.datetime(2022,2,5,4,5,6), + start_time=utc.localize(datetime.datetime(2022, 2, 5, 4, 5, 6)), duration=datetime.timedelta(minutes=90), url='https://example.com/another/url', deletion_token='delete-me-too', @@ -306,7 +316,7 @@ class ConferenceManagerTests(TestCase): 'session-1-uuid': { 'room': { 'id': 1, - 'start_time': datetime.datetime(2022,2,4,1,2,3), + 'start_time': utc.localize(datetime.datetime(2022,2,4,1,2,3)), 'duration': datetime.timedelta(minutes=45), 'description': 'some-description', }, @@ -325,7 +335,7 @@ class ConferenceManagerTests(TestCase): id=1, public_id='session-1-uuid', description='some-description', - start_time=datetime.datetime(2022,2,4,1,2,3), + start_time=utc.localize(datetime.datetime(2022,2,4,1,2,3)), duration=datetime.timedelta(minutes=45), url='https://example.com/some/url', deletion_token='delete-me', @@ -341,7 +351,7 @@ class ConferenceManagerTests(TestCase): 'session-1-uuid': { 'room': { 'id': 1, - 'start_time': datetime.datetime(2022,2,4,1,2,3), + 'start_time': utc.localize(datetime.datetime(2022,2,4,1,2,3)), 'duration': datetime.timedelta(minutes=45), 'description': 'some-description', }, @@ -359,7 +369,7 @@ class ConferenceManagerTests(TestCase): id=1, public_id='session-1-uuid', description='some-description', - start_time=datetime.datetime(2022,2,4,1,2,3), + start_time=utc.localize(datetime.datetime(2022,2,4,1,2,3)), duration=datetime.timedelta(minutes=45), url='https://example.com/some/url', deletion_token='delete-me',