diff --git a/gspread/spreadsheet.py b/gspread/spreadsheet.py index 5d5a2bb37..69ae98512 100644 --- a/gspread/spreadsheet.py +++ b/gspread/spreadsheet.py @@ -20,8 +20,8 @@ SPREADSHEET_VALUES_CLEAR_URL, SPREADSHEET_VALUES_URL, ) -from .utils import ExportFormat, finditem, quote -from .worksheet import Worksheet +from .utils import ExportFormat, extract_title_from_range, fill_gaps, finditem, quote +from .worksheet import ValueRange, Worksheet class Spreadsheet: @@ -739,3 +739,33 @@ def list_protected_ranges(self, sheetid): raise WorksheetNotFound("worksheet id {} not found".format(sheetid)) return sheet.get("protectedRanges", []) + + def get_all_worksheet_values(self, skip_worksheet_titles: list = None): + """Grabs all the data from all the worksheets in one API call. Skips any worksheets that were named in the + skip_worksheet_title param. + + :param list skip_worksheet_titles: A list of worksheet titles to skip. + :returns: a dict of worksheet data with worksheet title as key + :rtype: dict + """ + + if skip_worksheet_titles is None: + skip_worksheet_titles = [] + + ranges = [] + + for worksheet in self.worksheets().worksheets(): + if worksheet.title not in skip_worksheet_titles: + ranges.append(worksheet.title) + + values = self.values_batch_get(ranges=ranges) + + return_data = {} + + for values in values["valueRanges"]: + value_range = ValueRange.from_json(values) + return_data[extract_title_from_range(value_range.range)] = fill_gaps( + value_range + ) + + return return_data diff --git a/gspread/utils.py b/gspread/utils.py index d6e74fd43..319fec03c 100644 --- a/gspread/utils.py +++ b/gspread/utils.py @@ -27,6 +27,8 @@ URL_KEY_V1_RE = re.compile(r"key=([^&#]+)") URL_KEY_V2_RE = re.compile(r"/spreadsheets/d/([a-zA-Z0-9-_]+)") +TITLE_RANGE_RE = re.compile(r"'(.*?)'!.*") + Dimension = namedtuple("Dimension", ["rows", "cols"])("ROWS", "COLUMNS") ValueRenderOption = namedtuple( "ValueRenderOption", ["formatted", "unformatted", "formula"] @@ -519,6 +521,28 @@ def extract_id_from_url(url): raise NoValidUrlKeyFound +def extract_title_from_range(range_string: str) -> str: + """Will extract the sheet title from a range. + + :param str letter: A range string + :returns: the title of the worksheet from the range string + :rtype: str + + :raises: + :class:`~gspread.exceptions.InvalidInputValue`: if can't extract a title + + Example: + + >>> extract_title_from_range("'Volunteer Portal'!A1:Z1005" -> "Volunteer Portal") + 'Volunteer Portal' + """ + match = TITLE_RANGE_RE.search(range_string) + if match: + return match.group(1) + + raise InvalidInputValue + + def wid_to_gid(wid): """Calculate gid of a worksheet from its wid.""" widval = wid[1:] if len(wid) > 3 else wid diff --git a/tests/utils_test.py b/tests/utils_test.py index 0f1508caf..7213aef46 100644 --- a/tests/utils_test.py +++ b/tests/utils_test.py @@ -218,3 +218,23 @@ def test_column_letter_to_index(self): label, expected ), ) + + def test_extract_title_from_range(self): + # All the input values to test one after an other + # [0] input value + # [1] expected return value + # [2] expected exception to raise + inputs = [ + ("asdf", None, gspread.exceptions.InvalidInputValue), + ("'Volunteer Portal'!A1:Z1005", "Volunteer Portal", None), + ] + + for label, expected, exception in inputs: + if exception is not None: + # assert the exception is raised + with self.assertRaises(exception): + utils.extract_title_from_range(label) + else: + # assert the return values is correct + result = utils.extract_title_from_range(label) + self.assertEqual(result, expected)