Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 5 additions & 5 deletions openlibrary/core/booknotes.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
from openlibrary.utils.dateutil import DATE_ONE_MONTH_AGO, DATE_ONE_WEEK_AGO
from openlibrary.utils.dateutil import date_one_month_ago, date_one_week_ago

from . import db

Expand All @@ -14,13 +14,13 @@ def summary(cls) -> dict:
return {
"total_notes_created": {
"total": cls.total_booknotes(),
"month": cls.total_booknotes(since=DATE_ONE_MONTH_AGO),
"week": cls.total_booknotes(since=DATE_ONE_WEEK_AGO),
"month": cls.total_booknotes(since=date_one_month_ago()),
"week": cls.total_booknotes(since=date_one_week_ago()),
},
"total_note_takers": {
"total": cls.total_unique_users(),
"month": cls.total_unique_users(since=DATE_ONE_MONTH_AGO),
"week": cls.total_unique_users(since=DATE_ONE_WEEK_AGO),
"month": cls.total_unique_users(since=date_one_month_ago()),
"week": cls.total_unique_users(since=date_one_week_ago()),
},
}

Expand Down
10 changes: 5 additions & 5 deletions openlibrary/core/bookshelves.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@
from openlibrary.plugins.worksearch.schemes.works import WorkSearchScheme
from openlibrary.plugins.worksearch.search import get_solr
from openlibrary.utils.async_utils import async_bridge
from openlibrary.utils.dateutil import DATE_ONE_MONTH_AGO, DATE_ONE_WEEK_AGO
from openlibrary.utils.dateutil import date_one_month_ago, date_one_week_ago
from openlibrary.utils.request_context import site

from . import db
Expand Down Expand Up @@ -40,13 +40,13 @@ def summary(cls):
return {
"total_books_logged": {
"total": Bookshelves.total_books_logged(),
"month": Bookshelves.total_books_logged(since=DATE_ONE_MONTH_AGO),
"week": Bookshelves.total_books_logged(since=DATE_ONE_WEEK_AGO),
"month": Bookshelves.total_books_logged(since=date_one_month_ago()),
"week": Bookshelves.total_books_logged(since=date_one_week_ago()),
},
"total_users_logged": {
"total": Bookshelves.total_unique_users(),
"month": Bookshelves.total_unique_users(since=DATE_ONE_MONTH_AGO),
"week": Bookshelves.total_unique_users(since=DATE_ONE_WEEK_AGO),
"month": Bookshelves.total_unique_users(since=date_one_month_ago()),
"week": Bookshelves.total_unique_users(since=date_one_week_ago()),
},
}

Expand Down
6 changes: 3 additions & 3 deletions openlibrary/core/follows.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
from typing import cast

from openlibrary.core.bookshelves import Bookshelves
from openlibrary.utils.dateutil import DATE_ONE_MONTH_AGO, DATE_ONE_WEEK_AGO
from openlibrary.utils.dateutil import date_one_month_ago, date_one_week_ago

from . import db

Expand Down Expand Up @@ -146,8 +146,8 @@ def summary(cls):
return {
"total_following_count": {
"total": cls.total_followers(),
"month": cls.total_followers(since=DATE_ONE_MONTH_AGO),
"week": cls.total_followers(since=DATE_ONE_WEEK_AGO),
"month": cls.total_followers(since=date_one_month_ago()),
"week": cls.total_followers(since=date_one_week_ago()),
}
}

Expand Down
14 changes: 7 additions & 7 deletions openlibrary/core/observations.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
from infogami import config
from infogami.utils.view import public
from openlibrary.utils import extract_numeric_id_from_olid
from openlibrary.utils.dateutil import DATE_ONE_MONTH_AGO, DATE_ONE_WEEK_AGO
from openlibrary.utils.dateutil import date_one_month_ago, date_one_week_ago

from . import cache, db

Expand Down Expand Up @@ -731,18 +731,18 @@ def summary(cls):
return {
"total_reviews": {
"total": Observations.total_reviews(),
"month": Observations.total_reviews(since=DATE_ONE_MONTH_AGO),
"week": Observations.total_reviews(since=DATE_ONE_WEEK_AGO),
"month": Observations.total_reviews(since=date_one_month_ago()),
"week": Observations.total_reviews(since=date_one_week_ago()),
},
"total_books_reviewed": {
"total": Observations.total_books_reviewed(),
"month": Observations.total_books_reviewed(since=DATE_ONE_MONTH_AGO),
"week": Observations.total_books_reviewed(since=DATE_ONE_WEEK_AGO),
"month": Observations.total_books_reviewed(since=date_one_month_ago()),
"week": Observations.total_books_reviewed(since=date_one_week_ago()),
},
"total_reviewers": {
"total": Observations.total_unique_respondents(),
"month": Observations.total_unique_respondents(since=DATE_ONE_MONTH_AGO),
"week": Observations.total_unique_respondents(since=DATE_ONE_WEEK_AGO),
"month": Observations.total_unique_respondents(since=date_one_month_ago()),
"week": Observations.total_unique_respondents(since=date_one_week_ago()),
},
}

Expand Down
10 changes: 5 additions & 5 deletions openlibrary/core/ratings.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
from math import sqrt
from typing import TypedDict

from openlibrary.utils.dateutil import DATE_ONE_MONTH_AGO, DATE_ONE_WEEK_AGO
from openlibrary.utils.dateutil import date_one_month_ago, date_one_week_ago

