Esempio n. 1
0
class Individual(js.JsonSaveable):
    name = js.String()
    donations = js.List()

    def __init__(self, name, donations):
        self.name = name
        self.donations = donations

    def add_donation(self, donation):
        self.donations.append(donation)

    def number_donations(self):
        return int(len(self.donations))

    def sum_donations(self):
        return sum(self.donations)

    def avg_donations(self):
        return self.sum_donations() / \
               self.number_donations()

    def last_donation(self):
        return self.donations[-1]

    def json_format(self):
        setattr(self, 'donor_name', self.name)
        setattr(self, 'donor_donations', self.donations)
        return self.to_json_compat()

    @property
    def thank_you(self):
        """Add a donation to a donors records and print a report."""
        return (
            'Thank you so much for the generous gift of ${0:.2f}, {1}!'.format(
                self.donations[-1], self.name))
Esempio n. 2
0
class ClassWithList(js.JsonSaveable):

    x = js.Int()
    lst = js.List()

    def __init__(self, x, lst):
        self.x = x
        self.lst = lst
Esempio n. 3
0
class Donor(js.JsonSaveable):

    _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 __str__(self):
        return f"{self._name} donated ${sum(self._donations):,.2f}"

    def __repr__(self):
        return f"{self._name} donated ${sum(self._donations):,.2f}"

    @property
    def name(self):
        return self._name

    @property
    def donations(self):
        return self._donations

    @property
    def total(self):
        return sum(self._donations)

    @property
    def num_donation(self):
        return len(self._donations)

    @property
    def avg_donation(self):
        return sum(self._donations) / len(self._donations)

    def new_donation(self, donation):
        self._donations.append(donation)

    def __eq__(self, other):
        return sum(self._donations) == sum(other._donations)

    def __ne__(self, other):
        return not sum(self._donations) == sum(other._donations)

    def __lt__(self, other):
        return sum(self._donations) < sum(other._donations)

    def __gt__(self, other):
        return not sum(self._donations) < sum(other._donations)

    def __le__(self, other):
        return sum(self._donations) >= sum(other._donations)

    def __ge__(self, other):
        return not sum(self._donations) >= sum(other._donations)
class MyClass(js.JsonSaveable):

    x = js.Int()
    y = js.Float()
    lst = js.List()

    def __init__(self, x, lst):
        self.x = x
        self.lst = lst
Esempio n. 5
0
class DonorDB(js.JsonSaveable):

    _donors = js.List()

    def __init__(self, donors=None):
        if donors is None:
            self._donors = []
        else:
            self._donors = donors

    @property
    def donors(self):
        return self._donors

    def add_donor(self, donor):
        self._donors.append(donor)

    def load(self):
        with open("Donor_DB.json", "r") as db_file:
            self._donors = js.from_json(db_file)._donors

    def save(self):
        with open("Donor_DB.json", "w") as db_file:
            db_file.write(self.to_json())

    def generate_donor_list(self):
        output = "-" * 20 + "\nList of Donors:\n" + "-" * 20
        for donor in sorted(self._donors, key=lambda donor: donor.name):
            output += "\n" + donor.name
        output += "\n" + "-" * 20
        return output

    def build_report(self):
        donors = self._donors
        header = ["Donor Name", "Total Given", "Num Gifts", "Average Gifts"]
        report = ""
        report += "-" * 75
        report += f"\n{header[0]:30}| {header[1]:15}| {header[2]:10}| {header[3]:20}\n"
        report += "-" * 75
        for donor in sorted(self._donors,
                            key=lambda donor: donor.total,
                            reverse=True):
            report += f"\n{donor.name:30}${donor.total:>15,.2f}{donor.num_donation:>13}{donor.avg_donation:>15,.2f}"
        report += "\n" + "-" * 75
        return report
Esempio n. 6
0
class Donor(js.JsonSaveable):
    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)
