From 97d4d1e13694b4f054cf7949cb1d4ee4614561ed Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=B6rn-Michael=20Miehe?= Date: Thu, 14 Sep 2023 23:36:20 +0000 Subject: [PATCH] helpers.EventDays class + basic testing --- api/.vscode/settings.json | 5 ++ api/advent22_api/core/__init__.py | 11 +++++ api/advent22_api/core/helpers.py | 51 ++++++++++++++++++++ api/poetry.lock | 59 ++++++++++++++++++++++- api/pyproject.toml | 1 + api/test/test_helpers.py | 80 +++++++++++++++++++++++++++++++ 6 files changed, 206 insertions(+), 1 deletion(-) create mode 100644 api/advent22_api/core/__init__.py create mode 100644 api/test/test_helpers.py diff --git a/api/.vscode/settings.json b/api/.vscode/settings.json index 9254a19..aa67d94 100644 --- a/api/.vscode/settings.json +++ b/api/.vscode/settings.json @@ -13,4 +13,9 @@ "git.closeDiffOnOperation": true, "python.analysis.typeCheckingMode": "basic", "python.analysis.diagnosticMode": "workspace", + "python.testing.pytestArgs": [ + "test" + ], + "python.testing.unittestEnabled": false, + "python.testing.pytestEnabled": true, } \ No newline at end of file diff --git a/api/advent22_api/core/__init__.py b/api/advent22_api/core/__init__.py new file mode 100644 index 0000000..882448c --- /dev/null +++ b/api/advent22_api/core/__init__.py @@ -0,0 +1,11 @@ +from . import advent_image, calendar_config, config, depends, helpers, settings, webdav + +__all__ = [ + "advent_image", + "calendar_config", + "config", + "depends", + "helpers", + "settings", + "webdav", +] diff --git a/api/advent22_api/core/helpers.py b/api/advent22_api/core/helpers.py index e92dec8..929373b 100644 --- a/api/advent22_api/core/helpers.py +++ b/api/advent22_api/core/helpers.py @@ -1,11 +1,13 @@ import itertools import random import re +from datetime import date, datetime, timedelta from io import BytesIO from typing import Any, Self, Sequence, TypeVar from fastapi.responses import StreamingResponse from PIL import Image +from pydantic import BaseModel from .config import get_config from .webdav import WebDAV @@ -68,3 +70,52 @@ async def api_return_image(img: Image.Image) -> StreamingResponse: media_type="image/jpeg", content=img_buffer, ) + + +class EventDays(BaseModel): + first: date + next: date | None + last: date + end: date + + @classmethod + def get( + cls, + *, + # current date + today: date, + # month/day when events begin + begin_month: int, + begin_day: int, + # events: e.g. a 2 means there is an event 2 days after begin + # -> assume sorted (asc) + events_after: list[int], + # countdown to closing begins after last event + closing_after: int, + ) -> Self: + """ + foo bar + """ + + # account for the last event, then add closing period + duration = timedelta(days=events_after[-1] + closing_after) + + # the events may begin last year, this year or next year + maybe_begin = ( + datetime(today.year + year_diff, begin_month, begin_day).date() + for year_diff in (-1, 0, +1) + ) + + # find the first begin where the end date is in the future + begin = next(begin for begin in maybe_begin if today <= (begin + duration)) + + # all event dates + events = [begin + timedelta(days=event_after) for event_after in events_after] + + # return relevant event dates + return cls( + first=events[0], + next=next((event for event in events if event > today), None), + last=events[-1], + end=begin + duration, + ) diff --git a/api/poetry.lock b/api/poetry.lock index 24dcc1e..8abb00f 100644 --- a/api/poetry.lock +++ b/api/poetry.lock @@ -266,6 +266,17 @@ 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 = "lxml" version = "4.9.3" @@ -418,6 +429,17 @@ files = [ {file = "numpy-1.25.2.tar.gz", hash = "sha256:fd608e19c8d7c55021dffd43bfe5492fab8cc105cc8986f813f8c3c048b38760"}, ] +[[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 = "pillow" version = "10.0.0" @@ -487,6 +509,21 @@ files = [ docs = ["furo", "olefile", "sphinx (>=2.4)", "sphinx-copybutton", "sphinx-inline-tabs", "sphinx-removed-in", "sphinxext-opengraph"] tests = ["check-manifest", "coverage", "defusedxml", "markdown2", "olefile", "packaging", "pyroma", "pytest", "pytest-cov", "pytest-timeout"] +[[package]] +name = "pluggy" +version = "1.3.0" +description = "plugin and hook calling mechanisms for python" +optional = false +python-versions = ">=3.8" +files = [ + {file = "pluggy-1.3.0-py3-none-any.whl", hash = "sha256:d89c696a773f8bd377d18e5ecda92b7a3793cbe66c87060a6fb58c7b6e1061f7"}, + {file = "pluggy-1.3.0.tar.gz", hash = "sha256:cf61ae8f126ac6f7c451172cf30e3e43d3ca77615509771b3a984a0730651e12"}, +] + +[package.extras] +dev = ["pre-commit", "tox"] +testing = ["pytest", "pytest-benchmark"] + [[package]] name = "pycodestyle" version = "2.11.0" @@ -661,6 +698,26 @@ files = [ {file = "pyflakes-3.1.0.tar.gz", hash = "sha256:a0aae034c444db0071aa077972ba4768d40c830d9539fd45bf4cd3f8f6992efc"}, ] +[[package]] +name = "pytest" +version = "7.4.2" +description = "pytest: simple powerful testing with Python" +optional = false +python-versions = ">=3.7" +files = [ + {file = "pytest-7.4.2-py3-none-any.whl", hash = "sha256:1d881c6124e08ff0a1bb75ba3ec0bfd8b5354a01c194ddd5a0a870a48d99b002"}, + {file = "pytest-7.4.2.tar.gz", hash = "sha256:a766259cfab564a2ad52cb1aae1b881a75c3eb7e34ca3779697c23ed47c47069"}, +] + +[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 = "python-dateutil" version = "2.8.2" @@ -1047,4 +1104,4 @@ files = [ [metadata] lock-version = "2.0" python-versions = "^3.11" -content-hash = "c2a27f4ecbc1c234d1368320db111d6c51cd4278986e64e80398cad384037a8c" +content-hash = "9039ceba03b4dfdaa6507b4b88d45125f7d33c05438133462d1cb338c5650eed" diff --git a/api/pyproject.toml b/api/pyproject.toml index c472e49..aa09955 100644 --- a/api/pyproject.toml +++ b/api/pyproject.toml @@ -21,6 +21,7 @@ webdavclient3 = "^3.14.6" [tool.poetry.group.dev.dependencies] flake8 = "^6.1.0" +pytest = "^7.4.2" [build-system] build-backend = "poetry.core.masonry.api" diff --git a/api/test/test_helpers.py b/api/test/test_helpers.py new file mode 100644 index 0000000..ea25dda --- /dev/null +++ b/api/test/test_helpers.py @@ -0,0 +1,80 @@ +from datetime import datetime + +from advent22_api.core.helpers import EventDays + + +class TestEventDays: + @staticmethod + def test_get_before(): + ed = EventDays.get( + today=datetime(2023, 11, 30).date(), + begin_month=12, + begin_day=1, + events_after=list(range(24)), + closing_after=5, + ) + + assert ed.first == datetime(2023, 12, 1).date() + assert ed.next == datetime(2023, 12, 1).date() + assert ed.last == datetime(2023, 12, 24).date() + assert ed.end == datetime(2023, 12, 29).date() + + @staticmethod + def test_get_after(): + ed = EventDays.get( + today=datetime(2023, 12, 30).date(), + begin_month=12, + begin_day=1, + events_after=list(range(24)), + closing_after=5, + ) + + assert ed.first == datetime(2024, 12, 1).date() + assert ed.next == datetime(2024, 12, 1).date() + assert ed.last == datetime(2024, 12, 24).date() + assert ed.end == datetime(2024, 12, 29).date() + + @staticmethod + def test_get_during_events(): + ed = EventDays.get( + today=datetime(2023, 12, 10).date(), + begin_month=12, + begin_day=1, + events_after=list(range(24)), + closing_after=5, + ) + + assert ed.first == datetime(2023, 12, 1).date() + assert ed.next == datetime(2023, 12, 11).date() + assert ed.last == datetime(2023, 12, 24).date() + assert ed.end == datetime(2023, 12, 29).date() + + @staticmethod + def test_get_during_closing(): + ed = EventDays.get( + today=datetime(2023, 12, 29).date(), + begin_month=12, + begin_day=1, + events_after=list(range(24)), + closing_after=5, + ) + + assert ed.first == datetime(2023, 12, 1).date() + assert ed.next is None + assert ed.last == datetime(2023, 12, 24).date() + assert ed.end == datetime(2023, 12, 29).date() + + @staticmethod + def test_get_during_wrap(): + ed = EventDays.get( + today=datetime(2024, 1, 1).date(), + begin_month=12, + begin_day=1, + events_after=list(range(24)), + closing_after=8, + ) + + assert ed.first == datetime(2023, 12, 1).date() + assert ed.next is None + assert ed.last == datetime(2023, 12, 24).date() + assert ed.end == datetime(2024, 1, 1).date()