code: def make_image_sets(key_metadata, channel_metadata):
      """Make image sets by permuting key and channel metadata
  
      key_metadata: collection of 2 tuples. 2 tuple is key + collection of
                      values, for instance, [("Well", ("A01", "A02", "A03")),
                      ("Site", ("1","2","3","4"))]
  
      channel_metadata: collection of tuple of channel name, channel metadata key,
                          channel metadata value and image type
  
      returns a Groups module and a workspace with the image sets in its measurements
      """
  
      channel_descriptors = {
          channel_name: image_type
          for channel_name, metadata_key, metadata_value, image_type in channel_metadata
      }
  
      # iscds = [
      #     cellprofiler_core.pipeline.ImageSetChannelDescriptor(channel_name, image_type)
      #     for channel_name, metadata_key, metadata_value, image_type in channel_metadata
      # ]
      image_set_key_names = [key for key, values in key_metadata]
  
      slices = tuple(
          [slice(0, len(values)) for key, values in key_metadata]
      )
      ijkl = numpy.mgrid[slices]
      ijkl = ijkl.reshape(ijkl.shape[0], numpy.prod(ijkl.shape[1:]))
      image_set_values = [values for key, values in key_metadata]
      image_set_keys = [
          tuple(
              [
                  image_set_values[i][ijkl[i, j]]
                  for i in range(ijkl.shape[0])
              ]
          )
          for j in range(ijkl.shape[1])
      ]
      #
      # The image set dictionary is a dictionary of image set key
      # to a collection of ipds. We make up a file name that consists
      # of the image set keys strung together with the channel metadata key.
      # Likewise, the IPD metadata consists of the image set keys and
      # the channel metadata key/value.
      # Construct the dictionary by passing its constructor a collection
      # of image_set_key / ipd collection tuples.
      #
      image_sets = []
      for k in image_set_keys:
          image_set = []
          image_sets.append(image_set)
          for (
              _,
              channel_metadata_key,
              channel_metadata_value,
              _,
          ) in channel_metadata:
              file_name = (
                  "_".join(list(k) + [channel_metadata_value]) + ".tif"
              )
              metadata = dict(
                  [
                      (kn, kv)
                      for kn, kv in zip(
                          image_set_key_names + [channel_metadata_key],
                          list(k) + [channel_metadata_value],
                      )
                  ]
              )
              image_set.append((file_name, metadata))
      #
      # Scramble the image sets
      #
      r = numpy.random.RandomState()
      r.seed(
          numpy.frombuffer(
              "".join(["%s=%s" % kv for kv in key_metadata]).encode(),
              numpy.uint8,
          )
      )
      image_sets = [
          image_sets[i] for i in r.permutation(len(image_sets))
      ]
  
      m = cellprofiler_core.measurement.Measurements()
      m.set_metadata_tags(
          [
              "_".join(
                  (
                      cellprofiler_core.constants.measurement.C_METADATA,
                      k,
                  )
              )
              for k in image_set_key_names
          ]
      )
      m.set_channel_descriptors(channel_descriptors)
      for i, ipds in enumerate(image_sets):
          image_number = i + 1
          for (file_name, metadata), channel_name in zip(
              ipds, channel_descriptors.keys()
          ):
              for feature, value in (
                  (
                      cellprofiler_core.constants.measurement.C_FILE_NAME,
                      file_name,
                  ),
                  (
                      cellprofiler_core.constants.measurement.C_PATH_NAME,
                      os.pathsep + "images",
                  ),
                  (
                      cellprofiler_core.constants.measurement.C_URL,
                      "file://images/" + file_name,
                  ),
              ):
                  m[
                      cellprofiler_core.constants.measurement.IMAGE,
                      feature + "_" + channel_name,
                      image_number,
                  ] = value
              for key, value in list(metadata.items()):
                  feature = "_".join(
                      (
                          cellprofiler_core.constants.measurement.C_METADATA,
                          key,
                      )
                  )
                  m[
                      cellprofiler_core.constants.measurement.IMAGE,
                      feature,
                      image_number,
                  ] = value
  
      pipeline = cellprofiler_core.pipeline.Pipeline()
      module = cellprofiler_core.modules.groups.Groups()
      module.set_module_num(1)
      pipeline.add_module(module)
      workspace = cellprofiler_core.workspace.Workspace(
          pipeline, module, m, None, m, None
      )
      return module, workspace
  
file_path: CellProfiler/tests/core/modules/test_groups.py
imports: import io
  import os
  import numpy
  import cellprofiler_core.constants.measurement
  import cellprofiler_core.measurement
  import cellprofiler_core.modules.groups
  import cellprofiler_core.pipeline
  import cellprofiler_core.workspace
  import tests.core
label_desc: Use image node_id 4 for calling a global function with example usage: make_image_sets(key_metadata, channel_metadata) and returns: module,  workspace