added graphs

This commit is contained in:
Bryce Covert
2019-02-15 15:34:40 -08:00
parent 7e1de79fea
commit a86b7c03bf
8 changed files with 212 additions and 6 deletions

View File

@@ -18,6 +18,7 @@
[dk.ative/docjure "1.12.0"]
[org.clojure/java.jdbc "0.7.3"]
[cljsjs/dropzone "4.3.0-0"]
[cljsjs/recharts "1.4.2-0"]
[clj-fuzzy "0.4.1"]
[honeysql "0.9.2"]
[com.walmartlabs/lacinia "0.25.0"]
@@ -47,6 +48,7 @@
[com.amazonaws/aws-java-sdk-sqs "1.11.282"]
[com.amazonaws/aws-java-sdk-s3 "1.11.282"]
[org.clojure/data.json "0.2.6"]
[org.clojure/data.csv "0.1.4"]
[cider/cider-nrepl "0.16.0"]
[io.rkn/conformity "0.5.1"]
[hiccup "1.0.5"]]
@@ -74,7 +76,6 @@
[javax.servlet/servlet-api "2.5"]
[figwheel-sidecar "0.5.13"]
[com.cemerick/piggieback "0.2.2"]
[org.clojure/data.csv "0.1.4"]
]
:plugins [[lein-figwheel "0.5.13"]

View File

@@ -749,3 +749,15 @@
(defn migrate-users [conn]
[(load-users (users/get-all))])
(defn merge-query [query-part-1 query-part-2]
(-> query-part-1
(update-in [:query :find] into (get-in query-part-2 [:query :find]))
(update-in [:query :in] into (get-in query-part-2 [:query :in]))
(update-in [:query :where] into (get-in query-part-2 [:query :where]))
(update-in [:args] into (get-in query-part-2 [:args]))))

View File

@@ -69,3 +69,4 @@
(d/release conn)
(println "Done")))
#_(-main)

View File

