This is an example server for creating, scheduling and booking activities. It's a simple demo project using Rails4 with sqlite3 and an incomplete API.
To run
git clone ...
rake db:migrate
rails s
Now point your browser to localhost:3000 and confirm the test set exists. See API for further actions.
All API calls return 404 if activity/schedule ID is not found.
- POST /activites
- name: name of activity
- vendor: name of vendor
- Returns 201 on success:
- id: created id
example
curl -include --header "Content-type: application/json" --header "Accept: application/json" \
-X POST -d '{"name": "scuba", "vendor": "joe diver"}' http://localhost:3003/activities
=>
201, {"id":1}
- POST /activities/:activity_id/schedule
- date: ISO8601 date
- time: time of event as HH:MM
- spots: number of available spots
- price_cents: purchase price in 100th of currency
- price_currency: purchase price currency
- Returns 201 on success:
- id: created schedule id
example
curl -include --header "Content-type: application/json" --header "Accept: application/json" \
-X POST -d '{"date": "2013-12-24", "time": "17:00", "spots": 8, \
"price_cents": 10000, "price_currency": "USD"}' http://localhost:3003/activities/1/schedule
=>
201, {"id":1}
- POST /activities/:activity_id/schedule
- recurring: space seperated list of weekdays (sun mon tue wed thu fri sat) on which activity recurs.
- time: time of event as HH:MM
- spots: number of available spots
- price_cents: purchase price in 100th of currency
- price_currency: purchase price currency
- Returns 201 on success:
- id: created schedule id
example
curl -include --header "Content-type: application/json" --header "Accept: application/json" \
-X POST -d '{"recurring": "mon fri", "time": "17:00", "spots": 8, \
"price_cents": 10000, "price_currency": "USD"}' http://localhost:3003/activities/1/schedule
=>
201, {"id":2}
- DELETE /activities/:activity_id/schedule/:schedule_id
- Returns 200 on success
example
curl -include --header "Content-type: application/json" --header "Accept: application/json" \
-X DELETE http://localhost:3003/activities/1/schedule/1
=>
204 on success
- GET /schedules/query
- date: ISO8601 date to query
- activity_id: (optional) only query a specific activity
- Returns 200 on success
- activities: dictionary of activities, mapping from activity id to activity
- name: activity name
- vendor: activity vendor
- availabilities: dictionary of availabilities, mapping from date to a list of availabilities
- activity_id: activity id matching the activities returned in 'activities'
- time_at: time of availability as HH:MM
- spots: number of spots available
- activities: dictionary of activities, mapping from activity id to activity
Note if activity_id is omitted, all activities that have available slots on the given date will be returned.
example
curl -include --header "Content-type: application/json" --header "Accept: application/json" \
-X GET -d '{"date": "2013-12-24", "activity_id": 2}' http://localhost:3003/schedules/query
=>
200
{
"activities": {
"2": {
"id": 2,
"name": "Scuba",
"vendor": "Joe Diver"
}
},
"availabilities": {
"2013-12-24": [
{
"activity_id": 2,
"spots": 2,
"time_at": 28800
}
]
}
}
- GET /schedules/query
- from_date: ISO8601 date to query from
- to_date: ISO8601 date to query to
- activity_id: (optional) only query a specific activity
- Returns 200 on success
- (same data as querying a date)
Note if activity_id is omitted, all activities that have available slots in the given date range will be returned.
example
curl -include --header "Content-type: application/json" --header "Accept: application/json" \
-X GET -d '{"from_date": "2013-12-20", "to_date": "2013-12-26"}' http://localhost:3003/schedules/query
=>
200, json...
- POST /activities/:activity_id/book
- date: ISO8601 to book on
- time: time to book at as HH:MM
- spots: number of spots to book
- Returns 201 on success
- id: booking id
- Returns 403 if date/time is not availabe
- reason: "date/time"
- Returns 403 if not enough spots
- reason: "spots"
- spots: number of spots available
example
curl -include --header "Content-type: application/json" --header "Accept: application/json" \
-X POST -d '{"date": "2013-12-24", "time": "17:00", "spots": 4}' http://localhost:3003/activities/1/book
=>
{"id": 1}
An activity's price is associated with the scheduled event, this allows different dates/times to have different prices. The price stored as cents/currency and uses the Money gem.
The scheduled time is stored as seconds since midnight on the date/day. This is timezone agnostic since an event will always happen on the local time of event and therefore not relevant to the timezone of the client.
A activity scheduled by date takes precedence over events scheduled by recurrence.
Booking an activity generates an event. An event is not tied to a schedule as this isn't relevant at this stage.
Reasons to not use a lookup table;
-
The key set (weekdays) is fixed, it does not change.
-
Changing an activity's occurrences is now done on this lookup table, making sharding harder (the activities table can be sharded and multiple tables easily queried to obtain all activities for a certain set of weekdays).
-
It's more likely that the set of activities will first be reduced by a geographic and category, and the actual set of weekdays is the last reduction.
Reasons to not use a bit set;
-
We cannot guarantee that a storage system can index efficiently when doing (weekdays & N) queries.
-
Queries ala "WHERE weekdays & 0x4" are annoying to read in the long run.
Reasons to not use a concat string ala (mon,wed,sat);
- We can probably guarantee that no storage system can efficiently index this when querying...
Reasons for separate bool columns;
-
Easier to read/explain.
-
Queries are simple and most storage systems can efficiently index bool columns.
-
Even if the storage system does not pack them, it's only 7 bytes per entry, and the set of weekdays is fixed and will not grow (although you could add entries for weekdays/weekend-days).
Get test coverage;
rake test:coverage
open coverage/index.html