bgneal@33
|
1 """
|
bgneal@33
|
2 Models for the donations application.
|
bgneal@33
|
3 """
|
bgneal@34
|
4 import datetime
|
bgneal@34
|
5 import decimal
|
bgneal@34
|
6
|
bgneal@33
|
7 from django.db import models
|
bgneal@259
|
8 from django.contrib.auth.models import User
|
bgneal@35
|
9 from django.conf import settings
|
bgneal@619
|
10 from django.db.models import Sum
|
bgneal@33
|
11
|
bgneal@34
|
12
|
bgneal@34
|
13 class DonationManager(models.Manager):
|
bgneal@619
|
14 """Manager for the Donations model."""
|
bgneal@619
|
15
|
bgneal@34
|
16 def monthly_stats(self, year=None, month=None):
|
bgneal@34
|
17 """
|
bgneal@35
|
18 Returns a tuple of items for the given month in the given
|
bgneal@34
|
19 year. If year is None, the current year is used. If month is None,
|
bgneal@34
|
20 the current month is used.
|
bgneal@35
|
21 The returned tuple has the following items, in order:
|
bgneal@35
|
22 (gross, net, donations)
|
bgneal@35
|
23 where:
|
bgneal@34
|
24 'gross': total gross donations
|
bgneal@34
|
25 'net': total net donations
|
bgneal@35
|
26 'donations': list of donation objects
|
bgneal@34
|
27 """
|
bgneal@34
|
28 today = datetime.date.today()
|
bgneal@34
|
29 if year is None:
|
bgneal@34
|
30 year = today.year
|
bgneal@34
|
31 if month is None:
|
bgneal@34
|
32 month = today.month
|
bgneal@34
|
33
|
bgneal@34
|
34 qs = self.filter(payment_date__year=year,
|
bgneal@34
|
35 payment_date__month=month,
|
bgneal@350
|
36 test_ipn=settings.DONATIONS_DEBUG).order_by(
|
bgneal@350
|
37 'payment_date').select_related('user')
|
bgneal@34
|
38
|
bgneal@35
|
39 gross = decimal.Decimal()
|
bgneal@35
|
40 net = decimal.Decimal()
|
bgneal@35
|
41 donations = []
|
bgneal@34
|
42 for donation in qs:
|
bgneal@35
|
43 gross += donation.mc_gross
|
bgneal@35
|
44 net += donation.mc_gross - donation.mc_fee
|
bgneal@35
|
45 donations.append(donation)
|
bgneal@34
|
46
|
bgneal@35
|
47 return gross, net, donations
|
bgneal@34
|
48
|
bgneal@619
|
49 def monthly_goal_pct(self, year=None, month=None, limit=True):
|
bgneal@619
|
50 """Returns progress towards the given monthly goal as an integer
|
bgneal@619
|
51 percent.
|
bgneal@619
|
52
|
bgneal@619
|
53 If year is None, the current year is used.
|
bgneal@619
|
54 If month is None, the current month is used.
|
bgneal@619
|
55 If limit is True, the return value is limited to 100.
|
bgneal@619
|
56
|
bgneal@619
|
57 """
|
bgneal@619
|
58 today = datetime.datetime.today()
|
bgneal@619
|
59 if year is None:
|
bgneal@619
|
60 year = today.year
|
bgneal@619
|
61 if month is None:
|
bgneal@619
|
62 month = today.month
|
bgneal@619
|
63
|
bgneal@619
|
64 r = self.filter(payment_date__year=year, payment_date__month=month).aggregate(
|
bgneal@619
|
65 Sum('mc_gross'), Sum('mc_fee'))
|
bgneal@619
|
66
|
bgneal@619
|
67 gross, fee = r['mc_gross__sum'], r['mc_fee__sum']
|
bgneal@619
|
68
|
bgneal@619
|
69 if gross is not None and fee is not None:
|
bgneal@619
|
70 pct = int((gross - fee) / settings.DONATIONS_GOAL * 100)
|
bgneal@619
|
71 else:
|
bgneal@619
|
72 pct = 0
|
bgneal@619
|
73
|
bgneal@619
|
74 if limit:
|
bgneal@619
|
75 pct = min(pct, 100)
|
bgneal@619
|
76
|
bgneal@619
|
77 return pct
|
bgneal@619
|
78
|
bgneal@34
|
79
|
bgneal@33
|
80 class Donation(models.Model):
|
bgneal@33
|
81 """Model to represent a donation to the website."""
|
bgneal@33
|
82
|
bgneal@259
|
83 user = models.ForeignKey(User, null=True, blank=True)
|
bgneal@33
|
84 is_anonymous = models.BooleanField()
|
bgneal@33
|
85 test_ipn = models.BooleanField(default=False, verbose_name="Test IPN")
|
bgneal@33
|
86 txn_id = models.CharField(max_length=20, verbose_name="Txn ID")
|
bgneal@33
|
87 txn_type = models.CharField(max_length=64)
|
bgneal@33
|
88 first_name = models.CharField(max_length=64, blank=True)
|
bgneal@33
|
89 last_name = models.CharField(max_length=64, blank=True)
|
bgneal@33
|
90 payer_email = models.EmailField(max_length=127, blank=True)
|
bgneal@33
|
91 payer_id = models.CharField(max_length=13, blank=True, verbose_name="Payer ID")
|
bgneal@33
|
92 mc_fee = models.DecimalField(max_digits=8, decimal_places=2, verbose_name="Fee")
|
bgneal@33
|
93 mc_gross = models.DecimalField(max_digits=8, decimal_places=2, verbose_name="Gross")
|
bgneal@33
|
94 memo = models.TextField(blank=True)
|
bgneal@33
|
95 payer_status = models.CharField(max_length=10, blank=True)
|
bgneal@33
|
96 payment_date = models.DateTimeField()
|
bgneal@33
|
97
|
bgneal@34
|
98 objects = DonationManager()
|
bgneal@34
|
99
|
bgneal@33
|
100 class Meta:
|
bgneal@33
|
101 ordering = ('-payment_date', )
|
bgneal@33
|
102
|
bgneal@33
|
103 def __unicode__(self):
|
bgneal@33
|
104 if self.user:
|
bgneal@33
|
105 return u'%s from %s' % (self.mc_gross, self.user.username)
|
bgneal@33
|
106 return u'%s from %s %s' % (self.mc_gross, self.first_name, self.last_name)
|
bgneal@33
|
107
|
bgneal@34
|
108 def donor(self):
|
bgneal@34
|
109 """Returns the donor name for the donation."""
|
bgneal@34
|
110 if self.is_anonymous:
|
bgneal@35
|
111 return settings.DONATIONS_ANON_NAME
|
bgneal@34
|
112 if self.user is not None:
|
bgneal@34
|
113 return self.user.username
|
bgneal@34
|
114 if self.first_name or self.last_name:
|
bgneal@34
|
115 name = u'%s %s' % (self.first_name, self.last_name)
|
bgneal@34
|
116 return name.strip()
|
bgneal@35
|
117 return settings.DONATIONS_ANON_NAME
|
bgneal@34
|
118
|