diff --git a/README.md b/README.md index 4cc7357..5fe56ec 100644 --- a/README.md +++ b/README.md @@ -78,7 +78,8 @@ assert MonthEvents('active', now.year, now.month).has_events_marked() == True How many users have been active this week?: ```python -print(len(WeekEvents('active', now.year, now.isocalendar()[1]))) +iso_year, iso_week, _ = now.isocalendar() +print(len(WeekEvents('active', iso_year, iso_week))) ``` Iterate over all users active this week: @@ -111,7 +112,8 @@ MonthEvents('active').from_date(now) == MonthEvents('active', now.year, now.mont Get the list of these users (user ids): ```python -print(list(WeekEvents('active', now.year, now.isocalendar()[1]))) +iso_year, iso_week, _ = now.isocalendar() +print(list(WeekEvents('active', iso_year, iso_week))) ``` There are special methods `prev` and `next` returning "sibling" events and diff --git a/bitmapist/__init__.py b/bitmapist/__init__.py index 37757f3..6bebafc 100644 --- a/bitmapist/__init__.py +++ b/bitmapist/__init__.py @@ -46,7 +46,8 @@ How many users have been active this week?:: - print len(WeekEvents('active', now.year, now.isocalendar()[1])) + iso_year, iso_week, _ = now.isocalendar() + print len(WeekEvents('active', iso_year, iso_week)) Perform bit operations. Which users that have been active last month are still active this month?:: diff --git a/bitmapist/cohort/__init__.py b/bitmapist/cohort/__init__.py index 91d4e29..73453d1 100644 --- a/bitmapist/cohort/__init__.py +++ b/bitmapist/cohort/__init__.py @@ -384,7 +384,8 @@ def _day_events_fn(key: str, date: date, system: str): def _weeks_events_fn(key: str, date: date, system: str): cls = WeekEvents - cls_args = (date.year, date.isocalendar()[1], system) + iso_year, iso_week, _ = date.isocalendar() + cls_args = (iso_year, iso_week, system) return _dispatch(key, cls, cls_args) diff --git a/test/test_bitmapist.py b/test/test_bitmapist.py index f255087..7d264cb 100644 --- a/test/test_bitmapist.py +++ b/test/test_bitmapist.py @@ -26,8 +26,9 @@ def test_mark_with_diff_days(): assert 124 not in MonthEvents("active", now.year, now.month) # Week - assert 123 in WeekEvents("active", now.year, now.isocalendar()[1]) - assert 124 not in WeekEvents("active", now.year, now.isocalendar()[1]) + iso_year, iso_week, _ = now.isocalendar() + assert 123 in WeekEvents("active", iso_year, iso_week) + assert 124 not in WeekEvents("active", iso_year, iso_week) # Day assert 123 in DayEvents("active", now.year, now.month, now.day) diff --git a/test/test_cohort.py b/test/test_cohort.py index 918ee23..9a034e5 100644 --- a/test/test_cohort.py +++ b/test/test_cohort.py @@ -1,9 +1,9 @@ -from datetime import datetime, timedelta, timezone +from datetime import date, datetime, timedelta, timezone import pytest from bitmapist import mark_event -from bitmapist.cohort import get_dates_data +from bitmapist.cohort import _weeks_events_fn, get_dates_data @pytest.fixture @@ -35,22 +35,16 @@ def events(): ("active", None, "active", None, [2, 100, 50]), ("active", None, "unknown", None, [2, "", ""]), ("unknown", None, "active", None, [0, "", ""]), - # Tests with select1b (AND conditions for select1) ("signup", "active", "active", None, [2, 100, 50]), - # Tests with both select1b and select2b ("task1", "task2", "task2", "task1", [2, 100, 100]), - # When select1 has no events but select1b has events, result should be 0 ("unknown", "active", "active", None, [0, "", ""]), - # When select1 has events but select2 AND select2b results in 0 ("active", None, "unknown", "active", [2, "", ""]), - # When select1 has events but select1b has no events (no overlap) ("active", "unknown", "active", None, [0, "", ""]), - # When select2 has events but select2b has no events (no overlap) ("active", None, "active", "unknown", [2, "", ""]), ], @@ -67,3 +61,30 @@ def test_cohort(select1, select1b, select2, select2b, expected, events): num_of_rows=1, ) assert r[0][1:] == expected + + +def test_weeks_events_fn_iso_year_boundary(): + """Test that _weeks_events_fn uses ISO year, not calendar year. + + At year boundaries, a date's calendar year can differ from its ISO year. + For example, Dec 30, 2024 is in ISO week 1 of 2025. + """ + # Dec 30, 2024 is a Monday in ISO week 1 of 2025 + dec_30_2024 = date(2024, 12, 30) + iso_year, iso_week, _ = dec_30_2024.isocalendar() + assert iso_year == 2025 # Verify our test date assumption + assert iso_week == 1 + + event = _weeks_events_fn("test_event", dec_30_2024, "default") + # Should use ISO year (2025), not calendar year (2024) + assert event.year == 2025 + assert event.week == 1 + + # Jan 1, 2025 is also in ISO week 1 of 2025 + jan_1_2025 = date(2025, 1, 1) + event2 = _weeks_events_fn("test_event", jan_1_2025, "default") + assert event2.year == 2025 + assert event2.week == 1 + + # Both dates should produce the same week event + assert event.redis_key == event2.redis_key