diff --git a/gspread/utils.py b/gspread/utils.py index 2f16b74ea..cad032fb3 100644 --- a/gspread/utils.py +++ b/gspread/utils.py @@ -709,6 +709,41 @@ def wrapper(*args, **kwargs): return decorate +def combined_merge_values(worksheet_metadata, values): + """For each merged region, replace all values with the value of the top-left cell of the region. + e.g., replaces + [ + [1, None, None], + [None, None, None], + ] + with + [ + [1, 1, None], + [1, 1, None], + ] + if the top-left four cells are merged. + + :param worksheet_metadata: The metadata returned by the Google API for the worksheet. Should have a "merges" key. + + :param values: The values returned by the Google API for the worksheet. 2D array. + """ + merges = worksheet_metadata.get("merges", []) + # each merge has "startRowIndex", "endRowIndex", "startColumnIndex", "endColumnIndex + new_values = [[v for v in row] for row in values] + + for merge in merges: + start_row, end_row = merge["startRowIndex"], merge["endRowIndex"] + start_col, end_col = merge["startColumnIndex"], merge["endColumnIndex"] + top_left_value = values[start_row][start_col] + row_indices = range(start_row, end_row) + col_indices = range(start_col, end_col) + for row_index in row_indices: + for col_index in col_indices: + new_values[row_index][col_index] = top_left_value + + return new_values + + if __name__ == "__main__": import doctest diff --git a/gspread/worksheet.py b/gspread/worksheet.py index fa88a1dde..b148b80a3 100644 --- a/gspread/worksheet.py +++ b/gspread/worksheet.py @@ -24,6 +24,7 @@ accepted_kwargs, cast_to_a1_notation, cell_list_to_rect, + combined_merge_values, fill_gaps, filter_dict_values, finditem, @@ -344,10 +345,11 @@ def range(self, name=""): @accepted_kwargs( major_dimension=None, + combine_merged_cells=False, value_render_option=None, date_time_render_option=None, ) - def get_values(self, range_name=None, **kwargs): + def get_values(self, range_name=None, combine_merged_cells=False, **kwargs): """Returns a list of lists containing all values from specified range. By default values are returned as strings. See ``value_render_option`` @@ -362,6 +364,16 @@ def get_values(self, range_name=None, **kwargs): Defaults to Dimension.rows :type major_dimension: :namedtuple:`~gspread.utils.Dimension` + :param bool combine_merged_cells: (optional) If True, then all cells that + are part of a merged cell will have the same value as the top-left + cell of the merged cell. Defaults to False. + + .. warning:: + + Setting this to True will cause an additional API request to be + made to retrieve the values of all merged cells. + + :param str value_render_option: (optional) Determines how values should be rendered in the output. See `ValueRenderOption`_ in the Sheets API. @@ -434,7 +446,15 @@ def get_values(self, range_name=None, **kwargs): worksheet.get_values('A2:B4', value_render_option=ValueRenderOption.formula) """ try: - return fill_gaps(self.get(range_name, **kwargs)) + vals = fill_gaps(self.get(range_name, **kwargs)) + if combine_merged_cells is True: + spreadsheet_meta = self.spreadsheet.fetch_sheet_metadata() + worksheet_meta = finditem( + lambda x: x["properties"]["title"] == self.title, + spreadsheet_meta["sheets"], + ) + return combined_merge_values(worksheet_meta, vals) + return vals except KeyError: return [] diff --git a/tests/cassettes/WorksheetTest.test_get_values_and_combine_merged_cells.json b/tests/cassettes/WorksheetTest.test_get_values_and_combine_merged_cells.json new file mode 100644 index 000000000..e203e7888 --- /dev/null +++ b/tests/cassettes/WorksheetTest.test_get_values_and_combine_merged_cells.json @@ -0,0 +1,1001 @@ +{ + "version": 1, + "interactions": [ + { + "request": { + "method": "POST", + "uri": "https://www.googleapis.com/drive/v3/files?supportsAllDrives=True", + "body": "{\"name\": \"Test WorksheetTest test_get_values_and_combine_merged_cells\", \"mimeType\": \"application/vnd.google-apps.spreadsheet\"}", + "headers": { + "User-Agent": [ + "python-requests/2.31.0" + ], + "Accept-Encoding": [ + "gzip, deflate" + ], + "Accept": [ + "*/*" + ], + "Connection": [ + "keep-alive" + ], + "x-goog-api-client": [ + "cred-type/sa" + ], + "Content-Length": [ + "126" + ], + "Content-Type": [ + "application/json" + ], + "authorization": [ + "" + ] + } + }, + "response": { + "status": { + "code": 200, + "message": "OK" + }, + "headers": { + "Pragma": [ + "no-cache" + ], + "X-Content-Type-Options": [ + "nosniff" + ], + "Content-Type": [ + "application/json; charset=UTF-8" + ], + "X-XSS-Protection": [ + "0" + ], + "Cache-Control": [ + "no-cache, no-store, max-age=0, must-revalidate" + ], + "Transfer-Encoding": [ + "chunked" + ], + "Vary": [ + "Origin, X-Origin" + ], + "Server": [ + "ESF" + ], + "Date": [ + "Thu, 08 Jun 2023 18:55:14 GMT" + ], + "Expires": [ + "Mon, 01 Jan 1990 00:00:00 GMT" + ], + "X-Frame-Options": [ + "SAMEORIGIN" + ], + "Alt-Svc": [ + "h3=\":443\"; ma=2592000,h3-29=\":443\"; ma=2592000" + ], + "content-length": [ + "213" + ] + }, + "body": { + "string": "{\n \"kind\": \"drive#file\",\n \"id\": \"1txmu0gP2qQRNaGwtUo2rW5ikjBwfS7_kNKkmzbCJ-FA\",\n \"name\": \"Test WorksheetTest test_get_values_and_combine_merged_cells\",\n \"mimeType\": \"application/vnd.google-apps.spreadsheet\"\n}\n" + } + } + }, + { + "request": { + "method": "GET", + "uri": "https://sheets.googleapis.com/v4/spreadsheets/1txmu0gP2qQRNaGwtUo2rW5ikjBwfS7_kNKkmzbCJ-FA?includeGridData=false", + "body": null, + "headers": { + "User-Agent": [ + "python-requests/2.31.0" + ], + "Accept-Encoding": [ + "gzip, deflate" + ], + "Accept": [ + "*/*" + ], + "Connection": [ + "keep-alive" + ], + "x-goog-api-client": [ + "cred-type/sa" + ], + "authorization": [ + "" + ] + } + }, + "response": { + "status": { + "code": 200, + "message": "OK" + }, + "headers": { + "X-Content-Type-Options": [ + "nosniff" + ], + "Content-Type": [ + "application/json; charset=UTF-8" + ], + "X-XSS-Protection": [ + "0" + ], + "Cache-Control": [ + "private" + ], + "Transfer-Encoding": [ + "chunked" + ], + "Vary": [ + "Origin", + "X-Origin", + "Referer" + ], + "Server": [ + "ESF" + ], + "Date": [ + "Thu, 08 Jun 2023 18:55:15 GMT" + ], + "X-Frame-Options": [ + "SAMEORIGIN" + ], + "Alt-Svc": [ + "h3=\":443\"; ma=2592000,h3-29=\":443\"; ma=2592000" + ], + "content-length": [ + "3357" + ] + }, + "body": { + "string": "{\n \"spreadsheetId\": \"1txmu0gP2qQRNaGwtUo2rW5ikjBwfS7_kNKkmzbCJ-FA\",\n \"properties\": {\n \"title\": \"Test WorksheetTest test_get_values_and_combine_merged_cells\",\n \"locale\": \"en_US\",\n \"autoRecalc\": \"ON_CHANGE\",\n \"timeZone\": \"Etc/GMT\",\n \"defaultFormat\": {\n \"backgroundColor\": {\n \"red\": 1,\n \"green\": 1,\n \"blue\": 1\n },\n \"padding\": {\n \"top\": 2,\n \"right\": 3,\n \"bottom\": 2,\n \"left\": 3\n },\n \"verticalAlignment\": \"BOTTOM\",\n \"wrapStrategy\": \"OVERFLOW_CELL\",\n \"textFormat\": {\n \"foregroundColor\": {},\n \"fontFamily\": \"arial,sans,sans-serif\",\n \"fontSize\": 10,\n \"bold\": false,\n \"italic\": false,\n \"strikethrough\": false,\n \"underline\": false,\n \"foregroundColorStyle\": {\n \"rgbColor\": {}\n }\n },\n \"backgroundColorStyle\": {\n \"rgbColor\": {\n \"red\": 1,\n \"green\": 1,\n \"blue\": 1\n }\n }\n },\n \"spreadsheetTheme\": {\n \"primaryFontFamily\": \"Arial\",\n \"themeColors\": [\n {\n \"colorType\": \"TEXT\",\n \"color\": {\n \"rgbColor\": {}\n }\n },\n {\n \"colorType\": \"BACKGROUND\",\n \"color\": {\n \"rgbColor\": {\n \"red\": 1,\n \"green\": 1,\n \"blue\": 1\n }\n }\n },\n {\n \"colorType\": \"ACCENT1\",\n \"color\": {\n \"rgbColor\": {\n \"red\": 0.25882354,\n \"green\": 0.52156866,\n \"blue\": 0.95686275\n }\n }\n },\n {\n \"colorType\": \"ACCENT2\",\n \"color\": {\n \"rgbColor\": {\n \"red\": 0.91764706,\n \"green\": 0.2627451,\n \"blue\": 0.20784314\n }\n }\n },\n {\n \"colorType\": \"ACCENT3\",\n \"color\": {\n \"rgbColor\": {\n \"red\": 0.9843137,\n \"green\": 0.7372549,\n \"blue\": 0.015686275\n }\n }\n },\n {\n \"colorType\": \"ACCENT4\",\n \"color\": {\n \"rgbColor\": {\n \"red\": 0.20392157,\n \"green\": 0.65882355,\n \"blue\": 0.3254902\n }\n }\n },\n {\n \"colorType\": \"ACCENT5\",\n \"color\": {\n \"rgbColor\": {\n \"red\": 1,\n \"green\": 0.42745098,\n \"blue\": 0.003921569\n }\n }\n },\n {\n \"colorType\": \"ACCENT6\",\n \"color\": {\n \"rgbColor\": {\n \"red\": 0.27450982,\n \"green\": 0.7411765,\n \"blue\": 0.7764706\n }\n }\n },\n {\n \"colorType\": \"LINK\",\n \"color\": {\n \"rgbColor\": {\n \"red\": 0.06666667,\n \"green\": 0.33333334,\n \"blue\": 0.8\n }\n }\n }\n ]\n }\n },\n \"sheets\": [\n {\n \"properties\": {\n \"sheetId\": 0,\n \"title\": \"Sheet1\",\n \"index\": 0,\n \"sheetType\": \"GRID\",\n \"gridProperties\": {\n \"rowCount\": 1000,\n \"columnCount\": 26\n }\n }\n }\n ],\n \"spreadsheetUrl\": \"https://docs.google.com/spreadsheets/d/1txmu0gP2qQRNaGwtUo2rW5ikjBwfS7_kNKkmzbCJ-FA/edit\"\n}\n" + } + } + }, + { + "request": { + "method": "GET", + "uri": "https://sheets.googleapis.com/v4/spreadsheets/1txmu0gP2qQRNaGwtUo2rW5ikjBwfS7_kNKkmzbCJ-FA?includeGridData=false", + "body": null, + "headers": { + "User-Agent": [ + "python-requests/2.31.0" + ], + "Accept-Encoding": [ + "gzip, deflate" + ], + "Accept": [ + "*/*" + ], + "Connection": [ + "keep-alive" + ], + "x-goog-api-client": [ + "cred-type/sa" + ], + "authorization": [ + "" + ] + } + }, + "response": { + "status": { + "code": 200, + "message": "OK" + }, + "headers": { + "X-Content-Type-Options": [ + "nosniff" + ], + "Content-Type": [ + "application/json; charset=UTF-8" + ], + "X-XSS-Protection": [ + "0" + ], + "Cache-Control": [ + "private" + ], + "Transfer-Encoding": [ + "chunked" + ], + "Vary": [ + "Origin", + "X-Origin", + "Referer" + ], + "Server": [ + "ESF" + ], + "Date": [ + "Thu, 08 Jun 2023 18:55:15 GMT" + ], + "X-Frame-Options": [ + "SAMEORIGIN" + ], + "Alt-Svc": [ + "h3=\":443\"; ma=2592000,h3-29=\":443\"; ma=2592000" + ], + "content-length": [ + "3357" + ] + }, + "body": { + "string": "{\n \"spreadsheetId\": \"1txmu0gP2qQRNaGwtUo2rW5ikjBwfS7_kNKkmzbCJ-FA\",\n \"properties\": {\n \"title\": \"Test WorksheetTest test_get_values_and_combine_merged_cells\",\n \"locale\": \"en_US\",\n \"autoRecalc\": \"ON_CHANGE\",\n \"timeZone\": \"Etc/GMT\",\n \"defaultFormat\": {\n \"backgroundColor\": {\n \"red\": 1,\n \"green\": 1,\n \"blue\": 1\n },\n \"padding\": {\n \"top\": 2,\n \"right\": 3,\n \"bottom\": 2,\n \"left\": 3\n },\n \"verticalAlignment\": \"BOTTOM\",\n \"wrapStrategy\": \"OVERFLOW_CELL\",\n \"textFormat\": {\n \"foregroundColor\": {},\n \"fontFamily\": \"arial,sans,sans-serif\",\n \"fontSize\": 10,\n \"bold\": false,\n \"italic\": false,\n \"strikethrough\": false,\n \"underline\": false,\n \"foregroundColorStyle\": {\n \"rgbColor\": {}\n }\n },\n \"backgroundColorStyle\": {\n \"rgbColor\": {\n \"red\": 1,\n \"green\": 1,\n \"blue\": 1\n }\n }\n },\n \"spreadsheetTheme\": {\n \"primaryFontFamily\": \"Arial\",\n \"themeColors\": [\n {\n \"colorType\": \"TEXT\",\n \"color\": {\n \"rgbColor\": {}\n }\n },\n {\n \"colorType\": \"BACKGROUND\",\n \"color\": {\n \"rgbColor\": {\n \"red\": 1,\n \"green\": 1,\n \"blue\": 1\n }\n }\n },\n {\n \"colorType\": \"ACCENT1\",\n \"color\": {\n \"rgbColor\": {\n \"red\": 0.25882354,\n \"green\": 0.52156866,\n \"blue\": 0.95686275\n }\n }\n },\n {\n \"colorType\": \"ACCENT2\",\n \"color\": {\n \"rgbColor\": {\n \"red\": 0.91764706,\n \"green\": 0.2627451,\n \"blue\": 0.20784314\n }\n }\n },\n {\n \"colorType\": \"ACCENT3\",\n \"color\": {\n \"rgbColor\": {\n \"red\": 0.9843137,\n \"green\": 0.7372549,\n \"blue\": 0.015686275\n }\n }\n },\n {\n \"colorType\": \"ACCENT4\",\n \"color\": {\n \"rgbColor\": {\n \"red\": 0.20392157,\n \"green\": 0.65882355,\n \"blue\": 0.3254902\n }\n }\n },\n {\n \"colorType\": \"ACCENT5\",\n \"color\": {\n \"rgbColor\": {\n \"red\": 1,\n \"green\": 0.42745098,\n \"blue\": 0.003921569\n }\n }\n },\n {\n \"colorType\": \"ACCENT6\",\n \"color\": {\n \"rgbColor\": {\n \"red\": 0.27450982,\n \"green\": 0.7411765,\n \"blue\": 0.7764706\n }\n }\n },\n {\n \"colorType\": \"LINK\",\n \"color\": {\n \"rgbColor\": {\n \"red\": 0.06666667,\n \"green\": 0.33333334,\n \"blue\": 0.8\n }\n }\n }\n ]\n }\n },\n \"sheets\": [\n {\n \"properties\": {\n \"sheetId\": 0,\n \"title\": \"Sheet1\",\n \"index\": 0,\n \"sheetType\": \"GRID\",\n \"gridProperties\": {\n \"rowCount\": 1000,\n \"columnCount\": 26\n }\n }\n }\n ],\n \"spreadsheetUrl\": \"https://docs.google.com/spreadsheets/d/1txmu0gP2qQRNaGwtUo2rW5ikjBwfS7_kNKkmzbCJ-FA/edit\"\n}\n" + } + } + }, + { + "request": { + "method": "POST", + "uri": "https://sheets.googleapis.com/v4/spreadsheets/1txmu0gP2qQRNaGwtUo2rW5ikjBwfS7_kNKkmzbCJ-FA/values/%27Sheet1%27:clear", + "body": null, + "headers": { + "User-Agent": [ + "python-requests/2.31.0" + ], + "Accept-Encoding": [ + "gzip, deflate" + ], + "Accept": [ + "*/*" + ], + "Connection": [ + "keep-alive" + ], + "x-goog-api-client": [ + "cred-type/sa" + ], + "Content-Length": [ + "0" + ], + "authorization": [ + "" + ] + } + }, + "response": { + "status": { + "code": 200, + "message": "OK" + }, + "headers": { + "X-Content-Type-Options": [ + "nosniff" + ], + "Content-Type": [ + "application/json; charset=UTF-8" + ], + "X-XSS-Protection": [ + "0" + ], + "Cache-Control": [ + "private" + ], + "Transfer-Encoding": [ + "chunked" + ], + "Vary": [ + "Origin", + "X-Origin", + "Referer" + ], + "Server": [ + "ESF" + ], + "Date": [ + "Thu, 08 Jun 2023 18:55:15 GMT" + ], + "X-Frame-Options": [ + "SAMEORIGIN" + ], + "Alt-Svc": [ + "h3=\":443\"; ma=2592000,h3-29=\":443\"; ma=2592000" + ], + "content-length": [ + "107" + ] + }, + "body": { + "string": "{\n \"spreadsheetId\": \"1txmu0gP2qQRNaGwtUo2rW5ikjBwfS7_kNKkmzbCJ-FA\",\n \"clearedRange\": \"Sheet1!A1:Z1000\"\n}\n" + } + } + }, + { + "request": { + "method": "POST", + "uri": "https://sheets.googleapis.com/v4/spreadsheets/1txmu0gP2qQRNaGwtUo2rW5ikjBwfS7_kNKkmzbCJ-FA:batchUpdate", + "body": "{\"requests\": [{\"updateSheetProperties\": {\"properties\": {\"sheetId\": 0, \"gridProperties\": {\"rowCount\": 4, \"columnCount\": 4}}, \"fields\": \"gridProperties/rowCount,gridProperties/columnCount\"}}]}", + "headers": { + "User-Agent": [ + "python-requests/2.31.0" + ], + "Accept-Encoding": [ + "gzip, deflate" + ], + "Accept": [ + "*/*" + ], + "Connection": [ + "keep-alive" + ], + "x-goog-api-client": [ + "cred-type/sa" + ], + "Content-Length": [ + "190" + ], + "Content-Type": [ + "application/json" + ], + "authorization": [ + "" + ] + } + }, + "response": { + "status": { + "code": 200, + "message": "OK" + }, + "headers": { + "X-Content-Type-Options": [ + "nosniff" + ], + "Content-Type": [ + "application/json; charset=UTF-8" + ], + "X-XSS-Protection": [ + "0" + ], + "Cache-Control": [ + "private" + ], + "Transfer-Encoding": [ + "chunked" + ], + "Vary": [ + "Origin", + "X-Origin", + "Referer" + ], + "Server": [ + "ESF" + ], + "Date": [ + "Thu, 08 Jun 2023 18:55:15 GMT" + ], + "X-Frame-Options": [ + "SAMEORIGIN" + ], + "Alt-Svc": [ + "h3=\":443\"; ma=2592000,h3-29=\":443\"; ma=2592000" + ], + "content-length": [ + "97" + ] + }, + "body": { + "string": "{\n \"spreadsheetId\": \"1txmu0gP2qQRNaGwtUo2rW5ikjBwfS7_kNKkmzbCJ-FA\",\n \"replies\": [\n {}\n ]\n}\n" + } + } + }, + { + "request": { + "method": "PUT", + "uri": "https://sheets.googleapis.com/v4/spreadsheets/1txmu0gP2qQRNaGwtUo2rW5ikjBwfS7_kNKkmzbCJ-FA/values/%27Sheet1%27%21A1%3AD4?valueInputOption=RAW", + "body": "{\"values\": [[1, \"\", \"\", \"\"], [\"\", \"\", \"title\", \"\"], [\"\", \"\", 2, \"\"], [\"num\", \"val\", \"\", 0]]}", + "headers": { + "User-Agent": [ + "python-requests/2.31.0" + ], + "Accept-Encoding": [ + "gzip, deflate" + ], + "Accept": [ + "*/*" + ], + "Connection": [ + "keep-alive" + ], + "x-goog-api-client": [ + "cred-type/sa" + ], + "Content-Length": [ + "92" + ], + "Content-Type": [ + "application/json" + ], + "authorization": [ + "" + ] + } + }, + "response": { + "status": { + "code": 200, + "message": "OK" + }, + "headers": { + "X-Content-Type-Options": [ + "nosniff" + ], + "Content-Type": [ + "application/json; charset=UTF-8" + ], + "X-XSS-Protection": [ + "0" + ], + "Cache-Control": [ + "private" + ], + "Transfer-Encoding": [ + "chunked" + ], + "Vary": [ + "Origin", + "X-Origin", + "Referer" + ], + "Server": [ + "ESF" + ], + "Date": [ + "Thu, 08 Jun 2023 18:55:15 GMT" + ], + "X-Frame-Options": [ + "SAMEORIGIN" + ], + "Alt-Svc": [ + "h3=\":443\"; ma=2592000,h3-29=\":443\"; ma=2592000" + ], + "content-length": [ + "169" + ] + }, + "body": { + "string": "{\n \"spreadsheetId\": \"1txmu0gP2qQRNaGwtUo2rW5ikjBwfS7_kNKkmzbCJ-FA\",\n \"updatedRange\": \"Sheet1!A1:D4\",\n \"updatedRows\": 4,\n \"updatedColumns\": 4,\n \"updatedCells\": 16\n}\n" + } + } + }, + { + "request": { + "method": "POST", + "uri": "https://sheets.googleapis.com/v4/spreadsheets/1txmu0gP2qQRNaGwtUo2rW5ikjBwfS7_kNKkmzbCJ-FA:batchUpdate", + "body": "{\"requests\": [{\"mergeCells\": {\"mergeType\": \"MERGE_ALL\", \"range\": {\"startRowIndex\": 0, \"endRowIndex\": 2, \"startColumnIndex\": 0, \"endColumnIndex\": 2, \"sheetId\": 0}}}]}", + "headers": { + "User-Agent": [ + "python-requests/2.31.0" + ], + "Accept-Encoding": [ + "gzip, deflate" + ], + "Accept": [ + "*/*" + ], + "Connection": [ + "keep-alive" + ], + "x-goog-api-client": [ + "cred-type/sa" + ], + "Content-Length": [ + "165" + ], + "Content-Type": [ + "application/json" + ], + "authorization": [ + "" + ] + } + }, + "response": { + "status": { + "code": 200, + "message": "OK" + }, + "headers": { + "X-Content-Type-Options": [ + "nosniff" + ], + "Content-Type": [ + "application/json; charset=UTF-8" + ], + "X-XSS-Protection": [ + "0" + ], + "Cache-Control": [ + "private" + ], + "Transfer-Encoding": [ + "chunked" + ], + "Vary": [ + "Origin", + "X-Origin", + "Referer" + ], + "Server": [ + "ESF" + ], + "Date": [ + "Thu, 08 Jun 2023 18:55:16 GMT" + ], + "X-Frame-Options": [ + "SAMEORIGIN" + ], + "Alt-Svc": [ + "h3=\":443\"; ma=2592000,h3-29=\":443\"; ma=2592000" + ], + "content-length": [ + "97" + ] + }, + "body": { + "string": "{\n \"spreadsheetId\": \"1txmu0gP2qQRNaGwtUo2rW5ikjBwfS7_kNKkmzbCJ-FA\",\n \"replies\": [\n {}\n ]\n}\n" + } + } + }, + { + "request": { + "method": "POST", + "uri": "https://sheets.googleapis.com/v4/spreadsheets/1txmu0gP2qQRNaGwtUo2rW5ikjBwfS7_kNKkmzbCJ-FA:batchUpdate", + "body": "{\"requests\": [{\"mergeCells\": {\"mergeType\": \"MERGE_ALL\", \"range\": {\"startRowIndex\": 1, \"endRowIndex\": 2, \"startColumnIndex\": 2, \"endColumnIndex\": 4, \"sheetId\": 0}}}]}", + "headers": { + "User-Agent": [ + "python-requests/2.31.0" + ], + "Accept-Encoding": [ + "gzip, deflate" + ], + "Accept": [ + "*/*" + ], + "Connection": [ + "keep-alive" + ], + "x-goog-api-client": [ + "cred-type/sa" + ], + "Content-Length": [ + "165" + ], + "Content-Type": [ + "application/json" + ], + "authorization": [ + "" + ] + } + }, + "response": { + "status": { + "code": 200, + "message": "OK" + }, + "headers": { + "X-Content-Type-Options": [ + "nosniff" + ], + "Content-Type": [ + "application/json; charset=UTF-8" + ], + "X-XSS-Protection": [ + "0" + ], + "Cache-Control": [ + "private" + ], + "Transfer-Encoding": [ + "chunked" + ], + "Vary": [ + "Origin", + "X-Origin", + "Referer" + ], + "Server": [ + "ESF" + ], + "Date": [ + "Thu, 08 Jun 2023 18:55:16 GMT" + ], + "X-Frame-Options": [ + "SAMEORIGIN" + ], + "Alt-Svc": [ + "h3=\":443\"; ma=2592000,h3-29=\":443\"; ma=2592000" + ], + "content-length": [ + "97" + ] + }, + "body": { + "string": "{\n \"spreadsheetId\": \"1txmu0gP2qQRNaGwtUo2rW5ikjBwfS7_kNKkmzbCJ-FA\",\n \"replies\": [\n {}\n ]\n}\n" + } + } + }, + { + "request": { + "method": "POST", + "uri": "https://sheets.googleapis.com/v4/spreadsheets/1txmu0gP2qQRNaGwtUo2rW5ikjBwfS7_kNKkmzbCJ-FA:batchUpdate", + "body": "{\"requests\": [{\"mergeCells\": {\"mergeType\": \"MERGE_ALL\", \"range\": {\"startRowIndex\": 2, \"endRowIndex\": 4, \"startColumnIndex\": 2, \"endColumnIndex\": 3, \"sheetId\": 0}}}]}", + "headers": { + "User-Agent": [ + "python-requests/2.31.0" + ], + "Accept-Encoding": [ + "gzip, deflate" + ], + "Accept": [ + "*/*" + ], + "Connection": [ + "keep-alive" + ], + "x-goog-api-client": [ + "cred-type/sa" + ], + "Content-Length": [ + "165" + ], + "Content-Type": [ + "application/json" + ], + "authorization": [ + "" + ] + } + }, + "response": { + "status": { + "code": 200, + "message": "OK" + }, + "headers": { + "X-Content-Type-Options": [ + "nosniff" + ], + "Content-Type": [ + "application/json; charset=UTF-8" + ], + "X-XSS-Protection": [ + "0" + ], + "Cache-Control": [ + "private" + ], + "Transfer-Encoding": [ + "chunked" + ], + "Vary": [ + "Origin", + "X-Origin", + "Referer" + ], + "Server": [ + "ESF" + ], + "Date": [ + "Thu, 08 Jun 2023 18:55:16 GMT" + ], + "X-Frame-Options": [ + "SAMEORIGIN" + ], + "Alt-Svc": [ + "h3=\":443\"; ma=2592000,h3-29=\":443\"; ma=2592000" + ], + "content-length": [ + "97" + ] + }, + "body": { + "string": "{\n \"spreadsheetId\": \"1txmu0gP2qQRNaGwtUo2rW5ikjBwfS7_kNKkmzbCJ-FA\",\n \"replies\": [\n {}\n ]\n}\n" + } + } + }, + { + "request": { + "method": "GET", + "uri": "https://sheets.googleapis.com/v4/spreadsheets/1txmu0gP2qQRNaGwtUo2rW5ikjBwfS7_kNKkmzbCJ-FA/values/%27Sheet1%27", + "body": null, + "headers": { + "User-Agent": [ + "python-requests/2.31.0" + ], + "Accept-Encoding": [ + "gzip, deflate" + ], + "Accept": [ + "*/*" + ], + "Connection": [ + "keep-alive" + ], + "x-goog-api-client": [ + "cred-type/sa" + ], + "authorization": [ + "" + ] + } + }, + "response": { + "status": { + "code": 200, + "message": "OK" + }, + "headers": { + "X-Content-Type-Options": [ + "nosniff" + ], + "Content-Type": [ + "application/json; charset=UTF-8" + ], + "X-XSS-Protection": [ + "0" + ], + "Cache-Control": [ + "private" + ], + "Transfer-Encoding": [ + "chunked" + ], + "Vary": [ + "Origin", + "X-Origin", + "Referer" + ], + "Server": [ + "ESF" + ], + "Date": [ + "Thu, 08 Jun 2023 18:55:16 GMT" + ], + "X-Frame-Options": [ + "SAMEORIGIN" + ], + "Alt-Svc": [ + "h3=\":443\"; ma=2592000,h3-29=\":443\"; ma=2592000" + ], + "content-length": [ + "248" + ] + }, + "body": { + "string": "{\n \"range\": \"Sheet1!A1:D4\",\n \"majorDimension\": \"ROWS\",\n \"values\": [\n [\n \"1\"\n ],\n [\n \"\",\n \"\",\n \"title\"\n ],\n [\n \"\",\n \"\",\n \"2\"\n ],\n [\n \"num\",\n \"val\",\n \"\",\n \"0\"\n ]\n ]\n}\n" + } + } + }, + { + "request": { + "method": "GET", + "uri": "https://sheets.googleapis.com/v4/spreadsheets/1txmu0gP2qQRNaGwtUo2rW5ikjBwfS7_kNKkmzbCJ-FA/values/%27Sheet1%27", + "body": null, + "headers": { + "User-Agent": [ + "python-requests/2.31.0" + ], + "Accept-Encoding": [ + "gzip, deflate" + ], + "Accept": [ + "*/*" + ], + "Connection": [ + "keep-alive" + ], + "x-goog-api-client": [ + "cred-type/sa" + ], + "authorization": [ + "" + ] + } + }, + "response": { + "status": { + "code": 200, + "message": "OK" + }, + "headers": { + "X-Content-Type-Options": [ + "nosniff" + ], + "Content-Type": [ + "application/json; charset=UTF-8" + ], + "X-XSS-Protection": [ + "0" + ], + "Cache-Control": [ + "private" + ], + "Transfer-Encoding": [ + "chunked" + ], + "Vary": [ + "Origin", + "X-Origin", + "Referer" + ], + "Server": [ + "ESF" + ], + "Date": [ + "Thu, 08 Jun 2023 18:55:16 GMT" + ], + "X-Frame-Options": [ + "SAMEORIGIN" + ], + "Alt-Svc": [ + "h3=\":443\"; ma=2592000,h3-29=\":443\"; ma=2592000" + ], + "content-length": [ + "248" + ] + }, + "body": { + "string": "{\n \"range\": \"Sheet1!A1:D4\",\n \"majorDimension\": \"ROWS\",\n \"values\": [\n [\n \"1\"\n ],\n [\n \"\",\n \"\",\n \"title\"\n ],\n [\n \"\",\n \"\",\n \"2\"\n ],\n [\n \"num\",\n \"val\",\n \"\",\n \"0\"\n ]\n ]\n}\n" + } + } + }, + { + "request": { + "method": "GET", + "uri": "https://sheets.googleapis.com/v4/spreadsheets/1txmu0gP2qQRNaGwtUo2rW5ikjBwfS7_kNKkmzbCJ-FA?includeGridData=false", + "body": null, + "headers": { + "User-Agent": [ + "python-requests/2.31.0" + ], + "Accept-Encoding": [ + "gzip, deflate" + ], + "Accept": [ + "*/*" + ], + "Connection": [ + "keep-alive" + ], + "x-goog-api-client": [ + "cred-type/sa" + ], + "authorization": [ + "" + ] + } + }, + "response": { + "status": { + "code": 200, + "message": "OK" + }, + "headers": { + "X-Content-Type-Options": [ + "nosniff" + ], + "Content-Type": [ + "application/json; charset=UTF-8" + ], + "X-XSS-Protection": [ + "0" + ], + "Cache-Control": [ + "private" + ], + "Transfer-Encoding": [ + "chunked" + ], + "Vary": [ + "Origin", + "X-Origin", + "Referer" + ], + "Server": [ + "ESF" + ], + "Date": [ + "Thu, 08 Jun 2023 18:55:17 GMT" + ], + "X-Frame-Options": [ + "SAMEORIGIN" + ], + "Alt-Svc": [ + "h3=\":443\"; ma=2592000,h3-29=\":443\"; ma=2592000" + ], + "content-length": [ + "3805" + ] + }, + "body": { + "string": "{\n \"spreadsheetId\": \"1txmu0gP2qQRNaGwtUo2rW5ikjBwfS7_kNKkmzbCJ-FA\",\n \"properties\": {\n \"title\": \"Test WorksheetTest test_get_values_and_combine_merged_cells\",\n \"locale\": \"en_US\",\n \"autoRecalc\": \"ON_CHANGE\",\n \"timeZone\": \"Etc/GMT\",\n \"defaultFormat\": {\n \"backgroundColor\": {\n \"red\": 1,\n \"green\": 1,\n \"blue\": 1\n },\n \"padding\": {\n \"top\": 2,\n \"right\": 3,\n \"bottom\": 2,\n \"left\": 3\n },\n \"verticalAlignment\": \"BOTTOM\",\n \"wrapStrategy\": \"OVERFLOW_CELL\",\n \"textFormat\": {\n \"foregroundColor\": {},\n \"fontFamily\": \"arial,sans,sans-serif\",\n \"fontSize\": 10,\n \"bold\": false,\n \"italic\": false,\n \"strikethrough\": false,\n \"underline\": false,\n \"foregroundColorStyle\": {\n \"rgbColor\": {}\n }\n },\n \"backgroundColorStyle\": {\n \"rgbColor\": {\n \"red\": 1,\n \"green\": 1,\n \"blue\": 1\n }\n }\n },\n \"spreadsheetTheme\": {\n \"primaryFontFamily\": \"Arial\",\n \"themeColors\": [\n {\n \"colorType\": \"TEXT\",\n \"color\": {\n \"rgbColor\": {}\n }\n },\n {\n \"colorType\": \"BACKGROUND\",\n \"color\": {\n \"rgbColor\": {\n \"red\": 1,\n \"green\": 1,\n \"blue\": 1\n }\n }\n },\n {\n \"colorType\": \"ACCENT1\",\n \"color\": {\n \"rgbColor\": {\n \"red\": 0.25882354,\n \"green\": 0.52156866,\n \"blue\": 0.95686275\n }\n }\n },\n {\n \"colorType\": \"ACCENT2\",\n \"color\": {\n \"rgbColor\": {\n \"red\": 0.91764706,\n \"green\": 0.2627451,\n \"blue\": 0.20784314\n }\n }\n },\n {\n \"colorType\": \"ACCENT3\",\n \"color\": {\n \"rgbColor\": {\n \"red\": 0.9843137,\n \"green\": 0.7372549,\n \"blue\": 0.015686275\n }\n }\n },\n {\n \"colorType\": \"ACCENT4\",\n \"color\": {\n \"rgbColor\": {\n \"red\": 0.20392157,\n \"green\": 0.65882355,\n \"blue\": 0.3254902\n }\n }\n },\n {\n \"colorType\": \"ACCENT5\",\n \"color\": {\n \"rgbColor\": {\n \"red\": 1,\n \"green\": 0.42745098,\n \"blue\": 0.003921569\n }\n }\n },\n {\n \"colorType\": \"ACCENT6\",\n \"color\": {\n \"rgbColor\": {\n \"red\": 0.27450982,\n \"green\": 0.7411765,\n \"blue\": 0.7764706\n }\n }\n },\n {\n \"colorType\": \"LINK\",\n \"color\": {\n \"rgbColor\": {\n \"red\": 0.06666667,\n \"green\": 0.33333334,\n \"blue\": 0.8\n }\n }\n }\n ]\n }\n },\n \"sheets\": [\n {\n \"properties\": {\n \"sheetId\": 0,\n \"title\": \"Sheet1\",\n \"index\": 0,\n \"sheetType\": \"GRID\",\n \"gridProperties\": {\n \"rowCount\": 4,\n \"columnCount\": 4\n }\n },\n \"merges\": [\n {\n \"startRowIndex\": 0,\n \"endRowIndex\": 2,\n \"startColumnIndex\": 0,\n \"endColumnIndex\": 2\n },\n {\n \"startRowIndex\": 1,\n \"endRowIndex\": 2,\n \"startColumnIndex\": 2,\n \"endColumnIndex\": 4\n },\n {\n \"startRowIndex\": 2,\n \"endRowIndex\": 4,\n \"startColumnIndex\": 2,\n \"endColumnIndex\": 3\n }\n ]\n }\n ],\n \"spreadsheetUrl\": \"https://docs.google.com/spreadsheets/d/1txmu0gP2qQRNaGwtUo2rW5ikjBwfS7_kNKkmzbCJ-FA/edit\"\n}\n" + } + } + }, + { + "request": { + "method": "DELETE", + "uri": "https://www.googleapis.com/drive/v3/files/1txmu0gP2qQRNaGwtUo2rW5ikjBwfS7_kNKkmzbCJ-FA?supportsAllDrives=True", + "body": null, + "headers": { + "User-Agent": [ + "python-requests/2.31.0" + ], + "Accept-Encoding": [ + "gzip, deflate" + ], + "Accept": [ + "*/*" + ], + "Connection": [ + "keep-alive" + ], + "x-goog-api-client": [ + "cred-type/sa" + ], + "Content-Length": [ + "0" + ], + "authorization": [ + "" + ] + } + }, + "response": { + "status": { + "code": 204, + "message": "No Content" + }, + "headers": { + "Pragma": [ + "no-cache" + ], + "X-Content-Type-Options": [ + "nosniff" + ], + "Content-Type": [ + "text/html" + ], + "X-XSS-Protection": [ + "0" + ], + "Cache-Control": [ + "no-cache, no-store, max-age=0, must-revalidate" + ], + "Vary": [ + "Origin, X-Origin" + ], + "Server": [ + "ESF" + ], + "Date": [ + "Thu, 08 Jun 2023 18:55:17 GMT" + ], + "Expires": [ + "Mon, 01 Jan 1990 00:00:00 GMT" + ], + "Content-Length": [ + "0" + ], + "X-Frame-Options": [ + "SAMEORIGIN" + ], + "Alt-Svc": [ + "h3=\":443\"; ma=2592000,h3-29=\":443\"; ma=2592000" + ] + }, + "body": { + "string": "" + } + } + } + ] +} diff --git a/tests/utils_test.py b/tests/utils_test.py index 0f1508caf..0d193185d 100644 --- a/tests/utils_test.py +++ b/tests/utils_test.py @@ -218,3 +218,44 @@ def test_column_letter_to_index(self): label, expected ), ) + + def test_combine_merge_values(self): + sheet_data = [ + [1, None, None, None], + [None, None, "title", None], + [None, None, 2, None], + ["num", "val", None, 0], + ] + sheet_metadata = { + "properties": {"sheetId": 0}, + "merges": [ + { + "startRowIndex": 0, + "endRowIndex": 2, + "startColumnIndex": 0, + "endColumnIndex": 2, + }, + { + "startRowIndex": 1, + "endRowIndex": 2, + "startColumnIndex": 2, + "endColumnIndex": 4, + }, + { + "startRowIndex": 2, + "endRowIndex": 4, + "startColumnIndex": 2, + "endColumnIndex": 3, + }, + ], + } + expected_combine = [ + [1, 1, None, None], + [1, 1, "title", "title"], + [None, None, 2, None], + ["num", "val", 2, 0], + ] + + actual_combine = utils.combined_merge_values(sheet_metadata, sheet_data) + + self.assertEqual(actual_combine, expected_combine) diff --git a/tests/worksheet_test.py b/tests/worksheet_test.py index b1664bf70..ae56b1482 100644 --- a/tests/worksheet_test.py +++ b/tests/worksheet_test.py @@ -90,6 +90,35 @@ def test_range_get_all_values(self): self.assertSequenceEqual(tuples1, tuples2) + @pytest.mark.vcr() + def test_get_values_and_combine_merged_cells(self): + self.sheet.resize(4, 4) + sheet_data = [ + ["1", "", "", ""], + ["", "", "title", ""], + ["", "", "2", ""], + ["num", "val", "", "0"], + ] + + self.sheet.update("A1:D4", sheet_data) + + self.sheet.merge_cells("A1:B2") + self.sheet.merge_cells("C2:D2") + self.sheet.merge_cells("C3:C4") + + expected_merge = [ + ["1", "1", "", ""], + ["1", "1", "title", "title"], + ["", "", "2", ""], + ["num", "val", "2", "0"], + ] + + values = self.sheet.get_values() + values_with_merged = self.sheet.get_values(combine_merged_cells=True) + + self.assertEqual(values, sheet_data) + self.assertEqual(values_with_merged, expected_merge) + @pytest.mark.vcr() def test_update_acell(self): sg = self._sequence_generator()