def process_disconnect(call_log_entry): if call_log_entry.xforms_session_id is not None: session = get_session_by_session_id(call_log_entry.xforms_session_id) if session.is_open: if call_log_entry.submit_partial_form: submit_unfinished_form(session.session_id, call_log_entry.include_case_side_effects) else: session.end(completed=False) session.save()
def process_disconnect(call_log_entry): if call_log_entry.xforms_session_id is not None: session = get_session_by_session_id(call_log_entry.xforms_session_id) if session.is_open: if call_log_entry.submit_partial_form: submit_unfinished_form( session.session_id, call_log_entry.include_case_side_effects) else: session.end(completed=False) session.save()
def fire_sms_survey_event(reminder, handler, recipients, verified_numbers, logged_event): if reminder.callback_try_count > 0: # Handle timeouts if handler.submit_partial_forms and ( reminder.callback_try_count == len(reminder.current_event.callback_timeout_intervals) ): # Submit partial form completions for session_id in reminder.xforms_session_ids: submit_unfinished_form(session_id, handler.include_case_side_effects) else: # Resend current question for session_id in reminder.xforms_session_ids: session = get_session_by_session_id(session_id) if session.end_time is None: vn = VerifiedNumber.view( "sms/verified_number_by_owner_id", key=session.connection_id, include_docs=True ).first() if vn is not None: metadata = MessageMetadata( workflow=get_workflow(handler), reminder_id=reminder._id, xforms_session_couch_id=session._id, ) resp = current_question(session_id) send_sms_to_verified_number(vn, resp.event.text_prompt, metadata) else: reminder.xforms_session_ids = [] domain_obj = Domain.get_by_name(reminder.domain, strict=True) # Get the app, module, and form try: form_unique_id = reminder.current_event.form_unique_id form = Form.get_form(form_unique_id) app = form.get_app() module = form.get_module() except Exception: logged_event.error(MessagingEvent.ERROR_CANNOT_FIND_FORM) return # Start a touchforms session for each recipient for recipient in recipients: logged_subevent = logged_event.create_subevent(handler, reminder, recipient) verified_number, unverified_number = get_recipient_phone_number(reminder, recipient, verified_numbers) no_verified_number = verified_number is None cant_use_unverified_number = ( unverified_number is None or not domain_obj.send_to_duplicated_case_numbers or form_requires_input(form) ) if no_verified_number and cant_use_unverified_number: logged_subevent.error(MessagingEvent.ERROR_NO_TWO_WAY_PHONE_NUMBER) continue key = "start-sms-survey-for-contact-%s" % recipient.get_id with CriticalSection([key], timeout=60): # Get the case to submit the form against, if any if isinstance(recipient, CommCareCase) and not handler.force_surveys_to_use_triggered_case: case_id = recipient.get_id else: case_id = reminder.case_id if form.requires_case() and not case_id: logged_subevent.error(MessagingEvent.ERROR_NO_CASE_GIVEN) continue # Close all currently open sessions SQLXFormsSession.close_all_open_sms_sessions(reminder.domain, recipient.get_id) # Start the new session try: session, responses = start_session( reminder.domain, recipient, app, module, form, case_id, case_for_case_submission=handler.force_surveys_to_use_triggered_case, ) except TouchformsError as e: human_readable_message = e.response_data.get("human_readable_message", None) logged_subevent.error( MessagingEvent.ERROR_TOUCHFORMS_ERROR, additional_error_text=human_readable_message ) if touchforms_error_is_config_error(e): # Don't reraise the exception because this means there are configuration # issues with the form that need to be fixed continue else: # Reraise the exception so that the framework retries it again later raise except Exception as e: logged_subevent.error(MessagingEvent.ERROR_TOUCHFORMS_ERROR) # Reraise the exception so that the framework retries it again later raise session.survey_incentive = handler.survey_incentive session.workflow = get_workflow(handler) session.reminder_id = reminder._id session.save() reminder.xforms_session_ids.append(session.session_id) logged_subevent.xforms_session = session logged_subevent.save() # Send out first message if len(responses) > 0: message = format_message_list(responses) metadata = MessageMetadata( workflow=get_workflow(handler), reminder_id=reminder._id, xforms_session_couch_id=session._id ) if verified_number: send_sms_to_verified_number(verified_number, message, metadata) else: send_sms(reminder.domain, recipient, unverified_number, message, metadata) logged_subevent.completed()
def fire_sms_survey_event(reminder, handler, recipients, verified_numbers, logged_event): if reminder.callback_try_count > 0: # Handle timeouts if handler.submit_partial_forms and ( reminder.callback_try_count == len( reminder.current_event.callback_timeout_intervals)): # Submit partial form completions for session_id in reminder.xforms_session_ids: submit_unfinished_form(session_id, handler.include_case_side_effects) else: # Resend current question for session_id in reminder.xforms_session_ids: session = get_session_by_session_id(session_id) if session.end_time is None: vn = VerifiedNumber.view("sms/verified_number_by_owner_id", key=session.connection_id, include_docs=True).first() if vn is not None: metadata = MessageMetadata( workflow=get_workflow(handler), reminder_id=reminder._id, xforms_session_couch_id=session._id, ) resp = current_question(session_id) send_sms_to_verified_number(vn, resp.event.text_prompt, metadata) else: reminder.xforms_session_ids = [] domain_obj = Domain.get_by_name(reminder.domain, strict=True) # Get the app, module, and form try: form_unique_id = reminder.current_event.form_unique_id form = Form.get_form(form_unique_id) app = form.get_app() module = form.get_module() except Exception: logged_event.error(MessagingEvent.ERROR_CANNOT_FIND_FORM) return # Start a touchforms session for each recipient for recipient in recipients: logged_subevent = logged_event.create_subevent( handler, reminder, recipient) verified_number, unverified_number = get_recipient_phone_number( reminder, recipient, verified_numbers) no_verified_number = verified_number is None cant_use_unverified_number = ( unverified_number is None or not domain_obj.send_to_duplicated_case_numbers or form_requires_input(form)) if no_verified_number and cant_use_unverified_number: logged_subevent.error( MessagingEvent.ERROR_NO_TWO_WAY_PHONE_NUMBER) continue key = "start-sms-survey-for-contact-%s" % recipient.get_id with CriticalSection([key], timeout=60): # Get the case to submit the form against, if any if (isinstance(recipient, CommCareCase) and not handler.force_surveys_to_use_triggered_case): case_id = recipient.get_id else: case_id = reminder.case_id if form.requires_case() and not case_id: logged_subevent.error(MessagingEvent.ERROR_NO_CASE_GIVEN) continue # Close all currently open sessions SQLXFormsSession.close_all_open_sms_sessions( reminder.domain, recipient.get_id) # Start the new session try: session, responses = start_session( reminder.domain, recipient, app, module, form, case_id, case_for_case_submission=handler. force_surveys_to_use_triggered_case) except TouchformsError as e: human_readable_message = e.response_data.get( 'human_readable_message', None) logged_subevent.error( MessagingEvent.ERROR_TOUCHFORMS_ERROR, additional_error_text=human_readable_message) if touchforms_error_is_config_error(e): # Don't reraise the exception because this means there are configuration # issues with the form that need to be fixed continue else: # Reraise the exception so that the framework retries it again later raise except Exception as e: logged_subevent.error( MessagingEvent.ERROR_TOUCHFORMS_ERROR) # Reraise the exception so that the framework retries it again later raise session.survey_incentive = handler.survey_incentive session.workflow = get_workflow(handler) session.reminder_id = reminder._id session.save() reminder.xforms_session_ids.append(session.session_id) logged_subevent.xforms_session = session logged_subevent.save() # Send out first message if len(responses) > 0: message = format_message_list(responses) metadata = MessageMetadata( workflow=get_workflow(handler), reminder_id=reminder._id, xforms_session_couch_id=session._id, ) if verified_number: send_sms_to_verified_number(verified_number, message, metadata) else: send_sms(reminder.domain, recipient, unverified_number, message, metadata) logged_subevent.completed()
def incoming(phone_number, backend_module, gateway_session_id, ivr_event, input_data=None): # Look up the call if one already exists call_log_entry = CallLog.view("sms/call_by_session", startkey=[gateway_session_id, {}], endkey=[gateway_session_id], descending=True, include_docs=True, limit=1).one() answer_is_valid = False # This will be set to True if IVR validation passes error_occurred = False # This will be set to False if touchforms validation passes (i.e., no form constraints fail) if call_log_entry is not None and backend_module: if ivr_event == IVR_EVENT_NEW_CALL and call_log_entry.use_precached_first_response: return HttpResponse(call_log_entry.first_response) form = Form.get_form(call_log_entry.form_unique_id) app = form.get_app() module = form.get_module() recipient = call_log_entry.recipient if ivr_event == IVR_EVENT_NEW_CALL: case_id = call_log_entry.case_id case_for_case_submission = call_log_entry.case_for_case_submission session, responses = start_session(recipient.domain, recipient, app, module, form, case_id, yield_responses=True, session_type=XFORMS_SESSION_IVR, case_for_case_submission=case_for_case_submission) call_log_entry.xforms_session_id = session.session_id elif ivr_event == IVR_EVENT_INPUT: if call_log_entry.xforms_session_id is not None: current_q = current_question(call_log_entry.xforms_session_id) if validate_answer(input_data, current_q): answer_is_valid = True responses = _get_responses(recipient.domain, recipient._id, input_data, yield_responses=True, session_id=call_log_entry.xforms_session_id) else: call_log_entry.current_question_retry_count += 1 responses = [current_q] else: responses = [] else: responses = [] ivr_responses = [] hang_up = False for response in responses: if response.is_error: error_occurred = True call_log_entry.current_question_retry_count += 1 if response.text_prompt is None: ivr_responses = [] break else: ivr_responses.append(format_ivr_response(response.text_prompt, app)) elif response.event.type == "question": ivr_responses.append(format_ivr_response(response.event.caption, app)) elif response.event.type == "form-complete": hang_up = True if answer_is_valid and not error_occurred: call_log_entry.current_question_retry_count = 0 if call_log_entry.max_question_retries is not None and call_log_entry.current_question_retry_count > call_log_entry.max_question_retries: # Force hang-up ivr_responses = [] if len(ivr_responses) == 0: hang_up = True input_length = None if hang_up: if call_log_entry.xforms_session_id is not None: # Process disconnect session = get_session_by_session_id(call_log_entry.xforms_session_id) if session.end_time is None: if call_log_entry.submit_partial_form: submit_unfinished_form(session.session_id, call_log_entry.include_case_side_effects) else: session.end(completed=False) session.save() else: # Set input_length to let the ivr gateway know how many digits we need to collect. # Have to get the current question again, since the last XFormsResponse in responses # may not have an event if it was a response to a constraint error. if error_occurred: current_q = current_question(call_log_entry.xforms_session_id) else: current_q = responses[-1] input_length = get_input_length(current_q) call_log_entry.save() return HttpResponse(backend_module.get_http_response_string(gateway_session_id, ivr_responses, collect_input=(not hang_up), hang_up=hang_up, input_length=input_length)) # If not processed, just log the call if call_log_entry: # No need to log, already exists return HttpResponse("") cleaned_number = phone_number if cleaned_number is not None and len(cleaned_number) > 0 and cleaned_number[0] == "+": cleaned_number = cleaned_number[1:] # Try to look up the verified number entry v = VerifiedNumber.view("sms/verified_number_by_number", key=cleaned_number, include_docs=True ).one() # If none was found, try to match only the last digits of numbers in the database if v is None: v = VerifiedNumber.view("sms/verified_number_by_suffix", key=cleaned_number, include_docs=True ).one() # Save the call entry msg = CallLog( phone_number=cleaned_number, direction=INCOMING, date=datetime.utcnow(), backend_api=backend_module.API_ID if backend_module else None, gateway_session_id=gateway_session_id, ) if v is not None: msg.domain = v.domain msg.couch_recipient_doc_type = v.owner_doc_type msg.couch_recipient = v.owner_id msg.save() return HttpResponse("")
def fire_sms_survey_event(reminder, handler, recipients, verified_numbers): if reminder.callback_try_count > 0: # Handle timeouts if handler.submit_partial_forms and (reminder.callback_try_count == len(reminder.current_event.callback_timeout_intervals)): # Submit partial form completions for session_id in reminder.xforms_session_ids: submit_unfinished_form(session_id, handler.include_case_side_effects) else: # Resend current question for session_id in reminder.xforms_session_ids: session = get_session_by_session_id(session_id) if session.end_time is None: vn = VerifiedNumber.view("sms/verified_number_by_owner_id", key=session.connection_id, include_docs=True).first() if vn is not None: metadata = MessageMetadata( workflow=get_workflow(handler), reminder_id=reminder._id, xforms_session_couch_id=session._id, ) resp = current_question(session_id) send_sms_to_verified_number(vn, resp.event.text_prompt, metadata) return True else: reminder.xforms_session_ids = [] # Get the app, module, and form try: form_unique_id = reminder.current_event.form_unique_id form = Form.get_form(form_unique_id) app = form.get_app() module = form.get_module() except Exception: raise_error(reminder, ERROR_FORM) return False # Start a touchforms session for each recipient for recipient in recipients: verified_number, unverified_number = get_recipient_phone_number( reminder, recipient, verified_numbers) domain_obj = Domain.get_by_name(reminder.domain, strict=True) no_verified_number = verified_number is None cant_use_unverified_number = (unverified_number is None or not domain_obj.send_to_duplicated_case_numbers or form_requires_input(form)) if no_verified_number and cant_use_unverified_number: if len(recipients) == 1: raise_error(reminder, ERROR_NO_VERIFIED_NUMBER) return False else: continue key = "start-sms-survey-for-contact-%s" % recipient.get_id with CriticalSection([key], timeout=60): # Close all currently open sessions SQLXFormsSession.close_all_open_sms_sessions(reminder.domain, recipient.get_id) # Start the new session if (isinstance(recipient, CommCareCase) and not handler.force_surveys_to_use_triggered_case): case_id = recipient.get_id else: case_id = reminder.case_id session, responses = start_session(reminder.domain, recipient, app, module, form, case_id, case_for_case_submission= handler.force_surveys_to_use_triggered_case) session.survey_incentive = handler.survey_incentive session.workflow = get_workflow(handler) session.reminder_id = reminder._id session.save() reminder.xforms_session_ids.append(session.session_id) # Send out first message if len(responses) > 0: message = format_message_list(responses) metadata = MessageMetadata( workflow=get_workflow(handler), reminder_id=reminder._id, xforms_session_couch_id=session._id, ) if verified_number: result = send_sms_to_verified_number(verified_number, message, metadata) else: result = send_sms(reminder.domain, recipient, unverified_number, message, metadata) if len(recipients) == 1: return result return True