From e25603a0654be26ee0c5acf6fdd42e1c45c772ab Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Romain=20Gon=C3=A7alves?= Date: Sun, 12 May 2024 19:42:37 +0200 Subject: feat: add tests from last year --- .gitignore | 1 + cip_paris_client/client.py | 9 ++- cip_paris_client/parsers.py | 58 +++++++++------- cip_paris_client/tests/test_parsers.py | 9 +++ poetry.lock | 119 ++++++++++++++++++++++++++++++++- pyproject.toml | 5 ++ setup.cfg | 2 + 7 files changed, 175 insertions(+), 28 deletions(-) create mode 100644 .gitignore create mode 100644 cip_paris_client/tests/test_parsers.py create mode 100644 setup.cfg diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..bee8a64 --- /dev/null +++ b/.gitignore @@ -0,0 +1 @@ +__pycache__ diff --git a/cip_paris_client/client.py b/cip_paris_client/client.py index cc87f4d..bb58f39 100644 --- a/cip_paris_client/client.py +++ b/cip_paris_client/client.py @@ -38,7 +38,12 @@ def get_sessions( stop_after: int = 10, ) -> Iterator[Session]: """Retrieve available movie sessions.""" - for category, page_index in itertools.product(MovieCategories, range(1, stop_after)): - params = {"category": category.value, "page": page_index} + for category, page_index in itertools.product( + MovieCategories, range(1, stop_after) + ): + params = { + "category": category.value, + "page": page_index, + } response = httpx.get(url, params=params) yield parse_sessions(response.content) diff --git a/cip_paris_client/parsers.py b/cip_paris_client/parsers.py index e22de8e..34cc1ea 100644 --- a/cip_paris_client/parsers.py +++ b/cip_paris_client/parsers.py @@ -1,16 +1,13 @@ from datetime import ( date, datetime, + time, ) -from typing import TYPE_CHECKING from selectolax.parser import HTMLParser from cip_paris_client.schemas import Session -if TYPE_CHECKING: - from typing import Final - def is_released_next_year( current_month: int, @@ -25,40 +22,51 @@ def is_released_next_year( def parse_date(date_str: str) -> date: - """Parse the CIP date from webpage.""" - date_format: Final[str] = "" + """Parse date from the CIP webpage format.""" day, month = map(int, date_str.split()[1].split("/")) - - date = datetime.date + year = datetime.now().year if is_released_next_year(datetime.now().month, month): - print() + year += 1 + return date(year=year, month=month, day=day) -def parse_sessions(html: bytes) -> list[Session]: - """Parse movie sessions from an html webpage.""" - movie_container_query: Final[str] = "div.movie-results-container" - movie_name_query: Final[str] = "div.desc h3" - sessions_container_query: Final[str] = "div.session-date > div.item" - session_url_selector: Final[str] = "a" - session_date_selector: Final[str] = "div.sessionDate" - session_time_selector: Final[str] = "div p.time" +def parse_time(time_str: str) -> time: + """Parse the time from the CIP webpage format.""" + hour, minute = map(int, time_str.split(":")) + return time(hour=hour, minute=minute, second=0) + + +def parse_sessions( + html: bytes, + movie_container_query: str = "div.movie-results-container", + movie_name_query: str = "div.desc h3", + sessions_container_query: str = "div.session-date > div.item", + session_url_selector: str = "a", + session_date_selector: str = "div.sessionDate", + session_time_selector: str = "div p.time", +) -> list[Session]: + """Parse movie sessions from an html webpage.""" for movie_tree in HTMLParser(html).css(movie_container_query): movie_name = movie_tree.css_first(movie_name_query).text() for session_tree in movie_tree.css(sessions_container_query): - attributes = session_tree.css_first(session_url_selector).attributes - date: str = session_tree.css_first(session_date_selector).text() - time: str = session_tree.css_first(session_time_selector).text() - - print(date) - print(time) + # Extract attributes first, conversion is done later. + attributes = session_tree.css_first( + session_url_selector + ).attributes + date_str: str = session_tree.css_first( + session_date_selector + ).text() + time_str: str = session_tree.css_first( + session_time_selector + ).text() yield Session( url=attributes.get("href") or "", cinema=attributes.get("title") or "", movie=movie_name, - date=date, - time=time, + date=parse_date(date_str), + time=parse_time(time_str), ) diff --git a/cip_paris_client/tests/test_parsers.py b/cip_paris_client/tests/test_parsers.py new file mode 100644 index 0000000..8bc57e8 --- /dev/null +++ b/cip_paris_client/tests/test_parsers.py @@ -0,0 +1,9 @@ +from cip_paris_client import parsers + + +def test_is_released_next_year_true() -> None: + assert parsers.is_released_next_year(9, 2) is True + + +def test_is_released_next_year_false() -> None: + assert parsers.is_released_next_year(11, 8) is False diff --git a/poetry.lock b/poetry.lock index f2e54e1..ae3955f 100644 --- a/poetry.lock +++ b/poetry.lock @@ -42,6 +42,33 @@ files = [ {file = "certifi-2023.7.22.tar.gz", hash = "sha256:539cc1d13202e33ca466e88b2807e29f4c13049d6d87031a3c110744495cb082"}, ] +[[package]] +name = "colorama" +version = "0.4.6" +description = "Cross-platform colored terminal text." +optional = false +python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,!=3.5.*,!=3.6.*,>=2.7" +files = [ + {file = "colorama-0.4.6-py2.py3-none-any.whl", hash = "sha256:4f1d9991f5acc0ca119f9d443620b77f9d6b33703e51011c16baf57afb285fc6"}, + {file = "colorama-0.4.6.tar.gz", hash = "sha256:08695f5cb7ed6e0531a20572697297273c47b8cae5a63ffc6d6ed5c201be6e44"}, +] + +[[package]] +name = "flake8" +version = "6.1.0" +description = "the modular source code checker: pep8 pyflakes and co" +optional = false +python-versions = ">=3.8.1" +files = [ + {file = "flake8-6.1.0-py2.py3-none-any.whl", hash = "sha256:ffdfce58ea94c6580c77888a86506937f9a1a227dfcd15f245d694ae20a6b6e5"}, + {file = "flake8-6.1.0.tar.gz", hash = "sha256:d5b3857f07c030bdb5bf41c7f53799571d75c4491748a3adcd47de929e34cd23"}, +] + +[package.dependencies] +mccabe = ">=0.7.0,<0.8.0" +pycodestyle = ">=2.11.0,<2.12.0" +pyflakes = ">=3.1.0,<3.2.0" + [[package]] name = "h11" version = "0.14.0" @@ -108,6 +135,28 @@ files = [ {file = "idna-3.4.tar.gz", hash = "sha256:814f528e8dead7d329833b91c5faa87d60bf71824cd12a7530b5526063d02cb4"}, ] +[[package]] +name = "iniconfig" +version = "2.0.0" +description = "brain-dead simple config-ini parsing" +optional = false +python-versions = ">=3.7" +files = [ + {file = "iniconfig-2.0.0-py3-none-any.whl", hash = "sha256:b6a85871a79d2e3b22d2d1b94ac2824226a63c6b741c88f7ae975f18b6778374"}, + {file = "iniconfig-2.0.0.tar.gz", hash = "sha256:2d91e135bf72d31a410b17c16da610a82cb55f6b0477d1a902134b24a455b8b3"}, +] + +[[package]] +name = "mccabe" +version = "0.7.0" +description = "McCabe checker, plugin for flake8" +optional = false +python-versions = ">=3.6" +files = [ + {file = "mccabe-0.7.0-py2.py3-none-any.whl", hash = "sha256:6c2d30ab6be0e4a46919781807b4f0d834ebdd6c6e3dca0bda5a15f863427b6e"}, + {file = "mccabe-0.7.0.tar.gz", hash = "sha256:348e0240c33b60bbdf4e523192ef919f28cb2c3d7d5c7794f74009290f236325"}, +] + [[package]] name = "mypy" version = "1.4.1" @@ -164,6 +213,43 @@ files = [ {file = "mypy_extensions-1.0.0.tar.gz", hash = "sha256:75dbf8955dc00442a438fc4d0666508a9a97b6bd41aa2f0ffe9d2f2725af0782"}, ] +[[package]] +name = "packaging" +version = "23.1" +description = "Core utilities for Python packages" +optional = false +python-versions = ">=3.7" +files = [ + {file = "packaging-23.1-py3-none-any.whl", hash = "sha256:994793af429502c4ea2ebf6bf664629d07c1a9fe974af92966e4b8d2df7edc61"}, + {file = "packaging-23.1.tar.gz", hash = "sha256:a392980d2b6cffa644431898be54b0045151319d1e7ec34f0cfed48767dd334f"}, +] + +[[package]] +name = "pluggy" +version = "1.2.0" +description = "plugin and hook calling mechanisms for python" +optional = false +python-versions = ">=3.7" +files = [ + {file = "pluggy-1.2.0-py3-none-any.whl", hash = "sha256:c2fd55a7d7a3863cba1a013e4e2414658b1d07b6bc57b3919e0c63c9abb99849"}, + {file = "pluggy-1.2.0.tar.gz", hash = "sha256:d12f0c4b579b15f5e054301bb226ee85eeeba08ffec228092f8defbaa3a4c4b3"}, +] + +[package.extras] +dev = ["pre-commit", "tox"] +testing = ["pytest", "pytest-benchmark"] + +[[package]] +name = "pycodestyle" +version = "2.11.0" +description = "Python style guide checker" +optional = false +python-versions = ">=3.8" +files = [ + {file = "pycodestyle-2.11.0-py2.py3-none-any.whl", hash = "sha256:5d1013ba8dc7895b548be5afb05740ca82454fd899971563d2ef625d090326f8"}, + {file = "pycodestyle-2.11.0.tar.gz", hash = "sha256:259bcc17857d8a8b3b4a2327324b79e5f020a13c16074670f9c8c8f872ea76d0"}, +] + [[package]] name = "pydantic" version = "2.1.1" @@ -296,6 +382,37 @@ files = [ [package.dependencies] typing-extensions = ">=4.6.0,<4.7.0 || >4.7.0" +[[package]] +name = "pyflakes" +version = "3.1.0" +description = "passive checker of Python programs" +optional = false +python-versions = ">=3.8" +files = [ + {file = "pyflakes-3.1.0-py2.py3-none-any.whl", hash = "sha256:4132f6d49cb4dae6819e5379898f2b8cce3c5f23994194c24b77d5da2e36f774"}, + {file = "pyflakes-3.1.0.tar.gz", hash = "sha256:a0aae034c444db0071aa077972ba4768d40c830d9539fd45bf4cd3f8f6992efc"}, +] + +[[package]] +name = "pytest" +version = "7.4.0" +description = "pytest: simple powerful testing with Python" +optional = false +python-versions = ">=3.7" +files = [ + {file = "pytest-7.4.0-py3-none-any.whl", hash = "sha256:78bf16451a2eb8c7a2ea98e32dc119fd2aa758f1d5d66dbf0a59d69a3969df32"}, + {file = "pytest-7.4.0.tar.gz", hash = "sha256:b4bf8c45bd59934ed84001ad51e11b4ee40d40a1229d2c79f9c592b0a3f6bd8a"}, +] + +[package.dependencies] +colorama = {version = "*", markers = "sys_platform == \"win32\""} +iniconfig = "*" +packaging = "*" +pluggy = ">=0.12,<2.0" + +[package.extras] +testing = ["argcomplete", "attrs (>=19.2.0)", "hypothesis (>=3.56)", "mock", "nose", "pygments (>=2.7.2)", "requests", "setuptools", "xmlschema"] + [[package]] name = "selectolax" version = "0.3.16" @@ -390,4 +507,4 @@ files = [ [metadata] lock-version = "2.0" python-versions = "^3.11" -content-hash = "06b33a781cca0f4e6524ce0740f007a81e3e992c6b2e6b66475f4a1907b0ac04" +content-hash = "ad04dae340bc1184b4114acb46e9c2455840140586b1c5ef96597c4b233da519" diff --git a/pyproject.toml b/pyproject.toml index e62c337..b36e2c5 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -13,6 +13,8 @@ pydantic = "^2.1.1" httpx = "^0.24.1" mypy = "^1.4.1" selectolax = "^0.3.16" +pytest = "^7.4.0" +flake8 = "^6.1.0" [tool.mypy] plugins = [ @@ -32,6 +34,9 @@ init_forbid_extra = true init_typed = true warn_required_dynamic_aliases = true +[tool.black] +line-length = 79 + [build-system] requires = ["poetry-core"] build-backend = "poetry.core.masonry.api" diff --git a/setup.cfg b/setup.cfg new file mode 100644 index 0000000..bfead2c --- /dev/null +++ b/setup.cfg @@ -0,0 +1,2 @@ +[flake8] +max-line-length = 79 -- cgit v1.2.3