Dynamic Web Pages¶
Instead of placing the form in an HTML file
and the script in a .py
file,
we can have the Python scripts printing the forms,
recursively as needed.
Forms in Python Scripts¶
To run a simple CGI server, a simple Python script suffices.
The documentation string of myserver.py
is
"""
Launch this code in the directory where
the CGI script is that you want to run.
Point the browser to localhost:8000
(or whatever the value of PORTNUMBER is),
followed by / and the name of the script.
"""
To run the CGI scripts in this lecture, one does not really need Apache. The portnumber defined in the script must be open.
Tthe script myserver.py
is below.
PORTNUMBER = 8000
import http.server
import cgitb; cgitb.enable() # CGI error report
server = http.server.HTTPServer
handler = http.server.CGIHTTPRequestHandler
server_address = ("", PORTNUMBER)
handler.cgi_directories = ["/"]
httpd = server(server_address, handler)
try:
print('welcome to our cgi server')
print('press ctrl c to shut down the server')
httpd.serve_forever()
except:
print('ctrl c pressed, shutting down server')
httpd.socket.close()
Serving Python scripts suffices, as we can have scripts print HTML code. What we did before:
- The form element in an HTML page triggers an action, for example: submit user name and password.
- The action triggered by the submit button is defined by a Python script.
The advantage is that the separate HTML separates the layout from the actions. The main disadvantage is the possibility of error by a mismatch between the names of the values in the form and the names used the retrieve the values in the script.
Consider for example a form which prompts the user for a word, illustrated in Fig. 59.
The confirmation of the submitted word is shown in Fig. 60.
The script use_forms.py
starts with the printing of the header
for the HTML page.
#!/usr/bin/python
import cgi
def print_header(title):
"""
writes title and header of page
"""
print("""Content-type: text/html
<html>
<head>
<title>%s</title>
</head>
<body>""" % title)
print_header("using forms")
Then the script use_forms.py
continues with the printing of
the form to prompt the user for a word:
print("""<b>Enter a word</b>
<form method = "post" method = "use_forms.py">
<p>
<input type = "text" name = "word">
<input type = "submit">
</p>
</form>""")
FORM = cgi.FieldStorage()
if "word" in FORM:
print("""<p>Your word is <em>%s</em></p>
""" % FORM["word"].value)
print("</body></html>\n")
For our next example, we introduce default values for
input elements of type text. When the form is then printed,
a default value appears in the text element,
as shown in Fig. 61.
The default value in this example is 2
.
The code snippet which prints the form with the default value follows below.
print """<form method = "post"
action = "web_det.py">
<p>
Give dimension:
<input type = "text" name = "dim"
size = 4 value = 2>
<input type = "submit">
</p>
</form>"""
A Web Interface for a Determinant¶
In our next example we make our interactive forms dynamic, in the sense that the content of the form is determined when the application is running.
In Fig. 62 we see the form to ask for a matrix.
The computation of the determinant of the matrix entered in the form is illustrated in Fig. 63.
The script is a sequence of two forms:
- We ask for the dimension of the matrix. The dimension is then used to generate a table of input elements to give the entries of the matrix.
- We ask for the entries of the matrix. The second form is processed by the same script. The action of this form brings up a new page with the matrix and the determinant.
The dimension is passed from one form to the other
through an input element.
The main function of web_det.py
is below.
def main():
"""
web interface to compute a determinant
"""
print_header("compute determinant")
ask_dimension()
form = cgi.FieldStorage()
if "dim" in form:
dim = int(form["dim"].value)
ask_matrix(dim)
print("</body></html>\n")
The main()
calls the functions
print_header()
, ask_dimension()
, and ask_mastrix()
.
The function ask_matrix()
is defined below.
def ask_matrix(dim):
"""
form to enter a matrix of dimension dim
"""
print("""<form method = "post"
action = "compute_det.py">""")
print("""The dimension:
<input type = "text" name = "dim"
size = 4 value = %d>""" % dim)
Notice how the value of the dimension is set
in the input element dim
of the form.
Then the ask_matrix()
function continues
with the printing of the text elements for all
the entries in the matrix.
# printing a table of text elements
print("<p>Enter matrix :</p>")
print("<table>")
for row in range(0, dim):
print("<tr>")
for col in range(0, dim):
print("""<td><input type = "text"
name = %d,%d size = 4></td>""" % (row, col))
print("</table>")
print("""<input type = "submit">""")
print("</form>")
The names of the entries are strings row, col
.
To compute the determinant, we import the function det
from the numpy.linalg
package.
The script compute_det.py
begins with
#!/usr/bin/python
import cgi
from numpy import zeros
from numpy.linalg import det
The main function in compute_det.py
is
def main():
"""
web interface to compute a determinant
"""
print("Content-type: text/plain\n")
form = cgi.FieldStorage()
if "dim" in form:
dim = int(form["dim"].value)
detmat = determinant(form, dim)
print("The determinant :", detmat)
The determinant()
function is defined below.
def determinant(form, dim):
"""
returns the determinant of the matrix
available in the form
"""
mat = zeros((dim, dim), float)
for row in range(dim):
for col in range(dim):
info = "%d,%d" % (row, col)
if info in form:
mat[row, col] = float(form[info].value)
print("The matrix is")
print(mat)
return det(mat)
An Application¶
As an application of a web interface, consider the computation of stable pairs.
Suppose we have six programmers – Alice, Brian, Cindy, David, Eve, and Fred – and we want to form pairs of programmers.
Every programmer provides list of preferences, for example: the preference list for Alice is Eve, David, Cindy, Fred, Brian, listed in increasing order
Pairs (Brian, Eve) and (Cindy, David) are not stable if the pairing (Brian, Cindy) and (Eve, David) is preferable to all four: if Brian likes Cindy better than Eve, Cindy prefers Brian over David, David likes Eve better than Cindy, and Eve prefers David over Brian.
Likewise, if the pairing (Brian, David) and (Cindy, Eve) would be preferable to all four, then the pairs (Brian, Eve) and (Cindy, David) should then also not be generated.
Running web_pairs1.py
in a browser shows
the picture in Fig. 64.
The submit launches the second script web_pairs2.py
.
Each time the user submits, a new page comes up, asking for the preferences of the next person. Notice that the same script collects all input.
The preferences input for 1 are shown in Fig. 65.
The preferences input for 2 are shown in Fig. 66.
The preferences input for 3 are shown in Fig. 67.
The preferences input for 4 are shown in Fig. 68.
The result is displayed in Fig. 69.
The recursive use of forms in illustrated in the script below.
def ask_preferences(n, k):
"""
Form to enter the preferences for person k.
"""
print("<p> reading preferences for %d out of %d </p>" \
% (k, 2*n))
print("""<form method = "post"
action = "web_pairs2.py">
<p>
enter your name:
<input type = "text" name = "name" size = 20>
</p>
<p> enter your preferences: </p>""")
print("<OL>")
for i in range(0, 2*n-1):
print(""" <LI> <input type = "text" name = %d
size = 20> </LI> """ % i)
print("""</OL><input type = "submit"> </form>""")
The skeleton of the main program is below:
def main():
if "dim" in form:
ask_preferences(dim, 1)
else:
file = open('/tmp/preferences', 'r')
if person < 2*dim:
get_preferences(form, dim, person)
ask_preferences(dim, person+1)
else:
get_preferences(form, dim, person)
file = open('/tmp/preferences', 'r')
Exercises¶
- Modify the
use_forms.py
so that it places by default one word in the input element, with all vowels removed. Ask the user to supply the missing vowels. In reaction to the submit, compare the word of the user with the original word and give feedback. - Write a Python script
web_sum.py
to sum n numbers. Like inweb_det.py
, first the user is asked to supply n and then n input elements have to be filled out before another scriptcompute_sum.py
can compute the sum. - The current version of
web_det.py
takes undefined entries to equal zero. Change it so the user is asked to return and provide the missing entries. - Make a recursive form. The input element asks the user for a number. With each submit, the number is doubled.