<commit_before>from __future__ import absolute_import
from __future__ import unicode_literals
from collections import defaultdict, namedtuple
from functools import total_ordering
from os.path import commonprefix
import re
from xml.sax.saxutils import unescape

from corehq.apps.app_manager import id_strings
from corehq.apps.app_manager.const import (
    RETURN_TO, )
from corehq.apps.app_manager.exceptions import SuiteValidationError
from corehq.apps.app_manager.suite_xml.contributors import PostProcessor
from corehq.apps.app_manager.suite_xml.xml_models import StackDatum, Stack, CreateFrame
from corehq.apps.app_manager.xpath import CaseIDXPath, session_var, \
    XPath
from memoized import memoized
from six.moves import filter


class WorkflowHelper(PostProcessor):

    def __init__(self, suite, app, modules):
        super(WorkflowHelper, self).__init__(suite, app, modules)

    @property
    @memoized
    def root_module_datums(self):
        root_modules = [module for module in self.modules if module.put_in_root]
        return [
            datum for module in root_modules
            for datum in self.get_module_datums('m{}'.format(module.id)).values()
        ]

    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, command, target_module, 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.

        Given command may be a form (mX-fY) or case list menu item (mX-case-list).

        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.
        """
        match = re.search(r'^(m\d+)-(.*)', command)
        if not match:
            raise Exception("Unrecognized command: {}".format(command))
        target_module_id = match.group(1)
        target_form_id = match.group(2)

        frame_children = []

        def _add_new_datums(datums):
            datum_frames = [f for f in frame_children if isinstance(f, WorkflowDatumMeta)]
            frame_children.extend([d for d in datums if d not in datum_frames])

        # Determine datums needed for target form
        module_datums = self.get_module_datums(target_module_id)
        form_datums = module_datums[target_form_id]

        target_module_command = None
        root_module_command = None
        root_datums = [[]]

        module_command = id_strings.menu_id(target_module)
        if module_command == id_strings.ROOT:
            target_module_datums = self.root_module_datums
        else:
            target_module_command = CommandId(module_command)
            target_module_datums = list(module_datums.values())  # [ [datums for f0], [datums for f1], ...]
            root_module = target_module.root_module
            if root_module and include_target_root:
                # Module has a parent. Store the parent's needed datums and command.
                root_datums = list(self.get_module_datums(id_strings.menu_id(root_module)).values())
                root_module_command = id_strings.menu_id(target_module.root_module)
                if root_module_command != id_strings.ROOT:
                    root_module_command = CommandId(root_module_command)

        # First add menu commands
        if root_module_command:
            frame_children.append(root_module_command)
        if target_module_command:
            frame_children.append(target_module_command)

        # Then add datums needed by parent menu and target menu
        _add_new_datums(commonprefix(root_datums))<commit_msg>Fixed existing test.<commit_after>from __future__ import absolute_import
from __future__ import unicode_literals
from collections import defaultdict, namedtuple
from functools import total_ordering
from os.path import commonprefix
import re
from xml.sax.saxutils import unescape

from corehq.apps.app_manager import id_strings
from corehq.apps.app_manager.const import (
    RETURN_TO, )
from corehq.apps.app_manager.exceptions import SuiteValidationError
from corehq.apps.app_manager.suite_xml.contributors import PostProcessor
from corehq.apps.app_manager.suite_xml.xml_models import StackDatum, Stack, CreateFrame
from corehq.apps.app_manager.xpath import CaseIDXPath, session_var, \
    XPath
from memoized import memoized
from six.moves import filter


class WorkflowHelper(PostProcessor):

    def __init__(self, suite, app, modules):
        super(WorkflowHelper, self).__init__(suite, app, modules)

    @property
    @memoized
    def root_module_datums(self):
        root_modules = [module for module in self.modules if module.put_in_root]
        return [
            datum for module in root_modules
            for datum in self.get_module_datums('m{}'.format(module.id)).values()
        ]

    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, command, target_module, 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.

        Given command may be a form (mX-fY) or case list menu item (mX-case-list).

        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.
        """
        match = re.search(r'^(m\d+)-(.*)', command)
        if not match:
            raise Exception("Unrecognized command: {}".format(command))
        target_module_id = match.group(1)
        target_form_id = match.group(2)

        frame_children = []

        def _add_new_datums(datums):
            datum_frames = [f for f in frame_children if isinstance(f, WorkflowDatumMeta)]
            frame_children.extend([d for d in datums if d not in datum_frames])

        # Determine datums needed for target form
        module_datums = self.get_module_datums(target_module_id)
        form_datums = module_datums[target_form_id]

        target_module_command = None
        root_module_command = None
        root_datums = [[]]

        module_command = id_strings.menu_id(target_module)
        if module_command == id_strings.ROOT:
            target_module_datums = self.root_module_datums
        else:
            target_module_command = CommandId(module_command)
            target_module_datums = list(module_datums.values())  # [ [datums for f0], [datums for f1], ...]
            root_module = target_module.root_module
            if root_module and include_target_root:
                # Module has a parent. Store the parent's needed datums and command.
                root_datums = list(self.get_module_datums(id_strings.menu_id(root_module)).values())
                root_module_id = id_strings.menu_id(target_module.root_module)
                if root_module_id != id_strings.ROOT:
                    root_module_command = CommandId(root_module_id)

        # First add menu commands
        if root_module_command:
            frame_children.append(root_module_command)
        if target_module_command:
            frame_children.append(target_module_command)

        # Then add datums needed by parent menu and target menu
        _add_new_datums(commonprefix(root_datums))
{'fraction_non_alphanumeric': 0.06791546589817483, 'fraction_numerical': 0.005763688760806916, 'mean_word_length': 2.570301783264746, 'pattern_counts': {'":': 0, '<': 5, '<?xml version=': 0, '>': 5, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 8}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 2, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '2978815', 'n_tokens_mistral': 2985, 'n_tokens_neox': 2894, 'n_words': 887}