diff --git a/france/france_rsvp/database.py b/france/france_rsvp/database.py new file mode 100644 index 0000000..d0a1feb --- /dev/null +++ b/france/france_rsvp/database.py @@ -0,0 +1,99 @@ +import contextlib +from datetime import datetime +import logging +import os +import sqlite3 + + +logger = logging.getLogger(__name__) + + +DATABASE_FILE = os.environ['DATABASE'] + +assert os.path.isfile(DATABASE_FILE) + + +@contextlib.contextmanager +def get_db(): + db = sqlite3.connect(DATABASE_FILE) + try: + yield db + finally: + db.close() + + +def get_people_and_replies(code): + with get_db() as db: + return list(db.execute( + '''\ + SELECT + persons.id, persons.name, + ( + SELECT rsvp FROM replies + WHERE + replies.person_id = persons.id + AND replies.date = ( + SELECT MAX(date) + FROM replies + WHERE person_id = persons.id + ) + ) AS rsvp + FROM + persons + WHERE + persons.code = :code; + ''', + dict(code=code), + )) + + +def record_reply(code, persons): + assert isinstance(persons, dict) + with get_db() as db: + # Check the IDs of people match + expected_people_ids = set( + row[0] + for row in db.execute( + '''\ + SELECT + id + FROM + persons + WHERE + code = :code; + ''', + dict(code=code), + ) + ) + if not expected_people_ids: + logger.warning("Got reply with invalid code %r", code) + raise ValueError("Invalid code") + for person_id in persons.keys(): + if person_id not in expected_people_ids: + logger.warning( + "Got invalid person id=%d for code %r", + person_id, + code, + ) + raise ValueError("Invalid person") + + # Insert update + cursor = db.cursor() + date = datetime.utcnow() + for person_id, rsvp in persons.items(): + if rsvp not in ('yes', 'no'): + raise ValueError("Invalid rsvp %r", rsvp) + cursor.execute( + '''\ + INSERT INTO replies(person_id, date, rsvp) + VALUES(:id, :date, :rsvp); + ''', + dict(id=person_id, date=date, rsvp=rsvp), + ) + cursor.execute('COMMIT;') + logger.info( + "Recorded update for code=%r date=%r persons: %s", + code, + date, + ' '.join('%d=%s' % p for p in sorted(persons.items())), + ) diff --git a/france/schema.sql b/france/schema.sql new file mode 100644 index 0000000..1b03203 --- /dev/null +++ b/france/schema.sql @@ -0,0 +1,11 @@ +CREATE TABLE persons( + id INTEGER PRIMARY KEY, + code VARCHAR(8) NOT NULL, + name TEXT NOT NULL +); + +CREATE TABLE replies( + person_id INTEGER NOT NULL, + date DATETIME NOT NULL, + rsvp VARCHAR(8) NOT NULL +); diff --git a/france/test-data.sql b/france/test-data.sql new file mode 100644 index 0000000..a84e84d --- /dev/null +++ b/france/test-data.sql @@ -0,0 +1,9 @@ +INSERT INTO persons(id, code, name) VALUES + (1, 'aaaa', 'one'), + (2, 'aaaa', 'two'), + (3, 'bbbb', 'three'); + +INSERT INTO replies(person_id, date, rsvp) VALUES + (1, '2022-05-10 20:37:39', 'yes'), + (2, '2022-05-10 20:37:39', 'yes'), + (1, '2022-05-10 23:55:55', 'no');