Ejemplo n.º 1
0
def _get_case_schema_subsets(app, base_case_type, hashtag='#case/', source=None):
    builder = ParentCasePropertyBuilder.for_app(app, ['case_name'], include_parent_properties=False)
    related = builder.get_parent_type_map(None)
    map = builder.get_properties_by_case_type()
    descriptions_dict = get_case_property_description_dict(app.domain)

    # Generate hierarchy of case types, represented as a list of lists of strings:
    # [[base_case_type], [parent_type1, parent_type2...], [grandparent_type1, grandparent_type2...]]
    # Vellum case management only supports three levels
    generation_names = ['case', 'parent', 'grandparent']
    generations = [[] for g in generation_names]

    def _add_ancestors(ctype, generation):
        if generation < len(generation_names):
            generations[generation].append(ctype)
            for parent in related.get(ctype, {}).get('parent', []):
                _add_ancestors(parent, generation + 1)

    _add_ancestors(base_case_type, 0)

    # Remove any duplicate types or empty generations
    generations = [set(g) for g in generations if len(g)]

    name_source = f" - {source}" if source else ""
    id_source = f":{slugify(source)}" if source else ""
    hashtag = f"{hashtag}{id_source}"

    def _name(i, ctypes):
        if i > 0:
            return "{} ({}){}".format(generation_names[i], " or ".join(ctypes), name_source)
        return f"{base_case_type}{name_source}"

    return [{
        "id": f"{generation_names[i]}{id_source}",
        "name": _name(i, ctypes),
        "structure": {
            p: {"description": descriptions_dict.get(t, {}).get(p, '')}
            for t in ctypes for p in map[t]},
        "related": {"parent": {
            "hashtag": hashtag + generation_names[i + 1],
            "subset": f"{generation_names[i + 1]}{id_source}",
            "key": "@case_id",
        }} if i < len(generations) - 1 else None,
    } for i, ctypes in enumerate(generations)]
Ejemplo n.º 2
0
def get_form_view_context_and_template(request, domain, form, langs, current_lang, messages=messages):
    xform_questions = []
    xform = None
    form_errors = []
    xform_validation_errored = False
    xform_validation_missing = False

    try:
        xform = form.wrapped_xform()
    except XFormException as e:
        form_errors.append("Error in form: %s" % e)
    except Exception as e:
        logging.exception(e)
        form_errors.append("Unexpected error in form: %s" % e)

    has_case_error = False
    if xform and xform.exists():
        if xform.already_has_meta():
            messages.warning(
                request,
                "This form has a meta block already! "
                "It may be replaced by CommCare HQ's standard meta block."
            )

        try:
            xform_questions = xform.get_questions(langs, include_triggers=True)
            form.validate_form()
        except etree.XMLSyntaxError as e:
            form_errors.append("Syntax Error: %s" % e)
        except AppEditingError as e:
            form_errors.append("Error in application: %s" % e)
        except XFormValidationError:
            xform_validation_errored = True
            # showing these messages is handled by validate_form_for_build ajax
        except XFormValidationFailed:
            xform_validation_missing = True
            messages.warning(request, _("Unable to validate form due to server error."))
        except XFormException as e:
            form_errors.append("Error in form: %s" % e)
        # any other kind of error should fail hard,
        # but for now there are too many for that to be practical
        except Exception as e:
            if settings.DEBUG:
                raise
            notify_exception(request, 'Unexpected Build Error')
            form_errors.append("Unexpected System Error: %s" % e)
        else:
            # remove upload questions (attachments) until MM Case Properties
            # are released to general public
            is_previewer = toggles.MM_CASE_PROPERTIES.enabled_for_request(request)
            xform_questions = [q for q in xform_questions
                               if q["tag"] != "upload" or is_previewer]

        if not form_errors and not xform_validation_missing and not xform_validation_errored:
            try:
                form_action_errors = form.validate_for_build()
                if not form_action_errors:
                    form.add_stuff_to_xform(xform)
            except CaseError as e:
                has_case_error = True
                messages.error(request, "Error in Case Management: %s" % e)
            except XFormException as e:
                messages.error(request, str(e))
            except Exception as e:
                if settings.DEBUG:
                    raise
                logging.exception(str(e))
                messages.error(request, "Unexpected Error: %s" % e)

    try:
        languages = xform.get_languages()
    except Exception:
        languages = []

    for err in form_errors:
        messages.error(request, err)

    module_case_types = []
    app = form.get_app()
    all_modules = list(app.get_modules())
    for module in all_modules:
        for case_type in module.get_case_types():
            module_case_types.append({
                'id': module.unique_id,
                'module_name': trans(module.name, langs),
                'case_type': case_type,
                'module_type': module.doc_type
            })
    module = form.get_module()

    if not form.unique_id:
        form.get_unique_id()
        app.save()

    allow_usercase = domain_has_privilege(request.domain, privileges.USERCASE)
    valid_index_names = list(DEFAULT_CASE_INDEX_IDENTIFIERS.values())
    if allow_usercase:
        valid_index_names.append(USERCASE_PREFIX[0:-1])     # strip trailing slash

    form_has_schedule = isinstance(form, AdvancedForm) and module.has_schedule

    try:
        case_properties_map = get_all_case_properties(app)
        usercase_properties_map = get_usercase_properties(app)
    except CaseError as e:
        case_properties_map = {}
        usercase_properties_map = {}
        if not has_case_error:
            messages.error(request, "Error in Case Management: %s" % e)

    case_config_options = {
        'caseType': form.get_case_type(),
        'moduleCaseTypes': module_case_types,
        'propertiesMap': case_properties_map,
        'propertyDescriptions': get_case_property_description_dict(domain),
        'questions': xform_questions,
        'reserved_words': load_case_reserved_words(),
        'usercasePropertiesMap': usercase_properties_map,
    }
    context = {
        'nav_form': form,
        'xform_languages': languages,
        'form_errors': form_errors,
        'xform_validation_errored': xform_validation_errored,
        'xform_validation_missing': xform_validation_missing,
        'allow_form_copy': isinstance(form, (Form, AdvancedForm)),
        'allow_form_filtering': not form_has_schedule,
        'uses_form_workflow': form.post_form_workflow == WORKFLOW_FORM,
        'allow_usercase': allow_usercase,
        'is_usercase_in_use': is_usercase_in_use(request.domain),
        'is_module_filter_enabled': app.enable_module_filtering,
        'is_training_module': module.is_training_module,
        'is_allowed_to_be_release_notes_form': form.is_allowed_to_be_release_notes_form,
        'root_requires_same_case': module.root_requires_same_case(),
        'is_case_list_form': form.is_case_list_form,
        'edit_name_url': reverse('edit_form_attr', args=[app.domain, app.id, form.unique_id, 'name']),
        'form_filter_patterns': {
            'case_substring': CASE_XPATH_SUBSTRING_MATCHES,
            'usercase_substring': USERCASE_XPATH_SUBSTRING_MATCHES,
        },
        'custom_instances': [
            {'instanceId': instance.instance_id, 'instancePath': instance.instance_path}
            for instance in form.custom_instances
        ],
        'custom_assertions': [
            {'test': assertion.test, 'text': assertion.text.get(current_lang)}
            for assertion in form.custom_assertions
        ],
        'form_icon': None,
        'session_endpoints_enabled': toggles.SESSION_ENDPOINTS.enabled(domain),
        'module_is_multi_select': module.is_multi_select(),
        'module_loads_registry_case': module_loads_registry_case(module),
    }

    if toggles.CUSTOM_ICON_BADGES.enabled(domain):
        context['form_icon'] = form.custom_icon if form.custom_icon else CustomIcon()

    if toggles.COPY_FORM_TO_APP.enabled_for_request(request):
        context['apps_modules'] = get_apps_modules(domain, app.id, module.unique_id)

    if toggles.FORM_LINK_WORKFLOW.enabled(domain):
        context.update(_get_form_link_context(module, langs))

    if isinstance(form, AdvancedForm):
        def commtrack_programs():
            if app.commtrack_enabled:
                programs = Program.by_domain(app.domain)
                return [{'value': program.get_id, 'label': program.name} for program in programs]
            else:
                return []

        all_programs = [{'value': '', 'label': _('All Programs')}]
        case_config_options.update({
            'commtrack_enabled': app.commtrack_enabled,
            'commtrack_programs': all_programs + commtrack_programs(),
            'module_id': module.unique_id,
            'save_url': reverse("edit_advanced_form_actions", args=[app.domain, app.id, form.unique_id]),
            'arbitrary_datums': form.arbitrary_datums,
        })
        if form.form_type == "shadow_form":
            case_config_options.update({
                'actions': form.extra_actions,
                'isShadowForm': True,
            })
        else:
            case_config_options.update({
                'actions': form.actions,
                'isShadowForm': False,
            })
        if getattr(module, 'has_schedule', False):
            schedule_options = get_schedule_context(form)
            schedule_options.update({
                'phase': schedule_options['schedule_phase'],
                'questions': xform_questions,
                'save_url': reverse("edit_visit_schedule", args=[app.domain, app.id, form.unique_id]),
                'schedule': form.schedule,
            })
            context.update({
                'schedule_options': schedule_options,
            })
    else:
        context.update({
            'show_custom_ref': toggles.APP_BUILDER_CUSTOM_PARENT_REF.enabled_for_request(request),
        })
        case_config_options.update({
            'actions': form.actions,
            'allowUsercase': allow_usercase,
            'save_url': reverse("edit_form_actions", args=[app.domain, app.id, form.unique_id]),
            'valid_index_names': valid_index_names,
        })

    context.update({'case_config_options': case_config_options})
    return "app_manager/form_view.html", context
Ejemplo n.º 3
0
def view_generic(request,
                 domain,
                 app_id=None,
                 module_id=None,
                 form_id=None,
                 copy_app_form=None,
                 release_manager=False):
    """
    This is the main view for the app. All other views redirect to here.

    """
    if form_id and not module_id:
        return bail(request, domain, app_id)

    app = module = form = None
    try:
        if app_id:
            app = get_app(domain, app_id)
        if module_id:
            try:
                module = app.get_module(module_id)
            except ModuleNotFoundException:
                raise Http404()
            if not module.unique_id:
                module.get_or_create_unique_id()
                app.save()
        if form_id:
            try:
                form = module.get_form(form_id)
            except IndexError:
                raise Http404()
    except ModuleNotFoundException:
        return bail(request, domain, app_id)

    # Application states that should no longer exist
    if app:
        if app.application_version == APP_V1:
            _assert = soft_assert()
            _assert(False, 'App version 1.0', {
                'domain': domain,
                'app_id': app_id
            })
            template = get_app_manager_template(
                domain,
                'app_manager/v1/no_longer_supported.html',
                'app_manager/v2/no_longer_supported.html',
            )
            return render(request, template, {
                'domain': domain,
                'app': app,
            })
        if not app.vellum_case_management and not app.is_remote_app():
            # Soft assert but then continue rendering; template will contain a user-facing warning
            _assert = soft_assert(['jschweers' + '@' + 'dimagi.com'])
            _assert(False, 'vellum_case_management=False', {
                'domain': domain,
                'app_id': app_id
            })
        if (form is not None
                and toggles.USER_PROPERTY_EASY_REFS.enabled(domain)
                and "usercase_preload" in form.actions
                and form.actions.usercase_preload.preload):
            _assert = soft_assert(['dmiller' + '@' + 'dimagi.com'])
            _assert(
                False, 'User property easy refs + old-style config = bad', {
                    'domain': domain,
                    'app_id': app_id,
                    'module_id': module_id,
                    'form_id': form_id,
                })

    context = get_apps_base_context(request, domain, app)
    if app and app.copy_of:
        # don't fail hard.
        return HttpResponseRedirect(
            reverse(
                "view_app",
                args=[domain, app.copy_of]  # TODO - is this right?
            ))

    # grandfather in people who set commcare sense earlier
    if app and 'use_commcare_sense' in app:
        if app['use_commcare_sense']:
            if 'features' not in app.profile:
                app.profile['features'] = {}
            app.profile['features']['sense'] = 'true'
        del app['use_commcare_sense']
        app.save()

    context.update({
        'module': module,
        'form': form,
    })

    lang = context['lang']
    if app and not module and hasattr(app, 'translations'):
        context.update({"translations": app.translations.get(lang, {})})

    if form:
        template, form_context = get_form_view_context_and_template(
            request, domain, form, context['langs'])
        context.update({
            'case_properties':
            get_all_case_properties(app),
            'usercase_properties':
            get_usercase_properties(app),
            'property_descriptions':
            get_case_property_description_dict(domain)
        })

        context.update(form_context)
    elif module:
        template = get_module_template(domain, module)
        # make sure all modules have unique ids
        app.ensure_module_unique_ids(should_save=True)
        module_context = get_module_view_context(app, module, lang)
        context.update(module_context)
    elif app:
        context.update(get_app_view_context(request, app))

        v2_template = ('app_manager/v2/app_view_release_manager.html'
                       if release_manager else
                       'app_manager/v2/app_view_settings.html')

        template = get_app_manager_template(domain,
                                            'app_manager/v1/app_view.html',
                                            v2_template)

        if release_manager:
            context.update(get_releases_context(request, domain, app_id))
        context.update({
            'is_app_settings_page': not release_manager,
        })
    else:
        from corehq.apps.dashboard.views import NewUserDashboardView
        if toggles.APP_MANAGER_V2.enabled(domain):
            context.update(NewUserDashboardView.get_page_context(domain))
            template = NewUserDashboardView.template_name
        else:
            return HttpResponseRedirect(
                reverse(NewUserDashboardView.urlname, args=[domain]))

    # update multimedia context for forms and modules.
    menu_host = form or module
    if menu_host:
        default_file_name = 'module%s' % module_id
        if form_id:
            default_file_name = '%s_form%s' % (default_file_name, form_id)

        specific_media = {
            'menu': {
                'menu_refs':
                app.get_menu_media(module,
                                   module_id,
                                   form=form,
                                   form_index=form_id,
                                   to_language=lang),
                'default_file_name':
                '{name}_{lang}'.format(name=default_file_name, lang=lang),
            }
        }

        if module and module.uses_media():

            def _make_name(suffix):
                return "{default_name}_{suffix}_{lang}".format(
                    default_name=default_file_name,
                    suffix=suffix,
                    lang=lang,
                )

            specific_media['case_list_form'] = {
                'menu_refs':
                app.get_case_list_form_media(module,
                                             module_id,
                                             to_language=lang),
                'default_file_name':
                _make_name('case_list_form'),
            }
            specific_media['case_list_menu_item'] = {
                'menu_refs':
                app.get_case_list_menu_item_media(module,
                                                  module_id,
                                                  to_language=lang),
                'default_file_name':
                _make_name('case_list_menu_item'),
            }
            specific_media['case_list_lookup'] = {
                'menu_refs':
                app.get_case_list_lookup_image(module, module_id),
                'default_file_name':
                '{}_case_list_lookup'.format(default_file_name),
            }

            if hasattr(module, 'product_details'):
                specific_media['product_list_lookup'] = {
                    'menu_refs':
                    app.get_case_list_lookup_image(module,
                                                   module_id,
                                                   type='product'),
                    'default_file_name':
                    '{}_product_list_lookup'.format(default_file_name),
                }

        context.update({
            'multimedia': {
                "object_map": app.get_object_map(),
                'upload_managers': {
                    'icon':
                    MultimediaImageUploadController(
                        "hqimage",
                        reverse(ProcessImageFileUploadView.name,
                                args=[app.domain, app.get_id])),
                    'audio':
                    MultimediaAudioUploadController(
                        "hqaudio",
                        reverse(ProcessAudioFileUploadView.name,
                                args=[app.domain, app.get_id])),
                },
            }
        })
        try:
            context['multimedia']['references'] = app.get_references()
        except ReportConfigurationNotFoundError:
            pass
        context['multimedia'].update(specific_media)

    error = request.GET.get('error', '')

    context.update({
        'error': error,
        'app': app,
    })

    # Pass form for Copy Application to template
    domain_names = [d.name for d in Domain.active_for_user(request.couch_user)]
    domain_names.sort()
    if app and copy_app_form is None:
        toggle_enabled = toggles.EXPORT_ZIPPED_APPS.enabled(
            request.user.username)
        copy_app_form = CopyApplicationForm(
            domain, app, export_zipped_apps_enabled=toggle_enabled)
        context.update({
            'domain_names': domain_names,
        })
    linked_apps_enabled = toggles.LINKED_APPS.enabled(domain)
    context.update({
        'copy_app_form': copy_app_form,
        'linked_apps_enabled': linked_apps_enabled,
    })

    context['latest_commcare_version'] = get_commcare_versions(
        request.user)[-1]

    if app and app.doc_type == 'Application' and has_privilege(
            request, privileges.COMMCARE_LOGO_UPLOADER):
        uploader_slugs = ANDROID_LOGO_PROPERTY_MAPPING.keys()
        from corehq.apps.hqmedia.controller import MultimediaLogoUploadController
        from corehq.apps.hqmedia.views import ProcessLogoFileUploadView
        context.update({
            "sessionid":
            request.COOKIES.get('sessionid'),
            'uploaders': [
                MultimediaLogoUploadController(
                    slug,
                    reverse(
                        ProcessLogoFileUploadView.name,
                        args=[domain, app_id, slug],
                    )) for slug in uploader_slugs
            ],
            "refs": {
                slug: ApplicationMediaReference(
                    app.logo_refs.get(slug, {}).get("path", slug),
                    media_class=CommCareImage,
                    module_id=app.logo_refs.get(slug, {}).get("m_id"),
                ).as_dict()
                for slug in uploader_slugs
            },
            "media_info": {
                slug: app.logo_refs.get(slug)
                for slug in uploader_slugs if app.logo_refs.get(slug)
            },
        })

    domain_obj = Domain.get_by_name(domain)
    context.update({
        'show_live_preview':
        app
        and should_show_preview_app(request, app, request.couch_user.username),
        'can_preview_form':
        request.couch_user.has_permission(domain, 'edit_data')
    })

    response = render(request, template, context)

    response.set_cookie('lang', encode_if_unicode(lang))
    return response
Ejemplo n.º 4
0
 def _add_case_property_descriptions(self):
     descriptions_dict = get_case_property_description_dict(self.domain)
     for type_ in self.meta.case_types:
         for prop in type_.properties:
             prop.description = descriptions_dict.get(type_.name, {}).get(
                 prop.name, '')
Ejemplo n.º 5
0
def get_casedb_schema(form):
    """Get case database schema definition for vellum to display as an external data source.

    This lists all case types and their properties for the given app.
    """
    app = form.get_app()
    base_case_type = form.get_module().case_type
    case_types = app.get_case_types() | get_shared_case_types(app)
    per_type_defaults = get_per_type_defaults(app.domain, case_types)
    builder = ParentCasePropertyBuilder(app, ['case_name'], per_type_defaults)
    related = builder.get_parent_type_map(case_types, allow_multiple_parents=True)
    map = builder.get_case_property_map(case_types, include_parent_properties=False)
    descriptions_dict = get_case_property_description_dict(app.domain)

    if base_case_type:
        # Generate hierarchy of case types, represented as a list of lists of strings:
        # [[base_case_type], [parent_type1, parent_type2...], [grandparent_type1, grandparent_type2...]]
        # Vellum case management only supports three levels
        generation_names = ['case', 'parent', 'grandparent']
        generations = [[] for g in generation_names]

        def _add_ancestors(ctype, generation):
            if generation < len(generation_names):
                generations[generation].append(ctype)
                for parent in related.get(ctype, {}).get('parent', []):
                    _add_ancestors(parent, generation + 1)

        _add_ancestors(base_case_type, 0)

        # Remove any duplicate types or empty generations
        generations = [set(g) for g in generations if len(g)]
    else:
        generations = []

    subsets = [{
        "id": generation_names[i],
        "name": "{} ({})".format(generation_names[i], " or ".join(ctypes)) if i > 0 else base_case_type,
        "structure": {
            p: {"description": descriptions_dict.get(t, {}).get(p, '')}
            for t in ctypes for p in map[t]},
        "related": {"parent": {
            "hashtag": "#case/" + generation_names[i + 1],
            "subset": generation_names[i + 1],
            "key": "@case_id",
        }} if i < len(generations) - 1 else None,
    } for i, ctypes in enumerate(generations)]

    if is_usercase_in_use(app.domain) and toggles.USER_PROPERTY_EASY_REFS.enabled(app.domain):
        subsets.append({
            "id": USERCASE_TYPE,
            "name": "user",
            "key": "@case_type",
            "structure": {p: {} for p in get_usercase_properties(app)[USERCASE_TYPE]},
        })

    return {
        "id": "casedb",
        "uri": "jr://instance/casedb",
        "name": "case",
        "path": "/casedb/case",
        "structure": {},
        "subsets": subsets,
    }
Ejemplo n.º 6
0
def get_form_view_context_and_template(request, domain, form, langs, current_lang, messages=messages):
    xform_questions = []
    xform = None
    form_errors = []
    xform_validation_errored = False
    xform_validation_missing = False

    try:
        xform = form.wrapped_xform()
    except XFormException as e:
        form_errors.append("Error in form: %s" % e)
    except Exception as e:
        logging.exception(e)
        form_errors.append("Unexpected error in form: %s" % e)

    if xform and xform.exists():
        if xform.already_has_meta():
            messages.warning(
                request,
                "This form has a meta block already! "
                "It may be replaced by CommCare HQ's standard meta block."
            )

        try:
            xform_questions = xform.get_questions(langs, include_triggers=True)
            form.validate_form()
        except etree.XMLSyntaxError as e:
            form_errors.append("Syntax Error: %s" % e)
        except AppEditingError as e:
            form_errors.append("Error in application: %s" % e)
        except XFormValidationError:
            xform_validation_errored = True
            # showing these messages is handled by validate_form_for_build ajax
        except XFormValidationFailed:
            xform_validation_missing = True
            messages.warning(request, _("Unable to validate form due to server error."))
        except XFormException as e:
            form_errors.append("Error in form: %s" % e)
        # any other kind of error should fail hard,
        # but for now there are too many for that to be practical
        except Exception as e:
            if settings.DEBUG:
                raise
            notify_exception(request, 'Unexpected Build Error')
            form_errors.append("Unexpected System Error: %s" % e)
        else:
            # remove upload questions (attachments) until MM Case Properties
            # are released to general public
            is_previewer = toggles.MM_CASE_PROPERTIES.enabled_for_request(request)
            xform_questions = [q for q in xform_questions
                               if q["tag"] != "upload" or is_previewer]

        if not form_errors and not xform_validation_missing and not xform_validation_errored:
            try:
                form_action_errors = form.validate_for_build()
                if not form_action_errors:
                    form.add_stuff_to_xform(xform)
            except CaseError as e:
                messages.error(request, "Error in Case Management: %s" % e)
            except XFormException as e:
                messages.error(request, six.text_type(e))
            except Exception as e:
                if settings.DEBUG:
                    raise
                logging.exception(six.text_type(e))
                messages.error(request, "Unexpected Error: %s" % e)

    try:
        languages = xform.get_languages()
    except Exception:
        languages = []

    for err in form_errors:
        messages.error(request, err)

    module_case_types = []
    app = form.get_app()
    all_modules = list(app.get_modules())
    for module in all_modules:
        for case_type in module.get_case_types():
            module_case_types.append({
                'id': module.unique_id,
                'module_name': trans(module.name, langs),
                'case_type': case_type,
                'module_type': module.doc_type
            })
    module = form.get_module()

    if not form.unique_id:
        form.get_unique_id()
        app.save()

    allow_usercase = domain_has_privilege(request.domain, privileges.USER_CASE)
    valid_index_names = list(DEFAULT_CASE_INDEX_IDENTIFIERS.values())
    if allow_usercase:
        valid_index_names.append(USERCASE_PREFIX[0:-1])     # strip trailing slash

    form_has_schedule = isinstance(form, AdvancedForm) and module.has_schedule
    case_config_options = {
        'caseType': form.get_case_type(),
        'moduleCaseTypes': module_case_types,
        'propertiesMap': get_all_case_properties(app),
        'propertyDescriptions': get_case_property_description_dict(domain),
        'questions': xform_questions,
        'reserved_words': load_case_reserved_words(),
        'usercasePropertiesMap': get_usercase_properties(app),
    }
    context = {
        'nav_form': form,
        'xform_languages': languages,
        'form_errors': form_errors,
        'xform_validation_errored': xform_validation_errored,
        'xform_validation_missing': xform_validation_missing,
        'allow_form_copy': isinstance(form, (Form, AdvancedForm)),
        'allow_form_filtering': not form_has_schedule,
        'allow_form_workflow': True,
        'uses_form_workflow': form.post_form_workflow == WORKFLOW_FORM,
        'allow_usercase': allow_usercase,
        'is_usercase_in_use': is_usercase_in_use(request.domain),
        'is_module_filter_enabled': app.enable_module_filtering,
        'is_training_module': module.is_training_module,
        'is_allowed_to_be_release_notes_form': form.is_allowed_to_be_release_notes_form,
        'root_requires_same_case': module.root_requires_same_case(),
        'is_case_list_form': form.is_case_list_form,
        'edit_name_url': reverse('edit_form_attr', args=[app.domain, app.id, form.unique_id, 'name']),
        'form_filter_patterns': {
            'case_substring': CASE_XPATH_SUBSTRING_MATCHES,
            'usercase_substring': USER_CASE_XPATH_SUBSTRING_MATCHES,
        },
        'custom_instances': [
            {'instanceId': instance.instance_id, 'instancePath': instance.instance_path}
            for instance in form.custom_instances
        ],
        'custom_assertions': [
            {'test': assertion.test, 'text': assertion.text.get(current_lang)}
            for assertion in form.custom_assertions
        ],
        'can_preview_form': request.couch_user.has_permission(domain, 'edit_data'),
        'form_icon': None,
    }

    if toggles.CUSTOM_ICON_BADGES.enabled(domain):
        context['form_icon'] = form.custom_icon if form.custom_icon else CustomIcon()

    if toggles.COPY_FORM_TO_APP.enabled_for_request(request):
        context['apps_modules'] = get_apps_modules(domain, app.id, module.unique_id)

    if context['allow_form_workflow'] and toggles.FORM_LINK_WORKFLOW.enabled(domain):
        def qualified_form_name(form, auto_link):
            module_name = trans(module.name, langs)
            form_name = trans(form.name, langs)
            star = '* ' if auto_link else '  '
            return "{}{} -> {}".format(star, module_name, form_name)

        modules = [m for m in all_modules if m.case_type == module.case_type]
        if getattr(module, 'root_module_id', None) and module.root_module not in modules:
            modules.append(module.root_module)
        auto_linkable_forms = list(itertools.chain.from_iterable(list(m.get_forms()) for m in modules))

        def linkable_form(candidate_form):
            auto_link = candidate_form in auto_linkable_forms
            return {
                'unique_id': candidate_form.unique_id,
                'name': qualified_form_name(candidate_form, auto_link),
                'auto_link': auto_link
            }

        context['linkable_forms'] = [
            linkable_form(candidate_form) for candidate_module in all_modules
            for candidate_form in candidate_module.get_forms()
        ]

    if isinstance(form, AdvancedForm):
        def commtrack_programs():
            if app.commtrack_enabled:
                programs = Program.by_domain(app.domain)
                return [{'value': program.get_id, 'label': program.name} for program in programs]
            else:
                return []

        all_programs = [{'value': '', 'label': _('All Programs')}]
        case_config_options.update({
            'commtrack_enabled': app.commtrack_enabled,
            'commtrack_programs': all_programs + commtrack_programs(),
            'module_id': module.unique_id,
            'save_url': reverse("edit_advanced_form_actions", args=[app.domain, app.id, form.unique_id]),
        })
        if form.form_type == "shadow_form":
            case_config_options.update({
                'actions': form.extra_actions,
                'isShadowForm': True,
            })
        else:
            case_config_options.update({
                'actions': form.actions,
                'isShadowForm': False,
            })
        if getattr(module, 'has_schedule', False):
            schedule_options = get_schedule_context(form)
            schedule_options.update({
                'phase': schedule_options['schedule_phase'],
                'questions': xform_questions,
                'save_url': reverse("edit_visit_schedule", args=[app.domain, app.id, form.unique_id]),
                'schedule': form.schedule,
            })
            context.update({
                'schedule_options': schedule_options,
            })
    else:
        context.update({
            'show_custom_ref': toggles.APP_BUILDER_CUSTOM_PARENT_REF.enabled_for_request(request),
        })
        case_config_options.update({
            'actions': form.actions,
            'allowUsercase': allow_usercase,
            'save_url': reverse("edit_form_actions", args=[app.domain, app.id, form.unique_id]),
            'valid_index_names': valid_index_names,
        })

    context.update({'case_config_options': case_config_options})
    return "app_manager/form_view.html", context
Ejemplo n.º 7
0
def get_form_view_context_and_template(request,
                                       domain,
                                       form,
                                       langs,
                                       messages=messages):
    xform_questions = []
    xform = None
    form_errors = []
    xform_validation_errored = False
    xform_validation_missing = False

    try:
        xform = form.wrapped_xform()
    except XFormException as e:
        form_errors.append(u"Error in form: %s" % e)
    except Exception as e:
        logging.exception(e)
        form_errors.append(u"Unexpected error in form: %s" % e)

    if xform and xform.exists():
        if xform.already_has_meta():
            messages.warning(
                request, "This form has a meta block already! "
                "It may be replaced by CommCare HQ's standard meta block.")

        try:
            xform_questions = xform.get_questions(langs, include_triggers=True)
            form.validate_form()
        except etree.XMLSyntaxError as e:
            form_errors.append(u"Syntax Error: %s" % e)
        except AppEditingError as e:
            form_errors.append(u"Error in application: %s" % e)
        except XFormValidationError:
            xform_validation_errored = True
            # showing these messages is handled by validate_form_for_build ajax
            pass
        except XFormValidationFailed:
            xform_validation_missing = True
            messages.warning(request,
                             _("Unable to validate form due to server error."))
        except XFormException as e:
            form_errors.append(u"Error in form: %s" % e)
        # any other kind of error should fail hard,
        # but for now there are too many for that to be practical
        except Exception as e:
            if settings.DEBUG:
                raise
            notify_exception(request, 'Unexpected Build Error')
            form_errors.append(u"Unexpected System Error: %s" % e)
        else:
            # remove upload questions (attachemnts) until MM Case Properties
            # are released to general public
            is_previewer = toggles.MM_CASE_PROPERTIES.enabled(
                request.user.username)
            xform_questions = [
                q for q in xform_questions
                if q["tag"] != "upload" or is_previewer
            ]

        if not form_errors and not xform_validation_missing and not xform_validation_errored:
            try:
                form_action_errors = form.validate_for_build()
                if not form_action_errors:
                    form.add_stuff_to_xform(xform)
            except CaseError as e:
                messages.error(request, u"Error in Case Management: %s" % e)
            except XFormException as e:
                messages.error(request, unicode(e))
            except Exception as e:
                if settings.DEBUG:
                    raise
                logging.exception(unicode(e))
                messages.error(request, u"Unexpected Error: %s" % e)

    try:
        languages = xform.get_languages()
    except Exception:
        languages = []

    for err in form_errors:
        messages.error(request, err)

    module_case_types = []
    app = form.get_app()
    all_modules = list(app.get_modules())
    for module in all_modules:
        for case_type in module.get_case_types():
            module_case_types.append({
                'id': module.unique_id,
                'module_name': trans(module.name, langs),
                'case_type': case_type,
                'module_type': module.doc_type
            })

    if not form.unique_id:
        form.get_unique_id()
        app.save()

    allow_usercase = domain_has_privilege(request.domain, privileges.USER_CASE)
    valid_index_names = DEFAULT_CASE_INDEX_IDENTIFIERS.values()
    if allow_usercase:
        valid_index_names.append(USERCASE_PREFIX[0:-1])  # strip trailing slash

    form_has_schedule = isinstance(
        form, AdvancedForm) and form.get_module().has_schedule
    case_config_options = {
        'caseType': form.get_case_type(),
        'moduleCaseTypes': module_case_types,
        'propertiesMap': get_all_case_properties(app),
        'questions': xform_questions,
        'reserved_words': load_case_reserved_words(),
    }
    context = {
        'nav_form':
        form,
        'xform_languages':
        languages,
        "xform_questions":
        xform_questions,
        'form_errors':
        form_errors,
        'xform_validation_errored':
        xform_validation_errored,
        'xform_validation_missing':
        xform_validation_missing,
        'allow_cloudcare':
        isinstance(form, Form),
        'allow_form_copy':
        isinstance(form, (Form, AdvancedForm)),
        'allow_form_filtering':
        not isinstance(form, CareplanForm) and not form_has_schedule,
        'allow_form_workflow':
        not isinstance(form, CareplanForm),
        'uses_form_workflow':
        form.post_form_workflow == WORKFLOW_FORM,
        'allow_usercase':
        allow_usercase,
        'is_usercase_in_use':
        is_usercase_in_use(request.domain),
        'is_module_filter_enabled':
        app.enable_module_filtering,
        'is_case_list_form':
        form.is_case_list_form,
        'edit_name_url':
        reverse('edit_form_attr',
                args=[app.domain, app.id, form.unique_id, 'name']),
        'case_xpath_pattern_matches':
        CASE_XPATH_PATTERN_MATCHES,
        'case_xpath_substring_matches':
        CASE_XPATH_SUBSTRING_MATCHES,
        'user_case_xpath_pattern_matches':
        USER_CASE_XPATH_PATTERN_MATCHES,
        'user_case_xpath_substring_matches':
        USER_CASE_XPATH_SUBSTRING_MATCHES,
        'custom_instances': [{
            'instanceId': instance.instance_id,
            'instancePath': instance.instance_path
        } for instance in form.custom_instances],
        'can_preview_form':
        request.couch_user.has_permission(domain, 'edit_data')
    }

    if tours.NEW_APP.is_enabled(
            request.user) and not toggles.APP_MANAGER_V2.enabled(
                request.user.username):
        request.guided_tour = tours.NEW_APP.get_tour_data()

    if context['allow_form_workflow'] and toggles.FORM_LINK_WORKFLOW.enabled(
            domain):
        module = form.get_module()

        def qualified_form_name(form, auto_link):
            module_name = trans(form.get_module().name, langs)
            form_name = trans(form.name, langs)
            star = '* ' if auto_link else '  '
            return u"{}{} -> {}".format(star, module_name, form_name)

        modules = filter(lambda m: m.case_type == module.case_type,
                         all_modules)
        if getattr(module, 'root_module_id',
                   None) and module.root_module not in modules:
            modules.append(module.root_module)
        auto_linkable_forms = list(
            itertools.chain.from_iterable(
                list(m.get_forms()) for m in modules))

        def linkable_form(candidate_form):
            auto_link = candidate_form in auto_linkable_forms
            return {
                'unique_id': candidate_form.unique_id,
                'name': qualified_form_name(candidate_form, auto_link),
                'auto_link': auto_link
            }

        context['linkable_forms'] = [
            linkable_form(candidate_form) for candidate_module in all_modules
            for candidate_form in candidate_module.get_forms()
        ]

    if isinstance(form, CareplanForm):
        case_config_options.update({
            'save_url':
            reverse("edit_careplan_form_actions",
                    args=[app.domain, app.id, module.id, form.id]),
            'case_preload': [{
                'key': key,
                'path': path
            } for key, path in form.case_preload.items()],
            'customCaseUpdates': [{
                'key': key,
                'path': path
            } for key, path in form.custom_case_updates.items()],
            'fixedQuestions':
            form.get_fixed_questions(),
            'mode':
            form.mode,
        })
    elif isinstance(form, AdvancedForm):

        def commtrack_programs():
            if app.commtrack_enabled:
                programs = Program.by_domain(app.domain)
                return [{
                    'value': program.get_id,
                    'label': program.name
                } for program in programs]
            else:
                return []

        all_programs = [{'value': '', 'label': _('All Programs')}]
        case_config_options.update({
            'save_url':
            reverse("edit_advanced_form_actions",
                    args=[app.domain, app.id, module.id, form.id]),
            'commtrack_enabled':
            app.commtrack_enabled,
            'commtrack_programs':
            all_programs + commtrack_programs(),
            'module_id':
            module.unique_id,
            'propertyDescriptions':
            get_case_property_description_dict(domain),
        })
        if form.form_type == "shadow_form":
            case_config_options.update({
                'actions': form.extra_actions,
                'isShadowForm': True,
            })
        else:
            case_config_options.update({
                'actions': form.actions,
                'isShadowForm': False,
            })

        if module.has_schedule:
            visit_scheduler_options = get_schedule_context(form)
            visit_scheduler_options.update({
                'questions':
                xform_questions,
                'save_url':
                reverse("edit_visit_schedule",
                        args=[app.domain, app.id, module.id, form.id]),
                'schedule':
                form.schedule,
                'phase':
                visit_scheduler_options['schedule_phase'],
            })
            context.update(
                {'visit_scheduler_options': visit_scheduler_options})
    else:
        case_config_options.update({
            'actions':
            form.actions,
            'allowUsercase':
            allow_usercase,
            'valid_index_names':
            valid_index_names,
            'usercasePropertiesMap':
            get_usercase_properties(app),
            'propertyDescriptions':
            get_case_property_description_dict(domain),
            'save_url':
            reverse("edit_form_actions",
                    args=[app.domain, app.id, module.id, form.id]),
        })
        context.update({
            'show_custom_ref':
            toggles.APP_BUILDER_CUSTOM_PARENT_REF.enabled_for_request(request),
        })
    context.update({'case_config_options': case_config_options})
    template = get_app_manager_template(
        request.user,
        "app_manager/v1/form_view.html",
        "app_manager/v2/form_view.html",
    )
    return template, context
