Icon COS 333
Back to Assignments
☰
Back to Assignments

COS 333: Assignment 1


Purpose

The purpose of this assignment is to reinforce the lecture material on the Python programming language and database programming in Python. We expect you to use that lecture material when doing the assignment. If done properly, the assignment also will give you practice at composing modular code.


Rules

Make sure you study the COS 333 Policies web page before doing this assignment or any of the course's assignments.


Your Task

Any university registrar's office must maintain data about courses and classes offered during the upcoming semester. Those data typically are kept in a database. The registrar's office must provide an interface that allows students and other interested parties to query the database.

Pretend that you're working for Princeton's Registrar's Office. You're given a database containing data about courses and classes offered during an upcoming Princeton semester. (Actually the data are for the Spring 2010 semester.) Your task is to compose Python programs that allow Princeton students and other interested parties to query the database.

Subsequent assignments will be similar to this one. So it will be to your advantage to modularize your code so you easily can reuse code from this assignment in subsequent ones.


The Given Files

Browse to the TigerFile page for this assignment. Download the reg1.zip file. Then unzip that file to create files named readme1, readme2, reg.sqlite, ref_regoverviews.pyc, ref_regdetails.pyc, testregoverviewsgiven.py, testregdetailsgiven.py, and replace.py. Subsequent sections of this document describe those files.


The Database

The database is a SQLite database that is stored in a given file named reg.sqlite. The database consists of these five tables (primary keys are in boldface):

Table Fields
classes classid: INTEGER
courseid: INTEGER
days: TEXT
starttime: TEXT
endtime: TEXT
bldg : TEXT
roomnum: TEXT
courses courseid: INTEGER
area: TEXT
title: TEXT
descrip: TEXT
prereqs: TEXT
crosslistings courseid: INTEGER
dept: TEXT
coursenum: TEXT
coursesprofs courseid: INTEGER
profid: INTEGER
profs profid: INTEGER
profname: TEXT

These are the relationships among the tables:

Your first step must be to familiarize yourself with the database.


The regoverviews.py Program

You must compose two programs. The first program must be named regoverviews.py.

The given file named ref_regoverviews.pyc contains a reference program in bytecode form. Your regoverviews.py must have the same behavior as ref_regoverviews.pyc. That is, when given the same command-line arguments and database, your regoverviews.py and ref_regoverviews.pyc must write exactly the same (character-for-character) output to stdout and stderr, and must have the same exit status.

Run ref_regoverviews.pyc to determine the desired behavior of your regoverviews.py. Here are some highlights of that behavior...

When executed via a command that contains -h as a command-line argument, your regoverviews.py must display a help message:

$ python regoverviews.py -h
usage: regoverviews.py [-h] [-d dept] [-n num] [-a area] [-t title]

Registrar application: show overviews of classes

options:
  -h, --help  show this help message and exit
  -d dept     show only those classes whose department contains dept
  -n num      show only those classes whose course number contains num
  -a area     show only those classes whose distrib area contains area
  -t title    show only those classes whose course title contains title
Hint: Use the standard Python argparse module. The argparse module is not covered in lectures. So if you indeed do use the argparse module, then make sure you cite in your readme file the argparse-related resources that you used.

These example commands describe how your regoverviews.py must behave when given proper command-line arguments. Give them a try using ref_regoverviews.pyc:

