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
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()
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
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