Ejemplo n.º 8
0
def get_casedb_schema(form):
    """Get case database schema definition for vellum to display as an external data source.

    This lists all case types and their properties for the given app.
    """
    app = form.get_app()
    base_case_type = form.get_module().case_type if form.requires_case() else None
    builder = ParentCasePropertyBuilder.for_app(app, ['case_name'], include_parent_properties=False)
    related = builder.get_parent_type_map(None)
    map = builder.get_properties_by_case_type()
    descriptions_dict = get_case_property_description_dict(app.domain)

    if base_case_type:
        # Generate hierarchy of case types, represented as a list of lists of strings:
        # [[base_case_type], [parent_type1, parent_type2...], [grandparent_type1, grandparent_type2...]]
        # Vellum case management only supports three levels
        generation_names = ['case', 'parent', 'grandparent']
        generations = [[] for g in generation_names]

        def _add_ancestors(ctype, generation):
            if generation < len(generation_names):
                generations[generation].append(ctype)
                for parent in related.get(ctype, {}).get('parent', []):
                    _add_ancestors(parent, generation + 1)

        _add_ancestors(base_case_type, 0)

        # Remove any duplicate types or empty generations
        generations = [set(g) for g in generations if len(g)]
    else:
        generations = []

    subsets = [{
        "id": generation_names[i],
        "name": "{} ({})".format(generation_names[i], " or ".join(ctypes)) if i > 0 else base_case_type,
        "structure": {
            p: {"description": descriptions_dict.get(t, {}).get(p, '')}
            for t in ctypes for p in map[t]},
        "related": {"parent": {
            "hashtag": "#case/" + generation_names[i + 1],
            "subset": generation_names[i + 1],
            "key": "@case_id",
        }} if i < len(generations) - 1 else None,
    } for i, ctypes in enumerate(generations)]

    if is_usercase_in_use(app.domain):
        subsets.append({
            "id": USERCASE_TYPE,
            "name": "user",
            "key": "@case_type",
            "structure": {p: {} for p in get_usercase_properties(app)[USERCASE_TYPE]},
        })

    return {
        "id": "casedb",
        "uri": "jr://instance/casedb",
        "name": "case",
        "path": "/casedb/case",
        "structure": {},
        "subsets": subsets,
    }
Ejemplo n.º 9
0
 def _add_case_property_descriptions(self):
     descriptions_dict = get_case_property_description_dict(self.domain)
     for type_ in self.meta.case_types:
         for prop in type_.properties:
             prop.description = descriptions_dict.get(type_.name, {}).get(prop.name, '')