def overview_xlsx(self, out, session): checklist = list(DeptChecklistConf.instances.values()) departments = session.query(Department).options( subqueryload(Department.members_who_can_admin_checklist), subqueryload(Department.dept_checklist_items)) \ .order_by(Department.name) header_row = ['Department'] header_row.extend(item.name for item in checklist) header_row.extend(['Emails']) out.writerow(header_row) for dept in departments: out.writecell(dept.name) for item in checklist: checklist_item = dept.checklist_item_for_slug(item.slug) if checklist_item: out.writecell('', format={'bg_color': 'green'}) elif days_before(7, item.deadline)(): out.writecell('', format={'bg_color': 'orange'}) elif item.deadline < datetime.now(UTC): out.writecell('', format={'bg_color': 'red'}) else: out.writecell('') out.writecell(', '.join( [admin.email for admin in dept.checklist_admins]), last_cell=True)
def overview_xlsx(self, out, session): checklist = list(DeptChecklistConf.instances.values()) departments = session.query(Department).options( subqueryload(Department.members_who_can_admin_checklist), subqueryload(Department.dept_checklist_items)) \ .order_by(Department.name) header_row = ['Department'] header_row.extend(item.name for item in checklist) header_row.extend(['Emails']) out.writerow(header_row) for dept in departments: out.writecell(dept.name) for item in checklist: checklist_item = dept.checklist_item_for_slug(item.slug) if checklist_item: out.writecell('', {'bg_color': 'green'}) elif days_before(7, item.deadline)(): out.writecell('', {'bg_color': 'orange'}) elif item.deadline < datetime.now(UTC): out.writecell('', {'bg_color': 'red'}) else: out.writecell('') out.writecell(', '.join([admin.email for admin in dept.checklist_admins]), last_cell=True)
def __init__(self, conf): AutomatedEmail.__init__(self, Attendee, '{EVENT_NAME} Department Checklist: ' + conf.name, 'shifts/dept_checklist.txt', filter=lambda a: a.admin_account and any( not d.checklist_item_for_slug(conf.slug) for d in a.checklist_admin_depts), ident='department_checklist_{}'.format(conf.name), when=days_before(10, conf.deadline), sender=c.STAFF_EMAIL, extra_data={'conf': conf}, post_con=conf.email_post_con or False)
def __init__(self, conf): when = [days_before(10, conf.deadline)] if conf.email_post_con: when.append(after(c.EPOCH)) AutomatedEmailFixture.__init__( self, Attendee, '{EVENT_NAME} Department Checklist: ' + conf.name, 'shifts/dept_checklist.txt', filter=lambda a: a.admin_account and any( not d.checklist_item_for_slug(conf.slug) for d in a.checklist_admin_depts), ident='department_checklist_{}'.format(conf.name), when=when, sender=c.STAFF_EMAIL, extra_data={'conf': conf}, allow_post_con=conf.email_post_con)
def __init__(self, conf): when = [days_before(10, conf.deadline)] if conf.email_post_con: when.append(after(c.EPOCH)) AutomatedEmailFixture.__init__( self, Attendee, '{EVENT_NAME} Department Checklist: ' + conf.name, 'shifts/dept_checklist.txt', filter=lambda a: a.admin_account and any( not d.checklist_item_for_slug(conf.slug) for d in a.checklist_admin_depts), ident='department_checklist_{}'.format(conf.name), when=when, sender=c.STAFF_EMAIL, extra_data={'conf': conf}, allow_post_con=conf.email_post_con)
def overview(self, session, filtered=False, message=''): checklist = list(DeptChecklistConf.instances.values()) attendee = session.admin_attendee() dept_filter = [ Department.members_who_can_admin_checklist.any( Attendee.id == attendee.id) ] if filtered else [] departments = session.query(Department).filter(*dept_filter) \ .options( subqueryload(Department.members_who_can_admin_checklist), subqueryload(Department.dept_checklist_items)) \ .order_by(Department.name) overview = [] for dept in departments: is_checklist_admin = attendee.is_checklist_admin_of(dept) can_admin_checklist = attendee.can_admin_checklist_for(dept) statuses = [] for item in checklist: status = {'conf': item, 'name': item.name} checklist_item = dept.checklist_item_for_slug(item.slug) if checklist_item: status['done'] = True status['completed_by'] = checklist_item.attendee.full_name elif days_before(7, item.deadline)(): status['approaching'] = True elif item.deadline < datetime.now(UTC): status['missed'] = True statuses.append(status) if not filtered or can_admin_checklist: overview.append([ dept, is_checklist_admin, can_admin_checklist, statuses, dept.members_who_can_admin_checklist ]) return { 'message': message, 'filtered': filtered, 'overview': overview, 'checklist': checklist }
def overview(self, session, filtered=False, message=''): checklist = list(DeptChecklistConf.instances.values()) attendee = session.admin_attendee() dept_filter = [Department.members_who_can_admin_checklist.any( Attendee.id == attendee.id)] if filtered else [] departments = session.query(Department).filter(*dept_filter) \ .options( subqueryload(Department.members_who_can_admin_checklist), subqueryload(Department.dept_checklist_items)) \ .order_by(Department.name) overview = [] for dept in departments: is_checklist_admin = attendee.is_checklist_admin_of(dept) can_admin_checklist = attendee.can_admin_checklist_for(dept) statuses = [] for item in checklist: status = {'conf': item, 'name': item.name} checklist_item = dept.checklist_item_for_slug(item.slug) if checklist_item: status['done'] = True status['completed_by'] = checklist_item.attendee.full_name elif days_before(7, item.deadline)(): status['approaching'] = True elif item.deadline < datetime.now(UTC): status['missed'] = True statuses.append(status) if not filtered or can_admin_checklist: overview.append([ dept, is_checklist_admin, can_admin_checklist, statuses, dept.members_who_can_admin_checklist]) return { 'message': message, 'filtered': filtered, 'overview': overview, 'checklist': checklist }
def test_representation_days_before_until(self): assert days_before(days=10, deadline=DateBase.now(), until=5).active_when == 'between 09/05 and 09/10'
'Your {EVENT_NAME} Art Show application has been waitlisted', 'art_show/waitlisted.txt', lambda a: a.status == c.WAITLISTED, ident='art_show_waitlisted') ArtShowAppEmailFixture( 'Your {EVENT_NAME} Art Show application has been declined', 'art_show/declined.txt', lambda a: a.status == c.DECLINED, ident='art_show_declined') ArtShowAppEmailFixture( 'Reminder to pay for your {EVENT_NAME} Art Show application', 'art_show/payment_reminder.txt', lambda a: a.status == c.APPROVED and a.is_unpaid, when=days_before(14, c.ART_SHOW_PAYMENT_DUE), ident='art_show_payment_reminder') ArtShowAppEmailFixture( '{EVENT_NAME} Art Show piece entry needed', 'art_show/pieces_reminder.txt', lambda a: a.status == c.PAID and not a.art_show_pieces, when=days_before(15, c.EPOCH), ident='art_show_pieces_reminder') ArtShowAppEmailFixture( 'Reminder to assign an agent for your {EVENT_NAME} Art Show application', 'art_show/agent_reminder.html', lambda a: a.status == c.PAID and not a.agent, when=after( c.EVENT_TIMEZONE.localize(datetime(int(c.EVENT_YEAR), 11, 1))),
template='season_supporter_event_invite.txt', when=before(event.deadline), extra_data={'event': event}) for _event in SeasonEvent.instances.values(): SeasonSupporterEmailFixture(_event) AutomatedEmailFixture( Attendee, 'MAGFest schedule, maps, and other FAQs', 'precon_faqs.html', filter=lambda a: ( a.badge_status not in [c.INVALID_STATUS, c.DEFERRED_STATUS] and a.paid != c.NOT_PAID and (a.paid != c.PAID_BY_GROUP or a.group and not a.group.amount_unpaid)), ident='magprime_precon_faqs', when=days_before(7, c.EPOCH), sender='MAGFest <*****@*****.**>') AutomatedEmailFixture( Attendee, 'MAGFest food for guests', 'guest_food_restrictions.txt', lambda a: a.badge_type == c.GUEST_BADGE, sender='MAGFest Staff Suite <*****@*****.**>', ident='magprime_guest_food_restrictions') AutomatedEmailFixture( Attendee, 'MAGFest hospitality suite information', 'food/guest_food_info.txt', lambda a: a.badge_type == c.GUEST_BADGE, sender='MAGFest Staff Suite <*****@*****.**>', ident='magprime_hospitality_suite_guest_food_info') AutomatedEmailFixture(
from uber.config import c from uber.automated_emails import MarketplaceEmailFixture from uber.utils import before, days_before MarketplaceEmailFixture( 'Your {EVENT_NAME} ({EVENT_DATE}) Dealer registration is due in one week', 'dealers/payment_reminder.txt', lambda g: g.status == c.APPROVED and days_before(7, g.dealer_payment_due, 2 )() and g.is_unpaid, needs_approval=False, ident='dealer_reg_payment_reminder_due_soon_mff') MarketplaceEmailFixture( 'Last chance to pay for your {EVENT_NAME} ({EVENT_DATE}) Dealer registration', 'dealers/payment_reminder.txt', lambda g: g.status == c.APPROVED and days_before(2, g.dealer_payment_due) () and g.is_unpaid, needs_approval=False, ident='dealer_reg_payment_reminder_last_chance_mff') MarketplaceEmailFixture( 'Your {EVENT_NAME} ({EVENT_DATE}) dealer application has been waitlisted', 'dealers/pending_waitlisted.txt', lambda g: g.status == c.WAITLISTED and g.registered < c. DEALER_REG_DEADLINE, when=before(c.DEALER_WAITLIST_CLOSED), ident='dealer_pending_now_waitlisted_mff')
class TestAutomatedEmailCategory: def test_testing_environment(self, get_test_email_category): assert len(AutomatedEmail.instances) == 1 assert len(AutomatedEmail.queries[Attendee](None)) == 3 assert not get_test_email_category.unapproved_emails_not_sent def test_event_name(self, get_test_email_category): assert get_test_email_category.subject == E.SUBJECT_TO_FIND assert get_test_email_category.ident == E.IDENT_TO_FIND def test_approval_needed_and_we_have_it(self, monkeypatch, set_test_approved_idents, get_test_email_category, log_unsent_because_unapproved): job = SendAllAutomatedEmailsJob() assert get_test_email_category.approved assert job.log_unsent_because_unapproved.call_count == 0 def test_approval_needed_and_we_dont_have_it( self, monkeypatch, get_test_email_category, log_unsent_because_unapproved): job = SendAllAutomatedEmailsJob() assert not get_test_email_category.approved assert job.log_unsent_because_unapproved.call_count == 1 # attempt to send the same email and we should see the unapproved count go up because it's still unapproved assert not get_test_email_category.approved assert job.log_unsent_because_unapproved.call_count == 2 def test_approval_not_needed(self, monkeypatch, get_test_email_category): assert not get_test_email_category.approved monkeypatch.setattr(get_test_email_category, 'needs_approval', False) assert get_test_email_category.approved # -------------- test should_send() ------------------- def test_should_send_goes_through(self, get_test_email_category, set_test_approved_idents, attendee1): assert get_test_email_category._should_send(model_inst=attendee1) def test_should_send_incorrect_model_used(self, monkeypatch, get_test_email_category, attendee1): wrong_model = FakeModel() assert not get_test_email_category._should_send(model_inst=wrong_model) def test_should_send_no_email_present(self, monkeypatch, get_test_email_category, attendee1): delattr(attendee1, 'email') assert not get_test_email_category._should_send(model_inst=attendee1) def test_should_send_blank_email_present(self, monkeypatch, get_test_email_category, attendee1): attendee1.email = '' assert not get_test_email_category._should_send(model_inst=attendee1) def test_should_send_already_sent_this_email( self, get_test_email_category, set_test_approved_idents, set_previously_sent_emails_to_attendee1, attendee1): assert not get_test_email_category._should_send(model_inst=attendee1) def test_should_send_wrong_filter(self, get_test_email_category, set_test_approved_idents, attendee1): get_test_email_category.filter = lambda a: a.paid == c.HAS_PAID assert not get_test_email_category._should_send(model_inst=attendee1) def test_should_send_not_approved(self, get_test_email_category, attendee1): assert not get_test_email_category._should_send(model_inst=attendee1) def test_should_send_at_con(self, at_con, get_test_email_category, set_test_approved_idents, attendee1): assert not get_test_email_category._should_send(model_inst=attendee1) get_test_email_category.allow_during_con = True assert get_test_email_category._should_send(model_inst=attendee1) # ----------- def test_send_doesnt_throw_exception(self, monkeypatch, get_test_email_category): get_test_email_category.send_if_should(None, raise_errors=False) def test_send_throws_exception(self, monkeypatch, get_test_email_category): monkeypatch.setattr(get_test_email_category, '_should_send', Mock(side_effect=Exception('Boom!'))) with pytest.raises(Exception): get_test_email_category.send_if_should(None, raise_errors=True) def test_really_send_throws_exception(self, monkeypatch, get_test_email_category): monkeypatch.setattr(get_test_email_category, 'computed_subject', Mock(side_effect=Exception('Boom!'))) with pytest.raises(Exception): get_test_email_category.really_send(None) valid_when = days_after(3, sept_15th - timedelta(days=5)) invalid_when = days_after(3, sept_15th) @pytest.mark.parametrize("when, expected_result", [([invalid_when], False), ([valid_when], True), ([invalid_when, valid_when], False), ([valid_when, invalid_when], False), ([invalid_when, invalid_when], False), ([valid_when, valid_when], True), ((), True)]) def test_when_function(self, monkeypatch, get_test_email_category, set_datebase_now_to_sept_15th, attendee1, when, expected_result): monkeypatch.setattr(get_test_email_category, 'when', when) monkeypatch.setattr(AutomatedEmail, 'approved', True) assert get_test_email_category.filters_run( attendee1) == expected_result assert get_test_email_category._run_date_filters() == expected_result assert get_test_email_category._should_send( model_inst=attendee1) == expected_result @pytest.mark.parametrize("when, expected_text", [ ([ days_after(3, sept_15th - timedelta(days=5)), before(sept_15th - timedelta(days=3)), days_before(3, sept_15th + timedelta(days=5), 1), ], ['after 09/13', 'before 09/12', 'between 09/17 and 09/19']), ([days_after(3, sept_15th - timedelta(days=5))], ['after 09/13']), ]) def test_when_txt(self, monkeypatch, get_test_email_category, set_datebase_now_to_sept_15th, attendee1, when, expected_text): monkeypatch.setattr(get_test_email_category, 'when', when) assert get_test_email_category.when_txt == '\n'.join(expected_text) @pytest.mark.parametrize("filter, expected_result", [ (lambda a: False, False), (lambda a: True, True), (lambda a: a.paid == c.NEED_NOT_PAY, True), (lambda a: a.paid != c.NEED_NOT_PAY, False), ]) def test_filters(self, monkeypatch, get_test_email_category, attendee1, filter, expected_result): monkeypatch.setattr(get_test_email_category, 'filter', filter) monkeypatch.setattr(AutomatedEmail, 'approved', True) assert get_test_email_category.filters_run( attendee1) == expected_result assert get_test_email_category._should_send( model_inst=attendee1) == expected_result def test_none_filter(self): with pytest.raises(AssertionError): AutomatedEmail(Attendee, '', '', None, ident='test_none_filter') def test_no_filter(self): # this is slightly silly but, if this ever changes, we should be explicit about what the expected result is with pytest.raises(TypeError): AutomatedEmail(Attendee, '', '', ident='test_no_filter') def test_missing_ident_arg(self): with pytest.raises(TypeError): AutomatedEmail(Attendee, '', '', lambda a: False) def test_empty_ident_arg(self): with pytest.raises(AssertionError): AutomatedEmail(Attendee, '', '', lambda a: False, ident='') with pytest.raises(AssertionError): AutomatedEmail(Attendee, '', '', lambda a: False, ident=None)
'Your {EVENT_NAME} Art Show application has been waitlisted', 'art_show/waitlisted.txt', lambda a: a.status == c.WAITLISTED, ident='art_show_waitlisted') ArtShowAppEmailFixture( 'Your {EVENT_NAME} Art Show application has been declined', 'art_show/declined.txt', lambda a: a.status == c.DECLINED, ident='art_show_declined') ArtShowAppEmailFixture( 'Reminder to pay for your {EVENT_NAME} Art Show application', 'art_show/payment_reminder.txt', lambda a: a.status == c.APPROVED and a.is_unpaid, when=days_before(14, c.ART_SHOW_PAYMENT_DUE), ident='art_show_payment_reminder') ArtShowAppEmailFixture( '{EVENT_NAME} Art Show piece entry needed', 'art_show/pieces_reminder.txt', lambda a: a.status == c.PAID and not a.art_show_pieces, when=days_before(15, c.EPOCH), ident='art_show_pieces_reminder') ArtShowAppEmailFixture( 'Reminder to assign an agent for your {EVENT_NAME} Art Show application', 'art_show/agent_reminder.html', lambda a: a.status == c.PAID and not a.agent, when=after(c.EVENT_TIMEZONE.localize(datetime(int(c.EVENT_YEAR), 11, 1))), ident='art_show_agent_reminder')
lambda a: a.badge_type == c.GUEST_BADGE, ident='magwest_guest_food_restrictions', sender="MAGWest Tea Room <*****@*****.**>") AutomatedEmailFixture(Attendee, '{EVENT_NAME} hospitality suite information', 'guest_food_info.txt', lambda a: a.badge_type == c.GUEST_BADGE, ident='magwest_guest_food_info', sender="MAGWest Tea Room <*****@*****.**>") AutomatedEmailFixture( Attendee, '{EVENT_NAME} Volunteer Food', 'volunteer_food_info.txt', lambda a: a.staffing and days_before(7, c.FINAL_EMAIL_DEADLINE), ident='magwest_volunteer_food_info', sender="MAGWest Tea Room <*****@*****.**>") AutomatedEmailFixture(Attendee, '{EVENT_NAME} FAQ', 'prefest_faq.html', lambda a: a.badge_status == c.COMPLETED_STATUS and days_before(7, c.FINAL_EMAIL_DEADLINE), ident='magwest_prefest_faq') AutomatedEmailFixture(Attendee, '{EVENT_NAME} PC Gaming Survey', 'pc_gaming_survey.html', lambda a: c.LAN in a.interests_ints, ident='pc_gaming_survey',
MarketplaceEmail('Your {EVENT_NAME} Dealer registration has been approved', 'dealers/approved.html', lambda g: g.status == c.APPROVED, needs_approval=False, ident='dealer_reg_approved') MarketplaceEmail('Reminder to pay for your {EVENT_NAME} Dealer registration', 'dealers/payment_reminder.txt', lambda g: g.status == c.APPROVED and days_after(30, g.approved)() and g.is_unpaid, needs_approval=False, ident='dealer_reg_payment_reminder') MarketplaceEmail('Your {EVENT_NAME} {EVENT_DATE} Dealer registration is due in one week', 'dealers/payment_reminder.txt', lambda g: g.status == c.APPROVED and g.is_unpaid, when=days_before(7, c.DEALER_PAYMENT_DUE, 2), needs_approval=False, ident='dealer_reg_payment_reminder_due_soon') MarketplaceEmail('Last chance to pay for your {EVENT_NAME} {EVENT_DATE} Dealer registration', 'dealers/payment_reminder.txt', lambda g: g.status == c.APPROVED and g.is_unpaid, when=days_before(2, c.DEALER_PAYMENT_DUE), needs_approval=False, ident='dealer_reg_payment_reminder_last_chance') MarketplaceEmail('{EVENT_NAME} Dealer waitlist has been exhausted', 'dealers/waitlist_closing.txt', lambda g: g.status == c.WAITLISTED, when=days_after(0, c.DEALER_WAITLIST_CLOSED), ident='uber_marketplace_waitlist_exhausted')
'Reminder to pay for your {EVENT_NAME} Dealer registration', 'dealers/payment_reminder.txt', lambda g: g.status == c.APPROVED and days_after(30, g.approved)() and g.is_unpaid, # query=and_( # Group.status == c.APPROVED, # Group.approved < (func.now() - timedelta(days=30)), # Group.is_unpaid == True), needs_approval=True, ident='dealer_reg_payment_reminder') MarketplaceEmailFixture( 'Your {EVENT_NAME} ({EVENT_DATE}) Dealer registration is due in one week', 'dealers/payment_reminder.txt', lambda g: g.status == c.APPROVED and g.is_unpaid, # query=and_(Group.status == c.APPROVED, Group.is_unpaid == True), when=days_before(7, c.DEALER_PAYMENT_DUE, 2), needs_approval=True, ident='dealer_reg_payment_reminder_due_soon') MarketplaceEmailFixture( 'Last chance to pay for your {EVENT_NAME} ({EVENT_DATE}) Dealer registration', 'dealers/payment_reminder.txt', lambda g: g.status == c.APPROVED and g.is_unpaid, # query=and_(Group.status == c.APPROVED, Group.is_unpaid == True), when=days_before(2, c.DEALER_PAYMENT_DUE), needs_approval=True, ident='dealer_reg_payment_reminder_last_chance') MarketplaceEmailFixture( '{EVENT_NAME} Dealer waitlist has been exhausted', 'dealers/waitlist_closing.txt',
def test_representation_days_empty(self): assert days_before(days=1, deadline=None).active_when == ''
def test_representation_days_before(self): assert days_before( days=1, deadline=DateBase.now()).active_when == 'between 09/14 and 09/15'
def test_invalid_days(self): with pytest.raises(ValueError): days_before(days=0, deadline=DateBase.now()) with pytest.raises(ValueError): days_after(days=-1, deadline=DateBase.now())
def test_invalid_date_range(self): with pytest.raises(ValueError): days_before(days=3, deadline=DateBase.now(), until=5)
def test_no_deadline_set(self): assert not days_before(1, None)() assert not before(None)()
template='season_supporter_event_invite.txt', when=before(event.deadline), extra_data={'event': event}) for _event in SeasonEvent.instances.values(): SeasonSupporterEmailFixture(_event) AutomatedEmailFixture( Attendee, 'MAGFest schedule, maps, and other FAQs', 'precon_faqs.html', filter=lambda a: ( a.badge_status not in [c.INVALID_STATUS, c.DEFERRED_STATUS] and a.paid != c.NOT_PAID and (a.paid != c.PAID_BY_GROUP or a.group and not a.group.amount_unpaid)), ident='magprime_precon_faqs', when=days_before(7, c.EPOCH), sender='MAGFest <*****@*****.**>') AutomatedEmailFixture( Attendee, 'MAGFest food for guests', 'guest_food_restrictions.txt', lambda a: a.badge_type == c.GUEST_BADGE, sender='MAGFest Staff Suite <*****@*****.**>', ident='magprime_guest_food_restrictions') AutomatedEmailFixture( Attendee, 'MAGFest hospitality suite information', 'guest_food_info.txt', lambda a: a.badge_type == c.GUEST_BADGE, sender='MAGFest Staff Suite <*****@*****.**>', ident='magprime_hospitality_suite_guest_food_info') AutomatedEmailFixture(
def _AutomatedEmail_reconcile(self, fixture): _original_AutomatedEmail_reconcile(self, fixture) if self.ident == 'band_charity_reminder': self.subject = '{} charity raffle reminder'.format(c.EVENT_NAME) return self email.AutomatedEmail.reconcile = _AutomatedEmail_reconcile AutomatedEmailFixture( Attendee, '{EVENT_NAME} Check-in Barcode', 'checkin_barcode.html', lambda a: a.badge_status == c.COMPLETED_STATUS and days_before(7, c.FINAL_EMAIL_DEADLINE), ident='maglabs_checkin_barcode') AutomatedEmailFixture( Attendee, '{EVENT_NAME} FAQ', 'prefest_faq.html', lambda a: a.badge_status == c.COMPLETED_STATUS and days_before(7, c.FINAL_EMAIL_DEADLINE), ident='maglabs_prefest_faq') AutomatedEmailFixture( Room, '{EVENT_NAME} Hotel Room Assignment Update', 'hotel/room_assignment_update.txt', lambda r: r.locked_in, sender=c.ROOM_EMAIL_SENDER,