Code tidy up and more documentation.
This commit is contained in:
parent
abf50bd034
commit
0e814583b7
2 changed files with 95 additions and 61 deletions
|
|
@ -1,27 +1,49 @@
|
|||
(ns gaza-stats.core
|
||||
"Functions to interrogate the [`gaza.onl`](https://gaza.onl/app.db) database."
|
||||
(:require [clojure.string :refer [capitalize]]
|
||||
[cljc.java-time.local-date :as ld]
|
||||
[com.hypirion.clj-xchart :refer [xy-chart]]
|
||||
[korma.core :refer [as-sql fields join select where]]
|
||||
[korma.db :refer [defdb sqlite3]]))
|
||||
[korma.db :refer [defdb sqlite3]])
|
||||
(:import [java.util Date]
|
||||
[java.time LocalDate ZoneId ZoneOffset]))
|
||||
|
||||
;; create db
|
||||
(def db-spec {:classname "org.sqlite.JDBC"
|
||||
:subprotocol "sqlite"
|
||||
:subname "resources/app.db"})
|
||||
(def db-spec
|
||||
"Database specification in a format which can be used with either JDBC or
|
||||
Korma."
|
||||
{:classname "org.sqlite.JDBC"
|
||||
:subprotocol "sqlite"
|
||||
:subname "resources/app.db"})
|
||||
|
||||
(defdb db (sqlite3 db-spec))
|
||||
(defdb db
|
||||
;; Korma-specific database handle.
|
||||
(sqlite3 db-spec))
|
||||
|
||||
(defmacro date? [x]
|
||||
`(instance? java.time.LocalDate ~x))
|
||||
(defmacro local-date?
|
||||
"Is this `x` an instance of `java.time.LocalDate`?"
|
||||
[x]
|
||||
`(instance? LocalDate ~x))
|
||||
|
||||
(defmacro normalise-date [date default]
|
||||
`(cond (date? ~date) ~date
|
||||
(defmacro normalise-date
|
||||
"More messing around with Java's bizarre mess of date/time classes. We want
|
||||
our default date representation to be `java.time.LocalDate`, except, of
|
||||
course, when we don't."
|
||||
[date default]
|
||||
`(cond (local-date? ~date) ~date
|
||||
(string? ~date) (ld/parse ~date)
|
||||
:else (ld/parse ~default)))
|
||||
;; this one should catch java.sql.Date as well, since it is a subclass
|
||||
(instance? Date ~date) (.toLocalDate
|
||||
(.atZone (.toInstant ~date)
|
||||
(ZoneId/systemDefault)))
|
||||
(nil? ~date) (ld/now)
|
||||
;; if I'd written this as a fn, I'd just recurse,,, might be worth it.
|
||||
:else (if (local-date? ~default)
|
||||
~default
|
||||
(ld/parse (str ~default)))))
|
||||
|
||||
(defn total-donations-by-weeks
|
||||
"Return a list of pairs [java.time.LocalDate date, float amount] representing
|
||||
"Return a list of pairs `[java.time.LocalDate date, float amount]` representing
|
||||
the total of donations made between `start-date` and `end-date`. Arguments may
|
||||
be supplied as `java.time.LocalDate` objects, or as strings in the format
|
||||
`yyyy-mm-dd`. If `end-date` is not supplied, data for a single week will be
|
||||
|
|
@ -49,10 +71,14 @@
|
|||
(if (ld/is-before ed i) (reverse r')
|
||||
(recur i (ld/plus-days i 7) r')))))))
|
||||
|
||||
(defmacro datetime->date [x]
|
||||
`(java.util.Date/from
|
||||
(defmacro local-date->date
|
||||
"Construct and return a new `java.util.Date` object from a
|
||||
`java.time.LocalDate` object. As usual, doing anything with Java time/date
|
||||
stuff is a complete bureaucratic nightmare."
|
||||
[x]
|
||||
`(Date/from
|
||||
(.toInstant (.atStartOfDay ~x)
|
||||
(java.time.ZoneOffset/ofHours 0))))
|
||||
(ZoneOffset/ofHours 0))))
|
||||
|
||||
(defn chart-donations-by-weeks
|
||||
"Create and return an XY chart of data on field represented by `key` of
|
||||
|
|
@ -61,7 +87,7 @@
|
|||
[start-date end-date key]
|
||||
(let [data (total-donations-by-weeks start-date end-date)]
|
||||
(xy-chart {(format "%s Donations" (capitalize (name key)))
|
||||
[(map #(datetime->date (first %)) data)
|
||||
[(map #(local-date->date (first %)) data)
|
||||
(map #(:n (nth % 1)) data)]})))
|
||||
|
||||
(defn chart-total-donations-by-weeks
|
||||
|
|
@ -195,6 +221,6 @@
|
|||
[start-date end-date]
|
||||
(let [data (average-donations-received-by-month start-date end-date)]
|
||||
(xy-chart {(format "Average donations\nper active account\nbetween\n%s and\n%s" start-date end-date)
|
||||
[(map #(datetime->date %)(keys data)) (map :average (vals data))]
|
||||
[(map #(local-date->date %) (keys data)) (map :average (vals data))]
|
||||
(format "Active accounts\nbetween\n%s and\n%s" start-date end-date)
|
||||
[(map #(datetime->date %)(keys data)) (map :count (vals data))]})))
|
||||
[(map #(local-date->date %) (keys data)) (map :count (vals data))]})))
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue