Skip to content

Commit e81ffc9

Browse files
authored
Merge pull request #2 from altvec/chapter2-3
Complete chapter 2 and 3 exercises
2 parents f1678d0 + 5835b01 commit e81ffc9

File tree

3 files changed

+123
-20
lines changed

3 files changed

+123
-20
lines changed

Work/fileparse.py

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,46 @@
11
# fileparse.py
22
#
33
# Exercise 3.3
4+
5+
import csv
6+
7+
8+
def parse_csv(
9+
lines,
10+
select=None,
11+
types=None,
12+
has_headers=True,
13+
delimiter=',',
14+
silence_errors=False,
15+
):
16+
'''
17+
Parse a CSV file into a list of records.
18+
'''
19+
if select and not has_headers:
20+
raise RuntimeError("Select argument requires column headers")
21+
22+
rows = csv.reader(lines, delimiter=delimiter)
23+
headers = next(rows) if has_headers else []
24+
if select:
25+
indices = [headers.index(colname) for colname in select]
26+
headers = select
27+
records = []
28+
for idx, row in enumerate(rows, start=1):
29+
if not row:
30+
continue
31+
if select:
32+
row = [row[index] for index in indices]
33+
if types:
34+
try:
35+
row = [func(val) for func, val in zip(types, row)]
36+
except ValueError as e:
37+
if not silence_errors:
38+
print(f'Row {idx}: Couldn\'t convert {row}')
39+
print(f'Row {idx}: Reason {e}')
40+
continue
41+
if headers:
42+
record = dict(zip(headers, row))
43+
else:
44+
record = tuple(row)
45+
records.append(record)
46+
return records

Work/pcost.py

100644100755
Lines changed: 14 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -1,30 +1,24 @@
1+
#!/usr/bin/env python3
2+
13
# pcost.py
24
#
35
# Exercise 1.27
4-
import sys
5-
import csv
6+
from report import read_portfolio
67

78

89
def portfolio_cost(filename):
910
'''Return portfolio cost.'''
10-
cost = 0.0
11-
with open(filename, 'rt') as file:
12-
rows = csv.reader(file)
13-
next(rows) # skip headers
14-
for row in rows:
15-
_, shares, price = row
16-
try:
17-
cost += int(shares) * float(price)
18-
except ValueError as err:
19-
print(err)
20-
continue
21-
return cost
11+
portfolio = read_portfolio(filename)
12+
return sum([s['shares'] * s['price'] for s in portfolio])
13+
2214

15+
def main(args):
16+
if len(args) != 2:
17+
raise SystemExit(f'Usage: {args[0]} portfolio_file')
18+
cost = portfolio_cost(args[1])
19+
print(f'Total cost: {cost}')
2320

24-
if len(sys.argv) == 2:
25-
filename = sys.argv[1]
26-
else:
27-
filename = '../Work/Data/portfolio.csv'
2821

29-
cost = portfolio_cost(filename)
30-
print(f'Total cost: {cost}')
22+
if __name__ == '__main__':
23+
import sys
24+
main(sys.argv)

Work/report.py

100644100755
Lines changed: 66 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,69 @@
1+
#!/usr/bin/env python3
2+
13
# report.py
24
#
35
# Exercise 2.4
6+
from fileparse import parse_csv
7+
8+
9+
def read_portfolio(filename):
10+
'''
11+
Read a stock portfolio file into a list of dictionaries with keys
12+
name, shares, and price.
13+
'''
14+
with open(filename) as lines:
15+
return parse_csv(
16+
lines,
17+
select=['name', 'shares', 'price'],
18+
types=[str, int, float],
19+
)
20+
21+
22+
def read_prices(filename):
23+
'''
24+
Read a CSV file of price data into a dict mapping names to prices.
25+
'''
26+
with open(filename) as lines:
27+
return dict(parse_csv(lines, types=[str, float], has_headers=False))
28+
29+
30+
def make_report(portfolio, prices):
31+
report = []
32+
for s in portfolio:
33+
current_price = prices[s['name']]
34+
change = current_price - s['price']
35+
report.append(
36+
(s['name'], s['shares'], current_price, change)
37+
)
38+
return report
39+
40+
41+
def print_report(report):
42+
'''
43+
Print formatted report.
44+
'''
45+
headers = ('Name', 'Shares', 'Price', 'Change')
46+
print('%10s %10s %10s %10s' % headers)
47+
print(('-' * 10 + ' ') * len(headers))
48+
for row in report:
49+
print('%10s %10d %10.2f %10.2f' % row)
50+
51+
52+
def portfolio_report(portfolio_file, prices_file):
53+
'''
54+
Make a stock report given portfolio and price data files.
55+
'''
56+
portfolio = read_portfolio(portfolio_file)
57+
prices = read_prices(prices_file)
58+
report = make_report(portfolio, prices)
59+
print_report(report)
60+
61+
62+
def main(args):
63+
if len(args) != 3:
64+
raise SystemExit(f'Usage: {args[0]} portfolio_file prices_file')
65+
portfolio_report(args[1], args[2])
66+
67+
if __name__ == '__main__':
68+
import sys
69+
main(sys.argv)

0 commit comments

Comments
 (0)