diff --git a/README.md b/README.md index e2d8df6..5293176 100644 --- a/README.md +++ b/README.md @@ -178,17 +178,19 @@ exported (which can lead to data losses). #### How to change the size of the spreadsheet You can only expand the size of the spreadsheet (it's because of the -built-in behaviour of languages). We, however, strongly recommend not to -do so. +built-in behaviour of language construction). We, however, strongly recommend +not to do so. Simplified logic looks like: ``` -sheet.expand_size( - sheet.cell_indices.expand_size( - number_of_new_rows, number_of_new_columns, [new_rows_columns] - ) +# Append 7 rows and 8 columns to existing sheet: +sheet.expand( + 7, 8, + { + "native": ([...], [...]) # Fill 8 new values for rows, columns here + } ) ``` -Parameters of the `cell_indices.expand_size` method are of the same -logic as the parameters of `Spreadsheet.create_new_sheet`. +Parameters of the `Spreadsheet.expand` method are of the same +logic and order as the parameters of `Spreadsheet.create_new_sheet`. ### Shape of the Spreadsheet object If you want to know what is the actual size of the spreadsheet, you can @@ -362,6 +364,31 @@ sheet.iloc[i,j] = sheet.fn.conditional( ) ``` +### Raw statement +The raw statement represents the extreme way how to set-up value and +computation string of the cell. It should be used only to circumvent +issues with missing or defective functionality. + +The raw statement is accessible using `fn` property of the Spreadsheet class +object. + +The raw statement should never be used unless you really have to. + +#### Example of raw statement +Consider that you need to compute an arccosine value of some cell: +``` +sheet.iloc[i,j] = sheet.fn.raw( + # Value that should be used as the result (as a Cell instance): + sheet.fn.const(numpy.arccos(0.7)), + # Definition of words in each language: + { + 'python_numpy': "numpy.arccos(0.7)", + 'excel': "ACOS(0.7)" + # Potentialy some other languages, like 'native', etc. + } +) +``` + ### Offset function The offset function represents the possibility of reading the value that is shifted by some number rows left, and some number of columns @@ -436,6 +463,10 @@ is the instance of the Cell class): Usage: `sheet.iloc[i,j] = ~OPERAND`. _Also available in the `fn` property of the `sheet` object. Usage: `sheet.iloc[i,j] = sheet.fn.neg(OPERAND)`_ +9. **Signum function**: returns the signum of the input value. + For example sign(-4.5) = -1, sign(5) = 1, sign(0) = 0. + Available in the `fn` property of the `sheet` object. + Usage: `sheet.iloc[i,j] = sheet.fn.sign(OPERAND)` All unary operators are defined in the `fn` property of the Spreadsheet object (together with brackets, that works exactly the same - see bellow). @@ -608,7 +639,8 @@ sheet.to_excel( "name": "Name", "value": "Value", "description": "Description" - }) + }), + values_only: bool = False ) ``` The only required argument is the path to the destination file (positional @@ -628,6 +660,8 @@ to set them up (directly from the sheet). * `variables_sheet_header (Dict[str, str])`: Define the labels (header) for the sheet with variables (first row in the sheet). Dictionary should look like: `{"name": "Name", "value": "Value", "description": "Description"}`. +* `values_only (bool)`: If true, only values (and not formulas) are +exported. ##### Setting the format/style for Excel cells There is a possibility to set the style/format of each cell in the grid @@ -907,6 +941,7 @@ Output of the JSON format It can be done using the interface: ``` sheet.to_excel(*, + language: Optional[str] = None, spaces_replacement: str = ' ', top_right_corner_text: str = "Sheet", sep: str = ',', @@ -917,6 +952,8 @@ sheet.to_excel(*, ``` Parameters are (all optional and key-value only): +* `language (Optional[str])`: If set-up, export the word in this +language in each cell instead of values. * `spaces_replacement (str)`: All the spaces in the rows and columns descriptions (labels) are replaced with this string. * `top_right_corner_text (str)`: Text in the top right corner. @@ -944,6 +981,7 @@ R_4,17,18,19,20 It can be done using the interface: ``` sheet.to_markdown(*, + language: Optional[str] = None, spaces_replacement: str = ' ', top_right_corner_text: str = "Sheet", na_rep: str = '', @@ -952,6 +990,8 @@ sheet.to_markdown(*, ``` Parameters are (all optional, all key-value only): +* `language (Optional[str])`: If set-up, export the word in this +language in each cell instead of values. * `spaces_replacement (str)`: All the spaces in the rows and columns descriptions (labels) are replaced with this string. * `top_right_corner_text (str)`: Text in the top right corner. diff --git a/portable_spreadsheet/cell.py b/portable_spreadsheet/cell.py index 1b31aa6..874713a 100644 --- a/portable_spreadsheet/cell.py +++ b/portable_spreadsheet/cell.py @@ -682,6 +682,24 @@ def reference(other: 'Cell', /) -> 'Cell': # noqa E225 cell_type=CellType.computational ) + @staticmethod + def raw(other: 'Cell', words: Dict[str, str], /) -> 'Cell': # noqa: E225 + """Add the raw statement and use the value of input cell. + + Args: + other (Cell): Input cell that defines value and type of output. + words (Dict[str, str]): Word for each language (language is a key, + word is a value) + + Returns: + Cell: Expression with defined word + """ + return Cell(value=other.value, + words=WordConstructor.raw(other, words), + cell_indices=other.cell_indices, + cell_type=CellType.computational + ) + @staticmethod def variable(other: 'Cell', /) -> 'Cell': # noqa E225 """The cell as a variable. @@ -828,6 +846,22 @@ def sqrt(other: 'Cell', /) -> 'Cell': # noqa E225 cell_type=CellType.computational ) + @staticmethod + def signum(other: 'Cell', /) -> 'Cell': # noqa E225 + """Signum function of the value in the cell. + + Args: + other (Cell): Argument of the signum function. + + Returns: + Cell: signum of the input numeric value + """ + return Cell(value=np.sign(other.value), + words=WordConstructor.signum(other), + cell_indices=other.cell_indices, + cell_type=CellType.computational + ) + @staticmethod def logicalNegation(other: 'Cell', /) -> 'Cell': # noqa E225 """Logical negation of the value in the cell. diff --git a/portable_spreadsheet/cell_indices.py b/portable_spreadsheet/cell_indices.py index 94b4726..a8c1f29 100644 --- a/portable_spreadsheet/cell_indices.py +++ b/portable_spreadsheet/cell_indices.py @@ -257,20 +257,27 @@ def expand_size(self, if new_rows_labels is not None: expanded.rows_labels.extend(new_rows_labels) else: - expanded.rows_labels = list( - range(expanded.number_of_rows + new_number_of_rows) - ) + expanded.rows_labels = [ + str(i) + for i in range(expanded.number_of_rows + new_number_of_rows) + ] if new_columns_labels is not None: expanded.columns_labels.extend(new_columns_labels) else: - expanded.columns_labels = list( - range(expanded.number_of_columns + new_number_of_columns) - ) + expanded.columns_labels = [ + str(i) + for i in range( + expanded.number_of_columns + new_number_of_columns + ) + ] # Or define auto generated aliases as an integer sequence from 0 if new_columns_labels is None: - expanded.columns_labels = list( - range(expanded.number_of_columns + new_number_of_columns) - ) + expanded.columns_labels = [ + str(i) + for i in range( + expanded.number_of_columns + new_number_of_columns + ) + ] # assign the help texts if expanded.rows_help_text is not None and new_number_of_rows > 0: if new_rows_help_text is None: diff --git a/portable_spreadsheet/grammars.py b/portable_spreadsheet/grammars.py index 0bb1e6c..af8dda9 100644 --- a/portable_spreadsheet/grammars.py +++ b/portable_spreadsheet/grammars.py @@ -202,6 +202,10 @@ "prefix": "SQRT(", "suffix": ")", }, + "signum": { + "prefix": "SIGN(", + "suffix": ")", + }, # LOGICAL OPERATIONS "equal-to": { "prefix": "", @@ -474,6 +478,10 @@ "prefix": "square root of ", "suffix": "", }, + "signum": { + "prefix": "signum of ", + "suffix": "", + }, # LOGICAL OPERATIONS "equal-to": { "prefix": "", @@ -746,6 +754,10 @@ "prefix": "np.sqrt(", "suffix": ")", }, + "signum": { + "prefix": "np.sign(", + "suffix": ")", + }, # LOGICAL OPERATIONS "equal-to": { "prefix": "", @@ -1088,6 +1100,12 @@ "prefix": str, "suffix": str, }, + # Signum function (-1 if value is < 0, 0 if val == 0, 1 if val > 0) + "signum": { + "prefix": str, + "suffix": str, + }, + # === LOGICAL OPERATIONS (returns true or false) === # Equal to "equal-to": { diff --git a/portable_spreadsheet/serialization.py b/portable_spreadsheet/serialization.py index 71e2a13..ae8628c 100644 --- a/portable_spreadsheet/serialization.py +++ b/portable_spreadsheet/serialization.py @@ -115,7 +115,7 @@ def log_export_subset_warning_if_needed(self): def to_excel(self, file_path: str, - /, *, # noqa E999 + /, *, # noqa: E225, E999 sheet_name: str = "Results", spaces_replacement: str = ' ', label_row_format: dict = {'bold': True}, @@ -126,7 +126,8 @@ def to_excel(self, "name": "Name", "value": "Value", "description": "Description" - }) + }), + values_only: bool = False ) -> None: """Export the values inside Spreadsheet instance to the Excel 2010 compatible .xslx file @@ -145,6 +146,8 @@ def to_excel(self, to set them up (directly from the sheet). variables_sheet_header (Dict[str, str]): Define the labels (header) for the sheet with variables (first row in the sheet). + values_only (bool): If true, only values (and not formulas) are + exported. """ # Quick sanity check if ".xlsx" not in file_path[-5:]: @@ -204,7 +207,8 @@ def to_excel(self, cell_format = workbook.add_format(cell.excel_format) else: cell_format = None - if cell.cell_type == CellType.value_only: + # Write actual data + if values_only or (cell.cell_type == CellType.value_only): # If the cell is a value only, use method 'write' worksheet.write(row_idx + offset, col_idx + offset, @@ -470,33 +474,84 @@ def to_string_of_values(self) -> str: export += ",\n" return export + "]" - def to_2d_list(self) -> List[List[object]]: + def to_2d_list(self, *, + language: Optional[str] = None, + top_right_corner_text: str = "Sheet", + skip_labels: bool = False, + na_rep: Optional[object] = None, + spaces_replacement: str = ' ',) -> List[List[object]]: """Export values 2 dimensional Python array. + Args: + language (Optional[str]): If set-up, export the word in this + language in each cell instead of values. + spaces_replacement (str): All the spaces in the rows and columns + descriptions (labels) are replaced with this string. + top_right_corner_text (str): Text in the top right corner. + na_rep (str): Replacement for the missing data (cells with value + equals to None). + skip_labels (bool): If true, first row and column with labels is + skipped + Returns: - str: Python array. + List[List[object]]: Python array. """ # Log warning if needed self.log_export_subset_warning_if_needed() export: list = [] - for row_idx in range(self.shape[0]): + for row_idx in range(-1, self.shape[0]): row: list = [] - for col_idx in range(self.shape[1]): - row.append(self._get_cell_at(row_idx, col_idx).value) + if row_idx == -1: + if skip_labels: + continue + row.append(top_right_corner_text) + # Insert labels of columns: + for col_i in range(self.shape[1]): + col = self.cell_indices.columns_labels[ + col_i + self.export_offset[1] + ] + value = col.replace(' ', spaces_replacement) + row.append(value) + else: + if not skip_labels: + # Insert labels of rows + row.append(self.cell_indices.rows_labels[ + row_idx + self.export_offset[0] + ].replace(' ', spaces_replacement)) + for col_idx in range(self.shape[1]): + # Append actual values: + cell_at_position = self._get_cell_at(row_idx, col_idx) + + if language is not None: + # Get the word of cell on current position + value_to_write = cell_at_position.parse[language] + else: + # Get the value of cell on current position + value_to_write = cell_at_position.value + if value_to_write is None: + # Replacement for None + value_to_write = na_rep + row.append(value_to_write) + export.append(row) return export def to_csv(self, *, - spaces_replacement: str = ' ', + language: Optional[str] = None, top_right_corner_text: str = "Sheet", + skip_labels: bool = False, + na_rep: Optional[object] = '', + spaces_replacement: str = ' ', + sep: str = ',', line_terminator: str = '\n', - na_rep: str = '', - skip_labels: bool = False) -> str: + ) -> str: """Export values to the string in the CSV logic Args: + language (Optional[str]): If set-up, export the word in this + language in each cell instead of values. spaces_replacement (str): All the spaces in the rows and columns descriptions (labels) are replaced with this string. top_right_corner_text (str): Text in the top right corner. @@ -509,49 +564,33 @@ def to_csv(self, *, Returns: str: CSV of the values """ - # Log warning if needed - self.log_export_subset_warning_if_needed() - + sheet_as_array = self.to_2d_list( + top_right_corner_text=top_right_corner_text, na_rep=na_rep, + skip_labels=skip_labels, spaces_replacement=spaces_replacement, + language=language + ) export = "" - for row_idx in range(-1, self.shape[0]): - if row_idx == -1: - if skip_labels: - continue - export += top_right_corner_text + sep - # Insert labels of columns: - for col_i in range(self.shape[1]): - col = self.cell_indices.columns_labels[ - col_i + self.export_offset[1] - ] - export += col.replace(' ', spaces_replacement) - if col_i < self.shape[1] - 1: - export += sep - else: - if not skip_labels: - # Insert labels of rows - export += self.cell_indices.rows_labels[ - row_idx + self.export_offset[0] - ].replace(' ', spaces_replacement) + sep - # Insert actual values in the spreadsheet - for col_idx in range(self.shape[1]): - value = self._get_cell_at(row_idx, col_idx).value - if value is None: - value = na_rep - export += str(value) - if col_idx < self.shape[1] - 1: - export += sep - if row_idx < self.shape[0] - 1: + for row_idx in range(len(sheet_as_array)): + for col_idx in range(len(sheet_as_array[row_idx])): + export += str(sheet_as_array[row_idx][col_idx]) + if col_idx < (len(sheet_as_array[row_idx]) - 1): + export += sep + if row_idx < (len(sheet_as_array) - 1): export += line_terminator return export def to_markdown(self, *, - spaces_replacement: str = ' ', + language: Optional[str] = None, top_right_corner_text: str = "Sheet", - na_rep: str = '', - skip_labels: bool = False): + skip_labels: bool = False, + na_rep: Optional[object] = '', + spaces_replacement: str = ' ' + ): """Export values to the string in the Markdown (MD) file logic Args: + language (Optional[str]): If set-up, export the word in this + language in each cell instead of values. spaces_replacement (str): All the spaces in the rows and columns descriptions (labels) are replaced with this string. top_right_corner_text (str): Text in the top right corner. @@ -562,58 +601,47 @@ def to_markdown(self, *, Returns: str: Markdown (MD) compatible table of the values """ - # Log warning if needed - self.log_export_subset_warning_if_needed() - + sheet_as_array = self.to_2d_list( + top_right_corner_text=top_right_corner_text, na_rep=na_rep, + skip_labels=skip_labels, spaces_replacement=spaces_replacement, + language=language + ) export = "" - for row_idx in range(-2, self.shape[0]): - if row_idx == -2: - if skip_labels: - export += "|" - for col_i in range(self.shape[1]): - export += "|" - export += "|\n" - continue - # Add the labels and top right corner text - export += "| " + top_right_corner_text + " |" - for col_i in range(self.shape[1]): - # Insert column labels: - col = self.cell_indices.columns_labels[ - col_i + self.export_offset[1] - ] - export += "*" + col.replace(' ', spaces_replacement) + "*" - if col_i < self.shape[1] - 1: - export += " | " - elif col_i == self.shape[1] - 1: - export += " |\n" - - elif row_idx == -1: - # Add the separator to start the table body: - export += "|----|" - for col_i in range(self.shape[1]): - export += "----|" - if col_i == self.shape[1] - 1: - export += "\n" - else: - # Insert row labels - if not skip_labels: - export += "| *" - export += self.cell_indices.rows_labels[ - row_idx + self.export_offset[0] - ].replace(' ', spaces_replacement) - export += "* " - - export += "| " + for row_idx in range(len(sheet_as_array)): + # Add values: + export += "| " + for col_idx in range(len(sheet_as_array[row_idx])): + if (row_idx == 0 or col_idx == 0) and not skip_labels: + export += '*' + export += str(sheet_as_array[row_idx][col_idx]) + if (row_idx == 0 or col_idx == 0) and not skip_labels: + export += '*' + if col_idx < (len(sheet_as_array[row_idx]) - 1): + export += " | " + else: + export += " |" + export += "\n" + # Add |---| sequence after the first line + if row_idx == 0 and not skip_labels: + for col_idx in range(len(sheet_as_array[row_idx])): + export += "|----" + if col_idx == (len(sheet_as_array[row_idx]) - 1): + export += '|' + export += "\n" + # Add first two lines if labels are skipped + if skip_labels: + # Add empty || separators for missing labels + first_line = "|" + for col_idx in range(len(sheet_as_array[0])): + first_line += "|" + first_line += "|\n" + # Add |---| sequence after the first line + for col_idx in range(len(sheet_as_array[0])): + first_line += "|----" + if col_idx == (len(sheet_as_array[0]) - 1): + first_line += '|' + export = first_line + "\n" + export - for col_idx in range(self.shape[1]): - value = self._get_cell_at(row_idx, col_idx).value - if value is None: - value = na_rep - export += str(value) - if col_idx < self.shape[1] - 1: - export += " | " - elif col_idx == self.shape[1] - 1: - export += " |\n" return export def to_numpy(self) -> numpy.ndarray: diff --git a/portable_spreadsheet/spreadsheet.py b/portable_spreadsheet/spreadsheet.py index 11d7d4e..661535a 100644 --- a/portable_spreadsheet/spreadsheet.py +++ b/portable_spreadsheet/spreadsheet.py @@ -328,8 +328,47 @@ def _set_slice(self, cell_slice: CellSlice = self._get_slice(index_integer, index_label) cell_slice.set(value) - def expand_size(self, cell_indices: CellIndices) -> None: - """Resize the spreadsheet object to the greather size + def expand(self, + new_number_of_rows: int, + new_number_of_columns: int, + new_rows_columns: Optional[T_lg_col_row] = {}, + /, *, # noqa E225 + new_rows_labels: List[str] = None, + new_columns_labels: List[str] = None, + new_rows_help_text: List[str] = None, + new_columns_help_text: List[str] = None + ): + """Expand the size of the table. + + Args: + new_number_of_rows (int): Number of rows to be added. + new_number_of_columns (int): Number of columns to be added. + new_rows_columns (T_lg_col_row): List of all row names and column + names for each language to be added. + new_rows_labels (List[str]): List of masks (aliases) for row + names to be added. + new_columns_labels (List[str]): List of masks (aliases) for + column names to be added. + new_rows_help_text (List[str]): List of help texts for each row to + be added. + new_columns_help_text (List[str]): List of help texts for each + column to be added. + """ + self.expand_using_cell_indices( + self.cell_indices.expand_size( + new_number_of_rows, + new_number_of_columns, + new_rows_columns, + + new_rows_labels=new_rows_labels, + new_columns_labels=new_columns_labels, + new_rows_help_text=new_rows_help_text, + new_columns_help_text=new_columns_help_text + ) + ) + + def expand_using_cell_indices(self, cell_indices: CellIndices) -> None: + """Resize the spreadsheet object to the greater size Args: cell_indices (CellIndices): The definition of the shape and columns diff --git a/portable_spreadsheet/spreadsheet_utils.py b/portable_spreadsheet/spreadsheet_utils.py index a06e049..39ef92a 100644 --- a/portable_spreadsheet/spreadsheet_utils.py +++ b/portable_spreadsheet/spreadsheet_utils.py @@ -96,6 +96,23 @@ def const(self, value: Number) -> Cell: return Cell(value=value, cell_indices=self.spreadsheet.cell_indices) + @staticmethod + def raw(value: Cell, words: Dict[str, str]) -> Cell: + """Add the raw statement and use the value of input cell. + + Args: + value (Cell): Input cell that defines value and type of output. + words (Dict[str, str]): Word for each language (language is a key, + word is a value) + + Warnings: + Do not use this feature unless you really have to. + + Returns: + Cell: Expression with defined word + """ + return Cell.raw(value, words) + @staticmethod def brackets(body: Cell) -> Cell: """Shortcut for adding bracket around body. @@ -192,6 +209,18 @@ def sqrt(value: Cell) -> Cell: """ return Cell.sqrt(value) + @staticmethod + def sign(value: Cell) -> Cell: + """Signum function of the input. + + Args: + value (Cell): The input value for computation. + + Returns: + Cell: Signum function value of the input value. + """ + return Cell.signum(value) + @staticmethod def neg(value: Cell) -> Cell: """Logical negation of the input. diff --git a/portable_spreadsheet/word_constructor.py b/portable_spreadsheet/word_constructor.py index 6a481ff..e86dfc9 100644 --- a/portable_spreadsheet/word_constructor.py +++ b/portable_spreadsheet/word_constructor.py @@ -506,6 +506,22 @@ def parse(cell) -> T_word: words[language] = prefix + words[language] + suffix return words + @staticmethod + def raw(cell, words: T_word, /) -> 'WordConstructor': # noqa: E225 + """Returns the raw statement string. + + Args: + cell (Cell): Some cell. + words (T_word): Custom word definition + + Returns: + WordConstructor: Defined word. + """ + instance = WordConstructor(cell_indices=cell.cell_indices) + for language in instance.languages: + instance.words[language] = words[language] + return instance + @staticmethod def empty(cell, /) -> 'WordConstructor': # noqa E225 """Returns the empty string. @@ -746,6 +762,22 @@ def sqrt(cell, /) -> 'WordConstructor': # noqa E225 suffix_path=['operations', 'sqrt', 'suffix'] ) + @staticmethod + def signum(cell, /) -> 'WordConstructor': # noqa E225 + """Add signum value definition context around the cell. + + Args: + cell (Cell): The cell around which signum context is added. + + Returns: + WordConstructor: Word with signum function context. + """ + return WordConstructor._unary_operator( + cell=cell, + prefix_path=['operations', 'signum', 'prefix'], + suffix_path=['operations', 'signum', 'suffix'] + ) + @staticmethod def logicalNegation(cell, /) -> 'WordConstructor': # noqa E225 """Add logical negation definition context around the cell. diff --git a/setup.py b/setup.py index 2918f69..2a2503c 100644 --- a/setup.py +++ b/setup.py @@ -5,7 +5,7 @@ setuptools.setup( name="portable-spreadsheet", - version="0.1.4", + version="0.1.5", author="David Salac", author_email="info@davidsalac.eu", description="Simple spreadsheet that keeps tracks of each operation in " diff --git a/tests/test_cell_word_constructor.py b/tests/test_cell_word_constructor.py index 84986d5..d40eb78 100644 --- a/tests/test_cell_word_constructor.py +++ b/tests/test_cell_word_constructor.py @@ -459,6 +459,17 @@ def test_chain_of_operation(self): result.parse) self.assertAlmostEqual(result.value, 35) + def test_raw_statement(self): + """Test raw statement""" + result = Cell.raw(self.a_operand_1, { + 'python_numpy': "Hello from Python", + 'excel': "Excel welcomes" + }) + self.assertDictEqual({ + 'python_numpy': "Hello from Python", + 'excel': "=Excel welcomes" # Always computational + }, result.parse) + self.assertAlmostEqual(self.a_operand_1.value, 7) def test_concatenate(self): """Test string concatenation.""" diff --git a/tests/test_serialization.py b/tests/test_serialization.py index 2f4d5b1..b5d3348 100644 --- a/tests/test_serialization.py +++ b/tests/test_serialization.py @@ -3,6 +3,7 @@ import tempfile import os +import copy import numpy as np @@ -92,7 +93,7 @@ def test_to_csv(self): def test_to_markdown(self): """MD (Markdown) language export""" - expected_no_skip = """| Sheet |*NL_C_0* | *NL_C_1* | *NL_C_2* | *NL_C_3* | + expected_no_skip = """| *Sheet* | *NL_C_0* | *NL_C_1* | *NL_C_2* | *NL_C_3* | |----|----|----|----|----| | *R_0* | 1 | 2 | 3 | 4 | | *R_1* | 5 | 6 | 7 | 8 | @@ -102,7 +103,7 @@ def test_to_markdown(self): """ expected_skip = """|||||| -|----|----|----|----|----| +|----|----|----|----| | 1 | 2 | 3 | 4 | | 5 | 6 | 7 | 8 | | 9 | 10 | 11 | 12 | @@ -169,11 +170,44 @@ def test_to_numpy(self): def test_to_2d_list(self): """Test the serialization to 2D list""" - computed_2d_list = self.sheet.to_2d_list() + # Check values + computed_2d_list = self.sheet.to_2d_list(skip_labels=True) self.assertTrue(isinstance(computed_2d_list, list)) self.assertTrue( np.allclose(np.array(computed_2d_list), self.inserted_rand_values) ) + # Check labels: + corner = "yMq7W0bk" + computed_2d_list = self.sheet.to_2d_list(skip_labels=False, + top_right_corner_text=corner) + self.assertTrue(isinstance(computed_2d_list, list)) + self.assertTrue(computed_2d_list[0][0], corner) + # Check row labels + for row_idx, row_label in enumerate( + self.sheet.cell_indices.rows_labels): + self.assertEqual(computed_2d_list[row_idx + 1][0], row_label) + # Check column labels + for col_idx, col_label in enumerate( + self.sheet.cell_indices.columns_labels): + self.assertEqual(computed_2d_list[0][col_idx + 1], col_label) + # Check values inside: + computed = [arr[1:] for arr in computed_2d_list[1:]] + self.assertTrue( + np.allclose(np.array(computed), self.inserted_rand_values) + ) + # Check the language export + sheet = copy.deepcopy(self.sheet) + sheet.iloc[0, 0] = sheet.iloc[0,1] * sheet.iloc[1,0] + computed_2d_list = sheet.to_2d_list(skip_labels=True, + language='excel') + # Regression test + self.assertEqual(sheet.iloc[0, 0].parse['excel'], '=C2*B3') + # Test all the values inside + for row_idx in range(sheet.shape[0]): + for col_idx in range(sheet.shape[1]): + computed = computed_2d_list[row_idx][col_idx] + expected = sheet.iloc[row_idx, col_idx].parse['excel'] + self.assertEqual(expected, computed) def test_to_string_of_values(self): """Test the serialization to 2D list""" diff --git a/tests/test_spreadsheet.py b/tests/test_spreadsheet.py index 1e909ca..964d893 100644 --- a/tests/test_spreadsheet.py +++ b/tests/test_spreadsheet.py @@ -73,7 +73,7 @@ def test_expand_sheet(self): new_rows_help_text=new_rows_help_text, new_columns_help_text=new_columns_help_text ) - old_sheet.expand_size(new_cell_idx) + old_sheet.expand_using_cell_indices(new_cell_idx) # Test the expanded sheet cell_indices: CellIndices = old_sheet.cell_indices self.assertTupleEqual(