예제 #1
0
def test_person_query_with_extra_requested_fields(testdata, team, snapshot):
    filter = Filter(data={
        "properties": [
            {
                "key": "email",
                "type": "person",
                "value": "posthog",
                "operator": "icontains"
            },
        ],
        "breakdown":
        "person_prop_4326",
        "breakdown_type":
        "person",
    }, )

    assert person_query(team, filter) == snapshot
    assert run_query(team, filter) == {"rows": 2, "columns": 2}

    filter = filter.with_data({
        "breakdown": "email",
        "breakdown_type": "person"
    })
    assert person_query(team, filter) == snapshot
    assert run_query(team, filter) == {"rows": 2, "columns": 2}
예제 #2
0
    def _serialize_lifecycle(self, entity: Entity, filter: Filter,
                             team_id: int) -> List[Dict[str, Any]]:

        period = filter.interval or "day"
        num_intervals, prev_date_from, date_from, date_to, after_date_to = get_time_diff(
            period, filter.date_from, filter.date_to, team_id)
        interval_trunc, sub_interval = get_trunc_func(period=period)

        # include the before and after when filteirng all events

        filter = filter.with_data({
            "date_from": prev_date_from.isoformat(),
            "date_to": after_date_to.isoformat()
        })

        filtered_events = (Event.objects.filter(
            team_id=team_id).add_person_id(team_id).filter(
                filter_events(team_id, filter, entity)))
        event_query, event_params = queryset_to_named_query(
            filtered_events, "events")

        earliest_events_filtered = (Event.objects.filter(
            team_id=team_id).add_person_id(team_id).filter(
                filter_events(team_id, filter, entity, include_dates=False)))
        earliest_events_query, earliest_events_params = queryset_to_named_query(
            earliest_events_filtered, "earliest_events")

        with connection.cursor() as cursor:
            cursor.execute(
                LIFECYCLE_SQL.format(
                    action_join=ACTION_JOIN
                    if entity.type == TREND_FILTER_TYPE_ACTIONS else "",
                    event_condition="{} = %(event)s".format(
                        "action_id" if entity.type ==
                        TREND_FILTER_TYPE_ACTIONS else "event"),
                    events=event_query,
                    earliest_events=earliest_events_query,
                ),
                {
                    "team_id": team_id,
                    "event": entity.id,
                    "interval": interval_trunc,
                    "one_interval": "1 " + interval_trunc,
                    "sub_interval": "1 " + sub_interval,
                    "num_intervals": num_intervals,
                    "prev_date_from": prev_date_from,
                    "date_from": date_from,
                    "date_to": date_to,
                    "after_date_to": after_date_to,
                    **event_params,
                    **earliest_events_params,
                },
            )
            res = []
            for val in cursor.fetchall():
                label = "{} - {}".format(entity.name, val[2])
                additional_values = {"label": label, "status": val[2]}
                parsed_result = parse_response(val, filter, additional_values)
                res.append(parsed_result)
        return res
예제 #3
0
    def test_correlation_with_properties_raises_validation_error(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":
            "properties",
            # "funnel_correlation_names": ["$browser"], missing value
        }

        filter = Filter(data=filters)
        correlation = FunnelCorrelation(filter, self.team)

        _create_person(distinct_ids=[f"user_1"],
                       team_id=self.team.pk,
                       properties={"$browser": "Positive"})
        _create_event(
            team=self.team,
            event="user signed up",
            distinct_id=f"user_1",
            timestamp="2020-01-02T14:00:00Z",
        )
        _create_event(
            team=self.team,
            event="rick",
            distinct_id=f"user_1",
            timestamp="2020-01-03T14:00:00Z",
        )
        _create_event(
            team=self.team,
            event="paid",
            distinct_id=f"user_1",
            timestamp="2020-01-04T14:00:00Z",
        )

        with self.assertRaises(ValidationError):
            correlation._run()

        filter = filter.with_data(
            {"funnel_correlation_type": "event_with_properties"})
        # missing "funnel_correlation_event_names": ["rick"],
        with self.assertRaises(ValidationError):
            FunnelCorrelation(filter, self.team)._run()