Command-Line Arguments to python regoverviews.py Meaning
(none) Display data for all classes.
-d COS Display data for all classes whose department, when converted to lowercase letters, contains cos.
-n 333 Display data for all classes whose course number, when converted to lowercase letters, contains 333.
-n b Display data for all classes whose course number, when converted to lowercase letters, contains b.
-a Qr Display data for all classes whose distribution area, when converted to lowercase letters, contains qr.
-t intro Display data for all classes whose title, when converted to lowercase letters, contains intro.
-t science Display data for all classes whose title, when converted to lowercase letters, contains science.
-t C_S Display data for all classes whose title, when converted to lowercase letters, contains c_s. The point is that your program, unlike SQL, must not interpret the underscore character as a wildcard character.
-t c%S Display data for all classes whose title, when converted to lowercase letters, contains c%s. The point is that your program, unlike SQL, must not interpret the percent character as a wildcard character.
-d cos -n 3 Display data for all classes whose department when converted to lowercase letters contains cos and whose course number when converted to lowercase letters contains "3".
-d cos -a qr -n 2 -t intro Display data for all classes whose department when converted to lowercase letters contains cos and whose area when converted to lowercase letters contains qr and whose course number when converted to lowercase letters contains 2 and whose title when converted to lowercase letters contains intro.
-t "IndependentsStudy" (Where s indicates a space) display data for all classes whose title, when converted to lowercase letters, contains independentsstudy.
-t "IndependentsStudys" (Where s indicates a space) display data for all classes whose title, when converted to lowercase letters, contains independentsstudys.
-t "sIndependentsStudy" (Where s indicates a space) display data for all classes whose title, when converted to lowercase letters, contains sindependentsstudy.
-t "Int'l" Display data for all classes whose title, when converted to lowercase letters, contains Int'l.
-t 'Sport"' Display data for all classes whose title, when converted to lowercase letters, contains Sport".
-t=-c Display data for all classes whose title, when converted to lowercase letters, contains -c.

In short, your regoverviews.py must display the classid, dept, coursenum, area, and title of each class that matches the criteria specified by the user via command-line arguments.

Your regoverviews.py must use SQL prepared statements.

Your regoverviews.py must write its output in a human-readable format. These are some salient characteristics of the output:

Normally your regoverviews.py must terminate with exit status 0. If it detects a database-related error, then it must terminate with exit status 1. If it detects erroneous command-line arguments, then it must terminate with exit status 2 — as is the default behavior of argparse.

Hint: Use the Python percent (%) operator and C-style conversion specifications to format each row of output. An expression of this form works:
row = '%5s %4s %6s %4s %s' % (classid, dept, coursenum, area, title)
(There are other reasonable approaches; but given your knowledge from COS 217 of the C language and its printf() conversion specifications, that approach is likely to be the most straightforward for you.)

Then, having generated each row, use the standard Python textwrap module to wrap it to lines. Within each row, lines after the first must be indented using 5+1+4+1+6+1+4+1=23 spaces.

The textwrap module is not covered in lectures. So if you indeed do use the textwrap module, then make sure you cite in your readme file the textwrap-related resources that you used.

Hint: Consider the fact that a SQL statement of this form:
SELECT somefields FROM sometable WHERE somefield LIKE '%'
has the same semantics as a SQL statement of this form:
SELECT somefields FROM sometable
That fact could help you to express the logic in your regoverviews.py more succinctly.


The regdetails.py Program

Your second program must be named regdetails.py.

The given file named ref_regdetails.pyc contains a reference program in bytecode form. Your regdetails.py must have the same behavior as ref_regdetails.pyc. That is, when given the same command-line arguments and database, your regdetails.py and ref_regdetails.pyc must write exactly the same (character-for-character) output to stdout and stderr, and must have the same exit status.

Run ref_regdetails.pyc to determine the desired behavior of your regdetails.py. Here are some highlights of that behavior...

When executed via a command that contains -h as a command-line argument, your regdetails.py must display a help message:

$ python regdetails.py -h
usage: regdetails.py [-h] classid

Registrar application: show details about a class

positional arguments:
  classid     the id of the class whose details should be shown

options:
  -h, --help  show this help message and exit
Hint: Use the the standard Python argparse module.

When executed via a command that contains a valid classid, your regdetails.py must write to stdout the classid, courseid, days, starttime, endtime, bldg, roomnum, dept(s), coursenum(s), area, title, descrip, prereqs, and profname(s) for the class with the given classid. These commands illustrate. Give them a try using ref_regdetails.pyc:

