Princeton University
COS 333: Advanced Programming Techniques

Assignment 3: A Registrar Application: Web Version 1


Purpose

The purpose of this assignment is to help you learn or review server-side web programming.


Rules

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.


Your Task

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.


The Given Files

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

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.


The Reference Application

A reference application is running at https://cos333reg1.cs.princeton.edu. You can run the reference application by browsing to that address.

Of course the reference application delivers HTML code to your browser. You could use your browser's "view source" feature to examine that code. But don't do that! That is, don't examine the code that the reference application delivers to your browser. Doing that would decrease the value of the assignment for you, and would be a violation of course policies.


The Application

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.

Hint: Your application must be stateful to implement the page link back to the "class overviews" page.

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.

Note: By default, the Firefox browser on Mac may not handle tabbing between input elements and links. To enable tabbling, in Firefox click on Preferences -> General and make sure the Always use the cursor keys to navigate within pages check button is unchecked. Then, in Mac System Preferences, click on Keyboard -> Shortcuts and make sure the All Controls radio button is checked.

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!


Error Handling

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:

Incorrect command-line arguments

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.

Non-existing classid

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.

Missing classid

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.

Non-integer classid

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.

Database cannot be opened

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.

Corrupted database

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:



Testing

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:

Your "class details" page must contain:

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:

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 out4The contents must be identical.

Thereafter, edit your copy of testregdetails.py to add additional tests, and repeat that command sequence.


Important note: The 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.


Submission

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 runserver.py, all files (.py and .html) comprising your application, testregoverviews.py, testregdetails.py, and readme.


Grading

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:



Optional: Automated Statement Testing

To support your statement testing, you're encouraged (but not required) to use the Python 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:

  1. Delete the debug=True argument within the call of app.run(). The Python coverage tool doesn't work with Flask in debug mode.
  2. Launch your application server via the command python -m coverage run -p --source=. runserver.py someport.
  3. Use a browser to interact with your application, thereby creating a .coverageX file.
  4. Exit your application server.
  5. Repeat steps 2 through 4 as desired.
  6. Issue the command 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.
  7. Issue the command 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.
  8. Browse to htmlcov/index.html to check the report.
  9. The files in your 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.
Note: On a Microsoft Windows computer, when generating a coverage report it is possible to kill your 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.


Copyright © 2024 by Robert M. Dondero, Jr.