Пример #1
0
    def test_funnel_strict_basic_post(self):
        journeys_for(
            {
                "1": [{"event": "step one"}, {"event": "step two"},],
                "2": [{"event": "step one"}, {"event": "blahh"}, {"event": "step two"},],
            },
            self.team,
        )

        response = self.client.post(
            f"/api/projects/{self.team.id}/insights/funnel/",
            {
                "events": [
                    {"id": "step one", "type": "events", "order": 0},
                    {"id": "step two", "type": "events", "order": 1},
                ],
                "funnel_window_days": 14,
                "funnel_order_type": "strict",
                "insight": "funnels",
            },
        ).json()

        self.assertEqual(len(response["result"]), 2)
        self.assertEqual(response["result"][0]["name"], "step one")
        self.assertEqual(response["result"][1]["name"], "step two")
        self.assertEqual(response["result"][0]["count"], 2)
        self.assertEqual(response["result"][1]["count"], 1)

        # Should have 2 people, all got through step one, but as this is a
        # strict funnel, person with distinct_id "2" is not converted as they
        # performed bleh in between step one and step two
        assert get_funnel_people_breakdown_by_step(client=self.client, funnel_response=response) == [
            {"name": "step one", "converted": ["1", "2"], "dropped": []},
            {"name": "step two", "converted": ["1"], "dropped": ["2"]},
        ]
Пример #2
0
    def test_funnel_unordered_basic_post(self):
        journeys_for(
            {
                "1": [{"event": "step one"}, {"event": "step two"},],
                "2": [{"event": "step one"}, {"event": "step two"},],
            },
            self.team,
        )

        response = self.client.post(
            f"/api/projects/{self.team.id}/insights/funnel/",
            {
                "events": [
                    {"id": "step one", "type": "events", "order": 0},
                    {"id": "step two", "type": "events", "order": 1},
                ],
                "funnel_window_days": 14,
                "funnel_order_type": "unordered",
                "insight": "funnels",
            },
        ).json()

        self.assertEqual(len(response["result"]), 2)
        self.assertEqual(response["result"][0]["name"], "step one")
        self.assertEqual(response["result"][1]["name"], "step two")
        self.assertEqual(response["result"][0]["count"], 2)
        self.assertEqual(response["result"][1]["count"], 2)

        # Should have 2 people, all got to the end of the funnel
        assert get_funnel_people_breakdown_by_step(client=self.client, funnel_response=response) == [
            {"name": "step one", "converted": ["1", "2"], "dropped": []},
            {"name": "step two", "converted": ["1", "2"], "dropped": []},
        ]
Пример #3
0
    def test_strict_funnel_with_breakdown_by_event_property(self):
        # Setup three funnel people, with two different $browser values
        chrome_properties = {"key": "val", "$browser": "Chrome"}
        safari_properties = {"key": "val", "$browser": "Safari"}

        events = {
            "person1": [
                {"event": "sign up", "timestamp": "2020-01-01", "properties": chrome_properties},
                {"event": "play movie", "timestamp": "2020-01-02", "properties": chrome_properties},
                {"event": "buy", "timestamp": "2020-01-03", "properties": chrome_properties},
            ],
            "person2": [
                {"event": "sign up", "timestamp": "2020-01-01", "properties": safari_properties},
                {"event": "play movie", "timestamp": "2020-01-02", "properties": safari_properties},
                {
                    # This person should not convert here as we're in strict mode,
                    # and this event is not in the funnel definition
                    "event": "event not in funnel",
                    "timestamp": "2020-01-03",
                    "properties": safari_properties,
                },
                {"event": "buy", "timestamp": "2020-01-04", "properties": safari_properties},
            ],
            "person3": [{"event": "sign up", "timestamp": "2020-01-01", "properties": safari_properties},],
        }

        journeys_for(team=self.team, events_by_person=events)

        response = self.client.post(
            f"/api/projects/{self.team.pk}/insights/funnel/",
            {
                "events": [{"id": "sign up", "order": 0}, {"id": "play movie", "order": 1}, {"id": "buy", "order": 2}],
                "insight": "FUNNELS",
                "date_from": "2020-01-01",
                "date_to": "2020-01-08",
                "funnel_window_days": 7,
                "funnel_order_type": "strict",
                "breakdown_type": "event",
                "breakdown": "$browser",
            },
        ).json()

        assert get_funnel_breakdown_people_breakdown_by_step(client=self.client, funnel_response=response) == [
            {
                "breakdown_value": "Chrome",
                "steps": [
                    {"name": "sign up", "converted": ["person1"], "dropped": []},
                    {"name": "play movie", "converted": ["person1"], "dropped": []},
                    {"name": "buy", "converted": ["person1"], "dropped": []},
                ],
            },
            {
                "breakdown_value": "Safari",
                "steps": [
                    {"name": "sign up", "converted": ["person2", "person3"], "dropped": []},
                    {"name": "play movie", "converted": ["person2"], "dropped": ["person3"]},
                    {"name": "buy", "converted": [], "dropped": ["person2"]},
                ],
            },
        ]
Пример #4
0
    def test_funnel_trends_basic_post_backwards_compatibility(self):
        journeys_for(
            {
                "user_one": [
                    {"event": "step one", "timestamp": datetime(2021, 5, 1, 1)},
                    {"event": "step two", "timestamp": datetime(2021, 5, 3)},
                    {"event": "step three", "timestamp": datetime(2021, 5, 5)},
                ],
                "user_two": [
                    {"event": "step one", "timestamp": datetime(2021, 5, 2)},
                    {"event": "step two", "timestamp": datetime(2021, 5, 4)},
                    {"event": "step three", "timestamp": datetime(2021, 5, 5)},
                ],
            },
            self.team,
        )

        response = self.client.post(
            f"/api/projects/{self.team.id}/insights/funnel/",
            {
                "events": [
                    {"id": "step one", "type": "events", "order": 0},
                    {"id": "step two", "type": "events", "order": 1},
                    {"id": "step three", "type": "events", "order": 2},
                ],
                "funnel_window_days": 7,
                "date_from": "2021-05-01 00:00:00",
                "date_to": "2021-05-07 23:59:59",
                "display": "ActionsLineGraph",
            },
        ).json()

        self.assertEqual(len(response["result"]), 1)
        self.assertEqual(response["result"][0]["count"], 7)
        self.assertEqual(response["result"][0]["data"], [100, 100, 0, 0, 0, 0, 0])
Пример #5
0
    def test_funnel_basic_exclusions(self):
        journeys_for(
            {
                "1": [{"event": "step one"}, {"event": "step x"}, {"event": "step two"},],
                "2": [{"event": "step one"}, {"event": "step two"},],
            },
            self.team,
        )

        response = self.client.post(
            f"/api/projects/{self.team.id}/insights/funnel/",
            {
                "events": [
                    {"id": "step one", "type": "events", "order": 0},
                    {"id": "step two", "type": "events", "order": 1},
                ],
                "exclusions": [{"id": "step x", "type": "events", "funnel_from_step": 0, "funnel_to_step": 1},],
                "funnel_window_days": 14,
                "insight": "funnels",
            },
        ).json()

        self.assertEqual(len(response["result"]), 2)
        self.assertEqual(response["result"][0]["name"], "step one")
        self.assertEqual(response["result"][1]["name"], "step two")
        self.assertEqual(response["result"][0]["count"], 1)
        self.assertEqual(response["result"][1]["count"], 1)

        # Should only pick up the person with distinct id "2" as the "1" person
        # performed the "step x" event, which we're explicitly asking to be
        # excluded in the request payload
        assert get_funnel_people_breakdown_by_step(client=self.client, funnel_response=response) == [
            {"name": "step one", "converted": ["2"], "dropped": []},
            {"name": "step two", "converted": ["2"], "dropped": []},
        ]
