def initiate_sms_verification_workflow(contact, phone_number): # For now this is only applicable to mobile workers assert isinstance(contact, CommCareUser) logged_event = MessagingEvent.get_current_verification_event( contact.domain, contact.get_id, phone_number) with CriticalSection(['verifying-phone-number-%s' % phone_number]): vn = PhoneNumber.by_phone(phone_number, include_pending=True) if vn: if vn.owner_id != contact.get_id: return VERIFICATION__ALREADY_IN_USE if vn.verified: return VERIFICATION__ALREADY_VERIFIED else: result = VERIFICATION__RESENT_PENDING else: contact.save_verified_number(contact.domain, phone_number, False) result = VERIFICATION__WORKFLOW_STARTED # Always create a new event when the workflow starts if logged_event: logged_event.status = MessagingEvent.STATUS_NOT_COMPLETED logged_event.save() logged_event = MessagingEvent.create_verification_event( contact.domain, contact) if not logged_event: logged_event = MessagingEvent.create_verification_event( contact.domain, contact) send_verification(contact.domain, contact, phone_number, logged_event) return result
def initiate_sms_verification_workflow(contact, phone_number): # For now this is only applicable to mobile workers assert isinstance(contact, CommCareUser) logged_event = MessagingEvent.get_current_verification_event( contact.domain, contact.get_id, phone_number) with CriticalSection(['verifying-phone-number-%s' % phone_number]): vn = VerifiedNumber.by_phone(phone_number, include_pending=True) if vn: if vn.owner_id != contact._id: return VERIFICATION__ALREADY_IN_USE if vn.verified: return VERIFICATION__ALREADY_VERIFIED else: result = VERIFICATION__RESENT_PENDING else: contact.save_verified_number(contact.domain, phone_number, False) result = VERIFICATION__WORKFLOW_STARTED # Always create a new event when the workflow starts if logged_event: logged_event.status = MessagingEvent.STATUS_NOT_COMPLETED logged_event.save() logged_event = MessagingEvent.create_verification_event(contact.domain, contact) if not logged_event: logged_event = MessagingEvent.create_verification_event(contact.domain, contact) send_verification(contact.domain, contact, phone_number, logged_event) return result
def handle_current_event(self): content = self.memoized_schedule.get_current_event_content(self) if isinstance(self, CaseScheduleInstanceMixin): content.set_context(case=self.case, schedule_instance=self) else: content.set_context(schedule_instance=self) logged_event = MessagingEvent.create_from_schedule_instance( self, content) recipient_count = 0 for recipient in self.expand_recipients(): recipient_count += 1 content.send(recipient, logged_event) # As a precaution, always explicitly move to the next event after processing the current # event to prevent ever getting stuck on the current event. self.memoized_schedule.move_to_next_event(self) self.memoized_schedule.move_to_next_event_not_in_the_past(self) # Update the MessagingEvent for reporting if recipient_count == 0: logged_event.error(MessagingEvent.ERROR_NO_RECIPIENT) else: logged_event.completed()
def forward_sms(msg, domain, verified_number, text, backend_id): logged_event = MessagingEvent.create_event_for_adhoc_sms( domain, recipient=verified_number.owner, content_type=MessagingEvent.CONTENT_SMS, source=MessagingEvent.SOURCE_FORWARDED) inbound_subevent = logged_event.create_subevent_for_single_sms( verified_number.owner_doc_type, verified_number.owner_id) inbound_meta = MessageMetadata(workflow=WORKFLOW_FORWARD, messaging_subevent_id=inbound_subevent.pk) add_msg_tags(msg, inbound_meta) outbound_subevent = logged_event.create_subevent_for_single_sms( verified_number.owner_doc_type, verified_number.owner_id) outbound_meta = MessageMetadata(workflow=WORKFLOW_FORWARD, messaging_subevent_id=outbound_subevent.pk) send_sms_with_backend(domain, verified_number.phone_number, text, backend_id, metadata=outbound_meta) outbound_subevent.completed() inbound_subevent.completed() logged_event.completed()
def fallback_handler(verified_number, text, msg): domain_obj = Domain.get_by_name(verified_number.domain, strict=True) logged_event = MessagingEvent.create_event_for_adhoc_sms( verified_number.domain, recipient=verified_number.owner, content_type=MessagingEvent.CONTENT_SMS, source=MessagingEvent.SOURCE_UNRECOGNIZED) inbound_subevent = logged_event.create_subevent_for_single_sms( verified_number.owner_doc_type, verified_number.owner_id) inbound_meta = MessageMetadata(workflow=WORKFLOW_DEFAULT, messaging_subevent_id=inbound_subevent.pk) add_msg_tags(msg, inbound_meta) if domain_obj.use_default_sms_response and domain_obj.default_sms_response: outbound_subevent = logged_event.create_subevent_for_single_sms( verified_number.owner_doc_type, verified_number.owner_id) outbound_meta = MessageMetadata( workflow=WORKFLOW_DEFAULT, location_id=msg.location_id, messaging_subevent_id=outbound_subevent.pk) send_sms_to_verified_number(verified_number, domain_obj.default_sms_response, metadata=outbound_meta) outbound_subevent.completed() inbound_subevent.completed() logged_event.completed() return True
def add_event_count_info(self, result, days): end_date = self.domain_now.date() start_date = end_date - timedelta(days=days - 1) counts = MessagingEvent.get_counts_by_date(self.domain, start_date, end_date, self.timezone.zone) counts_dict = { row.date: {'error': row.error_count, 'total': row.total_count} for row in counts } error_values = [] success_values = [] for i in range(days): dt = start_date + timedelta(days=i) error_count = counts_dict.get(dt, {}).get('error', 0) total_count = counts_dict.get(dt, {}).get('total', 0) error_values.append({ 'x': dt.strftime('%Y-%m-%d'), 'y': error_count, }) success_values.append({ 'x': dt.strftime('%Y-%m-%d'), 'y': total_count - error_count, }) result['event_count_data'] = [ {'key': _("Error"), 'values': error_values}, {'key': _("Success"), 'values': success_values}, ]
def fallback_handler(v, text, msg): domain_obj = Domain.get_by_name(v.domain, strict=True) logged_event = MessagingEvent.create_event_for_adhoc_sms( v.domain, recipient=v.owner, content_type=MessagingEvent.CONTENT_SMS, source=MessagingEvent.SOURCE_UNRECOGNIZED) inbound_subevent = logged_event.create_subevent_for_single_sms( v.owner_doc_type, v.owner_id) inbound_meta = MessageMetadata(workflow=WORKFLOW_DEFAULT, messaging_subevent_id=inbound_subevent.pk) add_msg_tags(msg, inbound_meta) if domain_obj.use_default_sms_response and domain_obj.default_sms_response: outbound_subevent = logged_event.create_subevent_for_single_sms( v.owner_doc_type, v.owner_id) outbound_meta = MessageMetadata(workflow=WORKFLOW_DEFAULT, location_id=msg.location_id, messaging_subevent_id=outbound_subevent.pk) send_sms_to_verified_number(v, domain_obj.default_sms_response, metadata=outbound_meta) outbound_subevent.completed() inbound_subevent.completed() logged_event.completed() return True
def process_verification(v, msg, verification_keywords=None, create_subevent_for_inbound=True): verification_keywords = verification_keywords or ['123'] logged_event = MessagingEvent.get_current_verification_event( v.domain, v.owner_id, v.phone_number) if not logged_event: logged_event = MessagingEvent.create_verification_event(v.domain, v.owner) msg.domain = v.domain msg.couch_recipient_doc_type = v.owner_doc_type msg.couch_recipient = v.owner_id if create_subevent_for_inbound: subevent = logged_event.create_subevent_for_single_sms( v.owner_doc_type, v.owner_id ) subevent.completed() msg.messaging_subevent_id = subevent.pk msg.save() if ( not domain_has_privilege(msg.domain, privileges.INBOUND_SMS) or not verification_response_ok(msg.text, verification_keywords) ): return False v.set_two_way() v.set_verified() v.save() logged_event.completed() subevent = logged_event.create_subevent_for_single_sms( v.owner_doc_type, v.owner_id ) message = messages.get_message( messages.MSG_VERIFICATION_SUCCESSFUL, verified_number=v ) send_sms_to_verified_number(v, message, metadata=MessageMetadata(messaging_subevent_id=subevent.pk)) subevent.completed() return True
def process_verification(phone_number, msg, backend_id=None): v = VerifiedNumber.by_phone(phone_number, True) if not v: return logged_event = MessagingEvent.get_current_verification_event( v.domain, v.owner_id, phone_number) if not logged_event: logged_event = MessagingEvent.create_verification_event( v.domain, v.owner) subevent = logged_event.create_subevent_for_single_sms( v.owner_doc_type, v.owner_id) subevent.completed() msg.domain = v.domain msg.couch_recipient_doc_type = v.owner_doc_type msg.couch_recipient = v.owner_id msg.messaging_subevent_id = subevent.pk msg.save() if (not domain_has_privilege(msg.domain, privileges.INBOUND_SMS) or not verification_response_ok(msg.text)): return if backend_id: backend = MobileBackend.load(backend_id) else: backend = MobileBackend.auto_load(phone_number, v.domain) assert v.owner_doc_type == 'CommCareUser' owner = CommCareUser.get(v.owner_id) v = owner.save_verified_number(v.domain, phone_number, True, backend.name) logged_event.completed() subevent = logged_event.create_subevent_for_single_sms( v.owner_doc_type, v.owner_id) with localize(owner.language): send_sms_to_verified_number( v, _(CONFIRM), metadata=MessageMetadata(messaging_subevent_id=subevent.pk)) subevent.completed()
def process_verification(v, msg): if not v or v.verified: return logged_event = MessagingEvent.get_current_verification_event( v.domain, v.owner_id, v.phone_number) if not logged_event: logged_event = MessagingEvent.create_verification_event(v.domain, v.owner) subevent = logged_event.create_subevent_for_single_sms( v.owner_doc_type, v.owner_id ) subevent.completed() msg.domain = v.domain msg.couch_recipient_doc_type = v.owner_doc_type msg.couch_recipient = v.owner_id msg.messaging_subevent_id = subevent.pk msg.save() if ( not domain_has_privilege(msg.domain, privileges.INBOUND_SMS) or not verification_response_ok(msg.text) ): return v.verified = True v.save() logged_event.completed() subevent = logged_event.create_subevent_for_single_sms( v.owner_doc_type, v.owner_id ) message = messages.get_message( messages.MSG_VERIFICATION_SUCCESSFUL, verified_number=v ) send_sms_to_verified_number(v, message, metadata=MessageMetadata(messaging_subevent_id=subevent.pk)) subevent.completed()
def send_current_event_content_to_recipients(self): content = self.memoized_schedule.get_current_event_content(self) if isinstance(content, (IVRSurveyContent, SMSCallbackContent)): raise TypeError( "IVR and Callback use cases are no longer supported. " "How did this schedule instance end up as active?") if isinstance(self, CaseScheduleInstanceMixin): content.set_context(case=self.case, schedule_instance=self) else: content.set_context(schedule_instance=self) logged_event = MessagingEvent.create_from_schedule_instance( self, content) recipient_count = 0 for recipient in self.expand_recipients(): recipient_count += 1 # The framework will retry sending a non-processed schedule instance # once every hour. # If we are processing a long list of recipients here and an error # occurs half-way through, we don't want to reprocess the entire list # of recipients again when the framework retries it an hour later. # So we use a non-blocking lock tied to the event due time and recipient # to make sure that we don't try resending the same content to the same # recipient more than once in the event of a retry. # If we succeed in sending the content, we don't release the lock so # that it won't retry later. If we fail in sending the content, we release # the lock so that it will retry later. lock = self.get_content_send_lock(recipient) if lock.acquire(blocking=False): try: content.send(recipient, logged_event) except: error = sys.exc_info()[1] # Release the lock if an error happened so that we can try sending # to this recipient again later. lock.release() logged_event.error( MessagingEvent.ERROR_INTERNAL_SERVER_ERROR, additional_error_text=str(error), ) raise # Update the MessagingEvent for reporting if recipient_count == 0: logged_event.error(MessagingEvent.ERROR_NO_RECIPIENT) else: logged_event.completed()
def randomMessagingSubEventId(self): # Just create a dummy event and subevent to generate # a valid MessagingSubEvent foreign key event = MessagingEvent( domain=self.domain, date=self.randomDateTime(), source=MessagingEvent.SOURCE_KEYWORD, content_type=MessagingEvent.CONTENT_SMS, status=MessagingEvent.STATUS_COMPLETED, ) event.save() subevent = MessagingSubEvent( date=self.randomDateTime(), parent=event, recipient_type=MessagingEvent.RECIPIENT_CASE, content_type=MessagingEvent.CONTENT_SMS, status=MessagingEvent.STATUS_COMPLETED, ) subevent.save() return subevent.pk
def process_verification(v, msg): if not v or v.verified: return logged_event = MessagingEvent.get_current_verification_event( v.domain, v.owner_id, v.phone_number) if not logged_event: logged_event = MessagingEvent.create_verification_event(v.domain, v.owner) subevent = logged_event.create_subevent_for_single_sms( v.owner_doc_type, v.owner_id ) subevent.completed() msg.domain = v.domain msg.couch_recipient_doc_type = v.owner_doc_type msg.couch_recipient = v.owner_id msg.messaging_subevent_id = subevent.pk msg.save() if ( not domain_has_privilege(msg.domain, privileges.INBOUND_SMS) or not verification_response_ok(msg.text) ): return v.verified = True v.save() logged_event.completed() subevent = logged_event.create_subevent_for_single_sms( v.owner_doc_type, v.owner_id ) with localize(v.owner.get_language_code()): send_sms_to_verified_number(v, _(CONFIRM), metadata=MessageMetadata(messaging_subevent_id=subevent.pk)) subevent.completed()
def initiate_sms_verification_workflow(contact, phone_number): # For now this is only applicable to mobile workers assert isinstance(contact, CommCareUser) phone_number = apply_leniency(phone_number) logged_event = MessagingEvent.get_current_verification_event( contact.domain, contact.get_id, phone_number) p = PhoneNumber.get_reserved_number(phone_number) if p: if p.owner_id != contact.get_id: return VERIFICATION__ALREADY_IN_USE if p.verified: return VERIFICATION__ALREADY_VERIFIED else: result = VERIFICATION__RESENT_PENDING else: entry = contact.get_or_create_phone_entry(phone_number) try: entry.set_pending_verification() except PhoneNumberInUseException: # On the off chance that the phone number was reserved between # the check above and now return VERIFICATION__ALREADY_IN_USE result = VERIFICATION__WORKFLOW_STARTED # Always create a new event when the workflow starts if logged_event: logged_event.status = MessagingEvent.STATUS_NOT_COMPLETED logged_event.save() logged_event = MessagingEvent.create_verification_event( contact.domain, contact) if not logged_event: logged_event = MessagingEvent.create_verification_event( contact.domain, contact) send_verification(contact.domain, contact, phone_number, logged_event) return result
def process_verification(v, msg): if not v or v.verified: return logged_event = MessagingEvent.get_current_verification_event( v.domain, v.owner_id, v.phone_number) if not logged_event: logged_event = MessagingEvent.create_verification_event( v.domain, v.owner) subevent = logged_event.create_subevent_for_single_sms( v.owner_doc_type, v.owner_id) subevent.completed() msg.domain = v.domain msg.couch_recipient_doc_type = v.owner_doc_type msg.couch_recipient = v.owner_id msg.messaging_subevent_id = subevent.pk msg.save() if (not domain_has_privilege(msg.domain, privileges.INBOUND_SMS) or not verification_response_ok(msg.text)): return v.verified = True v.save() logged_event.completed() subevent = logged_event.create_subevent_for_single_sms( v.owner_doc_type, v.owner_id) message = messages.get_message(messages.MSG_VERIFICATION_SUCCESSFUL, verified_number=v) send_sms_to_verified_number( v, message, metadata=MessageMetadata(messaging_subevent_id=subevent.pk)) subevent.completed()
def send_current_event_content_to_recipients(self): client = get_redis_client() content = self.memoized_schedule.get_current_event_content(self) if isinstance(content, (IVRSurveyContent, SMSCallbackContent)): raise TypeError( "IVR and Callback use cases are no longer supported. " "How did this schedule instance end up as active?" ) if isinstance(self, CaseScheduleInstanceMixin): content.set_context(case=self.case, schedule_instance=self) else: content.set_context(schedule_instance=self) logged_event = MessagingEvent.create_from_schedule_instance(self, content) recipient_count = 0 for recipient in self.expand_recipients(): recipient_count += 1 # The framework will retry sending a non-processed schedule instance # once every hour. # If we are processing a long list of recipients here and an error # occurs half-way through, we don't want to reprocess the entire list # of recipients again when the framework retries it an hour later. # So we use a non-blocking lock tied to the event due time and recipient # to make sure that we don't try resending the same content to the same # recipient more than once in the event of a retry. # If we succeed in sending the content, we don't release the lock so # that it won't retry later. If we fail in sending the content, we release # the lock so that it will retry later. lock = self.get_content_send_lock(client, recipient) if lock.acquire(blocking=False): try: content.send(recipient, logged_event) except: # Release the lock if an error happened so that we can try sending # to this recipient again later. lock.release() raise # Update the MessagingEvent for reporting if recipient_count == 0: logged_event.error(MessagingEvent.ERROR_NO_RECIPIENT) else: logged_event.completed()
def initiate_sms_verification_workflow(contact, phone_number): # For now this is only applicable to mobile workers assert isinstance(contact, CommCareUser) phone_number = apply_leniency(phone_number) logged_event = MessagingEvent.get_current_verification_event( contact.domain, contact.get_id, phone_number) p = PhoneNumber.get_reserved_number(phone_number) if p: if p.owner_id != contact.get_id: return VERIFICATION__ALREADY_IN_USE if p.verified: return VERIFICATION__ALREADY_VERIFIED else: result = VERIFICATION__RESENT_PENDING else: entry = contact.get_or_create_phone_entry(phone_number) try: entry.set_pending_verification() except PhoneNumberInUseException: # On the off chance that the phone number was reserved between # the check above and now return VERIFICATION__ALREADY_IN_USE result = VERIFICATION__WORKFLOW_STARTED # Always create a new event when the workflow starts if logged_event: logged_event.status = MessagingEvent.STATUS_NOT_COMPLETED logged_event.save() logged_event = MessagingEvent.create_verification_event(contact.domain, contact) if not logged_event: logged_event = MessagingEvent.create_verification_event(contact.domain, contact) send_verification(contact.domain, contact, phone_number, logged_event) return result
def process_survey_keyword_actions(verified_number, survey_keyword, text, msg): sender = verified_number.owner case = None args = split_args(text, survey_keyword) logged_event = MessagingEvent.create_from_keyword(survey_keyword, sender) # Close any open sessions even if it's just an sms that we're # responding with. SQLXFormsSession.close_all_open_sms_sessions(verified_number.domain, verified_number.owner_id) if sender.doc_type == "CommCareCase": case = sender args = args[1:]
def _send_fallback_message(self, msg): logged_event = MessagingEvent.create_event_for_adhoc_sms( self.domain, recipient=msg.recipient) logged_subevent = logged_event.create_subevent_for_single_sms( recipient_doc_type=msg.recipient.doc_type, recipient_id=msg.recipient.get_id) metadata = MessageMetadata( messaging_subevent_id=logged_subevent.pk, custom_metadata={"fallback": "WhatsApp Contact Not Found"}, ) if send_sms_with_backend(self.domain, msg.phone_number, msg.text, self.config.fallback_backend_id, metadata): logged_subevent.completed() logged_event.completed() else: logged_subevent.error(MessagingEvent.ERROR_INTERNAL_SERVER_ERROR)
def send_current_event_content_to_recipients(self): client = get_redis_client() content = self.memoized_schedule.get_current_event_content(self) if isinstance(self, CaseScheduleInstanceMixin): content.set_context(case=self.case, schedule_instance=self) else: content.set_context(schedule_instance=self) logged_event = MessagingEvent.create_from_schedule_instance( self, content) recipient_count = 0 for recipient in self.expand_recipients(): recipient_count += 1 # The framework will retry sending a non-processed schedule instance # once every hour. # If we are processing a long list of recipients here and an error # occurs half-way through, we don't want to reprocess the entire list # of recipients again when the framework retries it an hour later. # So we use a non-blocking lock tied to the event due time and recipient # to make sure that we don't try resending the same content to the same # recipient more than once in the event of a retry. # If we succeed in sending the content, we don't release the lock so # that it won't retry later. If we fail in sending the content, we release # the lock so that it will retry later. lock = self.get_content_send_lock(client, recipient) if lock.acquire(blocking=False): try: content.send(recipient, logged_event) except: # Release the lock if an error happened so that we can try sending # to this recipient again later. lock.release() raise # Update the MessagingEvent for reporting if recipient_count == 0: logged_event.error(MessagingEvent.ERROR_NO_RECIPIENT) else: logged_event.completed()
def add_error_count_info(self, result, days): end_date = self.domain_now.date() start_date = end_date - timedelta(days=days - 1) counts = MessagingEvent.get_counts_of_errors(self.domain, start_date, end_date, self.timezone.zone) # Consolidate opt-out errors so they show up as one bar in the graph if SMS.ERROR_PHONE_NUMBER_OPTED_OUT in counts and MessagingEvent.ERROR_PHONE_OPTED_OUT in counts: counts[MessagingEvent.ERROR_PHONE_OPTED_OUT] += counts[SMS.ERROR_PHONE_NUMBER_OPTED_OUT] del counts[SMS.ERROR_PHONE_NUMBER_OPTED_OUT] sorted_counts = sorted(six.iteritems(counts), key=lambda item: item[1], reverse=True) result['error_count_data'] = [ { 'values': [ {'label': self.get_error_message(error), 'value': count} for error, count in sorted_counts ], }, ]
def process_survey_keyword_actions(verified_number, survey_keyword, text, msg): sender = verified_number.owner case = None args = split_args(text, survey_keyword) logged_event = MessagingEvent.create_from_keyword(survey_keyword, sender) # Log a messaging subevent for the incoming message subevent = logged_event.create_subevent_for_single_sms( msg.couch_recipient_doc_type, msg.couch_recipient, completed=True) add_msg_tags(msg, MessageMetadata(messaging_subevent_id=subevent.pk)) # Close any open sessions even if it's just an sms that we're # responding with. SQLXFormsSession.close_all_open_sms_sessions(verified_number.domain, verified_number.owner_id) if is_commcarecase(sender): case = sender args = args[1:]
def process_survey_keyword_actions(verified_number, survey_keyword, text, msg): sender = verified_number.owner case = None args = split_args(text, survey_keyword) logged_event = MessagingEvent.create_from_keyword(survey_keyword, sender) # Log a messaging subevent for the incoming message subevent = logged_event.create_subevent_for_single_sms( msg.couch_recipient_doc_type, msg.couch_recipient ) subevent.completed() add_msg_tags(msg, MessageMetadata(messaging_subevent_id=subevent.pk)) # Close any open sessions even if it's just an sms that we're # responding with. SQLXFormsSession.close_all_open_sms_sessions(verified_number.domain, verified_number.owner_id) if sender.doc_type == "CommCareCase": case = sender args = args[1:] elif sender.doc_type == "CommCareUser": if keyword_uses_form_that_requires_case(survey_keyword): if len(args) > 1: external_id = args[1] case, matches = get_case_by_external_id(verified_number.domain, external_id, sender) if matches == 0: send_keyword_response(verified_number, MSG_CASE_NOT_FOUND, logged_event) logged_event.error(MessagingEvent.ERROR_CASE_EXTERNAL_ID_NOT_FOUND) return elif matches > 1: send_keyword_response(verified_number, MSG_MULTIPLE_CASES_FOUND, logged_event) logged_event.error(MessagingEvent.ERROR_MULTIPLE_CASES_WITH_EXTERNAL_ID_FOUND) return else: send_keyword_response(verified_number, MSG_MISSING_EXTERNAL_ID, logged_event) logged_event.error(MessagingEvent.ERROR_NO_EXTERNAL_ID_GIVEN) return args = args[2:] else: args = args[1:] def cmp_fcn(a1, a2): a1_ss = (a1.action == METHOD_STRUCTURED_SMS) a2_ss = (a2.action == METHOD_STRUCTURED_SMS) if a1_ss and a2_ss: return 0 elif a1_ss: return -1 elif a2_ss: return 1 else: return 0 if case: subevent.case_id = case.get_id subevent.save() # Process structured sms actions first actions = sorted(survey_keyword.actions, cmp=cmp_fcn) for survey_keyword_action in actions: if survey_keyword_action.recipient == RECIPIENT_SENDER: contact = sender elif survey_keyword_action.recipient == RECIPIENT_OWNER: if sender.doc_type == "CommCareCase": contact = get_wrapped_owner(get_owner_id(sender)) else: contact = None elif survey_keyword_action.recipient == RECIPIENT_USER_GROUP: try: contact = Group.get(survey_keyword_action.recipient_id) assert contact.doc_type == "Group" assert contact.domain == verified_number.domain except Exception: contact = None else: contact = None if contact is None: continue if survey_keyword_action.action == METHOD_SMS: create_immediate_reminder(contact, METHOD_SMS, reminder_type=REMINDER_TYPE_KEYWORD_INITIATED, message=survey_keyword_action.message_content, case=case, logged_event=logged_event) elif survey_keyword_action.action == METHOD_SMS_SURVEY: create_immediate_reminder(contact, METHOD_SMS_SURVEY, reminder_type=REMINDER_TYPE_KEYWORD_INITIATED, form_unique_id=survey_keyword_action.form_unique_id, case=case, logged_event=logged_event) elif survey_keyword_action.action == METHOD_STRUCTURED_SMS: res = handle_structured_sms(survey_keyword, survey_keyword_action, sender, verified_number, text, send_response=True, msg=msg, case=case, text_args=args, logged_event=logged_event) if not res: # If the structured sms processing wasn't successful, don't # process any of the other actions return logged_event.completed()
def process_survey_keyword_actions(verified_number, survey_keyword, text, msg): sender = verified_number.owner case = None args = split_args(text, survey_keyword) logged_event = MessagingEvent.create_from_keyword(survey_keyword, sender) # Log a messaging subevent for the incoming message subevent = logged_event.create_subevent_for_single_sms( msg.couch_recipient_doc_type, msg.couch_recipient, completed=True ) add_msg_tags(msg, MessageMetadata(messaging_subevent_id=subevent.pk)) # Close any open sessions even if it's just an sms that we're # responding with. SQLXFormsSession.close_all_open_sms_sessions(verified_number.domain, verified_number.owner_id) if is_commcarecase(sender): case = sender args = args[1:] elif isinstance(sender, CommCareUser): if keyword_uses_form_that_requires_case(survey_keyword): if len(args) > 1: external_id = args[1] case, matches = get_case_by_external_id(verified_number.domain, external_id, sender) if matches == 0: send_keyword_response(verified_number, MSG_CASE_NOT_FOUND, logged_event) logged_event.error(MessagingEvent.ERROR_CASE_EXTERNAL_ID_NOT_FOUND) return elif matches > 1: send_keyword_response(verified_number, MSG_MULTIPLE_CASES_FOUND, logged_event) logged_event.error(MessagingEvent.ERROR_MULTIPLE_CASES_WITH_EXTERNAL_ID_FOUND) return else: send_keyword_response(verified_number, MSG_MISSING_EXTERNAL_ID, logged_event) logged_event.error(MessagingEvent.ERROR_NO_EXTERNAL_ID_GIVEN) return args = args[2:] else: args = args[1:] def cmp_fcn(a1, a2): a1_ss = (a1.action == KeywordAction.ACTION_STRUCTURED_SMS) a2_ss = (a2.action == KeywordAction.ACTION_STRUCTURED_SMS) if a1_ss and a2_ss: return 0 elif a1_ss: return -1 elif a2_ss: return 1 else: return 0 if case: subevent.case_id = case.case_id subevent.save() # Process structured sms actions first actions = sorted(survey_keyword.keywordaction_set.all(), cmp=cmp_fcn) for survey_keyword_action in actions: if survey_keyword_action.recipient == KeywordAction.RECIPIENT_SENDER: contact = sender elif survey_keyword_action.recipient == KeywordAction.RECIPIENT_OWNER: if is_commcarecase(sender): contact = get_wrapped_owner(get_owner_id(sender)) else: contact = None elif survey_keyword_action.recipient == KeywordAction.RECIPIENT_USER_GROUP: try: contact = Group.get(survey_keyword_action.recipient_id) assert contact.doc_type == "Group" assert contact.domain == verified_number.domain except Exception: contact = None else: contact = None if contact is None: continue # contact can be either a user, case, group, or location if survey_keyword_action.action in (KeywordAction.ACTION_SMS, KeywordAction.ACTION_SMS_SURVEY): if isinstance(contact, Group): recipients = list(ScheduleInstance.expand_group(contact)) elif isinstance(contact, SQLLocation): recipients = list(ScheduleInstance.expand_location_ids(contact.domain, [contact.location_id])) else: recipients = [contact] recipient_is_sender = survey_keyword_action.recipient == KeywordAction.RECIPIENT_SENDER if survey_keyword_action.action == KeywordAction.ACTION_SMS: content = SMSContent(message={'*': survey_keyword_action.message_content}) content.set_context(case=case) elif survey_keyword_action.action == KeywordAction.ACTION_SMS_SURVEY: content = SMSSurveyContent( form_unique_id=survey_keyword_action.form_unique_id, expire_after=SQLXFormsSession.MAX_SESSION_LENGTH, ) content.set_context( case=case, critical_section_already_acquired=recipient_is_sender, ) else: raise ValueError("Unexpected action %s" % survey_keyword_action.action) for recipient in recipients: phone_entry = verified_number if recipient_is_sender else None content.send(recipient, logged_event, phone_entry=phone_entry) elif survey_keyword_action.action == KeywordAction.ACTION_STRUCTURED_SMS: res = handle_structured_sms(survey_keyword, survey_keyword_action, sender, verified_number, text, send_response=True, msg=msg, case=case, text_args=args, logged_event=logged_event) if not res: # If the structured sms processing wasn't successful, don't # process any of the other actions return logged_event.completed()
def handle(verified_contact, text, msg): domain = msg.domain if not domain: return False if not EWSGhanaConfig.for_domain(domain): return False if not verified_contact: return False user = verified_contact.owner if not verified_contact.verified: if not process_verification(verified_contact, msg, VERIFICATION_KEYWORDS): logged_event = MessagingEvent.get_current_verification_event( domain, verified_contact.owner_id, verified_contact.phone_number) send_verification(domain, user, verified_contact.phone_number, logged_event) return True args = text.split() if not args: send_sms_to_verified_number(verified_contact, unicode(INVALID_MESSAGE)) return True keyword = args[0] args = args[1:] params = { 'user': user, 'domain': domain, 'args': args, 'msg': msg, 'verified_contact': verified_contact } def not_function(word): if args and re.match("del", word): return NotDeliveredHandler elif args and re.match("sub", word): return NotSubmittedHandler return None handlers = { ('help', ): HelpHandler, ('reminder', ): ReminderOnOffHandler, ('language', 'lang', 'lugha'): LanguageHandler, ('yes', 'no', 'y', 'n'): RequisitionHandler, ('undo', 'replace', 'revoke'): UndoHandler, ('soh',): SOHHandler, ('not',): not_function(args[0]) if args else None, ('rec', 'receipts', 'received'): ReceiptsHandler } def choose_handler(keyword): for k, v in handlers.iteritems(): if keyword.lower() in k: return v return None handler_class = choose_handler(keyword) handler = handler_class(**params) if handler_class else None if handler: if args: return handler.handle() else: handler.help() return True else: return SOHHandler(**params).handle()