Princeton University
COS 333: Advanced Programming Techniques

Assignment 4: A Registrar Application: Web Version 2


Purpose

The purpose of this assignment is to help you learn or review client-side web programming by creating a three-tiered single page application (SPA).


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 compose a web application. On the server side your application must use the Python Flask framework. On the client side your application must use HTML and Javascript with AJAX, and may use a client-side library. On the client side your application must use CSS, but may do so indirectly by using Bootstrap.

On the client side of your application you must use "raw" JavaScript, jQuery, or React. Your grader must be able to run your application simply by entering the command python runserver.py someport. Your grader must not be required to perform a build of your application before running it.

We recommend that you use Bootstrap instead of "raw" CSS on the client-side of your application. Using Bootstrap certainly is realistic, and will insulate you from the vagarities of CSS. In fact using "raw" CSS with external rules stored in a .css file would be problematic with respect to the submission of your work. (Flask would demand that the .css file reside in a "static" subdirectory, but the TigerFile application can't handle the submission of subdirectories.) If you want to use "raw" CSS with external rules, then discuss the matter with the COS 333 instructors.


Background

Arguably, Assignment 3 is flawed. The flaw is not in your implementation; instead the flaw (intentionally) is in the specification.

The problem is that an application that conforms to the Assignment 3 specification often has inconsistent page states. For example consider this sequence:

At that point the page's Dept input element contains COS, but the page displays data for all classes, not just for those classes whose department is COS. In that sense the application's page state is inconsistent.

Generalizing, any web application that accepts input and displays the resulting output in the same page runs the risk of inconsistent page states. One solution is to redesign the application such that it generates its output in a distinct page. Another solution is to redesign the application such that it refreshes its output within its single page with each input keystroke, such that the page state is always consistent. This assignment requires you to use the latter solution.


Your Task

As with previous assignments, 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 an application that allows Princeton students and other interested parties to query the database.

More specifically, your task is to redesign your Assignment 3 solution such that your application uses JavaScript and AJAX to generate new output in its single page dynamically as the user types. Your task also is to make your application responsive to the browser window size (such that it is a mobile web application), and better aesthetically.


The Given Files

Browse to the TigerFile page for this assignment. Download the reg4.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 file named reg.sqlite.


Reference Application

A reference application is running at https://cos333reg2.cs.princeton.edu. You can test your application by comparing its behavior with that of the reference application.

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 page must contain a header and a footer, both of which are "sticky" with respect to scrolling of the page.

Your application's page must contain four input elements. The input elements must allow the user to specify a dept, coursenum, area, and/or title. In contrast to the Assignment 3 application, your application's primary page must not contain a submit button.

Your application must handle the '%' and '_' characters as ordinary (not wildcard) characters, just as described in the previous assignment specifications.

In addition to the four input elements, your page must contain a results area, that is, a table containing the data that match the criteria defined by the values of the four elements. 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. The table must be well formatted.

When the user types a character in the dept, coursenum, area, or title input element, the page must use JavaScript and AJAX to refresh the contents of the results area of the page. For correctness, your JavaScript code must cancel any existing AJAX communication immediately before starting a new one, as described in lectures. For efficiency, your JavaScript code must "debounce" the AJAX communication at the 0.5 second level, as also described in lectures.

Your page must display a button for each class. When the user clicks on the button for a particular class, your page must use JavaScript and AJAX to display the details for that class in a modal. The modal must display the class's classid, days, starttime, endtime, bldg, roomnum, courseid, dept(s), coursenum(s), area, title, descrip, prereqs, and profname(s). The data must be well formatted.

Your page and modal must be reasonably attractive. Moreover, your page must be responsive to the size of the browser window. Specifically, your page must align the four input elements horizontally in wide windows and vertically in narrow windows. In that sense your application must be a mobile web application.

We assume that the layouts of your application's page and modal will be the same as those of the reference application. If you have a principled reason why you would like your application's layouts to differ from those of the reference application, then please discuss the matter with the course's instructors before you commit to your design. No surprises please!


The Communication Protocol

To request the application's page, a client (typically a browser) must send a HTTP request generated from a URL having any of these forms:

http://somehost:someport
http://somehost:someport/
http://somehost:someport/index

Your application's response must be a page consisting of HTML and JavaScript code. As noted previously, your JavaScript code must use one of these three options:(1) no client-side library, (2) jQuery, or (3) React.

To request class overviews, a client (not necessarily a browser) must send a HTTP request generated from a URL of this form:

http://somehost:someport/regoverviews?dept=somedept&coursenum=somecoursenum&area=somearea&title=sometitle

In that URL somedept, somecoursenum, somearea, or sometitle may be blank. For example, a client might send a HTTP request generated from this URL:

http://somehost:someport/regoverviews?dept=COS&coursenum=2&area=qr&title=intro

Your application's HTTP response must contain a JSON document which has the same format as prescribed in Assignment 2. For example, upon receipt of the example request shown previously, your application must send a HTTP response containing this JSON document:

[true, [
   {"classid":8308, "dept":"COS", "coursenum":"217", "area":"qr",
      "title":"Introduction to C_Science Programming Systems"},
   {"classid":9240, "dept":"COS", "coursenum":"342", "area":"qr",
      "title":"Introduction to Graph Theory"}]]

If your application, for whatever reason, cannot fulfull the class overviews request, then it must send a HTTP response containing this JSON document:

[false, "someerrormessage"]

The Error Handling section of this document provides more specifics.

To request class details, a client (not necessarily a browser) must send a HTTP request generated from a URL of this form:

http://somehost:someport/regdetails?classid=someclassid

For example, a client might send a HTTP request generated from this URL:

http://somehost:someport/regdetails?classid=8321

Your application's HTTP response must contain a JSON document which has the same format as prescribed in Assignment 2. For example, upon receipt of the example request shown previously, your application must send a HTTP response containing this JSON document:

[true, {
   "classid":8321,
   "days":"TTh",
   "starttime":"11:00AM",
   "endtime":"12:20PM",
   "bldg":"FRIEN",
   "roomnum":"006",
   "courseid":3672,
   "deptcoursenums":[{"dept": "COS", "coursenum": "333"}],
   "area":"",
   "title":"Advanced C%Science Programming Techniques",
   "descrip":"This is a course about the practice of programming.  Programming is more than just writing code.  Programmers must also assess tradeoffs, choose among design alternatives, debug and test, improve performance, and maintain software written by themselves & others. At the same time, they must be concerned with compatibility, robustness, and reliability, while meeting specifications.  Students will have the opportunity to develop these skills by working on their own code and in group projects.",
   "prereqs":"COS 217 and COS 226.",
   "profnames": ["Brian W. Kernighan"]
}]

Within the deptcoursenums list the elements must be sorted primarily by department alphabetically and secondarily by course number alphabetically. Within the profnames list the elements must be sorted alphabetically.

If your application, for whatever reason, cannot fulfull the class details request, then it must send a HTTP response containing this JSON document:

[false, "somerrormessage"]

The Error Handling section of this document provides more specifics.


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:

Generally it won't be possible to automate the testing of your application's error handling via your testregoverviews.py or testregdetails.py programs. Instead you must test your application's error handling manually.

Incorrect command-line arguments

If your runserver.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 argparse-generated message to its stderr and exit the process with status 2, as is the default when using argparse.

Testing: Run your runserver.py with incorrect command-line arguments and observe the result.

Failed AJAX call

If a "class overviews" or "class details" request sent by your page fails, maybe because the server isn't running at the time of the call, then the browser must display the message "Error: Failed to fetch data from server" in an alert box and leave your page unchanged.

Testing: Run your runserver.py and browse to it. Kill your runserver.py. In the browser initiate a "class overviews" request and observe observe the result. In the browser initiate a "class details" request and observe the result.

Non-existing class

If a client sends a "class details" request using a URL that has a classid=X name/value pair, and no class with classid X exists, then your application must send a HTTP response containing this JSON:
[false, "no class with classid X exists"]
Testing: Run your runserver.py on someipaddress at someport. Browse to http://someipaddress:someport/regdetails?classid=99999. In the browser observe the result, which must be the proper JSON document.

That could happen if the client directly (that is, not through your page) sends a "class details" request. That also could happen if (1) the browser through your page 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 through your page sends a "class details" request for classid 8321. In that case the browser must display the "no class with classid X exists" error message in an alert box and leave your page unchanged.

Testing: Run your runserver.py, and browse to it. Make sure the COS 333 class is displayed in the class overviews table. Edit your database, deleting the data for COS 333. Through the browser initiate a "class details" request for COS 333, and observe the result.

Missing classid

If a client sends a "class details" request using a URL that is missing the classid=X name/value pair, or in which X is missing, then your application must send a HTTP response containing this JSON:

[false, "missing classid"]
Testing: Run your runserver.py on someipaddress at someport. Browse to http://someipaddress:someport/regdetails. In the browser observe the result, which must be the proper JSON document. Then browse to http://someipaddress:someport/regdetails?classid=. In the browser observe the result, which must be the proper JSON document.

Non-integer classid

If a client sends a "class details" request using a URL containing the classid=X name/value pair, and X is not an integer, then your application must send a HTTP response containing this JSON:

[false, "non-integer classid"]
Testing: Run your runserver.py on someipaddress at someport. Browse to http://someipaddress:someport/regdetails?classid=abcd. In the browser observe the result, which must be the proper JSON document.

Database cannot be opened

If your application cannot open the database when it receives a "class overviews" or "class details" request, then your application must write a descriptive error message — the one contained within the thrown exception object — to its stderr. Then it must send to the client this JSON response:

[false, "A server error occurred. Please contact the system administrator."]

If the request was made by a browser through your page, then the browser must display that message in an alert box and leave the page unchanged.

Testing: Run your runserver.py, and browse to it. Delete your database. Through the browser initiate a "class overviews" request and observe the result. Through the browser initiate a "class details" request and observe the result.

Corrupted database

If your application discovers that the database is corrupted when it receives a "class overviews" or "class details" request, then your application must write a descriptive error message — the one contained within the thrown exception object — to its stderr. Then it must send to the client this JSON response:

[false, "A server error occurred. Please contact the system administrator."]

If the request was made by a browser through your page, then the browser must display that message in an alert box and leave the page unchanged.

Testing: Run your runserver.py, and browse to it. Corrupt your database, say, by dropping a critical table. Through the browser initiate a "class overviews" request and observe the result. Through the browser initiate a "class details" request and observe the result.

Advice

The Procedure

We recommend that you develop your assignment solution using three steps:

  1. Get "class overviews" requests working.
  2. Get "class details" requests working, crudely displaying the class details in an alert box.
  3. Get "class details" requests working, properly displaying the class details in a Bootstrap modal.

The Buttons

Each class overview displayed in your page must have an associated button. The HTML for the button for classid must have this form:

<button
   id="buttonclassid"
   onclick="getResultsDetails(classid)">
   classid
</button>

where getResultsDetails is a function which, given classid, performs an AJAX request to fetch the details for the specified class, and then displays those class details in a modal. (The id attribute is required for test automation, as described later in this document.)

The Modal

The last step — displaying the class details in a modal — is likely to be the trickiest one. Lectures provide hints about how to display a Bootstrap model using either no library, jQuery, or React. With React you will find it easier to use the react-bootstrap library rather than the bootstrap library.


Testing

Test your application just as you tested your Assignment 3 application. Also test by accessing your application via a browser on a laptop/desktop computer, with browser windows of various sizes. Then access your application via a browser on a smart phone. Your application have a reasonable appearance on both laptop/desktop computers and mobile devices.

Concerning test automation...

As noted previously, testing of the error-handling aspects of your application must be manual. But testing of the non-error-handling aspects must be automated.

Automation of the testing of your Assignment 4 application is mostly the same as was automation of the testing of your Assignment 3 application. The programs testregoverviewsgiven.py and testregdetailsgiven.py show how to automate your testing.

Make a copy of testregoverviewsgiven.py; name the copy testregoverviews.py. Also make a copy of testregdetailsgiven.py; name the copy testregdetails.py. Study the testregoverviews.py and testregdetails.py programs. Note that for testregoverviews.py and testregdetails.py to work with your application, your code must conform to some constraints. Specifically, your page must contain:

Your modal must contain:

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 these command-line arguments:

The delay command-line argument is new, and requires some explanation. Consider the Assignment 3 application. When the test programs enter data into the four input elements and click the Submit button, they can count on the results table appearing in a new page. In contrast, consider the Assignment 4 application. When the test programs enter data into the four input elements, the overviews table does not appear in a new page. Instead the overviews table appears in the same page after some delay, that is, after an AJAX call finishes. So the test programs must "sleep" until the AJAX call finishes.

The amount of time required for AJAX calls to finish isn't constant. If your computer is slow and loaded heavily, then it might take a few seconds; if your computer is fast and loaded lightly, then it might take a small fraction of a second.

That fact motivates the delay command-line argument. The value of delay should be some integer, typically between 1 and 5. Thereby delay tells the test programs how many seconds they should sleep to ensure that AJAX calls finish. If the number is too small, then the test programs will (attempt to) write output before AJAX calls finish. If the number is too big, then the test programs will write proper output, but will take lots of time to complete. Suggestion: Start with 1, and increase as necessary.

Then issue commands such as these to automate testing of your application with respect to its handling of class overviews requests:

python runserver.py 55555
python testregoverviews.py http://somehost:someport chrome normal 2 > out1 2>&1
python testregoverviews.py https://cos333reg2.cs.princeton.edu chrome normal 2 > out2 2>&1

In those commands you can replace chrome with firefox, normal with headless, and 2 with any reasonable delay.

Then compare the contents of files out1 and out2. Make sure that the contents of out1 and out2 are identical.

Thereafter, edit your 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 requests:

python runserver.py 55555
python testregdetails.py http://somehost:someport chrome normal 2 > out3 2>&1
python testregdetails.py https://cos333reg2.cs.princeton.edu chrome normal 2 > out4 2>&1

In those commands you can replace chrome with firefox, normal with headless, and 2 with any reasonable delay.

Then compare the contents of files out3 and out4. Make sure that the contents of out3 and out4 are identical.

Thereafter, edit your 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:



Appendix: 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. The steps are the same as they were for Assignment 3.


Copyright © 2024 by Robert M. Dondero, Jr.