예제 #4
0
    def test_element(self):
        _create_event(
            event="$autocapture",
            team=self.team,
            distinct_id="whatever",
            properties={"attr": "some_other_val"},
            elements=[
                Element(
                    tag_name="a",
                    href="/a-url",
                    attr_class=["small"],
                    text="bla bla",
                    attributes={},
                    nth_child=1,
                    nth_of_type=0,
                ),
                Element(tag_name="button", attr_class=["btn", "btn-primary"], nth_child=0, nth_of_type=0),
                Element(tag_name="div", nth_child=0, nth_of_type=0),
                Element(tag_name="label", nth_child=0, nth_of_type=0, attr_id="nested",),
            ],
        )
        _create_event(
            event="$pageview",
            team=self.team,
            distinct_id="whatever",
            properties={"attr": "some_val"},
            elements=[
                Element(
                    tag_name="a",
                    href="/a-url",
                    attr_class=["small"],
                    text="bla bla",
                    attributes={},
                    nth_child=1,
                    nth_of_type=0,
                ),
                Element(tag_name="button", attr_class=["btn", "btn-secondary"], nth_child=0, nth_of_type=0),
                Element(tag_name="div", nth_child=0, nth_of_type=0),
                Element(tag_name="img", nth_child=0, nth_of_type=0, attr_id="nested",),
            ],
        )

        filter = Filter(
            data={
                "events": [{"id": "event_name", "order": 0},],
                "properties": [{"key": "tag_name", "value": ["label"], "operator": "exact", "type": "element"}],
            }
        )

        self._run_query(filter)

        self._run_query(
            filter.with_data(
                {"properties": [{"key": "tag_name", "value": [], "operator": "exact", "type": "element"}],}
            )
        )
예제 #5
0
    def test_breakdown_by_group_props(self):
        self._create_groups()

        journey = {
            "person1": [
                {
                    "event": "sign up",
                    "timestamp": datetime(2020, 1, 2, 12),
                    "properties": {"$group_0": "org:5"},
                    "group0_properties": {"industry": "finance"},
                },
                {
                    "event": "sign up",
                    "timestamp": datetime(2020, 1, 2, 13),
                    "properties": {"$group_0": "org:6"},
                    "group0_properties": {"industry": "technology"},
                },
                {
                    "event": "sign up",
                    "timestamp": datetime(2020, 1, 2, 15),
                    "properties": {"$group_0": "org:7", "$group_1": "company:10"},
                    "group0_properties": {"industry": "finance"},
                    "group1_properties": {"industry": "finance"},
                },
            ],
        }

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

        filter = Filter(
            data={
                "date_from": "2020-01-01T00:00:00Z",
                "date_to": "2020-01-12",
                "breakdown": "industry",
                "breakdown_type": "group",
                "breakdown_group_type_index": 0,
                "events": [{"id": "sign up", "name": "sign up", "type": "events", "order": 0,}],
            }
        )
        response = Trends().run(filter, self.team,)

        self.assertEqual(len(response), 2)
        self.assertEqual(response[0]["breakdown_value"], "finance")
        self.assertEqual(response[0]["count"], 2)
        self.assertEqual(response[1]["breakdown_value"], "technology")
        self.assertEqual(response[1]["count"], 1)

        filter = filter.with_data(
            {"breakdown_value": "technology", "date_from": "2020-01-02T00:00:00Z", "date_to": "2020-01-03"}
        )
        entity = Entity({"id": "sign up", "name": "sign up", "type": "events", "order": 0,})
        res = self._get_trend_people(filter, entity)

        self.assertEqual(res[0]["distinct_ids"], ["person1"])
예제 #6
0
    def test_funnel_exclusions_invalid_params(self):
        filters = {
            "events": [
                {
                    "id": "user signed up",
                    "type": "events",
                    "order": 0
                },
                {
                    "id": "paid",
                    "type": "events",
                    "order": 1
                },
                {
                    "id": "blah",
                    "type": "events",
                    "order": 2
                },
            ],
            "insight":
            INSIGHT_FUNNELS,
            "funnel_window_days":
            14,
            "exclusions": [
                {
                    "id": "x",
                    "type": "events",
                    "funnel_from_step": 1,
                    "funnel_to_step": 1
                },
            ],
        }
        filter = Filter(data=filters)
        self.assertRaises(
            ValidationError,
            lambda: ClickhouseFunnelUnordered(filter, self.team).run())

        # partial windows not allowed for unordered
        filter = filter.with_data({
            "exclusions": [{
                "id": "x",
                "type": "events",
                "funnel_from_step": 0,
                "funnel_to_step": 1
            }]
        })
        self.assertRaises(
            ValidationError,
            lambda: ClickhouseFunnelUnordered(filter, self.team).run())
