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"]}, ]
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": []}, ]
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"]}, ], }, ]
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])
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": []}, ]
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"])
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": []}, ], }, ]
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, }, }, )
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]}, ], )
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"])
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
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, )
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)
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"])
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"])
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)
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)
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]}, ], )
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)])
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,], )
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)])
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))
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": [], }