Пример #6
0
    def test_single_property_breakdown(self):
        journeys_for(
            {
                "person1": [
                    {"event": "$pageview", "properties": {"$browser": "Chrome", "$browser_version": 95}},
                    {"event": "$pageleave", "properties": {"$browser": "Chrome", "$browser_version": 95}},
                ],
                "person2": [
                    {"event": "$pageview", "properties": {"$browser": "Safari", "$browser_version": 11}},
                    {"event": "$pageview", "properties": {"$browser": "Safari", "$browser_version": 11}},
                ],
            },
            self.team,
        )

        filter_with_breakdown = {
            "insight": "FUNNELS",
            "date_from": "-14d",
            "actions": [],
            "events": [
                {"id": "$pageview", "name": "$pageview", "type": "events", "order": 0},
                {"id": "$pageleave", "name": "$pageleave", "type": "events", "order": 1},
            ],
            "display": "FunnelViz",
            "interval": "day",
            "properties": [],
            "funnel_viz_type": "steps",
            "exclusions": [],
            "breakdown": "$browser",
            "breakdown_type": "event",
            "funnel_from_step": 0,
            "funnel_to_step": 1,
        }

        response = self.client.post(f"/api/projects/{self.team.id}/insights/funnel?refresh=true", filter_with_breakdown)
        self.assertEqual(200, response.status_code)

        response_data = response.json()
        result = response_data["result"]

        self.assertEqual(result[0][0]["name"], "$pageview")
        self.assertEqual(result[0][0]["count"], 1)
        self.assertEqual("Chrome", result[0][0]["breakdown"])
        self.assertEqual("Chrome", result[0][0]["breakdown_value"])

        self.assertEqual(result[0][1]["name"], "$pageleave")
        self.assertEqual(result[0][1]["count"], 1)
        self.assertEqual("Chrome", result[0][1]["breakdown"])
        self.assertEqual("Chrome", result[0][1]["breakdown_value"])

        self.assertEqual(result[1][0]["name"], "$pageview")
        self.assertEqual(result[1][0]["count"], 1)
        self.assertEqual("Safari", result[1][0]["breakdown"])
        self.assertEqual("Safari", result[1][0]["breakdown_value"])

        self.assertEqual(result[1][1]["name"], "$pageleave")
        self.assertEqual(result[1][1]["count"], 0)
        self.assertEqual("Safari", result[1][1]["breakdown"])
        self.assertEqual("Safari", result[1][1]["breakdown_value"])
Пример #7
0
    def test_funnel_with_breakdown_by_event_property(self):
        # Setup three funnel people, with two different $browser values
        # NOTE: this is mostly copied from
        # https://github.com/PostHog/posthog/blob/a0f5a0a46a0deca2e17a66dfb530ca18ac99e58c/ee/clickhouse/queries/funnels/test/breakdown_cases.py#L24:L24
        #
        person1_properties = {"key": "val", "$browser": "Chrome"}
        person2_properties = {"key": "val", "$browser": "Safari"}
        person3_properties = person2_properties

        events = {
            "person1": [
                {"event": "sign up", "timestamp": "2020-01-01", "properties": person1_properties},
                {"event": "play movie", "timestamp": "2020-01-02", "properties": person1_properties},
                {"event": "buy", "timestamp": "2020-01-03", "properties": person1_properties},
            ],
            "person2": [
                {"event": "sign up", "timestamp": "2020-01-01", "properties": person2_properties},
                {"event": "play movie", "timestamp": "2020-01-02", "properties": person2_properties},
                {"event": "buy", "timestamp": "2020-01-03", "properties": person2_properties},
            ],
            "person3": [{"event": "sign up", "timestamp": "2020-01-01", "properties": person3_properties},],
        }

        journeys_for(team=self.team, events_by_person=events)

        response = self.client.post(
            f"/api/projects/{self.team.pk}/insights/funnel/",
            {
                "events": [{"id": "sign up", "order": 0}, {"id": "play movie", "order": 1}, {"id": "buy", "order": 2}],
                "insight": "FUNNELS",
                "date_from": "2020-01-01",
                "date_to": "2020-01-08",
                "funnel_window_days": 7,
                "breakdown_type": "event",
                "breakdown": "$browser",
            },
        ).json()

        assert get_funnel_breakdown_people_breakdown_by_step(client=self.client, funnel_response=response) == [
            {
                "breakdown_value": "Chrome",
                "steps": [
                    {"name": "sign up", "converted": ["person1"], "dropped": []},
                    {"name": "play movie", "converted": ["person1"], "dropped": []},
                    {"name": "buy", "converted": ["person1"], "dropped": []},
                ],
            },
            {
                "breakdown_value": "Safari",
                "steps": [
                    {"name": "sign up", "converted": ["person2", "person3"], "dropped": []},
                    {"name": "play movie", "converted": ["person2"], "dropped": ["person3"]},
                    {"name": "buy", "converted": ["person2"], "dropped": []},
                ],
            },
        ]
Пример #8
0
    def test_funnel_time_to_convert_auto_bins_unordered(self):
        journeys_for(
            {
                "user a": [
                    {"event": "step one", "timestamp": datetime(2021, 6, 8, 18)},
                    {"event": "step two", "timestamp": datetime(2021, 6, 8, 19)},
                    # Converted from 0 to 1 in 3600 s
                    {"event": "step three", "timestamp": datetime(2021, 6, 8, 21)},
                ],
                "user b": [
                    {"event": "step two", "timestamp": datetime(2021, 6, 9, 13)},
                    {"event": "step one", "timestamp": datetime(2021, 6, 9, 13, 37)},
                    # Converted from 0 to 1 in 2200 s
                ],
                "user c": [
                    {"event": "step one", "timestamp": datetime(2021, 6, 11, 7)},
                    {"event": "step two", "timestamp": datetime(2021, 6, 12, 6)},
                    # Converted from 0 to 1 in 82_800 s
                ],
            },
            self.team,
        )

        response = self.client.post(
            f"/api/projects/{self.team.id}/insights/funnel/",
            {
                "insight": "funnels",
                "funnel_viz_type": "time_to_convert",
                "funnel_order_type": "unordered",
                "interval": "day",
                "date_from": "2021-06-07 00:00:00",
                "date_to": "2021-06-13 23:59:59",
                "funnel_to_step": 1,
                "funnel_window_days": 7,
                "events": [
                    {"id": "step one", "order": 0},
                    {"id": "step two", "order": 1},
                    {"id": "step three", "order": 2},
                ],
            },
        )

        self.assertEqual(response.status_code, 200)
        response_data = response.json()
        response_data.pop("last_refresh")
        self.assertEqual(
            response_data,
            {
                "is_cached": False,
                "result": {
                    "bins": [[2220.0, 2], [29080.0, 0], [55940.0, 0], [82800.0, 1]],
                    "average_conversion_time": 29540.0,
                },
            },
        )
Пример #9
0
    def test_test_account_filters_with_groups(self):
        self.team.test_account_filters = [
            {"key": "key", "type": "group", "value": "value", "group_type_index": 0},
        ]
        self.team.save()

        GroupTypeMapping.objects.create(team=self.team, group_type="organization", group_type_index=0)
        create_group(self.team.pk, group_type_index=0, group_key="in", properties={"key": "value"})
        create_group(self.team.pk, group_type_index=0, group_key="out", properties={"key": "othervalue"})

        with freeze_time("2020-01-11T12:00:00Z"):
            Person.objects.create(distinct_ids=["person1"], team_id=self.team.pk)

        with freeze_time("2020-01-09T12:00:00Z"):
            Person.objects.create(distinct_ids=["person2"], team_id=self.team.pk)

        journeys_for(
            {
                "person1": [
                    {"event": "$pageview", "timestamp": datetime(2020, 1, 11, 12), "properties": {"$group_0": "out"},},
                ],
                "person2": [
                    {"event": "$pageview", "timestamp": datetime(2020, 1, 9, 12), "properties": {"$group_0": "in"},},
                    {"event": "$pageview", "timestamp": datetime(2020, 1, 12, 12), "properties": {"$group_0": "in"},},
                    {"event": "$pageview", "timestamp": datetime(2020, 1, 15, 12), "properties": {"$group_0": "in"},},
                ],
            },
            self.team,
        )

        result = ClickhouseTrends().run(
            Filter(
                data={
                    "date_from": "2020-01-12T00:00:00Z",
                    "date_to": "2020-01-19T00:00:00Z",
                    "events": [{"id": "$pageview", "type": "events", "order": 0}],
                    "shown_as": TRENDS_LIFECYCLE,
                    FILTER_TEST_ACCOUNTS: True,
                },
                team=self.team,
            ),
            self.team,
        )

        self.assertLifecycleResults(
            result,
            [
                {"status": "dormant", "data": [0, -1, 0, 0, -1, 0, 0, 0]},
                {"status": "new", "data": [0, 0, 0, 0, 0, 0, 0, 0]},
                {"status": "resurrecting", "data": [1, 0, 0, 1, 0, 0, 0, 0]},
                {"status": "returning", "data": [0, 0, 0, 0, 0, 0, 0, 0]},
            ],
        )
