<commit_before>module CurationConcerns
  module Workflow
    # Welcome intrepid developer. You have stumbled into some complex data
    # interactions. There are a lot of data collaborators regarding these tests.
    # I would love this to be more in isolation, but that is not in the cards as
    # there are at least 16 database tables interacting to ultimately answer the
    # following question:
    #
    # * What actions can a given user take on an entity?
    #
    # Could there be more efficient queries? Yes. However, the composition of
    # queries has proven to be a very powerful means of understanding and
    # exploring the problem.
    #
    # @note There is an indication of public or private api. The intent of this
    #   is to differentiate what are methods that are the primary entry points
    #   as understood as of the commit that has the @api tag. However, these are
    #   public methods because they have been tested in isolation and are used
    #   to help compose the `@api public` methods.
    module PermissionQuery
      module_function

      # @api public
      #
      # For the given :user and :entity return only workflow actions that meet all of the following:
      #
      # * available for the :entity's workflow state
      # * permitted to be taken by one or more roles in which the user is assigned
      #   either at the workflow level or the entity level.
      #
      # * Actions to which the user Only actions permitted to the user
      #
      # @param user [User]
      # @param entity [#to_sipity_entity] an object that can be converted into a Sipity::Entity
      # @return [ActiveRecord::Relation<Sipity::WorkflowAction>]
      def scope_permitted_workflow_actions_available_for_current_state(user:, entity:)
        workflow_actions_scope = scope_workflow_actions_available_for_current_state(entity: entity)
        workflow_state_actions_scope = scope_permitted_entity_workflow_state_actions(user: user, entity: entity)
        workflow_actions_scope.where(
          workflow_actions_scope.arel_table[:id].in(
            workflow_state_actions_scope.arel_table.project(
              workflow_state_actions_scope.arel_table[:workflow_action_id]
            ).where(workflow_state_actions_scope.constraints.reduce)
          )
        )
      end

      # @api public
      #
      # Agents associated with the given :entity and how they are associated
      # with the given.
      #
      # @param entity [Object] that can be converted into a Sipity::Entity
      # @param role [Object] that can be converted into a Sipity::Role
      # @return [ActiveRecord::Relation<Sipity::Agent>] augmented with
      def scope_agents_associated_with_entity_and_role(entity:, role:)
        entity = PowerConverter.convert_to_sipity_entity(entity)
        role = PowerConverter.convert_to_sipity_role(role)
        workflow_roles = Sipity::WorkflowRole.arel_table
        workflow_responsibilities = Sipity::WorkflowResponsibility.arel_table
        entity_responsibilities = Sipity::EntitySpecificResponsibility.arel_table

        agents = Sipity::Agent.arel_table

        agents_select_manager = agents.project(
          :*,
          Arel.sql("'#{Sipity::Agent::ENTITY_LEVEL_AGENT_RELATIONSHIP}'").as('agent_processing_relationship')
        ).where(
          agents[:id].in(
            entity_responsibilities.project(entity_responsibilities[:agent_id]).join(workflow_roles).on(
              workflow_roles[:id].eq(entity_responsibilities[:workflow_role_id])
            ).where(
              entity_responsibilities[:entity_id].eq(entity.id).and(
                workflow_roles[:role_id].eq(role.id)
              )
            )
          )
        ).union(
          agents.project(
            :*,
            Arel.sql("'#{Sipity::Agent::WORKFLOW_LEVEL_AGENT_RELATIONSHIP}'").as('agent_processing_relationship')
          ).where(
            agents[:id].in(
              workflow_responsibilities.project(workflow_responsibilities[:agent_id]).join(workflow_roles).on(
                workflow_roles[:id].eq(workflow_responsibilities[:workflow_role_id])
              ).where(
                workflow_roles[:workflow_id].eq(entity.workflow_id).and(
                  workflow_roles[:role_id].eq(role.id)
                )
              )
            )
          )
        )
        # I would love to use the following:
        #  `Agent.find_by_sql(agents_select_manager.to_sql)`
        #
        # However AREL is adding an opening and closing parenthesis to the query
        # statement. So I needed to massage that output, as follows:
        #
        # ```ruby
        #  Agent.find_by_sql(
        #    agents_select_manager.to_sql.sub(/\A\s*\(\s*(.*)\s*\)\s*\Z/,'\1')
        #  )
        # ```
        #
        # Instead I'm taking an example from:
        # https://github.com/danshultz/mastering_active_record_sample_code/blob/a656c60ca7a2e27b5cd1aadbdf3bdc1814c37000/app/models/beer.rb#L77-L81
        #
        # Note, I'm making a dynamic query with a result the same as the table
        # name of the model that I'm using.
        Sipity::Agent.from(agents.create_table_alias(agents_select_manager, agents.table_name)).all
      end

      # @api public
      #
      # Roles associated with the given :entity
      # @param entity [Object] that can be converted into a Sipity::Entity
      # @return [ActiveRecord::Relation<Sipity::Role>]
      def scope_roles_associated_with_the_given_entity(entity:)
        entity = PowerConverter.convert_to_sipity_entity(entity)
        workflow_roles = Sipity::WorkflowRole.arel_table
        Sipity::Role.where(
          Sipity::Role.arel_table[:id].in(
            workflow_roles.project(workflow_roles[:role_id]).where(
              workflow_roles[:workflow_id].eq(entity.workflow_id)
            )
          )
        )
      end

      # @api public
      #
      # Is the user authorized to take the processing action on the given
      # entity?
      #
      # @param user [User]
      # @param entity an object that can be converted into a Sipity::Entity
      # @param action an object that can be converted into a Sipity::WorkflowAction#name
      # @return [Boolean]
      def authorized_for_processing?(user:, entity:, action:)
        action_name = PowerConverter.convert_to_sipity_action_name(action)
        scope_permitted_workflow_actions_available_for_current_state(user: user, entity: entity)
          .where(Sipity::WorkflowAction.arel_table[:name].eq(action_name)).count > 0
      end

      # @api public
      #
      # An ActiveRecord::Relation scope that meets the following criteria:
      #
      # * All of the Processing Agents directly associated with the given :user
      #
      # @param user [User]
      # @return [ActiveRecord::Relation<Sipity::Agent>]
      def scope_processing_agents_for(user:)
        return Sipity::Agent.where('1 = 0') unless user.present?
        return Sipity::Agent.where('1 = 0') unless user.persisted?
        agent = PowerConverter.convert_to_sipity_agent(user)<commit_msg>Replacing confusing query with more explicit none<commit_after>module CurationConcerns
  module Workflow
    # Welcome intrepid developer. You have stumbled into some complex data
    # interactions. There are a lot of data collaborators regarding these tests.
    # I would love this to be more in isolation, but that is not in the cards as
    # there are at least 16 database tables interacting to ultimately answer the
    # following question:
    #
    # * What actions can a given user take on an entity?
    #
    # Could there be more efficient queries? Yes. However, the composition of
    # queries has proven to be a very powerful means of understanding and
    # exploring the problem.
    #
    # @note There is an indication of public or private api. The intent of this
    #   is to differentiate what are methods that are the primary entry points
    #   as understood as of the commit that has the @api tag. However, these are
    #   public methods because they have been tested in isolation and are used
    #   to help compose the `@api public` methods.
    module PermissionQuery
      module_function

      # @api public
      #
      # For the given :user and :entity return only workflow actions that meet all of the following:
      #
      # * available for the :entity's workflow state
      # * permitted to be taken by one or more roles in which the user is assigned
      #   either at the workflow level or the entity level.
      #
      # * Actions to which the user Only actions permitted to the user
      #
      # @param user [User]
      # @param entity [#to_sipity_entity] an object that can be converted into a Sipity::Entity
      # @return [ActiveRecord::Relation<Sipity::WorkflowAction>]
      def scope_permitted_workflow_actions_available_for_current_state(user:, entity:)
        workflow_actions_scope = scope_workflow_actions_available_for_current_state(entity: entity)
        workflow_state_actions_scope = scope_permitted_entity_workflow_state_actions(user: user, entity: entity)
        workflow_actions_scope.where(
          workflow_actions_scope.arel_table[:id].in(
            workflow_state_actions_scope.arel_table.project(
              workflow_state_actions_scope.arel_table[:workflow_action_id]
            ).where(workflow_state_actions_scope.constraints.reduce)
          )
        )
      end

      # @api public
      #
      # Agents associated with the given :entity and how they are associated
      # with the given.
      #
      # @param entity [Object] that can be converted into a Sipity::Entity
      # @param role [Object] that can be converted into a Sipity::Role
      # @return [ActiveRecord::Relation<Sipity::Agent>] augmented with
      def scope_agents_associated_with_entity_and_role(entity:, role:)
        entity = PowerConverter.convert_to_sipity_entity(entity)
        role = PowerConverter.convert_to_sipity_role(role)
        workflow_roles = Sipity::WorkflowRole.arel_table
        workflow_responsibilities = Sipity::WorkflowResponsibility.arel_table
        entity_responsibilities = Sipity::EntitySpecificResponsibility.arel_table

        agents = Sipity::Agent.arel_table

        agents_select_manager = agents.project(
          :*,
          Arel.sql("'#{Sipity::Agent::ENTITY_LEVEL_AGENT_RELATIONSHIP}'").as('agent_processing_relationship')
        ).where(
          agents[:id].in(
            entity_responsibilities.project(entity_responsibilities[:agent_id]).join(workflow_roles).on(
              workflow_roles[:id].eq(entity_responsibilities[:workflow_role_id])
            ).where(
              entity_responsibilities[:entity_id].eq(entity.id).and(
                workflow_roles[:role_id].eq(role.id)
              )
            )
          )
        ).union(
          agents.project(
            :*,
            Arel.sql("'#{Sipity::Agent::WORKFLOW_LEVEL_AGENT_RELATIONSHIP}'").as('agent_processing_relationship')
          ).where(
            agents[:id].in(
              workflow_responsibilities.project(workflow_responsibilities[:agent_id]).join(workflow_roles).on(
                workflow_roles[:id].eq(workflow_responsibilities[:workflow_role_id])
              ).where(
                workflow_roles[:workflow_id].eq(entity.workflow_id).and(
                  workflow_roles[:role_id].eq(role.id)
                )
              )
            )
          )
        )
        # I would love to use the following:
        #  `Agent.find_by_sql(agents_select_manager.to_sql)`
        #
        # However AREL is adding an opening and closing parenthesis to the query
        # statement. So I needed to massage that output, as follows:
        #
        # ```ruby
        #  Agent.find_by_sql(
        #    agents_select_manager.to_sql.sub(/\A\s*\(\s*(.*)\s*\)\s*\Z/,'\1')
        #  )
        # ```
        #
        # Instead I'm taking an example from:
        # https://github.com/danshultz/mastering_active_record_sample_code/blob/a656c60ca7a2e27b5cd1aadbdf3bdc1814c37000/app/models/beer.rb#L77-L81
        #
        # Note, I'm making a dynamic query with a result the same as the table
        # name of the model that I'm using.
        Sipity::Agent.from(agents.create_table_alias(agents_select_manager, agents.table_name)).all
      end

      # @api public
      #
      # Roles associated with the given :entity
      # @param entity [Object] that can be converted into a Sipity::Entity
      # @return [ActiveRecord::Relation<Sipity::Role>]
      def scope_roles_associated_with_the_given_entity(entity:)
        entity = PowerConverter.convert_to_sipity_entity(entity)
        workflow_roles = Sipity::WorkflowRole.arel_table
        Sipity::Role.where(
          Sipity::Role.arel_table[:id].in(
            workflow_roles.project(workflow_roles[:role_id]).where(
              workflow_roles[:workflow_id].eq(entity.workflow_id)
            )
          )
        )
      end

      # @api public
      #
      # Is the user authorized to take the processing action on the given
      # entity?
      #
      # @param user [User]
      # @param entity an object that can be converted into a Sipity::Entity
      # @param action an object that can be converted into a Sipity::WorkflowAction#name
      # @return [Boolean]
      def authorized_for_processing?(user:, entity:, action:)
        action_name = PowerConverter.convert_to_sipity_action_name(action)
        scope_permitted_workflow_actions_available_for_current_state(user: user, entity: entity)
          .where(Sipity::WorkflowAction.arel_table[:name].eq(action_name)).count > 0
      end

      # @api public
      #
      # An ActiveRecord::Relation scope that meets the following criteria:
      #
      # * All of the Processing Agents directly associated with the given :user
      #
      # @param user [User]
      # @return [ActiveRecord::Relation<Sipity::Agent>]
      def scope_processing_agents_for(user:)
        return Sipity::Agent.none unless user.present?
        return Sipity::Agent.none unless user.persisted?
        agent = PowerConverter.convert_to_sipity_agent(user)
{'fraction_non_alphanumeric': 0.08359220559164078, 'fraction_numerical': 0.00437729454956227, 'mean_word_length': 2.6763560861666233, 'pattern_counts': {'":': 0, '<': 11, '<?xml version=': 0, '>': 13, 'https://': 2, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '6538761', 'n_tokens_mistral': 4123, 'n_tokens_neox': 3992, 'n_words': 1027}