(ns metabase.xrays.automagic-dashboards.util (:require [buddy.core.codecs :as codecs] [clojure.string :as str] [medley.core :as m] [metabase.analyze.core :as analyze] [metabase.legacy-mbql.predicates :as mbql.preds] [metabase.legacy-mbql.schema :as mbql.s] [metabase.legacy-mbql.util :as mbql.u] [metabase.lib.util.match :as lib.util.match] [metabase.models.interface :as mi] [metabase.util :as u] [metabase.util.json :as json] [metabase.util.log :as log] [metabase.util.malli :as mu] [metabase.util.malli.schema :as ms] [ring.util.codec :as codec] [toucan2.core :as t2])) | |
| (defn field-isa? [{:keys [base_type semantic_type]} t] (or (isa? (keyword semantic_type) t) (isa? (keyword base_type) t))) |
Workaround for our leaky type system which conflates types with properties. | (defn key-col? [{:keys [base_type semantic_type name]}] (and (isa? base_type :type/Number) (or (#{:type/PK :type/FK} semantic_type) (let [name (u/lower-case-en name)] (or (= name "id") (str/starts-with? name "id_") (str/ends-with? name "_id")))))) |
filter | (defn filter-tables [tablespec tables] (filter #(-> % :entity_type (isa? tablespec)) tables)) |
Is metric a saved metric? | (def ^{:arglists '([metric]) :doc } saved-metric? (partial mbql.u/is-clause? :metric)) |
Is this a custom expression? | (def ^{:arglists '([metric]) :doc } custom-expression? (partial mbql.u/is-clause? :aggregation-options)) |
Is this an adhoc metric? | (def ^{:arglists '([metric]) :doc } adhoc-metric? (complement (some-fn saved-metric? custom-expression?))) |
Encode given object as base-64 encoded JSON. | (def ^{:arglists '([x]) :doc "Base64 encode"} encode-base64-json (comp codec/base64-encode codecs/str->bytes json/encode)) |
(mu/defn field-reference->id :- [:maybe [:or ms/NonBlankString ms/PositiveInt]] "Extract field ID from a given field reference form." [clause] (lib.util.match/match-one clause [:field id _] id)) | |
(mu/defn collect-field-references :- [:maybe [:sequential mbql.s/field]] "Collect all `:field` references from a given form." [form] (lib.util.match/match form :field &match)) | |
(mu/defn ->field :- [:maybe (ms/InstanceOf :model/Field)] "Return `Field` instance for a given ID or name in the context of root." [{{result-metadata :result_metadata} :source, :as root} field-id-or-name-or-clause :- [:or ms/PositiveInt ms/NonBlankString [:fn mbql.preds/Field?]]] (let [id-or-name (if (sequential? field-id-or-name-or-clause) (field-reference->id field-id-or-name-or-clause) field-id-or-name-or-clause)] (or ;; Handle integer Field IDs. (when (integer? id-or-name) (t2/select-one :model/Field :id id-or-name)) ;; handle field string names. Only if we have result metadata. (Not sure why) (when (string? id-or-name) (when-not result-metadata (log/warn "Warning: Automagic analysis context is missing result metadata. Unable to resolve Fields by name.")) (when-let [field (m/find-first #(= (:name %) id-or-name) result-metadata)] (as-> field field (update field :base_type keyword) (update field :semantic_type keyword) (mi/instance :model/Field field) (analyze/run-classifiers field {})))) ;; otherwise this isn't returning something, and that's probably an error. Log it. (log/warnf "Cannot resolve Field %s in automagic analysis context\n%s" field-id-or-name-or-clause (u/pprint-to-str root))))) | |