From 4c5d5bdea29a7eb658b8baba756b42b05c6580ab Mon Sep 17 00:00:00 2001 From: "Elf M. Sternberg" Date: Sun, 7 Feb 2016 11:15:39 -0800 Subject: [PATCH] Init. --- .gitignore | 6 ++ main.py | 11 +++ notes/bs.md | 183 ++++++++++++++++++++++++++++++++++++++++++++++++ officehours.sql | 41 +++++++++++ 4 files changed, 241 insertions(+) create mode 100644 .gitignore create mode 100644 main.py create mode 100644 notes/bs.md create mode 100644 officehours.sql diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..dd5c5d8 --- /dev/null +++ b/.gitignore @@ -0,0 +1,6 @@ +#* +*# +.#* +*~ +venv/ +node_modules/ diff --git a/main.py b/main.py new file mode 100644 index 0000000..f22dd59 --- /dev/null +++ b/main.py @@ -0,0 +1,11 @@ +# from http://flask.pocoo.org/ tutorial +from flask import Flask + +app = Flask(__name__) + +@app.route("/") # take note of this decorator syntax, it's a common pattern +def hello(): + return "Hello World!" + +if __name__ == "__main__": + app.run() diff --git a/notes/bs.md b/notes/bs.md new file mode 100644 index 0000000..5e5a5a6 --- /dev/null +++ b/notes/bs.md @@ -0,0 +1,183 @@ +Note: "BS" stands for BrainStorming. + +#Arivale Coding Problem + +## Problem description: + + When clients sign up for the Arivale service, they develop a 1:1 + relationship with a wellness coach over the course of a year. These + coaches help them interpret their personal health data as well as + make actionable recommendations to improve a client’s overall + wellness. Clients need to schedule coaching calls on a monthly + basis. We want to create a web based experience that makes it easy + for clients to schedule a call. Clients should be able to see their + coach’s availability and then book hour long coaching slot. Once a + slot is booked, other clients should not be able to book that slot + with the same coach. + +## Solution requirements: + + * Data Store + * Middle Tier + * Front-End + +## Actors + + * Staff + * Client + +## Tables + + * Table of *Users* (flags "staff," "client," "staff_active," "client_active") [User] + ** Is there redundant information in those flags? Table-level + constraints to prevent "client = false" "client_active = true" + on the same object, for example? + * Table of *User/user relationships* [Staff_client_relationship] + ** staff_user_id (constraint: staff flag must be TRUE, staff_active flag must be TRUE) + ** client_user_id (constraint: user flag must be TRUE, user_active flag must be TRUE) + + * Table of OfficeHours + ** When the staff are available: + *** staff_id + *** availability: [(timestamp, timestamp)] defining TSRange: Availability + + * Table of *Appointments* + ** staff_client_relationship_id: ID + ** reservation: (timestamp, timestamp) defining TSRANGE: Reservation + + * Business rules -> rule values + ** This could get ugly. What are the limits? One rule table per + rule datatype? Does Postgres have a union type, and how + horrible is it to work with? + +## Accesses + + * Client + ** Retrieve: View upcoming appointments + ** Create: Create a new appointment + ** Update: Move an existing appointment + ** Delete: Delete an existing appointment + ** UNANSWERED QUESTIONS + *** Recurring appointments? Set/no set? How do others handle this? + + REST Documents: + Appointment request: user, staff, time range + * payload schema check + User present and active? + Staff present and active? + Relationship present? (note: Never publish staff or relationship ID; user a slug for the customer if possible) + Time range sane? + * Positive length? + ** Assertion + * Not in the past? + ** Assertion (review: "What programmers believe about time") + ** No, really: Timezones, Daylight Savings, all that jazz + * Legitimate length + ** Business Rule + * Not too far in the future? + ** Business Rule + ** Can the business rules be stored in the database? + ** If so, you'll need an admin page for them. + * Assume attack: + ** Handle true fuzz. + ** Handle corruptions. + ** Handle crap. + ** WHITELIST RULES ONLY + Time range available? + ** OFFICE HOURS - for Coach only. + ** Database can handle (Postgres has a RANGE operator now) + ** Questions about performance, but don't care at this + point; just throw more metal at it. + * Can the INSERT detect user/staff/relationship validity? + * Can the INSERT deal with (some of) the time issues? + * What happens on INSERT failure? + ** How to report? + + + Update request: + * All of the above, plus: + ** Existing appointment? + ** Add-then-delete as a transaction, OR Update? Read pros/cons + + Retrieve: + * Is about permissions and ranges + ** What the user sees is what the database allows them to see + * No further back in the past than (Business Rule) weeks + * If as OPA (i.e. JSON), retrieve a couple weeks in advance, + with scaling lookahead; Like you did for Spiral's data + sources list; the scaling rate can be found in Python List + implementation, I think. Or fibonacci with a limiter. + * 300ms or twiddle. + ** The above is front-end stuff. + ** No Bad Ideas. :-) + + Delete: + * Appoinment exist? + * This user? + + Other: + Non-REST issue + * Progressive Enhancement. If it doesn't work in Lynx, it + doesn't work. + ** Some kind of second-page form for a time range? + * ARIA/Section 508 coverage? + +Unit tests: + Create: An appointment is a USER wanting a RANGE + Logged in? + Good/bad user? + Good/bad range? + Good/bad placement? + + Delete: + Logged in? + Yours? + + Update: + Same as create plus: + Yours? + + Retrieve: + Only the user ID and the start of the week matter. + Constraint: No past views. Not too far in the future + ** Business Rule again + +Secondary considerations: A staff member has a M:1 relationship with +clients, but clients have only a 1:1 with staff. Encoding that into the +Users table would make sense, only the staff's field would be Null. +While I want to enforce NO-NULL idiom as much as possible, the separate +table for that relationship [1] could be overkill; [2] permits the +development of a M:M relationship where a user could have more than one +coach, time and money permitting, but [2a] (probably) YAGNI. + +Backend: Postgres, naturally. Leveraged as far as humanely possible. +It's been a few years, but I'm genuinely impressed with what it does. + +Middle Tier: What's the lightest Python application server there is? +Flask? + +Front End: HAML/Less/A script I don't know (Like Clojure, Pure, or Type? +Something fun and different!) + +What we don't care about (thus far): + + Logging in. + Session management. + + +TODO VirtualEnv +TODO Flask +TODO PsychoPG2 +TODO Flask skeleton +TODO Postgres Database +TODO Postgres retrieval example +TODO Postgres range example +TODO Deliver home page +TODO Deliver retreivals + + + + + + + diff --git a/officehours.sql b/officehours.sql new file mode 100644 index 0000000..1a945e8 --- /dev/null +++ b/officehours.sql @@ -0,0 +1,41 @@ +-- Users known by the application. "Nickname" is a misnomer. Nom de +-- user was too pretentious. It's how the customer wishes to be +↓-- addressed, but "address" would be confusing. + +CREATE TABLE users ( + id SERIAL, + email CITEX UNIQUE NOT NULL, + nickname TEXT NOT NULL +); + +-- Users who are currently staff + +CREATE TABLE staff ( + staff_id INTEGER UNIQUE NOT NULL REFERENCES users(id) ON DELETE CASCADE, + active BOOLEAN +); + +-- Users who are currently clients + +CREATE TABLE clients ( + client_id INTEGER UNIQUE NOT NULL REFERENCES users(id) ON DELETE CASCADE, + active BOOLEAN +); + +-- This is interesting, because we've basically created a M:1 +-- relationship of staff and clients, but a 1:1 relationship of +-- clients to staff. That satisfies the current assignment, mostly. +-- An appointment can then be made my a client, and there's only one +-- staff person who it could apply to, so the query is straightforward +-- then. +-- +-- To extend this into a M:M relationship, you'd have to remove the +-- "UNIQUE" setting from the staff_id field and use the +-- relationship.id field instead for appointments. + +CREATE TABLE relationship ( + id SERIAL, + client_id INTEGER NOT NULL REFERENCES clients(client_id) ON DELETE CASCADE, + staff_id INTEGER UNIQUE NOT NULL REFERENCES staff(staff_id) ON DELETE CASCADE +); +