<commit_before>module DTK
  module TargetCloneMixin
    def clone_post_copy_hook(clone_copy_output,opts={})
      case clone_copy_output.model_name()
       when :component
        ClonePostCopyHook.component(self,clone_copy_output,opts)
       when :node
        ClonePostCopyHook.node(self,clone_copy_output,opts)
       else #TODO: catchall that will be expanded
        new_id_handle = clone_copy_output.id_handles.first
        StateChange.create_pending_change_item(:new_item => new_id_handle, :parent => id_handle())
      end
    end
   private
    module ClonePostCopyHook
      def self.node(target,clone_copy_output,opts)
        target.update_object!(:iaas_type,:iaas_properties)
        new_id_handle = clone_copy_output.id_handles.first
        #add external ref values from target to node if node does not have them
        #assuming passed already check whether node consistent requirements with target
        #TODO: not handling yet constraint form where set of possibilities given
        node = clone_copy_output.objects.first
        node_ext_ref = node[:external_ref]
        target[:iaas_properties].each do |k,v|
          unless node_ext_ref.has_key?(k)
            node_ext_ref[k] = v
          end
        end
        node.update(:external_ref => node_ext_ref)
        StateChange.create_pending_change_item(:new_item => new_id_handle, :parent => target.id_handle())
      end

      def self.component(target,clone_copy_output,opts)
        if assembly = clone_copy_output.assembly?(:subclass_object=>true)
          assembly(target,assembly,clone_copy_output,opts)
        else
          raise Error.new("Not implemented clone of non assembly component to target")
        end
      end

      def self.assembly(target,assembly,clone_copy_output,opts)
        #clone_copy_output will be of form: assembly - node - component

        #adjust link_def_id on ports
        set_ports_link_def_and_cmp_ids(clone_copy_output)

        #for port links that get generated by add on service
        #TODO: currently not used; may deprecate create_add_on_port_and_attr_links?(target,clone_copy_output,opts)

        level = 1
        nodes = clone_copy_output.children_objects(level,:node,:cols=>[:display_name,:external_ref,:type])
        return if nodes.empty?

        Node.cache_attribute_values!(nodes,:cardinality)

        # This creates if needed target refs and links to them
        # and moves node attributes to the target refs
        create_target_refs_and_links?(target,assembly,nodes)

        # Computing port_links (and also attribute links after create_target_refs_and_links
        # because relying on the node attributes to be shifted to target refs if connected to target refs
        port_link_idhs = clone_copy_output.children_id_handles(level,:port_link)
        assembly__port_links(target,clone_copy_output,port_link_idhs,opts)

        level = 2
        component_child_hashes = clone_copy_output.children_hash_form(level,:component)
        return if component_child_hashes.empty?
        component_new_items = component_child_hashes.map do |child_hash|
          {:new_item => child_hash[:id_handle], :parent => target.id_handle()}
        end
        StateChange.create_pending_change_items(component_new_items)
      end

      # this creates needed target refs and their links to them
      # there are a number of cases treated on a node by node basis (i.e., member of nodes)
      #  if node is a group then creating new target refs for it as function of its cardinality
      #  if node has been designated as matched to an existing target ref, need to create links to these
      #  otherwise create a state change object
      def self.create_target_refs_and_links?(target,assembly,nodes)
        tr_create = Array.new
        tr_link = Hash.new
        ndx_needs_sc = Hash.new

        partition = [tr_create,Array.new]
        nodes.each{|node|partition[node.is_node_group?() ? 0 : 1] << node}

        # partition[1] are non node groups; elements we want to see if point to a node_template that is a target ref
        unless partition[1].empty?
          #initiallly set ndx_needs_state_change to true
          ndx_needs_sc = partition[1].inject(Hash.new){|h,n|h.merge(n[:id] => n)}

          ndx_node_template__node = partition[1].inject(Hash.new) do |h,n|
            n[:node_template_id] ? h.merge!(n[:node_template_id] => n[:id]) : h
          end
          unless ndx_node_template__node.empty?
            sp_hash = {
              :cols => [:id,:display_name,:type],
              :filter => [:oneof,:id,ndx_node_template__node.keys]
            }
            Model.get_objs(target.model_handle(:node),sp_hash).each do |nt|
              if nt[:type] == Node::TargetRef.type()
                node_id = ndx_node_template__node[nt[:id]]
                tr_link.merge!(node_id => nt)
                ndx_needs_sc[node_id] = nil
              end
            end
          end 
        end

        unless tr_link.empty? and tr_create.empty?<commit_msg>update so when node groups staged a state change object created too<commit_after>module DTK
  module TargetCloneMixin
    def clone_post_copy_hook(clone_copy_output,opts={})
      case clone_copy_output.model_name()
       when :component
        ClonePostCopyHook.component(self,clone_copy_output,opts)
       when :node
        ClonePostCopyHook.node(self,clone_copy_output,opts)
       else #TODO: catchall that will be expanded
        new_id_handle = clone_copy_output.id_handles.first
        StateChange.create_pending_change_item(:new_item => new_id_handle, :parent => id_handle())
      end
    end
   private
    module ClonePostCopyHook
      def self.node(target,clone_copy_output,opts)
        target.update_object!(:iaas_type,:iaas_properties)
        new_id_handle = clone_copy_output.id_handles.first
        #add external ref values from target to node if node does not have them
        #assuming passed already check whether node consistent requirements with target
        #TODO: not handling yet constraint form where set of possibilities given
        node = clone_copy_output.objects.first
        node_ext_ref = node[:external_ref]
        target[:iaas_properties].each do |k,v|
          unless node_ext_ref.has_key?(k)
            node_ext_ref[k] = v
          end
        end
        node.update(:external_ref => node_ext_ref)
        StateChange.create_pending_change_item(:new_item => new_id_handle, :parent => target.id_handle())
      end

      def self.component(target,clone_copy_output,opts)
        if assembly = clone_copy_output.assembly?(:subclass_object=>true)
          assembly(target,assembly,clone_copy_output,opts)
        else
          raise Error.new("Not implemented clone of non assembly component to target")
        end
      end

      def self.assembly(target,assembly,clone_copy_output,opts)
        #clone_copy_output will be of form: assembly - node - component

        #adjust link_def_id on ports
        set_ports_link_def_and_cmp_ids(clone_copy_output)

        #for port links that get generated by add on service
        #TODO: currently not used; may deprecate create_add_on_port_and_attr_links?(target,clone_copy_output,opts)

        level = 1
        nodes = clone_copy_output.children_objects(level,:node,:cols=>[:display_name,:external_ref,:type])
        return if nodes.empty?

        Node.cache_attribute_values!(nodes,:cardinality)

        # This creates if needed target refs and links to them
        # and moves node attributes to the target refs
        create_target_refs_and_links?(target,assembly,nodes)

        # Computing port_links (and also attribute links after create_target_refs_and_links
        # because relying on the node attributes to be shifted to target refs if connected to target refs
        port_link_idhs = clone_copy_output.children_id_handles(level,:port_link)
        assembly__port_links(target,clone_copy_output,port_link_idhs,opts)

        level = 2
        component_child_hashes = clone_copy_output.children_hash_form(level,:component)
        return if component_child_hashes.empty?
        component_new_items = component_child_hashes.map do |child_hash|
          {:new_item => child_hash[:id_handle], :parent => target.id_handle()}
        end
        StateChange.create_pending_change_items(component_new_items)
      end

      # this creates needed target refs and their links to them
      # there are a number of cases treated on a node by node basis (i.e., member of nodes)
      #  if node is a group then creating new target refs for it as function of its cardinality
      #  if node has been designated as matched to an existing target ref, need to create links to these
      #  otherwise create a state change object
      def self.create_target_refs_and_links?(target,assembly,nodes)
        tr_create = Array.new
        tr_link = Hash.new
        # ndx_needs_state_change is used to find nodes that need a state change object
        # meaning model is annoatted so these when a task is run will cause a node to be created
        # initiallly set ndx_needs_state_change to have all nodes and then in loop below remove ones 
        # that are linked to existing nodes

        # TODO: assuming that node groups need to be created; think node groups may be able to point to
        # - all nodes that need to be created
        # - all nodes that exist
        # - mixture
        ndx_needs_sc = Hash.new

        partition = [tr_create,Array.new]
        nodes.each do |node|
          partition[node.is_node_group?() ? 0 : 1] << node
          ndx_needs_sc.merge!(node[:id] => node)
        end

        # partition[1] are non node groups; elements we want to see if point to a node_template that is a target ref
        unless partition[1].empty?
          ndx_node_template__node = partition[1].inject(Hash.new) do |h,n|
            n[:node_template_id] ? h.merge!(n[:node_template_id] => n[:id]) : h
          end
          unless ndx_node_template__node.empty?
            sp_hash = {
              :cols => [:id,:display_name,:type],
              :filter => [:oneof,:id,ndx_node_template__node.keys]
            }
            Model.get_objs(target.model_handle(:node),sp_hash).each do |nt|
              if nt[:type] == Node::TargetRef.type()
                node_id = ndx_node_template__node[nt[:id]]
                tr_link.merge!(node_id => nt)
                ndx_needs_sc[node_id] = nil
              end
            end
          end 
        end

        unless tr_link.empty? and tr_create.empty?
{'fraction_non_alphanumeric': 0.07334847624455802, 'fraction_numerical': 0.0014196479273140261, 'mean_word_length': 2.834179970972424, 'pattern_counts': {'":': 0, '<': 7, '<?xml version=': 0, '>': 31, 'https://': 0, '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': '3724924', 'n_tokens_mistral': 3091, 'n_tokens_neox': 3023, 'n_words': 816}