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 _get_static_stack_frame(form_workflow, xpath=None): if form_workflow == WORKFLOW_ROOT: return StackFrameMeta(xpath, [], allow_empty_frame=True) elif form_workflow == WORKFLOW_MODULE: frame_children = frame_children_for_module( module, include_user_selections=False) return StackFrameMeta(xpath, frame_children) elif form_workflow == WORKFLOW_PARENT_MODULE: root_module = module.root_module frame_children = frame_children_for_module(root_module) return StackFrameMeta(xpath, frame_children) elif form_workflow == WORKFLOW_PREVIOUS: frame_children = self.helper.get_frame_children( id_strings.form_command(form), module, include_target_root=True) # since we want to go the 'previous' screen we need to drop the last # datum last = frame_children.pop() while isinstance( last, WorkflowDatumMeta) and not last.requires_selection: # keep removing last element until we hit a command # or a non-autoselect datum last = frame_children.pop() return StackFrameMeta(xpath, frame_children)
def _get_reg_form_action(self, module): """ Returns registration form action """ form = self.app.get_form(module.case_list_form.form_id) if self.app.enable_localized_menu_media: case_list_form = module.case_list_form action = LocalizedAction( menu_locale_id=id_strings.case_list_form_locale(module), media_image=case_list_form.uses_image( build_profile_id=self.build_profile_id), media_audio=case_list_form.uses_audio( build_profile_id=self.build_profile_id), image_locale_id=id_strings.case_list_form_icon_locale(module), audio_locale_id=id_strings.case_list_form_audio_locale(module), stack=Stack(), for_action_menu=True, ) else: action = Action(display=Display( text=Text(locale_id=id_strings.case_list_form_locale(module)), media_image=module.case_list_form.default_media_image, media_audio=module.case_list_form.default_media_audio, ), stack=Stack()) frame = PushFrame() frame.add_command(XPath.string(id_strings.form_command(form))) target_form_dm = self.entries_helper.get_datums_meta_for_form_generic( form) source_form_dm = [] if len(module.forms): source_form_dm = self.entries_helper.get_datums_meta_for_form_generic( module.get_form(0)) for target_meta in target_form_dm: if target_meta.requires_selection: # This is true for registration forms where the case being created is a subcase try: [source_dm] = [ source_meta for source_meta in source_form_dm if source_meta.case_type == target_meta.case_type ] except ValueError: pass else: frame.add_datum( StackDatum(id=target_meta.datum.id, value=session_var(source_dm.datum.id))) else: s_datum = target_meta.datum frame.add_datum( StackDatum(id=s_datum.id, value=s_datum.function)) frame.add_datum( StackDatum(id=RETURN_TO, value=XPath.string(id_strings.menu_id(module)))) action.stack.add_frame(frame) return action
def _add_action_to_detail(self, detail, module): # add form action to detail form = self.app.get_form(module.case_list_form.form_id) if self.app.enable_localized_menu_media: case_list_form = module.case_list_form detail.action = LocalizedAction( menu_locale_id=id_strings.case_list_form_locale(module), media_image=bool(len(case_list_form.all_image_paths())), media_audio=bool(len(case_list_form.all_audio_paths())), image_locale_id=id_strings.case_list_form_icon_locale(module), audio_locale_id=id_strings.case_list_form_audio_locale(module), stack=Stack(), for_action_menu=True, ) else: detail.action = Action( display=Display( text=Text(locale_id=id_strings.case_list_form_locale(module)), media_image=module.case_list_form.default_media_image, media_audio=module.case_list_form.default_media_audio, ), stack=Stack() ) frame = PushFrame() frame.add_command(XPath.string(id_strings.form_command(form))) target_form_dm = self.entries_helper.get_datums_meta_for_form_generic(form) source_form_dm = self.entries_helper.get_datums_meta_for_form_generic(module.get_form(0)) for target_meta in target_form_dm: if target_meta.requires_selection: # This is true for registration forms where the case being created is a subcase try: [source_dm] = [ source_meta for source_meta in source_form_dm if source_meta.case_type == target_meta.case_type ] except ValueError: message = _( "The '{form}' form selected as the case list registration form " "for the '{module}' module requires a '{case_type}' case. " "The '{module}' must load a case of this type.").format( form=form.default_name(), module=module.default_name(), case_type=target_meta.case_type ) raise SuiteValidationError(message) else: frame.add_datum(StackDatum( id=target_meta.datum.id, value=session_var(source_dm.datum.id)) ) else: s_datum = target_meta.datum frame.add_datum(StackDatum(id=s_datum.id, value=s_datum.function)) frame.add_datum(StackDatum(id=RETURN_TO, value=XPath.string(id_strings.menu_id(module)))) detail.action.stack.add_frame(frame)
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
def get_frame_children(self, target_form, module_only=False, include_target_root=False): """ For a form return the list of stack frame children that are required to navigate to that form. This is based on the following algorithm: * Add the module the form is in to the stack (we'll call this `m`) * Walk through all forms in the module, determine what datum selections are present in all of the forms (this may be an empty set) * Basically if there are three forms that respectively load * f1: v1, v2, v3, v4 * f2: v1, v2, v4 * f3: v1, v2 * The longest common chain is v1, v2 * Add a datum for each of those values to the stack * Add the form "command id" for the <entry> to the stack * Add the remainder of the datums for the current form to the stack * For the three forms above, the stack entries for "last element" would be * m, v1, v2, f1, v3, v4 * m, v1, v2, f2, v4 * m, v1, v2, f3 :returns: list of strings and DatumMeta objects. String represent stack commands and DatumMeta's represent stack datums. """ target_form_command = id_strings.form_command(target_form) target_module_id, target_form_id = target_form_command.split('-') module_command = id_strings.menu_id(target_form.get_module()) module_datums = self.get_module_datums(target_module_id) form_datums = module_datums[target_form_id] if module_command == id_strings.ROOT: datums_list = self.root_module_datums else: datums_list = list(module_datums.values() ) # [ [datums for f0], [datums for f1], ...] root_module = target_form.get_module().root_module if root_module and include_target_root: datums_list = datums_list + list( self.get_module_datums( id_strings.menu_id(root_module)).values()) common_datums = commonprefix(datums_list) remaining_datums = form_datums[len(common_datums):] frame_children = [CommandId(module_command) ] if module_command != id_strings.ROOT else [] frame_children.extend(common_datums) if not module_only: frame_children.append(CommandId(target_form_command)) frame_children.extend(remaining_datums) return frame_children
def _get_reg_form_action(self, module): """ Returns registration form action """ form = self.app.get_form(module.case_list_form.form_id) if self.app.enable_localized_menu_media: case_list_form = module.case_list_form action = LocalizedAction( menu_locale_id=id_strings.case_list_form_locale(module), media_image=bool(len(case_list_form.all_image_paths())), media_audio=bool(len(case_list_form.all_audio_paths())), image_locale_id=id_strings.case_list_form_icon_locale(module), audio_locale_id=id_strings.case_list_form_audio_locale(module), stack=Stack(), for_action_menu=True, ) else: action = Action( display=Display( text=Text(locale_id=id_strings.case_list_form_locale(module)), media_image=module.case_list_form.default_media_image, media_audio=module.case_list_form.default_media_audio, ), stack=Stack() ) frame = PushFrame() frame.add_command(XPath.string(id_strings.form_command(form))) target_form_dm = self.entries_helper.get_datums_meta_for_form_generic(form) source_form_dm = [] if len(module.forms): source_form_dm = self.entries_helper.get_datums_meta_for_form_generic(module.get_form(0)) for target_meta in target_form_dm: if target_meta.requires_selection: # This is true for registration forms where the case being created is a subcase try: [source_dm] = [ source_meta for source_meta in source_form_dm if source_meta.case_type == target_meta.case_type ] except ValueError: pass else: frame.add_datum(StackDatum( id=target_meta.datum.id, value=session_var(source_dm.datum.id)) ) else: s_datum = target_meta.datum frame.add_datum(StackDatum(id=s_datum.id, value=s_datum.function)) frame.add_datum(StackDatum(id=RETURN_TO, value=XPath.string(id_strings.menu_id(module)))) action.stack.add_frame(frame) return action
def get_module_contributions(self, module): # avoid circular dependency from corehq.apps.app_manager.models import CareplanModule menus = [] if isinstance(module, CareplanModule): update_menu = Menu( id=id_strings.menu_id(module), locale_id=id_strings.module_locale(module), ) if not module.display_separately: parent = self.app.get_module_by_unique_id( module.parent_select.module_id) create_goal_form = module.get_form_by_type( CAREPLAN_GOAL, 'create') create_menu = Menu( id=id_strings.menu_id(parent), locale_id=id_strings.module_locale(parent), ) create_menu.commands.append( Command(id=id_strings.form_command(create_goal_form))) menus.append(create_menu) update_menu.root = id_strings.menu_id(parent) else: update_menu.commands.extend([ Command(id=id_strings.form_command( module.get_form_by_type(CAREPLAN_GOAL, 'create'))), ]) update_menu.commands.extend([ Command(id=id_strings.form_command( module.get_form_by_type(CAREPLAN_GOAL, 'update'))), Command(id=id_strings.form_command( module.get_form_by_type(CAREPLAN_TASK, 'create'))), Command(id=id_strings.form_command( module.get_form_by_type(CAREPLAN_TASK, 'update'))), ]) menus.append(update_menu) return menus
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 update_suite(self): """ Add stack elements to form entry elements to configure app workflow. This updates the Entry objects in place. """ for module in self.modules: for form in module.get_suite_forms(): frames = self._get_stack_frames(form, module) if frames: entry = self._get_form_entry( id_strings.form_command(form, module)) self._add_frames_to_entry(entry, frames)
def get_module_contributions(self, module): # avoid circular dependency from corehq.apps.app_manager.models import CareplanModule menus = [] if isinstance(module, CareplanModule): update_menu = Menu( id=id_strings.menu_id(module), locale_id=id_strings.module_locale(module), ) if not module.display_separately: parent = self.app.get_module_by_unique_id(module.parent_select.module_id, error=_("Could not find module '{}' is attached to.").format(module.default_name())) create_goal_form = module.get_form_by_type(CAREPLAN_GOAL, 'create') create_menu = Menu( id=id_strings.menu_id(parent), locale_id=id_strings.module_locale(parent), ) create_menu.commands.append(Command(id=id_strings.form_command(create_goal_form))) menus.append(create_menu) update_menu.root = id_strings.menu_id(parent) else: update_menu.commands.extend([ Command(id=id_strings.form_command(module.get_form_by_type(CAREPLAN_GOAL, 'create'))), ]) update_menu.commands.extend([ Command(id=id_strings.form_command(module.get_form_by_type(CAREPLAN_GOAL, 'update'))), Command(id=id_strings.form_command(module.get_form_by_type(CAREPLAN_TASK, 'create'))), Command(id=id_strings.form_command(module.get_form_by_type(CAREPLAN_TASK, 'update'))), ]) menus.append(update_menu) return menus
def update_suite(self): """ Add stack elements to form entry elements to configure app workflow. This updates the Entry objects in place. """ for module in self.modules: for form in module.get_suite_forms(): form_command = id_strings.form_command(form, module) stack_frames = [] end_of_form_frames = EndOfFormNavigationWorkflow(self).form_workflow_frames(module, form) if end_of_form_frames: stack_frames.extend(end_of_form_frames) else: stack_frames.extend(CaseListFormWorkflow(self).case_list_forms_frames(form)) self.create_workflow_stack(form_command, stack_frames)
def get_frame_children(self, target_form, module_only=False): """ For a form return the list of stack frame children that are required to navigate to that form. This is based on the following algorithm: * Add the module the form is in to the stack (we'll call this `m`) * Walk through all forms in the module, determine what datum selections are present in all of the forms (this may be an empty set) * Basically if there are three forms that respectively load * f1: v1, v2, v3, v4 * f2: v1, v2, v4 * f3: v1, v2 * The longest common chain is v1, v2 * Add a datum for each of those values to the stack * Add the form "command id" for the <entry> to the stack * Add the remainder of the datums for the current form to the stack * For the three forms above, the stack entries for "last element" would be * m, v1, v2, f1, v3, v4 * m, v1, v2, f2, v4 * m, v1, v2, f3 :returns: list of strings and DatumMeta objects. String represent stack commands and DatumMeta's represent stack datums. """ target_form_command = id_strings.form_command(target_form) target_module_id, target_form_id = target_form_command.split('-') module_command = id_strings.menu_id(target_form.get_module()) module_datums = self.get_module_datums(target_module_id) form_datums = module_datums[target_form_id] if module_command == id_strings.ROOT: datums_list = self.root_module_datums else: datums_list = module_datums.values() # [ [datums for f0], [datums for f1], ...] common_datums = commonprefix(datums_list) remaining_datums = form_datums[len(common_datums):] frame_children = [CommandId(module_command)] if module_command != id_strings.ROOT else [] frame_children.extend(common_datums) if not module_only: frame_children.append(CommandId(target_form_command)) frame_children.extend(remaining_datums) return frame_children
def frame_children_for_module(module_, include_user_selections=True): frame_children = [] if module_.root_module: frame_children.extend(frame_children_for_module(module_.root_module)) if include_user_selections: this_module_children = self.helper.get_frame_children(id_strings.form_command(module_.get_form(0)), module_, module_only=True) for child in this_module_children: if child not in frame_children: frame_children.append(child) else: module_command = id_strings.menu_id(module_) if module_command != id_strings.ROOT: frame_children.append(CommandId(module_command)) return frame_children
def update_suite(self): """ Add stack elements to form entry elements to configure app workflow. This updates the Entry objects in place. """ for module in self.modules: for form in module.get_suite_forms(): form_command = id_strings.form_command(form, module) if_prefix = None stack_frames = [] case_list_form_frames = CaseListFormWorkflow(self).case_list_forms_frames(form) stack_frames.extend(case_list_form_frames) if case_list_form_frames: if_prefix = session_var(RETURN_TO).count().eq(0) stack_frames.extend(EndOfFormNavigationWorkflow(self).form_workflow_frames(if_prefix, module, form)) self.create_workflow_stack(form_command, stack_frames)
def _add_stack_children_for_target(self, stack_frames, target_module, source_form_datums): """ :param stack_frames: Stack to add children to :param target_module: Module that we're targeting for navigating to :param source_form_datums: List of datums from the source form. """ ids_on_stack = stack_frames.ids_on_stack command = None if len(target_module.forms): command = id_strings.form_command(target_module.get_form(0)) elif target_module.case_list and target_module.case_list.show: command = id_strings.case_list_command(target_module) if command: target_frame_children = self.helper.get_frame_children(command, target_module, module_only=True) remaining_target_frame_children = [fc for fc in target_frame_children if fc.id not in ids_on_stack] frame_children = WorkflowHelper.get_datums_matched_to_source( remaining_target_frame_children, source_form_datums ) stack_frames.add_children(frame_children)
def _get_static_stack_frame(form_workflow, xpath=None): if form_workflow == WORKFLOW_ROOT: return StackFrameMeta(xpath, [], allow_empty_frame=True) elif form_workflow == WORKFLOW_MODULE: frame_children = frame_children_for_module(module, include_user_selections=False) return StackFrameMeta(xpath, frame_children) elif form_workflow == WORKFLOW_PARENT_MODULE: root_module = module.root_module frame_children = frame_children_for_module(root_module) return StackFrameMeta(xpath, frame_children) elif form_workflow == WORKFLOW_PREVIOUS: frame_children = self.helper.get_frame_children(id_strings.form_command(form), module, include_target_root=True) # since we want to go the 'previous' screen we need to drop the last # datum last = frame_children.pop() while isinstance(last, WorkflowDatumMeta) and not last.requires_selection: # keep removing last element until we hit a command # or a non-autoselect datum last = frame_children.pop() return StackFrameMeta(xpath, frame_children)
def configure_entry_careplan_form(self, module, e, form=None, **kwargs): parent_module = self.app.get_module_by_unique_id(module.parent_select.module_id, error=_("Could not find module '{}' is attached to.").format(module.default_name())) e.datums.append(SessionDatum( id='case_id', nodeset=EntriesHelper.get_nodeset_xpath(parent_module.case_type), value="./@case_id", detail_select=self.details_helper.get_detail_id_safe(parent_module, 'case_short'), detail_confirm=self.details_helper.get_detail_id_safe(parent_module, 'case_long') )) def session_datum(datum_id, case_type, parent_ref, parent_val): nodeset = CaseTypeXpath(case_type).case().select( 'index/%s' % parent_ref, session_var(parent_val), quote=False ).select('@status', 'open') return SessionDatum( id=datum_id, nodeset=nodeset, value="./@case_id", detail_select=self.details_helper.get_detail_id_safe(module, '%s_short' % case_type), detail_confirm=self.details_helper.get_detail_id_safe(module, '%s_long' % case_type) ) e.stack = Stack() frame = CreateFrame() e.stack.add_frame(frame) if form.case_type == CAREPLAN_GOAL: if form.mode == 'create': new_goal_id_var = 'case_id_goal_new' e.datums.append(SessionDatum(id=new_goal_id_var, function='uuid()')) elif form.mode == 'update': new_goal_id_var = 'case_id_goal' e.datums.append(session_datum(new_goal_id_var, CAREPLAN_GOAL, 'parent', 'case_id')) if not module.display_separately: open_goal = CaseIDXPath(session_var(new_goal_id_var)).case().select('@status', 'open') frame.if_clause = '{count} = 1'.format(count=open_goal.count()) frame.add_command(XPath.string(id_strings.menu_id(parent_module))) frame.add_datum(StackDatum(id='case_id', value=session_var('case_id'))) frame.add_command(XPath.string(id_strings.menu_id(module))) frame.add_datum(StackDatum(id='case_id_goal', value=session_var(new_goal_id_var))) else: frame.add_command(XPath.string(id_strings.menu_id(module))) frame.add_datum(StackDatum(id='case_id', value=session_var('case_id'))) elif form.case_type == CAREPLAN_TASK: if not module.display_separately: frame.add_command(XPath.string(id_strings.menu_id(parent_module))) frame.add_datum(StackDatum(id='case_id', value=session_var('case_id'))) frame.add_command(XPath.string(id_strings.menu_id(module))) frame.add_datum(StackDatum(id='case_id_goal', value=session_var('case_id_goal'))) if form.mode == 'update': count = CaseTypeXpath(CAREPLAN_TASK).case().select( 'index/goal', session_var('case_id_goal'), quote=False ).select('@status', 'open').count() frame.if_clause = '{count} >= 1'.format(count=count) frame.add_command(XPath.string( id_strings.form_command(module.get_form_by_type(CAREPLAN_TASK, 'update')) )) else: frame.add_command(XPath.string(id_strings.menu_id(module))) frame.add_datum(StackDatum(id='case_id', value=session_var('case_id'))) if form.mode == 'create': e.datums.append(session_datum('case_id_goal', CAREPLAN_GOAL, 'parent', 'case_id')) elif form.mode == 'update': e.datums.append(session_datum('case_id_goal', CAREPLAN_GOAL, 'parent', 'case_id')) e.datums.append(session_datum('case_id_task', CAREPLAN_TASK, 'goal', 'case_id_goal'))
def entry_for_module(self, module): # avoid circular dependency from corehq.apps.app_manager.models import Module, AdvancedModule results = [] for form in module.get_suite_forms(): e = Entry() e.form = form.xmlns # Ideally all of this version check should happen in Command/Display class if self.app.enable_localized_menu_media: e.command = LocalizedCommand( id=id_strings.form_command(form, module), menu_locale_id=id_strings.form_locale(form), media_image=bool(len(form.all_image_paths())), media_audio=bool(len(form.all_audio_paths())), image_locale_id=id_strings.form_icon_locale(form), audio_locale_id=id_strings.form_audio_locale(form), ) else: e.command = Command( id=id_strings.form_command(form, module), locale_id=id_strings.form_locale(form), media_image=form.default_media_image, media_audio=form.default_media_audio, ) config_entry = { 'module_form': self.configure_entry_module_form, 'advanced_form': self.configure_entry_advanced_form, 'careplan_form': self.configure_entry_careplan_form, }[form.form_type] config_entry(module, e, form) if ( self.app.commtrack_enabled and session_var('supply_point_id') in getattr(form, 'source', "") ): from corehq.apps.app_manager.models import AUTO_SELECT_LOCATION datum, assertions = EntriesHelper.get_userdata_autoselect( 'commtrack-supply-point', 'supply_point_id', AUTO_SELECT_LOCATION, ) e.datums.append(datum) e.assertions.extend(assertions) results.append(e) if hasattr(module, 'case_list') and module.case_list.show: e = Entry() if self.app.enable_localized_menu_media: e.command = LocalizedCommand( id=id_strings.case_list_command(module), menu_locale_id=id_strings.case_list_locale(module), media_image=bool(len(module.case_list.all_image_paths())), media_audio=bool(len(module.case_list.all_audio_paths())), image_locale_id=id_strings.case_list_icon_locale(module), audio_locale_id=id_strings.case_list_audio_locale(module), ) else: e.command = Command( id=id_strings.case_list_command(module), locale_id=id_strings.case_list_locale(module), media_image=module.case_list.default_media_image, media_audio=module.case_list.default_media_audio, ) if isinstance(module, Module): for datum_meta in self.get_case_datums_basic_module(module): e.datums.append(datum_meta.datum) elif isinstance(module, AdvancedModule): e.datums.append(SessionDatum( id='case_id_case_%s' % module.case_type, nodeset=(EntriesHelper.get_nodeset_xpath(module.case_type)), value="./@case_id", detail_select=self.details_helper.get_detail_id_safe(module, 'case_short'), detail_confirm=self.details_helper.get_detail_id_safe(module, 'case_long'), detail_persistent=self.get_detail_persistent_attr(module, module, "case_short"), detail_inline=self.get_detail_inline_attr(module, module, "case_short"), autoselect=module.auto_select_case, )) if self.app.commtrack_enabled: e.datums.append(SessionDatum( id='product_id', nodeset=ProductInstanceXpath().instance(), value="./@id", detail_select=self.details_helper.get_detail_id_safe(module, 'product_short') )) results.append(e) for entry in module.get_custom_entries(): results.append(entry) return results
def _get_first_command(self, module): if module.module_type == "shadow": module = module.source_module return id_strings.form_command(module.get_form(0))
def entry_for_module(self, module): # avoid circular dependency from corehq.apps.app_manager.models import Module, AdvancedModule results = [] for form in module.get_suite_forms(): e = Entry() e.form = form.xmlns # Ideally all of this version check should happen in Command/Display class if self.app.enable_localized_menu_media: e.command = LocalizedCommand( id=id_strings.form_command(form, module), menu_locale_id=id_strings.form_locale(form), media_image=bool(len(form.all_image_paths())), media_audio=bool(len(form.all_audio_paths())), image_locale_id=id_strings.form_icon_locale(form), audio_locale_id=id_strings.form_audio_locale(form), ) else: e.command = Command( id=id_strings.form_command(form, module), locale_id=id_strings.form_locale(form), media_image=form.default_media_image, media_audio=form.default_media_audio, ) config_entry = { "module_form": self.configure_entry_module_form, "advanced_form": self.configure_entry_advanced_form, "careplan_form": self.configure_entry_careplan_form, }[form.form_type] config_entry(module, e, form) if self.app.commtrack_enabled and session_var("supply_point_id") in getattr(form, "source", ""): from corehq.apps.app_manager.models import AUTO_SELECT_LOCATION datum, assertions = EntriesHelper.get_userdata_autoselect( "commtrack-supply-point", "supply_point_id", AUTO_SELECT_LOCATION ) e.datums.append(datum) e.assertions.extend(assertions) results.append(e) if hasattr(module, "case_list") and module.case_list.show: e = Entry() if self.app.enable_localized_menu_media: e.command = LocalizedCommand( id=id_strings.case_list_command(module), menu_locale_id=id_strings.case_list_locale(module), media_image=bool(len(module.case_list.all_image_paths())), media_audio=bool(len(module.case_list.all_audio_paths())), image_locale_id=id_strings.case_list_icon_locale(module), audio_locale_id=id_strings.case_list_audio_locale(module), ) else: e.command = Command( id=id_strings.case_list_command(module), locale_id=id_strings.case_list_locale(module), media_image=module.case_list.default_media_image, media_audio=module.case_list.default_media_audio, ) if isinstance(module, Module): for datum_meta in self.get_datum_meta_module(module, use_filter=False): e.datums.append(datum_meta.datum) elif isinstance(module, AdvancedModule): detail_persistent, detail_inline = self.get_case_tile_datum_attrs(module, module) e.datums.append( SessionDatum( id="case_id_case_%s" % module.case_type, nodeset=(EntriesHelper.get_nodeset_xpath(module.case_type)), value="./@case_id", detail_select=self.details_helper.get_detail_id_safe(module, "case_short"), detail_confirm=self.details_helper.get_detail_id_safe(module, "case_long"), detail_persistent=detail_persistent, detail_inline=detail_inline, ) ) if self.app.commtrack_enabled: e.datums.append( SessionDatum( id="product_id", nodeset=ProductInstanceXpath().instance(), value="./@id", detail_select=self.details_helper.get_detail_id_safe(module, "product_short"), ) ) results.append(e) for entry in module.get_custom_entries(): results.append(entry) return results
def configure_entry_careplan_form(self, module, e, form=None, **kwargs): parent_module = self.app.get_module_by_unique_id(module.parent_select.module_id) e.datums.append( SessionDatum( id="case_id", nodeset=EntriesHelper.get_nodeset_xpath(parent_module.case_type), value="./@case_id", detail_select=self.details_helper.get_detail_id_safe(parent_module, "case_short"), detail_confirm=self.details_helper.get_detail_id_safe(parent_module, "case_long"), ) ) def session_datum(datum_id, case_type, parent_ref, parent_val): nodeset = ( CaseTypeXpath(case_type) .case() .select("index/%s" % parent_ref, session_var(parent_val), quote=False) .select("@status", "open") ) return SessionDatum( id=datum_id, nodeset=nodeset, value="./@case_id", detail_select=self.details_helper.get_detail_id_safe(module, "%s_short" % case_type), detail_confirm=self.details_helper.get_detail_id_safe(module, "%s_long" % case_type), ) e.stack = Stack() frame = CreateFrame() e.stack.add_frame(frame) if form.case_type == CAREPLAN_GOAL: if form.mode == "create": new_goal_id_var = "case_id_goal_new" e.datums.append(SessionDatum(id=new_goal_id_var, function="uuid()")) elif form.mode == "update": new_goal_id_var = "case_id_goal" e.datums.append(session_datum(new_goal_id_var, CAREPLAN_GOAL, "parent", "case_id")) if not module.display_separately: open_goal = CaseIDXPath(session_var(new_goal_id_var)).case().select("@status", "open") frame.if_clause = "{count} = 1".format(count=open_goal.count()) frame.add_command(XPath.string(id_strings.menu_id(parent_module))) frame.add_datum(StackDatum(id="case_id", value=session_var("case_id"))) frame.add_command(XPath.string(id_strings.menu_id(module))) frame.add_datum(StackDatum(id="case_id_goal", value=session_var(new_goal_id_var))) else: frame.add_command(XPath.string(id_strings.menu_id(module))) frame.add_datum(StackDatum(id="case_id", value=session_var("case_id"))) elif form.case_type == CAREPLAN_TASK: if not module.display_separately: frame.add_command(XPath.string(id_strings.menu_id(parent_module))) frame.add_datum(StackDatum(id="case_id", value=session_var("case_id"))) frame.add_command(XPath.string(id_strings.menu_id(module))) frame.add_datum(StackDatum(id="case_id_goal", value=session_var("case_id_goal"))) if form.mode == "update": count = ( CaseTypeXpath(CAREPLAN_TASK) .case() .select("index/goal", session_var("case_id_goal"), quote=False) .select("@status", "open") .count() ) frame.if_clause = "{count} >= 1".format(count=count) frame.add_command( XPath.string(id_strings.form_command(module.get_form_by_type(CAREPLAN_TASK, "update"))) ) else: frame.add_command(XPath.string(id_strings.menu_id(module))) frame.add_datum(StackDatum(id="case_id", value=session_var("case_id"))) if form.mode == "create": e.datums.append(session_datum("case_id_goal", CAREPLAN_GOAL, "parent", "case_id")) elif form.mode == "update": e.datums.append(session_datum("case_id_goal", CAREPLAN_GOAL, "parent", "case_id")) e.datums.append(session_datum("case_id_task", CAREPLAN_TASK, "goal", "case_id_goal"))
def get_form_datums(self, form): """ :return: List of DatumMeta objects for this form """ module_id, form_id = id_strings.form_command(form).split('-') return self.get_module_datums(module_id)[form_id]
def entry_for_module(self, module): # avoid circular dependency from corehq.apps.app_manager.models import Module, AdvancedModule results = [] for form in module.get_suite_forms(): e = Entry() e.form = form.xmlns # Ideally all of this version check should happen in Command/Display class if self.app.enable_localized_menu_media: form_custom_icon = form.custom_icon e.command = LocalizedCommand( id=id_strings.form_command(form, module), menu_locale_id=id_strings.form_locale(form), media_image=bool(len(form.all_image_paths())), media_audio=bool(len(form.all_audio_paths())), image_locale_id=id_strings.form_icon_locale(form), audio_locale_id=id_strings.form_audio_locale(form), custom_icon_locale_id=(id_strings.form_custom_icon_locale(form, form_custom_icon.form) if form_custom_icon and not form_custom_icon.xpath else None), custom_icon_form=(form_custom_icon.form if form_custom_icon else None), custom_icon_xpath=(form_custom_icon.xpath if form_custom_icon and form_custom_icon.xpath else None), ) else: e.command = Command( id=id_strings.form_command(form, module), locale_id=id_strings.form_locale(form), media_image=form.default_media_image, media_audio=form.default_media_audio, ) config_entry = { 'module_form': self.configure_entry_module_form, 'advanced_form': self.configure_entry_advanced_form, 'shadow_form': self.configure_entry_advanced_form, }[form.form_type] config_entry(module, e, form) if form.uses_usercase(): EntriesHelper.add_usercase_id_assertion(e) EntriesHelper.add_custom_assertions(e, form) if ( self.app.commtrack_enabled and session_var('supply_point_id') in getattr(form, 'source', "") ): from corehq.apps.app_manager.models import AUTO_SELECT_LOCATION datum, assertions = EntriesHelper.get_userdata_autoselect( 'commtrack-supply-point', 'supply_point_id', AUTO_SELECT_LOCATION, ) e.datums.append(datum) e.assertions.extend(assertions) results.append(e) if hasattr(module, 'case_list') and module.case_list.show: e = Entry() if self.app.enable_localized_menu_media: e.command = LocalizedCommand( id=id_strings.case_list_command(module), menu_locale_id=id_strings.case_list_locale(module), media_image=bool(len(module.case_list.all_image_paths())), media_audio=bool(len(module.case_list.all_audio_paths())), image_locale_id=id_strings.case_list_icon_locale(module), audio_locale_id=id_strings.case_list_audio_locale(module), ) else: e.command = Command( id=id_strings.case_list_command(module), locale_id=id_strings.case_list_locale(module), media_image=module.case_list.default_media_image, media_audio=module.case_list.default_media_audio, ) if isinstance(module, Module): for datum_meta in self.get_case_datums_basic_module(module): e.datums.append(datum_meta.datum) elif isinstance(module, AdvancedModule): detail_inline = self.get_detail_inline_attr(module, module, "case_short") detail_confirm = None if not detail_inline: detail_confirm = self.details_helper.get_detail_id_safe(module, 'case_long') e.datums.append(SessionDatum( id='case_id_case_%s' % module.case_type, nodeset=(EntriesHelper.get_nodeset_xpath(module.case_type)), value="./@case_id", detail_select=self.details_helper.get_detail_id_safe(module, 'case_short'), detail_confirm=detail_confirm, detail_persistent=self.get_detail_persistent_attr(module, module, "case_short"), detail_inline=detail_inline, autoselect=module.auto_select_case, )) if self.app.commtrack_enabled: e.datums.append(SessionDatum( id='product_id', nodeset=ProductInstanceXpath().instance(), value="./@id", detail_select=self.details_helper.get_detail_id_safe(module, 'product_short') )) results.append(e) for entry in module.get_custom_entries(): results.append(entry) return results
def get_frame_children(self, module, form=None, include_root_module=False): """ For a form or module return the list of stack frame children that are required to navigate there; a mix of commands and datum declarations. This is based on the following algorithm: * Add the module (or form's module) to the stack (we'll call this `m`) * Walk through all forms in the module, determine what datum selections are present in all of the forms (this may be an empty set) * Basically if there are three forms that respectively load * f1: v1, v2, v3, v4 * f2: v1, v2, v4 * f3: v1, v2 * The longest common chain is v1, v2 * Add a datum for each of those values to the stack For forms: * Add the form "command id" for the <entry> to the stack * Add the remainder of the datums for the current form to the stack * For the three forms above, the stack entries for "last element" would be * m, v1, v2, f1, v3, v4 * m, v1, v2, f2, v4 * m, v1, v2, f3 :returns: list of strings and DatumMeta objects. String represent stack commands and DatumMeta's represent stack datums. """ module_command = id_strings.menu_id(module) if form is None and module.module_type == "shadow": module_datums = self.get_module_datums( f'm{module.source_module.id}') else: module_datums = self.get_module_datums(f'm{module.id}') frame_children = [] if module_command == id_strings.ROOT: datums_list = self._root_module_datums else: datums_list = list(module_datums.values() ) # [ [datums for f0], [datums for f1], ...] root_module = module.root_module if root_module and include_root_module: datums_list.extend( self.get_module_datums( id_strings.menu_id(root_module)).values()) root_module_command = id_strings.menu_id(root_module) if root_module_command != id_strings.ROOT: frame_children.append(CommandId(root_module_command)) frame_children.append(CommandId(module_command)) common_datums = commonprefix(datums_list) frame_children.extend(common_datums) if form: frame_children.append( CommandId(id_strings.form_command(form, module))) form_datums = module_datums[f'f{form.id}'] remaining_datums = form_datums[len(common_datums):] frame_children.extend(remaining_datums) return frame_children
def _get_form_endpoint(self, form, module): id_string = id_strings.form_command(form) return self._make_session_endpoint(id_string, module, form.session_endpoint_id)
def configure_entry_careplan_form(self, module, e, form=None, **kwargs): parent_module = self.app.get_module_by_unique_id(module.parent_select.module_id) e.datums.append(SessionDatum( id='case_id', nodeset=EntriesHelper.get_nodeset_xpath(parent_module.case_type), value="./@case_id", detail_select=self.details_helper.get_detail_id_safe(parent_module, 'case_short'), detail_confirm=self.details_helper.get_detail_id_safe(parent_module, 'case_long') )) def session_datum(datum_id, case_type, parent_ref, parent_val): nodeset = CaseTypeXpath(case_type).case().select( 'index/%s' % parent_ref, session_var(parent_val), quote=False ).select('@status', 'open') return SessionDatum( id=datum_id, nodeset=nodeset, value="./@case_id", detail_select=self.details_helper.get_detail_id_safe(module, '%s_short' % case_type), detail_confirm=self.details_helper.get_detail_id_safe(module, '%s_long' % case_type) ) e.stack = Stack() frame = CreateFrame() e.stack.add_frame(frame) if form.case_type == CAREPLAN_GOAL: if form.mode == 'create': new_goal_id_var = 'case_id_goal_new' e.datums.append(SessionDatum(id=new_goal_id_var, function='uuid()')) elif form.mode == 'update': new_goal_id_var = 'case_id_goal' e.datums.append(session_datum(new_goal_id_var, CAREPLAN_GOAL, 'parent', 'case_id')) if not module.display_separately: open_goal = CaseIDXPath(session_var(new_goal_id_var)).case().select('@status', 'open') frame.if_clause = '{count} = 1'.format(count=open_goal.count()) frame.add_command(XPath.string(id_strings.menu_id(parent_module))) frame.add_datum(StackDatum(id='case_id', value=session_var('case_id'))) frame.add_command(XPath.string(id_strings.menu_id(module))) frame.add_datum(StackDatum(id='case_id_goal', value=session_var(new_goal_id_var))) else: frame.add_command(XPath.string(id_strings.menu_id(module))) frame.add_datum(StackDatum(id='case_id', value=session_var('case_id'))) elif form.case_type == CAREPLAN_TASK: if not module.display_separately: frame.add_command(XPath.string(id_strings.menu_id(parent_module))) frame.add_datum(StackDatum(id='case_id', value=session_var('case_id'))) frame.add_command(XPath.string(id_strings.menu_id(module))) frame.add_datum(StackDatum(id='case_id_goal', value=session_var('case_id_goal'))) if form.mode == 'update': count = CaseTypeXpath(CAREPLAN_TASK).case().select( 'index/goal', session_var('case_id_goal'), quote=False ).select('@status', 'open').count() frame.if_clause = '{count} >= 1'.format(count=count) frame.add_command(XPath.string( id_strings.form_command(module.get_form_by_type(CAREPLAN_TASK, 'update')) )) else: frame.add_command(XPath.string(id_strings.menu_id(module))) frame.add_datum(StackDatum(id='case_id', value=session_var('case_id'))) if form.mode == 'create': e.datums.append(session_datum('case_id_goal', CAREPLAN_GOAL, 'parent', 'case_id')) elif form.mode == 'update': e.datums.append(session_datum('case_id_goal', CAREPLAN_GOAL, 'parent', 'case_id')) e.datums.append(session_datum('case_id_task', CAREPLAN_TASK, 'goal', 'case_id_goal'))
def form_workflow_frames(self, module, form): """ post_form_workflow = 'module': * Add stack frame and a command with value = "module command" post_form_workflow = 'previous_screen': * Add stack frame and a command with value = "module command" * Find longest list of common datums between form entries for the module and add datums to the stack frame for each. * Add a command to the frame with value = "form command" * Add datums to the frame for any remaining datums for that form. * Remove any autoselect items from the end of the stack frame. * Finally remove the last item from the stack frame. """ from corehq.apps.app_manager.const import ( WORKFLOW_PREVIOUS, WORKFLOW_MODULE, WORKFLOW_ROOT, WORKFLOW_FORM, WORKFLOW_PARENT_MODULE ) def frame_children_for_module(module_, include_user_selections=True): frame_children = [] if module_.root_module: frame_children.extend(frame_children_for_module(module_.root_module)) if include_user_selections: this_module_children = self.helper.get_frame_children(id_strings.form_command(module_.get_form(0)), module_, module_only=True) for child in this_module_children: if child not in frame_children: frame_children.append(child) else: module_command = id_strings.menu_id(module_) if module_command != id_strings.ROOT: frame_children.append(CommandId(module_command)) return frame_children def _get_static_stack_frame(form_workflow, xpath=None): if form_workflow == WORKFLOW_ROOT: return StackFrameMeta(xpath, [], allow_empty_frame=True) elif form_workflow == WORKFLOW_MODULE: frame_children = frame_children_for_module(module, include_user_selections=False) return StackFrameMeta(xpath, frame_children) elif form_workflow == WORKFLOW_PARENT_MODULE: root_module = module.root_module frame_children = frame_children_for_module(root_module) return StackFrameMeta(xpath, frame_children) elif form_workflow == WORKFLOW_PREVIOUS: frame_children = self.helper.get_frame_children(id_strings.form_command(form), module, include_target_root=True) # since we want to go the 'previous' screen we need to drop the last # datum last = frame_children.pop() while isinstance(last, WorkflowDatumMeta) and not last.requires_selection: # keep removing last element until we hit a command # or a non-autoselect datum last = frame_children.pop() return StackFrameMeta(xpath, frame_children) stack_frames = [] if form.post_form_workflow == WORKFLOW_FORM: source_form_datums = self.helper.get_form_datums(form) for link in form.form_links: target_form = self.helper.app.get_form(link.form_id) target_module = target_form.get_module() target_frame_children = self.helper.get_frame_children(id_strings.form_command(target_form), target_module) if link.datums: frame_children = EndOfFormNavigationWorkflow.get_datums_matched_to_manual_values( target_frame_children, link.datums, form ) else: frame_children = WorkflowHelper.get_datums_matched_to_source( target_frame_children, source_form_datums ) if target_module in module.get_child_modules(): parent_frame_children = self.helper.get_frame_children( id_strings.form_command(module.get_form(0)), module, module_only=True) # exclude frame children from the child module if they are already # supplied by the parent module parent_ids = {parent.id for parent in parent_frame_children} frame_children = parent_frame_children + [ child for child in frame_children if child.id not in parent_ids ] stack_frames.append(StackFrameMeta(link.xpath, frame_children, current_session=source_form_datums)) if form.post_form_workflow_fallback: # for the fallback negative all if conditions/xpath expressions and use that as the xpath for this link_xpaths = [link.xpath for link in form.form_links] # remove any empty string link_xpaths = [x for x in link_xpaths if x.strip()] if link_xpaths: negate_of_all_link_paths = ( ' and '.join( ['not(' + link_xpath + ')' for link_xpath in link_xpaths] ) ) static_stack_frame_for_fallback = _get_static_stack_frame( form.post_form_workflow_fallback, negate_of_all_link_paths ) if static_stack_frame_for_fallback: stack_frames.append(static_stack_frame_for_fallback) else: static_stack_frame = _get_static_stack_frame(form.post_form_workflow) if static_stack_frame: stack_frames.append(static_stack_frame) return stack_frames
def form_workflow_frames(self, module, form): """ post_form_workflow = 'module': * Add stack frame and a command with value = "module command" post_form_workflow = 'previous_screen': * Add stack frame and a command with value = "module command" * Find longest list of common datums between form entries for the module and add datums to the stack frame for each. * Add a command to the frame with value = "form command" * Add datums to the frame for any remaining datums for that form. * Remove any autoselect items from the end of the stack frame. * Finally remove the last item from the stack frame. """ from corehq.apps.app_manager.const import (WORKFLOW_PREVIOUS, WORKFLOW_MODULE, WORKFLOW_ROOT, WORKFLOW_FORM, WORKFLOW_PARENT_MODULE) def frame_children_for_module(module_, include_user_selections=True): frame_children = [] if module_.root_module: frame_children.extend( frame_children_for_module(module_.root_module)) if include_user_selections: this_module_children = self.helper.get_frame_children( id_strings.form_command(module_.get_form(0)), module_, module_only=True) for child in this_module_children: if child not in frame_children: frame_children.append(child) else: module_command = id_strings.menu_id(module_) if module_command != id_strings.ROOT: frame_children.append(CommandId(module_command)) return frame_children def _get_static_stack_frame(form_workflow, xpath=None): if form_workflow == WORKFLOW_ROOT: return StackFrameMeta(xpath, [], allow_empty_frame=True) elif form_workflow == WORKFLOW_MODULE: frame_children = frame_children_for_module( module, include_user_selections=False) return StackFrameMeta(xpath, frame_children) elif form_workflow == WORKFLOW_PARENT_MODULE: root_module = module.root_module frame_children = frame_children_for_module(root_module) return StackFrameMeta(xpath, frame_children) elif form_workflow == WORKFLOW_PREVIOUS: frame_children = self.helper.get_frame_children( id_strings.form_command(form), module, include_target_root=True) # since we want to go the 'previous' screen we need to drop the last # datum last = frame_children.pop() while isinstance( last, WorkflowDatumMeta) and not last.requires_selection: # keep removing last element until we hit a command # or a non-autoselect datum last = frame_children.pop() return StackFrameMeta(xpath, frame_children) stack_frames = [] if form.post_form_workflow == WORKFLOW_FORM: source_form_datums = self.helper.get_form_datums(form) for link in form.form_links: target_form = self.helper.app.get_form(link.form_id) target_module = target_form.get_module() target_frame_children = self.helper.get_frame_children( id_strings.form_command(target_form), target_module) if link.datums: frame_children = EndOfFormNavigationWorkflow.get_datums_matched_to_manual_values( target_frame_children, link.datums, form) else: frame_children = WorkflowHelper.get_datums_matched_to_source( target_frame_children, source_form_datums) if target_module in module.get_child_modules(): parent_frame_children = self.helper.get_frame_children( id_strings.form_command(module.get_form(0)), module, module_only=True) # exclude frame children from the child module if they are already # supplied by the parent module parent_ids = { parent.id for parent in parent_frame_children } frame_children = parent_frame_children + [ child for child in frame_children if child.id not in parent_ids ] stack_frames.append( StackFrameMeta(link.xpath, frame_children, current_session=source_form_datums)) if form.post_form_workflow_fallback: # for the fallback negative all if conditions/xpath expressions and use that as the xpath for this link_xpaths = [link.xpath for link in form.form_links] # remove any empty string link_xpaths = [x for x in link_xpaths if x.strip()] if link_xpaths: negate_of_all_link_paths = (' and '.join([ 'not(' + link_xpath + ')' for link_xpath in link_xpaths ])) static_stack_frame_for_fallback = _get_static_stack_frame( form.post_form_workflow_fallback, negate_of_all_link_paths) if static_stack_frame_for_fallback: stack_frames.append(static_stack_frame_for_fallback) else: static_stack_frame = _get_static_stack_frame( form.post_form_workflow) if static_stack_frame: stack_frames.append(static_stack_frame) return stack_frames