Skip to content

Commit

Permalink
Many bug fixes for MemberAlerts. Closes #144, #146, #147, #149
Browse files Browse the repository at this point in the history
  • Loading branch information
jsayles committed Jul 3, 2015
1 parent 01646aa commit e9a2333
Show file tree
Hide file tree
Showing 2 changed files with 52 additions and 31 deletions.
63 changes: 38 additions & 25 deletions nadine/models/alerts.py
Expand Up @@ -17,13 +17,22 @@

logger = logging.getLogger(__name__)

# Exiting members is defined by a specific window. If a new membership
# is added within this window then it is a change not an exit.
# This is the amount of days before and after that create our window.
EXITING_MEMBER_WINDOW = 4

class MemberAlertManager(models.Manager):

def create_if_not_open(self, user, key):
unresolved = self.filter(user=user, key=key, resolved_ts__isnull=True, muted_ts__isnull=True)
if unresolved.count() == 0:
MemberAlert.objects.create(user=user, key=key)
return True
return False

def create_if_new(self, user, key, new_since):
old_alerts = MemberAlert.objects.filter(user=user, key=key, created_ts__gte=new_since)
if old_alerts.count() == 0:
MemberAlert.objects.create(user=user, key=key)
return True
return False

def unresolved(self, key, active_only=True):
unresolved = self.filter(key=key, resolved_ts__isnull=True, muted_ts__isnull=True)
# Persistent alerts apply even if a member is inactive
Expand All @@ -34,20 +43,21 @@ def unresolved(self, key, active_only=True):
return unresolved

def trigger_periodic_check(self):
# Check for exiting members (3 days back, 3 days forward)
exiting_members = Member.objects.exiting_members(EXITING_MEMBER_WINDOW)
# Check for exiting members in the coming week
exit_date = timezone.now() + timedelta(days=5)
exiting_members = Member.objects.exiting_members(exit_date)
for m in exiting_members:
start = timezone.now() - timedelta(days=EXITING_MEMBER_WINDOW)
# Only trigger exiting membership if no exit alerts were created in the last week
start = timezone.now() - timedelta(days=5)
if MemberAlert.objects.filter(user=m.user, key__in=MemberAlert.PERSISTENT_ALERTS, created_ts__gte=start).count() == 0:
self.trigger_exiting_membership(m.user)
self.trigger_exiting_membership(m.user, exit_date)

# Check for stale membership
smd = Member.objects.stale_member_date()
for m in Member.objects.stale_members():
existing_alerts = MemberAlert.objects.filter(user=m.user, key=MemberAlert.STALE_MEMBER, created_ts__gte=smd)
if not existing_alerts:
MemberAlert.objects.create(user=m.user, key=MemberAlert.STALE_MEMBER)
MemberAlert.objects.create_if_not_open(user=m.user, key=MemberAlert.STALE_MEMBER)

# Expire old and unresolved alerts
#active_users = Member.objects.active_users()
Expand All @@ -60,43 +70,47 @@ def trigger_periodic_check(self):
# if not alert.user in active_users:
# alert.mute(None, note="membership ended")

def trigger_exiting_membership(self, user):
def trigger_exiting_membership(self, user, day=None):
if day == None:
day = timezone.now()

new_alerts = False
open_alerts = user.profile.alerts_by_key(include_resolved=False)

