Created
September 11, 2009 13:27
-
-
Save wilkes/185288 to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
(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