diff --git a/Work/bounce.py b/Work/bounce.py index 3660ddd82..107b3d9bc 100644 --- a/Work/bounce.py +++ b/Work/bounce.py @@ -1,3 +1,11 @@ # bounce.py # # Exercise 1.5 + +height = 100 +bounce = 1 + +while bounce <= 10: + height *= (3/5) + print(bounce, round(height, ndigits=4)) + bounce += 1 \ No newline at end of file diff --git a/Work/fileparse.py b/Work/fileparse.py index 1d499e733..b2347255f 100644 --- a/Work/fileparse.py +++ b/Work/fileparse.py @@ -1,3 +1,56 @@ # fileparse.py # # Exercise 3.3 + +import csv + +def parse_csv(obj, select=None, types=None, has_headers=True, delimiter=',', silence_errors=False): + ''' + Parse iterable non-string object as csv into a list of records + ''' + + if isinstance(obj, str): + raise RuntimeError("obj must be an interable non-string object") + + if select and has_headers == False: + raise RuntimeError("select argument requires column headers") + + rows = csv.reader(obj, delimiter=delimiter) + + if has_headers: + # Read the file headers + headers = next(rows) + + # If a column selector was given, find indices of the specified columns. + # Also narrow the set of headers used for resulting dictionaries + if select: + indices = [headers.index(colname) for colname in select] + headers = select + + records = [] + for i, row in enumerate(rows): + try: + if not row: # Skip rows with no data + continue + + # Filter the row if specific columns were selected + if select: + row = [row[index] for index in indices] + # Convert values if types were specified + if types: + row = [func(val) for func, val in zip(types, row)] + + if has_headers: + # Make a dictionary + record = dict(zip(headers, row)) + else: + # Make a tuple + record = tuple(row) + except ValueError as e: + if not silence_errors: + print(f'Row {i}: Couldn\'t convert {row}\nRow {i}: Reason: {e}') + continue + + records.append(record) + + return records \ No newline at end of file diff --git a/Work/mortgage.py b/Work/mortgage.py index d527314e3..566008bce 100644 --- a/Work/mortgage.py +++ b/Work/mortgage.py @@ -1,3 +1,14 @@ # mortgage.py # # Exercise 1.7 + +principal = 500000.0 +rate = 0.05 +payment = 2684.11 +total_paid = 0.0 + +while principal > 0: + principal = principal * (1+rate/12) - payment + total_paid = total_paid + payment + +print('Total paid', total_paid) \ No newline at end of file diff --git a/Work/pcost.py b/Work/pcost.py index e68aa20b4..76c9d98d5 100644 --- a/Work/pcost.py +++ b/Work/pcost.py @@ -1,3 +1,26 @@ # pcost.py # # Exercise 1.27 + +import csv +from report import read_portfolio + +def portfolio_cost(filename): + 'Get total portfolio cost of a csv' + + total = sum([s['price']*s['shares'] for s in read_portfolio(filename)]) + + return total + +def main(argv): + if len(argv) == 2: + filename = argv[1] + else: + filename = 'Data/portfolio.csv' + + print('Total cost', portfolio_cost(filename)) + + +if __name__ == '__main__': + import sys + main(sys.argv) \ No newline at end of file diff --git a/Work/report.py b/Work/report.py index 47d5da7b1..a98f26555 100644 --- a/Work/report.py +++ b/Work/report.py @@ -1,3 +1,85 @@ # report.py # # Exercise 2.4 + +import stock, tableformat +from fileparse import parse_csv + +def read_portfolio(filename): + 'Create list of stocks from csv' + + with open(filename) as file: + portdicts = parse_csv(file, select=['name', 'shares', 'price'], types=[str, int, float]) + + portfolio = [ stock.Stock(d['name'], d['shares'], d['price']) for d in portdicts] + + return portfolio + +def read_prices(filename): + 'Create dict from csv' + + with open(filename) as file: + prices = { name: price for name, price in parse_csv(file, types=[str, float], has_headers=False) } + + return prices + +def portfolio_cost(portfolio): + 'Get total portfolio cost' + + cost = 0 + + for holding in portfolio: + cost += holding.shares * holding.price + + return cost + +def current_value(portfolio, prices): + 'Get current value of portfolio' + + current_value = 0 + + for holding in portfolio: + if holding.name in prices: + current_value += holding.shares * prices[holding.name] + + return current_value + +def make_report_data(portfolio, prices): + 'Generate report' + + report = [] + + for holding in portfolio: + if holding.name in prices: + report.append(( + holding.name, + holding.shares, + prices[holding.name], + prices[holding.name] - holding.price + )) + + return report + +def print_report(reportdata, formatter): + 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(portfile, pricefile, fmt='txt'): + reportdata = make_report_data(read_portfolio(portfile), read_prices(pricefile)) + formatter = tableformat.create_formatter(fmt) + print_report(reportdata, formatter) + +def main(argv): + if len(argv) < 3 or len(argv) > 4: + print(f'Usage: {argv[0]} portfile pricefile [output_format]') + elif len(argv) == 3: + portfolio_report(argv[1], argv[2]) + else: + portfolio_report(argv[1], argv[2], argv[3]) + +if __name__ == '__main__': + import sys + main(sys.argv) \ No newline at end of file diff --git a/Work/sears.py b/Work/sears.py new file mode 100644 index 000000000..f5778dd21 --- /dev/null +++ b/Work/sears.py @@ -0,0 +1,13 @@ +bill_thickness = 0.11 * 0.001 # Meters (0.11 mm) +sears_height = 442 # Height (meters) +num_bills = 1 +day = 1 + +while num_bills * bill_thickness < sears_height: + print(day, num_bills, num_bills * bill_thickness) + day = day + 1 + num_bills = num_bills * 2 + +print('Number of days', day) +print('Number of bills', num_bills) +print('Final height', num_bills * bill_thickness) \ No newline at end of file diff --git a/Work/stock.py b/Work/stock.py new file mode 100644 index 000000000..a77e58717 --- /dev/null +++ b/Work/stock.py @@ -0,0 +1,22 @@ +class Stock: + def __init__(self, name, shares, price): + self.name = name + self.shares = shares + self.price = price + + def __repr__(self): + return f'Stock({self.name}, {self.shares}, {self.price})' + + def cost(self): + return self.shares * self.price + + def sell(self, amt): + self.shares -= amt + # print(f'Sold {amt} of {self.name}. We now have {self.shares} shares of {self.name}.') + +class MyStock(Stock): + def cost(self): + return 1.25 * super().cost() + + def panic(self): + self.sell(self.shares) \ No newline at end of file diff --git a/Work/tableformat.py b/Work/tableformat.py new file mode 100644 index 000000000..bbcc21802 --- /dev/null +++ b/Work/tableformat.py @@ -0,0 +1,65 @@ +class TableFormatter: + def headings(self, headers): + ''' + Emit the table headings. + ''' + raise NotImplementedError() + + def row(self, rowdata): + ''' + Emit a single row of table data. + ''' + raise NotImplementedError() + +class TextTableFormatter(TableFormatter): + 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): + def headings(self, headers): + print(','.join(headers)) + + def row(self, rowdata): + print(','.join(rowdata)) + +class HTMLTableFormatter(TableFormatter): + def headings(self, headers): + th = '' + for h in headers: + th += f'