Skip to content

Commit 8767793

Browse files
committed
end of 4.2
1 parent 05ef82e commit 8767793

6 files changed

Lines changed: 161 additions & 28 deletions

File tree

Work/fileparse.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,8 @@
66

77
def parse_csv(source, select=None, types=None, has_headers=True, delimiter=',', silence_errors=True):
88
'''
9-
Parse an iterable into a list of records
9+
Parse an iterable source into a list of records.
10+
Returns a list of dictionaries if has_headers is True, otherwise returns a list of tuples.
1011
'''
1112
if type(source) == str:
1213
raise RuntimeError("source must be iterable")

Work/pcost314.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,8 @@ def portfolio_cost(filename):
77
''' Calculate total cost of portofolio '''
88

99
portfolio = read_portfolio(filename)
10-
return sum([i['shares'] * i['price'] for i in portfolio ])
10+
# return sum([i['shares'] * i['price'] for i in portfolio ])
11+
return sum([i.shares * i.price for i in portfolio ])
1112

1213

1314

Work/report.py

Lines changed: 56 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -1,70 +1,101 @@
11
# report.py
22
#
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
66
from fileparse import parse_csv
7+
import stock
8+
import tableformat
79

810

911
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+
"""
1116

1217
with open(filename) as f:
1318
prices = parse_csv(f, types=[str,float], has_headers=False)
1419
return dict(prices)
1520

1621
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+
"""
1826

1927
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]
2131
return portfolio
2232

2333
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+
'''
2540
report = []
2641
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)
3045
current_price = prices.get(name, 0.0) # this is why prices must be a dict, not a list
3146
gain_loss = (current_price - purchase_price)
3247
report.append((name, shares, current_price, gain_loss))
3348
return report
3449

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 ($)'])
4256

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)
4563

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
4672
curr_prices = read_prices(prices_file)
4773
portfolio = read_portfolio(portfolio_file)
48-
74+
4975
if portfolio is None:
5076
return # Exit if portfolio file was not found
5177

78+
# Generate the report data
5279
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)
5484

5585

5686
def main(argv):
5787
pf_file = argv[1]
5888
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)
6192

6293

6394
if __name__ == '__main__':
6495
import sys
6596
# 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>')
6899

69100
main(sys.argv)
70101

Work/tableformat.py

Lines changed: 80 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,80 @@
1+
2+
3+
class TableFormatter:
4+
def headings(self, headers):
5+
'''
6+
Emit the table headings.
7+
'''
8+
raise NotImplementedError()
9+
10+
def row(self, rowdata):
11+
'''
12+
Emit a single row of table data.
13+
'''
14+
raise NotImplementedError()
15+
16+
17+
18+
class TextTableFormatter(TableFormatter):
19+
'''
20+
Emit a table in plain-text format
21+
'''
22+
def headings(self, headers):
23+
for h in headers:
24+
print(f'{h:>10s}', end=' ')
25+
print()
26+
print(('-'*10 + ' ')*len(headers))
27+
28+
def row(self, rowdata):
29+
for d in rowdata:
30+
print(f'{d:>10s}', end=' ')
31+
print()
32+
33+
34+
35+
class CSVTableFormatter(TableFormatter):
36+
'''
37+
Emit a table in CSV format
38+
'''
39+
def headings(self, headers):
40+
print(','.join(headers))
41+
42+
def row(self, rowdata):
43+
print(','.join(rowdata))
44+
45+
46+
47+
class HTMLTableFormatter(TableFormatter):
48+
'''
49+
Emit a table in HTML format
50+
'''
51+
def headings(self, headers):
52+
print('<tr>', end='')
53+
for h in headers:
54+
print(f'<th>{h}</th>', end='')
55+
print('</tr>')
56+
57+
def row(self, rowdata):
58+
print('<tr>', end='')
59+
for d in rowdata:
60+
print(f'<td>{d}</td>', end='')
61+
print('</tr>')
62+
63+
64+
65+
def create_formatter(fmt):
66+
'''
67+
Factory function to create a formatter based on the specified format.
68+
69+
:param fmt: A string indicating the desired format ('txt', 'csv', 'html').
70+
:return: An instance of a TableFormatter subclass corresponding to the specified format.
71+
'''
72+
if fmt == 'txt':
73+
return TextTableFormatter()
74+
elif fmt == 'csv':
75+
return CSVTableFormatter()
76+
elif fmt == 'html':
77+
return HTMLTableFormatter()
78+
else:
79+
raise ValueError(f'Unknown format: {fmt}')
80+

Work/using_pcost.py

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
2+
import pcost314 as pcost
3+
# from pcost314 import portfolio_cost
4+
5+
cost = pcost.portfolio_cost('.\\Data\\portfolio.csv')
6+
# cost = portfolio_cost('.\\Data\\portfolio.csv')
7+
8+
print(f'Total cost of portfolio: ${cost:0.2f}')
9+

Work/using_report.py

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,10 @@
11
import report as r
2+
import stock
3+
24

3-
# pf_files = ['.\\Data\\portfolio.csv', '.\\Data\\portfolio2.csv']
45
price_file = '.\\Data\\prices.csv'
56

7+
# pf_files = ['.\\Data\\portfolio.csv', '.\\Data\\portfolio2.csv']
68
# for file in pf_files:
79
# print(f'{file:-^43s}')
810
# r.portfolio_report(file, price_file)
@@ -13,3 +15,12 @@
1315
print(f'{pf_file:-^43s}')
1416
r.portfolio_report(pf_file, price_file)
1517
print()
18+
19+
r.portfolio_report(pf_file, price_file, 'html')
20+
print()
21+
22+
r.portfolio_report(pf_file, price_file, 'csv')
23+
print()
24+
25+
# r.portfolio_report(pf_file, price_file, 'aku_ankka')
26+

0 commit comments

Comments
 (0)