From 244dd89cf9ee83131f4a324c78fdc73f470f73e7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Laur=C3=A9line=20Gu=C3=A9rin?= Date: Tue, 7 Jun 2022 15:20:20 +0200 Subject: [PATCH] misc: retrieve check types from lingo (#66015) --- chrono/utils/lingo.py | 77 +++++++++++++++++++++++++++++++++++++++++++ tests/settings.py | 9 +++++ tests/test_utils.py | 69 ++++++++++++++++++++++++++++++++++++++ 3 files changed, 155 insertions(+) create mode 100644 chrono/utils/lingo.py diff --git a/chrono/utils/lingo.py b/chrono/utils/lingo.py new file mode 100644 index 00000000..545a91de --- /dev/null +++ b/chrono/utils/lingo.py @@ -0,0 +1,77 @@ +# chrono - agendas system +# Copyright (C) 2022 Entr'ouvert +# +# This program is free software: you can redistribute it and/or modify it +# under the terms of the GNU Affero General Public License as published +# by the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Affero General Public License for more details. +# +# You should have received a copy of the GNU Affero General Public License +# along with this program. If not, see . + +import dataclasses +import json + +from django.conf import settings +from requests.exceptions import RequestException + +from chrono.utils.requests_wrapper import requests + + +def is_lingo_enabled(): + return hasattr(settings, 'KNOWN_SERVICES') and settings.KNOWN_SERVICES.get('lingo') + + +def get_lingo_service(): + if not is_lingo_enabled(): + return {} + return list(settings.KNOWN_SERVICES.get('lingo').values())[0] + + +def get_lingo_json(path, log_errors=True): + lingo_site = get_lingo_service() + if lingo_site is None: + return + try: + response = requests.get( + path, + remote_service=lingo_site, + without_user=True, + headers={'accept': 'application/json'}, + log_errors=log_errors, + ) + response.raise_for_status() + except RequestException as e: + if e.response is not None: + try: + # return json if available (on 404 responses by example) + return e.response.json() + except json.JSONDecodeError: + pass + return + return response.json() + + +@dataclasses.dataclass +class CheckType: + slug: str + label: str + kind: str + + +def get_agenda_check_types(agenda): + result = get_lingo_json('api/agenda/%s/check-types/' % agenda.slug) + if result is None: + return [] + if result.get('data') is None: + return [] + + check_types = [] + for ct in result['data']: + check_types.append(CheckType(slug=ct['id'], label=ct['text'], kind=ct['kind'])) + return check_types diff --git a/tests/settings.py b/tests/settings.py index 52a56b75..823bc58a 100644 --- a/tests/settings.py +++ b/tests/settings.py @@ -27,6 +27,15 @@ KNOWN_SERVICES = { 'backoffice-menu-url': 'http://example.org/manage/', } }, + 'lingo': { + 'default': { + 'title': 'test', + 'url': 'http://lingo.example.org', + 'secret': 'chrono', + 'orig': 'chrono', + 'backoffice-menu-url': 'http://example.org/manage/', + } + }, } LEGACY_URLS_MAPPING = {'old.org': 'new.org'} diff --git a/tests/test_utils.py b/tests/test_utils.py index 1091c0be..77f95d29 100644 --- a/tests/test_utils.py +++ b/tests/test_utils.py @@ -1,6 +1,13 @@ import datetime +import json +from unittest import mock +from requests.exceptions import ConnectionError +from requests.models import Response + +from chrono.agendas.models import Agenda from chrono.utils.date import get_weekday_index +from chrono.utils.lingo import CheckType, get_agenda_check_types def test_get_weekday_index(): @@ -21,3 +28,65 @@ def test_get_weekday_index(): assert get_weekday_index(date.replace(day=28)) == 4 assert get_weekday_index(date.replace(day=29)) == 5 assert get_weekday_index(date.replace(day=30)) == 5 + + +CHECK_TYPES_DATA = [ + {'id': 'bar-reason', 'kind': 'presence', 'text': 'Bar reason'}, + {'id': 'foo-reason', 'kind': 'absence', 'text': 'Foo reason'}, +] + + +class MockedRequestResponse(mock.Mock): + status_code = 200 + + def json(self): + return json.loads(self.content) + + +def test_get_agenda_check_types_no_service(settings): + agenda = Agenda(slug='foo') + + settings.KNOWN_SERVICES = {} + assert get_agenda_check_types(agenda) == [] + + settings.KNOWN_SERVICES = {'other': []} + assert get_agenda_check_types(agenda) == [] + + +def test_get_agenda_check_types(): + agenda = Agenda(slug='foo') + + with mock.patch('requests.Session.get') as requests_get: + requests_get.side_effect = ConnectionError() + assert get_agenda_check_types(agenda) == [] + + with mock.patch('requests.Session.get') as requests_get: + mock_resp = Response() + mock_resp.status_code = 500 + requests_get.return_value = mock_resp + assert get_agenda_check_types(agenda) == [] + + with mock.patch('requests.Session.get') as requests_get: + mock_resp = Response() + mock_resp.status_code = 404 + requests_get.return_value = mock_resp + assert get_agenda_check_types(agenda) == [] + + with mock.patch('requests.Session.get') as requests_get: + requests_get.return_value = MockedRequestResponse(content=json.dumps({'foo': 'bar'})) + assert get_agenda_check_types(agenda) == [] + + data = {'data': []} + with mock.patch('requests.Session.get') as requests_get: + requests_get.return_value = MockedRequestResponse(content=json.dumps(data)) + assert get_agenda_check_types(agenda) == [] + assert requests_get.call_args_list[0][0] == ('api/agenda/foo/check-types/',) + assert requests_get.call_args_list[0][1]['remote_service']['url'] == 'http://lingo.example.org' + + data = {'data': CHECK_TYPES_DATA} + with mock.patch('requests.Session.get') as requests_get: + requests_get.return_value = MockedRequestResponse(content=json.dumps(data)) + assert get_agenda_check_types(agenda) == [ + CheckType(slug='bar-reason', label='Bar reason', kind='presence'), + CheckType(slug='foo-reason', label='Foo reason', kind='absence'), + ]