Пример #10
0
    def test_aggregating_by_group(self):
        self._create_groups()

        events_by_person = {
            "person1": [
                {
                    "event": "$pageview",
                    "timestamp": datetime(2020, 1, 2, 12),
                    "properties": {
                        "$group_0": "org:5"
                    }
                },
                {
                    "event": "$pageview",
                    "timestamp": datetime(2020, 1, 2, 12),
                    "properties": {
                        "$group_0": "org:6"
                    }
                },
                {
                    "event": "$pageview",
                    "timestamp": datetime(2020, 1, 2, 12),
                    "properties": {
                        "$group_0": "org:6",
                        "$group_1": "company:10"
                    },
                },
            ],
        }
        journeys_for(events_by_person, self.team)

        request = TrendsRequest(
            date_from="2020-01-01 00:00:00",
            date_to="2020-01-12 00:00:00",
            events=[{
                "id": "$pageview",
                "type": "events",
                "order": 0,
                "math": "unique_group",
                "math_group_type_index": 0,
            }],
        )
        data_response = get_trends_time_series_ok(self.client, request,
                                                  self.team)

        assert data_response["$pageview"]["2020-01-01"].value == 0
        assert data_response["$pageview"]["2020-01-02"].value == 2

        curr_people = get_people_from_url_ok(
            self.client, data_response["$pageview"]["2020-01-02"].person_url)

        assert sorted([p["group_key"]
                       for p in curr_people]) == sorted(["org:5", "org:6"])
Пример #11
0
    def _create_browser_breakdown_events(self):
        person1 = _create_person(distinct_ids=["person1"], team_id=self.team.pk, properties={"$country": "PL"})
        person2 = _create_person(distinct_ids=["person2"], team_id=self.team.pk, properties={"$country": "EE"})
        journeys_for(
            {
                "person1": [
                    {
                        "event": "sign up",
                        "timestamp": datetime(2020, 1, 1, 12),
                        "properties": {"$browser": "Chrome", "$browser_version": "95"},
                    },
                    {
                        "event": "play movie",
                        "timestamp": datetime(2020, 1, 1, 13),
                        "properties": {"$browser": "Chrome", "$browser_version": "95"},
                    },
                    {
                        "event": "buy",
                        "timestamp": datetime(2020, 1, 1, 15),
                        "properties": {"$browser": "Chrome", "$browser_version": "95"},
                    },
                ],
                "person2": [
                    {
                        "event": "sign up",
                        "timestamp": datetime(2020, 1, 2, 14),
                        "properties": {"$browser": "Safari", "$browser_version": "14"},
                    },
                    {
                        "event": "play movie",
                        "timestamp": datetime(2020, 1, 2, 16),
                        "properties": {"$browser": "Safari", "$browser_version": "14"},
                    },
                ],
            },
            self.team,
        )

        _create_event(
            team=self.team,
            event="sign up",
            distinct_id="person2",
            properties={"key": "val", "$browser": "Safari"},
            timestamp="2020-01-02T14:00:00Z",
        )
        _create_event(
            team=self.team,
            event="play movie",
            distinct_id="person2",
            properties={"key": "val", "$browser": "Safari"},
            timestamp="2020-01-02T16:00:00Z",
        )
        return person1, person2
Пример #12
0
    def _setup_returning_lifecycle_data(self, days):
        with freeze_time("2019-01-01T12:00:00Z"):
            Person.objects.create(distinct_ids=["person1"], team_id=self.team.pk)

        journeys_for(
            {
                "person1": [
                    {"event": "$pageview", "timestamp": (now() - timedelta(days=n)).strftime("%Y-%m-%d %H:%M:%S.%f")}
                    for n in range(days)
                ],
            },
            self.team,
            create_people=False,
        )
Пример #13
0
    def test_funnel_invalid_exclusions(self):
        journeys_for(
            {
                "1": [{"event": "step one"}, {"event": "step x"}, {"event": "step two"},],
                "2": [{"event": "step one"}, {"event": "step two"},],
            },
            self.team,
        )

        for exclusion_id, exclusion_from_step, exclusion_to_step, error in [
            ("step one", 0, 1, True),
            ("step two", 0, 1, True),
            ("step two", 0, 2, True),
            ("step one", 0, 2, True),
            ("step three", 0, 2, True),
            ("step three", 0, 1, False),
        ]:
            response = self.client.post(
                f"/api/projects/{self.team.id}/insights/funnel/",
                {
                    "events": [
                        {"id": "step one", "type": "events", "order": 0},
                        {"id": "step two", "type": "events", "order": 1},
                        {"id": "step three", "type": "events", "order": 2},
                    ],
                    "exclusions": [
                        {
                            "id": exclusion_id,
                            "type": "events",
                            "funnel_from_step": exclusion_from_step,
                            "funnel_to_step": exclusion_to_step,
                        },
                    ],
                    "funnel_window_days": 14,
                    "insight": "funnels",
                },
            )

            if error:
                self.assertEqual(response.status_code, 400)
                self.assertEqual(
                    response.json(), self.validation_error_response("Exclusion event can't be the same as funnel step")
                )
            else:
                self.assertEqual(response.status_code, 200)
Пример #14
0
    def test_filter_events_with_date_format(self):
        journeys_for(
            {
                "2": [
                    {
                        "event": "should_be_included",
                        "properties": {"prop_that_is_an_sdk_style_unix_timestamp": 1639427152.339},
                    },
                    {
                        "event": "should_be_excluded",
                        "properties": {
                            "prop_that_is_an_sdk_style_unix_timestamp": 1639427152.339 * 2
                        },  # the far future
                    },
                    {
                        "event": "should_be_excluded",
                        "properties": {
                            "prop_that_is_an_sdk_style_unix_timestamp": 1639427152.339 * 2
                        },  # the far future
                    },
                ]
            },
            self.team,
        )

        response = self.client.get(
            f"/api/projects/{self.team.id}/events/?properties=%s"
            % (
                json.dumps(
                    [
                        {
                            "key": "prop_that_is_an_sdk_style_unix_timestamp",
                            "value": "2021-12-25 12:00:00",
                            "operator": "is_date_before",
                            "type": "event",
                            "property_type": "DateTime",
                            "property_type_format": "unix_timestamp",
                        }
                    ]
                )
            )
        ).json()

        self.assertEqual(len(response["results"]), 1)
        self.assertEqual([r["event"] for r in response["results"]], ["should_be_included"])
Пример #15
0
    def test_filter_events_by_being_after_properties_with_date_type(self):
        journeys_for(
            {
                "2": [
                    {
                        "event": "should_be_excluded",
                        "properties": {
                            "prop_that_is_a_unix_timestamp":
                            datetime(2012, 1, 7, 18).timestamp()
                        },
                    },
                    {
                        "event": "should_be_included",
                        "properties": {
                            "prop_that_is_a_unix_timestamp":
                            datetime(2012, 1, 7, 19).timestamp()
                        },
                    },
                    {
                        "event": "should_be_included",
                        "properties": {
                            "prop_that_is_a_unix_timestamp":
                            datetime(2012, 1, 7, 20).timestamp()
                        },
                    },
                ]
            },
            self.team,
        )

        response = self.client.get(
            f"/api/projects/{self.team.id}/events/?properties=%s" %
            (json.dumps([{
                "key": "prop_that_is_a_unix_timestamp",
                "value": "2012-01-07 18:30:00",
                "operator": "is_date_after",
                "type": "event",
            }]))).json()

        self.assertEqual(len(response["results"]), 2)
        self.assertEqual([r["event"] for r in response["results"]],
                         ["should_be_included", "should_be_included"])
