From 570746dc69ea32f2a017aedb51b8a763df631844 Mon Sep 17 00:00:00 2001 From: Kevin Reed Date: Mon, 31 Jul 2023 09:57:08 -0500 Subject: [PATCH 01/21] 1.1, 1.2 --- Work/bounce.py | 7 +++++++ Work/hello.py | 1 + Work/sears.py | 27 +++++++++++++++++++++++++++ 3 files changed, 35 insertions(+) create mode 100644 Work/hello.py create mode 100644 Work/sears.py diff --git a/Work/bounce.py b/Work/bounce.py index 3660ddd82..652b1444c 100644 --- a/Work/bounce.py +++ b/Work/bounce.py @@ -1,3 +1,10 @@ # bounce.py # # Exercise 1.5 + +current_height = 100 # meters +fall_fraction = 3 / 5 # fraction of from_height + +for i in range(1, 11): + current_height *= fall_fraction + print(i, round(current_height, 4)) \ No newline at end of file diff --git a/Work/hello.py b/Work/hello.py new file mode 100644 index 000000000..6b561325f --- /dev/null +++ b/Work/hello.py @@ -0,0 +1 @@ +print('Suh, Dude?') \ No newline at end of file diff --git a/Work/sears.py b/Work/sears.py new file mode 100644 index 000000000..f36953f1e --- /dev/null +++ b/Work/sears.py @@ -0,0 +1,27 @@ +# One morning, you go out and place a dollar bill +# on the sidewalk by the Sears tower in Chicago. +# Each day thereafter, you go out double the number +# of bills. How long does it take for the stack of +# bills to exceed the height of the tower? + +bill_thickness = 0.11 * 0.001 # meters (0.11 mm) +sears_height = 442 # height (meters) +num_bills = 1 +day = 1 + +def print_day(day, num_bills, height): + remaining_height = sears_height - height + print('{:<10}{:<21}{:<20}{:<30}' + .format( + f'day: {day}', + f'num_bills: {num_bills}', + f'height: {height}', + f'height_remaining: {round(remaining_height, 2)}')) + + +while num_bills * bill_thickness < sears_height: + print_day(day, num_bills, num_bills * bill_thickness) + day += 1 + num_bills *= 2 + +print_day(day, num_bills, num_bills * bill_thickness) \ No newline at end of file From f4497408a3963fb39e2244149a425107e0f24383 Mon Sep 17 00:00:00 2001 From: Kevin Reed Date: Mon, 31 Jul 2023 11:38:15 -0500 Subject: [PATCH 02/21] add tests and test runner helper --- Work/bounce.py | 13 ++++++++----- Work/bounce_test.py | 17 +++++++++++++++++ Work/requirements.txt | 2 ++ Work/work.ps1 | 18 ++++++++++++++++++ 4 files changed, 45 insertions(+), 5 deletions(-) create mode 100644 Work/bounce_test.py create mode 100644 Work/requirements.txt create mode 100644 Work/work.ps1 diff --git a/Work/bounce.py b/Work/bounce.py index 652b1444c..739f61c9d 100644 --- a/Work/bounce.py +++ b/Work/bounce.py @@ -2,9 +2,12 @@ # # Exercise 1.5 -current_height = 100 # meters -fall_fraction = 3 / 5 # fraction of from_height +def bounce(): + current_height = 100 # meters + fall_fraction = 3 / 5 # fraction of from_height -for i in range(1, 11): - current_height *= fall_fraction - print(i, round(current_height, 4)) \ No newline at end of file + for i in range(1, 11): + current_height *= fall_fraction + print(i, round(current_height, 4)) + +bounce() \ No newline at end of file diff --git a/Work/bounce_test.py b/Work/bounce_test.py new file mode 100644 index 000000000..2612b10cf --- /dev/null +++ b/Work/bounce_test.py @@ -0,0 +1,17 @@ +from bounce import * + +def test_main(capfd): + bounce() + + out, err = capfd.readouterr() + assert out == """1 60.0 +2 36.0 +3 21.6 +4 12.96 +5 7.776 +6 4.6656 +7 2.7994 +8 1.6796 +9 1.0078 +10 0.6047 +""" diff --git a/Work/requirements.txt b/Work/requirements.txt new file mode 100644 index 000000000..653c63ce5 --- /dev/null +++ b/Work/requirements.txt @@ -0,0 +1,2 @@ +pytest==7.4.0 +pytest-watch==4.2.0 diff --git a/Work/work.ps1 b/Work/work.ps1 new file mode 100644 index 000000000..5631580ca --- /dev/null +++ b/Work/work.ps1 @@ -0,0 +1,18 @@ +param([string]$Command) + +switch ($Command) { + "env" { + python -m venv env + iex $PSScriptRoot\env\Scripts\Activate.ps1 + pip install -r $PSScriptRoot\requirements.txt + } + "test" { + pytest $PSScriptRoot -q + } + "test:watch" { + ptw $PSScriptRoot -- -q + } + default { + Write-Error "Command '$Command' not found." + } +} \ No newline at end of file From cc99c990ab8647b2fba57aecc4a5fe2938f275a1 Mon Sep 17 00:00:00 2001 From: Kevin Reed Date: Mon, 31 Jul 2023 21:56:44 -0500 Subject: [PATCH 03/21] 1.3 --- Work/mortgage.py | 33 +++++++++++++++++++++++++++++++++ Work/mortgage_test.py | 7 +++++++ Work/sears.py | 1 - Work/work.ps1 | 10 +++++----- 4 files changed, 45 insertions(+), 6 deletions(-) create mode 100644 Work/mortgage_test.py diff --git a/Work/mortgage.py b/Work/mortgage.py index d527314e3..a0163174e 100644 --- a/Work/mortgage.py +++ b/Work/mortgage.py @@ -1,3 +1,36 @@ # mortgage.py # # Exercise 1.7 + +def mortgage(extra_payment_start_month=0, + extra_payment_end_month=0, + extra_payment=0.0): + principal = 500000.0 + rate = 0.05 + default_payment = 2684.11 + paid = 0.0 + month = 0 + + while principal > 0: + month += 1 + payment = default_payment + + if month >= extra_payment_start_month and month <= extra_payment_end_month: + payment += extra_payment + + if payment > principal: + payment = principal + principal = 0 + else: + principal = principal * (1 + rate / 12) - payment + + paid = paid + payment + print(month, paid, principal) + + print('Total paid', round(paid, 2)) + print('Months', month) + return round(paid, 2) + +print('1.7', mortgage()) +print('1.8', mortgage(1, 12, 1000)) +print('1.9', mortgage(61, 108, 1000)) \ No newline at end of file diff --git a/Work/mortgage_test.py b/Work/mortgage_test.py new file mode 100644 index 000000000..75dbcefef --- /dev/null +++ b/Work/mortgage_test.py @@ -0,0 +1,7 @@ +from mortgage import mortgage + +def test_mortgage_1_7(): + assert round(mortgage(), 1) == 966266.9 + +def test_mortgage_1_8(): + assert round(mortgage(1, 12, 1000), 2) == 927989.46 \ No newline at end of file diff --git a/Work/sears.py b/Work/sears.py index f36953f1e..7f946c2ae 100644 --- a/Work/sears.py +++ b/Work/sears.py @@ -17,7 +17,6 @@ def print_day(day, num_bills, height): f'num_bills: {num_bills}', f'height: {height}', f'height_remaining: {round(remaining_height, 2)}')) - while num_bills * bill_thickness < sears_height: print_day(day, num_bills, num_bills * bill_thickness) diff --git a/Work/work.ps1 b/Work/work.ps1 index 5631580ca..e77726a12 100644 --- a/Work/work.ps1 +++ b/Work/work.ps1 @@ -2,15 +2,15 @@ param([string]$Command) switch ($Command) { "env" { - python -m venv env - iex $PSScriptRoot\env\Scripts\Activate.ps1 - pip install -r $PSScriptRoot\requirements.txt + & python -m venv env + & $PSScriptRoot\env\Scripts\Activate.ps1 + & pip install -r $PSScriptRoot\requirements.txt } "test" { - pytest $PSScriptRoot -q + & pytest $PSScriptRoot -q } "test:watch" { - ptw $PSScriptRoot -- -q + & ptw $PSScriptRoot -- -q } default { Write-Error "Command '$Command' not found." From df8619076b09102a08d6ab66ea05d389e5af78e7 Mon Sep 17 00:00:00 2001 From: Kevin Reed Date: Mon, 31 Jul 2023 22:21:43 -0500 Subject: [PATCH 04/21] 1.17 --- Work/mortgage.py | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/Work/mortgage.py b/Work/mortgage.py index a0163174e..2009f0e6e 100644 --- a/Work/mortgage.py +++ b/Work/mortgage.py @@ -11,6 +11,7 @@ def mortgage(extra_payment_start_month=0, paid = 0.0 month = 0 + print(f'{"Mo":>3} {f"Paid":>11} {f"Principal":>11}') while principal > 0: month += 1 payment = default_payment @@ -25,12 +26,12 @@ def mortgage(extra_payment_start_month=0, principal = principal * (1 + rate / 12) - payment paid = paid + payment - print(month, paid, principal) + print(f'{month:>3} {f"${paid:,.2f}":>11} {f"${principal:,.2f}":>11}') - print('Total paid', round(paid, 2)) - print('Months', month) + print('------------------') + print(f'Total paid: ${round(paid, 2):,.2f}') + print('Months: ', month) + print('------------------') return round(paid, 2) -print('1.7', mortgage()) -print('1.8', mortgage(1, 12, 1000)) -print('1.9', mortgage(61, 108, 1000)) \ No newline at end of file +mortgage(61, 108, 1000) \ No newline at end of file From ddfc62fc5efab1ca4212c97ff672f597ddd96313 Mon Sep 17 00:00:00 2001 From: Kevin Reed Date: Tue, 1 Aug 2023 22:32:06 -0500 Subject: [PATCH 05/21] 1.27 --- Work/pcost.py | 13 +++++++++++++ Work/pcost_test.py | 4 ++++ 2 files changed, 17 insertions(+) create mode 100644 Work/pcost_test.py diff --git a/Work/pcost.py b/Work/pcost.py index e68aa20b4..631d305c7 100644 --- a/Work/pcost.py +++ b/Work/pcost.py @@ -1,3 +1,16 @@ # pcost.py # # Exercise 1.27 + +def pcost(): + with open('Data/portfolio.csv', 'rt') as f: + next(f) # skip header + cost = 0.0 + for line in f: + row = line.split(',') + shares = int(row[1]) + stock_cost = float(row[2]) + cost += shares * stock_cost + return cost + +print(f'Total cost ${pcost():,.2f}') \ No newline at end of file diff --git a/Work/pcost_test.py b/Work/pcost_test.py new file mode 100644 index 000000000..5ee490a38 --- /dev/null +++ b/Work/pcost_test.py @@ -0,0 +1,4 @@ +from pcost import pcost + +def test_pcost(): + assert pcost() == 44671.15 \ No newline at end of file From af5042fc1ad2bbbb5478b98b5fcc405b3e698e68 Mon Sep 17 00:00:00 2001 From: Kevin Reed Date: Tue, 1 Aug 2023 22:38:17 -0500 Subject: [PATCH 06/21] 1.31 --- Work/pcost.py | 18 +++++++++++------- Work/pcost_test.py | 7 +++++-- 2 files changed, 16 insertions(+), 9 deletions(-) diff --git a/Work/pcost.py b/Work/pcost.py index 631d305c7..463c8e050 100644 --- a/Work/pcost.py +++ b/Work/pcost.py @@ -2,15 +2,19 @@ # # Exercise 1.27 -def pcost(): - with open('Data/portfolio.csv', 'rt') as f: +def pcost(filename): + '''Returns the total cost of the portfolio''' + with open(filename, 'rt') as f: next(f) # skip header cost = 0.0 for line in f: - row = line.split(',') - shares = int(row[1]) - stock_cost = float(row[2]) - cost += shares * stock_cost + try: + fields = line.split(',') + shares = int(fields[1]) + stock_cost = float(fields[2]) + cost += shares * stock_cost + except ValueError: + print(f'Could not parse {line}') return cost -print(f'Total cost ${pcost():,.2f}') \ No newline at end of file +print(f'Total cost ${pcost("Data/portfolio.csv"):,.2f}') \ No newline at end of file diff --git a/Work/pcost_test.py b/Work/pcost_test.py index 5ee490a38..d679f38af 100644 --- a/Work/pcost_test.py +++ b/Work/pcost_test.py @@ -1,4 +1,7 @@ from pcost import pcost -def test_pcost(): - assert pcost() == 44671.15 \ No newline at end of file +def test_pcost_1_27(): + assert pcost('Data/portfolio.csv') == 44671.15 + +def test_pcost_1_31(): + assert pcost('Data/missing.csv') == 27381.15 \ No newline at end of file From e922a4df0ee0b6d7da2a25a723fe73be88328e28 Mon Sep 17 00:00:00 2001 From: Kevin Reed Date: Tue, 1 Aug 2023 22:41:32 -0500 Subject: [PATCH 07/21] 1.32 --- Work/pcost.py | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/Work/pcost.py b/Work/pcost.py index 463c8e050..7e27aae92 100644 --- a/Work/pcost.py +++ b/Work/pcost.py @@ -1,20 +1,21 @@ # pcost.py # # Exercise 1.27 +import csv def pcost(filename): '''Returns the total cost of the portfolio''' with open(filename, 'rt') as f: - next(f) # skip header + rows = csv.reader(f) + next(rows) # skip header cost = 0.0 - for line in f: + for fields in rows: try: - fields = line.split(',') shares = int(fields[1]) stock_cost = float(fields[2]) cost += shares * stock_cost except ValueError: - print(f'Could not parse {line}') + print(f'Could not parse {fields}') return cost print(f'Total cost ${pcost("Data/portfolio.csv"):,.2f}') \ No newline at end of file From a204fa29b2360c02882ace3751e84aa2a6bb6f5d Mon Sep 17 00:00:00 2001 From: Kevin Reed Date: Tue, 1 Aug 2023 22:45:38 -0500 Subject: [PATCH 08/21] 1.33 --- Work/pcost.py | 4 +--- Work/pcost_command.py | 12 ++++++++++++ 2 files changed, 13 insertions(+), 3 deletions(-) create mode 100644 Work/pcost_command.py diff --git a/Work/pcost.py b/Work/pcost.py index 7e27aae92..ba4d0032f 100644 --- a/Work/pcost.py +++ b/Work/pcost.py @@ -16,6 +16,4 @@ def pcost(filename): cost += shares * stock_cost except ValueError: print(f'Could not parse {fields}') - return cost - -print(f'Total cost ${pcost("Data/portfolio.csv"):,.2f}') \ No newline at end of file + return cost \ No newline at end of file diff --git a/Work/pcost_command.py b/Work/pcost_command.py new file mode 100644 index 000000000..d06f207e6 --- /dev/null +++ b/Work/pcost_command.py @@ -0,0 +1,12 @@ +# Excersize 1.33 + +from pcost import pcost +import sys + +if len(sys.argv) == 2: + filename = sys.argv[1] +else: + filename = 'Data/portfolio.csv' + +cost = pcost(filename) +print(f'Total cost ${cost:,.2f}') \ No newline at end of file From eea0cfb19e194ea5fccaf332f458cd646e6b0a42 Mon Sep 17 00:00:00 2001 From: Kevin Reed Date: Tue, 1 Aug 2023 22:46:13 -0500 Subject: [PATCH 09/21] 1.33 --- Work/pcost_command.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/Work/pcost_command.py b/Work/pcost_command.py index d06f207e6..38012374e 100644 --- a/Work/pcost_command.py +++ b/Work/pcost_command.py @@ -3,6 +3,8 @@ from pcost import pcost import sys +print(sys) + if len(sys.argv) == 2: filename = sys.argv[1] else: From 7fefd9b295b1081bb9ce77f6d0edac61473d324e Mon Sep 17 00:00:00 2001 From: Kevin Reed Date: Tue, 1 Aug 2023 22:47:54 -0500 Subject: [PATCH 10/21] 1.33 --- Work/pcost_command.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/Work/pcost_command.py b/Work/pcost_command.py index 38012374e..4f7d1d531 100644 --- a/Work/pcost_command.py +++ b/Work/pcost_command.py @@ -3,9 +3,10 @@ from pcost import pcost import sys -print(sys) +print(sys.argv) +print() -if len(sys.argv) == 2: +if len(sys.argv) > 1: filename = sys.argv[1] else: filename = 'Data/portfolio.csv' From ee283eb843840848269d6070b0c74ce81e877b75 Mon Sep 17 00:00:00 2001 From: Kevin Reed Date: Sun, 6 Aug 2023 17:49:21 -0500 Subject: [PATCH 11/21] 2.7 --- Work/Data/stocksim.py | 16 ++++++++-- Work/bounce_test.py | 2 +- Work/mortgage.py | 2 +- Work/report.py | 73 +++++++++++++++++++++++++++++++++++++++++++ Work/report_test.py | 33 +++++++++++++++++++ Work/requirements.txt | 1 + Work/work.ps1 | 12 +++++-- 7 files changed, 131 insertions(+), 8 deletions(-) create mode 100644 Work/report_test.py diff --git a/Work/Data/stocksim.py b/Work/Data/stocksim.py index 52ebe4185..5ef258b56 100755 --- a/Work/Data/stocksim.py +++ b/Work/Data/stocksim.py @@ -112,13 +112,23 @@ def update(self): def incr(self,dt): self.time += dt if self.index < (len(self.history) - 2): - while self.index < (len(self.history) - 2) and self.time >= self.history[self.index+1][3]: + while self.index < (len(self.history) - 2) and \ + self.time >= self.history[self.index+1][3]: self.index += 1 self.update() def make_record(self): - return [self.name,round(self.price,2),self.date,minutes_to_str(self.time),round(self.change,2),self.open,round(self.high,2), - round(self.low,2),self.volume] + return [ + self.name, + round(self.price, 2), + self.date, + minutes_to_str(self.time), + round(self.change, 2), + self.open, + round(self.high, 2), + round(self.low, 2), + self.volume + ] class MarketSimulator(object): def __init__(self): diff --git a/Work/bounce_test.py b/Work/bounce_test.py index 2612b10cf..5d3569863 100644 --- a/Work/bounce_test.py +++ b/Work/bounce_test.py @@ -1,4 +1,4 @@ -from bounce import * +from bounce import bounce def test_main(capfd): bounce() diff --git a/Work/mortgage.py b/Work/mortgage.py index 2009f0e6e..b79395b68 100644 --- a/Work/mortgage.py +++ b/Work/mortgage.py @@ -11,7 +11,7 @@ def mortgage(extra_payment_start_month=0, paid = 0.0 month = 0 - print(f'{"Mo":>3} {f"Paid":>11} {f"Principal":>11}') + print(f'{"Mo":>3} {"Paid":>11} {"Principal":>11}') while principal > 0: month += 1 payment = default_payment diff --git a/Work/report.py b/Work/report.py index 47d5da7b1..0e89a5355 100644 --- a/Work/report.py +++ b/Work/report.py @@ -1,3 +1,76 @@ # report.py # # Exercise 2.4 +import csv +from pprint import pprint + +def read_portfolio(filename): + portfolio = [] + with open(filename, 'rt') as f: + rows = csv.reader(f) + next(rows) # skip header + for fields in rows: + try: + holding = (fields[0], int(fields[1]), float(fields[2])) + portfolio.append(holding) + except ValueError: + print(f'Could not parse {fields}') + return portfolio + +def read_portfolio_2_5(filename): + portfolio = [] + with open(filename, 'rt') as f: + rows = csv.reader(f) + next(rows) # skip header + for fields in rows: + try: + holding = { + 'name': fields[0], + 'shares': int(fields[1]), + 'price': float(fields[2]) + } + portfolio.append(holding) + except ValueError: + print(f'Could not parse {fields}') + return portfolio + +def read_prices_2_6(filename): + prices = {} + with open(filename, 'rt') as f: + rows = csv.reader(f) + # next(rows) # skip header + for row in rows: + if (len(row) == 0): + continue + + try: + prices[row[0]] = float(row[1]) + except ValueError: + print(f'Could not parse {row}') + + pprint(prices) + return prices + +def get_gainloss_2_7(stocksFilename, pricesFilename): + prices = read_prices_2_6(pricesFilename) + stocks = read_portfolio_2_5(stocksFilename) + + total_value = 0.0 + total_market_value = 0.0 + total_gain_loss = 0.0 + for stock in stocks: + stock['current_value'] = stock['price'] * stock['shares'] + stock['market_value'] = prices[stock['name']] * stock['shares'] + stock['gain_loss'] = stock['market_value'] - stock['current_value'] + + total_value += stock['current_value'] + total_market_value += stock['market_value'] + total_gain_loss += stock['gain_loss'] + + print('{name:s} {gain_loss:,.2f}'.format_map(stock)) + + print(f'Total value: {total_value:,.2f}') + print(f'Total market value: {total_market_value:,.2f}') + print(f'Total gain/loss: {total_gain_loss:,.2f}') + + return total_gain_loss \ No newline at end of file diff --git a/Work/report_test.py b/Work/report_test.py new file mode 100644 index 000000000..7508675bd --- /dev/null +++ b/Work/report_test.py @@ -0,0 +1,33 @@ +from report import read_portfolio, read_portfolio_2_5, read_prices_2_6, get_gainloss_2_7 + +def test_read_portfolio_2_4(): + portfolio = read_portfolio('Data/portfolio.csv') + assert portfolio == [ + ('AA', 100, 32.2), + ('IBM', 50, 91.1), + ('CAT', 150, 83.44), + ('MSFT', 200, 51.23), + ('GE', 95, 40.37), + ('MSFT', 50, 65.1), + ('IBM', 100, 70.44) + ] + +def test_read_portfolio_2_5(): + portfolio = read_portfolio_2_5('Data/portfolio.csv') + assert portfolio == [ + {'name': 'AA', 'shares': 100, 'price': 32.2}, {'name': 'IBM', 'shares': 50, 'price': 91.1}, + {'name': 'CAT', 'shares': 150, 'price': 83.44}, {'name': 'MSFT', 'shares': 200, 'price': 51.23}, + {'name': 'GE', 'shares': 95, 'price': 40.37}, {'name': 'MSFT', 'shares': 50, 'price': 65.1}, + {'name': 'IBM', 'shares': 100, 'price': 70.44} + ] + +def test_read_prices_2_6(): + prices = read_prices_2_6('Data/prices.csv') + assert prices['AA'] == 9.22 + assert prices['AXP'] == 24.85 + assert prices['IBM'] == 106.28 + assert prices['MSFT'] == 20.89 + +def test_get_gainloss_2_7(): + gain_loss = get_gainloss_2_7('Data/portfolio.csv', 'Data/prices.csv') + assert round(gain_loss, 2) == -15985.05 \ No newline at end of file diff --git a/Work/requirements.txt b/Work/requirements.txt index 653c63ce5..5dbd22163 100644 --- a/Work/requirements.txt +++ b/Work/requirements.txt @@ -1,2 +1,3 @@ pytest==7.4.0 pytest-watch==4.2.0 +ruff==0.0.282 diff --git a/Work/work.ps1 b/Work/work.ps1 index e77726a12..a74ccfb21 100644 --- a/Work/work.ps1 +++ b/Work/work.ps1 @@ -1,9 +1,9 @@ param([string]$Command) switch ($Command) { - "env" { - & python -m venv env - & $PSScriptRoot\env\Scripts\Activate.ps1 + "venv" { + & python -m venv venv + & $PSScriptRoot\venv\Scripts\Activate.ps1 & pip install -r $PSScriptRoot\requirements.txt } "test" { @@ -12,6 +12,12 @@ switch ($Command) { "test:watch" { & ptw $PSScriptRoot -- -q } + "lint" { + & ruff check . + } + "lint:fix" { + & ruff check . --fix + } default { Write-Error "Command '$Command' not found." } From 084d8f5ffe9aa8da81db71347a02f0a7d416127d Mon Sep 17 00:00:00 2001 From: Kevin Reed Date: Mon, 7 Aug 2023 22:23:40 -0500 Subject: [PATCH 12/21] refactor --- .vscode/settings.json | 8 ++++++++ Work/pcost_test.py | 7 +++++-- Work/report_test.py | 22 +++++++++++++++------- Work/work.ps1 | 4 ++-- 4 files changed, 30 insertions(+), 11 deletions(-) create mode 100644 .vscode/settings.json diff --git a/.vscode/settings.json b/.vscode/settings.json new file mode 100644 index 000000000..ce3bda480 --- /dev/null +++ b/.vscode/settings.json @@ -0,0 +1,8 @@ +{ + "python.testing.pytestArgs": [ + "Work", + "--rootdir=Work", + ], + "python.testing.unittestEnabled": false, + "python.testing.pytestEnabled": true +} \ No newline at end of file diff --git a/Work/pcost_test.py b/Work/pcost_test.py index d679f38af..10286d468 100644 --- a/Work/pcost_test.py +++ b/Work/pcost_test.py @@ -1,7 +1,10 @@ from pcost import pcost +from os import path + +data_dir = path.join(path.dirname(__file__), 'Data') def test_pcost_1_27(): - assert pcost('Data/portfolio.csv') == 44671.15 + assert pcost(path.join(data_dir, 'portfolio.csv')) == 44671.15 def test_pcost_1_31(): - assert pcost('Data/missing.csv') == 27381.15 \ No newline at end of file + assert pcost(path.join(data_dir, 'missing.csv')) == 27381.15 \ No newline at end of file diff --git a/Work/report_test.py b/Work/report_test.py index 7508675bd..c25cdd307 100644 --- a/Work/report_test.py +++ b/Work/report_test.py @@ -1,7 +1,10 @@ +from os import path from report import read_portfolio, read_portfolio_2_5, read_prices_2_6, get_gainloss_2_7 +data_dir = path.join(path.dirname(__file__), 'Data') + def test_read_portfolio_2_4(): - portfolio = read_portfolio('Data/portfolio.csv') + portfolio = read_portfolio(path.join(data_dir, 'portfolio.csv')) assert portfolio == [ ('AA', 100, 32.2), ('IBM', 50, 91.1), @@ -13,21 +16,26 @@ def test_read_portfolio_2_4(): ] def test_read_portfolio_2_5(): - portfolio = read_portfolio_2_5('Data/portfolio.csv') + portfolio = read_portfolio_2_5(path.join(data_dir, 'portfolio.csv')) assert portfolio == [ - {'name': 'AA', 'shares': 100, 'price': 32.2}, {'name': 'IBM', 'shares': 50, 'price': 91.1}, - {'name': 'CAT', 'shares': 150, 'price': 83.44}, {'name': 'MSFT', 'shares': 200, 'price': 51.23}, - {'name': 'GE', 'shares': 95, 'price': 40.37}, {'name': 'MSFT', 'shares': 50, 'price': 65.1}, + {'name': 'AA', 'shares': 100, 'price': 32.2}, + {'name': 'IBM', 'shares': 50, 'price': 91.1}, + {'name': 'CAT', 'shares': 150, 'price': 83.44}, + {'name': 'MSFT', 'shares': 200, 'price': 51.23}, + {'name': 'GE', 'shares': 95, 'price': 40.37}, + {'name': 'MSFT', 'shares': 50, 'price': 65.1}, {'name': 'IBM', 'shares': 100, 'price': 70.44} ] def test_read_prices_2_6(): - prices = read_prices_2_6('Data/prices.csv') + prices = read_prices_2_6(path.join(data_dir, 'prices.csv')) assert prices['AA'] == 9.22 assert prices['AXP'] == 24.85 assert prices['IBM'] == 106.28 assert prices['MSFT'] == 20.89 def test_get_gainloss_2_7(): - gain_loss = get_gainloss_2_7('Data/portfolio.csv', 'Data/prices.csv') + gain_loss = get_gainloss_2_7( + path.join(data_dir, 'portfolio.csv'), + path.join(data_dir, 'prices.csv')) assert round(gain_loss, 2) == -15985.05 \ No newline at end of file diff --git a/Work/work.ps1 b/Work/work.ps1 index a74ccfb21..d7a38c3d2 100644 --- a/Work/work.ps1 +++ b/Work/work.ps1 @@ -2,8 +2,8 @@ param([string]$Command) switch ($Command) { "venv" { - & python -m venv venv - & $PSScriptRoot\venv\Scripts\Activate.ps1 + & python -m venv .venv + & $PSScriptRoot\.venv\Scripts\Activate.ps1 & pip install -r $PSScriptRoot\requirements.txt } "test" { From 3a7839ad6e71b9907534861bc32ecbad468522cf Mon Sep 17 00:00:00 2001 From: Kevin Reed Date: Mon, 7 Aug 2023 23:26:54 -0500 Subject: [PATCH 13/21] 2.12 --- Work/currency.py | 4 ++++ Work/currency_test.py | 13 +++++++++++++ Work/report.py | 35 ++++++++++++++++++++++++----------- Work/report_command.py | 8 ++++++++ Work/report_test.py | 2 +- 5 files changed, 50 insertions(+), 12 deletions(-) create mode 100644 Work/currency.py create mode 100644 Work/currency_test.py create mode 100644 Work/report_command.py diff --git a/Work/currency.py b/Work/currency.py new file mode 100644 index 000000000..6ffee15ab --- /dev/null +++ b/Work/currency.py @@ -0,0 +1,4 @@ +def usd(value: float) -> str: + """Format a number as USD.""" + neg = '-' if value < 0 else '' + return f"{neg}${abs(value):,.2f}" \ No newline at end of file diff --git a/Work/currency_test.py b/Work/currency_test.py new file mode 100644 index 000000000..98139a348 --- /dev/null +++ b/Work/currency_test.py @@ -0,0 +1,13 @@ +import pytest +from currency import usd + +@pytest.mark.parametrize("input,output", [ + (1, "$1.00"), + (11.5, "$11.50"), + (1.75, "$1.75"), + (1200.751, "$1,200.75"), + (-1245.758, "-$1,245.76"), + (-1.754, "-$1.75") +]) +def test_usd(input, output): + assert usd(input) == output \ No newline at end of file diff --git a/Work/report.py b/Work/report.py index 0e89a5355..94a903473 100644 --- a/Work/report.py +++ b/Work/report.py @@ -2,7 +2,7 @@ # # Exercise 2.4 import csv -from pprint import pprint +from currency import usd def read_portfolio(filename): portfolio = [] @@ -48,7 +48,7 @@ def read_prices_2_6(filename): except ValueError: print(f'Could not parse {row}') - pprint(prices) + # pprint(prices) return prices def get_gainloss_2_7(stocksFilename, pricesFilename): @@ -57,20 +57,33 @@ def get_gainloss_2_7(stocksFilename, pricesFilename): total_value = 0.0 total_market_value = 0.0 - total_gain_loss = 0.0 + total_gain = 0.0 + for stock in stocks: + market_price = prices[stock['name']] + stock['change'] = market_price - stock['price'] stock['current_value'] = stock['price'] * stock['shares'] - stock['market_value'] = prices[stock['name']] * stock['shares'] - stock['gain_loss'] = stock['market_value'] - stock['current_value'] + stock['market_value'] = market_price * stock['shares'] + stock['value_gain'] = stock['market_value'] - stock['current_value'] total_value += stock['current_value'] total_market_value += stock['market_value'] - total_gain_loss += stock['gain_loss'] + total_gain += stock['value_gain'] + + return (total_gain, stocks) - print('{name:s} {gain_loss:,.2f}'.format_map(stock)) +def make_report_2_9( + stocksFilename, + pricesFilename): + + (_, stocks) = get_gainloss_2_7( + stocksFilename, + pricesFilename) - print(f'Total value: {total_value:,.2f}') - print(f'Total market value: {total_market_value:,.2f}') - print(f'Total gain/loss: {total_gain_loss:,.2f}') + print() + print(' '.join([f'{h:>10s}' for h in ['Name', 'Shares', 'Price', 'Change']])) + print(' '.join(['----------' for _ in range(4)])) - return total_gain_loss \ No newline at end of file + for stock in stocks: + (name, shares, price, change, _, _, _) = stock.values() + print(f'{name:>10s} {shares:>10d} {usd(price):>10s} {usd(change):>10s}') \ No newline at end of file diff --git a/Work/report_command.py b/Work/report_command.py new file mode 100644 index 000000000..8c7fae4ba --- /dev/null +++ b/Work/report_command.py @@ -0,0 +1,8 @@ +from os import path +from report import make_report_2_9 + +data_dir = path.join(path.dirname(__file__), 'Data') + +make_report_2_9( + path.join(data_dir, 'portfolio.csv'), + path.join(data_dir, 'prices.csv')) \ No newline at end of file diff --git a/Work/report_test.py b/Work/report_test.py index c25cdd307..3a8a22058 100644 --- a/Work/report_test.py +++ b/Work/report_test.py @@ -35,7 +35,7 @@ def test_read_prices_2_6(): assert prices['MSFT'] == 20.89 def test_get_gainloss_2_7(): - gain_loss = get_gainloss_2_7( + (gain_loss, _) = get_gainloss_2_7( path.join(data_dir, 'portfolio.csv'), path.join(data_dir, 'prices.csv')) assert round(gain_loss, 2) == -15985.05 \ No newline at end of file From 782eb58ad3981ed9d3db171b481a6cacda58673a Mon Sep 17 00:00:00 2001 From: Kevin Reed Date: Mon, 14 Aug 2023 22:21:21 -0500 Subject: [PATCH 14/21] 2.15 --- Work/pcost.py | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/Work/pcost.py b/Work/pcost.py index ba4d0032f..d772579b1 100644 --- a/Work/pcost.py +++ b/Work/pcost.py @@ -7,13 +7,12 @@ def pcost(filename): '''Returns the total cost of the portfolio''' with open(filename, 'rt') as f: rows = csv.reader(f) - next(rows) # skip header cost = 0.0 - for fields in rows: + for line_no, row in enumerate(rows, start=1): try: - shares = int(fields[1]) - stock_cost = float(fields[2]) + shares = int(row[1]) + stock_cost = float(row[2]) cost += shares * stock_cost except ValueError: - print(f'Could not parse {fields}') + print(f'Could not parse line {line_no} with data: {row}') return cost \ No newline at end of file From 881b0bb893a157d767c3de7b6a1d532aabbe906f Mon Sep 17 00:00:00 2001 From: Kevin Reed Date: Mon, 14 Aug 2023 22:35:27 -0500 Subject: [PATCH 15/21] 2.16 --- Work/pcost.py | 15 +++++++++------ Work/pcost_test.py | 5 ++++- Work/report.py | 15 ++++++++------- Work/report_test.py | 10 ++++++++-- 4 files changed, 29 insertions(+), 16 deletions(-) diff --git a/Work/pcost.py b/Work/pcost.py index d772579b1..d9f5dfb04 100644 --- a/Work/pcost.py +++ b/Work/pcost.py @@ -6,13 +6,16 @@ def pcost(filename): '''Returns the total cost of the portfolio''' with open(filename, 'rt') as f: - rows = csv.reader(f) + lines = csv.reader(f) cost = 0.0 - for line_no, row in enumerate(rows, start=1): + header_line = next(lines) + print(header_line) + for line_no, line in enumerate(lines): + record = dict(zip(header_line, line)) try: - shares = int(row[1]) - stock_cost = float(row[2]) - cost += shares * stock_cost + shares = int(record['shares']) + price = float(record['price']) + cost += shares * price except ValueError: - print(f'Could not parse line {line_no} with data: {row}') + print(f'Could not parse line {line_no} with data: {line}') return cost \ No newline at end of file diff --git a/Work/pcost_test.py b/Work/pcost_test.py index 10286d468..e9effb416 100644 --- a/Work/pcost_test.py +++ b/Work/pcost_test.py @@ -7,4 +7,7 @@ def test_pcost_1_27(): assert pcost(path.join(data_dir, 'portfolio.csv')) == 44671.15 def test_pcost_1_31(): - assert pcost(path.join(data_dir, 'missing.csv')) == 27381.15 \ No newline at end of file + assert pcost(path.join(data_dir, 'missing.csv')) == 27381.15 + +def test_pcost_2_16(): + assert pcost(path.join(data_dir, 'portfoliodate.csv')) == 44671.15 \ No newline at end of file diff --git a/Work/report.py b/Work/report.py index 94a903473..d7c98b78a 100644 --- a/Work/report.py +++ b/Work/report.py @@ -4,7 +4,7 @@ import csv from currency import usd -def read_portfolio(filename): +def read_portfolio_2_4(filename): portfolio = [] with open(filename, 'rt') as f: rows = csv.reader(f) @@ -21,17 +21,18 @@ def read_portfolio_2_5(filename): portfolio = [] with open(filename, 'rt') as f: rows = csv.reader(f) - next(rows) # skip header - for fields in rows: + header = next(rows) # skip header + for row_no, fields in enumerate(rows): try: + record = dict(zip(header, fields)) holding = { - 'name': fields[0], - 'shares': int(fields[1]), - 'price': float(fields[2]) + 'name': record['name'], + 'shares': int(record['shares']), + 'price': float(record['price']) } portfolio.append(holding) except ValueError: - print(f'Could not parse {fields}') + print(f'Could not parse row {row_no} with data {fields}') return portfolio def read_prices_2_6(filename): diff --git a/Work/report_test.py b/Work/report_test.py index 3a8a22058..968b4e8bd 100644 --- a/Work/report_test.py +++ b/Work/report_test.py @@ -1,10 +1,10 @@ from os import path -from report import read_portfolio, read_portfolio_2_5, read_prices_2_6, get_gainloss_2_7 +from report import read_portfolio_2_4, read_portfolio_2_5, read_prices_2_6, get_gainloss_2_7 data_dir = path.join(path.dirname(__file__), 'Data') def test_read_portfolio_2_4(): - portfolio = read_portfolio(path.join(data_dir, 'portfolio.csv')) + portfolio = read_portfolio_2_4(path.join(data_dir, 'portfolio.csv')) assert portfolio == [ ('AA', 100, 32.2), ('IBM', 50, 91.1), @@ -38,4 +38,10 @@ def test_get_gainloss_2_7(): (gain_loss, _) = get_gainloss_2_7( path.join(data_dir, 'portfolio.csv'), path.join(data_dir, 'prices.csv')) + assert round(gain_loss, 2) == -15985.05 + +def test_report_2_16(): + (gain_loss, _) = get_gainloss_2_7( + path.join(data_dir, 'portfoliodate.csv'), + path.join(data_dir, 'prices.csv')) assert round(gain_loss, 2) == -15985.05 \ No newline at end of file From 1736c36a104f2644f018653f0ae19a7ffcc5a55f Mon Sep 17 00:00:00 2001 From: Kevin Reed Date: Mon, 14 Aug 2023 23:32:36 -0500 Subject: [PATCH 16/21] 2.26 --- Work/dowstocks.py | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) create mode 100644 Work/dowstocks.py diff --git a/Work/dowstocks.py b/Work/dowstocks.py new file mode 100644 index 000000000..86c76cd47 --- /dev/null +++ b/Work/dowstocks.py @@ -0,0 +1,20 @@ +import csv + +def date_tuple(s): + return tuple(str(s).split('/')) + +def main(): + with open('Data/dowstocks.csv') as f: + rows = csv.reader(f) + headers = next(rows) + print(headers) + types = [str, float, date_tuple, str, float, float, float, float, int] + dow_stocks = [ + { key: func(val) for key, func, val in zip(headers, types, row) } + for row in rows + ] + print(dow_stocks[0]['date']) + print(len(dow_stocks)) + +if __name__ == '__main__': + main() \ No newline at end of file From adaefca46453755ae6a6f8073557ac74f3db194a Mon Sep 17 00:00:00 2001 From: Kevin Reed Date: Tue, 15 Aug 2023 23:06:03 -0500 Subject: [PATCH 17/21] 3.4 --- Work/fileparse.py | 27 +++++++++++++++++++++++++++ Work/fileparse_test.py | 8 ++++++++ 2 files changed, 35 insertions(+) create mode 100644 Work/fileparse_test.py diff --git a/Work/fileparse.py b/Work/fileparse.py index 1d499e733..bd095465c 100644 --- a/Work/fileparse.py +++ b/Work/fileparse.py @@ -1,3 +1,30 @@ # fileparse.py # # Exercise 3.3 + +import csv + +def parse_csv(filename, select: list): + ''' + Parse a CSV file into a list of records + ''' + with open(filename) as f: + rows = csv.reader(f) + + # Read the file headers + headers = next(rows) + select_indices = [headers.index(name) for name in select] \ + if select \ + else [i for i in range(len(headers))] + if select: + headers = select + records = [] + for row in rows: + if not row: # Skip rows with no data + continue + record = dict(zip( + headers, + [row[i] for i in select_indices])) + records.append(record) + + return records \ No newline at end of file diff --git a/Work/fileparse_test.py b/Work/fileparse_test.py new file mode 100644 index 000000000..abc67f501 --- /dev/null +++ b/Work/fileparse_test.py @@ -0,0 +1,8 @@ +from fileparse import parse_csv + +def test_parse_csv_select_3_4(): + assert parse_csv('Data/portfolio.csv', select=['name','shares']) == \ + [{'name': 'AA', 'shares': '100'}, {'name': 'IBM', 'shares': '50'}, + {'name': 'CAT', 'shares': '150'}, {'name': 'MSFT', 'shares': '200'}, + {'name': 'GE', 'shares': '95'}, {'name': 'MSFT', 'shares': '50'}, + {'name': 'IBM', 'shares': '100'}] From 8b8f323c080948a9bd4d393d760fabee34cab1cd Mon Sep 17 00:00:00 2001 From: Kevin Reed Date: Tue, 15 Aug 2023 23:23:11 -0500 Subject: [PATCH 18/21] 3.5 --- .vscode/settings.json | 6 +++++- Work/fileparse.py | 29 ++++++++++++++++------------- Work/fileparse_test.py | 39 ++++++++++++++++++++++++++++++++++----- 3 files changed, 55 insertions(+), 19 deletions(-) diff --git a/.vscode/settings.json b/.vscode/settings.json index ce3bda480..dafd670c2 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -4,5 +4,9 @@ "--rootdir=Work", ], "python.testing.unittestEnabled": false, - "python.testing.pytestEnabled": true + "python.testing.pytestEnabled": true, + "[python]": { + "editor.defaultFormatter": "ms-python.black-formatter" + }, + "python.formatting.provider": "none" } \ No newline at end of file diff --git a/Work/fileparse.py b/Work/fileparse.py index bd095465c..cd76dc1ad 100644 --- a/Work/fileparse.py +++ b/Work/fileparse.py @@ -4,27 +4,30 @@ import csv -def parse_csv(filename, select: list): - ''' + +def parse_csv(filename, select: list = [], types: list = []): + """ Parse a CSV file into a list of records - ''' + """ with open(filename) as f: rows = csv.reader(f) # Read the file headers - headers = next(rows) - select_indices = [headers.index(name) for name in select] \ - if select \ + file_headers = next(rows) + headers = select if select else file_headers + indices = ( + [headers.index(name) for name in select] + if select else [i for i in range(len(headers))] - if select: - headers = select + ) records = [] for row in rows: - if not row: # Skip rows with no data + if not row: # Skip rows with no data continue - record = dict(zip( - headers, - [row[i] for i in select_indices])) + select_row = [row[col] for col in indices] + if types: + select_row = [func(col) for func, col in zip(types, select_row)] + record = dict(zip(headers, select_row)) records.append(record) - return records \ No newline at end of file + return records diff --git a/Work/fileparse_test.py b/Work/fileparse_test.py index abc67f501..1e8d5b475 100644 --- a/Work/fileparse_test.py +++ b/Work/fileparse_test.py @@ -1,8 +1,37 @@ from fileparse import parse_csv + def test_parse_csv_select_3_4(): - assert parse_csv('Data/portfolio.csv', select=['name','shares']) == \ - [{'name': 'AA', 'shares': '100'}, {'name': 'IBM', 'shares': '50'}, - {'name': 'CAT', 'shares': '150'}, {'name': 'MSFT', 'shares': '200'}, - {'name': 'GE', 'shares': '95'}, {'name': 'MSFT', 'shares': '50'}, - {'name': 'IBM', 'shares': '100'}] + assert parse_csv("Data/portfolio.csv", select=["name", "shares"]) == [ + {"name": "AA", "shares": "100"}, + {"name": "IBM", "shares": "50"}, + {"name": "CAT", "shares": "150"}, + {"name": "MSFT", "shares": "200"}, + {"name": "GE", "shares": "95"}, + {"name": "MSFT", "shares": "50"}, + {"name": "IBM", "shares": "100"}, + ] + + +def test_parse_csv_select_3_5(): + assert parse_csv("Data/portfolio.csv", types=[str, int, float]) == [ + {"price": 32.2, "name": "AA", "shares": 100}, + {"price": 91.1, "name": "IBM", "shares": 50}, + {"price": 83.44, "name": "CAT", "shares": 150}, + {"price": 51.23, "name": "MSFT", "shares": 200}, + {"price": 40.37, "name": "GE", "shares": 95}, + {"price": 65.1, "name": "MSFT", "shares": 50}, + {"price": 70.44, "name": "IBM", "shares": 100}, + ] + + assert parse_csv( + "Data/portfolio.csv", select=["name", "shares"], types=[str, int] + ) == [ + {"name": "AA", "shares": 100}, + {"name": "IBM", "shares": 50}, + {"name": "CAT", "shares": 150}, + {"name": "MSFT", "shares": 200}, + {"name": "GE", "shares": 95}, + {"name": "MSFT", "shares": 50}, + {"name": "IBM", "shares": 100}, + ] From ab9a1263a14f1570df4ef91558ea7c98985989a5 Mon Sep 17 00:00:00 2001 From: Kevin Reed Date: Tue, 15 Aug 2023 23:32:56 -0500 Subject: [PATCH 19/21] 3.6 --- Work/fileparse.py | 29 ++++++++++++----------------- Work/fileparse_test.py | 35 +++++++++++++++++++++++++++++++++++ 2 files changed, 47 insertions(+), 17 deletions(-) diff --git a/Work/fileparse.py b/Work/fileparse.py index cd76dc1ad..317db9e75 100644 --- a/Work/fileparse.py +++ b/Work/fileparse.py @@ -1,33 +1,28 @@ # fileparse.py # # Exercise 3.3 - import csv -def parse_csv(filename, select: list = [], types: list = []): +def parse_csv( + filename: str, select: list = [], types: list = [], has_headers: bool = True +) -> list: """ Parse a CSV file into a list of records """ with open(filename) as f: rows = csv.reader(f) - - # Read the file headers - file_headers = next(rows) - headers = select if select else file_headers - indices = ( - [headers.index(name) for name in select] - if select - else [i for i in range(len(headers))] - ) + if has_headers: + orig_headers = next(rows) + headers = select if select else orig_headers + indices = [headers.index(name) for name in headers] records = [] - for row in rows: - if not row: # Skip rows with no data + for orig_row in rows: + if not orig_row: continue - select_row = [row[col] for col in indices] + row = [orig_row[i] for i in indices] if has_headers else orig_row if types: - select_row = [func(col) for func, col in zip(types, select_row)] - record = dict(zip(headers, select_row)) + row = [func(field) for func, field in zip(types, row)] + record = dict(zip(headers, row)) if has_headers else tuple(row) records.append(record) - return records diff --git a/Work/fileparse_test.py b/Work/fileparse_test.py index 1e8d5b475..7cb92a27f 100644 --- a/Work/fileparse_test.py +++ b/Work/fileparse_test.py @@ -35,3 +35,38 @@ def test_parse_csv_select_3_5(): {"name": "MSFT", "shares": 50}, {"name": "IBM", "shares": 100}, ] + + +def test_parse_csv_no_headers_3_6(): + assert parse_csv("Data/prices.csv", types=[str, float], has_headers=False) == [ + ("AA", 9.22), + ("AXP", 24.85), + ("BA", 44.85), + ("BAC", 11.27), + ("C", 3.72), + ("CAT", 35.46), + ("CVX", 66.67), + ("DD", 28.47), + ("DIS", 24.22), + ("GE", 13.48), + ("GM", 0.75), + ("HD", 23.16), + ("HPQ", 34.35), + ("IBM", 106.28), + ("INTC", 15.72), + ("JNJ", 55.16), + ("JPM", 36.9), + ("KFT", 26.11), + ("KO", 49.16), + ("MCD", 58.99), + ("MMM", 57.1), + ("MRK", 27.58), + ("MSFT", 20.89), + ("PFE", 15.19), + ("PG", 51.94), + ("T", 24.79), + ("UTX", 52.61), + ("VZ", 29.26), + ("WMT", 49.74), + ("XOM", 69.35), + ] From 47368e9d37614419411d01ca906d9c9e6d7fabae Mon Sep 17 00:00:00 2001 From: Kevin Reed Date: Tue, 15 Aug 2023 23:45:58 -0500 Subject: [PATCH 20/21] 3.7 --- Work/fileparse.py | 8 ++++++-- Work/fileparse_test.py | 12 ++++++++++++ 2 files changed, 18 insertions(+), 2 deletions(-) diff --git a/Work/fileparse.py b/Work/fileparse.py index 317db9e75..0e35f5b00 100644 --- a/Work/fileparse.py +++ b/Work/fileparse.py @@ -5,13 +5,17 @@ def parse_csv( - filename: str, select: list = [], types: list = [], has_headers: bool = True + filename: str, + select: list = [], + types: list = [], + has_headers: bool = True, + delimiter: str = ",", ) -> list: """ Parse a CSV file into a list of records """ with open(filename) as f: - rows = csv.reader(f) + rows = csv.reader(f, delimiter=delimiter) if has_headers: orig_headers = next(rows) headers = select if select else orig_headers diff --git a/Work/fileparse_test.py b/Work/fileparse_test.py index 7cb92a27f..0fe4e8a0d 100644 --- a/Work/fileparse_test.py +++ b/Work/fileparse_test.py @@ -70,3 +70,15 @@ def test_parse_csv_no_headers_3_6(): ("WMT", 49.74), ("XOM", 69.35), ] + + +def test_parse_csv_delimiter_3_7(): + assert parse_csv("Data/portfolio.dat", types=[str, int, float], delimiter=" ") == [ + { "name": "AA", "shares": 100, "price": 32.2 }, + { "name": "IBM", "shares": 50, "price": 91.1 }, + { "name": "CAT", "shares": 150, "price": 83.44 }, + { "name": "MSFT", "shares": 200, "price": 51.23 }, + { "name": "GE", "shares": 95, "price": 40.37 }, + { "name": "MSFT", "shares": 50, "price": 65.1 }, + { "name": "IBM", "shares": 100, "price": 70.44 }, + ] From 1ac115a7f3e823006f12d25019c1a99613f583b8 Mon Sep 17 00:00:00 2001 From: Kevin Reed Date: Tue, 15 Aug 2023 23:44:25 -0500 Subject: [PATCH 21/21] Output correct types in example --- Notes/03_Program_organization/02_More_functions.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Notes/03_Program_organization/02_More_functions.md b/Notes/03_Program_organization/02_More_functions.md index 579a4b980..2c47872b7 100644 --- a/Notes/03_Program_organization/02_More_functions.md +++ b/Notes/03_Program_organization/02_More_functions.md @@ -501,7 +501,7 @@ For example: ```python >>> portfolio = parse_csv('Data/portfolio.dat', types=[str, int, float], delimiter=' ') >>> portfolio -[{'price': '32.20', 'name': 'AA', 'shares': '100'}, {'price': '91.10', 'name': 'IBM', 'shares': '50'}, {'price': '83.44', 'name': 'CAT', 'shares': '150'}, {'price': '51.23', 'name': 'MSFT', 'shares': '200'}, {'price': '40.37', 'name': 'GE', 'shares': '95'}, {'price': '65.10', 'name': 'MSFT', 'shares': '50'}, {'price': '70.44', 'name': 'IBM', 'shares': '100'}] +[{'name': 'AA', 'shares': 100, 'price': 32.2}, {'name': 'IBM', 'shares': 50, 'price': 91.1}, {'name': 'CAT', 'shares': 150, 'price': 83.44}, {'name': 'MSFT', 'shares': 200, 'price': 51.23}, {'name': 'GE', 'shares': 95, 'price': 40.37}, {'name': 'MSFT', 'shares': 50, 'price': 65.1}, {'name': 'IBM', 'shares': 100, 'price': 70.44}] >>> ```