Documentation.

This commit is contained in:
Simon Brooke 2026-09-28 14:24:43 +01:00
parent 27ec4e620a
commit 78f00dd97e
2 changed files with 148 additions and 36 deletions

View file

@ -15,6 +15,11 @@
(defmacro date? [x]
`(instance? java.time.LocalDate ~x))
(defmacro normalise-date [date default]
`(cond (date? ~date) ~date
(string? ~date) (ld/parse ~date)
:else (ld/parse ~default)))
(defn total-donations-by-weeks
"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
@ -22,17 +27,11 @@
`yyyy-mm-dd`. If `end-date` is not supplied, data for a single week will be
returned."
([start-date]
(let [sd (cond (date? start-date) start-date
(string? start-date) (ld/parse start-date)
:else (ld/parse "2024-01-01"))]
(let [sd (normalise-date start-date "2024-01-01")]
(total-donations-by-weeks sd (ld/plus-days sd 7))))
([start-date end-date]
(let [sd (cond (date? start-date) start-date
(string? start-date) (ld/parse start-date)
:else (ld/parse "2024-01-01"))
ed (cond (date? end-date) end-date
(string? end-date) (ld/parse end-date)
:else (ld/now))]
(let [sd (normalise-date start-date "2024-01-01")
ed (normalise-date end-date (ld/now))]
(loop [s sd
i (ld/plus-days sd 7)
r nil]
@ -95,6 +94,17 @@
{:created_at [> sd]}
{:created_at [<= ed]})))))))
(defn active-accounts
"Return, as an integer, the number of accounts believed to have been active
on this `date`, which should be
supplied as `java.time.LocalDate` objects, or as strings in the format
`yyyy-mm-dd`"
[date]
(let [d (normalise-date date (ld/now))]
(select 'accounts
(fields :id :display_name :created_at :campaign_url)
(where {:created_at [< d]}))))
(defn accounts-with-donations-by-date-range
"Return a list of maps, each representing one account from the `gaza.onl`
database, with a key `:donations`, whose value is a float representing the
@ -102,15 +112,9 @@
(exclusive) and `end-date` (inclusive). Date arguments should be supplied
as `java.time.LocalDate` objects, or as strings in the format `yyyy-mm-dd`."
[start-date end-date]
(let [sd (cond (date? start-date) start-date
(string? start-date) (ld/parse start-date))
ed (cond (date? end-date) end-date
(string? end-date) (ld/parse end-date)
:else (ld/now))
accounts (select 'accounts
(fields :id :display_name :created_at :campaign_url)
(where {:created_at [< sd]}))]
(map #(enhance-account-with-donations % sd ed) accounts)))
(let [sd (normalise-date start-date "2024-01-01")
ed (normalise-date end-date (ld/now))]
(map #(enhance-account-with-donations % sd ed) (active-accounts sd))))
(defn chart-donations-by-accounts-in-date-range
"Return a chart of donations received by individual accounts between
@ -146,20 +150,16 @@
are not displayed in sequential order; and there appears to be occasional
spurious data (but I don't know why)."
[start-date end-date]
(let [sd (cond (date? start-date) start-date
(string? start-date) (ld/parse start-date)
:else (ld/now))
ed (cond (date? end-date) end-date
(string? end-date) (ld/parse end-date)
:else (ld/now))]
(let [sd (normalise-date start-date "2024-01-01")
ed (normalise-date end-date (ld/now))]
(loop [s (.withDayOfMonth sd 1)
i (.withDayOfMonth (.plusMonths sd 1) 1)
m {}]
m (sorted-map)]
(let [data (accounts-with-donations-by-date-range s i)
m' (assoc m (format "Donations between\n%s and\n%s" s i)
[(range (count data)) (sort (map :donations data))])]
(if (ld/is-before ed i) (xy-chart m')
(recur i (.withDayOfMonth (.plusMonths i 1) 1) m'))))))
(if (ld/is-before ed i) (xy-chart m')
(recur i (.withDayOfMonth (.plusMonths i 1) 1) m'))))))
;; this does not work and I don't know why
;; (as-sql '(select 'campaign_donations
@ -168,3 +168,37 @@
;; {:campaign_url [= "https://www.chuffed.org/project/128261-photographer-from-gaza-rebuilding-life-dreams"]}
;; {:created_at [> "2024-04-01"]}
;; {:created_at [<= "2o24-04-30"]}))))
(defn average-donations-received-by-month
"Return data as a map keyed by the date of the first of each month between
`start-date` and `end-date`; the values are maps with keys `:average`,
`:count` and `:total`. Arguments should be supplied as `java.time.LocalDate`
objects, or as strings in the format `yyyy-mm-dd`."
[start-date end-date]
(let [sd (normalise-date start-date "2024-01-01")
ed (normalise-date end-date (ld/now))]
(loop [s (.withDayOfMonth sd 1)
i (.withDayOfMonth (.plusMonths sd 1) 1)
m (sorted-map)]
(let [accs (accounts-with-donations-by-date-range s i)
n (count accs)
total (reduce + (map :donations accs))
av (if (zero? n)
n
(float (/ total n)))
m' (assoc m s {:average av :count n :total total})]
(if (ld/is-before ed i)
m'
(recur i (.withDayOfMonth (.plusMonths i 1) 1) m'))))))
(defn chart-average-donations-received-by-month
"Return a chart comparing the average donations received per account with
the total number of active accounts per month between `start-date` and
`end-date`. Arguments should be supplied as `java.time.LocalDate` objects,
or as strings in the format `yyyy-mm-dd`."
[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))]
(format "Active accounts\nbetween\n%s and\n%s" start-date end-date)
[(map #(datetime->date %)(keys data)) (map :count (vals data))]})))