Run update21, if you haven't already, to create the
cs21/labs/09 directory. Then cd into
your cs21/labs/09 directory and edit the python
program contributions.py for lab 09 in this directory
(handin21 looks for your lab 09 assignments in
your cs21/labs/09 directory).
$ update21
$ cd cs21/labs/09
$ emacs contributions.py
Campaign Contribution Database
The 2016 Presidential Election is shaping up to be a historically
unusual and expensive election. In this lab we
will examine the role campaign contributions play in the election by
looking at individual contributions for each of the five remaining
major party Presidential candidates: Hillary Clinton, Ted Cruz, John
Kasich, Bernie Sanders, and Donald Trump.
Your assignment this week is to create a program
(contributions.py) that allows the user to explore our
campaign contribution database. We've provided some function
implementations and some stubbed out functions to get you started.
The contribution database is contained in a single
file, /usr/local/doc/contributions2016.txt, which contains
individual contribution data for the candidates aggregated by zip
code.
Each line of the file contains seven fields separated by commas:
- ZIP code
- city name
- state
- candidate last name
- candidate first name
- number of contributions received by this candidate from this zip code
- total amount of contributions received by this candidate from this zip code
The entries for Swarthmore are shown here:
19081,SWARTHMORE,PA,Clinton,Hillary,2,2700
19081,SWARTHMORE,PA,Sanders,Bernie,1,500
If there is no entry for a candidate, he/she received no
contributions from this zip code. If you want, you can look at the
file with the more command (use the space bar to see the next screenful of data or type 'q' to quit):
$ more /usr/local/doc/contributions2016.txt
Note: We also provided you with two smaller datasets to aid in
the development/debugging process. The file
small2016.txt
contains only contribution information from Delaware. The
file
med2016.txt contains only contributions from states in
the Northeast. Feel free to use either of these as you are
implementing your program.
High-level Requirements
Your program should prompt the user for either a zip code, at least
part of a city name, or a two letter state abbreviation. Depending on
what the user types, your program should do one of the following:
- If given a zip code:
- If the zip code is found in the contribution data, show the
information for that zip code sorted in descending order of
campaign contributions.
- If given a city name (or part of a city name):
- Give the following information about all cities that start
with that name, sorted by zip code:
- City name, State, Zip code, total contribution for Democrats,
total contribution for Republicans.
- If the total contribution
for Democrats and Republicans is zero for a zip code
matching the city name, do not print out the city.
- If given a state:
Getting Started
Edit the
file contributions.py where you will implement your
application. The file includes a few completed functions, some
stubbed out functions, and a unit test for the sorting function you'll
need to implement.
Read through the explanation below
and
follow the instructions highlighted in
blue.
- First, implement the listSort(L, index) function. This
function should take a list of lists L as well as
an index, and perform a sorting of L, using
the index elements of each sublist for comparisons. For
example, suppose the list of lists was:
L = [["apple",4], ["pear", 1], ["banana", 3], ["mango", 2]]
We could sort L by fruit or by number. After
calling listSort(L, 0), then L would be sorted as:
L = [["apple",4], ["banana", 3], ["mango", 2], ["pear", 1]]
After calling listSort(L, 1), then L would be sorted as:
L = [["pear", 1], ["mango", 2], ["banana", 3], ["apple",4]]
Before moving on to the second step, test your sort
implementation using the unit test we provided and any other tests you
can think of. Are you sure your sort function is working correctly?
- Second, implement your main function using top-down
design. We have provided you with some completed functions, as well
as some stubbed-out functions. At this stage of development, you
should call the functions we've given you, but don't implement
the stubbed out functions yet.
We've given you the following completed functions:
- getContributions(fileName) This function generates a list
of campaign contributions sorted by zip
code. getContributions takes in a name of the file
containing the campaign contribution database and returns its entries
in a list.
- getLatLong(fileName) This function loads a database of
latitude/longitude information from the given file into a dictionary.
The dictionary takes a zip code string as a key and
stores [latitude, longitude] values.
We have also provided the following stubbed-out functions:
- zipQuery(contributions) Given a list of campaign
contributions sorted by zip code, this function should perform all
functionality associated with a user asking the database for
information about a single zip code.
- cityQuery(contributions) Given a list of campaign
contributions, this function should perform all functionality
associated with a user asking for information about a city.
- stateQuery(contributions, zips) Given a list of
contributions and zip code locations, this function should perform all
functionality associated with a user asking for information about a
state. Use getLatLong to get the zip code locations.
Note: If designed correctly, your program at this stage
should do little more than load data from files, ask the user what
they want to do, and return dummy values.
- Implement the stubbed out functions one at a time, and test
them before moving to the next function. First, make sure the user is
able to enter a zip code and get back campaign information about that
zip code. Second, allow the user to enter a city and receive
information about campaign contributions from that city for each
party. Third, allow the user to enter a two-letter state code and
retrieve the top contribution zip codes for each user.
- Finally, when
all else has been implemented, enable the graphics portion of this
lab.
Requirements and tips
As always, you should practice good top-down design, incrementally
implement and test your solution, and document your code with
comments. The requirements below are designed to avoid some headaches
in initial design.
- Use the functions getContributions
and getLatLong to read in the contribution and zip code
databases from files. The file /usr/local/doc/zipcodes.txt
contains latitude and longitude information for each zip code.
- Make sure to validate any input from a user. For example, when
the user decides to enter a zip code, make sure it is a
5-digit numeric string.
- Your searches (e.g., for zip codes or for cities in a state) can
be done using linear or binary search, but must be reasonably
efficient. Even on the complete data set, searches should take no
more than a couple of seconds.
- If you cannot find an entry for a particular zip code or city,
inform the user that you cannot find that location.
- For reporting the top contribution zip codes in each state, you
should sort the data from largest to smallest in total contributions.
- Store the zip codes as strings---some zip codes begin with zeros,
and python removes leading zeros from integers.
- Again, we encourage you to finish the text part of this program
before you add the graphics. This makes testing easier and faster.
Once you have the text portion working, then add the graphics.
- To plot state boundaries, we have written some functions to create
the graphics window with the state boundary already drawn. Your
program must then draw points for all zip codes, using the
latitude as the y coordinate and the longitude as the x
coordinate. You should also show which party collected more
contributions from this zip code by drawing a red point for Republican
zip codes and a blue point for Democratic zip codes. If neither
candidate received contributions from a zip code, do not plot a point.
- The graphics for state boundaries is in the boundaries
module. An example usage lies below:
from boundaries import *
state = "PA"
statewin = getStateGraphWin(state)
if statewin != None:
#plot all zip codes for this state here, using lat/long for each zip code
...
statewin.getMouse()
statewin.close()
- Print a message to the user (on the screen, not in the window)
telling them to click anywhere on the graph window to continue.
- Two-letter state codes are:
states = ["AL", "AK", "AZ", "AR", "CA", "CO", "CT", "DC", "DE",
"FL", "GA", "HI", "ID", "IL", "IN", "IA", "KS", "KY",
"LA", "MD", "ME", "MA", "MI", "MN", "MS", "MO", "MT",
"NE", "NV", "NH", "NJ", "NM", "NY", "ND", "NC", "OH", "OR",
"OK", "PA", "RI", "SC", "SD", "TN", "TX", "UT", "VT",
"VA", "WA", "WV", "WI", "WY"]
Example code runs
Here are some sample code runs, using each of the provided data sets:
Data Sources
The contribution data used in this lab was compiled
from opensecrets.org. We tried to compile this data as
accurately as possible, but there are likely inaccuracies and
omissions. Use this data for entertainment purposes only. :) If you
are interested in learning more about money in politics,
opensecrets.org is a great resource to help you learn more.
Submit
Once you are satisfied with your program, hand it in by typing
handin21 in a terminal window.
include("../style/footer.php"); ?>