def keyword_uses_form_that_requires_case(survey_keyword): for action in survey_keyword.actions: if action.action in [METHOD_SMS_SURVEY, METHOD_STRUCTURED_SMS]: form = Form.get_form(action.form_unique_id) if form.requires_case(): return True return False
def create_data_source_from_app(request, domain): if request.method == 'POST': form = ConfigurableDataSourceFromAppForm(domain, request.POST) if form.is_valid(): # save config app_source = form.app_source_helper.get_app_source( form.cleaned_data) app = Application.get(app_source.application) if app_source.source_type == 'case': data_source = get_case_data_source(app, app_source.source) data_source.save() messages.success( request, _(u"Data source created for '{}'".format( app_source.source))) else: assert app_source.source_type == 'form' xform = Form.get_form(app_source.source) data_source = get_form_data_source(app, xform) data_source.save() messages.success( request, _(u"Data source created for '{}'".format( xform.default_name()))) return HttpResponseRedirect( reverse('edit_configurable_data_source', args=[domain, data_source._id])) else: form = ConfigurableDataSourceFromAppForm(domain) context = _shared_context(domain) context['sources_map'] = form.app_source_helper.all_sources context['form'] = form return render(request, 'userreports/data_source_from_app.html', context)
def get_form_name(form_unique_id): try: form = Form.get_form(form_unique_id) except ResourceNotFound: return _("[unknown]") return form.full_path_name
def dehydrate_module(self, app, module, langs): """ Convert a Module object to a JValue representation with just the good parts. NOTE: This is not a tastypie "magic"-name method to dehydrate the "module" field; there is no such field. """ dehydrated = {} dehydrated['case_type'] = module.case_type dehydrated['case_properties'] = app_manager_util.get_case_properties(app, [module.case_type], defaults=['name'])[module.case_type] dehydrated['forms'] = [] for form in module.forms: form = Form.get_form(form.unique_id) form_jvalue = { 'xmlns': form.xmlns, 'name': form.name, 'questions': form.get_questions(langs), } dehydrated['forms'].append(form_jvalue) return dehydrated
def dehydrate_module(self, app, module, langs): """ Convert a Module object to a JValue representation with just the good parts. NOTE: This is not a tastypie "magic"-name method to dehydrate the "module" field; there is no such field. """ try: dehydrated = {} dehydrated["case_type"] = module.case_type dehydrated["case_properties"] = app_manager_util.get_case_properties( app, [module.case_type], defaults=["name"] )[module.case_type] dehydrated["unique_id"] = module.unique_id dehydrated["forms"] = [] for form in module.forms: form_unique_id = form.unique_id form = Form.get_form(form_unique_id) form_jvalue = { "xmlns": form.xmlns, "name": form.name, "questions": form.get_questions(langs, include_translations=True), "unique_id": form_unique_id, } dehydrated["forms"].append(form_jvalue) return dehydrated except Exception as e: return {"error": unicode(e)}
def dehydrate_module(self, app, module, langs): """ Convert a Module object to a JValue representation with just the good parts. NOTE: This is not a tastypie "magic"-name method to dehydrate the "module" field; there is no such field. """ dehydrated = {} dehydrated['case_type'] = module.case_type dehydrated['case_properties'] = app_manager_util.get_case_properties( app, [module.case_type], defaults=['name'])[module.case_type] dehydrated['forms'] = [] for form in module.forms: form = Form.get_form(form.unique_id) form_jvalue = { 'xmlns': form.xmlns, 'name': form.name, 'questions': form.get_questions(langs), } dehydrated['forms'].append(form_jvalue) return dehydrated
def post(self, request, *args, **kwargs): if self.form.is_valid(): app_source = self.form.app_source_helper.get_app_source( self.form.cleaned_data) app = Application.get(app_source.application) if app_source.source_type == 'case': data_source = get_case_data_source(app, app_source.source) data_source.save() messages.success( request, _(u"Data source created for '{}'".format( app_source.source))) else: assert app_source.source_type == 'form' xform = Form.get_form(app_source.source) data_source = get_form_data_source(app, xform) data_source.save() messages.success( request, _(u"Data source created for '{}'".format( xform.default_name()))) return HttpResponseRedirect( reverse(EditDataSourceView.urlname, args=[self.domain, data_source._id])) return self.get(request, *args, **kwargs)
def create_data_source_from_app(request, domain): if request.method == 'POST': form = ConfigurableDataSourceFromAppForm(domain, request.POST) if form.is_valid(): # save config app_source = form.app_source_helper.get_app_source(form.cleaned_data) app = Application.get(app_source.application) if app_source.source_type == 'case': data_source = get_case_data_source(app, app_source.source) data_source.save() messages.success(request, _(u"Data source created for '{}'".format(app_source.source))) else: assert app_source.source_type == 'form' xform = Form.get_form(app_source.source) data_source = get_form_data_source(app, xform) data_source.save() messages.success(request, _(u"Data source created for '{}'".format(xform.default_name()))) return HttpResponseRedirect(reverse('edit_configurable_data_source', args=[domain, data_source._id])) else: form = ConfigurableDataSourceFromAppForm(domain) context = _shared_context(domain) context['sources_map'] = form.app_source_helper.all_sources context['form'] = form return render(request, 'userreports/data_source_from_app.html', context)
def start_session_from_keyword(survey_keyword, verified_number): try: form_unique_id = survey_keyword.form_unique_id form = Form.get_form(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) if len(responses) > 0: message = format_message_list(responses) send_sms_to_verified_number(verified_number, message) except Exception: logging.exception( "Exception while starting survey for keyword %s, domain %s" % (survey_keyword.keyword, verified_number.domain))
def keyword_uses_form_that_requires_case(survey_keyword): for action in survey_keyword.keywordaction_set.all(): if action.action in [KeywordAction.ACTION_SMS_SURVEY, KeywordAction.ACTION_STRUCTURED_SMS]: form = Form.get_form(action.form_unique_id) if form.requires_case(): return True return False
def get_form_data_schema(request, domain, form_unique_id): """Get data schema One of `app_id` or `form_unique_id` is required. `app_id` is ignored if `form_unique_id` is provided. :returns: A list of data source schema definitions. A data source schema definition is a dictionary. For details on the content of the dictionary, see https://github.com/dimagi/Vellum/blob/master/src/datasources.js """ data = [] try: form, app = Form.get_form(form_unique_id, and_app=True) except ResourceConflict: raise Http404() if app.domain != domain: raise Http404() try: data.append(get_session_schema(form)) if form.requires_case() or is_usercase_in_use(domain): data.append(get_casedb_schema(form)) except Exception: logger.exception("schema error") return HttpResponseBadRequest("schema error, see log for details") data.extend( sorted(item_lists_by_domain(domain), key=lambda x: x['name'].lower()) ) kw = {} if "pretty" in request.GET: kw["indent"] = 2 return HttpResponse(json.dumps(data, **kw))
def get_form(form_unique_id, include_app_module=False): form = Form.get_form(form_unique_id) if include_app_module: app = form.get_app() module = form.get_module() return (app, module, form) else: return form
def form_casexml(request, domain, form_unique_id): try: form, app = Form.get_form(form_unique_id, and_app=True) except ResourceNotFound: raise Http404() if domain != app.domain: raise Http404() return HttpResponse(form.create_casexml())
def _resolve_from_template(self, template, query_context): # todo: support other types and options assert template.type == 'form' startdate, enddate = get_daterange_start_end_dates(template.time_range) xmlns = Form.get_form(template.source_id).xmlns return FormES().user_id(query_context.user._id).xmlns([xmlns]).submitted( gte=startdate, lte=enddate, ).size(0).count()
def _resolve_from_template(self, template, query_context): # todo: support other types and options assert template.type == 'form' startdate, enddate = get_daterange_start_end_dates(template.time_range) xmlns = Form.get_form(template.source_id).xmlns return FormES().user_id(query_context.user._id).xmlns( [xmlns]).submitted( gte=startdate, lte=enddate, ).size(0).count()
def _resolve_from_template(self, template, query_context): # todo: support other types and options assert template.type == 'form' startdate, enddate = get_daterange_start_end_dates(template.time_range) xmlns = Form.get_form(template.source_id).xmlns return FormData.objects.filter( user_id=query_context.user._id, xmlns=xmlns, received_on__gte=startdate, received_on__lt=enddate, ).count()
def search_form_by_id_response(self): """ Returns a dict of {"id": [form unique id], "text": [full form path]} """ form_unique_id = self.search_term try: form = Form.get_form(form_unique_id) assert form.get_app().domain == self.domain return {"text": form.full_path_name, "id": form_unique_id} except: return {}
def clean_form_unique_id(self): value = self.cleaned_data.get("form_unique_id") if value is None: raise ValidationError(_("Please create a form first, and then add a keyword for it.")) try: form = CCHQForm.get_form(value) app = form.get_app() assert app.domain == self._cchq_domain except Exception: raise ValidationError(_("Invalid form chosen.")) return value
def clean(self): cleaned_data = super(ConfigurableFormDataSourceFromAppForm, self).clean() app = Application.get(cleaned_data['app_id']) form = Form.get_form(cleaned_data['form_id']) if form.get_app()._id != app._id: raise ValidationError(_('Form name {} not found in application {}').format( form.default_name(), app.name )) self.app = app self.form = form return cleaned_data
def get_app_module_form(call_log_entry, logged_subevent): """ Returns (app, module, form, error) """ try: form = Form.get_form(call_log_entry.form_unique_id) app = form.get_app() module = form.get_module() return (app, module, form, False) except: log_error(MessagingEvent.ERROR_CANNOT_FIND_FORM, call_log_entry, logged_subevent) return (None, None, None, True)
def get_app_module_form(form_unique_id, logged_subevent=None): """ Returns (app, module, form, error, error_code) """ try: form = Form.get_form(form_unique_id) app = form.get_app() module = form.get_module() return (app, module, form, False, None) except: log_error(MessagingEvent.ERROR_CANNOT_FIND_FORM, logged_subevent) return (None, None, None, True, MSG_FORM_NOT_FOUND)
def get_memoized_app_module_form(self, domain): try: form = Form.get_form(self.form_unique_id) app = form.get_app() module = form.get_module() except (ResourceNotFound, XFormIdNotUnique): return None, None, None if app.domain != domain: return None, None, None return app, module, form
def validate_form_unique_id(form_unique_id, domain): error_msg = _('Invalid form chosen.') try: form = CCHQForm.get_form(form_unique_id) app = form.get_app() except Exception: raise ValidationError(error_msg) if app.domain != domain: raise ValidationError(error_msg) return form_unique_id
def get_memoized_app_module_form(self, domain): try: form = Form.get_form(self.form_unique_id) app = form.get_app() module = form.get_module() except (ResourceNotFound, XFormIdNotUnique): return None, None, None, None if app.domain != domain: return None, None, None, None return app, module, form, form_requires_input(form)
def clean_form_unique_id(self): value = self.cleaned_data.get("form_unique_id") if value is None: raise ValidationError( _("Please create a form first, and then add a keyword for it.") ) try: form = CCHQForm.get_form(value) app = form.get_app() assert app.domain == self._cchq_domain except Exception: raise ValidationError(_("Invalid form chosen.")) return value
def get_form_name(form_unique_id): form = Form.get_form(form_unique_id) app = form.get_app() module = form.get_module() lang = app.langs[0] try: module_name = module.name[lang] except Exception: module_name = module.name.items()[0][1] try: form_name = form.name[lang] except Exception: form_name = form.name.items()[0][1] return app.name + "/" + module_name + "/" + form_name
def search_form_by_id_response(self): """ Returns a dict of {"id": [form unique id], "text": [full form path]} """ form_unique_id = self.search_term try: form = Form.get_form(form_unique_id) assert form.get_app().domain == self.domain return { 'text': form.full_path_name, 'id': form_unique_id, } except: return {}
def __init__(self, domain, app, source_type, source_id): assert (source_type in ['case', 'form']) self.domain = domain self.app = app self.source_type = source_type # source_id is a case type of form id self.source_id = source_id if self.source_type == 'form': self.source_form = Form.get_form(self.source_id) self.source_xform = XForm(self.source_form.source) if self.source_type == 'case': prop_map = get_case_properties( self.app, [self.source_id], defaults=DEFAULT_CASE_PROPERTY_DATATYPES.keys() ) self.case_properties = sorted(set(prop_map[self.source_id]) | {'closed'})
def rename_language(request, domain, form_unique_id): old_code = request.POST.get('oldCode') new_code = request.POST.get('newCode') try: form, app = Form.get_form(form_unique_id, and_app=True) except ResourceConflict: raise Http404() if app.domain != domain: raise Http404() try: form.rename_xform_language(old_code, new_code) app.save() return HttpResponse(json.dumps({"status": "ok"})) except XFormException as e: response = HttpResponse(json.dumps({'status': 'error', 'message': unicode(e)}), status=409) return response
def rename_language(request, domain, form_unique_id): old_code = request.POST.get("oldCode") new_code = request.POST.get("newCode") try: form, app = Form.get_form(form_unique_id, and_app=True) except ResourceConflict: raise Http404() if app.domain != domain: raise Http404() try: form.rename_xform_language(old_code, new_code) app.save() return HttpResponse(json.dumps({"status": "ok"})) except XFormException as e: response = HttpResponse(json.dumps({"status": "error", "message": unicode(e)}), status=409) return response
def get_form_name(form_unique_id): try: form = Form.get_form(form_unique_id) except ResourceNotFound: return _("[unknown]") app = form.get_app() module = form.get_module() lang = app.langs[0] try: module_name = module.name[lang] except Exception: module_name = module.name.items()[0][1] try: form_name = form.name[lang] except Exception: form_name = form.name.items()[0][1] return app.name + "/" + module_name + "/" + form_name
def __init__(self, domain, app, source_type, source_id): assert (source_type in ['case', 'form']) self.domain = domain self.app = app self.source_type = source_type # source_id is a case type of form id self.source_id = source_id if self.source_type == 'form': self.source_form = Form.get_form(self.source_id) self.source_xform = XForm(self.source_form.source) if self.source_type == 'case': prop_map = get_case_properties( self.app, [self.source_id], defaults=DEFAULT_CASE_PROPERTY_DATATYPES.keys()) self.case_properties = sorted( set(prop_map[self.source_id]) | {'closed'})
def __init__(self, domain, app, source_type, source_id): assert (source_type in ['case', 'form']) self.domain = domain self.app = app self.source_type = source_type # source_id is a case type of form id self.source_id = source_id if self.source_type == 'form': self.source_form = Form.get_form(self.source_id) self.source_xform = XForm(self.source_form.source) if self.source_type == 'case': property_builder = ParentCasePropertyBuilder( self.app, DEFAULT_CASE_PROPERTY_DATATYPES.keys() ) self.case_properties = list( property_builder.get_properties(self.source_id) | {'closed'} )
def xform_display(request, domain, form_unique_id): try: form, app = Form.get_form(form_unique_id, and_app=True) except ResourceNotFound: raise Http404() if domain != app.domain: raise Http404() langs = [request.GET.get('lang')] + app.langs questions = form.get_questions(langs, include_triggers=True, include_groups=True) if request.GET.get('format') == 'html': questions = [FormQuestionResponse(q) for q in questions] return render(request, "app_manager/xform_display.html", {'questions': questions_in_hierarchy(questions)}) else: return json_response(questions)
def container_fieldset(self): source_name = '' if self.source_type == 'case': source_name = self.report_source_id if self.source_type == 'form': source_name = Form.get_form(self.report_source_id).default_name() return crispy.Fieldset( '', crispy.Fieldset( _legend( _("Rows"), _('This report will show one row for each {name} {source}').format( name=source_name, source=self.source_type ) ) ), self.column_fieldset, self.filter_fieldset )
def post(self, request, *args, **kwargs): if self.form.is_valid(): app_source = self.form.app_source_helper.get_app_source(self.form.cleaned_data) app = Application.get(app_source.application) if app_source.source_type == 'case': data_source = get_case_data_source(app, app_source.source) data_source.save() messages.success(request, _(u"Data source created for '{}'".format(app_source.source))) else: assert app_source.source_type == 'form' xform = Form.get_form(app_source.source) data_source = get_form_data_source(app, xform) data_source.save() messages.success(request, _(u"Data source created for '{}'".format(xform.default_name()))) return HttpResponseRedirect(reverse( EditDataSourceView.urlname, args=[self.domain, data_source._id] )) return self.get(request, *args, **kwargs)
def xform_display(request, domain, form_unique_id): try: form, app = Form.get_form(form_unique_id, and_app=True) except ResourceNotFound: raise Http404() if domain != app.domain: raise Http404() langs = [request.GET.get('lang')] + app.langs questions = form.get_questions(langs, include_triggers=True, include_groups=True) if request.GET.get('format') == 'html': questions = [FormQuestionResponse(q) for q in questions] return render(request, 'app_manager/v1/xform_display.html', { 'questions': questions_in_hierarchy(questions) }) else: return json_response(questions)
def get_form_data_schema(request, domain, form_unique_id): """Get data schema One of `app_id` or `form_unique_id` is required. `app_id` is ignored if `form_unique_id` is provided. :returns: A list of data source schema definitions. A data source schema definition is a dictionary. For details on the content of the dictionary, see https://github.com/dimagi/Vellum/blob/master/src/datasources.js """ data = [] try: form, app = Form.get_form(form_unique_id, and_app=True) except ResourceConflict: raise Http404() if app.domain != domain: raise Http404() try: data.append(get_session_schema(form)) if form.requires_case() or is_usercase_in_use(domain): data.append(get_casedb_schema(form)) except AppManagerException as e: notify_exception(request, message=str(e)) return HttpResponseBadRequest( str(e) or _("There is an error in the case management of your application. " "Please fix the error to see case properties in this tree") ) except Exception as e: notify_exception(request, message=six.text_type(e)) return HttpResponseBadRequest("schema error, see log for details") data.extend( sorted(item_lists_by_domain(domain), key=lambda x: x['name'].lower()) ) kw = {} if "pretty" in request.GET: kw["indent"] = 2 return HttpResponse(json.dumps(data, **kw))
def start_session_from_keyword(survey_keyword, verified_number): try: form_unique_id = survey_keyword.form_unique_id form = Form.get_form(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) if len(responses) > 0: message = format_message_list(responses) send_sms_to_verified_number(verified_number, message) except Exception: logging.exception("Exception while starting survey for keyword %s, domain %s" % (survey_keyword.keyword, verified_number.domain))
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 get_form_data_schema(request, domain, form_unique_id): """Get data schema One of `app_id` or `form_unique_id` is required. `app_id` is ignored if `form_unique_id` is provided. :returns: A list of data source schema definitions. A data source schema definition is a dictionary with the following format: ``` { "id": string (default instance id) "uri": string (instance src) "path": string (path of root nodeset, not including `instance(...)`) "name": string (human readable name) "structure": { element: { "name": string (optional human readable name) "structure": { nested-element: { ... } }, }, ref-element: { "reference": { "source": string (optional data source id, defaults to this data source) "subset": string (optional subset id) "key": string (referenced property) } }, @attribute: { }, ... }, "subsets": [ { "id": string (unique identifier for this subset) "key": string (unique identifier property name) "name": string (optional human readable name) "structure": { ... } "related": { string (relationship): string (related subset name), ... } }, ... ] } ``` A structure may contain nested structure elements. A nested element may contain one of "structure" (a concrete structure definition) or "reference" (a link to some other structure definition). Any structure item may have a human readable "name". """ data = [] try: form, app = Form.get_form(form_unique_id, and_app=True) except ResourceConflict: raise Http404() if app.domain != domain: raise Http404() try: data.append(get_session_schema(form)) if form and form.requires_case(): data.append(get_casedb_schema(form)) except Exception as e: return HttpResponseBadRequest(e) data.extend( sorted(item_lists_by_domain(domain), key=lambda x: x['name'].lower())) kw = {} if "pretty" in request.GET: kw["indent"] = 2 return HttpResponse(json.dumps(data, **kw))
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 __init__(self, domain, data_source_type, data_source_id): super(FormDataSourceMeta, self).__init__(domain, data_source_type, data_source_id) self.source_form = Form.get_form(self.data_source_id) self.source_xform = XForm(self.source_form.source)
def fire_sms_survey_event(reminder, handler, recipients, verified_numbers, logged_event): current_event = reminder.current_event if reminder.callback_try_count > 0: # Leaving this as an explicit reminder that all survey related actions now happen # in a different process. Eventually all of this code will be removed when we move # to the new reminders framework. pass 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 = 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 form_requires_input(form)) if no_verified_number and cant_use_unverified_number: logged_subevent.error( MessagingEvent.ERROR_NO_TWO_WAY_PHONE_NUMBER) continue if verified_number: pb = PhoneBlacklist.get_by_phone_number_or_none( verified_number.phone_number) else: pb = PhoneBlacklist.get_by_phone_number_or_none( unverified_number) if pb and not pb.send_sms: logged_subevent.error(MessagingEvent.ERROR_PHONE_OPTED_OUT) continue with critical_section_for_smsforms_sessions(recipient.get_id): # Get the case to submit the form against, if any if (is_commcarecase(recipient) and not handler.force_surveys_to_use_triggered_case): case_id = recipient.case_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: if current_event.callback_timeout_intervals: if handler.submit_partial_forms: expire_after = sum( current_event.callback_timeout_intervals) reminder_intervals = current_event.callback_timeout_intervals[: -1] else: expire_after = SQLXFormsSession.MAX_SESSION_LENGTH reminder_intervals = current_event.callback_timeout_intervals submit_partially_completed_forms = handler.submit_partial_forms include_case_updates_in_partial_submissions = handler.include_case_side_effects else: expire_after = SQLXFormsSession.MAX_SESSION_LENGTH reminder_intervals = [] submit_partially_completed_forms = False include_case_updates_in_partial_submissions = False session, responses = start_session( SQLXFormsSession.create_session_object( reminder.domain, recipient, verified_number.phone_number if verified_number else unverified_number, app, form, expire_after=expire_after, reminder_intervals=reminder_intervals, submit_partially_completed_forms= submit_partially_completed_forms, include_case_updates_in_partial_submissions= include_case_updates_in_partial_submissions), 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 = get_formplayer_exception( reminder.domain, e) logged_subevent.error( MessagingEvent.ERROR_TOUCHFORMS_ERROR, additional_error_text=human_readable_message) if touchforms_error_is_config_error(reminder.domain, 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, logged_subevent=logged_subevent) 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 = XFormsSession.latest_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, 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 initiate_outbound_call(call_log_entry, *args, **kwargs): phone_number = call_log_entry.phone_number if phone_number.startswith("+"): phone_number = phone_number[1:] if phone_number.startswith("91"): phone_number = "0" + phone_number[2:] else: call_log_entry.error = True call_log_entry.error_message = "Kookoo can only send to Indian phone numbers." call_log_entry.save() return False form = Form.get_form(call_log_entry.form_unique_id) app = form.get_app() module = form.get_module() # Only precache the first response if it's not an only-label form, otherwise we could end up # submitting the form regardless of whether the person actually answers the call. if form_requires_input(form): recipient = call_log_entry.recipient case_id = get_case_id(call_log_entry) session, responses = start_session(recipient.domain, recipient, app, module, form, case_id, yield_responses=True, session_type=XFORMS_SESSION_IVR) ivr_responses = [] if len(responses) == 0: call_log_entry.error = True call_log_entry.error_message = "No prompts seen in form. Please check that the form does not have errors." call_log_entry.save() return False for response in responses: ivr_responses.append( format_ivr_response(response.event.caption, app)) input_length = get_input_length(responses[-1]) call_log_entry.use_precached_first_response = True call_log_entry.xforms_session_id = session.session_id url_base = get_url_base() params = urlencode({ "phone_no": phone_number, "api_key": kwargs["api_key"], "outbound_version": "2", "url": url_base + reverse("corehq.apps.kookoo.views.ivr"), "callback_url": url_base + reverse("corehq.apps.kookoo.views.ivr_finished"), }) url = "http://www.kookoo.in/outbound/outbound.php?%s" % params response = urlopen(url, timeout=settings.IVR_GATEWAY_TIMEOUT).read() root = XML(response) for child in root: if child.tag.endswith("status"): status = child.text elif child.tag.endswith("message"): message = child.text if status == "queued": call_log_entry.error = False call_log_entry.gateway_session_id = "KOOKOO-" + message elif status == "error": call_log_entry.error = True call_log_entry.error_message = message else: call_log_entry.error = True call_log_entry.error_message = "Unknown status received from Kookoo." if call_log_entry.error: call_log_entry.use_precached_first_response = False if call_log_entry.use_precached_first_response: call_log_entry.first_response = get_http_response_string( call_log_entry.gateway_session_id, ivr_responses, collect_input=True, hang_up=False, input_length=input_length) call_log_entry.save() return not call_log_entry.error
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_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