Example #1
0
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
Example #2
0
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)
Example #3
0
def get_form_name(form_unique_id):
    try:
        form = Form.get_form(form_unique_id)
    except ResourceNotFound:
        return _("[unknown]")

    return form.full_path_name
Example #4
0
    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
Example #5
0
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
Example #6
0
    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)}
Example #7
0
    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
Example #8
0
def get_form_name(form_unique_id):
    try:
        form = Form.get_form(form_unique_id)
    except ResourceNotFound:
        return _("[unknown]")

    return form.full_path_name
Example #9
0
    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)
Example #10
0
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)
Example #11
0
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))
Example #12
0
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
Example #13
0
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))
Example #14
0
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
Example #15
0
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
Example #16
0
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())
Example #17
0
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())
Example #18
0
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
Example #19
0
 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()
Example #20
0
 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()
Example #21
0
 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()
Example #22
0
 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 {}
Example #23
0
 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
Example #24
0
 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
Example #25
0
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)
Example #26
0
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)
Example #27
0
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)
Example #28
0
    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
Example #29
0
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
Example #30
0
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
Example #31
0
    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)
Example #32
0
 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
Example #33
0
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)
Example #34
0
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
Example #35
0
 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 {}
Example #36
0
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
Example #37
0
    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'})
Example #38
0
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
Example #39
0
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
Example #40
0
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
Example #41
0
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
Example #42
0
    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'})
Example #43
0
    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'}
            )
Example #44
0
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)
Example #45
0
 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
     )
Example #46
0
    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)
Example #47
0
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)
Example #48
0
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))
Example #49
0
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))
Example #50
0
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
Example #51
0
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))
Example #52
0
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
Example #53
0
 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)
Example #54
0
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()
Example #55
0
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("")
Example #56
0
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()
Example #57
0
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()
Example #58
0
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
Example #60
0
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