From 78f00dd97ea6a2a80a02f46cd39422600275e34f Mon Sep 17 00:00:00 2001 From: Simon Brooke Date: Mon, 28 Sep 2026 14:24:43 +0100 Subject: [PATCH] Documentation. --- README.md | 96 +++++++++++++++++++++++++++++++++++++---- src/gaza_stats/core.clj | 88 +++++++++++++++++++++++++------------ 2 files changed, 148 insertions(+), 36 deletions(-) diff --git a/README.md b/README.md index 12748c6..6ab6ccf 100644 --- a/README.md +++ b/README.md @@ -26,7 +26,27 @@ At present several useful functions are implemented: 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 total donations received by that account between the dates `start-date` (exclusive) and `end-date` (inclusive). Date arguments should be supplied as `java.time.LocalDate` objects, or as strings in the format `yyyy-mm-dd`. -[view source](https://git.journeyman.cc/simon/gaza-stats/src/branch/main/src/gaza_stats/core.clj#L98) +[view source](https://git.journeyman.cc/simon/gaza-stats/src/branch/main/src/gaza_stats/core.clj#L108) + +### active-accounts + +``` +(active-accounts date) +``` + +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` + +[view source](https://git.journeyman.cc/simon/gaza-stats/src/branch/main/src/gaza_stats/core.clj#L97) + +### average-donations-received-by-month + +``` +(average-donations-received-by-month start-date end-date) +``` + +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`. + +[view source](https://git.journeyman.cc/simon/gaza-stats/src/branch/main/src/gaza_stats/core.clj#L172) ### chart-accounts-with-donations-by-months-in-range @@ -38,7 +58,17 @@ Return a chart with one line for each calendar month covering from `start-date` TODO: There are currently two problems with this at present: chart legends are not displayed in sequential order; and there appears to be occasional spurious data (but I don’t know why). -[view source](https://git.journeyman.cc/simon/gaza-stats/src/branch/main/src/gaza_stats/core.clj#L138) +[view source](https://git.journeyman.cc/simon/gaza-stats/src/branch/main/src/gaza_stats/core.clj#L142) + +### chart-average-donations-received-by-month + +``` +(chart-average-donations-received-by-month start-date end-date) +``` + +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`. + +[view source](https://git.journeyman.cc/simon/gaza-stats/src/branch/main/src/gaza_stats/core.clj#L194) ### chart-donations-as-percentage-in-date-range @@ -48,7 +78,7 @@ TODO: There are currently two problems with this at present: chart legends are Return a chart of donations received by individual accounts between `start-date` (exclusive) and `end-date` (inclusive). Arguments should be supplied as `java.time.LocalDate` objects, or as strings in the format `yyyy-mm-dd`. Accounts are identified in the chart only by rank order. -[view source](https://git.journeyman.cc/simon/gaza-stats/src/branch/main/src/gaza_stats/core.clj#L125) +[view source](https://git.journeyman.cc/simon/gaza-stats/src/branch/main/src/gaza_stats/core.clj#L129) ### chart-donations-by-accounts-in-date-range @@ -58,7 +88,7 @@ Return a chart of donations received by individual accounts between `start-date` Return a chart of donations received by individual accounts between `start-date` (exclusive) and `end-date` (inclusive). Arguments should be supplied as `java.time.LocalDate` objects, or as strings in the format `yyyy-mm-dd`. Accounts are identified in the chart only by rank order. -[view source](https://git.journeyman.cc/simon/gaza-stats/src/branch/main/src/gaza_stats/core.clj#L115) +[view source](https://git.journeyman.cc/simon/gaza-stats/src/branch/main/src/gaza_stats/core.clj#L119) ### chart-donations-by-weeks @@ -68,7 +98,7 @@ Return a chart of donations received by individual accounts between `start-date` Create and return an XY chart of data on field represented by `key` 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`. -[view source](https://git.journeyman.cc/simon/gaza-stats/src/branch/main/src/gaza_stats/core.clj#L58) +[view source](https://git.journeyman.cc/simon/gaza-stats/src/branch/main/src/gaza_stats/core.clj#L57) ### chart-number-donations-by-weeks @@ -78,7 +108,7 @@ Create and return an XY chart of data on field represented by `key` of donations Create and return an XY chart of data on number 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`. -[view source](https://git.journeyman.cc/simon/gaza-stats/src/branch/main/src/gaza_stats/core.clj#L75) +[view source](https://git.journeyman.cc/simon/gaza-stats/src/branch/main/src/gaza_stats/core.clj#L74) ### chart-total-donations-by-weeks @@ -88,7 +118,43 @@ Create and return an XY chart of data on number of donations made between `start Create and return an XY chart of data on total 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`. -[view source](https://git.journeyman.cc/simon/gaza-stats/src/branch/main/src/gaza_stats/core.clj#L68) +[view source](https://git.journeyman.cc/simon/gaza-stats/src/branch/main/src/gaza_stats/core.clj#L67) + +### date? + +#### macro + +``` +(date? x) +``` + +**TODO**: write docs + +[view source](https://git.journeyman.cc/simon/gaza-stats/src/branch/main/src/gaza_stats/core.clj#L15) + +### datetime->date + +#### macro + +``` +(datetime->date x) +``` + +**TODO**: write docs + +[view source](https://git.journeyman.cc/simon/gaza-stats/src/branch/main/src/gaza_stats/core.clj#L52) + +### db + +**TODO**: write docs + +[view source](https://git.journeyman.cc/simon/gaza-stats/src/branch/main/src/gaza_stats/core.clj#L13) + +### db-spec + +**TODO**: write docs + +[view source](https://git.journeyman.cc/simon/gaza-stats/src/branch/main/src/gaza_stats/core.clj#L9) ### enhance-account-with-donations @@ -98,7 +164,19 @@ Create and return an XY chart of data on total donations made between `start-dat Take this `account_map`, which must at least have a valid value for the key `:campaign_url` and return a similar map including the key `:donations`, whose value is a float representing the total donations received by that campaign URL between the dates `sd` (exclusive) and `ed` (inclusive). Date arguments should be supplied as `java.time.LocalDate` objects. -[view source](https://git.journeyman.cc/simon/gaza-stats/src/branch/main/src/gaza_stats/core.clj#L82) +[view source](https://git.journeyman.cc/simon/gaza-stats/src/branch/main/src/gaza_stats/core.clj#L81) + +### normalise-date + +#### macro + +``` +(normalise-date date default) +``` + +**TODO**: write docs + +[view source](https://git.journeyman.cc/simon/gaza-stats/src/branch/main/src/gaza_stats/core.clj#L18) ### total-donations-by-weeks @@ -106,7 +184,7 @@ Take this `account_map`, which must at least have a valid value for the key `:ca Return a list of pairs [java.time.LocalDate date, float amount](file:///home/simon/workspace/gaza-stats/docs/codox/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 returned. -[view source](https://git.journeyman.cc/simon/gaza-stats/src/branch/main/src/gaza_stats/core.clj#L18) +[view source](https://git.journeyman.cc/simon/gaza-stats/src/branch/main/src/gaza_stats/core.clj#L23) ### Viewing charts diff --git a/src/gaza_stats/core.clj b/src/gaza_stats/core.clj index 94ca779..d415609 100644 --- a/src/gaza_stats/core.clj +++ b/src/gaza_stats/core.clj @@ -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))]})))