diff --git a/index.html b/index.html index def0c8f..09feefc 100644 --- a/index.html +++ b/index.html @@ -20052,7 +20052,6 @@

Important Libraries

[clojure.set :as set] [clojure.string :as str] [clojure.walk :as walk] - [clojure.zip :as zip] [kixi.stats.core :as stats] [kixi.stats.math :as math] [medley.core :as m] @@ -20251,8 +20250,8 @@

Important Libraries

s)) form)) x) - (m/update-existing :visualization #(instantiate-visualization % bindings available-metrics)))) -
(defn- singular-cell-dimensions
+      (m/update-existing :visualization #(instantiate-visualization % bindings available-metrics))))

Return the set of ids referenced in a cell query

+
(defn- singular-cell-dimension-field-ids
   [{:keys [cell-query]}]
   (letfn [(collect-dimensions [[op & args]]
             (case (some-> op qp.util/normalize-token)
@@ -20409,7 +20408,7 @@ 

Important Libraries

;; Why do we need (or do we) the last remove form? :filters (->> dashboard_filters (mapcat (comp :matches grounded-dimensions)) - (remove (comp (singular-cell-dimensions root) id-or-name))) + (remove (comp (singular-cell-dimension-field-ids root) id-or-name))) :cards dashcards)))
(def ^:private ^:const ^Long max-related 8)
 (def ^:private ^:const ^Long max-cards 15)

Turn entity into an entry in :related.

@@ -20668,24 +20667,15 @@

Important Libraries

(into [] (comp cat (map analyze)) [(collect-metrics root question) - (collect-breakout-fields root question)])))

Recursively finds key in coll, returns true or false

-
(defn- key-in?
-  [coll k]
-  (boolean (let [coll-zip (zip/zipper coll? #(if (map? %) (vals %) %) nil coll)]
-             (loop [x coll-zip]
-               (when-not (zip/end? x)
-                 (if (k (zip/node x)) true (recur (zip/next x))))))))
-
(defn- splice-in
-  [join-statement card-member]
-  (let [query (get-in card-member [:card :dataset_query :query])]
-    (if (key-in? query :join-alias)
-      ;; Always in the top level even if the join-alias is found deep in there
-      (assoc-in card-member [:card :dataset_query :query :joins] join-statement)
-      card-member)))

Hack to shove back in joins when they get automagically stripped out by the question decomposition into metrics

-
(defn- maybe-enrich-joins
+           (collect-breakout-fields root question)])))

Hack to shove back in joins when they get automagically stripped out by the question decomposition into metrics

+
(defn- preserve-joins
   [entity dashboard]
   (if-let [join-statement (get-in entity [:dataset_query :query :joins])]
-    (update dashboard :dashcards #(map (partial splice-in join-statement) %))
+    (letfn [(splice-joins [dashcard]
+              (cond-> dashcard
+                (get-in dashcard [:card :dataset_query :query])
+                (assoc-in [:card :dataset_query :query :joins] join-statement)))]
+      (update dashboard :dashcards (partial map splice-joins)))
     dashboard))
(defn- query-based-analysis
   [{:keys [entity] :as root} opts {:keys [cell-query cell-url]}]
@@ -20711,7 +20701,7 @@ 

Important Libraries

(let [title (tru "A closer look at {0}" (names/cell-title root cell-query))] {:transient_name title :name title})))))] - (maybe-enrich-joins (:entity root) transient-dash)))
+ (preserve-joins (:entity root) transient-dash)))
(defmethod automagic-analysis Card
   [card {:keys [cell-query] :as opts}]
   (let [root     (->root card)
@@ -44939,7 +44929,7 @@ 
`:abstract?` (default = false)
[n unit] (let [n (if (string? n) (keyword n) n) unit (if (string? unit) (keyword unit) unit)] - (lib.core/describe-relative-datetime n unit)))

Adds an aggregation to query.

+ (lib.core/describe-relative-datetime n unit)))

Adds an aggregation to query.

(defn ^:export aggregate
   [a-query stage-number an-aggregate-clause]
   (lib.core/aggregate a-query stage-number (js->clj an-aggregate-clause :keywordize-keys true)))

Get the aggregations in a given stage of a query.

