models: add months returning supporter had lapsed
A new public method, months_expired(), which is similar to the existing status() in layout, but instead of returning whether the supporter is new/active/lapsed/etc., it checks to see whether the supporter is returning after having been lapsed, and if so, returns the month range corresponding to how long they had been lapsed before returning as a supporter in the current month.
This commit is contained in:
parent
9350167d29
commit
0d3d9db4ed
1 changed files with 56 additions and 0 deletions
|
@ -75,6 +75,13 @@ class Supporter:
|
|||
STATUS_LAPSED = 'Lapsed'
|
||||
STATUS_LOST = 'Lost'
|
||||
|
||||
RETURNING_0MO = 'ReturningAfter0-3monthLapse'
|
||||
RETURNING_3MO = 'ReturningAfter3-6monthLapse'
|
||||
RETURNING_6MO = 'ReturningAfter6-9monthLapse'
|
||||
RETURNING_9MO = 'ReturningAfter9-12monthLapse'
|
||||
RETURNING_12MO = 'ReturningAfter>12monthLapse'
|
||||
RETURNING_NOT = 'NotReturning'
|
||||
|
||||
LOST_THRESHOLD = datetime.timedelta(days=365)
|
||||
LAPSED_THRESHOLD = datetime.timedelta()
|
||||
|
||||
|
@ -131,6 +138,11 @@ class Supporter:
|
|||
self._supporter_type(payments))
|
||||
lapse_date = _expose(_lapse_date)
|
||||
|
||||
def _second_last_lapse_date(self, payments):
|
||||
# TODO: find a way without listification - needed due to indexing
|
||||
return self._calculate_lapse_date(list(payments)[-2].date,
|
||||
self._supporter_type(payments))
|
||||
|
||||
def status(self, as_of_date=None):
|
||||
if as_of_date is None:
|
||||
as_of_date = Date.today()
|
||||
|
@ -148,3 +160,47 @@ class Supporter:
|
|||
return self.STATUS_NEW
|
||||
else:
|
||||
return self.STATUS_ACTIVE
|
||||
|
||||
def months_expired(self, as_of_date=None):
|
||||
if as_of_date is None:
|
||||
as_of_date = Date.today()
|
||||
payments = self.payments(as_of_date)
|
||||
payments_count = payments.count()
|
||||
if payments_count == 0:
|
||||
return None
|
||||
lapse_date = self._lapse_date(payments)
|
||||
days_past_due = as_of_date - lapse_date
|
||||
|
||||
if as_of_date.adjust_month(-1, 1) < payments.first().date <= as_of_date:
|
||||
return self.RETURNING_NOT # started paying this month so not "returning"
|
||||
|
||||
elif as_of_date.adjust_month(-1, 1) < payments.last().date <= as_of_date:
|
||||
# (there are at least 2 payments because first().date != last().date)
|
||||
if payments.last().date <= self._second_last_lapse_date(payments):
|
||||
|
||||
# the most recent payment was this month, and it was before or on
|
||||
# the lapse date for the last payment (i.e. it was "on-time") so
|
||||
# this is a normal active subscriber, not a "re-"subscriber
|
||||
return self.RETURNING_NOT
|
||||
else:
|
||||
# the most recent payment was this month, and it was after the lapse
|
||||
# date for the last payment (so this is a "re-"subscriber)
|
||||
|
||||
# let's see how far past due the payment was, and return accordingly
|
||||
last_past_due = (payments.last().date
|
||||
- self._second_last_lapse_date(payments))
|
||||
|
||||
# TODO: use real month boundaries (approximating ok, but not great)
|
||||
if last_past_due < datetime.timedelta(days=91):
|
||||
return self.RETURNING_0MO
|
||||
elif last_past_due < datetime.timedelta(days=183):
|
||||
return self.RETURNING_3MO
|
||||
elif last_past_due < datetime.timedelta(days=274):
|
||||
return self.RETURNING_6MO
|
||||
elif last_past_due < datetime.timedelta(days=365):
|
||||
return self.RETURNING_9MO
|
||||
else:
|
||||
return self.RETURNING_12MO
|
||||
else:
|
||||
# supporter lapsed/lost or an annual supporter who paid 2-12 months ago
|
||||
return self.RETURNING_NOT
|
||||
|
|
Loading…
Reference in a new issue