Added solution code

This commit is contained in:
David Beazley
2020-05-27 17:03:35 -05:00
parent 960d4fa2fa
commit 5b6f15db17
136 changed files with 5828 additions and 350 deletions

View File

@@ -0,0 +1,47 @@
# fileparse.py
import csv
def parse_csv(lines, select=None, types=None, has_headers=True, delimiter=',', silence_errors=False):
'''
Parse a CSV file into a list of records with type conversion.
'''
if select and not has_headers:
raise RuntimeError('select requires column headers')
rows = csv.reader(lines, delimiter=delimiter)
# Read the file headers (if any)
headers = next(rows) if has_headers else []
# If specific columns have been selected, make indices for filtering and set output columns
if select:
indices = [ headers.index(colname) for colname in select ]
headers = select
records = []
for rowno, row in enumerate(rows, 1):
if not row: # Skip rows with no data
continue
# If specific column indices are selected, pick them out
if select:
row = [ row[index] for index in indices]
# Apply type conversion to the row
if types:
try:
row = [func(val) for func, val in zip(types, row)]
except ValueError as e:
if not silence_errors:
print(f"Row {rowno}: Couldn't convert {row}")
print(f"Row {rowno}: Reason {e}")
continue
# Make a dictionary or a tuple
if headers:
record = dict(zip(headers, row))
else:
record = tuple(row)
records.append(record)
return records

20
Solutions/5_8/pcost.py Normal file
View File

@@ -0,0 +1,20 @@
# pcost.py
import report
def portfolio_cost(filename):
'''
Computes the total cost (shares*price) of a portfolio file
'''
portfolio = report.read_portfolio(filename)
return sum([s.cost() for s in portfolio])
def main(args):
if len(args) != 2:
raise SystemExit('Usage: %s portfoliofile' % args[0])
filename = args[1]
print('Total cost:', portfolio_cost(filename))
if __name__ == '__main__':
import sys
main(sys.argv)

71
Solutions/5_8/report.py Normal file
View File

@@ -0,0 +1,71 @@
# report.py
import fileparse
from stock import Stock
import tableformat
def read_portfolio(filename):
'''
Read a stock portfolio file into a list of dictionaries with keys
name, shares, and price.
'''
with open(filename) as lines:
portdicts = fileparse.parse_csv(lines,
select=['name','shares','price'],
types=[str,int,float])
portfolio = [ Stock(d['name'], d['shares'], d['price']) for d in portdicts ]
return portfolio
def read_prices(filename):
'''
Read a CSV file of price data into a dict mapping names to prices.
'''
with open(filename) as lines:
return dict(fileparse.parse_csv(lines, types=[str,float], has_headers=False))
def make_report_data(portfolio, prices):
'''
Make a list of (name, shares, price, change) tuples given a portfolio list
and prices dictionary.
'''
rows = []
for s in portfolio:
current_price = prices[s.name]
change = current_price - s.price
summary = (s.name, s.shares, current_price, change)
rows.append(summary)
return rows
def print_report(reportdata, formatter):
'''
Print a nicely formated table from a list of (name, shares, price, change) tuples.
'''
formatter.headings(['Name','Shares','Price','Change'])
for name, shares, price, change in reportdata:
rowdata = [ name, str(shares), f'{price:0.2f}', f'{change:0.2f}' ]
formatter.row(rowdata)
def portfolio_report(portfoliofile, pricefile, fmt='txt'):
'''
Make a stock report given portfolio and price data files.
'''
# Read data files
portfolio = read_portfolio(portfoliofile)
prices = read_prices(pricefile)
# Create the report data
report = make_report_data(portfolio, prices)
# Print it out
formatter = tableformat.create_formatter(fmt)
print_report(report, formatter)
def main(args):
if len(args) != 4:
raise SystemExit('Usage: %s portfile pricefile format' % args[0])
portfolio_report(args[1], args[2], args[3])
if __name__ == '__main__':
import sys
main(sys.argv)

37
Solutions/5_8/stock.py Normal file
View File

@@ -0,0 +1,37 @@
# stock.py
class Stock(object):
'''
An instance of a stock holding consisting of name, shares, and price.
'''
__slots__ = ('name','_shares','price')
def __init__(self,name, shares, price):
self.name = name
self.shares = shares
self.price = price
def __repr__(self):
return f'Stock({self.name!r}, {self.shares!r}, {self.price!r})'
@property
def shares(self):
return self._shares
@shares.setter
def shares(self, value):
if not isinstance(value,int):
raise TypeError("Must be integer")
self._shares = value
@property
def cost(self):
'''
Return the cost as shares*price
'''
return self.shares * self.price
def sell(self, nshares):
'''
Sell a number of shares and return the remaining number.
'''
self.shares -= nshares

View File

@@ -0,0 +1,81 @@
# tableformat.py
class TableFormatter(object):
def headings(self, headers):
'''
Emit the table headers
'''
raise NotImplementedError()
def row(self, rowdata):
'''
Emit a single row of table data
'''
raise NotImplementedError()
class TextTableFormatter(TableFormatter):
'''
Output data in plain-text format.
'''
def headings(self, headers):
for h in headers:
print(f'{h:>10s}', end=' ')
print()
print(('-'*10 + ' ')*len(headers))
def row(self, rowdata):
for d in rowdata:
print(f'{d:>10s}', end=' ')
print()
class CSVTableFormatter(TableFormatter):
'''
Output data in CSV format.
'''
def headings(self, headers):
print(','.join(headers))
def row(self, rowdata):
print(','.join(rowdata))
class HTMLTableFormatter(TableFormatter):
'''
Output data in HTML format.
'''
def headings(self, headers):
print('<tr>', end='')
for h in headers:
print(f'<th>{h}</th>', end='')
print('</tr>')
def row(self, rowdata):
print('<tr>', end='')
for d in rowdata:
print(f'<td>{d}</td>', end='')
print('</tr>')
class FormatError(Exception):
pass
def create_formatter(name):
'''
Create an appropriate formatter given an output format name
'''
if name == 'txt':
return TextTableFormatter()
elif name == 'csv':
return CSVTableFormatter()
elif name == 'html':
return HTMLTableFormatter()
else:
raise FormatError(f'Unknown table format {name}')
def print_table(objects, columns, formatter):
'''
Make a nicely formatted table from a list of objects and attribute names.
'''
formatter.headings(columns)
for obj in objects:
rowdata = [ str(getattr(obj, name)) for name in columns ]
formatter.row(rowdata)