def test_first_step_breakdowns_with_multi_property_breakdown(self): person1, person2 = self._create_browser_breakdown_events() filter = Filter( data={ "insight": INSIGHT_FUNNELS, "date_from": "2020-01-01", "date_to": "2020-01-08", "interval": "day", "funnel_window_days": 7, "funnel_step": 1, "events": [{"id": "sign up", "order": 0}, {"id": "play movie", "order": 1}, {"id": "buy", "order": 2}], "breakdown_type": "event", "breakdown": ["$browser", "$browser_version"], } ) _, results = ClickhouseFunnelActors(filter, self.team).get_actors() self.assertCountEqual([val["id"] for val in results], [person1.uuid, person2.uuid]) _, results = ClickhouseFunnelActors( filter.with_data({"funnel_step_breakdown": "Chrome"}), self.team ).get_actors() self.assertCountEqual([val["id"] for val in results], [person1.uuid]) _, results = ClickhouseFunnelActors( filter.with_data({"funnel_step_breakdown": "Safari"}), self.team ).get_actors() self.assertCountEqual([val["id"] for val in results], [person2.uuid])
def test_steps_with_custom_steps_parameter_are_equivalent_to_funnel_step(self): self._create_sample_data_multiple_dropoffs() data = { "insight": INSIGHT_FUNNELS, "interval": "day", "date_from": "2021-05-01 00:00:00", "date_to": "2021-05-07 00:00:00", "funnel_window_days": 7, "events": [ {"id": "step one", "order": 0}, {"id": "step two", "order": 1}, {"id": "step three", "order": 2}, ], } base_filter = Filter(data=data) parameters = [ # funnel_step, custom_steps, expected_results (1, [1, 2, 3], 35), (2, [2, 3], 15), (3, [3], 5), (-2, [1], 20), (-3, [2], 10), ] for funnel_step, custom_steps, expected_count in parameters: filter = base_filter.with_data({"funnel_step": funnel_step}) _, results = ClickhouseFunnelActors(filter, self.team).get_actors() new_filter = base_filter.with_data({"funnel_custom_steps": custom_steps}) _, new_results = ClickhouseFunnelActors(new_filter, self.team).get_actors() self.assertEqual(new_results, results) self.assertEqual(len(results), expected_count)
def test_first_step_breakdowns(self): person1, person2 = self._create_browser_breakdown_events() filter = Filter( data={ "insight": INSIGHT_FUNNELS, "date_from": "2020-01-01", "date_to": "2020-01-08", "interval": "day", "funnel_window_days": 7, "funnel_step": 1, "events": [ { "id": "sign up", "order": 0 }, { "id": "play movie", "order": 1 }, { "id": "buy", "order": 2 }, ], "breakdown_type": "event", "breakdown": "$browser", }) results = ClickhouseFunnelPersons(filter, self.team)._exec_query() self.assertCountEqual([val[0] for val in results], [person1.uuid, person2.uuid]) results = ClickhouseFunnelPersons( filter.with_data({"funnel_step_breakdown": "Chrome"}), self.team)._exec_query() self.assertCountEqual([val[0] for val in results], [person1.uuid]) results = ClickhouseFunnelPersons( filter.with_data({"funnel_step_breakdown": "Safari"}), self.team)._exec_query() self.assertCountEqual([val[0] for val in results], [person2.uuid]) results = ClickhouseFunnelPersons( filter.with_data({"funnel_step_breakdown": "Safari, Chrome"}), self.team)._exec_query() self.assertCountEqual([val[0] for val in results], [person2.uuid, person1.uuid])
def calculate_funnel_correlation_persons( self, request: request.Request ) -> Dict[str, Tuple[list, Optional[str], Optional[str]]]: if request.user.is_anonymous or not self.team: return {"result": ([], None, None)} filter = Filter(request=request, data={"insight": INSIGHT_FUNNELS}, team=self.team) if not filter.correlation_person_limit: filter = filter.with_data({FUNNEL_CORRELATION_PERSON_LIMIT: 100}) base_uri = request.build_absolute_uri("/") actors, serialized_actors = FunnelCorrelationActors( filter=filter, team=self.team, base_uri=base_uri ).get_actors() _should_paginate = should_paginate(actors, filter.correlation_person_limit) next_url = ( format_query_params_absolute_url( request, filter.correlation_person_offset + filter.correlation_person_limit, offset_alias=FUNNEL_CORRELATION_PERSON_OFFSET, limit_alias=FUNNEL_CORRELATION_PERSON_LIMIT, ) if _should_paginate else None ) initial_url = format_query_params_absolute_url(request, 0) # cached_function expects a dict with the key result return {"result": (serialized_actors, next_url, initial_url)}
def test_steps_with_custom_steps_parameter_where_funnel_step_equivalence_isnt_possible(self): self._create_sample_data_multiple_dropoffs() data = { "insight": INSIGHT_FUNNELS, "interval": "day", "date_from": "2021-05-01 00:00:00", "date_to": "2021-05-07 00:00:00", "funnel_window_days": 7, "events": [ {"id": "step one", "order": 0}, {"id": "step two", "order": 1}, {"id": "step three", "order": 2}, ], } base_filter = Filter(data=data) parameters = [ # custom_steps, expected_results ([1, 2], 30), ([1, 3], 25), ([3, 1], 25), ([1, 3, 3, 1], 25), ] for custom_steps, expected_count in parameters: new_filter = base_filter.with_data({"funnel_custom_steps": custom_steps}) _, new_results = ClickhouseFunnelActors(new_filter, self.team).get_actors() self.assertEqual(len(new_results), expected_count)
def test_first_step_breakdown_person(self): person1, person2 = self._create_browser_breakdown_events() filter = Filter( data={ "insight": INSIGHT_FUNNELS, "date_from": "2020-01-01", "date_to": "2020-01-08", "interval": "day", "funnel_window_days": 7, "funnel_step": 1, "events": [{"id": "sign up", "order": 0}, {"id": "play movie", "order": 1}, {"id": "buy", "order": 2}], "breakdown_type": "person", "breakdown": "$country", } ) _, results = ClickhouseFunnelActors(filter, self.team).get_actors() self.assertCountEqual([val["id"] for val in results], [person1.uuid, person2.uuid]) _, results = ClickhouseFunnelActors(filter.with_data({"funnel_step_breakdown": "EE"}), self.team).get_actors() self.assertCountEqual([val["id"] for val in results], [person2.uuid]) # Check custom_steps give same answers for breakdowns _, custom_step_results = ClickhouseFunnelActors( filter.with_data({"funnel_step_breakdown": "EE", "funnel_custom_steps": [1, 2, 3]}), self.team ).get_actors() self.assertEqual(results, custom_step_results) _, results = ClickhouseFunnelActors(filter.with_data({"funnel_step_breakdown": "PL"}), self.team).get_actors() self.assertCountEqual([val["id"] for val in results], [person1.uuid]) # Check custom_steps give same answers for breakdowns _, custom_step_results = ClickhouseFunnelActors( filter.with_data({"funnel_step_breakdown": "PL", "funnel_custom_steps": [1, 2, 3]}), self.team ).get_actors() self.assertEqual(results, custom_step_results)
def calculate_sessions(self, events: QuerySet, filter: Filter, team: Team) -> List[Dict[str, Any]]: all_sessions, sessions_sql_params = self.build_all_sessions_query(events) if filter.session == SESSION_AVG: if not filter.date_from: filter = filter.with_data( { "date_from": Event.objects.filter(team=team) .order_by("timestamp")[0] .timestamp.replace(hour=0, minute=0, second=0, microsecond=0) .isoformat(), } ) return self._session_avg(all_sessions, sessions_sql_params, filter) else: # SESSION_DIST return self._session_dist(all_sessions, sessions_sql_params)
def calculate_funnel_persons( self, request: request.Request ) -> Dict[str, Tuple[list, Optional[str], Optional[str]]]: if request.user.is_anonymous or not self.team: return {"result": ([], None, None)} filter = Filter(request=request, data={"insight": INSIGHT_FUNNELS}, team=self.team) if not filter.limit: filter = filter.with_data({LIMIT: 100}) funnel_actor_class = get_funnel_actor_class(filter) actors, serialized_actors = funnel_actor_class(filter, self.team).get_actors() _should_paginate = should_paginate(actors, filter.limit) next_url = format_query_params_absolute_url(request, filter.offset + filter.limit) if _should_paginate else None initial_url = format_query_params_absolute_url(request, 0) # cached_function expects a dict with the key result return {"result": (serialized_actors, next_url, initial_url)}
def test_update_cache_item_calls_right_funnel_class_clickhouse( self, funnel_mock: MagicMock, funnel_trends_mock: MagicMock, funnel_time_to_convert_mock: MagicMock, funnel_strict_mock: MagicMock, funnel_unordered_mock: MagicMock, ) -> None: # basic funnel base_filter = Filter( data={ "insight": "FUNNELS", "events": [ { "id": "$pageview", "order": 0, "type": "events" }, { "id": "$pageview", "order": 1, "type": "events" }, ], }) with self.settings(EE_AVAILABLE=True, PRIMARY_DB="clickhouse"): filter = base_filter funnel_mock.return_value.run.return_value = {} update_cache_item( generate_cache_key("{}_{}".format(filter.toJSON(), self.team.pk)), CacheType.FUNNEL, { "filter": filter.toJSON(), "team_id": self.team.pk, }, ) funnel_mock.assert_called_once() # trends funnel filter = base_filter.with_data({"funnel_viz_type": "trends"}) funnel_trends_mock.return_value.run.return_value = {} update_cache_item( generate_cache_key("{}_{}".format(filter.toJSON(), self.team.pk)), CacheType.FUNNEL, { "filter": filter.toJSON(), "team_id": self.team.pk, }, ) funnel_trends_mock.assert_called_once() self.assertEqual( funnel_trends_mock.call_args[1]["funnel_order_class"], funnel_mock) funnel_trends_mock.reset_mock() # trends unordered funnel filter = base_filter.with_data({ "funnel_viz_type": "trends", "funnel_order_type": "unordered" }) funnel_trends_mock.return_value.run.return_value = {} update_cache_item( generate_cache_key("{}_{}".format(filter.toJSON(), self.team.pk)), CacheType.FUNNEL, { "filter": filter.toJSON(), "team_id": self.team.pk, }, ) funnel_trends_mock.assert_called_once() self.assertEqual( funnel_trends_mock.call_args[1]["funnel_order_class"], funnel_unordered_mock) funnel_trends_mock.reset_mock() # time to convert strict funnel filter = base_filter.with_data({ "funnel_viz_type": "time_to_convert", "funnel_order_type": "strict" }) funnel_time_to_convert_mock.return_value.run.return_value = {} update_cache_item( generate_cache_key("{}_{}".format(filter.toJSON(), self.team.pk)), CacheType.FUNNEL, { "filter": filter.toJSON(), "team_id": self.team.pk, }, ) funnel_time_to_convert_mock.assert_called_once() self.assertEqual( funnel_time_to_convert_mock.call_args[1]["funnel_order_class"], funnel_strict_mock) funnel_time_to_convert_mock.reset_mock() # strict funnel filter = base_filter.with_data({"funnel_order_type": "strict"}) funnel_strict_mock.return_value.run.return_value = {} update_cache_item( generate_cache_key("{}_{}".format(filter.toJSON(), self.team.pk)), CacheType.FUNNEL, { "filter": filter.toJSON(), "team_id": self.team.pk, }, ) funnel_strict_mock.assert_called_once()
def test_first_step_breakdowns(self): person1 = _create_person(distinct_ids=["person1"], team_id=self.team.pk) _create_event( team=self.team, event="sign up", distinct_id="person1", properties={ "key": "val", "$browser": "Chrome" }, timestamp="2020-01-01T12:00:00Z", ) _create_event( team=self.team, event="play movie", distinct_id="person1", properties={ "key": "val", "$browser": "Chrome" }, timestamp="2020-01-01T13:00:00Z", ) _create_event( team=self.team, event="buy", distinct_id="person1", properties={ "key": "val", "$browser": "Chrome" }, timestamp="2020-01-01T15:00:00Z", ) person2 = _create_person(distinct_ids=["person2"], team_id=self.team.pk) _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", ) data = { "insight": INSIGHT_FUNNELS, "date_from": "2020-01-01", "date_to": "2020-01-08", "interval": "day", "funnel_window_days": 7, "funnel_step": 1, "events": [ { "id": "sign up", "order": 0 }, { "id": "play movie", "order": 1 }, { "id": "buy", "order": 2 }, ], "breakdown_type": "event", "breakdown": "$browser", } filter = Filter(data=data) results = ClickhouseFunnelPersons(filter, self.team)._exec_query() self.assertCountEqual([val[0] for val in results], [person1.uuid, person2.uuid]) results = ClickhouseFunnelPersons( filter.with_data({"funnel_step_breakdown": "Chrome"}), self.team)._exec_query() print(results) self.assertCountEqual([val[0] for val in results], [person1.uuid]) results = ClickhouseFunnelPersons( filter.with_data({"funnel_step_breakdown": "Safari"}), self.team)._exec_query() self.assertCountEqual([val[0] for val in results], [person2.uuid]) results = ClickhouseFunnelPersons( filter.with_data({"funnel_step_breakdown": "Safari, Chrome"}), self.team)._exec_query() self.assertCountEqual([val[0] for val in results], [person2.uuid, person1.uuid])
def test_basic_funnel_correlation_with_events(self): filters = { "events": [ { "id": "user signed up", "type": "events", "order": 0 }, { "id": "paid", "type": "events", "order": 1 }, ], "insight": INSIGHT_FUNNELS, "date_from": "2020-01-01", "date_to": "2020-01-14", "funnel_correlation_type": "events", } filter = Filter(data=filters) success_target_persons = [] failure_target_persons = [] for i in range(10): person = _create_person(distinct_ids=[f"user_{i}"], team_id=self.team.pk) _create_event( team=self.team, event="user signed up", distinct_id=f"user_{i}", timestamp="2020-01-02T14:00:00Z", ) if i % 2 == 0: _create_event( team=self.team, event="positively_related", distinct_id=f"user_{i}", timestamp="2020-01-03T14:00:00Z", ) success_target_persons.append(str(person.uuid)) _create_event( team=self.team, event="paid", distinct_id=f"user_{i}", timestamp="2020-01-04T14:00:00Z", ) for i in range(10, 20): person = _create_person(distinct_ids=[f"user_{i}"], team_id=self.team.pk) _create_event( team=self.team, event="user signed up", distinct_id=f"user_{i}", timestamp="2020-01-02T14:00:00Z", ) if i % 2 == 0: _create_event( team=self.team, event="negatively_related", distinct_id=f"user_{i}", timestamp="2020-01-03T14:00:00Z", ) failure_target_persons.append(str(person.uuid)) # One positively_related as failure person_fail = _create_person(distinct_ids=[f"user_fail"], team_id=self.team.pk) _create_event( team=self.team, event="user signed up", distinct_id=f"user_fail", timestamp="2020-01-02T14:00:00Z", ) _create_event( team=self.team, event="positively_related", distinct_id=f"user_fail", timestamp="2020-01-03T14:00:00Z", ) # One negatively_related as success person_succ = _create_person(distinct_ids=[f"user_succ"], team_id=self.team.pk) _create_event( team=self.team, event="user signed up", distinct_id=f"user_succ", timestamp="2020-01-02T14:00:00Z", ) _create_event( team=self.team, event="negatively_related", distinct_id=f"user_succ", timestamp="2020-01-03T14:00:00Z", ) _create_event( team=self.team, event="paid", distinct_id=f"user_succ", timestamp="2020-01-04T14:00:00Z", ) # TESTS # test positively_related successes filter = filter.with_data({ "funnel_correlation_person_entity": { "id": "positively_related", "type": "events" }, "funnel_correlation_person_converted": "TrUe", }) results, has_more_results = FunnelCorrelationPersons( filter, self.team).run() self.assertFalse(has_more_results) self.assertCountEqual([val["uuid"] for val in results], success_target_persons) # test negatively_related failures filter = filter.with_data({ "funnel_correlation_person_entity": { "id": "negatively_related", "type": "events" }, "funnel_correlation_person_converted": "falsE", }) results, has_more_results = FunnelCorrelationPersons( filter, self.team).run() self.assertFalse(has_more_results) self.assertCountEqual([val["uuid"] for val in results], failure_target_persons) # test positively_related failures filter = filter.with_data({ "funnel_correlation_person_entity": { "id": "positively_related", "type": "events" }, "funnel_correlation_person_converted": "False", }) results, has_more_results = FunnelCorrelationPersons( filter, self.team).run() self.assertFalse(has_more_results) self.assertCountEqual([val["uuid"] for val in results], [str(person_fail.uuid)]) # test negatively_related successes filter = filter.with_data({ "funnel_correlation_person_entity": { "id": "negatively_related", "type": "events" }, "funnel_correlation_person_converted": "trUE", }) results, has_more_results = FunnelCorrelationPersons( filter, self.team).run() self.assertFalse(has_more_results) self.assertCountEqual([val["uuid"] for val in results], [str(person_succ.uuid)]) # test all positively_related filter = filter.with_data({ "funnel_correlation_person_entity": { "id": "positively_related", "type": "events" }, "funnel_correlation_person_converted": None, }) results, has_more_results = FunnelCorrelationPersons( filter, self.team).run() self.assertFalse(has_more_results) self.assertCountEqual([val["uuid"] for val in results], [*success_target_persons, str(person_fail.uuid)]) # test all negatively_related filter = filter.with_data({ "funnel_correlation_person_entity": { "id": "negatively_related", "type": "events" }, "funnel_correlation_person_converted": None, }) results, has_more_results = FunnelCorrelationPersons( filter, self.team).run() self.assertFalse(has_more_results) self.assertCountEqual([val["uuid"] for val in results], [*failure_target_persons, str(person_succ.uuid)])