Skip to content
Snippets Groups Projects
Commit fa4baacd authored by Stefano Dissegna's avatar Stefano Dissegna
Browse files

added excel export format

parent 0c6de8ca
Branches
Tags
No related merge requests found
......@@ -13,6 +13,8 @@ import * as Urls from "metabase/lib/urls";
import _ from "underscore";
import cx from "classnames";
const EXPORT_FORMATS = ["csv", "xlsx", "json"];
const QueryDownloadWidget = ({ className, card, result, uuid, token }) =>
<PopoverWithTrigger
triggerElement={
......@@ -22,7 +24,7 @@ const QueryDownloadWidget = ({ className, card, result, uuid, token }) =>
}
triggerClasses={cx(className, "text-brand-hover")}
>
<div className="p2" style={{ maxWidth: 300 }}>
<div className="p2" style={{ maxWidth: 320 }}>
<h4>Download</h4>
{ result.data.rows_truncated != null &&
<FieldSet className="my2 text-gold border-gold" legend="Warning">
......@@ -31,7 +33,7 @@ const QueryDownloadWidget = ({ className, card, result, uuid, token }) =>
</FieldSet>
}
<div className="flex flex-row mt2">
{["csv", "json"].map(type =>
{EXPORT_FORMATS.map(type =>
uuid ?
<PublicQueryButton key={type} type={type} uuid={uuid} className="mr1 text-uppercase text-default" />
: token ?
......
......@@ -78,7 +78,8 @@
[ring/ring-json "0.4.0"] ; Ring middleware for reading/writing JSON automatically
[stencil "0.5.0"] ; Mustache templates for Clojure
[toucan "1.0.2" ; Model layer, hydration, and DB utilities
:exclusions [honeysql]]]
:exclusions [honeysql]]
[dk.ative/docjure "1.11.0"]] ; Excel export
:repositories [["bintray" "https://dl.bintray.com/crate/crate"]] ; Repo for Crate JDBC driver
:plugins [[lein-environ "1.1.0"] ; easy access to environment variables
[lein-ring "0.11.0" ; start the HTTP server with 'lein ring server'
......
......@@ -427,6 +427,16 @@
:constraints nil
:context :csv-download))))
(defendpoint POST "/:card-id/query/xlsx"
"Run the query associated with a Card, and return its results as XLSX. Note that this expects the parameters as serialized JSON in the 'parameters' parameter"
[card-id parameters]
{parameters (s/maybe su/JSONString)}
(binding [cache/*ignore-cached-results* true]
(dataset-api/as-xlsx (run-query-for-card card-id
:parameters (json/parse-string parameters keyword)
:constraints nil
:context :xlsx-download))))
(defendpoint POST "/:card-id/query/json"
"Run the query associated with a Card, and return its results as JSON. Note that this expects the parameters as serialized JSON in the 'parameters' parameter"
[card-id parameters]
......
......@@ -3,6 +3,7 @@
(:require [clojure.data.csv :as csv]
[cheshire.core :as json]
[compojure.core :refer [GET POST]]
[dk.ative.docjure.spreadsheet :as spreadsheet]
[metabase.api.common :refer :all]
(toucan [db :as db]
[hydrate :refer [hydrate]])
......@@ -62,6 +63,24 @@
{:status 500
:body (:error response)}))
(defn as-xlsx
"Return an XLSX response containing the RESULTS of a query."
{:arglists '([results])}
[{{:keys [columns rows]} :data, :keys [status], :as response}]
(if (= status :completed)
;; successful query, send XLSX file
{:status 200
:body (let [wb (spreadsheet/create-workbook "Query result" (conj rows (mapv name columns)))
; note: byte array streams don't need to be closed
out (java.io.ByteArrayOutputStream.)]
(spreadsheet/save-workbook! out wb)
(java.io.ByteArrayInputStream. (.toByteArray out)))
:headers {"Content-Type" "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet; charset=utf-8"
"Content-Disposition" (str "attachment; filename=\"query_result_" (u/date->iso-8601) ".xlsx\"")}}
;; failed query, send error message
{:status 500
:body (:error response)}))
(defn as-json
"Return a JSON response containing the RESULTS of a query."
{:arglists '([results])}
......@@ -84,6 +103,14 @@
(read-check Database (:database query))
(as-csv (qp/dataset-query (dissoc query :constraints) {:executed-by *current-user-id*, :context :csv-download}))))
(defendpoint POST "/xlsx"
"Execute a query and download the result data as an XLSX file."
[query]
{query su/JSONString}
(let [query (json/parse-string query keyword)]
(read-check Database (:database query))
(as-xlsx (qp/dataset-query (dissoc query :constraints) {:executed-by *current-user-id*, :context :xlsx-download}))))
(defendpoint POST "/json"
"Execute a query and download the result data as a JSON file."
[query]
......
......@@ -279,6 +279,10 @@
[token & query-params]
(dataset-api/as-csv (run-query-for-unsigned-token (eu/unsign token) query-params, :constraints nil)))
(api/defendpoint GET "/card/:token/query/xlsx"
"Like `GET /api/embed/card/query`, but returns the results as XLSX."
[token & query-params]
(dataset-api/as-xlsx (run-query-for-unsigned-token (eu/unsign token) query-params, :constraints nil)))
(api/defendpoint GET "/card/:token/query/json"
"Like `GET /api/embed/card/query`, but returns the results as JSOn."
......
......@@ -87,6 +87,12 @@
{parameters (s/maybe su/JSONString)}
(dataset-api/as-csv (run-query-for-card-with-public-uuid uuid parameters, :constraints nil)))
(api/defendpoint GET "/card/:uuid/query/xlsx"
"Fetch a publically-accessible Card and return query results as XLSX. Does not require auth credentials. Public sharing must be enabled."
[uuid parameters]
{parameters (s/maybe su/JSONString)}
(dataset-api/as-xlsx (run-query-for-card-with-public-uuid uuid parameters, :constraints nil)))
;;; ------------------------------------------------------------ Public Dashboards ------------------------------------------------------------
......
......@@ -16,6 +16,7 @@
"Schema for valid values of QueryExecution `:context`."
(s/enum :ad-hoc
:csv-download
:xlsx-download
:dashboard
:embedded-dashboard
:embedded-question
......
......@@ -34,11 +34,13 @@
(defroutes ^:private public-routes
(GET ["/question/:uuid.csv" :uuid u/uuid-regex] [uuid] (resp/redirect (format "/api/public/card/%s/query/csv" uuid)))
(GET ["/question/:uuid.xlsx" :uuid u/uuid-regex] [uuid] (resp/redirect (format "/api/public/card/%s/query/xlsx" uuid)))
(GET ["/question/:uuid.json" :uuid u/uuid-regex] [uuid] (resp/redirect (format "/api/public/card/%s/query/json" uuid)))
(GET "*" [] public))
(defroutes ^:private embed-routes
(GET "/question/:token.csv" [token] (resp/redirect (format "/api/embed/card/%s/query/csv" token)))
(GET "/question/:token.xlsx" [token] (resp/redirect (format "/api/embed/card/%s/query/xlsx" token)))
(GET "/question/:token.json" [token] (resp/redirect (format "/api/embed/card/%s/query/json" token)))
(GET "*" [] embed))
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment