def close_open_sessions(domain, connection_id): sessions = XFormsSession.view("smsforms/open_sms_sessions_by_connection", key=[domain, connection_id], include_docs=True).all() for session in sessions: session.end(False) session.save()
def close_open_sessions(domain, connection_id): sessions = XFormsSession.view( "smsforms/open_sms_sessions_by_connection", key=[domain, connection_id], include_docs=True ).all() for session in sessions: session.end(False) session.save()
def handle(self, *args, **options): sessions = XFormsSession.view( "smsforms/open_sms_sessions_by_connection", include_docs=True).all() for session in sessions: try: get_raw_instance(session.session_id) except InvalidSessionIdException: print "Closing %s %s" % (session.domain, session._id) session.end(False) session.save() except Exception as e: print "An unexpected error occurred when processing %s %s" % ( session.domain, session._id) print e
def handle(self, *args, **options): sessions = XFormsSession.view( "smsforms/open_sms_sessions_by_connection", include_docs=True ).all() for session in sessions: try: get_raw_instance(session.session_id) except InvalidSessionIdException: print "Closing %s %s" % (session.domain, session._id) session.end(False) session.save() except Exception as e: print "An unexpected error occurred when processing %s %s" % (session.domain, session._id) print e
def rows(self): startdate = json_format_datetime(self.datespan.startdate_utc) enddate = json_format_datetime(self.datespan.enddate_utc) data = CallLog.by_domain_date(self.domain, startdate, enddate) result = [] # Store the results of lookups for faster loading contact_cache = {} form_map = {} xforms_sessions = {} direction_map = { INCOMING: _("Incoming"), OUTGOING: _("Outgoing"), } # Retrieve message log options message_log_options = getattr(settings, "MESSAGE_LOG_OPTIONS", {}) abbreviated_phone_number_domains = message_log_options.get("abbreviated_phone_number_domains", []) abbreviate_phone_number = (self.domain in abbreviated_phone_number_domains) for call in data: doc_info = self.get_recipient_info(call, contact_cache) form_unique_id = call.form_unique_id if form_unique_id in [None, ""]: form_name = "-" elif form_unique_id in form_map: form_name = form_map.get(form_unique_id) else: form_name = get_form_name(form_unique_id) form_map[form_unique_id] = form_name phone_number = call.phone_number if abbreviate_phone_number and phone_number is not None: phone_number = phone_number[0:7] if phone_number[0:1] == "+" else phone_number[0:6] timestamp = tz_utils.adjust_datetime_to_timezone(call.date, pytz.utc.zone, self.timezone.zone) if call.direction == INCOMING: answered = "-" else: answered = _("Yes") if call.answered else _("No") if call.xforms_session_id: xforms_sessions[call.xforms_session_id] = None row = [ call.xforms_session_id, self._fmt_timestamp(timestamp), self._fmt_contact_link(call, doc_info), self._fmt(phone_number), self._fmt(direction_map.get(call.direction,"-")), self._fmt(form_name), self._fmt("-"), self._fmt(answered), self._fmt(call.duration), self._fmt(_("Yes") if call.error else _("No")), self._fmt(call.error_message), ] if self.request.couch_user.is_previewer(): row.append(self._fmt(call.gateway_session_id)) result.append(row) # Look up the XFormsSession documents 500 at a time. # Had to do this because looking up one document at a time slows things # down a lot. all_session_ids = xforms_sessions.keys() limit = 500 range_max = int(ceil(len(all_session_ids) * 1.0 / limit)) for i in range(range_max): lower_bound = i * limit upper_bound = (i + 1) * limit sessions = XFormsSession.view("smsforms/sessions_by_touchforms_id", keys=all_session_ids[lower_bound:upper_bound], include_docs=True).all() for session in sessions: xforms_sessions[session.session_id] = session.submission_id # Add into the final result the link to the submission based on the # outcome of the above lookups. final_result = [] for row in result: final_row = row[1:] session_id = row[0] if session_id: submission_id = xforms_sessions[session_id] if submission_id: final_row[5] = self._fmt_submission_link(submission_id) final_result.append(final_row) return final_result
def form_session_handler(v, text): # Circular Import from corehq.apps.reminders.models import SurveyKeyword, FORM_TYPE_ONE_BY_ONE # Handle incoming sms session = XFormsSession.view( "smsforms/open_sms_sessions_by_connection", key=[v.domain, v.owner_id], include_docs=True ).one() text_words = text.upper().split() # Respond to "#START <keyword>" command if len(text_words) > 0 and text_words[0] == "#START": if len(text_words) > 1: sk = SurveyKeyword.get_keyword(v.domain, text_words[1]) if sk is not None and sk.form_type == FORM_TYPE_ONE_BY_ONE: if session is not None: session.end(False) session.save() start_session_from_keyword(sk, v) else: send_sms_to_verified_number(v, "Survey '" + text_words[1] + "' not found.") else: send_sms_to_verified_number(v, "Usage: #START <keyword>") # Respond to "#STOP" keyword elif len(text_words) > 0 and text_words[0] == "#STOP": if session is not None: session.end(False) session.save() # Respond to "#CURRENT" keyword elif len(text_words) > 0 and text_words[0] == "#CURRENT": if session is not None: resp = current_question(session.session_id) send_sms_to_verified_number(v, resp.event.text_prompt) # Respond to unknown command elif len(text_words) > 0 and text_words[0][0] == "#": send_sms_to_verified_number(v, "Unknown command '" + text_words[0] + "'") # If there's an open session, treat the inbound text as the answer to the next question elif session is not None: resp = current_question(session.session_id) event = resp.event valid, text, error_msg = validate_answer(event, text) if valid: responses = _get_responses(v.domain, v.owner_id, text) if len(responses) > 0: response_text = format_message_list(responses) send_sms_to_verified_number(v, response_text) else: send_sms_to_verified_number(v, error_msg + event.text_prompt) # Try to match the text against a keyword to start a survey elif len(text_words) > 0: sk = SurveyKeyword.get_keyword(v.domain, text_words[0]) if sk is not None and sk.form_type == FORM_TYPE_ONE_BY_ONE: start_session_from_keyword(sk, v) # TODO should clarify what scenarios this handler actually handles. i.e., # should the error responses instead be handler by some generic error/fallback # handler return True
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 = XFormsSession.view("smsforms/sessions_by_touchforms_id", startkey=[session_id], endkey=[session_id, {}], include_docs=True).one() 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 as e: 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 # Close all currently open sessions XFormsSession.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
def fire(self, reminder): """ Sends the message associated with the given CaseReminder's current event. reminder The CaseReminder which to fire. return True on success, False on failure """ # Get the proper recipient recipient = reminder.recipient # Retrieve the VerifiedNumber entry for the recipient try: verified_number = recipient.get_verified_number() except Exception: verified_number = None # Get the language of the recipient try: lang = recipient.get_language_code() except Exception: lang = None if reminder.method == "survey": # Close all currently open sessions sessions = XFormsSession.view("smsforms/open_sessions_by_connection", key=[reminder.domain, recipient.get_id], include_docs=True).all() for session in sessions: session.end(False) session.save() # Start the new session 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 as e: print e print "ERROR: Could not load survey form for handler " + reminder.handler_id + ", event " + str(reminder.current_event_sequence_num) return False session, responses = start_session(reminder.domain, recipient, app, module, form, reminder.case_id) # Send out first message if len(responses) > 0: message = format_message_list(responses) if verified_number is not None: return send_sms_to_verified_number(verified_number, message) else: return True else: # If it is a callback reminder and the callback has been received, skip sending the next timeout message if (reminder.method == "callback" or reminder.method == "callback_test") and len(reminder.current_event.callback_timeout_intervals) > 0 and (reminder.callback_try_count > 0): if CallLog.inbound_call_exists(recipient.doc_type, recipient._id, reminder.last_fired): reminder.callback_received = True return True elif len(reminder.current_event.callback_timeout_intervals) == reminder.callback_try_count: # On the last callback timeout, instead of sending the SMS again, log the missed callback event = EventLog( domain = reminder.domain, date = self.get_now(), event_type = MISSED_EXPECTED_CALLBACK ) if verified_number is not None: event.couch_recipient_doc_type = verified_number.owner_doc_type event.couch_recipient = verified_number.owner_id event.save() return True reminder.last_fired = self.get_now() message = reminder.current_event.message.get(lang, reminder.current_event.message[self.default_lang]) message = Message.render(message, case=reminder.case.case_properties()) if reminder.method == "sms" or reminder.method == "callback": if verified_number is not None: return send_sms_to_verified_number(verified_number, message) elif self.recipient == RECIPIENT_USER: # If there is no verified number, but the recipient is a CommCareUser, still try to send it try: phone_number = reminder.user.phone_number except Exception: # If the user has no phone number, we cannot send any SMS return False return send_sms(reminder.domain, reminder.user_id, phone_number, message) else: return False elif reminder.method == "test" or reminder.method == "callback_test": print(message) return True
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 = XFormsSession.view( "smsforms/sessions_by_touchforms_id", startkey=[session_id], endkey=[session_id, {}], include_docs=True).one() 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 as e: 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 # Close all currently open sessions XFormsSession.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
def form_session_handler(v, text): # Circular Import from corehq.apps.reminders.models import SurveyKeyword # Handle incoming sms session = XFormsSession.view("smsforms/open_sms_sessions_by_connection", key=[v.domain, v.owner_id], include_docs=True).one() text_words = text.upper().split() # Respond to "#START <keyword>" command if len(text_words) > 0 and text_words[0] == "#START": if len(text_words) > 1: sk = SurveyKeyword.get_keyword(v.domain, text_words[1]) if sk is not None: if session is not None: session.end(False) session.save() start_session_from_keyword(sk, v) else: send_sms_to_verified_number(v, "Survey '" + text_words[1] + "' not found.") else: send_sms_to_verified_number(v, "Usage: #START <keyword>") # Respond to "#STOP" keyword elif len(text_words) > 0 and text_words[0] == "#STOP": if session is not None: session.end(False) session.save() # Respond to "#CURRENT" keyword elif len(text_words) > 0 and text_words[0] == "#CURRENT": if session is not None: resp = current_question(session.session_id) send_sms_to_verified_number(v, resp.event.text_prompt) # Respond to unknown command elif len(text_words) > 0 and text_words[0][0] == "#": send_sms_to_verified_number(v, "Unknown command '" + text_words[0] + "'") # If there's an open session, treat the inbound text as the answer to the next question elif session is not None: resp = current_question(session.session_id) event = resp.event valid = False text = text.strip() upper_text = text.upper() # Validate select if event.datatype == "select": # Try to match on phrase (i.e., "Yes" or "No") choices = format_choices(event._dict["choices"]) if upper_text in choices: text = str(choices[upper_text]) valid = True else: try: answer = int(text) if answer >= 1 and answer <= len(event._dict["choices"]): valid = True except ValueError: pass # Validate multiselect elif event.datatype == "multiselect": choices = format_choices(event._dict["choices"]) max_index = len(event._dict["choices"]) proposed_answers = text.split() final_answers = {} try: if event._dict.get("required", True): assert len(proposed_answers) > 0 for answer in proposed_answers: upper_answer = answer.upper() if upper_answer in choices: final_answers[str(choices[upper_answer])] = "" else: int_answer = int(answer) assert int_answer >= 1 and int_answer <= max_index final_answers[str(int_answer)] = "" text = " ".join(final_answers.keys()) valid = True except Exception: pass # Validate int elif event.datatype == "int": try: int(text) valid = True except ValueError: pass # Validate float elif event.datatype == "float": try: float(text) valid = True except ValueError: pass # Validate longint elif event.datatype == "longint": try: long(text) valid = True except ValueError: pass # Validate date (Format: YYYYMMDD) elif event.datatype == "date": try: assert len(text) == 8 int(text) text = text[0:4] + "-" + text[4:6] + "-" + text[6:] parse(text) valid = True except Exception: pass # Validate time (Format: HHMM, 24-hour) elif event.datatype == "time": try: assert len(text) == 4 hour = int(text[0:2]) minute = int(text[2:]) assert hour >= 0 and hour <= 23 assert minute >= 0 and minute <= 59 text = "%s:%s" % (hour, minute) valid = True except Exception: pass # Other question types pass else: valid = True if valid: responses = _get_responses(v.domain, v.owner_id, text) if len(responses) > 0: response_text = format_message_list(responses) send_sms_to_verified_number(v, response_text) else: error_msg = "Invalid Response. " + event.text_prompt send_sms_to_verified_number(v, error_msg) # Try to match the text against a keyword to start a survey elif len(text_words) > 0: sk = SurveyKeyword.get_keyword(v.domain, text_words[0]) if sk is not None: start_session_from_keyword(sk, v) # TODO should clarify what scenarios this handler actually handles. i.e., # should the error responses instead be handler by some generic error/fallback # handler return True
def fire_sms_survey_event(reminder, handler, recipients, verified_numbers): if handler.recipient in [RECIPIENT_CASE, RECIPIENT_SURVEY_SAMPLE]: 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 = XFormsSession.view("smsforms/sessions_by_touchforms_id", startkey=[session_id], endkey=[session_id, {}], include_docs=True).one() if session.end_time is None: vn = VerifiedNumber.view("sms/verified_number_by_owner_id", key=session.connection_id, include_docs=True).one() if vn is not None: resp = current_question(session_id) send_sms_to_verified_number(vn, resp.event.text_prompt) 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 as e: raise_error(reminder, ERROR_FORM) return False # Start a touchforms session for each recipient for recipient in recipients: verified_number = verified_numbers[recipient.get_id] if verified_number is None: if len(recipients) == 1: raise_error(reminder, ERROR_NO_VERIFIED_NUMBER) return False else: raise_warning() # ERROR_NO_VERIFIED_NUMBER continue # Close all currently open sessions close_open_sessions(reminder.domain, recipient.get_id) # Start the new session session, responses = start_session(reminder.domain, recipient, app, module, form, recipient.get_id) session.survey_incentive = handler.survey_incentive session.save() reminder.xforms_session_ids.append(session.session_id) # Send out first message if len(responses) > 0: message = format_message_list(responses) result = send_sms_to_verified_number(verified_number, message) if not result: raise_warning() # Could not send SMS if len(recipients) == 1: return result return True else: # TODO: Make sure the above flow works for RECIPIENT_USER and RECIPIENT_OWNER return False
def form_session_handler(v, text): # Circular Import from corehq.apps.reminders.models import SurveyKeyword, FORM_TYPE_ONE_BY_ONE # Handle incoming sms session = XFormsSession.view("smsforms/open_sms_sessions_by_connection", key=[v.domain, v.owner_id], include_docs=True).one() text_words = text.upper().split() # Respond to "#START <keyword>" command if len(text_words) > 0 and text_words[0] == "#START": if len(text_words) > 1: sk = SurveyKeyword.get_keyword(v.domain, text_words[1]) if sk is not None and sk.form_type == FORM_TYPE_ONE_BY_ONE: if session is not None: session.end(False) session.save() start_session_from_keyword(sk, v) else: send_sms_to_verified_number( v, "Survey '" + text_words[1] + "' not found.") else: send_sms_to_verified_number(v, "Usage: #START <keyword>") # Respond to "#STOP" keyword elif len(text_words) > 0 and text_words[0] == "#STOP": if session is not None: session.end(False) session.save() # Respond to "#CURRENT" keyword elif len(text_words) > 0 and text_words[0] == "#CURRENT": if session is not None: resp = current_question(session.session_id) send_sms_to_verified_number(v, resp.event.text_prompt) # Respond to unknown command elif len(text_words) > 0 and text_words[0][0] == "#": send_sms_to_verified_number(v, "Unknown command '" + text_words[0] + "'") # If there's an open session, treat the inbound text as the answer to the next question elif session is not None: resp = current_question(session.session_id) event = resp.event valid, text, error_msg = validate_answer(event, text) if valid: responses = _get_responses(v.domain, v.owner_id, text) if len(responses) > 0: response_text = format_message_list(responses) send_sms_to_verified_number(v, response_text) else: send_sms_to_verified_number(v, error_msg + event.text_prompt) # Try to match the text against a keyword to start a survey elif len(text_words) > 0: sk = SurveyKeyword.get_keyword(v.domain, text_words[0]) if sk is not None and sk.form_type == FORM_TYPE_ONE_BY_ONE: start_session_from_keyword(sk, v) # TODO should clarify what scenarios this handler actually handles. i.e., # should the error responses instead be handler by some generic error/fallback # handler return True
def fire_sms_survey_event(reminder, handler, recipients, verified_numbers): if handler.recipient in [RECIPIENT_CASE, RECIPIENT_SURVEY_SAMPLE]: 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 = XFormsSession.view( "smsforms/sessions_by_touchforms_id", startkey=[session_id], endkey=[session_id, {}], include_docs=True).one() if session.end_time is None: vn = VerifiedNumber.view( "sms/verified_number_by_owner_id", key=session.connection_id, include_docs=True).one() if vn is not None: resp = current_question(session_id) send_sms_to_verified_number( vn, resp.event.text_prompt) 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 as e: raise_error(reminder, ERROR_FORM) return False # Start a touchforms session for each recipient for recipient in recipients: verified_number = verified_numbers[recipient.get_id] if verified_number is None: if len(recipients) == 1: raise_error(reminder, ERROR_NO_VERIFIED_NUMBER) return False else: raise_warning() # ERROR_NO_VERIFIED_NUMBER continue # Close all currently open sessions close_open_sessions(reminder.domain, recipient.get_id) # Start the new session session, responses = start_session(reminder.domain, recipient, app, module, form, recipient.get_id) session.survey_incentive = handler.survey_incentive session.save() reminder.xforms_session_ids.append(session.session_id) # Send out first message if len(responses) > 0: message = format_message_list(responses) result = send_sms_to_verified_number( verified_number, message) if not result: raise_warning() # Could not send SMS if len(recipients) == 1: return result return True else: # TODO: Make sure the above flow works for RECIPIENT_USER and RECIPIENT_OWNER return False
def rows(self): startdate = json_format_datetime(self.datespan.startdate_utc) enddate = json_format_datetime(self.datespan.enddate_utc) data = CallLog.by_domain_date(self.domain, startdate, enddate) result = [] # Store the results of lookups for faster loading contact_cache = {} form_map = {} xforms_sessions = {} direction_map = { INCOMING: _("Incoming"), OUTGOING: _("Outgoing"), } # Retrieve message log options message_log_options = getattr(settings, "MESSAGE_LOG_OPTIONS", {}) abbreviated_phone_number_domains = message_log_options.get( "abbreviated_phone_number_domains", []) abbreviate_phone_number = (self.domain in abbreviated_phone_number_domains) for call in data: doc_info = self.get_recipient_info(call, contact_cache) form_unique_id = call.form_unique_id if form_unique_id in [None, ""]: form_name = "-" elif form_unique_id in form_map: form_name = form_map.get(form_unique_id) else: form_name = get_form_name(form_unique_id) form_map[form_unique_id] = form_name phone_number = call.phone_number if abbreviate_phone_number and phone_number is not None: phone_number = phone_number[0:7] if phone_number[ 0:1] == "+" else phone_number[0:6] timestamp = tz_utils.adjust_datetime_to_timezone( call.date, pytz.utc.zone, self.timezone.zone) if call.direction == INCOMING: answered = "-" else: answered = _("Yes") if call.answered else _("No") if call.xforms_session_id: xforms_sessions[call.xforms_session_id] = None row = [ call.xforms_session_id, self._fmt_timestamp(timestamp), self._fmt_contact_link(call, doc_info), self._fmt(phone_number), self._fmt(direction_map.get(call.direction, "-")), self._fmt(form_name), self._fmt("-"), self._fmt(answered), self._fmt(call.duration), self._fmt(_("Yes") if call.error else _("No")), self._fmt(call.error_message), ] if self.request.couch_user.is_previewer(): row.append(self._fmt(call.gateway_session_id)) result.append(row) # Look up the XFormsSession documents 500 at a time. # Had to do this because looking up one document at a time slows things # down a lot. all_session_ids = xforms_sessions.keys() limit = 500 range_max = int(ceil(len(all_session_ids) * 1.0 / limit)) for i in range(range_max): lower_bound = i * limit upper_bound = (i + 1) * limit sessions = XFormsSession.view( "smsforms/sessions_by_touchforms_id", keys=all_session_ids[lower_bound:upper_bound], include_docs=True).all() for session in sessions: xforms_sessions[session.session_id] = session.submission_id # Add into the final result the link to the submission based on the # outcome of the above lookups. final_result = [] for row in result: final_row = row[1:] session_id = row[0] if session_id: submission_id = xforms_sessions[session_id] if submission_id: final_row[5] = self._fmt_submission_link(submission_id) final_result.append(final_row) return final_result
def incoming(phone_number, text, backend_api): phone_without_plus = str(phone_number) if phone_without_plus[0] == "+": phone_without_plus = phone_without_plus[1:] phone_with_plus = "+" + phone_without_plus # Circular Import from corehq.apps.reminders.models import SurveyKeyword v = VerifiedNumber.view("sms/verified_number_by_number", key=phone_without_plus, include_docs=True ).one() # Log message in message log msg = SMSLog( phone_number = phone_with_plus, direction = INCOMING, date = datetime.utcnow(), text = text, backend_api = backend_api ) if v is not None: msg.couch_recipient_doc_type = v.owner_doc_type msg.couch_recipient = v.owner_id msg.domain = v.domain msg.save() # Handle incoming sms if v is not None: session = XFormsSession.view("smsforms/open_sessions_by_connection", key=[v.domain, v.owner_id], include_docs=True).one() text_words = text.upper().split() # Respond to "#START <keyword>" command if len(text_words) > 0 and text_words[0] == "#START": if len(text_words) > 1: sk = SurveyKeyword.get_keyword(v.domain, text_words[1]) if sk is not None: if session is not None: session.end(False) session.save() start_session_from_keyword(sk, v) else: send_sms_to_verified_number(v, "Survey '" + text_words[1] + "' not found.") else: send_sms_to_verified_number(v, "Usage: #START <keyword>") # Respond to "#STOP" keyword elif len(text_words) > 0 and text_words[0] == "#STOP": if session is not None: session.end(False) session.save() # Respond to "#CURRENT" keyword elif len(text_words) > 0 and text_words[0] == "#CURRENT": if session is not None: resp = current_question(session.session_id) send_sms_to_verified_number(v, resp.event.text_prompt) # Respond to unknown command elif len(text_words) > 0 and text_words[0][0] == "#": send_sms_to_verified_number(v, "Unknown command '" + text_words[0] + "'") # If there's an open session, treat the inbound text as the answer to the next question elif session is not None: resp = current_question(session.session_id) event = resp.event valid = False error_msg = None # Validate select questions if event.datatype == "select": try: answer = int(text.strip()) if answer >= 1 and answer <= len(event._dict["choices"]): valid = True except Exception: pass if not valid: error_msg = "Invalid Response. " + event.text_prompt # For now, anything else passes else: valid = True if valid: responses = get_responses(msg) if len(responses) > 0: response_text = format_message_list(responses) send_sms_to_verified_number(v, response_text) else: send_sms_to_verified_number(v, error_msg) # Try to match the text against a keyword to start a survey elif len(text_words) > 0: sk = SurveyKeyword.get_keyword(v.domain, text_words[0]) if sk is not None: start_session_from_keyword(sk, v) else: #TODO: Registration via SMS pass