def test_staff_badge(self, monkeypatch): with Session() as session: assert session monkeypatch.setattr(Attendee, 'session', Mock()) a = Attendee(badge_type=c.STAFF_BADGE, badge_num=123) a.unset_volunteering() assert a.badge_type == c.ATTENDEE_BADGE and a.badge_num is None
def test_unpaid_dept_head(self, dept): dept_membership = DeptMembership( department=dept, is_dept_head=True) a = Attendee(dept_memberships=[dept_membership]) a._staffing_adjustments() assert a.paid == c.NEED_NOT_PAY
def delete(self, session, id, return_to='index?'): attendee = session.attendee(id, allow_invalid=True) if attendee.group: if attendee.group.leader_id == attendee.id: message = 'You cannot delete the leader of a group; ' \ 'you must make someone else the leader first, or just delete the entire group' elif attendee.is_unassigned: session.delete_from_group(attendee, attendee.group) message = 'Unassigned badge removed.' else: replacement_attendee = Attendee(**{attr: getattr(attendee, attr) for attr in [ 'group', 'registered', 'badge_type', 'badge_num', 'paid', 'amount_paid', 'amount_extra' ]}) if replacement_attendee.group and replacement_attendee.group.is_dealer: replacement_attendee.ribbon = add_opt(replacement_attendee.ribbon_ints, c.DEALER_RIBBON) session.add(replacement_attendee) attendee._skip_badge_shift_on_delete = True session.delete_from_group(attendee, attendee.group) message = 'Attendee deleted, but this badge is still ' \ 'available to be assigned to someone else in the same group' else: session.delete(attendee) message = 'Attendee deleted' raise HTTPRedirect(return_to + ('' if return_to[-1] == '?' else '&') + 'message={}', message)
def test_dept_head_invariants(self, dept): dept_membership = DeptMembership( department=dept, is_dept_head=True) a = Attendee(dept_memberships=[dept_membership]) a._staffing_adjustments() assert a.staffing assert a.badge_type == c.STAFF_BADGE
def test_self_service_refunds_payment_status(monkeypatch, paid, expected): monkeypatch.setattr(config.Config, 'SELF_SERVICE_REFUNDS_OPEN', property(lambda s: True)) attendee = Attendee(paid=paid, amount_paid=10) txn = StripeTransaction(amount=1000) attendee.stripe_txn_share_logs = [ StripeTransactionAttendee(attendee_id=attendee.id, txn_id=txn.id, share=1000)] assert attendee.can_self_service_refund_badge == expected
def test_self_service_refunds_if_on(monkeypatch, open, expected): monkeypatch.setattr(config.Config, 'SELF_SERVICE_REFUNDS_OPEN', property(open)) attendee = Attendee(paid=c.HAS_PAID, amount_paid=10) txn = StripeTransaction(amount=1000) attendee.stripe_txn_share_logs = [ StripeTransactionAttendee(attendee_id=attendee.id, txn_id=txn.id, share=1000)] assert attendee.can_self_service_refund_badge == expected
def test_self_service_refunds_group_leader(monkeypatch): monkeypatch.setattr(config.Config, 'SELF_SERVICE_REFUNDS_OPEN', property(lambda s: True)) attendee = Attendee(paid=c.HAS_PAID, amount_paid=10) attendee.group = Group(leader_id=attendee.id) txn = StripeTransaction(amount=1000) attendee.stripe_txn_share_logs = [ StripeTransactionAttendee(attendee_id=attendee.id, txn_id=txn.id, share=1000)] assert not attendee.can_self_service_refund_badge
def test_self_service_refunds_misc(monkeypatch, amount_paid, checked_in, expected): monkeypatch.setattr(config.Config, 'SELF_SERVICE_REFUNDS_OPEN', property(lambda s: True)) attendee = Attendee(paid=c.HAS_PAID, amount_paid=amount_paid) txn = StripeTransaction(amount=1000) attendee.stripe_txn_share_logs = [ StripeTransactionAttendee(attendee_id=attendee.id, txn_id=txn.id, share=1000)] attendee.checked_in = checked_in assert attendee.can_self_service_refund_badge == expected
def test_dept_head_ribbon_label_from_ribbon_attr(): a = Attendee() assert a.ribbon_labels == [] a.ribbon = '{}'.format(c.DEPT_HEAD_RIBBON) assert a.ribbon_labels == ['Department Head'] a.ribbon = '{},{}'.format(c.VOLUNTEER_RIBBON, c.DEPT_HEAD_RIBBON) assert a.ribbon_labels == ['Department Head', 'Volunteer'] a.ribbon = '{}'.format(c.VOLUNTEER_RIBBON) assert a.ribbon_labels == ['Volunteer']
def create_judge(self, session, message='', first_name='', last_name='', email='', **params): judge = session.indie_judge(params, checkgroups=['genres', 'platforms']) if cherrypy.request.method == 'POST': message = check(judge) if not message and not first_name or not last_name or not email: message = 'First name, last name, and email address are all required to add a judge' if not message: # only match on last name and email, to prevent nickname issues; this could cause # problems if we had two judges with the same last name AND the same email address attendee = session.query(Attendee).filter_by(last_name=last_name, email=email).first() if attendee and attendee.admin_account: if attendee.admin_account.judge: raise HTTPRedirect( 'index?message={}{}', attendee.full_name, ' is already registered as a judge') else: attendee.admin_account.judge = judge attendee.admin_account.access = ','.join(map(str, set( attendee.admin_account.access_ints + [c.INDIE_JUDGE]))) raise HTTPRedirect('index?message={}{}', attendee.full_name, ' has been granted judge access') if not attendee: attendee = Attendee(first_name=first_name, last_name=last_name, email=email, placeholder=True, badge_type=c.ATTENDEE_BADGE, paid=c.NEED_NOT_PAY) session.add(attendee) password = genpasswd() attendee.admin_account = AdminAccount( judge=judge, access=str(c.INDIE_JUDGE), hashed=bcrypt.hashpw(password, bcrypt.gensalt()) ) email_body = render('emails/accounts/new_account.txt', { 'password': password, 'account': attendee.admin_account }, encoding=None) send_email.delay( c.MIVS_EMAIL, attendee.email, 'New {} Ubersystem Account'.format(c.EVENT_NAME), email_body, model=attendee.to_dict('id')) raise HTTPRedirect( 'index?message={}{}', attendee.full_name, ' has been given an admin account as an Indie Judge') return { 'message': message, 'judge': judge, 'first_name': first_name, 'last_name': last_name, 'email': email }
def test_set_group_paid_to_complete(self, monkeypatch): monkeypatch.setattr(Group, 'amount_unpaid', 0) g = Group() a = Attendee( paid=c.PAID_BY_GROUP, badge_status=c.NEW_STATUS, first_name='Paid', placeholder=False, group=g, group_id=g.id) a._status_adjustments() assert a.badge_status == c.COMPLETED_STATUS
def test_requested_any_dept(): dept1 = Department(name='Dept1', description='Dept1') dept2 = Department(name='Dept2', description='Dept2') volunteer = Attendee(paid=c.HAS_PAID, first_name='V', last_name='One') volunteer.dept_membership_requests = [ DeptMembershipRequest(attendee=volunteer)] with Session() as session: session.add_all([dept1, dept2, volunteer]) session.commit() session.refresh(volunteer) all_depts = session.query(Department).order_by(Department.name).all() assert all_depts == volunteer.requested_depts
def test_would_send_if_approved(self, automated_email_fixture): with Session() as session: email = session.query(AutomatedEmail).one() attendee = Attendee(first_name='A', last_name='Z', email='') assert not email.would_send_if_approved(attendee) attendee.email = '*****@*****.**' assert email.would_send_if_approved(attendee) email.fixture.filter = lambda x: False assert not email.would_send_if_approved(attendee) email.fixture.filter = lambda x: True assert email.would_send_if_approved(attendee)
def attendees(self, session, target_server='', api_token='', query='', message=''): target_url, target_host, remote_api_token = _format_import_params(target_server, api_token) results = {} if cherrypy.request.method == 'POST': if not remote_api_token: message = 'No API token given and could not find a token for: {}'.format(target_host) elif not target_url: message = 'Unrecognized hostname: {}'.format(target_server) if not message: try: uri = '{}/jsonrpc/'.format(target_url) service = ServerProxy(uri=uri, extra_headers={'X-Auth-Token': remote_api_token}) results = service.attendee.export(query=query) except Exception as ex: message = str(ex) attendees = results.get('attendees', []) for attendee in attendees: attendee['href'] = '{}/registration/form?id={}'.format(target_url, attendee['id']) if attendees: attendees_by_name_email = groupify(attendees, lambda a: ( a['first_name'].lower(), a['last_name'].lower(), Attendee.normalize_email(a['email']), )) filters = [ and_( func.lower(Attendee.first_name) == first, func.lower(Attendee.last_name) == last, Attendee.normalized_email == email, ) for first, last, email in attendees_by_name_email.keys() ] existing_attendees = session.query(Attendee).filter(or_(*filters)).all() for attendee in existing_attendees: existing_key = (attendee.first_name.lower(), attendee.last_name.lower(), attendee.normalized_email) attendees_by_name_email.pop(existing_key, {}) attendees = list(chain(*attendees_by_name_email.values())) else: existing_attendees = [] return { 'target_server': target_server, 'api_token': api_token, 'query': query, 'message': message, 'unknown_ids': results.get('unknown_ids', []), 'unknown_emails': results.get('unknown_emails', []), 'unknown_names': results.get('unknown_names', []), 'unknown_names_and_emails': results.get('unknown_names_and_emails', []), 'attendees': attendees, 'existing_attendees': existing_attendees, }
def test_basic(self, dept, trusted_role): a = Attendee( staffing=True, requested_depts=[dept], ribbon=c.VOLUNTEER_RIBBON, shifts=[Shift()]) a.dept_memberships = [DeptMembership( attendee=a, department=dept, dept_roles=[trusted_role])] a.assigned_depts = [dept] a.unset_volunteering() assert not a.staffing assert not a.has_role_somewhere assert not a.requested_depts assert not a.dept_memberships assert not a.shifts assert a.ribbon == ''
def test_diff_type_with_num_in_range(self, session, monkeypatch): session.add(Attendee(badge_type=c.CONTRACTOR_BADGE, badge_num=6)) # We want to force the badge number we set even though it's incorrect @presave_adjustment def _empty_adjustment(self): pass monkeypatch.setattr(Attendee, '_badge_adjustments', _empty_adjustment) session.commit() assert 7 == session.auto_badge_num(c.STAFF_BADGE)
def undo_new_checkin(self, session, id): attendee = session.attendee(id, allow_invalid=True) if attendee.group: session.add(Attendee( group=attendee.group, paid=c.PAID_BY_GROUP, badge_type=attendee.badge_type, ribbon=attendee.ribbon)) attendee.badge_num = None attendee.checked_in = attendee.group = None raise HTTPRedirect('new?message={}', 'Attendee un-checked-in')
def test_is_not_transferable_trusted(monkeypatch, dept, trusted_role): monkeypatch.setattr(Attendee, 'is_new', False) with Session() as session: attendee = Attendee(paid=c.HAS_PAID) dept_membership = DeptMembership( attendee=attendee, department=dept, dept_roles=[trusted_role]) session.add_all([attendee, dept, trusted_role, dept_membership]) session.flush() assert not attendee.is_transferable
def test_charge_log_transaction(self): attendee = Attendee() charge = Charge(targets=[attendee], amount=1000, description="Test charge") charge.response = stripe.Charge(id=10) result = charge.stripe_transaction_from_charge() assert result.stripe_id == 10 assert result.amount == 1000 assert result.desc == "Test charge" assert result.type == c.PAYMENT assert result.who == 'non-admin' assert result.fk_id == attendee.id assert result.fk_model == attendee.__class__.__name__
def test_over_limit_price_bump_before_event(self, monkeypatch): monkeypatch.setattr(c, 'EPOCH', localized_now() + timedelta(days=1)) session = Session().session assert c.BADGES_SOLD == 0 with request_cached_context(): session.add( Attendee(paid=c.HAS_PAID, badge_status=c.COMPLETED_STATUS)) session.commit() assert c.BADGES_SOLD == 1 assert 50 == c.get_attendee_price()
def create_test_attendees(): """ Creates 5 test attendees with ids 000...000 through 000...004. """ with Session() as session: for s in map(str, range(5)): attendee = Attendee( id='00000000-0000-0000-0000-00000000000{}'.format(s), first_name=s, last_name=s, email='{}@example.com'.format(s)) session.add(attendee)
def test_refunded_badge_price_bump_during_event(self, monkeypatch): monkeypatch.setattr(c, 'EPOCH', localized_now() - timedelta(days=1)) session = Session().session assert c.BADGES_SOLD == 0 with request_cached_context(): session.add( Attendee(paid=c.REFUNDED, badge_status=c.COMPLETED_STATUS)) session.commit() assert c.BADGES_SOLD == 1 assert 40 == c.get_attendee_price()
def transfer_badge(self, session, message='', **params): old = session.attendee(params['id']) assert old.is_transferable, 'This badge is not transferrable.' session.expunge(old) attendee = session.attendee(params, restricted=True) if 'first_name' in params: message = check(attendee, prereg=True) if (old.first_name == attendee.first_name and old.last_name == attendee.last_name) \ or (old.legal_name and old.legal_name == attendee.legal_name): message = 'You cannot transfer your badge to yourself.' elif not message and (not params['first_name'] and not params['last_name']): message = check(attendee, prereg=True) if not message and (not params['first_name'] and not params['last_name']): message = 'First and Last names are required.' if not message: subject = c.EVENT_NAME + ' Registration Transferred' body = render('emails/reg_workflow/badge_transfer.txt', { 'new': attendee, 'old': old }) try: send_email(c.REGDESK_EMAIL, [old.email, attendee.email, c.REGDESK_EMAIL], subject, body, model=attendee) except Exception: log.error('unable to send badge change email', exc_info=True) if attendee.amount_unpaid: raise HTTPRedirect('attendee_donation_form?id={}', attendee.id) else: raise HTTPRedirect( 'badge_updated?id={}&message={}', attendee.id, 'Your registration has been transferred') else: for attr in c.UNTRANSFERABLE_ATTRS: setattr(attendee, attr, getattr(Attendee(), attr)) return { 'old': old, 'attendee': attendee, 'message': message, 'affiliates': session.affiliates() }
def test_badge_cost(self): assert 10 == Attendee(badge_type=c.ONE_DAY_BADGE).badge_cost assert 20 == Attendee().badge_cost assert 30 == Attendee(overridden_price=30).badge_cost assert 0 == Attendee(paid=c.NEED_NOT_PAY).badge_cost assert 20 == Attendee(paid=c.PAID_BY_GROUP).badge_cost assert 30 == Attendee(base_badge_price=30).badge_cost
def create(self, first_name, last_name, email, params): """ Create an attendee with at least a first name, last name, and email. Prevents duplicate attendees. `params` should be a dictionary with column name: value to set other values. Use labels for Choice and MultiChoice columns, and a string like "no" or "yes" for Boolean columns. Date and DateTime columns should be parsed correctly as long as they follow a standard format. Example: <pre>{"legal_name": "First Last", "cellphone": "5555555555", "can_work_setup": "yes"}</pre> """ with Session() as session: attendee_query = session.query(Attendee).filter_by(first_name=first_name, last_name=last_name, email=email) if attendee_query.first(): raise HTTPError(400, 'An attendee with this name and email address already exists') attendee = Attendee(first_name=first_name, last_name=last_name, email=email) for key, val in params.items(): params[key] = _parse_if_datetime(key, val) params[key] = _parse_if_boolean(key, val) attendee.apply(params, restricted=False) session.add(attendee) message = check(attendee) if message: session.rollback() raise HTTPError(400, message) # Duplicates functionality on the admin form that makes placeholder badges need not pay # Staff (not volunteers) also almost never need to pay by default if (attendee.placeholder or attendee.staffing and c.VOLUNTEER_RIBBON not in attendee.ribbon_ints) and 'paid' not in params: attendee.paid = c.NEED_NOT_PAY return attendee.id
def test_badge_cost(monkeypatch, clear_price_bumps): monkeypatch.setattr(c, 'get_group_price', Mock(return_value=c.DEALER_BADGE_PRICE + 10)) assert 4 * c.DEALER_BADGE_PRICE + 20 == Group(attendees=[ Attendee(paid=c.REFUNDED), Attendee(ribbon=c.DEALER_RIBBON), Attendee(paid=c.PAID_BY_GROUP), Attendee(paid=c.PAID_BY_GROUP), Attendee(paid=c.PAID_BY_GROUP, ribbon=c.DEALER_RIBBON), Attendee(paid=c.PAID_BY_GROUP, ribbon=c.DEALER_RIBBON) ]).badge_cost
def test_num_staff_shirts_owed(self, monkeypatch): for gets_shirt, replacement_shirts, expected in [ (False, 0, 0), (False, 1, 0), (True, 0, 3), (True, 1, 2), # replacement_staff_shirts as currently programmed will only ever be 0 or 1 but # we're testing with a higher value just to ensure this will work if that changes (True, 2, 1)]: monkeypatch.setattr(Attendee, 'gets_staff_shirt', gets_shirt) monkeypatch.setattr(Attendee, 'replacement_staff_shirts', replacement_shirts) assert expected == Attendee().num_staff_shirts_owed
def test_is_transferable(monkeypatch): assert not Attendee(paid=c.HAS_PAID).is_transferable monkeypatch.setattr(Attendee, 'is_new', False) assert Attendee(paid=c.HAS_PAID).is_transferable assert Attendee(paid=c.PAID_BY_GROUP).is_transferable assert not Attendee(paid=c.NOT_PAID).is_transferable assert not Attendee(paid=c.HAS_PAID, checked_in=datetime.now(UTC)).is_transferable assert not Attendee(paid=c.HAS_PAID, badge_type=c.STAFF_BADGE).is_transferable assert not Attendee(paid=c.HAS_PAID, badge_type=c.GUEST_BADGE).is_transferable
def test_dupe_nums(self, session, monkeypatch): session.add(Attendee( badge_type=c.ATTENDEE_BADGE, checked_in=datetime.now(UTC), first_name="3002", paid=c.HAS_PAID, badge_num=3001)) session.add(Attendee( badge_type=c.ATTENDEE_BADGE, checked_in=datetime.now(UTC), first_name="3000", paid=c.HAS_PAID, badge_num=3001)) # Skip the badge adjustments here, which prevent us from setting duplicate numbers @presave_adjustment def _empty_adjustment(self): pass monkeypatch.setattr(Attendee, '_badge_adjustments', _empty_adjustment) session.commit() assert 3002 == session.auto_badge_num(c.ATTENDEE_BADGE)
def test_volunteer_event_shirt_earned(self, monkeypatch): for (eligible, takes_shifts, worked_hours), expected in { (False, False, 5): False, (False, False, 6): False, (False, True, 5): False, (False, True, 6): False, (True, False, 5): True, (True, True, 5): False, (True, False, 6): True, (True, True, 6): True}.items(): monkeypatch.setattr(Attendee, 'takes_shifts', takes_shifts) monkeypatch.setattr(Attendee, 'volunteer_event_shirt_eligible', eligible) assert expected == Attendee(nonshift_hours=worked_hours).volunteer_event_shirt_earned
def setup_fake_test_attendees(monkeypatch): # replace all email categories in the system with an empty list so we can add to it later monkeypatch.setattr( AutomatedEmail, 'queries', { Attendee: lambda ignored_param: [ Attendee( placeholder=True, first_name="Gambler", last_name="Kirkdouglas", email="*****@*****.**", paid=c.NEED_NOT_PAY, badge_type=c.GUEST_BADGE, id='b699bfd3-1ada-4f47-b07f-cb7939783afa', ), Attendee( placeholder=True, first_name="Kilroy", last_name="Kilroy", email="*****@*****.**", paid=c.NEED_NOT_PAY, badge_type=c.GUEST_BADGE, id='e91e6c7e-699e-4784-b43f-303acc419dd5', ), Attendee( placeholder=False, first_name="Reanimator", last_name="Lovejoy", email="*****@*****.**", paid=c.HAS_PAID, badge_type=c.ATTENDEE_BADGE, id='c8b35ec5-4385-4ad7-b7db-b6f082f74aeb', ), ], # Group: lambda ignored_param: would need to replace with: # session.query(Group).options(subqueryload(Group.attendees)) })
def test_names(self): a = Attendee(first_name='nac', last_name='mac Feegle') a._misc_adjustments() assert a.full_name == 'Nac mac Feegle' a = Attendee(first_name='NAC', last_name='mac feegle') a._misc_adjustments() assert a.full_name == 'Nac Mac Feegle'
def import_attendees(self, session, target_server='', api_token='', query='', message=''): service, service_message, target_url = get_api_service_from_server(target_server, api_token) message = message or service_message attendees, existing_attendees, results = {}, {}, {} if service: try: results = service.attendee.export(query=query) except Exception as ex: message = str(ex) if cherrypy.request.method == 'POST' and not message: attendees = results.get('attendees', []) for attendee in attendees: attendee['href'] = '{}/registration/form?id={}'.format(target_url, attendee['id']) if attendees: attendees_by_name_email = groupify(attendees, lambda a: ( a['first_name'].lower(), a['last_name'].lower(), Attendee.normalize_email(a['email']), )) filters = [ and_( func.lower(Attendee.first_name) == first, func.lower(Attendee.last_name) == last, Attendee.normalized_email == email, ) for first, last, email in attendees_by_name_email.keys() ] existing_attendees = session.query(Attendee).filter(or_(*filters)).all() for attendee in existing_attendees: existing_key = (attendee.first_name.lower(), attendee.last_name.lower(), attendee.normalized_email) attendees_by_name_email.pop(existing_key, {}) attendees = list(chain(*attendees_by_name_email.values())) return { 'target_server': target_server, 'api_token': api_token, 'query': query, 'message': message, 'unknown_ids': results.get('unknown_ids', []), 'unknown_emails': results.get('unknown_emails', []), 'unknown_names': results.get('unknown_names', []), 'unknown_names_and_emails': results.get('unknown_names_and_emails', []), 'attendees': attendees, 'existing_attendees': existing_attendees, }
def test_must_contact(): dept1 = Department(name='Dept1', description='Dept1') dept2 = Department(name='Dept2', description='Dept2') poc_dept1 = Attendee( paid=c.NEED_NOT_PAY, first_name='Poc', last_name='Dept1') poc_dept2 = Attendee( paid=c.NEED_NOT_PAY, first_name='Poc', last_name='Dept2') poc_both = Attendee( paid=c.NEED_NOT_PAY, first_name='Poc', last_name='Both') poc_dept1.dept_memberships = [DeptMembership( department=dept1, is_poc=True)] poc_dept2.dept_memberships = [DeptMembership( department=dept2, is_poc=True)] poc_both.dept_memberships = [ DeptMembership( department=dept1, is_poc=True), DeptMembership( department=dept2, is_poc=True)] start_time = datetime.now(tz=pytz.UTC) job1 = Job( name='Job1', description='Job1', start_time=start_time, duration=1, weight=1, slots=1, department=dept1) job2 = Job( name='Job2', description='Job2', start_time=start_time, duration=1, weight=1, slots=1, department=dept2) volunteer = Attendee(paid=c.HAS_PAID, first_name='V', last_name='One') job1.shifts = [Shift(attendee=volunteer, job=job1)] job2.shifts = [Shift(attendee=volunteer, job=job2)] with Session() as session: session.add_all([ dept1, dept2, poc_dept1, poc_dept2, poc_both, job1, job2, volunteer]) session.commit() assert volunteer.must_contact == '(Dept1) Poc Both / Poc Dept1<br/>(Dept2) Poc Both / Poc Dept2'
def searchable(self): with Session() as session: attendee = Attendee(placeholder=True, first_name='Searchable', last_name='Attendee', email='*****@*****.**', zip_code='12345') session.add(attendee) session.add( Attendee(placeholder=True, first_name='Two First', last_name='Names', email='*****@*****.**', zip_code='12345')) session.add( Attendee(placeholder=True, first_name='Two', last_name='Last Names', email='*****@*****.**', zip_code='12345')) for status in [c.NEW_STATUS, c.INVALID_STATUS, c.REFUNDED_STATUS]: session.add( Attendee(placeholder=True, first_name='Duplicate', last_name=c.BADGE_STATUS[status], email='*****@*****.**', zip_code='12345', badge_status=status)) session.add( Attendee(placeholder=True, first_name='Duplicate', last_name=c.BADGE_STATUS[status], email='*****@*****.**', zip_code='12345', badge_status=c.COMPLETED_STATUS)) return attendee.id
def test_shirt_info_marked_after_deadline(self, monkeypatch): monkeypatch.setattr(c, 'AFTER_SHIRT_DEADLINE', True) for marked, gets_shirt, second_shirt, expected in [ (False, False, c.UNKNOWN, False), (False, False, c.TWO_STAFF_SHIRTS, False), (False, True, c.UNKNOWN, False), (False, True, c.TWO_STAFF_SHIRTS, False), (True, False, c.UNKNOWN, True), (True, False, c.TWO_STAFF_SHIRTS, True), (True, True, c.UNKNOWN, True), (True, True, c.TWO_STAFF_SHIRTS, True)]: monkeypatch.setattr(Attendee, 'shirt_size_marked', marked) monkeypatch.setattr(Attendee, 'gets_staff_shirt', gets_shirt) assert expected == Attendee(second_shirt=second_shirt).shirt_info_marked
def test_staffing_still_trusted_assigned(self, dept, shiftless_dept): """ After applying staffing adjustements: Any depts you are both trusted and assigned to should remain unchanged """ a = Attendee(staffing=True) dept_memberships = [ DeptMembership(attendee=a, attendee_id=a.id, department=dept, department_id=dept.id, is_dept_head=True), DeptMembership(attendee=a, attendee_id=a.id, department=shiftless_dept, department_id=shiftless_dept.id, dept_roles=[DeptRole()]) ] a.assigned_depts = [dept, shiftless_dept] a.dept_memberships_with_role = dept_memberships a._staffing_adjustments() assert a.assigned_to(dept) and a.trusted_in(dept) assert a.assigned_to(shiftless_dept) and a.trusted_in(shiftless_dept)
def test_has_role_somewhere(dept, trusted_role): with Session() as session: attendee = Attendee(paid=c.HAS_PAID) dept_membership = DeptMembership(attendee=attendee, department=dept, dept_roles=[trusted_role]) session.add_all([attendee, dept, trusted_role, dept_membership]) session.flush() assert attendee.has_role_somewhere dept_membership.dept_roles = [] session.flush() session.refresh(attendee) assert not attendee.has_role_somewhere
def test_num_event_shirts_owed(self, monkeypatch): for paid, volunteer, replacement, owed in [(False, False, 0, 0), (False, True, 0, 1), (True, False, 0, 1), (True, True, 0, 2), (False, False, 1, 1), (False, True, 1, 2), (True, False, 1, 2), (True, True, 1, 3)]: monkeypatch.setattr(Attendee, 'paid_for_a_shirt', paid) monkeypatch.setattr(Attendee, 'volunteer_event_shirt_eligible', volunteer) monkeypatch.setattr(Attendee, 'num_event_shirts', replacement) assert owed == Attendee().num_event_shirts_owed
def test_discount(self, code, badge_cost, attr, value): attendee = Attendee() assert attendee.badge_cost == 40 with Session() as session: attendee.promo_code = session.promo_code(code=code) assert attendee.badge_cost == badge_cost attendee._use_promo_code() assert getattr(attendee, attr) == value attendee.promo_code = None
def import_attendees(session): for a in dump['attendees']: a['group'] = groups.get(a.pop('group_id', None)) secret_id = a.pop('secret_id') a['assigned_depts_ids'] = all_dept_ids_from_existing_locations(a.pop('assigned_depts', '')) a['requested_depts_ids'] = all_dept_ids_from_existing_locations(a.pop('requested_depts', '')) attendees[secret_id] = Attendee(**a) session.add(attendees[secret_id]) for f in dump['food']: f['attendee'] = attendees[f.pop('attendee_id')] f.setdefault('sandwich_pref', str(c.PEANUT_BUTTER)) # sandwich_pref didn't exist when the dump was taken session.add(FoodRestrictions(**f)) for h in dump['hotel']: h['attendee'] = attendees[h.pop('attendee_id')] session.add(HotelRequests(**h))
def create_badge(self, session, applicant_id): try: applicant = session.mits_applicant(applicant_id) applicant.attendee = Attendee(placeholder=True, paid=c.NEED_NOT_PAY, badge_type=c.ATTENDEE_BADGE, first_name=applicant.first_name, last_name=applicant.last_name, email=applicant.email, cellphone=applicant.cellphone) session.add(applicant.attendee) session.commit() except Exception: log.error('unexpected error adding new applicant', exc_info=True) return {'error': 'Unexpected error: unable to add attendee'} else: return {'comp_count': applicant.team.comped_badge_count}
def staff(self, session, target_server='', api_token='', query='', message=''): target_url = _server_to_url(target_server) if cherrypy.request.method == 'POST': try: uri = '{}/jsonrpc/'.format(target_url) service = ServerProxy( uri=uri, extra_headers={'X-Auth-Token': api_token.strip()}) results = service.attendee.export(query=query) except Exception as ex: message = str(ex) results = {} else: results = {} attendees = results.get('attendees', []) for attendee in attendees: attendee['href'] = '{}/registration/form?id={}'.format( target_url, attendee['id']) if attendees: attendees_by_email = groupify( attendees, lambda a: Attendee.normalize_email(a['email'])) emails = list(attendees_by_email.keys()) existing_attendees = session.query(Attendee).filter( Attendee.normalized_email.in_(emails)).all() for attendee in existing_attendees: attendees_by_email.pop(attendee.normalized_email, {}) attendees = list(chain(*attendees_by_email.values())) else: existing_attendees = [] return { 'target_server': target_server, 'api_token': api_token, 'query': query, 'message': message, 'unknown_emails': results.get('unknown_emails', []), 'unknown_names': results.get('unknown_names', []), 'attendees': attendees, 'existing_attendees': existing_attendees, }
def match_to_group_preconditions(): group_id = None # leader_id = None with Session() as session: console = Department(name='Console_01', description='Console_01') leader = Attendee( first_name='Fearless', last_name='Leader', email='*****@*****.**', zip_code='21211', ec_name='Nana Fearless', ec_phone='555-555-1234', cellphone='555-555-2345', birthdate=date(1964, 12, 30), registered=localized_now(), paid=c.PAID_BY_GROUP, badge_type=c.STAFF_BADGE, badge_printed_name='Fearmore', ribbon='', staffing=True, assigned_depts=[console]) group = Group(name='Too Many Badges!') group.auto_recalc = False group.attendees = [leader] group.leader = leader session.add(leader) session.add(group) assert session.assign_badges( group, 4, new_badge_type=c.STAFF_BADGE, new_ribbon_type='', paid=c.PAID_BY_GROUP) is None session.flush() group_id = group.id # leader_id = leader.id yield group_id with Session() as session: session.query(Group).filter(Group.id == group_id).delete( synchronize_session=False)
def test_staffing_still_trusted_assigned(self, dept, shiftless_dept): """ After applying staffing adjustements: Any depts you are both trusted and assigned to should remain unchanged """ a = Attendee(staffing=True) dept_memberships = [ DeptMembership( attendee=a, attendee_id=a.id, department=dept, department_id=dept.id, is_dept_head=True), DeptMembership( attendee=a, attendee_id=a.id, department=shiftless_dept, department_id=shiftless_dept.id, dept_roles=[DeptRole()])] a.assigned_depts = [dept, shiftless_dept] a.dept_memberships_with_role = dept_memberships a._staffing_adjustments() assert a.assigned_to(dept) and a.trusted_in(dept) assert a.assigned_to(shiftless_dept) and a.trusted_in(shiftless_dept)
def birthdays(): dates = [ date(1964, 12, 30), date(1964, 12, 31), date(1964, 1, 1), date(1964, 1, 2), date(1964, 1, 9), date(1964, 1, 10), date(1964, 1, 11), date(1964, 1, 12), date(1964, 1, 30), date(1964, 1, 31), date(1964, 2, 1), date(1964, 2, 2), date(1964, 2, 27), date(1964, 2, 28), date(1964, 2, 29), date(1964, 3, 1), date(1964, 3, 2) ] attendees = [] for d in dates: attendees.append( Attendee(placeholder=True, first_name='Born on', last_name=d.strftime('%B %-d, %Y'), ribbon=c.VOLUNTEER_RIBBON, staffing=True, birthdate=d)) ids = [] with Session() as session: session.bulk_insert(attendees) ids = [a.id for a in attendees] yield ids with Session() as session: session.query(Attendee).filter( Attendee.id.in_(ids)).delete(synchronize_session=False)
def test_email_templates(plugins, expected, monkeypatch): monkeypatch.setattr(JinjaEnv, '_env', None) monkeypatch.setattr(JinjaEnv, '_exportable_functions', {}) monkeypatch.setattr(JinjaEnv, '_filter_functions', {}) monkeypatch.setattr(JinjaEnv, '_test_functions', {}) monkeypatch.setattr(JinjaEnv, '_template_dirs', []) monkeypatch.setattr(AutomatedEmail, '_fixtures', OrderedDict()) for plugin in plugins: JinjaEnv.insert_template_dir( os.path.join(__here__, plugin, 'templates')) with Session() as session: for email in session.query(AutomatedEmail).all(): session.delete(email) for i in range(1, 4): AutomatedEmailFixture(Attendee, 'Test Template {}'.format(i), 'test_template.txt', lambda a: True, needs_approval=False, ident='test_template_{}'.format(i)) AutomatedEmail.reconcile_fixtures() with Session() as session: attendee = Attendee(first_name='Test', last_name='Email', email='*****@*****.**') for i in range(1, 4): automated_email = session.query(AutomatedEmail).filter_by( ident='test_template_{}'.format(i)).one() for i in range(1, 4): assert expected == automated_email.render_body( attendee).strip(), 'render() call {}'.format(i)
def test_placeholder_not_completed(self): a = Attendee(paid=c.NEED_NOT_PAY, badge_status=c.NEW_STATUS, first_name='Paid', placeholder=True) a._status_adjustments() assert a.badge_status == c.NEW_STATUS
def test_by_default_no_setup_and_teardown(self): attendee = Attendee() attendee.presave_adjustments() assert not attendee.can_work_setup assert not attendee.can_work_teardown
def test_banned_to_deferred(self, monkeypatch): a = Attendee(paid=c.HAS_PAID, badge_status=c.NEW_STATUS, first_name='Paid', placeholder=False) monkeypatch.setattr(Attendee, 'banned', True) a._status_adjustments() assert a.badge_status == c.WATCHED_STATUS
def test_staffers_can_have_other_ribbons(self): a = Attendee(badge_type=c.STAFF_BADGE, ribbon=c.DEALER_RIBBON) a._staffing_adjustments() assert c.DEALER_RIBBON in a.ribbon_ints
def test_unassigned_not_completed(self): a = Attendee(paid=c.NEED_NOT_PAY, badge_status=c.NEW_STATUS, first_name='') a._status_adjustments() assert a.badge_status == c.NEW_STATUS
def test_group_to_attendee(self): a = Attendee(badge_type=c.PSEUDO_GROUP_BADGE) a._badge_adjustments() assert a.badge_type == c.ATTENDEE_BADGE and a.ribbon == ''
def duplicate_badge_num_preconditions(): group_id = None leader_id = None with Session() as session: leader = Attendee( first_name='Fearless', last_name='Leader', email='*****@*****.**', zip_code='21211', ec_name='Nana Fearless', ec_phone='555-555-1234', cellphone='555-555-2345', birthdate=date(1964, 12, 30), registered=localized_now(), paid=c.PAID_BY_GROUP, ribbon='', staffing=True, badge_type=c.PSEUDO_GROUP_BADGE) group = Group(name='Too Many Badges!') group.attendees = [leader] group.leader = leader session.add(leader) session.add(group) assert session.assign_badges( group, 15, new_badge_type=c.STAFF_BADGE, new_ribbon_type='', paid=c.NEED_NOT_PAY) is None session.flush() group_id = group.id leader_id = leader.id with Session() as session: console = Department(name='DEPT_01', description='DEPT_01') leader = session.query(Attendee).get(leader_id) leader.paid = c.NEED_NOT_PAY leader.badge_printed_name = 'Fearmore' leader.badge_type = c.STAFF_BADGE leader.assigned_depts = [console] group = session.query(Group).get(group_id) group.auto_recalc = False for i in range(10): with Session() as session: console = session.query(Department).filter_by(name='DEPT_01').one() group = session.query(Group).get(group_id) is_staff = (i < 9) params = { 'first_name': 'Doubtful', 'last_name': 'Follower{}'.format(i), 'email': 'fearsome{}@example.com'.format(i), 'zip_code': '21211', 'ec_name': 'Nana Fearless', 'ec_phone': '555-555-1234', 'cellphone': '555-555-321{}'.format(i), 'birthdate': date(1964, 12, 30), 'registered': localized_now(), 'staffing': is_staff, 'badge_status': str(c.COMPLETED_STATUS), 'badge_printed_name': 'Fears{}'.format(i) if is_staff else '', 'assigned_depts': [console] if is_staff else ''} attendee = group.unassigned[0] attendee.apply(params, restricted=False) with Session() as session: group = session.query(Group).get(group_id) badge_nums = [a.badge_num for a in group.attendees] # SQLite doesn't support deferred constraints, so our test database # doesn't actually have a unique constraint on the badge_num # column. So we have to manually check for duplicate badge numbers. assert_unique(badge_nums) yield group_id with Session() as session: session.query(Group).filter(Group.id == group_id).delete( synchronize_session=False)
def test_dealer_to_attendee(self): a = Attendee(badge_type=c.PSEUDO_DEALER_BADGE) a._badge_adjustments() assert a.badge_type == c.ATTENDEE_BADGE and a.ribbon_ints == [c.DEALER_RIBBON]
def _decline_and_convert_dealer_group(session, group, status=c.DECLINED): """ Deletes the waitlisted dealer group and converts all of the group members to the appropriate badge type. Unassigned, unpaid badges will be deleted. """ admin_note = 'Converted badge from waitlisted dealer group "{}".'.format(group.name) group.status = status if not group.is_unpaid: group.tables = 0 for attendee in group.attendees: attendee.append_admin_note(admin_note) attendee.ribbon = remove_opt(attendee.ribbon_ints, c.DEALER_RIBBON) return 'Group dealer status removed' message = ['Group declined'] emails_failed = 0 emails_sent = 0 badges_converted = 0 for attendee in list(group.attendees): if _is_dealer_convertible(attendee): attendee.badge_status = c.INVALID_STATUS if not attendee.is_unassigned: new_attendee = Attendee() for attr in c.UNTRANSFERABLE_ATTRS: setattr(new_attendee, attr, getattr(attendee, attr)) new_attendee.overridden_price = attendee.overridden_price new_attendee.base_badge_price = attendee.base_badge_price new_attendee.append_admin_note(admin_note) session.add(new_attendee) try: send_email.delay( c.MARKETPLACE_EMAIL, new_attendee.email, 'Do you still want to come to {}?'.format(c.EVENT_NAME), render('emails/dealers/badge_converted.html', { 'attendee': new_attendee, 'group': group}, encoding=None), format='html', model=attendee.to_dict('id')) emails_sent += 1 except Exception: emails_failed += 1 badges_converted += 1 else: if attendee.paid not in [c.HAS_PAID, c.NEED_NOT_PAY]: attendee.paid = c.NOT_PAID attendee.append_admin_note(admin_note) attendee.ribbon = remove_opt(attendee.ribbon_ints, c.DEALER_RIBBON) for count, template in [ (badges_converted, '{} badge{} converted'), (emails_sent, '{} email{} sent'), (emails_failed, '{} email{} failed to send')]: if count > 0: message.append(template.format(count, pluralize(count))) return ', '.join(message)
def export(self, query, full=False): """ Searches for attendees by either email, "first last" name, or "first last <email>" combinations. `query` should be a comma or newline separated list of email/name queries. Example: <pre>Merrium Webster, [email protected], John Doe <[email protected]></pre> Results are returned in the format expected by <a href="../import/staff">the staff importer</a>. """ _re_name_email = re.compile(r'^\s*(.*?)\s*<\s*(.*?@.*?)\s*>\s*$') _re_sep = re.compile(r'[\n,]') _re_whitespace = re.compile(r'\s+') queries = [s.strip() for s in _re_sep.split(normalize_newlines(query)) if s.strip()] names = dict() emails = dict() names_and_emails = dict() ids = set() for q in queries: if '@' in q: match = _re_name_email.match(q) if match: name = match.group(1) email = Attendee.normalize_email(match.group(2)) if name: first, last = (_re_whitespace.split(name.lower(), 1) + [''])[0:2] names_and_emails[(first, last, email)] = q else: emails[email] = q else: emails[Attendee.normalize_email(q)] = q elif q: try: ids.add(str(uuid.UUID(q))) except Exception: first, last = (_re_whitespace.split(q.lower(), 1) + [''])[0:2] names[(first, last)] = q with Session() as session: if full: options = [ subqueryload(Attendee.dept_memberships).subqueryload(DeptMembership.department), subqueryload(Attendee.dept_membership_requests).subqueryload(DeptMembershipRequest.department)] else: options = [] email_attendees = [] if emails: email_attendees = session.query(Attendee).filter(Attendee.normalized_email.in_(list(emails.keys()))) \ .options(*options).order_by(Attendee.email, Attendee.id).all() known_emails = set(a.normalized_email for a in email_attendees) unknown_emails = sorted([raw for normalized, raw in emails.items() if normalized not in known_emails]) name_attendees = [] if names: filters = [ and_(func.lower(Attendee.first_name) == first, func.lower(Attendee.last_name) == last) for first, last in names.keys()] name_attendees = session.query(Attendee).filter(or_(*filters)) \ .options(*options).order_by(Attendee.email, Attendee.id).all() known_names = set((a.first_name.lower(), a.last_name.lower()) for a in name_attendees) unknown_names = sorted([raw for normalized, raw in names.items() if normalized not in known_names]) name_and_email_attendees = [] if names_and_emails: filters = [ and_( func.lower(Attendee.first_name) == first, func.lower(Attendee.last_name) == last, Attendee.normalized_email == email) for first, last, email in names_and_emails.keys()] name_and_email_attendees = session.query(Attendee).filter(or_(*filters)) \ .options(*options).order_by(Attendee.email, Attendee.id).all() known_names_and_emails = set( (a.first_name.lower(), a.last_name.lower(), a.normalized_email) for a in name_and_email_attendees) unknown_names_and_emails = sorted([ raw for normalized, raw in names_and_emails.items() if normalized not in known_names_and_emails]) id_attendees = [] if ids: id_attendees = session.query(Attendee).filter(Attendee.id.in_(ids)) \ .options(*options).order_by(Attendee.email, Attendee.id).all() known_ids = set(str(a.id) for a in id_attendees) unknown_ids = sorted([i for i in ids if i not in known_ids]) seen = set() all_attendees = [ a for a in (id_attendees + email_attendees + name_attendees + name_and_email_attendees) if a.id not in seen and not seen.add(a.id)] fields = [ 'first_name', 'last_name', 'birthdate', 'email', 'zip_code', 'birthdate', 'international', 'ec_name', 'ec_phone', 'cellphone', 'badge_printed_name', 'found_how', 'comments', 'admin_notes', 'all_years', 'badge_status', 'badge_status_label', ] if full: fields.extend(['shirt']) attendees = [] for a in all_attendees: d = a.to_dict(fields) if full: assigned_depts = {} checklist_admin_depts = {} dept_head_depts = {} poc_depts = {} for membership in a.dept_memberships: assigned_depts[membership.department_id] = membership.department.name if membership.is_checklist_admin: checklist_admin_depts[membership.department_id] = membership.department.name if membership.is_dept_head: dept_head_depts[membership.department_id] = membership.department.name if membership.is_poc: poc_depts[membership.department_id] = membership.department.name d.update({ 'assigned_depts': assigned_depts, 'checklist_admin_depts': checklist_admin_depts, 'dept_head_depts': dept_head_depts, 'poc_depts': poc_depts, 'requested_depts': { (m.department_id if m.department_id else 'All'): (m.department.name if m.department_id else 'Anywhere') for m in a.dept_membership_requests}, }) attendees.append(d) return { 'unknown_ids': unknown_ids, 'unknown_emails': unknown_emails, 'unknown_names': unknown_names, 'unknown_names_and_emails': unknown_names_and_emails, 'attendees': attendees, }
def test_set_comped_to_complete(self): a = Attendee(paid=c.NEED_NOT_PAY, badge_status=c.NEW_STATUS, first_name='Paid', placeholder=False) a._status_adjustments() assert a.badge_status == c.COMPLETED_STATUS
def test_unpaid_group_not_completed(self, monkeypatch): monkeypatch.setattr(Group, 'amount_unpaid', 100) g = Group() a = Attendee(paid=c.PAID_BY_GROUP, badge_status=c.NEW_STATUS, first_name='Paid', placeholder=False, group=g) a._status_adjustments() assert a.badge_status == c.NEW_STATUS