from . import db

Expand All @@ -28,13 +28,13 @@ def summary(cls) -> dict:
return {
"total_books_starred": {
"total": Ratings.total_num_books_rated(),
"month": Ratings.total_num_books_rated(since=DATE_ONE_MONTH_AGO),
"week": Ratings.total_num_books_rated(since=DATE_ONE_WEEK_AGO),
"month": Ratings.total_num_books_rated(since=date_one_month_ago()),
"week": Ratings.total_num_books_rated(since=date_one_week_ago()),
},
"total_star_raters": {
"total": Ratings.total_num_unique_raters(),
"month": Ratings.total_num_unique_raters(since=DATE_ONE_MONTH_AGO),
"week": Ratings.total_num_unique_raters(since=DATE_ONE_WEEK_AGO),
"month": Ratings.total_num_unique_raters(since=date_one_month_ago()),
"week": Ratings.total_num_unique_raters(since=date_one_week_ago()),
},
}

Expand Down
6 changes: 3 additions & 3 deletions openlibrary/core/yearly_reading_goals.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
from datetime import date, datetime
from typing import ClassVar

from openlibrary.utils.dateutil import DATE_ONE_MONTH_AGO, DATE_ONE_WEEK_AGO
from openlibrary.utils.dateutil import date_one_month_ago, date_one_week_ago

from . import db

Expand All @@ -14,8 +14,8 @@ def summary(cls) -> dict[str, dict[str, int]]:
return {
"total_yearly_reading_goals": {
"total": YearlyReadingGoals.total_yearly_reading_goals(),
"month": YearlyReadingGoals.total_yearly_reading_goals(since=DATE_ONE_MONTH_AGO),
"week": YearlyReadingGoals.total_yearly_reading_goals(since=DATE_ONE_WEEK_AGO),
"month": YearlyReadingGoals.total_yearly_reading_goals(since=date_one_month_ago()),
"week": YearlyReadingGoals.total_yearly_reading_goals(since=date_one_week_ago()),
},
}

Expand Down
20 changes: 16 additions & 4 deletions openlibrary/utils/dateutil.py
Original file line number Diff line number Diff line change
Expand Up @@ -37,10 +37,22 @@ def date_n_days_ago(n: int | None = None, start=None) -> datetime.date | None:
return (_start - datetime.timedelta(days=n)) if n else None


DATE_ONE_YEAR_AGO = date_n_days_ago(n=365)
DATE_ONE_MONTH_AGO = date_n_days_ago(n=days_in_current_month())
DATE_ONE_WEEK_AGO = date_n_days_ago(n=7)
DATE_ONE_DAY_AGO = date_n_days_ago(n=1)
# These are functions, not module-level constants: a constant would be evaluated
# once at import time and go stale in a long-running process.
def date_one_year_ago() -> datetime.date:
return todays_date_minus(days=365)


def date_one_month_ago() -> datetime.date:
return todays_date_minus(days=days_in_current_month())


def date_one_week_ago() -> datetime.date:
return todays_date_minus(days=7)


def date_one_day_ago() -> datetime.date:
return todays_date_minus(days=1)


def parse_date(datestr: str) -> datetime.date:
Expand Down
31 changes: 31 additions & 0 deletions openlibrary/utils/tests/test_dateutil.py
Original file line number Diff line number Diff line change
Expand Up @@ -44,3 +44,34 @@ def test_parse_daterange():
datetime.date(2010, 2, 3),
datetime.date(2010, 2, 4),
)


def test_date_cutoffs_are_computed_at_call_time(monkeypatch):
"""Regression test: the "one week/month ago" cutoffs must follow the current
date instead of being frozen when the module is imported."""

def set_today(today: datetime.date):
class FakeDate(datetime.date):
@classmethod
def today(cls):
return today

class FakeDatetime(datetime.datetime):
@classmethod
def now(cls, tz=None):
return datetime.datetime(today.year, today.month, today.day)

monkeypatch.setattr(dateutil.datetime, "date", FakeDate)
monkeypatch.setattr(dateutil.datetime, "datetime", FakeDatetime)

set_today(datetime.date(2026, 9, 1))
assert dateutil.date_one_day_ago() == datetime.date(2026, 8, 31)
assert dateutil.date_one_week_ago() == datetime.date(2026, 8, 25)
assert dateutil.date_one_month_ago() == datetime.date(2026, 8, 2)

# Same process, a month later: the cutoffs must move with the clock.
set_today(datetime.date(2026, 10, 2))
assert dateutil.date_one_day_ago() == datetime.date(2026, 10, 1)
assert dateutil.date_one_week_ago() == datetime.date(2026, 9, 25)
assert dateutil.date_one_month_ago() == datetime.date(2026, 9, 1)
assert dateutil.date_one_year_ago() == datetime.date(2025, 10, 2)
2 changes: 1 addition & 1 deletion openlibrary/views/loanstats.py
Original file line number Diff line number Diff line change
Expand Up @@ -110,7 +110,7 @@ def reading_log_leaderboard(limit: int):
most_wanted_month = Bookshelves.most_logged_books(
shelf_ids=[Bookshelves.PRESET_BOOKSHELVES["Want to Read"]],
limit=limit,
since=dateutil.DATE_ONE_MONTH_AGO,
since=dateutil.date_one_month_ago(),
)
return {
"leaderboard": {
Expand Down
Loading