Use requests for external requests

L'API de requests est beaucoup plus claire que celle d'urllib et urllib3.
This commit is contained in:
imperosol 2025-01-20 19:20:13 +01:00
parent 3df33261ce
commit 85c8b7d11c
5 changed files with 23 additions and 25 deletions

View File

@ -2,7 +2,7 @@ from datetime import datetime, timedelta
from pathlib import Path from pathlib import Path
from typing import final from typing import final
import urllib3 import requests
from dateutil.relativedelta import relativedelta from dateutil.relativedelta import relativedelta
from django.conf import settings from django.conf import settings
from django.urls import reverse from django.urls import reverse
@ -35,16 +35,15 @@ class IcsCalendar:
@classmethod @classmethod
def make_external(cls) -> Path | None: def make_external(cls) -> Path | None:
calendar = urllib3.request( calendar = requests.get(
"GET", "https://calendar.google.com/calendar/ical/ae.utbm%40gmail.com/public/basic.ics"
"https://calendar.google.com/calendar/ical/ae.utbm%40gmail.com/public/basic.ics",
) )
if calendar.status != 200: if not calendar.ok:
return None return None
cls._CACHE_FOLDER.mkdir(parents=True, exist_ok=True) cls._CACHE_FOLDER.mkdir(parents=True, exist_ok=True)
with open(cls._EXTERNAL_CALENDAR, "wb") as f: with open(cls._EXTERNAL_CALENDAR, "wb") as f:
_ = f.write(calendar.data) _ = f.write(calendar.content)
return cls._EXTERNAL_CALENDAR return cls._EXTERNAL_CALENDAR
@classmethod @classmethod

View File

@ -16,11 +16,11 @@ from com.calendar import IcsCalendar
@dataclass @dataclass
class MockResponse: class MockResponse:
status: int ok: bool
value: str value: str
@property @property
def data(self): def content(self):
return self.value.encode("utf8") return self.value.encode("utf8")
@ -38,7 +38,7 @@ class TestExternalCalendar:
@pytest.fixture @pytest.fixture
def mock_request(self): def mock_request(self):
mock = MagicMock() mock = MagicMock()
with patch("urllib3.request", mock): with patch("requests.get", mock):
yield mock yield mock
@pytest.fixture @pytest.fixture
@ -52,15 +52,12 @@ class TestExternalCalendar:
def clear_cache(self): def clear_cache(self):
IcsCalendar._EXTERNAL_CALENDAR.unlink(missing_ok=True) IcsCalendar._EXTERNAL_CALENDAR.unlink(missing_ok=True)
@pytest.mark.parametrize("error_code", [403, 404, 500]) def test_fetch_error(self, client: Client, mock_request: MagicMock):
def test_fetch_error( mock_request.return_value = MockResponse(ok=False, value="not allowed")
self, client: Client, mock_request: MagicMock, error_code: int
):
mock_request.return_value = MockResponse(error_code, "not allowed")
assert client.get(reverse("api:calendar_external")).status_code == 404 assert client.get(reverse("api:calendar_external")).status_code == 404
def test_fetch_success(self, client: Client, mock_request: MagicMock): def test_fetch_success(self, client: Client, mock_request: MagicMock):
external_response = MockResponse(200, "Definitely an ICS") external_response = MockResponse(ok=True, value="Definitely an ICS")
mock_request.return_value = external_response mock_request.return_value = external_response
response = client.get(reverse("api:calendar_external")) response = client.get(reverse("api:calendar_external"))
assert response.status_code == 200 assert response.status_code == 200

View File

@ -1,7 +1,6 @@
"""Set of functions to interact with the UTBM UV api.""" """Set of functions to interact with the UTBM UV api."""
import urllib import requests
from django.conf import settings from django.conf import settings
from pedagogy.schemas import ShortUvList, UtbmFullUvSchema, UtbmShortUvSchema, UvSchema from pedagogy.schemas import ShortUvList, UtbmFullUvSchema, UtbmShortUvSchema, UvSchema
@ -12,8 +11,8 @@ def find_uv(lang, year, code) -> UvSchema | None:
# query the UV list # query the UV list
base_url = settings.SITH_PEDAGOGY_UTBM_API base_url = settings.SITH_PEDAGOGY_UTBM_API
uvs_url = f"{base_url}/uvs/{lang}/{year}" uvs_url = f"{base_url}/uvs/{lang}/{year}"
response = urllib.request.urlopen(uvs_url) response = requests.get(uvs_url)
uvs: list[UtbmShortUvSchema] = ShortUvList.validate_json(response.read()) uvs: list[UtbmShortUvSchema] = ShortUvList.validate_json(response.content)
short_uv = next((uv for uv in uvs if uv.code == code), None) short_uv = next((uv for uv in uvs if uv.code == code), None)
if short_uv is None: if short_uv is None:
@ -21,12 +20,12 @@ def find_uv(lang, year, code) -> UvSchema | None:
# get detailed information about the UV # get detailed information about the UV
uv_url = f"{base_url}/uv/{lang}/{year}/{code}/{short_uv.code_formation}" uv_url = f"{base_url}/uv/{lang}/{year}/{code}/{short_uv.code_formation}"
response = urllib.request.urlopen(uv_url) response = requests.get(uv_url)
full_uv = UtbmFullUvSchema.model_validate_json(response.read()) full_uv = UtbmFullUvSchema.model_validate_json(response.content)
return _make_clean_uv(short_uv, full_uv) return make_clean_uv(short_uv, full_uv)
def _make_clean_uv(short_uv: UtbmShortUvSchema, full_uv: UtbmFullUvSchema) -> UvSchema: def make_clean_uv(short_uv: UtbmShortUvSchema, full_uv: UtbmFullUvSchema) -> UvSchema:
"""Cleans the data up so that it corresponds to our data representation. """Cleans the data up so that it corresponds to our data representation.
Some of the needed information are in the short uv schema, some Some of the needed information are in the short uv schema, some

View File

@ -44,6 +44,7 @@ dependencies = [
"django-honeypot<2.0.0,>=1.2.1", "django-honeypot<2.0.0,>=1.2.1",
"pydantic-extra-types<3.0.0,>=2.10.1", "pydantic-extra-types<3.0.0,>=2.10.1",
"ical<9.0.0,>=8.3.0", "ical<9.0.0,>=8.3.0",
"requests>=2.32.3",
] ]
[project.urls] [project.urls]

6
uv.lock generated
View File

@ -155,7 +155,7 @@ name = "click"
version = "8.1.8" version = "8.1.8"
source = { registry = "https://pypi.org/simple" } source = { registry = "https://pypi.org/simple" }
dependencies = [ dependencies = [
{ name = "colorama", marker = "sys_platform == 'win32'" }, { name = "colorama", marker = "platform_system == 'Windows'" },
] ]
sdist = { url = "https://files.pythonhosted.org/packages/b9/2e/0090cbf739cee7d23781ad4b89a9894a41538e4fcf4c31dcdd705b78eb8b/click-8.1.8.tar.gz", hash = "sha256:ed53c9d8990d83c2a27deae68e4ee337473f6330c040a31d4225c9574d16096a", size = 226593 } sdist = { url = "https://files.pythonhosted.org/packages/b9/2e/0090cbf739cee7d23781ad4b89a9894a41538e4fcf4c31dcdd705b78eb8b/click-8.1.8.tar.gz", hash = "sha256:ed53c9d8990d83c2a27deae68e4ee337473f6330c040a31d4225c9574d16096a", size = 226593 }
wheels = [ wheels = [
@ -744,7 +744,7 @@ version = "1.6.1"
source = { registry = "https://pypi.org/simple" } source = { registry = "https://pypi.org/simple" }
dependencies = [ dependencies = [
{ name = "click" }, { name = "click" },
{ name = "colorama", marker = "sys_platform == 'win32'" }, { name = "colorama", marker = "platform_system == 'Windows'" },
{ name = "ghp-import" }, { name = "ghp-import" },
{ name = "jinja2" }, { name = "jinja2" },
{ name = "markdown" }, { name = "markdown" },
@ -1437,6 +1437,7 @@ dependencies = [
{ name = "pydantic-extra-types" }, { name = "pydantic-extra-types" },
{ name = "python-dateutil" }, { name = "python-dateutil" },
{ name = "reportlab" }, { name = "reportlab" },
{ name = "requests" },
{ name = "sentry-sdk" }, { name = "sentry-sdk" },
{ name = "sphinx" }, { name = "sphinx" },
{ name = "tomli" }, { name = "tomli" },
@ -1495,6 +1496,7 @@ requires-dist = [
{ name = "pydantic-extra-types", specifier = ">=2.10.1,<3.0.0" }, { name = "pydantic-extra-types", specifier = ">=2.10.1,<3.0.0" },
{ name = "python-dateutil", specifier = ">=2.9.0.post0,<3.0.0.0" }, { name = "python-dateutil", specifier = ">=2.9.0.post0,<3.0.0.0" },
{ name = "reportlab", specifier = ">=4.2.5,<5.0.0" }, { name = "reportlab", specifier = ">=4.2.5,<5.0.0" },
{ name = "requests", specifier = ">=2.32.3" },
{ name = "sentry-sdk", specifier = ">=2.19.2,<3.0.0" }, { name = "sentry-sdk", specifier = ">=2.19.2,<3.0.0" },
{ name = "sphinx", specifier = ">=5,<6" }, { name = "sphinx", specifier = ">=5,<6" },
{ name = "tomli", specifier = ">=2.2.1,<3.0.0" }, { name = "tomli", specifier = ">=2.2.1,<3.0.0" },