Пример #16
0
        def test_funnel_step_conversion_times(self):
            filters = {
                "events": [{"id": "sign up", "order": 0}, {"id": "play movie", "order": 1}, {"id": "buy", "order": 2},],
                "insight": INSIGHT_FUNNELS,
                "date_from": "2020-01-01",
                "date_to": "2020-01-08",
                "funnel_window_days": 7,
            }

            filter = Filter(data=filters)
            funnel = Funnel(filter, self.team)

            journeys_for(
                {
                    "person1": [
                        {"event": "sign up", "timestamp": datetime(2020, 1, 1, 12)},
                        {"event": "play movie", "timestamp": datetime(2020, 1, 1, 13)},
                        {"event": "buy", "timestamp": datetime(2020, 1, 1, 15)},
                    ],
                    "person2": [
                        {"event": "sign up", "timestamp": datetime(2020, 1, 2, 14)},
                        {"event": "play movie", "timestamp": datetime(2020, 1, 2, 16)},
                    ],
                    "person3": [
                        {"event": "sign up", "timestamp": datetime(2020, 1, 2, 14)},
                        {"event": "play movie", "timestamp": datetime(2020, 1, 2, 16)},
                        {"event": "buy", "timestamp": datetime(2020, 1, 2, 17)},
                    ],
                },
                self.team,
            )

            result = funnel.run()

            self.assertEqual(result[0]["average_conversion_time"], None)
            self.assertEqual(result[1]["average_conversion_time"], 6000)
            self.assertEqual(result[2]["average_conversion_time"], 5400)

            self.assertEqual(result[0]["median_conversion_time"], None)
            self.assertEqual(result[1]["median_conversion_time"], 7200)
            self.assertEqual(result[2]["median_conversion_time"], 5400)
Пример #17
0
    def _create_sample_data_multiple_dropoffs(self):
        events_by_person = {}
        for i in range(5):
            events_by_person[f"user_{i}"] = [
                {
                    "event": "step one",
                    "timestamp": datetime(2021, 5, 1)
                },
                {
                    "event": "step fake",
                    "timestamp": datetime(2021, 5, 2)
                },
                {
                    "event": "step two",
                    "timestamp": datetime(2021, 5, 3)
                },
                {
                    "event": "step three",
                    "timestamp": datetime(2021, 5, 5)
                },
            ]

        for i in range(5, 15):
            events_by_person[f"user_{i}"] = [
                {
                    "event": "step one",
                    "timestamp": datetime(2021, 5, 1)
                },
                {
                    "event": "step two",
                    "timestamp": datetime(2021, 5, 3)
                },
            ]

        for i in range(15, 35):
            events_by_person[f"user_{i}"] = [{
                "event": "step one",
                "timestamp": datetime(2021, 5, 1)
            }]

        journeys_for(events_by_person, self.team)
Пример #18
0
    def test_lifecycle_edge_cases(self):
        # This test tests behavior when created_at is different from first matching event and dormant/resurrecting/returning logic
        with freeze_time("2020-01-11T12:00:00Z"):
            Person.objects.create(distinct_ids=["person1"], team_id=self.team.pk)

        journeys_for(
            {
                "person1": [
                    {"event": "$pageview", "timestamp": datetime(2020, 1, 12, 12),},
                    {"event": "$pageview", "timestamp": datetime(2020, 1, 13, 12),},
                    {"event": "$pageview", "timestamp": datetime(2020, 1, 15, 12),},
                    {"event": "$pageview", "timestamp": datetime(2020, 1, 16, 12),},
                ],
            },
            self.team,
        )

        result = ClickhouseTrends().run(
            Filter(
                data={
                    "date_from": "2020-01-11T00:00:00Z",
                    "date_to": "2020-01-18T00:00:00Z",
                    "events": [{"id": "$pageview", "type": "events", "order": 0}],
                    "shown_as": TRENDS_LIFECYCLE,
                },
                team=self.team,
            ),
            self.team,
        )

        self.assertLifecycleResults(
            result,
            [
                {"status": "dormant", "data": [0, 0, 0, -1, 0, 0, -1, 0]},
                {"status": "new", "data": [0, 0, 0, 0, 0, 0, 0, 0]},
                {"status": "resurrecting", "data": [0, 1, 0, 0, 1, 0, 0, 0]},
                {"status": "returning", "data": [0, 0, 1, 0, 0, 1, 0, 0]},
            ],
        )
Пример #19
0
    def test_insight_trends_clean_arg(self):

        events_by_actor = {
            "1": [
                {
                    "event": "$pageview",
                    "timestamp": datetime(2012, 1, 14, 3),
                    "properties": {
                        "key": "val"
                    }
                },
            ],
            "2": [
                {
                    "event": "$pageview",
                    "timestamp": datetime(2012, 1, 14, 3)
                },
            ],
        }
        created_actors = journeys_for(events_by_actor, self.team)

        with freeze_time("2012-01-15T04:01:34.000Z"):

            request = TrendsRequest(
                date_from="-14d",
                display="ActionsLineGraph",
                events=[{
                    "id": "$pageview",
                    "math":
                    None,  # this argument will now be removed from the request instead of becoming a string
                    "name": "$pageview",
                    "custom_name": None,
                    "type": "events",
                    "order": 0,
                    "properties": [{
                        "key": "key",
                        "value": "val"
                    }],
                    "math_property": None,
                }],
            )
            data = get_trends_time_series_ok(self.client, request, self.team)

        actors = get_people_from_url_ok(
            self.client, data["$pageview"]["2012-01-14"].person_url)

        # this would return 2 people prior to #8103 fix
        # 'None' values have to be purged before formatting into the actor url
        assert sorted([p["id"] for p in actors
                       ]) == sorted([str(created_actors["1"].uuid)])
Пример #20
0
        def test_funnel_with_multiple_incomplete_tries(self):
            filters = {
                "events": [
                    {"id": "user signed up", "type": "events", "order": 0},
                    {"id": "$pageview", "type": "events", "order": 1},
                    {"id": "something else", "type": "events", "order": 2},
                ],
                "funnel_window_days": 1,
                "date_from": "2021-05-01 00:00:00",
                "date_to": "2021-05-14 00:00:00",
                "insight": INSIGHT_FUNNELS,
            }

            filter = Filter(data=filters)
            funnel = Funnel(filter, self.team)

            people = journeys_for(
                {
                    "person1": [
                        # person1 completed funnel on 2021-05-01
                        {"event": "user signed up", "timestamp": datetime(2021, 5, 1, 1)},
                        {"event": "$pageview", "timestamp": datetime(2021, 5, 1, 2)},
                        {"event": "something else", "timestamp": datetime(2021, 5, 1, 3)},
                        # person1 completed part of funnel on 2021-05-03 and took 2 hours to convert
                        {"event": "user signed up", "timestamp": datetime(2021, 5, 3, 4)},
                        {"event": "$pageview", "timestamp": datetime(2021, 5, 3, 5)},
                        # person1 completed part of funnel on 2021-05-04 and took 3 hours to convert
                        {"event": "user signed up", "timestamp": datetime(2021, 5, 4, 7)},
                        {"event": "$pageview", "timestamp": datetime(2021, 5, 4, 10)},
                    ],
                },
                self.team,
            )

            result = funnel.run()

            self.assertEqual(result[0]["name"], "user signed up")
            self.assertEqual(result[1]["name"], "$pageview")
            self.assertEqual(result[2]["name"], "something else")
            self.assertEqual(result[0]["count"], 1)
            self.assertEqual(
                result[1]["average_conversion_time"], 3600
            )  # one hour to convert, disregard the incomplete tries
            self.assertEqual(result[1]["median_conversion_time"], 3600)

            # check ordering of people in every step
            self.assertCountEqual(
                self._get_actor_ids_at_step(filter, 1), [people["person1"].uuid,],
            )
