gets transaction insights kind of working
This commit is contained in:
1871
data/inference-outcome.csv
Normal file
1871
data/inference-outcome.csv
Normal file
File diff suppressed because it is too large
Load Diff
@@ -16,11 +16,22 @@
|
||||
(into [:div.htmx-indicator-hidden.inline-flex.gap-2.items-center.justify-center ] children)])
|
||||
|
||||
(defn icon-button- [params & children]
|
||||
(into
|
||||
[:button (update params :class str " inline-flex items-center justify-center bg-white dark:bg-gray-600 items-center p-3 text-sm font-medium border border-gray-300 dark:border-gray-700 text-center text-gray-500 hover:text-gray-800 rounded-lg dark:text-gray-400 dark:hover:text-gray-100")
|
||||
[:div.htmx-indicator.flex.items-center
|
||||
(svg/spinner {:class "inline w-4 h-4 text-white"})]
|
||||
[:div.htmx-indicator-hidden.inline-flex.gap-2.items-center.justify-center (into [:div.h-4.w-4] children)]]))
|
||||
(into
|
||||
[:button
|
||||
|
||||
(update params :class
|
||||
#(cond-> %
|
||||
true (str " inline-flex items-center justify-center items-center p-3 text-sm font-medium border border-gray-300 dark:border-gray-700 text-center rounded-lg ")
|
||||
(= :secondary (:color params)) (str " bg-blue-500 hover:bg-blue-600 focus:ring-blue-300 dark:bg-blue-600 dark:hover:bg-blue-700")
|
||||
(= :primary (:color params)) (str " bg-green-500 hover:bg-green-600 focus:ring-green-300 dark:bg-green-600 dark:hover:bg-green-700 ")
|
||||
(= :primary-light (:color params)) (str " bg-green-200 hover:bg-green-300 focus:ring-green-200 dark:bg-green-700 dark:hover:bg-green-600 text-gray-800 dark:text-gray-200")
|
||||
(= :secondary-light (:color params)) (str " bg-blue-200 hover:bg-blue-300 focus:ring-blue-200 dark:bg-blue-700 dark:hover:bg-blue-600 text-gray-800 dark:text-gray-200")
|
||||
(= :danger-light (:color params)) (str " bg-red-200 hover:bg-red-300 focus:ring-red-200 dark:bg-red-700 dark:hover:bg-red-600 text-gray-800 dark:text-gray-200")
|
||||
(nil? (:color params))
|
||||
(str " bg-white dark:bg-gray-600 border-gray-300 dark:border-gray-700 text-gray-500 hover:text-gray-800 dark:text-gray-400 dark:hover:text-gray-100")))
|
||||
[:div.htmx-indicator.flex.items-center
|
||||
(svg/spinner {:class "inline w-4 h-4 text-white"})]
|
||||
[:div.htmx-indicator-hidden.inline-flex.gap-2.items-center.justify-center (into [:div.h-4.w-4] children)]]))
|
||||
|
||||
(defn a-icon-button- [params & children]
|
||||
(into
|
||||
|
||||
@@ -8,7 +8,8 @@
|
||||
|
||||
(defn header- [params & rest]
|
||||
(into [:th.px-4.py-3 {:scope "col" :class (:class params)
|
||||
"_" (hiccup/raw (when (:sort-key params ) (format "on click trigger sorted(key:\"%s\")", (:sort-key params))))}]
|
||||
"_" (hiccup/raw (when (:sort-key params ) (format "on click trigger sorted(key:\"%s\")", (:sort-key params))))
|
||||
:style (:style params)}]
|
||||
(if (:sort-key params)
|
||||
[(into [:a {:href "#"} ] rest)]
|
||||
rest)))
|
||||
@@ -23,7 +24,7 @@
|
||||
:class str " border-b dark:border-gray-600 hover:bg-gray-100 dark:hover:bg-gray-700")] rest))
|
||||
|
||||
(defn cell- [params & rest]
|
||||
(into [:td.px-4.py-2 {:class (:class params)}] rest))
|
||||
(into [:td.px-4.py-2 params ] rest))
|
||||
|
||||
(defn right-stack-cell- [params & rest]
|
||||
(cell- params (into [:div.flex.flex-row-reverse.items-center.justify-between
|
||||
|
||||
@@ -35,7 +35,7 @@ curModal.hide();
|
||||
]])
|
||||
|
||||
(defn modal-card- [params header content footer]
|
||||
[:div#modal-card
|
||||
[:div#modal-card params
|
||||
[:div {:class "relative bg-white rounded-lg shadow dark:bg-gray-700 dark:text-white fade-in slide-up duration-300 transition-all modal-content"}
|
||||
[:div {:class "flex items-start justify-between p-4 border-b rounded-t dark:border-gray-600"} header]
|
||||
[:div {:class "p-6 space-y-6"}
|
||||
|
||||
@@ -419,3 +419,15 @@
|
||||
[:path {:d "M22.629,4.572A6.22,6.22,0,0,1,23,6.5v16a1,1,0,0,1-1,1H2a1,1,0,0,1-1-1V6.5a6.22,6.22,0,0,1,.371-1.928L2.629,1.428A1.6,1.6,0,0,1,4,.5H20a1.6,1.6,0,0,1,1.371.928Z", :fill "none", :stroke "currentColor", :stroke-linecap "round", :stroke-linejoin "round"}]
|
||||
[:line {:x1 "12", :y1 "6", :x2 "12", :y2 "0.5", :fill "none", :stroke "currentColor", :stroke-linecap "round", :stroke-linejoin "round"}]
|
||||
[:line {:x1 "1.034", :y1 "6", :x2 "22.966", :y2 "6", :fill "none", :stroke "currentColor", :stroke-linecap "round", :stroke-linejoin "round"}]])
|
||||
|
||||
(def thumbs-up
|
||||
[:svg {:xmlns "http://www.w3.org/2000/svg", :viewbox "0 0 24 24"}
|
||||
[:defs]
|
||||
[:title "like"]
|
||||
[:path {:d "M19.5,16.065h0a1.5,1.5,0,0,1,0,3h-1a1.5,1.5,0,0,1,0,3H12c-4,0-3-2-11-2v-9H4a7.949,7.949,0,0,0,7.5-8c0-1.581,3-1.781,3,1.219a31.593,31.593,0,0,1-1,5.781h8a1.5,1.5,0,0,1,0,3h-1a1.5,1.5,0,0,1,0,3h-1", :fill "none", :stroke "currentColor", :stroke-linecap "round", :stroke-linejoin "round"}]])
|
||||
|
||||
(def thumbs-down
|
||||
[:svg {:xmlns "http://www.w3.org/2000/svg", :viewbox "0 0 24 24"}
|
||||
[:defs]
|
||||
[:title "dislike"]
|
||||
[:path {:d "M4.5,8h0a1.5,1.5,0,0,1,0-3h1a1.5,1.5,0,0,1,0-3H12c4,0,3,1.87,11,1.87V13H20a7.811,7.811,0,0,0-7.5,7.856c0,1.582-3,1.813-3-1.187A29.774,29.774,0,0,1,10.5,14h-8a1.5,1.5,0,0,1,0-3h1a1.5,1.5,0,0,1,0-3h1", :fill "none", :stroke "currentColor", :stroke-linecap "round", :stroke-linejoin "round"}]])
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
(ns auto-ap.ssr.transaction.insights
|
||||
(:require
|
||||
[auto-ap.datomic :refer [conn visible-clients]]
|
||||
[auto-ap.shared-views.company.sidebar :refer [company-side-bar]]
|
||||
[auto-ap.ssr-routes :as ssr-routes]
|
||||
[auto-ap.ssr.ui :refer [base-page]]
|
||||
[auto-ap.ssr.utils :refer [html-response]]
|
||||
@@ -10,7 +9,9 @@
|
||||
[clj-time.coerce :as coerce]
|
||||
[datomic.api :as dc]
|
||||
[hiccup2.core :as hiccup]
|
||||
[clj-time.core :as time]))
|
||||
[auto-ap.ssr.components :as com]
|
||||
[auto-ap.client-routes :as client-routes]
|
||||
[auto-ap.ssr.svg :as svg]))
|
||||
|
||||
(def pull-expr [:transaction/description-original
|
||||
:db/id
|
||||
@@ -25,77 +26,84 @@
|
||||
|
||||
(defn transaction-recommendations [identity selected-client & {:keys [after]}]
|
||||
(let [visible-clients (visible-clients identity)]
|
||||
(->>
|
||||
(dc/q '[:find (pull ?t pull-expr)
|
||||
:in $ [?c ...] pull-expr
|
||||
:where [?t :transaction/recommended-account]
|
||||
[?t :transaction/client ?c]
|
||||
[?t :transaction/approval-status :transaction-approval-status/unapproved]
|
||||
(not [?t :transaction/vendor])]
|
||||
(dc/db conn)
|
||||
(if selected-client
|
||||
[selected-client]
|
||||
visible-clients)
|
||||
pull-expr)
|
||||
(map first)
|
||||
(sort-by :transaction/date)
|
||||
(reverse)
|
||||
(drop-while (fn [x]
|
||||
(if after
|
||||
(not= (Long/parseLong after) (:db/id x))
|
||||
false)))
|
||||
(#(if after
|
||||
(->>
|
||||
(dc/qseq {:query '[:find (pull ?t pull-expr)
|
||||
:in $ [?c ...] pull-expr
|
||||
:where [?t :transaction/recommended-account]
|
||||
[?t :transaction/client ?c]
|
||||
[?t :transaction/approval-status :transaction-approval-status/unapproved]
|
||||
(not [?t :transaction/vendor])]
|
||||
:args [(dc/db conn)
|
||||
(if selected-client
|
||||
[selected-client]
|
||||
visible-clients)
|
||||
pull-expr]})
|
||||
(map first)
|
||||
(sort-by :transaction/date)
|
||||
(reverse)
|
||||
(drop-while (fn [x]
|
||||
(if after
|
||||
(not= (Long/parseLong after) (:db/id x))
|
||||
false)))
|
||||
(#(if after
|
||||
(drop 1 %)
|
||||
%))
|
||||
(take 10)
|
||||
(into []))))
|
||||
(take 10)
|
||||
(into []))))
|
||||
|
||||
|
||||
(defn transaction-row [r & {:keys [hide-actions? class last?]}]
|
||||
[:tr (cond-> {:class class}
|
||||
last? (assoc :hx-get (bidi/path-for ssr-routes/only-routes
|
||||
:transaction-insight-rows
|
||||
:after (:db/id r))
|
||||
:hx-trigger "intersect once"
|
||||
:hx-indicator "#insight-table"
|
||||
:hx-swap "afterend"))
|
||||
[:td {:style {:width "8em"}}(:client/code (:transaction/client r))]
|
||||
[:td {:style {:width "10em"}} (:bank-account/code (:transaction/bank-account r))]
|
||||
[:td {:style {:width "12em"}} (some-> (:transaction/date r) coerce/to-date-time (atime/unparse-local atime/normal-date))]
|
||||
[:td {:style {:width "30em" :max-width "30em"}} (str (:transaction/description-original r))]
|
||||
[:td {:style {:width "10em"}}
|
||||
(if (> (:transaction/amount r) 0.0 )
|
||||
[:div.tag.is-success.is-light (str "$" (Math/round (:transaction/amount r)))]
|
||||
[:div.tag.is-danger.is-light (str "$" (Math/round (:transaction/amount r)))])]
|
||||
[:td {:style {:width "12em"}}
|
||||
[:div [:div.tag (:vendor/name (:transaction/recommended-vendor r))]]
|
||||
[:div [:div.tag (str (:account/numeric-code (:transaction/recommended-account r)) " - " (:account/name (:transaction/recommended-account r)))]]
|
||||
[:div [:div.tag
|
||||
{:class (cond
|
||||
(> (:transaction/account-confidence r) 0.90)
|
||||
"is-success is-light"
|
||||
(> (:transaction/account-confidence r) 0.80)
|
||||
"is-info is-light"
|
||||
(com/data-grid-row
|
||||
(cond-> {:class class}
|
||||
last? (assoc :hx-get (bidi/path-for ssr-routes/only-routes
|
||||
:transaction-insight-rows
|
||||
:after (:db/id r))
|
||||
:hx-trigger "intersect once"
|
||||
:hx-indicator "#insight-table"
|
||||
:hx-swap "afterend"))
|
||||
(com/data-grid-cell {} (:client/code (:transaction/client r)))
|
||||
(com/data-grid-cell {} (:bank-account/code (:transaction/bank-account r)))
|
||||
(com/data-grid-cell {} (some-> (:transaction/date r) coerce/to-date-time (atime/unparse-local atime/normal-date)))
|
||||
|
||||
:else
|
||||
"is-warning is-light")}
|
||||
(str "%" (Math/round (* 100.0 (:transaction/account-confidence r))))]]]
|
||||
[:td
|
||||
(when-not hide-actions?
|
||||
[:div.buttons
|
||||
[:button.button {:hx-post (bidi/path-for ssr-routes/only-routes
|
||||
:transaction-insight-approve
|
||||
:transaction-id (:db/id r))
|
||||
:hx-target "closest tr"}
|
||||
[:i.fa.fa-thumbs-up ]]
|
||||
[:button.button
|
||||
[:i.fa.fa-thumbs-down ]]
|
||||
[:a.button {:hx-get (bidi/path-for ssr-routes/only-routes
|
||||
:transaction-insight-explain
|
||||
:transaction-id (:db/id r))
|
||||
:hx-target "#modal-holder"
|
||||
:hx-swap "beforeend"}
|
||||
[:i.fa.fa-question ]]])]])
|
||||
(com/data-grid-cell {} (str (:transaction/description-original r)))
|
||||
(com/data-grid-cell {}
|
||||
(if (> (:transaction/amount r) 0.0)
|
||||
[:div.tag.is-success.is-light (str "$" (Math/round (:transaction/amount r)))]
|
||||
[:div.tag.is-danger.is-light (str "$" (Math/round (:transaction/amount r)))]))
|
||||
(com/data-grid-cell {:style {:width "12em"}}
|
||||
[:div.flex.gap-2.flex-wrap {:style {:width "12em"}}
|
||||
(com/pill {:color :primary} (:vendor/name (:transaction/recommended-vendor r)))
|
||||
(com/pill {:color :secondary} (str (:account/numeric-code (:transaction/recommended-account r)) " - " (:account/name (:transaction/recommended-account r))))
|
||||
(com/pill {:class (cond
|
||||
(> (:transaction/account-confidence r) 0.90)
|
||||
"is-success is-light"
|
||||
(> (:transaction/account-confidence r) 0.80)
|
||||
"is-info is-light"
|
||||
|
||||
:else
|
||||
"is-warning is-light")} (str "%" (Math/round (* 100.0 (:transaction/account-confidence r)))))])
|
||||
(com/data-grid-right-stack-cell {}
|
||||
(when-not hide-actions?
|
||||
[:form.flex.gap-2
|
||||
[:input {:type :hidden :name "id" :value (:db/id r)}]
|
||||
(com/icon-button {:hx-post (bidi/path-for ssr-routes/only-routes
|
||||
:transaction-insight-approve
|
||||
:transaction-id (:db/id r))
|
||||
:hx-target "closest tr"
|
||||
:color :primary-light}
|
||||
svg/thumbs-up)
|
||||
(com/icon-button {:hx-delete (bidi/path-for ssr-routes/only-routes
|
||||
:transaction-insight-approve
|
||||
:transaction-id (:db/id r))
|
||||
:hx-swap "closest tr"
|
||||
:color :danger-light}
|
||||
svg/thumbs-down)
|
||||
(com/icon-button {:hx-get (bidi/path-for ssr-routes/only-routes
|
||||
:transaction-insight-explain
|
||||
:transaction-id (:db/id r))
|
||||
:hx-target "#modal-holder"
|
||||
:hx-swap "outerHTML"}
|
||||
svg/question)]))))
|
||||
|
||||
(defn approve [{:keys [identity session] {:keys [transaction-id]} :route-params}]
|
||||
(html-response (transaction-row
|
||||
@@ -108,15 +116,15 @@
|
||||
|
||||
(defn explain [{:keys [identity session] {:keys [transaction-id]} :route-params}]
|
||||
(let [r (dc/pull (dc/db conn)
|
||||
pull-expr
|
||||
(Long/parseLong transaction-id))
|
||||
pull-expr
|
||||
(Long/parseLong transaction-id))
|
||||
similar (->> (dc/q '[:find ?date ?do ?amt
|
||||
:in $ ?tr
|
||||
:where
|
||||
[(iol-ion.query/recent-date 180) ?start-date]
|
||||
[?tr :transaction/client ?c]
|
||||
[?tr :transaction/recommended-account ?a ]
|
||||
[?tr :transaction/recommended-vendor ?v ]
|
||||
[?tr :transaction/recommended-account ?a]
|
||||
[?tr :transaction/recommended-vendor ?v]
|
||||
[?t2 :transaction/client ?c]
|
||||
[?t2 :transaction/date ?date]
|
||||
[(>= ?date ?start-date)]
|
||||
@@ -130,38 +138,31 @@
|
||||
(take 5)
|
||||
sort
|
||||
reverse)]
|
||||
(html-response [:div.modal.is-active.wide
|
||||
[:div.modal-background {"_" (hiccup/raw "on click remove <#modal-holder div/>")}]
|
||||
[:div.modal-card
|
||||
[:div.modal-card-head
|
||||
[:h1.title "Similar transactions"]
|
||||
[:div.tags
|
||||
[:div.tag.is-large.is-info.is-light (:vendor/name (:transaction/recommended-vendor r))]
|
||||
[:div.tag.is-large.is-info.is-light (str (:account/numeric-code (:transaction/recommended-account r)) " - " (:account/name (:transaction/recommended-account r)))]]]
|
||||
[:div.modal-card-body
|
||||
[:table.table
|
||||
[:thead
|
||||
[:tr
|
||||
[:td "Date"]
|
||||
[:td "Description"]
|
||||
[:td "Amount"]]]
|
||||
[:tbody
|
||||
[:tr
|
||||
[:th (some-> r :transaction/date coerce/to-date-time (atime/unparse-local atime/normal-date))]
|
||||
[:th (-> r :transaction/description-original)]
|
||||
[:th (if (> (-> r :transaction/amount) 0.0 )
|
||||
[:div.tag.is-success.is-light (str "$" (Math/round (:transaction/amount r)))]
|
||||
[:div.tag.is-danger.is-light (str "$" (Math/round (:transaction/amount r)))])]]
|
||||
(for [[date description amt] similar]
|
||||
[:tr
|
||||
[:td (some-> date coerce/to-date-time (atime/unparse-local atime/normal-date))]
|
||||
[:td description]
|
||||
[:td (if (> amt 0.0 )
|
||||
[:div.tag.is-success.is-light (str "$" (Math/round amt))]
|
||||
[:div.tag.is-danger.is-light (str "$" (Math/round amt))])]])]]
|
||||
|
||||
]]
|
||||
[:button.modal-close.is-large {"_" (hiccup/raw "on click remove <#modal-holder div/>")}]])))
|
||||
(html-response
|
||||
(com/modal {}
|
||||
(com/modal-card {:style {:width "900px"}}
|
||||
[:div.flex [:div.p-2 "Similar Transactions"]]
|
||||
[:table.w-full
|
||||
[:thead
|
||||
[:tr
|
||||
[:td "Date"]
|
||||
[:td "Description"]
|
||||
[:td "Amount"]]]
|
||||
[:tbody
|
||||
[:tr
|
||||
[:th.text-left (some-> r :transaction/date coerce/to-date-time (atime/unparse-local atime/normal-date))]
|
||||
[:th.text-left (-> r :transaction/description-original)]
|
||||
[:th.text-left (if (> (-> r :transaction/amount) 0.0)
|
||||
[:div.tag.is-success.is-light (str "$" (Math/round (:transaction/amount r)))]
|
||||
[:div.tag.is-danger.is-light (str "$" (Math/round (:transaction/amount r)))])]]
|
||||
(for [[date description amt] similar]
|
||||
[:tr
|
||||
[:td (some-> date coerce/to-date-time (atime/unparse-local atime/normal-date))]
|
||||
[:td description]
|
||||
[:td (if (> amt 0.0)
|
||||
[:div.tag.is-success.is-light (str "$" (Math/round amt))]
|
||||
[:div.tag.is-danger.is-light (str "$" (Math/round amt))])]])]]
|
||||
[:div])))))
|
||||
|
||||
(defn transaction-rows* [{:keys [selected-client identity after]}]
|
||||
(let [recommendations (transaction-recommendations identity selected-client :after after)]
|
||||
@@ -178,28 +179,23 @@
|
||||
:after (:after route-params)})))
|
||||
|
||||
(defn insight-table* [{:keys [selected-client identity]}]
|
||||
[:div#insight-table {:hx-get (bidi/path-for ssr-routes/only-routes
|
||||
:transaction-insight-table
|
||||
:request-method :get)
|
||||
:hx-trigger "clientSelected from:body"
|
||||
:hx-swap "outerHTML swap:100ms"}
|
||||
|
||||
[:table.table
|
||||
[:thead
|
||||
[:tr
|
||||
[:td "Client"]
|
||||
[:td "Account"]
|
||||
[:td "Date"]
|
||||
[:td "Description"]
|
||||
[:td "Amount"]
|
||||
[:td "Vendor / Account"]
|
||||
[:td "action"]]]
|
||||
[:tbody
|
||||
(transaction-rows* {:selected-client selected-client
|
||||
:identity identity})]]
|
||||
[:div.container.htmx-indicator
|
||||
[:div.column.is-4.is-offset-4.has-text-centered
|
||||
[:div.loader.is-loading.is-active.big.is-centered]]]])
|
||||
(let [recommendations (transaction-recommendations identity selected-client)]
|
||||
(com/data-grid-card {:id "insight-table"
|
||||
:title "Transaction Insights"
|
||||
:route :transaction-insight-table
|
||||
:paginate? false
|
||||
:total (count recommendations)
|
||||
:action-buttons nil
|
||||
:rows (for [r recommendations
|
||||
:let [last? (= r (last recommendations))]]
|
||||
(transaction-row r :last? last?))
|
||||
:headers [(com/data-grid-header {} "Client")
|
||||
(com/data-grid-header {} "Account")
|
||||
(com/data-grid-header {} "Date")
|
||||
(com/data-grid-header {} "Description")
|
||||
(com/data-grid-header {} "Amount")
|
||||
(com/data-grid-header {:style {:width "4em"}} "Vendor / Account")
|
||||
(com/data-grid-header {})]})))
|
||||
|
||||
(defn insight-table [{:keys [session identity]}]
|
||||
(html-response (insight-table* {:selected-client
|
||||
@@ -208,10 +204,24 @@
|
||||
|
||||
(defn page [{:keys [identity matched-route session] :as request}]
|
||||
(base-page
|
||||
request
|
||||
[:div
|
||||
[:h1.title "Transaction Insights"]
|
||||
(insight-table* {:selected-client
|
||||
(-> session :client :db/id)
|
||||
:identity identity})]
|
||||
"Transaction Insights"))
|
||||
request
|
||||
(com/page {:nav (com/admin-aside-nav)
|
||||
:active-client (:client (:session request))
|
||||
:identity (:identity request)
|
||||
:app-params {:hx-get (bidi/path-for ssr-routes/only-routes
|
||||
:admin-history)
|
||||
:hx-trigger "clientSelected from:body"
|
||||
:hx-select "#app-contents"
|
||||
:hx-swap "outerHTML swap:300ms"}}
|
||||
(com/breadcrumbs {}
|
||||
[:a {:href (bidi/path-for client-routes/routes
|
||||
:transactions)}
|
||||
"Transactions"]
|
||||
[:a {:href (bidi/path-for ssr-routes/only-routes
|
||||
:transaction-insights)}
|
||||
"Insights"])
|
||||
(insight-table* {:selected-client
|
||||
(-> session :client :db/id)
|
||||
:identity identity}))
|
||||
|
||||
"Transaction Insights"))
|
||||
|
||||
Reference in New Issue
Block a user