예제 #1
0
    def update(self, funnel: Funnel, validated_data: Any) -> Funnel: # type: ignore
        request = self.context['request']

        funnel.deleted = validated_data.get('deleted', funnel.deleted)
        funnel.name = validated_data.get('name', funnel.name)
        funnel.save()

        # If there's no steps property at all we just ignore it
        # If there is a step property but it's an empty array [], we'll delete all the steps
        if 'steps' in request.data:
            steps = request.data.pop('steps')

            steps_to_delete = funnel.steps.exclude(pk__in=[step.get('id') for step in steps if step.get('id') and '-' not in str(step['id'])])
            steps_to_delete.delete()
            for index, step in enumerate(steps):
                if step.get('action_id'):
                    # make sure it's not a uuid, in which case we can just ignore id
                    if step.get('id') and '-' not in str(step['id']):
                        db_step = FunnelStep.objects.get(funnel=funnel, pk=step['id'])
                        db_step.action_id = step['action_id']
                        db_step.order = index
                        db_step.save()
                    else:
                        FunnelStep.objects.create(
                            funnel=funnel,
                            order=index,
                            action_id=step['action_id']
                        )
        return funnel
예제 #2
0
    def get_steps(self, funnel: Funnel) -> List[Dict[str, Any]]:
        # for some reason, rest_framework executes SerializerMethodField multiple times,
        # causing lots of slow queries.
        # Seems a known issue: https://stackoverflow.com/questions/55023511/serializer-being-called-multiple-times-django-python
        if hasattr(funnel, 'steps_cache'):
            return []
        funnel.steps_cache = True  # type: ignore

        if self.context['view'].action != 'retrieve' or self.context[
                'request'].GET.get('exclude_count'):
            return []
        return funnel.get_steps()
예제 #3
0
    def get_steps(self, funnel: Funnel) -> List[Dict[str, Any]]:
        # for some reason, rest_framework executes SerializerMethodField multiple times,
        # causing lots of slow queries.
        # Seems a known issue: https://stackoverflow.com/questions/55023511/serializer-being-called-multiple-times-django-python
        if hasattr(funnel, 'steps_cache'):
            return {}
        funnel.steps_cache = True  # type: ignore

        funnel_steps = funnel.steps.all().prefetch_related('action')
        if self.context['view'].action != 'retrieve':
            return [{
                'id': step.id,
                'action_id': step.action.id,
                'name': step.action.name,
                'order': step.order
            } for step in funnel_steps]

        if len(funnel_steps) == 0:
            return []
        annotations = {}
        for step in funnel_steps:
            annotations['step_{}'.format(step.order)] = Subquery(
                Event.objects.filter_by_action(step.action) # type: ignore
                    .annotate(person_id=OuterRef('id'))
                    .filter(
                        distinct_id__in=Subquery(
                            PersonDistinctId.objects.filter(
                                person_id=OuterRef('person_id')
                            ).values('distinct_id')
                        ),
                        pk__gt=OuterRef('step_{}'.format(step.order-1)) if step.order > 0 else 0
                    )\
                    .order_by('pk')\
                    .values('pk')[:1]
            , output_field=models.IntegerField())

        people = Person.objects.all()\
            .annotate(**annotations)\
            .filter(step_0__isnull=False)

        people = [person for person in people]

        steps = []
        for step in funnel_steps:
            relevant_people = [
                person.id for person in people
                if getattr(person, 'step_{}'.format(step.order))
            ]
            steps.append({
                'id': step.id,
                'action_id': step.action.id,
                'name': step.action.name,
                'order': step.order,
                'people': relevant_people[:100],
                'count': len(relevant_people)
            })
        if len(steps) > 0:
            steps[0]['people'] = self._order_people_in_step(
                steps, steps[0]['people'])
        return steps
예제 #4
0
    def get_steps(self, funnel: Funnel) -> List[Dict[str, Any]]:
        # for some reason, rest_framework executes SerializerMethodField multiple times,
        # causing lots of slow queries. 
        # Seems a known issue: https://stackoverflow.com/questions/55023511/serializer-being-called-multiple-times-django-python
        if hasattr(funnel, 'steps_cache'):
            return []
        funnel.steps_cache = True # type: ignore

        funnel_steps = funnel.steps.all().order_by('order').prefetch_related('action')
        if self.context['view'].action != 'retrieve' or self.context['request'].GET.get('exclude_count'):
            return [self._serialize_step(step) for step in funnel_steps]

        if len(funnel_steps) == 0:
            return []

        people = Person.objects.all()\
            .filter(
                team_id=funnel.team_id,
                persondistinctid__distinct_id__isnull=False
            )\
            .annotate(**self._annotate_steps(
                team_id=funnel.team_id,
                funnel_steps=funnel_steps,
                date_query=request_to_date_query(self.context['request'])
            ))\
            .filter(step_0__isnull=False)\
            .distinct('pk')

        steps = []
        for index, funnel_step in enumerate(funnel_steps):
            relevant_people = [person.id for person in people if getattr(person, 'step_{}'.format(index))]
            steps.append(self._serialize_step(funnel_step, relevant_people))

        if len(steps) > 0:
            for index, _ in enumerate(steps):
                steps[index]['people'] = self._order_people_in_step(steps, steps[index]['people'])[0:100]
        return steps