          (m/map-vals (partial tf/unparse (tf/formatters :year-month-day))))))
 
 
-(defn- relative-date->range
-  "Take a given string description of a relative date range such as 'lastmonth' and return a MAP with a given
-   `:start` and `:end` as iso8601 string formatted dates.  Values should be appropriate for the given REPORT-TIMEZONE."
-  [value report-timezone]
-  (let [tz        (t/time-zone-for-id report-timezone)
-        formatter (tf/formatter "yyyy-MM-dd" tz)
-        today     (.withTimeAtStartOfDay (t/to-time-zone (t/now) tz))]
-    (->> (condp = value
-           "past7days"  {:end   (t/minus today (t/days 1))
-                         :start (t/minus today (t/days 7))}
-           "past30days" {:end   (t/minus today (t/days 1))
-                         :start (t/minus today (t/days 30))}
-           "thisweek"   (week-range today)
-           "thismonth"  (month-range today)
-           "thisyear"   (year-range today)
-           "lastweek"   (week-range (t/minus today (t/weeks 1)))
-           "lastmonth"  (month-range (t/minus today (t/months 1)))
-           "lastyear"   (year-range (t/minus today (t/years 1)))
-           "yesterday"  {:end   (t/minus today (t/days 1))
-                         :start (t/minus today (t/days 1))}
-           "today"      {:end   today
-                         :start today})
-         ;; the above values are JodaTime objects, so unparse them to iso8601 strings
-         (m/map-vals (partial tf/unparse formatter)))))
+;(defn- relative-date->range
+;  "Take a given string description of a relative date range such as 'lastmonth' and return a MAP with a given
+;   `:start` and `:end` as iso8601 string formatted dates.  Values should be appropriate for the given REPORT-TIMEZONE."
+;  [value report-timezone]
+;  (let [tz        (t/time-zone-for-id report-timezone)
+;        formatter (tf/formatter "yyyy-MM-dd" tz)
+;        today     (.withTimeAtStartOfDay (t/to-time-zone (t/now) tz))]
+;    (->> (condp = value
+;           "past7days"  {:end   (t/minus today (t/days 1))
+;                         :start (t/minus today (t/days 7))}
+;           "past30days" {:end   (t/minus today (t/days 1))
+;                         :start (t/minus today (t/days 30))}
+;           "thisweek"   (week-range today)
+;           "thismonth"  (month-range today)
+;           "thisyear"   (year-range today)
+;           "lastweek"   (week-range (t/minus today (t/weeks 1)))
+;           "lastmonth"  (month-range (t/minus today (t/months 1)))
+;           "lastyear"   (year-range (t/minus today (t/years 1)))
+;           "yesterday"  {:end   (t/minus today (t/days 1))
+;                         :start (t/minus today (t/days 1))}
+;           "today"      {:end   today
+;                         :start today})
+;         ;; the above values are JodaTime objects, so unparse them to iso8601 strings
+;         (m/map-vals (partial tf/unparse formatter)))))
 
 
 ;;; +-------------------------------------------------------------------------------------------------------+

  {:end   (t/last-day-of-the-month (t/plus dt (t/months 2)))
   :start (t/first-day-of-the-month dt)})

(defn- year-range [^DateTime dt]
  {:end   (t/last-day-of-the-month (.withMonthOfYear dt DateTimeConstants/DECEMBER))
   :start (t/first-day-of-the-month (.withMonthOfYear dt DateTimeConstants/JANUARY))})

(defn- absolute-date->range
  "Take a given string description of an absolute date range and return a MAP with a given `:start` and `:end`.

;;; +-------------------------------------------------------------------------------------------------------+


;(defn- extract-dates [value report-timezone]
;  (if-not (contains? relative-dates value)
;    ;; absolute date range
;    (absolute-date->range value)
;    ;; relative date range
;    (relative-date->range value report-timezone)))
;
;(defn- expand-date-range-param [report-timezone {[target param-name] :target, param-type :type, param-value :value, :as param}]
;  (if-not (= param-type "date")
;    param
;    (let [{:keys [start end]} (extract-dates param-value report-timezone)]
;      [(assoc param :target [target (str param-name ":start")], :value start)
;       (assoc param :target [target (str param-name ":end")],   :value end)])))
;
;(defn- substitute-param [param-name value query]
;  ;; TODO: escaping and protection against SQL injection!
;  (s/replace query (re-pattern (str "\\{\\{" param-name "\\}\\}")) value))
;
;(defn- substitute-all-params [query-dict [{:keys [value], [_ param-name] :target, :as param} & rest]]
;  (if param
;    (if-not (and value (string? param-name))
;      (substitute-all-params query-dict rest)
;      (let [query (update-in query-dict [:native :query] (partial substitute-param param-name value))]
;        (substitute-all-params query rest)))
;    query-dict))
;
;;; these are various regex patterns used by the functions below to parse/extract our custom clauses
;;; an "outer-clause" is of the form [[PREFIX ...]] and an "inner-clause" is of the form <...>
;;; an incomplete clause is any clause which has an unsubstituted parameter left in it.  e.g. [[AND foo={{bar}}]]
;(def ^:private ^:const outer-clause-pattern            #"\[\[.*?\]\]")
;(def ^:private ^:const outer-clause-prefix-pattern     #"^\[\[(.*?)\s.*\]\]$")
;(def ^:private ^:const incomplete-outer-clause-pattern #"\[\[.*?\{\{.*?\}\}.*?\]\]")
;(def ^:private ^:const inner-clause-pattern            #"<(.*?)>")
;(def ^:private ^:const incomplete-inner-clause-pattern #"<.*?\{\{.*?\}\}.*?>")
;
;(defn- remove-incomplete-clauses
;  "Scans the native query body and removes any custom clauses which were not substituted with a parameter."
;  [query-dict]
;  (let [find-and-replace (fn [sql]
;                           (-> sql
;                               (s/replace incomplete-outer-clause-pattern "")
;                               (s/replace incomplete-inner-clause-pattern "")))]
;    (update-in query-dict [:native :query] find-and-replace)))
;
;(defn- format-outer-clause
;  "Formats an outer clause, finding all inner clauses and joining them via `AND` and then concatenating that onto the clause prefix.
;   e.g. \"[[WHERE <foo='bar'> <tou='can'>]]\" -> \"WHERE foo='bar' AND tou='can'\""
;  [clause]
;  (let [prefix (second (re-find outer-clause-prefix-pattern clause))]
;    ;; re-seq produces a vector for each match like [matched-form grouping1] and we only want grouping1.
;    (str prefix " " (s/join " AND " (map second (re-seq inner-clause-pattern clause))))))
;
;(defn- process-outer-clauses
;  "Takes a native query body and completely processes all included outer clauses present."
;  [query-dict]
;  (if-let [outer-clauses (re-seq outer-clause-pattern (get-in query-dict [:native :query]))]
;    (update-in query-dict [:native :query] (fn [q]
;                                             (loop [sql                   q
;                                                    [outer-clause & rest] outer-clauses]
;                                               (if outer-clause
;                                                 (recur (s/replace-first sql outer-clause (format-outer-clause outer-clause)) rest)
;                                                 sql))))
;    query-dict))
;
;(defn- process-inner-clauses
;  "Takes a native query body and completely processes all included inner clauses present.
;   e.g. \"<foo='bar'>\" -> \"foo='bar'\""
;  [query-dict]
;  (if-let [inner-clauses (re-seq inner-clause-pattern (get-in query-dict [:native :query]))]
;    (update-in query-dict [:native :query] (fn [q]
;                                             (loop [sql                      q
;                                                    [[orig stripped] & rest] inner-clauses]
;                                               (if orig
;                                                 (recur (s/replace-first sql orig stripped) rest)
;                                                 sql))))
;    query-dict))
;
;(defn- expand-params-native [{:keys [driver], :as query-dict} params]
;  (if-not (driver/driver-supports? driver :native-parameters)
;    query-dict
;    (let [report-timezone (get-in query-dict [:settings :report-timezone])
;          params          (flatten (map (partial expand-date-range-param report-timezone) params))]
;      (-> (substitute-all-params query-dict params)
;          remove-incomplete-clauses
;          process-outer-clauses
;          process-inner-clauses))))


;;; +-------------------------------------------------------------------------------------------------------+
