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
276 changes: 276 additions & 0 deletions posthog/hogql_queries/insights/trends/test/test_trends_query_runner.py
Original file line number Diff line number Diff line change
Expand Up @@ -561,6 +561,282 @@ def _run_days_of_week_query(
)
return TrendsQueryRunner(team=self.team, query=query).calculate()

def _create_daily_pageviews(
self,
counts: dict[str, int],
*,
distinct_id: str = "daily-user",
browser: str = "Chrome",
) -> None:
self._create_events(
[
SeriesFixture(
distinct_id=distinct_id,
events=[
Series(
event="$pageview",
timestamps=[f"{day}T12:00:00Z" for day, count in counts.items() for _ in range(count)],
)
],
properties={"$browser": browser},
)
]
)

def _run_weekday_query(
self,
date_from: str,
date_to: str | None,
*,
compare: bool = False,
breakdown: BreakdownFilter | None = None,
) -> TrendsQueryResponse:
return TrendsQueryRunner(
team=self.team,
query=TrendsQuery(
series=[EventsNode(event="$pageview")],
dateRange=DateRange(date_from=date_from, date_to=date_to, daysOfWeek=[1, 2, 3, 4, 5]),
interval=IntervalType.DAY,
compareFilter=CompareFilter(compare=True) if compare else None,
breakdownFilter=breakdown,
),
).calculate()

def test_legacy_hide_weekends_is_ignored(self) -> None:
self._create_daily_pageviews(
{
"2024-06-07": 1,
"2024-06-08": 2,
"2024-06-09": 2,
"2024-06-10": 3,
"2024-06-11": 3,
"2024-06-12": 3,
"2024-06-13": 12,
}
)

responses = [
self._run_trends_query(
"2024-06-07",
"2024-06-13",
IntervalType.DAY,
[EventsNode(event="$pageview")],
trends_filters=TrendsFilter(hideWeekends=hide_weekends),
)
for hide_weekends in (False, True)
]

visible = responses[0].results[0]
legacy_hidden = responses[1].results[0]
assert legacy_hidden["days"] == visible["days"]
assert legacy_hidden["labels"] == visible["labels"]
assert legacy_hidden["data"] == visible["data"]
assert legacy_hidden["count"] == visible["count"]
assert legacy_hidden["days"] == [
"2024-06-07",
"2024-06-08",
"2024-06-09",
"2024-06-10",
"2024-06-11",
"2024-06-12",
"2024-06-13",
]
assert legacy_hidden["data"] == [1, 2, 2, 3, 3, 3, 12]

@parameterized.expand(
[
("monday", "2024-06-17", WeekStartDay.SUNDAY),
("tuesday", "2024-06-18", WeekStartDay.MONDAY),
("wednesday", "2024-06-19", WeekStartDay.SUNDAY),
("thursday", "2024-06-20", WeekStartDay.MONDAY),
("friday", "2024-06-21", WeekStartDay.SUNDAY),
("saturday", "2024-06-22", WeekStartDay.MONDAY),
("sunday", "2024-06-23", WeekStartDay.SUNDAY),
]
)
def test_weekday_filter_preserves_aligned_values_for_every_range_end(
self, _name: str, date_to: str, week_start_day: WeekStartDay
) -> None:
counts = {
day.strftime("%Y-%m-%d"): day.day % 5 + 1
for day in (datetime(2024, 6, 1) + timedelta(days=offset) for offset in range(23))
}
counts["2024-06-17"] = 3
counts["2024-06-18"] = 3
counts["2024-06-19"] = 3
counts["2024-06-20"] = 12
self._create_daily_pageviews(counts)
self.team.week_start_day = week_start_day
self.team.save(update_fields=["week_start_day"])

unfiltered = self._run_trends_query(
"2024-06-01", date_to, IntervalType.DAY, [EventsNode(event="$pageview")]
).results[0]
weekdays = self._run_weekday_query("2024-06-01", date_to).results[0]
unfiltered_by_day = dict(zip(unfiltered["days"], unfiltered["data"]))
expected_days = [day for day in unfiltered["days"] if datetime.fromisoformat(day).isoweekday() <= 5]

