diff --git a/.dir-locals.el b/.dir-locals.el index 2abef28921480f743fde18818ed00d030d3c81ad..f1800ab898184cca8799238cfeef0cff65fc6dfe 100644 --- a/.dir-locals.el +++ b/.dir-locals.el @@ -16,10 +16,8 @@ (catch-api-exceptions 0) (check 1) (checkp 1) - (cond-as-> 2) (context 2) (create-database-definition 1) - (engine-case 0) (execute-query 1) (execute-sql! 2) (expect 0) @@ -28,25 +26,18 @@ (expect-with-all-engines 0) (expect-with-engine 1) (expect-with-engines 1) - (ins 1) (let-400 1) (let-404 1) (let-500 1) - (macrolet 1) (match 1) (match-$ 1) - (org-perms-case 1) (post-select 1) (pre-cascade-delete 1) (pre-insert 1) (pre-update 1) (project 1) (qp-expect-with-engines 1) - (query-with-temp-db 1) (resolve-private-fns 1) (select 1) - (subselect 1) (sync-in-context 2) - (upd 2) - (when-testing-engine 1) - (with-credentials 1))))))) + (when-testing-engine 1))))))) diff --git a/src/metabase/api/common/internal.clj b/src/metabase/api/common/internal.clj index 05b3710488cf041b0852061369d4434fa27bf3f1..1a84cb5bf22c291a0f887aff842d9c02f87fd5dd 100644 --- a/src/metabase/api/common/internal.clj +++ b/src/metabase/api/common/internal.clj @@ -181,11 +181,12 @@ (typify-route \"/:id/card\") -> [\"/:id/card\" :id #\"[0-9]+\"]" [route] - (if (vector? route) route - (let [arg-types (->> (route-arg-keywords route) - typify-args)] - (if (empty? arg-types) route - (apply vector route arg-types))))) + (if (vector? route) + route + (let [arg-types (typify-args (route-arg-keywords route))] + (if (empty? arg-types) + route + (apply vector route arg-types))))) ;;; ## ROUTE ARG AUTO PARSING @@ -195,11 +196,11 @@ that can be used in a `let` form." [arg-symbol] (when (symbol? arg-symbol) - (some-> (arg-type arg-symbol) ; :int - *auto-parse-types* ; {:parser ... } - :parser ; Integer/parseInt + (some-> (arg-type arg-symbol) ; :int + *auto-parse-types* ; {:parser ... } + :parser ; Integer/parseInt ((fn [parser] `(when ~arg-symbol (~parser ~arg-symbol)))) ; (when id (Integer/parseInt id)) - ((partial vector arg-symbol))))) ; [id (Integer/parseInt id)] + ((partial vector arg-symbol))))) ; [id (Integer/parseInt id)] (defmacro auto-parse "Create a `let` form that applies corresponding parse-fn for any symbols in ARGS that are present in `*auto-parse-types*`." diff --git a/src/metabase/api/database.clj b/src/metabase/api/database.clj index d3613b50e0f77da5566ec19914c0e6b1776c7b8f..11c20e44bc261e03f3c566273019dfa306eb79f4 100644 --- a/src/metabase/api/database.clj +++ b/src/metabase/api/database.clj @@ -115,8 +115,7 @@ details (assoc details :password (get-in database [:details :password]))) conn-error (test-database-connection engine details) - is_full_sync (if (nil? is_full_sync) - nil + is_full_sync (when-not (nil? is_full_sync) (boolean is_full_sync))] (if-not conn-error ;; no error, proceed with update diff --git a/src/metabase/api/tiles.clj b/src/metabase/api/tiles.clj index e4bbf05e8dd1a36a62d12f3e9753fb2df8370482..62afdc0b8cec4f3ca3da4f92214ce0691110f98a 100644 --- a/src/metabase/api/tiles.clj +++ b/src/metabase/api/tiles.clj @@ -81,7 +81,7 @@ (.setColor graphics color-white) (.fillRect graphics (tile-pixel :x) (tile-pixel :y) pin-size pin-size) (.setColor graphics color-blue) - (.fillRect graphics (+ 1 (tile-pixel :x)) (+ 1 (tile-pixel :y)) (- pin-size 2) (- pin-size 2)))) + (.fillRect graphics (inc (tile-pixel :x)) (inc (tile-pixel :y)) (- pin-size 2) (- pin-size 2)))) (catch Throwable e (.printStackTrace e)) (finally diff --git a/src/metabase/driver.clj b/src/metabase/driver.clj index 61709800a98a29961368d5f7d86d175f47436bdf..5270cc4ada289ac4ee3bfa148c24579f23efce29 100644 --- a/src/metabase/driver.clj +++ b/src/metabase/driver.clj @@ -221,12 +221,13 @@ (filter identity) (take max-sync-lazy-seq-results)) field-values-count (count field-values)] - (if (= field-values-count 0) 0 - (int (math/round (/ (->> field-values - (map str) - (map count) - (reduce +)) - field-values-count)))))) + (if (zero? field-values-count) + 0 + (int (math/round (/ (->> field-values + (map str) + (map count) + (reduce +)) + field-values-count)))))) (def IDriverDefaultsMixin diff --git a/src/metabase/driver/druid/query_processor.clj b/src/metabase/driver/druid/query_processor.clj index 3ece09b11c5337105e9038376f8b1338de36959c..eb6a8406a3f34272b72ca20df0656106b3f55ea2 100644 --- a/src/metabase/driver/druid/query_processor.clj +++ b/src/metabase/driver/druid/query_processor.clj @@ -132,7 +132,7 @@ (defn- handle-aggregation [query-type {{ag-type :aggregation-type, ag-field :field} :aggregation} druid-query] (when (isa? query-type ::ag-query) (merge druid-query - (let [ag-type (if (= ag-type :rows) nil ag-type)] + (let [ag-type (when-not (= ag-type :rows) ag-type)] (match [ag-type ag-field] ;; For 'distinct values' queries (queries with a breakout by no aggregation) just aggregate by count, but name it :___count so it gets discarded automatically [nil nil] {:aggregations [(ag:count :___count)]} diff --git a/src/metabase/driver/generic_sql.clj b/src/metabase/driver/generic_sql.clj index 4198e5f8d7f8fee84cc31ed2a70517fe498dfe32..87586ad86d4f8057584661d881a184dcb6554122 100644 --- a/src/metabase/driver/generic_sql.clj +++ b/src/metabase/driver/generic_sql.clj @@ -252,7 +252,7 @@ 0))) (defn- url-percentage [url-count total-count] - (if (and total-count (> total-count 0) url-count) + (if (and total-count (pos? total-count) url-count) (float (/ url-count total-count)) 0.0)) @@ -367,13 +367,11 @@ (defn- describe-table-fks [driver database table] (with-metadata [metadata driver database] - (set (->> (.getImportedKeys metadata nil (:schema table) (:name table)) - jdbc/result-set-seq - (mapv (fn [result] - {:fk-column-name (:fkcolumn_name result) - :dest-table {:name (:pktable_name result) - :schema (:pktable_schem result)} - :dest-column-name (:pkcolumn_name result)})))))) + (set (for [result (jdbc/result-set-seq (.getImportedKeys metadata nil (:schema table) (:name table)))] + {:fk-column-name (:fkcolumn_name result) + :dest-table {:name (:pktable_name result) + :schema (:pktable_schem result)} + :dest-column-name (:pkcolumn_name result)})))) (defn analyze-table diff --git a/src/metabase/driver/mongo.clj b/src/metabase/driver/mongo.clj index 61d7da4df8854e4caf12e3a02413f5660ad41db7..9dccabc2dd56b89cfa88f95728fa9a906e1ba94e 100644 --- a/src/metabase/driver/mongo.clj +++ b/src/metabase/driver/mongo.clj @@ -1,6 +1,7 @@ (ns metabase.driver.mongo "MongoDB Driver." - (:require [clojure.set :as set] + (:require (clojure [set :as set] + [string :as s]) [clojure.tools.logging :as log] [cheshire.core :as json] (monger [collection :as mc] @@ -74,40 +75,42 @@ fields (recur more-keys (update fields k (partial update-field-attrs (k field-value))))))) +(defn- safe-inc [n] + (inc (or n 0))) + (defn- update-field-attrs [field-value field-def] - (let [safe-inc #(inc (or % 0))] - (-> field-def - (update :count safe-inc) - (update :len #(if (string? field-value) - (+ (or % 0) (count field-value)) - %)) - (update :types (fn [types] - (update types (type field-value) safe-inc))) - (update :special-types (fn [special-types] - (if-let [st (val->special-type field-value)] - (update special-types st safe-inc) - special-types))) - (update :nested-fields (fn [nested-fields] - (if (isa? (type field-value) clojure.lang.IPersistentMap) - (find-nested-fields field-value nested-fields) - nested-fields)))))) + (-> field-def + (update :count safe-inc) + (update :len #(if (string? field-value) + (+ (or % 0) (count field-value)) + %)) + (update :types (fn [types] + (update types (type field-value) safe-inc))) + (update :special-types (fn [special-types] + (if-let [st (val->special-type field-value)] + (update special-types st safe-inc) + special-types))) + (update :nested-fields (fn [nested-fields] + (if (isa? (type field-value) clojure.lang.IPersistentMap) + (find-nested-fields field-value nested-fields) + nested-fields))))) (defn- describe-table-field [field-kw field-info] ;; TODO: indicate preview-display status based on :len (cond-> {:name (name field-kw) - :base-type (->> (into [] (:types field-info)) + :base-type (->> (vec (:types field-info)) (sort-by second) last first driver/class->base-type)} - (= :_id field-kw) (assoc :pk? true) - (:special-types field-info) (assoc :special-type (->> (into [] (:special-types field-info)) - (filter #(not (nil? (first %)))) - (sort-by second) - last - first)) - (:nested-fields field-info) (assoc :nested-fields (set (for [field (keys (:nested-fields field-info))] - (describe-table-field field (field (:nested-fields field-info)))))))) + (= :_id field-kw) (assoc :pk? true) + (:special-types field-info) (assoc :special-type (->> (vec (:special-types field-info)) + (filter #(not (nil? (first %)))) + (sort-by second) + last + first)) + (:nested-fields field-info) (assoc :nested-fields (set (for [field (keys (:nested-fields field-info))] + (describe-table-field field (field (:nested-fields field-info)))))))) (defn- describe-database [database] (with-mongo-connection [^com.mongodb.DB conn database] @@ -154,7 +157,7 @@ name-components (rest (field/qualified-name-components field))] (assert (seq name-components)) (for [row (mq/with-collection *mongo-connection* (:name table) - (mq/fields [(apply str (interpose "." name-components))]))] + (mq/fields [(s/join \. name-components)]))] (get-in row (map keyword name-components)))))) diff --git a/src/metabase/driver/mongo/query_processor.clj b/src/metabase/driver/mongo/query_processor.clj index b6dc241eab6e6e4506b9a9c9ed73c5afef6837b5..bd9ea858bd25f4e5c83d9213ec527676f7ad3a19 100644 --- a/src/metabase/driver/mongo/query_processor.clj +++ b/src/metabase/driver/mongo/query_processor.clj @@ -66,7 +66,7 @@ (defn- field->name "Return a single string name for FIELD. For nested fields, this creates a combined qualified name." ^String [^Field field, ^String separator] - (apply str (interpose separator (rest (qualified-name-components field))))) + (s/join separator (rest (qualified-name-components field)))) (defmacro ^:private mongo-let {:style/indent 1} diff --git a/src/metabase/driver/sqlite.clj b/src/metabase/driver/sqlite.clj index 0f080076bf79d81ac0248c72420494985e0d79b7..d65bc4509ff525bd0a216d742c57bfbf8055f681 100644 --- a/src/metabase/driver/sqlite.clj +++ b/src/metabase/driver/sqlite.clj @@ -1,5 +1,6 @@ (ns metabase.driver.sqlite - (:require [clojure.set :as set] + (:require (clojure [set :as set] + [string :as s]) (honeysql [core :as hsql] [format :as hformat]) [metabase.config :as config] @@ -32,7 +33,7 @@ ;; register the SQLite concatnation operator `||` with HoneySQL as `sqlite-concat` ;; (hsql/format (hsql/call :sqlite-concat :a :b)) -> "(a || b)" (defmethod hformat/fn-handler "sqlite-concat" [_ & args] - (str "(" (apply str (interpose " || " (map hformat/to-sql args))) ")")) + (str "(" (s/join " || " (map hformat/to-sql args)) ")")) (def ^:private ->date (partial hsql/call :date)) (def ^:private ->datetime (partial hsql/call :datetime)) diff --git a/src/metabase/events.clj b/src/metabase/events.clj index 763e23e692b7c6e46ae66c884125848accec1bcc..d5e7b0e32d94a601440198b2f69b372991896635 100644 --- a/src/metabase/events.clj +++ b/src/metabase/events.clj @@ -53,7 +53,7 @@ (def ^:private events-publication "Publication for general events channel. Expects a map as input and the map must have a `:topic` key." - (async/pub events-channel #(:topic %))) + (async/pub events-channel :topic)) (defn publish-event "Publish an item into the events stream. diff --git a/src/metabase/events/metabot_lifecycle.clj b/src/metabase/events/metabot_lifecycle.clj index bc6406c01499c6322ae0ca1a55b81606441bc0ba..c93f8a2ddf532dd223b5551c83f3734bb60d0f8c 100644 --- a/src/metabase/events/metabot_lifecycle.clj +++ b/src/metabase/events/metabot_lifecycle.clj @@ -30,7 +30,7 @@ (let [{:keys [slack-token metabot-enabled]} object] (cond (and (contains? object :metabot-enabled) - (not (= "true" metabot-enabled))) (metabot/stop-metabot!) + (not= "true" metabot-enabled)) (metabot/stop-metabot!) (and (contains? object :slack-token) (seq slack-token)) (metabot/start-metabot!))) (catch Throwable e diff --git a/src/metabase/events/notifications.clj b/src/metabase/events/notifications.clj index 43cd6ed75cbaec0e1e6c4b17743c0ee7f1f22785..f01b84f4c02630cc41532a15abdf9922a6c363bc 100644 --- a/src/metabase/events/notifications.clj +++ b/src/metabase/events/notifications.clj @@ -41,24 +41,26 @@ ;; if we have no dependencies on cards then do nothing deps-by-model ;; otherwise pull out dependent card ids and add dashboard/pulse dependencies - (let [card-ids (mapv :model_id (get deps-by-model "Card"))] + (let [card-ids (map :model_id (get deps-by-model "Card"))] (assoc deps-by-model - "Dashboard" (for [dashcard (db/select [DashboardCard :dashboard_id], :card_id [:in card-ids])] - (set/rename-keys dashcard {:dashboard_id :model_id})) - "Pulse" (for [pulsecard (db/select [PulseCard :pulse_id], :card_id [:in card-ids])] - (set/rename-keys pulsecard {:pulse_id :model_id})))))) + "Dashboard" (when (seq card-ids) + (for [dashcard (db/select [DashboardCard :dashboard_id], :card_id [:in card-ids])] + (set/rename-keys dashcard {:dashboard_id :model_id}))) + "Pulse" (when (seq card-ids) + (for [pulsecard (db/select [PulseCard :pulse_id], :card_id [:in card-ids])] + (set/rename-keys pulsecard {:pulse_id :model_id}))))))) (defn- pull-dependencies [model model-id] (when-let [deps (db/select [Dependency :model :model_id] :dependent_on_model model :dependent_on_id model-id)] - (let [deps-by-model (-> (group-by :model deps) - add-objects-dependent-on-cards) + (let [deps-by-model (add-objects-dependent-on-cards (group-by :model deps)) deps-with-details (for [model (keys deps-by-model) :let [ids (mapv :model_id (get deps-by-model model))]] ;; TODO: this is slightly dangerous because we assume :name and :creator_id are available - (for [object (db/select [(model->entity (keyword model)) :id :name :creator_id] - :id [:in ids])] + (for [object (when (seq ids) + (db/select [(model->entity (keyword model)) :id :name :creator_id] + :id [:in ids]))] (assoc object :model model)))] ;; we end up with a list of lists, so flatten before returning (flatten deps-with-details)))) diff --git a/src/metabase/metabot.clj b/src/metabase/metabot.clj index 938b8ce34852673c0b7c968ec8bb6d50ede46d12..a1df63eaaf97f845d8e8fe5c46cff53c484d360f 100644 --- a/src/metabase/metabot.clj +++ b/src/metabase/metabot.clj @@ -26,9 +26,9 @@ ([message m] (str message " " (keys-description m))) ([m] - (apply str (interpose ", " (sort (for [[k varr] m - :when (not (:unlisted (meta varr)))] - (str \` (name k) \`))))))) + (str/join ", " (sort (for [[k varr] m + :when (not (:unlisted (meta varr)))] + (str \` (name k) \`)))))) (defn- dispatch-fn [verb tag] (let [fn-map (into {} (for [[symb varr] (ns-interns *ns*) @@ -102,7 +102,7 @@ (throw (Exception. "Not Found")))) ;; If the card name comes without spaces, e.g. (show 'my 'wacky 'card) turn it into a string an recur: (show "my wacky card") ([word & more] - (show (apply str (interpose " " (cons word more)))))) + (show (str/join " " (cons word more))))) (defn meme:up-and-to-the-right diff --git a/src/metabase/models/dashboard_card.clj b/src/metabase/models/dashboard_card.clj index 2f82f8bef27e035ff657ae10135e2d357fda1db3..f36a73edf428d58ff75b8fd4768a97976409c40c 100644 --- a/src/metabase/models/dashboard_card.clj +++ b/src/metabase/models/dashboard_card.clj @@ -73,7 +73,7 @@ ;; first off, just delete all series on the dashboard card (we add them again below) (db/cascade-delete! DashboardCardSeries :dashboardcard_id id) ;; now just insert all of the series that were given to us - (when-not (empty? card-ids) + (when (seq card-ids) (let [cards (map-indexed (fn [i card-id] {:dashboardcard_id id, :card_id card-id, :position i}) card-ids)] diff --git a/src/metabase/models/field.clj b/src/metabase/models/field.clj index 5e95585bb6425b6d1456856c5d66cd00b7a0ad89..55c8a5f05122a05401c5a3279da5e1a538fed514 100644 --- a/src/metabase/models/field.clj +++ b/src/metabase/models/field.clj @@ -1,6 +1,6 @@ (ns metabase.models.field - (:require [clojure.data :as d] - [clojure.string :as s] + (:require (clojure [data :as d] + [string :as s]) [medley.core :as m] [metabase.db :as db] (metabase.models [common :as common] @@ -112,7 +112,7 @@ (defn qualified-name "Return a combined qualified name for FIELD, e.g. `table_name.parent_field_name.field_name`." [field] - (apply str (interpose \. (qualified-name-components field)))) + (s/join \. (qualified-name-components field))) (defn table "Return the `Table` associated with this `Field`." diff --git a/src/metabase/models/pulse.clj b/src/metabase/models/pulse.clj index 0044440aad10e3080083c48b17679240113b1ea2..e25d4590a4a98a300cb9a96a24787f66f4a1ed6c 100644 --- a/src/metabase/models/pulse.clj +++ b/src/metabase/models/pulse.clj @@ -62,7 +62,7 @@ ;; first off, just delete any cards associated with this pulse (we add them again below) (db/cascade-delete! PulseCard :pulse_id id) ;; now just insert all of the cards that were given to us - (when-not (empty? card-ids) + (when (seq card-ids) (let [cards (map-indexed (fn [idx itm] {:pulse_id id :card_id itm :position idx}) card-ids)] (db/insert-many! PulseCard cards)))) @@ -103,9 +103,11 @@ (let [new-channels (group-by (comp keyword :channel_type) channels) old-channels (group-by (comp keyword :channel_type) (db/select PulseChannel :pulse_id id)) handle-channel #(create-update-delete-channel! id (first (get new-channels %)) (first (get old-channels %)))] - (assert (= 0 (count (get new-channels nil))) "Cannot have channels without a :channel_type attribute") + (assert (zero? (count (get new-channels nil))) + "Cannot have channels without a :channel_type attribute") ;; for each of our possible channel types call our handler function - (dorun (map handle-channel (vec (keys pulse-channel/channel-types)))))) + (doseq [[channel-type _] pulse-channel/channel-types] + (handle-channel channel-type)))) (defn retrieve-pulse "Fetch a single `Pulse` by its ID value." diff --git a/src/metabase/models/user.clj b/src/metabase/models/user.clj index 783a8dfd3af9e11386e0ab4877c7dfa1491a1fde..94325d22bb508eb145445a99f0aceb551c9008b9 100644 --- a/src/metabase/models/user.clj +++ b/src/metabase/models/user.clj @@ -17,7 +17,7 @@ (not (s/blank? password)))) (assert (not (:password_salt user)) "Don't try to pass an encrypted password to (ins User). Password encryption is handled by pre-insert.") - (let [salt (.toString (java.util.UUID/randomUUID)) + (let [salt (str (java.util.UUID/randomUUID)) defaults {:date_joined (u/new-sql-timestamp) :last_login nil :is_staff true @@ -81,7 +81,7 @@ :email email-address :first_name first-name :last_name last-name - :password (if (not (nil? password)) + :password (if-not (nil? password) password (str (java.util.UUID/randomUUID))))] (when send-welcome @@ -95,7 +95,7 @@ (defn set-user-password! "Updates the stored password for a specified `User` by hashing the password with a random salt." [user-id password] - (let [salt (.toString (java.util.UUID/randomUUID)) + (let [salt (str (java.util.UUID/randomUUID)) password (creds/hash-bcrypt (str salt password))] ;; NOTE: any password change expires the password reset token (db/update! User user-id diff --git a/src/metabase/pulse/render.clj b/src/metabase/pulse/render.clj index de3efc45f59971ed6a42e392d82251417a3c798f..d11b61876eee29edd3d2a14e714410eb2c0efb5f 100644 --- a/src/metabase/pulse/render.clj +++ b/src/metabase/pulse/render.clj @@ -1,7 +1,7 @@ (ns metabase.pulse.render (:require [clojure.java.io :as io] (clojure [pprint :refer [cl-format]] - [string :refer [upper-case]]) + [string :as s]) [clojure.tools.logging :as log] (clj-time [coerce :as c] [core :as t] @@ -80,9 +80,9 @@ (style {:font-weight 400, :color \"white\"}) -> \"font-weight: 400; color: white;\"" [& style-maps] - (apply str (interpose " " (for [[k v] (into {} style-maps) - :let [v (if (keyword? v) (name v) v)]] - (str (name k) ": " v ";"))))) + (s/join " " (for [[k v] (into {} style-maps) + :let [v (if (keyword? v) (name v) v)]] + (str (name k) ": " v ";")))) (defn- datetime-field? @@ -109,7 +109,11 @@ :hour (f/unparse (f/formatter "h a - MMM YYYY") (c/from-long timestamp)) :week (str "Week " (f/unparse (f/formatter "w - YYYY") (c/from-long timestamp))) :month (f/unparse (f/formatter "MMMM YYYY") (c/from-long timestamp)) - :quarter (str "Q" (+ 1 (int (/ (t/month (c/from-long timestamp)) 3))) " - " (t/year (c/from-long timestamp))) + :quarter (str "Q" + (inc (int (/ (t/month (c/from-long timestamp)) + 3))) + " - " + (t/year (c/from-long timestamp))) :year (str timestamp) :hour-of-day (str timestamp) ; TODO: probably shouldn't even be showing sparkline for x-of-y groupings? :day-of-week (str timestamp) @@ -127,7 +131,8 @@ (t/within? (t/interval (t/minus interval-start interval) interval-start) date) last-interval-name)) (defn- start-of-this-week [] (-> (org.joda.time.LocalDate.) .weekOfWeekyear .roundFloorCopy .toDateTimeAtStartOfDay)) -(defn- start-of-this-quarter [] (t/date-midnight (year) (+ 1 (* 3 (Math/floor (/ (dec (month)) 3)))))) +(defn- start-of-this-quarter [] (t/date-midnight (year) (inc (* 3 (Math/floor (/ (dec (month)) + 3)))))) (defn- format-timestamp-relative "Formats timestamps with relative names (today, yesterday, this *, last *) based on column :unit, if possible, otherwie returns nil" @@ -223,15 +228,15 @@ [:table {:style (style {:padding-bottom :8px, :border-bottom (str "4px solid " color-gray-1)})} [:thead [:tr - (for [col-idx col-indexes :let [col (-> cols (nth col-idx))]] + (for [col-idx col-indexes :let [col (nth cols col-idx)]] [:th {:style (style bar-td-style bar-th-style {:min-width :60px})} - (h (upper-case (name (or (:display_name col) (:name col)))))]) + (h (s/upper-case (name (or (:display_name col) (:name col)))))]) (when bar-column [:th {:style (style bar-td-style bar-th-style {:width "99%"})}])]] [:tbody (map-indexed (fn [row-idx row] [:tr {:style (style {:color (if (odd? row-idx) color-gray-2 color-gray-3)})} - (for [col-idx col-indexes :let [col (-> cols (nth col-idx))]] + (for [col-idx col-indexes :let [col (nth cols col-idx)]] [:td {:style (style bar-td-style (when (and bar-column (= col-idx 1)) {:font-weight 700}))} (-> row (nth col-idx) (format-cell col) h)]) (when bar-column @@ -319,7 +324,7 @@ (let [ft-row (if (datetime-field? (first cols)) #(.getTime ^Date (u/->Timestamp %)) identity) - rows (if (> (ft-row (first (first rows))) + rows (if (> (ft-row (ffirst rows)) (ft-row (first (last rows)))) (reverse rows) rows) diff --git a/src/metabase/query_processor.clj b/src/metabase/query_processor.clj index 0cf39c682248ca63f2830c6acf48d5a3c767f3d5..4ae647ba714d42d3321165599a9eef7440da7985 100644 --- a/src/metabase/query_processor.clj +++ b/src/metabase/query_processor.clj @@ -577,7 +577,7 @@ {:arglists '([query options])} [query {:keys [executed_by]}] {:pre [(integer? executed_by)]} - (let [query-uuid (.toString (java.util.UUID/randomUUID)) + (let [query-uuid (str (java.util.UUID/randomUUID)) query-hash (hash query) query-execution {:uuid query-uuid :executor_id executed_by diff --git a/src/metabase/query_processor/resolve.clj b/src/metabase/query_processor/resolve.clj index b350942cc4506ec3a4a50e464f97894fcea5be02..55b8e49cd053fae7f319775c7dcaabef1378a557 100644 --- a/src/metabase/query_processor/resolve.clj +++ b/src/metabase/query_processor/resolve.clj @@ -166,10 +166,10 @@ (defn- collect-ids-with [f expanded-query-dict] (let [ids (transient #{})] - (->> expanded-query-dict - (walk/postwalk (fn [form] - (when-let [id (f form)] - (conj! ids id))))) + (walk/postwalk (fn [form] + (when-let [id (f form)] + (conj! ids id))) + expanded-query-dict) (persistent! ids))) (def ^:private collect-unresolved-field-ids (partial collect-ids-with unresolved-field-id)) @@ -186,7 +186,7 @@ Record `:table-ids` referenced in the Query." [expanded-query-dict] (loop [max-iterations 5, expanded-query-dict expanded-query-dict] - (when (< max-iterations 0) + (when (neg? max-iterations) (throw (Exception. "Failed to resolve fields: too many iterations."))) (let [field-ids (collect-unresolved-field-ids expanded-query-dict)] (if-not (seq field-ids) diff --git a/src/metabase/setup.clj b/src/metabase/setup.clj index 5336011eb62393637d1013cffdb4cc25d83ed364..37f3c671e90bc9356f8d80251e82573809ec4535 100644 --- a/src/metabase/setup.clj +++ b/src/metabase/setup.clj @@ -19,7 +19,7 @@ "Create and set a new `@setup-token`. Returns the newly created token." [] - (reset! setup-token (.toString (java.util.UUID/randomUUID)))) + (reset! setup-token (str (java.util.UUID/randomUUID)))) (defn token-clear "Clear the `@setup-token` if it exists and reset it to nil." diff --git a/src/metabase/sync_database/analyze.clj b/src/metabase/sync_database/analyze.clj index 98be0e459eb0de1b0af4ccb60c28947ec2958b1d..b888a2a0b8b85a6481e276c4be75209f681d3946 100644 --- a/src/metabase/sync_database/analyze.clj +++ b/src/metabase/sync_database/analyze.clj @@ -70,8 +70,8 @@ non-nil-values)] ;; TODO: eventually we can check for :nullable? based on the original values above (cond-> (assoc field-stats :values distinct-values) - (and (nil? (:special_type field)) - (< 0 (count distinct-values))) (assoc :special-type :category)))) + (and (nil? (:special_type field)) + (pos? (count distinct-values))) (assoc :special-type :category)))) (defn- test:no-preview-display "If FIELD's is textual and its average length is too great, mark it so it isn't displayed in the UI." @@ -117,11 +117,10 @@ (s/blank? val) (recur at-least-one-non-nil-value? more) ;; If val is non-nil, check that it's a JSON dictionary or array. We don't want to mark Fields containing other ;; types of valid JSON values as :json (e.g. a string representation of a number or boolean) - :else (let [val (json/parse-string val)] - (when (not (or (map? val) - (sequential? val))) - (throw (Exception.))) - (recur true more)))) + :else (do (u/prog1 (json/parse-string val) + (assert (or (map? <>) + (sequential? <>)))) + (recur true more)))) (catch Throwable _ false))) @@ -134,8 +133,7 @@ ;; this field isn't suited for this test field-stats ;; check for json values - (if-not (values-are-valid-json? (->> (driver/field-values-lazy-seq driver field) - (take driver/max-sync-lazy-seq-results))) + (if-not (values-are-valid-json? (take driver/max-sync-lazy-seq-results (driver/field-values-lazy-seq driver field))) field-stats (do (log/debug (u/format-color 'green "Field '%s' looks like it contains valid JSON objects. Setting special_type to :json." (field/qualified-name field))) @@ -193,7 +191,7 @@ :visibility_type (when (false? preview-display) :details-only) :special_type special-type)) ;; handle field values, setting them if applicable otherwise clearing them - (if (and id values (< 0 (count (filter identity values)))) + (if (and id values (pos? (count (filter identity values)))) (field-values/save-field-values! id values) (field-values/clear-field-values! id)))) diff --git a/src/metabase/task.clj b/src/metabase/task.clj index 3ee62d74efcda18121610400ec086059e2974b0d..10e76440173fb4f27336470ee3ad94fcfa928a96 100644 --- a/src/metabase/task.clj +++ b/src/metabase/task.clj @@ -34,7 +34,7 @@ (when-not @quartz-scheduler (log/debug "Starting Quartz Scheduler") ;; keep a reference to our scheduler - (reset! quartz-scheduler (-> (qs/initialize) qs/start)) + (reset! quartz-scheduler (qs/start (qs/initialize))) ;; look for job/trigger definitions (find-and-load-tasks!))) diff --git a/src/metabase/task/send_pulses.clj b/src/metabase/task/send_pulses.clj index 6e2100127b8b14e9b19da46094bb7ec4e6fe171a..d709b295098334b8c2185d6552f840038a4ae910 100644 --- a/src/metabase/task/send_pulses.clj +++ b/src/metabase/task/send_pulses.clj @@ -48,7 +48,7 @@ curr-hour (time/hour now) ;; joda time produces values of 1-7 here (Mon -> Sun) and we subtract 1 from it to ;; make the values zero based to correspond to the indexes in pulse-channel/days-of-week - curr-weekday (->> (- (time/day-of-week now) 1) + curr-weekday (->> (dec (time/day-of-week now)) (get pulse-channel/days-of-week) :id) curr-monthday (monthday now) diff --git a/src/metabase/task/sync_databases.clj b/src/metabase/task/sync_databases.clj index 00b75b9e8829720d9cda012a633d0c26fdf81d5f..f9cedbf5cb9ba248dece1f0725f0210e826a5aca 100644 --- a/src/metabase/task/sync_databases.clj +++ b/src/metabase/task/sync_databases.clj @@ -21,7 +21,7 @@ (doseq [database (db/select Database, :is_sample false)] ; skip Sample Dataset DB (try ;; NOTE: this happens synchronously for now to avoid excessive load if there are lots of databases - (if-not (and (= 0 (t/hour (t/now))) + (if-not (and (zero? (t/hour (t/now))) (driver/driver-supports? (driver/engine->driver (:engine database)) :dynamic-schema)) ;; most of the time we do a quick sync and avoid the lengthy analysis process (sync-database/sync-database! database :full-sync? false) diff --git a/src/metabase/util.clj b/src/metabase/util.clj index 4c5aaf28b062641ce7492cf39cfc751f0aa75686..d933b3e449ef902529dd318c9d6b5563fd5a1617 100644 --- a/src/metabase/util.clj +++ b/src/metabase/util.clj @@ -273,7 +273,7 @@ (loop [acc []] (if-let [line (.readLine this)] (recur (conj acc line)) - (apply str (interpose "\n" acc)))))) + (s/join "\n" acc))))) ;; H2 -- See also http://h2database.com/javadoc/org/h2/jdbc/JdbcClob.html org.h2.jdbc.JdbcClob @@ -472,8 +472,8 @@ filleds (int (* percent-done meter-width)) blanks (- meter-width filleds)] (str "[" - (apply str (repeat filleds "*")) - (apply str (repeat blanks "·")) + (s/join (repeat filleds "*")) + (s/join (repeat blanks "·")) (format "] %s %3.0f%%" (percent-done->emoji percent-done) (* percent-done 100.0))))))) (defn filtered-stacktrace @@ -544,26 +544,6 @@ [timeout-ms & body] `(deref-with-timeout (future ~@body) ~timeout-ms)) -(defmacro cond-as-> - "Anaphoric version of `cond->`. Binds EXPR to NAME through a series - of pairs of TEST and FORM. NAME is successively bound to the value - of each FORM whose TEST succeeds. - - (defn maybe-wrap-fn [before after f] - (as-> f <> - (fn? before) (fn [] (before) (<>)) - (fn? after) (fn [] (try (<>) - (finally (after))))))" - {:arglists '([expr nm tst form & more])} - [expr nm & clauses] - {:pre [(even? (count clauses))]} - `(let [~nm ~expr - ~@(apply concat (for [[tst form] (partition 2 clauses)] - [nm `(if ~tst - ~form - ~nm)]))] - ~nm)) - (defn round-to-decimals "Round (presumabily floating-point) NUMBER to DECIMAL-PLACE. Returns a `Double`. @@ -613,10 +593,10 @@ Downcase the name and replace non-alphanumeric characters with underscores." ^String [s] (when (seq s) - (apply str (for [c (s/lower-case (name s))] - (if (contains? slugify-valid-chars c) - c - \_))))) + (s/join (for [c (s/lower-case (name s))] + (if (contains? slugify-valid-chars c) + c + \_))))) (defn do-with-auto-retries "Execute F, a function that takes no arguments, and return the results. diff --git a/src/metabase/util/infer_spaces.clj b/src/metabase/util/infer_spaces.clj index f0295e14229ec6fd5ca1c768354bdcdf8b1ce11f..6c36640e73b77a7e2cc0adf5c5641a8395cee67a 100644 --- a/src/metabase/util/infer_spaces.clj +++ b/src/metabase/util/infer_spaces.clj @@ -3,60 +3,60 @@ [clojure.string :as s]) (:import java.lang.Math)) -; ported from https://stackoverflow.com/questions/8870261/how-to-split-text-without-spaces-into-list-of-words/11642687#11642687 +;; ported from https://stackoverflow.com/questions/8870261/how-to-split-text-without-spaces-into-list-of-words/11642687#11642687 (def ^:const ^:private special-words ["checkins"]) -; # Build a cost dictionary, assuming Zipf's law and cost = -math.log(probability). +;; # Build a cost dictionary, assuming Zipf's law and cost = -math.log(probability). (def ^:private words (concat special-words (s/split-lines (slurp (io/resource "words-by-frequency.txt"))))) -; wordcost = dict((k, log((i+1)*log(len(words)))) for i,k in enumerate(words)) +;; wordcost = dict((k, log((i+1)*log(len(words)))) for i,k in enumerate(words)) (def ^:private word-cost (apply hash-map (flatten (map-indexed - (fn [idx word] [word (Math/log (* (+ idx 1) (Math/log (count words))))]) words)))) + (fn [idx word] [word (Math/log (* (inc idx) (Math/log (count words))))]) words)))) -; maxword = max(len(x) for x in words) +;; maxword = max(len(x) for x in words) (def ^:private max-word (apply max (map count words))) -; def infer_spaces(s): -; """Uses dynamic programming to infer the location of spaces in a string -; without spaces.""" - -; # Find the best match for the i first characters, assuming cost has -; # been built for the i-1 first characters. -; # Returns a pair (match_cost, match_length). -; def best_match(i): -; candidates = enumerate(reversed(cost[max(0, i-maxword):i])) -; return min((c + wordcost.get(s[i-k-1:i], 9e999), k+1) for k,c in candidates) +;; def infer_spaces(s): +;; """Uses dynamic programming to infer the location of spaces in a string +;; without spaces.""" +; +;; # Find the best match for the i first characters, assuming cost has +;; # been built for the i-1 first characters. +;; # Returns a pair (match_cost, match_length). +;; def best_match(i): +;; candidates = enumerate(reversed(cost[max(0, i-maxword):i])) +;; return min((c + wordcost.get(s[i-k-1:i], 9e999), k+1) for k,c in candidates) (defn- best-match [i s cost] (let [candidates (reverse (subvec cost (max 0 (- i max-word)) i))] - (apply min-key first (map-indexed (fn [k c] [(+ c (get word-cost (subs s (- i k 1) i) 9e9999)) (+ k 1)]) candidates)))) + (apply min-key first (map-indexed (fn [k c] [(+ c (get word-cost (subs s (- i k 1) i) 9e9999)) (inc k)]) candidates)))) -; # Build the cost array. -; cost = [0] -; for i in range(1,len(s)+1): -; c,k = best_match(i) -; cost.append(c) +;; # Build the cost array. +;; cost = [0] +;; for i in range(1,len(s)+1): +;; c,k = best_match(i) +;; cost.append(c) (defn- build-cost-array [s] (loop [i 1 cost [0]] - (if-not (< i (+ 1 (count s))) + (if-not (< i (inc (count s))) cost - (recur (+ i 1) + (recur (inc i) (conj cost (first (best-match i s cost))))))) -; # Backtrack to recover the minimal-cost string. -; out = [] -; i = len(s) -; while i>0: -; c,k = best_match(i) -; assert c == cost[i] -; out.append(s[i-k:i]) -; i -= k -; -; return " ".join(reversed(out)) +;; # Backtrack to recover the minimal-cost string. +;; out = [] +;; i = len(s) +;; while i>0: +;; c,k = best_match(i) +;; assert c == cost[i] +;; out.append(s[i-k:i]) +;; i -= k +;; +;; return " ".join(reversed(out)) (defn infer-spaces "Splits a string with no spaces into words using magic" [input] @@ -64,7 +64,7 @@ cost (build-cost-array s)] (loop [i (count s) out []] - (if-not (> i 0) + (if-not (pos? i) (reverse out) (let [[c k] (best-match i s cost)] (recur (- i k) diff --git a/test/metabase/api/database_test.clj b/test/metabase/api/database_test.clj index 5f433ff72bd1e16c149cef4ea9705372f0fdc713..e405984a038365899b32f2c4d3d66e6041db1dd1 100644 --- a/test/metabase/api/database_test.clj +++ b/test/metabase/api/database_test.clj @@ -89,7 +89,7 @@ :is_full_sync false :organization_id nil :description nil - :features (into [] (driver/features (driver/engine->driver :postgres)))}) + :features (vec (driver/features (driver/engine->driver :postgres)))}) (Database (:id db))) diff --git a/test/metabase/middleware_test.clj b/test/metabase/middleware_test.clj index 76535003d14648144cc33d5d21863785c8f88715..5dfb07f29b5a1a62f0cabfdf6757c996ff1a703a 100644 --- a/test/metabase/middleware_test.clj +++ b/test/metabase/middleware_test.clj @@ -60,8 +60,7 @@ (auth-enforced-handler (mock/request :get "/anyurl"))) (defn- random-session-id [] - {:post [(string? %)]} - (.toString (java.util.UUID/randomUUID))) + (str (java.util.UUID/randomUUID))) ;; valid session ID (expect diff --git a/test/metabase/test/data/datasets.clj b/test/metabase/test/data/datasets.clj index c8e7e5e5737b87d0314aee4d5ca7a23da6f10c90..3fb58b4660e2442973412c7e0a081a143600072f 100644 --- a/test/metabase/test/data/datasets.clj +++ b/test/metabase/test/data/datasets.clj @@ -125,18 +125,6 @@ [expected actual] `(expect-with-engines all-valid-engines ~expected ~actual)) -(defmacro engine-case - "Case statement that switches off of the current dataset. - - (engine-case - :h2 ... - :postgres ...)" - [& pairs] - `(cond ~@(mapcat (fn [[engine then]] - (assert (contains? all-valid-engines engine)) - [`(= *engine* ~engine) - then]) - (partition 2 pairs)))) ;;; Load metabase.test.data.* namespaces for all available drivers (doseq [[engine _] (driver/available-drivers)] diff --git a/test/metabase/util_test.clj b/test/metabase/util_test.clj index f0ee9da16215c87b24efba1adfcc98da1ebe70c7..cc64814e045f864415883bb303c5dbd835a68b93 100644 --- a/test/metabase/util_test.clj +++ b/test/metabase/util_test.clj @@ -83,27 +83,6 @@ ((rpartial - 5 10) 8)) -;;; ## cond-as-> -(expect 100 - (cond-as-> 100 <>)) - -(expect 106 - (cond-as-> 100 <> - true (+ 1 <>) - false (+ 10 <>) - :ok (+ 5 <>))) - -(expect 101 - (cond-as-> 100 <> - (odd? <>) (inc <>) - (even? <>) (inc <>))) - -(expect 102 - (cond-as-> 100 <> - (even? <>) (inc <>) - (odd? <>) (inc <>))) - - ;;; TESTS FOR key-by (expect {1 {:id 1, :name "Rasta"}