def assign(self, session, game_id, judge_id, return_to): return_to = return_to + '&message={}' for gid in listify(game_id): for jid in listify(judge_id): if not session.query(IndieGameReview).filter_by(game_id=gid, judge_id=jid).first(): session.add(IndieGameReview(game_id=gid, judge_id=jid)) raise HTTPRedirect(return_to, 'Assignment successful')
def remove(self, session, game_id, judge_id, return_to): return_to = return_to + '&message={}' for gid in listify(game_id): for jid in listify(judge_id): review = session.query(IndieGameReview).filter_by(game_id=gid, judge_id=jid).first() if review: session.delete(review) raise HTTPRedirect(return_to, 'Removal successful')
def __init__(self, href=None, access=None, submenu=None, name=None): assert submenu or href, "menu items must contain ONE nonempty: href or submenu" assert not submenu or not href, "menu items must not contain both a href and submenu" if submenu: self.submenu = listify(submenu) else: self.href = href self.name = name self.access = set(listify(access)) if access else set()
def __init__(self, model, subject, template, filter, ident, *, query=(), query_options=(), when=(), sender=None, cc=(), bcc=(), needs_approval=True, allow_at_the_con=False, allow_post_con=False, extra_data=None): assert ident, 'AutomatedEmail ident may not be empty.' assert ident not in AutomatedEmail._fixtures, 'AutomatedEmail ident "{}" already registered.'.format( ident) AutomatedEmail._fixtures[ident] = self self.model = model self.subject = subject \ .replace('{EVENT_NAME}', c.EVENT_NAME) \ .replace('{EVENT_YEAR}', c.EVENT_YEAR) \ .replace('{EVENT_DATE}', c.EPOCH.strftime('%b %Y')) self.template = template self.format = 'text' if template.endswith('.txt') else 'html' self.filter = lambda x: (x.gets_emails and filter(x)) self.ident = ident self.query = listify(query) self.query_options = listify(query_options) self.sender = sender or c.REGDESK_EMAIL self.cc = listify(cc) self.bcc = listify(bcc) self.needs_approval = needs_approval self.allow_at_the_con = allow_at_the_con self.allow_post_con = allow_post_con self.extra_data = extra_data or {} when = listify(when) after = [d.active_after for d in when if d.active_after] self.active_after = min(after) if after else None before = [d.active_before for d in when if d.active_before] self.active_before = max(before) if before else None
def __init__( self, model, subject, template, filter, ident, *, query=(), query_options=(), when=(), sender=None, cc=(), bcc=(), needs_approval=True, allow_at_the_con=False, allow_post_con=False, extra_data=None): assert ident, 'AutomatedEmail ident may not be empty.' assert ident not in AutomatedEmail._fixtures, 'AutomatedEmail ident "{}" already registered.'.format(ident) AutomatedEmail._fixtures[ident] = self self.model = model self.subject = subject \ .replace('{EVENT_NAME}', c.EVENT_NAME) \ .replace('{EVENT_YEAR}', c.EVENT_YEAR) \ .replace('{EVENT_DATE}', c.EPOCH.strftime('%b %Y')) self.template = template self.format = 'text' if template.endswith('.txt') else 'html' self.filter = filter or (lambda x: True) self.ident = ident self.query = listify(query) self.query_options = listify(query_options) self.sender = sender or c.REGDESK_EMAIL self.cc = listify(cc) self.bcc = listify(bcc) self.needs_approval = needs_approval self.allow_at_the_con = allow_at_the_con self.allow_post_con = allow_post_con self.extra_data = extra_data or {} when = listify(when) after = [d.active_after for d in when if d.active_after] self.active_after = min(after) if after else None before = [d.active_before for d in when if d.active_before] self.active_before = max(before) if before else None
def multi_merch_pickup(self, session, message="", csrf_token=None, picker_upper=None, badges=(), **shirt_sizes): picked_up = [] if csrf_token: check_csrf(csrf_token) try: picker_upper = session.query(Attendee).filter_by(badge_num=int(picker_upper)).one() except Exception: message = 'Please enter a valid badge number for the person picking up the merch: ' \ '{} is not in the system'.format(picker_upper) else: for badge_num in set(badges): if badge_num: try: attendee = session.query(Attendee).filter_by(badge_num=int(badge_num)).one() except Exception: picked_up.append('{!r} is not a valid badge number'.format(badge_num)) else: if attendee.got_merch: picked_up.append( '{a.full_name} (badge {a.badge_num}) already got their merch'.format(a=attendee)) else: attendee.got_merch = True shirt_key = 'shirt_{}'.format(attendee.badge_num) if shirt_key in shirt_sizes: attendee.shirt = int(listify(shirt_sizes.get(shirt_key, c.SIZE_UNKNOWN))[0]) picked_up.append('{a.full_name} (badge {a.badge_num}): {a.merch}'.format(a=attendee)) session.add(MerchPickup(picked_up_by=picker_upper, picked_up_for=attendee)) session.commit() return { 'message': message, 'picked_up': picked_up, 'picker_upper': picker_upper }
def get_unpaid_promo_code_uses_count(cls, id, already_counted_attendee_ids=None): attendees_with_promo_code = set() if already_counted_attendee_ids: attendees_with_promo_code.update(listify(already_counted_attendee_ids)) promo_code_count = 0 targets = [t for t in cls.unpaid_preregs.values() if '_model' in t] for target in targets: if target['_model'] == 'Attendee': if target.get('id') not in attendees_with_promo_code \ and target.get('promo_code') \ and target['promo_code'].get('id') == id: attendees_with_promo_code.add(target.get('id')) promo_code_count += 1 elif target['_model'] == 'Group': for attendee in target.get('attendees', []): if attendee.get('id') not in attendees_with_promo_code \ and attendee.get('promo_code') \ and attendee['promo_code'].get('id') == id: attendees_with_promo_code.add(attendee.get('id')) promo_code_count += 1 elif target['_model'] == 'PromoCode' and target.get('id') == id: # Should never get here promo_code_count += 1 return promo_code_count
def new(self, session, message='', **params): if params.get('id', 'None') != 'None': raise HTTPRedirect('form?id={}', params['id']) if 'advance_notices' in params: ns = listify(params.get('advance_notices', [])) params['advance_notices'] = [int(n) for n in ns if n != ''] admin_account = session.current_admin_account() attraction = session.attraction(params, bools=Attraction.all_bools, checkgroups=Attraction.all_checkgroups) if not attraction.department_id: attraction.department_id = None if cherrypy.request.method == 'POST': message = check(attraction) if not message: attraction.owner = admin_account session.add(attraction) raise HTTPRedirect('form?id={}', attraction.id) return { 'admin_account': admin_account, 'attraction': attraction, 'message': message, }
def options(options, default='""'): """ We do need to accomodate explicitly passing in other options though (include None), so make sure to check all the client calls for that info. """ if isinstance(default, datetime): default = default.astimezone(c.EVENT_TIMEZONE) results = [] for opt in options: if len(listify(opt)) == 1: opt = [opt, opt] val, desc = opt if isinstance(val, datetime): selected = 'selected="selected"' if val == default else '' val = val.strftime(c.TIMESTAMP_FORMAT) else: selected = 'selected="selected"' if str(val) == str( default) else '' val = html.escape(str(val), quote=False).replace('"', '"').replace('\n', '') desc = html.escape(str(desc), quote=False).replace('"', '"').replace('\n', '') results.append('<option value="{}" {}>{}</option>'.format( val, selected, desc)) return safe_string('\n'.join(results))
def new(self, session, message='', **params): if params.get('id', 'None') != 'None': raise HTTPRedirect('form?id={}', params['id']) if 'advance_notices' in params: ns = listify(params.get('advance_notices', [])) params['advance_notices'] = [int(n) for n in ns if n != ''] admin_account = session.current_admin_account() attraction = session.attraction( params, bools=Attraction.all_bools, checkgroups=Attraction.all_checkgroups) if not attraction.department_id: attraction.department_id = None if cherrypy.request.method == 'POST': message = check(attraction) if not message: attraction.owner = admin_account session.add(attraction) raise HTTPRedirect('form?id={}', attraction.id) return { 'admin_account': admin_account, 'attraction': attraction, 'message': message, }
def access_groups_ids(self, value): values = set(s for s in listify(value) if s) for group in list(self.access_groups): if group.id not in values: # Manually remove the group to ensure the associated # rows in the admin_access_group table are deleted. self.access_groups.remove(group) self._set_relation_ids('access_groups', AccessGroup, list(values))
def index(self, message=''): if not Charge.unpaid_preregs: raise HTTPRedirect('form?message={}', message) if message else HTTPRedirect('form') else: return { 'message': message, 'charge': Charge(listify(Charge.unpaid_preregs.values())) }
def send_reviews(self, session, game_id, review_id=None): game = session.indie_game(id=game_id) for review in game.reviews: if review.id in listify(review_id): review.send_to_studio = True elif review.send_to_studio: review.send_to_studio = False session.add(review) raise HTTPRedirect('game_results?id={}&message={}', game_id, 'Reviews marked for sending!')
def __init__(self, targets=(), amount=None, description=None, receipt_email=None): self._targets = listify(targets) self._amount = amount self._description = description self._receipt_email = receipt_email
def form(self, session, message='', panelists=(), **params): event = session.event(params, allowed=['location', 'start_time']) if 'name' in params: session.add(event) # Associate a panel app with this event, and if the event is new, use the panel app's name and title if 'panel_id' in params and params['panel_id']: add_panel = session.panel_application(id=params['panel_id']) add_panel.event_id = event.id session.add(add_panel) if event.is_new: event.name = add_panel.name event.description = add_panel.description for pa in add_panel.applicants: if pa.attendee_id: assigned_panelist = AssignedPanelist( attendee_id=pa.attendee.id, event_id=event.id) session.add(assigned_panelist) message = check(event) if not message: new_panelist_ids = set(listify(panelists)) old_panelist_ids = { ap.attendee_id for ap in event.assigned_panelists } for ap in event.assigned_panelists: if ap.attendee_id not in new_panelist_ids: session.delete(ap) for attendee_id in new_panelist_ids: if attendee_id not in old_panelist_ids: attendee = session.attendee(id=attendee_id) session.add( AssignedPanelist(event=event, attendee=attendee)) raise HTTPRedirect('edit#{}', event.start_slot and (event.start_slot - 1)) assigned_panelists = sorted(event.assigned_panelists, reverse=True, key=lambda a: a.attendee.first_name) all_panelists = session.query(Attendee).filter( or_(Attendee.ribbon.contains(c.PANELIST_RIBBON), Attendee.badge_type == c.GUEST_BADGE)).order_by( Attendee.full_name).all() approved_panel_apps = session.query(PanelApplication).filter( PanelApplication.status == c.ACCEPTED).order_by('applied') return { 'message': message, 'event': event, 'assigned': [ap.attendee_id for ap in assigned_panelists], 'panelists': [(a.id, a.full_name) for a in all_panelists], 'approved_panel_apps': approved_panel_apps }
def signups_requiring_notification(self, session, from_time, to_time, options=None): """ Returns a dict of AttractionSignups that require notification. The keys of the returned dict are the amount of advanced notice, given in seconds. A key of -1 indicates confirmation notices after a signup. The query generated by this method looks horrific, but is surprisingly efficient. """ advance_checkin = max(0, self.advance_checkin) subqueries = [] for advance_notice in sorted(set([-1] + self.advance_notices)): event_filters = [AttractionEvent.attraction_id == self.id] if advance_notice == -1: notice_ident = cast(AttractionSignup.attraction_event_id, UnicodeText) notice_param = bindparam( 'confirm_notice', advance_notice).label('advance_notice') else: advance_notice = max(0, advance_notice) + advance_checkin notice_delta = timedelta(seconds=advance_notice) event_filters += [ AttractionEvent.start_time >= from_time + notice_delta, AttractionEvent.start_time < to_time + notice_delta ] notice_ident = func.concat( AttractionSignup.attraction_event_id, '_{}'.format(advance_notice)) notice_param = bindparam( 'advance_notice_{}'.format(advance_notice), advance_notice).label('advance_notice') subquery = session.query(AttractionSignup, notice_param).filter( AttractionSignup.is_unchecked_in, AttractionSignup.attraction_event_id.in_( session.query(AttractionEvent.id).filter(*event_filters)), not_(exists().where( and_( AttractionNotification.ident == notice_ident, AttractionNotification.attraction_event_id == AttractionSignup.attraction_event_id, AttractionNotification.attendee_id == AttractionSignup.attendee_id)))).with_labels() subqueries.append(subquery) query = subqueries[0].union(*subqueries[1:]) if options: query = query.options(*listify(options)) query.order_by(AttractionSignup.id) return groupify(query, lambda x: x[0], lambda x: x[1])
def index(self, session, message=''): if not Charge.unpaid_preregs: raise HTTPRedirect('form?message={}', message) if message else HTTPRedirect('form') else: charge = Charge(listify(Charge.unpaid_preregs.values())) for attendee in charge.attendees: if attendee.promo_code and attendee.promo_code.group: attendee.group_name = session.query(PromoCode).filter_by( code=attendee.promo_code.code).first().group.name return {'message': message, 'charge': charge}
def process_bind_param(self, value, dialect): """ Our MultiChoice options may be in one of three forms: a single string, a single integer, or a list of strings. We want to end up with a single comma-separated string. We also want to make sure an object has only unique values in its MultiChoice columns. Therefore, we listify() the object to make sure it's in list form, we convert it to a set to make all the values unique, and we map the values inside it to strings before joining them with commas because the join function can't handle a list of integers. """ return ','.join(map(str, list(set(listify(value))))) if value else ''
def normalize_data(data, count=1): """ Normalizes a CrudApi data parameter. A singular data can be a string, a list of strings, or a dict:: 'attr' ['attr1', 'attr2'] {'attr1':True, 'attr2':True} A plural data must be specified as a list of lists or a list of dicts:: [['attr1', 'attr2'], ['attr1', 'attr2']] [{'attr1':True, 'attr2':True}, {'attr1':True, 'attr2':True}] Note that if data is specified as a list of strings, it is considered to be singular. Only a list of lists or a list of dicts is considered plural. Returns the plural form of data as the comprehensive form of a list of dictionaries mapping <keyname> to True, extended to count length. If a singular data is given, the result will be padded by repeating that value. If a plural data is given, it will be padded with None, for example:: >>> normalize_data('attr', 1) [{'attr': True}] >>> normalize_data('attr', 3) [{'attr': True}, {'attr': True}, {'attr': True}] """ if not data: return listify(None, minlen=count) if isinstance(data, six.string_types): data = [{data: True}] elif isinstance(data, collections.Mapping): data = [data] elif isinstance(data, collections.Iterable): if any(isinstance(element, six.string_types) for element in data): data = [data] data = [mappify(v) for v in data] else: raise TypeError('Unknown datatype: {}: {!r}', type(data), data) if len(data) < count: if len(data) == 1: data.extend([deepcopy(data[0]) for i in range(count - len(data))]) else: data.extend([None for i in range(count - len(data))]) return data
def convert_if_labels(self, value): try: int(listify(value)[0]) except ValueError: # This is a string list, is it the labels? label_lookup = {val: key for key, val in self.choices} try: vals = [label_lookup[label] for label in re.split('; |, |\*|\n| / ',value)] except KeyError: # It's probably just a string list of the int values return value value = ','.join(map(str, vals)) return value
def index(self, session, message=''): if not Charge.unpaid_preregs: raise HTTPRedirect('form?message={}', message) if message else HTTPRedirect('form') else: charge = Charge(listify(Charge.unpaid_preregs.values())) for attendee in charge.attendees: if attendee.promo_code: real_code = session.query(PromoCode).filter_by(code=attendee.promo_code.code).first() if real_code and real_code.group: attendee.group_name = real_code.group.name return { 'message': message, 'charge': charge }
def process_free_prereg(self, session): charge = Charge(listify(Charge.unpaid_preregs.values())) if charge.total_cost <= 0: for attendee in charge.attendees: session.add(attendee) for group in charge.groups: session.add(group) Charge.unpaid_preregs.clear() Charge.paid_preregs.extend(charge.targets) raise HTTPRedirect('paid_preregistrations?payment_received={}', charge.dollar_amount) else: message = "These badges aren't free! Please pay for them." raise HTTPRedirect('index?message={}', message)
def remove_opt(opts, other): """ Remove an option from an _ints property, converting it to a comma-separated string. This is for use with our MultiChoice columns. Args: opts: An integer or list of integers, such as when using attendee.ribbon_ints other: An integer to remove, such as c.VOLUNTEER_RIBBON Returns: A comma-separated string representing all options in the list, with the option removed. """ other = listify(other) if other else [] return ','.join(map(str, set(opts).difference(other)))
def add_opt(opts, other): """ Add an option to an integer or list of integers, converting it to a comma-separated string. This is for use with our MultiChoice columns. Args: opts: An integer or list of integers, such as when using attendee.ribbon_ints other: An integer to add, such as c.VOLUNTEER_RIBBON Returns: A comma-separated string representing all options in the list, with the new option added. """ other = listify(other) if other else [] opts.extend(other) return ','.join(set(map(str, opts)))
def check_all(models, *, prereg=False): """ Runs all default validations against multiple model instances. Args: models (list): A single model instance or a list of instances. prereg (bool): True if this is an ephemeral model used in the preregistration workflow. Returns: str: None for success, or the first failure message encountered. """ models = listify(models) if models else [] for model in models: message = check(model, prereg=prereg) if message: return message
def form(self, session, message='', panelists=(), **params): event = session.event(params, allowed=['location', 'start_time']) if 'name' in params: session.add(event) # Associate a panel app with this event, and if the event is new, use the panel app's name and title if 'panel_id' in params and params['panel_id']: add_panel = session.panel_application(id=params['panel_id']) add_panel.event_id = event.id session.add(add_panel) if event.is_new: event.name = add_panel.name event.description = add_panel.description for pa in add_panel.applicants: if pa.attendee_id: assigned_panelist = AssignedPanelist(attendee_id=pa.attendee.id, event_id=event.id) session.add(assigned_panelist) message = check(event) if not message: new_panelist_ids = set(listify(panelists)) old_panelist_ids = {ap.attendee_id for ap in event.assigned_panelists} for ap in event.assigned_panelists: if ap.attendee_id not in new_panelist_ids: session.delete(ap) for attendee_id in new_panelist_ids: if attendee_id not in old_panelist_ids: attendee = session.attendee(id=attendee_id) session.add(AssignedPanelist(event=event, attendee=attendee)) raise HTTPRedirect('edit#{}', event.start_slot and (event.start_slot - 1)) assigned_panelists = sorted(event.assigned_panelists, reverse=True, key=lambda a: a.attendee.first_name) approved_panel_apps = session.query(PanelApplication).filter( PanelApplication.status == c.ACCEPTED, PanelApplication.event_id == None).order_by('applied') # noqa: E711 return { 'message': message, 'event': event, 'assigned': [ap.attendee_id for ap in assigned_panelists], 'panelists': [(a.id, a.full_name) for a in session.all_panelists()], 'approved_panel_apps': approved_panel_apps }
def send_email(source, dest, subject, body, format='text', cc=(), bcc=(), model=None, ident=None): subject = subject.format(EVENT_NAME=c.EVENT_NAME) to, cc, bcc = map(listify, [dest, cc, bcc]) ident = ident or subject if c.DEV_BOX: for xs in [to, cc, bcc]: xs[:] = [email for email in xs if _is_dev_email(email)] if c.SEND_EMAILS and to: msg_kwargs = {'bodyText' if format == 'text' else 'bodyHtml': body} message = EmailMessage(subject=subject, **msg_kwargs) AmazonSES(c.AWS_ACCESS_KEY, c.AWS_SECRET_KEY).sendEmail(source=source, toAddresses=to, ccAddresses=cc, bccAddresses=bcc, message=message) sleep(0.1) # Avoid hitting rate limit else: log.error('email sending turned off, so unable to send {}', locals()) if model and dest: body = body.decode('utf-8') if isinstance(body, bytes) else body if model == 'n/a': fk_kwargs = {'model': 'n/a'} else: fk_kwargs = {'fk_id': model.id, 'model': model.__class__.__name__} _record_email_sent( uber.models.email.Email(subject=subject, dest=','.join(listify(dest)), body=body, ident=ident, **fk_kwargs))
def __init__(self, model, subject, template, filter, ident, *, when=(), sender=None, extra_data=None, cc=None, bcc=None, post_con=False, needs_approval=True, allow_during_con=False): self.subject = subject.format(EVENT_NAME=c.EVENT_NAME, EVENT_DATE=c.EPOCH.strftime("(%b %Y)")) self.ident = ident self.model = model assert self.ident, 'error: automated email ident may not be empty.' assert self.ident not in self.instances, \ 'error: automated email ident "{}" is registered twice.'.format(self.ident) self.instances[self.ident] = self self.instances_by_model[self.model].append(self) self.template, self.needs_approval, self.allow_during_con = template, needs_approval, allow_during_con self.cc = cc or [] self.bcc = bcc or [] self.extra_data = extra_data or {} self.sender = sender or c.REGDESK_EMAIL self.when = listify(when) assert filter is not None if post_con: self.filter = lambda model_inst: c.POST_CON and filter(model_inst) else: self.filter = lambda model_inst: not c.POST_CON and filter( model_inst)
def signups_requiring_notification(self, session, from_time, to_time, options=None): """ Returns a dict of AttractionSignups that require notification. The keys of the returned dict are the amount of advanced notice, given in seconds. A key of -1 indicates confirmation notices after a signup. The query generated by this method looks horrific, but is surprisingly efficient. """ advance_checkin = max(0, self.advance_checkin) subqueries = [] for advance_notice in sorted(set([-1] + self.advance_notices)): event_filters = [AttractionEvent.attraction_id == self.id] if advance_notice == -1: notice_ident = cast(AttractionSignup.attraction_event_id, UnicodeText) notice_param = bindparam('confirm_notice', advance_notice).label('advance_notice') else: advance_notice = max(0, advance_notice) + advance_checkin notice_delta = timedelta(seconds=advance_notice) event_filters += [ AttractionEvent.start_time >= from_time + notice_delta, AttractionEvent.start_time < to_time + notice_delta] notice_ident = func.concat(AttractionSignup.attraction_event_id, '_{}'.format(advance_notice)) notice_param = bindparam( 'advance_notice_{}'.format(advance_notice), advance_notice).label('advance_notice') subquery = session.query(AttractionSignup, notice_param).filter( AttractionSignup.is_unchecked_in, AttractionSignup.attraction_event_id.in_( session.query(AttractionEvent.id).filter(*event_filters)), not_(exists().where(and_( AttractionNotification.ident == notice_ident, AttractionNotification.attraction_event_id == AttractionSignup.attraction_event_id, AttractionNotification.attendee_id == AttractionSignup.attendee_id)))).with_labels() subqueries.append(subquery) query = subqueries[0].union(*subqueries[1:]) if options: query = query.options(*listify(options)) query.order_by(AttractionSignup.id) return groupify(query, lambda x: x[0], lambda x: x[1])
def emails_by_interest_csv(self, out, session, **params): """ Generate a list of emails of attendees who match one of c.INTEREST_OPTS (interests are like "LAN", "music", "gameroom", etc) This is intended for use to export emails to a third-party email system, like MadMimi or Mailchimp """ if 'interests' not in params: raise HTTPRedirect('emails_by_interest?message={}', 'You must select at least one interest') interests = [int(i) for i in listify(params['interests'])] assert all(k in c.INTERESTS for k in interests) attendees = session.query(Attendee).filter_by(can_spam=True).order_by('email').all() out.writerow(["fullname", "email", "zipcode"]) for a in attendees: if set(interests).intersection(a.interests_ints): out.writerow([a.full_name, a.email, a.zip_code])
def hotel_requests(self, session, message='', **params): team = session.logged_in_mits_team() if cherrypy.request.method == 'POST': for applicant in team.applicants: applicant.declined_hotel_space = '{}-declined'.format( applicant.id) in params applicant.requested_room_nights = ','.join( listify(params.get('{}-night'.format(applicant.id), []))) if not applicant.declined_hotel_space and not applicant.requested_room_nights: message = '{} must either declined hotel space or ' \ 'indicate which room nights they need'.format(applicant.full_name) break elif applicant.declined_hotel_space and applicant.requested_room_nights: message = '{} cannot both decline hotel space and ' \ 'request specific room nights'.format(applicant.full_name) break if not message: raise HTTPRedirect('index?message={}', 'Room nights uploaded') return {'team': team, 'message': message}
def _collect_models(cls, query): models = set() for d in listify(query): try: model = Session.resolve_model(d['_model']) except Exception: log.debug('unable to resolve model {} in query {}', d.get('_model'), d) else: models.add(model) for attr_name in _collect_fields(d): curr_model = model for prop_name in attr_name.split('.'): if hasattr(curr_model, prop_name): prop = getattr(curr_model, prop_name) if isinstance( prop, InstrumentedAttribute) and hasattr( prop.property, 'mapper'): curr_model = prop.property.mapper.class_ models.update([curr_model]) if prop_name in d: subquery = deepcopy(d[prop_name]) if isinstance(subquery, (list, set, tuple)) \ and not filter(lambda x: isinstance(x, dict), subquery): subquery = { i: True for i in subquery } elif isinstance( subquery, six.string_types): subquery = {subquery: True} if isinstance(subquery, dict): subquery[ '_model'] = curr_model.__name__ models.update( cls._collect_models(subquery)) else: break return models
def _extract_sort_field(model, value, index=0): field = None fields = listify(value) for f in fields: if isinstance(f, six.string_types): parts = f.split('.') if len(parts) == 1 and field is None: if not model or (model and hasattr(model, parts[0])): field = parts[0] elif len(parts) > 1 and model and parts[0] == model.__name__: field = parts[1] else: field = f if field and isinstance(field, six.string_types) and model: attr = getattr(model, field) if not (isinstance(attr, InstrumentedAttribute) and isinstance(attr.property, ColumnProperty)) \ and not isinstance(attr, ClauseElement): raise ValueError( 'SQLAlchemy model classes may only be sorted by columns that exist in the database. ' 'Provided: {}.{}'.format(model.__name__, field)) return field or 'id'
def emails_by_interest_csv(self, out, session, **params): """ Generate a list of emails of attendees who match one of c.INTEREST_OPTS (interests are like "LAN", "music", "gameroom", etc) This is intended for use to export emails to a third-party email system, like MadMimi or Mailchimp """ if 'interests' not in params: raise HTTPRedirect('emails_by_interest?message={}', 'You must select at least one interest') interests = [int(i) for i in listify(params['interests'])] assert all(k in c.INTERESTS for k in interests) attendees = session.query(Attendee).filter_by( can_spam=True).order_by('email').all() out.writerow(["fullname", "email", "zipcode"]) for a in attendees: if set(interests).intersection(a.interests_ints): out.writerow([a.full_name, a.email, a.zip_code])
def hotel_requests(self, session, message='', **params): team = session.logged_in_mits_team() if cherrypy.request.method == 'POST': for applicant in team.applicants: applicant.declined_hotel_space = '{}-declined'.format(applicant.id) in params applicant.requested_room_nights = ','.join(listify(params.get('{}-night'.format(applicant.id), []))) if not applicant.declined_hotel_space and not applicant.requested_room_nights: message = '{} must either declined hotel space or ' \ 'indicate which room nights they need'.format(applicant.full_name) break elif applicant.declined_hotel_space and applicant.requested_room_nights: message = '{} cannot both decline hotel space and ' \ 'request specific room nights'.format(applicant.full_name) break if not message: raise HTTPRedirect('index?message={}', 'Room nights uploaded') return { 'team': team, 'message': message }
def options(options, default='""'): """ We do need to accomodate explicitly passing in other options though (include None), so make sure to check all the client calls for that info. """ if isinstance(default, datetime): default = default.astimezone(c.EVENT_TIMEZONE) results = [] for opt in options: if len(listify(opt)) == 1: opt = [opt, opt] val, desc = opt if isinstance(val, datetime): selected = 'selected="selected"' if val == default else '' val = val.strftime(c.TIMESTAMP_FORMAT) else: selected = 'selected="selected"' if str(val) == str(default) else '' val = html.escape(str(val), quote=False).replace('"', '"').replace('\n', '') desc = html.escape(str(desc), quote=False).replace('"', '"').replace('\n', '') results.append('<option value="{}" {}>{}</option>'.format(val, selected, desc)) return safe_string('\n'.join(results))
def form(self, session, message='', **params): attraction_id = params.get('id') if not attraction_id or attraction_id == 'None': raise HTTPRedirect('index') if cherrypy.request.method == 'POST': if 'advance_notices' in params: ns = listify(params.get('advance_notices', [])) params['advance_notices'] = [int(n) for n in ns if n != ''] attraction = session.attraction( params, bools=Attraction.all_bools, checkgroups=Attraction.all_checkgroups) message = check(attraction) if not message: if not attraction.department_id: attraction.department_id = None session.add(attraction) raise HTTPRedirect( 'form?id={}&message={}', attraction.id, '{} updated successfully'.format(attraction.name)) else: attraction = session.query(Attraction) \ .filter_by(id=attraction_id) \ .options( subqueryload(Attraction.department), subqueryload(Attraction.features) .subqueryload(AttractionFeature.events) .subqueryload(AttractionEvent.attendees)) \ .order_by(Attraction.id).one() return { 'admin_account': session.current_admin_account(), 'message': message, 'attraction': attraction }
def confirm_attendees(self, session, badge_type, admin_notes, target_server, api_token, query, attendee_ids): if cherrypy.request.method != 'POST': raise HTTPRedirect('attendees?target_server={}&api_token={}&query={}', target_server, api_token, query) target_url, target_host, remote_api_token = _format_import_params(target_server, api_token) results = {} try: uri = '{}/jsonrpc/'.format(target_url) service = ServerProxy(uri=uri, extra_headers={'X-Auth-Token': remote_api_token}) results = service.attendee.export(query=','.join(listify(attendee_ids)), full=True) except Exception as ex: raise HTTPRedirect( 'attendees?target_server={}&api_token={}&query={}&message={}', target_server, remote_api_token, query, str(ex)) depts = {} def _guess_dept(id_name): id, name = id_name if id in depts: return (id, depts[id]) dept = session.query(Department).filter(or_( Department.id == id, Department.normalized_name == Department.normalize_name(name))).first() if dept: depts[id] = dept return (id, dept) return None badge_type = int(badge_type) badge_label = c.BADGES[badge_type].lower() attendees = results.get('attendees', []) for d in attendees: import_from_url = '{}/registration/form?id={}\n\n'.format(target_url, d['id']) new_admin_notes = '{}\n\n'.format(admin_notes) if admin_notes else '' old_admin_notes = 'Old Admin Notes:\n{}\n'.format(d['admin_notes']) if d['admin_notes'] else '' d.update({ 'badge_type': badge_type, 'badge_status': c.NEW_STATUS, 'paid': c.NEED_NOT_PAY, 'placeholder': True, 'requested_hotel_info': True, 'admin_notes': 'Imported {} from {}{}{}'.format( badge_label, import_from_url, new_admin_notes, old_admin_notes), 'past_years': d['all_years'], }) del d['id'] del d['all_years'] if badge_type != c.STAFF_BADGE: attendee = Attendee().apply(d, restricted=False) else: assigned_depts = {d[0]: d[1] for d in map(_guess_dept, d.pop('assigned_depts', {}).items()) if d} checklist_admin_depts = d.pop('checklist_admin_depts', {}) dept_head_depts = d.pop('dept_head_depts', {}) poc_depts = d.pop('poc_depts', {}) requested_depts = d.pop('requested_depts', {}) d.update({ 'staffing': True, 'ribbon': str(c.DEPT_HEAD_RIBBON) if dept_head_depts else '', }) attendee = Attendee().apply(d, restricted=False) for id, dept in assigned_depts.items(): attendee.dept_memberships.append(DeptMembership( department=dept, attendee=attendee, is_checklist_admin=bool(id in checklist_admin_depts), is_dept_head=bool(id in dept_head_depts), is_poc=bool(id in poc_depts), )) requested_anywhere = requested_depts.pop('All', False) requested_depts = {d[0]: d[1] for d in map(_guess_dept, requested_depts.items()) if d} if requested_anywhere: attendee.dept_membership_requests.append(DeptMembershipRequest(attendee=attendee)) for id, dept in requested_depts.items(): attendee.dept_membership_requests.append(DeptMembershipRequest( department=dept, attendee=attendee, )) session.add(attendee) attendee_count = len(attendees) raise HTTPRedirect( 'attendees?target_server={}&api_token={}&query={}&message={}', target_server, api_token, query, '{count} attendee{s} imported with {a}{badge_label} badge{s}'.format( count=attendee_count, s=pluralize(attendee_count), a=pluralize(attendee_count, singular='an ' if badge_label.startswith('a') else 'a ', plural=''), badge_label=badge_label, ) )
def confirm_staff(self, session, target_server, api_token, query, attendee_ids): if cherrypy.request.method != 'POST': raise HTTPRedirect('staff?target_server={}&api_token={}&query={}', target_server, api_token, query) target_url = _server_to_url(target_server) results = {} try: uri = '{}/jsonrpc/'.format(target_url) service = ServerProxy( uri=uri, extra_headers={'X-Auth-Token': api_token.strip()}) results = service.attendee.export(query=','.join( listify(attendee_ids)), full=True) except Exception as ex: raise HTTPRedirect( 'staff?target_server={}&api_token={}&query={}&message={}', target_server, api_token, query, str(ex)) depts = {} def _guess_dept(id_name): id, name = id_name if id in depts: return (id, depts[id]) dept = session.query(Department).filter( or_( Department.id == id, Department.normalized_name == Department.normalize_name(name))).first() if dept: depts[id] = dept return (id, dept) return None attendees = results.get('attendees', []) for d in attendees: import_from_url = '{}/registration/form?id={}'.format( target_url, d['id']) old_admin_notes = '\n\nOld Admin Notes:\n{}'.format( d['admin_notes']) if d['admin_notes'] else '' assigned_depts = { d[0]: d[1] for d in map(_guess_dept, d.pop('assigned_depts', {}).items()) if d } checklist_admin_depts = d.pop('checklist_admin_depts', {}) dept_head_depts = d.pop('dept_head_depts', {}) poc_depts = d.pop('poc_depts', {}) requested_depts = d.pop('requested_depts', {}) d.update({ 'badge_type': c.STAFF_BADGE, 'paid': c.NEED_NOT_PAY, 'placeholder': True, 'staffing': True, 'requested_hotel_info': True, 'admin_notes': 'Imported staff from {}{}'.format(import_from_url, old_admin_notes), 'ribbon': str(c.DEPT_HEAD_RIBBON) if dept_head_depts else '', 'past_years': d['all_years'], }) del d['id'] del d['all_years'] attendee = Attendee().apply(d, restricted=False) for id, dept in assigned_depts.items(): attendee.dept_memberships.append( DeptMembership( department=dept, attendee=attendee, is_checklist_admin=bool(id in checklist_admin_depts), is_dept_head=bool(id in dept_head_depts), is_poc=bool(id in poc_depts), )) requested_anywhere = requested_depts.pop('All', False) requested_depts = { d[0]: d[1] for d in map(_guess_dept, requested_depts.items()) if d } if requested_anywhere: attendee.dept_membership_requests.append( DeptMembershipRequest(attendee=attendee)) for id, dept in requested_depts.items(): attendee.dept_membership_requests.append( DeptMembershipRequest( department=dept, attendee=attendee, )) session.add(attendee) attendee_count = len(attendees) raise HTTPRedirect( 'staff?target_server={}&api_token={}&query={}&message={}', target_server, api_token, query, '{} attendee{} imported'.format(attendee_count, pluralize(attendee_count)))
def JSONColumnMixin(column_name, fields, admin_only=False): """ Creates a new mixin class with a JSON column named column_name. The newly created mixin class will have a SQLAlchemy JSON Column, named `column_name`, along with two other attributes column_name_fields and column_name_qualified_fields which describe the fields that the JSON Column is expected to hold. For example:: >>> SocialMediaMixin = JSONColumnMixin('social_media', ['Twitter', 'LinkedIn']) >>> SocialMediaMixin.social_media # doctest: +ELLIPSIS Column('social_media', JSON(), ... server_default=DefaultClause('{}', for_update=False)) >>> SocialMediaMixin._social_media_fields OrderedDict([('twitter', 'Twitter'), ('linked_in', 'LinkedIn')]) >>> SocialMediaMixin._social_media_qualified_fields OrderedDict([('social_media__twitter', 'twitter'), ('social_media__linked_in', 'linked_in')]) Instances of the newly created mixin class have convenience accessors for the attributes defined by `fields`, both directly and using their fully qualified forms:: >>> sm = SocialMediaMixin() >>> sm.twitter = 'https://twitter.com/MAGFest' # Get and set "twitter" directly >>> sm.twitter 'https://twitter.com/MAGFest' >>> sm.social_media__twitter # Get and set qualified "social_media__twitter" 'https://twitter.com/MAGFest' >>> sm.social_media__twitter = '@MAGFest' >>> sm.social_media__twitter '@MAGFest' >>> sm.social_media # Actual column updated appropriately {'linked_in': '', 'twitter': '@MAGFest'} Args: column_name (str): The name of the column. fields (list): A list of field names you expect the column to hold. This can be: - A single string, if you're only expecting the column to hold a single field. - A list of strings, which will be treated as the column labels, and converted from CamelCase to under_score for the fields. - A map of {string: string}, which will be treated as a mapping of field names to field labels. Returns: type: A new mixin class with a JSON column named column_name. """ fields_name = '_{}_fields'.format(column_name) qualified_fields_name = '_{}_qualified_fields'.format(column_name) if isinstance(fields, Mapping): fields = OrderedDict([(fieldify(k), v) for k, v in fields.items()]) else: fields = OrderedDict([(fieldify(s), s) for s in listify(fields)]) qualified_fields = OrderedDict([(column_name + '__' + s, s) for s in fields.keys()]) column = Column(column_name, JSON, default={}, server_default='{}') attrs = { column_name: column, fields_name: fields, qualified_fields_name: qualified_fields } _Mixin = type(camel(column_name) + 'Mixin', (object,), attrs) def _Mixin__init__(self, *args, **kwargs): setattr(self, column_name, {}) for attr in getattr(self.__class__, fields_name).keys(): setattr(self, attr, kwargs.pop(attr, '')) super(_Mixin, self).__init__(*args, **kwargs) _Mixin.__init__ = _Mixin__init__ def _Mixin__declare_last__(cls): setattr(getattr(cls, column_name), 'admin_only', admin_only) column = cls.__table__.columns.get(column_name) setattr(column, 'admin_only', admin_only) _Mixin.__declare_last__ = classmethod(_Mixin__declare_last__) def _Mixin__unqualify(cls, name): if name in getattr(cls, qualified_fields_name): return getattr(cls, qualified_fields_name)[name] else: return name _Mixin.unqualify = classmethod(_Mixin__unqualify) def _Mixin__getattr__(self, name): name = self.unqualify(name) if name in getattr(self.__class__, fields_name): return getattr(self, column_name).get(name, '') else: return super(_Mixin, self).__getattr__(name) _Mixin.__getattr__ = _Mixin__getattr__ def _Mixin__setattr__(self, name, value): name = self.unqualify(name) if name in getattr(self.__class__, fields_name): fields = getattr(self, column_name) if fields.get(name) != value: fields[name] = value super(_Mixin, self).__setattr__(column_name, dict(fields)) else: super(_Mixin, self).__setattr__(name, value) _Mixin.__setattr__ = _Mixin__setattr__ return _Mixin
def send_email( sender, to, subject, body, format='text', cc=(), bcc=(), model=None, ident=None, automated_email=None, session=None): to, cc, bcc = map(lambda x: listify(x if x else []), [to, cc, bcc]) original_to, original_cc, original_bcc = to, cc, bcc ident = ident or subject if c.DEV_BOX: to, cc, bcc = map(lambda xs: list(filter(_is_dev_email, xs)), [to, cc, bcc]) if c.SEND_EMAILS and to: msg_kwargs = {'bodyText' if format == 'text' else 'bodyHtml': body} message = EmailMessage(subject=subject, **msg_kwargs) AmazonSES(c.AWS_ACCESS_KEY, c.AWS_SECRET_KEY).sendEmail( source=sender, toAddresses=to, ccAddresses=cc, bccAddresses=bcc, message=message) sleep(0.1) # Avoid hitting rate limit else: log.error('Email sending turned off, so unable to send {}', locals()) if original_to: body = body.decode('utf-8') if isinstance(body, bytes) else body if isinstance(model, MagModel): fk_kwargs = {'fk_id': model.id, 'model': model.__class__.__name__} elif isinstance(model, Mapping): fk_kwargs = {'fk_id': model.get('id', None), 'model': model.get('_model', model.get('__type__', 'n/a'))} else: fk_kwargs = {'model': 'n/a'} if automated_email: if isinstance(automated_email, MagModel): fk_kwargs['automated_email_id'] = automated_email.id elif isinstance(model, Mapping): fk_kwargs['automated_email_id'] = automated_email.get('id', None) email = Email( subject=subject, body=body, sender=sender, to=','.join(original_to), cc=','.join(original_cc), bcc=','.join(original_bcc), ident=ident, **fk_kwargs) session = session or getattr(model, 'session', getattr(automated_email, 'session', None)) if session: session.add(email) else: with Session() as session: session.add(email)
def _render_empty(template_name_list): if listify(template_name_list)[0].endswith('.txt'): return '{{ attendee.full_name }}\n{{ c.EVENT_NAME }}\n{{ extra_data }}' return '<html><body>{{ attendee.full_name }}<br>{{ c.EVENT_NAME }}<br>{{ extra_data }}</body></html>'