# Key? Let's get it back!
last_membership = user.profile.last_membership()
if last_membership:
if last_membership.has_key:
if not MemberAlert.objects.filter(user=user, key=MemberAlert.RETURN_DOOR_KEY, created_ts__gte=last_membership.start_date):
MemberAlert.objects.create(user=user, key=MemberAlert.RETURN_DOOR_KEY)
membership = user.profile.membership_for_day(day)
if membership:
if membership.has_key:
if MemberAlert.objects.create_if_new(user, MemberAlert.RETURN_DOOR_KEY, membership.start_date):
new_alerts = True
if last_membership.has_desk:
if membership.has_desk:
if MemberAlert.ASSIGN_CABINET in open_alerts:
# We never assigned a mailbox so we can just resolve that now
user.profile.resolve_alerts(MemberAlert.ASSIGN_CABINET)
if not MemberAlert.objects.filter(user=user, key=MemberAlert.RETURN_DESK_KEY, created_ts__gte=last_membership.start_date):
if not MemberAlert.objects.filter(user=user, key=MemberAlert.RETURN_DESK_KEY, created_ts__gte=membership.start_date):
MemberAlert.objects.create(user=user, key=MemberAlert.RETURN_DESK_KEY)
new_alerts = True
if last_membership.has_mail:
if membership.has_mail:
if MemberAlert.ASSIGN_MAILBOX in open_alerts:
# We never assigned a mailbox so we can just resolve that now
user.profile.resolve_alerts(MemberAlert.ASSIGN_MAILBOX)
elif not MemberAlert.objects.filter(user=user, key=MemberAlert.REMOVE_MAILBOX, created_ts__gte=last_membership.start_date):
elif not MemberAlert.objects.filter(user=user, key=MemberAlert.REMOVE_MAILBOX, created_ts__gte=membership.start_date):
MemberAlert.objects.create(user=user, key=MemberAlert.REMOVE_MAILBOX)
new_alerts = True

# Take down their photo.
if user.profile.photo:
if MemberAlert.POST_PHOTO in open_alerts:
user.profile.resolve_alerts(MemberAlert.POST_PHOTO)
elif not MemberAlert.objects.filter(user=user, key=MemberAlert.REMOVE_PHOTO, created_ts__gte=last_membership.start_date):
elif not MemberAlert.objects.filter(user=user, key=MemberAlert.REMOVE_PHOTO, created_ts__gte=membership.start_date):
MemberAlert.objects.create(user=user, key=MemberAlert.REMOVE_PHOTO)
new_alerts = True

# Send an email to the team announcing their exit
if new_alerts:
mailgun.send_manage_member(user, subject="Exiting Member")
end = membership.end_date
subject = "Exiting Member: %s/%s/%s" % (end.month, end.day, end.year)
mailgun.send_manage_member(user, subject=subject)


def trigger_new_membership(self, user):
Expand Down Expand Up @@ -293,9 +307,8 @@ def membership_callback(sender, **kwargs):
if created:
MemberAlert.objects.trigger_new_membership(membership.member.user)
else:
# If this membership has an end date that puts it outside our exiting membership window
# we are going to go straight to the exiting member logic from here
window_start = timezone.now() - timedelta(days=EXITING_MEMBER_WINDOW)
# If this membership is older then a week we'll go straight to exiting member logic
window_start = timezone.now() - timedelta(days=5)
if membership.end_date and membership.end_date < window_start.date():
MemberAlert.objects.trigger_exiting_membership(membership.member.user)
post_save.connect(membership_callback, sender=Membership)
20 changes: 14 additions & 6 deletions nadine/models/core.py
Expand Up @@ -155,12 +155,17 @@ def active_users(self):
def daily_members(self):
return self.active_members().exclude(id__in=self.members_with_desks())

def exiting_members(self, day_window):
from_day = timezone.now() - timedelta(days=day_window)
to_day = timezone.now() + timedelta(days=day_window)
ending = Membership.objects.filter(end_date__gte=from_day, end_date__lte=to_day)
starting = Membership.objects.filter(start_date__gte=from_day, start_date__lte=to_day, end_date__isnull=True)
exiting = ending.exclude(member__in=starting.values('member'))
def exiting_members(self, day=None):
if day == None:
day = timezone.now()
next_day = day + timedelta(days=1)

# Exiting members are here today and gone tomorrow. Pull up all the active
# memberships for today and remove the list of active members tomorrow.
today_memberships = Membership.objects.active_memberships(day)
tomorrow_memberships = Membership.objects.active_memberships(next_day)
exiting = today_memberships.exclude(member__in=tomorrow_memberships.values('member'))

return Member.objects.filter(id__in=exiting.values('member'))

def stale_member_date(self):
Expand Down Expand Up @@ -365,6 +370,9 @@ def active_membership(self):
return membership
return None

def membership_for_day(self, day):
return Membership.objects.active_memberships(target_date=day).filter(member=self).first()

def activity_this_month(self, test_date=None):
if not test_date:
test_date = date.today()
Expand Down

0 comments on commit e9a2333

Please sign in to comment.