class Donor(json.JsonSaveable):
    name = json.String()
    donation = json.List()
    
    def __init__(self, name=None, donation=None):
        self.name = name
        self.donation = donation

    def __lt__(self, other):
        return self.name < other.name
        
    @property
    def average(self):
        return sum(self.donation)/len(self.donation)
    
    @property
    def total(self):
        return sum(self.donation)
       
    @property
    def display(self):
        print('{:20}'.format(self.name) +
                '$ {:>10.2f} {:7}'.format(self.total, len(self.donation)) +
                ' '*14 + '$ {:>10.2f}'.format(self.average))

    @property
    def letter(self):
        content = ('To {},\n'.format(self.name) +
                    'Thank you for your donation of ${:.2f}.'.format(self.total) +
                    '\n'*2 + '\t'*5 + '-System Generated Email')
        with open(self.name + '.txt', 'w') as text:
            text.write(content)

    def add_donation(self, amount):
        self.donation.append(amount)
예제 #2
0
class OtherSaveable(js.JsonSaveable):

    foo = js.String()
    bar = js.Int()

    def __init__(self, foo, bar):
        self.foo = foo
        self.bar = bar
예제 #3
0
class Donor(js.JsonSaveable):
    """Contains all information for a single donor"""

    name = js.String()
    _donations = js.List()
    _total = js.Int()
    _ave = js.Int()

    def __init__(self, name, donations=None):
        if not donations:
            donations = []

        self.name = str(name)
        self._donations = list(donations)
        self._total = sum(self._donations)
        self._ave = self._total / len(self._donations) if donations else 0

    @property
    def donations(self):
        """Get list of donations for this donor"""
        return self._donations

    @property
    def num_donations(self):
        """Get number of donations given by this donor"""
        return len(self._donations)

    @property
    def total_donations(self):
        """Get total amount of donations given by this donor"""
        return self._total

    @property
    def average_donations(self):
        """Get average donation amount for this donor"""
        return self._ave

    def add_donation(self, amount):
        """Add donation to this donor's donation history

        Args:
            donation (int): donation amount
        """
        self._donations.append(amount)
        self._total += amount
        self._ave = self._total / self.num_donations
class Mailroom(json.JsonSaveable):
    name = json.String()
    donorlist = json.List()
    
    def __init__(self, name=None, donorlist=None):
        self.name = name
        self.donorlist = donorlist
    
    @property
    def save(self):
        mailroom_saved = self.to_json()
        with open('mailroom.json', 'w') as file:
            file.write(mailroom_saved) 
        print('Mailroom database saved as mailroom.json....')
    
    @property
    def load(self):
        with open('mailroom.json', 'r') as file:
            mailroom_loaded = json.from_json(file.read())
        return mailroom_loaded

    @property
    def sort(self):
        self.donorlist.sort()
    
    @property
    def display_all(self):
        if self.donorlist[0].name is not None:
            self.sort
        for i in self.donorlist:
            i.display
    @property
    def letter_all(self):
        for i in self.donorlist:
            i.letter

    def add_donor(self, donor_name, amount):
        if self.donorlist[0].name is None:
            self.donorlist[0].name = donor_name
            self.donorlist[0].donation = [amount]
        else:
            if donor_name not in [x.name for x in self.donorlist]:
                self.donorlist += [Donor(donor_name, [amount])]
            else:
                i = [x.name for x in self.donorlist].index(donor_name)
                self.donorlist[i].donation += [amount]
예제 #5
0
class NoInit(js.JsonSaveable):
    x = js.Int()
    y = js.String()
