From 9d4980c97e48c42c877d774fc767ccace3b31beb Mon Sep 17 00:00:00 2001 From: A D Date: Tue, 19 Feb 2019 17:44:11 -0800 Subject: [PATCH] Adding mailroom_v4 files from lesson 6 --- .../aaron/homework_lesson6/mailroom_v4.py | 178 ++++++++++++++++++ .../homework_lesson6/mailroom_v4_test.py | 123 ++++++++++++ 2 files changed, 301 insertions(+) create mode 100755 students/aaron/homework_lesson6/mailroom_v4.py create mode 100755 students/aaron/homework_lesson6/mailroom_v4_test.py diff --git a/students/aaron/homework_lesson6/mailroom_v4.py b/students/aaron/homework_lesson6/mailroom_v4.py new file mode 100755 index 0000000..9b213ce --- /dev/null +++ b/students/aaron/homework_lesson6/mailroom_v4.py @@ -0,0 +1,178 @@ +#!/usr/bin/env python3 + +#------------------------------------- +# Assignment: mailroom (version 4, from lesson 6) +# Changelog: +# 2019-02-19,Aaron Devey,Created +#------------------------------------- + +import re + +donors = {'bob barker': {'donations': 2, 'total': 24456.24, 'name': 'Bob Barker'}, + 'roger rabbit': {'donations': 1, 'total': 4930.26, 'name': 'Roger Rabbit'}, + 'bruce lee': {'donations': 3, 'total': 52246.75, 'name': 'Bruce Lee'}, + 'frodo baggins': {'donations': 1, 'total': 1249.44, 'name': 'Frodo Baggins'}, + 'kermit the frog': {'donations': 2, 'total': 23475.20, 'name': 'Kermit the Frogg'}} + +# prints a main menu +def show_main_menu(): + print(generate_main_menu()) + +# moving the actual menu text into a testable def +def generate_main_menu(): + menu = ("-" * 70) + "\n" + for number, func in sorted(menu_opts.items()): + menu += "{}. {}\n".format(number, menu_opts_text[func]) + return menu + +# gets a menu selection +def get_main_selection(): + user_in = "" + while user_in == "": + show_main_menu() + user_in = input("Enter a menu number: ") + if user_in not in menu_opts.keys(): + print("Invalid selection. Enter a menu selection:", list(menu_opts.keys())) + user_in = "" + return user_in + +# used for testing, returns the entire donors dict +def get_donors(): + return donors + +# logic for user-interaction of a new donation +# moved testable pieces into different defs +def send_thankyou(): + user_in = "" + names = [n.lower() for n in list(donors.keys())] + while user_in == "": + print("----------------------------------------------------------------") + user_in = input("Enter a full name or type 'list' for a list of names: ") + user_lower = user_in.lower() + if user_lower == 'list': + print("Donors: ") + for name in names: + print(" " + name) + user_in = "" + amount = 0 + while amount == 0: + try: + amount = float(input("Enter a donation amount: ")) + except: + amount = 0 + print("-> ERROR: Please enter a valid floating point number.") + add_donation(user_in, amount) + output_thankyou(user_in, amount) + +# adds a new donor +def new_donor(donor_name): + donors[donor_name.lower()] = {'donations': 0, 'total': 0, 'name': donor_name} + return donors[donor_name.lower()] + +# adds a new donation for a donor +def add_donation(donor_name, amount): + lower_donor_name = donor_name.lower() + if lower_donor_name not in list(donors.keys()): + new_donor(donor_name) + donors[donor_name.lower()]['donations'] += 1 + donors[donor_name.lower()]['total'] += amount + return donors[donor_name.lower()] + +# print a report +def output_report(): + print(generate_report()) + +def generate_report(): + table_headerfmt = "{:<19s}|{:>15s} |{:>14s} |{:>15s}\n" + table_formatter = "{:<19s} ${:13.2f} {:14d} ${:13.2f}\n" + table_seperator = "-" * 68 + "\n" + generated_text = "" + + generated_text += table_headerfmt.format("Donor Name", "Total Given", "Num Gifts", "Average Gift") + generated_text += table_seperator + + # unpack to a list of lists + donor_list = [[donors[donor]['name'], donors[donor]['donations'], donors[donor]['total']] for donor in donors] + + # sort the list + donor_list = sorted(donor_list, key = lambda x: x[2] * -1) + + # add the list to the generated text + for donor in donor_list: + generated_text += table_formatter.format(donor[0], donor[2], donor[1], donor[2] / donor[1]) + + # return the generated report + return generated_text + +# print a thank you for a recent donation +def output_thankyou(donor_name, latest_amount): + print(generate_thankyou(donor_name, latest_amount, True)) + +# moved the writing logic to a separate def, just printing in this one. +def write_letters(): + for donor, file in write_letter_files().items(): + print('>>> Wrote thank you note for {} to {}'.format(donor, file)) + +# write a thank you note to disk for a donor +# return a dict of {donor_name: donor_file} +def write_letter_files(): + files_written = {} + for donor_name in donors.keys(): + # convert any non-alphanumeric characters to _ for the filename + donor_file = re.sub(r"[^a-zA-Z0-9_-]+", "_", donor_name) + ".txt" + + note = generate_thankyou(donor_name) + f = open(donor_file, 'w') + f.write(note) + f.close() + files_written[donor_name] = donor_file + return files_written + +# generate a thank you note for a donor +def generate_thankyou(donor_name, latest_amount=0, recent=False): + lower_donor_name = donor_name.lower() + donations_total = donors[lower_donor_name]["total"] + donations_count = donors[lower_donor_name]["donations"] + cased_donor_name = donors[lower_donor_name]["name"] + format_values = {'donations_total': donations_total, + 'donations_count': donations_count, + 'donor_name': cased_donor_name, + 'latest_amount': latest_amount} + if recent: + template = '''---------------------------------------------------------------------- +Dear {donor_name}, + Thank you for your generous donation of {latest_amount:.2f}! +That brings your total of {donations_count} donation(s) to ${donations_total:.2f} +Sincerely, + -Me +''' + else: + template = '''---------------------------------------------------------------------- +Dear {donor_name}, + Thank you for all {donations_count} of your generous donations for a total of {donations_total:.2f}! +We will put the money to good use. +Sincerely, + -Me +''' + letter = template.format(**format_values) + return(letter) + +# the main loop +def main(): + selection = "2" + while menu_opts[selection] != quit: + selection = get_main_selection() + menu_opts[selection]() + +# a dict for mapping user selection to functions +menu_opts = {'1': send_thankyou, + '2': output_report, + '3': write_letters, + '4': quit} +menu_opts_text = {send_thankyou: "Send a Thank You to a single donor.", + output_report: "Create a report.", + write_letters: "Send letters to all donors.", + quit: "Quit."} + +if __name__ == '__main__': + main() diff --git a/students/aaron/homework_lesson6/mailroom_v4_test.py b/students/aaron/homework_lesson6/mailroom_v4_test.py new file mode 100755 index 0000000..72aae4e --- /dev/null +++ b/students/aaron/homework_lesson6/mailroom_v4_test.py @@ -0,0 +1,123 @@ +#!/usr/bin/env python3 +# +#------------------------------------- +# Assignment: mailroom tests (mailroom version 4, from lesson 6) +# Description: This file contains tests for mailroom_v4.py +# Changelog: +# 2019-02-19,Aaron Devey,Created +#------------------------------------- +# +# + +import pytest +import glob +from mailroom_v4 import generate_main_menu, generate_report, generate_thankyou, write_letter_files, new_donor, add_donation, get_donors + + +def test_generate_main_menu_text(): + test_text = '''---------------------------------------------------------------------- +1. Send a Thank You to a single donor. +2. Create a report. +3. Send letters to all donors. +4. Quit. +''' + assert(generate_main_menu() == test_text) + +def test_generate_report_text(): + test_text = '''Donor Name | Total Given | Num Gifts | Average Gift +-------------------------------------------------------------------- +Bruce Lee $ 52246.75 3 $ 17415.58 +Bob Barker $ 24456.24 2 $ 12228.12 +Kermit the Frogg $ 23475.20 2 $ 11737.60 +Roger Rabbit $ 4930.26 1 $ 4930.26 +Frodo Baggins $ 1249.44 1 $ 1249.44 +''' + assert(generate_report() == test_text) + +def test_generate_thankyou_report_text(): + test_text = '''---------------------------------------------------------------------- +Dear Bruce Lee, + Thank you for all 3 of your generous donations for a total of 52246.75! +We will put the money to good use. +Sincerely, + -Me +''' + assert(generate_thankyou("Bruce Lee") == test_text) + +def test_generate_thankyou_new_donation(): + test_text = '''---------------------------------------------------------------------- +Dear Bruce Lee, + Thank you for your generous donation of 232.12! +That brings your total of 3 donation(s) to $52246.75 +Sincerely, + -Me +''' + assert(generate_thankyou("Bruce Lee", 232.12, True) == test_text) + +def test_generate_thankyou_unknown_donor(): + with pytest.raises(KeyError): + generate_thankyou("Adam West") + with pytest.raises(KeyError): + generate_thankyou("Adam West", 1232.12, True) + +def test_write_letter_files(): + test_written = {'bob barker': 'bob_barker.txt', + 'bruce lee': 'bruce_lee.txt', + 'frodo baggins': 'frodo_baggins.txt', + 'kermit the frog': 'kermit_the_frog.txt', + 'roger rabbit': 'roger_rabbit.txt'} + assert(write_letter_files() == test_written) + +def test_new_donor(): + test_donor = {'name': 'Adam West', 'total': 0, 'donations': 0} + test_all_donors = {'adam west': {'donations': 0, 'name': 'Adam West', 'total': 0}, + 'bob barker': {'donations': 2, 'name': 'Bob Barker', 'total': 24456.24}, + 'bruce lee': {'donations': 3, 'name': 'Bruce Lee', 'total': 52246.75}, + 'frodo baggins': {'donations': 1, 'name': 'Frodo Baggins', 'total': 1249.44}, + 'kermit the frog': {'donations': 2, 'name': 'Kermit the Frogg', 'total': 23475.2}, + 'roger rabbit': {'donations': 1, 'name': 'Roger Rabbit', 'total': 4930.26}} + assert(new_donor('Adam West') == test_donor) + assert(get_donors() == test_all_donors) + +def test_add_donation(): + test_donor = {'donations': 1, 'name': 'Adam West', 'total': 1122.33} + test_all_donors = {'adam west': {'donations': 1, 'name': 'Adam West', 'total': 1122.33}, + 'bob barker': {'donations': 2, 'name': 'Bob Barker', 'total': 24456.24}, + 'bruce lee': {'donations': 3, 'name': 'Bruce Lee', 'total': 52246.75}, + 'frodo baggins': {'donations': 1, 'name': 'Frodo Baggins', 'total': 1249.44}, + 'kermit the frog': {'donations': 2, 'name': 'Kermit the Frogg', 'total': 23475.2}, + 'roger rabbit': {'donations': 1, 'name': 'Roger Rabbit', 'total': 4930.26}} + assert(add_donation('Adam West', 1122.33) == test_donor) + assert(get_donors() == test_all_donors) + +def test_add_donation_new_donor(): + test_donor = {'donations': 1, 'name': 'Han Solo', 'total': 3322.11} + test_all_donors = {'adam west': {'donations': 1, 'name': 'Adam West', 'total': 1122.33}, + 'bob barker': {'donations': 2, 'name': 'Bob Barker', 'total': 24456.24}, + 'bruce lee': {'donations': 3, 'name': 'Bruce Lee', 'total': 52246.75}, + 'frodo baggins': {'donations': 1, 'name': 'Frodo Baggins', 'total': 1249.44}, + 'kermit the frog': {'donations': 2, 'name': 'Kermit the Frogg', 'total': 23475.2}, + 'roger rabbit': {'donations': 1, 'name': 'Roger Rabbit', 'total': 4930.26}, + 'han solo': test_donor} + + assert(add_donation('Han Solo', 3322.11) == test_donor) + assert(get_donors() == test_all_donors) + +def test_written_file_outputs(): + test_written = {'adam west': 'adam_west.txt', + 'bob barker': 'bob_barker.txt', + 'bruce lee': 'bruce_lee.txt', + 'frodo baggins': 'frodo_baggins.txt', + 'han solo': 'han_solo.txt', + 'kermit the frog': 'kermit_the_frog.txt', + 'roger rabbit': 'roger_rabbit.txt'} + assert(write_letter_files() == test_written) + +def test_written_file_contents(): + test_first_line = "-" * 70 + "\n" + test_second_line = "Dear Adam West,\n" + f = open("adam_west.txt", "r") + actual_first_line = f.readline() + actual_second_line = f.readline() + assert(actual_first_line == test_first_line) + assert(actual_second_line == test_second_line)