Example #1
0
 def _add_columns_for_case_details(_module):
     _module.case_details.short.columns = [
         DetailColumn(header={'en': 'a'},
                      model='case',
                      field='a',
                      format='plain',
                      case_tile_field='header'),
         DetailColumn(header={'en': 'b'},
                      model='case',
                      field='b',
                      format='plain',
                      case_tile_field='top_left'),
         DetailColumn(header={'en': 'c'},
                      model='case',
                      field='c',
                      format='enum',
                      enum=[
                          MappingItem(key='male', value={'en': 'Male'}),
                          MappingItem(key='female', value={'en': 'Female'}),
                      ],
                      case_tile_field='sex'),
         DetailColumn(header={'en': 'd'},
                      model='case',
                      field='d',
                      format='plain',
                      case_tile_field='bottom_left'),
         DetailColumn(header={'en': 'e'},
                      model='case',
                      field='e',
                      format='date',
                      case_tile_field='date'),
     ]
Example #2
0
    def test_case_tile_pull_down(self):
        app = Application.new_app('domain', 'Untitled Application')

        module = app.add_module(Module.new_module('Untitled Module', None))
        module.case_type = 'patient'
        module.case_details.short.use_case_tiles = True
        module.case_details.short.persist_tile_on_forms = True
        module.case_details.short.pull_down_tile = True

        module.case_details.short.columns = [
            DetailColumn(
                header={'en': 'a'},
                model='case',
                field='a',
                format='plain',
                case_tile_field='header'
            ),
            DetailColumn(
                header={'en': 'b'},
                model='case',
                field='b',
                format='plain',
                case_tile_field='top_left'
            ),
            DetailColumn(
                header={'en': 'c'},
                model='case',
                field='c',
                format='enum',
                enum=[
                    MappingItem(key='male', value={'en': 'Male'}),
                    MappingItem(key='female', value={'en': 'Female'}),
                ],
                case_tile_field='sex'
            ),
            DetailColumn(
                header={'en': 'd'},
                model='case',
                field='d',
                format='plain',
                case_tile_field='bottom_left'
            ),
            DetailColumn(
                header={'en': 'e'},
                model='case',
                field='e',
                format='date',
                case_tile_field='date'
            ),
        ]

        form = app.new_form(0, "Untitled Form", None)
        form.xmlns = 'http://id_m0-f0'
        form.requires = 'case'

        self.assertXmlPartialEqual(
            self.get_xml('case_tile_pulldown_session'),
            app.create_suite(),
            "./entry/session"
        )
Example #3
0
    def test_case_detail_icon_mapping(self):
        app = Application.new_app('domain',
                                  'Untitled Application',
                                  application_version=APP_V2)

        module = app.add_module(Module.new_module('Untitled Module', None))
        module.case_type = 'patient'

        module.case_details.short.columns = [
            DetailColumn(
                header={'en': 'Age range'},
                model='case',
                field='age',
                format='enum-image',
                enum=[
                    MappingItem(key='10',
                                value={'en': 'jr://icons/10-year-old.png'}),
                    MappingItem(key='age > 50',
                                value={'en': 'jr://icons/old-icon.png'}),
                ],
            ),
        ]

        key1_varname = '10'
        key2_varname = hashlib.md5('age > 50').hexdigest()[:8]

        icon_mapping_spec = """
            <partial>
              <template form="image" width="13%">
                <text>
                  <xpath function="if(age = '10', $k{key1_varname}, if(age > 50, $h{key2_varname}, ''))">
                    <variable name="h{key2_varname}">
                      <locale id="m0.case_short.case_age_1.enum.h{key2_varname}"/>
                    </variable>
                    <variable name="k{key1_varname}">
                      <locale id="m0.case_short.case_age_1.enum.k{key1_varname}"/>
                    </variable>
                  </xpath>
                </text>
              </template>
            </partial>
        """.format(
            key1_varname=key1_varname,
            key2_varname=key2_varname,
        )
        # check correct suite is generated
        self.assertXmlPartialEqual(icon_mapping_spec, app.create_suite(),
                                   './detail/field/template[@form="image"]')
        # check icons map correctly
        app_strings = commcare_translations.loads(app.create_app_strings('en'))
        self.assertEqual(
            app_strings['m0.case_short.case_age_1.enum.h{key2_varname}'.format(
                key2_varname=key2_varname, )], 'jr://icons/old-icon.png')
        self.assertEqual(
            app_strings['m0.case_short.case_age_1.enum.k{key1_varname}'.format(
                key1_varname=key1_varname, )], 'jr://icons/10-year-old.png')
Example #4
0
    def test_case_detail_calculated_conditional_enum(self, *args):
        app = Application.new_app('domain', 'Untitled Application')

        module = app.add_module(Module.new_module('Unititled Module', None))
        module.case_type = 'patient'

        module.case_details.short.columns = [
            DetailColumn(
                header={'en': 'Gender'},
                model='case',
                field="if(gender = 'male', 'boy', 'girl')",
                format='enum',
                enum=[
                    MappingItem(key="boy", value={'en': 'Boy'}),
                    MappingItem(key="girl", value={'en': 'Girl'}),
                ],
            ),
        ]

        icon_mapping_spec = """
        <partial>
          <template>
            <text>
              <xpath function="replace(join(' ', if(selected(if(gender = 'male', 'boy', 'girl'), 'boy'), $kboy, ''), if(selected(if(gender = 'male', 'boy', 'girl'), 'girl'), $kgirl, '')), '\\s+', ' ')">
                <variable name="kboy">
                  <locale id="m0.case_short.case_if(gender  'male', 'boy', 'girl')_1.enum.kboy"/>
                </variable>
                <variable name="kgirl">
                  <locale id="m0.case_short.case_if(gender  'male', 'boy', 'girl')_1.enum.kgirl"/>
                </variable>
              </xpath>
            </text>
          </template>
        </partial>
        """  # noqa: #501
        # check correct suite is generated
        self.assertXmlPartialEqual(
            icon_mapping_spec, app.create_suite(),
            './detail[@id="m0_case_short"]/field/template')
        # check app strings mapped correctly
        app_strings = commcare_translations.loads(app.create_app_strings('en'))
        self.assertEqual(
            app_strings[
                "m0.case_short.case_if(gender  'male', 'boy', 'girl')_1.enum.kboy"],
            'Boy')
        self.assertEqual(
            app_strings[
                "m0.case_short.case_if(gender  'male', 'boy', 'girl')_1.enum.kgirl"],
            'Girl')
Example #5
0
    def test_case_detail_conditional_enum(self, *args):
        app = Application.new_app('domain', 'Untitled Application')

        module = app.add_module(Module.new_module('Unititled Module', None))
        module.case_type = 'patient'

        module.case_details.short.columns = [
            DetailColumn(
                header={'en': 'Gender'},
                model='case',
                field='gender',
                format='conditional-enum',
                enum=[
                    MappingItem(key="gender = 'male' and age <= 21",
                                value={'en': 'Boy'}),
                    MappingItem(key="gender = 'female' and age <= 21",
                                value={'en': 'Girl'}),
                    MappingItem(key="gender = 'male' and age > 21",
                                value={'en': 'Man'}),
                    MappingItem(key="gender = 'female' and age > 21",
                                value={'en': 'Woman'}),
                ],
            ),
        ]

        key1_varname = hashlib.md5(
            "gender = 'male' and age <= 21".encode('utf-8')).hexdigest()[:8]
        key2_varname = hashlib.md5(
            "gender = 'female' and age <= 21".encode('utf-8')).hexdigest()[:8]
        key3_varname = hashlib.md5(
            "gender = 'male' and age > 21".encode('utf-8')).hexdigest()[:8]
        key4_varname = hashlib.md5(
            "gender = 'female' and age > 21".encode('utf-8')).hexdigest()[:8]

        icon_mapping_spec = """
        <partial>
          <template>
            <text>
              <xpath function="if(gender = 'male' and age &lt;= 21, $h{key1_varname}, if(gender = 'female' and age &lt;= 21, $h{key2_varname}, if(gender = 'male' and age &gt; 21, $h{key3_varname}, if(gender = 'female' and age &gt; 21, $h{key4_varname}, ''))))">
                <variable name="h{key4_varname}">
                  <locale id="m0.case_short.case_gender_1.enum.h{key4_varname}"/>
                </variable>
                <variable name="h{key2_varname}">
                  <locale id="m0.case_short.case_gender_1.enum.h{key2_varname}"/>
                </variable>
                <variable name="h{key3_varname}">
                  <locale id="m0.case_short.case_gender_1.enum.h{key3_varname}"/>
                </variable>
                <variable name="h{key1_varname}">
                  <locale id="m0.case_short.case_gender_1.enum.h{key1_varname}"/>
                </variable>
              </xpath>
            </text>
          </template>
        </partial>
        """.format(  # noqa: #501
            key1_varname=key1_varname,
            key2_varname=key2_varname,
            key3_varname=key3_varname,
            key4_varname=key4_varname,
        )
        # check correct suite is generated
        self.assertXmlPartialEqual(
            icon_mapping_spec, app.create_suite(),
            './detail[@id="m0_case_short"]/field/template')
        # check app strings mapped correctly
        app_strings = commcare_translations.loads(app.create_app_strings('en'))
        self.assertEqual(
            app_strings[
                'm0.case_short.case_gender_1.enum.h{key1_varname}'.format(
                    key1_varname=key1_varname, )], 'Boy')
        self.assertEqual(
            app_strings[
                'm0.case_short.case_gender_1.enum.h{key2_varname}'.format(
                    key2_varname=key2_varname, )], 'Girl')
        self.assertEqual(
            app_strings[
                'm0.case_short.case_gender_1.enum.h{key3_varname}'.format(
                    key3_varname=key3_varname, )], 'Man')
        self.assertEqual(
            app_strings[
                'm0.case_short.case_gender_1.enum.h{key4_varname}'.format(
                    key4_varname=key4_varname, )], 'Woman')
Example #6
0
def _edit_form_attr(request, domain, app_id, form_unique_id, attr):
    """
    Called to edit any (supported) form attribute, given by attr

    """

    ajax = json.loads(request.POST.get('ajax', 'true'))
    resp = {}

    app = get_app(domain, app_id)
    try:
        form = app.get_form(form_unique_id)
    except FormNotFoundException as e:
        if ajax:
            return HttpResponseBadRequest(str(e))
        else:
            messages.error(request, _("There was an error saving, please try again!"))
            return back_to_main(request, domain, app_id=app_id)
    lang = request.COOKIES.get('lang', app.langs[0])

    def should_edit(attribute):
        return attribute in request.POST

    if 'sha1' in request.POST and (should_edit("xform") or "xform" in request.FILES):
        conflict = _get_xform_conflict_response(form, request.POST['sha1'])
        if conflict is not None:
            return conflict

    if should_edit("name"):
        name = request.POST['name']
        form.name[lang] = name
        if not form.form_type == "shadow_form":
            xform = form.wrapped_xform()
            if xform.exists():
                xform.set_name(name)
                save_xform(app, form, xform.render())
        resp['update'] = {'.variable-form_name': trans(form.name, [lang], use_delim=False)}

    if should_edit('comment'):
        form.comment = request.POST['comment']

    if should_edit("name_enum"):
        name_enum = json.loads(request.POST.get("name_enum"))
        form.name_enum = [MappingItem(i) for i in name_enum]

    if should_edit("xform") or "xform" in request.FILES:
        try:
            # support FILES for upload and POST for ajax post from Vellum
            try:
                xform = request.FILES.get('xform').read()
            except Exception:
                xform = request.POST.get('xform')
            else:
                try:
                    xform = str(xform, encoding="utf-8")
                except Exception:
                    raise Exception("Error uploading form: Please make sure your form is encoded in UTF-8")
            if request.POST.get('cleanup', False):
                try:
                    # First, we strip all newlines and reformat the DOM.
                    px = parseString(xform.replace('\r\n', '')).toprettyxml()
                    # Then we remove excess newlines from the DOM output.
                    text_re = re.compile(r'>\n\s+([^<>\s].*?)\n\s+</', re.DOTALL)
                    prettyXml = text_re.sub(r'>\g<1></', px)
                    xform = prettyXml
                except Exception:
                    pass
            if xform:
                if isinstance(xform, str):
                    xform = xform.encode('utf-8')
                save_xform(app, form, xform)
            else:
                raise Exception("You didn't select a form to upload")
        except Exception as e:
            notify_exception(request, str(e))
            if ajax:
                return HttpResponseBadRequest(str(e))
            else:
                messages.error(request, str(e))
    if should_edit("references") or should_edit("case_references"):
        form.case_references = _get_case_references(request.POST)
    if should_edit("show_count"):
        show_count = request.POST['show_count']
        form.show_count = True if show_count == "True" else False
    if should_edit("put_in_root"):
        put_in_root = request.POST['put_in_root']
        form.put_in_root = True if put_in_root == "True" else False
    if should_edit('form_filter'):
        form.form_filter = request.POST['form_filter']
    if should_edit('post_form_workflow'):
        form.post_form_workflow = request.POST['post_form_workflow']
    if should_edit('auto_gps_capture'):
        form.auto_gps_capture = request.POST['auto_gps_capture'] == 'true'
    if should_edit('is_release_notes_form'):
        form.is_release_notes_form = request.POST['is_release_notes_form'] == 'true'
    if should_edit('enable_release_notes'):
        form.enable_release_notes = request.POST['enable_release_notes'] == 'true'
        if not form.is_release_notes_form and form.enable_release_notes:
            return json_response(
                {'message': _("You can't enable a form as release notes without allowing it as "
                    "a release notes form <TODO messaging>")},
                status_code=400
            )
    if should_edit('no_vellum'):
        form.no_vellum = request.POST['no_vellum'] == 'true'
    if (should_edit("form_links_xpath_expressions") and
            should_edit("form_links_form_ids") and
            toggles.FORM_LINK_WORKFLOW.enabled(domain)):
        form_links = zip(
            request.POST.getlist('form_links_xpath_expressions'),
            request.POST.getlist('form_links_form_ids'),
            [
                json.loads(datum_json) if datum_json else []
                for datum_json in request.POST.getlist('datums_json')
            ],
        )
        form.form_links = [FormLink(
            xpath=link[0],
            form_id=link[1],
            datums=[
                FormDatum(name=datum['name'], xpath=datum['xpath'])
                for datum in link[2]
            ]
        ) for link in form_links]

    if should_edit('post_form_workflow_fallback'):
        form.post_form_workflow_fallback = request.POST.get('post_form_workflow_fallback')

    if should_edit('custom_instances'):
        instances = json.loads(request.POST.get('custom_instances'))
        try:  # validate that custom instances can be added into the XML
            for instance in instances:
                etree.fromstring(
                    "<instance id='{}' src='{}' />".format(
                        instance.get('instanceId'),
                        instance.get('instancePath')
                    )
                )
        except etree.XMLSyntaxError as error:
            return json_response(
                {'message': _("There was an issue with your custom instances: {}").format(error)},
                status_code=400
            )

        form.custom_instances = [
            CustomInstance(
                instance_id=instance.get("instanceId"),
                instance_path=instance.get("instancePath"),
            ) for instance in instances
        ]

    if should_edit('custom_assertions'):
        assertions = json.loads(request.POST.get('custom_assertions'))
        try:  # validate that custom assertions can be added into the XML
            for assertion in assertions:
                etree.fromstring(
                    '<assertion test="{test}"><text><locale id="abc.def"/>{text}</text></assertion>'.format(
                        **assertion
                    )
                )
        except etree.XMLSyntaxError as error:
            return json_response(
                {'message': _("There was an issue with your custom assertions: {}").format(error)},
                status_code=400
            )

        existing_assertions = {assertion.test: assertion for assertion in form.custom_assertions}
        new_assertions = []
        for assertion in assertions:
            try:
                new_assertion = existing_assertions[assertion.get('test')]
                new_assertion.text[lang] = assertion.get('text')
            except KeyError:
                new_assertion = CustomAssertion(
                    test=assertion.get('test'),
                    text={lang: assertion.get('text')}
                )
            new_assertions.append(new_assertion)

        form.custom_assertions = new_assertions

    if should_edit("shadow_parent"):
        form.shadow_parent_form_id = request.POST['shadow_parent']

    if should_edit("custom_icon_form"):
        error_message = handle_custom_icon_edits(request, form, lang)
        if error_message:
            return json_response(
                {'message': error_message},
                status_code=400
            )
    handle_media_edits(request, form, should_edit, resp, lang)

    app.save(resp)
    notify_form_changed(domain, request.couch_user, app_id, form_unique_id)
    if ajax:
        return HttpResponse(json.dumps(resp))
    else:
        return back_to_main(request, domain, app_id=app_id, form_unique_id=form_unique_id)
