Skip to content

Instantly share code, notes, and snippets.

@wilkes
Created September 11, 2009 13:27
Show Gist options
  • Save wilkes/185288 to your computer and use it in GitHub Desktop.
Save wilkes/185288 to your computer and use it in GitHub Desktop.
(ns floyd.reports
(:use [clojure.contrib.seq-utils :only [includes?]]
[floyd.formatters :only [billing-minutes billing-seconds as-minutes]]
[floyd.str-utils :only [join-str to-display-str]]))
(defparseable column-descriptor
[:table-column {:required true}]
[:name {:type :pair :default :table-column}]
[:label {:type :pair :default (fn [this] (to-display-str (:name this)))}]
[:calc {:enums #{:sum :avg :count}}]
[:sort {:enums #{:asc :desc}}]
[:parts {:enums #{:year :month :day}}]
[:display-format {:type :pair}]
[:download-format {:type :pair}])
(defstruct column-descriptor :name :table-column :label :calc :sort :parts :display-format :download-format)
(defstruct filter-descriptor :table-column :label :check :required :autocomplete)
(def sort-vals #{:asc :desc})
(def calc-vals #{:sum :avg :count})
(def parts-vals #{:year :month :day})
(def check-vals #{:daterange :equals})
(def bool-vals #{:required})
(def pair-vals #{:label :autocomplete})
(def *report-list* (atom []))
(defn add-report [spec]
(swap! *report-list* conj spec))
(defn report-list []
@*report-list*)
(defn name-column [table-column calc]
(keyword (str table-column "_" calc)))
(defn parse-options [defaults options option-parser]
(loop [results defaults
opts options]
(if (empty? opts)
results
(let [[results opts] (option-parser results opts)]
(recur results opts)))))
(defn parse-filter [m [h & tail]]
(letfn [(parse-pair [] [(assoc m h (first tail)) (rest tail)])]
(cond
(includes? pair-vals h) (parse-pair)
(includes? check-vals h) [(assoc m :check h) tail]
(includes? bool-vals h) [(assoc m h true) tail]
:otherwise [m tail])))
(defn parse-column-option [m [h & tail]]
(letfn [(parse-pair [] [(assoc m h (first tail))
(rest tail)])
(parse-k [k] [(assoc m k h)
tail])
(parse-k-list [k] [(assoc m k (concat (get m k) [h]))
tail])
(set-name [new-m]
[(assoc new-m :name (name-column (:table-column m)
(name h)))
tail])]
(cond
(= h :label) (parse-pair)
(= h :display-format) (parse-pair)
(= h :download-format) (parse-pair)
(includes? sort-vals h) (parse-k :sort)
(includes? calc-vals h) (do (let [[updated-m _] (parse-k :calc)]
(set-name updated-m)))
(includes? parts-vals h) (parse-k-list :parts)
:otherwise [m tail])))
(defn report-column [name & options]
(let [defaults (merge (struct-map column-descriptor)
{:name (keyword name)}
{:table-column name})]
(parse-options defaults options parse-column-option)))
(defn report-filter [name & options]
(let [defaults (merge (struct-map filter-descriptor)
{:table-column name}
{:label (to-display-str name)})]
(parse-options defaults options parse-filter)))
(defn parse-report [spec]
(letfn [(parse [key parser]
(map #(apply parser %) (key spec)))]
{:columns (parse :columns report-column)
:filters (parse :filters report-filter)}))
(defn filter-names [a-filter]
(let [funcs {:daterange (fn [f]
(let [name (:table-column f)]
[(str name "_start") (str name "_end")]))
:equals (fn [f]
[(str (:table-column f) "_equals")])}
namer ((:check a-filter) funcs)]
(namer a-filter)))
(defn columns-with [key report]
(filter #(key %) (:columns report)))
(defn groupings [report]
(filter #(not (:calc %)) (:columns report)))
(defn headers [report]
(map #(or (:label %)
(to-display-str (name (:name %))))
(:columns report)))
(defn orderings [report]
(columns-with :sort report))
(defn column-named [report val]
(first (filter #(= (:table-column %) val) (:columns report))))
(defmacro defreport [name comment spec]
`(do
(def ~name (merge {:name ~(str name)}
{:comment ~(str comment)}
(parse-report ~spec)))
(add-report ~name)))
(defreport billing ""
{:columns [["main_account_number" :label "Account" :asc]
["ivr_time" :sum :label "IVR" :display-format billing-minutes :download-format billing-seconds]
["op_time" :sum :label "OP" :display-format billing-minutes :download-format billing-seconds]
["final_patch_time" :sum :label "Patch" :display-format billing-minutes :download-format billing-seconds]
["main_account_number" :count :label "Calls"]]
:filters [["call_date" :daterange :require]
["main_account_number" :label "Account" :equals :optional
:autocomplete ["cisco_billing_cdr" "main_account_number"]]]})
(defreport aggregates ""
{:columns [["main_account_number" :label "Account" :asc]
["account_number" :label "Subaccount" :asc]
["op_time" :sum :display-format as-minutes]
["op_time" :avg :display-format as-minutes]
["ivr_time" :sum :display-format as-minutes]
["ivr_time" :avg :display-format as-minutes]
["final_patch_time" :sum :label "Patch Sum" :display-format as-minutes]
["final_patch_time" :avg :label "Patch Avg" :display-format as-minutes]
["ring_time" :avg :display-format as-minutes]
["delay_time" :avg :display-format as-minutes]
["hold_time" :avg :display-format as-minutes]
["local_q_time" :avg :display-format as-minutes]
;;["call_date" :year :month :asc]
]
:filters [["call_date" :daterange :required]
["main_account_number" :equals :optional
:label "Account"
:autocomplete ["cisco_billing_cdr" "main_account_number"]]
["account_number" :equals :optional
:label "Subaccount"
:autocomplete ["cisco_billing_cdr" "account_number"]]]})
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment