def get(self, request, organization): if not self.has_feature(organization, request): return Response(status=404) try: params = self.get_snuba_params(request, organization) except NoProjects: return Response([]) with sentry_sdk.start_span(op="discover.endpoint", description="trend_dates"): middle_date = request.GET.get("middle") if middle_date: try: middle = parse_datetime_string(middle_date) except InvalidQuery: raise ParseError(detail="{} is not a valid date format".format(middle_date)) if middle <= params["start"] or middle >= params["end"]: raise ParseError( detail="The middle date should be within the duration of the query" ) else: middle = params["start"] + timedelta( seconds=(params["end"] - params["start"]).total_seconds() * 0.5 ) start, middle, end = ( datetime.strftime(params["start"], DateArg.date_format), datetime.strftime(middle, DateArg.date_format), datetime.strftime(params["end"], DateArg.date_format), ) trend_type = request.GET.get("trendType", REGRESSION) if trend_type not in TREND_TYPES: raise ParseError(detail=u"{} is not a supported trend type".format(trend_type)) params["aliases"] = self.get_function_aliases(trend_type) trend_function = request.GET.get("trendFunction", "p50()") function, columns = parse_function(trend_function) trend_columns = self.get_trend_columns(function, columns, start, middle, end) selected_columns = request.GET.getlist("field")[:] orderby = self.get_orderby(request) query = request.GET.get("query") def data_fn(offset, limit): return discover.query( selected_columns=selected_columns + trend_columns, query=query, params=params, orderby=orderby, offset=offset, limit=limit, referrer="api.trends.get-percentage-change", auto_fields=True, auto_aggregations=True, use_aggregate_conditions=True, ) with self.handle_query_errors(): return self.paginate( request=request, paginator=GenericOffsetPaginator(data_fn=data_fn), on_results=self.build_result_handler( request, organization, params, trend_function, selected_columns, orderby, query ), default_per_page=5, max_per_page=5, )
def find_histogram_buckets(field, params, conditions): _, columns, _ = parse_function( field, err_msg=u"received {}, expected histogram function".format(field)) if len(columns) != 2: raise InvalidSearchQuery( u"histogram_deprecated(...) expects 2 column arguments, received {:g} arguments" .format(len(columns))) column = columns[0] # TODO evanh: This can be expanded to more fields at a later date, for now keep this limited. if column != "transaction.duration": raise InvalidSearchQuery( "histogram_deprecated(...) can only be used with the transaction.duration column" ) try: num_buckets = int(columns[1]) if num_buckets < 1 or num_buckets > 500: raise Exception() except Exception: raise InvalidSearchQuery( u"histogram_deprecated(...) requires a bucket value between 1 and 500, not {}" .format(columns[1])) max_alias = u"max_{}".format(column) min_alias = u"min_{}".format(column) conditions = deepcopy(conditions) if conditions else [] found = False for cond in conditions: if len(cond) == 3 and (cond[0], cond[1], cond[2]) == ("event.type", "=", "transaction"): found = True break if not found: conditions.append(["event.type", "=", "transaction"]) snuba_filter = eventstore.Filter(conditions=conditions) translated_args, _ = resolve_discover_aliases(snuba_filter) results = raw_query( filter_keys={"project_id": params.get("project_id")}, start=params.get("start"), end=params.get("end"), dataset=Dataset.Discover, conditions=translated_args.conditions, aggregations=[["max", "duration", max_alias], ["min", "duration", min_alias]], ) if len(results["data"]) != 1: # If there are no transactions, so no max duration, return one empty bucket return "histogram_deprecated({}, 1, 1, 0)".format(column) bucket_min = results["data"][0][min_alias] bucket_max = results["data"][0][max_alias] if bucket_max == 0: raise InvalidSearchQuery( u"Cannot calculate histogram for {}".format(field)) bucket_size = ceil((bucket_max - bucket_min) / float(num_buckets)) if bucket_size == 0.0: bucket_size = 1.0 # Determine the first bucket that will show up in our results so that we can # zerofill correctly. offset = int(floor(bucket_min / bucket_size) * bucket_size) return "histogram_deprecated({}, {:g}, {:.0f}, {:.0f})".format( column, num_buckets, bucket_size, offset)
def get(self, request, organization): if not self.has_feature(organization, request): return Response(status=404) with sentry_sdk.start_span(op="discover.endpoint", description="filter_params") as span: span.set_tag("organization", organization) try: params = self.get_filter_params(request, organization) except NoProjects: return Response([]) params = self.quantize_date_params(request, params) has_global_views = features.has("organizations:global-views", organization, actor=request.user) if not has_global_views and len(params.get("project_id", [])) > 1: raise ParseError( detail="You cannot view events from multiple projects.") middle = params["start"] + timedelta( seconds=(params["end"] - params["start"]).total_seconds() * 0.5) start, middle, end = ( datetime.strftime(params["start"], DateArg.date_format), datetime.strftime(middle, DateArg.date_format), datetime.strftime(params["end"], DateArg.date_format), ) trend_function = request.GET.get("trendFunction", "p50()") function, columns = parse_function(trend_function) trend_column = self.trend_columns.get(function) if trend_column is None: raise ParseError(detail=u"{} is not a supported trend function". format(trend_function)) count_column = self.trend_columns.get("count_range") percentage_column = self.trend_columns["percentage"] selected_columns = request.GET.getlist("field")[:] query = request.GET.get("query") orderby = self.get_orderby(request) def data_fn(offset, limit): return discover.query( selected_columns=selected_columns + [ trend_column["format"].format( *columns, start=start, end=middle, index="1"), trend_column["format"].format( *columns, start=middle, end=end, index="2"), percentage_column["format"].format( alias=trend_column["alias"]), "minus({alias}2,{alias}1)".format( alias=trend_column["alias"]), count_column["format"].format( start=start, end=middle, index="1"), count_column["format"].format( start=middle, end=end, index="2"), percentage_column["format"].format( alias=count_column["alias"]), "absolute_correlation()", ], query=query, params=params, orderby=orderby, offset=offset, limit=limit, referrer="api.trends.get-percentage-change", auto_fields=True, use_aggregate_conditions=True, ) def on_results(events_results): def get_event_stats(query_columns, query, params, rollup, reference_event): return discover.top_events_timeseries( query_columns, selected_columns, query, params, orderby, rollup, min(5, len(events_results["data"])), organization, top_events=events_results, referrer="api.trends.get-event-stats", ) stats_results = (self.get_event_stats_data( request, organization, get_event_stats, top_events=True, query_column=trend_function, ) if len(events_results["data"]) > 0 else {}) return { "events": self.handle_results_with_meta(request, organization, params["project_id"], events_results), "stats": stats_results, } with self.handle_query_errors(): return self.paginate( request=request, paginator=GenericOffsetPaginator(data_fn=data_fn), on_results=on_results, default_per_page=5, max_per_page=5, )
def get(self, request, organization): if not self.has_feature(organization, request): return Response(status=404) try: params = self.get_snuba_params(request, organization) except NoProjects: return Response([]) with sentry_sdk.start_span(op="discover.endpoint", description="trend_dates"): middle = params["start"] + timedelta( seconds=(params["end"] - params["start"]).total_seconds() * 0.5 ) start, middle, end = ( datetime.strftime(params["start"], DateArg.date_format), datetime.strftime(middle, DateArg.date_format), datetime.strftime(params["end"], DateArg.date_format), ) trend_function = request.GET.get("trendFunction", "p50()") function, columns = parse_function(trend_function) trend_column = self.trend_columns.get(function) if trend_column is None: raise ParseError(detail=u"{} is not a supported trend function".format(trend_function)) count_column = self.trend_columns.get("count_range") percentage_column = self.trend_columns["percentage"] selected_columns = request.GET.getlist("field")[:] orderby = self.get_orderby(request) # t_score, and the columns required to calculate it variance_column = self.trend_columns["variance"] avg_column = self.trend_columns["avg"] t_score = self.trend_columns["t_score"]["format"].format( avg=avg_column["alias"], variance=variance_column["alias"], count=count_column["alias"], ) t_score_columns = [ variance_column["format"].format(start=start, end=middle, index="1"), variance_column["format"].format(start=middle, end=end, index="2"), t_score, ] # Only add average when its not the baseline if function != "avg": t_score_columns.extend( [ avg_column["format"].format(start=start, end=middle, index="1"), avg_column["format"].format(start=middle, end=end, index="2"), ] ) trend_percentage = percentage_column["format"].format(alias=trend_column["alias"]) query = self.get_query(request, trend_percentage, t_score) def data_fn(offset, limit): return discover.query( selected_columns=selected_columns + t_score_columns + [ trend_column["format"].format(*columns, start=start, end=middle, index="1"), trend_column["format"].format(*columns, start=middle, end=end, index="2"), trend_percentage, "minus({alias}2,{alias}1)".format(alias=trend_column["alias"]), count_column["format"].format(start=start, end=middle, index="1"), count_column["format"].format(start=middle, end=end, index="2"), percentage_column["format"].format(alias=count_column["alias"]), ], query=query, params=params, orderby=orderby, offset=offset, limit=limit, referrer="api.trends.get-percentage-change", auto_fields=True, auto_aggregations=True, use_aggregate_conditions=True, ) with self.handle_query_errors(): return self.paginate( request=request, paginator=GenericOffsetPaginator(data_fn=data_fn), on_results=self.build_result_handler( request, organization, params, trend_function, selected_columns, orderby, query ), default_per_page=5, max_per_page=5, )