class DonorHistory: name = js.String() donations = js.List() def __init__(self, name, donations=None): self.name = name if donations is None: self.donations = [] else: self.donations = donations def add_donation(self, amount): try: self.donations.append(int(amount)) except ValueError: print("Please enter a donation amount.") def number_donations(self): return len(self.donations) def total_donation(self): try: return sum(self.donations) except TypeError: return self.donations def donation_avg(self): try: return self.total_donation() / self.number_donations() except TypeError: return self.donations
class Donor(): name = js.String() amount = js.Float() lst = js.List() def __init__(self, name, lst = []): self.name = name.capitalize() self.donation = lst def add_donations(self, amount): self.donation.append(amount) @property def total(self): return sum(self.donation) @property def times(self): return len(self.donation) @property def ave(self): return self.total/self.times @property def last(self): if len(self.donation) > 0: return self.donation[-1] else: return 0 def __str__(self): return f'{self.name}:{self.donation}'
class Donor: name = js.String() donations = js.List() def __init__(self, name, donations): self.name = name self.donations = donations @property def total_donations(self): return sum(self.donations) @property def num_of_gifts(self): return len(self.donations) def add_donation(self, donation): self.donations.append(donation) def save_data(self): data = self.to_json_compat() data = json.dumps(data) with open('{}.json'.format(self.name), 'w') as datafh: datafh.write(data) @classmethod def load_data(cls, donor_name): with open('{}'.format(donor_name), 'r') as ddatafh: ddata = ddatafh.read() ddata = json.loads(ddata) return cls.from_json_dict(ddata)
class ClassWithList: x = js.Int() lst = js.List() def __init__(self, x, lst): self.x = x self.lst = lst
class Donor: _f_name = js.String() _l_name = js.String() _donations = js.List() def __init__(self, f_name, l_name, donation=None): self._f_name = f_name self._l_name = l_name if donation is None: self._donations = [] else: self._donations = donation def f_name(self): return self._f_name def l_name(self): return self._l_name @property def full_name(self): return self._f_name + " " + self._l_name @property def get_donation_count(self): return len(self._donations) @property def get_donation_total(self): return sum(self._donations) @property def get_avg_donation(self): if len(self._donations) == 0: return 0 else: return round(sum(self._donations) / len(self._donations), 2) def add_donation(self, value): self._donations.append(value) def get_email_text(self, current_donation): """Prints a thank you email to a donator Donor name and amount is passed in as a parameter""" return "Dear {:s} {:s},\n\ Thank you for the generous donation of ${:,.2f}.\n\ Sincerely,\n\ Your Local Charity".format(*current_donation) def get_letter_text(self): """Returns a message of the donor name and donation total""" message = "Dear {:s},\n\ Thank you for donating ${:,.2f}.\n\ Sincerely,\n\ Your Local Charity" return message.format(self.full_name, self.get_donation_total)
class MyClass: x = js.Int() y = js.Float() lst = js.List() def __init__(self, x, lst): self.x = x self.lst = lst
class Donor: """ Instance of donor class for each donor. Takes a tuple of (name, [donations]) and has methods for finding total donations and average donation. Sorts on donor name or total donations. Implementing save as JSON feature """ name = js.String() _donations = js.List() def __init__(self, *args, **kwargs): """ Expected input format is a tuple containting a donor name and a list of donation values. """ self.name = str(args[0]) # Using protected _donations value because I don't want people # overwriting These values. I don't mind if they change the name. try: self._donations = [float(x) for x in args[1]] except IndexError: self._donations = [] def add_donation(self, val): """ Appends a positive numerical value to donations""" if val < 0: raise ValueError('Donation must be greater than 0') self._donations.append(float(val)) def sort_key(self): return self.name def sort_by_total(self): return self.total @property def donations(self): return self._donations @property def total(self): return sum(self._donations) @property def count(self): return len(self._donations) @property def average(self): try: return self.total / self.count except ZeroDivisionError: return 0
class DonorCollection: """ Keeps track of the collection of donors as well as writing thank you letters and the donor report """ donorList = js.List() def __init__(self): self.donorList = js.List() def addDonor(self, donor): """ Add a donor to the list """ self.donorList.append(donor) def findDonor(self, searchName): """ Search for a donor in the list by the donor's name """ result = None for donor in self.donorList: if donor.name.lower() == searchName.strip().lower(): result = donor break return result def listDonors(self): """ List all the donors """ for donor in self.donorList: print(donor.name) def storeThankYouLetters(self): """ Save each thank you letter to a file by the donor's name """ for donor in self.donorList: f = open("{:s}.txt".format(donor.name).replace(" ", "_"), 'w') f.write(donor.createThankYouEmail()) f.close() def getReport(self): """ Return a report of all the donors and donation roll-up """ result = ( "\nDonor Name | Total Given | Num Gifts | Average Gift" "\n------------------------------------------------------------") for donor in self.donorList: result += "\n{:20s}| ${:10.2f} |{:10d} | ${:10.2f}".format( donor.name, donor.getTotDonation(), donor.getNumDonations(), donor.getAvgDonation()) return result def getDictRep(self): return [donor.getDictRep() for donor in self.donorList] def saveDB(self): print(self.to_json_compat()) f = open("donors.json", 'w') f.write(self.to_json_compat()) f.close()
class json_dh: Donors = js.List() def __init__(self, dh): self.Donors = [{'name': donor_object.name, 'donations': donor_object.donations} \ for donor_object in dh.donors.values()] def save(self): with open('json_out.json', 'w') as outfile: self.to_json(fp=outfile) @classmethod def load(cls, file='json_in.json'): with open(file, 'r') as infile: return js.from_json(infile)
class Donor(): _first = js.String() _last = js.String() _donations = js.List() def __init__(self, first_name, last_name, donations=None): self._first = first_name self._last = last_name if donations is None: self._donations = [] else: self._donations = donations @property def full_name(self): return '{} {}'.format(self._first, self._last) def thank_you_letter_template(self, donor): if self.num_of_donations > 1: return "Dear {},\nThank you for your {} generous donations of ${:.2f}. Your continued support helps our charity stay in business.\n\nSincerely,\n-The Team\n".format( donor.full_name, donor.num_of_donations, donor.total_donation) else: return "Dear {},\nThank you for your generous donation of ${:.2f}. Your support helps our charity stay in business.\n\nSincerely,\n-The Team\n".format( donor.full_name, donor.total_donation) def add_donation(self, amount): return self._donations.append(amount) @property def num_of_donations(self): return len(self._donations) @property def total_donation(self): return sum(self._donations) @property def avg_donation(self): return sum(self._donations / len(self._donations)) @property def donation_lis(self): return self._donations def __str__(self): return f"{self.full_name:20} ${self.total_donation:>17.2f} {self.num_of_donations:>6} ${self.avg_donation:>16.2f}"
class Donor: """ Class to keep track of a donor with a donor name and list of donation amounts """ name = js.String() donationList = js.List() def __init__(self, name=''): self.name = name.strip() self.donationList = js.List() def addDonation(self, amount): """ Add a donation amount to the list """ self.donationList.append(amount) # CHANGE INTO A PROPERTY def getNumDonations(self): """ Get the number of donations the donor has made """ return len(self.donationList) def getAvgDonation(self): """ Get the average value of the donations the donor has made""" try: return self.getTotDonation() / self.getNumDonations() except ZeroDivisionError: return 0.0 def getTotDonation(self): """ Get the total value of the donations the donor has made """ return sum(self.donationList) def createThankYouEmail(self): """ Return donation thank you e-mail text based on the donor name and the donated amount """ result = ( "\nDear {:s},\n\n" "\tThank you so much for your generous donation of ${:,.2f}!\n\n" "\tIt will be put to very good use.\n\n" "\t\tSincerely,\n\t\t\t- The Team".format(self.name, self.getTotDonation())) return result def getDictRep(self): return {self.name: self.donationList}
class Donor(): first_name = js.String() last_name = js.String() donations = js.List() def __init__(self, first, last, amount): self.first_name = first self.last_name = last if type(amount) is list: self.donations = amount else: self.donations = [amount] def add_donation(self, amount): if type(amount) is list: self.donations.extend(amount) else: self.donations.append(amount) def challenge(self, factor, min_donation=None, max_donation=None): if min_donation is not None: self.donations = list( filter(lambda x: x > min_donation, self.donations)) if max_donation is not None: self.donations = list( filter(lambda x: x < max_donation, self.donations)) self.donations = list(map(lambda x: x * factor, self.donations)) @property def total(self): return sum(self.donations) @property def count(self): return len(self.donations) @property def average(self): if self.count == 0: return 0 else: return self.total / self.count def __lt__(self, other): return self.total < other.total def __le__(self, other): return self.total <= other.total def __eq__(self, other): return self.total == other.total def __ge__(self, other): return self.total >= other.total def __gt__(self, other): return self.total > other.total def __ne__(self, other): return self.total != other.total
class Donor(object): name = js.String() donations = js.List() def __init__(self, name, donations=None): if not name: raise ValueError("Donor name cannot be empty.") self.name = name if not donations: self.donations = [] else: self.donations = donations @property def first(self): name_sp = self.name.split() if len(name_sp) == 1: return '' else: return name_sp[0] @property def last(self): name_sp = self.name.split() if len(name_sp) == 1: return self.name else: return ' '.join(name_sp[1:]) def add_donation(self, amount): if float(amount) <= 0: raise ValueError('Donation amount must be a positive number.') else: self.donations.append(amount) @property def last_donation(self): if not self.donations: return None else: return self.donations[-1] def generate_letter(self): """ Generates a Thank You letter to send to a donor. Uses the last value in their donations list to mention their last donation amount. :param donor: a donor dictionary entry :return: string containing the text of the Thank You letter. """ format_string = """ Dear {first_name} {last_name}, Thank you for your donation of ${last_donation:.2f}. Warmest Regards, Local Charity """ return format_string.format(last_donation=float(self.last_donation), first_name=self.first, last_name=self.last)
class Donors(object): DATA_FILE = 'donors' donorlist = js.List() def __init__(self, donorlist=None): if donorlist: self.donorlist = donorlist else: self.donorlist = [] def add_donor(self, donor): self.donorlist.append(donor) def print_report(self): """ Prints a formatted report on the donors with name, amount given, number of gifts, and average gift. :return: None """ # Find longest name in donor list, or use name_min value if self.donorlist: name_max = max(*[len(donor.name) for donor in self.donorlist], 25) else: name_max = 25 rpt_title = "Donor Name" + ' ' * ( name_max - 9) + "| Total Given | Num Gifts | Average Gift" print(rpt_title) print("-" * len(rpt_title)) for donor in self.donorlist: print(f"{donor.name:{name_max}} ", end='') ddons = donor.donations print( f"$ {sum(ddons):>10.2f} {len(ddons):>9} ${sum(ddons)/len(ddons):>12.2f}" ) def send_letters_all(self): """ Runs through donor data structure, generates a thank you letter for each and saves it to the current working directory with in a date+donorname.txt file :return: None """ for donor in self.donorlist: print(f'Generating letter for {donor.name}') now = time.localtime() f_name = f"{now.tm_year}{now.tm_mon:02}{now.tm_mday:02}_" f_name += donor.name.replace(" ", "_") + ".txt" with open(f_name, 'w') as file_out: file_out.write(donor.generate_letter()) return None @property def list_donors(self): return [donor.name for donor in self.donorlist] @property def count(self): return len(self.donorlist) def get_donor(self, dname): if dname == "": return None for donor in self.donorlist: if donor.name == dname: return donor newdonor = Donor(dname) self.add_donor(newdonor) return newdonor def print_json(self): print(self.to_json()) def load_json(self): print('loading from json') with open(self.DATA_FILE + ".json") as file_in: temp = js.from_json(file_in) self.donorlist = temp.donorlist return self.count def save_json(self): print('saving to json') json_dlist = self.to_json() with open(self.DATA_FILE + ".json", 'w') as file_out: file_out.write(json_dlist) return self.count def load_donorlist(self): print('loading from pickle') with open(self.DATA_FILE + ".pkl", 'rb') as file_in: self.donorlist = pickle.load(file_in) return self.count def save_donorlist(self): print('saving to pickle') with open(self.DATA_FILE + ".pkl", 'wb') as file_out: pickle.dump(self.donorlist, file_out) return self.count def total_contribution(self): return sum([sum(d.donations) for d in self.donorlist]) def challenge(self, mul, min_don=None, max_don=None): """ Multiplies donations, returns a Donors object. Note that this function destroys the original order of the donations, as multiplied donation values get grouped together, and non-multiplied donations get moved to the end of the list. :param mul: a multiplier to be applied to each donation value of each donor :param min_don: multiplier only applies to values greater than this amount, if None - does not apply :param max_don: multiplier only applies to values less than or equal to this amount, if None - does not apply :return: a new Donors object, with multiplied donation values """ # define the filter functions, based on if min or max values for donation multipliers were added # unfortunately, PEP8 abhors assigning lambdas to variables, so here are some glorious Defs. # fi = donations that should be multiplied # not_fi = donations that should not be multiplied if min_don and max_don: def fi(x): return max_don >= x > min_don def not_fi(x): return x <= min_don or x > max_don elif min_don: def fi(x): return x > min_don def not_fi(x): return x <= min_don elif max_don: def fi(x): return x <= max_don def not_fi(x): return x > max_don else: def fi(x): return True def not_fi(x): return False # iterate the donorlist, apply filters to the list of donations for each donor, multiply the donations on only # one of the filters, add these two donation lists together, slap it together with the donors' name to create # a donor object. List comprehension makes a list of the new Donors, which is then used as an input to create # a new Donors() class object. return Donors([ Donor( donor.name, list(map(lambda y: y * mul, filter(fi, donor.donations))) + list(filter(not_fi, donor.donations))) for donor in self.donorlist ])
class Donor: """ class to hold the information about a single donor """ name = js.String() donations = js.List() # reference to the DB its in -- this will be set in the instance # when added to the DonorDB _donor_db = None def __init__(self, name, donations=None): """ create a new Donor object :param name: the full name of the donor :param donations=None: iterable of past donations """ self.norm_name = self.normalize_name(name) self.name = name.strip() if donations is None: self.donations = [] else: self.donations = list(donations) def __str__(self): msg = (f"Donor: {self.name}, with {self.num_donations:d} " f"donations, totaling: ${self.total_donations:.2f}") return msg def mutating(method): """ Decorator that saves the DB when a change is made It should be applied to all mutating methods, so the data will be saved whenever it's been changed. NOTE: This requires that the donor object is in a DonorDB. """ # note that this is expecting to decorate a method # so self will be the first argument def wrapped(self, *args, **kwargs): print("wrapped method called") print(self) print(self._donor_db) res = method(self, *args, **kwargs) if self._donor_db is not None: self._donor_db.save() return res return wrapped @staticmethod def normalize_name(name): """ return a normalized version of a name to use as a comparison key simple enough to not be in a method now, but maybe you'd want to make it fancier later. """ return name.lower().strip() @property def last_donation(self): """ The most recent donation made """ try: return self.donations[-1] except IndexError: return None @property def total_donations(self): return sum(self.donations) @property def num_donations(self): return len(self.donations) @property def average_donation(self): return self.total_donations / self.num_donations @mutating def add_donation(self, amount): """ add a new donation """ print("add_donation called") amount = float(amount) if amount <= 0.0: raise ValueError("Donation must be greater than zero") self.donations.append(amount) def gen_letter(self): """ Generate a thank you letter for the donor :param: donor tuple :returns: string with letter note: This doesn't actually write to a file -- that's a separate function. This makes it more flexible and easier to test. """ return dedent('''Dear {0:s}, Thank you for your very kind donation of ${1:.2f}. It will be put to very good use. Sincerely, -The Team '''.format(self.name, self.last_donation))
class Roster: donor_list = js.List() def __init__(self, donors): self.donor_list = donors def donor_roster(self): donor_names = [] for donor in self.donor_list: donor_names.append(donor.name) return donor_names def save_roster(self): data = self.to_json_compat() data = json.dumps(data) with open('roster.json', 'w') as data_file: data_file.write(data) def load_roster(self): global mailroom with open('roster.json', 'r') as roster_json: roster = roster_json.read() roster_dict = json.loads(roster) mailroom = self.from_json_dict(roster_dict) return mailroom def thank_you(self, new_donor, amount): if new_donor in self.donor_list: try: donor_list[new_donor].add_donation(amount) print('Thank you {} for your generous donation of ${:.2f}'. format(new_donor, amount)) except ValueError: print("Please enter a valid amount.") else: new_donor_object = DonorHistory(new_donor, [amount]) self.donor_list.append(new_donor_object) print('Thank you {} for your generous donation of ${:.2f}'.format( new_donor, amount)) def donor_report(self): print('Here is a list of donors and the amount donated.') report = [] report.append('|{:<20}|{:<20}|{:<20}|{:<20}|'.format( 'Name', 'Total Donation', 'No. of Donations', 'Average Donation')) for donor in self.donor_list: report.append('|{:<20}|{:>20}|{:>20}|{:>20}|'.format( donor.name, donor.total_donation(), donor.number_donations(), donor.donation_avg())) return '\n'.join(report) def print_donors(self): print(self.donor_report()) def write_files(self): """Creates a text file with a thank you message for each of the donors in the dictionary""" for donor_data in self.donor_list: filename = donor_data.name.replace(" ", "_") + ".txt" total_donation = donor_data.total_donation() letter = ( 'Thank you {} for you generous contributions totalling {:.2f}!' .format(donor_data.name, total_donation)) with open(filename, 'w') as letter_file: letter_file.write(letter) print(f"{donor_data.name}'s letter has been saved to " + filename) def challenge(self, factor, lo=None, hi=None): challenge = [] if lo and hi: if lo > hi: raise ValueError( 'Maximum must be greater than the minimum amount.') else: for donor in self.donor_list: challenge.append( list( map( lambda x: x * factor, filter(lambda y: min <= y <= max, donor.donations)))) return challenge elif min: challenge.append( list( map(lambda x: x * factor, filter(lambda y: y >= min, donor.donations)))) return challenge elif max: challenge.append( list( map(lambda x: x * factor, filter(lambda y: y <= max, donor.donations)))) return challenge else: challenge.append(list(map(lambda x: x * factor, donor.donations))) return challenge def make_roster(self, factor, hi=None, lo=None): donor_names = self.donor_roster() philanthropist_donation = self.challenge(factor, hi, lo) donors_new = list(zip(donor_names, philanthropist_donation)) for name in donors_new: new_list = [] name = DonorHistory(name[0], name[1]) new_list.append(name) return Roster(new_list) def donation_projected(self, factor, lo=None, hi=None): return sum(list(sum(self.challenge(factor, lo, hi), [])))
class DonorDonations(object): """ Class to hold the records of a donor and their donations. """ # Class attributes # It becomes apparent when looking at the constructor and the format we will be working with. # Need to work with the data in JSON format - here we will use: # string --> Donor name donor_name = js.String() # list --> Donations made by donor donations = js.List() # May not be needed but adding in case need it initial_state = False # Constructor - take in the name of the donor and donations def __init__(self, donor_name, donations=None): """Constructor for instantiating a donor.""" # Follow the example in example_dec.py # self.donor_name = donor_name # self.donations = [] self.donor_name = donor_name if donations is None: self.donations = [] else: self.donations = list(donations) @property def first_name(self): forename = self.donor_name.split() return forename[0] @property def last_name(self): surname = self.donor_name.split() return surname[1] @property def last_donation(self): """Grab the most recent donation from the list of donations.""" try: # We want the last donation so we want to use the negative index return self.donations[-1] except IndexError: return None @property def total_donations(self): """Total up all the donations that were made by a donor.""" return sum(self.donations) @property def average_donation(self): """Take the total_donations that were calculated and divide by the amount of donations made using len() on the donations list.""" return self.total_donations / len(self.donations) def letter_template(self): """Template for writing a letter to a donor, thanking them for their donation.""" return """Dear {}{},\n Thank you for your very kind donation of ${:.2f}.\n\n It will be put to very good use.\n\n \t\tSincerely,\n\t\t\t -The Team""".format(self.first_name, self.last_name, self.last_donation)
class Donor(): _name = js.Tuple() _donations = js.List() def __init__(self, val=None): self.name = val self._donations = [] @property def name(self): return self._name @name.setter def name(self, val): if val is None: self._name = None else: self._name = tuple(val.split()) @staticmethod def check_list(l): return all( (isinstance(item, int) or isinstance(item, float)) for item in l) @property def donations(self): return self._donations @donations.setter def donations(self, val): if isinstance(val, int) or isinstance(val, float): self.donations.append(val) elif self.check_list(val): self._donations = val else: raise ValueError("Donation should be in USD!") def above_min(self, min_don): return list(filter(lambda x: x >= min_don, self.donations)) def below_max(self, max_don): return list(filter(lambda x: x <= max_don, self.donations)) def between_min_max(self, min_don, max_don): return list(filter(lambda x: min_don <= x <= max_don, self.donations)) def above(self, min_don): return list(filter(lambda x: x > min_don, self.donations)) def below(self, max_don): return list(filter(lambda x: x < max_don, self.donations)) def multiply_donations(self, factor, min_donation=None, max_donation=None): if min_donation and max_donation: ll = self.below(min_donation) + \ list(map(lambda x: x * factor, self.between_min_max(min_donation, max_donation))) + \ self.above(max_donation) elif min_donation and not max_donation: ll = self.below(min_donation) + (list( map(lambda x: x * factor, self.above_min(min_donation)))) elif max_donation and not min_donation: ll = list( map(lambda x: x * factor, self.below_max(max_donation))) + self.above(max_donation) else: ll = list(map(lambda x: x * factor, self.donations)) self.donations = ll return (self) @property def total(self): return sum(self.donations) @property def average(self): return sum(self.donations) / len(self.donations) @property def number(self): return len(self.donations) @property def greetings(self): g1 = """ Ex Programmers Charity 1999 Heartbeat Avenue 11111 Fresh Spring, Alaska Dear""" g2 = "".join([' {}'] * len(self.name)).format(*self.name) g3 = """ Thank you so much for your generous donation of ${} It will be put to very good use. Sincerely, -The Team """.format(self.total) return (g1 + g2 + "," + g3) @property def print_greetings(self): print(self.greetings) def write_letter(self): try: with open( "_".join(['{} '] * len(self.name)).format(*self.name) + '.txt', 'w') as f: f.write(self.greetings) except IOErrors as e: print(""" Cannot write a file, cought {} """.format(e))
def __init__(self): self.donorList = js.List()
def __init__(self, name=''): self.name = name.strip() self.donationList = js.List()
class Donors(): data_file = 'donors' donors_list = js.List() def __init__(self, donors_list=None): # list of donors objects if donors_list: self.donors_list = donors_list else: self.donors_list = [] @property def list_of_donors(self): return [donor.name for donor in self.donors_list] @property def count(self): return len(self.donors_list) def add_donor(self, donor): self.donors_list.append(donor) def get_donor(self, name): if name == "": return None for donor in self.donors_list: if donor == name: return donor new_donor = Donor(name, []) self.add_donor(new_donor) return new_donor def send_letters(self): """ Send letters to every one, the letters will be stored as text files on disk """ for donor in self.donors_list: file_name = donor.name + ".txt" letter = donor.generate_letter() with open(file_name, "w") as f: f.write(letter) def create_a_report(self): """ Prints donor information for all donors """ print( "Donor Name | Total Given | Num Gifts | Average Gift" ) for donor in self.donors_list: if donor.donations: print( f"{donor.name:26} $ {sum(donor.donations):>10.2f} {len(donor.donations):9} ${sum(donor.donations)/len(donor.donations):>12.2f}" ) else: print("coming to else") def load_donors_list(self): temp_list = [] donations = [] for donor in donor_data: donor_obj = Donor(donor, donations) donor_obj.donations = donor_data[donor] temp_list.append(donor_obj) self.donors_list = temp_list return self.count def load_json_from_file(self): print("Loading from json") with open(self.data_file + ".json", 'r') as file_in: temp = js.from_json(file_in) self.donors_list = temp.donors_list return self.count def save_donors_list(self): for donor in self.donors_list: if donor.name not in donor_data: donor_data[donor.name] = donor.donations return self.count def save_json_to_file(self): print("Saving to json file") json_dlist = self.to_json() with open(self.data_file + ".json", 'w') as file_out: file_out.write(json_dlist) return self.count def print_json(self): print(self.to_json()) def total_contribution(self): return sum([sum(d.donations) for d in self.donors_list]) def challenge(self, mul, min_donation=None, max_donation=None): if min_donation and max_donation: def func(x): return max_donation > x > min_donation elif min_donation: def func(x): return x > min_donation elif max_donation: def func(x): return x < max_donation else: def func(x): return True return Donors([ Donor(donor.name, list(map(lambda x: x**mul, filter(func, donor.donations)))) for donor in self.donors_list ])
class Donor(object): name = js.String() donations = js.List() def __init__(self, name, donations=None): if not name: raise ValueError("Donor name can not be empty") self.name = name if donations: self.donations = donations else: self.donations = [] @property def first_name(self): name_split = self.name.split() if len(name_split) >= 1: return name_split[0] @property def last_name(self): name_split = self.name.split() if len(name_split) == 1: return '' else: return ''.join(name_split[1:]) @property def donor_donations(self): """ Returns list of donor donations :return: list of donor donations """ return self.donations @property def donor_donations_sum(self): """ Returns sum of all donor donations :return: donor latest donation """ return sum(self.donations) @property def latest_donation(self): """ Returns donor latest donation :return: donor latest donation """ if self.donations: return self.donations[-1] def add_donation(self, amount): """ Adds donation to donor donations :return: """ if float(amount) <= 0: raise ValueError("donation amount can not be negative") self.donations.append(float(amount)) def generate_letter(self): """ Generate letter for donor """ return "Dear {},\n \nThank you for your generous donation {}.\n \n\n\t\tSincerely, \n\t\tLocal Charity". \ format(self.name, self.latest_donation)