Esempio n. 7
0
class Donors(js.JsonSaveable):
    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:
            #print("donor is {}..".format(donor))
            if donor.name == 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):
        donor_data = {}
        for donor in self.donors_list:
            name = donor.name
            donations = donor.donations
            donor_data[name] = 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:
            self.to_json(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])
Esempio n. 8
0
class Donors(js.JsonSaveable):
    """Provide a class to handle a collection of donors."""

    _donors = js.List()

    def __init__(self, _donors):
        """Instantiate a Donors class object with a list of SingleDonors."""
        self._donors = _donors

    def __iter__(self):
        """Make the Donors class object iterable."""
        return iter(self._donors)

    # def __str__(self):
    #     # Used only for debugging -- to be removed or commented out
    #     return str([donor.__repr__() for donor in self._donors])

    def __contains__(self, donor_str):
        """Provide a method to check if donor (expects a str) is in donors."""
        return donor_str in [donor.name for donor in self._donors]

    def get_donor(self, name):
        """Given a name (str), return the donor object, or raise ValueError."""
        for donor in self._donors:
            if donor.name == name:
                return donor
        else:
            raise ValueError("No such donor exists")

    def append(self, donor):
        """Append a donor object to the list of donors."""
        self._donors.append(donor)

    def print_donor_names(self):
        """Print existing donor names on screen in alphabetical order."""
        donors_L = [
            donor.name
            for donor in sorted(self._donors, key=SingleDonor.sort_by_name)
        ]
        num = len(donors_L)
        donors_S = (("\n" + ", ".join(["{}"] * num)).format(*donors_L))
        print(donors_S)

    def create_report(self):
        """Create and print a report."""
        report = ""
        title_line_form = "\n{:<26}{:^3}{:>13}{:^3}{:>13}{:^3}{:>13}\n"
        title_line_text = ('Donor Name', '|', 'Total Given', '|', 'Num Gifts',
                           '|', 'Average Gift')
        report += title_line_form.format(*title_line_text)
        report += str('- ' * 38)
        form_line = "\n{:<26}{:>3}{:>13}{:>3}{:>13}{:>3}{:>13}"
        donors_list = sorted(self._donors,
                             key=SingleDonor.sort_by_total,
                             reverse=True)
        for donor in donors_list:
            report += (form_line.format(
                donor.name, '$', sum(donor.donations), ' ',
                len(donor.donations), '$',
                round((sum(donor.donations) / len(donor.donations)), 2)))
        report += "\n"
        print(report)

    def challenge(self, factor, min_donation, max_donation, projection):
        """Return a new Donors class object or a projected sum."""
        result = [
            donor.challenge(factor, min_donation, max_donation, projection)
            for donor in self._donors
        ]
        if projection:
            return sum(result)
        else:
            return Donors(result)

    def get_total(self):
        """Return the aggregate amount of donations for all donors."""
        return sum([sum(donor.donations) for donor in self._donors])
Esempio n. 9
0
class SingleDonor(js.JsonSaveable):
    """Provide a class for a single donor."""

    _donations = js.List()
    _name = js.String()

    def __init__(self, _name, _donations):
        """Instantiate a SingleDonor class object."""
        self._name = _name
        if isinstance(_donations, list):
            self._donations = _donations
        elif isinstance(_donations, tuple):
            self._donations = list(_donations)
        else:
            self._donations = [_donations]

    @property
    def name(self):
        """Provide a getter method for the name property."""
        return self._name

    @property
    def donations(self):
        """Provide a getter method for the donations property."""
        return self._donations

    def sort_by_total(self):
        """Provide a sort_key for sorting by total donations."""
        return sum(self._donations)

    def sort_by_name(self):
        """Provide a sort_key for sorting by name."""
        return self._name

    def __str__(self):
        """Return self._name."""
        return self._name

    def __repr__(self):
        """Return SingleDonor("Name", [donations])."""
        if len(self._donations) == 1:
            return 'SingleDonor("{}", {})'.format(self._name,
                                                  self._donations[0])
        else:
            return 'SingleDonor("{}", {})'.format(self._name, self._donations)

    def __eq__(self, other):
        """Return True if names and donations are the same."""
        return (self._name, self._donations) == (other.name, other.donations)

    def __lt__(self, other):
        """Provide __lt__ method used in sorting somehow, I guess."""
        return ((self._name, self._donations) < (other.name, other.donations))

    def add_donation(self, amount):
        """Add a donation."""
        self._donations.append(amount)

    def get_last_donation(self):
        """Return the last donation."""
        return self._donations[-1]

    # def challenge(self,
    #               factor,
    #               min_donation,
    #               max_donation,
    #               projection):
    #     """Return a SingleDonor class object or the projected contribution.
    #
    #     Either multiply all donations by the factor, or
    #     multiply only those donations which are above min_donation or
    #     below max_donation, if any of these parameters is provided,
    #     while the remaining donations remain unchanged.
    #     If the projection is True, return the projected contribution.
    #     """
    #     # Several safeguards
    #     if type(factor) is str or factor <= 1:
    #         raise ValueError("Factor must be a number > 1")
    #     elif type(min_donation) is str or type(max_donation) is str:
    #         raise ValueError("Input must be a number")
    #     elif min_donation is not None and max_donation is not None:
    #         raise ValueError("Min and max must not be both defined")
    #
    #     # Helper function.
    #     def subject_to_increase(x):
    #         """Return True if x above min /below max or if min/max undefined."""
    #         if min_donation is not None:
    #             return x > min_donation
    #         elif max_donation is not None:
    #             return x < max_donation
    #         else:
    #             return True
    #
    #     # The only reason for the following ugly construct is because
    #     # I couldn't imagine how to structure my solution to use map/filter
    #     some_donations = list(filter(subject_to_increase, self.donations))
    #     updated_donations = list(map(lambda x: x * factor
    #                                  if x in some_donations
    #                                  else x,
    #                                  self.donations
    #                                  )
    #                              )
    #
    #     # The following does the same as above but looks much clearer
    #     # updated_donations = [x * factor
    #     #                      if subject_to_increase(x)
    #     #                      else x
    #     #                      for x in self.donations
    #     #                      ]
    #
    #     # projected contribution = increased donationed minus old donations
    #     if projection:
    #         return sum(updated_donations) - sum(self.donations)
    #     else:
    #         return SingleDonor(self.name, updated_donations)

    def multiplier_factory(self, factor, min_donation, max_donation):
        """Create the multiplier function for use in challenge method.

           Args:
                factor (float): the multiplier to be locked in
                                the return function
                min_donation (float or None): a condition to be locked in
                max_donation (None or float): a condition to be locked in
            Returns:
                  a function which will multiply its argument by factor
                  subject to conditions.
        """
        # Several safeguards
        if type(factor) is str or factor <= 1:
            raise ValueError("Factor must be a number > 1")
        elif type(min_donation) is str or type(max_donation) is str:
            raise ValueError("Input must be a number")
        elif min_donation is not None and max_donation is not None:
            raise ValueError("Min and max must not be both defined")

        def func(x):
            def subject_to_increase(x):
                """Decide if the donation (i.e. x) must be increased."""
                if min_donation is not None:
                    return x > min_donation
                elif max_donation is not None:
                    return x < max_donation
                else:
                    return True

            if subject_to_increase(x):
                return factor * x
            else:
                return x

        return func

    def challenge(self, factor, min_donation, max_donation, projection):
        """Return an updated SingleDonor object or a projected contribution."""
        multiplier = self.multiplier_factory(factor, min_donation,
                                             max_donation)
        updated_donations = list(map(multiplier, self.donations))

        # projected contribution = increased donations minus old donations
        if projection:
            return sum(updated_donations) - sum(self.donations)
        else:
            return SingleDonor(self.name, updated_donations)
