def advance_checkin_label(self): if self.advance_checkin < 0: return 'anytime during the event' return humanize_timedelta(seconds=self.advance_checkin, separator=' ', now='by the time the event starts', prefix='at least ', suffix=' before the event starts')
def advance_checkin_label(self): if self.advance_checkin < 0: return 'anytime during the event' return humanize_timedelta( seconds=self.advance_checkin, separator=' ', now='by the time the event starts', prefix='at least ', suffix=' before the event starts')
def checklist_completion(self, out, session): header_row = ['Studio'] for key, val in c.MIVS_CHECKLIST.items(): header_row.append(val['name']) header_row.append('Past Due?') out.writerow(header_row) for studio in session.query(IndieStudio).join(IndieStudio.group).join(Group.guest): row = [studio.name] for key, val in c.MIVS_CHECKLIST.items(): row.extend([ 'Not Completed' if getattr(studio, key + "_status", None) is None else getattr(studio, key + "_status"), 'No' if localized_now() <= studio.checklist_deadline(key) else humanize_timedelta(studio.past_checklist_deadline(key), granularity='hours'), ]) out.writerow(row)
def test_humanize_timedelta(self, test_args, test_kwargs, expected): assert expected == humanize_timedelta(*test_args, **test_kwargs)
def send_attraction_notifications(session): for attraction in session.query(Attraction): now = datetime.now(pytz.UTC) from_time = now - timedelta(seconds=300) to_time = now + timedelta(seconds=300) signups = attraction.signups_requiring_notification( session, from_time, to_time, [ subqueryload(AttractionSignup.attendee).subqueryload( Attendee.attraction_notifications), subqueryload(AttractionSignup.event).subqueryload( AttractionEvent.feature) ]) for signup, advance_notices in signups.items(): attendee = signup.attendee if not attendee.first_name or not attendee.email: try: log.error('ERROR: Unassigned attendee signed up for an ' 'attraction, deleting signup:\n' '\tAttendee.id: {}\n' '\tAttraction.id: {}\n' '\tAttractionEvent.id: {}\n' '\tAttractionSignup.id: {}'.format( attendee.id, signup.attraction_id, signup.attraction_event_id, signup.id)) session.delete(signup) session.commit() except: log.error( 'ERROR: Failed to delete signup with ' 'unassigned attendee', exc_info=True) continue # The first time someone signs up for an attractions, they always # receive the welcome email (even if they've chosen SMS or None # for their notification prefs). If they've chosen to receive SMS # notifications, they'll also get a text message. is_first_signup = not (attendee.attraction_notifications) if not is_first_signup and \ attendee.notification_pref == Attendee.NOTIFICATION_NONE: continue use_text = twilio_client \ and attendee.cellphone \ and attendee.notification_pref == Attendee.NOTIFICATION_TEXT event = signup.event # If we overlap multiple notices, we only want to send a single # notification. So if we have both "5 minutes before checkin" and # "when checkin starts", we only want to send the notification # for "when checkin starts". advance_notice = min(advance_notices) if advance_notice == -1 or advance_notice > 1800: checkin = 'is at {}'.format(event.checkin_start_time_label) else: checkin = humanize_timedelta(event.time_remaining_to_checkin, granularity='minutes', separator=' ', prefix='is in ', now='is right now', past_prefix='was ', past_suffix=' ago') ident = AttractionEvent.get_ident(event.id, advance_notice) try: if use_text: type_ = Attendee.NOTIFICATION_TEXT type_str = 'TEXT' from_ = c.PANELS_TWILIO_NUMBER to_ = attendee.cellphone body = TEXT_TPL.format(signup=signup, checkin=checkin) subject = '' sid = send_sms(to_, body, from_) if not use_text or is_first_signup: type_ = Attendee.NOTIFICATION_EMAIL type_str = 'EMAIL' from_ = c.ATTRACTIONS_EMAIL to_ = attendee.email if is_first_signup: template = 'emails/attractions_welcome.html' subject = 'Welcome to {} Attractions'.format( c.EVENT_NAME) else: template = 'emails/attractions_notification.html' subject = 'Checkin for {} is at {}'.format( event.name, event.checkin_start_time_label) body = render(template, { 'signup': signup, 'checkin': checkin, 'c': c }).decode('utf-8') sid = ident send_email(from_, to_, subject=subject, body=body, format='html', model=attendee, ident=ident) except: log.error('Error sending notification\n' '\tfrom: {}\n' '\tto: {}\n' '\tsubject: {}\n' '\tbody: {}\n' '\ttype: {}\n' '\tattendee: {}\n' '\tident: {}\n'.format(from_, to_, subject, body, type_str, attendee.id, ident), exc_info=True) else: session.add( AttractionNotification(attraction_event_id=event.id, attraction_id=event.attraction_id, attendee_id=attendee.id, notification_type=type_, ident=ident, sid=sid, sent_time=datetime.now(pytz.UTC), subject=subject, body=body)) session.commit()
def attractions_send_notifications(): twilio_client = get_twilio_client(c.PANELS_TWILIO_SID, c.PANELS_TWILIO_TOKEN) with Session() as session: for attraction in session.query(Attraction): now = datetime.now(pytz.UTC) from_time = now - timedelta(seconds=300) to_time = now + timedelta(seconds=300) signups = attraction.signups_requiring_notification(session, from_time, to_time, [ subqueryload( AttractionSignup.attendee).subqueryload( Attendee.attraction_notifications), subqueryload( AttractionSignup.event).subqueryload( AttractionEvent.feature)]) for signup, advance_notices in signups.items(): attendee = signup.attendee if not attendee.first_name or not attendee.email: try: log.error( 'ERROR: Unassigned attendee signed up for an attraction, deleting signup:\n' '\tAttendee.id: {}\n' '\tAttraction.id: {}\n' '\tAttractionEvent.id: {}\n' '\tAttractionSignup.id: {}'.format( attendee.id, signup.attraction_id, signup.attraction_event_id, signup.id)) session.delete(signup) session.commit() except Exception: log.error('ERROR: Failed to delete signup with unassigned attendee', exc_info=True) continue # The first time someone signs up for an attractions, they always # receive the welcome email (even if they've chosen SMS or None # for their notification prefs). If they've chosen to receive SMS # notifications, they'll also get a text message. is_first_signup = not(attendee.attraction_notifications) if not is_first_signup and attendee.notification_pref == Attendee._NOTIFICATION_NONE: continue use_text = twilio_client \ and c.PANELS_TWILIO_NUMBER \ and attendee.cellphone \ and attendee.notification_pref == Attendee._NOTIFICATION_TEXT event = signup.event # If we overlap multiple notices, we only want to send a single # notification. So if we have both "5 minutes before checkin" and # "when checkin starts", we only want to send the notification # for "when checkin starts". advance_notice = min(advance_notices) if advance_notice == -1 or advance_notice > 1800: checkin = 'is at {}'.format(event.checkin_start_time_label) else: checkin = humanize_timedelta( event.time_remaining_to_checkin, granularity='minutes', separator=' ', prefix='is in ', now='is right now', past_prefix='was ', past_suffix=' ago') ident = AttractionEvent.get_ident(event.id, advance_notice) try: if use_text: type_ = Attendee._NOTIFICATION_TEXT type_str = 'TEXT' from_ = c.PANELS_TWILIO_NUMBER to_ = attendee.cellphone body = TEXT_TEMPLATE.format(signup=signup, checkin=checkin) subject = '' sid = send_sms_with_client(twilio_client, to_, body, from_) if not use_text or is_first_signup: type_ = Attendee._NOTIFICATION_EMAIL type_str = 'EMAIL' from_ = c.ATTRACTIONS_EMAIL to_ = attendee.email if is_first_signup: template = 'emails/panels/attractions_welcome.html' subject = 'Welcome to {} Attractions'.format(c.EVENT_NAME) else: template = 'emails/panels/attractions_notification.html' subject = 'Checkin for {} is at {}'.format(event.name, event.checkin_start_time_label) body = render(template, { 'signup': signup, 'checkin': checkin, 'c': c}).decode('utf-8') sid = ident send_email(from_, to_, subject=subject, body=body, format='html', model=attendee, ident=ident) except Exception: log.error( 'Error sending notification\n' '\tfrom: {}\n' '\tto: {}\n' '\tsubject: {}\n' '\tbody: {}\n' '\ttype: {}\n' '\tattendee: {}\n' '\tident: {}\n'.format( from_, to_, subject, body, type_str, attendee.id, ident), exc_info=True) else: session.add(AttractionNotification( attraction_event_id=event.id, attraction_id=event.attraction_id, attendee_id=attendee.id, notification_type=type_, ident=ident, sid=sid, sent_time=datetime.now(pytz.UTC), subject=subject, body=body)) session.commit()
def duration_label(self): if self.duration: return humanize_timedelta(seconds=self.duration, separator=' ') return 'unknown duration'
def time_remaining_to_checkin_label(self): return humanize_timedelta(self.time_remaining_to_checkin, granularity='minutes', separator=' ')