$ python regdetails.py 8321
$ python regdetails.py 9032
$ python regdetails.py 8293
$ python regdetails.py 9977
$ python regdetails.py 9012
$ python regdetails.py 10188

Your regdetails.py must use SQL prepared statements.

Your regdetails.py must write its data in a human-readable format. These are some salient characteristics of that format:

Normally your regdetails.py must terminate with exit status 0. If it detects a database-related error, or if no class has the specified classid, then it must terminate with exit status 1. If it detects erroneous command-line arguments, then it must terminate with exit status 2 — as is the default behavior of argparse.

Hint: Use the standard Python textwrap module to wrap each row to lines.

Hint: Consider whether it's reasonable (or even possible!) to retrieve all required data from the database by executing a single SQL SELECT statement. Consider whether it's more reasonable to retrieve the required data by executing multiple SQL SELECT statements.


Error Handling

Your regoverviews.py and regdetails.py must be robust.

Your regoverviews.py must handle erroneous command-line arguments gracefully. These commands illustrate. Give them a try using ref_regoverviews.pyc:

$ python regoverviews.py a qr
$ python regoverviews.py -A qr
$ python regoverviews.py "-a " qr
$ python regoverviews.py -a qr st
$ python regoverviews.py -a
$ python regoverviews.py -a qr -d
$ python regoverviews.py -a -d cos
$ python regoverviews.py -x

Your regdetails.py must handle "no class with classid XXX" errors gracefully. This commands illustrates. Give it a try using ref_regdetails.pyc:

$ python regdetails.py 9034

Your regdetails.py must handle erroneous command-line arguments gracefully. These commands illustrate. Give them a try using ref_regdetails.pyc:

$ python regdetails.py
$ python regdetails.py 8321 9032
$ python regdetails.py abc123

Your regoverviews.py and regdetails.py must handle "database cannot be opened" errors. If the database cannot be opened, then your programs must write a descriptive error message — the one contained within the thrown Exception object — to their stderr.

Your regoverviews.py and regdetails.py also must handle "corrupted database" errors. In the context of this assignment, a corrupted reg.sqlite file is one that causes the database driver, upon interaction with the database, to throw an exception. A reg.sqlite file might be corrupted in various ways: it might be missing a table that the database driver requires, or might be missing a column/field that the database driver requires, or might even consist of plain text. If reg.sqlite is corrupted such that the SQLite driver's execution of a SELECT statement throws an exception, then your programs must write a descriptive error message — the one contained within the thrown Exception object — to their stderr.

A Common Question and its Answer

Suppose the user runs regdetails.py for a particular classid, the program queries the classes table to find the corresponding courseid, and the program then queries the database to fetch the row in the courses table with that courseid. Furthermore, suppose the database is corrupted such that no row with that courseid exists in the courses table. Must our regdetails.py handle that particular database corruption error?

Answer

No. Your grader won't test your regdetails.py program on that error.

The reg.sqlite database defines the constraint that when a row in the classes table contains a particular courseid, the courses table must contain a row with that courseid. The SQLite database management system enforces that constraint. So your regdetails.py program will not encounter that error. And so your regdetails.py program need not, and should not, check for that database corruption error.

Generalizing... As explained in lectures, relational databases can define foreign key integrity constraints, and relational database management systems can enforce those constraints. Thereby programs that use relational databases need not check for violations of foreign key constrants. That's a very good thing. It would be difficult for a program that uses a database systematically to check for foreign key constraint violations. And it would be absurdly redundant for every program that uses a database to check for such violations.

So it's fine to compose your programs such that they check for only the kinds of database-related errors that are described in this specification: file-level errors and schema-level errors. The first easily could happen if the reg.sqlite file is missing from the working directory. The second could happen if, for example, the reg.sqlite file is present but is missing the classes table or an important column/field of the classes table.