Esempio n. 10
0
class Donor(js.JsonSaveable):
    """donor giving to organization"""
    id = js.Int()
    firstname = js.String()
    lastname = js.String()
    email = js.String()
    _donations = js.List()
    _donation_id = js.Int()

    def __init__(self, id, firstname=None, lastname=None, email=None):
        """args:
            id (int): identification for donor.  Will try to force to int when
                initiated or raise error.
            firstname (str, optional): string representing given name
            lastnamt (str, optional): string representing surname

            _donations (list): contains Donation objects from donor
            _donation_id (int): tracks indentification for donations"""
        try:
            self.id = int(id)
        except ValueError:
            raise ValueError('id input should be interpreted as integer')
        self.firstname = firstname
        self.lastname = lastname
        self.email = email
        self._donations = []
        self._donation_id = 1

    def donation_total(self):
        """returns the total amount the donor has donated"""
        if self.donations:
            print(self.donations)
            return sum([i.amount for i in self.donations])
        else:
            return 0

    def add_donation(self, amount, date=datetime.datetime.utcnow()):
        """adds donation for user"""
        self._donations.append(Donation(amount=amount, date=date, id=self._donation_id))
        self._donation_id += 1

    def donation_count(self):
        """returns count of donations"""
        return len(self._donations)

    @property
    def donations(self):
        """returns donations"""
        return self._donations

    @property
    def fullname(self):
        """returns combine first and last name"""
        return " ".join([self.firstname, self.lastname])

    def summarize_donor(self):
        """provides summary tuple of donor"""
        return (self.id, self.fullname, self.donation_total(), self.donation_count(), self.donation_total()/self.donation_count())

    def __repr__(self):
            return str(self.to_json_compat())
class DonorData(js.JsonSaveable):
    data = js.List()

    def __init__(self, data):
        self.data = data
Esempio n. 12
0
class Donor(js.JsonSaveable):
    _donation = js.List()
    _name = js.String()

    def __init__(self, name, donation=None):
        if name == '':
            self._name = 'Anonymous'
        else:
            self._name = name
        if donation is None:
            self._donation = []
        elif isinstance(donation, (int, float)):
            self._donation = [donation]
        else:
            self._donation = donation

    @property
    def name(self):
        return self._name

    @name.setter
    def name(self, name):
        self._name = str(name)

    @property
    def donation(self):
        return self._donation

    @donation.setter
    def donation(self, donation):
        if type(donation) == list:
            self._donation = donation
        else:
            raise TypeError("Donation has to be a list of int")

    def add_donation(self, donation_amount):
        if donation_amount >= 0:
            self._donation.append(donation_amount)
        else:
            raise ValueError("Donation amount must be greater than 0.")

    def __repr__(self):
        return "Donor({} : {})".format(self._name, self._donation)

    def __str__(self):
        return "Donor: {} donated {}\n".format(self._name, self._donation)

    def donation_occurrences(self):
        return len(self._donation)

    def total_donation_amount(self):
        return sum(self._donation)

    def average_total_donor_amount(self):
        try:
            return self.total_donation_amount() / self.donation_occurrences()
        except ValueError:
            return self._donation

    def stats(self):
        return [
            self.total_donation_amount(),
            self.donation_occurrences(),
            self.average_total_donor_amount()
        ]