assert weekdays["days"] == expected_days
assert weekdays["data"] == [unfiltered_by_day[day] for day in expected_days]
assert len(weekdays["days"]) == len(weekdays["labels"]) == len(weekdays["data"])
assert all(datetime.fromisoformat(day).isoweekday() <= 5 for day in weekdays["days"])

@parameterized.expand(
[
(
"us_pacific_dst_start",
"US/Pacific",
"2024-03-08",
"2024-03-11",
[
"2024-03-09T07:30:00Z",
"2024-03-10T07:30:00Z",
"2024-03-11T06:30:00Z",
"2024-03-11T07:30:00Z",
],
["2024-03-08", "2024-03-11"],
[1, 1],
),
(
"india_half_hour_offset",
"Asia/Kolkata",
"2024-06-07",
"2024-06-10",
[
"2024-06-07T18:00:00Z",
"2024-06-07T18:31:00Z",
"2024-06-09T18:29:00Z",
"2024-06-09T18:31:00Z",
],
["2024-06-07", "2024-06-10"],
[1, 1],
),
]
)
def test_weekday_filter_uses_project_timezone_across_offsets_and_dst(
self,
_name: str,
timezone: str,
date_from: str,
date_to: str,
timestamps: list[str],
expected_days: list[str],
expected_data: list[int],
) -> None:
self.team.timezone = timezone
self.team.save(update_fields=["timezone"])
self._create_events(
[
SeriesFixture(
distinct_id="timezone-user",
events=[Series(event="$pageview", timestamps=timestamps)],
properties={},
)
]
)

result = self._run_weekday_query(date_from, date_to).results[0]

assert result["days"] == expected_days
assert result["data"] == expected_data

def test_weekday_filter_preserves_comparison_breakdown_alignment(self) -> None:
self._create_events(
[
SeriesFixture(
distinct_id="chrome-user",
events=[
Series(
event="$pageview",
timestamps=[
"2024-06-03T12:00:00Z",
"2024-06-04T12:00:00Z",
"2024-06-08T12:00:00Z",
"2024-06-10T12:00:00Z",
"2024-06-10T12:00:00Z",
"2024-06-10T12:00:00Z",
"2024-06-11T12:00:00Z",
"2024-06-11T12:00:00Z",
"2024-06-11T12:00:00Z",
"2024-06-13T12:00:00Z",
],
)
],
properties={"$browser": "Chrome"},
),
SeriesFixture(
distinct_id="firefox-user",
events=[
Series(
event="$pageview",
timestamps=[
"2024-06-05T12:00:00Z",
"2024-06-09T12:00:00Z",
"2024-06-10T12:00:00Z",
"2024-06-12T12:00:00Z",
"2024-06-12T12:00:00Z",
"2024-06-14T12:00:00Z",
],
)
],
properties={"$browser": "Firefox"},
),
]
)
breakdown = BreakdownFilter(breakdown_type=BreakdownType.EVENT, breakdown="$browser")
unfiltered = self._run_trends_query(
"2024-06-10",
"2024-06-16",
IntervalType.DAY,
[EventsNode(event="$pageview")],
breakdown=breakdown,
compare_filters=CompareFilter(compare=True),
)
weekdays = self._run_weekday_query("2024-06-10", "2024-06-16", compare=True, breakdown=breakdown)
unfiltered_by_series = {
(result["compare_label"], result["breakdown_value"]): result for result in unfiltered.results
}

assert len(weekdays.results) == 4
for result in weekdays.results:
source = unfiltered_by_series[(result["compare_label"], result["breakdown_value"])]
source_by_day = dict(zip(source["days"], source["data"]))
assert result["data"] == [source_by_day[day] for day in result["days"]]
assert len(result["days"]) == len(result["labels"]) == len(result["data"])
assert all(datetime.fromisoformat(day).isoweekday() <= 5 for day in result["days"])

