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)]
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
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
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, '')
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, }
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
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
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, }
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, '')