diff --git a/gspread/spreadsheet.py b/gspread/spreadsheet.py index 236347258..13bff4379 100644 --- a/gspread/spreadsheet.py +++ b/gspread/spreadsheet.py @@ -718,6 +718,22 @@ def update_locale(self, locale: str) -> Any: self._properties["locale"] = locale return res + def list_conditional_formatting_rules(self, sheetid: int) -> List[Any]: + """Lists the spreadsheet's conditional formats""" + sheets: List[Mapping[str, Any]] = self.fetch_sheet_metadata( + params={"fields": "sheets.properties,sheets.conditionalFormats"} + )["sheets"] + + try: + sheet = finditem( + lambda sheet: sheet["properties"]["sheetId"] == sheetid, sheets + ) + + except StopIteration: + raise WorksheetNotFound("worksheet id {} not found".format(sheetid)) + + return sheet.get("conditionalFormats", []) + def list_protected_ranges(self, sheetid: int) -> List[Any]: """Lists the spreadsheet's protected named ranges""" sheets: List[Mapping[str, Any]] = self.fetch_sheet_metadata( diff --git a/gspread/worksheet.py b/gspread/worksheet.py index ba9fde0a4..9c7a30f9d 100644 --- a/gspread/worksheet.py +++ b/gspread/worksheet.py @@ -2132,6 +2132,26 @@ def delete_protected_range(self, id: str) -> JSONResponse: } return self.client.batch_update(self.spreadsheet_id, body) + + def delete_conditional_formatting_rule(self, index: int) -> JSONResponse: + """Delete conditional formatting rule identified by the index ``index``. + + To retrieve the ID of a conditional formatting rule use the following method + to list them all: :func:`~gspread.Spreadsheet.list_conditional_formatting_rules` + """ + + body = { + "requests": [ + { + "deleteConditionalFormatRule": { + "sheetId": self.id, + "index": index, + } + } + ] + } + + return self.client.batch_update(self.spreadsheet_id, body) def delete_dimension( self, dimension: Dimension, start_index: int, end_index: Optional[int] = None diff --git a/tests/spreadsheet_test.py b/tests/spreadsheet_test.py index ca1b5ed79..7a6cdcbdb 100644 --- a/tests/spreadsheet_test.py +++ b/tests/spreadsheet_test.py @@ -245,3 +245,14 @@ def test_export_spreadsheet(self): self.assertEqual( values[0], res_values, "exported values are not the value initially set" ) + + @pytest.mark.vcr() + def test_listing_conditional_format_rules(self): + """Test listing conditional format rules of a spreadsheet""" + + worksheet = self.spreadsheet.sheet1 + worksheet.format("A1", {"backgroundColor": {"red": 1.0}}) + + rules = self.spreadsheet.list_conditional_formatting_rules(worksheet.id) + + self.assertEqual(len(rules), 1) diff --git a/tests/worksheet_test.py b/tests/worksheet_test.py index f8523b5bd..309ea9f40 100644 --- a/tests/worksheet_test.py +++ b/tests/worksheet_test.py @@ -2007,3 +2007,28 @@ def test_add_validation(self): # Further ensure we are able to access the exception's properties after pickling reloaded_exception = pickle.loads(pickle.dumps(ex.exception)) # nosec self.assertEqual(reloaded_exception.args[0]["status"], "INVALID_ARGUMENT") + + @pytest.mark.vcr() + def test_delete_conditional_formatting_rule(self): + sheet = self.sheet + spreadsheet = self.spreadsheet + worksheet = self.spreadsheet.sheet1 + + # add a conditional format rule to the spreadsheet + worksheet.format( + "A1:A2", + { + "backgroundColor": {"green": 1, "blue": 1}, + }, + ) + + # list the conditions on the spreadsheet + rules = spreadsheet.list_conditional_formatting_rules(0) + self.assertEqual(len(rules), 1) + + # delete the first rule by index + sheet.delete_conditional_formatting_rule(0) + + # verify rule was removed + rules = spreadsheet.list_conditional_formatting_rules(0) + self.assertEqual(len(rules), 0)