diff --git a/core/utils/tests_utils.py b/core/utils/tests_utils.py new file mode 100644 index 0000000..8d6e2f7 --- /dev/null +++ b/core/utils/tests_utils.py @@ -0,0 +1,88 @@ +from unittest import TestCase +from unittest.mock import MagicMock, patch + +from langcodes import Language + +from core.utils.utils import _get_user, language_iso + + +class LanguageIsoTests(TestCase): + """Tests for the ``language_iso`` helper.""" + + def test_normalises_pt_br(self): + self.assertEqual(language_iso("pt-BR"), "pt") + + def test_normalises_en_us(self): + self.assertEqual(language_iso("en-US"), "en") + + def test_normalises_es_419(self): + self.assertEqual(language_iso("es-419"), "es") + + def test_simple_code_unchanged(self): + self.assertEqual(language_iso("fr"), "fr") + + def test_empty_string_returns_empty(self): + self.assertEqual(language_iso(""), "") + + def test_none_returns_empty(self): + self.assertEqual(language_iso(None), "") + + def test_invalid_code_returns_empty(self): + self.assertEqual(language_iso("zzzzzz"), "") + + +class GetUserTests(TestCase): + """Tests for the ``_get_user`` helper.""" + + @patch("core.utils.utils.get_user_model") + def test_resolves_by_request_user_id(self, mock_get_user_model): + mock_user_model = MagicMock() + mock_get_user_model.return_value = mock_user_model + request = MagicMock() + request.user_id = 42 + sentinel = object() + mock_user_model.objects.get.return_value = sentinel + + result = _get_user(request) + + mock_user_model.objects.get.assert_called_once_with(pk=42) + self.assertIs(result, sentinel) + + @patch("core.utils.utils.get_user_model") + def test_falls_back_to_user_id(self, mock_get_user_model): + mock_user_model = MagicMock() + mock_get_user_model.return_value = mock_user_model + request = MagicMock(spec=[]) # no user_id attribute + sentinel = object() + mock_user_model.objects.get.return_value = sentinel + + result = _get_user(request, user_id=7) + + mock_user_model.objects.get.assert_called_once_with(pk=7) + self.assertIs(result, sentinel) + + @patch("core.utils.utils.get_user_model") + def test_falls_back_to_username(self, mock_get_user_model): + mock_user_model = MagicMock() + mock_get_user_model.return_value = mock_user_model + request = MagicMock(spec=[]) # no user_id attribute + sentinel = object() + mock_user_model.objects.get.return_value = sentinel + + result = _get_user(request, username="alice") + + mock_user_model.objects.get.assert_called_once_with(username="alice") + self.assertIs(result, sentinel) + + @patch("core.utils.utils.get_user_model") + def test_user_id_preferred_over_username(self, mock_get_user_model): + mock_user_model = MagicMock() + mock_get_user_model.return_value = mock_user_model + request = MagicMock(spec=[]) # no user_id attribute + sentinel = object() + mock_user_model.objects.get.return_value = sentinel + + result = _get_user(request, username="alice", user_id=7) + + mock_user_model.objects.get.assert_called_once_with(pk=7) + self.assertIs(result, sentinel) diff --git a/core/utils/utils.py b/core/utils/utils.py new file mode 100644 index 0000000..da8b463 --- /dev/null +++ b/core/utils/utils.py @@ -0,0 +1,56 @@ +import logging + +from django.contrib.auth import get_user_model + +from langcodes import Language + +from core.utils.requester import NonRetryableError, fetch_data + + +logger = logging.getLogger(__name__) + +__all__ = [ + "fetch_data", + "language_iso", + "NonRetryableError", + "_get_user", +] + + +def language_iso(code): + """Normalize a language code to its shortest ISO 639 form. + + Uses the ``langcodes`` library to parse ``code`` and return the two-letter + language subtag when valid (e.g. ``"pt-BR"`` → ``"pt"``). + + Returns an empty string when the code is ``None``, empty or cannot be + parsed into a valid language. + """ + if not code: + return "" + try: + lang = Language.get(code) + if lang.is_valid(): + return lang.language or "" + except ValueError: + pass + return "" + + +def _get_user(request, username=None, user_id=None): + """Resolve the acting user from the current request context. + + Attempts to look up the user by ``request.user_id`` first. If that + attribute is missing (``AttributeError``), falls back to ``user_id`` + or ``username``. + + Returns ``None`` when no lookup parameter is available. + """ + User = get_user_model() + try: + return User.objects.get(pk=request.user_id) + except AttributeError: + if user_id: + return User.objects.get(pk=user_id) + if username: + return User.objects.get(username=username) diff --git a/requirements/base.txt b/requirements/base.txt index 5c1bac1..ff491b9 100644 --- a/requirements/base.txt +++ b/requirements/base.txt @@ -42,3 +42,7 @@ git+https://git@github.com/scieloorg/packtools@4.12.6#egg=packtools # Langdetect # ------------------------------------------------------------------------------ langdetect~=1.0.9 + +# Langcodes +# ------------------------------------------------------------------------------ +langcodes==3.5.1 # https://pypi.org/project/langcodes/