def broadcast_to_student(student_key, event_key, message_key, batch_id=""): """Send broadcast to each of the student's contacts.""" from sosbeacon.event.student_marker import create_or_update_marker from sosbeacon.student import Student # Needed to load the entity. from sosbeacon.utils import insert_tasks student = student_key.get() if not student: # TODO: ? logging.info("Tried to broadcast %s to missing student %s.", message_key.urlsafe(), student_key.urlsafe()) return tasks = [] # contacts = {} for contact in student.contacts: # TODO: Optimize task building with memcache markers to # avoid building tasks that already exist. task = get_contact_broadcast_task(event_key, message_key, student_key, contact, batch_id) if not task: continue tasks.append(task) # TODO: WTF is this? # contacts[contact['t']] = contact if tasks: insert_tasks(tasks, CONTACT_TX_QUEUE) create_or_update_marker(event_key, student, message_key)
def broadcast_to_contact(event_key, message_key, student_key, contact, batch_id=""): """Insert tasks to send message to each contact method, and create a contact marker. """ from sosbeacon.event.contact_marker import create_or_update_marker from sosbeacon.utils import insert_tasks SEARCH_TYPES = ["e", "t"] # Find methods we want to query by. methods = set() for method in contact.get("methods", ()): method_type = method.get("type") value = method.get("value") if method_type == "t" and not value: SEARCH_TYPES.append("p") if not value or method_type not in SEARCH_TYPES: continue methods.add(value) if not methods: return short_id = create_or_update_marker(event_key, student_key, message_key, contact, methods) method_tasks = [] for method in methods: method_tasks.append(get_method_broadcast_task(event_key, message_key, short_id, method, batch_id)) insert_tasks(method_tasks, METHOD_TX_QUEUE)
def broadcast_to_groups(group_keys, event_key, message_key, batch_id): """Scan over the given set of groups, sending the broadcast to everyone in those groups. """ from sosbeacon.group import ADMIN_GROUPS_ID from sosbeacon.group import Group from sosbeacon.utils import insert_tasks # This is done to dedupe the group list, for better resilience. group_keys = list(set(group_keys)) if len(group_keys) == 1 and group_keys[0].id() == ADMIN_GROUPS_ID: group_keys = Group.query().order(Group.key).iter(keys_only=True) tasks = [] for group_key in group_keys: # TODO: Batch tasks or start tasks.append(get_group_broadcast_task(group_key, event_key, message_key, batch_id)) if len(tasks) > 10: insert_tasks(tasks, GROUP_TX_QUEUE) tasks = [] if tasks: insert_tasks(tasks, GROUP_TX_QUEUE)
def broadcast_to_groups(group_keys, event_key, message_key, batch_id): """Scan over the given set of groups, sending the broadcast to everyone in those groups. """ from sosbeacon.group import ADMIN_GROUPS_ID from sosbeacon.group import Group from sosbeacon.utils import insert_tasks # This is done to dedupe the group list, for better resilience. group_keys = list(set(group_keys)) if len(group_keys) == 1 and group_keys[0].id() == ADMIN_GROUPS_ID: group_keys = Group.query().order(Group.key).iter(keys_only=True) tasks = [] for group_key in group_keys: # TODO: Batch tasks or start tasks.append( get_group_broadcast_task(group_key, event_key, message_key, batch_id)) if len(tasks) > 10: insert_tasks(tasks, GROUP_TX_QUEUE) tasks = [] if tasks: insert_tasks(tasks, GROUP_TX_QUEUE)
def robocall_start(event_key, is_direct, user_urlsafe, tz): event_urlsafe = ndb.Key(urlsafe=event_key) event = event_urlsafe.get() if not event: logging.error('Event %s not found!', event_urlsafe) return if event.status == EVENT_STATUS_CLOSED: logging.error('Event %s closed!', event_urlsafe) return query = StudentMarker.query(StudentMarker.event == event_urlsafe, StudentMarker.acknowledged == False, StudentMarker.is_direct == is_direct) student_markers = query.fetch() list_broadcast = Message.query( Message.event == event_urlsafe, Message.message_type.IN(['b', 'eo', 'em', 'ec'])).order(Message.timestamp).fetch() last_broadcast = list_broadcast[0] tasks = [] phones = [] # if voice phone is empty, text phone will replace voice phone to make a call for student_marker in student_markers: for contact in student_marker.contacts: if contact['methods'][2]['value']: phones.append(contact['methods'][2]['value']) else: phones.append(contact['methods'][1]['value']) phones = list(set(phones)) if not phones: return for phone in phones: tasks.append( get_robocall_task(event_urlsafe, phone, last_broadcast.key, tz)) if len(tasks) > 10: insert_tasks(tasks, ROBOCALL_QUEUE_NAME) tasks = [] if tasks: tasks.append( get_sent_email_task(event_urlsafe, user_urlsafe, last_broadcast.key, phones)) insert_tasks(tasks, ROBOCALL_QUEUE_NAME)
def create_marker_user(event_key, message_key, user_key): """Scan over the given set of groups, sending the broadcast to everyone in those groups. """ from sosbeacon.utils import insert_tasks from sosbeacon.event.contact_marker import ContactMarker from sosbeacon.event.contact_marker import get_marker_for_methods from sosbeacon.student import Student from sosbeacon.student import DEFAULT_STUDENT_ID methods = set() if message_key.get().message_type == 'eo': methods.add(user_key.get().email) else: methods.add(user_key.get().phone) methods.add(user_key.get().email) marker = get_marker_for_methods(event_key, methods) student_key = ndb.Key(Student, "%s-%s" % (DEFAULT_STUDENT_ID, user_key.id()), namespace='_x_') if not marker: # TODO: What needs set? short_id = str(ContactMarker.allocate_ids(size=1, parent=event_key)[0]) key_id = "%s:%s" % (event_key.id(), user_key.id()) marker = ContactMarker( id=key_id, event=event_key, first_name=user_key.get().first_name, last_name=user_key.get().last_name, students={str(student_key.id()): []}, short_id=short_id, methods=[user_key.get().email, user_key.get().phone], count_comment=0, count_visit=0, is_user=True) marker.acknowledged = True marker.put() tasks = [] for method in methods: # TODO: Batch tasks or start tasks.append( get_broadcast_method_to_user_task(event_key, message_key, user_key, method)) if len(tasks) > 10: insert_tasks(tasks, USER_TX_QUEUE) tasks = [] if tasks: insert_tasks(tasks, USER_TX_QUEUE)
def robocall_start(event_key, is_direct, user_urlsafe, tz): event_urlsafe = ndb.Key(urlsafe=event_key) event = event_urlsafe.get() if not event: logging.error('Event %s not found!', event_urlsafe) return if event.status == EVENT_STATUS_CLOSED: logging.error('Event %s closed!', event_urlsafe) return query = StudentMarker.query(StudentMarker.event == event_urlsafe, StudentMarker.acknowledged == False, StudentMarker.is_direct == is_direct) student_markers = query.fetch() list_broadcast = Message.query(Message.event == event_urlsafe, Message.message_type.IN(['b', 'eo', 'em', 'ec'])).order(Message.timestamp).fetch() last_broadcast = list_broadcast[0] tasks = [] phones = [] # if voice phone is empty, text phone will replace voice phone to make a call for student_marker in student_markers: for contact in student_marker.contacts: if contact['methods'][2]['value']: phones.append(contact['methods'][2]['value']) else: phones.append(contact['methods'][1]['value']) phones = list(set(phones)) if not phones: return for phone in phones: tasks.append( get_robocall_task(event_urlsafe, phone, last_broadcast.key, tz) ) if len(tasks) > 10: insert_tasks(tasks, ROBOCALL_QUEUE_NAME) tasks = [] if tasks: tasks.append(get_sent_email_task(event_urlsafe, user_urlsafe, last_broadcast.key, phones)) insert_tasks(tasks, ROBOCALL_QUEUE_NAME)
def create_marker_user(event_key, message_key, user_key): """Scan over the given set of groups, sending the broadcast to everyone in those groups. """ from sosbeacon.utils import insert_tasks from sosbeacon.event.contact_marker import ContactMarker from sosbeacon.event.contact_marker import get_marker_for_methods from sosbeacon.student import Student from sosbeacon.student import DEFAULT_STUDENT_ID methods = set() if message_key.get().message_type == "eo": methods.add(user_key.get().email) else: methods.add(user_key.get().phone) methods.add(user_key.get().email) marker = get_marker_for_methods(event_key, methods) student_key = ndb.Key(Student, "%s-%s" % (DEFAULT_STUDENT_ID, user_key.id()), namespace="_x_") if not marker: # TODO: What needs set? short_id = str(ContactMarker.allocate_ids(size=1, parent=event_key)[0]) key_id = "%s:%s" % (event_key.id(), user_key.id()) marker = ContactMarker( id=key_id, event=event_key, first_name=user_key.get().first_name, last_name=user_key.get().last_name, students={str(student_key.id()): []}, short_id=short_id, methods=[user_key.get().email, user_key.get().phone], count_comment=0, count_visit=0, is_user=True, ) marker.acknowledged = True marker.put() tasks = [] for method in methods: # TODO: Batch tasks or start tasks.append(get_broadcast_method_to_user_task(event_key, message_key, user_key, method)) if len(tasks) > 10: insert_tasks(tasks, USER_TX_QUEUE) tasks = [] if tasks: insert_tasks(tasks, USER_TX_QUEUE)
def test_insert_batch(self, queue_mock): """Ensure taskqueue.Queue.add is called exactly once.""" from sosbeacon.utils import insert_tasks tasks = [] for i in xrange(1, 10): tasks.append(object()) added = insert_tasks(tasks, 'default') self.assertEqual(added, 9)
def broadcast_to_contact(event_key, message_key, student_key, contact, batch_id=''): """Insert tasks to send message to each contact method, and create a contact marker. """ from sosbeacon.event.contact_marker import create_or_update_marker from sosbeacon.utils import insert_tasks SEARCH_TYPES = ['e', 't'] # Find methods we want to query by. methods = set() for method in contact.get('methods', ()): method_type = method.get('type') value = method.get('value') if method_type == 't' and not value: SEARCH_TYPES.append('p') if not value or method_type not in SEARCH_TYPES: continue methods.add(value) if not methods: return short_id = create_or_update_marker(event_key, student_key, message_key, contact, methods) method_tasks = [] for method in methods: method_tasks.append( get_method_broadcast_task(event_key, message_key, short_id, method, batch_id)) insert_tasks(method_tasks, METHOD_TX_QUEUE)
def broadcast_to_group(group_key, event_key, message_key, batch_id="", iteration=0, cursor=None): """Scan over people in the group, starting from cursor if provided, sending the broadcast to every contact. """ from sosbeacon.group import get_student_keys from sosbeacon.utils import insert_tasks students, cursor, more = get_student_keys(group_key, cursor) if more: continuation = get_group_broadcast_task(group_key, event_key, message_key, batch_id, iteration + 1, cursor) insert_tasks((continuation,), GROUP_TX_QUEUE) tasks = [] for student_key in students: task = get_student_broadcast_task(student_key, event_key, message_key, batch_id) if not task: continue tasks.append(task) if len(tasks) > 10: insert_tasks(tasks, STUDENT_TX_QUEUE) tasks = [] if tasks: insert_tasks(tasks, STUDENT_TX_QUEUE)
def test_splits_on_taskexists(self, queue_add_mock): """Ensure task batches are split and insertion is retried on TaskAlreadyExistsError. """ from google.appengine.api import taskqueue from sosbeacon.utils import insert_tasks queue_add_mock.side_effect = taskqueue.TaskAlreadyExistsError tasks = [i for i in xrange(0, 10)] added = insert_tasks(tasks, 'default') self.assertEqual(added, 0) self.assertEqual(queue_add_mock.call_count, 19)
def broadcast_to_student(student_key, event_key, message_key, batch_id=''): """Send broadcast to each of the student's contacts.""" from sosbeacon.event.student_marker import create_or_update_marker from sosbeacon.student import Student # Needed to load the entity. from sosbeacon.utils import insert_tasks student = student_key.get() if not student: # TODO: ? logging.info('Tried to broadcast %s to missing student %s.', message_key.urlsafe(), student_key.urlsafe()) return tasks = [] #contacts = {} for contact in student.contacts: # TODO: Optimize task building with memcache markers to # avoid building tasks that already exist. task = get_contact_broadcast_task(event_key, message_key, student_key, contact, batch_id) if not task: continue tasks.append(task) # TODO: WTF is this? #contacts[contact['t']] = contact if tasks: insert_tasks(tasks, CONTACT_TX_QUEUE) create_or_update_marker(event_key, student, message_key)
def test_splits_once(self, queue_add_mock): """Ensure task batches are split and insertion is retried on TaskAlreadyExistsError. """ from google.appengine.api import taskqueue from sosbeacon.utils import insert_tasks def side_effect(*args): if 2 in args[0]: raise taskqueue.TombstonedTaskError('uh oh') queue_add_mock.side_effect = side_effect tasks = [i for i in xrange(0, 9)] added = insert_tasks(tasks, 'default') self.assertEqual(added, 8) self.assertEqual(queue_add_mock.call_count, 7)
def broadcast_to_group(group_key, event_key, message_key, batch_id='', iteration=0, cursor=None): """Scan over people in the group, starting from cursor if provided, sending the broadcast to every contact. """ from sosbeacon.group import get_student_keys from sosbeacon.utils import insert_tasks students, cursor, more = get_student_keys(group_key, cursor) if more: continuation = get_group_broadcast_task(group_key, event_key, message_key, batch_id, iteration + 1, cursor) insert_tasks((continuation, ), GROUP_TX_QUEUE) tasks = [] for student_key in students: task = get_student_broadcast_task(student_key, event_key, message_key, batch_id) if not task: continue tasks.append(task) if len(tasks) > 10: insert_tasks(tasks, STUDENT_TX_QUEUE) tasks = [] if tasks: insert_tasks(tasks, STUDENT_TX_QUEUE)