robin900 / gspread-formatting

Complete cell formatting support for Google spreadsheets via gspread package.

Repository from Github https://github.comrobin900/gspread-formattingRepository from Github https://github.comrobin900/gspread-formatting

BooleanCondition for ONE_OF_RANGE

yoonghm opened this issue · comments

I understand that ONE_OF_RANGE data validation is not yet handled. Could you share what to be done so that I could implement it for my application. Thanks.

A sample example:

import gspread
import gspread_formatting as gsf

// ...

vr_1 = gsf.DataValidationRule(
    gsf.BooleanCondition('ONE_OF_RANGE', '=Sheet1!A2:285'),
    showCustomUi=True
)

gsf.set_data_validation_for_cell_range(
    wb.worksheet('Sheet2'),
    "M2:M",
    vr_1
)

@yoonghm Thanks for submitting this issue; I'll do my best to answer your questions.

For data validation (not conditional formatting), ONE_OF_RANGE condition type is certainly supported. The single value for a condition of that type must be a string representing a "valid range in A1 notation", and I think the problem may be that your range value above starts with a = character. Try removing the = character.

The other thing that I've noticed is that in some cases, the Sheets API is very flexible and graceful in interpreting range values, and in other cases can be rather strict. So perhaps A2:285 isn't being interpreted gracefully to mean "cells in column A from row 2 through row 285", and instead you want to say A2:A285.

Here's the relevant section on range values in the Sheets API documentation.

Thank you for your prompt response:

class BooleanCondition(ConditionalFormattingComponent):
    # ...
    TYPES = {
        # ...
        'ONE_OF_RANGE': 1,
        'ONE_OF_LIST': (lambda x: isinstance(x, (list, tuple)) and len(x) > 0),
        # ...
    }
    # ...
    def __init__(self, type, values):
        self.type = _parse_string_enum("type", type, BooleanCondition.TYPES)
        validator = BooleanCondition.TYPES[self.type]
        valid = validator(values) if callable(validator) else len(values) == validator
        if not valid:
            raise ValueError(
                "BooleanCondition.values has inappropriate "
                "length/content for condition type %s" % self.type
            )
        # values are either RelativeDate enum values or user-entered values
        self.values = [
            v if isinstance(v, ConditionValue) else (
                ConditionValue.from_props(v)
                if isinstance(v, dict)
                else ConditionValue(userEnteredValue=v)
            )
            for v in values
        ]

callable(validator) for "ONE_OF_LIST" is a lambda function which is callable but "ONE_OF_RANGE" is not. Routines after this try to add properties for this type but the range type is not supported. I did not spend too much time as Google Sheet API v4 is not well documented.

Is your example code causing a ValueError: BooleanCondition.values has inappropriate length/content for condition type ONE_OF_RANGE?

The constructor for BooleanCondition accepts two parameters, type (a string with the ConditionType value) and values, a list of the values expected, even if a condition type wants only one value. So try:

vr_1 = gsf.DataValidationRule(
    gsf.BooleanCondition('ONE_OF_RANGE', ['Sheet1!A2:A285']),
    showCustomUi=True
)
vr_1 = gsf.DataValidationRule(
    gsf.BooleanCondition('ONE_OF_RANGE', ['=Sheet1!A2:A285']),
    showCustomUi=True
)

This works! Thanks for the help.