Пример #21
0
    def test_insight_trends_basic(self):

        events_by_person = {
            "1": [
                {
                    "event": "$pageview",
                    "timestamp": datetime(2012, 1, 14, 3)
                },
            ],
            "2": [
                {
                    "event": "$pageview",
                    "timestamp": datetime(2012, 1, 14, 3)
                },
            ],
        }
        created_people = journeys_for(events_by_person, self.team)

        with freeze_time("2012-01-15T04:01:34.000Z"):

            request = TrendsRequest(
                date_from="-14d",
                display="ActionsLineGraph",
                events=[{
                    "id": "$pageview",
                    "math": "dau",
                    "name": "$pageview",
                    "custom_name": None,
                    "type": "events",
                    "order": 0,
                    "properties": [],
                    "math_property": None,
                }],
            )
            data = get_trends_time_series_ok(self.client, request, self.team)

        assert data["$pageview"]["2012-01-13"].value == 0
        assert data["$pageview"]["2012-01-14"].value == 2
        assert data["$pageview"]["2012-01-14"].label == "14-Jan-2012"
        assert data["$pageview"]["2012-01-15"].value == 0

        with freeze_time("2012-01-15T04:01:34.000Z"):
            people = get_people_from_url_ok(
                self.client, data["$pageview"]["2012-01-14"].person_url)

        assert sorted([p["id"] for p in people]) == sorted(
            [str(created_people["1"].uuid),
             str(created_people["2"].uuid)])
