def get_commands(): for form in module.get_suite_forms(): command = Command(id=id_strings.form_command(form, module)) if form.requires_case(): form_datums = self.entries_helper.get_datums_meta_for_form_generic(form, module) var_name = next( meta.datum.id for meta in reversed(form_datums) if meta.action and meta.requires_selection ) case = CaseIDXPath(session_var(var_name)).case() else: case = None if ( getattr(form, "form_filter", None) and not module.put_in_root and (module.all_forms_require_a_case() or is_usercase_in_use(self.app.domain)) ): fixture_xpath = ( session_var(id_strings.fixture_session_var(module)) if module.fixture_select.active else None ) command.relevant = interpolate_xpath(form.form_filter, case, fixture_xpath) if getattr(module, "has_schedule", False) and module.all_forms_require_a_case(): # If there is a schedule and another filter condition, disregard it... # Other forms of filtering are disabled in the UI schedule_filter_condition = MenuContributor._schedule_filter_conditions(form, module, case) if schedule_filter_condition is not None: command.relevant = schedule_filter_condition yield command if hasattr(module, "case_list") and module.case_list.show: yield Command(id=id_strings.case_list_command(module))
def test_interpolate_xpath(self): replacements = { 'case': "<casedb stuff>", 'user': UserCaseXPath().case(), 'session': "instance('commcaresession')/session", } cases = [ ('./lmp < 570.5', '{case}/lmp < 570.5'), ('#case/lmp < 570.5', '{case}/lmp < 570.5'), ('stuff ./lmp < 570.', 'stuff {case}/lmp < 570.'), ('stuff #case/lmp < 570.', 'stuff {case}/lmp < 570.'), ('.53 < hello.', '.53 < hello{case}'), ('.53 < hello#case', '.53 < hello{case}'), ('#session/data/username', '{session}/data/username'), ('"jack" = #session/username', '"jack" = {session}/username'), ('./@case_id = #session/userid', '{case}/@case_id = {session}/userid'), ('#case/@case_id = #user/@case_id', '{case}/@case_id = {user}/@case_id'), ('#host/foo = 42', "instance('casedb')/casedb/case[@case_id={case}/index/host]/foo = 42"), ("'ham' = #parent/spam", "'ham' = instance('casedb')/casedb/case[@case_id={case}/index/parent]/spam"), ] for case in cases: self.assertEqual( interpolate_xpath(case[0], replacements['case']), case[1].format(**replacements) )
def get_filter_xpath(module, delegation=False): filter = module.case_details.short.filter if filter: xpath = "[%s]" % interpolate_xpath(filter) else: xpath = "" if delegation: xpath += "[index/parent/@case_type = '%s']" % module.case_type xpath += "[start_date = '' or double(date(start_date)) <= double(now())]" return xpath
def get_filter_xpath(module, delegation=False): filter = module.case_details.short.filter if filter: xpath = '[%s]' % interpolate_xpath(filter) else: xpath = '' if delegation: xpath += "[index/parent/@case_type = '%s']" % module.case_type xpath += "[start_date = '' or double(date(start_date)) <= double(now())]" return xpath
def get_commands(excluded_form_ids): @memoized def module_uses_case(): return module.all_forms_require_a_case() @memoized def domain_uses_usercase(): return is_usercase_in_use(self.app.domain) for form in module.get_suite_forms(): if form.unique_id in excluded_form_ids: continue command = Command(id=id_strings.form_command(form, module)) if form.requires_case(): form_datums = self.entries_helper.get_datums_meta_for_form_generic(form, module) var_name = next( meta.datum.id for meta in reversed(form_datums) if meta.action and meta.requires_selection ) case = CaseIDXPath(session_var(var_name)).case() else: case = None if getattr(form, 'form_filter', None): fixture_xpath = ( session_var(id_strings.fixture_session_var(module)) if module.fixture_select.active else None ) interpolated_xpath = interpolate_xpath(form.form_filter, case, fixture_xpath, module=module, form=form) if xpath_references_case(interpolated_xpath) and \ (not module_uses_case() or module.put_in_root and not module.root_requires_same_case()): raise CaseXPathValidationError(module=module, form=form) if xpath_references_user_case(interpolated_xpath) and not domain_uses_usercase(): raise UserCaseXPathValidationError(module=module, form=form) command.relevant = interpolated_xpath if getattr(module, 'has_schedule', False) and module.all_forms_require_a_case(): # If there is a schedule and another filter condition, disregard it... # Other forms of filtering are disabled in the UI schedule_filter_condition = MenuContributor._schedule_filter_conditions(form, module, case) if schedule_filter_condition is not None: command.relevant = schedule_filter_condition yield command if hasattr(module, 'case_list') and module.case_list.show: yield Command(id=id_strings.case_list_command(module))
def validate_details_for_build(self): errors = [] for sort_element in self.module.detail_sort_elements: try: self._validate_detail_screen_field(sort_element.field) except ValueError: errors.append({ 'type': 'invalid sort field', 'field': sort_element.field, 'module': self.get_module_info(), }) if self.module.case_list_filter: try: # test filter is valid, while allowing for advanced user hacks like "foo = 1][bar = 2" case_list_filter = interpolate_xpath( 'dummy[' + self.module.case_list_filter + ']') etree.XPath(case_list_filter) except (etree.XPathSyntaxError, CaseXPathValidationError): errors.append({ 'type': 'invalid filter xpath', 'module': self.get_module_info(), 'filter': self.module.case_list_filter, }) for detail in [ self.module.case_details.short, self.module.case_details.long ]: if detail.use_case_tiles: if not detail.display == "short": errors.append({ 'type': "invalid tile configuration", 'module': self.get_module_info(), 'reason': _('Case tiles may only be used for the case list (not the case details).' ) }) col_by_tile_field = { c.case_tile_field: c for c in detail.columns } for field in [ "header", "top_left", "sex", "bottom_left", "date" ]: if field not in col_by_tile_field: errors.append({ 'type': "invalid tile configuration", 'module': self.get_module_info(), 'reason': _('A case property must be assigned to the "{}" tile field.' .format(field)) }) return errors
def build_query_prompts(self): prompts = [] for prop in self.module.search_config.properties: text = Text(locale_id=id_strings.search_property_locale( self.module, prop.name)) if prop.hint: display = Display( text=text, hint=Hint(text=Text( locale_id=id_strings.search_property_hint_locale( self.module, prop.name)))) else: display = Display(text=text) kwargs = {'key': prop.name, 'display': display} if not prop.appearance or prop.itemset.nodeset: kwargs['receive'] = prop.receiver_expression if prop.hidden: kwargs['hidden'] = prop.hidden if prop.appearance and self.app.enable_search_prompt_appearance: if prop.appearance == 'address': kwargs['input_'] = prop.appearance else: kwargs['appearance'] = prop.appearance if prop.input_: kwargs['input_'] = prop.input_ if prop.default_value and self.app.enable_default_value_expression: kwargs['default_value'] = prop.default_value if prop.itemset.nodeset: kwargs['itemset'] = Itemset( nodeset=prop.itemset.nodeset, label_ref=prop.itemset.label, value_ref=prop.itemset.value, sort_ref=prop.itemset.sort, ) if prop.allow_blank_value: kwargs['allow_blank_value'] = prop.allow_blank_value if prop.exclude: kwargs['exclude'] = "true()" if prop.required: kwargs['required'] = interpolate_xpath(prop.required) prompts.append(QueryPrompt(**kwargs)) return prompts
def _get_detail_tab_nodeset(self, module, detail, tab): if not tab.has_nodeset: return None if tab.nodeset: return tab.nodeset if tab.nodeset_case_type: nodeset = CaseTypeXpath(tab.nodeset_case_type) nodeset = nodeset.case( instance_name=detail.get_instance_name(module)) nodeset = nodeset.select( CaseXPath().parent_id(), CaseXPath("current()").property("@case_id")) nodeset = nodeset.select("@status", "open") if tab.nodeset_filter: nodeset = nodeset.select_raw( interpolate_xpath(tab.nodeset_filter)) return nodeset return None
def validate_details_for_build(self): errors = [] for sort_element in self.module.detail_sort_elements: try: self._validate_detail_screen_field(sort_element.field) except ValueError: errors.append({ 'type': 'invalid sort field', 'field': sort_element.field, 'module': self.get_module_info(), }) if self.module.case_list_filter: try: # test filter is valid, while allowing for advanced user hacks like "foo = 1][bar = 2" case_list_filter = interpolate_xpath('dummy[' + self.module.case_list_filter + ']') etree.XPath(case_list_filter) except (etree.XPathSyntaxError, CaseXPathValidationError): errors.append({ 'type': 'invalid filter xpath', 'module': self.get_module_info(), 'filter': self.module.case_list_filter, }) for detail in [self.module.case_details.short, self.module.case_details.long]: if detail.use_case_tiles: if not detail.display == "short": errors.append({ 'type': "invalid tile configuration", 'module': self.get_module_info(), 'reason': _('Case tiles may only be used for the case list (not the case details).') }) col_by_tile_field = {c.case_tile_field: c for c in detail.columns} for field in ["header", "top_left", "sex", "bottom_left", "date"]: if field not in col_by_tile_field: errors.append({ 'type': "invalid tile configuration", 'module': self.get_module_info(), 'reason': _('A case property must be assigned to the "{}" tile field.'.format(field)) }) return errors
def test_interpolate_xpath_error(self): for case in ('./lmp < 570.5', '#case/lmp < 570.5'): with self.assertRaises(CaseXPathValidationError): interpolate_xpath(case, None),
def get_module_contributions(self, module): def get_commands(): for form in module.get_suite_forms(): command = Command(id=id_strings.form_command(form, module)) if form.requires_case(): form_datums = self.entries_helper.get_datums_meta_for_form_generic(form, module) var_name = next( meta.datum.id for meta in reversed(form_datums) if meta.action and meta.requires_selection ) case = CaseIDXPath(session_var(var_name)).case() else: case = None if ( getattr(form, 'form_filter', None) and not module.put_in_root and (module.all_forms_require_a_case() or is_usercase_in_use(self.app.domain)) ): fixture_xpath = ( session_var(id_strings.fixture_session_var(module)) if module.fixture_select.active else None ) command.relevant = interpolate_xpath(form.form_filter, case, fixture_xpath) if getattr(module, 'has_schedule', False) and module.all_forms_require_a_case(): # If there is a schedule and another filter condition, disregard it... # Other forms of filtering are disabled in the UI schedule_filter_condition = MenuContributor._schedule_filter_conditions(form, module, case) if schedule_filter_condition is not None: command.relevant = schedule_filter_condition yield command if hasattr(module, 'case_list') and module.case_list.show: yield Command(id=id_strings.case_list_command(module)) supports_module_filter = ( self.app.domain and MODULE_FILTER.enabled(self.app.domain) and self.app.enable_module_filtering and getattr(module, 'module_filter', None) ) menus = [] if hasattr(module, 'get_menus'): for menu in module.get_menus(supports_module_filter=supports_module_filter): menus.append(menu) elif module.module_type != 'careplan': id_modules = [module] root_modules = [] shadow_modules = [m for m in self.app.get_modules() if m.doc_type == "ShadowModule" and m.source_module_id] put_in_root = getattr(module, 'put_in_root', False) if not put_in_root and getattr(module, 'root_module', False): root_modules.append(module.root_module) for shadow in shadow_modules: if module.root_module.unique_id == shadow.source_module_id: root_modules.append(shadow) else: root_modules.append(None) if put_in_root and getattr(module, 'root_module', False): for shadow in shadow_modules: if module.root_module.unique_id == shadow.source_module_id: id_modules.append(shadow) for id_module in id_modules: for root_module in root_modules: menu_kwargs = {} suffix = "" if root_module: menu_kwargs.update({'root': id_strings.menu_id(root_module)}) suffix = id_strings.menu_id(root_module) if root_module.doc_type == 'ShadowModule' else "" menu_kwargs.update({'id': id_strings.menu_id(id_module, suffix)}) if supports_module_filter: menu_kwargs['relevant'] = interpolate_xpath(module.module_filter) if self.app.enable_localized_menu_media: menu_kwargs.update({ 'menu_locale_id': id_strings.module_locale(module), 'media_image': bool(len(module.all_image_paths())), 'media_audio': bool(len(module.all_audio_paths())), 'image_locale_id': id_strings.module_icon_locale(module), 'audio_locale_id': id_strings.module_audio_locale(module), }) menu = LocalizedMenu(**menu_kwargs) else: menu_kwargs.update({ 'locale_id': id_strings.module_locale(module), 'media_image': module.default_media_image, 'media_audio': module.default_media_audio, }) menu = Menu(**menu_kwargs) menu.commands.extend(get_commands()) menus.append(menu) if self.app.use_grid_menus: self._give_root_menus_grid_style(menus) return menus
class MenuContributor(SuiteContributorByModule): def get_module_contributions(self, module): def get_commands(excluded_form_ids): @memoized def module_uses_case(): return module.all_forms_require_a_case() @memoized def domain_uses_usercase(): return is_usercase_in_use(self.app.domain) for form in module.get_suite_forms(): if form.unique_id in excluded_form_ids: continue command = Command(id=id_strings.form_command(form, module)) if form.requires_case(): form_datums = self.entries_helper.get_datums_meta_for_form_generic( form, module) var_name = next(meta.datum.id for meta in reversed(form_datums) if meta.action and meta.requires_selection) case = CaseIDXPath(session_var(var_name)).case() else: case = None if getattr(form, 'form_filter', None): fixture_xpath = (session_var( id_strings.fixture_session_var(module)) if module.fixture_select.active else None) interpolated_xpath = interpolate_xpath(form.form_filter, case, fixture_xpath, module=module, form=form) if xpath_references_case(interpolated_xpath) and \ (not module_uses_case() or module.put_in_root and not module.root_requires_same_case()): raise CaseXPathValidationError(module=module, form=form) if xpath_references_user_case( interpolated_xpath) and not domain_uses_usercase(): raise UserCaseXPathValidationError(module=module, form=form) command.relevant = interpolated_xpath if getattr(module, 'has_schedule', False) and module.all_forms_require_a_case(): # If there is a schedule and another filter condition, disregard it... # Other forms of filtering are disabled in the UI schedule_filter_condition = MenuContributor._schedule_filter_conditions( form, module, case) if schedule_filter_condition is not None: command.relevant = schedule_filter_condition yield command if hasattr(module, 'case_list') and module.case_list.show: yield Command(id=id_strings.case_list_command(module)) supports_module_filter = self.app.enable_module_filtering and getattr( module, 'module_filter', None) menus = [] if hasattr(module, 'get_menus'): for menu in module.get_menus( supports_module_filter=supports_module_filter): menus.append(menu) else: from corehq.apps.app_manager.models import ShadowModule id_modules = [module] root_modules = [] shadow_modules = [ m for m in self.app.get_modules() if isinstance(m, ShadowModule) and m.source_module_id ] put_in_root = getattr(module, 'put_in_root', False) if not put_in_root and getattr(module, 'root_module', False): root_modules.append(module.root_module) for shadow in shadow_modules: if module.root_module.unique_id == shadow.source_module_id: root_modules.append(shadow) else: root_modules.append(None) if put_in_root and getattr(module, 'root_module', False): for shadow in shadow_modules: if module.root_module.unique_id == shadow.source_module_id: id_modules.append(shadow) for id_module in id_modules: for root_module in root_modules: menu_kwargs = {} suffix = "" if root_module: menu_kwargs.update( {'root': id_strings.menu_id(root_module)}) suffix = id_strings.menu_id(root_module) if isinstance( root_module, ShadowModule) else "" menu_kwargs.update( {'id': id_strings.menu_id(id_module, suffix)}) if supports_module_filter: menu_kwargs['relevant'] = interpolate_xpath( module.module_filter) if self.app.enable_localized_menu_media: module_custom_icon = module.custom_icon menu_kwargs.update({ 'menu_locale_id': id_strings.module_locale(module), 'media_image': bool(len(module.all_image_paths())), 'media_audio': bool(len(module.all_audio_paths())), 'image_locale_id': id_strings.module_icon_locale(module), 'audio_locale_id': id_strings.module_audio_locale(module), 'custom_icon_locale_id': (id_strings.module_custom_icon_locale( module, module_custom_icon.form) if module_custom_icon and not module_custom_icon.xpath else None), 'custom_icon_form': (module_custom_icon.form if module_custom_icon else None), 'custom_icon_xpath': (module_custom_icon.xpath if module_custom_icon and module_custom_icon.xpath else None), }) menu = LocalizedMenu(**menu_kwargs) else: menu_kwargs.update({ 'locale_id': id_strings.module_locale(module), 'media_image': module.default_media_image, 'media_audio': module.default_media_audio, }) menu = Menu(**menu_kwargs) excluded_form_ids = [] if root_module and isinstance(root_module, ShadowModule): excluded_form_ids = root_module.excluded_form_ids if id_module and isinstance(id_module, ShadowModule): excluded_form_ids = id_module.excluded_form_ids menu.commands.extend(get_commands(excluded_form_ids)) if len(menu.commands): menus.append(menu) if self.app.grid_display_for_all_modules() or \ self.app.grid_display_for_some_modules() and module.grid_display_style(): self._give_non_root_menus_grid_style(menus) if self.app.use_grid_menus: self._give_root_menu_grid_style(menus) return menus
def _generate_menu(self, module, root_module, training_menu, id_module): # In general, `id_module` and `module` will be the same thing. # In the case of v1 shadow menus, `id_module` is either the current module or one of that module's shadows # For more information, see the note in `_generate_v1_shadow_menus`. from corehq.apps.app_manager.models import ShadowModule menu_kwargs = {} suffix = "" if id_module.is_training_module: menu_kwargs.update({'root': 'training-root'}) elif root_module: menu_kwargs.update({'root': id_strings.menu_id(root_module)}) suffix = id_strings.menu_id(root_module) if isinstance( root_module, ShadowModule) else "" menu_kwargs.update({'id': id_strings.menu_id(id_module, suffix)}) # Determine relevancy if self.app.enable_module_filtering: relevancy = id_module.module_filter # If module has a parent, incorporate the parent's relevancy. # This is only necessary when the child uses display only forms. if id_module.put_in_root and id_module.root_module and id_module.root_module.module_filter: if relevancy: relevancy = str( XPath.and_( XPath(relevancy).paren(force=True), XPath(id_module.root_module.module_filter).paren( force=True))) else: relevancy = id_module.root_module.module_filter if relevancy: menu_kwargs['relevant'] = interpolate_xpath(relevancy) if self.app.enable_localized_menu_media: module_custom_icon = module.custom_icon menu_kwargs.update({ 'menu_locale_id': get_module_locale_id(module), 'media_image': module.uses_image(build_profile_id=self.build_profile_id), 'media_audio': module.uses_audio(build_profile_id=self.build_profile_id), 'image_locale_id': id_strings.module_icon_locale(module), 'audio_locale_id': id_strings.module_audio_locale(module), 'custom_icon_locale_id': (id_strings.module_custom_icon_locale( module, module_custom_icon.form) if module_custom_icon and not module_custom_icon.xpath else None), 'custom_icon_form': (module_custom_icon.form if module_custom_icon else None), 'custom_icon_xpath': (module_custom_icon.xpath if module_custom_icon and module_custom_icon.xpath else None), }) menu = LocalizedMenu(**menu_kwargs) else: menu_kwargs.update({ 'media_image': module.default_media_image, 'media_audio': module.default_media_audio, 'locale_id': get_module_locale_id(module), }) menu = Menu(**menu_kwargs) excluded_form_ids = [] if root_module and isinstance(root_module, ShadowModule): excluded_form_ids = root_module.excluded_form_ids if id_module and isinstance(id_module, ShadowModule): excluded_form_ids = id_module.excluded_form_ids commands = self._get_commands(excluded_form_ids, module) if module.is_training_module and module.put_in_root and training_menu: training_menu.commands.extend(commands) else: menu.commands.extend(commands) return menu
class MenuContributor(SuiteContributorByModule): def get_module_contributions(self, module, training_menu): def get_commands(excluded_form_ids): @memoized def module_uses_case(): return module.all_forms_require_a_case() @memoized def domain_uses_usercase(): return is_usercase_in_use(self.app.domain) for form in module.get_suite_forms(): if form.unique_id in excluded_form_ids: continue command = Command(id=id_strings.form_command(form, module)) if form.requires_case(): form_datums = self.entries_helper.get_datums_meta_for_form_generic(form) var_name = next( meta.datum.id for meta in reversed(form_datums) if meta.action and meta.requires_selection ) case = CaseIDXPath(session_var(var_name)).case() else: case = None if getattr(form, 'form_filter', None): fixture_xpath = ( session_var(id_strings.fixture_session_var(module)) if module.fixture_select.active else None ) interpolated_xpath = interpolate_xpath(form.form_filter, case, fixture_xpath, module=module, form=form) if xpath_references_case(interpolated_xpath) and \ (not module_uses_case() or module.put_in_root and not module.root_requires_same_case()): raise CaseXPathValidationError(module=module, form=form) if xpath_references_user_case(interpolated_xpath) and not domain_uses_usercase(): raise UserCaseXPathValidationError(module=module, form=form) command.relevant = interpolated_xpath if getattr(module, 'has_schedule', False) and module.all_forms_require_a_case(): # If there is a schedule and another filter condition, disregard it... # Other forms of filtering are disabled in the UI schedule_filter_condition = MenuContributor._schedule_filter_conditions(form, module, case) if schedule_filter_condition is not None: command.relevant = schedule_filter_condition yield command if hasattr(module, 'case_list') and module.case_list.show: yield Command(id=id_strings.case_list_command(module)) menus = [] if hasattr(module, 'get_menus'): for menu in module.get_menus(build_profile_id=self.build_profile_id): menus.append(menu) else: from corehq.apps.app_manager.models import ShadowModule id_modules = [module] # the current module and all of its shadows root_modules = [] # the current module's parent and all of that parent's shadows shadow_modules = [m for m in self.app.get_modules() if isinstance(m, ShadowModule) and m.source_module_id] if not module.put_in_root and module.root_module: root_modules.append(module.root_module) for shadow in shadow_modules: if module.root_module.unique_id == shadow.source_module_id: root_modules.append(shadow) else: root_modules.append(None) if module.put_in_root and module.root_module: for shadow in shadow_modules: if module.root_module.unique_id == shadow.source_module_id: id_modules.append(shadow) for id_module in id_modules: for root_module in root_modules: menu_kwargs = {} suffix = "" if id_module.is_training_module: menu_kwargs.update({'root': 'training-root'}) elif root_module: menu_kwargs.update({'root': id_strings.menu_id(root_module)}) suffix = id_strings.menu_id(root_module) if isinstance(root_module, ShadowModule) else "" menu_kwargs.update({'id': id_strings.menu_id(id_module, suffix)}) # Determine relevancy if self.app.enable_module_filtering: relevancy = id_module.module_filter # If module has a parent, incorporate the parent's relevancy. # This is only necessary when the child uses display only forms. if id_module.put_in_root and id_module.root_module and id_module.root_module.module_filter: if relevancy: relevancy = str(XPath.and_(XPath(relevancy).paren(force=True), XPath(id_module.root_module.module_filter).paren(force=True))) else: relevancy = id_module.root_module.module_filter if relevancy: menu_kwargs['relevant'] = interpolate_xpath(relevancy) if self.app.enable_localized_menu_media: module_custom_icon = module.custom_icon menu_kwargs.update({ 'menu_locale_id': get_module_locale_id(module), 'menu_enum_text': get_module_enum_text(module), 'media_image': module.uses_image(build_profile_id=self.build_profile_id), 'media_audio': module.uses_audio(build_profile_id=self.build_profile_id), 'image_locale_id': id_strings.module_icon_locale(module), 'audio_locale_id': id_strings.module_audio_locale(module), 'custom_icon_locale_id': ( id_strings.module_custom_icon_locale(module, module_custom_icon.form) if module_custom_icon and not module_custom_icon.xpath else None), 'custom_icon_form': (module_custom_icon.form if module_custom_icon else None), 'custom_icon_xpath': (module_custom_icon.xpath if module_custom_icon and module_custom_icon.xpath else None), }) menu = LocalizedMenu(**menu_kwargs) else: menu_kwargs.update({ 'media_image': module.default_media_image, 'media_audio': module.default_media_audio, 'locale_id': get_module_locale_id(module), 'enum_text': get_module_enum_text(module), }) menu = Menu(**menu_kwargs) excluded_form_ids = [] if root_module and isinstance(root_module, ShadowModule): excluded_form_ids = root_module.excluded_form_ids if id_module and isinstance(id_module, ShadowModule): excluded_form_ids = id_module.excluded_form_ids commands = get_commands(excluded_form_ids) if module.is_training_module and module.put_in_root and training_menu: training_menu.commands.extend(commands) else: menu.commands.extend(commands) if len(menu.commands): menus.append(menu) if self.app.grid_display_for_all_modules() or \ self.app.grid_display_for_some_modules() and module.grid_display_style(): self._give_non_root_menus_grid_style(menus) if self.app.use_grid_menus: self._give_root_menu_grid_style(menus) return menus
def get_module_contributions(self, module): def get_commands(excluded_form_ids): @memoized def module_uses_case(): return module.all_forms_require_a_case() @memoized def domain_uses_usercase(): return is_usercase_in_use(self.app.domain) for form in module.get_suite_forms(): if form.unique_id in excluded_form_ids: continue command = Command(id=id_strings.form_command(form, module)) if form.requires_case(): form_datums = self.entries_helper.get_datums_meta_for_form_generic(form, module) var_name = next( meta.datum.id for meta in reversed(form_datums) if meta.action and meta.requires_selection ) case = CaseIDXPath(session_var(var_name)).case() else: case = None if getattr(form, 'form_filter', None): fixture_xpath = ( session_var(id_strings.fixture_session_var(module)) if module.fixture_select.active else None ) interpolated_xpath = interpolate_xpath(form.form_filter, case, fixture_xpath, module=module, form=form) if xpath_references_case(interpolated_xpath) and \ (not module_uses_case() or module.put_in_root): raise CaseXPathValidationError(module=module, form=form) if xpath_references_user_case(interpolated_xpath) and not domain_uses_usercase(): raise UserCaseXPathValidationError(module=module, form=form) command.relevant = interpolated_xpath if getattr(module, 'has_schedule', False) and module.all_forms_require_a_case(): # If there is a schedule and another filter condition, disregard it... # Other forms of filtering are disabled in the UI schedule_filter_condition = MenuContributor._schedule_filter_conditions(form, module, case) if schedule_filter_condition is not None: command.relevant = schedule_filter_condition yield command if hasattr(module, 'case_list') and module.case_list.show: yield Command(id=id_strings.case_list_command(module)) supports_module_filter = self.app.enable_module_filtering and getattr(module, 'module_filter', None) menus = [] if hasattr(module, 'get_menus'): for menu in module.get_menus(supports_module_filter=supports_module_filter): menus.append(menu) elif module.module_type != 'careplan': from corehq.apps.app_manager.models import ShadowModule id_modules = [module] root_modules = [] shadow_modules = [m for m in self.app.get_modules() if isinstance(m, ShadowModule) and m.source_module_id] put_in_root = getattr(module, 'put_in_root', False) if not put_in_root and getattr(module, 'root_module', False): root_modules.append(module.root_module) for shadow in shadow_modules: if module.root_module.unique_id == shadow.source_module_id: root_modules.append(shadow) else: root_modules.append(None) if put_in_root and getattr(module, 'root_module', False): for shadow in shadow_modules: if module.root_module.unique_id == shadow.source_module_id: id_modules.append(shadow) for id_module in id_modules: for root_module in root_modules: menu_kwargs = {} suffix = "" if root_module: menu_kwargs.update({'root': id_strings.menu_id(root_module)}) suffix = id_strings.menu_id(root_module) if isinstance(root_module, ShadowModule) else "" menu_kwargs.update({'id': id_strings.menu_id(id_module, suffix)}) if supports_module_filter: menu_kwargs['relevant'] = interpolate_xpath(module.module_filter) if self.app.enable_localized_menu_media: menu_kwargs.update({ 'menu_locale_id': id_strings.module_locale(module), 'media_image': bool(len(module.all_image_paths())), 'media_audio': bool(len(module.all_audio_paths())), 'image_locale_id': id_strings.module_icon_locale(module), 'audio_locale_id': id_strings.module_audio_locale(module), }) menu = LocalizedMenu(**menu_kwargs) else: menu_kwargs.update({ 'locale_id': id_strings.module_locale(module), 'media_image': module.default_media_image, 'media_audio': module.default_media_audio, }) menu = Menu(**menu_kwargs) excluded_form_ids = [] if root_module and isinstance(root_module, ShadowModule): excluded_form_ids = root_module.excluded_form_ids if id_module and isinstance(id_module, ShadowModule): excluded_form_ids = id_module.excluded_form_ids menu.commands.extend(get_commands(excluded_form_ids)) if len(menu.commands): menus.append(menu) if self.app.grid_display_for_all_modules() or \ self.app.grid_display_for_some_modules() and module.grid_display_style(): self._give_non_root_menus_grid_style(menus) if self.app.grid_menu_toggle_enabled() and self.app.use_grid_menus: self._give_root_menu_grid_style(menus) return menus
def check_actions(self): errors = [] from corehq.apps.app_manager.models import AdvancedOpenCaseAction, LoadUpdateAction case_tags = list(self.form.actions.get_case_tags()) for action in self.form.actions.get_subcase_actions(): for case_index in action.case_indices: if case_index.tag not in case_tags: errors.append({'type': 'missing parent tag', 'case_tag': case_index.tag}) if case_index.relationship == 'question' and not case_index.relationship_question: errors.append({'type': 'missing relationship question', 'case_tag': case_index.tag}) if isinstance(action, AdvancedOpenCaseAction): if not action.name_path: errors.append({'type': 'case_name required', 'case_tag': action.case_tag}) for case_index in action.case_indices: meta = self.form.actions.actions_meta_by_tag.get(case_index.tag) if meta and meta['type'] == 'open' and meta['action'].repeat_context: if ( not action.repeat_context or not action.repeat_context.startswith(meta['action'].repeat_context) ): errors.append({'type': 'subcase repeat context', 'case_tag': action.case_tag, 'parent_tag': case_index.tag}) errors.extend(self.check_case_properties( subcase_names=action.get_property_names(), case_tag=action.case_tag )) for action in self.form.actions.get_all_actions(): if not action.case_type and (not isinstance(action, LoadUpdateAction) or not action.auto_select): errors.append({'type': "no case type in action", 'case_tag': action.case_tag}) if isinstance(action, LoadUpdateAction) and action.auto_select: mode = action.auto_select.mode if not action.auto_select.value_key: key_names = { AUTO_SELECT_CASE: _('Case property'), AUTO_SELECT_FIXTURE: _('Lookup Table field'), AUTO_SELECT_USER: _('custom user property'), AUTO_SELECT_RAW: _('custom XPath expression'), } if mode in key_names: errors.append({'type': 'auto select key', 'key_name': key_names[mode]}) if not action.auto_select.value_source: source_names = { AUTO_SELECT_CASE: _('Case tag'), AUTO_SELECT_FIXTURE: _('Lookup Table tag'), } if mode in source_names: errors.append({'type': 'auto select source', 'source_name': source_names[mode]}) elif mode == AUTO_SELECT_CASE: case_tag = action.auto_select.value_source if not self.form.actions.get_action_from_tag(case_tag): errors.append({'type': 'auto select case ref', 'case_tag': action.case_tag}) errors.extend(self.check_case_properties( all_names=action.get_property_names(), case_tag=action.case_tag )) if self.form.form_filter: # Replace any dots with #case, which doesn't make for valid xpath # but will trigger any appropriate validation errors interpolated_form_filter = interpolate_xpath(self.form.form_filter, case_xpath="#case", module=self.form.get_module(), form=self.form) form_filter_references_case = ( xpath_references_case(interpolated_form_filter) or xpath_references_user_case(interpolated_form_filter) ) if form_filter_references_case: if not any(action for action in self.form.actions.load_update_cases if not action.auto_select): errors.append({'type': "filtering without case"}) def generate_paths(): for action in self.form.actions.get_all_actions(): for path in action.get_paths(): yield path if self.form.schedule: if self.form.schedule.transition_condition.type == 'if': yield self.form.schedule.transition_condition.question if self.form.schedule.termination_condition.type == 'if': yield self.form.schedule.termination_condition.question errors.extend(self.check_paths(generate_paths())) return errors
def check_actions(self): errors = [] from corehq.apps.app_manager.models import AdvancedOpenCaseAction, LoadUpdateAction for action in self.form.actions.get_subcase_actions(): case_tags = self.form.actions.get_case_tags() for case_index in action.case_indices: if case_index.tag not in case_tags: errors.append({'type': 'missing parent tag', 'case_tag': case_index.tag}) if case_index.relationship == 'question' and not case_index.relationship_question: errors.append({'type': 'missing relationship question', 'case_tag': case_index.tag}) if isinstance(action, AdvancedOpenCaseAction): if not action.name_path: errors.append({'type': 'case_name required', 'case_tag': action.case_tag}) for case_index in action.case_indices: meta = self.form.actions.actions_meta_by_tag.get(case_index.tag) if meta and meta['type'] == 'open' and meta['action'].repeat_context: if ( not action.repeat_context or not action.repeat_context.startswith(meta['action'].repeat_context) ): errors.append({'type': 'subcase repeat context', 'case_tag': action.case_tag, 'parent_tag': case_index.tag}) errors.extend(self.check_case_properties( subcase_names=action.get_property_names(), case_tag=action.case_tag )) for action in self.form.actions.get_all_actions(): if not action.case_type and (not isinstance(action, LoadUpdateAction) or not action.auto_select): errors.append({'type': "no case type in action", 'case_tag': action.case_tag}) if isinstance(action, LoadUpdateAction) and action.auto_select: mode = action.auto_select.mode if not action.auto_select.value_key: key_names = { AUTO_SELECT_CASE: _('Case property'), AUTO_SELECT_FIXTURE: _('Lookup Table field'), AUTO_SELECT_USER: _('custom user property'), AUTO_SELECT_RAW: _('custom XPath expression'), } if mode in key_names: errors.append({'type': 'auto select key', 'key_name': key_names[mode]}) if not action.auto_select.value_source: source_names = { AUTO_SELECT_CASE: _('Case tag'), AUTO_SELECT_FIXTURE: _('Lookup Table tag'), } if mode in source_names: errors.append({'type': 'auto select source', 'source_name': source_names[mode]}) elif mode == AUTO_SELECT_CASE: case_tag = action.auto_select.value_source if not self.form.actions.get_action_from_tag(case_tag): errors.append({'type': 'auto select case ref', 'case_tag': action.case_tag}) errors.extend(self.check_case_properties( all_names=action.get_property_names(), case_tag=action.case_tag )) if self.form.form_filter: # Replace any dots with #case, which doesn't make for valid xpath # but will trigger any appropriate validation errors interpolated_form_filter = interpolate_xpath(self.form.form_filter, case_xpath="#case", module=self.form.get_module(), form=self.form) form_filter_references_case = ( xpath_references_case(interpolated_form_filter) or xpath_references_user_case(interpolated_form_filter) ) if form_filter_references_case: if not any(action for action in self.form.actions.load_update_cases if not action.auto_select): errors.append({'type': "filtering without case"}) def generate_paths(): for action in self.form.actions.get_all_actions(): for path in action.get_paths(): yield path if self.form.schedule: if self.form.schedule.transition_condition.type == 'if': yield self.form.schedule.transition_condition.question if self.form.schedule.termination_condition.type == 'if': yield self.form.schedule.termination_condition.question errors.extend(self.check_paths(generate_paths())) return errors