diff --git a/posthog/heatmaps/heatmaps_api.py b/posthog/heatmaps/heatmaps_api.py index f06899e3c4178..e3fa68b5b4db3 100644 --- a/posthog/heatmaps/heatmaps_api.py +++ b/posthog/heatmaps/heatmaps_api.py @@ -17,17 +17,17 @@ from posthog.utils import relative_date_parse_with_delta_mapping DEFAULT_QUERY = """ - select pointer_target_fixed, relative_client_x, client_y, {aggregation_count} + select pointer_target_fixed, pointer_relative_x, client_y, {aggregation_count} from ( select distinct_id, pointer_target_fixed, - round((x / viewport_width), 2) as relative_client_x, + round((x / viewport_width), 2) as pointer_relative_x, y * scale_factor as client_y from heatmaps where {predicates} ) - group by `pointer_target_fixed`, relative_client_x, client_y + group by `pointer_target_fixed`, pointer_relative_x, client_y """ SCROLL_DEPTH_QUERY = """ @@ -106,7 +106,7 @@ class HeatmapsResponseSerializer(serializers.Serializer): class HeatmapScrollDepthResponseItemSerializer(serializers.Serializer): cumulative_count = serializers.IntegerField(required=True) bucket_count = serializers.IntegerField(required=True) - scroll_depth_bucket = serializers.FloatField(required=True) + scroll_depth_bucket = serializers.IntegerField(required=True) class HeatmapsScrollDepthResponseSerializer(serializers.Serializer): @@ -163,8 +163,8 @@ def _predicate_expressions(placeholders: dict[str, Expr]) -> List[ast.Expr]: # "type": "`type` = {type}", # optional "date_to": "timestamp <= {date_to} + interval 1 day", - "viewport_width_min": "viewport_width >= ceil({viewport_width_min} / 16)", - "viewport_width_max": "viewport_width <= ceil({viewport_width_max} / 16)", + "viewport_width_min": "viewport_width >= round({viewport_width_min} / 16)", + "viewport_width_max": "viewport_width <= round({viewport_width_max} / 16)", "url_exact": "current_url = {url_exact}", "url_pattern": "match(current_url, {url_pattern})", } diff --git a/posthog/heatmaps/test/__snapshots__/test_heatmaps_api.ambr b/posthog/heatmaps/test/__snapshots__/test_heatmaps_api.ambr index 1f5fe0431add1..faa7d77c2c79a 100644 --- a/posthog/heatmaps/test/__snapshots__/test_heatmaps_api.ambr +++ b/posthog/heatmaps/test/__snapshots__/test_heatmaps_api.ambr @@ -3,18 +3,18 @@ ''' /* user_id:0 request:_snapshot_ */ SELECT pointer_target_fixed AS pointer_target_fixed, - relative_client_x AS relative_client_x, + pointer_relative_x AS pointer_relative_x, client_y AS client_y, count(*) AS cnt FROM (SELECT heatmaps.distinct_id AS distinct_id, heatmaps.pointer_target_fixed AS pointer_target_fixed, - round(divide(heatmaps.x, heatmaps.viewport_width), 2) AS relative_client_x, + round(divide(heatmaps.x, heatmaps.viewport_width), 2) AS pointer_relative_x, multiply(heatmaps.y, heatmaps.scale_factor) AS client_y FROM heatmaps WHERE and(equals(heatmaps.team_id, 2), equals(heatmaps.type, 'rageclick'), greaterOrEquals(toTimeZone(heatmaps.timestamp, 'UTC'), toDate('2023-03-08')), equals(heatmaps.current_url, 'http://example.com'))) GROUP BY pointer_target_fixed, - relative_client_x, + pointer_relative_x, client_y LIMIT 1000000 SETTINGS readonly=2, max_execution_time=60, @@ -25,18 +25,18 @@ ''' /* user_id:0 request:_snapshot_ */ SELECT pointer_target_fixed AS pointer_target_fixed, - relative_client_x AS relative_client_x, + pointer_relative_x AS pointer_relative_x, client_y AS client_y, count(*) AS cnt FROM (SELECT heatmaps.distinct_id AS distinct_id, heatmaps.pointer_target_fixed AS pointer_target_fixed, - round(divide(heatmaps.x, heatmaps.viewport_width), 2) AS relative_client_x, + round(divide(heatmaps.x, heatmaps.viewport_width), 2) AS pointer_relative_x, multiply(heatmaps.y, heatmaps.scale_factor) AS client_y FROM heatmaps WHERE and(equals(heatmaps.team_id, 2), equals(heatmaps.type, 'rageclick'), greaterOrEquals(toTimeZone(heatmaps.timestamp, 'UTC'), toDate('2023-03-08')), equals(heatmaps.current_url, 'http://example.com/about'))) GROUP BY pointer_target_fixed, - relative_client_x, + pointer_relative_x, client_y LIMIT 1000000 SETTINGS readonly=2, max_execution_time=60, @@ -47,282 +47,282 @@ ''' /* user_id:0 request:_snapshot_ */ SELECT pointer_target_fixed AS pointer_target_fixed, - relative_client_x AS relative_client_x, + pointer_relative_x AS pointer_relative_x, client_y AS client_y, count(*) AS cnt FROM (SELECT heatmaps.distinct_id AS distinct_id, heatmaps.pointer_target_fixed AS pointer_target_fixed, - round(divide(heatmaps.x, heatmaps.viewport_width), 2) AS relative_client_x, + round(divide(heatmaps.x, heatmaps.viewport_width), 2) AS pointer_relative_x, multiply(heatmaps.y, heatmaps.scale_factor) AS client_y FROM heatmaps WHERE and(equals(heatmaps.team_id, 2), equals(heatmaps.type, 'rageclick'), greaterOrEquals(toTimeZone(heatmaps.timestamp, 'UTC'), toDate('2023-03-08')), match(heatmaps.current_url, 'http://example.com*'))) GROUP BY pointer_target_fixed, - relative_client_x, + pointer_relative_x, client_y LIMIT 1000000 SETTINGS readonly=2, max_execution_time=60, allow_experimental_object_type=1 ''' # --- -# name: TestSessionRecordings.test_can_get_all_data_response +# name: TestSessionRecordings.test_can_filter_by_viewport_0_min_150 ''' /* user_id:0 request:_snapshot_ */ SELECT pointer_target_fixed AS pointer_target_fixed, - relative_client_x AS relative_client_x, + pointer_relative_x AS pointer_relative_x, client_y AS client_y, count(*) AS cnt FROM (SELECT heatmaps.distinct_id AS distinct_id, heatmaps.pointer_target_fixed AS pointer_target_fixed, - round(divide(heatmaps.x, heatmaps.viewport_width), 2) AS relative_client_x, + round(divide(heatmaps.x, heatmaps.viewport_width), 2) AS pointer_relative_x, multiply(heatmaps.y, heatmaps.scale_factor) AS client_y FROM heatmaps - WHERE and(equals(heatmaps.team_id, 2), equals(heatmaps.type, 'click'), greaterOrEquals(toTimeZone(heatmaps.timestamp, 'UTC'), toDate('2023-03-08')))) + WHERE and(equals(heatmaps.team_id, 2), ifNull(greaterOrEquals(heatmaps.viewport_width, round(divide(150, 16))), 0), equals(heatmaps.type, 'click'), greaterOrEquals(toTimeZone(heatmaps.timestamp, 'UTC'), toDate('2023-03-08')))) GROUP BY pointer_target_fixed, - relative_client_x, + pointer_relative_x, client_y LIMIT 1000000 SETTINGS readonly=2, max_execution_time=60, allow_experimental_object_type=1 ''' # --- -# name: TestSessionRecordings.test_can_get_count_by_aggregation +# name: TestSessionRecordings.test_can_filter_by_viewport_1_min_161 ''' /* user_id:0 request:_snapshot_ */ SELECT pointer_target_fixed AS pointer_target_fixed, - relative_client_x AS relative_client_x, + pointer_relative_x AS pointer_relative_x, client_y AS client_y, count(*) AS cnt FROM (SELECT heatmaps.distinct_id AS distinct_id, heatmaps.pointer_target_fixed AS pointer_target_fixed, - round(divide(heatmaps.x, heatmaps.viewport_width), 2) AS relative_client_x, + round(divide(heatmaps.x, heatmaps.viewport_width), 2) AS pointer_relative_x, multiply(heatmaps.y, heatmaps.scale_factor) AS client_y FROM heatmaps - WHERE and(equals(heatmaps.team_id, 2), equals(heatmaps.type, 'click'), greaterOrEquals(toTimeZone(heatmaps.timestamp, 'UTC'), toDate('2023-03-08')))) + WHERE and(equals(heatmaps.team_id, 2), ifNull(greaterOrEquals(heatmaps.viewport_width, round(divide(161, 16))), 0), equals(heatmaps.type, 'click'), greaterOrEquals(toTimeZone(heatmaps.timestamp, 'UTC'), toDate('2023-03-08')))) GROUP BY pointer_target_fixed, - relative_client_x, + pointer_relative_x, client_y LIMIT 1000000 SETTINGS readonly=2, max_execution_time=60, allow_experimental_object_type=1 ''' # --- -# name: TestSessionRecordings.test_can_get_count_by_aggregation.1 +# name: TestSessionRecordings.test_can_filter_by_viewport_2_min_177 ''' /* user_id:0 request:_snapshot_ */ SELECT pointer_target_fixed AS pointer_target_fixed, - relative_client_x AS relative_client_x, + pointer_relative_x AS pointer_relative_x, client_y AS client_y, - count(DISTINCT distinct_id) AS cnt + count(*) AS cnt FROM (SELECT heatmaps.distinct_id AS distinct_id, heatmaps.pointer_target_fixed AS pointer_target_fixed, - round(divide(heatmaps.x, heatmaps.viewport_width), 2) AS relative_client_x, + round(divide(heatmaps.x, heatmaps.viewport_width), 2) AS pointer_relative_x, multiply(heatmaps.y, heatmaps.scale_factor) AS client_y FROM heatmaps - WHERE and(equals(heatmaps.team_id, 2), equals(heatmaps.type, 'click'), greaterOrEquals(toTimeZone(heatmaps.timestamp, 'UTC'), toDate('2023-03-08')))) + WHERE and(equals(heatmaps.team_id, 2), ifNull(greaterOrEquals(heatmaps.viewport_width, round(divide(177, 16))), 0), equals(heatmaps.type, 'click'), greaterOrEquals(toTimeZone(heatmaps.timestamp, 'UTC'), toDate('2023-03-08')))) GROUP BY pointer_target_fixed, - relative_client_x, + pointer_relative_x, client_y LIMIT 1000000 SETTINGS readonly=2, max_execution_time=60, allow_experimental_object_type=1 ''' # --- -# name: TestSessionRecordings.test_can_get_empty_response +# name: TestSessionRecordings.test_can_filter_by_viewport_3_min_201 ''' /* user_id:0 request:_snapshot_ */ SELECT pointer_target_fixed AS pointer_target_fixed, - relative_client_x AS relative_client_x, + pointer_relative_x AS pointer_relative_x, client_y AS client_y, count(*) AS cnt FROM (SELECT heatmaps.distinct_id AS distinct_id, heatmaps.pointer_target_fixed AS pointer_target_fixed, - round(divide(heatmaps.x, heatmaps.viewport_width), 2) AS relative_client_x, + round(divide(heatmaps.x, heatmaps.viewport_width), 2) AS pointer_relative_x, multiply(heatmaps.y, heatmaps.scale_factor) AS client_y FROM heatmaps - WHERE and(equals(heatmaps.team_id, 2), equals(heatmaps.type, 'click'), greaterOrEquals(toTimeZone(heatmaps.timestamp, 'UTC'), toDate('2024-05-03')))) + WHERE and(equals(heatmaps.team_id, 2), ifNull(greaterOrEquals(heatmaps.viewport_width, round(divide(201, 16))), 0), equals(heatmaps.type, 'click'), greaterOrEquals(toTimeZone(heatmaps.timestamp, 'UTC'), toDate('2023-03-08')))) GROUP BY pointer_target_fixed, - relative_client_x, + pointer_relative_x, client_y LIMIT 1000000 SETTINGS readonly=2, max_execution_time=60, allow_experimental_object_type=1 ''' # --- -# name: TestSessionRecordings.test_can_get_filter_by_click +# name: TestSessionRecordings.test_can_filter_by_viewport_4_min_161_and_max_192 ''' /* user_id:0 request:_snapshot_ */ SELECT pointer_target_fixed AS pointer_target_fixed, - relative_client_x AS relative_client_x, + pointer_relative_x AS pointer_relative_x, client_y AS client_y, count(*) AS cnt FROM (SELECT heatmaps.distinct_id AS distinct_id, heatmaps.pointer_target_fixed AS pointer_target_fixed, - round(divide(heatmaps.x, heatmaps.viewport_width), 2) AS relative_client_x, + round(divide(heatmaps.x, heatmaps.viewport_width), 2) AS pointer_relative_x, multiply(heatmaps.y, heatmaps.scale_factor) AS client_y FROM heatmaps - WHERE and(equals(heatmaps.team_id, 2), equals(heatmaps.type, 'click'), greaterOrEquals(toTimeZone(heatmaps.timestamp, 'UTC'), toDate('2023-03-08')))) + WHERE and(equals(heatmaps.team_id, 2), ifNull(greaterOrEquals(heatmaps.viewport_width, round(divide(161, 16))), 0), ifNull(lessOrEquals(heatmaps.viewport_width, round(divide(192, 16))), 0), equals(heatmaps.type, 'click'), greaterOrEquals(toTimeZone(heatmaps.timestamp, 'UTC'), toDate('2023-03-08')))) GROUP BY pointer_target_fixed, - relative_client_x, + pointer_relative_x, client_y LIMIT 1000000 SETTINGS readonly=2, max_execution_time=60, allow_experimental_object_type=1 ''' # --- -# name: TestSessionRecordings.test_can_get_filter_by_click.1 +# name: TestSessionRecordings.test_can_get_all_data_response ''' /* user_id:0 request:_snapshot_ */ SELECT pointer_target_fixed AS pointer_target_fixed, - relative_client_x AS relative_client_x, + pointer_relative_x AS pointer_relative_x, client_y AS client_y, count(*) AS cnt FROM (SELECT heatmaps.distinct_id AS distinct_id, heatmaps.pointer_target_fixed AS pointer_target_fixed, - round(divide(heatmaps.x, heatmaps.viewport_width), 2) AS relative_client_x, + round(divide(heatmaps.x, heatmaps.viewport_width), 2) AS pointer_relative_x, multiply(heatmaps.y, heatmaps.scale_factor) AS client_y FROM heatmaps - WHERE and(equals(heatmaps.team_id, 2), equals(heatmaps.type, 'rageclick'), greaterOrEquals(toTimeZone(heatmaps.timestamp, 'UTC'), toDate('2023-03-08')))) + WHERE and(equals(heatmaps.team_id, 2), equals(heatmaps.type, 'click'), greaterOrEquals(toTimeZone(heatmaps.timestamp, 'UTC'), toDate('2023-03-08')))) GROUP BY pointer_target_fixed, - relative_client_x, + pointer_relative_x, client_y LIMIT 1000000 SETTINGS readonly=2, max_execution_time=60, allow_experimental_object_type=1 ''' # --- -# name: TestSessionRecordings.test_can_get_filter_by_date_from +# name: TestSessionRecordings.test_can_get_count_by_aggregation ''' /* user_id:0 request:_snapshot_ */ SELECT pointer_target_fixed AS pointer_target_fixed, - relative_client_x AS relative_client_x, + pointer_relative_x AS pointer_relative_x, client_y AS client_y, count(*) AS cnt FROM (SELECT heatmaps.distinct_id AS distinct_id, heatmaps.pointer_target_fixed AS pointer_target_fixed, - round(divide(heatmaps.x, heatmaps.viewport_width), 2) AS relative_client_x, + round(divide(heatmaps.x, heatmaps.viewport_width), 2) AS pointer_relative_x, multiply(heatmaps.y, heatmaps.scale_factor) AS client_y FROM heatmaps WHERE and(equals(heatmaps.team_id, 2), equals(heatmaps.type, 'click'), greaterOrEquals(toTimeZone(heatmaps.timestamp, 'UTC'), toDate('2023-03-08')))) GROUP BY pointer_target_fixed, - relative_client_x, + pointer_relative_x, client_y LIMIT 1000000 SETTINGS readonly=2, max_execution_time=60, allow_experimental_object_type=1 ''' # --- -# name: TestSessionRecordings.test_can_get_filter_by_min_and_max_viewport +# name: TestSessionRecordings.test_can_get_count_by_aggregation.1 ''' /* user_id:0 request:_snapshot_ */ SELECT pointer_target_fixed AS pointer_target_fixed, - relative_client_x AS relative_client_x, + pointer_relative_x AS pointer_relative_x, client_y AS client_y, - count(*) AS cnt + count(DISTINCT distinct_id) AS cnt FROM (SELECT heatmaps.distinct_id AS distinct_id, heatmaps.pointer_target_fixed AS pointer_target_fixed, - round(divide(heatmaps.x, heatmaps.viewport_width), 2) AS relative_client_x, + round(divide(heatmaps.x, heatmaps.viewport_width), 2) AS pointer_relative_x, multiply(heatmaps.y, heatmaps.scale_factor) AS client_y FROM heatmaps - WHERE and(equals(heatmaps.team_id, 2), ifNull(greaterOrEquals(heatmaps.viewport_width, ceil(divide(161, 16))), 0), ifNull(lessOrEquals(heatmaps.viewport_width, ceil(divide(192, 16))), 0), equals(heatmaps.type, 'click'), greaterOrEquals(toTimeZone(heatmaps.timestamp, 'UTC'), toDate('2023-03-08')))) + WHERE and(equals(heatmaps.team_id, 2), equals(heatmaps.type, 'click'), greaterOrEquals(toTimeZone(heatmaps.timestamp, 'UTC'), toDate('2023-03-08')))) GROUP BY pointer_target_fixed, - relative_client_x, + pointer_relative_x, client_y LIMIT 1000000 SETTINGS readonly=2, max_execution_time=60, allow_experimental_object_type=1 ''' # --- -# name: TestSessionRecordings.test_can_get_filter_by_min_viewport +# name: TestSessionRecordings.test_can_get_empty_response ''' /* user_id:0 request:_snapshot_ */ SELECT pointer_target_fixed AS pointer_target_fixed, - relative_client_x AS relative_client_x, + pointer_relative_x AS pointer_relative_x, client_y AS client_y, count(*) AS cnt FROM (SELECT heatmaps.distinct_id AS distinct_id, heatmaps.pointer_target_fixed AS pointer_target_fixed, - round(divide(heatmaps.x, heatmaps.viewport_width), 2) AS relative_client_x, + round(divide(heatmaps.x, heatmaps.viewport_width), 2) AS pointer_relative_x, multiply(heatmaps.y, heatmaps.scale_factor) AS client_y FROM heatmaps - WHERE and(equals(heatmaps.team_id, 2), ifNull(greaterOrEquals(heatmaps.viewport_width, ceil(divide(150, 16))), 0), equals(heatmaps.type, 'click'), greaterOrEquals(toTimeZone(heatmaps.timestamp, 'UTC'), toDate('2023-03-08')))) + WHERE and(equals(heatmaps.team_id, 2), equals(heatmaps.type, 'click'), greaterOrEquals(toTimeZone(heatmaps.timestamp, 'UTC'), toDate('2024-05-03')))) GROUP BY pointer_target_fixed, - relative_client_x, + pointer_relative_x, client_y LIMIT 1000000 SETTINGS readonly=2, max_execution_time=60, allow_experimental_object_type=1 ''' # --- -# name: TestSessionRecordings.test_can_get_filter_by_min_viewport.1 +# name: TestSessionRecordings.test_can_get_filter_by_click ''' /* user_id:0 request:_snapshot_ */ SELECT pointer_target_fixed AS pointer_target_fixed, - relative_client_x AS relative_client_x, + pointer_relative_x AS pointer_relative_x, client_y AS client_y, count(*) AS cnt FROM (SELECT heatmaps.distinct_id AS distinct_id, heatmaps.pointer_target_fixed AS pointer_target_fixed, - round(divide(heatmaps.x, heatmaps.viewport_width), 2) AS relative_client_x, + round(divide(heatmaps.x, heatmaps.viewport_width), 2) AS pointer_relative_x, multiply(heatmaps.y, heatmaps.scale_factor) AS client_y FROM heatmaps - WHERE and(equals(heatmaps.team_id, 2), ifNull(greaterOrEquals(heatmaps.viewport_width, ceil(divide(161, 16))), 0), equals(heatmaps.type, 'click'), greaterOrEquals(toTimeZone(heatmaps.timestamp, 'UTC'), toDate('2023-03-08')))) + WHERE and(equals(heatmaps.team_id, 2), equals(heatmaps.type, 'click'), greaterOrEquals(toTimeZone(heatmaps.timestamp, 'UTC'), toDate('2023-03-08')))) GROUP BY pointer_target_fixed, - relative_client_x, + pointer_relative_x, client_y LIMIT 1000000 SETTINGS readonly=2, max_execution_time=60, allow_experimental_object_type=1 ''' # --- -# name: TestSessionRecordings.test_can_get_filter_by_min_viewport.2 +# name: TestSessionRecordings.test_can_get_filter_by_click.1 ''' /* user_id:0 request:_snapshot_ */ SELECT pointer_target_fixed AS pointer_target_fixed, - relative_client_x AS relative_client_x, + pointer_relative_x AS pointer_relative_x, client_y AS client_y, count(*) AS cnt FROM (SELECT heatmaps.distinct_id AS distinct_id, heatmaps.pointer_target_fixed AS pointer_target_fixed, - round(divide(heatmaps.x, heatmaps.viewport_width), 2) AS relative_client_x, + round(divide(heatmaps.x, heatmaps.viewport_width), 2) AS pointer_relative_x, multiply(heatmaps.y, heatmaps.scale_factor) AS client_y FROM heatmaps - WHERE and(equals(heatmaps.team_id, 2), ifNull(greaterOrEquals(heatmaps.viewport_width, ceil(divide(177, 16))), 0), equals(heatmaps.type, 'click'), greaterOrEquals(toTimeZone(heatmaps.timestamp, 'UTC'), toDate('2023-03-08')))) + WHERE and(equals(heatmaps.team_id, 2), equals(heatmaps.type, 'rageclick'), greaterOrEquals(toTimeZone(heatmaps.timestamp, 'UTC'), toDate('2023-03-08')))) GROUP BY pointer_target_fixed, - relative_client_x, + pointer_relative_x, client_y LIMIT 1000000 SETTINGS readonly=2, max_execution_time=60, allow_experimental_object_type=1 ''' # --- -# name: TestSessionRecordings.test_can_get_filter_by_min_viewport.3 +# name: TestSessionRecordings.test_can_get_filter_by_date_from ''' /* user_id:0 request:_snapshot_ */ SELECT pointer_target_fixed AS pointer_target_fixed, - relative_client_x AS relative_client_x, + pointer_relative_x AS pointer_relative_x, client_y AS client_y, count(*) AS cnt FROM (SELECT heatmaps.distinct_id AS distinct_id, heatmaps.pointer_target_fixed AS pointer_target_fixed, - round(divide(heatmaps.x, heatmaps.viewport_width), 2) AS relative_client_x, + round(divide(heatmaps.x, heatmaps.viewport_width), 2) AS pointer_relative_x, multiply(heatmaps.y, heatmaps.scale_factor) AS client_y FROM heatmaps - WHERE and(equals(heatmaps.team_id, 2), ifNull(greaterOrEquals(heatmaps.viewport_width, ceil(divide(193, 16))), 0), equals(heatmaps.type, 'click'), greaterOrEquals(toTimeZone(heatmaps.timestamp, 'UTC'), toDate('2023-03-08')))) + WHERE and(equals(heatmaps.team_id, 2), equals(heatmaps.type, 'click'), greaterOrEquals(toTimeZone(heatmaps.timestamp, 'UTC'), toDate('2023-03-08')))) GROUP BY pointer_target_fixed, - relative_client_x, + pointer_relative_x, client_y LIMIT 1000000 SETTINGS readonly=2, max_execution_time=60, @@ -333,18 +333,18 @@ ''' /* user_id:0 request:_snapshot_ */ SELECT pointer_target_fixed AS pointer_target_fixed, - relative_client_x AS relative_client_x, + pointer_relative_x AS pointer_relative_x, client_y AS client_y, count(*) AS cnt FROM (SELECT heatmaps.distinct_id AS distinct_id, heatmaps.pointer_target_fixed AS pointer_target_fixed, - round(divide(heatmaps.x, heatmaps.viewport_width), 2) AS relative_client_x, + round(divide(heatmaps.x, heatmaps.viewport_width), 2) AS pointer_relative_x, multiply(heatmaps.y, heatmaps.scale_factor) AS client_y FROM heatmaps WHERE and(equals(heatmaps.team_id, 2), equals(heatmaps.type, 'click'), greaterOrEquals(toTimeZone(heatmaps.timestamp, 'UTC'), toDate('2023-03-08')))) GROUP BY pointer_target_fixed, - relative_client_x, + pointer_relative_x, client_y LIMIT 1000000 SETTINGS readonly=2, max_execution_time=60, diff --git a/posthog/heatmaps/test/test_heatmaps_api.py b/posthog/heatmaps/test/test_heatmaps_api.py index e07343a2760b4..18a2c2205d4e6 100644 --- a/posthog/heatmaps/test/test_heatmaps_api.py +++ b/posthog/heatmaps/test/test_heatmaps_api.py @@ -1,5 +1,3 @@ -import math - import freezegun from django.http import HttpResponse from parameterized import parameterized @@ -145,12 +143,21 @@ def test_can_filter_by_exact_url(self) -> None: @snapshot_clickhouse_queries def test_can_get_scrolldepth_counts(self) -> None: + # to calculate expected scroll depth bucket from y and viewport height + # ((round(y/16) + round(viewport_height/16)) * 16 // 100) * 100 + + # scroll depth bucket 1000 self._create_heatmap_event("session_1", "scrolldepth", "2023-03-08T07:00:00", y=10, viewport_height=1000) self._create_heatmap_event("session_2", "scrolldepth", "2023-03-08T08:00:00", y=100, viewport_height=1000) + # scroll depth bucket 1100 self._create_heatmap_event("session_3", "scrolldepth", "2023-03-08T08:01:00", y=200, viewport_height=1000) + # scroll depth bucket 1200 self._create_heatmap_event("session_4", "scrolldepth", "2023-03-08T08:01:00", y=300, viewport_height=1000) + # scroll depth bucket 1300 self._create_heatmap_event("session_5", "scrolldepth", "2023-03-08T08:01:00", y=400, viewport_height=1000) + # scroll depth bucket 1400 self._create_heatmap_event("session_6", "scrolldepth", "2023-03-08T08:01:00", y=500, viewport_height=1000) + # scroll depth bucket 1800 self._create_heatmap_event("session_7", "scrolldepth", "2023-03-08T08:01:00", y=900, viewport_height=1000) self._create_heatmap_event("session_8", "scrolldepth", "2023-03-08T08:01:00", y=900, viewport_height=1000) @@ -159,74 +166,139 @@ def test_can_get_scrolldepth_counts(self) -> None: assert scroll_response.json() == { "results": [ { - "bucket_count": 1, + "bucket_count": 2, "cumulative_count": 8, "scroll_depth_bucket": 1000, }, { "bucket_count": 1, - "cumulative_count": 7, + "cumulative_count": 6, "scroll_depth_bucket": 1100, }, { - "bucket_count": 2, - "cumulative_count": 6, + "bucket_count": 1, + "cumulative_count": 5, "scroll_depth_bucket": 1200, }, { "bucket_count": 1, "cumulative_count": 4, - "scroll_depth_bucket": 1400, + "scroll_depth_bucket": 1300, }, { "bucket_count": 1, "cumulative_count": 3, - "scroll_depth_bucket": 1500, + "scroll_depth_bucket": 1400, }, { "bucket_count": 2, "cumulative_count": 2, - "scroll_depth_bucket": 1900, + "scroll_depth_bucket": 1800, }, ], } - @snapshot_clickhouse_queries - def test_can_get_filter_by_min_viewport(self) -> None: - # all scale to 10 - self._create_heatmap_event("session_1", "click", "2023-03-08T08:00:00", 150) - self._create_heatmap_event("session_2", "click", "2023-03-08T08:00:00", 151) - self._create_heatmap_event("session_3", "click", "2023-03-08T08:01:00", 152) - # scale to 11 - self._create_heatmap_event("session_3", "click", "2023-03-08T08:01:00", 161) - # scales to 12 - self._create_heatmap_event("session_3", "click", "2023-03-08T08:01:00", 177) + def test_can_get_scrolldepth_counts_by_visitor(self) -> None: + # scroll depth bucket 1000 + self._create_heatmap_event( + "session_1", "scrolldepth", "2023-03-08T07:00:00", y=100, viewport_height=1000, distinct_id="12345" + ) - self._assert_heatmap_single_result_count({"date_from": "2023-03-08", "viewport_width_min": "150"}, 5) - self._assert_heatmap_single_result_count({"date_from": "2023-03-08", "viewport_width_min": "161"}, 2) - self._assert_heatmap_single_result_count({"date_from": "2023-03-08", "viewport_width_min": "177"}, 1) - self._assert_heatmap_no_result_count({"date_from": "2023-03-08", "viewport_width_min": "193"}) + # one person only scrolls a little way + # scroll depth bucket 1000 + self._create_heatmap_event( + "session_2", "scrolldepth", "2023-03-08T08:00:00", y=100, viewport_height=1000, distinct_id="34567" + ) + + # the first person scrolls further + # scroll depth bucket 1100 + self._create_heatmap_event( + "session_3", "scrolldepth", "2023-03-08T08:01:00", y=200, viewport_height=1000, distinct_id="12345" + ) + + scroll_response = self._get_heatmap( + {"date_from": "2023-03-06", "type": "scrolldepth", "aggregation": "unique_visitors"} + ) + + assert scroll_response.json() == { + "results": [ + { + "bucket_count": 2, + "cumulative_count": 3, + "scroll_depth_bucket": 1000, + }, + { + "bucket_count": 1, + "cumulative_count": 1, + "scroll_depth_bucket": 1100, + }, + ], + } + @staticmethod + def heatmap_result(relative_x: float, count: int) -> dict: + return { + "count": count, + "pointer_relative_x": relative_x, + "pointer_target_fixed": True, + "pointer_y": 16, + } + + @parameterized.expand( + [ + [ + "min_150", + {"date_from": "2023-03-08", "viewport_width_min": "150"}, + [heatmap_result(0.08, 1), heatmap_result(0.09, 1), heatmap_result(0.1, 1), heatmap_result(0.11, 2)], + ], + [ + "min_161", + {"date_from": "2023-03-08", "viewport_width_min": "161"}, + [ + heatmap_result(0.08, 1), + heatmap_result(0.09, 1), + heatmap_result(0.1, 1), + ], + ], + [ + "min_177", + {"date_from": "2023-03-08", "viewport_width_min": "177"}, + [ + heatmap_result(0.08, 1), + heatmap_result(0.09, 1), + ], + ], + ["min_201", {"date_from": "2023-03-08", "viewport_width_min": "201"}, []], + [ + "min_161_and_max_192", + {"date_from": "2023-03-08", "viewport_width_min": 161, "viewport_width_max": 192}, + [heatmap_result(0.08, 1), heatmap_result(0.09, 1), heatmap_result(0.1, 1)], + ], + ] + ) @snapshot_clickhouse_queries - def test_can_get_filter_by_min_and_max_viewport(self) -> None: - # all scale to 10 + def test_can_filter_by_viewport(self, _name: str, query_params: dict, expected_results: list) -> None: + # all these xs = round(10/16) = 1 + + # viewport widths that scale to 9 self._create_heatmap_event("session_1", "click", "2023-03-08T08:00:00", 150) self._create_heatmap_event("session_2", "click", "2023-03-08T08:00:00", 151) + + # viewport widths that scale to 10 self._create_heatmap_event("session_3", "click", "2023-03-08T08:01:00", 152) - # scale to 11 self._create_heatmap_event("session_3", "click", "2023-03-08T08:01:00", 161) - # scales to 12 + + # viewport width that scales to 11 self._create_heatmap_event("session_3", "click", "2023-03-08T08:01:00", 177) - # scales to 13 + # viewport width that scales to 12 self._create_heatmap_event("session_3", "click", "2023-03-08T08:01:00", 193) - self._assert_heatmap_single_result_count( - {"date_from": "2023-03-08", "viewport_width_min": 161, "viewport_width_max": 192}, 2 - ) + response = self._get_heatmap(query_params) + assert sorted(response.json()["results"], key=lambda k: k["pointer_relative_x"]) == expected_results @snapshot_clickhouse_queries def test_can_get_count_by_aggregation(self) -> None: - # 3 items but 2 viitors + # 3 items but 2 visitors self._create_heatmap_event("session_1", "click", distinct_id="12345") self._create_heatmap_event("session_2", "click", distinct_id="12345") self._create_heatmap_event("session_3", "click", distinct_id="54321") @@ -250,37 +322,6 @@ def test_only_allow_valid_values_for_aggregation(self, choice: str | None, expec {"date_from": "2023-03-08", "aggregation": choice}, expected_status_code=expected_status_code ) - def test_can_get_scrolldepth_counts_by_visitor(self) -> None: - self._create_heatmap_event( - "session_1", "scrolldepth", "2023-03-08T07:00:00", y=100, viewport_height=1000, distinct_id="12345" - ) - # one person only scrolls a little way - self._create_heatmap_event( - "session_2", "scrolldepth", "2023-03-08T08:00:00", y=100, viewport_height=1000, distinct_id="34567" - ) - self._create_heatmap_event( - "session_3", "scrolldepth", "2023-03-08T08:01:00", y=200, viewport_height=1000, distinct_id="12345" - ) - - scroll_response = self._get_heatmap( - {"date_from": "2023-03-06", "type": "scrolldepth", "aggregation": "unique_visitors"} - ) - - assert scroll_response.json() == { - "results": [ - { - "bucket_count": 2, - "cumulative_count": 3, - "scroll_depth_bucket": 1100, - }, - { - "bucket_count": 1, - "cumulative_count": 1, - "scroll_depth_bucket": 1200, - }, - ], - } - def _create_heatmap_event( self, session_id: str, @@ -288,6 +329,7 @@ def _create_heatmap_event( date_from: str = "2023-03-08T09:00:00", viewport_width: int = 100, viewport_height: int = 100, + x: int = 10, y: int = 20, current_url: str | None = None, distinct_id: str = "user_distinct_id", @@ -306,12 +348,12 @@ def _create_heatmap_event( "team_id": team_id, "distinct_id": distinct_id, "timestamp": format_clickhouse_timestamp(date_from), - "x": 10 / 16, - "y": y / 16, + "x": round(x / 16), + "y": round(y / 16), "scale_factor": 16, # this adjustment is done at ingestion - "viewport_width": math.ceil(viewport_width / 16), - "viewport_height": math.ceil(viewport_height / 16), + "viewport_width": round(viewport_width / 16), + "viewport_height": round(viewport_height / 16), "type": type, "pointer_target_fixed": True, "current_url": current_url if current_url else "http://posthog.com",