feat(grfn/bbbg): Add attendee checks
Change-Id: I7f96597ab3f0552cdecd0abac1ef50a68d3e0b7b Reviewed-on: https://cl.tvl.fyi/c/depot/+/4508 Tested-by: BuildkiteCI Reviewed-by: grfn <grfn@gws.fyi> Autosubmit: grfn <grfn@gws.fyi>
This commit is contained in:
		
							parent
							
								
									4ad94b9cf8
								
							
						
					
					
						commit
						4643585e01
					
				
					 13 changed files with 333 additions and 7 deletions
				
			
		| 
						 | 
					@ -40,11 +40,13 @@
 | 
				
			||||||
  yogthos/config {:mvn/version "1.1.8"}
 | 
					  yogthos/config {:mvn/version "1.1.8"}
 | 
				
			||||||
  clojure.java-time/clojure.java-time {:mvn/version "0.3.3"}
 | 
					  clojure.java-time/clojure.java-time {:mvn/version "0.3.3"}
 | 
				
			||||||
  cheshire/cheshire {:mvn/version "5.10.1"}
 | 
					  cheshire/cheshire {:mvn/version "5.10.1"}
 | 
				
			||||||
 | 
					  org.apache.commons/commons-lang3 {:mvn/version "3.11"}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  ;; Spec
 | 
					  ;; Spec
 | 
				
			||||||
  org.clojure/spec.alpha {:mvn/version "0.3.214"}
 | 
					  org.clojure/spec.alpha {:mvn/version "0.3.214"}
 | 
				
			||||||
  org.clojure/core.specs.alpha {:mvn/version "0.2.62"}
 | 
					  org.clojure/core.specs.alpha {:mvn/version "0.2.62"}
 | 
				
			||||||
  expound/expound {:mvn/version "0.8.10"}}
 | 
					  expound/expound {:mvn/version "0.8.10"}
 | 
				
			||||||
 | 
					  org.clojure/test.check {:mvn/version "1.1.0"}}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 :paths
 | 
					 :paths
 | 
				
			||||||
 ["src"
 | 
					 ["src"
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -119,6 +119,19 @@ let repos = [
 | 
				
			||||||
    paths = [ src ];
 | 
					    paths = [ src ];
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  rec {
 | 
				
			||||||
 | 
					    name = "commons-lang3/org.apache.commons";
 | 
				
			||||||
 | 
					    src = fetchMavenArtifact {
 | 
				
			||||||
 | 
					      inherit repos;
 | 
				
			||||||
 | 
					      artifactId = "commons-lang3";
 | 
				
			||||||
 | 
					      groupId = "org.apache.commons";
 | 
				
			||||||
 | 
					      sha512 = "c1f6b5cb9ac47cfb612423a71b347568f3697cf88018b5808678be5234c50b22888db23cb833b7d8d458d39707ab9e4d839107d1d3306de2e4e422010c95180f";
 | 
				
			||||||
 | 
					      version = "3.11";
 | 
				
			||||||
 | 
					      
 | 
				
			||||||
 | 
					    };
 | 
				
			||||||
 | 
					    paths = [ src ];
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  rec {
 | 
					  rec {
 | 
				
			||||||
    name = "tools.logging/org.clojure";
 | 
					    name = "tools.logging/org.clojure";
 | 
				
			||||||
    src = fetchMavenArtifact {
 | 
					    src = fetchMavenArtifact {
 | 
				
			||||||
| 
						 | 
					@ -1198,6 +1211,19 @@ let repos = [
 | 
				
			||||||
    paths = [ src ];
 | 
					    paths = [ src ];
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  rec {
 | 
				
			||||||
 | 
					    name = "test.check/org.clojure";
 | 
				
			||||||
 | 
					    src = fetchMavenArtifact {
 | 
				
			||||||
 | 
					      inherit repos;
 | 
				
			||||||
 | 
					      artifactId = "test.check";
 | 
				
			||||||
 | 
					      groupId = "org.clojure";
 | 
				
			||||||
 | 
					      sha512 = "68caa189e7292da5dfde92d795ce35ff1980108a579dc11ca618bf0e480101b8ded16fc8ab816b30f364afbccbb8f02fc9194271e7f5323b8042d468164ecb64";
 | 
				
			||||||
 | 
					      version = "1.1.0";
 | 
				
			||||||
 | 
					      
 | 
				
			||||||
 | 
					    };
 | 
				
			||||||
 | 
					    paths = [ src ];
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  rec {
 | 
					  rec {
 | 
				
			||||||
    name = "ring-servlet/ring";
 | 
					    name = "ring-servlet/ring";
 | 
				
			||||||
    src = fetchMavenArtifact {
 | 
					    src = fetchMavenArtifact {
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -0,0 +1 @@
 | 
				
			||||||
 | 
					DROP TABLE "attendee_check";
 | 
				
			||||||
| 
						 | 
					@ -0,0 +1,7 @@
 | 
				
			||||||
 | 
					CREATE TABLE attendee_check (
 | 
				
			||||||
 | 
					    "id" UUID PRIMARY KEY NOT NULL DEFAULT uuid_generate_v4(),
 | 
				
			||||||
 | 
					    "attendee_id" UUID NOT NULL REFERENCES attendee ("id"),
 | 
				
			||||||
 | 
					    "user_id" UUID NOT NULL REFERENCES "public"."user" ("id"),
 | 
				
			||||||
 | 
					    "last_dose_at" DATE,
 | 
				
			||||||
 | 
					    "checked_at" TIMESTAMP WITHOUT TIME ZONE NOT NULL DEFAULT now()
 | 
				
			||||||
 | 
					);
 | 
				
			||||||
							
								
								
									
										4
									
								
								users/grfn/bbbg/src/bbbg/attendee_check.clj
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										4
									
								
								users/grfn/bbbg/src/bbbg/attendee_check.clj
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
					@ -0,0 +1,4 @@
 | 
				
			||||||
 | 
					(ns bbbg.attendee-check
 | 
				
			||||||
 | 
					  (:require [clojure.spec.alpha :as s]))
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					(s/def ::id uuid?)
 | 
				
			||||||
| 
						 | 
					@ -355,8 +355,6 @@
 | 
				
			||||||
 | 
					
 | 
				
			||||||
(comment
 | 
					(comment
 | 
				
			||||||
  (def db (:db bbbg.core/system))
 | 
					  (def db (:db bbbg.core/system))
 | 
				
			||||||
  (generate-migration db "init-schema")
 | 
					  (generate-migration db "add-attendee-checks")
 | 
				
			||||||
  (migrate! db)
 | 
					  (migrate! db)
 | 
				
			||||||
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  )
 | 
					  )
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -55,5 +55,6 @@
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  (db/list db (with-stats))
 | 
					  (db/list db (with-stats))
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  (map? db)
 | 
					  (db/insert! db :attendee {::attendee/meetup-name "Rando Guy"
 | 
				
			||||||
 | 
					                            ::attendee/discord-name "rando"})
 | 
				
			||||||
  )
 | 
					  )
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
							
								
								
									
										46
									
								
								users/grfn/bbbg/src/bbbg/db/attendee_check.clj
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										46
									
								
								users/grfn/bbbg/src/bbbg/db/attendee_check.clj
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
					@ -0,0 +1,46 @@
 | 
				
			||||||
 | 
					(ns bbbg.db.attendee-check
 | 
				
			||||||
 | 
					  (:require
 | 
				
			||||||
 | 
					   [bbbg.attendee :as attendee]
 | 
				
			||||||
 | 
					   [bbbg.attendee-check :as attendee-check]
 | 
				
			||||||
 | 
					   [bbbg.db :as db]
 | 
				
			||||||
 | 
					   [bbbg.user :as user]
 | 
				
			||||||
 | 
					   [bbbg.util.core :as u]))
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					(defn attendees-with-last-checks
 | 
				
			||||||
 | 
					  [db attendees]
 | 
				
			||||||
 | 
					  (let [ids (map ::attendee/id attendees)
 | 
				
			||||||
 | 
					        checks
 | 
				
			||||||
 | 
					        (db/list db {:select [:attendee-check.*]
 | 
				
			||||||
 | 
					                     :from [:attendee-check]
 | 
				
			||||||
 | 
					                     :join [[{:select [:%max.attendee-check.checked-at
 | 
				
			||||||
 | 
					                                       :attendee-check.attendee-id]
 | 
				
			||||||
 | 
					                              :from [:attendee-check]
 | 
				
			||||||
 | 
					                              :group-by [:attendee-check.attendee-id]
 | 
				
			||||||
 | 
					                              :where [:in :attendee-check.attendee-id ids]}
 | 
				
			||||||
 | 
					                             :last-check]
 | 
				
			||||||
 | 
					                            [:=
 | 
				
			||||||
 | 
					                             :attendee-check.attendee-id
 | 
				
			||||||
 | 
					                             :last-check.attendee-id]]})
 | 
				
			||||||
 | 
					        users (u/key-by
 | 
				
			||||||
 | 
					               ::user/id
 | 
				
			||||||
 | 
					               (db/list db {:select [:public.user.*]
 | 
				
			||||||
 | 
					                            :from [:public.user]
 | 
				
			||||||
 | 
					                            :where [:in :id (map ::user/id checks)]}))
 | 
				
			||||||
 | 
					        checks (map #(assoc % :user (users (::user/id %))) checks)
 | 
				
			||||||
 | 
					        attendee-id->check (u/key-by ::attendee/id checks)]
 | 
				
			||||||
 | 
					    (map #(assoc % :last-check (attendee-id->check (::attendee/id %)))
 | 
				
			||||||
 | 
					         attendees)))
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					(comment
 | 
				
			||||||
 | 
					  (def db (:db bbbg.core/system))
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  (attendees-with-last-checks
 | 
				
			||||||
 | 
					   db
 | 
				
			||||||
 | 
					   (db/list db :attendee)
 | 
				
			||||||
 | 
					   )
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  (db/insert! db :attendee-check
 | 
				
			||||||
 | 
					              {::attendee/id #uuid "58bcd372-ff6e-49df-b280-23d24c5ba0f0"
 | 
				
			||||||
 | 
					               ::user/id #uuid "303fb606-5ef0-4682-ad7d-6429c670cd78"
 | 
				
			||||||
 | 
					               ::attendee-check/last-dose-at "2021-12-19"})
 | 
				
			||||||
 | 
					  )
 | 
				
			||||||
							
								
								
									
										55
									
								
								users/grfn/bbbg/src/bbbg/handlers/attendee_checks.clj
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										55
									
								
								users/grfn/bbbg/src/bbbg/handlers/attendee_checks.clj
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
					@ -0,0 +1,55 @@
 | 
				
			||||||
 | 
					(ns bbbg.handlers.attendee-checks
 | 
				
			||||||
 | 
					  (:require
 | 
				
			||||||
 | 
					   [bbbg.attendee :as attendee]
 | 
				
			||||||
 | 
					   [bbbg.db :as db]
 | 
				
			||||||
 | 
					   [bbbg.handlers.core :refer [page-response wrap-auth-required]]
 | 
				
			||||||
 | 
					   [bbbg.util.display :refer [format-date]]
 | 
				
			||||||
 | 
					   [compojure.coercions :refer [as-uuid]]
 | 
				
			||||||
 | 
					   [compojure.core :refer [context GET POST]]
 | 
				
			||||||
 | 
					   [ring.util.response :refer [not-found]]
 | 
				
			||||||
 | 
					   [bbbg.attendee-check :as attendee-check]
 | 
				
			||||||
 | 
					   [bbbg.user :as user]))
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					(defn- edit-attendee-checks-page [{:keys [existing-check]
 | 
				
			||||||
 | 
					                                   attendee-id ::attendee/id}]
 | 
				
			||||||
 | 
					  [:div
 | 
				
			||||||
 | 
					   (when existing-check
 | 
				
			||||||
 | 
					     [:p
 | 
				
			||||||
 | 
					      "Already checked on "
 | 
				
			||||||
 | 
					      (-> existing-check ::attendee-check/checked-at format-date)
 | 
				
			||||||
 | 
					      " by "
 | 
				
			||||||
 | 
					      (::user/username existing-check)])
 | 
				
			||||||
 | 
					   [:form {:method :post
 | 
				
			||||||
 | 
					           :action (str "/attendees/" attendee-id "/checks")}
 | 
				
			||||||
 | 
					    [:div.form-group
 | 
				
			||||||
 | 
					     [:label
 | 
				
			||||||
 | 
					      "Last Dose"
 | 
				
			||||||
 | 
					      [:input {:type :date
 | 
				
			||||||
 | 
					               :name :last-dose-at}]]]
 | 
				
			||||||
 | 
					    [:div.form-group
 | 
				
			||||||
 | 
					     [:input {:type :submit
 | 
				
			||||||
 | 
					              :value "Mark Checked"}]]]])
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					(defn attendee-checks-routes [{:keys [db]}]
 | 
				
			||||||
 | 
					  (wrap-auth-required
 | 
				
			||||||
 | 
					   (context "/attendees/:attendee-id/checks" [attendee-id :<< as-uuid]
 | 
				
			||||||
 | 
					     (GET "/edit" []
 | 
				
			||||||
 | 
					       (if (db/exists? db {:select [1]
 | 
				
			||||||
 | 
					                           :from [:attendee]
 | 
				
			||||||
 | 
					                           :where [:= :id attendee-id]})
 | 
				
			||||||
 | 
					         (let [existing-check (db/fetch
 | 
				
			||||||
 | 
					                               db
 | 
				
			||||||
 | 
					                               {:select [:attendee-check.*
 | 
				
			||||||
 | 
					                                         :public.user.*]
 | 
				
			||||||
 | 
					                                :from [:attendee-check]
 | 
				
			||||||
 | 
					                                :join [:public.user
 | 
				
			||||||
 | 
					                                       [:=
 | 
				
			||||||
 | 
					                                        :attendee-check.user-id
 | 
				
			||||||
 | 
					                                        :public.user.id]]
 | 
				
			||||||
 | 
					                                :where [:= :attendee-id attendee-id]})]
 | 
				
			||||||
 | 
					           (page-response
 | 
				
			||||||
 | 
					            (edit-attendee-checks-page
 | 
				
			||||||
 | 
					             {:existing-check existing-check
 | 
				
			||||||
 | 
					              ::attendee/id attendee-id})))
 | 
				
			||||||
 | 
					         (not-found "Attendee not found")))
 | 
				
			||||||
 | 
					     (POST "/" []))))
 | 
				
			||||||
| 
						 | 
					@ -1,18 +1,23 @@
 | 
				
			||||||
(ns bbbg.handlers.attendees
 | 
					(ns bbbg.handlers.attendees
 | 
				
			||||||
  (:require
 | 
					  (:require
 | 
				
			||||||
   [bbbg.attendee :as attendee]
 | 
					   [bbbg.attendee :as attendee]
 | 
				
			||||||
 | 
					   [bbbg.attendee-check :as attendee-check]
 | 
				
			||||||
   [bbbg.db :as db]
 | 
					   [bbbg.db :as db]
 | 
				
			||||||
   [bbbg.db.attendee :as db.attendee]
 | 
					   [bbbg.db.attendee :as db.attendee]
 | 
				
			||||||
 | 
					   [bbbg.db.attendee-check :as db.attendee-check]
 | 
				
			||||||
   [bbbg.db.event :as db.event]
 | 
					   [bbbg.db.event :as db.event]
 | 
				
			||||||
   [bbbg.event :as event]
 | 
					   [bbbg.event :as event]
 | 
				
			||||||
   [bbbg.handlers.core :refer [page-response wrap-auth-required]]
 | 
					   [bbbg.handlers.core :refer [page-response wrap-auth-required]]
 | 
				
			||||||
 | 
					   [bbbg.user :as user]
 | 
				
			||||||
 | 
					   [bbbg.util.display :refer [format-date]]
 | 
				
			||||||
   [bbbg.views.flash :as flash]
 | 
					   [bbbg.views.flash :as flash]
 | 
				
			||||||
   [cheshire.core :as json]
 | 
					   [cheshire.core :as json]
 | 
				
			||||||
   [compojure.coercions :refer [as-uuid]]
 | 
					   [compojure.coercions :refer [as-uuid]]
 | 
				
			||||||
   [compojure.core :refer [GET POST routes]]
 | 
					   [compojure.core :refer [GET POST routes]]
 | 
				
			||||||
   [honeysql.helpers :refer [merge-where]]
 | 
					   [honeysql.helpers :refer [merge-where]]
 | 
				
			||||||
   [ring.util.response :refer [content-type redirect response not-found]])
 | 
					   [ring.util.response :refer [content-type not-found redirect response]])
 | 
				
			||||||
  (:import java.util.UUID))
 | 
					  (:import
 | 
				
			||||||
 | 
					   java.util.UUID))
 | 
				
			||||||
 | 
					
 | 
				
			||||||
(defn- attendees-page [{:keys [attendees q edit-notes]}]
 | 
					(defn- attendees-page [{:keys [attendees q edit-notes]}]
 | 
				
			||||||
  [:div
 | 
					  [:div
 | 
				
			||||||
| 
						 | 
					@ -30,6 +35,7 @@
 | 
				
			||||||
      [:th "Events RSVPd"]
 | 
					      [:th "Events RSVPd"]
 | 
				
			||||||
      [:th "Events Attended"]
 | 
					      [:th "Events Attended"]
 | 
				
			||||||
      [:th "No-Shows"]
 | 
					      [:th "No-Shows"]
 | 
				
			||||||
 | 
					      [:th "Last Vaccination Check"]
 | 
				
			||||||
      [:th "Notes"]]]
 | 
					      [:th "Notes"]]]
 | 
				
			||||||
    [:tbody
 | 
					    [:tbody
 | 
				
			||||||
     (for [attendee attendees
 | 
					     (for [attendee attendees
 | 
				
			||||||
| 
						 | 
					@ -40,6 +46,15 @@
 | 
				
			||||||
        [:td (:events-rsvpd attendee)]
 | 
					        [:td (:events-rsvpd attendee)]
 | 
				
			||||||
        [:td (:events-attended attendee)]
 | 
					        [:td (:events-attended attendee)]
 | 
				
			||||||
        [:td (:no-shows attendee)]
 | 
					        [:td (:no-shows attendee)]
 | 
				
			||||||
 | 
					        (if-let [last-check (:last-check attendee)]
 | 
				
			||||||
 | 
					          [:td (str (-> last-check
 | 
				
			||||||
 | 
					                        ::attendee-check/checked-at
 | 
				
			||||||
 | 
					                        format-date)
 | 
				
			||||||
 | 
					                    ", by "
 | 
				
			||||||
 | 
					                    (get-in last-check [:user ::user/username]))]
 | 
				
			||||||
 | 
					          [:td "Not Checked"
 | 
				
			||||||
 | 
					           [:a {:href (str "/attendees/" id "/checks/edit")}
 | 
				
			||||||
 | 
					            "Edit"]])
 | 
				
			||||||
        (if (= edit-notes id)
 | 
					        (if (= edit-notes id)
 | 
				
			||||||
          [:td
 | 
					          [:td
 | 
				
			||||||
           [:form.organizer-notes {:method :post
 | 
					           [:form.organizer-notes {:method :post
 | 
				
			||||||
| 
						 | 
					@ -59,6 +74,9 @@
 | 
				
			||||||
     (GET "/attendees" [q edit-notes]
 | 
					     (GET "/attendees" [q edit-notes]
 | 
				
			||||||
       (let [attendees (db/list db (cond-> (db.attendee/with-stats)
 | 
					       (let [attendees (db/list db (cond-> (db.attendee/with-stats)
 | 
				
			||||||
                                     q (db.attendee/search q)))
 | 
					                                     q (db.attendee/search q)))
 | 
				
			||||||
 | 
					             attendees (db.attendee-check/attendees-with-last-checks
 | 
				
			||||||
 | 
					                        db
 | 
				
			||||||
 | 
					                        attendees)
 | 
				
			||||||
             edit-notes (some-> edit-notes UUID/fromString)]
 | 
					             edit-notes (some-> edit-notes UUID/fromString)]
 | 
				
			||||||
         (page-response (attendees-page {:attendees attendees
 | 
					         (page-response (attendees-page {:attendees attendees
 | 
				
			||||||
                                         :q q
 | 
					                                         :q q
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
							
								
								
									
										17
									
								
								users/grfn/bbbg/src/bbbg/util/display.clj
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										17
									
								
								users/grfn/bbbg/src/bbbg/util/display.clj
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
					@ -0,0 +1,17 @@
 | 
				
			||||||
 | 
					(ns bbbg.util.display
 | 
				
			||||||
 | 
					  (:require
 | 
				
			||||||
 | 
					   [bbbg.util.time :as t])
 | 
				
			||||||
 | 
					  (:import
 | 
				
			||||||
 | 
					   [java.time.format DateTimeFormatter FormatStyle]))
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					(defn format-date
 | 
				
			||||||
 | 
					  ([d] (format-date d FormatStyle/MEDIUM))
 | 
				
			||||||
 | 
					  ([d ^FormatStyle format-style]
 | 
				
			||||||
 | 
					   (let [formatter (DateTimeFormatter/ofLocalizedDate format-style)]
 | 
				
			||||||
 | 
					     (.format (t/->LocalDate d) formatter))))
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					(comment
 | 
				
			||||||
 | 
					  (format-date #inst "2021-12-19T05:00:00.000-00:00")
 | 
				
			||||||
 | 
					  (format-date #inst "2021-12-19T05:00:00.000-00:00"
 | 
				
			||||||
 | 
					               FormatStyle/SHORT)
 | 
				
			||||||
 | 
					  )
 | 
				
			||||||
							
								
								
									
										149
									
								
								users/grfn/bbbg/src/bbbg/util/time.clj
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										149
									
								
								users/grfn/bbbg/src/bbbg/util/time.clj
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
					@ -0,0 +1,149 @@
 | 
				
			||||||
 | 
					(ns bbbg.util.time
 | 
				
			||||||
 | 
					  "Utilities for dealing with date/time"
 | 
				
			||||||
 | 
					  (:require [clojure.spec.alpha :as s]
 | 
				
			||||||
 | 
					            [clojure.test.check.generators :as gen]
 | 
				
			||||||
 | 
					            [java-time :as jt])
 | 
				
			||||||
 | 
					  (:import [java.time
 | 
				
			||||||
 | 
					            LocalDateTime LocalTime OffsetDateTime ZoneId ZoneOffset
 | 
				
			||||||
 | 
					            LocalDate Year]
 | 
				
			||||||
 | 
					           [java.time.format DateTimeFormatter DateTimeParseException]
 | 
				
			||||||
 | 
					           java.util.Calendar
 | 
				
			||||||
 | 
					           org.apache.commons.lang3.time.DurationFormatUtils))
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					(set! *warn-on-reflection* true)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					(defprotocol ToOffsetDateTime
 | 
				
			||||||
 | 
					  (->OffsetDateTime [this]
 | 
				
			||||||
 | 
					    "Coerces its argument to a `java.time.OffsetDateTime`"))
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					(extend-protocol ToOffsetDateTime
 | 
				
			||||||
 | 
					  OffsetDateTime
 | 
				
			||||||
 | 
					  (->OffsetDateTime [odt] odt)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  java.util.Date
 | 
				
			||||||
 | 
					  (->OffsetDateTime [d]
 | 
				
			||||||
 | 
					    (-> d
 | 
				
			||||||
 | 
					        .toInstant
 | 
				
			||||||
 | 
					        (OffsetDateTime/ofInstant (ZoneId/of "UTC")))))
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					(defprotocol ToLocalTime (->LocalTime [this]))
 | 
				
			||||||
 | 
					(extend-protocol ToLocalTime
 | 
				
			||||||
 | 
					  LocalTime
 | 
				
			||||||
 | 
					  (->LocalTime [lt] lt)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  java.sql.Time
 | 
				
			||||||
 | 
					  (->LocalTime [t]
 | 
				
			||||||
 | 
					    (let [^Calendar cal (doto (Calendar/getInstance)
 | 
				
			||||||
 | 
					                          (.setTime t))]
 | 
				
			||||||
 | 
					      (LocalTime/of
 | 
				
			||||||
 | 
					       (.get cal Calendar/HOUR_OF_DAY)
 | 
				
			||||||
 | 
					       (.get cal Calendar/MINUTE)
 | 
				
			||||||
 | 
					       (.get cal Calendar/SECOND))))
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  java.util.Date
 | 
				
			||||||
 | 
					  (->LocalTime [d]
 | 
				
			||||||
 | 
					    (-> d .toInstant (LocalTime/ofInstant (ZoneId/of "UTC")))))
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					(defn local-time? [x] (satisfies? ToLocalTime x))
 | 
				
			||||||
 | 
					(s/def ::local-time
 | 
				
			||||||
 | 
					  (s/with-gen local-time?
 | 
				
			||||||
 | 
					    #(gen/let [hour (gen/choose 0 23)
 | 
				
			||||||
 | 
					               minute (gen/choose 0 59)
 | 
				
			||||||
 | 
					               second (gen/choose 0 59)
 | 
				
			||||||
 | 
					               nanos gen/nat]
 | 
				
			||||||
 | 
					       (LocalTime/of hour minute second nanos))))
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					(defprotocol ToLocalDate (->LocalDate [this]))
 | 
				
			||||||
 | 
					(extend-protocol ToLocalDate
 | 
				
			||||||
 | 
					  LocalDate
 | 
				
			||||||
 | 
					  (->LocalDate [ld] ld)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  java.util.Date
 | 
				
			||||||
 | 
					  (->LocalDate [d]
 | 
				
			||||||
 | 
					    (-> d .toInstant (LocalDate/ofInstant (ZoneId/of "UTC")))))
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					(defn local-date? [x] (satisfies? ToLocalDate x))
 | 
				
			||||||
 | 
					(s/def ::local-date
 | 
				
			||||||
 | 
					  (s/with-gen local-date?
 | 
				
			||||||
 | 
					    #(gen/let [year (gen/choose Year/MIN_VALUE Year/MAX_VALUE)
 | 
				
			||||||
 | 
					               day (gen/choose 1 (if (.isLeap (Year/of year))
 | 
				
			||||||
 | 
					                                   366
 | 
				
			||||||
 | 
					                                   365))]
 | 
				
			||||||
 | 
					       (LocalDate/ofYearDay year day))))
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					(extend-protocol Inst
 | 
				
			||||||
 | 
					  OffsetDateTime
 | 
				
			||||||
 | 
					  (inst-ms* [zdt]
 | 
				
			||||||
 | 
					    (inst-ms* (.toInstant zdt)))
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  LocalDateTime
 | 
				
			||||||
 | 
					  (inst-ms* [^LocalDateTime ldt]
 | 
				
			||||||
 | 
					    (inst-ms* (.toInstant ldt ZoneOffset/UTC))))
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					(let [formatter DateTimeFormatter/ISO_OFFSET_DATE_TIME]
 | 
				
			||||||
 | 
					  (defn ^OffsetDateTime parse-iso-8601
 | 
				
			||||||
 | 
					    "Parse s as an iso-8601 datetime, returning nil if invalid"
 | 
				
			||||||
 | 
					    [^String s]
 | 
				
			||||||
 | 
					    (try
 | 
				
			||||||
 | 
					      (OffsetDateTime/parse s formatter)
 | 
				
			||||||
 | 
					      (catch DateTimeParseException _ nil)))
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  (defn format-iso-8601
 | 
				
			||||||
 | 
					    "Format dt, which can be an OffsetDateTime or java.util.Date, as iso-8601"
 | 
				
			||||||
 | 
					    [dt]
 | 
				
			||||||
 | 
					    (some->> dt ->OffsetDateTime (.format formatter))))
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					(let [formatter DateTimeFormatter/ISO_TIME]
 | 
				
			||||||
 | 
					  (defn parse-iso-8601-time
 | 
				
			||||||
 | 
					    "Parse s as an iso-8601 timestamp, returning nil if invalid"
 | 
				
			||||||
 | 
					    [^String s]
 | 
				
			||||||
 | 
					    (try
 | 
				
			||||||
 | 
					      (LocalTime/parse s formatter)
 | 
				
			||||||
 | 
					      (catch DateTimeParseException _ nil)))
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  (defn format-iso-8601-time
 | 
				
			||||||
 | 
					    "Format lt, which can be a LocalTime or java.sql.Time, as an iso-8601
 | 
				
			||||||
 | 
					    formatted timestamp without a date."
 | 
				
			||||||
 | 
					    [lt]
 | 
				
			||||||
 | 
					    (some->> lt ->LocalTime (.format formatter))))
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					(defmethod print-dup LocalTime [t w]
 | 
				
			||||||
 | 
					  (binding [*out* w]
 | 
				
			||||||
 | 
					    (print "#local-time ")
 | 
				
			||||||
 | 
					    (print (str "\"" (format-iso-8601-time t) "\""))))
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					(defmethod print-method LocalTime [t w]
 | 
				
			||||||
 | 
					  (print-dup t w))
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					(let [formatter DateTimeFormatter/ISO_LOCAL_DATE]
 | 
				
			||||||
 | 
					  (defn parse-iso-8601-date
 | 
				
			||||||
 | 
					    "Parse s as an iso-8601 date, returning nil if invalid"
 | 
				
			||||||
 | 
					    [^String s]
 | 
				
			||||||
 | 
					    (try
 | 
				
			||||||
 | 
					      (LocalDate/parse s formatter)
 | 
				
			||||||
 | 
					      (catch DateTimeParseException _ nil)))
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  (defn format-iso-8601-date
 | 
				
			||||||
 | 
					    "Format lt, which can be a LocalDate, as an iso-8601 formatted date without
 | 
				
			||||||
 | 
					    a timestamp."
 | 
				
			||||||
 | 
					    [lt]
 | 
				
			||||||
 | 
					    (some->> lt ->LocalDate (.format formatter))))
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					(defmethod print-dup LocalDate [t w]
 | 
				
			||||||
 | 
					  (binding [*out* w]
 | 
				
			||||||
 | 
					    (print "#local-date ")
 | 
				
			||||||
 | 
					    (print (str "\"" (format-iso-8601-date t) "\""))))
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					(defmethod print-method LocalDate [t w]
 | 
				
			||||||
 | 
					  (print-dup t w))
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					(defn ^String human-format-duration
 | 
				
			||||||
 | 
					  "Human-format the given duration"
 | 
				
			||||||
 | 
					  [^java.time.Duration dur]
 | 
				
			||||||
 | 
					  (DurationFormatUtils/formatDurationWords (Math/abs (.toMillis dur)) true true))
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					(comment
 | 
				
			||||||
 | 
					  (human-format-duration (jt/hours 5))
 | 
				
			||||||
 | 
					  (human-format-duration (jt/plus (jt/hours 5) (jt/minutes 7)))
 | 
				
			||||||
 | 
					  )
 | 
				
			||||||
| 
						 | 
					@ -1,6 +1,7 @@
 | 
				
			||||||
(ns bbbg.web
 | 
					(ns bbbg.web
 | 
				
			||||||
  (:require
 | 
					  (:require
 | 
				
			||||||
   [bbbg.discord.auth :as discord.auth :refer [wrap-discord-auth]]
 | 
					   [bbbg.discord.auth :as discord.auth :refer [wrap-discord-auth]]
 | 
				
			||||||
 | 
					   [bbbg.handlers.attendee-checks :as attendee-checks]
 | 
				
			||||||
   [bbbg.handlers.attendees :as attendees]
 | 
					   [bbbg.handlers.attendees :as attendees]
 | 
				
			||||||
   [bbbg.handlers.events :as events]
 | 
					   [bbbg.handlers.events :as events]
 | 
				
			||||||
   [bbbg.handlers.home :as home]
 | 
					   [bbbg.handlers.home :as home]
 | 
				
			||||||
| 
						 | 
					@ -74,6 +75,7 @@
 | 
				
			||||||
         (content-type "text/javascript")))
 | 
					         (content-type "text/javascript")))
 | 
				
			||||||
 | 
					
 | 
				
			||||||
   (attendees/attendees-routes env)
 | 
					   (attendees/attendees-routes env)
 | 
				
			||||||
 | 
					   (attendee-checks/attendee-checks-routes env)
 | 
				
			||||||
   (signup-form/signup-form-routes env)
 | 
					   (signup-form/signup-form-routes env)
 | 
				
			||||||
   (events/events-routes env)
 | 
					   (events/events-routes env)
 | 
				
			||||||
   (home/home-routes env)))
 | 
					   (home/home-routes env)))
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue