from datetime import timedelta
+from django.apps import apps
from django.utils.timezone import now
-from django.utils.translation import ugettext_lazy as _
-from .recipient import Recipient
+from django.utils.translation import gettext_lazy as _
+
+
+class Level:
+ COLD = 10
+ TRIED = 20
+ SINGLE = 30
+ RECURRING = 40
+ MANUAL_MEMBER = 45
+ OPT_OUT = 50
class State:
allow_negative_offset = False
- context_fields = []
+ level = None
+ expired = None
-
- def __init__(self, offset=0, time=None):
+ def __init__(self, time=None, min_days_since=None, max_days_since=None, test=False):
self.time = time or now()
- if isinstance(offset, int):
- offset = timedelta(offset)
- self.offset = offset
-
- def get_recipients(self):
- return [
- Recipient(
- hash_value=self.get_hash_value(obj),
- email=self.get_email(obj),
- context=self.get_context(obj),
+ self.min_days_since = min_days_since
+ self.max_days_since = max_days_since
+ self.test = test
+
+ def get_contacts(self):
+ Contact = apps.get_model('messaging', 'Contact')
+ contacts = Contact.objects.filter(level=self.level)
+
+ if self.min_days_since is not None or self.expired:
+ cutoff = self.time - timedelta(self.min_days_since or 0)
+ if self.expired:
+ contacts = contacts.filter(expires_at__lt=cutoff)
+ else:
+ contacts = contacts.filter(since__lt=cutoff)
+
+ if self.max_days_since is not None:
+ cutoff = self.time - timedelta(self.max_days_since)
+ if self.expired:
+ contacts = contacts.filter(expires_at__gt=cutoff)
+ else:
+ contacts = contacts.filter(since__gt=cutoff)
+
+ if self.expired is False:
+ contacts = contacts.exclude(expires_at__lt=self.time)
+
+ return contacts
+
+ def get_context(self, contact):
+ if self.test:
+ return self.get_example_context(contact)
+
+ Schedule = apps.get_model('club', 'Schedule')
+ schedules = Schedule.objects.filter(email=contact.email)
+ return {
+ "schedule": self.get_schedule(schedules)
+ }
+
+ def get_example_context(self, contact):
+ Schedule = apps.get_model('club', 'Schedule')
+ return {
+ "schedule": Schedule(
+ email=contact.email,
+ key='xxxxxxxxx',
+ amount=100,
+ payed_at=self.time - timedelta(2),
+ started_at=self.time - timedelta(1),
+ expires_at=self.time + timedelta(1),
)
- for obj in self.get_objects()
- ]
+ }
- def get_objects(self):
- raise NotImplemented
- def get_hash_value(self, obj):
- return str(obj.pk)
-
- def get_email(self, obj):
- return obj.email
+class ClubSingle(State):
+ slug = 'club-single'
+ name = _('club one-time donors')
+ level = Level.SINGLE
+ expired = False
- def get_context(self, obj):
- ctx = {
- obj._meta.model_name: obj,
- }
- return ctx
+ def get_schedule(self, schedules):
+ # Find first single non-expired schedule.
+ return schedules.filter(
+ monthly=False, yearly=False,
+ expires_at__gt=self.time
+ ).order_by('started_at').first()
-class ClubMembershipExpiring(State):
+class ClubSingleExpired(State):
slug = 'club-membership-expiring'
allow_negative_offset = True
- name = _('club membership expiring')
-
- def get_objects(self):
- from club.models import Schedule
- return Schedule.objects.filter(
- is_active=True,
- expires_at__lt=self.time - self.offset
- )
+ name = _('club one-time donors with donation expiring')
+ level = Level.SINGLE
+ expired = True
- def get_hashed_value(self, obj):
- return '%s:%s' % (obj.pk, obj.expires_at.isoformat())
+ def get_schedule(self, schedules):
+ # Find last single expired schedule.
+ return schedules.filter(
+ monthly=False, yearly=False,
+ expires_at__lt=self.time
+ ).order_by('-expires_at').first()
-class ClubPaymentUnfinished(State):
+class ClubTried(State):
slug = 'club-payment-unfinished'
- name = _('club payment unfinished')
+ name = _('club would-be donors')
+ level = Level.TRIED
- def get_objects(self):
- from club.models import Schedule
- return Schedule.objects.filter(
- payuorder=None,
- started_at__lt=self.time - self.offset,
- )
+ def get_schedule(self, schedules):
+ # Find last unpaid schedule.
+ return schedules.filter(
+ payed_at=None
+ ).order_by('-started_at').first()
+
+
+class ClubRecurring(State):
+ slug = 'club-recurring'
+ name = _('club recurring donors')
+ level = Level.RECURRING
+ expired = False
+ def get_schedule(self, schedules):
+ # Find first recurring non-expired schedule.
+ return schedules.exclude(
+ monthly=False, yearly=False
+ ).filter(
+ expires_at__gt=self.time
+ ).order_by('started_at').first()
-class ClubRecurringPaymentProblem(State):
+
+class ClubRecurringExpired(State):
slug = 'club-recurring-payment-problem'
- name = _('club recurring payment problem')
+ name = _('club recurring donors with donation expired')
+ level = Level.RECURRING
+ expired = True
+
+ def get_schedule(self, schedules):
+ # Find last recurring expired schedule.
+ return schedules.exclude(
+ monthly=False, yearly=False
+ ).filter(
+ expires_at__lt=self.time
+ ).order_by('-expires_at').first()
+
+
+class Cold(State):
+ slug = 'cold'
+ name = _('cold group')
+ level = Level.COLD
- def get_objects(self):
- from club.models import Schedule
- return Schedule.objects.none()
+ def get_context(self, contact):
+ return {}
states = [
- ClubMembershipExpiring,
- ClubPaymentUnfinished,
- ClubRecurringPaymentProblem,
+ Cold,
+ ClubTried,
+ ClubSingle,
+ ClubSingleExpired,
+ ClubRecurring,
+ ClubRecurringExpired,
]