Each error message written by your regoverviews.py must be prefixed with sys.argv[0] (which is normally but not necessarily "regoverviews.py") followed by a colon and a space. Similarly, each error message written by your regdetails.py must be prefixed with sys.argv[0] (which is normally but not necessarily "regdetails.py") followed by a colon and a space.


Testing

We'll cover software testing techniques in lectures later in the semester. In the meantime, to test your programs 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 regoverviews.py and regdetails.py by (1) reviewing this assignment specification thoroughly, making sure that your programs conform to every aspect of it, and (2) comparing the behavior of your programs with the behavior of the reference programs.

Perform boundary (alias corner case) testing. Of course, make sure that your programs handle normal data. But also make sure that your programs handle unusual data: courses that have multiple cross-referenced departments/numbers, long titles, long descriptions, multiple professors, no professors, and so forth. Also make sure that your programs handle errors: erroneous command-line arguments, database cannot be opened, corrupted database, and so forth.

Perform statement (alias coverage) testing. Your tests should cause every statement of your regoverviews.py and regdetails.py to be executed.

Make a copy of the testregoverviewsgiven.py file; name the copy testregoverviews.py. Also make a copy of the testregdetailsgiven.py file; name the copy testregdetails.py. Automate your testing using the testregoverviews.py, testregdetails.py, and replace.py programs. First study those programs. Then issue these commands to automate your testing of your regoverviews.py:

python testregoverviews.py regoverviews.py > out1 2>&1
python testregoverviews.py ref_regoverviews.pyc > out2 2>&1
python replace.py out2 ref_regoverviews.pyc regoverviews.py

The first command runs testregoverviews.py on your regoverviews.py, and stores the output in a file named out1. The second command runs testregoverviews.py on the given ref_regoverviews.pyc, and stores the output in a file named out2. The third command replaces, within out2, each occurrence of "ref_regoverviews.pyc" (such as might appear at the beginning of error messages) with "regoverviews.py".

Then compare the contents of the out1 and out2 files to make sure that they are identical.

Thereafter, edit your testregoverviews.py to add additional tests, and repeat that sequence.

Similarly, issue these commands to automate testing of your regdetails.py:

python testregdetails.py regdetails.py > out3 2>&1
python testregdetails.py ref_regdetails.pyc > out4 2>&1
python replace.py out4 ref_regdetails.pyc regdetails.py

Then compare the contents of the out3 and out4 files to make sure that they are identical.

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

There are multiple ways to compare the contents of the out1 and out2 (or out3 and out4) files:

Your grade for your testregoverviews.py (or testregdetails.py) will be based upon the quantity and quality of your tests. The example queries shown in this assignment specification are for demonstration of the desired functionality of your program. However, they are insufficient for systematic testing of your program. Your testregoverviews.py (or testregdetails.py) program must contain many more example queries.


It's possible to design your testregoverviews.py (or testregdetails.py) program so it tests the behavior of your regoverviews.py (or regdetails.py) program in the presence of database problems. In particular, at some point your testregoverviews.py (or testregdetails.py) program can:

If you include such tests in your testregoverviews.py (or testregdetails.py) program, then make sure you submit your regflawed.sqlite file. Otherwise your testregoverviews.py (or testregdetails.py) program will fail when your grader runs it.



Program Style

Your programs must be well styled. Generally, by consensus of the Python community, good Python style is defined by the PEP 8 -- Style Guide for Python Code website.

The Python community has developed a static code analysis tool -- a linter tool -- named pylint. The pylint tool is similar to the splint tool that you used in COS 217: just as splint (configured with a .splintrc file) generates a report critiquing the style of given C code, so pylint (configured with a .pylintrc file) generates a report critiquing the style of given Python code. The pylint tool enforces many of the PEP 8 guidelines, and some additional guidelines too. In COS 333 "good style" is defined by the pylint tool, as configured with the given .pylintrc file. The lecture example programs (with a few intentional or unavoidable exceptions) cause pylint to generate perfect reports, and your assignment programs should do the same.