Esempio n. 13
0
class DonorList(js.JsonSaveable):
    _donors = js.List()
    file_name = "donor_list_json_file.json"
    new_donors_list = js.List()

    def __init__(self, donors=[]):
        if donors is None:
            self._donors = []
        elif isinstance(donors, Donor):
            self._donors = [donors]
        else:
            self._donors = donors

    """def save_to_json(self):
        target_directory = os.getcwd()
        target_file_path = os.path.join(target_directory, self.file_name)
        new_donors_list = table_dictionary.to_json()
        with open(target_file_path, 'w') as fp:
            js.to_json(new_donors_list, fp, indent=4)"""

    def save_to_json(self):
        target_directory = os.getcwd()
        target_file_path = os.path.join(target_directory, self.file_name)
        donor_to_json = table_dictionary.to_json()
        with open(target_file_path, "w") as fp:
            fp.write(donor_to_json)

    def load_from_json(self):
        target_directory = os.getcwd()
        target_file_path = os.path.join(target_directory, self.file_name)
        with open(target_file_path, 'r') as fp:
            donor_load = fp.read()
        return js.from_json(donor_load)

    def populate_dict(self):
        target_directory = os.getcwd()
        target_file_path = os.path.join(target_directory, self.file_name)
        if os.path.exists(target_file_path):
            abd = self.load_from_json()
            self._donors = abd._donors
        else:
            self.init_dict()

    def init_dict(self):
        self._donors = [
            Donor('Toni Orlando', [150.00, 200.00, 100.00]),
            Donor('Amanda Clark', [1800.00]),
            Donor('Robin Hood', [1234.56, 4500.34, 765.28]),
            Donor('Gina Travis', [523.10, 75.00]),
            Donor('Mark Johnson', [850.00, 20.14])
        ]

    @property
    def donors(self):
        return self._donors

    def __str__(self):
        d_list = ''
        for d in self._donors:
            d_list += d + '\n'
        return d_list

    def __repr__(self):
        d_list = 'repr'
        for d in self._donors:
            d_list += str(d)
        return d_list

    def add_donor_list(self, donor):
        return self._donors.append(donor)

    def names_only_list(self):
        names_list = []
        for d in self._donors:
            names_list.append(d.name)
        return names_list

    def find_donor_history(self, name):
        for d in self._donors:
            if d.name == name:
                return d

    def display_donor_list(self):
        """Prints the full list of donors and corresponding donation history."""
        for d in self._donors:
            print("{:<20}: {}".format(d.name, d.donation))
        pass

    """def __iter__(self):
        return iter(self._donors)"""

    def stat_donor_list(self):
        new_list = []
        for k in self._donors:
            sum_donations = k.total_donation_amount()
            total_gifts = k.donation_occurrences()
            average_gift = k.average_total_donor_amount()
            new_list.append([k.name, sum_donations, total_gifts, average_gift])
        sorted_new_list = sorted(new_list, key=itemgetter(1), reverse=True)
        return sorted_new_list
Esempio n. 14
0
class Donor(js.JsonSaveable):
    _name = js.String()
    _list_donations = js.List()
    _donation_count = js.Int()
    _amount = js.Float()

    def __init__(self, name, list_donations):
        self._name = name
        self._list_donations = list_donations
        self._donation_count = len(list_donations)
        self._amount = sum(list_donations)

    @property
    def name(self):
        return self._name

    @property
    def amount(self):
        return self._amount

    @amount.setter
    def amount(self, amount):
        self._amount = amount

    def add(self, donation_amount):
        self._amount += donation_amount
        self._donation_count += 1
        self._list_donations.append(donation_amount)

    @property
    def donation_count(self):
        return self._donation_count

    @property
    def average(self):
        return self._amount / self._donation_count

    def get_letter_text(self, name, amount):
        msg = []
        msg.append('Dear {},'.format(name))
        msg.append(
            '\n\n\tThank you for your very kind donation of ${:.2f}.'.format(
                amount))
        msg.append('\n\n\tIt will be put to very good use.')
        msg.append('\n\n\t\t\t\tSincerely,')
        msg.append('\n\t\t\t\t-The Team\n')
        return "".join(msg)

    def challenge(self, factor, min_donation=None, max_donation=None):
        """Get the sum of projection donation from a donor"""
        new_list = []
        if min_donation is not None and max_donation is not None:
            #filter minimum & maximum
            new_list = list(
                filter(lambda donation: donation >= min_donation,
                       self._list_donations))
            new_list = list(
                filter(lambda donation_amount: donation_amount <= max_donation,
                       new_list))
            new_list = list(map(lambda x: x * factor, new_list))
        elif min_donation is None and max_donation is not None:
            #filter maximum
            new_list = list(
                filter(lambda donation: donation <= max_donation,
                       self._list_donations))
            new_list = list(map(lambda x: x * factor, new_list))
        elif min_donation is not None and max_donation is None:
            # filter minimum
            new_list = list(
                filter(lambda donation: donation >= min_donation,
                       self._list_donations))
            new_list = list(map(lambda x: x * factor, new_list))
        else:
            # no minimum and maximum
            new_list = list(map(lambda x: x * factor, self._list_donations))

        return sum(new_list)

    def __lt__(self, other):
        return self._amount < other._amount

    def __gt__(self, other):
        return self._amount > other._amount

    def __eq__(self, other):
        return self._amount == other._amount
Esempio n. 15
0
class Donor(js.JsonSaveable):
    """
    Class to store a donor record

    Args:
        full_name(str)      format: <first name> [<middle_name>] <last_name>
                                    [,<suffix>]
            or
        first_name(str)
        middle_name(str)
        last_name(str)
        suffix(str)

        donation(float)     amount of today's donation
            or
        donations(list)     list of dicts containing donations

        Not typically specified
        -----------------------
        did(str)            string representation of a record UUID
        created(str)        string representation of an utcnow isoformat
                            timestamp with "Z" appended

    Usage:

        The class allows flexibility in how a record is set.  It is allowable
        to create an empty instance of the class and update it piecemeal.

        It is also allowable to pass in all information needed to create a
        record using different strategies such as a full_name vs the
        constituent parts.

        In general, record manipulation is simply the name and donation.
        However, it is also allowable to set information that is normally
        auto-created such as the did, time created and entire donation list.
        This allows a record to be created from its repr which may have
        practical uses in record backup/recreation as needed.

    Example Use Cases:

        # empty donor record
        In [599]: d=Donor()
        In [600]: repr(d)
        Out[600]: "Donor(did='97d744de-de23-11e7-bee5-0800274b0a84',
            created='2017-12-11T03:30:11.077937Z' )"
        In [601]:

        # automatic parsing of full_name
        In [601]: d=Donor(full_name="sally q smith, iv")
        In [602]: repr(d)
        Out[602]: "Donor(did='067f51c4-de24-11e7-bee5-0800274b0a84',
                         first_name='Sally', middle_name='Q',
                         last_name='Smith', suffix='IV',
                         created='2017-12-11T03:33:16.728654Z' )"
        In [603]:

        # name creation based name sub-components
        In [594]: d=Donor(first_name="joe", last_name="smith", donation=100)
        In [595]: repr(d)
        Out[595]: "Donor(did='7724e750-de23-11e7-bee5-0800274b0a84',
                         first_name='Joe',last_name='Smith',
                         donations=[{'amount': 100.0, 'date': '2017-12-11Z'}],
                         created='2017-12-11T03:29:16.221954Z' )"
        In [596]:

        In [636]: d=Donor(full_name="john adams")
        In [637]: d.add_donation=1000
        In [638]: repr(d)
        Out[638]: "Donor(did='7e93d724-de25-11e7-bee5-0800274b0a84',
                         first_name='John',last_name='Adams',
                         donations=[{'amount': 1000.0, 'date': '2017-12-11Z'},
                         {'amount': 1000.0, 'date': '2017-12-11Z'}],
                         created='2017-12-11T03:43:47.686457Z' )"
        In [639]:

    """

    created = js.String()
    audit = js.List()
    _did = js.String()
    _first_name = js.String()
    _last_name = js.String()
    _middle_name = js.String()
    _suffix = js.String()
    _donations = js.List()

    def __init__(self,
                 did="",
                 created="",
                 full_name="",
                 first_name="",
                 last_name="",
                 middle_name="",
                 suffix="",
                 donations=None,
                 donation=None):

        self.__name__ = "Donor"

        # normally no did is passed in, so we create one
        if did == "":
            did = str(uuid.uuid1())
        # create a uuid for each record
        self.did = did

        # normally, no timestamp is passed in, so we create one
        if created == "":
            created = datetime.datetime.utcnow().isoformat() + "Z"
        # keep track of record creation
        self.created = created

        # test to see if any of the name components are set
        sub_name_set = any(sub_name != ""
                           for sub_name in (first_name, middle_name, last_name,
                                            suffix))

        # we don't expect full_name and a subset to be set at the same time
        if full_name != "" and sub_name_set:
            raise ValueError("You cannot set 'full_name' and a subset of the "
                             "name at the same time.")

        # set the name via full_name or via the constituent parts
        if full_name != "":
            self.full_name = full_name
        else:
            self.first_name = first_name.title()
            self.last_name = last_name.title()
            self.middle_name = middle_name.title()
            self.suffix = suffix.upper()

        if (donations is not None) and (donation is not None):
            raise ValueError("You cannot set 'donations' and 'donation' at "
                             "the same time.")

        if donation is not None:
            # if a donation is passed in, initialize the donations,
            # then add the donation
            self._donations = list()
            self.add_donation(donation)
        else:
            if donations is not None:
                # TODO donations need more validation
                self._donations = donations
            else:
                self._donations = list()

    @property
    def did(self):
        """ return the donor did """
        return self._did

    @did.setter
    @audit_log
    def did(self, value):
        """ set the did """
        self._did = value

    @property
    def donations(self):
        """ print all donations """
        return self._donations

    @donations.setter
    @audit_log
    def donations(self, value):
        """ allow bulk setting of donation entries """
        self._donations = value

    @audit_log
    def add_donation(self, value):
        """ add a single donation """
        today = datetime.datetime.utcnow().date().isoformat() + "Z"
        try:
            value = float(value)
        except ValueError:
            raise ValueError("Donations must be values.")
        self._donations.append({"amount": value, "date": today})

    @property
    def number_donations(self):
        """ return the number of donations """
        return len(self._donations)

    @property
    def total_donations(self):
        """ return a sum of the donations """
        total_donations = 0
        for donation in self.donations:
            total_donations += donation["amount"]
        return round(total_donations, 2)

    @property
    def average_donations(self):
        """ return the average donation amount """
        try:
            return round(self.total_donations / self.number_donations, 2)
        except ZeroDivisionError:
            return 0

    @property
    def first_name(self):
        """ return the first name """
        return self._first_name

    @first_name.setter
    @audit_log
    def first_name(self, value):
        """ set the first name """
        self._first_name = value.title()

    @property
    def middle_name(self):
        """ return the middle name """
        return self._middle_name

    @middle_name.setter
    @audit_log
    def middle_name(self, value):
        """ set the middle name """
        self._middle_name = value.title()

    @property
    def last_name(self):
        """ return the last name """
        return self._last_name

    @last_name.setter
    @audit_log
    def last_name(self, value):
        """ set the last name """
        self._last_name = value.title()

    @property
    def suffix(self):
        """ return the suffix """
        return self._suffix

    @suffix.setter
    @audit_log
    def suffix(self, value):
        """ set the suffix """
        # special case roman numbers to all caps
        if value.lower() in ["i", "ii", "iii", "iv", "v"]:
            self._suffix = value.upper()
        else:
            self._suffix = value.title()

    @property
    def informal_name(self):
        """ return full name minus the suffix """
        return " ".join(
            filter(None, [self.first_name, self.middle_name, self.last_name]))

    @property
    def full_name(self):
        """ return the formal, full name """
        # if suffix, add with a comma
        if self.suffix != "":
            suffix = ", " + self.suffix
        else:
            suffix = ""
        return " ".join(
            filter(None, [self.first_name, self.middle_name, self.last_name
                          ])) + suffix

    @full_name.setter
    def full_name(self, full_name):
        """ allow the donor information to be updated via full_name as well """

        # for simplicity's sake, we make the assumption a suffix will
        # follow a comma

        # capture suffix, if present
        suffix = ""
        if full_name.find(","):
            try:
                suffix = full_name.split(",")[1].strip()
            except Exception:
                pass

        # name with suffix removed
        informal_name = full_name.split(",")[0].strip()

        # we assume the last name is one word minus the suffix
        last_name = informal_name.split()[-1]

        # build a list with everything to the left of the last name
        everything_but_last = informal_name.split()[0:-1]

        # pull the first name off
        try:
            first_name = everything_but_last.pop(0)
        except IndexError:
            # if we get an error, they probably gave us a malformed name
            first_name = ""

        try:
            # pull the middle, if exists
            middle_name = everything_but_last.pop(0)
        except IndexError:
            middle_name = ""

        self.first_name = first_name
        self.middle_name = middle_name
        self.last_name = last_name
        self.suffix = suffix

    def _query_attributes(self, which_attributes=(), return_empty=True):
        """ return list of formatted attribute/value entries """
        attributes = []
        for attr in which_attributes:
            # for our purposes, never print internal attributes
            if not attr.startswith("_"):
                try:
                    str_val = repr(getattr(self, attr))
                    # don't return empty values if they don't want them
                    if eval(str_val) or return_empty:
                        attributes.append(attr + "=" + str_val)
                except (AttributeError, SyntaxError):
                    # we know certain attributes are not readable, so skip them
                    pass
        return attributes

    def __repr__(self):
        """
        Return only the (settable) attributes

        Return only settable attributes so
        eval(repr(Donor(<foo>))) == Donor(<foo>)
        """
        which_attributes = [
            "did", "first_name", "middle_name", "last_name", "suffix",
            "donations", "created"
        ]
        attributes = self._query_attributes(which_attributes,
                                            return_empty=False)
        return "Donor( " + ", ".join(attributes) + " )"

    def __str__(self):
        """ return all the non-internal attributes """
        which_attributes = []
        for attr in dir(self):
            if not attr.startswith("_"):
                which_attributes.append(attr)
        return "str(Donor(\n    " + ",\n    ".join(
            self._query_attributes(which_attributes)) + " ))"
