bgneal@33: """ bgneal@33: Models for the donations application. bgneal@33: """ bgneal@34: import datetime bgneal@34: import decimal bgneal@34: bgneal@33: from django.db import models bgneal@259: from django.contrib.auth.models import User bgneal@35: from django.conf import settings bgneal@619: from django.db.models import Sum bgneal@33: bgneal@34: bgneal@34: class DonationManager(models.Manager): bgneal@619: """Manager for the Donations model.""" bgneal@619: bgneal@34: def monthly_stats(self, year=None, month=None): bgneal@34: """ bgneal@35: Returns a tuple of items for the given month in the given bgneal@34: year. If year is None, the current year is used. If month is None, bgneal@34: the current month is used. bgneal@35: The returned tuple has the following items, in order: bgneal@35: (gross, net, donations) bgneal@35: where: bgneal@34: 'gross': total gross donations bgneal@34: 'net': total net donations bgneal@35: 'donations': list of donation objects bgneal@34: """ bgneal@34: today = datetime.date.today() bgneal@34: if year is None: bgneal@34: year = today.year bgneal@34: if month is None: bgneal@34: month = today.month bgneal@34: bgneal@34: qs = self.filter(payment_date__year=year, bgneal@34: payment_date__month=month, bgneal@350: test_ipn=settings.DONATIONS_DEBUG).order_by( bgneal@350: 'payment_date').select_related('user') bgneal@34: bgneal@35: gross = decimal.Decimal() bgneal@35: net = decimal.Decimal() bgneal@35: donations = [] bgneal@34: for donation in qs: bgneal@35: gross += donation.mc_gross bgneal@35: net += donation.mc_gross - donation.mc_fee bgneal@35: donations.append(donation) bgneal@34: bgneal@35: return gross, net, donations bgneal@34: bgneal@619: def monthly_goal_pct(self, year=None, month=None, limit=True): bgneal@619: """Returns progress towards the given monthly goal as an integer bgneal@619: percent. bgneal@619: bgneal@619: If year is None, the current year is used. bgneal@619: If month is None, the current month is used. bgneal@619: If limit is True, the return value is limited to 100. bgneal@619: bgneal@619: """ bgneal@619: today = datetime.datetime.today() bgneal@619: if year is None: bgneal@619: year = today.year bgneal@619: if month is None: bgneal@619: month = today.month bgneal@619: bgneal@619: r = self.filter(payment_date__year=year, payment_date__month=month).aggregate( bgneal@619: Sum('mc_gross'), Sum('mc_fee')) bgneal@619: bgneal@619: gross, fee = r['mc_gross__sum'], r['mc_fee__sum'] bgneal@619: bgneal@619: if gross is not None and fee is not None: bgneal@619: pct = int((gross - fee) / settings.DONATIONS_GOAL * 100) bgneal@619: else: bgneal@619: pct = 0 bgneal@619: bgneal@619: if limit: bgneal@619: pct = min(pct, 100) bgneal@619: bgneal@619: return pct bgneal@619: bgneal@620: def top_donors(self, n=10): bgneal@620: """Returns a list of the top n donors as user objects that have a bgneal@620: total_donations field annotation. bgneal@620: bgneal@620: The data is taken from non anonymous donations from logged in users. bgneal@620: bgneal@620: """ bgneal@620: qs = User.objects.filter(donation__isnull=False, bgneal@620: donation__is_anonymous=False) \ bgneal@620: .distinct() \ bgneal@620: .annotate(total_donations=Sum('donation__mc_gross')) \ bgneal@620: .order_by('-total_donations')[:n] bgneal@620: bgneal@620: return qs bgneal@620: bgneal@34: bgneal@33: class Donation(models.Model): bgneal@33: """Model to represent a donation to the website.""" bgneal@33: bgneal@259: user = models.ForeignKey(User, null=True, blank=True) bgneal@33: is_anonymous = models.BooleanField() bgneal@33: test_ipn = models.BooleanField(default=False, verbose_name="Test IPN") bgneal@33: txn_id = models.CharField(max_length=20, verbose_name="Txn ID") bgneal@33: txn_type = models.CharField(max_length=64) bgneal@33: first_name = models.CharField(max_length=64, blank=True) bgneal@33: last_name = models.CharField(max_length=64, blank=True) bgneal@33: payer_email = models.EmailField(max_length=127, blank=True) bgneal@33: payer_id = models.CharField(max_length=13, blank=True, verbose_name="Payer ID") bgneal@33: mc_fee = models.DecimalField(max_digits=8, decimal_places=2, verbose_name="Fee") bgneal@33: mc_gross = models.DecimalField(max_digits=8, decimal_places=2, verbose_name="Gross") bgneal@33: memo = models.TextField(blank=True) bgneal@33: payer_status = models.CharField(max_length=10, blank=True) bgneal@33: payment_date = models.DateTimeField() bgneal@33: bgneal@34: objects = DonationManager() bgneal@34: bgneal@33: class Meta: bgneal@33: ordering = ('-payment_date', ) bgneal@33: bgneal@33: def __unicode__(self): bgneal@33: if self.user: bgneal@33: return u'%s from %s' % (self.mc_gross, self.user.username) bgneal@33: return u'%s from %s %s' % (self.mc_gross, self.first_name, self.last_name) bgneal@33: bgneal@34: def donor(self): bgneal@34: """Returns the donor name for the donation.""" bgneal@34: if self.is_anonymous: bgneal@35: return settings.DONATIONS_ANON_NAME bgneal@34: if self.user is not None: bgneal@34: return self.user.username bgneal@34: if self.first_name or self.last_name: bgneal@34: name = u'%s %s' % (self.first_name, self.last_name) bgneal@34: return name.strip() bgneal@35: return settings.DONATIONS_ANON_NAME bgneal@34: