Init.
This commit is contained in:
commit
4c5d5bdea2
|
@ -0,0 +1,6 @@
|
||||||
|
#*
|
||||||
|
*#
|
||||||
|
.#*
|
||||||
|
*~
|
||||||
|
venv/
|
||||||
|
node_modules/
|
|
@ -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()
|
|
@ -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
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -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
|
||||||
|
);
|
||||||
|
|
Loading…
Reference in New Issue