"See this month by weeks" type of transform. Entry points:
Requirements:
Query transformation:
Question transformation:
| (ns metabase.lib.drill-thru.zoom-in-timeseries (:require [metabase.lib.breakout :as lib.breakout] [metabase.lib.drill-thru.common :as lib.drill-thru.common] [metabase.lib.equality :as lib.equality] [metabase.lib.filter :as lib.filter] [metabase.lib.remove-replace :as lib.remove-replace] [metabase.lib.schema :as lib.schema] [metabase.lib.schema.common :as lib.schema.common] [metabase.lib.schema.drill-thru :as lib.schema.drill-thru] [metabase.lib.schema.temporal-bucketing :as lib.schema.temporal-bucketing] [metabase.lib.temporal-bucket :as lib.temporal-bucket] [metabase.lib.underlying :as lib.underlying] [metabase.lib.util :as lib.util] [metabase.util.i18n :as i18n] [metabase.util.malli :as mu])) |
(mu/defn- valid-current-units :- [:sequential ::lib.schema.temporal-bucketing/unit.date-time.truncate]
[query :- ::lib.schema/query
stage :- :int
field :- :mbql.clause/field]
(->> (lib.temporal-bucket/available-temporal-buckets query stage field)
(map lib.temporal-bucket/raw-temporal-bucket)
(filter lib.schema.temporal-bucketing/datetime-truncation-units)
reverse)) | |
(mu/defn- matching-breakout-dimension :- [:maybe ::lib.schema.drill-thru/context.row.value]
[query :- ::lib.schema/query
stage-number :- :int
dimensions :- [:sequential ::lib.schema.drill-thru/context.row.value]]
(first (for [[breakout-ref breakout-col] (map vector
(lib.breakout/breakouts query stage-number)
(lib.breakout/breakouts-metadata query stage-number))
:when (and (lib.util/clause-of-type? breakout-ref :field)
(lib.temporal-bucket/temporal-bucket breakout-ref))
{:keys [column] :as dimension} dimensions
:when (and (lib.equality/find-matching-column breakout-ref [column])
(= (lib.temporal-bucket/raw-temporal-bucket breakout-ref)
(or (lib.temporal-bucket/raw-temporal-bucket column)
;; If query is multi-stage and column comes from a call
;; to [[lib.calculation/returned-columns]], then it may have an
;; :inherited-temporal-unit instead of a :temporal-unit.
(:inherited-temporal-unit column))))]
;; If stage-number is not -1, then the column from the input dimension will be from the last stage,
;; whereas breakout-col will be the corresponding breakout column from [[lib.underlying/top-level-stage]].
(assoc dimension :column breakout-col :column-ref breakout-ref)))) | |
(mu/defn- next-breakout-unit :- [:maybe ::lib.schema.drill-thru/drill-thru.zoom-in.timeseries.next-unit]
[query :- ::lib.schema/query
stage :- :int
field :- :mbql.clause/field]
(when-let [current-unit (lib.temporal-bucket/raw-temporal-bucket field)]
(->> (valid-current-units query stage field)
(drop-while #(not= % current-unit))
second))) | |
(mu/defn- describe-next-unit :- ::lib.schema.common/non-blank-string
[unit :- ::lib.schema.drill-thru/drill-thru.zoom-in.timeseries.next-unit]
(case unit
:quarter (i18n/tru "See this year by quarter")
:month (i18n/tru "See this quarter by month")
:week (i18n/tru "See this month by week")
:day (i18n/tru "See this week by day")
:hour (i18n/tru "See this day by hour")
:minute (i18n/tru "See this hour by minute"))) | |
(mu/defn zoom-in-timeseries-drill :- [:maybe ::lib.schema.drill-thru/drill-thru.zoom-in.timeseries]
"Zooms in on some window, showing it in finer detail.
For example: The month of a year, days or weeks of a quarter, smaller lat/long regions, etc.
This is different from the `:drill-thru/zoom` type, which is for showing the details of a single object."
[query :- ::lib.schema/query
_stage-number :- :int
{:keys [dimensions], :as _context} :- ::lib.schema.drill-thru/context]
;; For multi-stage queries, we want the stage-number of the underlying stage with breakouts or aggregations.
(let [stage-number (lib.underlying/top-level-stage-number query)]
(when (and (lib.drill-thru.common/mbql-stage? query stage-number)
dimensions)
(when-let [{:keys [value column-ref], :as dimension}
(matching-breakout-dimension query stage-number dimensions)]
(when value
(when-let [next-unit (next-breakout-unit query stage-number column-ref)]
{:lib/type :metabase.lib.drill-thru/drill-thru
:display-name (describe-next-unit next-unit)
:type :drill-thru/zoom-in.timeseries
:dimension dimension
:next-unit next-unit})))))) | |
(mu/defmethod lib.drill-thru.common/drill-thru-method :drill-thru/zoom-in.timeseries
[query :- ::lib.schema/query
_stage-number :- :int
{:keys [dimension next-unit]} :- ::lib.schema.drill-thru/drill-thru.zoom-in.timeseries]
(let [{:keys [column value]} dimension
old-breakout (:column-ref dimension)
new-breakout (lib.temporal-bucket/with-temporal-bucket old-breakout next-unit)
stage-number (lib.underlying/top-level-stage-number query)
resovled-column (lib.drill-thru.common/breakout->resolved-column query stage-number column)]
(-> query
(lib.filter/filter stage-number (lib.filter/= resovled-column value))
(lib.remove-replace/replace-clause stage-number old-breakout new-breakout)))) | |