Esempio n. 16
0
class donor_database(js.JsonSaveable):

    dl = js.List()

    def __init__(self, donors=None):
        self.donor_data = {d: d for d in donors}

    def save_to_file(self, filename):
        for donor in self.donor_data.values():
            name = donor.name
            gifts = donor.donations
            temp = [name, gifts]
            self.dl.append(temp)

        print(self.dl)
        '''
        with open(filename, 'w') as outfile:
            json.dump(self.dl, outfile)
            print("\njson dump complete!")
        '''

        with open(filename, 'w') as outfile:
            self.to_json(outfile)
        print("\nto_json complete!")

    @classmethod
    def load_from_file(cls, filename):
        with open(filename, 'r') as infile:
            obj = js.from_json(infile)
        print("\nfrom_json complete!")
        return obj

    @property
    def donors(self):
        return self.donor_data.values()

    def donor_list(self):
        listing = []
        for donor in self.donors:
            listing.append(donor.name)
        return "\n".join(listing)

    def find_donor(self, name):
        return self.donor_data.get(name)

    def add_donor(self, name):
        print('Donor added: ' + name)
        donor = donor_record(name)
        self.donor_data[donor] = donor
        return donor

    def print_screen_report(self):
        print('')
        print('{:20}{:>15}{:>10}{:>10}'.format('Donor Name', '| Total Gifts',
                                               '| Num Gifts', '| Ave Gift'))
        print('-' * 55)
        for donor in self.donor_data.values():
            name = donor.name
            gifts = donor.donations
            num_gifts = len(gifts)
            total_gifts = "{:.2f}".format(donor.total_donations)
            avg_gift = "{:.2f}".format(donor.average_donation)
            print('{:20}{:>15}{:>10}{:>10}'.format(name, total_gifts,
                                                   num_gifts, avg_gift))

    def create_individual_letters(self):
        try:
            for donor in self.donor_data.values():
                name = donor.name
                total_gifts = donor.total_donations
                file_name = name.replace(" ", "_") + ".txt"
                my_file = open(file_name, "w")
                my_file.write(gen_letter_body(name, total_gifts))
                my_file.close()
        except IOError:
            return ("\n" + "File error!")
        return ("*** Files saved! ***")