Exercise 2.3 - Solution

Starting with this exercise, we will be adding features to the report.py program that you have been writing. The solution will list the complete version of the program which you can copy if you become stuck. Changes related to the exercise are enclosed by comments.

(b) Collecting Data

# report.py
import csv

def read_portfolio(filename):
    '''
    Read a stock portfolio file into a list of dictionaries with keys
    name, shares, and price.
    '''
    portfolio = []
    f = open(filename)
    f_csv = csv.reader(f)
    headers = next(f_csv)

    for row in f_csv:
        stock = {
             'name'   : row[0],
             'shares' : int(row[1]),
             'price'   : float(row[2])
        }
        portfolio.append(stock)
    f.close()
    return portfolio

def read_prices(filename):
    '''
    Read a CSV file of price data into a dict mapping names to prices.
    '''
    prices = {}
    f = open(filename)
    f_csv = csv.reader(f)
    for row in f_csv:
        try:
            prices[row[0]] = float(row[1])
        except IndexError:
            pass
    f.close()
    return prices

# ---- ADDED CODE
def make_report(portfolio, prices):
    '''
    Make a list of (name, shares, price, change) tuples given a portfolio list
    and prices dictionary.
    '''
    rows = []
    for stock in portfolio:
        current_price = prices[stock['name']]
        change        = current_price - stock['price']
        summary       = (stock['name'], stock['shares'], current_price, change)
        rows.append(summary)
    return rows
# ---- END

# Read data files and create the report data

portfolio = read_portfolio('Data/portfolio.csv')
prices    = read_prices('Data/prices.csv')

# ---- ADDED
# Generate the report data

report    = make_report(portfolio, prices)
for row in report:
    print row
# ---- END

(c) Making a formatted table

# report.py

...

# Read data files and create the report data

portfolio = read_portfolio('Data/portfolio.csv')
prices    = read_prices('Data/prices.csv')

# Generate the report data
report    = make_report(portfolio, prices)

# ---- CHANGED
# Print a table
for row in report:
    print '%10s %10d %10.2f %10.2f' % row
# ---- END

(d) Adding some headers

Here are different ways to output headers:

headers = ('Name','Shares','Price','Change')

# Solution 1:  An obvious, but hard-coded approach

print '%10s %10s %10s %10s' % headers
print '---------- ---------- ---------- ----------'

# Solution 2: An algorithmic approach

for h in headers:
    print '%10s' % h,     # Note: , omits the newline
print
print ('-'*10 + ' ')*len(headers)

# Solution 3: Use the string replication operator

# This next line takes a format string like '%10s' and replicates it into
# a format string like '%10s %10s %10s ...'.  We then format that against a
# tuple of the headers

print ('%10s '*len(headers)) % headers
print ('-'*10 + ' ')*len(headers)

Complete Solution

# report.py
import csv

def read_portfolio(filename):
    '''
    Read a stock portfolio file into a list of dictionaries with keys
    name, shares, and price.
    '''
    portfolio = []
    f = open(filename)
    f_csv = csv.reader(f)
    headers = next(f_csv)

    for row in f_csv:
        stock = {
             'name'   : row[0],
             'shares' : int(row[1]),
             'price'   : float(row[2])
        }
        portfolio.append(stock)
    f.close()
    return portfolio

def read_prices(filename):
    '''
    Read a CSV file of price data into a dict mapping names to prices.
    '''
    prices = {}
    f = open(filename)
    f_csv = csv.reader(f)
    for row in f_csv:
        try:
            prices[row[0]] = float(row[1])
        except IndexError:
            pass
    f.close()
    return prices

def make_report(portfolio, prices):
    '''
    Make a list of (name, shares, price, change) tuples given a portfolio list
    and prices dictionary.
    '''
    rows = []
    for stock in portfolio:
        current_price = prices[stock['name']]
        change        = current_price - stock['price']
        summary       = (stock['name'], stock['shares'], current_price, change)
        rows.append(summary)
    return rows

# Read data files and create the report data

portfolio = read_portfolio('Data/portfolio.csv')
prices    = read_prices('Data/prices.csv')

# Generate the report data

report    = make_report(portfolio, prices)

# Output the report
headers = ('Name', 'Shares', 'Price', 'Change')
print '%10s %10s %10s %10s' % headers
print ('-' * 10 + ' ') * len(headers)
for row in report:
    print '%10s %10d %10.2f %10.2f' % row

[ Back ]