├── .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 |
152 | """
153 |
154 | for user in get_users():
155 | html += '- {name} ({email_address})
\n'.format(
156 | id=user['id'],
157 | name=user['name'],
158 | email_address=user['email_address'] or "No email"
159 | )
160 |
161 | html += """
162 |
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 |
176 | """
177 |
178 | for room in get_rooms():
179 | html += '- {name} ({location})
\n'.format(
180 | id=room['id'],
181 | name=room['name'],
182 | location=room['location'] or "Location unknown"
183 | )
184 |
185 | html += """
186 |
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
130 | tags, and the content within the body, after the
131 | """
132 | return """
133 |
134 |
135 | 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 = ""
170 | for user in get_users():
171 | html += '- {name} ({email_address})
\n'.format(
172 | id=user['id'],
173 | name=user['name'],
174 | email_address=user['email_address'] or "No email"
175 | )
176 | 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 = ""
183 | for room in get_rooms():
184 | html += '- {name} ({location})
\n'.format(
185 | id=room['id'],
186 | name=room['name'],
187 | location=room['location'] or "Location unknown"
188 | )
189 | 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
130 | tags, and the content within the body, after the
131 | """
132 | return """
133 |
134 |
135 | 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 = ""
175 | for user in get_users():
176 | html += '- {name} ({email_address})
\n'.format(
177 | id=user['id'],
178 | name=user['name'],
179 | email_address=user['email_address'] or "No email"
180 | )
181 | 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 = ""
188 | for room in get_rooms():
189 | html += '- {name} ({location})
\n'.format(
190 | id=room['id'],
191 | name=room['name'],
192 | location=room['location'] or "Location unknown"
193 | )
194 | 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 += "| Room | Date | Times |
"
203 | for booking in get_bookings_for_user(user_id):
204 | html += "| {room_name} | {booked_on} | {booked_from} - {booked_to} |
".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 += "
"
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 += "| User | Date | Times |
"
219 | for booking in get_bookings_for_room(room_id):
220 | html += "| {user_name} | {booked_on} | {booked_from} - {booked_to} |
".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 += "
"
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
153 | tags, and the content within the body, after the
154 | """
155 | return """
156 |
157 |
158 | 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 = ""
198 | for user in get_users():
199 | html += '- {name} ({email_address})
\n'.format(
200 | id=user['id'],
201 | name=user['name'],
202 | email_address=user['email_address'] or "No email"
203 | )
204 | html += "
"
205 | html += "
"
206 | html += """
"""
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 = ""
217 | for room in get_rooms():
218 | html += '- {name} ({location})
\n'.format(
219 | id=room['id'],
220 | name=room['name'],
221 | location=room['location'] or "Location unknown"
222 | )
223 | 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 += "| Room | Date | Times |
"
232 | for booking in get_bookings_for_user(user_id):
233 | html += "| {room_name} | {booked_on} | {booked_from} - {booked_to} |
".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 += "
"
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 += "| User | Date | Times |
"
248 | for booking in get_bookings_for_room(room_id):
249 | html += "| {user_name} | {booked_on} | {booked_from} - {booked_to} |
".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 += "
"
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
162 | tags, and the content within the body, after the
163 | """
164 | return """
165 |
166 |
167 | 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 = ""
207 | for user in get_users():
208 | html += '- {name} ({email_address})
\n'.format(
209 | id=user['id'],
210 | name=user['name'],
211 | email_address=user['email_address'] or "No email"
212 | )
213 | html += "
"
214 | html += "
"
215 | html += """
"""
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 = ""
226 | for room in get_rooms():
227 | html += '- {name} ({location})
\n'.format(
228 | id=room['id'],
229 | name=room['name'],
230 | location=room['location'] or "Location unknown"
231 | )
232 | html += "
"
233 | html += "
"
234 | html += """"""
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 += "| Room | Date | Times |
"
247 | for booking in get_bookings_for_user(user_id):
248 | html += "| {room_name} | {booked_on} | {booked_from} - {booked_to} |
".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 += "
"
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 += "| User | Date | Times |
"
263 | for booking in get_bookings_for_room(room_id):
264 | html += "| {user_name} | {booked_on} | {booked_from} - {booked_to} |
".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 += "
"
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
186 | tags, and the content within the body, after the
187 | """
188 | return """
189 |
190 |
191 | 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 = ""
231 | for user in get_users():
232 | html += '- {name} ({email_address})
\n'.format(
233 | id=user['id'],
234 | name=user['name'],
235 | email_address=user['email_address'] or "No email"
236 | )
237 | html += "
"
238 | html += "
"
239 | html += """
"""
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 = ""
250 | for room in get_rooms():
251 | html += '- {name} ({location})
\n'.format(
252 | id=room['id'],
253 | name=room['name'],
254 | location=room['location'] or "Location unknown"
255 | )
256 | html += "
"
257 | html += "
"
258 | html += """"""
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 += "| Room | Date | Times |
"
272 | for booking in get_bookings_for_user(user_id):
273 | html += "| {room_name} | {booked_on} | {booked_from} - {booked_to} |
".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 += "
"
280 | html += "
"
281 | 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 += "| User | Date | Times |
"
301 | for booking in get_bookings_for_room(room_id):
302 | html += "| {user_name} | {booked_on} | {booked_from} - {booked_to} |
".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 += "
"
309 | html += "
"
310 | 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
191 | tags, and the content within the body, after the
192 | """
193 | return """
194 |
195 |
196 | 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 = ""
236 | for user in get_users():
237 | html += '- {name} ({email_address})
\n'.format(
238 | id=user['id'],
239 | name=user['name'],
240 | email_address=user['email_address'] or "No email"
241 | )
242 | html += "
"
243 | html += "
"
244 | html += """
"""
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 = ""
255 | for room in get_rooms():
256 | html += '- {name} ({location})
\n'.format(
257 | id=room['id'],
258 | name=room['name'],
259 | location=room['location'] or "Location unknown"
260 | )
261 | html += "
"
262 | html += "
"
263 | html += """"""
268 | return page("Rooms", html)
269 |
270 | def all_bookings_page(environ):
271 | """Provide a list of all bookings
272 | """
273 | html = ""
274 | html += "| Room | User | Date | Times |
"
275 | for booking in get_bookings():
276 | html += "| {user_name} | {room_name} | {booked_on} | {booked_from} - {booked_to} |
".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 += "
"
284 |
285 | html += "
"
286 | 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 += "| Room | Date | Times |
"
316 | for booking in get_bookings_for_user(user_id):
317 | html += "| {room_name} | {booked_on} | {booked_from} - {booked_to} |
".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 += "
"
324 | html += "
"
325 | 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 += "| User | Date | Times |
"
345 | for booking in get_bookings_for_room(room_id):
346 | html += "| {user_name} | {booked_on} | {booked_from} - {booked_to} |
".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 += "
"
353 | html += "
"
354 | 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 |
--------------------------------------------------------------------------------