diff --git a/src/metabase/api/x_ray.clj b/src/metabase/api/x_ray.clj index 6bc998e54c0f85b4131eff522b5a19c2b9d512ce..c05726a76a7e4989cc4e2303ed9fe670e47ef36a 100644 --- a/src/metabase/api/x_ray.clj +++ b/src/metabase/api/x_ray.clj @@ -1,11 +1,12 @@ (ns metabase.api.x-ray + (:refer-clojure :exclude [compare]) (:require [compojure.core :refer [GET POST]] [metabase.api.common :as api] [metabase.feature-extraction [core :as fe] [costs :as costs]] [metabase.models - [card :refer [Card]] + [card :refer [Card] :as card] [database :refer [Database] :as database] [field :refer [Field]] [metric :refer [Metric]] @@ -165,6 +166,17 @@ (api/read-check Segment segment-id) field)) +(api/defendpoint POST "/compare/table/:table-id/segment/:segment-id/field/:field" + "Get comparison x-ray for field named `field` in table with ID + `table-id` and segment with ID `segment-id`." + [table-id segment-id field max_query_cost max_computation_cost] + {max_query_cost MaxQueryCost + max_computation_cost MaxComputationCost} + (compare-filtered-field (max-cost max_query_cost max_computation_cost) + (api/read-check Table table-id) + (api/read-check Segment segment-id) + field)) + (api/defendpoint POST "/compare/card/:id/query" "Get comparison x-ray of card and ad-hoc query." [id max_query_cost max_computation_cost :as {query :body}] diff --git a/src/metabase/feature_extraction/core.clj b/src/metabase/feature_extraction/core.clj index 3ada9b7fe061883a003d36216c2c9fbfa386336d..2d56167d907883bf60a5d3e075145d9728bfd6a9 100644 --- a/src/metabase/feature_extraction/core.clj +++ b/src/metabase/feature_extraction/core.clj @@ -139,7 +139,12 @@ (defn- top-contributors [comparisons] - (if (map? comparisons) + (if (:top-contributors comparisons) + (->> comparisons + :top-contributors + (map (fn [[feature difference]] + {:feature feature + :difference difference}))) (->> comparisons (comparison/head-tails-breaks (comp :distance val)) (mapcat (fn [[field {:keys [top-contributors distance]}]] @@ -147,12 +152,7 @@ {:feature feature :field field :contribution (* (math/sqrt distance) difference)}))) - (comparison/head-tails-breaks :contribution)) - (->> comparisons - :top-contributors - (map (fn [[feature difference]] - {:feature feature - :difference difference}))))) + (comparison/head-tails-breaks :contribution)))) (defn compare-features "Compare feature vectors of two models." diff --git a/test/metabase/api/x_ray_test.clj b/test/metabase/api/x_ray_test.clj new file mode 100644 index 0000000000000000000000000000000000000000..f233225b767a9ae5abd50ab7d9830380a22383ec --- /dev/null +++ b/test/metabase/api/x_ray_test.clj @@ -0,0 +1,120 @@ +(ns metabase.api.x-ray-test + "Tests for /api/x-ray endpoints" + (:require [expectations :refer :all] + [metabase.api.x-ray :refer :all] + [metabase.models + [card :refer [Card]] + [database :refer [Database] :as database] + [field :refer [Field]] + [segment :refer [Segment]] + [table :refer [Table]]] + [metabase.test.data :refer :all] + [metabase.test.data.users :refer :all] + [toucan.util.test :as tt])) + +(expect + 100.0 + (-> ((user->client :rasta) :get 200 (str "x-ray/field/" (id :venues :price))) + :features + :count + :value)) + +(expect + 100.0 + (-> ((user->client :rasta) :get 200 (str "x-ray/table/" (id :venues))) + :constituents + :PRICE + :count + :value)) + +(def ^:private test-query + {:database (id) + :type :query + :query {:source_table (id :checkins) + :aggregation [[:count]] + :breakout [[:datetime-field [:field-id (id :checkins :date)] :month]]} + :parameters []}) + +(def ^:private test-card + {:name "test card" + :dataset_query test-query}) + +(def ^:private test-segment + {:name "test segment" + :table_id (id :venues) + :definition {:filter [:= [:field-id (id :venues :category_id)] 7] + :source_table (id :venues)}}) + +(expect + 1000.0 + (-> ((user->client :rasta) :post 200 "x-ray/query" test-query) + :constituents + :count + :sum + :value)) + +(tt/expect-with-temp [Card [{card-id :id} test-card]] + 1000.0 + (-> ((user->client :rasta) :get 200 (str "x-ray/card/" card-id)) + :constituents + :count + :sum + :value)) + +(tt/expect-with-temp [Segment [{segment-id :id} test-segment]] + 10.0 + (-> ((user->client :rasta) :get 200 (str "x-ray/segment/" segment-id)) + :constituents + :PRICE + :count + :value)) + +(expect + true + (:significant? ((user->client :rasta) :get 200 + (format "x-ray/compare/fields/%s/%s" + (id :venues :longitude) + (id :venues :latitude))))) + +(expect + false + (boolean (:significant? ((user->client :rasta) :get 200 + (format "x-ray/compare/tables/%s/%s" + (id :venues) + (id :venues)))))) + +(tt/expect-with-temp [Segment [{segment1-id :id} test-segment] + Segment [{segment2-id :id} + (assoc-in test-segment [:definition :filter 2] 5)]] + true + (:significant? ((user->client :rasta) :get 200 + (format "x-ray/compare/segments/%s/%s" + segment1-id + segment2-id)))) + +(tt/expect-with-temp [Segment [{segment-id :id} test-segment]] + true + (:significant? ((user->client :rasta) :get 200 + (format "x-ray/compare/table/%s/segment/%s" + (id :venues) + segment-id)))) + +(expect + [true + false] + [(-> ((user->client :rasta) :post 200 "x-ray/query" test-query) + :features + :seasonal-decomposition + :value + :trend + :rows + count + pos?) + (-> ((user->client :rasta) :post 200 "x-ray/query?max_computation_cost=linear" test-query) + :features + :seasonal-decomposition + :value + :trend + :rows + count + pos?)])