class DonorDatabase(js.JsonSaveable):
    """A database of Donors"""

    _donors = DonorDict()
    min_col_width = js.Int()
    def_pad = js.Int()
    col_sep = js.String()
    cols = js.List()
    thank_you_fmt = js.String()

    def __init__(self, *donors):
        if not donors:
            donors = []

        self._donors = DonorDict(*donors)
        self.min_col_width = 12
        self.def_pad = 7
        self.col_sep = ' | '
        self.cols = ['Donor Name', 'Total Given', 'Num Gifts', 'Average Gift']
        self.thank_you_fmt = (
            '\nDear {:s},\n'
            'Thank you for your generous donation of ${:,.2f}.\n'
            '\t\tSincerely,\n'
            '\t\t  -Your conscience')

    def __contains__(self, item):
        return item in self._donors.values()

    def __getitem__(self, key):
        return self._donors[key]

    def __iter__(self):
        for donor in self._donors.values():
            yield donor
        return

    def __setitem__(self, key, value):
        self._donors[key] = value

    def challenge(self, factor, min_don=None, max_don=None):
        """Return a new database with all donations multiplied by the factor
           value if within the specified min and max donation range.

        Args:
            factor (float): Factor value
            min_don (float, optional): Defaults to None. Min donation value
            max_don (float, optional): Defaults to None. Max donation value

        Returns:
            DonorDatabase: New DonorDatabase with factor value applied
        """
        new_donors = []
        for donor in self:
            new_donors.append(
                Donor(
                    donor.name,
                    self.filter_and_factor(factor,
                                           donor.donations,
                                           min_don=min_don,
                                           max_don=max_don)))
        return DonorDatabase(*new_donors)

    def create_report(self):
        """Generate report of all donors and donations in database."""
        sorted_dnr_keys = sorted(self.keys(),
                                 key=lambda d: self[d].total_donations,
                                 reverse=True)

        max_name = len(max([name
                            for name in self.keys()], key=len)) + self.def_pad
        max_total = len(max([str(d.total_donations)
                             for d in self], key=len)) + self.def_pad
        max_gifts = len(max([str(d.num_donations)
                             for d in self], key=len)) + self.def_pad
        max_ave = max_total

        if max_name < self.min_col_width:
            max_name = self.min_col_width
        if max_total < self.min_col_width:
            max_total = max_ave = self.min_col_width
        if max_gifts < self.min_col_width:
            max_gifts = self.min_col_width

        hdr_fmt = (f'\n{{:^{max_name}s}}{self.col_sep}{{:^{max_total}s}}'
                   f'{self.col_sep}{{:^{max_gifts}s}}{self.col_sep}'
                   f'{{:^{max_ave}s}}\n' + '-' *
                   (max_name + max_total + max_gifts + max_ave +
                    len(self.col_sep) * 3) + '\n')

        row_fmt = (f'{{:<{max_name}s}}{self.col_sep}${{:>{max_total - 1},.2f}}'
                   f'{self.col_sep}{{:>{max_gifts}d}}{self.col_sep}'
                   f'${{:>{max_ave - 1},.2f}}')

        header = hdr_fmt.format(*self.cols)

        rows = [
            row_fmt.format(dnr, self[dnr].total_donations,
                           self[dnr].num_donations,
                           self[dnr].average_donations)
            for dnr in sorted_dnr_keys
        ]

        return header + '\n'.join(rows)

    def keys(self):
        """Generator providing name of each donor in database"""
        for donor in self._donors:
            yield donor

    @staticmethod
    def filter_and_factor(factor, donations, min_don=None, max_don=None):
        """For each donation, multiply by the factor value if the donation is
           within the min and max donation range, if set.

        Args:
            factor (float): Factor value
            donations (list): List of donations to filter and factor
            min_don (float, optional): Defaults to None. Min donation value
            max_don (float, optional): Defaults to None. Max donation value

        Raises:
            ValueError: Min donation value is greater than max value

        Returns:
            list: New list of filtered and factored donations
        """
        if min_don and max_don:
            if min_don > max_don:
                raise ValueError('Min donation value is greater than max')

            return list(
                map(lambda x: x * factor,
                    filter(lambda d: min_don <= d <= max_don, donations)))
        elif min_don:
            return list(
                map(lambda x: x * factor,
                    filter(lambda d: d >= min_don, donations)))
        elif max_don:
            return list(
                map(lambda x: x * factor,
                    filter(lambda d: d <= max_don, donations)))
        else:
            return list(map(lambda x: x * factor, donations))

    def items(self):
        """Generator yielding name of each donor and the donor itself"""
        for name, donor in self._donors.items():
            yield (name, donor)

    @classmethod
    def load(cls, _json):
        """Generates a Donor Database based on the passed json file"""
        if isinstance(_json, str):
            return cls.from_json_dict(json.loads(_json))
        else:  # assume a file-like object
            return cls.from_json_dict(json.load(_json))

    def projection(self, factor, min_don=None, max_don=None):
        """Return projection for a contribution that multiplies all current
        donations by 'factor' that are within the specified min and max
        donation range, if specified.

        Args:
            factor (float): Factor value
            min_don (float, optional): Defaults to None. Min donation value
            max_don (float, optional): Defaults to None. Max donation value

        Returns:
            float: Projected contribution value
        """
        projection = 0
        for donor in self:
            projection += sum(
                self.filter_and_factor(factor,
                                       donor.donations,
                                       min_don=min_don,
                                       max_don=max_don))
        return projection

    def save(self):
        """Saves the current Donor Database to a json file"""
        now = datetime.datetime.today().strftime('%m-%d-%Y')
        f_name = f'donor_dict_{now}.json'

        with open(f_name, 'w') as f:
            f.write(self.to_json())

    def send_letters(self):
        """Create a letter for each donor and write to disk as a text file"""
        now = datetime.datetime.today().strftime('%m-%d-%Y')

        for donor, data in self.items():
            f_name = f'{donor.replace(" ", "_")}_{now}.txt'
            with open(f_name, 'w') as f:
                f.write(self.thank_you_fmt.format(donor, data.total_donations))