From 83ef8fc06910ec4677cf7febfbac35adfb0db268 Mon Sep 17 00:00:00 2001 From: aboutflying Date: Sun, 7 Jun 2020 07:24:25 -0400 Subject: [PATCH 1/8] progress --- Work/bounce.py | 8 ++++ Work/mortgage.py | 11 ++++++ Work/pcost.py | 26 +++++++++++++ Work/report.py | 98 ++++++++++++++++++++++++++++++++++++++++++++++++ Work/sears.py | 13 +++++++ 5 files changed, 156 insertions(+) create mode 100644 Work/sears.py 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/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..a7b35773e 100644 --- a/Work/pcost.py +++ b/Work/pcost.py @@ -1,3 +1,29 @@ # pcost.py # # Exercise 1.27 + +import csv, sys + +def portfolio_cost(filename): + 'Get total portfolio cost of a csv' + + total = 0 + + with open(filename, 'rt') as f: + rows = csv.reader(f) + next(rows) + for row in rows: + try: + total += int(row[1]) * float(row[2]) + except ValueError: + print('Something is not valid in row: ', row) + + return total + +if len(sys.argv) == 2: + filename = sys.argv[1] +else: + filename = 'Data/portfolio.csv' + +cost = portfolio_cost(filename) +print('Total cost', cost) \ No newline at end of file diff --git a/Work/report.py b/Work/report.py index 47d5da7b1..6446d8048 100644 --- a/Work/report.py +++ b/Work/report.py @@ -1,3 +1,101 @@ # report.py # # Exercise 2.4 + +import csv, sys, locale +from pprint import pprint + +locale.setlocale(locale.LC_ALL, '') + +def read_portfolio(filename): + 'Create list of dicts from csv' + + portfolio = [] + + with open(filename, 'rt') as f: + rows = csv.reader(f) + headers = next(rows) + for i, row in enumerate(rows): + record = dict(zip(headers, row)) + try: + holding = {'name':record['name'], 'shares':int(record['shares']), 'price':float(record['price'])} + except ValueError: + # holding = {'name':row[0], 'shares':0, 'price':float(row[2])} + print(f'Row {i}: Bad row: {row}') + + portfolio.append(holding) + + return portfolio + +def read_prices(filename): + 'Create dict from csv' + + prices = {} + + with open(filename, 'rt') as f: + rows = csv.reader(f) + for row in rows: + if row: + prices[row[0]] = float(row[1]) + + 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(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(report): + headers = ('Name','Shares','Price','Change') + + print('%10s %10s %10s %10s'% headers) + print(('-' * 10 + ' ') * len(headers)) + + for name, shares, price, change in report: + price = locale.currency(price) + print(f'{name:>10s} {shares:>10d} {price:>10s} {change:>10,.2f}') + + print('\n') + +def portfolio_report(portfolio_filename="Data/portfolio.csv", prices_filename="Data/prices.csv"): + report = make_report(read_portfolio(portfolio_filename), read_prices(prices_filename)) + print(f'{portfolio_filename:*^43s}') + print_report(report) + +if len(sys.argv) > 1: + for file in sys.argv[1:]: + portfolio_report(file) +else: + portfolio_report() \ 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 From b42f46e6d23dd3c9592745aaebf39f1a6d53b095 Mon Sep 17 00:00:00 2001 From: aboutflying Date: Sun, 7 Jun 2020 09:52:21 -0400 Subject: [PATCH 2/8] progress --- Work/fileparse.py | 50 +++++++++++++++++++++++++++++++++++++++++++++++ Work/report.py | 25 +++--------------------- 2 files changed, 53 insertions(+), 22 deletions(-) diff --git a/Work/fileparse.py b/Work/fileparse.py index 1d499e733..e50e5a9c1 100644 --- a/Work/fileparse.py +++ b/Work/fileparse.py @@ -1,3 +1,53 @@ # fileparse.py # # Exercise 3.3 + +import csv + +def parse_csv(filename, select=None, types=None, has_headers=True, delimiter=',', silence_errors=False): + ''' + Parse a CSV file into a list of records + ''' + + if select and has_headers == False: + raise RuntimeError("select argument requires column headers") + + with open(filename) as f: + rows = csv.reader(f, 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/report.py b/Work/report.py index 6446d8048..9acefd014 100644 --- a/Work/report.py +++ b/Work/report.py @@ -3,40 +3,21 @@ # Exercise 2.4 import csv, sys, locale -from pprint import pprint +from fileparse import parse_csv locale.setlocale(locale.LC_ALL, '') def read_portfolio(filename): 'Create list of dicts from csv' - portfolio = [] - - with open(filename, 'rt') as f: - rows = csv.reader(f) - headers = next(rows) - for i, row in enumerate(rows): - record = dict(zip(headers, row)) - try: - holding = {'name':record['name'], 'shares':int(record['shares']), 'price':float(record['price'])} - except ValueError: - # holding = {'name':row[0], 'shares':0, 'price':float(row[2])} - print(f'Row {i}: Bad row: {row}') - - portfolio.append(holding) + portfolio = parse_csv(filename, select=['name', 'shares', 'price'], types=[str, int, float]) return portfolio def read_prices(filename): 'Create dict from csv' - prices = {} - - with open(filename, 'rt') as f: - rows = csv.reader(f) - for row in rows: - if row: - prices[row[0]] = float(row[1]) + prices = { name: price for name, price in parse_csv(filename, types=[str, float], has_headers=False) } return prices From a061a73d2101f46d05379e79e7415cb1e01b1095 Mon Sep 17 00:00:00 2001 From: aboutflying Date: Sun, 7 Jun 2020 10:18:42 -0400 Subject: [PATCH 3/8] progress --- Work/pcost.py | 31 ++++++++++++++----------------- Work/report.py | 17 +++++++++++------ 2 files changed, 25 insertions(+), 23 deletions(-) diff --git a/Work/pcost.py b/Work/pcost.py index a7b35773e..76c9d98d5 100644 --- a/Work/pcost.py +++ b/Work/pcost.py @@ -2,28 +2,25 @@ # # Exercise 1.27 -import csv, sys +import csv +from report import read_portfolio def portfolio_cost(filename): 'Get total portfolio cost of a csv' - total = 0 - - with open(filename, 'rt') as f: - rows = csv.reader(f) - next(rows) - for row in rows: - try: - total += int(row[1]) * float(row[2]) - except ValueError: - print('Something is not valid in row: ', row) + total = sum([s['price']*s['shares'] for s in read_portfolio(filename)]) return total -if len(sys.argv) == 2: - filename = sys.argv[1] -else: - filename = 'Data/portfolio.csv' +def main(argv): + if len(argv) == 2: + filename = argv[1] + else: + filename = 'Data/portfolio.csv' + + print('Total cost', portfolio_cost(filename)) + -cost = portfolio_cost(filename) -print('Total cost', cost) \ No newline at end of file +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 9acefd014..ddc3862f5 100644 --- a/Work/report.py +++ b/Work/report.py @@ -2,7 +2,7 @@ # # Exercise 2.4 -import csv, sys, locale +import csv, locale from fileparse import parse_csv locale.setlocale(locale.LC_ALL, '') @@ -75,8 +75,13 @@ def portfolio_report(portfolio_filename="Data/portfolio.csv", prices_filename="D print(f'{portfolio_filename:*^43s}') print_report(report) -if len(sys.argv) > 1: - for file in sys.argv[1:]: - portfolio_report(file) -else: - portfolio_report() \ No newline at end of file +def main(argv): + if len(argv) > 1: + for file in argv[1:]: + portfolio_report(file) + else: + portfolio_report() + +if __name__ == '__main__': + import sys + main(sys.argv) \ No newline at end of file From 51fa08957ef70ecc8e75eab50af8f030e29b0c58 Mon Sep 17 00:00:00 2001 From: aboutflying Date: Sun, 7 Jun 2020 10:51:37 -0400 Subject: [PATCH 4/8] more flexible fileparse --- Work/fileparse.py | 74 +++++++++++++++++++++++------------------------ 1 file changed, 37 insertions(+), 37 deletions(-) diff --git a/Work/fileparse.py b/Work/fileparse.py index e50e5a9c1..c2d6e172e 100644 --- a/Work/fileparse.py +++ b/Work/fileparse.py @@ -4,50 +4,50 @@ import csv -def parse_csv(filename, select=None, types=None, has_headers=True, delimiter=',', silence_errors=False): +def parse_csv(lines, select=None, types=None, has_headers=True, delimiter=',', silence_errors=False): ''' - Parse a CSV file into a list of records + Parse iterable as csv into a list of records ''' if select and has_headers == False: raise RuntimeError("select argument requires column headers") - with open(filename) as f: - rows = csv.reader(f, 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}') + rows = csv.reader(lines, 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 - records.append(record) + # 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 From 17ea73fafa4fae481da2acda6027f872c5cdcbe2 Mon Sep 17 00:00:00 2001 From: aboutflying Date: Sun, 7 Jun 2020 12:42:55 -0400 Subject: [PATCH 5/8] progress --- Work/fileparse.py | 9 ++++++--- Work/report.py | 50 +++++++++++++++++++++++++---------------------- 2 files changed, 33 insertions(+), 26 deletions(-) diff --git a/Work/fileparse.py b/Work/fileparse.py index c2d6e172e..b2347255f 100644 --- a/Work/fileparse.py +++ b/Work/fileparse.py @@ -4,15 +4,18 @@ import csv -def parse_csv(lines, select=None, types=None, has_headers=True, delimiter=',', silence_errors=False): +def parse_csv(obj, select=None, types=None, has_headers=True, delimiter=',', silence_errors=False): ''' - Parse iterable as csv into a list of records + 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(lines, delimiter=delimiter) + rows = csv.reader(obj, delimiter=delimiter) if has_headers: # Read the file headers diff --git a/Work/report.py b/Work/report.py index ddc3862f5..eedeeafb2 100644 --- a/Work/report.py +++ b/Work/report.py @@ -2,22 +2,26 @@ # # Exercise 2.4 -import csv, locale +import locale, stock, tableformat from fileparse import parse_csv locale.setlocale(locale.LC_ALL, '') def read_portfolio(filename): - 'Create list of dicts from csv' + 'Create list of stocks from csv' - portfolio = parse_csv(filename, select=['name', 'shares', 'price'], types=[str, int, float]) + 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' - prices = { name: price for name, price in parse_csv(filename, types=[str, float], has_headers=False) } + with open(filename) as file: + prices = { name: price for name, price in parse_csv(file, types=[str, float], has_headers=False) } return prices @@ -27,7 +31,7 @@ def portfolio_cost(portfolio): cost = 0 for holding in portfolio: - cost += holding['shares'] * holding['price'] + cost += holding.shares * holding.price return cost @@ -37,43 +41,43 @@ def current_value(portfolio, prices): current_value = 0 for holding in portfolio: - if holding['name'] in prices: - current_value += holding['shares'] * prices[holding['name']] + if holding.name in prices: + current_value += holding.shares * prices[holding.name] return current_value -def make_report(portfolio, prices): +def make_report_data(portfolio, prices): 'Generate report' report = [] for holding in portfolio: - if holding['name'] in prices: + if holding.name in prices: report.append(( - holding['name'], - holding['shares'], - prices[holding['name']], - prices[holding['name']] - holding['price'] + holding.name, + holding.shares, + prices[holding.name], + prices[holding.name] - holding.price )) return report -def print_report(report): - headers = ('Name','Shares','Price','Change') - - print('%10s %10s %10s %10s'% headers) - print(('-' * 10 + ' ') * len(headers)) +def print_report(reportdata, formatter): + formatter.headings(['Name','Shares','Price','Change']) - for name, shares, price, change in report: - price = locale.currency(price) - print(f'{name:>10s} {shares:>10d} {price:>10s} {change:>10,.2f}') + for name, shares, price, change in reportdata: + # price = locale.currency(price) + # rowdata = [ name, str(shares), f'{locale.currency(price):>10s}', f'{change:0.2f}'] + rowdata = [ name, str(shares), f'{price:>0.2f}', f'{change:0.2f}'] + formatter.row(rowdata) print('\n') def portfolio_report(portfolio_filename="Data/portfolio.csv", prices_filename="Data/prices.csv"): - report = make_report(read_portfolio(portfolio_filename), read_prices(prices_filename)) + reportdata = make_report_data(read_portfolio(portfolio_filename), read_prices(prices_filename)) + formatter = tableformat.HTMLTableFormatter() print(f'{portfolio_filename:*^43s}') - print_report(report) + print_report(reportdata, formatter) def main(argv): if len(argv) > 1: From fd2bb0a5ef9525d239f7203466c934971f55f484 Mon Sep 17 00:00:00 2001 From: aboutflying Date: Sun, 7 Jun 2020 12:43:29 -0400 Subject: [PATCH 6/8] progress --- Work/stock.py | 19 +++++++++++++++++++ Work/tableformat.py | 44 ++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 63 insertions(+) create mode 100644 Work/stock.py create mode 100644 Work/tableformat.py diff --git a/Work/stock.py b/Work/stock.py new file mode 100644 index 000000000..b67f4c116 --- /dev/null +++ b/Work/stock.py @@ -0,0 +1,19 @@ +class Stock: + def __init__(self, name, shares, price): + self.name = name + self.shares = shares + self.price = 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..35edf9cef --- /dev/null +++ b/Work/tableformat.py @@ -0,0 +1,44 @@ +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'{h}' + print(f'{th}') + + def row(self, rowdata): + td = '' + for d in rowdata: + td += f'{d}' + print(f'{td}') \ No newline at end of file From 0dc0b21fc91fa76f9e9882bd2e287b4f5b7a5095 Mon Sep 17 00:00:00 2001 From: aboutflying Date: Sun, 7 Jun 2020 13:03:53 -0400 Subject: [PATCH 7/8] progress --- Work/report.py | 24 +++++++++--------------- Work/tableformat.py | 14 +++++++++++++- 2 files changed, 22 insertions(+), 16 deletions(-) diff --git a/Work/report.py b/Work/report.py index eedeeafb2..a98f26555 100644 --- a/Work/report.py +++ b/Work/report.py @@ -2,11 +2,9 @@ # # Exercise 2.4 -import locale, stock, tableformat +import stock, tableformat from fileparse import parse_csv -locale.setlocale(locale.LC_ALL, '') - def read_portfolio(filename): 'Create list of stocks from csv' @@ -66,25 +64,21 @@ def print_report(reportdata, formatter): formatter.headings(['Name','Shares','Price','Change']) for name, shares, price, change in reportdata: - # price = locale.currency(price) - # rowdata = [ name, str(shares), f'{locale.currency(price):>10s}', f'{change:0.2f}'] rowdata = [ name, str(shares), f'{price:>0.2f}', f'{change:0.2f}'] formatter.row(rowdata) - print('\n') - -def portfolio_report(portfolio_filename="Data/portfolio.csv", prices_filename="Data/prices.csv"): - reportdata = make_report_data(read_portfolio(portfolio_filename), read_prices(prices_filename)) - formatter = tableformat.HTMLTableFormatter() - print(f'{portfolio_filename:*^43s}') +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) > 1: - for file in argv[1:]: - portfolio_report(file) + 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() + portfolio_report(argv[1], argv[2], argv[3]) if __name__ == '__main__': import sys diff --git a/Work/tableformat.py b/Work/tableformat.py index 35edf9cef..5c9e1bd18 100644 --- a/Work/tableformat.py +++ b/Work/tableformat.py @@ -41,4 +41,16 @@ def row(self, rowdata): td = '' for d in rowdata: td += f'{d}' - print(f'{td}') \ No newline at end of file + print(f'{td}') + +def create_formatter(name): + if name == 'txt': + formatter = TextTableFormatter() + elif name == 'csv': + formatter = CSVTableFormatter() + elif name == 'html': + formatter = HTMLTableFormatter() + else: + raise RuntimeError(f'Unknown format: {name}') + + return formatter \ No newline at end of file From ecc21f614aaf1f775ccdb280ae50306c52263943 Mon Sep 17 00:00:00 2001 From: aboutflying Date: Sun, 7 Jun 2020 13:39:23 -0400 Subject: [PATCH 8/8] progress --- Work/stock.py | 3 +++ Work/tableformat.py | 13 +++++++++++-- 2 files changed, 14 insertions(+), 2 deletions(-) diff --git a/Work/stock.py b/Work/stock.py index b67f4c116..a77e58717 100644 --- a/Work/stock.py +++ b/Work/stock.py @@ -4,6 +4,9 @@ def __init__(self, name, shares, price): 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 diff --git a/Work/tableformat.py b/Work/tableformat.py index 5c9e1bd18..bbcc21802 100644 --- a/Work/tableformat.py +++ b/Work/tableformat.py @@ -43,6 +43,9 @@ def row(self, rowdata): td += f'{d}' print(f'{td}') +class FormatError(Exception): + pass + def create_formatter(name): if name == 'txt': formatter = TextTableFormatter() @@ -51,6 +54,12 @@ def create_formatter(name): elif name == 'html': formatter = HTMLTableFormatter() else: - raise RuntimeError(f'Unknown format: {name}') + raise FormatError(f'Unknown table format: {name}') + + return formatter - return formatter \ No newline at end of file +def print_table(obj, cols, formatter): + formatter.headings(cols) + for o in obj: + rowdata = [ str(getattr(o, colname)) for colname in cols ] + formatter.row(rowdata) \ No newline at end of file