jennex

Our Wedding Site
git clone git://git.alexkarle.com.com/jennex
Log | Files | Refs | README | LICENSE

commit 91e8cae0fc4cc52057210662f1ba7c0b8ec17413 (patch)
parent 11f6e0210495c6cd99125b53b501990d7ec76a09
Author: Alex Karle <alex@alexkarle.com>
Date:   Sun, 13 Nov 2022 15:38:16 -0500

rsvp: Add initial scaffolding for persistent rsvp storage

I should have broken this up into smaller changes, but I'm still
trying to learn emacs and REPL development and am out of my comfort
zone a bit.

This patch implements:

* The beginnings of a "fetch RSVP's from DB"
* The forms to dispaly the current RSVP state

The next step will be to persist these to the db on submit.

Diffstat:
Asrc/.gitignore | 2++
Asrc/db.scm | 74++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Msrc/handler.scm | 67+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++------
Asrc/setup.sql | 27+++++++++++++++++++++++++++
4 files changed, 164 insertions(+), 6 deletions(-)

diff --git a/src/.gitignore b/src/.gitignore @@ -0,0 +1 @@ +rsvp.sqlite3 +\ No newline at end of file diff --git a/src/db.scm b/src/db.scm @@ -0,0 +1,74 @@ +(import sqlite3 + sql-null + (chicken condition) + (chicken format)) + +(define conn (open-database "rsvp.sqlite3")) + +(define-record guest + id name party-id going meal-choice + has-plus1 plus1-going plus1-meal-choice) + +(define (get-guest-by-name name) + (apply make-guest + (first-row conn +"SELECT + id, + name, + party_id, + going, + meal_choice, + has_plus1, + plus1_going, + plus1_meal_choice +FROM guests WHERE name = $name" name))) + +(define (get-guests-in-party pid) + (map-row make-guest conn +"SELECT + id, + name, + party_id, + going, + meal_choice, + has_plus1, + plus1_going, + plus1_meal_choice +FROM guests WHERE party_id = $pid" pid)) + +;(map guest-name (get-party-by-name "Alex")) +;(map guest-name (get-party-by-name "Sarah")) +;(map guest-name (get-party-by-name "Foo")) +(define (get-party-by-name name) + ;; get-guest-by-name can throw if none found + (call/cc + (lambda (c) + (with-exception-handler + (lambda (x) (c '())) + (lambda () + (let* ((guest (get-guest-by-name name)) + (party-id (guest-party-id guest))) + (if (not (sql-null? party-id)) + (get-guests-in-party party-id) + (list guest)))))))) + +(define (update-guest g) + (call/cc + (lambda (c) + (with-exception-handler + (lambda (x) (print x) (c #f)) + (lambda () + (update conn " +UPDATE guests SET + name = $1, + going = $2, + meal_choice = $3, + plus1_going = $4, + plus1_meal_choice = $5 +WHERE id = $6" + (guest-name g) + (guest-going g) + (guest-meal-choice g) + (guest-plus1-going g) + (guest-plus1-meal-choice g) + (guest-id g))))))) diff --git a/src/handler.scm b/src/handler.scm @@ -3,7 +3,11 @@ spiffy uri-common intarweb - sxml-serializer) + sxml-serializer + sql-null + (chicken string)) + +(load "db.scm") (define routes ;; See uri-match for format: http://wiki.call-cc.org/eggref/5/uri-match#routes-format @@ -51,6 +55,47 @@ (button "Lookup")) (p "Please let us know by DATE whether you can make it!"))))) +(define (guest-to-form g) + ;; Given a guest record, generate a SXML form for their settings + ;; TODO: expand plus-1's + (define (get-going-attrs val) + `((name ,(conc (number->string (guest-id g)) "__going")) + (value ,val) + (type "radio") + (required "true") + ,@(let ((going (guest-going g))) + (cond ((equal? val "no") (if (eq? going 0) '((checked)) '())) + ((equal? val "yes") (if (eq? going 1) '((checked)) '())) + ((equal? val "null") (if (sql-null? going) '((checked)) '())) + (else (error "Bad val")))))) + + (define (get-meal-attrs val) + `((name ,(conc (number->string (guest-id g)) "__meal_choice")) + (value ,val) + (type "radio") + (required "true") + ,@(if (equal? (guest-meal-choice g) val) + '((checked)) + '()))) + + ;; TODO: add notes section for allergies, etc + `((div (@ (class "guest")) + (h4 ,(guest-name g)) + (fieldset + (legend "Will You be Attending?") + (label (input (@ ,@(get-going-attrs "yes"))) "Yes!") + (br) + (label (input (@ ,@(get-going-attrs "no"))) "No :(") + (br) + (label (input (@ ,@(get-going-attrs "null"))) "Not Sure Yet...")) + (fieldset + (legend "Meal Choice") + (label (input (@ ,@(get-meal-attrs "chicken"))) "Chicken") + (br) + (label (input (@ ,@(get-meal-attrs "beef"))) "Beef") + (br) + (label (input (@ ,@(get-meal-attrs "vegetarian"))) "Chicken"))))) + (define (route-get-rsvp c) ;; TODO: consider a POST instead of GET to prevent people from sharing ;; their edit links? @@ -58,16 +103,26 @@ (name (alist-ref 'rsvp-name q))) (send-sxml (template-page - ;; TODO: look up name in database! - (if name + (let ((guests (get-party-by-name name))) + (if (not (null? guests)) `((h2 "RSVP") (p "Great news! You're invited :) We can't wait to celebrate with you!") (p "We've found the following guests under your name. For each, please " - "select whether you'll make it and your choice of meal.")) + "select whether you'll make it and your choice of meal.") + (form (@ (action "/rsvp") (method "POST")) + ,@(map guest-to-form guests) + (button "Save"))) `((h2 "RSVP") (p "Sorry! We can't find anyone under the name '" ,name "'. Please double check the spelling and if it looks like a " "mistake on our end email us at " - (a (@ (href "mailto:rsvp@jennex.org")) "rsvp@jennex.org") "."))))))) + (a (@ (href "mailto:rsvp@jennex.org")) "rsvp@jennex.org") ".")))))))) -(define (route-post-rsvp c) (send-response status: 'ok body: "hello!")) +(define (route-post-rsvp c) + (let ((fdata (read-urlencoded-request-data (current-request)))) + (send-sxml + (template-page + `((h2 "RSVP") + (p "Success! Thanks for RSVP-ing.") + (p (a (@ (href "/rsvp")) "Edit your response")) + (p (a (@ (href "https://jennex.org")) "Read more about the event"))))))) diff --git a/src/setup.sql b/src/setup.sql @@ -0,0 +1,27 @@ +-- Parties just group a series of guests so they can RSVP for eachother +CREATE TABLE parties ( + id INTEGER PRIMARY KEY AUTOINCREMENT +); + +-- Guests store individual preferences for meals, etc +CREATE TABLE guests ( + id INTEGER PRIMARY KEY AUTOINCREMENT, + name VARCHAR(255) NOT NULL, + party_id INTEGER, + going INTEGER, + meal_choice VARCHAR(255), + has_plus1 INTEGER NOT NULL, + plus1_going INTEGER, + plus1_meal_choice VARCHAR(255), + + FOREIGN KEY(party_id) REFERENCES parties(id) +); + +INSERT INTO parties (id) VALUES (1), (2); + +INSERT INTO guests (name, party_id, has_plus1) VALUES + ('Alex', 1, False), + ('Jennie', 1, False), + ('Matt', 2, True), + ('Sarah', NULL, True), + ('Sammy', 2, True);