def device_data(request): if "data" not in request.POST: return HttpResponseBadRequest("Missing 'data' POST parameter.") data = request.POST.get("data") data = data.strip() data_points = data.split(",") device_id = None for data_point in data_points: key_value = data_point.partition("=") key = key_value[0].strip().upper() value = key_value[2].strip() if key == "SN": device_id = value break if device_id is None: return HttpResponseBadRequest("Missing 'SN' in data string.") # This view lookup is an implicit assert that either one device exists # with the given device_id, or no devices exist with this device_id. case = CommConnectCase.view("wisepill/device", key=[device_id], include_docs=True).one() event = WisePillDeviceEvent( domain=case.domain if case is not None else None, data=data, received_on=datetime.utcnow(), case_id=case._id if case is not None else None, processed=False, ) event.save() if case is not None: survey_keywords = SurveyKeyword.get_all(case.domain) for survey_keyword in survey_keywords: if survey_keyword.keyword.upper() == "DEVICE_EVENT": for survey_keyword_action in survey_keyword.actions: if survey_keyword_action.action == METHOD_STRUCTURED_SMS: handle_structured_sms( survey_keyword, survey_keyword_action, case, None, "DEVICE_EVENT,%s" % data, send_response=False, ) event.processed = True event.save() break return HttpResponse("")
def handle_domain_keywords(v, text, msg, text_words, sessions): any_session_open = len(sessions) > 0 for survey_keyword in SurveyKeyword.get_all(v.domain): args = split_args(text, survey_keyword) keyword = args[0].upper() if keyword == survey_keyword.keyword.upper(): if any_session_open and not survey_keyword.override_open_sessions: # We don't want to override any open sessions, so just pass and # let the form session handler handle the message return False elif not contact_can_use_keyword(v, survey_keyword): # The contact type is not allowed to invoke this keyword return False else: inbound_metadata = MessageMetadata(workflow=WORKFLOW_KEYWORD, ) add_msg_tags(msg, inbound_metadata) process_survey_keyword_actions(v, survey_keyword, text, msg) return True # No keywords matched, so pass the message onto the next handler return False
def handle_domain_keywords(v, text, msg, text_words, sessions): any_session_open = len(sessions) > 0 for survey_keyword in SurveyKeyword.get_all(v.domain): args = split_args(text, survey_keyword) keyword = args[0].upper() if keyword == survey_keyword.keyword.upper(): if any_session_open and not survey_keyword.override_open_sessions: # We don't want to override any open sessions, so just pass and # let the form session handler handle the message return False elif not contact_can_use_keyword(v, survey_keyword): # The contact type is not allowed to invoke this keyword return False else: inbound_metadata = MessageMetadata( workflow=WORKFLOW_KEYWORD, ) add_msg_tags(msg, inbound_metadata) process_survey_keyword_actions(v, survey_keyword, text, msg) return True # No keywords matched, so pass the message onto the next handler return False
def structured_sms_handler(verified_number, text): # Circular Import from corehq.apps.reminders.models import SurveyKeyword, FORM_TYPE_ALL_AT_ONCE text = text.strip() if text == "": return False for survey_keyword in SurveyKeyword.get_all(verified_number.domain): if survey_keyword.form_type == FORM_TYPE_ALL_AT_ONCE: if survey_keyword.delimiter is not None: args = text.split(survey_keyword.delimiter) else: args = text.split() keyword = args[0].strip().upper() if keyword != survey_keyword.keyword.upper(): continue try: error_occurred = False error_msg = "" form_complete = False # Close any open sessions close_open_sessions(verified_number.domain, verified_number.owner_id) # Start the session form = Form.get_form(survey_keyword.form_unique_id) app = form.get_app() module = form.get_module() if verified_number.owner_doc_type == "CommCareCase": case_id = verified_number.owner_id else: # TODO: Need a way to choose the case when it's a user that's playing the form case_id = None session, responses = start_session( verified_number.domain, verified_number.owner, app, module, form, case_id=case_id, yield_responses=True, ) assert len(responses) > 0, "There should be at least one response." current_question = responses[-1] form_complete = is_form_complete(current_question) if not form_complete: if survey_keyword.use_named_args: # Arguments in the sms are named xpath_answer = {} # Dictionary of {xpath : answer} for answer in args[1:]: answer = answer.strip() answer_upper = answer.upper() if survey_keyword.named_args_separator is not None: # A separator is used for naming arguments; for example, the "=" in "register name=joe age=25" answer_parts = answer.partition(survey_keyword.named_args_separator) if answer_parts[1] != survey_keyword.named_args_separator: error_occurred = True error_msg = "ERROR: Expected name and value to be joined by" + ( " '%s'" % survey_keyword.named_args_separator ) break else: arg_name = answer_parts[0].upper().strip() xpath = survey_keyword.named_args.get(arg_name, None) if xpath is not None: if xpath in xpath_answer: error_occurred = True error_msg = "ERROR: More than one answer found for" + (" '%s'" % arg_name) break xpath_answer[xpath] = answer_parts[2].strip() else: # Ignore unexpected named arguments pass else: # No separator is used for naming arguments; for example, "update a100 b34 c5" matches = 0 for k, v in survey_keyword.named_args.items(): if answer_upper.startswith(k): matches += 1 if matches > 1: error_occurred = True error_msg = "ERROR: More than one question matches" + (" '%s'" % answer) break if v in xpath_answer: error_occurred = True error_msg = "ERROR: More than one answer found for" + (" '%s'" % k) break xpath_answer[v] = answer[len(k) :].strip() if matches == 0: # Ignore unexpected named arguments pass if error_occurred: break # Go through each question in the form, answering only the questions that the sms has answers for while not form_complete and not error_occurred: if current_question.is_error: error_occurred = True error_msg = current_question.text_prompt or "ERROR: Internal server error" break xpath = current_question.event._dict["binding"] if xpath in xpath_answer: valid, answer, _error_msg = validate_answer(current_question.event, xpath_answer[xpath]) if not valid: error_occurred = True error_msg = "ERROR: " + _error_msg break responses = _get_responses( verified_number.domain, verified_number.owner_id, answer, yield_responses=True ) else: responses = _get_responses( verified_number.domain, verified_number.owner_id, "", yield_responses=True ) current_question = responses[-1] if is_form_complete(current_question): form_complete = True else: # Arguments in the sms are not named; pass each argument to each question in order for answer in args[1:]: if form_complete: # Form is complete, ignore remaining answers break if current_question.is_error: error_occurred = True error_msg = current_question.text_prompt or "ERROR: Internal server error" break valid, answer, _error_msg = validate_answer(current_question.event, answer.strip()) if not valid: error_occurred = True error_msg = "ERROR: " + _error_msg break responses = _get_responses( verified_number.domain, verified_number.owner_id, answer, yield_responses=True ) current_question = responses[-1] form_complete = is_form_complete(current_question) # If the form isn't finished yet but we're out of arguments, try to leave each remaining question blank and continue while not form_complete and not error_occurred: responses = _get_responses( verified_number.domain, verified_number.owner_id, "", yield_responses=True ) current_question = responses[-1] if current_question.is_error: error_occurred = True error_msg = current_question.text_prompt or "ERROR: Internal server error" if is_form_complete(current_question): form_complete = True except Exception: logging.exception( "Could not process structured sms for verified number %s, domain %s, keyword %s" % (verified_number._id, verified_number.domain, keyword) ) error_occurred = True error_msg = "ERROR: Internal server error" if error_occurred: send_sms_to_verified_number(verified_number, error_msg) if error_occurred or not form_complete: session = XFormsSession.get(session._id) session.end(False) session.save() return True return False
def manage_surveys(request, domain): context = { "domain" : domain, "keywords" : SurveyKeyword.get_all(domain) } return render_to_response(request, "reminders/partial/manage_surveys.html", context)
def sms_keyword_handler(v, text, msg=None): from corehq.apps.reminders.models import SurveyKeyword text = text.strip() if text == "": return False sessions = XFormsSession.get_all_open_sms_sessions(v.domain, v.owner_id) any_session_open = len(sessions) > 0 text_words = text.upper().split() if text.startswith("#"): if len(text_words) > 0 and text_words[0] == "#START": # Respond to "#START <keyword>" command if len(text_words) > 1: sk = SurveyKeyword.get_keyword(v.domain, text_words[1]) if sk is not None: if len(sk.initiator_doc_type_filter) > 0 and v.owner_doc_type not in sk.initiator_doc_type_filter: # The contact type is not allowed to invoke this keyword return False process_survey_keyword_actions(v, sk, text[6:].strip(), msg=msg) else: send_sms_to_verified_number( v, "Keyword not found: '%s'." % text_words[1], workflow=WORKFLOW_KEYWORD ) else: send_sms_to_verified_number(v, "Usage: #START <keyword>", workflow=WORKFLOW_KEYWORD) elif len(text_words) > 0 and text_words[0] == "#STOP": # Respond to "#STOP" keyword XFormsSession.close_all_open_sms_sessions(v.domain, v.owner_id) elif len(text_words) > 0 and text_words[0] == "#CURRENT": # Respond to "#CURRENT" keyword if len(sessions) == 1: resp = current_question(sessions[0].session_id) send_sms_to_verified_number( v, resp.event.text_prompt, workflow=sessions[0].workflow, reminder_id=sessions[0].reminder_id, xforms_session_couch_id=sessions[0]._id, ) else: # Response to unknown command send_sms_to_verified_number(v, "Unknown command: '%s'" % text_words[0]) if msg is not None: msg.workflow = WORKFLOW_KEYWORD msg.save() return True else: for survey_keyword in SurveyKeyword.get_all(v.domain): if survey_keyword.delimiter is not None: args = text.split(survey_keyword.delimiter) else: args = text.split() keyword = args[0].strip().upper() if keyword == survey_keyword.keyword.upper(): if any_session_open and not survey_keyword.override_open_sessions: # We don't want to override any open sessions, so just pass and let the form session handler handle the message return False elif ( len(survey_keyword.initiator_doc_type_filter) > 0 and v.owner_doc_type not in survey_keyword.initiator_doc_type_filter ): # The contact type is not allowed to invoke this keyword return False else: process_survey_keyword_actions(v, survey_keyword, text, msg=msg) if msg is not None: msg.workflow = WORKFLOW_KEYWORD msg.save() return True # No keywords matched, so pass the message onto the next handler return False
def manage_keywords(request, domain): context = {"domain": domain, "keywords": SurveyKeyword.get_all(domain)} return render(request, "reminders/partial/manage_keywords.html", context)
def structured_sms_handler(verified_number, text): # Circular Import from corehq.apps.reminders.models import SurveyKeyword, FORM_TYPE_ALL_AT_ONCE text = text.strip() if text == "": return False for survey_keyword in SurveyKeyword.get_all(verified_number.domain): if survey_keyword.form_type == FORM_TYPE_ALL_AT_ONCE: if survey_keyword.delimiter is not None: args = text.split(survey_keyword.delimiter) else: args = text.split() keyword = args[0].strip().upper() if keyword != survey_keyword.keyword.upper(): continue try: error_occurred = False error_msg = "" form_complete = False # Close any open sessions close_open_sessions(verified_number.domain, verified_number.owner_id) # Start the session form = Form.get_form(survey_keyword.form_unique_id) app = form.get_app() module = form.get_module() if verified_number.owner_doc_type == "CommCareCase": case_id = verified_number.owner_id else: #TODO: Need a way to choose the case when it's a user that's playing the form case_id = None session, responses = start_session(verified_number.domain, verified_number.owner, app, module, form, case_id=case_id, yield_responses=True) assert len( responses) > 0, "There should be at least one response." current_question = responses[-1] form_complete = is_form_complete(current_question) if not form_complete: if survey_keyword.use_named_args: # Arguments in the sms are named xpath_answer = {} # Dictionary of {xpath : answer} for answer in args[1:]: answer = answer.strip() answer_upper = answer.upper() if survey_keyword.named_args_separator is not None: # A separator is used for naming arguments; for example, the "=" in "register name=joe age=25" answer_parts = answer.partition( survey_keyword.named_args_separator) if answer_parts[ 1] != survey_keyword.named_args_separator: error_occurred = True error_msg = "ERROR: Expected name and value to be joined by" + ( " '%s'" % survey_keyword.named_args_separator) break else: arg_name = answer_parts[0].upper().strip() xpath = survey_keyword.named_args.get( arg_name, None) if xpath is not None: if xpath in xpath_answer: error_occurred = True error_msg = "ERROR: More than one answer found for" + ( " '%s'" % arg_name) break xpath_answer[xpath] = answer_parts[ 2].strip() else: # Ignore unexpected named arguments pass else: # No separator is used for naming arguments; for example, "update a100 b34 c5" matches = 0 for k, v in survey_keyword.named_args.items(): if answer_upper.startswith(k): matches += 1 if matches > 1: error_occurred = True error_msg = "ERROR: More than one question matches" + ( " '%s'" % answer) break if v in xpath_answer: error_occurred = True error_msg = "ERROR: More than one answer found for" + ( " '%s'" % k) break xpath_answer[v] = answer[ len(k):].strip() if matches == 0: # Ignore unexpected named arguments pass if error_occurred: break # Go through each question in the form, answering only the questions that the sms has answers for while not form_complete and not error_occurred: if current_question.is_error: error_occurred = True error_msg = current_question.text_prompt or "ERROR: Internal server error" break xpath = current_question.event._dict["binding"] if xpath in xpath_answer: valid, answer, _error_msg = validate_answer( current_question.event, xpath_answer[xpath]) if not valid: error_occurred = True error_msg = "ERROR: " + _error_msg break responses = _get_responses( verified_number.domain, verified_number.owner_id, answer, yield_responses=True) else: responses = _get_responses( verified_number.domain, verified_number.owner_id, "", yield_responses=True) current_question = responses[-1] if is_form_complete(current_question): form_complete = True else: # Arguments in the sms are not named; pass each argument to each question in order for answer in args[1:]: if form_complete: # Form is complete, ignore remaining answers break if current_question.is_error: error_occurred = True error_msg = current_question.text_prompt or "ERROR: Internal server error" break valid, answer, _error_msg = validate_answer( current_question.event, answer.strip()) if not valid: error_occurred = True error_msg = "ERROR: " + _error_msg break responses = _get_responses( verified_number.domain, verified_number.owner_id, answer, yield_responses=True) current_question = responses[-1] form_complete = is_form_complete(current_question) # If the form isn't finished yet but we're out of arguments, try to leave each remaining question blank and continue while not form_complete and not error_occurred: responses = _get_responses( verified_number.domain, verified_number.owner_id, "", yield_responses=True) current_question = responses[-1] if current_question.is_error: error_occurred = True error_msg = current_question.text_prompt or "ERROR: Internal server error" if is_form_complete(current_question): form_complete = True except Exception: logging.exception( "Could not process structured sms for verified number %s, domain %s, keyword %s" % (verified_number._id, verified_number.domain, keyword)) error_occurred = True error_msg = "ERROR: Internal server error" if error_occurred: send_sms_to_verified_number(verified_number, error_msg) if error_occurred or not form_complete: session = XFormsSession.get(session._id) session.end(False) session.save() return True return False
def deleteAllObjects(self): for obj in SurveyKeyword.get_all(self.domain): obj.delete() Keyword.objects.filter(domain=self.domain).delete()