The purpose of this assignment is to help you learn or review server-side web programming.
Make sure you study the COS 333 Policies web page before doing this assignment or any of the course's assignments.
This assignment asks you to create a web application. You must do so using the Python Flask framework and Jinja2 template engine.
As with Assignments 1 and 2, pretend that you're working for Princeton's Registrar's Office. You're given a database containing data about classes and courses offered during an upcoming Princeton semester. Your task is to create a web application that allows Princeton students and other interested parties to query the database.
Your Assignment 2 application is more realistic than your Assignment 1 programs. However, your Assignment 2 application requires the users to install clients on their local computers. It would be more convenient for your application to deliver HTML pages to the users, which the users then could render in browsers — which, of course, already are installed on their computers.
For this assignment you must compose a program named runserver.py
. The program must be a web application.
Browse to the TigerFile page for this assignment. Download the reg3.zip
file. Then unzip that file to create files named reg.sqlite
, testregoverviewsgiven.py
, and testregdetailsgiven.py
. Subsequent sections of this document describe those files.
The database is identical to the one from Assignment 1. The specification of Assignment 1 provides a description. The database is stored in the given file named reg.sqlite
.
A reference application is running at https://cos333reg1.cs.princeton.edu. You can run the reference application by browsing to that address.
Compose a program named runserver.py
. When executed with -h
as a command-line argument, runserver.py
must display a help message that describes the program's behavior:
$ python runserver.py -h usage: runserver.py [-h] port The registrar application positional arguments: port the port at which the server should listen options: -h, --help show this help message and exit
Your runserver.py
program must run the Flask test server on the specified port, which in turn must run your application.
Your application's "class overviews" page must contain a form. The form must allow the user to specify a dept
, coursenum
, area
, and/or title
. The form also must contain a button that the user can click to submit the form.
Your program must handle the '%' and '_' characters as ordinary (not wildcard) characters, just as described in the Assignment 1 and 2 specifications.
In addition to the form, your "class overviews" page must display a table containing the results of the request that is triggered by submission of the form. Specifically, the table must contain the classid
, dept
, coursenum
, area
, and title
of each class that matches the specified criteria, or of all classes if the user specifies no criteria. The rows must be sorted; the primary sort must be by dept
in ascending order, the secondary sort must be by coursenum
in ascending order, and tertiary sort must be by classid
in ascending order.
Along with results of the request, your "class overviews" page also must display, within its form, the criteria that user entered to generate those results. For example, if the user entered cos
in the dept
input of the form, entered qr
in the area
input of the form, and submitted the form, then the resulting page must display cos
in the dept
input of the form and qr
in the area
input of the form — along with the data for all classes that match those request criteria.
Your "class overviews" page must allow the user to click on the classid
for any of the classes that it displays, thereby allowing the user to learn more about that class. When the user clicks on the classid
for a particular class, your application must display a secondary "class details" web page containing the selected class's courseid
, days
, starttime
, endtime
, bldg
, roomnum
, dept(s)
, coursenum(s)
, area
, title
, descrip
, prereqs
, and profname(s)
. The data must be presented within two tables.
The "class details" page must contain a page link back to the "class overviews" page, thus allowing the user easily to perform another class overviews query. In the resulting "class overviews" page, the form must show the same data that the user entered most recently. The resulting "class overviews" page also must show the class data that resulted from the submission of that form.
Your application must be usable without a pointing device (mouse, touchpad, etc.). That is, your application must be usable with only the keyboard. The user must be able to cycle the keyboard focus through the input elements and links by pressing the Tab key, and trigger a server interaction by pressing the Enter key.
It is possible to design a better interface than the one just described. In fact, you will do so in the next assignment! We assume that the layout of your application's pages will be the same as that of the reference application. If you have a principled reason why you would like the layout of your application's pages to differ from that of the reference application, then please discuss the matter with the course's instructors before you commit to your design. No surprises please!
Your application must be robust. It must be impossible for any client request to cause your application to exit. Your application must handle the following errors:
If yourrunserver.py
is given command-line arguments that argparse can detect as incorrect, then argparse indeed must detect them as incorrect. Then your program must write a descriptive message to its stderr
and exit the process with status 2, as is the default when using argparse.
If the user directly (that is, not through a "class overviews" page) commands the browser to fetch a "class details" page using a URL that has a classid=X
name/value pair, and no class with classid X
exists, then the application must send to the browser an application-specific reasonably-styled error page that displays a "no class with classid X exists" error message.
A non-existing classid error also could occur if (1) the browser sends a "class overviews" request that displays data for classid 8321 (the classid for COS 333), (2) some other process deletes the row with classid 8321 from the classes table in the database, and (3) the browser sends a "class details" request for classid 8321.
If the user directly (that is, not through a "class overviews" page) commands the browser to fetch a "class details" page using a URL that is missing the classid=X
name/value pair, or that has a classid=X
name/value pair in which X
is missing, then the application must send to the browser an application-specific reasonably-styled error page that displays a "missing classid" error message.
If the user directly (that is, not through a "class overviews" page) commands the browser to fetch a "class details" page using a URL has a classid=X
name/value pair, and X
is not an integer, then the application must send to the browser an application-specific reasonably-styled error page that displays a "non-integer classid" error message.
If the database cannot be opened when the browser sends a request, then the application must write a descriptive error message — the one contained within the thrown exception object — to its stderr
, and send to the browser an application-specific reasonably-styled error page that contains the generic "A server error occurred. Please contact the system administrator." error message.
If the database is corrupted when the browser sends a request, then the application must write a descriptive error message — the one contained within the thrown exception object — to its stderr
, and send to the browser an application-specific reasonably-styled error page that contains the generic "A server error occurred. Please contact the system administrator." error message.
You have no control of the database used by the reference application. So it won't be possible for you to run the reference application when its database cannot be opened or has been corrupted. So it won't be possible for you to observe the page generated by the reference application in those cases. And so this screenshot shows that page:
We'll cover software testing techniques in lectures later in the semester. In the meantime, to test your work on this assignment it will be sufficient to rely upon (1) your knowledge of testing from the COS 217 course, and (2) this A Software Testing Taxonomy document.
Test your application by (1) reviewing this assignment specification thoroughly, making sure that your application conforms to every aspect of it, and (2) comparing the behavior of your application with the behavior of the reference application.
Focus on boundary (alias corner case) testing. Of course, you must make sure that your application handles normal data. But you also must make sure that your application handles unusual data: courses that have multiple cross-referenced departments/numbers, long titles, long descriptions, multiple professors, no professors, and so forth. You also must make sure that your application handles errors: bad manually-entered URLs, missing database, corrupted database, and so forth.
Hint: Make sure your application handles queries for these titles: "Independent Study"
, "Independent Study "
, "Independent Study "
, " Independent Study"
, and " Independent Study"
.
Also focus on statement (alias coverage) testing. Your tests should cause every statement of your application to be executed. The appendix of this page describes how you can automate your statement testing.
Concerning test automation...
It's more difficult to automate the testing of the Assignment 3 application than it was to automate the testing of the Assignment 1 or 2 applications. Generalizing, it's more difficult to automate the testing of web applications than it is to automate the testing of applications that are purely textual.
Nevertheless, it's possible to automate the testing of many web applications using a library named Selenium. A Python program that uses Selenium can send commands to a browser, which in turn can communicate with your web application. The testregoverviewsgiven.py
and testregdetailsgiven.py
programs use Selenium in that way.
Study those programs. Note that for testregoverviewsgiven.py
and testregdetailsgiven.py
to work with your application, your code must conform to some constraints. Specifically, your "class overviews" page must contain:
input
element that has an id
attribute whose value is deptInput
.input
element that has an id
attribute whose value is coursenumInput
.input
element that has an id
attribute whose value is areaInput
.input
element that has an id
attribute whose value is titleInput
.input
element that has an id
attribute whose value is submitButton
.table
element that has an id
attribute whose value is overviewsTable
.Your "class details" page must contain:
table
element that has an id
attribute whose value is classDetailsTable
.table
element that has an id
attribute whose value is courseDetailsTable
.Make a copy of testregoverviewsgiven.py
; name the copy testregoverviews.py
. Also make a copy of testregdetailsgiven.py
; name copy testregdetails.py
.
Issue these commands to make sure you know how to run the test programs:
python testregoverviews.py -h python testregdetails.py -h
Note that each program requires you to provide:
normal
or headless
. Running the program in headless mode suppresses the browser window display, and so is faster — but maybe less informative.Then issue commands such as these to automate testing of your application with respect to its generation of "class overviews" pages:
python runserver.py someport python testregoverviews.py http://somehost:someport chrome normal > out1 2>&1 python testregoverviews.py https://cos333reg1.cs.princeton.edu chrome normal > out2 2>&1
In those commands you can replace chrome
with firefox
, and normal
with headless
.
The first command runs your application on some host computer (call it somehost) at port someport. The second command runs testregoverviews.py
such that it sends requests to the web application at somehost and someport, receives responses, and writes the responses to a file named out1
. The third command runs testregoverviews.py
such that it sends requests to the reference web application, receives responses, and writes the responses to a file named out2
.
Then compare the contents of files out1
and out2
. The contents must be identical.
Thereafter, edit your copy of testregoverviews.py
to add additional tests, and repeat that command sequence.
Similarly, issue commands such as these to automate testing of your application with respect to its generation of "class details" pages:
python runserver.py someport python testregdetails.py http://somehost:someport chrome normal > out3 2>&1 python testregdetails.py https://cos333reg1.cs.princeton.edu chrome normal > out4 2>&1
In those commands you can replace chrome
with firefox
, and normal
with headless
.
Then compare the contents of files out3
and out4
The contents must be identical.
Thereafter, edit your copy of testregdetails.py
to add additional tests, and repeat that command sequence.
testregoverviews.py
and testregdetails.py
programs are primarily for you — to help you know if your application is correct, and thereby to help you to earn a high grade. But those programs are secondarily for your grader — to help your grader to grade your work accurately and quickly. So it's important that your application work with those programs. If your application doesn't work with those programs, then your grader will need to test entirely manually. In that unfortunate circumstance, your grade would suffer.
Compose a readme
file. Your readme
file must contain:
Your readme
file must be a plain text file. Don't create your readme
file using Microsoft Word or any other word processor.
Submit your assignment files using the TigerFile page for this assignment. Make sure you submit Assume that your grader already has activated the cos333 virtual environment before he/she runs your programs. The document from the first lecture entitled A COS 333 Computing Environment describes the cos333 virtual environment. Your grade will be based upon: To support your statement testing, you're encouraged (but not required) to use the Python Copyright © 2024 by Robert M. Dondero, Jr.runserver.py
, all files (.py
and .html
) comprising your application, testregoverviews.py
, testregdetails.py
, and readme
.
Grading
pylint
tool, when using the given .pylintrc
file, and when executed via the command python -m pylint *.py
. Ten points (that is, ten percent) of your grade will be based upon the quality of your program style as reported by pylint
. Your grader will start with the 10-point score reported by pylint
. Your grader then will "round down" that score to the 0.5 level to compute your program style grade. For example, if your pylint
score is 9.8, then your program style grade will be 9.5; if your pylint
score is 7.4, then your program style grade will be 7.0. Your grader will not run pylint on your testregoverviews.py
or testregdetails.py
programs.testregoverviews.py
and testregdetails.py
programs. Two points (that is, two percent) of your grade will be based upon your testregoverviews.py
program. You'll receive two points if your testregoverviews.py
program works and is reasonably thorough, one point if your testregoverviews.py
program works and is minimal, 1 point if your testregoverviews.py
program is reasonably thorough but doesn't work, and 0 points if you didn't submit a testregoverviews.py
program. Similarly, two points of your grade will be based upon your testregdetails.py
program.
Optional: Automated Statement Testing
coverage
tool to generate a coverage report showing which lines of your application have and have not been executed by your tests. These are the steps:
debug=True
argument within the call of app.run()
. The Python coverage tool doesn't work with Flask in debug mode.python -m coverage run -p --source=. runserver.py someport
..coverageX
file.python -m coverage combine
to combine the coverage reports generated by steps 2 through 4 into one large coverage report in a file named .coverage
.python -m coverage html
to use the .coverage
file to generate a human-readable report as a set of HTML documents in a directory named htmlcov
.htmlcov/index.html
to check the report.htmlcov
directory should show that 100% of your application's lines were executed. If the report doesn't show 100% coverage, then revise your testing plan accordingly, delete the .coverage
file and the htmlcov
directory, and repeat steps 2 through 8.regserver.py
by typing Ctrl-c, and you should do so. Typing Ctrl-Break causes such an abrupt exit that the coverage report is not generated.