├── .gitignore ├── step09 ├── README.txt ├── create.sql └── bookings.py ├── step13 ├── README.txt ├── create.sql └── bookings.py ├── step07 ├── README.txt ├── create.sql └── bookings.py ├── step11 ├── README.txt ├── create.sql └── bookings.py ├── step04 ├── README.txt ├── create.sql └── bookings.py ├── step08 ├── README.txt ├── create.sql └── bookings.py ├── step05 ├── README.txt ├── create.sql └── bookings.py ├── step06 ├── README.txt ├── create.sql └── bookings.py ├── step12 ├── README.txt ├── create.sql └── bookings.py ├── step10 ├── README.txt ├── create.sql └── bookings.py ├── step03 ├── README.txt ├── create.sql └── bookings.py ├── step01 ├── README.txt ├── create.sql └── bookings.py ├── step02 ├── README.txt ├── create.sql └── bookings.py └── README.md /.gitignore: -------------------------------------------------------------------------------- 1 | __pycache__ 2 | bookings.db 3 | -------------------------------------------------------------------------------- /step09/README.txt: -------------------------------------------------------------------------------- 1 | The /bookings code is more convoluted as it has to dispatch on its second 2 | and third parameters (whether users or rooms and then which id). -------------------------------------------------------------------------------- /step13/README.txt: -------------------------------------------------------------------------------- 1 | Show all the bookings 2 | Add a booking for any user in any room 3 | 4 | This is really more of the same: the code is largely cut-and-pasted from elsewhere -------------------------------------------------------------------------------- /step07/README.txt: -------------------------------------------------------------------------------- 1 | This adds a couple of pages, one for users and one for rooms. The code 2 | behind those is kept very straightforward and no extra styling is applied 3 | for now. -------------------------------------------------------------------------------- /step11/README.txt: -------------------------------------------------------------------------------- 1 | Add a room via an HTML form on the /rooms page. 2 | 3 | Very straightforward cut-and-paste from the Users example in the previous step. 4 | The only reason I didn't do them together was not to introduce too much 5 | in one step. 6 | -------------------------------------------------------------------------------- /step04/README.txt: -------------------------------------------------------------------------------- 1 | Add functions to retrieve all bookings for a particular user or room. 2 | 3 | I've used the database view v_bookings which does the necessary joins 4 | to bring the users, rooms & bookings together. An alternative is to join 5 | separately within Python. -------------------------------------------------------------------------------- /step08/README.txt: -------------------------------------------------------------------------------- 1 | Refactored the users_page and rooms_page by pulling out the common 2 | code (mostly text) and using a parameterised format to generate the 3 | variations between the pages. 4 | 5 | Just to make the point, I've added some generic styling to the common code -------------------------------------------------------------------------------- /step05/README.txt: -------------------------------------------------------------------------------- 1 | This is a very simple refactor. The functions which queried the database 2 | have obvious commonality, so I've pulled out the common material and 3 | parameterised it. 4 | 5 | The end result should be exactly the same, because refactoring should 6 | reorganise the codebase without affecting the functionality or API. -------------------------------------------------------------------------------- /step06/README.txt: -------------------------------------------------------------------------------- 1 | This makes a start on a very simple web, based entirely on Python built-in 2 | components. For now, we'll just serve a front page which links to other 3 | pages (which will fail until we put some code behind them). 4 | 5 | The webserver will run until you Ctrl-C to kill it. You can access it via: 6 | 7 | http://localhost:8000 8 | 9 | -------------------------------------------------------------------------------- /step12/README.txt: -------------------------------------------------------------------------------- 1 | Add a booking from a user's page 2 | Add a booking from a room's page 3 | 4 | Quite a lot happening here as this is bringing together all the pieces 5 | of data we have. There's next to no validation on the form at front or 6 | backend. And sqlite is extremely forgiving (it basically stores everything 7 | as text regardless of the column's datatype). So there's real scope here 8 | for messing around with data. 9 | 10 | Note: the same code (add_booking) is used from both forms: each has the 11 | same information (user_id, room_id, when) and in each case one is 12 | selected by the user while the other is supplied -- hidden -- by the 13 | computer. 14 | -------------------------------------------------------------------------------- /step10/README.txt: -------------------------------------------------------------------------------- 1 | Add a user via an HTML form on the /users page. 2 | 3 | This required adding a general-purpose execute function for SQL, 4 | an add_user_to_database function which creates a user from a name 5 | and (optional) email address, and trapping the /add-user URL. 6 | 7 | Note the redirect once the form's complete: this is standard practice 8 | as it avoids the possibility of backing into a POST-ed form. (These days, 9 | most browsers avoid doing that anyway or warn you but the POST-then-301 10 | dance is still considered good practice). 11 | 12 | The effect here is that, once you've pressed "Add User", the screen 13 | comes back to the users again with the new one added. -------------------------------------------------------------------------------- /step03/README.txt: -------------------------------------------------------------------------------- 1 | I've used the simplest possible approach to retrieve the users and rooms. 2 | In particular, I've avoided try-finally and with-statements, both of which 3 | would typically apply here. You could rework the code like this: 4 | 5 | def get_users(): 6 | """Get all the users from the database 7 | """ 8 | db = sqlite3.connect(DATABASE_FILEPATH) 9 | db.row_factory = sqlite3.Row 10 | q = db.cursor() 11 | try: 12 | q.execute("SELECT * FROM users") 13 | users = q.fetchall() 14 | return users 15 | finally: 16 | q.close() 17 | db.close() 18 | 19 | and you could also refactor slightly, since "users" no longer needs 20 | to be named; you could just return the result of q.fetchall(). 21 | 22 | I haven't approached the bookings data here as this is more complicated. -------------------------------------------------------------------------------- /step01/README.txt: -------------------------------------------------------------------------------- 1 | I've pulled the main create statements out into a separate .sql file, both 2 | because they're quite bulky text to include directly in Python code, and 3 | because it's a fairly decent technique to pull text in and execute it against 4 | the database. 5 | 6 | You could do the same thing (and more easily) via the command-line if you 7 | had access to the sqlite.exe executable: 8 | 9 | sqlite < create.sql 10 | 11 | For simplicity, I've avoided with-statements and try-finally blocks. 12 | For more robust code, you'd normally use one or other of these (depending 13 | mostly on whether the code had been set up with context managers). You're 14 | looking at something like this: 15 | 16 | with open("create.sql") as f: 17 | text = f.read() 18 | 19 | or 20 | 21 | db = sql.connect("...") 22 | try: 23 | ... 24 | finally: 25 | db.close() 26 | -------------------------------------------------------------------------------- /step02/README.txt: -------------------------------------------------------------------------------- 1 | For simplicity, I've hardwired names etc. into the inline INSERT statements. 2 | A more sophisticated approach could use CSV files to hold the initial data. 3 | Assuming that you had a CSV file of rooms called rooms.csv: 4 | 5 | Room A, Next to the stairway 6 | Room B, On the Second Floor 7 | Main Hall, 8 | 9 | you could do something like this: 10 | 11 | import csv 12 | import sqlite3 13 | 14 | 15 | db = sqlite.connect("...") 16 | q = db.cursor() 17 | 18 | f = open("rooms.csv", "b") 19 | reader = csv.reader(f) 20 | for row in reader: 21 | q.execute("INSERT INTO rooms(name, location) VALUES (?, ?)", row) 22 | 23 | q.close() 24 | db.commit() 25 | db.close() 26 | 27 | 28 | I've sidestepped the fact that sqlite will auto-generate primary key ids 29 | if a column is specified as INTEGER PRIMARY KEY. This is to make the test 30 | data inserts easier to read. The csv code above *does* assume that sqlite 31 | will autogenerate ids. -------------------------------------------------------------------------------- /step01/create.sql: -------------------------------------------------------------------------------- 1 | CREATE TABLE 2 | users 3 | ( 4 | id INTEGER PRIMARY KEY NOT NULL, 5 | name VARCHAR(200) NOT NULL, 6 | email_address VARCHAR(200) NULL 7 | ) 8 | ; 9 | 10 | CREATE TABLE 11 | rooms 12 | ( 13 | id INTEGER PRIMARY KEY NOT NULL, 14 | name VARCHAR(200) NOT NULL, 15 | location VARCHAR(1024) NULL 16 | ) 17 | ; 18 | 19 | CREATE TABLE 20 | bookings 21 | ( 22 | user_id INTEGER NOT NULL, 23 | room_id INTEGER NOT NULL, 24 | booked_on DATE NOT NULL, 25 | booked_from TIME NULL, 26 | booked_to TIME NULL 27 | ) 28 | ; 29 | 30 | CREATE VIEW 31 | v_bookings 32 | AS SELECT 33 | boo.user_id, 34 | usr.name AS user_name, 35 | boo.room_id, 36 | roo.name AS room_name, 37 | boo.booked_on, 38 | boo.booked_from, 39 | boo.booked_to 40 | FROM 41 | bookings AS boo 42 | JOIN users AS usr ON 43 | usr.id = boo.user_id 44 | JOIN rooms AS roo ON 45 | roo.id = boo.room_id 46 | ; 47 | -------------------------------------------------------------------------------- /step02/create.sql: -------------------------------------------------------------------------------- 1 | CREATE TABLE 2 | users 3 | ( 4 | id INTEGER PRIMARY KEY NOT NULL, 5 | name VARCHAR(200) NOT NULL, 6 | email_address VARCHAR(200) NULL 7 | ) 8 | ; 9 | 10 | CREATE TABLE 11 | rooms 12 | ( 13 | id INTEGER PRIMARY KEY NOT NULL, 14 | name VARCHAR(200) NOT NULL, 15 | location VARCHAR(1024) NULL 16 | ) 17 | ; 18 | 19 | CREATE TABLE 20 | bookings 21 | ( 22 | user_id INTEGER NOT NULL, 23 | room_id INTEGER NOT NULL, 24 | booked_on DATE NOT NULL, 25 | booked_from TIME NULL, 26 | booked_to TIME NULL 27 | ) 28 | ; 29 | 30 | CREATE VIEW 31 | v_bookings 32 | AS SELECT 33 | boo.user_id, 34 | usr.name AS user_name, 35 | boo.room_id, 36 | roo.name AS room_name, 37 | boo.booked_on, 38 | boo.booked_from, 39 | boo.booked_to 40 | FROM 41 | bookings AS boo 42 | JOIN users AS usr ON 43 | usr.id = boo.user_id 44 | JOIN rooms AS roo ON 45 | roo.id = boo.room_id 46 | ; 47 | -------------------------------------------------------------------------------- /step03/create.sql: -------------------------------------------------------------------------------- 1 | CREATE TABLE 2 | users 3 | ( 4 | id INTEGER PRIMARY KEY NOT NULL, 5 | name VARCHAR(200) NOT NULL, 6 | email_address VARCHAR(200) NULL 7 | ) 8 | ; 9 | 10 | CREATE TABLE 11 | rooms 12 | ( 13 | id INTEGER PRIMARY KEY NOT NULL, 14 | name VARCHAR(200) NOT NULL, 15 | location VARCHAR(1024) NULL 16 | ) 17 | ; 18 | 19 | CREATE TABLE 20 | bookings 21 | ( 22 | user_id INTEGER NOT NULL, 23 | room_id INTEGER NOT NULL, 24 | booked_on DATE NOT NULL, 25 | booked_from TIME NULL, 26 | booked_to TIME NULL 27 | ) 28 | ; 29 | 30 | CREATE VIEW 31 | v_bookings 32 | AS SELECT 33 | boo.user_id, 34 | usr.name AS user_name, 35 | boo.room_id, 36 | roo.name AS room_name, 37 | boo.booked_on, 38 | boo.booked_from, 39 | boo.booked_to 40 | FROM 41 | bookings AS boo 42 | JOIN users AS usr ON 43 | usr.id = boo.user_id 44 | JOIN rooms AS roo ON 45 | roo.id = boo.room_id 46 | ; 47 | -------------------------------------------------------------------------------- /step04/create.sql: -------------------------------------------------------------------------------- 1 | CREATE TABLE 2 | users 3 | ( 4 | id INTEGER PRIMARY KEY NOT NULL, 5 | name VARCHAR(200) NOT NULL, 6 | email_address VARCHAR(200) NULL 7 | ) 8 | ; 9 | 10 | CREATE TABLE 11 | rooms 12 | ( 13 | id INTEGER PRIMARY KEY NOT NULL, 14 | name VARCHAR(200) NOT NULL, 15 | location VARCHAR(1024) NULL 16 | ) 17 | ; 18 | 19 | CREATE TABLE 20 | bookings 21 | ( 22 | user_id INTEGER NOT NULL, 23 | room_id INTEGER NOT NULL, 24 | booked_on DATE NOT NULL, 25 | booked_from TIME NULL, 26 | booked_to TIME NULL 27 | ) 28 | ; 29 | 30 | CREATE VIEW 31 | v_bookings 32 | AS SELECT 33 | boo.user_id, 34 | usr.name AS user_name, 35 | boo.room_id, 36 | roo.name AS room_name, 37 | boo.booked_on, 38 | boo.booked_from, 39 | boo.booked_to 40 | FROM 41 | bookings AS boo 42 | JOIN users AS usr ON 43 | usr.id = boo.user_id 44 | JOIN rooms AS roo ON 45 | roo.id = boo.room_id 46 | ; 47 | -------------------------------------------------------------------------------- /step05/create.sql: -------------------------------------------------------------------------------- 1 | CREATE TABLE 2 | users 3 | ( 4 | id INTEGER PRIMARY KEY NOT NULL, 5 | name VARCHAR(200) NOT NULL, 6 | email_address VARCHAR(200) NULL 7 | ) 8 | ; 9 | 10 | CREATE TABLE 11 | rooms 12 | ( 13 | id INTEGER PRIMARY KEY NOT NULL, 14 | name VARCHAR(200) NOT NULL, 15 | location VARCHAR(1024) NULL 16 | ) 17 | ; 18 | 19 | CREATE TABLE 20 | bookings 21 | ( 22 | user_id INTEGER NOT NULL, 23 | room_id INTEGER NOT NULL, 24 | booked_on DATE NOT NULL, 25 | booked_from TIME NULL, 26 | booked_to TIME NULL 27 | ) 28 | ; 29 | 30 | CREATE VIEW 31 | v_bookings 32 | AS SELECT 33 | boo.user_id, 34 | usr.name AS user_name, 35 | boo.room_id, 36 | roo.name AS room_name, 37 | boo.booked_on, 38 | boo.booked_from, 39 | boo.booked_to 40 | FROM 41 | bookings AS boo 42 | JOIN users AS usr ON 43 | usr.id = boo.user_id 44 | JOIN rooms AS roo ON 45 | roo.id = boo.room_id 46 | ; 47 | -------------------------------------------------------------------------------- /step06/create.sql: -------------------------------------------------------------------------------- 1 | CREATE TABLE 2 | users 3 | ( 4 | id INTEGER PRIMARY KEY NOT NULL, 5 | name VARCHAR(200) NOT NULL, 6 | email_address VARCHAR(200) NULL 7 | ) 8 | ; 9 | 10 | CREATE TABLE 11 | rooms 12 | ( 13 | id INTEGER PRIMARY KEY NOT NULL, 14 | name VARCHAR(200) NOT NULL, 15 | location VARCHAR(1024) NULL 16 | ) 17 | ; 18 | 19 | CREATE TABLE 20 | bookings 21 | ( 22 | user_id INTEGER NOT NULL, 23 | room_id INTEGER NOT NULL, 24 | booked_on DATE NOT NULL, 25 | booked_from TIME NULL, 26 | booked_to TIME NULL 27 | ) 28 | ; 29 | 30 | CREATE VIEW 31 | v_bookings 32 | AS SELECT 33 | boo.user_id, 34 | usr.name AS user_name, 35 | boo.room_id, 36 | roo.name AS room_name, 37 | boo.booked_on, 38 | boo.booked_from, 39 | boo.booked_to 40 | FROM 41 | bookings AS boo 42 | JOIN users AS usr ON 43 | usr.id = boo.user_id 44 | JOIN rooms AS roo ON 45 | roo.id = boo.room_id 46 | ; 47 | -------------------------------------------------------------------------------- /step07/create.sql: -------------------------------------------------------------------------------- 1 | CREATE TABLE 2 | users 3 | ( 4 | id INTEGER PRIMARY KEY NOT NULL, 5 | name VARCHAR(200) NOT NULL, 6 | email_address VARCHAR(200) NULL 7 | ) 8 | ; 9 | 10 | CREATE TABLE 11 | rooms 12 | ( 13 | id INTEGER PRIMARY KEY NOT NULL, 14 | name VARCHAR(200) NOT NULL, 15 | location VARCHAR(1024) NULL 16 | ) 17 | ; 18 | 19 | CREATE TABLE 20 | bookings 21 | ( 22 | user_id INTEGER NOT NULL, 23 | room_id INTEGER NOT NULL, 24 | booked_on DATE NOT NULL, 25 | booked_from TIME NULL, 26 | booked_to TIME NULL 27 | ) 28 | ; 29 | 30 | CREATE VIEW 31 | v_bookings 32 | AS SELECT 33 | boo.user_id, 34 | usr.name AS user_name, 35 | boo.room_id, 36 | roo.name AS room_name, 37 | boo.booked_on, 38 | boo.booked_from, 39 | boo.booked_to 40 | FROM 41 | bookings AS boo 42 | JOIN users AS usr ON 43 | usr.id = boo.user_id 44 | JOIN rooms AS roo ON 45 | roo.id = boo.room_id 46 | ; 47 | -------------------------------------------------------------------------------- /step08/create.sql: -------------------------------------------------------------------------------- 1 | CREATE TABLE 2 | users 3 | ( 4 | id INTEGER PRIMARY KEY NOT NULL, 5 | name VARCHAR(200) NOT NULL, 6 | email_address VARCHAR(200) NULL 7 | ) 8 | ; 9 | 10 | CREATE TABLE 11 | rooms 12 | ( 13 | id INTEGER PRIMARY KEY NOT NULL, 14 | name VARCHAR(200) NOT NULL, 15 | location VARCHAR(1024) NULL 16 | ) 17 | ; 18 | 19 | CREATE TABLE 20 | bookings 21 | ( 22 | user_id INTEGER NOT NULL, 23 | room_id INTEGER NOT NULL, 24 | booked_on DATE NOT NULL, 25 | booked_from TIME NULL, 26 | booked_to TIME NULL 27 | ) 28 | ; 29 | 30 | CREATE VIEW 31 | v_bookings 32 | AS SELECT 33 | boo.user_id, 34 | usr.name AS user_name, 35 | boo.room_id, 36 | roo.name AS room_name, 37 | boo.booked_on, 38 | boo.booked_from, 39 | boo.booked_to 40 | FROM 41 | bookings AS boo 42 | JOIN users AS usr ON 43 | usr.id = boo.user_id 44 | JOIN rooms AS roo ON 45 | roo.id = boo.room_id 46 | ; 47 | -------------------------------------------------------------------------------- /step09/create.sql: -------------------------------------------------------------------------------- 1 | CREATE TABLE 2 | users 3 | ( 4 | id INTEGER PRIMARY KEY NOT NULL, 5 | name VARCHAR(200) NOT NULL, 6 | email_address VARCHAR(200) NULL 7 | ) 8 | ; 9 | 10 | CREATE TABLE 11 | rooms 12 | ( 13 | id INTEGER PRIMARY KEY NOT NULL, 14 | name VARCHAR(200) NOT NULL, 15 | location VARCHAR(1024) NULL 16 | ) 17 | ; 18 | 19 | CREATE TABLE 20 | bookings 21 | ( 22 | user_id INTEGER NOT NULL, 23 | room_id INTEGER NOT NULL, 24 | booked_on DATE NOT NULL, 25 | booked_from TIME NULL, 26 | booked_to TIME NULL 27 | ) 28 | ; 29 | 30 | CREATE VIEW 31 | v_bookings 32 | AS SELECT 33 | boo.user_id, 34 | usr.name AS user_name, 35 | boo.room_id, 36 | roo.name AS room_name, 37 | boo.booked_on, 38 | boo.booked_from, 39 | boo.booked_to 40 | FROM 41 | bookings AS boo 42 | JOIN users AS usr ON 43 | usr.id = boo.user_id 44 | JOIN rooms AS roo ON 45 | roo.id = boo.room_id 46 | ; 47 | -------------------------------------------------------------------------------- /step10/create.sql: -------------------------------------------------------------------------------- 1 | CREATE TABLE 2 | users 3 | ( 4 | id INTEGER PRIMARY KEY NOT NULL, 5 | name VARCHAR(200) NOT NULL, 6 | email_address VARCHAR(200) NULL 7 | ) 8 | ; 9 | 10 | CREATE TABLE 11 | rooms 12 | ( 13 | id INTEGER PRIMARY KEY NOT NULL, 14 | name VARCHAR(200) NOT NULL, 15 | location VARCHAR(1024) NULL 16 | ) 17 | ; 18 | 19 | CREATE TABLE 20 | bookings 21 | ( 22 | user_id INTEGER NOT NULL, 23 | room_id INTEGER NOT NULL, 24 | booked_on DATE NOT NULL, 25 | booked_from TIME NULL, 26 | booked_to TIME NULL 27 | ) 28 | ; 29 | 30 | CREATE VIEW 31 | v_bookings 32 | AS SELECT 33 | boo.user_id, 34 | usr.name AS user_name, 35 | boo.room_id, 36 | roo.name AS room_name, 37 | boo.booked_on, 38 | boo.booked_from, 39 | boo.booked_to 40 | FROM 41 | bookings AS boo 42 | JOIN users AS usr ON 43 | usr.id = boo.user_id 44 | JOIN rooms AS roo ON 45 | roo.id = boo.room_id 46 | ; 47 | -------------------------------------------------------------------------------- /step11/create.sql: -------------------------------------------------------------------------------- 1 | CREATE TABLE 2 | users 3 | ( 4 | id INTEGER PRIMARY KEY NOT NULL, 5 | name VARCHAR(200) NOT NULL, 6 | email_address VARCHAR(200) NULL 7 | ) 8 | ; 9 | 10 | CREATE TABLE 11 | rooms 12 | ( 13 | id INTEGER PRIMARY KEY NOT NULL, 14 | name VARCHAR(200) NOT NULL, 15 | location VARCHAR(1024) NULL 16 | ) 17 | ; 18 | 19 | CREATE TABLE 20 | bookings 21 | ( 22 | user_id INTEGER NOT NULL, 23 | room_id INTEGER NOT NULL, 24 | booked_on DATE NOT NULL, 25 | booked_from TIME NULL, 26 | booked_to TIME NULL 27 | ) 28 | ; 29 | 30 | CREATE VIEW 31 | v_bookings 32 | AS SELECT 33 | boo.user_id, 34 | usr.name AS user_name, 35 | boo.room_id, 36 | roo.name AS room_name, 37 | boo.booked_on, 38 | boo.booked_from, 39 | boo.booked_to 40 | FROM 41 | bookings AS boo 42 | JOIN users AS usr ON 43 | usr.id = boo.user_id 44 | JOIN rooms AS roo ON 45 | roo.id = boo.room_id 46 | ; 47 | -------------------------------------------------------------------------------- /step12/create.sql: -------------------------------------------------------------------------------- 1 | CREATE TABLE 2 | users 3 | ( 4 | id INTEGER PRIMARY KEY NOT NULL, 5 | name VARCHAR(200) NOT NULL, 6 | email_address VARCHAR(200) NULL 7 | ) 8 | ; 9 | 10 | CREATE TABLE 11 | rooms 12 | ( 13 | id INTEGER PRIMARY KEY NOT NULL, 14 | name VARCHAR(200) NOT NULL, 15 | location VARCHAR(1024) NULL 16 | ) 17 | ; 18 | 19 | CREATE TABLE 20 | bookings 21 | ( 22 | user_id INTEGER NOT NULL, 23 | room_id INTEGER NOT NULL, 24 | booked_on DATE NOT NULL, 25 | booked_from TIME NULL, 26 | booked_to TIME NULL 27 | ) 28 | ; 29 | 30 | CREATE VIEW 31 | v_bookings 32 | AS SELECT 33 | boo.user_id, 34 | usr.name AS user_name, 35 | boo.room_id, 36 | roo.name AS room_name, 37 | boo.booked_on, 38 | boo.booked_from, 39 | boo.booked_to 40 | FROM 41 | bookings AS boo 42 | JOIN users AS usr ON 43 | usr.id = boo.user_id 44 | JOIN rooms AS roo ON 45 | roo.id = boo.room_id 46 | ; 47 | -------------------------------------------------------------------------------- /step13/create.sql: -------------------------------------------------------------------------------- 1 | CREATE TABLE 2 | users 3 | ( 4 | id INTEGER PRIMARY KEY NOT NULL, 5 | name VARCHAR(200) NOT NULL, 6 | email_address VARCHAR(200) NULL 7 | ) 8 | ; 9 | 10 | CREATE TABLE 11 | rooms 12 | ( 13 | id INTEGER PRIMARY KEY NOT NULL, 14 | name VARCHAR(200) NOT NULL, 15 | location VARCHAR(1024) NULL 16 | ) 17 | ; 18 | 19 | CREATE TABLE 20 | bookings 21 | ( 22 | user_id INTEGER NOT NULL, 23 | room_id INTEGER NOT NULL, 24 | booked_on DATE NOT NULL, 25 | booked_from TIME NULL, 26 | booked_to TIME NULL 27 | ) 28 | ; 29 | 30 | CREATE VIEW 31 | v_bookings 32 | AS SELECT 33 | boo.user_id, 34 | usr.name AS user_name, 35 | boo.room_id, 36 | roo.name AS room_name, 37 | boo.booked_on, 38 | boo.booked_from, 39 | boo.booked_to 40 | FROM 41 | bookings AS boo 42 | JOIN users AS usr ON 43 | usr.id = boo.user_id 44 | JOIN rooms AS roo ON 45 | roo.id = boo.room_id 46 | ; 47 | -------------------------------------------------------------------------------- /step01/bookings.py: -------------------------------------------------------------------------------- 1 | #!python3 2 | import os, sys 3 | import sqlite3 4 | 5 | # 6 | # Ensure we're using the same database filename throughout. 7 | # It doesn't matter what this is called or where it is: 8 | # sqlite3 will just accept anything. 9 | # 10 | DATABASE_FILEPATH = "bookings.db" 11 | def create_database(): 12 | """Connect to the database, read the CREATE statements and split 13 | them at the semicolon into individual statements. Once each 14 | statement has been executed, close the connection. 15 | """ 16 | # 17 | # Since we might be re-running this, delete the file and rebuild 18 | # it if necessary. 19 | # 20 | if os.path.exists(DATABASE_FILEPATH): 21 | os.remove(DATABASE_FILEPATH) 22 | 23 | # 24 | # A database cursor the the Python mechanism for running something 25 | # against any database. You create a cursor and then .execute 26 | # SQL statements through it. 27 | # 28 | db = sqlite3.connect(DATABASE_FILEPATH) 29 | q = db.cursor() 30 | 31 | # 32 | # Read all the contents of create.sql in one gulp 33 | # 34 | sql = open("create.sql").read() 35 | # 36 | # Split it into individual statements, breaking on the semicolon 37 | # 38 | statements = sql.split(";") 39 | # 40 | # Execute each of the individual statements against the database 41 | # 42 | for statement in statements: 43 | q.execute(statement) 44 | 45 | # 46 | # Close everything 47 | # 48 | q.close() 49 | db.commit() 50 | db.close() 51 | 52 | if __name__ == '__main__': 53 | print("About to create database %s" % DATABASE_FILEPATH) 54 | create_database() 55 | print("Finished") 56 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | A Room Booking System 2 | ===================== 3 | 4 | This project forms part of the One Screenful of Python github organisation. For more 5 | information about that idea, see the README in the screenfuls repo: 6 | 7 | https://github.com/OneScreenfulOfPython/screenfuls/blob/master/README.md 8 | 9 | Summary 10 | ------- 11 | 12 | Build a booking system (ostensibly for rooms but could be for 13 | anything: gym equipment, library books etc.). 14 | 15 | Background Requiements 16 | ---------------------- 17 | 18 | * Only use built-in modules (but suggest alternatives) 19 | 20 | * Run unchanged on Python 2 and 3 21 | 22 | * Run unchanged on any Python-supported platform (esp. including RPi) 23 | 24 | * Keep core code simple, without using any more sophisticated Python idioms, 25 | but suggest alternatives / improvements. 26 | 27 | * Remain import-table to allow for experimentation. This is especially 28 | useful as it makes it possible to experiment easily at the command 29 | line, eg: 30 | 31 | import bookings 32 | 33 | bookings.create_database() 34 | bookings.populate_database() 35 | print(bookings.get_user(1)) 36 | print(bookings.get_room(1)) 37 | bookings.add_booking_to_database(1, 1, '2014-11-18', '12:00') 38 | 39 | 40 | Project Requirements 41 | -------------------- 42 | 43 | * People have names & email addresses 44 | 45 | * Rooms have names and locations 46 | 47 | * Bookings are made by one person for one room on one day between Time A and Time B 48 | 49 | * There should be a usable interface to book rooms / view room bookings 50 | 51 | Steps 52 | ----- 53 | 54 | Steps -- each intended to be about a screenful. Each step leaves the 55 | application in a "working" state, albeit not necessarily a very interesting 56 | one, at least at first. 57 | 58 | 1) Create the empty database 59 | 60 | 2) Populate with some sample data: users, rooms, bookings 61 | 62 | 3) Create simple functions for accessing users & rooms 63 | 64 | 4) Create simple functions for accessing bookings via users & rooms 65 | 66 | 5) Refactor the database queries to use common functionality 67 | 68 | 6) Create a simple website with just a front page 69 | 70 | 7) Add /users to list users & /rooms to list rooms 71 | 72 | 8) Refactor the HTML pages to use common functionality 73 | 74 | 9) Add /bookings to list bookings for a user or a room 75 | 76 | 10) Add a user 77 | 78 | 11) Add a room 79 | 80 | 12) Add a booking (of a room by a user) 81 | 82 | 13) Add a booking (by a user of a room) 83 | 84 | 14) Show all bookings and allow additions 85 | 86 | Hints 87 | ----- 88 | 89 | * You can use the sqlite ".dump" command to quickly see the database contents: 90 | 91 | sqlite bookings.db .dump 92 | 93 | -------------------------------------------------------------------------------- /step02/bookings.py: -------------------------------------------------------------------------------- 1 | #!python3 2 | import os, sys 3 | import sqlite3 4 | 5 | # 6 | # Ensure we're using the same database filename throughout. 7 | # It doesn't matter what this is called or where it is: 8 | # sqlite3 will just accept anything. 9 | # 10 | DATABASE_FILEPATH = "bookings.db" 11 | 12 | def create_database(): 13 | """Connect to the database, read the CREATE statements and split 14 | them at the semicolon into individual statements. Once each 15 | statement has been executed, close the connection. 16 | """ 17 | # 18 | # Since we might be re-running this, delete the file and rebuild 19 | # it if necessary. 20 | # 21 | if os.path.exists(DATABASE_FILEPATH): 22 | os.remove(DATABASE_FILEPATH) 23 | 24 | # 25 | # A database cursor the the Python mechanism for running something 26 | # against any database. You create a cursor and then .execute 27 | # SQL statements through it. 28 | # 29 | db = sqlite3.connect(DATABASE_FILEPATH) 30 | q = db.cursor() 31 | 32 | # 33 | # Read all the contents of create.sql in one gulp 34 | # 35 | sql = open("create.sql").read() 36 | # 37 | # Split it into individual statements, breaking on the semicolon 38 | # 39 | statements = sql.split(";") 40 | # 41 | # Execute each of the individual statements against the database 42 | # 43 | for statement in statements: 44 | q.execute(statement) 45 | 46 | # 47 | # Close everything 48 | # 49 | q.close() 50 | db.commit() 51 | db.close() 52 | 53 | def populate_database(): 54 | """Populate the database with some valid test data 55 | """ 56 | db = sqlite3.connect(DATABASE_FILEPATH) 57 | q = db.cursor() 58 | 59 | sql = "INSERT INTO users(id, name, email_address) VALUES(?, ?, ?)" 60 | q.execute(sql, [1, "Mickey Mouse", "mickey.mouse@example.com"]) 61 | q.execute(sql, [2, "Donald Duck", "donald.duck@example.com"]) 62 | q.execute(sql, [3, "Kermit the Frog", None]) 63 | 64 | sql = "INSERT INTO rooms(id, name, location) VALUES(?, ?, ?)" 65 | q.execute(sql, [1, "Room A", "Next to the stairway"]) 66 | q.execute(sql, [2, "Room B", "On the Second Floor"]) 67 | q.execute(sql, [3, "Main Hall", None]) 68 | 69 | # 70 | # Triple-quoted strings can cross lines 71 | # NB the column order doesn't matter if you specify it 72 | # 73 | sql = """ 74 | INSERT INTO 75 | bookings 76 | ( 77 | room_id, user_id, booked_on, booked_from, booked_to 78 | ) 79 | VALUES( 80 | ?, ?, ?, ?, ? 81 | )""" 82 | q.execute(sql, [1, 1, '2014-09-25', '09:00', '10:00']) # Room A (1) booked by Mickey (1) from 9am to 10am on 25th Sep 2014 83 | q.execute(sql, [3, 1, '2015-09-25', None, None]) # Main Hall (3) booked by Mickey (1) from all day on 25th Sep 2014 84 | q.execute(sql, [2, 3, '2014-09-22', '12:00', None]) # Room B (2) booked by Kermit (3) from midday onwards on 22nd Sep 2014 85 | q.execute(sql, [1, 2, '2015-02-14', '09:30', '10:00']) # Room A (1) booked by Donald (2) from 9.30am to 10am on 15th Feb 2014 86 | 87 | q.close() 88 | db.commit() 89 | db.close() 90 | 91 | if __name__ == '__main__': 92 | print("About to create database %s" % DATABASE_FILEPATH) 93 | create_database() 94 | print("About to populate database %s" % DATABASE_FILEPATH) 95 | populate_database() 96 | print("Finished") 97 | -------------------------------------------------------------------------------- /step03/bookings.py: -------------------------------------------------------------------------------- 1 | #!python3 2 | import os, sys 3 | import sqlite3 4 | 5 | # 6 | # Ensure we're using the same database filename throughout. 7 | # It doesn't matter what this is called or where it is: 8 | # sqlite3 will just accept anything. 9 | # 10 | DATABASE_FILEPATH = "bookings.db" 11 | 12 | def create_database(): 13 | """Connect to the database, read the CREATE statements and split 14 | them at the semicolon into individual statements. Once each 15 | statement has been executed, close the connection. 16 | """ 17 | # 18 | # Since we might be re-running this, delete the file and rebuild 19 | # it if necessary. 20 | # 21 | if os.path.exists(DATABASE_FILEPATH): 22 | os.remove(DATABASE_FILEPATH) 23 | 24 | # 25 | # A database cursor the the Python mechanism for running something 26 | # against any database. You create a cursor and then .execute 27 | # SQL statements through it. 28 | # 29 | db = sqlite3.connect(DATABASE_FILEPATH) 30 | q = db.cursor() 31 | 32 | # 33 | # Read all the contents of create.sql in one gulp 34 | # 35 | sql = open("create.sql").read() 36 | # 37 | # Split it into individual statements, breaking on the semicolon 38 | # 39 | statements = sql.split(";") 40 | # 41 | # Execute each of the individual statements against the database 42 | # 43 | for statement in statements: 44 | q.execute(statement) 45 | 46 | # 47 | # Close everything 48 | # 49 | q.close() 50 | db.commit() 51 | db.close() 52 | 53 | def populate_database(): 54 | """Populate the database with some valid test data 55 | """ 56 | db = sqlite3.connect(DATABASE_FILEPATH) 57 | q = db.cursor() 58 | 59 | sql = "INSERT INTO users(id, name, email_address) VALUES(?, ?, ?)" 60 | q.execute(sql, [1, "Mickey Mouse", "mickey.mouse@example.com"]) 61 | q.execute(sql, [2, "Donald Duck", "donald.duck@example.com"]) 62 | q.execute(sql, [3, "Kermit the Frog", None]) 63 | 64 | sql = "INSERT INTO rooms(id, name, location) VALUES(?, ?, ?)" 65 | q.execute(sql, [1, "Room A", "Next to the stairway"]) 66 | q.execute(sql, [2, "Room B", "On the Second Floor"]) 67 | q.execute(sql, [3, "Main Hall", None]) 68 | 69 | # 70 | # Triple-quoted strings can cross lines 71 | # NB the column order doesn't matter if you specify it 72 | # 73 | sql = """ 74 | INSERT INTO 75 | bookings 76 | ( 77 | room_id, user_id, booked_on, booked_from, booked_to 78 | ) 79 | VALUES( 80 | ?, ?, ?, ?, ? 81 | )""" 82 | q.execute(sql, [1, 1, '2014-09-25', '09:00', '10:00']) # Room A (1) booked by Mickey (1) from 9am to 10am on 25th Sep 2014 83 | q.execute(sql, [3, 1, '2015-09-25', None, None]) # Main Hall (3) booked by Mickey (1) from all day on 25th Sep 2014 84 | q.execute(sql, [2, 3, '2014-09-22', '12:00', None]) # Room B (2) booked by Kermit (3) from midday onwards on 22nd Sep 2014 85 | q.execute(sql, [1, 2, '2015-02-14', '09:30', '10:00']) # Room A (1) booked by Donald (2) from 9.30am to 10am on 15th Feb 2014 86 | 87 | q.close() 88 | db.commit() 89 | db.close() 90 | 91 | def get_users(): 92 | """Get all the users from the database 93 | """ 94 | db = sqlite3.connect(DATABASE_FILEPATH) 95 | db.row_factory = sqlite3.Row 96 | q = db.cursor() 97 | 98 | q.execute("SELECT * FROM users") 99 | users = q.fetchall() 100 | 101 | q.close() 102 | db.close() 103 | 104 | return users 105 | 106 | def get_rooms(): 107 | """Get all the rooms from the database 108 | """ 109 | db = sqlite3.connect(DATABASE_FILEPATH) 110 | db.row_factory = sqlite3.Row 111 | q = db.cursor() 112 | 113 | q.execute("SELECT * FROM rooms") 114 | rooms = q.fetchall() 115 | 116 | q.close() 117 | db.close() 118 | 119 | return rooms 120 | 121 | if __name__ == '__main__': 122 | print("About to create database %s" % DATABASE_FILEPATH) 123 | create_database() 124 | print("About to populate database %s" % DATABASE_FILEPATH) 125 | populate_database() 126 | print("About to select data from %s" % DATABASE_FILEPATH) 127 | for user in get_users(): 128 | print(user['name']) 129 | for room in get_rooms(): 130 | print(room['name']) 131 | print("Finished") 132 | -------------------------------------------------------------------------------- /step05/bookings.py: -------------------------------------------------------------------------------- 1 | #!python3 2 | import os, sys 3 | import sqlite3 4 | 5 | # 6 | # Ensure we're using the same database filename throughout. 7 | # It doesn't matter what this is called or where it is: 8 | # sqlite3 will just accept anything. 9 | # 10 | DATABASE_FILEPATH = "bookings.db" 11 | 12 | def create_database(): 13 | """Connect to the database, read the CREATE statements and split 14 | them at the semicolon into individual statements. Once each 15 | statement has been executed, close the connection. 16 | """ 17 | # 18 | # Since we might be re-running this, delete the file and rebuild 19 | # it if necessary. 20 | # 21 | if os.path.exists(DATABASE_FILEPATH): 22 | os.remove(DATABASE_FILEPATH) 23 | 24 | # 25 | # A database cursor the the Python mechanism for running something 26 | # against any database. You create a cursor and then .execute 27 | # SQL statements through it. 28 | # 29 | db = sqlite3.connect(DATABASE_FILEPATH) 30 | q = db.cursor() 31 | 32 | # 33 | # Read all the contents of create.sql in one gulp 34 | # 35 | sql = open("create.sql").read() 36 | # 37 | # Split it into individual statements, breaking on the semicolon 38 | # 39 | statements = sql.split(";") 40 | # 41 | # Execute each of the individual statements against the database 42 | # 43 | for statement in statements: 44 | q.execute(statement) 45 | 46 | # 47 | # Close everything 48 | # 49 | q.close() 50 | db.commit() 51 | db.close() 52 | 53 | def populate_database(): 54 | """Populate the database with some valid test data 55 | """ 56 | db = sqlite3.connect(DATABASE_FILEPATH) 57 | q = db.cursor() 58 | 59 | sql = "INSERT INTO users(id, name, email_address) VALUES(?, ?, ?)" 60 | q.execute(sql, [1, "Mickey Mouse", "mickey.mouse@example.com"]) 61 | q.execute(sql, [2, "Donald Duck", "donald.duck@example.com"]) 62 | q.execute(sql, [3, "Kermit the Frog", None]) 63 | 64 | sql = "INSERT INTO rooms(id, name, location) VALUES(?, ?, ?)" 65 | q.execute(sql, [1, "Room A", "Next to the stairway"]) 66 | q.execute(sql, [2, "Room B", "On the Second Floor"]) 67 | q.execute(sql, [3, "Main Hall", None]) 68 | 69 | # 70 | # Triple-quoted strings can cross lines 71 | # NB the column order doesn't matter if you specify it 72 | # 73 | sql = """ 74 | INSERT INTO 75 | bookings 76 | ( 77 | room_id, user_id, booked_on, booked_from, booked_to 78 | ) 79 | VALUES( 80 | ?, ?, ?, ?, ? 81 | )""" 82 | q.execute(sql, [1, 1, '2014-09-25', '09:00', '10:00']) # Room A (1) booked by Mickey (1) from 9am to 10am on 25th Sep 2014 83 | q.execute(sql, [3, 1, '2015-09-25', None, None]) # Main Hall (3) booked by Mickey (1) from all day on 25th Sep 2014 84 | q.execute(sql, [2, 3, '2014-09-22', '12:00', None]) # Room B (2) booked by Kermit (3) from midday onwards on 22nd Sep 2014 85 | q.execute(sql, [1, 2, '2015-02-14', '09:30', '10:00']) # Room A (1) booked by Donald (2) from 9.30am to 10am on 15th Feb 2014 86 | 87 | q.close() 88 | db.commit() 89 | db.close() 90 | 91 | def select(sql_statement, params=None): 92 | """General-purpose routine to read from the database 93 | """ 94 | if params is None: 95 | params = [] 96 | db = sqlite3.connect(DATABASE_FILEPATH) 97 | db.row_factory = sqlite3.Row 98 | q = db.cursor() 99 | try: 100 | q.execute(sql_statement, params) 101 | return q.fetchall() 102 | finally: 103 | q.close() 104 | db.close() 105 | 106 | def get_users(): 107 | """Get all the users from the database 108 | """ 109 | return select("SELECT * FROM users") 110 | 111 | def get_rooms(): 112 | """Get all the rooms from the database 113 | """ 114 | return select("SELECT * FROM rooms") 115 | 116 | def get_bookings_for_user(user_id): 117 | """Get all the bookings made by a user 118 | """ 119 | return select("SELECT * FROM v_bookings WHERE user_id = ?", [user_id]) 120 | 121 | def get_bookings_for_room(room_id): 122 | """Get all the bookings made against a room 123 | """ 124 | return select("SELECT * FROM v_bookings WHERE room_id = ?", [room_id]) 125 | 126 | if __name__ == '__main__': 127 | print("About to create database %s" % DATABASE_FILEPATH) 128 | create_database() 129 | print("About to populate database %s" % DATABASE_FILEPATH) 130 | populate_database() 131 | print("Bookings for user: 1") 132 | for booking in get_bookings_for_user(1): 133 | print(booking['room_name'], booking['booked_on']) 134 | print("Bookings for room: 2") 135 | for booking in get_bookings_for_room(2): 136 | print(booking['user_name'], booking['booked_on']) 137 | print("Finished") 138 | -------------------------------------------------------------------------------- /step04/bookings.py: -------------------------------------------------------------------------------- 1 | #!python3 2 | import os, sys 3 | import sqlite3 4 | 5 | # 6 | # Ensure we're using the same database filename throughout. 7 | # It doesn't matter what this is called or where it is: 8 | # sqlite3 will just accept anything. 9 | # 10 | DATABASE_FILEPATH = "bookings.db" 11 | 12 | def create_database(): 13 | """Connect to the database, read the CREATE statements and split 14 | them at the semicolon into individual statements. Once each 15 | statement has been executed, close the connection. 16 | """ 17 | # 18 | # Since we might be re-running this, delete the file and rebuild 19 | # it if necessary. 20 | # 21 | if os.path.exists(DATABASE_FILEPATH): 22 | os.remove(DATABASE_FILEPATH) 23 | 24 | # 25 | # A database cursor the the Python mechanism for running something 26 | # against any database. You create a cursor and then .execute 27 | # SQL statements through it. 28 | # 29 | db = sqlite3.connect(DATABASE_FILEPATH) 30 | q = db.cursor() 31 | 32 | # 33 | # Read all the contents of create.sql in one gulp 34 | # 35 | sql = open("create.sql").read() 36 | # 37 | # Split it into individual statements, breaking on the semicolon 38 | # 39 | statements = sql.split(";") 40 | # 41 | # Execute each of the individual statements against the database 42 | # 43 | for statement in statements: 44 | q.execute(statement) 45 | 46 | # 47 | # Close everything 48 | # 49 | q.close() 50 | db.commit() 51 | db.close() 52 | 53 | def populate_database(): 54 | """Populate the database with some valid test data 55 | """ 56 | db = sqlite3.connect(DATABASE_FILEPATH) 57 | q = db.cursor() 58 | 59 | sql = "INSERT INTO users(id, name, email_address) VALUES(?, ?, ?)" 60 | q.execute(sql, [1, "Mickey Mouse", "mickey.mouse@example.com"]) 61 | q.execute(sql, [2, "Donald Duck", "donald.duck@example.com"]) 62 | q.execute(sql, [3, "Kermit the Frog", None]) 63 | 64 | sql = "INSERT INTO rooms(id, name, location) VALUES(?, ?, ?)" 65 | q.execute(sql, [1, "Room A", "Next to the stairway"]) 66 | q.execute(sql, [2, "Room B", "On the Second Floor"]) 67 | q.execute(sql, [3, "Main Hall", None]) 68 | 69 | # 70 | # Triple-quoted strings can cross lines 71 | # NB the column order doesn't matter if you specify it 72 | # 73 | sql = """ 74 | INSERT INTO 75 | bookings 76 | ( 77 | room_id, user_id, booked_on, booked_from, booked_to 78 | ) 79 | VALUES( 80 | ?, ?, ?, ?, ? 81 | )""" 82 | q.execute(sql, [1, 1, '2014-09-25', '09:00', '10:00']) # Room A (1) booked by Mickey (1) from 9am to 10am on 25th Sep 2014 83 | q.execute(sql, [3, 1, '2015-09-25', None, None]) # Main Hall (3) booked by Mickey (1) from all day on 25th Sep 2014 84 | q.execute(sql, [2, 3, '2014-09-22', '12:00', None]) # Room B (2) booked by Kermit (3) from midday onwards on 22nd Sep 2014 85 | q.execute(sql, [1, 2, '2015-02-14', '09:30', '10:00']) # Room A (1) booked by Donald (2) from 9.30am to 10am on 15th Feb 2014 86 | 87 | q.close() 88 | db.commit() 89 | db.close() 90 | 91 | def get_users(): 92 | """Get all the users from the database 93 | """ 94 | db = sqlite3.connect(DATABASE_FILEPATH) 95 | db.row_factory = sqlite3.Row 96 | q = db.cursor() 97 | 98 | q.execute("SELECT * FROM users") 99 | users = q.fetchall() 100 | 101 | q.close() 102 | db.close() 103 | 104 | return users 105 | 106 | def get_rooms(): 107 | """Get all the rooms from the database 108 | """ 109 | db = sqlite3.connect(DATABASE_FILEPATH) 110 | db.row_factory = sqlite3.Row 111 | q = db.cursor() 112 | 113 | q.execute("SELECT * FROM rooms") 114 | rooms = q.fetchall() 115 | 116 | q.close() 117 | db.close() 118 | 119 | return rooms 120 | 121 | def get_bookings_for_user(user_id): 122 | """Get all the bookings made by a user 123 | """ 124 | db = sqlite3.connect(DATABASE_FILEPATH) 125 | db.row_factory = sqlite3.Row 126 | q = db.cursor() 127 | 128 | q.execute("SELECT * FROM v_bookings WHERE user_id = ?", [user_id]) 129 | bookings = q.fetchall() 130 | 131 | q.close() 132 | db.close() 133 | 134 | return bookings 135 | 136 | def get_bookings_for_room(user_id): 137 | """Get all the bookings made against a room 138 | """ 139 | db = sqlite3.connect(DATABASE_FILEPATH) 140 | db.row_factory = sqlite3.Row 141 | q = db.cursor() 142 | 143 | q.execute("SELECT * FROM v_bookings WHERE user_id = ?", [user_id]) 144 | bookings = q.fetchall() 145 | 146 | q.close() 147 | db.close() 148 | 149 | return bookings 150 | 151 | if __name__ == '__main__': 152 | print("About to create database %s" % DATABASE_FILEPATH) 153 | create_database() 154 | print("About to populate database %s" % DATABASE_FILEPATH) 155 | populate_database() 156 | print("Bookings for user: 1") 157 | for booking in get_bookings_for_user(1): 158 | print(booking['room_name'], booking['booked_on']) 159 | print("Bookings for room: 2") 160 | for booking in get_bookings_for_room(2): 161 | print(booking['user_name'], booking['booked_on']) 162 | print("Finished") 163 | -------------------------------------------------------------------------------- /step06/bookings.py: -------------------------------------------------------------------------------- 1 | #!python3 2 | import os, sys 3 | import sqlite3 4 | from wsgiref.util import setup_testing_defaults, shift_path_info 5 | from wsgiref.simple_server import make_server 6 | 7 | # 8 | # Ensure we're using the same database filename throughout. 9 | # It doesn't matter what this is called or where it is: 10 | # sqlite3 will just accept anything. 11 | # 12 | DATABASE_FILEPATH = "bookings.db" 13 | 14 | def create_database(): 15 | """Connect to the database, read the CREATE statements and split 16 | them at the semicolon into individual statements. Once each 17 | statement has been executed, close the connection. 18 | """ 19 | # 20 | # Since we might be re-running this, delete the file and rebuild 21 | # it if necessary. 22 | # 23 | if os.path.exists(DATABASE_FILEPATH): 24 | os.remove(DATABASE_FILEPATH) 25 | 26 | # 27 | # A database cursor the the Python mechanism for running something 28 | # against any database. You create a cursor and then .execute 29 | # SQL statements through it. 30 | # 31 | db = sqlite3.connect(DATABASE_FILEPATH) 32 | q = db.cursor() 33 | 34 | # 35 | # Read all the contents of create.sql in one gulp 36 | # 37 | sql = open("create.sql").read() 38 | # 39 | # Split it into individual statements, breaking on the semicolon 40 | # 41 | statements = sql.split(";") 42 | # 43 | # Execute each of the individual statements against the database 44 | # 45 | for statement in statements: 46 | q.execute(statement) 47 | 48 | # 49 | # Close everything 50 | # 51 | q.close() 52 | db.commit() 53 | db.close() 54 | 55 | def populate_database(): 56 | """Populate the database with some valid test data 57 | """ 58 | db = sqlite3.connect(DATABASE_FILEPATH) 59 | q = db.cursor() 60 | 61 | sql = "INSERT INTO users(id, name, email_address) VALUES(?, ?, ?)" 62 | q.execute(sql, [1, "Mickey Mouse", "mickey.mouse@example.com"]) 63 | q.execute(sql, [2, "Donald Duck", "donald.duck@example.com"]) 64 | q.execute(sql, [3, "Kermit the Frog", None]) 65 | 66 | sql = "INSERT INTO rooms(id, name, location) VALUES(?, ?, ?)" 67 | q.execute(sql, [1, "Room A", "Next to the stairway"]) 68 | q.execute(sql, [2, "Room B", "On the Second Floor"]) 69 | q.execute(sql, [3, "Main Hall", None]) 70 | 71 | # 72 | # Triple-quoted strings can cross lines 73 | # NB the column order doesn't matter if you specify it 74 | # 75 | sql = """ 76 | INSERT INTO 77 | bookings 78 | ( 79 | room_id, user_id, booked_on, booked_from, booked_to 80 | ) 81 | VALUES( 82 | ?, ?, ?, ?, ? 83 | )""" 84 | q.execute(sql, [1, 1, '2014-09-25', '09:00', '10:00']) # Room A (1) booked by Mickey (1) from 9am to 10am on 25th Sep 2014 85 | q.execute(sql, [3, 1, '2015-09-25', None, None]) # Main Hall (3) booked by Mickey (1) from all day on 25th Sep 2014 86 | q.execute(sql, [2, 3, '2014-09-22', '12:00', None]) # Room B (2) booked by Kermit (3) from midday onwards on 22nd Sep 2014 87 | q.execute(sql, [1, 2, '2015-02-14', '09:30', '10:00']) # Room A (1) booked by Donald (2) from 9.30am to 10am on 15th Feb 2014 88 | 89 | q.close() 90 | db.commit() 91 | db.close() 92 | 93 | def select(sql_statement, params=None): 94 | """General-purpose routine to read from the database 95 | """ 96 | if params is None: 97 | params = [] 98 | db = sqlite3.connect(DATABASE_FILEPATH) 99 | db.row_factory = sqlite3.Row 100 | q = db.cursor() 101 | try: 102 | q.execute(sql_statement, params) 103 | return q.fetchall() 104 | finally: 105 | q.close() 106 | db.close() 107 | 108 | def get_users(): 109 | """Get all the users from the database 110 | """ 111 | return select("SELECT * FROM users") 112 | 113 | def get_rooms(): 114 | """Get all the rooms from the database 115 | """ 116 | return select("SELECT * FROM rooms") 117 | 118 | def get_bookings_for_user(user_id): 119 | """Get all the bookings made by a user 120 | """ 121 | return select("SELECT * FROM v_bookings WHERE user_id = ?", [user_id]) 122 | 123 | def get_bookings_for_room(room_id): 124 | """Get all the bookings made against a room 125 | """ 126 | return select("SELECT * FROM v_bookings WHERE room_id = ?", [room_id]) 127 | 128 | def index_page(environ): 129 | return """ 130 | 131 | Room Booking System 132 | 133 | 134 |

Room Booking System

135 | 140 | 141 | 142 | """ 143 | 144 | def webapp(environ, start_response): 145 | """Serve simple pages, based on whether the URL requests 146 | users, rooms or bookings. For now, just serve the Home page 147 | """ 148 | setup_testing_defaults(environ) 149 | 150 | # 151 | # Assume we're going to serve a valid HTML page 152 | # 153 | status = '200 OK' 154 | headers = [('Content-type', 'text/html; charset=utf-8')] 155 | # 156 | # Pick up the first segment on the path and pass 157 | # the rest along. 158 | # 159 | # ie if we're looking for /users/1/bookings, 160 | # param1 will be "users", and the remaining path will 161 | # be "/1/bookings". 162 | # 163 | param1 = shift_path_info(environ) 164 | if param1 == "": 165 | data = index_page(environ) 166 | else: 167 | status = '404 Not Found' 168 | data = "Not Found: %s" % param1 169 | 170 | start_response(status, headers) 171 | return [data.encode("utf-8")] 172 | 173 | def run_website(): 174 | httpd = make_server('', 8000, webapp) 175 | print("Serving on port 8000...") 176 | httpd.serve_forever() 177 | 178 | if __name__ == '__main__': 179 | print("About to create database %s" % DATABASE_FILEPATH) 180 | create_database() 181 | print("About to populate database %s" % DATABASE_FILEPATH) 182 | populate_database() 183 | print("About to run webserver") 184 | run_website() 185 | print("Finished") 186 | -------------------------------------------------------------------------------- /step07/bookings.py: -------------------------------------------------------------------------------- 1 | #!python3 2 | import os, sys 3 | import sqlite3 4 | from wsgiref.util import setup_testing_defaults, shift_path_info 5 | from wsgiref.simple_server import make_server 6 | 7 | # 8 | # Ensure we're using the same database filename throughout. 9 | # It doesn't matter what this is called or where it is: 10 | # sqlite3 will just accept anything. 11 | # 12 | DATABASE_FILEPATH = "bookings.db" 13 | 14 | def create_database(): 15 | """Connect to the database, read the CREATE statements and split 16 | them at the semicolon into individual statements. Once each 17 | statement has been executed, close the connection. 18 | """ 19 | # 20 | # Since we might be re-running this, delete the file and rebuild 21 | # it if necessary. 22 | # 23 | if os.path.exists(DATABASE_FILEPATH): 24 | os.remove(DATABASE_FILEPATH) 25 | 26 | # 27 | # A database cursor the the Python mechanism for running something 28 | # against any database. You create a cursor and then .execute 29 | # SQL statements through it. 30 | # 31 | db = sqlite3.connect(DATABASE_FILEPATH) 32 | q = db.cursor() 33 | 34 | # 35 | # Read all the contents of create.sql in one gulp 36 | # 37 | sql = open("create.sql").read() 38 | # 39 | # Split it into individual statements, breaking on the semicolon 40 | # 41 | statements = sql.split(";") 42 | # 43 | # Execute each of the individual statements against the database 44 | # 45 | for statement in statements: 46 | q.execute(statement) 47 | 48 | # 49 | # Close everything 50 | # 51 | q.close() 52 | db.commit() 53 | db.close() 54 | 55 | def populate_database(): 56 | """Populate the database with some valid test data 57 | """ 58 | db = sqlite3.connect(DATABASE_FILEPATH) 59 | q = db.cursor() 60 | 61 | sql = "INSERT INTO users(id, name, email_address) VALUES(?, ?, ?)" 62 | q.execute(sql, [1, "Mickey Mouse", "mickey.mouse@example.com"]) 63 | q.execute(sql, [2, "Donald Duck", "donald.duck@example.com"]) 64 | q.execute(sql, [3, "Kermit the Frog", None]) 65 | 66 | sql = "INSERT INTO rooms(id, name, location) VALUES(?, ?, ?)" 67 | q.execute(sql, [1, "Room A", "Next to the stairway"]) 68 | q.execute(sql, [2, "Room B", "On the Second Floor"]) 69 | q.execute(sql, [3, "Main Hall", None]) 70 | 71 | # 72 | # Triple-quoted strings can cross lines 73 | # NB the column order doesn't matter if you specify it 74 | # 75 | sql = """ 76 | INSERT INTO 77 | bookings 78 | ( 79 | room_id, user_id, booked_on, booked_from, booked_to 80 | ) 81 | VALUES( 82 | ?, ?, ?, ?, ? 83 | )""" 84 | q.execute(sql, [1, 1, '2014-09-25', '09:00', '10:00']) # Room A (1) booked by Mickey (1) from 9am to 10am on 25th Sep 2014 85 | q.execute(sql, [3, 1, '2015-09-25', None, None]) # Main Hall (3) booked by Mickey (1) from all day on 25th Sep 2014 86 | q.execute(sql, [2, 3, '2014-09-22', '12:00', None]) # Room B (2) booked by Kermit (3) from midday onwards on 22nd Sep 2014 87 | q.execute(sql, [1, 2, '2015-02-14', '09:30', '10:00']) # Room A (1) booked by Donald (2) from 9.30am to 10am on 15th Feb 2014 88 | 89 | q.close() 90 | db.commit() 91 | db.close() 92 | 93 | def select(sql_statement, params=None): 94 | """General-purpose routine to read from the database 95 | """ 96 | if params is None: 97 | params = [] 98 | db = sqlite3.connect(DATABASE_FILEPATH) 99 | db.row_factory = sqlite3.Row 100 | q = db.cursor() 101 | try: 102 | q.execute(sql_statement, params) 103 | return q.fetchall() 104 | finally: 105 | q.close() 106 | db.close() 107 | 108 | def get_users(): 109 | """Get all the users from the database 110 | """ 111 | return select("SELECT * FROM users") 112 | 113 | def get_rooms(): 114 | """Get all the rooms from the database 115 | """ 116 | return select("SELECT * FROM rooms") 117 | 118 | def get_bookings_for_user(user_id): 119 | """Get all the bookings made by a user 120 | """ 121 | return select("SELECT * FROM v_bookings WHERE user_id = ?", [user_id]) 122 | 123 | def get_bookings_for_room(room_id): 124 | """Get all the bookings made against a room 125 | """ 126 | return select("SELECT * FROM v_bookings WHERE room_id = ?", [room_id]) 127 | 128 | def index_page(environ): 129 | return """ 130 | 131 | Room Booking System 132 | 133 | 134 |

Room Booking System

135 | 140 | 141 | 142 | """ 143 | 144 | def users_page(environ): 145 | html = """ 146 | 147 | Room Booking System: Users 148 | 149 | 150 |

Users

151 | 163 | 164 | 165 | """ 166 | return html 167 | 168 | def rooms_page(environ): 169 | html = """ 170 | 171 | Room Booking System: Rooms 172 | 173 | 174 |

Rooms

175 | 187 | 188 | 189 | """ 190 | return html 191 | 192 | def webapp(environ, start_response): 193 | """Serve simple pages, based on whether the URL requests 194 | users, rooms or bookings. For now, just serve the Home page 195 | """ 196 | setup_testing_defaults(environ) 197 | 198 | # 199 | # Assume we're going to serve a valid HTML page 200 | # 201 | status = '200 OK' 202 | headers = [('Content-type', 'text/html; charset=utf-8')] 203 | # 204 | # Pick up the first segment on the path and pass 205 | # the rest along. 206 | # 207 | # ie if we're looking for /users/1/bookings, 208 | # param1 will be "users", and the remaining path will 209 | # be "/1/bookings". 210 | # 211 | param1 = shift_path_info(environ) 212 | if param1 == "": 213 | data = index_page(environ) 214 | elif param1 == "users": 215 | data = users_page(environ) 216 | elif param1 == "rooms": 217 | data = rooms_page(environ) 218 | else: 219 | status = '404 Not Found' 220 | data = "Not Found: %s" % param1 221 | 222 | start_response(status, headers) 223 | return [data.encode("utf-8")] 224 | 225 | def run_website(): 226 | httpd = make_server('', 8000, webapp) 227 | print("Serving on port 8000...") 228 | httpd.serve_forever() 229 | 230 | if __name__ == '__main__': 231 | print("About to create database %s" % DATABASE_FILEPATH) 232 | create_database() 233 | print("About to populate database %s" % DATABASE_FILEPATH) 234 | populate_database() 235 | print("About to run webserver") 236 | run_website() 237 | print("Finished") 238 | -------------------------------------------------------------------------------- /step08/bookings.py: -------------------------------------------------------------------------------- 1 | #!python3 2 | import os, sys 3 | import sqlite3 4 | from wsgiref.util import setup_testing_defaults, shift_path_info 5 | from wsgiref.simple_server import make_server 6 | 7 | # 8 | # Ensure we're using the same database filename throughout. 9 | # It doesn't matter what this is called or where it is: 10 | # sqlite3 will just accept anything. 11 | # 12 | DATABASE_FILEPATH = "bookings.db" 13 | 14 | def create_database(): 15 | """Connect to the database, read the CREATE statements and split 16 | them at the semicolon into individual statements. Once each 17 | statement has been executed, close the connection. 18 | """ 19 | # 20 | # Since we might be re-running this, delete the file and rebuild 21 | # it if necessary. 22 | # 23 | if os.path.exists(DATABASE_FILEPATH): 24 | os.remove(DATABASE_FILEPATH) 25 | 26 | # 27 | # A database cursor the the Python mechanism for running something 28 | # against any database. You create a cursor and then .execute 29 | # SQL statements through it. 30 | # 31 | db = sqlite3.connect(DATABASE_FILEPATH) 32 | q = db.cursor() 33 | 34 | # 35 | # Read all the contents of create.sql in one gulp 36 | # 37 | sql = open("create.sql").read() 38 | # 39 | # Split it into individual statements, breaking on the semicolon 40 | # 41 | statements = sql.split(";") 42 | # 43 | # Execute each of the individual statements against the database 44 | # 45 | for statement in statements: 46 | q.execute(statement) 47 | 48 | # 49 | # Close everything 50 | # 51 | q.close() 52 | db.commit() 53 | db.close() 54 | 55 | def populate_database(): 56 | """Populate the database with some valid test data 57 | """ 58 | db = sqlite3.connect(DATABASE_FILEPATH) 59 | q = db.cursor() 60 | 61 | sql = "INSERT INTO users(id, name, email_address) VALUES(?, ?, ?)" 62 | q.execute(sql, [1, "Mickey Mouse", "mickey.mouse@example.com"]) 63 | q.execute(sql, [2, "Donald Duck", "donald.duck@example.com"]) 64 | q.execute(sql, [3, "Kermit the Frog", None]) 65 | 66 | sql = "INSERT INTO rooms(id, name, location) VALUES(?, ?, ?)" 67 | q.execute(sql, [1, "Room A", "Next to the stairway"]) 68 | q.execute(sql, [2, "Room B", "On the Second Floor"]) 69 | q.execute(sql, [3, "Main Hall", None]) 70 | 71 | # 72 | # Triple-quoted strings can cross lines 73 | # NB the column order doesn't matter if you specify it 74 | # 75 | sql = """ 76 | INSERT INTO 77 | bookings 78 | ( 79 | room_id, user_id, booked_on, booked_from, booked_to 80 | ) 81 | VALUES( 82 | ?, ?, ?, ?, ? 83 | )""" 84 | q.execute(sql, [1, 1, '2014-09-25', '09:00', '10:00']) # Room A (1) booked by Mickey (1) from 9am to 10am on 25th Sep 2014 85 | q.execute(sql, [3, 1, '2015-09-25', None, None]) # Main Hall (3) booked by Mickey (1) from all day on 25th Sep 2014 86 | q.execute(sql, [2, 3, '2014-09-22', '12:00', None]) # Room B (2) booked by Kermit (3) from midday onwards on 22nd Sep 2014 87 | q.execute(sql, [1, 2, '2015-02-14', '09:30', '10:00']) # Room A (1) booked by Donald (2) from 9.30am to 10am on 15th Feb 2014 88 | 89 | q.close() 90 | db.commit() 91 | db.close() 92 | 93 | def select(sql_statement, params=None): 94 | """General-purpose routine to read from the database 95 | """ 96 | if params is None: 97 | params = [] 98 | db = sqlite3.connect(DATABASE_FILEPATH) 99 | db.row_factory = sqlite3.Row 100 | q = db.cursor() 101 | try: 102 | q.execute(sql_statement, params) 103 | return q.fetchall() 104 | finally: 105 | q.close() 106 | db.close() 107 | 108 | def get_users(): 109 | """Get all the users from the database 110 | """ 111 | return select("SELECT * FROM users") 112 | 113 | def get_rooms(): 114 | """Get all the rooms from the database 115 | """ 116 | return select("SELECT * FROM rooms") 117 | 118 | def get_bookings_for_user(user_id): 119 | """Get all the bookings made by a user 120 | """ 121 | return select("SELECT * FROM v_bookings WHERE user_id = ?", [user_id]) 122 | 123 | def get_bookings_for_room(room_id): 124 | """Get all the bookings made against a room 125 | """ 126 | return select("SELECT * FROM v_bookings WHERE room_id = ?", [room_id]) 127 | 128 | def page(title, content): 129 | """Return a complete HTML page with the title as the and <h1> 130 | tags, and the content within the body, after the <h1> 131 | """ 132 | return """ 133 | <html> 134 | <head> 135 | <title>Room Booking System: {title} 136 | 146 | 147 | 148 |

{title}

149 | {content} 150 | 151 | 152 | """.format(title=title, content=content) 153 | 154 | def index_page(environ): 155 | """Provide a list of all the pages 156 | """ 157 | html = """ 158 | 163 | """ 164 | return page("Starting Page", html) 165 | 166 | def users_page(environ): 167 | """Provide a list of all the users, linking to their bookings 168 | """ 169 | html = "" 177 | return page("Users", html) 178 | 179 | def rooms_page(environ): 180 | """Provide a list of all the rooms, linking to their bookings 181 | """ 182 | html = "" 190 | return page("Rooms", html) 191 | 192 | def webapp(environ, start_response): 193 | """Serve simple pages, based on whether the URL requests 194 | users, rooms or bookings. For now, just serve the Home page 195 | """ 196 | setup_testing_defaults(environ) 197 | 198 | # 199 | # Assume we're going to serve a valid HTML page 200 | # 201 | status = '200 OK' 202 | headers = [('Content-type', 'text/html; charset=utf-8')] 203 | # 204 | # Pick up the first segment on the path and pass 205 | # the rest along. 206 | # 207 | # ie if we're looking for /users/1/bookings, 208 | # param1 will be "users", and the remaining path will 209 | # be "/1/bookings". 210 | # 211 | param1 = shift_path_info(environ) 212 | if param1 == "": 213 | data = index_page(environ) 214 | elif param1 == "users": 215 | data = users_page(environ) 216 | elif param1 == "rooms": 217 | data = rooms_page(environ) 218 | else: 219 | status = '404 Not Found' 220 | data = "Not Found: %s" % param1 221 | 222 | start_response(status, headers) 223 | return [data.encode("utf-8")] 224 | 225 | def run_website(): 226 | httpd = make_server('', 8000, webapp) 227 | print("Serving on port 8000...") 228 | httpd.serve_forever() 229 | 230 | if __name__ == '__main__': 231 | print("About to create database %s" % DATABASE_FILEPATH) 232 | create_database() 233 | print("About to populate database %s" % DATABASE_FILEPATH) 234 | populate_database() 235 | print("About to run webserver") 236 | run_website() 237 | print("Finished") 238 | -------------------------------------------------------------------------------- /step09/bookings.py: -------------------------------------------------------------------------------- 1 | #!python3 2 | import os, sys 3 | import sqlite3 4 | from wsgiref.util import setup_testing_defaults, shift_path_info 5 | from wsgiref.simple_server import make_server 6 | 7 | # 8 | # Ensure we're using the same database filename throughout. 9 | # It doesn't matter what this is called or where it is: 10 | # sqlite3 will just accept anything. 11 | # 12 | DATABASE_FILEPATH = "bookings.db" 13 | 14 | def create_database(): 15 | """Connect to the database, read the CREATE statements and split 16 | them at the semicolon into individual statements. Once each 17 | statement has been executed, close the connection. 18 | """ 19 | # 20 | # Since we might be re-running this, delete the file and rebuild 21 | # it if necessary. 22 | # 23 | if os.path.exists(DATABASE_FILEPATH): 24 | os.remove(DATABASE_FILEPATH) 25 | 26 | # 27 | # A database cursor the the Python mechanism for running something 28 | # against any database. You create a cursor and then .execute 29 | # SQL statements through it. 30 | # 31 | db = sqlite3.connect(DATABASE_FILEPATH) 32 | q = db.cursor() 33 | 34 | # 35 | # Read all the contents of create.sql in one gulp 36 | # 37 | sql = open("create.sql").read() 38 | # 39 | # Split it into individual statements, breaking on the semicolon 40 | # 41 | statements = sql.split(";") 42 | # 43 | # Execute each of the individual statements against the database 44 | # 45 | for statement in statements: 46 | q.execute(statement) 47 | 48 | # 49 | # Close everything 50 | # 51 | q.close() 52 | db.commit() 53 | db.close() 54 | 55 | def populate_database(): 56 | """Populate the database with some valid test data 57 | """ 58 | db = sqlite3.connect(DATABASE_FILEPATH) 59 | q = db.cursor() 60 | 61 | sql = "INSERT INTO users(id, name, email_address) VALUES(?, ?, ?)" 62 | q.execute(sql, [1, "Mickey Mouse", "mickey.mouse@example.com"]) 63 | q.execute(sql, [2, "Donald Duck", "donald.duck@example.com"]) 64 | q.execute(sql, [3, "Kermit the Frog", None]) 65 | 66 | sql = "INSERT INTO rooms(id, name, location) VALUES(?, ?, ?)" 67 | q.execute(sql, [1, "Room A", "Next to the stairway"]) 68 | q.execute(sql, [2, "Room B", "On the Second Floor"]) 69 | q.execute(sql, [3, "Main Hall", None]) 70 | 71 | # 72 | # Triple-quoted strings can cross lines 73 | # NB the column order doesn't matter if you specify it 74 | # 75 | sql = """ 76 | INSERT INTO 77 | bookings 78 | ( 79 | room_id, user_id, booked_on, booked_from, booked_to 80 | ) 81 | VALUES( 82 | ?, ?, ?, ?, ? 83 | )""" 84 | q.execute(sql, [1, 1, '2014-09-25', '09:00', '10:00']) # Room A (1) booked by Mickey (1) from 9am to 10am on 25th Sep 2014 85 | q.execute(sql, [3, 1, '2015-09-25', None, None]) # Main Hall (3) booked by Mickey (1) from all day on 25th Sep 2014 86 | q.execute(sql, [2, 3, '2014-09-22', '12:00', None]) # Room B (2) booked by Kermit (3) from midday onwards on 22nd Sep 2014 87 | q.execute(sql, [1, 2, '2015-02-14', '09:30', '10:00']) # Room A (1) booked by Donald (2) from 9.30am to 10am on 15th Feb 2014 88 | 89 | q.close() 90 | db.commit() 91 | db.close() 92 | 93 | def select(sql_statement, params=None): 94 | """General-purpose routine to read from the database 95 | """ 96 | if params is None: 97 | params = [] 98 | db = sqlite3.connect(DATABASE_FILEPATH) 99 | db.row_factory = sqlite3.Row 100 | q = db.cursor() 101 | try: 102 | q.execute(sql_statement, params) 103 | return q.fetchall() 104 | finally: 105 | q.close() 106 | db.close() 107 | 108 | def get_users(): 109 | """Get all the users from the database 110 | """ 111 | return select("SELECT * FROM users") 112 | 113 | def get_rooms(): 114 | """Get all the rooms from the database 115 | """ 116 | return select("SELECT * FROM rooms") 117 | 118 | def get_bookings_for_user(user_id): 119 | """Get all the bookings made by a user 120 | """ 121 | return select("SELECT * FROM v_bookings WHERE user_id = ?", [user_id]) 122 | 123 | def get_bookings_for_room(room_id): 124 | """Get all the bookings made against a room 125 | """ 126 | return select("SELECT * FROM v_bookings WHERE room_id = ?", [room_id]) 127 | 128 | def page(title, content): 129 | """Return a complete HTML page with the title as the and <h1> 130 | tags, and the content within the body, after the <h1> 131 | """ 132 | return """ 133 | <html> 134 | <head> 135 | <title>Room Booking System: {title} 136 | 151 | 152 | 153 |

{title}

154 | {content} 155 | 156 | 157 | """.format(title=title, content=content) 158 | 159 | def index_page(environ): 160 | """Provide a list of all the pages 161 | """ 162 | html = """ 163 | 168 | """ 169 | return page("Starting Page", html) 170 | 171 | def users_page(environ): 172 | """Provide a list of all the users, linking to their bookings 173 | """ 174 | html = "" 182 | return page("Users", html) 183 | 184 | def rooms_page(environ): 185 | """Provide a list of all the rooms, linking to their bookings 186 | """ 187 | html = "" 195 | return page("Rooms", html) 196 | 197 | def bookings_user_page(environ): 198 | """Provide a list of bookings by user, showing room and date/time 199 | """ 200 | user_id = int(shift_path_info(environ)) 201 | html = "" 202 | html += "" 203 | for booking in get_bookings_for_user(user_id): 204 | html += "".format( 205 | room_name=booking['room_name'], 206 | booked_on=booking['booked_on'], 207 | booked_from=booking['booked_from'] or "", 208 | booked_to=booking['booked_to'] or "" 209 | ) 210 | html += "
RoomDateTimes
{room_name}{booked_on}{booked_from} - {booked_to}
" 211 | return page("Bookings for user %d" % user_id, html) 212 | 213 | def bookings_room_page(environ): 214 | """Provide a list of bookings by room, showing user and date/time 215 | """ 216 | room_id = int(shift_path_info(environ)) 217 | html = "" 218 | html += "" 219 | for booking in get_bookings_for_room(room_id): 220 | html += "".format( 221 | user_name=booking['user_name'], 222 | booked_on=booking['booked_on'], 223 | booked_from=booking['booked_from'] or "", 224 | booked_to=booking['booked_to'] or "" 225 | ) 226 | html += "
UserDateTimes
{user_name}{booked_on}{booked_from} - {booked_to}
" 227 | return page("Bookings for room %d" % room_id, html) 228 | 229 | def bookings_page(environ): 230 | """Provide a list of all bookings by a user or room, showing 231 | the other thing (room or user) and the date/time 232 | """ 233 | category = shift_path_info(environ) 234 | if category == "user": 235 | return bookings_user_page(environ) 236 | elif category == "room": 237 | return bookings_room_page(environ) 238 | else: 239 | return "No such booking category" 240 | 241 | def webapp(environ, start_response): 242 | """Serve simple pages, based on whether the URL requests 243 | users, rooms or bookings. For now, just serve the Home page 244 | """ 245 | setup_testing_defaults(environ) 246 | 247 | # 248 | # Assume we're going to serve a valid HTML page 249 | # 250 | status = '200 OK' 251 | headers = [('Content-type', 'text/html; charset=utf-8')] 252 | # 253 | # Pick up the first segment on the path and pass 254 | # the rest along. 255 | # 256 | # ie if we're looking for /users/1/bookings, 257 | # param1 will be "users", and the remaining path will 258 | # be "/1/bookings". 259 | # 260 | param1 = shift_path_info(environ) 261 | if param1 == "": 262 | data = index_page(environ) 263 | elif param1 == "users": 264 | data = users_page(environ) 265 | elif param1 == "rooms": 266 | data = rooms_page(environ) 267 | elif param1 == "bookings": 268 | data = bookings_page(environ) 269 | else: 270 | status = '404 Not Found' 271 | data = "Not Found: %s" % param1 272 | 273 | start_response(status, headers) 274 | return [data.encode("utf-8")] 275 | 276 | def run_website(): 277 | httpd = make_server('', 8000, webapp) 278 | print("Serving on port 8000...") 279 | httpd.serve_forever() 280 | 281 | if __name__ == '__main__': 282 | print("About to create database %s" % DATABASE_FILEPATH) 283 | create_database() 284 | print("About to populate database %s" % DATABASE_FILEPATH) 285 | populate_database() 286 | print("About to run webserver") 287 | run_website() 288 | print("Finished") 289 | -------------------------------------------------------------------------------- /step10/bookings.py: -------------------------------------------------------------------------------- 1 | #!python3 2 | import os, sys 3 | import cgi 4 | import sqlite3 5 | from wsgiref.util import setup_testing_defaults, shift_path_info 6 | from wsgiref.simple_server import make_server 7 | 8 | # 9 | # Ensure we're using the same database filename throughout. 10 | # It doesn't matter what this is called or where it is: 11 | # sqlite3 will just accept anything. 12 | # 13 | DATABASE_FILEPATH = "bookings.db" 14 | 15 | def create_database(): 16 | """Connect to the database, read the CREATE statements and split 17 | them at the semicolon into individual statements. Once each 18 | statement has been executed, close the connection. 19 | """ 20 | # 21 | # Since we might be re-running this, delete the file and rebuild 22 | # it if necessary. 23 | # 24 | if os.path.exists(DATABASE_FILEPATH): 25 | os.remove(DATABASE_FILEPATH) 26 | 27 | # 28 | # A database cursor the the Python mechanism for running something 29 | # against any database. You create a cursor and then .execute 30 | # SQL statements through it. 31 | # 32 | db = sqlite3.connect(DATABASE_FILEPATH) 33 | q = db.cursor() 34 | 35 | # 36 | # Read all the contents of create.sql in one gulp 37 | # 38 | sql = open("create.sql").read() 39 | # 40 | # Split it into individual statements, breaking on the semicolon 41 | # 42 | statements = sql.split(";") 43 | # 44 | # Execute each of the individual statements against the database 45 | # 46 | for statement in statements: 47 | q.execute(statement) 48 | 49 | # 50 | # Close everything 51 | # 52 | q.close() 53 | db.commit() 54 | db.close() 55 | 56 | def populate_database(): 57 | """Populate the database with some valid test data 58 | """ 59 | db = sqlite3.connect(DATABASE_FILEPATH) 60 | q = db.cursor() 61 | 62 | sql = "INSERT INTO users(id, name, email_address) VALUES(?, ?, ?)" 63 | q.execute(sql, [1, "Mickey Mouse", "mickey.mouse@example.com"]) 64 | q.execute(sql, [2, "Donald Duck", "donald.duck@example.com"]) 65 | q.execute(sql, [3, "Kermit the Frog", None]) 66 | 67 | sql = "INSERT INTO rooms(id, name, location) VALUES(?, ?, ?)" 68 | q.execute(sql, [1, "Room A", "Next to the stairway"]) 69 | q.execute(sql, [2, "Room B", "On the Second Floor"]) 70 | q.execute(sql, [3, "Main Hall", None]) 71 | 72 | # 73 | # Triple-quoted strings can cross lines 74 | # NB the column order doesn't matter if you specify it 75 | # 76 | sql = """ 77 | INSERT INTO 78 | bookings 79 | ( 80 | room_id, user_id, booked_on, booked_from, booked_to 81 | ) 82 | VALUES( 83 | ?, ?, ?, ?, ? 84 | )""" 85 | q.execute(sql, [1, 1, '2014-09-25', '09:00', '10:00']) # Room A (1) booked by Mickey (1) from 9am to 10am on 25th Sep 2014 86 | q.execute(sql, [3, 1, '2015-09-25', None, None]) # Main Hall (3) booked by Mickey (1) from all day on 25th Sep 2014 87 | q.execute(sql, [2, 3, '2014-09-22', '12:00', None]) # Room B (2) booked by Kermit (3) from midday onwards on 22nd Sep 2014 88 | q.execute(sql, [1, 2, '2015-02-14', '09:30', '10:00']) # Room A (1) booked by Donald (2) from 9.30am to 10am on 15th Feb 2014 89 | 90 | q.close() 91 | db.commit() 92 | db.close() 93 | 94 | def select(sql_statement, params=None): 95 | """General-purpose routine to read from the database 96 | """ 97 | if params is None: 98 | params = [] 99 | db = sqlite3.connect(DATABASE_FILEPATH) 100 | db.row_factory = sqlite3.Row 101 | q = db.cursor() 102 | try: 103 | q.execute(sql_statement, params) 104 | return q.fetchall() 105 | finally: 106 | q.close() 107 | db.close() 108 | 109 | def execute(sql_statement, params=None): 110 | """General-purpose routine to write to the database 111 | """ 112 | if params is None: 113 | params = [] 114 | db = sqlite3.connect(DATABASE_FILEPATH) 115 | q = db.cursor() 116 | try: 117 | q.execute(sql_statement, params) 118 | db.commit() 119 | finally: 120 | q.close() 121 | db.close() 122 | 123 | def get_users(): 124 | """Get all the users from the database 125 | """ 126 | return select("SELECT * FROM users") 127 | 128 | def get_rooms(): 129 | """Get all the rooms from the database 130 | """ 131 | return select("SELECT * FROM rooms") 132 | 133 | def get_bookings_for_user(user_id): 134 | """Get all the bookings made by a user 135 | """ 136 | return select("SELECT * FROM v_bookings WHERE user_id = ?", [user_id]) 137 | 138 | def get_bookings_for_room(room_id): 139 | """Get all the bookings made against a room 140 | """ 141 | return select("SELECT * FROM v_bookings WHERE room_id = ?", [room_id]) 142 | 143 | def add_user_to_database(name, email_address): 144 | """Add a user to the database 145 | """ 146 | execute( 147 | "INSERT INTO users(name, email_address) VALUES (?, ?)", 148 | [name, email_address] 149 | ) 150 | 151 | def page(title, content): 152 | """Return a complete HTML page with the title as the and <h1> 153 | tags, and the content within the body, after the <h1> 154 | """ 155 | return """ 156 | <html> 157 | <head> 158 | <title>Room Booking System: {title} 159 | 174 | 175 | 176 |

{title}

177 | {content} 178 | 179 | 180 | """.format(title=title, content=content) 181 | 182 | def index_page(environ): 183 | """Provide a list of all the pages 184 | """ 185 | html = """ 186 | 191 | """ 192 | return page("Starting Page", html) 193 | 194 | def users_page(environ): 195 | """Provide a list of all the users, linking to their bookings 196 | """ 197 | html = "" 205 | html += "
" 206 | html += """
207 |   208 |   209 | 210 |
""" 211 | return page("Users", html) 212 | 213 | def rooms_page(environ): 214 | """Provide a list of all the rooms, linking to their bookings 215 | """ 216 | html = "" 224 | return page("Rooms", html) 225 | 226 | def bookings_user_page(environ): 227 | """Provide a list of bookings by user, showing room and date/time 228 | """ 229 | user_id = int(shift_path_info(environ)) 230 | html = "" 231 | html += "" 232 | for booking in get_bookings_for_user(user_id): 233 | html += "".format( 234 | room_name=booking['room_name'], 235 | booked_on=booking['booked_on'], 236 | booked_from=booking['booked_from'] or "", 237 | booked_to=booking['booked_to'] or "" 238 | ) 239 | html += "
RoomDateTimes
{room_name}{booked_on}{booked_from} - {booked_to}
" 240 | return page("Bookings for user %d" % user_id, html) 241 | 242 | def bookings_room_page(environ): 243 | """Provide a list of bookings by room, showing user and date/time 244 | """ 245 | room_id = int(shift_path_info(environ)) 246 | html = "" 247 | html += "" 248 | for booking in get_bookings_for_room(room_id): 249 | html += "".format( 250 | user_name=booking['user_name'], 251 | booked_on=booking['booked_on'], 252 | booked_from=booking['booked_from'] or "", 253 | booked_to=booking['booked_to'] or "" 254 | ) 255 | html += "
UserDateTimes
{user_name}{booked_on}{booked_from} - {booked_to}
" 256 | return page("Bookings for room %d" % room_id, html) 257 | 258 | def bookings_page(environ): 259 | """Provide a list of all bookings by a user or room, showing 260 | the other thing (room or user) and the date/time 261 | """ 262 | category = shift_path_info(environ) 263 | if category == "user": 264 | return bookings_user_page(environ) 265 | elif category == "room": 266 | return bookings_room_page(environ) 267 | else: 268 | return "No such booking category" 269 | 270 | def add_user(environ): 271 | print("About to add_user") 272 | form = cgi.FieldStorage(fp=environ['wsgi.input'], environ=environ.copy(), keep_blank_values=True) 273 | add_user_to_database(form.getfirst("name"), form.getfirst('email_address', "")) 274 | print("Added to db") 275 | 276 | def webapp(environ, start_response): 277 | """Serve simple pages, based on whether the URL requests 278 | users, rooms or bookings. For now, just serve the Home page 279 | """ 280 | setup_testing_defaults(environ) 281 | 282 | # 283 | # Assume we're going to serve a valid HTML page 284 | # 285 | status = '200 OK' 286 | headers = [('Content-type', 'text/html; charset=utf-8')] 287 | # 288 | # Pick up the first segment on the path and pass 289 | # the rest along. 290 | # 291 | # ie if we're looking for /users/1/bookings, 292 | # param1 will be "users", and the remaining path will 293 | # be "/1/bookings". 294 | # 295 | param1 = shift_path_info(environ) 296 | if param1 == "": 297 | data = index_page(environ) 298 | elif param1 == "users": 299 | data = users_page(environ) 300 | elif param1 == "rooms": 301 | data = rooms_page(environ) 302 | elif param1 == "bookings": 303 | data = bookings_page(environ) 304 | elif param1 == "add-user": 305 | print("Found add-user") 306 | add_user(environ) 307 | print("User added") 308 | status = "301 Redirect" 309 | headers.append(("Location", "/users")) 310 | data = "" 311 | else: 312 | status = '404 Not Found' 313 | data = "Not Found: %s" % param1 314 | 315 | start_response(status, headers) 316 | return [data.encode("utf-8")] 317 | 318 | def run_website(): 319 | httpd = make_server('', 8000, webapp) 320 | print("Serving on port 8000...") 321 | httpd.serve_forever() 322 | 323 | if __name__ == '__main__': 324 | print("About to create database %s" % DATABASE_FILEPATH) 325 | create_database() 326 | print("About to populate database %s" % DATABASE_FILEPATH) 327 | populate_database() 328 | print("About to run webserver") 329 | run_website() 330 | print("Finished") 331 | -------------------------------------------------------------------------------- /step11/bookings.py: -------------------------------------------------------------------------------- 1 | #!python3 2 | import os, sys 3 | import cgi 4 | import sqlite3 5 | from wsgiref.util import setup_testing_defaults, shift_path_info 6 | from wsgiref.simple_server import make_server 7 | 8 | # 9 | # Ensure we're using the same database filename throughout. 10 | # It doesn't matter what this is called or where it is: 11 | # sqlite3 will just accept anything. 12 | # 13 | DATABASE_FILEPATH = "bookings.db" 14 | 15 | def create_database(): 16 | """Connect to the database, read the CREATE statements and split 17 | them at the semicolon into individual statements. Once each 18 | statement has been executed, close the connection. 19 | """ 20 | # 21 | # Since we might be re-running this, delete the file and rebuild 22 | # it if necessary. 23 | # 24 | if os.path.exists(DATABASE_FILEPATH): 25 | os.remove(DATABASE_FILEPATH) 26 | 27 | # 28 | # A database cursor the the Python mechanism for running something 29 | # against any database. You create a cursor and then .execute 30 | # SQL statements through it. 31 | # 32 | db = sqlite3.connect(DATABASE_FILEPATH) 33 | q = db.cursor() 34 | 35 | # 36 | # Read all the contents of create.sql in one gulp 37 | # 38 | sql = open("create.sql").read() 39 | # 40 | # Split it into individual statements, breaking on the semicolon 41 | # 42 | statements = sql.split(";") 43 | # 44 | # Execute each of the individual statements against the database 45 | # 46 | for statement in statements: 47 | q.execute(statement) 48 | 49 | # 50 | # Close everything 51 | # 52 | q.close() 53 | db.commit() 54 | db.close() 55 | 56 | def populate_database(): 57 | """Populate the database with some valid test data 58 | """ 59 | db = sqlite3.connect(DATABASE_FILEPATH) 60 | q = db.cursor() 61 | 62 | sql = "INSERT INTO users(id, name, email_address) VALUES(?, ?, ?)" 63 | q.execute(sql, [1, "Mickey Mouse", "mickey.mouse@example.com"]) 64 | q.execute(sql, [2, "Donald Duck", "donald.duck@example.com"]) 65 | q.execute(sql, [3, "Kermit the Frog", None]) 66 | 67 | sql = "INSERT INTO rooms(id, name, location) VALUES(?, ?, ?)" 68 | q.execute(sql, [1, "Room A", "Next to the stairway"]) 69 | q.execute(sql, [2, "Room B", "On the Second Floor"]) 70 | q.execute(sql, [3, "Main Hall", None]) 71 | 72 | # 73 | # Triple-quoted strings can cross lines 74 | # NB the column order doesn't matter if you specify it 75 | # 76 | sql = """ 77 | INSERT INTO 78 | bookings 79 | ( 80 | room_id, user_id, booked_on, booked_from, booked_to 81 | ) 82 | VALUES( 83 | ?, ?, ?, ?, ? 84 | )""" 85 | q.execute(sql, [1, 1, '2014-09-25', '09:00', '10:00']) # Room A (1) booked by Mickey (1) from 9am to 10am on 25th Sep 2014 86 | q.execute(sql, [3, 1, '2015-09-25', None, None]) # Main Hall (3) booked by Mickey (1) from all day on 25th Sep 2014 87 | q.execute(sql, [2, 3, '2014-09-22', '12:00', None]) # Room B (2) booked by Kermit (3) from midday onwards on 22nd Sep 2014 88 | q.execute(sql, [1, 2, '2015-02-14', '09:30', '10:00']) # Room A (1) booked by Donald (2) from 9.30am to 10am on 15th Feb 2014 89 | 90 | q.close() 91 | db.commit() 92 | db.close() 93 | 94 | def select(sql_statement, params=None): 95 | """General-purpose routine to read from the database 96 | """ 97 | if params is None: 98 | params = [] 99 | db = sqlite3.connect(DATABASE_FILEPATH) 100 | db.row_factory = sqlite3.Row 101 | q = db.cursor() 102 | try: 103 | q.execute(sql_statement, params) 104 | return q.fetchall() 105 | finally: 106 | q.close() 107 | db.close() 108 | 109 | def execute(sql_statement, params=None): 110 | """General-purpose routine to write to the database 111 | """ 112 | if params is None: 113 | params = [] 114 | db = sqlite3.connect(DATABASE_FILEPATH) 115 | q = db.cursor() 116 | try: 117 | q.execute(sql_statement, params) 118 | db.commit() 119 | finally: 120 | q.close() 121 | db.close() 122 | 123 | def get_users(): 124 | """Get all the users from the database 125 | """ 126 | return select("SELECT * FROM users") 127 | 128 | def get_rooms(): 129 | """Get all the rooms from the database 130 | """ 131 | return select("SELECT * FROM rooms") 132 | 133 | def get_bookings_for_user(user_id): 134 | """Get all the bookings made by a user 135 | """ 136 | return select("SELECT * FROM v_bookings WHERE user_id = ?", [user_id]) 137 | 138 | def get_bookings_for_room(room_id): 139 | """Get all the bookings made against a room 140 | """ 141 | return select("SELECT * FROM v_bookings WHERE room_id = ?", [room_id]) 142 | 143 | def add_user_to_database(name, email_address): 144 | """Add a user to the database 145 | """ 146 | print("%r, %r" % (name, email_address)) 147 | execute( 148 | "INSERT INTO users(name, email_address) VALUES (?, ?)", 149 | [name, email_address] 150 | ) 151 | 152 | def add_room_to_database(name, location): 153 | """Add a user to the database 154 | """ 155 | execute( 156 | "INSERT INTO rooms(name, location) VALUES (?, ?)", 157 | [name, location] 158 | ) 159 | 160 | def page(title, content): 161 | """Return a complete HTML page with the title as the and <h1> 162 | tags, and the content within the body, after the <h1> 163 | """ 164 | return """ 165 | <html> 166 | <head> 167 | <title>Room Booking System: {title} 168 | 183 | 184 | 185 |

{title}

186 | {content} 187 | 188 | 189 | """.format(title=title, content=content) 190 | 191 | def index_page(environ): 192 | """Provide a list of all the pages 193 | """ 194 | html = """ 195 | 200 | """ 201 | return page("Starting Page", html) 202 | 203 | def users_page(environ): 204 | """Provide a list of all the users, linking to their bookings 205 | """ 206 | html = "" 214 | html += "
" 215 | html += """
216 |   217 |   218 | 219 |
""" 220 | return page("Users", html) 221 | 222 | def rooms_page(environ): 223 | """Provide a list of all the rooms, linking to their bookings 224 | """ 225 | html = "" 233 | html += "
" 234 | html += """
235 |   236 |   237 | 238 |
""" 239 | return page("Rooms", html) 240 | 241 | def bookings_user_page(environ): 242 | """Provide a list of bookings by user, showing room and date/time 243 | """ 244 | user_id = int(shift_path_info(environ)) 245 | html = "" 246 | html += "" 247 | for booking in get_bookings_for_user(user_id): 248 | html += "".format( 249 | room_name=booking['room_name'], 250 | booked_on=booking['booked_on'], 251 | booked_from=booking['booked_from'] or "", 252 | booked_to=booking['booked_to'] or "" 253 | ) 254 | html += "
RoomDateTimes
{room_name}{booked_on}{booked_from} - {booked_to}
" 255 | return page("Bookings for user %d" % user_id, html) 256 | 257 | def bookings_room_page(environ): 258 | """Provide a list of bookings by room, showing user and date/time 259 | """ 260 | room_id = int(shift_path_info(environ)) 261 | html = "" 262 | html += "" 263 | for booking in get_bookings_for_room(room_id): 264 | html += "".format( 265 | user_name=booking['user_name'], 266 | booked_on=booking['booked_on'], 267 | booked_from=booking['booked_from'] or "", 268 | booked_to=booking['booked_to'] or "" 269 | ) 270 | html += "
UserDateTimes
{user_name}{booked_on}{booked_from} - {booked_to}
" 271 | return page("Bookings for room %d" % room_id, html) 272 | 273 | def bookings_page(environ): 274 | """Provide a list of all bookings by a user or room, showing 275 | the other thing (room or user) and the date/time 276 | """ 277 | category = shift_path_info(environ) 278 | if category == "user": 279 | return bookings_user_page(environ) 280 | elif category == "room": 281 | return bookings_room_page(environ) 282 | else: 283 | return "No such booking category" 284 | 285 | def add_user(environ): 286 | form = cgi.FieldStorage(fp=environ['wsgi.input'], environ=environ.copy(), keep_blank_values=True) 287 | add_user_to_database(form.getfirst("name"), form.getfirst('email_address', "")) 288 | 289 | def add_room(environ): 290 | form = cgi.FieldStorage(fp=environ['wsgi.input'], environ=environ.copy(), keep_blank_values=True) 291 | add_room_to_database(form.getfirst("name"), form.getfirst('location', None)) 292 | 293 | def webapp(environ, start_response): 294 | """Serve simple pages, based on whether the URL requests 295 | users, rooms or bookings. For now, just serve the Home page 296 | """ 297 | setup_testing_defaults(environ) 298 | 299 | # 300 | # Assume we're going to serve a valid HTML page 301 | # 302 | status = '200 OK' 303 | headers = [('Content-type', 'text/html; charset=utf-8')] 304 | # 305 | # Pick up the first segment on the path and pass 306 | # the rest along. 307 | # 308 | # ie if we're looking for /users/1/bookings, 309 | # param1 will be "users", and the remaining path will 310 | # be "/1/bookings". 311 | # 312 | param1 = shift_path_info(environ) 313 | if param1 == "": 314 | data = index_page(environ) 315 | elif param1 == "users": 316 | data = users_page(environ) 317 | elif param1 == "rooms": 318 | data = rooms_page(environ) 319 | elif param1 == "bookings": 320 | data = bookings_page(environ) 321 | elif param1 == "add-user": 322 | add_user(environ) 323 | status = "301 Redirect" 324 | headers.append(("Location", "/users")) 325 | data = "" 326 | elif param1 == "add-room": 327 | add_room(environ) 328 | status = "301 Redirect" 329 | headers.append(("Location", "/rooms")) 330 | data = "" 331 | else: 332 | status = '404 Not Found' 333 | data = "Not Found: %s" % param1 334 | 335 | start_response(status, headers) 336 | return [data.encode("utf-8")] 337 | 338 | def run_website(): 339 | httpd = make_server('', 8000, webapp) 340 | print("Serving on port 8000...") 341 | httpd.serve_forever() 342 | 343 | if __name__ == '__main__': 344 | print("About to create database %s" % DATABASE_FILEPATH) 345 | create_database() 346 | print("About to populate database %s" % DATABASE_FILEPATH) 347 | populate_database() 348 | print("About to run webserver") 349 | run_website() 350 | print("Finished") 351 | -------------------------------------------------------------------------------- /step12/bookings.py: -------------------------------------------------------------------------------- 1 | #!python3 2 | import os, sys 3 | import cgi 4 | import datetime 5 | import sqlite3 6 | from wsgiref.util import setup_testing_defaults, shift_path_info 7 | from wsgiref.simple_server import make_server 8 | 9 | # 10 | # Ensure we're using the same database filename throughout. 11 | # It doesn't matter what this is called or where it is: 12 | # sqlite3 will just accept anything. 13 | # 14 | DATABASE_FILEPATH = "bookings.db" 15 | 16 | def create_database(): 17 | """Connect to the database, read the CREATE statements and split 18 | them at the semicolon into individual statements. Once each 19 | statement has been executed, close the connection. 20 | """ 21 | # 22 | # Since we might be re-running this, delete the file and rebuild 23 | # it if necessary. 24 | # 25 | if os.path.exists(DATABASE_FILEPATH): 26 | os.remove(DATABASE_FILEPATH) 27 | 28 | # 29 | # A database cursor the the Python mechanism for running something 30 | # against any database. You create a cursor and then .execute 31 | # SQL statements through it. 32 | # 33 | db = sqlite3.connect(DATABASE_FILEPATH) 34 | q = db.cursor() 35 | 36 | # 37 | # Read all the contents of create.sql in one gulp 38 | # 39 | sql = open("create.sql").read() 40 | # 41 | # Split it into individual statements, breaking on the semicolon 42 | # 43 | statements = sql.split(";") 44 | # 45 | # Execute each of the individual statements against the database 46 | # 47 | for statement in statements: 48 | q.execute(statement) 49 | 50 | # 51 | # Close everything 52 | # 53 | q.close() 54 | db.commit() 55 | db.close() 56 | 57 | def populate_database(): 58 | """Populate the database with some valid test data 59 | """ 60 | db = sqlite3.connect(DATABASE_FILEPATH) 61 | q = db.cursor() 62 | 63 | sql = "INSERT INTO users(id, name, email_address) VALUES(?, ?, ?)" 64 | q.execute(sql, [1, "Mickey Mouse", "mickey.mouse@example.com"]) 65 | q.execute(sql, [2, "Donald Duck", "donald.duck@example.com"]) 66 | q.execute(sql, [3, "Kermit the Frog", None]) 67 | 68 | sql = "INSERT INTO rooms(id, name, location) VALUES(?, ?, ?)" 69 | q.execute(sql, [1, "Room A", "Next to the stairway"]) 70 | q.execute(sql, [2, "Room B", "On the Second Floor"]) 71 | q.execute(sql, [3, "Main Hall", None]) 72 | 73 | # 74 | # Triple-quoted strings can cross lines 75 | # NB the column order doesn't matter if you specify it 76 | # 77 | sql = """ 78 | INSERT INTO 79 | bookings 80 | ( 81 | room_id, user_id, booked_on, booked_from, booked_to 82 | ) 83 | VALUES( 84 | ?, ?, ?, ?, ? 85 | )""" 86 | q.execute(sql, [1, 1, '2014-09-25', '09:00', '10:00']) # Room A (1) booked by Mickey (1) from 9am to 10am on 25th Sep 2014 87 | q.execute(sql, [3, 1, '2015-09-25', None, None]) # Main Hall (3) booked by Mickey (1) from all day on 25th Sep 2014 88 | q.execute(sql, [2, 3, '2014-09-22', '12:00', None]) # Room B (2) booked by Kermit (3) from midday onwards on 22nd Sep 2014 89 | q.execute(sql, [1, 2, '2015-02-14', '09:30', '10:00']) # Room A (1) booked by Donald (2) from 9.30am to 10am on 15th Feb 2014 90 | 91 | q.close() 92 | db.commit() 93 | db.close() 94 | 95 | def select(sql_statement, params=None): 96 | """General-purpose routine to read from the database 97 | """ 98 | if params is None: 99 | params = [] 100 | db = sqlite3.connect(DATABASE_FILEPATH) 101 | db.row_factory = sqlite3.Row 102 | q = db.cursor() 103 | try: 104 | q.execute(sql_statement, params) 105 | return q.fetchall() 106 | finally: 107 | q.close() 108 | db.close() 109 | 110 | def execute(sql_statement, params=None): 111 | """General-purpose routine to write to the database 112 | """ 113 | if params is None: 114 | params = [] 115 | db = sqlite3.connect(DATABASE_FILEPATH) 116 | q = db.cursor() 117 | try: 118 | q.execute(sql_statement, params) 119 | db.commit() 120 | finally: 121 | q.close() 122 | db.close() 123 | 124 | def get_user(user_id): 125 | """Return the user matching user_id 126 | """ 127 | for user in select("SELECT * FROM users WHERE id = ?", [user_id]): 128 | return user 129 | 130 | def get_room(room_id): 131 | """Return the room matching room_id 132 | """ 133 | for room in select("SELECT * FROM rooms WHERE id = ?", [room_id]): 134 | return room 135 | 136 | def get_users(): 137 | """Get all the users from the database 138 | """ 139 | return select("SELECT * FROM users") 140 | 141 | def get_rooms(): 142 | """Get all the rooms from the database 143 | """ 144 | return select("SELECT * FROM rooms") 145 | 146 | def get_bookings_for_user(user_id): 147 | """Get all the bookings made by a user 148 | """ 149 | return select("SELECT * FROM v_bookings WHERE user_id = ?", [user_id]) 150 | 151 | def get_bookings_for_room(room_id): 152 | """Get all the bookings made against a room 153 | """ 154 | return select("SELECT * FROM v_bookings WHERE room_id = ?", [room_id]) 155 | 156 | def add_user_to_database(name, email_address): 157 | """Add a user to the database 158 | """ 159 | print("%r, %r" % (name, email_address)) 160 | execute( 161 | "INSERT INTO users(name, email_address) VALUES (?, ?)", 162 | [name, email_address] 163 | ) 164 | 165 | def add_room_to_database(name, location): 166 | """Add a user to the database 167 | """ 168 | execute( 169 | "INSERT INTO rooms(name, location) VALUES (?, ?)", 170 | [name, location] 171 | ) 172 | 173 | def add_booking_to_database(user_id, room_id, booked_on, booked_from=None, booked_to=None): 174 | """Add a booking to the database 175 | """ 176 | execute( 177 | """ 178 | INSERT INTO bookings(user_id, room_id, booked_on, booked_from, booked_to) 179 | VALUES(?, ?, ?, ?, ?) 180 | """, 181 | [user_id, room_id, booked_on, booked_from, booked_to] 182 | ) 183 | 184 | def page(title, content): 185 | """Return a complete HTML page with the title as the and <h1> 186 | tags, and the content within the body, after the <h1> 187 | """ 188 | return """ 189 | <html> 190 | <head> 191 | <title>Room Booking System: {title} 192 | 207 | 208 | 209 |

{title}

210 | {content} 211 | 212 | 213 | """.format(title=title, content=content) 214 | 215 | def index_page(environ): 216 | """Provide a list of all the pages 217 | """ 218 | html = """ 219 | 224 | """ 225 | return page("Starting Page", html) 226 | 227 | def users_page(environ): 228 | """Provide a list of all the users, linking to their bookings 229 | """ 230 | html = "" 238 | html += "
" 239 | html += """
240 |   241 |   242 | 243 |
""" 244 | return page("Users", html) 245 | 246 | def rooms_page(environ): 247 | """Provide a list of all the rooms, linking to their bookings 248 | """ 249 | html = "" 257 | html += "
" 258 | html += """
259 |   260 |   261 | 262 |
""" 263 | return page("Rooms", html) 264 | 265 | def bookings_user_page(environ): 266 | """Provide a list of bookings by user, showing room and date/time 267 | """ 268 | user_id = int(shift_path_info(environ)) 269 | user = get_user(user_id) 270 | html = "" 271 | html += "" 272 | for booking in get_bookings_for_user(user_id): 273 | html += "".format( 274 | room_name=booking['room_name'], 275 | booked_on=booking['booked_on'], 276 | booked_from=booking['booked_from'] or "", 277 | booked_to=booking['booked_to'] or "" 278 | ) 279 | html += "
RoomDateTimes
{room_name}{booked_on}{booked_from} - {booked_to}
" 280 | html += "
" 281 | html += '
' 282 | html += ''.format(user_id=user_id) 283 | html += ' ' 287 | html += ' | ' 288 | html += ' '.format(today=datetime.date.today()) 289 | html += '  ' 290 | html += '  ' 291 | html += '
' 292 | return page("Bookings for %s" % user['name'], html) 293 | 294 | def bookings_room_page(environ): 295 | """Provide a list of bookings by room, showing user and date/time 296 | """ 297 | room_id = int(shift_path_info(environ)) 298 | room = get_room(room_id) 299 | html = "" 300 | html += "" 301 | for booking in get_bookings_for_room(room_id): 302 | html += "".format( 303 | user_name=booking['user_name'], 304 | booked_on=booking['booked_on'], 305 | booked_from=booking['booked_from'] or "", 306 | booked_to=booking['booked_to'] or "" 307 | ) 308 | html += "
UserDateTimes
{user_name}{booked_on}{booked_from} - {booked_to}
" 309 | html += "
" 310 | html += '
' 311 | html += ''.format(room_id=room_id) 312 | html += ' ' 316 | html += ' | ' 317 | html += ' '.format(today=datetime.date.today()) 318 | html += '  ' 319 | html += '  ' 320 | html += '
' 321 | return page("Bookings for %s" % room['name'], html) 322 | 323 | def bookings_page(environ): 324 | """Provide a list of all bookings by a user or room, showing 325 | the other thing (room or user) and the date/time 326 | """ 327 | category = shift_path_info(environ) 328 | if category == "user": 329 | return bookings_user_page(environ) 330 | elif category == "room": 331 | return bookings_room_page(environ) 332 | else: 333 | return "No such booking category" 334 | 335 | def add_user(environ): 336 | form = cgi.FieldStorage(fp=environ['wsgi.input'], environ=environ.copy(), keep_blank_values=True) 337 | add_user_to_database(form.getfirst("name"), form.getfirst('email_address', "")) 338 | 339 | def add_room(environ): 340 | form = cgi.FieldStorage(fp=environ['wsgi.input'], environ=environ.copy(), keep_blank_values=True) 341 | add_room_to_database(form.getfirst("name"), form.getfirst('location', None)) 342 | 343 | def add_booking(environ): 344 | form = cgi.FieldStorage(fp=environ['wsgi.input'], environ=environ.copy(), keep_blank_values=True) 345 | add_booking_to_database( 346 | form.getfirst("user_id"), 347 | form.getfirst("room_id"), 348 | form.getfirst("booked_on"), 349 | form.getfirst("booked_from"), 350 | form.getfirst("booked_to") 351 | ) 352 | 353 | def webapp(environ, start_response): 354 | """Serve simple pages, based on whether the URL requests 355 | users, rooms or bookings. For now, just serve the Home page 356 | """ 357 | setup_testing_defaults(environ) 358 | 359 | # 360 | # Assume we're going to serve a valid HTML page 361 | # 362 | status = '200 OK' 363 | headers = [('Content-type', 'text/html; charset=utf-8')] 364 | # 365 | # Pick up the first segment on the path and pass 366 | # the rest along. 367 | # 368 | # ie if we're looking for /users/1/bookings, 369 | # param1 will be "users", and the remaining path will 370 | # be "/1/bookings". 371 | # 372 | param1 = shift_path_info(environ) 373 | if param1 == "": 374 | data = index_page(environ) 375 | elif param1 == "users": 376 | data = users_page(environ) 377 | elif param1 == "rooms": 378 | data = rooms_page(environ) 379 | elif param1 == "bookings": 380 | data = bookings_page(environ) 381 | elif param1 == "add-user": 382 | add_user(environ) 383 | status = "301 Redirect" 384 | headers.append(("Location", "/users")) 385 | data = "" 386 | elif param1 == "add-room": 387 | add_room(environ) 388 | status = "301 Redirect" 389 | headers.append(("Location", "/rooms")) 390 | data = "" 391 | elif param1 == "add-booking": 392 | add_booking(environ) 393 | status = "301 Redirect" 394 | headers.append(("Location", environ.get("HTTP_REFERER", "/bookings"))) 395 | data = "" 396 | else: 397 | status = '404 Not Found' 398 | data = "Not Found: %s" % param1 399 | 400 | start_response(status, headers) 401 | return [data.encode("utf-8")] 402 | 403 | def run_website(): 404 | httpd = make_server('', 8000, webapp) 405 | print("Serving on port 8000...") 406 | httpd.serve_forever() 407 | 408 | if __name__ == '__main__': 409 | print("About to create database %s" % DATABASE_FILEPATH) 410 | create_database() 411 | print("About to populate database %s" % DATABASE_FILEPATH) 412 | populate_database() 413 | print("About to run webserver") 414 | run_website() 415 | print("Finished") 416 | -------------------------------------------------------------------------------- /step13/bookings.py: -------------------------------------------------------------------------------- 1 | #!python3 2 | import os, sys 3 | import cgi 4 | import datetime 5 | import sqlite3 6 | from wsgiref.util import setup_testing_defaults, shift_path_info 7 | from wsgiref.simple_server import make_server 8 | 9 | # 10 | # Ensure we're using the same database filename throughout. 11 | # It doesn't matter what this is called or where it is: 12 | # sqlite3 will just accept anything. 13 | # 14 | DATABASE_FILEPATH = "bookings.db" 15 | 16 | def create_database(): 17 | """Connect to the database, read the CREATE statements and split 18 | them at the semicolon into individual statements. Once each 19 | statement has been executed, close the connection. 20 | """ 21 | # 22 | # Since we might be re-running this, delete the file and rebuild 23 | # it if necessary. 24 | # 25 | if os.path.exists(DATABASE_FILEPATH): 26 | os.remove(DATABASE_FILEPATH) 27 | 28 | # 29 | # A database cursor the the Python mechanism for running something 30 | # against any database. You create a cursor and then .execute 31 | # SQL statements through it. 32 | # 33 | db = sqlite3.connect(DATABASE_FILEPATH) 34 | q = db.cursor() 35 | 36 | # 37 | # Read all the contents of create.sql in one gulp 38 | # 39 | sql = open("create.sql").read() 40 | # 41 | # Split it into individual statements, breaking on the semicolon 42 | # 43 | statements = sql.split(";") 44 | # 45 | # Execute each of the individual statements against the database 46 | # 47 | for statement in statements: 48 | q.execute(statement) 49 | 50 | # 51 | # Close everything 52 | # 53 | q.close() 54 | db.commit() 55 | db.close() 56 | 57 | def populate_database(): 58 | """Populate the database with some valid test data 59 | """ 60 | db = sqlite3.connect(DATABASE_FILEPATH) 61 | q = db.cursor() 62 | 63 | sql = "INSERT INTO users(id, name, email_address) VALUES(?, ?, ?)" 64 | q.execute(sql, [1, "Mickey Mouse", "mickey.mouse@example.com"]) 65 | q.execute(sql, [2, "Donald Duck", "donald.duck@example.com"]) 66 | q.execute(sql, [3, "Kermit the Frog", None]) 67 | 68 | sql = "INSERT INTO rooms(id, name, location) VALUES(?, ?, ?)" 69 | q.execute(sql, [1, "Room A", "Next to the stairway"]) 70 | q.execute(sql, [2, "Room B", "On the Second Floor"]) 71 | q.execute(sql, [3, "Main Hall", None]) 72 | 73 | # 74 | # Triple-quoted strings can cross lines 75 | # NB the column order doesn't matter if you specify it 76 | # 77 | sql = """ 78 | INSERT INTO 79 | bookings 80 | ( 81 | room_id, user_id, booked_on, booked_from, booked_to 82 | ) 83 | VALUES( 84 | ?, ?, ?, ?, ? 85 | )""" 86 | q.execute(sql, [1, 1, '2014-09-25', '09:00', '10:00']) # Room A (1) booked by Mickey (1) from 9am to 10am on 25th Sep 2014 87 | q.execute(sql, [3, 1, '2015-09-25', None, None]) # Main Hall (3) booked by Mickey (1) from all day on 25th Sep 2014 88 | q.execute(sql, [2, 3, '2014-09-22', '12:00', None]) # Room B (2) booked by Kermit (3) from midday onwards on 22nd Sep 2014 89 | q.execute(sql, [1, 2, '2015-02-14', '09:30', '10:00']) # Room A (1) booked by Donald (2) from 9.30am to 10am on 15th Feb 2014 90 | 91 | q.close() 92 | db.commit() 93 | db.close() 94 | 95 | def select(sql_statement, params=None): 96 | """General-purpose routine to read from the database 97 | """ 98 | if params is None: 99 | params = [] 100 | db = sqlite3.connect(DATABASE_FILEPATH) 101 | db.row_factory = sqlite3.Row 102 | q = db.cursor() 103 | try: 104 | q.execute(sql_statement, params) 105 | return q.fetchall() 106 | finally: 107 | q.close() 108 | db.close() 109 | 110 | def execute(sql_statement, params=None): 111 | """General-purpose routine to write to the database 112 | """ 113 | if params is None: 114 | params = [] 115 | db = sqlite3.connect(DATABASE_FILEPATH) 116 | q = db.cursor() 117 | try: 118 | q.execute(sql_statement, params) 119 | db.commit() 120 | finally: 121 | q.close() 122 | db.close() 123 | 124 | def get_user(user_id): 125 | """Return the user matching user_id 126 | """ 127 | for user in select("SELECT * FROM users WHERE id = ?", [user_id]): 128 | return user 129 | 130 | def get_room(room_id): 131 | """Return the room matching room_id 132 | """ 133 | for room in select("SELECT * FROM rooms WHERE id = ?", [room_id]): 134 | return room 135 | 136 | def get_users(): 137 | """Get all the users from the database 138 | """ 139 | return select("SELECT * FROM users") 140 | 141 | def get_rooms(): 142 | """Get all the rooms from the database 143 | """ 144 | return select("SELECT * FROM rooms") 145 | 146 | def get_bookings(): 147 | """Get all the bookings ever made 148 | """ 149 | return select("SELECT * FROM v_bookings") 150 | 151 | def get_bookings_for_user(user_id): 152 | """Get all the bookings made by a user 153 | """ 154 | return select("SELECT * FROM v_bookings WHERE user_id = ?", [user_id]) 155 | 156 | def get_bookings_for_room(room_id): 157 | """Get all the bookings made against a room 158 | """ 159 | return select("SELECT * FROM v_bookings WHERE room_id = ?", [room_id]) 160 | 161 | def add_user_to_database(name, email_address): 162 | """Add a user to the database 163 | """ 164 | print("%r, %r" % (name, email_address)) 165 | execute( 166 | "INSERT INTO users(name, email_address) VALUES (?, ?)", 167 | [name, email_address] 168 | ) 169 | 170 | def add_room_to_database(name, location): 171 | """Add a user to the database 172 | """ 173 | execute( 174 | "INSERT INTO rooms(name, location) VALUES (?, ?)", 175 | [name, location] 176 | ) 177 | 178 | def add_booking_to_database(user_id, room_id, booked_on, booked_from=None, booked_to=None): 179 | """Add a booking to the database 180 | """ 181 | execute( 182 | """ 183 | INSERT INTO bookings(user_id, room_id, booked_on, booked_from, booked_to) 184 | VALUES(?, ?, ?, ?, ?) 185 | """, 186 | [user_id, room_id, booked_on, booked_from, booked_to] 187 | ) 188 | 189 | def page(title, content): 190 | """Return a complete HTML page with the title as the and <h1> 191 | tags, and the content within the body, after the <h1> 192 | """ 193 | return """ 194 | <html> 195 | <head> 196 | <title>Room Booking System: {title} 197 | 212 | 213 | 214 |

{title}

215 | {content} 216 | 217 | 218 | """.format(title=title, content=content) 219 | 220 | def index_page(environ): 221 | """Provide a list of all the pages 222 | """ 223 | html = """ 224 | 229 | """ 230 | return page("Starting Page", html) 231 | 232 | def users_page(environ): 233 | """Provide a list of all the users, linking to their bookings 234 | """ 235 | html = "" 243 | html += "
" 244 | html += """
245 |   246 |   247 | 248 |
""" 249 | return page("Users", html) 250 | 251 | def rooms_page(environ): 252 | """Provide a list of all the rooms, linking to their bookings 253 | """ 254 | html = "" 262 | html += "
" 263 | html += """
264 |   265 |   266 | 267 |
""" 268 | return page("Rooms", html) 269 | 270 | def all_bookings_page(environ): 271 | """Provide a list of all bookings 272 | """ 273 | html = "" 274 | html += "" 275 | for booking in get_bookings(): 276 | html += "".format( 277 | user_name=booking['user_name'], 278 | room_name=booking['room_name'], 279 | booked_on=booking['booked_on'], 280 | booked_from=booking['booked_from'] or "", 281 | booked_to=booking['booked_to'] or "" 282 | ) 283 | html += "
RoomUserDateTimes
{user_name}{room_name}{booked_on}{booked_from} - {booked_to}
" 284 | 285 | html += "
" 286 | html += '
' 287 | 288 | html += ' ' 292 | 293 | html += ' | ' 294 | 295 | html += ' ' 299 | 300 | html += ' | ' 301 | html += ' '.format(today=datetime.date.today()) 302 | html += '  ' 303 | html += '  ' 304 | html += '
' 305 | 306 | return page("All Bookings", html) 307 | 308 | 309 | def bookings_user_page(environ): 310 | """Provide a list of bookings by user, showing room and date/time 311 | """ 312 | user_id = int(shift_path_info(environ)) 313 | user = get_user(user_id) 314 | html = "" 315 | html += "" 316 | for booking in get_bookings_for_user(user_id): 317 | html += "".format( 318 | room_name=booking['room_name'], 319 | booked_on=booking['booked_on'], 320 | booked_from=booking['booked_from'] or "", 321 | booked_to=booking['booked_to'] or "" 322 | ) 323 | html += "
RoomDateTimes
{room_name}{booked_on}{booked_from} - {booked_to}
" 324 | html += "
" 325 | html += '
' 326 | html += ''.format(user_id=user_id) 327 | html += ' ' 331 | html += ' | ' 332 | html += ' '.format(today=datetime.date.today()) 333 | html += '  ' 334 | html += '  ' 335 | html += '
' 336 | return page("Bookings for %s" % user['name'], html) 337 | 338 | def bookings_room_page(environ): 339 | """Provide a list of bookings by room, showing user and date/time 340 | """ 341 | room_id = int(shift_path_info(environ)) 342 | room = get_room(room_id) 343 | html = "" 344 | html += "" 345 | for booking in get_bookings_for_room(room_id): 346 | html += "".format( 347 | user_name=booking['user_name'], 348 | booked_on=booking['booked_on'], 349 | booked_from=booking['booked_from'] or "", 350 | booked_to=booking['booked_to'] or "" 351 | ) 352 | html += "
UserDateTimes
{user_name}{booked_on}{booked_from} - {booked_to}
" 353 | html += "
" 354 | html += '
' 355 | html += ''.format(room_id=room_id) 356 | html += ' ' 360 | html += ' | ' 361 | html += ' '.format(today=datetime.date.today()) 362 | html += '  ' 363 | html += '  ' 364 | html += '
' 365 | return page("Bookings for %s" % room['name'], html) 366 | 367 | def bookings_page(environ): 368 | """Provide a list of all bookings by a user or room, showing 369 | the other thing (room or user) and the date/time 370 | """ 371 | category = shift_path_info(environ) 372 | if not category: 373 | return all_bookings_page(environ) 374 | elif category == "user": 375 | return bookings_user_page(environ) 376 | elif category == "room": 377 | return bookings_room_page(environ) 378 | else: 379 | return "No such booking category" 380 | 381 | def add_user(environ): 382 | form = cgi.FieldStorage(fp=environ['wsgi.input'], environ=environ.copy(), keep_blank_values=True) 383 | add_user_to_database(form.getfirst("name"), form.getfirst('email_address', "")) 384 | 385 | def add_room(environ): 386 | form = cgi.FieldStorage(fp=environ['wsgi.input'], environ=environ.copy(), keep_blank_values=True) 387 | add_room_to_database(form.getfirst("name"), form.getfirst('location', None)) 388 | 389 | def add_booking(environ): 390 | form = cgi.FieldStorage(fp=environ['wsgi.input'], environ=environ.copy(), keep_blank_values=True) 391 | add_booking_to_database( 392 | form.getfirst("user_id"), 393 | form.getfirst("room_id"), 394 | form.getfirst("booked_on"), 395 | form.getfirst("booked_from"), 396 | form.getfirst("booked_to") 397 | ) 398 | 399 | def webapp(environ, start_response): 400 | """Serve simple pages, based on whether the URL requests 401 | users, rooms or bookings. For now, just serve the Home page 402 | """ 403 | setup_testing_defaults(environ) 404 | 405 | # 406 | # Assume we're going to serve a valid HTML page 407 | # 408 | status = '200 OK' 409 | headers = [('Content-type', 'text/html; charset=utf-8')] 410 | # 411 | # Pick up the first segment on the path and pass 412 | # the rest along. 413 | # 414 | # ie if we're looking for /users/1/bookings, 415 | # param1 will be "users", and the remaining path will 416 | # be "/1/bookings". 417 | # 418 | param1 = shift_path_info(environ) 419 | if param1 == "": 420 | data = index_page(environ) 421 | elif param1 == "users": 422 | data = users_page(environ) 423 | elif param1 == "rooms": 424 | data = rooms_page(environ) 425 | elif param1 == "bookings": 426 | data = bookings_page(environ) 427 | elif param1 == "add-user": 428 | add_user(environ) 429 | status = "301 Redirect" 430 | headers.append(("Location", "/users")) 431 | data = "" 432 | elif param1 == "add-room": 433 | add_room(environ) 434 | status = "301 Redirect" 435 | headers.append(("Location", "/rooms")) 436 | data = "" 437 | elif param1 == "add-booking": 438 | add_booking(environ) 439 | status = "301 Redirect" 440 | headers.append(("Location", environ.get("HTTP_REFERER", "/bookings"))) 441 | data = "" 442 | else: 443 | status = '404 Not Found' 444 | data = "Not Found: %s" % param1 445 | 446 | start_response(status, headers) 447 | return [data.encode("utf-8")] 448 | 449 | def run_website(): 450 | httpd = make_server('', 8000, webapp) 451 | print("Serving on port 8000...") 452 | httpd.serve_forever() 453 | 454 | if __name__ == '__main__': 455 | print("About to create database %s" % DATABASE_FILEPATH) 456 | create_database() 457 | print("About to populate database %s" % DATABASE_FILEPATH) 458 | populate_database() 459 | print("About to run webserver") 460 | run_website() 461 | print("Finished") 462 | --------------------------------------------------------------------------------