Пример #22
0
    def test_strict_breakdown_events_with_multiple_properties(self):

        filters = {
            "events": [{"id": "sign up", "order": 0}, {"id": "play movie", "order": 1}],
            "insight": INSIGHT_FUNNELS,
            "date_from": "2020-01-01",
            "date_to": "2020-01-08",
            "funnel_window_days": 7,
            "breakdown_type": "event",
            "breakdown": "$browser",
        }

        filter = Filter(data=filters)
        funnel = ClickhouseFunnelStrict(filter, self.team)

        people = journeys_for(
            {
                "person1": [
                    {"event": "sign up", "timestamp": datetime(2020, 1, 1, 12), "properties": {"$browser": "Chrome"}},
                    {"event": "blah", "timestamp": datetime(2020, 1, 1, 13), "properties": {"$browser": "Chrome"}},
                    {
                        "event": "play movie",
                        "timestamp": datetime(2020, 1, 1, 14),
                        "properties": {"$browser": "Chrome"},
                    },
                ],
                "person2": [
                    {"event": "sign up", "timestamp": datetime(2020, 1, 2, 13), "properties": {"$browser": "Safari"}},
                    {
                        "event": "play movie",
                        "timestamp": datetime(2020, 1, 2, 14),
                        "properties": {"$browser": "Safari"},
                    },
                ],
            },
            self.team,
        )

        result = funnel.run()
        assert_funnel_results_equal(
            result[0],
            [
                {
                    "action_id": "sign up",
                    "name": "sign up",
                    "custom_name": None,
                    "order": 0,
                    "people": [],
                    "count": 1,
                    "type": "events",
                    "average_conversion_time": None,
                    "median_conversion_time": None,
                    "breakdown": ["Chrome"],
                    "breakdown_value": ["Chrome"],
                },
                {
                    "action_id": "play movie",
                    "name": "play movie",
                    "custom_name": None,
                    "order": 1,
                    "people": [],
                    "count": 0,
                    "type": "events",
                    "average_conversion_time": None,
                    "median_conversion_time": None,
                    "breakdown": ["Chrome"],
                    "breakdown_value": ["Chrome"],
                },
            ],
        )
        self.assertCountEqual(self._get_actor_ids_at_step(filter, 1, ["Chrome"]), [people["person1"].uuid])
        self.assertCountEqual(self._get_actor_ids_at_step(filter, 2, ["Chrome"]), [])

        assert_funnel_results_equal(
            result[1],
            [
                {
                    "action_id": "sign up",
                    "name": "sign up",
                    "custom_name": None,
                    "order": 0,
                    "people": [],
                    "count": 1,
                    "type": "events",
                    "average_conversion_time": None,
                    "median_conversion_time": None,
                    "breakdown": ["Safari"],
                    "breakdown_value": ["Safari"],
                },
                {
                    "action_id": "play movie",
                    "name": "play movie",
                    "custom_name": None,
                    "order": 1,
                    "people": [],
                    "count": 1,
                    "type": "events",
                    "average_conversion_time": 3600,
                    "median_conversion_time": 3600,
                    "breakdown": ["Safari"],
                    "breakdown_value": ["Safari"],
                },
            ],
        )
        self.assertCountEqual(self._get_actor_ids_at_step(filter, 1, ["Safari"]), [people["person2"].uuid])
        self.assertCountEqual(self._get_actor_ids_at_step(filter, 2, ["Safari"]), [people["person2"].uuid])
    def test_secondary_metric_results_for_multiple_variants(self):
        journeys_for(
            {
                # trend metric first
                "person1_2_trend": [
                    {
                        "event": "$pageview_trend",
                        "timestamp": "2020-01-02",
                        "properties": {"$feature/a-b-test": "test_2"},
                    },
                ],
                "person1_1_trend": [
                    {
                        "event": "$pageview_trend",
                        "timestamp": "2020-01-02",
                        "properties": {"$feature/a-b-test": "test_1"},
                    },
                ],
                "person2_1_trend": [
                    {
                        "event": "$pageview_trend",
                        "timestamp": "2020-01-02",
                        "properties": {"$feature/a-b-test": "test_1"},
                    },
                ],
                "person2_trend": [
                    {
                        "event": "$pageview_trend",
                        "timestamp": "2020-01-03",
                        "properties": {"$feature/a-b-test": "control"},
                    },
                ],
                "person3_trend": [
                    {
                        "event": "$pageview_trend",
                        "timestamp": "2020-01-04",
                        "properties": {"$feature/a-b-test": "control"},
                    },
                ],
                "person4_trend": [
                    {
                        "event": "$pageview_trend",
                        "timestamp": "2020-01-04",
                        "properties": {"$feature/a-b-test": "control"},
                    },
                ],
                # doesn't have feature set
                "person_out_of_control": [{"event": "$pageview_trend", "timestamp": "2020-01-03",},],
                "person_out_of_end_date": [
                    {
                        "event": "$pageview_trend",
                        "timestamp": "2020-08-03",
                        "properties": {"$feature/a-b-test": "control"},
                    },
                ],
                # funnel metric second
                "person1_2": [
                    {"event": "$pageview", "timestamp": "2020-01-02", "properties": {"$feature/a-b-test": "test_2"},},
                    {"event": "$pageleave", "timestamp": "2020-01-04", "properties": {"$feature/a-b-test": "test_2"},},
                ],
                "person1_1": [
                    {"event": "$pageview", "timestamp": "2020-01-02", "properties": {"$feature/a-b-test": "test_1"},},
                    {"event": "$pageleave", "timestamp": "2020-01-04", "properties": {"$feature/a-b-test": "test_1"},},
                ],
                "person2_1": [
                    {"event": "$pageview", "timestamp": "2020-01-02", "properties": {"$feature/a-b-test": "test_1"},},
                    {"event": "$pageleave", "timestamp": "2020-01-04", "properties": {"$feature/a-b-test": "test_1"},},
                ],
                "person1": [
                    {"event": "$pageview", "timestamp": "2020-01-02", "properties": {"$feature/a-b-test": "test"},},
                    {"event": "$pageleave", "timestamp": "2020-01-04", "properties": {"$feature/a-b-test": "test"},},
                ],
                "person2": [
                    {"event": "$pageview", "timestamp": "2020-01-03", "properties": {"$feature/a-b-test": "control"}},
                    {"event": "$pageleave", "timestamp": "2020-01-05", "properties": {"$feature/a-b-test": "control"}},
                ],
                "person3": [
                    {"event": "$pageview", "timestamp": "2020-01-04", "properties": {"$feature/a-b-test": "control"}},
                    {"event": "$pageleave", "timestamp": "2020-01-05", "properties": {"$feature/a-b-test": "control"}},
                ],
                # doesn't have feature set
                "person_out_of_control": [
                    {"event": "$pageview", "timestamp": "2020-01-03",},
                    {"event": "$pageleave", "timestamp": "2020-01-05",},
                ],
                "person_out_of_end_date": [
                    {"event": "$pageview", "timestamp": "2020-08-03", "properties": {"$feature/a-b-test": "control"}},
                    {"event": "$pageleave", "timestamp": "2020-08-05", "properties": {"$feature/a-b-test": "control"}},
                ],
                # non-converters with FF
                "person4": [
                    {"event": "$pageview", "timestamp": "2020-01-03", "properties": {"$feature/a-b-test": "test"},},
                ],
                "person5": [
                    {"event": "$pageview", "timestamp": "2020-01-04", "properties": {"$feature/a-b-test": "test"},},
                ],
                "person6_1": [
                    {"event": "$pageview", "timestamp": "2020-01-02", "properties": {"$feature/a-b-test": "test_1"},},
                ],
            },
            self.team,
        )

        ff_key = "a-b-test"
        # generates the FF which should result in the above events^
        creation_response = self.client.post(
            f"/api/projects/{self.team.id}/experiments/",
            {
                "name": "Test Experiment",
                "description": "",
                "start_date": "2020-01-01T00:00",
                "end_date": "2020-01-06T00:00",
                "feature_flag_key": ff_key,
                "parameters": {
                    "feature_flag_variants": [
                        {"key": "control", "name": "Control Group", "rollout_percentage": 25},
                        {"key": "test_1", "name": "Test Variant 1", "rollout_percentage": 25},
                        {"key": "test_2", "name": "Test Variant 2", "rollout_percentage": 25},
                        {"key": "test", "name": "Test Variant 3", "rollout_percentage": 25},
                    ],
                },
                "secondary_metrics": [
                    {
                        "name": "secondary metric",
                        "filters": {"insight": "trends", "events": [{"order": 0, "id": "$pageview_trend"}]},
                    },
                    {
                        "name": "funnel metric",
                        "filters": {
                            "insight": "funnels",
                            "events": [{"order": 0, "id": "$pageview"}, {"order": 1, "id": "$pageleave"}],
                        },
                    },
                ],
                # target metric insignificant since we're testing secondaries right now
                "filters": {"insight": "trends", "events": [{"order": 0, "id": "whatever"}],},
            },
        )

        id = creation_response.json()["id"]

        response = self.client.get(f"/api/projects/{self.team.id}/experiments/{id}/secondary_results?id=0")
        self.assertEqual(200, response.status_code)

        response_data = response.json()

        # trend missing 'test' variant, so it's not in the results
        self.assertEqual(len(response_data["result"].items()), 3)

        self.assertEqual(response_data["result"]["control"], 3)
        self.assertEqual(response_data["result"]["test_1"], 2)
        self.assertEqual(response_data["result"]["test_2"], 1)

        response = self.client.get(f"/api/projects/{self.team.id}/experiments/{id}/secondary_results?id=1")
        self.assertEqual(200, response.status_code)

        response_data = response.json()

        # funnel not missing 'test' variant, so it's in the results
        self.assertEqual(len(response_data["result"].items()), 4)

        self.assertAlmostEqual(response_data["result"]["control"], 1)
        self.assertAlmostEqual(response_data["result"]["test"], round(1 / 3, 3))
        self.assertAlmostEqual(response_data["result"]["test_1"], round(2 / 3, 3))
        self.assertAlmostEqual(response_data["result"]["test_2"], 1)
    def test_basic_secondary_metric_results(self):
        journeys_for(
            {
                # For a trend pageview metric
                "person1": [
                    {"event": "$pageview", "timestamp": "2020-01-02", "properties": {"$feature/a-b-test": "test"},},
                ],
                "person2": [
                    {"event": "$pageview", "timestamp": "2020-01-03", "properties": {"$feature/a-b-test": "control"}},
                    {"event": "$pageview", "timestamp": "2020-01-03", "properties": {"$feature/a-b-test": "control"}},
                ],
                "person3": [
                    {"event": "$pageview", "timestamp": "2020-01-04", "properties": {"$feature/a-b-test": "control"}},
                ],
                # doesn't have feature set
                "person_out_of_control": [{"event": "$pageview", "timestamp": "2020-01-03",},],
                "person_out_of_end_date": [
                    {"event": "$pageview", "timestamp": "2020-08-03", "properties": {"$feature/a-b-test": "control"}},
                ],
                # for a funnel conversion metric
                "person1_funnel": [
                    {
                        "event": "$pageview_funnel",
                        "timestamp": "2020-01-02",
                        "properties": {"$feature/a-b-test": "test"},
                    },
                    {
                        "event": "$pageleave_funnel",
                        "timestamp": "2020-01-04",
                        "properties": {"$feature/a-b-test": "test"},
                    },
                ],
                "person2_funnel": [
                    {
                        "event": "$pageview_funnel",
                        "timestamp": "2020-01-03",
                        "properties": {"$feature/a-b-test": "control"},
                    },
                    {
                        "event": "$pageleave_funnel",
                        "timestamp": "2020-01-05",
                        "properties": {"$feature/a-b-test": "control"},
                    },
                ],
                "person3_funnel": [
                    {
                        "event": "$pageview_funnel",
                        "timestamp": "2020-01-04",
                        "properties": {"$feature/a-b-test": "control"},
                    },
                    {
                        "event": "$pageleave_funnel",
                        "timestamp": "2020-01-05",
                        "properties": {"$feature/a-b-test": "control"},
                    },
                ],
                # doesn't have feature set
                "person_out_of_control_funnel": [
                    {"event": "$pageview_funnel", "timestamp": "2020-01-03",},
                    {"event": "$pageleave_funnel", "timestamp": "2020-01-05",},
                ],
                "person_out_of_end_date_funnel": [
                    {
                        "event": "$pageview_funnel",
                        "timestamp": "2020-08-03",
                        "properties": {"$feature/a-b-test": "control"},
                    },
                    {
                        "event": "$pageleave_funnel",
                        "timestamp": "2020-08-05",
                        "properties": {"$feature/a-b-test": "control"},
                    },
                ],
                # non-converters with FF
                "person4_funnel": [
                    {
                        "event": "$pageview_funnel",
                        "timestamp": "2020-01-03",
                        "properties": {"$feature/a-b-test": "test"},
                    },
                ],
                "person5_funnel": [
                    {
                        "event": "$pageview_funnel",
                        "timestamp": "2020-01-04",
                        "properties": {"$feature/a-b-test": "test"},
                    },
                ],
            },
            self.team,
        )

        ff_key = "a-b-test"
        # generates the FF which should result in the above events^
        creation_response = self.client.post(
            f"/api/projects/{self.team.id}/experiments/",
            {
                "name": "Test Experiment",
                "description": "",
                "start_date": "2020-01-01T00:00",
                "end_date": "2020-01-06T00:00",
                "feature_flag_key": ff_key,
                "parameters": {},
                "secondary_metrics": [
                    {
                        "name": "trends whatever",
                        "filters": {
                            "insight": "trends",
                            "events": [{"order": 0, "id": "$pageview"}],
                            "properties": [
                                {
                                    "key": "$geoip_country_name",
                                    "type": "person",
                                    "value": ["france"],
                                    "operator": "exact",
                                }
                                # properties superceded by FF breakdown
                            ],
                        },
                    },
                    {
                        "name": "funnels whatever",
                        "filters": {
                            "insight": "funnels",
                            "events": [{"order": 0, "id": "$pageview_funnel"}, {"order": 1, "id": "$pageleave_funnel"}],
                            "properties": [
                                {
                                    "key": "$geoip_country_name",
                                    "type": "person",
                                    "value": ["france"],
                                    "operator": "exact",
                                }
                                # properties superceded by FF breakdown
                            ],
                        },
                    },
                ],
                # target metric insignificant since we're testing secondaries right now
                "filters": {"insight": "trends", "events": [{"order": 0, "id": "whatever"}],},
            },
        )

        id = creation_response.json()["id"]

        response = self.client.get(f"/api/projects/{self.team.id}/experiments/{id}/secondary_results?id=0")
        self.assertEqual(200, response.status_code)

        response_data = response.json()

        self.assertEqual(len(response_data["result"].items()), 2)

        self.assertEqual(response_data["result"]["control"], 3)
        self.assertEqual(response_data["result"]["test"], 1)

        response = self.client.get(f"/api/projects/{self.team.id}/experiments/{id}/secondary_results?id=1")
        self.assertEqual(200, response.status_code)

        response_data = response.json()

        self.assertEqual(len(response_data["result"].items()), 2)

        self.assertAlmostEqual(response_data["result"]["control"], 1)
        self.assertEqual(response_data["result"]["test"], round(1 / 3, 3))
