Use correct UTC time when creating Meetecho conferences. Fixes #3565. Commit ready for merge.

- Legacy-Id: 19969
This commit is contained in:
Jennifer Richards 2022-02-23 20:51:18 +00:00
parent f0d40680fe
commit 82ea659ed4
4 changed files with 54 additions and 41 deletions

View file

@ -1099,7 +1099,7 @@ def create_interim_session_conferences(sessions):
confs = meetecho_manager.create( confs = meetecho_manager.create(
group=session.group, group=session.group,
description=str(session), description=str(session),
start_time=ts.time, start_time=ts.utc_start_time(),
duration=ts.duration, duration=ts.duration,
) )
except Exception as err: except Exception as err:

View file

@ -456,8 +456,8 @@ class InterimTests(TestCase):
def test_create_interim_session_conferences(self, mock): def test_create_interim_session_conferences(self, mock):
mock_conf_mgr = mock.return_value # "instance" seen by the internals mock_conf_mgr = mock.return_value # "instance" seen by the internals
sessions = [ sessions = [
SessionFactory(meeting__type_id='interim', remote_instructions='junk'), SessionFactory(meeting__type_id='interim', meeting__time_zone='america/halifax', remote_instructions='junk'),
SessionFactory(meeting__type_id='interim', remote_instructions=''), SessionFactory(meeting__type_id='interim', meeting__time_zone='asia/kuala_lumpur', remote_instructions=''),
] ]
timeslots = [ timeslots = [
session.official_timeslotassignment().timeslot for session in sessions session.official_timeslotassignment().timeslot for session in sessions
@ -482,18 +482,18 @@ class InterimTests(TestCase):
mock_conf_mgr.create.return_value = [ mock_conf_mgr.create.return_value = [
Conference( Conference(
manager=mock_conf_mgr, id=1, public_id='some-uuid', description='desc', 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', deletion_token='please-delete-me',
), ),
] ]
create_interim_session_conferences([sessions[0]]) create_interim_session_conferences([sessions[0]])
self.assertTrue(mock_conf_mgr.create.called) self.assertTrue(mock_conf_mgr.create.called)
self.assertCountEqual( self.assertEqual(
mock_conf_mgr.create.call_args[1], mock_conf_mgr.create.call_args[1],
{ {
'group': sessions[0].group, 'group': sessions[0].group,
'description': str(sessions[0]), 'description': str(sessions[0]),
'start_time': timeslots[0].time, 'start_time': timeslots[0].utc_start_time(),
'duration': timeslots[0].duration, 'duration': timeslots[0].duration,
} }
) )
@ -507,30 +507,30 @@ class InterimTests(TestCase):
mock_conf_mgr.create.side_effect = [ mock_conf_mgr.create.side_effect = [
[Conference( [Conference(
manager=mock_conf_mgr, id=1, public_id='some-uuid', description='desc', 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', deletion_token='please-delete-me',
)], )],
[Conference( [Conference(
manager=mock_conf_mgr, id=2, public_id='another-uuid', description='desc', 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', deletion_token='please-delete-me-too',
)], )],
] ]
create_interim_session_conferences([sessions[0], sessions[1]]) create_interim_session_conferences([sessions[0], sessions[1]])
self.assertTrue(mock_conf_mgr.create.called) self.assertTrue(mock_conf_mgr.create.called)
self.assertCountEqual( self.assertEqual(
mock_conf_mgr.create.call_args_list, mock_conf_mgr.create.call_args_list,
[ [
({ ({
'group': sessions[0].group, 'group': sessions[0].group,
'description': str(sessions[0]), 'description': str(sessions[0]),
'start_time': timeslots[0].time, 'start_time': timeslots[0].utc_start_time(),
'duration': timeslots[0].duration, 'duration': timeslots[0].duration,
},), },),
({ ({
'group': sessions[1].group, 'group': sessions[1].group,
'description': str(sessions[1]), 'description': str(sessions[1]),
'start_time': timeslots[1].time, 'start_time': timeslots[1].utc_start_time(),
'duration': timeslots[1].duration, 'duration': timeslots[1].duration,
},), },),
] ]

View file

@ -15,11 +15,14 @@ import debug # pyflakes: ignore
from datetime import datetime, timedelta from datetime import datetime, timedelta
from json import JSONDecodeError from json import JSONDecodeError
from pytz import utc
from typing import Dict, Sequence, Union from typing import Dict, Sequence, Union
from urllib.parse import urljoin from urllib.parse import urljoin
class MeetechoAPI: class MeetechoAPI:
timezone = utc
def __init__(self, api_base: str, client_id: str, client_secret: str, request_timeout=3.01): def __init__(self, api_base: str, client_id: str, client_secret: str, request_timeout=3.01):
self.client_id = client_id self.client_id = client_id
self.client_secret = client_secret self.client_secret = client_secret
@ -57,10 +60,10 @@ class MeetechoAPI:
return None return None
def _deserialize_time(self, s: str) -> datetime: 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: 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: def _deserialize_duration(self, minutes: int) -> timedelta:
return timedelta(minutes=minutes) return timedelta(minutes=minutes)

View file

@ -4,6 +4,7 @@ import datetime
import requests import requests
import requests_mock import requests_mock
from pytz import timezone, utc
from unittest.mock import patch from unittest.mock import patch
from urllib.parse import urljoin from urllib.parse import urljoin
@ -91,7 +92,7 @@ class APITests(TestCase):
api = MeetechoAPI(API_BASE, CLIENT_ID, CLIENT_SECRET) api = MeetechoAPI(API_BASE, CLIENT_ID, CLIENT_SECRET)
api_response = api.schedule_meeting( api_response = api.schedule_meeting(
wg_token='my-token', 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), duration=datetime.timedelta(minutes=130),
description='interim-2021-wgname-01', description='interim-2021-wgname-01',
extrainfo='message for staff', extrainfo='message for staff',
@ -117,23 +118,32 @@ class APITests(TestCase):
}, },
'Incorrect request content' 'Incorrect request content'
) )
self.assertEqual( # same time in different time zones
api_response, for start_time in [
{ utc.localize(datetime.datetime(2021, 9, 14, 10, 0, 0)),
'rooms': { timezone('america/halifax').localize(datetime.datetime(2021, 9, 14, 7, 0, 0)),
'3d55bce0-535e-4ba8-bb8e-734911cf3c32': { timezone('europe/kiev').localize(datetime.datetime(2021, 9, 14, 13, 0, 0)),
'room': { timezone('pacific/easter').localize(datetime.datetime(2021, 9, 14, 5, 0, 0)),
'id': 18, timezone('africa/porto-novo').localize(datetime.datetime(2021, 9, 14, 11, 0, 0)),
'start_time': datetime.datetime(2021, 9, 14, 10, 0, 0), ]:
'duration': datetime.timedelta(minutes=130), self.assertEqual(
'description': 'interim-2021-wgname-01', 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): def test_fetch_meetings(self):
self.maxDiff = 2048 self.maxDiff = 2048
@ -181,7 +191,7 @@ class APITests(TestCase):
'3d55bce0-535e-4ba8-bb8e-734911cf3c32': { '3d55bce0-535e-4ba8-bb8e-734911cf3c32': {
'room': { 'room': {
'id': 18, '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), 'duration': datetime.timedelta(minutes=130),
'description': 'interim-2021-wgname-01', 'description': 'interim-2021-wgname-01',
}, },
@ -191,7 +201,7 @@ class APITests(TestCase):
'e68e96d4-d38f-475b-9073-ecab46ca96a5': { 'e68e96d4-d38f-475b-9073-ecab46ca96a5': {
'room': { 'room': {
'id': 23, '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), 'duration': datetime.timedelta(minutes=30),
'description': 'interim-2021-wgname-02', 'description': 'interim-2021-wgname-02',
}, },
@ -239,7 +249,7 @@ class APITests(TestCase):
def test_time_serialization(self): def test_time_serialization(self):
"""Time de/serialization should be consistent""" """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) api = MeetechoAPI(API_BASE, CLIENT_ID, CLIENT_SECRET)
self.assertEqual(api._deserialize_time(api._serialize_time(time)), time) self.assertEqual(api._deserialize_time(api._serialize_time(time)), time)
@ -253,7 +263,7 @@ class ConferenceManagerTests(TestCase):
'session-1-uuid': { 'session-1-uuid': {
'room': { 'room': {
'id': 1, '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), 'duration': datetime.timedelta(minutes=45),
'description': 'some-description', 'description': 'some-description',
}, },
@ -263,7 +273,7 @@ class ConferenceManagerTests(TestCase):
'session-2-uuid': { 'session-2-uuid': {
'room': { 'room': {
'id': 2, '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), 'duration': datetime.timedelta(minutes=90),
'description': 'another-description', 'description': 'another-description',
}, },
@ -280,7 +290,7 @@ class ConferenceManagerTests(TestCase):
id=1, id=1,
public_id='session-1-uuid', public_id='session-1-uuid',
description='some-description', 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), duration=datetime.timedelta(minutes=45),
url='https://example.com/some/url', url='https://example.com/some/url',
deletion_token='delete-me', deletion_token='delete-me',
@ -290,7 +300,7 @@ class ConferenceManagerTests(TestCase):
id=2, id=2,
public_id='session-2-uuid', public_id='session-2-uuid',
description='another-description', 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), duration=datetime.timedelta(minutes=90),
url='https://example.com/another/url', url='https://example.com/another/url',
deletion_token='delete-me-too', deletion_token='delete-me-too',
@ -306,7 +316,7 @@ class ConferenceManagerTests(TestCase):
'session-1-uuid': { 'session-1-uuid': {
'room': { 'room': {
'id': 1, '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), 'duration': datetime.timedelta(minutes=45),
'description': 'some-description', 'description': 'some-description',
}, },
@ -325,7 +335,7 @@ class ConferenceManagerTests(TestCase):
id=1, id=1,
public_id='session-1-uuid', public_id='session-1-uuid',
description='some-description', 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), duration=datetime.timedelta(minutes=45),
url='https://example.com/some/url', url='https://example.com/some/url',
deletion_token='delete-me', deletion_token='delete-me',
@ -341,7 +351,7 @@ class ConferenceManagerTests(TestCase):
'session-1-uuid': { 'session-1-uuid': {
'room': { 'room': {
'id': 1, '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), 'duration': datetime.timedelta(minutes=45),
'description': 'some-description', 'description': 'some-description',
}, },
@ -359,7 +369,7 @@ class ConferenceManagerTests(TestCase):
id=1, id=1,
public_id='session-1-uuid', public_id='session-1-uuid',
description='some-description', 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), duration=datetime.timedelta(minutes=45),
url='https://example.com/some/url', url='https://example.com/some/url',
deletion_token='delete-me', deletion_token='delete-me',