Skip to content

Commit 03797f6

Browse files
committed
feat(config): add STATS_EVENTS_USE_NAIVE_UTC_DATETIME flag
Introduce STATS_EVENTS_USE_NAIVE_UTC_DATETIME (default: True) to strip tzinfo from event timestamps at build time. Set to False to opt-in to timezone-aware UTC datetimes.
1 parent cea19ce commit 03797f6

3 files changed

Lines changed: 56 additions & 5 deletions

File tree

invenio_stats/config.py

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -76,3 +76,11 @@
7676
7777
Default behaviour will register the templates as search templates.
7878
"""
79+
80+
STATS_EVENTS_USE_NAIVE_UTC_DATETIME = True
81+
"""Use naive UTC datetimes (without tzinfo) when building event timestamps.
82+
83+
When set to ``True`` (default), the ``tzinfo`` will be stripped from the UTC
84+
datetime used as the event timestamp (i.e. ``datetime.replace(tzinfo=None)``
85+
is applied). Set to ``False`` to opt-in to timezone-aware UTC datetimes.
86+
"""

invenio_stats/contrib/event_builders.py

Lines changed: 11 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -12,17 +12,25 @@
1212

1313
import datetime
1414

15-
from flask import request
15+
from flask import current_app, request
1616

1717
from ..utils import get_user
1818

1919

20+
def _build_timestamp():
21+
"""Build an event timestamp, stripping tzinfo if configured."""
22+
ts = datetime.datetime.now(datetime.timezone.utc)
23+
if current_app.config["STATS_EVENTS_USE_NAIVE_UTC_DATETIME"]:
24+
ts = ts.replace(tzinfo=None)
25+
return ts.isoformat()
26+
27+
2028
def file_download_event_builder(event, sender_app, obj=None, **kwargs):
2129
"""Build a file-download event."""
2230
event.update(
2331
{
2432
# When:
25-
"timestamp": datetime.datetime.now(datetime.timezone.utc).isoformat(),
33+
"timestamp": _build_timestamp(),
2634
# What:
2735
"bucket_id": str(obj.bucket_id),
2836
"file_id": str(obj.file_id),
@@ -53,7 +61,7 @@ def record_view_event_builder(event, sender_app, pid=None, record=None, **kwargs
5361
event.update(
5462
{
5563
# When:
56-
"timestamp": datetime.datetime.now(datetime.timezone.utc).isoformat(),
64+
"timestamp": _build_timestamp(),
5765
# What:
5866
"record_id": str(record.id),
5967
"pid_type": pid.pid_type,

tests/contrib/test_event_builders.py

Lines changed: 37 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -43,7 +43,7 @@ def test_file_download_event_builder(app, mock_user_ctx, sequential_ids, objects
4343
file_download_event_builder(event, app, file_obj)
4444
assert event == {
4545
# When:
46-
"timestamp": NewDate.now(tzinfo=timezone.utc).isoformat(),
46+
"timestamp": NewDate.now(tzinfo=timezone.utc).replace(tzinfo=None).isoformat(),
4747
# What:
4848
"bucket_id": str(file_obj.bucket_id),
4949
"file_id": str(file_obj.file_id),
@@ -63,7 +63,7 @@ def test_record_view_event_builder(app, mock_user_ctx, record, pid):
6363
record_view_event_builder(event, app, pid, record)
6464
assert event == {
6565
# When:
66-
"timestamp": NewDate.now(tzinfo=timezone.utc).isoformat(),
66+
"timestamp": NewDate.now(tzinfo=timezone.utc).replace(tzinfo=None).isoformat(),
6767
# What:
6868
"record_id": str(record.id),
6969
"pid_type": pid.pid_type,
@@ -72,3 +72,38 @@ def test_record_view_event_builder(app, mock_user_ctx, record, pid):
7272
# Who:
7373
**get_user(),
7474
}
75+
76+
77+
def test_file_download_event_builder_aware_datetime(
78+
app, mock_user_ctx, sequential_ids, objects
79+
):
80+
"""Test file-download event builder produces aware UTC datetime when opted in."""
81+
file_obj = objects[0]
82+
file_obj.bucket_id = sequential_ids[0]
83+
84+
app.config["STATS_EVENTS_USE_NAIVE_UTC_DATETIME"] = False
85+
try:
86+
with app.test_request_context(headers=headers):
87+
event = {}
88+
with patch("datetime.datetime", NewDate):
89+
file_download_event_builder(event, app, file_obj)
90+
assert event["timestamp"] == NewDate.now(tzinfo=timezone.utc).isoformat()
91+
# Aware ISO format must contain timezone offset
92+
assert "+00:00" in event["timestamp"]
93+
finally:
94+
app.config["STATS_EVENTS_USE_NAIVE_UTC_DATETIME"] = True
95+
96+
97+
def test_record_view_event_builder_aware_datetime(app, mock_user_ctx, record, pid):
98+
"""Test record-view event builder produces aware UTC datetime when opted in."""
99+
app.config["STATS_EVENTS_USE_NAIVE_UTC_DATETIME"] = False
100+
try:
101+
with app.test_request_context(headers=headers):
102+
event = {}
103+
with patch("datetime.datetime", NewDate):
104+
record_view_event_builder(event, app, pid, record)
105+
assert event["timestamp"] == NewDate.now(tzinfo=timezone.utc).isoformat()
106+
# Aware ISO format must contain timezone offset
107+
assert "+00:00" in event["timestamp"]
108+
finally:
109+
app.config["STATS_EVENTS_USE_NAIVE_UTC_DATETIME"] = True

0 commit comments

Comments
 (0)