class SuiteNameEnumsTest(SimpleTestCase, TestXmlMixin, SuiteMixin):
    file_path = ('data', 'suite')
    case_type = 'patient'
    enum = [
        MappingItem(key='int(double(now())) mod 1 = 0', value={'en': 'evens'}),
        MappingItem(key='int(double(now())) mod 1 = 1', value={'en': 'odds'}),
    ]

    def setUp(self):
        self.factory = AppFactory(build_version='2.40.0', domain='domain')
        self.basic_module, self.basic_form = self.factory.new_basic_module(
            'basic', self.case_type)
        self.basic_module.name_enum = self.enum
        self.basic_form.name_enum = self.enum

    def test_module(self):
        self.assertXmlPartialEqual(
            """
            <partial>
                <menu id="m0">
                    <text>
                        <xpath function="if(int(double(now())) mod 1 = 0, $h2cb17fc7, if(int(double(now())) mod 1 = 1, $h137bc41f, ''))">
                            <variable name="h2cb17fc7">
                                <locale id="m0.enum.h2cb17fc7"/>
                            </variable>
                            <variable name="h137bc41f">
                                <locale id="m0.enum.h137bc41f"/>
                            </variable>
                        </xpath>
                    </text>
                    <command id="m0-f0"/>
                </menu>
            </partial>
            """,
            self.factory.app.create_suite(),
            'menu[@id="m0"]',
        )

    def test_module_with_media(self):
        self.basic_module.media_audio = {
            'en': 'jr://file/commcare/audio/en/module0.mp3'
        }
        self.basic_module.media_image = {
            'en': 'jr://file/commcare/image/module0_en.png'
        }
        self.assertXmlPartialEqual(
            """
            <partial>
                <menu id="m0">
                    <display>
                        <text>
                            <xpath function="if(int(double(now())) mod 1 = 0, $h2cb17fc7, if(int(double(now())) mod 1 = 1, $h137bc41f, ''))">
                                <variable name="h2cb17fc7">
                                    <locale id="m0.enum.h2cb17fc7"/>
                                </variable>
                                <variable name="h137bc41f">
                                    <locale id="m0.enum.h137bc41f"/>
                                </variable>
                            </xpath>
                        </text>
                        <text form="image">
                            <locale id="modules.m0.icon"/>
                        </text>
                        <text form="audio">
                            <locale id="modules.m0.audio"/>
                        </text>
                    </display>
                    <command id="m0-f0"/>
                </menu>
            </partial>
            """,
            self.factory.app.create_suite(),
            'menu[@id="m0"]',
        )

    def test_report_module(self):
        self.report_module = self.factory.new_report_module('basic')
        self.report_module.name_enum = self.enum
        self.assertXmlPartialEqual(
            """
            <partial>
                <menu id="m1">
                    <text>
                        <xpath function="if(int(double(now())) mod 1 = 0, $h2cb17fc7, if(int(double(now())) mod 1 = 1, $h137bc41f, ''))">
                            <variable name="h2cb17fc7">
                                <locale id="m1.enum.h2cb17fc7"/>
                            </variable>
                            <variable name="h137bc41f">
                                <locale id="m1.enum.h137bc41f"/>
                            </variable>
                        </xpath>
                    </text>
                </menu>
            </partial>
            """,
            self.factory.app.create_suite(),
            'menu[@id="m1"]',
        )

    def test_form(self):
        self.assertXmlPartialEqual(
            """
            <partial>
                <entry>
                    <command id="m0-f0">
                        <text>
                            <xpath function="if(int(double(now())) mod 1 = 0, $h2cb17fc7, if(int(double(now())) mod 1 = 1, $h137bc41f, ''))">
                                <variable name="h2cb17fc7">
                                    <locale id="m0f0.enum.h2cb17fc7"/>
                                </variable>
                                <variable name="h137bc41f">
                                    <locale id="m0f0.enum.h137bc41f"/>
                                </variable>
                            </xpath>
                        </text>
                    </command>
                </entry>
            </partial>
            """,
            self.factory.app.create_suite(),
            'entry[1]',
        )

    def test_form_with_media(self):
        self.basic_form.media_audio = {
            'en': 'jr://file/commcare/audio/en/module0.mp3'
        }
        self.basic_form.media_image = {
            'en': 'jr://file/commcare/image/module0_en.png'
        }
        self.assertXmlPartialEqual(
            """
            <partial>
                <entry>
                    <command id="m0-f0">
                        <display>
                            <text>
                                <xpath function="if(int(double(now())) mod 1 = 0, $h2cb17fc7, if(int(double(now())) mod 1 = 1, $h137bc41f, ''))">
                                    <variable name="h2cb17fc7">
                                        <locale id="m0f0.enum.h2cb17fc7"/>
                                    </variable>
                                    <variable name="h137bc41f">
                                        <locale id="m0f0.enum.h137bc41f"/>
                                    </variable>
                                </xpath>
                            </text>
                            <text form="image">
                                <locale id="forms.m0f0.icon"/>
                            </text>
                            <text form="audio">
                                <locale id="forms.m0f0.audio"/>
                            </text>
                        </display>
                    </command>
                </entry>
            </partial>
            """,
            self.factory.app.create_suite(),
            'entry[1]',
        )