(ns metabase.channel.email.result-attachment (:require [clojure.java.io :as io] [java-time.api :as t] [metabase.driver :as driver] [metabase.driver.util :as driver.u] [metabase.lib.schema.id :as lib.schema.id] [metabase.query-processor.store :as qp.store] [metabase.query-processor.streaming :as qp.streaming] [metabase.query-processor.streaming.interface :as qp.si] [metabase.util.date-2 :as u.date] [metabase.util.i18n :refer [tru]] [metabase.util.malli :as mu] [metabase.util.malli.schema :as ms]) (:import (java.io File IOException OutputStream))) | |
(set! *warn-on-reflection* true) | |
For legacy compatibility. Takes QP results in the normal TODO -- this function is provided mainly because rewriting all the Pulse/Alert code to stream results directly was a lot of work. I intend to rework that code so we can stream directly to the correct export format(s) at some point in the future; for now, this function is a stopgap. Results are streamed synchronously. Caller is responsible for closing | (mu/defn- stream-api-results-to-export-format [^OutputStream os :- (ms/InstanceOfClass OutputStream) {:keys [export-format format-rows? pivot?], :as _options} :- [:map [:export-format :keyword] [:format-rows? {:optional true} [:maybe :boolean]] [:pivot? {:optional true} [:maybe :boolean]]] {{:keys [rows]} :data, database-id :database_id, :as results} :- [:map [:database_id ::lib.schema.id/database]]] ;; make sure Database/driver info is available for the streaming results writers -- they might need this in order to ;; get timezone information when writing results (driver/with-driver (driver.u/database->driver database-id) (qp.store/with-metadata-provider database-id (let [w (qp.si/streaming-results-writer export-format os) cols (-> results :data :cols) viz-settings (-> results :data :viz-settings) [ordered-cols output-order] (qp.streaming/order-cols cols viz-settings) viz-settings' (assoc viz-settings :output-order output-order)] (qp.si/begin! w (-> results (assoc-in [:data :format-rows?] format-rows?) (assoc-in [:data :pivot?] pivot?) (assoc-in [:data :ordered-cols] ordered-cols)) viz-settings') (dorun (map-indexed (fn [i row] (qp.si/write-row! w row i ordered-cols viz-settings')) rows)) (qp.si/finish! w results))))) |
Separate from | (defn- create-temp-file [suffix] (doto (File/createTempFile "metabase_attachment" suffix) .deleteOnExit)) |
Tries to create a temp file, will give the users a better error message if we are unable to create the temp file | (defn- create-temp-file-or-throw [suffix] (try (create-temp-file suffix) (catch IOException e (let [ex-msg (tru "Unable to create temp file in `{0}` for email attachments " (System/getProperty "java.io.tmpdir"))] (throw (IOException. ex-msg e)))))) |
(defn- create-result-attachment-map [export-type card-name ^File attachment-file] (let [{:keys [content-type]} (qp.si/stream-options export-type)] {:type :attachment :content-type content-type :file-name (format "%s_%s.%s" (or card-name "query_result") (u.date/format (t/zoned-date-time)) (name export-type)) :content (-> attachment-file .toURI .toURL) :description (format "More results for '%s'" card-name)})) | |
Create result attachments for an email. | (defn result-attachment [{{card-name :name format-rows :format_rows pivot-results :pivot_results :as card} :card result :result :as part}] (when (pos-int? (:row_count result)) (let [realize-data-rows (requiring-resolve 'metabase.channel.shared/realize-data-rows) result (:result (realize-data-rows part))] [(when-let [temp-file (and (:include_csv card) (create-temp-file-or-throw "csv"))] (with-open [os (io/output-stream temp-file)] (stream-api-results-to-export-format os {:export-format :csv :format-rows? format-rows :pivot? pivot-results} result)) (create-result-attachment-map "csv" card-name temp-file)) (when-let [temp-file (and (:include_xls card) (create-temp-file-or-throw "xlsx"))] (with-open [os (io/output-stream temp-file)] (stream-api-results-to-export-format os {:export-format :xlsx :format-rows? format-rows :pivot? pivot-results} result)) (create-result-attachment-map "xlsx" card-name temp-file))]))) |