current_chrome = next(
result
for result in weekdays.results
if result["compare_label"] == "current" and result["breakdown_value"] == "Chrome"
)
assert current_chrome["days"] == [
"2024-06-10",
"2024-06-11",
"2024-06-12",
"2024-06-13",
"2024-06-14",
]
assert current_chrome["data"] == [3, 3, 0, 1, 0]

@freeze_time("2024-06-20T15:00:00Z")
def test_weekday_filter_preserves_incomplete_current_period(self) -> None:
self._create_daily_pageviews(
{
"2024-06-15": 2,
"2024-06-16": 2,
"2024-06-17": 3,
"2024-06-18": 3,
"2024-06-19": 3,
"2024-06-20": 12,
}
)

result = self._run_weekday_query("2024-06-15", None).results[0]

assert result["days"] == ["2024-06-17", "2024-06-18", "2024-06-19", "2024-06-20"]
assert result["data"] == [3, 3, 3, 12]

def test_days_of_week_filters_events_and_day_buckets(self):
# 2020-01-06 is a Monday
self._create_events(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,81 @@ import { TrendsLineChart } from './TrendsLineChart'

type Story = StoryObj<{}>

const deterministicDays = [
'2024-06-07',
'2024-06-08',
'2024-06-09',
'2024-06-10',
'2024-06-11',
'2024-06-12',
'2024-06-13',
'2024-06-14',
'2024-06-15',
'2024-06-16',
'2024-06-17',
'2024-06-18',
'2024-06-19',
'2024-06-20',
]
const deterministicLabels = [
'7-Jun-2024',
'8-Jun-2024',
'9-Jun-2024',
'10-Jun-2024',
'11-Jun-2024',
'12-Jun-2024',
'13-Jun-2024',
'14-Jun-2024',
'15-Jun-2024',
'16-Jun-2024',
'17-Jun-2024',
'18-Jun-2024',
'19-Jun-2024',
'20-Jun-2024',
]
const deterministicData = [1, 5, 6, 3, 3, 0, 2, 1, 4, 4, 3, 3, 3, 12]

function makeDeterministicFixture(weekdaysOnly: boolean): Record<string, unknown> {
const keptIndices = deterministicDays.flatMap((day, index) => {
const isoDay = new Date(`${day}T00:00:00Z`).getUTCDay()
return !weekdaysOnly || (isoDay !== 0 && isoDay !== 6) ? [index] : []
})
const days = keptIndices.map((index) => deterministicDays[index])
const labels = keptIndices.map((index) => deterministicLabels[index])
const data = keptIndices.map((index) => deterministicData[index])

return {
...trendsLineFixture,
id: weekdaysOnly ? 61782 : 61781,
short_id: weekdaysOnly ? 'weekdays-only' : 'all-days',
derived_name: weekdaysOnly ? 'Weekdays only' : 'All days',
result: [
{
...trendsLineFixture.result[0],
count: data.reduce((sum, value) => sum + value, 0),
data,
labels,
days,
},
],
query: {
...trendsLineFixture.query,
source: {
...trendsLineFixture.query.source,
dateRange: {
date_from: '2024-06-07',
date_to: '2024-06-20',
daysOfWeek: weekdaysOnly ? [1, 2, 3, 4, 5] : undefined,
},
trendsFilter: {
...trendsLineFixture.query.source.trendsFilter,
showValuesOnSeries: true,
},
},
},
}
}

const meta: Meta = {
title: 'Insights/TrendsLineChart',
component: TrendsLineChart,
Expand Down Expand Up @@ -129,6 +204,16 @@ export const Breakdown: Story = {
render: () => renderTrendsLineChart(trendsLineBreakdownFixture),
}

export const AllDays: Story = {
name: 'All days',
render: () => renderTrendsLineChart(makeDeterministicFixture(false)),
}

export const WeekdaysOnly: Story = {
name: 'Weekdays only',
render: () => renderTrendsLineChart(makeDeterministicFixture(true)),
}

// The area display renders through the same line chart component with filled series
export const Area: Story = {
render: () => renderTrendsLineChart(trendsAreaFixture),
Expand Down
Loading
Loading