Пример #25
0
    def test_filter_out_team_members_with_grouped_properties(self):
        _create_person(
            team_id=self.team.pk,
            distinct_ids=["person1"],
            properties={"email": "*****@*****.**", "name": "test", "age": "10"},
        )
        _create_person(
            team_id=self.team.pk,
            distinct_ids=["person2"],
            properties={"email": "*****@*****.**", "name": "test", "age": "20"},
        )
        _create_person(
            team_id=self.team.pk,
            distinct_ids=["person3"],
            properties={"email": "*****@*****.**", "name": "test", "age": "30"},
        )
        _create_person(
            team_id=self.team.pk,
            distinct_ids=["person4"],
            properties={"email": "*****@*****.**", "name": "test", "age": "40"},
        )
        _create_person(
            team_id=self.team.pk,
            distinct_ids=["person5"],
            properties={"email": "*****@*****.**", "name": "test", "age": "50"},
        )

        self.team.test_account_filters = [
            {"key": "email", "value": "@posthog.com", "operator": "not_icontains", "type": "person"}
        ]
        self.team.save()

        journeys_for(
            team=self.team,
            create_people=False,
            events_by_person={
                "person1": [
                    {"event": "$pageview", "properties": {"key": "val", "$browser": "Safari", "$browser_version": 14},}
                ],
                "person2": [
                    {"event": "$pageview", "properties": {"key": "val", "$browser": "Safari", "$browser_version": 14},}
                ],
                "person3": [
                    {"event": "$pageview", "properties": {"key": "val", "$browser": "Safari", "$browser_version": 14},}
                ],
                "person4": [
                    {"event": "$pageview", "properties": {"key": "val", "$browser": "Safari", "$browser_version": 14},}
                ],
                "person5": [
                    {"event": "$pageview", "properties": {"key": "val", "$browser": "Safari", "$browser_version": 14},}
                ],
            },
        )

        filter = Filter(
            data={
                FILTER_TEST_ACCOUNTS: True,
                "events": [{"id": "$pageview"}],
                "properties": {
                    "type": "OR",
                    "values": [
                        {
                            "type": "OR",
                            "values": [
                                {"key": "age", "value": "10", "operator": "exact", "type": "person"},
                                {"key": "age", "value": "20", "operator": "exact", "type": "person"},
                                # choose person 1 and 2
                            ],
                        },
                        {
                            "type": "AND",
                            "values": [
                                {"key": "$browser", "value": "Safari", "operator": "exact", "type": "event"},
                                {"key": "age", "value": "50", "operator": "exact", "type": "person"},
                                # choose person 5
                            ],
                        },
                    ],
                },
            },
            team=self.team,
        )
        events = _filter_events(filter=filter, team=self.team)
        # test account filters delete person 5, so only 1 and 2 remain
        self.assertEqual(len(events), 2)
    def test_event_correlation_endpoint_does_not_include_funnel_steps(self):
        with freeze_time("2020-01-01"):
            self.client.force_login(self.user)

            # Add Person1 with only the funnel steps involved

            events = {
                "Person 1": [
                    {
                        "event": "signup",
                        "timestamp": datetime(2020, 1, 1)
                    },
                    {
                        "event": "some waypoint",
                        "timestamp": datetime(2020, 1, 2)
                    },
                    {
                        "event": "",
                        "timestamp": datetime(2020, 1, 3)
                    },
                ],
                # We need atleast 1 success and failure to return a result
                "Person 2": [
                    {
                        "event": "signup",
                        "timestamp": datetime(2020, 1, 1)
                    },
                    {
                        "event": "some waypoint",
                        "timestamp": datetime(2020, 1, 2)
                    },
                    {
                        "event": "view insights",
                        "timestamp": datetime(2020, 1, 3)
                    },
                ],
            }
            # '' is a weird event name to have, but if it exists, our duty to report it

            journeys_for(events_by_person=events, team=self.team)

            # We need to make sure we clear the cache other tests that have run
            # done interfere with this test
            cache.clear()

            odds = get_funnel_correlation_ok(
                client=self.client,
                team_id=self.team.pk,
                request=FunnelCorrelationRequest(
                    events=json.dumps([
                        EventPattern(id="signup"),
                        EventPattern(id="some waypoint"),
                        EventPattern(id="view insights")
                    ]),
                    date_to="2020-04-04",
                ),
            )

        assert odds == {
            "is_cached": False,
            "last_refresh": "2020-01-01T00:00:00Z",
            "result": {
                "events": [{
                    "correlation_type": "failure",
                    "event": {
                        "event": "",
                        "elements": [],
                        "properties": {}
                    },
                    "failure_count": 1,
                    "odds_ratio": 1 / 4,
                    "success_count": 0,
                    "success_people_url": ANY,
                    "failure_people_url": ANY,
                }],
                "skewed":
                False,
            },
        }
    def test_events_correlation_endpoint_provides_people_drill_down_urls(self):
        """
        Here we are setting up three users, and looking to retrieve one
        correlation for watched video, with a url we can use to retrieve people
        that successfully completed the funnel AND watched the video, and
        another for people that did not complete the funnel but also watched the
        video.
        """

        with freeze_time("2020-01-01"):
            self.client.force_login(self.user)

            events = {
                "Person 1": [
                    # Failure / watched
                    {
                        "event": "signup",
                        "timestamp": datetime(2020, 1, 1)
                    },
                    {
                        "event": "watched video",
                        "timestamp": datetime(2020, 1, 2)
                    },
                ],
                "Person 2": [
                    # Success / watched
                    {
                        "event": "signup",
                        "timestamp": datetime(2020, 1, 1)
                    },
                    {
                        "event": "watched video",
                        "timestamp": datetime(2020, 1, 2)
                    },
                    {
                        "event": "view insights",
                        "timestamp": datetime(2020, 1, 3)
                    },
                ],
                "Person 3": [
                    # Success / did not watched. We don't expect to retrieve
                    # this one as part of the
                    {
                        "event": "signup",
                        "timestamp": datetime(2020, 1, 1)
                    },
                    {
                        "event": "view insights",
                        "timestamp": datetime(2020, 1, 3)
                    },
                ],
            }

            journeys_for(events_by_person=events, team=self.team)

            odds = get_funnel_correlation_ok(
                client=self.client,
                team_id=self.team.pk,
                request=FunnelCorrelationRequest(
                    events=json.dumps([
                        EventPattern(id="signup"),
                        EventPattern(id="view insights")
                    ]),
                    date_to="2020-04-04",
                ),
            )

            assert odds["result"]["events"][0]["event"][
                "event"] == "watched video"
            watched_video_correlation = odds["result"]["events"][0]

            assert get_people_for_correlation_ok(
                client=self.client, correlation=watched_video_correlation) == {
                    "success": ["Person 2"],
                    "failure": ["Person 1"],
                }
    def test_event_correlation_endpoint_picks_up_events_for_odds_ratios(self):
        with freeze_time("2020-01-01"):
            self.client.force_login(self.user)

            # Add in two people:
            #
            # Person 1 - a single signup event
            # Person 2 - a signup event and a view insights event
            #
            # Both of them have a "watched video" event
            #
            # We then create Person 3, one successful, the other
            # not. Both have not watched the video.
            #
            # So our contingency table for "watched video" should be
            #
            # |                  | success  | failure  | total    |
            # | ---------------- | -------- | -------- | -------- |
            # | watched          | 1        | 1        | 2        |
            # | did not watched  | 1        | 0        | 1        |
            # | total            | 2        | 1        | 3        |
            #
            # For Calculating Odds Ratio, we add a prior count of 1 to everything
            #
            # So our odds ratio should be
            #  (success + prior / failure + prior) * (failure_total - failure + prior / success_total - success + prior)
            # = ( 1 + 1 / 1 + 1) * ( 1 - 1 + 1 / 2 - 1 + 1)
            # = 1 / 2

            events = {
                "Person 1": [
                    #  Failure / watched
                    {
                        "event": "signup",
                        "timestamp": datetime(2020, 1, 1)
                    },
                    {
                        "event": "watched video",
                        "timestamp": datetime(2020, 1, 2)
                    },
                ],
                "Person 2": [
                    #  Success / watched
                    {
                        "event": "signup",
                        "timestamp": datetime(2020, 1, 1)
                    },
                    {
                        "event": "watched video",
                        "timestamp": datetime(2020, 1, 2)
                    },
                    {
                        "event": "view insights",
                        "timestamp": datetime(2020, 1, 3)
                    },
                ],
                "Person 3": [
                    # Success / did not watched
                    {
                        "event": "signup",
                        "timestamp": datetime(2020, 1, 1)
                    },
                    {
                        "event": "view insights",
                        "timestamp": datetime(2020, 1, 3)
                    },
                ],
            }

            journeys_for(events_by_person=events, team=self.team)

            odds = get_funnel_correlation_ok(
                client=self.client,
                team_id=self.team.pk,
                request=FunnelCorrelationRequest(
                    events=json.dumps([
                        EventPattern(id="signup"),
                        EventPattern(id="view insights")
                    ]),
                    date_to="2020-04-04",
                ),
            )

        assert odds == {
            "is_cached": False,
            "last_refresh": "2020-01-01T00:00:00Z",
            "result": {
                "events": [
                    {
                        "event": {
                            "event": "watched video",
                            "elements": [],
                            "properties": {}
                        },
                        "failure_count": 1,
                        "success_count": 1,
                        "success_people_url": ANY,
                        "failure_people_url": ANY,
                        "odds_ratio": 1 / 2,
                        "correlation_type": "failure",
                    },
                ],
                "skewed":
                False,
            },
        }
    def test_events_with_properties_correlation_endpoint_provides_people_drill_down_urls(
            self):
        with freeze_time("2020-01-01"):
            self.client.force_login(self.user)

            events = {
                "Person 1": [
                    # Failure / watched
                    {
                        "event": "signup",
                        "timestamp": datetime(2020, 1, 1)
                    },
                    {
                        "event": "watched video",
                        "properties": {
                            "$browser": "1"
                        },
                        "timestamp": datetime(2020, 1, 2)
                    },
                ],
                "Person 2": [
                    # Success / watched
                    {
                        "event": "signup",
                        "timestamp": datetime(2020, 1, 1)
                    },
                    {
                        "event": "watched video",
                        "properties": {
                            "$browser": "1"
                        },
                        "timestamp": datetime(2020, 1, 2)
                    },
                    {
                        "event": "view insights",
                        "timestamp": datetime(2020, 1, 3)
                    },
                ],
                "Person 3": [
                    # Success / watched. We need to have three event instances
                    # for this test otherwise the endpoint doesn't return results
                    {
                        "event": "signup",
                        "timestamp": datetime(2020, 1, 1)
                    },
                    {
                        "event": "watched video",
                        "properties": {
                            "$browser": "1"
                        },
                        "timestamp": datetime(2020, 1, 2)
                    },
                    {
                        "event": "view insights",
                        "timestamp": datetime(2020, 1, 3)
                    },
                ],
                "Person 4": [
                    # Success / didn't watch. Want to use this user to verify
                    # that we don't pull in unrelated users erroneously
                    {
                        "event": "signup",
                        "timestamp": datetime(2020, 1, 1)
                    },
                    {
                        "event": "view insights",
                        "timestamp": datetime(2020, 1, 3)
                    },
                ],
            }

            journeys_for(events_by_person=events, team=self.team)

            odds = get_funnel_correlation_ok(
                client=self.client,
                team_id=self.team.pk,
                request=FunnelCorrelationRequest(
                    funnel_correlation_type=FunnelCorrelationType.
                    EVENT_WITH_PROPERTIES,
                    funnel_correlation_event_names=json.dumps(
                        ["watched video"]),
                    events=json.dumps([
                        EventPattern(id="signup"),
                        EventPattern(id="view insights")
                    ]),
                    date_to="2020-04-04",
                ),
            )

            assert odds["result"]["events"][0]["event"][
                "event"] == "watched video::$browser::1"
            watched_video_correlation = odds["result"]["events"][0]

            assert get_people_for_correlation_ok(
                client=self.client, correlation=watched_video_correlation) == {
                    "success": ["Person 2", "Person 3"],
                    "failure": ["Person 1"],
                }
    def test_properties_correlation_endpoint_provides_people_drill_down_urls(
            self):
        """
        Here we are setting up three users, two with a specified property but
        differing values, and one with this property absent. We expect to be
        able to use the correlation people drill down urls to retrieve the
        associated people for each.
        """

        with freeze_time("2020-01-01"):
            self.client.force_login(self.user)

            update_or_create_person(distinct_ids=["Person 1"],
                                    team_id=self.team.pk,
                                    properties={"$browser": "1"})
            update_or_create_person(distinct_ids=["Person 2"],
                                    team_id=self.team.pk,
                                    properties={"$browser": "1"})

            events = {
                "Person 1": [
                    # Failure / $browser::1
                    {
                        "event": "signup",
                        "timestamp": datetime(2020, 1, 1)
                    },
                ],
                "Person 2": [
                    # Success / $browser::1
                    {
                        "event": "signup",
                        "timestamp": datetime(2020, 1, 1)
                    },
                    {
                        "event": "view insights",
                        "timestamp": datetime(2020, 1, 3)
                    },
                ],
                "Person 3": [
                    # Success / $browser not set
                    {
                        "event": "signup",
                        "timestamp": datetime(2020, 1, 1)
                    },
                    {
                        "event": "view insights",
                        "timestamp": datetime(2020, 1, 3)
                    },
                ],
            }

            journeys_for(events_by_person=events, team=self.team)

            odds = get_funnel_correlation_ok(
                client=self.client,
                team_id=self.team.pk,
                request=FunnelCorrelationRequest(
                    events=json.dumps([
                        EventPattern(id="signup"),
                        EventPattern(id="view insights")
                    ]),
                    date_to="2020-04-04",
                    funnel_correlation_type=FunnelCorrelationType.PROPERTIES,
                    funnel_correlation_names=json.dumps(["$browser"]),
                ),
            )

            (browser_correlation, ) = [
                correlation for correlation in odds["result"]["events"]
                if correlation["event"]["event"] == "$browser::1"
            ]

            (notset_correlation, ) = [
                correlation for correlation in odds["result"]["events"]
                if correlation["event"]["event"] == "$browser::"
            ]

            assert get_people_for_correlation_ok(
                client=self.client, correlation=browser_correlation) == {
                    "success": ["Person 2"],
                    "failure": ["Person 1"],
                }

            assert get_people_for_correlation_ok(
                client=self.client, correlation=notset_correlation) == {
                    "success": ["Person 3"],
                    "failure": [],
                }