def draft_app_warning(request): """ Warn orgs of impending draft freezes NOTE: must run exactly once a day Gives 7 day warning if created 7+ days before close, otherwise 3 day warning """ drafts = DraftGrantApplication.objects.all() eight_days = timedelta(days=8) for draft in drafts: time_left = draft.grant_cycle.close - timezone.now() created_delta = draft.grant_cycle.close - draft.created if ((created_delta > eight_days and eight_days > time_left > timedelta(days=7)) or (created_delta < eight_days and timedelta(days=3) > time_left >= timedelta(days=2))): to_email = draft.organization.get_email() if not to_email: logger.warn( 'Unable to send draft reminder; org is not registered %d', draft.organization.pk) continue utils.send_email(subject='Grant cycle closing soon', sender=c.GRANT_EMAIL, to=[to_email], template='grants/email_draft_warning.html', context={ 'org': draft.organization, 'cycle': draft.grant_cycle }) logger.info( 'Email sent to %s regarding draft application soon to expire', to_email) return HttpResponse('')
def yer_reminder_email(request): """ Remind orgs of upcoming year end reports that are due NOTE: Must run exactly once a day. ONLY SUPPORTS UP TO 2-YEAR GRANTS Sends reminder emails at 1 month and 1 week """ today = timezone.now().date() # get awards due in 7 or 30 days year_ago = today.replace(year=today.year - 1) reminder_deltas = [timedelta(days=7), timedelta(days=30)] award_dates = [] for delta in reminder_deltas: award_dates.append(today + delta) award_dates.append((today + delta).replace(year=today.year - 1)) awards = GivingProjectGrant.objects.filter(first_yer_due__in=award_dates) for award in awards: if award.yearendreport_set.count() < award.grant_length(): app = award.projectapp.application to = app.organization.get_email() or app.email_address utils.send_email(subject='Year end report', sender=c.GRANT_EMAIL, to=[to], template='grants/email_yer_due.html', context={ 'award': award, 'app': app, 'gp': award.projectapp.giving_project, 'base_url': c.APP_BASE_URL }) logger.info('YER reminder email sent to %s for award %d', to, award.pk) return HttpResponse('success')
def email_overdue(request): today = datetime.date.today() ships = models.Membership.objects.filter( giving_project__fundraising_deadline__gte=today) limit = today - datetime.timedelta(days=7) subject = 'Fundraising Steps' from_email = c.FUND_EMAIL for ship in ships: member = ship.member if not ship.emailed or (ship.emailed <= limit): count, step = ship.overdue_steps(get_next=True) if count > 0 and step: to_email = ship.member.user.username logger.info('%s has overdue step(s), emailing.', to_email) utils.send_email(subject=subject, to=[to_email], sender=from_email, template='fund/emails/overdue_steps.html', context={ 'login_url': c.APP_BASE_URL + '/fund/login', 'ship': ship, 'num': count, 'step': step, 'base_url': c.APP_BASE_URL }) ship.emailed = today ship.save(skip=True) return HttpResponse('')
def notify_approval(membership): to_email = membership.member.user.username utils.send_email(subject='Membership Approved', sender=c.FUND_EMAIL, to=[to_email], template='fund/emails/account_approved.html', context={ 'login_url': c.APP_BASE_URL + '/fund/login', 'project': membership.giving_project }) logger.info(u'Approval email sent to %s at %s', membership, to_email)
def report_reminder_email(request): """ Remind orgs of upcoming grantee reports that are due NOTE: Must run exactly once a day. ONLY SUPPORTS UP TO 2-YEAR GRANTS Sends reminder emails at 1 month and 1 week """ today = timezone.now().date() seven_days = timedelta(days=7) thirty_days = timedelta(days=30) award_dates = [today + seven_days, today + thirty_days] awards = (GivingProjectGrant.objects.filter( Q(first_report_due__in=award_dates) | Q(second_report_due__in=award_dates)).annotate( report_count=Count('granteereport'))) for award in awards: due = False if award.report_count == 0: due = award.first_report_due elif award.report_count == 1 and award.second_report_due: due = award.second_report_due if due: app = award.projectapp.application to = app.organization.get_email() or app.email_address utils.send_email(subject='Grantee report', sender=c.GRANT_EMAIL, to=[to], template='grants/email_report_due.html', context={ 'award': award, 'app': app, 'gp': award.projectapp.giving_project, 'base_url': c.APP_BASE_URL, 'due_date': due }) logger.info( 'Grantee report reminder email sent to %s for award %d', to, award.pk) return HttpResponse('success')
def gift_notify(request): """ Set gift received notifications on membership object and send an email Marks donors as notified """ donors = (models.Donor.objects.select_related('membership__member').filter( gift_notified=False).exclude(received_this=0, received_next=0, received_afternext=0)) memberships = {} for donor in donors: if donor.membership not in memberships: memberships[donor.membership] = [] memberships[donor.membership].append(donor) login_url = c.APP_BASE_URL + '/fund/' subject = 'Gift or pledge received' from_email = c.FUND_EMAIL for ship, donor_list in memberships.iteritems(): gift_str = '' for donor in donor_list: gift_str += u'${} gift or pledge received from {}! '.format( donor.received(), donor) ship.notifications = gift_str ship.save(skip=True) utils.send_email(subject=subject, to=[ship.member.user.username], sender=from_email, template='fund/emails/gift_received.html', context={ 'login_url': login_url, 'gift_str': ship.notifications }) logger.info('Set gift notification and sent email to %s', ship.member.user.username) donors.update(gift_notified=True) return HttpResponse('')
def new_accounts(request): """ Send GP leaders an email saying how many unapproved memberships exist Will continue emailing about the same membership until it's approved/deleted. """ subject = 'Accounts pending approval' from_email = c.FUND_EMAIL active_gps = models.GivingProject.objects.filter( fundraising_deadline__gte=timezone.now().date()) for gp in active_gps: memberships = models.Membership.objects.filter(giving_project=gp) need_approval = memberships.filter(approved=False).count() if need_approval > 0: leaders = memberships.filter(leader=True) to_emails = [leader.member.user.username for leader in leaders] if to_emails: utils.send_email( subject=subject, to=to_emails, sender=from_email, template='fund/emails/accounts_need_approval.html', context={ 'admin_url': c.APP_BASE_URL + '/admin/fund/membership/', 'count': need_approval, 'giving_project': unicode(gp), 'support_email': c.SUPPORT_EMAIL }) logger.info( '%d unapproved memberships in %s. Email sent to %s', need_approval, unicode(gp), ', '.join(to_emails)) return HttpResponse('')
def auto_create_cycles(request): now = timezone.now() if now.hour != 8: # UTC logger.error( 'auto_create_cycles running at unexpected time %s; aborting', now) return HttpResponse(status=500) cycles = GrantCycle.objects.filter( Q(title__startswith='Rapid Response') | Q(title__startswith='Seed '), close__range=(now - timedelta(hours=2), now)) if len(cycles) == 0: logger.info('auto_create_cycles found no recently closed cycles') return HttpResponse(status=200) created = [] for cycle in cycles: prefix = 'Rapid Response' if cycle.get_type( ) == 'rapid' else 'Seed Grant' if GrantCycle.objects.filter(title__startswith=prefix, close__gte=now).exists(): logger.info( 'auto_create_cycles skipping %s cycle; next one exists', prefix) continue title = '{} {}.{}.{} - {}.{}.{}'.format( prefix, cycle.open.month, cycle.open.day, cycle.open.year, cycle.close.month, cycle.close.day, cycle.close.year, ) new_cycle = GrantCycle.objects.copy(cycle, title, timezone.now(), cycle.close + timedelta(days=14)) # add some context for use in email new_cycle.prev_cycle = cycle new_cycle.drafts_count = (DraftGrantApplication.objects.filter( grant_cycle_id=cycle.id).update(grant_cycle_id=new_cycle.id)) created.append(new_cycle) if len(created) > 0: logger.info('auto_create_cycles created %d new cycles', len(created)) utils.send_email(subject='Grant cycles created', sender=c.GRANT_EMAIL, to=['*****@*****.**'], template='grants/emails/auto_create_cycles.html', context={'cycles': created}) return HttpResponse(status=201) else: logger.info( 'auto_create_cycles did nothing; new cycles already existed') return HttpResponse()