From 3f5e483f66823aefc07a4c66b520552354c8d766 Mon Sep 17 00:00:00 2001 From: Alexandre Lavigne Date: Thu, 18 Jan 2024 22:31:35 +0100 Subject: [PATCH 1/4] improve docstring - add small example --- gspread/utils.py | 29 +++++++++++++++++++++++++++++ gspread/worksheet.py | 9 +++++++++ 2 files changed, 38 insertions(+) diff --git a/gspread/utils.py b/gspread/utils.py index 880991c92..a22b232d2 100644 --- a/gspread/utils.py +++ b/gspread/utils.py @@ -878,6 +878,35 @@ def get_a1_from_absolute_range(range_name: str) -> str: return range_name +def to_records( + headers: Iterable[Any] = [], values: Iterable[Iterable[Any]] = [[]] +) -> List[Dict[str, Union[str, int, float]]]: + """Builds the list of dictionaries, all of them have the headers sequence as keys set, + each key is associated to the corresponding value for the same index in each list from + the matrix ``values``. + There are as many dictionaries as they are entry in the list of given values. + + :param list: headers the key set for all dictionaries + :param list: values a matrix of values + + Examples:: + + >>> to_records(["name", "City"], [["Spiderman", "NY"], ["Batman", "Gotham"]]) + [ + { + "Name": "Spiderman", + "City": "NY", + }, + { + "Name": "Batman", + "City": "Gotham", + }, + ] + """ + + return [dict(zip(headers, row)) for row in values] + + # SHOULD NOT BE NEEDED UNTIL NEXT MAJOR VERSION # def deprecation_warning(version: str, msg: str) -> None: # """Emit a deprecation warning. diff --git a/gspread/worksheet.py b/gspread/worksheet.py index 8b4f551fd..2ab82cc61 100644 --- a/gspread/worksheet.py +++ b/gspread/worksheet.py @@ -462,6 +462,15 @@ def get_all_records( dictionaries holding the contents of subsequent rows of cells as values. + This method uses the function :func:`gspread.utils.to_records` to build the resulting + records. It mainly wraps around the function and handle the simplest use case + using a header row (default = 1) and the the reste of the entire sheet. + + .. note:: + + for any particular use-case, please get your dataset, your headers + then use the function :func:`gspread.utils.to_records` to build the records. + Cell values are numericised (strings that can be read as ints or floats are converted), unless specified in numericise_ignore From 10755726080c1f4bd1cdb6c49ae78348ec572a53 Mon Sep 17 00:00:00 2001 From: Alexandre Lavigne Date: Fri, 19 Jan 2024 21:47:38 +0100 Subject: [PATCH 2/4] Add migration guide for method `get_all_records` Add the new section in the v6 migration guide about the method `Worksheet.get_all_records` to allow users to migrate from previous method signature to the new one. closes #1376 Signed-off-by: Alexandre Lavigne --- README.md | 35 +++++++++++++++++++++++++++++++++++ 1 file changed, 35 insertions(+) diff --git a/README.md b/README.md index c84e66086..5ca112a29 100644 --- a/README.md +++ b/README.md @@ -88,6 +88,41 @@ file.sheet1.update_tab_color(tab_color) + age = spreadsheet.get_lastUpdateTime() ``` +### Removed method `Worksheet.get_records` + +The method `Worksheet.get_records()` has been removed. +The function `utils.to_records` has been added. + +It takes hearder row, a matrix values and build the records from them. + +### Simplify method `Worksheet.get_all_records` + +v6 simplified the method `Worksheet.get_all_records`, it does not accept a bounded ranges of rows for +the values to fill the records. The below arguments have been remove from the method signature: + +- first_index +- last_index + +It now uses the function `utils.to_records`. + +Update the method signature as follow: + +```diff + get_all_records( + empty2zero=False, + head=1, +- first_index=None, +- last_index=None, + default_blank="", + allow_underscores_in_numeric_literals=False, + numericise_ignore=[], + value_render_option=None, + expected_headers=None, + ) +``` + +In order to achieve the previous behavior one case use the new function `utils.to_records` by manually providing the headers and the values. + ### Silence warnings In version 5 there are many warnings to mark deprecated feature/functions/methods. From de307cbbd56eed7400cf2845d1103ddd76b6c20b Mon Sep 17 00:00:00 2001 From: Alexandre Lavigne Date: Mon, 15 Jan 2024 23:38:31 +0100 Subject: [PATCH 3/4] Add util function `to_record` to build records Add a new util function that helps building record values. Records are list of dictionaries, each dictionary has the same set of keys, they are set from the given list of headers. Each key is associated to a value from the given matrix of values. They are as many dictionaries as they are lines in the matrix. Each line in the matrix will be associated to a dictionary, each key is associated to a value from that line in the given order. Example: Headers: A B C Values: 1 2 3 7 8 9 Result: [ { A: 1, B: 2, C: 3}, {A: 7, B: 8, C: 9}] closes #1367 Signed-off-by: Alexandre Lavigne --- gspread/worksheet.py | 9 ++++----- tests/utils_test.py | 38 ++++++++++++++++++++++++++++++++++++++ 2 files changed, 42 insertions(+), 5 deletions(-) diff --git a/gspread/worksheet.py b/gspread/worksheet.py index 2ab82cc61..a6d6bc192 100644 --- a/gspread/worksheet.py +++ b/gspread/worksheet.py @@ -55,6 +55,7 @@ is_full_a1_notation, numericise_all, rowcol_to_a1, + to_records, ) CellFormat = TypedDict( @@ -507,10 +508,10 @@ def get_all_records( # Read all rows from the sheet >>> worksheet.get_all_records() - { + [ {"A1": "A6", "B2": "B7", "C3": "C8"}, {"A1": "A11", "B2": "B12", "C3": "C13"} - } + ] """ entire_sheet = self.get( value_render_option=value_render_option, @@ -560,9 +561,7 @@ def get_all_records( for row in values ] - formatted_records = [dict(zip(keys, row)) for row in values] - - return formatted_records + return to_records(keys, values) def get_all_cells(self) -> List[Cell]: """Returns a list of all `Cell` of the current sheet.""" diff --git a/tests/utils_test.py b/tests/utils_test.py index 14144fa6f..187e74eb0 100644 --- a/tests/utils_test.py +++ b/tests/utils_test.py @@ -465,3 +465,41 @@ def test_get_a1_from_absolute_range(self): self.assertEqual(utils.get_a1_from_absolute_range("A1:B2"), "A1:B2") self.assertEqual(utils.get_a1_from_absolute_range("A1:B"), "A1:B") self.assertEqual(utils.get_a1_from_absolute_range("2"), "2") + + def test_to_records_empty_args(self): + """Test to_records with empty args""" + + self.assertListEqual(utils.to_records([], []), []) + self.assertListEqual(utils.to_records([], [[]]), [{}]) + self.assertListEqual(utils.to_records(["a1", "b2"], []), []) + self.assertListEqual(utils.to_records(["a1", "b2"], [[]]), [{}]) + self.assertListEqual(utils.to_records([], [["a1"]]), [{}]) + self.assertListEqual(utils.to_records([], [["a1"], ["a2"]]), [{}, {}]) + self.assertListEqual(utils.to_records([], [[], ["a2"]]), [{}, {}]) + + def test_to_records(self): + """Test to_records with values""" + + headers = ["HA", "HB", "HC"] + values = [["A2", "B2", "C2"], ["A3", "B3"], ["", "B4", "C4"]] + + records = utils.to_records(headers, values) + + self.assertEqual(len(values), len(records)) + + for i in range(len(records)): + record = records[i] + keys = record.keys() + + # Some rows have shorter values ("A3", "B3") + # so the list of keys is smaller + # but never bigger than the list of headers + self.assertLessEqual(len(keys), len(headers)) + + # Each resulting key must be part of the given header + for key in keys: + self.assertIn(key, headers) + + # given key are unordered + # but they must match a value from the given input values + self.assertIn(record[key], values[i]) From 8465fce03037e14a0ab50a50fe3e7ad38643728f Mon Sep 17 00:00:00 2001 From: alifeee Date: Sat, 20 Jan 2024 15:40:40 +0000 Subject: [PATCH 4/4] briefen migration guide readme --- README.md | 39 +++++++++------------------------------ 1 file changed, 9 insertions(+), 30 deletions(-) diff --git a/README.md b/README.md index 5ca112a29..caf3b7ac9 100644 --- a/README.md +++ b/README.md @@ -88,41 +88,20 @@ file.sheet1.update_tab_color(tab_color) + age = spreadsheet.get_lastUpdateTime() ``` -### Removed method `Worksheet.get_records` +### Replace method `Worksheet.get_records` -The method `Worksheet.get_records()` has been removed. -The function `utils.to_records` has been added. - -It takes hearder row, a matrix values and build the records from them. - -### Simplify method `Worksheet.get_all_records` - -v6 simplified the method `Worksheet.get_all_records`, it does not accept a bounded ranges of rows for -the values to fill the records. The below arguments have been remove from the method signature: - -- first_index -- last_index - -It now uses the function `utils.to_records`. - -Update the method signature as follow: +In v6 you can now only get *all* sheet records, using `Worksheet.get_all_records()`. The method `Worksheet.get_records()` has been removed. You can get some records using your own fetches and combine them with `gspread.utils.to_records()`. ```diff - get_all_records( - empty2zero=False, - head=1, -- first_index=None, -- last_index=None, - default_blank="", - allow_underscores_in_numeric_literals=False, - numericise_ignore=[], - value_render_option=None, - expected_headers=None, - ) ++ from gspread import utils + all_records = spreadsheet.get_all_records(head=1) +- some_records = spreadsheet.get_all_records(head=1, first_index=6, last_index=9) +- some_records = spreadsheet.get_records(head=1, first_index=6, last_index=9) ++ header = spreadsheet.get("1:1")[0] ++ cells = spreadsheet.get("6:9") ++ some_records = utils.to_records(header, cells) ``` -In order to achieve the previous behavior one case use the new function `utils.to_records` by manually providing the headers and the values. - ### Silence warnings In version 5 there are many warnings to mark deprecated feature/functions/methods.