Skip to content

Commit 1f77047

Browse files
committed
end of 5.8
1 parent b2368c3 commit 1f77047

8 files changed

Lines changed: 76 additions & 54 deletions

File tree

Work/fileparse.py

Lines changed: 7 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44
import csv
55
import gzip
66

7-
def parse_csv(source, select=None, types=None, has_headers=True, delimiter=',', silence_errors=True):
7+
def parse_csv(source, select=None, types=None, has_headers=True, delimiter=',', silence_errors=False):
88
'''
99
Parse an iterable source into a list of records.
1010
Returns a list of dictionaries if has_headers is True, otherwise returns a list of tuples.
@@ -37,6 +37,7 @@ def parse_csv(source, select=None, types=None, has_headers=True, delimiter=',',
3737
for rownum, row in enumerate(rows, start=start):
3838

3939
if not row: # Skip rows with no data
40+
print(f'Row {rownum}: Empty row')
4041
continue
4142
try:
4243
# Filter the row if specific columns were selected
@@ -46,11 +47,12 @@ def parse_csv(source, select=None, types=None, has_headers=True, delimiter=',',
4647
# Convert types if a list of types is provided
4748
if types:
4849
row = [func(val) for func, val in zip(types, row) ]
50+
print(f'Row {rownum}: Converted row: {row}')
4951

5052
except ValueError as e:
5153
if not silence_errors:
5254
print(f"Row {rownum}: Could not convert: {row}")
53-
print(f"Row {rownum}: Reason: {e}\n")
55+
print(f"Row {rownum}: Reason: {e}")
5456
continue
5557

5658
# print(list(zip(headers, row)))
@@ -64,10 +66,10 @@ def parse_csv(source, select=None, types=None, has_headers=True, delimiter=',',
6466

6567

6668

67-
# with open('.\\Data\\portfolio.csv') as f:
68-
# d = parse_csv(f, types=[str, int, float])
69+
with open('.\\Data\\missing.csv') as f:
70+
d = parse_csv(f, types=[str, int, float])
6971

70-
# print(d)
72+
print(d)
7173

7274

7375

@@ -82,23 +84,3 @@ def parse_csv(source, select=None, types=None, has_headers=True, delimiter=',',
8284
# pf = parse_csv(lines, types=[str,int,float])
8385
# print(pf)
8486

85-
86-
87-
88-
# portfolio = parse_csv('.\\Data\\portfolio.csv', select=['name','shares'], types=[str, int])
89-
# print(portfolio)
90-
91-
# portfolio = parse_csv('.\\Data\\portfolio.csv', types=[str, int, float])
92-
# print(portfolio)
93-
94-
# prices = parse_csv('.\\Data\\prices.csv', types=[str,float], has_headers=False)
95-
# print(prices)
96-
97-
# portfolio = parse_csv('.\\Data\\portfolio.dat', types=[str, int, float], delimiter=' ')
98-
# print(portfolio)
99-
100-
# portfolio = parse_csv('.\\Data\\portfolio.csv', select=['name','shares'], types=[str, int], has_headers=False)
101-
# print(portfolio)
102-
103-
# portfolio = parse_csv('.\\Data\\missing.csv', types=[str, int, float], silence_errors=False)
104-
# print(portfolio)

Work/pcost215.py

Lines changed: 24 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44
#
55
import csv
66

7-
def portfolio_cost(filename):
7+
def portfolio_cost(filename, show_errors=False):
88
"""Calculates the total cost of a stock portfolio from a CSV file with handling for missing files."""
99
value = 0.0
1010
data_list = []
@@ -14,24 +14,35 @@ def portfolio_cost(filename):
1414
headers = next(rows) # take headers from first line
1515

1616
for linenum, line in enumerate(rows, start=2):
17+
if not line: # Skip empty lines
18+
if show_errors:
19+
print(f'Line {linenum}: Empty line')
20+
continue
21+
22+
types = [str, int, float]
23+
24+
try:
25+
line = [func(val) for func, val in zip(types, line) ]
26+
27+
except ValueError as e:
28+
if show_errors:
29+
# pass
30+
print(f'Line {linenum}: Bad line: {line}')
31+
print(f'Line {linenum}: Reason: {e}')
32+
continue
33+
1734
record = dict(zip(headers, line))
18-
# print(record)
19-
try:
20-
num_shares = int(record['shares'])
21-
price = float(record['price'])
22-
value += num_shares * price
23-
data_list.append(record)
24-
except ValueError:
25-
# print(f'Line {linenum}: Bad line: {line.strip()}')
26-
print(f'Line {linenum}: Bad line: {line}')
35+
data_list.append(record)
2736

2837
# return value
2938
return data_list
3039

3140

3241

3342

34-
cost = portfolio_cost('.\\Data\\missing.csv')
35-
# print(f'Total cost of portfolio: ${cost}')
36-
print(cost)
43+
pf = portfolio_cost('.\\Data\\missing.csv')
44+
print(pf)
45+
46+
cost = sum([int(i['shares']) * float(i['price']) for i in pf ])
47+
print(f'\nTotal cost of portfolio: ${cost:0.2f}')
3748

Work/pcost314.py

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,11 @@
44
from report import read_portfolio
55

66
def portfolio_cost(filename):
7-
''' Calculate total cost of portofolio '''
7+
''' Calculate total cost of portofolio
8+
9+
:param filename: The name of the CSV file containing the portfolio data.
10+
:return: The total cost of the portfolio as a float.
11+
'''
812

913
portfolio = read_portfolio(filename)
1014
# return sum([i['shares'] * i['price'] for i in portfolio ])

Work/stock.py

Lines changed: 20 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -2,20 +2,36 @@
22

33
class Stock:
44

5+
__slots__ = ['name', '_shares', 'price'] # this is an optimization to save memory, but it also prevents adding new attributes to the class
6+
57
def __init__(self, name, shares, price):
68
self.name = name
7-
self.shares = shares
9+
self.shares = shares # This assignment calls the setter below
810
self.price = price
911

10-
# without this, the print of a Stock object is not very informative
12+
# without __repr__, the print of a Stock object is not very informative
1113
# [<stock.Stock object at 0x0000022C9F7C9FD0>, <stock.Stock object at ...>]
1214

1315
def __repr__(self):
1416
return f"Stock({self.name}, {self.shares}, {self.price})"
17+
18+
# the shares property internally uses a private name, but the rest of the class can use the public name
19+
# goog.__dict__ gives: {'name': 'GOOG', '_shares': 100, 'price': 490.1}
20+
21+
@property
22+
def shares(self):
23+
return self._shares
24+
25+
@shares.setter
26+
def shares(self, shares):
27+
if not isinstance(shares, int) or shares < 0:
28+
raise ValueError('Shares must be non-negative integer')
29+
self._shares = shares
30+
1531

32+
@property
1633
def cost(self):
17-
cost = self.shares * self.price
18-
return cost
34+
return self.shares * self.price
1935

2036
def sell(self, cnt):
2137
self.shares -= cnt

Work/tableformat.py

Lines changed: 7 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -60,6 +60,8 @@ def row(self, rowdata):
6060
print(f'<td>{d}</td>', end='')
6161
print('</tr>')
6262

63+
class FormatError(Exception):
64+
pass
6365

6466

6567
def create_formatter(fmt):
@@ -76,25 +78,25 @@ def create_formatter(fmt):
7678
elif fmt == 'html':
7779
return HTMLTableFormatter()
7880
else:
79-
raise ValueError(f'Unknown format: {fmt}')
81+
raise FormatError(f'Unknown table format: {fmt}')
8082

8183

8284

83-
def print_table(data, cols, fmt):
85+
def print_table(data, cols, formatter):
8486
'''
8587
Print a table of data using the specified format.
8688
8789
:param data: A list of stock objects containing the table data.
8890
:param cols: A list of column names to be used as headings in the table.
89-
:param fmt: A string indicating the desired format ('txt', 'csv', 'html').
91+
:param formatter: A formatter object indicating the desired format (e.g. TextTableFormatter, ...)
9092
'''
9193

92-
fmt.headings(cols)
94+
formatter.headings(cols)
9395

9496
for d in data:
9597
rowdata = [str(getattr(d, colname)) for colname in cols] # ['AA', 100]
9698
# rowdata = [str(item) for item in rowdata] # ['AA', '100']
9799

98-
fmt.row(rowdata)
100+
formatter.row(rowdata)
99101

100102

Work/using_pcost.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
import pcost314 as pcost
33
# from pcost314 import portfolio_cost
44

5-
cost = pcost.portfolio_cost('.\\Data\\portfolio.csv')
5+
cost = pcost.portfolio_cost('.\\Data\\missing.csv')
66
# cost = portfolio_cost('.\\Data\\portfolio.csv')
77

88
print(f'Total cost of portfolio: ${cost:0.2f}')

Work/using_stock.py

Lines changed: 10 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,10 @@
44

55

66
a = stock.Stock('GOOG',100,490.10)
7-
print(a)
7+
8+
print(a) # Stock(GOOG, 100, 490.1)
9+
print(a.__dict__) # {'name': 'GOOG', '_shares': 100, 'price': 490.1}
10+
811
columns = ['name', 'shares']
912
for colname in columns:
1013
print(colname, '=', getattr(a, colname))
@@ -21,12 +24,16 @@
2124
for s in stocks:
2225
print(f'{s.name:>10s} {s.shares:>10d} {s.price:>10.2f}')
2326

24-
print(a.cost())
27+
# print(a.cost())
28+
print(a.cost) # because cost is now a property, not a method
2529

2630
a.sell(25)
2731

2832
print(a.shares)
29-
print(a.cost())
33+
# print(a.cost())
34+
print(a.cost)
35+
36+
3037

3138

3239

Work/using_tableformat.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,6 @@
1111

1212
tf.print_table(portfolio, ['name','shares'], formatter)
1313

14-
tf.print_table(portfolio, ['name','shares','price'], formatter)
14+
# tf.print_table(portfolio, ['name','shares','price'], formatter)
1515

16-
tf.print_table(portfolio, ['name','price'], formatter)
16+
# tf.print_table(portfolio, ['name','price'], formatter)

0 commit comments

Comments
 (0)