|
1 | 1 | # report.py |
2 | 2 | # |
3 | | -# Exercise 3.18 |
4 | | -import csv |
5 | | -from pprint import pprint |
| 3 | +# Exercise 3.18 4.4 |
| 4 | +# import csv |
| 5 | +# from pprint import pprint |
6 | 6 | from fileparse import parse_csv |
| 7 | +import stock |
| 8 | +import tableformat |
7 | 9 |
|
8 | 10 |
|
9 | 11 | def read_prices(filename): |
10 | | - """Reads stock prices from a CSV file and returns a dictionary mapping stock names to prices.""" |
| 12 | + """ |
| 13 | + Reads stock prices from a CSV file (stockname, price). |
| 14 | + Returns a dictionary mapping stock names to prices. |
| 15 | + """ |
11 | 16 |
|
12 | 17 | with open(filename) as f: |
13 | 18 | prices = parse_csv(f, types=[str,float], has_headers=False) |
14 | 19 | return dict(prices) |
15 | 20 |
|
16 | 21 | def read_portfolio(filename): |
17 | | - """Reads a stock portfolio from a CSV file with handling for missing files and returns a list.""" |
| 22 | + """ |
| 23 | + Reads a stock portfolio from a CSV file with handling for missing files. |
| 24 | + Returns a list of Stock objects. |
| 25 | + """ |
18 | 26 |
|
19 | 27 | with open(filename) as f: |
20 | | - portfolio = parse_csv(f, types=[str, int, float]) |
| 28 | + portdicts = parse_csv(f, types=[str, int, float]) |
| 29 | + |
| 30 | + portfolio = [ stock.Stock(d['name'], d['shares'], d['price']) for d in portdicts] |
21 | 31 | return portfolio |
22 | 32 |
|
23 | 33 | def make_report(portfolio, prices): |
24 | | - """Generates a report of the portfolio with current prices and gain/loss.""" |
| 34 | + ''' |
| 35 | + Returns a list of tuples containing (name, shares, current price, gain/loss) for each stock in the portfolio. |
| 36 | + |
| 37 | + :param portfolio: list of Stock objects representing the portfolio |
| 38 | + :param prices: dictionary mapping stock names to current prices |
| 39 | + ''' |
25 | 40 | report = [] |
26 | 41 | for stock in portfolio: |
27 | | - name = stock['name'] |
28 | | - shares = int(stock['shares']) |
29 | | - purchase_price = float(stock['price']) |
| 42 | + name = stock.name |
| 43 | + shares = int(stock.shares) |
| 44 | + purchase_price = float(stock.price) |
30 | 45 | current_price = prices.get(name, 0.0) # this is why prices must be a dict, not a list |
31 | 46 | gain_loss = (current_price - purchase_price) |
32 | 47 | report.append((name, shares, current_price, gain_loss)) |
33 | 48 | return report |
34 | 49 |
|
35 | | -def print_report(report): |
36 | | - """Prints the report in a formatted manner.""" |
37 | | - print('%10s %10s %10s %10s' % ('Name', 'Shares', 'Price', 'Change')) |
38 | | - print(('-' * 10 + ' ') * 4) |
39 | | - for name, shares, price, change in report: |
40 | | - dollar_price = f'${price:0.2f}' |
41 | | - print(f'{name:>10s} {shares:>10d} {dollar_price:>10s} {change:>10.2f}') |
| 50 | +def print_report(reportdata, formatter): |
| 51 | + ''' |
| 52 | + Print a nicely formatted table from a list of (name, shares, price, change) tuples. |
| 53 | + ''' |
| 54 | + |
| 55 | + formatter.headings(['Name','Shares','Price ($)','Change ($)']) |
42 | 56 |
|
43 | | -def portfolio_report(portfolio_file, prices_file): |
44 | | - """Generates and prints a portfolio report from given files.""" |
| 57 | + # for name, shares, price, change in reportdata: |
| 58 | + # dollar_price = f'${price:0.2f}' |
| 59 | + # print(f'{name:>10s} {shares:>10d} {dollar_price:>10s} {change:>10.2f}') |
| 60 | + for name, shares, price, change in reportdata: |
| 61 | + rowdata = [ name, str(shares), f'{price:0.2f}', f'{change:0.2f}' ] |
| 62 | + formatter.row(rowdata) |
45 | 63 |
|
| 64 | +def portfolio_report(portfolio_file, prices_file, fmt='txt'): |
| 65 | + ''' |
| 66 | + Generates and prints a report of the portfolio with current prices and gain/loss. |
| 67 | + |
| 68 | + :param portfolio_file: a file containing the portfolio data (stock name, shares, purchase price) |
| 69 | + :param prices_file: a file containing the current stock prices (stock name, price) |
| 70 | + ''' |
| 71 | + # Read current prices and portfolio data |
46 | 72 | curr_prices = read_prices(prices_file) |
47 | 73 | portfolio = read_portfolio(portfolio_file) |
48 | | - |
| 74 | + |
49 | 75 | if portfolio is None: |
50 | 76 | return # Exit if portfolio file was not found |
51 | 77 |
|
| 78 | + # Generate the report data |
52 | 79 | report = make_report(portfolio, curr_prices) |
53 | | - print_report(report) |
| 80 | + |
| 81 | + # Print the report using a table formatter |
| 82 | + formatter = tableformat.create_formatter(fmt) |
| 83 | + print_report(report, formatter) |
54 | 84 |
|
55 | 85 |
|
56 | 86 | def main(argv): |
57 | 87 | pf_file = argv[1] |
58 | 88 | price_file = argv[2] |
59 | | - |
60 | | - portfolio_report(pf_file, price_file) |
| 89 | + fmt = argv[3] if len(argv) > 3 else 'txt' # Default to 'txt' if format is not provided |
| 90 | + |
| 91 | + portfolio_report(pf_file, price_file, fmt) |
61 | 92 |
|
62 | 93 |
|
63 | 94 | if __name__ == '__main__': |
64 | 95 | import sys |
65 | 96 | # print('hey this file is executed as main, not imported') |
66 | | - if len(sys.argv) != 3: |
67 | | - raise SystemExit(f'Usage: {sys.argv[0]} ' 'portfile pricefile') |
| 97 | + if len(sys.argv) not in [3, 4]: |
| 98 | + raise SystemExit(f'Usage: {sys.argv[0]} ' 'portfile pricefile <format>') |
68 | 99 |
|
69 | 100 | main(sys.argv) |
70 | 101 |
|
|
0 commit comments