예제 #7
0
 def _get_people_for_event(self,
                           filter: Filter,
                           event_name: str,
                           properties=None,
                           success=True):
     person_filter = filter.with_data({
         "funnel_correlation_person_entity": {
             "id": event_name,
             "type": "events",
             "properties": properties
         },
         "funnel_correlation_person_converted":
         "TrUe" if success else "falSE",
     })
     results, _ = FunnelCorrelationPersons(person_filter, self.team).run()
     return [row["uuid"] for row in results]
예제 #8
0
파일: person.py 프로젝트: PostHog/posthog
def _handle_date_interval(filter: Filter) -> Filter:
    # adhoc date handling. parsed differently with django orm
    date_from = filter.date_from or timezone.now()
    data: Dict = {}
    if filter.interval == "month":
        data.update({
            "date_to": (date_from + relativedelta(months=1) -
                        timedelta(days=1)).strftime("%Y-%m-%d %H:%M:%S")
        })
    elif filter.interval == "week":
        data.update({
            "date_to": (date_from + relativedelta(weeks=1) -
                        timedelta(days=1)).strftime("%Y-%m-%d %H:%M:%S")
        })
    elif filter.interval == "day":
        data.update({"date_to": date_from})
    elif filter.interval == "hour":
        data.update({"date_to": date_from + timedelta(hours=1)})
    return filter.with_data(data)
    def test_invalid_steps(self):
        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,
            "funnel_step":
            "blah",
            "events": [
                {
                    "id": "step one",
                    "order": 0
                },
                {
                    "id": "step two",
                    "order": 1
                },
                {
                    "id": "step three",
                    "order": 2
                },
            ],
        }
        filter = Filter(data=data)
        with self.assertRaises(ValueError):
            ClickhouseFunnelUnorderedPersons(filter, self.team).run()

        filter = filter.with_data({"funnel_step": -1})
        results, _ = ClickhouseFunnelUnorderedPersons(filter, self.team).run()
        self.assertEqual(0, len(results))
예제 #10
0
    def get_people(
        self,
        filter: Filter,
        team_id: int,
        target_date: datetime,
        lifecycle_type: str,
        request: Request,
        limit: int = 100,
    ):
        entity = filter.entities[0]
        period = filter.interval or "day"
        num_intervals, prev_date_from, date_from, date_to, after_date_to = get_time_diff(
            period, filter.date_from, filter.date_to, team_id
        )
        interval_trunc, sub_interval = get_trunc_func(period=period)

        # include the before and after when filteirng all events
        filter = filter.with_data({"date_from": prev_date_from.isoformat(), "date_to": after_date_to.isoformat()})

        filtered_events = (
            Event.objects.filter(team_id=team_id).add_person_id(team_id).filter(filter_events(team_id, filter, entity))
        )
        event_query, event_params = queryset_to_named_query(filtered_events)

        earliest_events_filtered = (
            Event.objects.filter(team_id=team_id)
            .add_person_id(team_id)
            .filter(filter_events(team_id, filter, entity, include_dates=False))
        )
        earliest_events_query, earliest_events_params = queryset_to_named_query(
            earliest_events_filtered, "earliest_events"
        )

        with connection.cursor() as cursor:
            cursor.execute(
                LIFECYCLE_PEOPLE_SQL.format(
                    action_join=ACTION_JOIN if entity.type == TREND_FILTER_TYPE_ACTIONS else "",
                    event_condition="{} = %(event)s".format(
                        "action_id" if entity.type == TREND_FILTER_TYPE_ACTIONS else "event"
                    ),
                    events=event_query,
                    earliest_events=earliest_events_query,
                ),
                {
                    "team_id": team_id,
                    "event": entity.id,
                    "interval": interval_trunc,
                    "one_interval": "1 " + interval_trunc,
                    "sub_interval": "1 " + sub_interval,
                    "num_intervals": num_intervals,
                    "prev_date_from": prev_date_from,
                    "date_from": date_from,
                    "date_to": date_to,
                    "after_date_to": after_date_to,
                    "target_date": target_date,
                    "status": lifecycle_type,
                    "offset": filter.offset,
                    "limit": limit,
                    **event_params,
                    **earliest_events_params,
                },
            )
            pids = cursor.fetchall()

            people = Person.objects.filter(team_id=team_id, id__in=[p[0] for p in pids],)
            from posthog.api.person import PersonSerializer

            people = filter_persons(team_id, request, people)  # type: ignore
            people = people.prefetch_related(Prefetch("persondistinctid_set", to_attr="distinct_ids_cache"))

            return PersonSerializer(people, many=True).data
예제 #11
0
        "value": ["value"],
        "operator": "exact",
        "type": "group",
        "group_type_index": 2
    },
]

BASE_FILTER = Filter(
    {"events": [{
        "id": "$pageview",
        "type": "events",
        "order": 0
    }]})
FILTER_WITH_GROUPS = BASE_FILTER.with_data(
    {"properties": {
        "type": "AND",
        "values": PROPERTIES_OF_ALL_TYPES
    }})
TEAM_ID = 3


class TestPersonPropertySelector(unittest.TestCase):
    def test_basic_selector(self):

        filter = BASE_FILTER.with_data({
            "properties": {
                "type":
                "OR",
                "values": [
                    {
                        "key": "person_prop",
예제 #12
0
    {
        "key": "group_prop",
        "value": ["value"],
        "operator": "exact",
        "type": "group",
        "group_type_index": 2
    },
]

BASE_FILTER = Filter(
    {"events": [{
        "id": "$pageview",
        "type": "events",
        "order": 0
    }]})
FILTER_WITH_PROPERTIES = BASE_FILTER.with_data(
    {"properties": PROPERTIES_OF_ALL_TYPES})


class TestColumnOptimizer(ClickhouseTestMixin, APIBaseTest):
    def setUp(self):
        super().setUp()
        self.team.test_account_filters = PROPERTIES_OF_ALL_TYPES
        self.team.save()

    def test_properties_used_in_filter(self):
        properties_used_in_filter = lambda filter: ColumnOptimizer(
            filter, self.team.id).properties_used_in_filter

        self.assertEqual(properties_used_in_filter(BASE_FILTER), {})
        self.assertEqual(
            properties_used_in_filter(FILTER_WITH_PROPERTIES),
예제 #13
0
        def test_funnel_times_with_different_conversion_windows(self):
            filters = {
                "events": [
                    {"id": "user signed up", "type": "events", "order": 0},
                    {"id": "pageview", "type": "events", "order": 1},
                ],
                "insight": INSIGHT_FUNNELS,
                "funnel_window_interval": 14,
                "funnel_window_interval_unit": "day",
                "date_from": "2020-01-01",
                "date_to": "2020-01-14",
            }

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

            # event
            people = journeys_for(
                {
                    "stopped_after_signup1": [
                        {"event": "user signed up", "timestamp": datetime(2020, 1, 2, 14)},
                        {"event": "pageview", "timestamp": datetime(2020, 1, 2, 14, 5)},
                    ],
                    "stopped_after_signup2": [{"event": "user signed up", "timestamp": datetime(2020, 1, 2, 14, 3)},],
                    "stopped_after_signup3": [
                        {"event": "user signed up", "timestamp": datetime(2020, 1, 2, 12)},
                        {"event": "pageview", "timestamp": datetime(2020, 1, 2, 12, 15)},
                    ],
                },
                self.team,
            )

            result = funnel.run()
            self.assertEqual(result[0]["name"], "user signed up")
            self.assertEqual(result[0]["count"], 3)
            self.assertEqual(result[1]["count"], 2)
            self.assertEqual(result[1]["average_conversion_time"], 600)

            self.assertCountEqual(
                self._get_actor_ids_at_step(filter, 1),
                [
                    people["stopped_after_signup1"].uuid,
                    people["stopped_after_signup2"].uuid,
                    people["stopped_after_signup3"].uuid,
                ],
            )

            self.assertCountEqual(
                self._get_actor_ids_at_step(filter, 2),
                [people["stopped_after_signup1"].uuid, people["stopped_after_signup3"].uuid],
            )

            filter = filter.with_data({"funnel_window_interval": 5, "funnel_window_interval_unit": "minute"})

            funnel = Funnel(filter, self.team)
            result4 = funnel.run()

            self.assertNotEqual(result, result4)
            self.assertEqual(result4[0]["name"], "user signed up")
            self.assertEqual(result4[0]["count"], 3)
            self.assertEqual(result4[1]["count"], 1)
            self.assertEqual(result4[1]["average_conversion_time"], 300)

            self.assertCountEqual(
                self._get_actor_ids_at_step(filter, 1),
                [
                    people["stopped_after_signup1"].uuid,
                    people["stopped_after_signup2"].uuid,
                    people["stopped_after_signup3"].uuid,
                ],
            )

            self.assertCountEqual(
                self._get_actor_ids_at_step(filter, 2), [people["stopped_after_signup1"].uuid],
            )
예제 #14
0
        def test_funnel_times_with_different_conversion_windows(self):
            filters = {
                "events": [
                    {
                        "id": "user signed up",
                        "type": "events",
                        "order": 0
                    },
                    {
                        "id": "pageview",
                        "type": "events",
                        "order": 1
                    },
                ],
                "insight":
                INSIGHT_FUNNELS,
                "funnel_window_interval":
                14,
                "funnel_window_interval_unit":
                "day",
                "date_from":
                "2020-01-01",
                "date_to":
                "2020-01-14",
            }

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

            # event
            person1_stopped_after_two_signups = _create_person(
                distinct_ids=["stopped_after_signup1"], team_id=self.team.pk)
            _create_event(
                team=self.team,
                event="user signed up",
                distinct_id="stopped_after_signup1",
                timestamp="2020-01-02T14:00:00Z",
            )
            _create_event(team=self.team,
                          event="pageview",
                          distinct_id="stopped_after_signup1",
                          timestamp="2020-01-02T14:05:00Z")

            person2_stopped_after_signup = _create_person(
                distinct_ids=["stopped_after_signup2"], team_id=self.team.pk)
            _create_event(
                team=self.team,
                event="user signed up",
                distinct_id="stopped_after_signup2",
                timestamp="2020-01-02T14:03:00Z",
            )

            person3_stopped_after_two_signups = _create_person(
                distinct_ids=["stopped_after_signup3"], team_id=self.team.pk)
            _create_event(
                team=self.team,
                event="user signed up",
                distinct_id="stopped_after_signup3",
                timestamp="2020-01-02T12:00:00Z",
            )
            _create_event(team=self.team,
                          event="pageview",
                          distinct_id="stopped_after_signup3",
                          timestamp="2020-01-02T12:15:00Z")

            result = funnel.run()
            self.assertEqual(result[0]["name"], "user signed up")
            self.assertEqual(result[0]["count"], 3)
            self.assertEqual(result[1]["count"], 2)
            self.assertEqual(result[1]["average_conversion_time"], 600)

            self.assertCountEqual(
                self._get_people_at_step(filter, 1),
                [
                    person1_stopped_after_two_signups.uuid,
                    person2_stopped_after_signup.uuid,
                    person3_stopped_after_two_signups.uuid,
                ],
            )

            self.assertCountEqual(
                self._get_people_at_step(filter, 2),
                [
                    person1_stopped_after_two_signups.uuid,
                    person3_stopped_after_two_signups.uuid
                ],
            )

            filter = filter.with_data({
                "funnel_window_interval": 5,
                "funnel_window_interval_unit": "minute"
            })

            funnel = Funnel(filter, self.team)
            result4 = funnel.run()

            self.assertNotEqual(result, result4)
            self.assertEqual(result4[0]["name"], "user signed up")
            self.assertEqual(result4[0]["count"], 3)
            self.assertEqual(result4[1]["count"], 1)
            self.assertEqual(result4[1]["average_conversion_time"], 300)

            self.assertCountEqual(
                self._get_people_at_step(filter, 1),
                [
                    person1_stopped_after_two_signups.uuid,
                    person2_stopped_after_signup.uuid,
                    person3_stopped_after_two_signups.uuid,
                ],
            )

            self.assertCountEqual(
                self._get_people_at_step(filter, 2),
                [person1_stopped_after_two_signups.uuid],
            )
예제 #15
0
    def test_correlation_with_multiple_properties(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":
            "properties",
            "funnel_correlation_names": ["$browser", "$nice"],
        }

        filter = Filter(data=filters)
        correlation = FunnelCorrelation(filter, self.team)

        #  5 successful people with both properties
        for i in range(5):
            _create_person(distinct_ids=[f"user_{i}"],
                           team_id=self.team.pk,
                           properties={
                               "$browser": "Positive",
                               "$nice": "very"
                           })
            _create_event(
                team=self.team,
                event="user signed up",
                distinct_id=f"user_{i}",
                timestamp="2020-01-02T14:00:00Z",
            )
            _create_event(
                team=self.team,
                event="paid",
                distinct_id=f"user_{i}",
                timestamp="2020-01-04T14:00:00Z",
            )

        #  10 successful people with some different properties
        for i in range(5, 15):
            _create_person(distinct_ids=[f"user_{i}"],
                           team_id=self.team.pk,
                           properties={
                               "$browser": "Positive",
                               "$nice": "not"
                           })
            _create_event(
                team=self.team,
                event="user signed up",
                distinct_id=f"user_{i}",
                timestamp="2020-01-02T14:00:00Z",
            )
            _create_event(
                team=self.team,
                event="paid",
                distinct_id=f"user_{i}",
                timestamp="2020-01-04T14:00:00Z",
            )

        # 5 Unsuccessful people with some common properties
        for i in range(15, 20):
            _create_person(distinct_ids=[f"user_{i}"],
                           team_id=self.team.pk,
                           properties={
                               "$browser": "Negative",
                               "$nice": "smh"
                           })
            _create_event(
                team=self.team,
                event="user signed up",
                distinct_id=f"user_{i}",
                timestamp="2020-01-02T14:00:00Z",
            )

        # One Positive with failure, no $nice property
        _create_person(distinct_ids=[f"user_fail"],
                       team_id=self.team.pk,
                       properties={"$browser": "Positive"})
        _create_event(
            team=self.team,
            event="user signed up",
            distinct_id=f"user_fail",
            timestamp="2020-01-02T14:00:00Z",
        )

        # One Negative with success, no $nice property
        _create_person(distinct_ids=[f"user_succ"],
                       team_id=self.team.pk,
                       properties={"$browser": "Negative"})
        _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="paid",
            distinct_id=f"user_succ",
            timestamp="2020-01-04T14:00:00Z",
        )

        result = correlation._run()[0]

        # Success Total = 5 + 10 + 1 = 16
        # Failure Total = 5 + 1 = 6
        # Add 1 for priors

        odds_ratios = [item.pop("odds_ratio")
                       for item in result]  # type: ignore
        expected_odds_ratios = [
            (16 / 2) * ((7 - 1) / (17 - 15)),
            (11 / 1) * ((7 - 0) / (17 - 10)),
            (6 / 1) * ((7 - 0) / (17 - 5)),
            (1 / 6) * ((7 - 5) / (17 - 0)),
            (2 / 6) * ((7 - 5) / (17 - 1)),
            (2 / 2) * ((7 - 1) / (17 - 1)),
        ]
        # (success + 1) / (failure + 1)

        for odds, expected_odds in zip(odds_ratios, expected_odds_ratios):
            self.assertAlmostEqual(odds, expected_odds)

        expected_result = [
            {
                "event": "$browser::Positive",
                "success_count": 15,
                "failure_count": 1,
                # "odds_ratio": 24,
                "correlation_type": "success",
            },
            {
                "event": "$nice::not",
                "success_count": 10,
                "failure_count": 0,
                # "odds_ratio": 11,
                "correlation_type": "success",
            },
            {
                "event": "$nice::very",
                "success_count": 5,
                "failure_count": 0,
                # "odds_ratio": 3.5,
                "correlation_type": "success",
            },
            {
                "event": "$nice::smh",
                "success_count": 0,
                "failure_count": 5,
                # "odds_ratio": 0.0196078431372549,
                "correlation_type": "failure",
            },
            {
                "event": "$browser::Negative",
                "success_count": 1,
                "failure_count": 5,
                # "odds_ratio": 0.041666666666666664,
                "correlation_type": "failure",
            },
            {
                "event": "$nice::",
                "success_count": 1,
                "failure_count": 1,
                # "odds_ratio": 0.375,
                "correlation_type": "failure",
            },
        ]

        self.assertEqual(result, expected_result)

        # _run property correlation with filter on all properties
        filter = filter.with_data({"funnel_correlation_names": ["$all"]})
        correlation = FunnelCorrelation(filter, self.team)

        new_result = correlation._run()[0]

        odds_ratios = [item.pop("odds_ratio")
                       for item in new_result]  # type: ignore

        new_expected_odds_ratios = expected_odds_ratios[:-1]
        new_expected_result = expected_result[:-1]
        # When querying all properties, we don't consider properties that don't exist for part of the data
        # since users aren't explicitly asking for that property. Thus,
        # We discard $nice:: because it's an empty result set

        for odds, expected_odds in zip(odds_ratios, new_expected_odds_ratios):
            self.assertAlmostEqual(odds, expected_odds)

        self.assertEqual(new_result, new_expected_result)

        filter = filter.with_data(
            {"funnel_correlation_exclude_names": ["$browser"]})
        # search for $all but exclude $browser
        correlation = FunnelCorrelation(filter, self.team)

        new_result = correlation._run()[0]
        odds_ratios = [item.pop("odds_ratio")
                       for item in new_result]  # type: ignore

        new_expected_odds_ratios = expected_odds_ratios[
            1:4]  # choosing the $nice property values
        new_expected_result = expected_result[1:4]

        for odds, expected_odds in zip(odds_ratios, new_expected_odds_ratios):
            self.assertAlmostEqual(odds, expected_odds)

        self.assertEqual(new_result, new_expected_result)
예제 #16
0
    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)
        correlation = FunnelCorrelation(filter, self.team)

        for i in range(10):
            _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",
                )
            _create_event(
                team=self.team,
                event="paid",
                distinct_id=f"user_{i}",
                timestamp="2020-01-04T14:00:00Z",
            )

        for i in range(10, 20):
            _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",
                )

        result = correlation._run()[0]

        odds_ratios = [item.pop("odds_ratio")
                       for item in result]  # type: ignore
        expected_odds_ratios = [11, 1 / 11]

        for odds, expected_odds in zip(odds_ratios, expected_odds_ratios):
            self.assertAlmostEqual(odds, expected_odds)

        self.assertEqual(
            result,
            [
                {
                    "event": "positively_related",
                    "success_count": 5,
                    "failure_count": 0,
                    # "odds_ratio": 11.0,
                    "correlation_type": "success",
                },
                {
                    "event": "negatively_related",
                    "success_count": 0,
                    "failure_count": 5,
                    # "odds_ratio": 1 / 11,
                    "correlation_type": "failure",
                },
            ],
        )

        self.assertEqual(
            len(self._get_people_for_event(filter, "positively_related")), 5)
        self.assertEqual(
            len(
                self._get_people_for_event(filter,
                                           "positively_related",
                                           success=False)), 0)
        self.assertEqual(
            len(
                self._get_people_for_event(filter,
                                           "negatively_related",
                                           success=False)), 5)
        self.assertEqual(
            len(self._get_people_for_event(filter, "negatively_related")), 0)

        # Now exclude positively_related
        filter = filter.with_data(
            {"funnel_correlation_exclude_event_names": ["positively_related"]})
        correlation = FunnelCorrelation(filter, self.team)

        result = correlation._run()[0]

        odds_ratio = result[0].pop("odds_ratio")  # type: ignore
        expected_odds_ratio = 1 / 11

        self.assertAlmostEqual(odds_ratio, expected_odds_ratio)

        self.assertEqual(
            result,
            [
                {
                    "event": "negatively_related",
                    "success_count": 0,
                    "failure_count": 5,
                    # "odds_ratio": 1 / 11,
                    "correlation_type": "failure",
                },
            ],
        )
        # Getting specific people isn't affected by exclude_events
        self.assertEqual(
            len(self._get_people_for_event(filter, "positively_related")), 5)
        self.assertEqual(
            len(
                self._get_people_for_event(filter,
                                           "positively_related",
                                           success=False)), 0)
        self.assertEqual(
            len(
                self._get_people_for_event(filter,
                                           "negatively_related",
                                           success=False)), 5)
        self.assertEqual(
            len(self._get_people_for_event(filter, "negatively_related")), 0)