Skip to content

Commit

Permalink
retention timezones
Browse files Browse the repository at this point in the history
  • Loading branch information
aspicer committed Aug 2, 2024
1 parent bdb25ac commit 428ecb1
Show file tree
Hide file tree
Showing 3 changed files with 107 additions and 11 deletions.
8 changes: 4 additions & 4 deletions frontend/src/scenes/retention/retentionTableLogic.ts
Original file line number Diff line number Diff line change
Expand Up @@ -81,19 +81,19 @@ export const retentionTableLogic = kea<retentionTableLogicType>([
} else {
switch (period) {
case 'Hour':
firstColumn = dayjs(currentResult.date).format('MMM D, h A')
firstColumn = dayjs.utc(currentResult.date).format('MMM D, h A')
break
case 'Month':
firstColumn = dayjs(currentResult.date).format('MMM YYYY')
firstColumn = dayjs.utc(currentResult.date).format('MMM YYYY')
break
case 'Week': {
const startDate = dayjs(currentResult.date)
const startDate = dayjs.utc(currentResult.date)
const endDate = startDate.add(6, 'day') // To show last day of the week we add 6 days, not 7
firstColumn = `${startDate.format('MMM D')} to ${endDate.format('MMM D')}`
break
}
default:
firstColumn = dayjs(currentResult.date).format('MMM D')
firstColumn = dayjs.utc(currentResult.date).format('MMM D')
}
}

Expand Down
16 changes: 9 additions & 7 deletions posthog/hogql_queries/insights/retention_query_runner.py
Original file line number Diff line number Diff line change
Expand Up @@ -368,6 +368,14 @@ def _refresh_frequency(self):

return refresh_frequency

def get_date(self, first_interval):
date = self.query_date_range.date_from() + self.query_date_range.determine_time_delta(
first_interval, self.query_date_range.interval_name.title()
)
if self.query_date_range.interval_type == IntervalType.HOUR:
date = date + self.team.timezone_info.utcoffset(date)

Check failure on line 376 in posthog/hogql_queries/insights/retention_query_runner.py

View workflow job for this annotation

GitHub Actions / Python code quality checks

Unsupported operand types for + ("datetime" and "None")

Check failure on line 376 in posthog/hogql_queries/insights/retention_query_runner.py

View workflow job for this annotation

GitHub Actions / Python code quality checks

Right operand is of type "timedelta | None"
return date

def calculate(self) -> RetentionQueryResponse:
query = self.to_query()
hogql = to_printed_hogql(query, self.team)
Expand All @@ -388,20 +396,14 @@ def calculate(self) -> RetentionQueryResponse:
}
for (breakdown_values, intervals_from_base, count) in response.results
}

results = [
{
"values": [
result_dict.get(((first_interval,), return_interval), {"count": 0})
for return_interval in range(self.query_date_range.total_intervals - first_interval)
],
"label": f"{self.query_date_range.interval_name.title()} {first_interval}",
"date": (
self.query_date_range.date_from()
+ self.query_date_range.determine_time_delta(
first_interval, self.query_date_range.interval_name.title()
)
),
"date": self.get_date(first_interval),
}
for first_interval in range(self.query_date_range.total_intervals)
]
Expand Down
94 changes: 94 additions & 0 deletions posthog/hogql_queries/insights/test/test_retention_query_runner.py
Original file line number Diff line number Diff line change
Expand Up @@ -660,6 +660,100 @@ def test_hour_interval(self):
],
)

def test_hour_interval_team_timezone(self):
self.team.timezone = "US/Pacific"
self.team.save()

_create_person(
team=self.team,
distinct_ids=["person1", "alias1"],
properties={"email": "[email protected]"},
)
_create_person(
team=self.team,
distinct_ids=["person2"],
properties={"email": "[email protected]"},
)

_create_events(
self.team,
[
("person1", _date(day=0, hour=6)),
("person2", _date(day=0, hour=6)),
("person1", _date(day=0, hour=7)),
("person2", _date(day=0, hour=7)),
("person1", _date(day=0, hour=8)),
("person2", _date(day=0, hour=8)),
("person1", _date(day=0, hour=10)),
("person1", _date(day=0, hour=11)),
("person2", _date(day=0, hour=11)),
("person2", _date(day=0, hour=12)),
("person1", _date(day=0, hour=14)),
("person2", _date(day=0, hour=16)),
],
)

result = self.run_query(
query={
"dateRange": {"date_to": _date(0, hour=16, minute=13)},
"retentionFilter": {
"period": "Hour",
"totalIntervals": 11,
},
}
)

self.assertEqual(
pluck(result, "label"),
[
"Hour 0",
"Hour 1",
"Hour 2",
"Hour 3",
"Hour 4",
"Hour 5",
"Hour 6",
"Hour 7",
"Hour 8",
"Hour 9",
"Hour 10",
],
)

self.assertEqual(
pluck(result, "values", "count"),
[
[2, 2, 2, 0, 1, 2, 1, 0, 1, 0, 1],
[2, 2, 0, 1, 2, 1, 0, 1, 0, 1],
[2, 0, 1, 2, 1, 0, 1, 0, 1],
[0, 0, 0, 0, 0, 0, 0, 0],
[1, 1, 0, 0, 1, 0, 0],
[2, 1, 0, 1, 0, 1],
[1, 0, 0, 0, 1],
[0, 0, 0, 0],
[1, 0, 0],
[0, 0],
[1],
],
)

self.assertEqual(
pluck(result, "date"),
[
datetime(2020, 6, 10, 6, tzinfo=ZoneInfo("UTC")),
datetime(2020, 6, 10, 7, tzinfo=ZoneInfo("UTC")),
datetime(2020, 6, 10, 8, tzinfo=ZoneInfo("UTC")),
datetime(2020, 6, 10, 9, tzinfo=ZoneInfo("UTC")),
datetime(2020, 6, 10, 10, tzinfo=ZoneInfo("UTC")),
datetime(2020, 6, 10, 11, tzinfo=ZoneInfo("UTC")),
datetime(2020, 6, 10, 12, tzinfo=ZoneInfo("UTC")),
datetime(2020, 6, 10, 13, tzinfo=ZoneInfo("UTC")),
datetime(2020, 6, 10, 14, tzinfo=ZoneInfo("UTC")),
datetime(2020, 6, 10, 15, tzinfo=ZoneInfo("UTC")),
datetime(2020, 6, 10, 16, tzinfo=ZoneInfo("UTC")),
],
)

# ensure that the first interval is properly rounded according to the specified period
def test_interval_rounding(self):
_create_person(
Expand Down

0 comments on commit 428ecb1

Please sign in to comment.