@@ -8,6 +8,9 @@
[buddy.auth :refer [throw-unauthorized]]
[auto-ap.utils :refer [by]]
[auto-ap.graphql.utils :refer [assert-admin can-see-client? assert-can-see-client]]
[auto-ap.datomic :refer [uri merge-query]]
[datomic.api :as d]
[auto-ap.expense-accounts :as e-expense-accounts]
[auto-ap.datomic.clients :as d-clients]
[auto-ap.datomic.checks :as d-checks]
[auto-ap.datomic.users :as d-users]
@@ -199,12 +202,31 @@
:start {:type 'Int}
:end {:type 'Int}}}
:check_result {:fields {:invoices {:type '(list :invoice)}
:pdf_url {:type 'String}}}}
:pdf_url {:type 'String}}}
:expense_account_stat {:fields {:expense_account_id {:type 'Int}
:expense_account_name {:type 'String}
:total {:type 'String}}}
:invoice_stat {:fields {:name {:type 'String}
:paid {:type 'String}
:unpaid {:type 'String}}}
}
:queries
{:invoice_page {:type '(list :invoice_page)
{:expense_account_stats {:type '(list :expense_account_stat)
:args {:client_id {:type :id}}
:resolve :get-expense-account-stats}
:invoice_stats {:type '(list :invoice_stat)
:args {:client_id {:type :id}}
:resolve :get-invoice-stats}
:invoice_page {:type '(list :invoice_page)
:args {:import_status {:type 'String}
:status {:type 'String}
:client_id {:type :id}
@@ -465,6 +487,61 @@
(:bank_account_id args)
(:type args))))
(defn get-expense-account-stats [context {:keys [client_id] } value]
(let [result (cond-> {:query {:find ['?expense-account-id '(sum ?amount)]
:in ['$]
:where []}
:args [(d/db (d/connect uri)) client_id]}
client_id (merge-query {:query {:in ['?c]}
:args [client_id]})
(not client_id) (merge-query {:query {:where ['[?c :client/name]]}})
true (merge-query {:query {:where ['[?i :invoice/client ?c]
'[?i :invoice/expense-accounts ?expense-account]
'[?expense-account :invoice-expense-account/expense-account-id ?expense-account-id]
'[?expense-account :invoice-expense-account/amount ?amount]]}})
true (doto println)
true (d/query ))]
(for [[expense-account-id total] result]
{:expense_account_id expense-account-id :total total :expense_account_name (-> expense-account-id e-expense-accounts/expense-accounts :name)})))
(defn categorize [x]
(cond (<= x 0) :due
(<= x 30 ) :due-30
(<= x 60 ) :due-60
:else :due-later))
(defn get-invoice-stats [context {:keys [client_id] } value]
(let [result (cond-> {:query {:find ['?name '(sum ?outstanding-balance) '(sum ?total)]
:in ['$]
:where []}
:args [(d/db (d/connect uri)) client_id]}
client_id (merge-query {:query {:in ['?c]}
:args [client_id]})
(not client_id) (merge-query {:query {:where ['[?c :client/name]]}})
true (merge-query {:query {:where ['[?i :invoice/client ?c]
'[?i :invoice/outstanding-balance ?outstanding-balance]
'[?i :invoice/total ?total]
'[?i :invoice/date ?date]
'[(.toInstant ^java.util.Date ?date) ?d2]
'[(.between java.time.temporal.ChronoUnit/DAYS (java.time.Instant/now) ?d2 ) ?d3]
'[(+ 30 ?d3) ?d4]
'[(auto-ap.graphql/categorize ?d4) ?name]]}})
true (d/query ))
result (group-by first result)]
(for [[id name] [[:due "Due"] [:due-30 "0-30 days"] [:due-60 "31-60 days"] [:due-later ">60 days"] ]
:let [[[_ outstanding-balance total] ] (id result nil)
outstanding-balance (or outstanding-balance 0)
total (or total 0)]]
{:name name :unpaid outstanding-balance :paid (if (= :due id)
0
(- total outstanding-balance))})))
(def schema
@@ -474,6 +551,8 @@
:get-all-payments get-all-payments
:get-payment-page gq-checks/get-payment-page
:get-transaction-page gq-transactions/get-transaction-page
:get-expense-account-stats get-expense-account-stats
:get-invoice-stats get-invoice-stats
:get-client gq-clients/get-client
:get-user get-user

View File

@@ -8,7 +8,8 @@
[auto-ap.effects :as effects]
[pushy.core :as pushy]
[auto-ap.history :as p]
[bidi.bidi :as bidi]))
[bidi.bidi :as bidi]
[cljsjs.recharts]))
(defn dev-setup []
(when true

View File

@@ -18,6 +18,7 @@
::initialize-db
(fn [{:keys [db]} [_ token]]
(let [handler (:handler (bidi/match-route routes/routes (.. js/window -location -pathname)))]
(prn (and token (jwt->data token)))
(cond
(and (not= :login handler) (not token))
{:redirect "/login"

View File

@@ -14,6 +14,7 @@
[auto-ap.views.pages.login :refer [login-page]]
[auto-ap.views.pages.checks :refer [checks-page]]
[auto-ap.views.pages.admin :refer [admin-page]]
[auto-ap.views.pages.home :refer [home-page]]
[auto-ap.views.pages.admin.clients :refer [admin-clients-page]]
[auto-ap.views.pages.admin.vendors :refer [admin-vendors-page]]
[auto-ap.views.pages.admin.excel-import :refer [admin-excel-import-page]]
@@ -59,8 +60,8 @@
(admin-yodlee-page))
(defmethod page :index [_]
[side-bar-layout {:side-bar [:div]
:main [:h1 "Home"]}])
(home-page)
)
(defmethod page :login [_]
[login-page])

View File

@@ -0,0 +1,110 @@
(ns auto-ap.views.pages.home
(:require [auto-ap.views.components.layouts :refer [side-bar-layout]]
[re-frame.core :as re-frame]
[auto-ap.subs :as subs]
[reagent.core :as r]))
(def pie-chart (r/adapt-react-class js/Recharts.PieChart))
(def pie (r/adapt-react-class js/Recharts.Pie))
(def bar-chart (r/adapt-react-class js/Recharts.BarChart))
(def x-axis (r/adapt-react-class js/Recharts.XAxis))
(def y-axis (r/adapt-react-class js/Recharts.YAxis))
(def bar (r/adapt-react-class js/Recharts.Bar))
(def legend (r/adapt-react-class js/Recharts.Legend))
(def cell (r/adapt-react-class js/Recharts.Cell))
(def tool-tip (r/adapt-react-class js/Recharts.Tooltip))
(def colors ["hsl(171, 100%, 41%)" "hsl(217, 71%, 53%)" "hsl(141, 71%, 48%)" "hsl(48, 100%, 67%)" "hsl(348, 100%, 61%)" "hsl(217, 71%, 53%)"])
(def light-colors ["hsl(171, 60%, 80%)" "hsl(217, 71%, 53%)" "hsl(141, 71%, 48%)" "hsl(48, 100%, 67%)" "hsl(348, 100%, 61%)" "hsl(217, 71%, 53%)"])
(defn make-pie-chart
[{:keys [width height data]}]
[pie-chart {:width width
:height height}
[pie {:fill "#82ca9d"
:data data
:dataKey "value"
:inner-radius 20}
(map (fn [x y]
^{:key y}
[cell {:key y :fill (colors y)}]) data (range))
]
[tool-tip]
[legend]])
(defn make-bar-chart [{:keys [width height data]}]
[bar-chart {:width width :height height :data data :fill "#FFFFFF"}
[tool-tip]
[bar {:dataKey "paid" :fill (get colors 0) :stackId "a" :name "Paid"}]
[bar {:dataKey "unpaid" :fill (get light-colors 0) :stackId "a" :name "Unpaid"}]
[x-axis {:dataKey "name"}]
[y-axis]
[legend]]
)
(re-frame/reg-event-db
::received
(fn [db [_ {:keys [expense-account-stats invoice-stats]}]]
(let [expense-account-stats (->> expense-account-stats
(map #(update % :total (fn [t] (js/parseFloat t))))
(sort-by :total)
(reverse)
(take 5))
top-5 (vec (take 5 expense-account-stats))
rest (drop 5 expense-account-stats)
other {:expense-account-id 0 :expense-account-name "Other" :total (reduce + 0 (map :total rest))}]
(cond-> db
(seq top-5)
(assoc ::top-expense-categories (conj top-5 other))
(seq invoice-stats)
(assoc ::invoice-stats invoice-stats)
)
)))
(re-frame/reg-sub
::invoice-stats
(fn [db]
(println (::invoice-stats db))
(::invoice-stats db)))
(re-frame/reg-sub
::top-expense-categories
(fn [db]
(::top-expense-categories db)))
(re-frame/reg-event-fx
::mounted
(fn [{:keys [db]} _]
{:db (assoc db ::top-expense-categories nil)
:graphql {:token (-> db :user)
:query-obj {:venia/queries [[:expense_account_stats
{:client-id (:id @(re-frame/subscribe [::subs/client]))}
[:expense-account-id :total :expense-account-name]]
[:invoice_stats
{:client-id (:id @(re-frame/subscribe [::subs/client]))}
[:name :paid :unpaid]]]}
:on-success [::received]}}))
(defn home-content []
(let [client-id (-> @(re-frame/subscribe [::subs/client]) :id)]
^{:key client-id}
[side-bar-layout {:side-bar [:div]
:main [:div [:h1.title "Home"]
[:h1.title.is-4 "Top expense categories"]
(let [expense-categories @(re-frame/subscribe [::top-expense-categories])]
(make-pie-chart {:width 800 :height 500 :data (clj->js
(map (fn [x] {:name (:expense-account-name x) :value (:total x)}) expense-categories))}))
[:h1.title.is-4 "Upcoming Bills"]
(make-bar-chart {:width 800 :height 500 :data (clj->js
@(re-frame/subscribe [::invoice-stats]))})]}]))
(defn home-page []
(let [client-id (-> @(re-frame/subscribe [::subs/client]) :id)]
(re-frame/dispatch [::mounted])
^{:key client-id} [home-content]))