diff --git a/project.clj b/project.clj index 6aa9916..ae13963 100644 --- a/project.clj +++ b/project.clj @@ -5,6 +5,7 @@ [com.widdindustries/cljc.java-time "0.1.22"] [org.xerial/sqlite-jdbc "3.7.15-M1"]] :description "FIXME: write description" + :jvm-opts ["-Xmx4g"] :license {:name "GNU General Public License,version 2.0 or (at your option) an y later version" :url "https://www.gnu.org/licenses/old-licenses/gpl-2.0.en.html"} diff --git a/src/gaza_stats/core.clj b/src/gaza_stats/core.clj index 9ac610b..2415f8e 100644 --- a/src/gaza_stats/core.clj +++ b/src/gaza_stats/core.clj @@ -2,14 +2,15 @@ (:require [clojure.string :refer [capitalize]] [cljc.java-time.local-date :as ld] [com.hypirion.clj-xchart :refer [xy-chart]] - [korma.core :refer [fields join select where]] + [korma.core :refer [as-sql fields join select where]] [korma.db :refer [defdb sqlite3]])) ;; create db -(defdb db (sqlite3 - {:classname "org.sqlite.JDBC" - :subprotocol "sqlite" - :subname "resources/app.db"})) +(def db-spec {:classname "org.sqlite.JDBC" + :subprotocol "sqlite" + :subname "resources/app.db"}) + +(defdb db (sqlite3 db-spec)) (defmacro date? [x] `(instance? java.time.LocalDate ~x)) @@ -22,11 +23,13 @@ returned." ([start-date] (let [sd (cond (date? start-date) start-date - (string? start-date) (ld/parse start-date))] + (string? start-date) (ld/parse start-date) + :else (ld/parse "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)) + (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))] @@ -37,7 +40,7 @@ (fields :amount) (where (and {:created_at [> s]} {:created_at [<= i]}))) - amounts (map :amount data) + amounts (map :amount data) r' (cons [s {:total (reduce + amounts) :n (count data) @@ -49,8 +52,8 @@ (defmacro datetime->date [x] `(java.util.Date/from - (.toInstant (.atStartOfDay ~x) - (java.time.ZoneOffset/ofHours 0)))) + (.toInstant (.atStartOfDay ~x) + (java.time.ZoneOffset/ofHours 0)))) (defn chart-donations-by-weeks "Create and return an XY chart of data on field represented by `key` of @@ -62,7 +65,6 @@ [(map #(datetime->date (first %)) data) (map #(:n (nth % 1)) data)]}))) - (defn chart-total-donations-by-weeks "Create and return an XY chart of data on total donations made between `start-date` and `end-date`. Arguments may be supplied as @@ -76,3 +78,54 @@ `java.time.LocalDate` objects, or as strings in the format `yyyy-mm-dd`." [start-date end-date] (chart-donations-by-weeks start-date end-date :n)) + +(defn enhance-account-with-donations + "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." + [account-map sd ed] + (assoc account-map :donations + (reduce + 0 (map :amount + (select 'campaign_donations + (fields :amount) + (where (and + {:campaign_url [= (:campaign_url account-map)]} + {:created_at [> sd]} + {:created_at [<= ed]}))))))) + +(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 + 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`." + [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))) + +(defn chart-donations-by-accounts-in-date-range + "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." + [start-date end-date] + (let [data (accounts-with-donations-by-date-range start-date end-date)] + (xy-chart {(format "Donations between\n%s and\n%s" start-date end-date) + [(range (count data)) (sort (map :donations data))]}))) + +;; this does not work and I don't know why +;; (as-sql '(select 'campaign_donations +;; (fields :amount :campaign_url :created_at) +;; (where (and +;; {:campaign_url [= "https://www.chuffed.org/project/128261-photographer-from-gaza-rebuilding-life-dreams"]} +;; {:created_at [> "2024-04-01"]} +;; {:created_at [<= "2o24-04-30"]}))))