@@ -44987,14 +44977,14 @@
`:abstract?` (default = false)
[a-query stage-number an-expression-clause] (let [parts (lib.core/expression-parts a-query stage-number an-expression-clause)] (walk/postwalk - (fn [node] - (if (and (map? node) (= :mbql/expression-parts (:lib/type node))) - (let [{:keys [operator options args]} node] - #js {:operator (name operator) - :options (clj->js (select-keys options [:case-sensitive :include-current])) - :args (to-array (map #(if (keyword? %) (u/qualified-name %) %) args))}) - node)) - parts)))

Returns true if arg is a a ColumnMetadata

+ (fn [node] + (if (and (map? node) (= :mbql/expression-parts (:lib/type node))) + (let [{:keys [operator options args]} node] + #js {:operator (name operator) + :options (clj->js (select-keys options [:case-sensitive :include-current])) + :args (to-array (map #(if (keyword? %) (u/qualified-name %) %) args))}) + node)) + parts)))

Returns true if arg is a a ColumnMetadata

(defn ^:export is-column-metadata
   [arg]
   (and (map? arg) (= :metadata/column (:lib/type arg))))

Sets boolean-expression as a filter on query.

@@ -45068,7 +45058,13 @@
`:abstract?` (default = false)
(let [stage (lib.util/query-stage a-query stage-number) vis-columns (lib.metadata.calculation/visible-columns a-query stage-number stage) ret-columns (lib.metadata.calculation/returned-columns a-query stage-number stage)] - (to-array (lib.equality/mark-selected-columns a-query stage-number vis-columns ret-columns))))

Given a column metadata from eg. [[fieldable-columns]], return it as a legacy JSON field ref.

+ (to-array (lib.equality/mark-selected-columns a-query stage-number vis-columns ret-columns))))

Return a sequence of column metadatas for columns returned by the query.

+
(defn ^:export returned-columns
+  [a-query stage-number]
+  (let [stage (lib.util/query-stage a-query stage-number)]
+    (->> (lib.metadata.calculation/returned-columns a-query stage-number stage)
+         (map #(assoc % :selected? true))
+         to-array)))

Given a column metadata from eg. [[fieldable-columns]], return it as a legacy JSON field ref.

(defn ^:export legacy-field-ref
   [column]
   (-> column
@@ -46217,42 +46213,42 @@ 
`:abstract?` (default = false)
(lib.dispatch/dispatch-value x)) :hierarchy lib.hierarchy/hierarchy)
(mr/register! ::display-info
-  [:map
-   [:display-name :string]
-   [:long-display-name {:optional true} :string]
+              [:map
+               [:display-name :string]
+               [:long-display-name {:optional true} :string]
    ;; for things that have a Table, e.g. a Field
-   [:table {:optional true} [:maybe [:ref ::display-info]]]
+               [:table {:optional true} [:maybe [:ref ::display-info]]]
    ;; these are derived from the `:lib/source`/`:metabase.lib.schema.metadata/column-source`, but instead of using
    ;; that value directly we're returning a different property so the FE doesn't break if we change those keys in the
    ;; future, e.g. if we consolidate or split some of those keys. This is all the FE really needs to know.
    ;;
    ;; if this is a Column, does it come from a previous stage?
-   [:is-from-previous-stage {:optional true} [:maybe :boolean]]
+               [:is-from-previous-stage {:optional true} [:maybe :boolean]]
    ;; if this is a Column, does it come from a join in this stage?
-   [:is-from-join {:optional true} [:maybe :boolean]]
+               [:is-from-join {:optional true} [:maybe :boolean]]
    ;; if this is a Column, is it 'calculated', i.e. does it come from an expression in this stage?
-   [:is-calculated {:optional true} [:maybe :boolean]]
+               [:is-calculated {:optional true} [:maybe :boolean]]
    ;; if this is a Column, is it an implicitly joinable one? I.e. is it from a different table that we have not
    ;; already joined, but could implicitly join against?
-   [:is-implicitly-joinable {:optional true} [:maybe :boolean]]
+               [:is-implicitly-joinable {:optional true} [:maybe :boolean]]
    ;; For the `:table` field of a Column, is this the source table, or a joined table?
-   [:is-source-table {:optional true} [:maybe :boolean]]
+               [:is-source-table {:optional true} [:maybe :boolean]]
    ;; does this column occur in the breakout clause?
-   [:is-breakout-column {:optional true} [:maybe :boolean]]
+               [:is-breakout-column {:optional true} [:maybe :boolean]]
    ;; does this column occur in the order-by clause?
-   [:is-order-by-column {:optional true} [:maybe :boolean]]
+               [:is-order-by-column {:optional true} [:maybe :boolean]]
    ;; for joins
-   [:name {:optional true} :string]
+               [:name {:optional true} :string]
    ;; for aggregation operators
-   [:column-name {:optional true} :string]
-   [:description {:optional true} :string]
-   [:short-name {:optional true} :string]
-   [:requires-column {:optional true} :boolean]
-   [:selected {:optional true} :boolean]
+               [:column-name {:optional true} :string]
+               [:description {:optional true} :string]
+               [:short-name {:optional true} :string]
+               [:requires-column {:optional true} :boolean]
+               [:selected {:optional true} :boolean]
    ;; for binning and bucketing
-   [:default {:optional true} :boolean]
+               [:default {:optional true} :boolean]
    ;; for order by
-   [:direction {:optional true} [:enum :asc :desc]]])
+ [:direction {:optional true} [:enum :asc :desc]]])
(mu/defn display-info :- ::display-info
   "Given some sort of Cljs object, return a map with the info you'd need to implement UI for it. This is mostly meant to
   power the Frontend JavaScript UI; in JS, results will be converted to plain JavaScript objects, so avoid returning
@@ -46298,7 +46294,9 @@ 
`:abstract?` (default = false)
{:is-from-previous-stage (= source :source/previous-stage) :is-from-join (= source :source/joins) :is-calculated (= source :source/expressions) - :is-implicitly-joinable (= source :source/implicitly-joinable)}) + :is-implicitly-joinable (= source :source/implicitly-joinable) + :is-aggregation (= source :source/aggregations) + :is-breakout (= source :source/breakouts)}) (when-some [selected (:selected? x-metadata)] {:selected selected}) (select-keys x-metadata [:breakout-position :order-by-position :filter-positions]))))
@@ -54566,17 +54564,26 @@
`:abstract?` (default = false)
(dashboard-card/create-dashboard-cards! (map #(assoc % :dashboard_id dashboard-id) to-create))) (when (seq to-update) (doseq [update-card to-update] - (dashboard-card/update-dashboard-card! update-card (id->current-card (:id update-card))))))) + (dashboard-card/update-dashboard-card! update-card (id->current-card (:id update-card)))))))

Given a list of dashcards, remove any dashcard that references cards that are either archived or not exist.

+
(defn- remove-invalid-dashcards
+  [dashcards]
+  (let [card-ids          (set (keep :card_id dashcards))
+        active-card-ids   (when-let [card-ids (seq card-ids)]
+                            (t2/select-pks-set :model/Card :id [:in card-ids] :archived false))
+        inactive-card-ids (set/difference card-ids active-card-ids)]
+   (remove #(contains? inactive-card-ids (:card_id %)) dashcards)))
(defmethod revision/revert-to-revision! :model/Dashboard
   [_model dashboard-id _user-id serialized-dashboard]
   ;; Update the dashboard description / name / permissions
   (t2/update! :model/Dashboard dashboard-id (dissoc serialized-dashboard :cards :tabs))
   ;; Now update the tabs and cards as needed
-  (let [serialized-cards          (:cards serialized-dashboard)
+  (let [serialized-dashcards      (:cards serialized-dashboard)
         current-tabs              (t2/select-fn-vec #(dissoc (t2.realize/realize %) :created_at :updated_at :entity_id :dashboard_id)
                                                     :model/DashboardTab :dashboard_id dashboard-id)
         {:keys [old->new-tab-id]} (dashboard-tab/do-update-tabs! dashboard-id current-tabs (:tabs serialized-dashboard))
-        serialized-cards          (cond->> serialized-cards
+        serialized-dashcards      (cond->> serialized-dashcards
+                                    true
+                                    remove-invalid-dashcards
                                     ;; in case reverting result in new tabs being created,
                                     ;; we need to remap the tab-id
                                     (seq old->new-tab-id)
@@ -54584,7 +54591,7 @@ 
`:abstract?` (default = false)
(if-let [new-tab-id (get old->new-tab-id (:dashboard_tab_id card))] (assoc card :dashboard_tab_id new-tab-id) card))))] - (revert-dashcards dashboard-id serialized-cards)) + (revert-dashcards dashboard-id serialized-dashcards)) serialized-dashboard)
(defmethod revision/diff-strings :model/Dashboard
   [_model prev-dashboard dashboard]
@@ -55114,11 +55121,11 @@ 
`:abstract?` (default = false)
(when (seq dashboard-cards) (t2/with-transaction [_conn] (let [dashboard-card-ids (t2/insert-returning-pks! - DashboardCard - (for [dashcard dashboard-cards] - (merge {:parameter_mappings [] - :visualization_settings {}} - (dissoc dashcard :id :created_at :updated_at :entity_id :series :card :collection_authority_level))))] + DashboardCard + (for [dashcard dashboard-cards] + (merge {:parameter_mappings [] + :visualization_settings {}} + (dissoc dashcard :id :created_at :updated_at :entity_id :series :card :collection_authority_level))))] ;; add series to the DashboardCard (update-dashboard-cards-series! (zipmap dashboard-card-ids (map #(get % :series []) dashboard-cards))) ;; return the full DashboardCard