diff --git a/src/metabase/api/activity.clj b/src/metabase/api/activity.clj index a6806f51a71cde0f611c90265fe338cd042cdb45..ea334dfb090a85eb13e494aeaac0c21e3cbf4d83 100644 --- a/src/metabase/api/activity.clj +++ b/src/metabase/api/activity.clj @@ -138,23 +138,23 @@ from the query_execution table. The query context is always a `:question`. The results are normalized and concatenated to the query results for dashboard and table views." [views-limit card-runs-limit all-users?] - (let [dashboard-and-table-views (db/select [ViewLog :user_id :model :model_id + (let [dashboard-and-table-views (db/select [ViewLog :%min.view_log.user_id :model :model_id [:%count.* :cnt] [:%max.timestamp :max_ts]] - {:group-by [(db/qualify ViewLog :user_id) :model :model_id] + {:group-by [:model :model_id] :where [:and (when-not all-users? [:= (db/qualify ViewLog :user_id) *current-user-id*]) [:in :model #{"dashboard" "table"}] [:= :bm.id nil]] - :order-by [[:max_ts :desc]] + :order-by [[:model :desc] [:max_ts :desc]] :limit views-limit :left-join [[DashboardBookmark :bm] [:and [:not [:= :model "table"]] [:= :bm.user_id *current-user-id*] [:= :model_id :bm.dashboard_id]]]}) - card-runs (->> (db/select [QueryExecution [:executor_id :user_id] [(db/qualify QueryExecution :card_id) :model_id] + card-runs (->> (db/select [QueryExecution [:%min.executor_id :user_id] [(db/qualify QueryExecution :card_id) :model_id] [:%count.* :cnt] [:%max.started_at :max_ts]] - {:group-by [:executor_id (db/qualify QueryExecution :card_id) :context] + {:group-by [(db/qualify QueryExecution :card_id) :context] :where [:and (when-not all-users? [:= :executor_id *current-user-id*]) [:= :context (hx/literal :question)] @@ -232,6 +232,14 @@ (* (/ cnt max-count) views-wt)]] (assoc item :score (double (reduce + scores)))))))) +(def ^:private model-precedence ["dashboard" "card" "dataset" "table"]) + +(defn- order-items + [items] + (when (seq items) + (let [groups (group-by :model items)] + (mapcat #(get groups %) model-precedence)))) + (defendpoint GET "/popular_items" "Get the list of 5 popular things for the current user. Query takes 8 and limits to 5 so that if it finds anything archived, deleted, etc it can hopefully still get 5." @@ -256,6 +264,7 @@ (->> scored-views (sort-by :score) reverse + order-items (take 5) (map #(dissoc % :score))))) diff --git a/test/metabase/api/activity_test.clj b/test/metabase/api/activity_test.clj index 09081a6f0b7e467b327e5594b8748ea549f5c6a7..32ffe343b92a9a04d9c6508d23d53d38491b4c03 100644 --- a/test/metabase/api/activity_test.clj +++ b/test/metabase/api/activity_test.clj @@ -160,6 +160,9 @@ Dashboard [dash1 {:name "rand-name" :description "rand-name" :creator_id (mt/user->id :crowberto)}] + Dashboard [dash2 {:name "other-dashboard" + :description "just another dashboard" + :creator_id (mt/user->id :crowberto)}] Table [table1 {:name "rand-name"}] Table [hidden-table {:name "hidden table" :visibility_type "hidden"}] @@ -168,21 +171,47 @@ :creator_id (mt/user->id :crowberto) :display "table" :visualization_settings {}}]] - (mt/with-model-cleanup [ViewLog QueryExecution] - (create-views! (concat - ;; one item with many views is considered more popular - (repeat 10 [(mt/user->id :rasta) "card" (:id dataset)]) - [[(mt/user->id :rasta) "dashboard" (:id dash1)] - [(mt/user->id :rasta) "card" (:id card1)] - [(mt/user->id :rasta) "table" (:id table1)] - [(mt/user->id :rasta) "card" (:id card1)]])) - (is (= [["dataset" (:id dataset)] - ["card" (:id card1)] - ["table" (:id table1)] - ["dashboard" (:id dash1)]] - ;; all views are from :rasta, but :crowberto can still see popular items - (for [popular-item (mt/user-http-request :crowberto :get 200 "activity/popular_items")] - ((juxt :model :model_id) popular-item))))))) + (testing "Items viewed by multiple users are not duplicated in the popular items list." + (mt/with-model-cleanup [ViewLog QueryExecution] + (create-views! [[(mt/user->id :rasta) "dashboard" (:id dash1)] + [(mt/user->id :crowberto) "dashboard" (:id dash1)] + [(mt/user->id :rasta) "card" (:id card1)] + [(mt/user->id :crowberto) "card" (:id card1)]]) + (is (= [["dashboard" (:id dash1)] + ["card" (:id card1)]] + ;; all views are from :rasta, but :crowberto can still see popular items + (for [popular-item (mt/user-http-request :crowberto :get 200 "activity/popular_items")] + ((juxt :model :model_id) popular-item)))))) + (testing "Items viewed by other users can still show up in popular items." + (mt/with-model-cleanup [ViewLog QueryExecution] + (create-views! [[(mt/user->id :rasta) "dashboard" (:id dash1)] + [(mt/user->id :rasta) "card" (:id card1)] + [(mt/user->id :rasta) "table" (:id table1)] + [(mt/user->id :rasta) "card" (:id dataset)]]) + (is (= [["dashboard" (:id dash1)] + ["card" (:id card1)] + ["dataset" (:id dataset)] + ["table" (:id table1)]] + ;; all views are from :rasta, but :crowberto can still see popular items + (for [popular-item (mt/user-http-request :crowberto :get 200 "activity/popular_items")] + ((juxt :model :model_id) popular-item)))))) + (testing "Items with more views show up sooner in popular items." + (mt/with-model-cleanup [ViewLog QueryExecution] + (create-views! (concat + ;; one item with many views is considered more popular + (repeat 10 [(mt/user->id :rasta) "dashboard" (:id dash1)]) + [[(mt/user->id :rasta) "dashboard" (:id dash2)] + [(mt/user->id :rasta) "card" (:id dataset)] + [(mt/user->id :rasta) "table" (:id table1)] + [(mt/user->id :rasta) "card" (:id card1)]])) + (is (= [["dashboard" (:id dash1)] + ["dashboard" (:id dash2)] + ["card" (:id card1)] + ["dataset" (:id dataset)] + ["table" (:id table1)]] + ;; all views are from :rasta, but :crowberto can still see popular items + (for [popular-item (mt/user-http-request :crowberto :get 200 "activity/popular_items")] + ((juxt :model :model_id) popular-item)))))))) ;;; activities->referenced-objects, referenced-objects->existing-objects, add-model-exists-info