Skip to content

Instantly share code, notes, and snippets.

What would you like to do?
Code for, part 2
(require '[cheshire.core :refer [parse-string]])
(require '[clojure.set :refer [subset? difference ]])
(defn db-array [col] (reify java.sql.Array (getArray [_] (object-array col))))
(defn- json->data [data fields]
{:pre [(sequential? data) (sequential? fields)]}
(map (fn [data]
(reduce (fn to-json [data field]
{:pre [(map? data) (string? (get data field)) (keyword? field)]}
(assoc data field (parse-string (get data field) true)))
data fields)) data))
(defn- discount-size [{:keys [amount percent] :as discount}]
{:pre [(number? amount) (number? percent) (<= 0 amount) (<= 0 percent 100)]
:post [(#{:high :normal} %)]}
(if (or
(> amount 10000)
(> percent 5))
(defn- jdbc-array-to-set
"Convert a PostgreSQL JDBC4Array inside the map `m` - at the key `k` - into a se"
[key m]
{:pre [(keyword? key) (map? m) (let [a (key m)] (or (nil? a) (instance? java.sql.Array a)))]}
(update-in m [key] #(some->> % (.getArray) (into #{}))))
(defn car? [{:keys [id] :as car}]
(and (map? car) id))
(defn- compute-discount
"Derive the :discount map based on the car's own discount and its active campaign, if applicable"
[{{:keys [discount_amount discount_percent] :as json} :json
:keys [campaign_discount_amount campaign_discount_percent] :as car}]
{:pre [(car? car) (map? json) (number? discount_amount) (number? discount_percent)]
:post [(:discount %) (:discount-size %)]}
(let [discount? (:use_campaign car)
amount (if discount?
(apply + (remove nil? [discount_amount campaign_discount_amount]))
percent (if discount?
(apply + (remove nil? [discount_percent campaign_discount_percent]))
discount {:amount amount
:percent percent}
discount-size (discount-size discount)
(assoc car :discount discount :discount-size discount-size)))
(defn select-campaign
"Return a single car map with a selected campaign."
[{:keys [campaigns] :as car}]
{:pre [(car? car) (sequential? campaigns)]
:post [(contains? % :best-campaign)]}
(let [best-campaign (->> campaigns
(filter :active)
(sort-by :use_campaign) ;; true, if any, will be last
(-> car
(dissoc :campaigns)
(assoc :best-campaign best-campaign))))
(defn nest-campaign [car]
;; :pre check for campaing keys would require too much repetition => an assert instead
{:pre [(car? car)]
:post [((comp map? :campaign) %)]}
(let [ks (set (keys car))
campaign-ks #{:campaign_discount_amount :campaign_discount_percent :use_campaign :active}
campaign (select-keys car campaign-ks)]
(assert (subset? campaign-ks ks)
(str "Campaign keys missing from the car " (:id car) ": "
(difference campaign-ks ks)))
(-> (apply dissoc car campaign-ks)
(assoc :campaign campaign))))
(defn group-rows-by-car [cars-raw]
{:pre [(sequential? cars-raw) (every? map? cars-raw)]
:post [(sequential? %) (every? vector? %)]}
(vals (group-by :id cars-raw)))
(defn join-campaigns [[car+campaign :as all]]
{:pre [(sequential? all) (:campaign car+campaign)]
:post [(:campaigns %)]}
(-> car+campaign
(assoc :campaigns
(map :campaign all))
(dissoc :campaign)))
(defn refine-car [car]
{:pre [(car? car)]
:post [(:discount %)]} ; keywords and :category_ref are optional
(->> car
(jdbc-array-to-set :category_ref)
(jdbc-array-to-set :keywords)
(#(update-in % [:keywords] (partial remove nil?))) ;; {NULL} => []
(defn refine-cars
"Process get-cars query result set - derive additional data, transform values into better ones
There is one row per car and campaign, a car may have more campaigns - we pick the best one.
(->> cars-raw
(#(json->data % [:json]))
(map nest-campaign)
(map join-campaigns)
(map refine-car)
(refine-cars [
{:id 1
:json "{\"discount_amount\":9000,\"discount_percent\":0}"
:campaign_discount_amount 2000
:campaign_discount_percent nil
:use_campaign false
:active true
:keywords (db-array ["fast"])
:category_ref (db-array [])}])
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
You can’t perform that action at this time.