def people(self, request: Request, *args: Any, **kwargs: Any) -> Response: team = request.user.team filter = Filter(request=request) shown_as = request.GET.get("shown_as") if len(filter.entities) >= 1: entity = filter.entities[0] else: entity = Entity({ "id": request.GET["entityId"], "type": request.GET["type"] }) # adhoc date handling. parsed differently with django orm date_from = filter.date_from or timezone.now() if filter.interval == "month": filter._date_to = (date_from + relativedelta(months=1) - timedelta(days=1)).strftime("%Y-%m-%d %H:%M:%S") elif filter.interval == "week": filter._date_to = date_from + timedelta(weeks=1) elif filter.interval == "hour": filter._date_to = date_from + timedelta(hours=1) elif filter.interval == "minute": filter._date_to = date_from + timedelta(minutes=1) current_url = request.get_full_path() if shown_as is not None and shown_as == "Stickiness": stickiness_day = int(request.GET["stickiness_days"]) serialized_people = self._calculate_stickiness_entity_people( team, entity, filter, stickiness_day) else: serialized_people = self._calculate_entity_people( team, entity, filter) current_url = request.get_full_path() next_url: Optional[str] = request.get_full_path() offset = filter.offset if len(serialized_people) > 100 and next_url: if "offset" in next_url: next_url = next_url[1:] next_url = next_url.replace("offset=" + str(offset), "offset=" + str(offset + 100)) else: next_url = request.build_absolute_uri("{}{}offset={}".format( next_url, "&" if "?" in next_url else "?", offset + 100)) else: next_url = None return Response({ "results": [{ "people": serialized_people[0:100], "count": len(serialized_people[0:99]) }], "next": next_url, "previous": current_url[1:], })
def _calculate_trends(self, filter: Filter, team: Team) -> List[Dict[str, Any]]: # format default dates if not filter._date_from: filter._date_from = relative_date_parse("-7d") if not filter._date_to: filter._date_to = timezone.now() result = [] for entity in filter.entities: if filter.compare: compare_filter = determine_compared_filter(filter=filter) entity_result = self._serialize_entity(entity, filter, team) entity_result = convert_to_comparison( entity_result, filter, "{} - {}".format(entity.name, "current")) result.extend(entity_result) previous_entity_result = self._serialize_entity( entity, compare_filter, team) previous_entity_result = convert_to_comparison( previous_entity_result, filter, "{} - {}".format(entity.name, "previous")) result.extend(previous_entity_result) else: entity_result = self._serialize_entity(entity, filter, team) result.extend(entity_result) return result
def calculate_list(self, filter: Filter, team: Team, limit: int, offset: int): filters, params = parse_prop_clauses("uuid", filter.properties, team) if not filter._date_from: filter._date_from = timezone.now().replace(hour=0, minute=0, second=0, microsecond=0) if not filter._date_to and filter.date_from: filter._date_to = filter.date_from + relativedelta(days=1) date_from, date_to = parse_timestamps(filter) params = { **params, "team_id": team.pk, "limit": limit, "offset": offset } query = SESSION_SQL.format( date_from=date_from, date_to=date_to, filters="{}".format(filters) if filter.properties else "", sessions_limit="LIMIT %(offset)s, %(limit)s", ) query_result = sync_execute(query, params) result = self._parse_list_results(query_result) self._add_person_properties(team, result) return result
def calculate_paths(self, filter: Filter, team: Team): # format default dates if not filter._date_from: filter._date_from = relative_date_parse("-7d") if not filter._date_to: filter._date_to = timezone.now() parsed_date_from, parsed_date_to = parse_timestamps(filter=filter) event, path_type, start_comparator = self._determine_path_type(filter.path_type if filter else None) prop_filters, prop_filter_params = parse_prop_clauses("uuid", filter.properties, team) # Step 0. Event culling subexpression for step 1. # Make an expression that removes events in a session that are definitely unused. # For example the 4th, 5th, etc row after a "new_session = 1" or "marked_session_start = 1" row gets removed excess_row_filter = "(" for i in range(4): if i > 0: excess_row_filter += " or " excess_row_filter += "neighbor(new_session, {}, 0) = 1".format(-i) if filter and filter.start_point: excess_row_filter += " or neighbor(marked_session_start, {}, 0) = 1".format(-i) excess_row_filter += ")" paths_query = PATHS_QUERY_FINAL.format( event_query="event = %(event)s" if event else "event NOT IN ('$autocapture', '$pageview', '$identify', '$pageleave', '$screen')", path_type=path_type, parsed_date_from=parsed_date_from, parsed_date_to=parsed_date_to, filters=prop_filters if filter.properties else "", marked_session_start="{} = %(start_point)s".format(start_comparator) if filter and filter.start_point else "new_session", excess_row_filter=excess_row_filter, select_elements_chain=", events.elements_chain as elements_chain" if event == AUTOCAPTURE_EVENT else "", group_by_elements_chain=", events.elements_chain" if event == AUTOCAPTURE_EVENT else "", ) params: Dict = { "team_id": team.pk, "property": "$current_url", "event": event, "start_point": filter.start_point, } params = {**params, **prop_filter_params} rows = sync_execute(paths_query, params) resp: List[Dict[str, str]] = [] for row in rows: resp.append( {"source": row[0], "source_id": row[1], "target": row[2], "target_id": row[3], "value": row[4],} ) resp = sorted(resp, key=lambda x: x["value"], reverse=True) return resp
def _calculate_stickiness(self, filter: Filter, team: Team) -> List[Dict[str, Any]]: if not filter._date_from: filter._date_from = relative_date_parse("-7d") if not filter._date_to: filter._date_to = timezone.now() result = [] for entity in filter.entities: if entity.type == TREND_FILTER_TYPE_ACTIONS: entity.name = Action.objects.only("name").get(team=team, pk=entity.id).name entity_result = self._serialize_entity(entity, filter, team) result.extend(entity_result) return result
def calculate_retention(self, filter: Filter, team: Team, total_intervals: int) -> List[Dict[str, Any]]: period = filter.period or "Day" tdelta, trunc_func, t1 = self._determineTimedelta( total_intervals, period) filter._date_to = ((filter.date_to if filter.date_to else now()) + t1).isoformat() if period == "Hour": date_to = filter.date_to if filter.date_to else now() date_from = date_to - tdelta else: date_to = (filter.date_to if filter.date_to else now()).replace( hour=0, minute=0, second=0, microsecond=0) date_from = date_to - tdelta filter._date_from = date_from.isoformat() filter._date_to = date_to.isoformat() prop_filters, prop_filter_params = parse_prop_clauses( filter.properties, team) target_query = "" target_params: Dict = {} target_entity = (Entity({ "id": "$pageview", "type": TREND_FILTER_TYPE_EVENTS }) if not filter.target_entity else filter.target_entity) if target_entity.type == TREND_FILTER_TYPE_ACTIONS: action = Action.objects.get(pk=target_entity.id) action_query, target_params = format_action_filter(action, use_loop=True) target_query = "AND e.uuid IN ({})".format(action_query) elif target_entity.type == TREND_FILTER_TYPE_EVENTS: target_query = "AND e.event = %(target_event)s" target_params = {"target_event": target_entity.id} result = sync_execute( RETENTION_SQL.format( target_query=target_query, filters="{filters}".format( filters=prop_filters) if filter.properties else "", trunc_func=trunc_func, ), { "team_id": team.pk, "start_date": date_from.strftime("%Y-%m-%d{}".format( " %H:%M:%S" if filter.period == "Hour" else " 00:00:00")), "end_date": date_to.strftime("%Y-%m-%d{}".format( " %H:%M:%S" if filter.period == "Hour" else " 00:00:00")), **prop_filter_params, **target_params, "period": period, }, ) result_dict = {} for res in result: result_dict.update({ (res[0], res[1]): { "count": res[2], "people": [] } }) if period == "Week": date_from = date_from - timedelta(days=date_from.isoweekday() % 7) parsed = [{ "values": [ result_dict.get((first_day, day), { "count": 0, "people": [] }) for day in range(total_intervals - first_day) ], "label": "{} {}".format(period, first_day), "date": (date_from + self._determineTimedelta(first_day, period)[0]), } for first_day in range(total_intervals)] return parsed
def _set_default_dates(self, filter: Filter, team_id: int) -> None: if not filter._date_from: filter._date_from = relative_date_parse("-7d") if not filter._date_to: filter._date_to = timezone.now()