Using pylint is easy. You can critique one file comprising your programs at a time:

python -m pylint regoverviews.py
python -m pylint regdetails.py
...

However, a better approach is to critique all files comprising your programs at the same time:

python -m pylint regoverviews.py regdetails.py ...

When given multiple files, pylint performs some cross-file critiquing. So the "all files at the same time" approach may generate warnings in addition to those generated by the "one file at a time" approach. Make sure you use the "all files at the same time" approach, as your grader will.


Advice

The command-line format of regoverviews.py is incomplete in the logical sense. Most notably the format doesn't allow the user to express OR relationships in queries. For example, the format doesn't allow the user to express queries of the form "Display data for all classes whose area is 'qr' OR 'st'." or "Display data for all classes whose dept is 'cos' OR whose area is 'qr'." Don't be concerned about that. Remember that the purpose of the assignment is not to develop a great registrar's office application per se. Instead its purpose is to give you experience with database programming — experience that, we hope, will help you when developing your project, and beyond.

Modularize your code, at both the file level and the function level, as much as you can. Good modularity will help you to reuse your code from this assignment in the next assignment.

Make sure that the output of your regoverviews.py has the same format as does the output of ref_regoverviews.pyc. If it doesn't, then (no doubt) your testing programs will detect many differences between the output of the two programs, and it will be difficult for you to determine how well your regoverviews.py works. Moreover, if it doesn't, then your regoverviews.py will fail many of our automated checks, it will be difficult for your grader to determine if how well your regoverviews.py works, and your grade will suffer. Same for your regdetails.py.


readme File

Compose a readme file. If you worked alone, then use the given readme1 file as a template. If you worked with a teammate, then use the given readme2 file as a template.

Your readme file must be a plain text file. Don't create your readme file using Microsoft Word or any other word processor.

You must enter a reply in each section of your readme file. The absence of a reply differs from the reply "None." If you submit a readme file that is missing replies in some sections, then your grader will ask you to edit your readme file and resubmit it. That would inconvenience both you and your grader. In that case the grading of your submission could be delayed and penalized.

An important section of your readme file requires you to list the sources of information other than the lecture material (and other than your assignment teammate) that you used while doing the assignment. We will apply a potentially substantial point penalty for each aspect of your assignment solution that is outside of the material that was presented in lectures and for which you do not list a source. As noted in the readme file templates, it's acceptable to list "prior knowledge gained by ___" as a source.


Submission

Submit your assignment files using the TigerFile page for this assignment. Make sure you submit regoverviews.py, regdetails.py, testregoverviews.py, testregdetails.py, any .py files used by those programs, and readme. After submitting your files, click on the TigerFile "Check Submitted Files" button. Doing so runs your application with the testregoverviewsgiven.py and testregdetailsgiven.py programs, and so can give you some assurance that you submitted all files that you intended to submit.


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. Repeatedly issue commands of the form python -m coverage run -p regoverviews.py arguments. Each of those commands runs your regoverviews.py with the specified arguments, and generates a coverage report in a file named .coverageX (for some X). The report indicates which lines of your regoverviews.py were executed and which were not.
  2. Repeatedly issue commands of the form python -m coverage run -p regdetails.py argument. Each of those commands runs your regdetails.py with the specified argument, and generates a coverage report in a file named .coverageX (for some X). The report indicates which lines of your regdetails.py were executed and which were not.
  3. Issue the command python -m coverage combine to combine the coverage reports generated by steps 1 and 2 into one large coverage report in a file named .coverage.
  4. 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.
  5. Browse to htmlcov/index.html to check the report.
  6. Ideally, the files in your htmlcov directory should show that 100% of your programs' lines were executed. If the report shows less than 100% coverage, then revise your testing plan accordingly, delete the .coverage* files and the htmlcov directory, and repeat steps 1 through 5.

Copyright © 2026 by Robert M. Dondero, Jr.