view donations/models.py @ 693:ad69236e8501

For issue #52, update many 3rd party Javascript libraries. Updated to jquery 1.10.2, jquery ui 1.10.3. This broke a lot of stuff. - Found a newer version of the jquery cycle all plugin (3.0.3). - Updated JPlayer to 2.4.0. - Updated to MarkItUp 1.1.14. This also required me to add multiline attributes set to true on various buttons in the markdown set. - As per a stackoverflow post, added some code to get multiline titles in a jQuery UI dialog. They removed that functionality but allow you to put it back. Tweaked the MarkItUp preview CSS to show blockquotes in italic. Did not update TinyMCE at this time. I'm not using the JQuery version and this version appears to work ok for now. What I should do is make a repo for MarkItUp and do a vendor branch thing so I don't have to futz around diffing directories to figure out if I'll lose changes when I update.
author Brian Neal <bgneal@gmail.com>
date Wed, 04 Sep 2013 19:55:20 -0500
parents 40ae28f33b3d
children e14f54f16dbc
line wrap: on
line source
"""
Models for the donations application.
"""
import datetime
import decimal

from django.db import models
from django.contrib.auth.models import User
from django.conf import settings
from django.db.models import Sum


class DonationManager(models.Manager):
    """Manager for the Donations model."""

    def monthly_stats(self, year=None, month=None):
        """
        Returns a tuple of items for the given month in the given
        year. If year is None, the current year is used. If month is None,
        the current month is used.
        The returned tuple has the following items, in order:
            (gross, net, donations)
        where:
            'gross': total gross donations
            'net': total net donations
            'donations': list of donation objects
        """
        today = datetime.date.today()
        if year is None:
            year = today.year
        if month is None:
            month = today.month

        qs = self.filter(payment_date__year=year,
                payment_date__month=month,
                test_ipn=settings.DONATIONS_DEBUG).order_by(
                        'payment_date').select_related('user')

        gross = decimal.Decimal()
        net = decimal.Decimal()
        donations = []
        for donation in qs:
            gross += donation.mc_gross
            net += donation.mc_gross - donation.mc_fee
            donations.append(donation)

        return gross, net, donations

    def monthly_goal_pct(self, year=None, month=None, limit=True):
        """Returns progress towards the given monthly goal as an integer
        percent.

        If year is None, the current year is used.
        If month is None, the current month is used.
        If limit is True, the return value is limited to 100.

        """
        today = datetime.datetime.today()
        if year is None:
            year = today.year
        if month is None:
            month = today.month

        r = self.filter(payment_date__year=year, payment_date__month=month).aggregate(
                Sum('mc_gross'), Sum('mc_fee'))

        gross, fee = r['mc_gross__sum'], r['mc_fee__sum']

        if gross is not None and fee is not None:
            pct = int((gross - fee) / settings.DONATIONS_GOAL * 100)
        else:
            pct = 0

        if limit:
            pct = min(pct, 100)

        return pct

    def top_donors(self, n=10):
        """Returns a list of the top n donors as user objects that have a
        total_donations field annotation.

        The data is taken from non anonymous donations from logged in users.

        """
        qs = User.objects.filter(donation__isnull=False,
                                 donation__is_anonymous=False) \
                .distinct() \
                .annotate(total_donations=Sum('donation__mc_gross')) \
                .order_by('-total_donations')[:n]

        return qs


class Donation(models.Model):
    """Model to represent a donation to the website."""

    user = models.ForeignKey(User, null=True, blank=True)
    is_anonymous = models.BooleanField()
    test_ipn = models.BooleanField(default=False, verbose_name="Test IPN")
    txn_id = models.CharField(max_length=20, verbose_name="Txn ID")
    txn_type = models.CharField(max_length=64)
    first_name = models.CharField(max_length=64, blank=True)
    last_name = models.CharField(max_length=64, blank=True)
    payer_email = models.EmailField(max_length=127, blank=True)
    payer_id = models.CharField(max_length=13, blank=True, verbose_name="Payer ID")
    mc_fee = models.DecimalField(max_digits=8, decimal_places=2, verbose_name="Fee")
    mc_gross = models.DecimalField(max_digits=8, decimal_places=2, verbose_name="Gross")
    memo = models.TextField(blank=True)
    payer_status = models.CharField(max_length=10, blank=True)
    payment_date = models.DateTimeField()

    objects = DonationManager()

    class Meta:
        ordering = ('-payment_date', )

    def __unicode__(self):
        if self.user:
            return u'%s from %s' % (self.mc_gross, self.user.username)
        return u'%s from %s %s' % (self.mc_gross, self.first_name, self.last_name)

    def donor(self):
        """Returns the donor name for the donation."""
        if self.is_anonymous:
            return settings.DONATIONS_ANON_NAME
        if self.user is not None:
            return self.user.username
        if self.first_name or self.last_name:
            name = u'%s %s' % (self.first_name, self.last_name)
            return name.strip()
        return settings.DONATIONS_ANON_NAME