diff --git a/migrator/migrations/1523064177-UP-add-reminders.sql b/migrator/migrations/1523064177-UP-add-reminders.sql index bda22ced..2a15a85f 100644 --- a/migrator/migrations/1523064177-UP-add-reminders.sql +++ b/migrator/migrations/1523064177-UP-add-reminders.sql @@ -3,6 +3,6 @@ CREATE TABLE reminders ( id serial primary key, vendor_id integer, - scheduled date, - sent date + scheduled timestamp with time zone, + sent timestamp with time zone ); diff --git a/src/clj/auto_ap/db/reminders.clj b/src/clj/auto_ap/db/reminders.clj index f1d3e04f..b2b13e64 100644 --- a/src/clj/auto_ap/db/reminders.clj +++ b/src/clj/auto_ap/db/reminders.clj @@ -1,8 +1,44 @@ (ns auto-ap.db.reminders (:require [clojure.java.jdbc :as j] [auto-ap.parse :as parse] - [auto-ap.db.utils :refer [clj->db db->clj get-conn]])) + [auto-ap.db.utils :refer [clj->db db->clj get-conn]] + [clj-time.jdbc] + [clj-time.core :as time] + [clojure.string :as str])) + +(defn insert [row] + (db->clj (first (j/insert! (get-conn) + :reminders + (clj->db row))))) (defn get-all [] - (doto (map db->clj (j/query (get-conn) "SELECT reminders.*, vendors.name as vendor_name FROM reminders INNER join vendors on reminders.vendor_id = vendors.id")) - println)) + (map db->clj (j/query (get-conn) + (str + " SELECT reminders.*, vendors.name as vendor_name " + " FROM reminders INNER join vendors on reminders.vendor_id = vendors.id " + " ORDER BY sent DESC, scheduled DESC")))) + +(defn delete-all [] + (j/delete! (get-conn) :reminders [])) + +(defn find-future [vendors] + (map db->clj (j/query (get-conn) (into [(str "SELECT * FROM reminders " + "WHERE vendor_id in (" + (str/join "," (take (count vendors) (repeat "?"))) + ") " + "AND " + "scheduled > NOW()" + ) ] + vendors)))) + + +(defn get-ready [] + (map db->clj (j/query (get-conn) + (str + " SELECT reminders.*, vendors.name as vendor_name, vendors.email " + " FROM reminders INNER join vendors on reminders.vendor_id = vendors.id " + " WHERE reminders.sent IS NULL AND reminders.scheduled < NOW()")))) + +(defn finish [id] + (j/update! (get-conn) :reminders {:sent (time/now)} ["id = ?" id] )) + diff --git a/src/clj/auto_ap/db/vendors.clj b/src/clj/auto_ap/db/vendors.clj index d7a0b8b7..43286173 100644 --- a/src/clj/auto_ap/db/vendors.clj +++ b/src/clj/auto_ap/db/vendors.clj @@ -24,3 +24,6 @@ (parse (first (j/insert! (get-conn) :vendors (clj->db data))))) + +(defn find-with-reminders [] + (map parse (j/query (get-conn) ["SELECT * FROM vendors WHERE invoice_reminder_schedule = ?" "Weekly"]))) diff --git a/src/clj/auto_ap/routes/reminders.clj b/src/clj/auto_ap/routes/reminders.clj index 2f803fce..d1c51a11 100644 --- a/src/clj/auto_ap/routes/reminders.clj +++ b/src/clj/auto_ap/routes/reminders.clj @@ -4,26 +4,74 @@ [auto-ap.db.reminders :as reminders] [auto-ap.routes.utils :refer [wrap-secure]] [amazonica.aws.simpleemail :as ses] - )) + [clj-time.core :as time] + [clj-time.coerce :as c] + [clj-time.predicates :as pred] + [clj-time.periodic :as p] + [clj-time.local :as l] + [clj-time.jdbc] + ) + (:import [org.joda.time DateTime])) + +(defn next-sunday [] + (let [sunday (->> (p/periodic-seq (time/today) (time/days 1)) + (filter pred/sunday?) + first) + ] + + (.toDateTime (time/local-date-time (time/year sunday) (time/month sunday) (time/day sunday))))) + +(defn schedule-reminders [] + (let [vendors (vendors/find-with-reminders) + future-reminders (reminders/find-future (map :id vendors)) + has-reminder-scheduled? (set (map :vendor-id future-reminders)) + vendors-without-scheduled (filter #(not (has-reminder-scheduled? (:id %))) vendors)] + (println "Reminders already scheduled:" future-reminders) + (println "Reminders will happen at" (next-sunday)) + (println "Reminders to schedule" vendors-without-scheduled) + + (doseq [{:keys [id invoice-reminder-schedule]} vendors-without-scheduled] + (reminders/insert {:vendor-id id + :scheduled (next-sunday)})))) + +(defn find-ready-reminders [] + (let [vendors (vendors/get-all) + ready-reminders (reminders/get-ready)] + ready-reminders + )) + +(defn send-emails [reminders] + (doseq [{:keys [name email id]} reminders] + (println "Sending email to" email) + (ses/send-email :destination {:to-addresses [email]} + :source "invoices@mail.integreat.aws.brycecovertoperations.com" + :message {:subject "Reminder to send invoices" + :body {:html (str "
This is a reminder to send this week's invoices to us. You can just reply to this email.
- Integreat.
") + :text (str "Hello " name ",\r\nThis is a reminder to send this week's invoices to us. You can just reply to this email.\r\n - Integreat.")}}) + (reminders/finish id))) + +(defn replace-joda [x] + (into {} (map (fn [[k v]] + [k (if (instance? DateTime v) + (c/to-string v) + v)]) + x)) + ) (defroutes routes (context "/reminders" [] (POST "/send" {:keys [query-params]} - (doseq [{:keys [name email invoice-reminder-schedule]} (vendors/get-all)] - (when (= "Weekly" invoice-reminder-schedule) - (println "Sending email to" email) - (ses/send-email :destination {:to-addresses [email]} - :source "invoices@mail.integreat.aws.brycecovertoperations.com" - :message {:subject "Reminder to send invoices" - :body {:html (str "This is a reminder to send this week's invoices to us. You can just reply to this email.
- Integreat.
") - :text (str "Hello " name ",\r\nThis is a reminder to send this week's invoices to us. You can just reply to this email.\r\n - Integreat.")}}))) + (schedule-reminders) + (-> (reminders/get-ready) + (send-emails)) + {:status 200 :body "{}" :headers {"Content-Type" "application/edn"}}) (wrap-routes (GET "/" {:keys [query-params]} {:status 200 - :body (pr-str (reminders/get-all)) + :body (pr-str (map replace-joda (reminders/get-all))) :headers {"Content-Type" "application/edn"}}) wrap-secure))) diff --git a/src/cljs/auto_ap/views/pages/admin/reminders.cljs b/src/cljs/auto_ap/views/pages/admin/reminders.cljs index 48ec386c..2e6ed089 100644 --- a/src/cljs/auto_ap/views/pages/admin/reminders.cljs +++ b/src/cljs/auto_ap/views/pages/admin/reminders.cljs @@ -20,9 +20,9 @@ ^{:key id} [:tr [:td vendor-name] - [:td (.toString scheduled)] + [:td (str scheduled)] [:td (when sent - [:span [:span.icon [:i.fa.fa-check]] "Sent " (.toString sent)]) ]])]])) + [:span [:span.icon [:i.fa.fa-check]] "Sent " (str sent)]) ]])]])) (defn admin-reminders-page []