def generate_group_by_range_and_index_str(group_by, data_str, value_str,
                                          index_str):
    """
    Generate the range and index string for GROUP BY expression.

    Args:
        group_by (str): the column name to be grouped.
        data_str (str): a string that represents the total table data.
        value_str (str): a string that represents the cell value in the table.
        index_str (str): a string that represents the row index of the table.

    Returns:
        If group_by is None or "", return (None, None, None).
        Otherwise, return (outer_range_str, inner_range_str, iter_vars_str).
        For example, "SUM(x) <= 10 GROUP BY y" would be translated to be:

        for ${iter_vars_str} in ${outer_range_str}:
            sum([model.x for i in ${inner_range_str}] <= 10
    """
    if not group_by:
        return None, None, None

    numpy_str = '__import__("numpy")'
    group_by_data_str = '%s["%s"]' % (data_str, group_by)
    outer_range_str = 'zip(*%s.unique(%s, return_index=True))' % (
        numpy_str, group_by_data_str)
    inner_range_str = '%s.where(%s == %s)[0]' % (numpy_str, group_by_data_str,
                                                 value_str)
    return outer_range_str, inner_range_str, [value_str, index_str]