diff --git a/README.md b/README.md index 0931941..a5d9c33 100644 --- a/README.md +++ b/README.md @@ -185,6 +185,8 @@ on the beginning of the sheet as a offset for labels. added on the beginning of the sheet as a offset for labels. 8. `warning_logger (Callable[[str], None]])`: Function that logs the warnings (or `None` if logging should be skipped). +9. `values_only (bool)`: If set to True, only values are computed and +nothing can be exported (makes script run faster) First two are the most important because they define labels for the columns and rows indices. The warnings mention above occurs when the slices are diff --git a/portable_spreadsheet/__init__.py b/portable_spreadsheet/__init__.py index 24f61e8..db7ccdb 100644 --- a/portable_spreadsheet/__init__.py +++ b/portable_spreadsheet/__init__.py @@ -7,5 +7,5 @@ from .grammar_utils import GrammarUtils # noqa from .skipped_label import SkippedLabel # noqa -__version__ = "2.1.8" +__version__ = "2.1.9" __status__ = "Production" diff --git a/portable_spreadsheet/cell.py b/portable_spreadsheet/cell.py index 9bd2478..f497177 100644 --- a/portable_spreadsheet/cell.py +++ b/portable_spreadsheet/cell.py @@ -42,6 +42,8 @@ class Cell(object): variable defined by formulas. excel_data_validation (dict): Define data validation restriction based on xlsxwriter 'data_validation' possibilities. + compute_only_values (bool): If true, only values are computed and + no word is constructed. """ def __init__(self, row: Optional[int] = None, @@ -84,12 +86,16 @@ def __init__(self, self._excel_format: dict = {} self._description: Optional[str] = None self._excel_row_position: Optional[int] = None - - if words is not None: - self._constructing_words: WordConstructor = words + self.compute_only_values: bool = self.cell_indices.values_only + + if not self.compute_only_values: + if words is not None: + self._constructing_words: WordConstructor = words + else: + self._constructing_words: WordConstructor = \ + WordConstructor.init_from_new_cell(self) else: - self._constructing_words: WordConstructor = \ - WordConstructor.init_from_new_cell(self) + self._constructing_words: WordConstructor = None self._variable_words: WordConstructor = None self.excel_data_validation: dict = None @@ -290,6 +296,14 @@ def _compute_value(function: callable, # Maximally generic here is OK return CellValueError() + @staticmethod + def _construct_word(source: 'Cell', + function: callable, + *args) -> Optional[WordConstructor]: + if not source.compute_only_values: + return function(*args) + else: + return None # ===================================== # === BINARY OPERATORS: === @@ -304,7 +318,11 @@ def add(self, other: 'Cell', /) -> 'Cell': # noqa: E225 """ return Cell(value=self._compute_value(lambda a, b: a + b, a=self.value, b=other.value), - words=WordConstructor.add(self, other), + words=self._construct_word( + self, + WordConstructor.add, + self, other + ), cell_indices=other.cell_indices, cell_type=CellType.computational ) @@ -320,7 +338,11 @@ def subtract(self, other: 'Cell', /) -> 'Cell': # noqa: E225 """ return Cell(value=self._compute_value(lambda a, b: a - b, a=self.value, b=other.value), - words=WordConstructor.subtract(self, other), + words=self._construct_word( + self, + WordConstructor.subtract, + self, other + ), cell_indices=other.cell_indices, cell_type=CellType.computational ) @@ -336,7 +358,11 @@ def multiply(self, other: 'Cell', /) -> 'Cell': # noqa: E225 """ return Cell(value=self._compute_value(lambda a, b: a * b, a=self.value, b=other.value), - words=WordConstructor.multiply(self, other), + words=self._construct_word( + self, + WordConstructor.multiply, + self, other + ), cell_indices=other.cell_indices, cell_type=CellType.computational ) @@ -352,7 +378,11 @@ def divide(self, other: 'Cell', /) -> 'Cell': # noqa: E225 """ return Cell(value=self._compute_value(lambda a, b: a / b, a=self.value, b=other.value), - words=WordConstructor.divide(self, other), + words=self._construct_word( + self, + WordConstructor.divide, + self, other + ), cell_indices=other.cell_indices, cell_type=CellType.computational ) @@ -368,7 +398,11 @@ def modulo(self, other: 'Cell', /) -> 'Cell': # noqa: E225 """ return Cell(value=self._compute_value(lambda a, b: a % b, a=self.value, b=other.value), - words=WordConstructor.modulo(self, other), + words=self._construct_word( + self, + WordConstructor.modulo, + self, other + ), cell_indices=other.cell_indices, cell_type=CellType.computational ) @@ -384,7 +418,11 @@ def power(self, other: 'Cell', /) -> 'Cell': # noqa: E225 """ return Cell(value=self._compute_value(lambda a, b: a ** b, a=self.value, b=other.value), - words=WordConstructor.power(self, other), + words=self._construct_word( + self, + WordConstructor.power, + self, other + ), cell_indices=other.cell_indices, cell_type=CellType.computational ) @@ -400,7 +438,11 @@ def equalTo(self, other: 'Cell', /) -> 'Cell': # noqa: E225 """ return Cell(value=self._compute_value(lambda a, b: a == b, a=self.value, b=other.value), - words=WordConstructor.equalTo(self, other), + words=self._construct_word( + self, + WordConstructor.equalTo, + self, other + ), cell_indices=other.cell_indices, cell_type=CellType.computational ) @@ -416,7 +458,11 @@ def notEqualTo(self, other: 'Cell', /) -> 'Cell': # noqa: E225 """ return Cell(value=self._compute_value(lambda a, b: a != b, a=self.value, b=other.value), - words=WordConstructor.notEqualTo(self, other), + words=self._construct_word( + self, + WordConstructor.notEqualTo, + self, other + ), cell_indices=other.cell_indices, cell_type=CellType.computational ) @@ -432,7 +478,11 @@ def greaterThan(self, other: 'Cell', /) -> 'Cell': # noqa: E225 """ return Cell(value=self._compute_value(lambda a, b: a > b, a=self.value, b=other.value), - words=WordConstructor.greaterThan(self, other), + words=self._construct_word( + self, + WordConstructor.greaterThan, + self, other + ), cell_indices=other.cell_indices, cell_type=CellType.computational ) @@ -448,7 +498,11 @@ def greaterThanOrEqualTo(self, other: 'Cell', /) -> 'Cell': # noqa: E225 """ return Cell(value=self._compute_value(lambda a, b: a >= b, a=self.value, b=other.value), - words=WordConstructor.greaterThanOrEqualTo(self, other), + words=self._construct_word( + self, + WordConstructor.greaterThanOrEqualTo, + self, other + ), cell_indices=other.cell_indices, cell_type=CellType.computational ) @@ -464,7 +518,11 @@ def lessThan(self, other: 'Cell', /) -> 'Cell': # noqa: E225 """ return Cell(value=self._compute_value(lambda a, b: a < b, a=self.value, b=other.value), - words=WordConstructor.lessThan(self, other), + words=self._construct_word( + self, + WordConstructor.lessThan, + self, other + ), cell_indices=other.cell_indices, cell_type=CellType.computational ) @@ -480,7 +538,11 @@ def lessThanOrEqualTo(self, other: 'Cell', /) -> 'Cell': # noqa: E225 """ return Cell(value=self._compute_value(lambda a, b: a <= b, a=self.value, b=other.value), - words=WordConstructor.lessThanOrEqualTo(self, other), + words=self._construct_word( + self, + WordConstructor.lessThanOrEqualTo, + self, other + ), cell_indices=other.cell_indices, cell_type=CellType.computational ) @@ -496,7 +558,11 @@ def logicalConjunction(self, other: 'Cell', /) -> 'Cell': # noqa: E225 """ return Cell(value=self._compute_value(lambda a, b: a and b, a=self.value, b=other.value), - words=WordConstructor.logicalConjunction(self, other), + words=self._construct_word( + self, + WordConstructor.logicalConjunction, + self, other + ), cell_indices=other.cell_indices, cell_type=CellType.computational ) @@ -512,7 +578,11 @@ def logicalDisjunction(self, other: 'Cell', /) -> 'Cell': # noqa: E225 """ return Cell(value=self._compute_value(lambda a, b: a or b, a=self.value, b=other.value), - words=WordConstructor.logicalDisjunction(self, other), + words=self._construct_word( + self, + WordConstructor.logicalDisjunction, + self, other + ), cell_indices=other.cell_indices, cell_type=CellType.computational ) @@ -528,7 +598,11 @@ def concatenate(self, other: 'Cell', /) -> 'Cell': # noqa: E225 """ return Cell(value=self._compute_value(lambda a, b: str(a.value) + str(b.value), a=self, b=other), - words=WordConstructor.concatenate(self, other), + words=self._construct_word( + self, + WordConstructor.concatenate, + self, other + ), cell_indices=other.cell_indices, cell_type=CellType.computational ) @@ -798,7 +872,9 @@ def _aggregate_fun( cell_value = CellValueError() return Cell(value=cell_value, - words=WordConstructor.aggregation( + words=Cell._construct_word( + cell_start, + WordConstructor.aggregation, cell_start, cell_end, grammar_method ), cell_indices=cell_start.cell_indices, @@ -821,18 +897,25 @@ def reference(other: 'Cell', /) -> 'Cell': # noqa: E225 raise ValueError("The referenced cell has to be anchored.") return Cell(value=Cell._compute_value(lambda x: x.value, x=other), - words=WordConstructor.reference(other), + words=Cell._construct_word( + other, + WordConstructor.reference, + other + ), cell_indices=other.cell_indices, cell_type=CellType.computational ) @staticmethod - def cross_reference(target: 'Cell', sheet: 'Sheet') -> 'Cell': + def cross_reference(target: 'Cell', + target_sheet: 'Sheet', + source_sheet: 'Sheet') -> 'Cell': """Cross reference to other sheet in the workbook. Args: target (Cell): Target cell in a different sheet. - sheet (Sheet): Sheet of the target cell. + target_sheet (Sheet): Sheet of the target cell. + source_sheet (Sheet): Source sheet (where cell is set to exist). Return: Cell: reference to the different location. @@ -842,8 +925,12 @@ def cross_reference(target: 'Cell', sheet: 'Sheet') -> 'Cell': raise ValueError("The referenced cell has to be anchored.") return Cell(value=Cell._compute_value(lambda x: x.value, x=target), - words=WordConstructor.cross_reference(target, sheet), - cell_indices=target.cell_indices, + words=Cell._construct_word( + target, + WordConstructor.cross_reference, + target, target_sheet + ), + cell_indices=source_sheet.cell_indices, cell_type=CellType.computational ) @@ -860,7 +947,11 @@ def raw(other: 'Cell', words: Dict[str, str], /) -> 'Cell': # noqa: E225 Cell: Expression with defined word """ return Cell(value=Cell._compute_value(lambda x: x.value, x=other), - words=WordConstructor.raw(other, words), + words=Cell._construct_word( + other, + WordConstructor.raw, + other, words + ), cell_indices=other.cell_indices, cell_type=CellType.computational ) @@ -878,7 +969,11 @@ def variable(other: 'Cell', /) -> 'Cell': # noqa: E225 if not other.is_variable: raise ValueError("Only the variable type cell is accepted!") return Cell(value=Cell._compute_value(lambda x: x.value, x=other), - words=WordConstructor.variable(other), + words=Cell._construct_word( + other, + WordConstructor.variable, + other + ), cell_indices=other.cell_indices, cell_type=CellType.computational ) @@ -894,7 +989,11 @@ def brackets(other: 'Cell', /) -> 'Cell': # noqa: E225 Cell: Expression in brackets """ return Cell(value=Cell._compute_value(lambda x: x.value, x=other), - words=WordConstructor.brackets(other), + words=Cell._construct_word( + other, + WordConstructor.brackets, + other + ), cell_indices=other.cell_indices, cell_type=CellType.computational ) @@ -911,7 +1010,11 @@ def logarithm(other: 'Cell', /) -> 'Cell': # noqa: E225 """ return Cell(value=Cell._compute_value(lambda x: np.log(x.value), x=other), - words=WordConstructor.logarithm(other), + words=Cell._construct_word( + other, + WordConstructor.logarithm, + other + ), cell_indices=other.cell_indices, cell_type=CellType.computational ) @@ -928,7 +1031,11 @@ def exponential(other: 'Cell', /) -> 'Cell': # noqa: E225 """ return Cell(value=Cell._compute_value(lambda x: np.exp(x.value), x=other), - words=WordConstructor.exponential(other), + words=Cell._construct_word( + other, + WordConstructor.exponential, + other + ), cell_indices=other.cell_indices, cell_type=CellType.computational ) @@ -945,7 +1052,11 @@ def ceil(other: 'Cell', /) -> 'Cell': # noqa: E225 """ return Cell(value=Cell._compute_value(lambda x: np.ceil(x.value), x=other), - words=WordConstructor.ceil(other), + words=Cell._construct_word( + other, + WordConstructor.ceil, + other + ), cell_indices=other.cell_indices, cell_type=CellType.computational ) @@ -962,7 +1073,11 @@ def floor(other: 'Cell', /) -> 'Cell': # noqa: E225 """ return Cell(value=Cell._compute_value(lambda x: np.floor(x.value), x=other), - words=WordConstructor.floor(other), + words=Cell._construct_word( + other, + WordConstructor.floor, + other + ), cell_indices=other.cell_indices, cell_type=CellType.computational ) @@ -979,7 +1094,11 @@ def round(other: 'Cell', /) -> 'Cell': # noqa: E225 """ return Cell(value=Cell._compute_value(lambda x: np.round(x.value), x=other), - words=WordConstructor.round(other), + words=Cell._construct_word( + other, + WordConstructor.round, + other + ), cell_indices=other.cell_indices, cell_type=CellType.computational ) @@ -996,7 +1115,11 @@ def abs(other: 'Cell', /) -> 'Cell': # noqa: E225 """ return Cell(value=Cell._compute_value(lambda x: np.abs(x.value), x=other), - words=WordConstructor.abs(other), + words=Cell._construct_word( + other, + WordConstructor.abs, + other + ), cell_indices=other.cell_indices, cell_type=CellType.computational ) @@ -1013,7 +1136,11 @@ def sqrt(other: 'Cell', /) -> 'Cell': # noqa: E225 """ return Cell(value=Cell._compute_value(lambda x: np.sqrt(x.value), x=other), - words=WordConstructor.sqrt(other), + words=Cell._construct_word( + other, + WordConstructor.sqrt, + other + ), cell_indices=other.cell_indices, cell_type=CellType.computational ) @@ -1030,7 +1157,11 @@ def signum(other: 'Cell', /) -> 'Cell': # noqa: E225 """ return Cell(value=Cell._compute_value(lambda x: np.sign(x.value), x=other), - words=WordConstructor.signum(other), + words=Cell._construct_word( + other, + WordConstructor.signum, + other + ), cell_indices=other.cell_indices, cell_type=CellType.computational ) @@ -1047,7 +1178,11 @@ def logicalNegation(other: 'Cell', /) -> 'Cell': # noqa: E225 """ return Cell(value=Cell._compute_value(lambda x: not x.value, x=other), - words=WordConstructor.logicalNegation(other), + words=Cell._construct_word( + other, + WordConstructor.logicalNegation, + other + ), cell_indices=other.cell_indices, cell_type=CellType.computational ) @@ -1077,7 +1212,9 @@ def conditional(condition: 'Cell', return Cell(value=Cell._compute_value( lambda cons, cond, alte: cons.value if cond.value else alte.value, cons=consequent, cond=condition, alte=alternative), - words=WordConstructor.conditional( + words=Cell._construct_word( + condition, + WordConstructor.conditional, condition, consequent, alternative ), cell_indices=condition.cell_indices, @@ -1113,7 +1250,9 @@ def offset(reference: 'Cell', raise ValueError("Both reference cell and target cell must be" " anchored!") return Cell(value=Cell._compute_value(lambda x: x.value, x=target), - words=WordConstructor.offset( + words=Cell._construct_word( + reference, + WordConstructor.offset, reference, row_skip, column_skip ), cell_indices=reference.cell_indices, @@ -1153,7 +1292,9 @@ def linear_interpolation( (y_e.value - y_s.value) / (x_e.value - x_s.value), x_s=x_start, y_s=y_start, x_e=x_end, y_e=y_end, x_v=x ), - words=WordConstructor.linear_interpolation( + words=Cell._construct_word( + x_start, + WordConstructor.linear_interpolation, x_start, y_start, x_end, y_end, x ), cell_indices=x.cell_indices, diff --git a/portable_spreadsheet/cell_indices.py b/portable_spreadsheet/cell_indices.py index 4ec82b1..6ce9a19 100644 --- a/portable_spreadsheet/cell_indices.py +++ b/portable_spreadsheet/cell_indices.py @@ -146,25 +146,18 @@ def __init__(self, # assign the help texts self.rows_help_text: List[str] = copy.deepcopy(rows_help_text) self.columns_help_text: List[str] = copy.deepcopy(columns_help_text) + # This set the sheet to store only values and not to constructs words + # it is used by Cell class + self.values_only: bool = False @property - def supported_languages(self) -> List[str]: - """Returns all languages supported by the indicies. - - Returns: - List[str]: All languages supported by this indices. - """ - return [].extend(self.user_defined_languages, system_languages) - - @property - def shape(self) -> Tuple[int]: + def shape(self) -> Tuple[int, int]: """Return the shape of the object in the NumPy logic. Returns: - Tuple[int]: Number of rows, Number of columns + Tuple[int, int]: Number of rows, Number of columns """ - language = list(self.columns.keys())[0] - return len(self.rows[language]), len(self.columns[language]) + return self.number_of_rows, self.number_of_columns @property def languages(self) -> List[str]: diff --git a/portable_spreadsheet/sheet.py b/portable_spreadsheet/sheet.py index dc77b76..57731e3 100644 --- a/portable_spreadsheet/sheet.py +++ b/portable_spreadsheet/sheet.py @@ -80,7 +80,8 @@ def create_new_sheet( columns_help_text: List[str] = None, excel_append_row_labels: bool = True, excel_append_column_labels: bool = True, - warning_logger: Optional[Callable[[str], None]] = None + warning_logger: Optional[Callable[[str], None]] = None, + values_only: bool = False ) -> 'Sheet': """Direct way of creating instance. @@ -104,6 +105,9 @@ def create_new_sheet( on the beginning of the sheet as a offset for labels. warning_logger (Optional[Callable[[str], None]]): Function that logs the warnings (or None if skipped). + values_only (bool): Set the sheet to store only values and not to + constructs words - makes computation faster but disable all + export functionality Returns: Sheet: New instance of spreadsheet. @@ -120,6 +124,7 @@ def create_new_sheet( excel_append_column_labels=excel_append_column_labels, warning_logger=warning_logger ) + class_index.values_only = values_only return cls(class_index, warning_logger, name) def _initialise_array(self) -> T_sheet: diff --git a/portable_spreadsheet/sheet_utils.py b/portable_spreadsheet/sheet_utils.py index 7b99d37..3b2396a 100644 --- a/portable_spreadsheet/sheet_utils.py +++ b/portable_spreadsheet/sheet_utils.py @@ -177,8 +177,7 @@ def brackets(body: Cell) -> Cell: """ return Cell.brackets(body) - @staticmethod - def cross_reference(target: Cell, sheet: 'Sheet') -> Cell: + def cross_reference(self, target: Cell, sheet: 'Sheet') -> Cell: """Cross reference to other sheet in the workbook. Args: @@ -188,7 +187,7 @@ def cross_reference(target: Cell, sheet: 'Sheet') -> Cell: Return: Cell: reference to the different location. """ - return Cell.cross_reference(target, sheet) + return Cell.cross_reference(target, sheet, self.spreadsheet) @staticmethod def ln(value: Cell) -> Cell: diff --git a/setup.py b/setup.py index 9d56a7a..49856a8 100644 --- a/setup.py +++ b/setup.py @@ -5,7 +5,7 @@ setuptools.setup( name="portable-spreadsheet", - version="2.1.8", + version="2.1.9", author="David Salac", author_email="info@davidsalac.eu", description="A simple spreadsheet that keeps tracks of each operation of each cell in defined languages. Logic allows exporting sheets to Excel files (and see how each cell is computed), to the JSON strings with a description of computation of each cell (e. g. in the native language). Other formats, like HTML, CSV and Markdown (MD), are also implemented (user can define own format). It also allows reconstructing behaviours in native Python with NumPy.", # noqa diff --git a/tests/test_cell_word_constructor.py b/tests/test_cell_word_constructor.py index ee0ba7f..0a1e0e7 100644 --- a/tests/test_cell_word_constructor.py +++ b/tests/test_cell_word_constructor.py @@ -738,8 +738,8 @@ def test_cross_reference(self): 3, 3 ) with self.assertRaises(ValueError): - Cell.cross_reference(self.u_operand, sheet) - u_reference = Cell.cross_reference(self.a_operand, sheet) + Cell.cross_reference(self.u_operand, sheet, sheet) + u_reference = Cell.cross_reference(self.a_operand, sheet, sheet) u_ref_word = u_reference.parse self.assertEqual(u_ref_word['excel'], "=" + "'Results'!F5") self.assertEqual(u_ref_word['python_numpy'], "Results.values[3,4]")