Software Carpentry
Exercise 4

Due: 5:00 p.m. EST, Friday 04 November 2005 (but see Question 1).

Weight: 10% of course grade.

Submit your solutions by adding files to the directory ex04 in your Subversion repository. Please remember to use svn update to get the ex04 directory that has been created for you, rather than creating one of your own.

Question 1 (to be completed by Friday, 28 October 2005)

Find four ambiguities, or missing pieces of information, in Question 2 (below). Describe each in a sentence; submit your answer in a file in your ex04 directory called ambiguity.txt.

Question 2

You are putting together a museum exhibition for children called &lquot;The Wonderful Earth&rquot;. There will be three kinds of exhibit items: fossils, rocks, and photographs. Every item has a ten-digit ID, a caption, an acquisition date, and a location. Fossils have a species name and an age; rocks have a chemical composition; and photographs have a photographer's name. All values are stored as strings.

Create a file in your ex04 directory called exhibits.py that defines four classes: ExhibitItem, Fossil, Rock, and Photograph, where the last three are derived from the first. Your classes should be defined so that the following code executes without errors:

dinosaur = Fossil("4718293847", "The world's largest carnivore",
                  "2002-07-19", "Drumheller, Alberta",
                  "Tyrannosaurus rex", "71 million years")
pyrite = Rock("4718292273", "Fool's gold",
              "1990-11-01", "Sudbury, Ontario",
              "FeS2")
picture = Photograph("2005061728", "Curator being chased by T.rex",
                     "2005-06-17", "Toronto, Ontario",
                     "Georges Cuvier")
mammoth = Fossil("4718290017", "Woolly and warm",
                 "2001-04-01", "Novosibirsk, Siberia",
                 "Mammuthus primigenius", "1 million years")
allItems = [dinosaur, pyrite, picture]

def sortByDate(x, y):
    return cmp(x.getAcquisitionDate(), y.getAcquisitionDate())
allItems.sort(sortByDate)

for i in allItems:
    if isinstance(i, Fossil):
        i.setAge(i.getAge().replace("million years", "MY"))
    print '%10s (%10s): %s' % \
          (i.getId(), i.getAcquisitionDate(), i.getCaption())

Question 3

A colleague has written a function called nanotech that calculates which compounds can be made from a set of atoms (just like nanotech.py in Exercise 3). nanotech takes two dictionaries as arguments. The first has molecule names as keys, and dictionaries of required atom counts as values; the second tells nanotech how many atoms of each kind are available. The function's result is a list of makeable molecules, in alphabetically sorted order:

from nanotech import nanotech

possible = {
    'water'                : {'H' : 2, 'O' : 1},
    'ammonia'              : {'N' : 1, 'H' : 3},
    'carbon tetrachloride' : {'C' : 1, 'Cl' : 4},
    'hydrogen peroxide'    : {'H' : 2, 'O' : 2},
    'nitrosyl hydride'     : {'N' : 1, 'O' : 1, 'H' : 1}
}

available = {
    'H'  : 2,
    'O'  : 1,
    'Cl' : 8,
    'N'  : 2
}

makeable = nanotech(possible, available)
assert makeable == ['nitrosyl hydride', 'water']

If either of its input arguments is badly formatted, nanotech must raise a ValueError exception:

from nanotech import nanotech
try:
    # arguments are backward!
    nanotech({'H' : 2}, {'hydrogen' : {'H' : 2}})
    assert False, 'failed to throw exception'

except ValueError, e:
    pass    # threw the right kind of exception

except:
    assert False, 'threw the wrong kind of exception'

Your job is to write unit tests for nanotech using Python's unittest module. Create a file in your ex04 directory called nanotest.py, and fill in the skeleton shown below:

import unittest
from nanotech import nanotech

class NanotechTests(unittest.TestCase):
    ...your code goes here...

if __name__ == '__main__':
    unittest.main()

If you find any ambiguities in this exercise specification, add comments to the top of nanotest.py describing the ambiguity, and what you have assumed the correct behavior should be (i.e., what your unit tests are testing for).