From 47829f9d76ce1cac468c3eed28d5a5ade00c9ab0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fr=C3=A9d=C3=A9ric=20P=C3=A9ters?= Date: Sat, 11 Jan 2020 12:10:19 +0100 Subject: [PATCH] agendas: treat remote ICS as UTF-8 if it looks like it (#38510) --- chrono/agendas/models.py | 7 +++++++ tests/test_agendas.py | 20 ++++++++++++++++++-- 2 files changed, 25 insertions(+), 2 deletions(-) diff --git a/chrono/agendas/models.py b/chrono/agendas/models.py index 50ec863b..04f73411 100644 --- a/chrono/agendas/models.py +++ b/chrono/agendas/models.py @@ -578,6 +578,13 @@ class Desk(models.Model): if source is None: source = TimePeriodExceptionSource(desk=self, ics_url=ics_url) + try: + # override response encoding received in HTTP headers as it may + # often be missing and defaults to iso-8859-15. + response.content.decode('utf-8') + response.encoding = 'utf-8' + except UnicodeDecodeError as e: + pass return self._import_timeperiod_exceptions_from_ics(source=source, data=response.text) def import_timeperiod_exceptions_from_ics_file(self, ics_file, source=None): diff --git a/tests/test_agendas.py b/tests/test_agendas.py index 8b57585d..abd3a809 100644 --- a/tests/test_agendas.py +++ b/tests/test_agendas.py @@ -30,7 +30,7 @@ DTSTAMP:20170824T082855Z DTSTART:20170831T170800Z DTEND:20170831T203400Z SEQUENCE:1 -SUMMARY:Event 1 +SUMMARY:Événement 1 END:VEVENT BEGIN:VEVENT DTSTAMP:20170824T092855Z @@ -211,7 +211,7 @@ def test_timeperiodexception_creation_from_ics_without_startdt(): ics_sample = ContentFile("\n".join(lines), name='sample.ics') with pytest.raises(ICSError) as e: desk.import_timeperiod_exceptions_from_ics_file(ics_sample) - assert 'Event "Event 1" has no start date.' == str(e.value) + assert 'Event "Événement 1" has no start date.' == str(e.value) def test_timeperiodexception_creation_from_ics_without_enddt(): @@ -299,6 +299,7 @@ def test_timeperiodexception_creation_from_remote_ics(mocked_get): mocked_get.return_value = mocked_response exceptions_count = desk.import_timeperiod_exceptions_from_remote_ics('http://example.com/sample.ics') assert exceptions_count == 2 + assert 'Événement 1' in [x.label for x in desk.timeperiodexception_set.all()] mocked_response.text = ICS_SAMPLE_WITH_NO_EVENTS mocked_get.return_value = mocked_response @@ -307,6 +308,21 @@ def test_timeperiodexception_creation_from_remote_ics(mocked_get): assert str(e.value) == "The file doesn't contain any events." +@mock.patch('chrono.agendas.models.requests.get') +def test_timeperiodexception_remote_ics_encoding(mocked_get): + agenda = Agenda(label=u'Test 8 agenda') + agenda.save() + desk = Desk(label='Test 8 desk', agenda=agenda) + desk.save() + mocked_response = mock.Mock() + mocked_response.content = ICS_SAMPLE.encode('iso-8859-15') + mocked_response.text = ICS_SAMPLE + mocked_get.return_value = mocked_response + exceptions_count = desk.import_timeperiod_exceptions_from_remote_ics('http://example.com/sample.ics') + assert exceptions_count == 2 + assert 'Événement 1' in [x.label for x in desk.timeperiodexception_set.all()] + + @mock.patch('chrono.agendas.models.requests.get') def test_timeperiodexception_creation_from_unreachable_remote_ics(mocked_get): agenda = Agenda(label=u'Test 9 agenda')