gnumeric 0.1.0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- gnumeric/.constants.json +8 -0
- gnumeric/__init__.py +41 -0
- gnumeric/cell.py +294 -0
- gnumeric/evaluation_errors.py +24 -0
- gnumeric/exceptions.py +49 -0
- gnumeric/expression.py +151 -0
- gnumeric/expression_evaluation.py +315 -0
- gnumeric/formula_functions/__init__.py +0 -0
- gnumeric/formula_functions/argument_helpers.py +21 -0
- gnumeric/formula_functions/mathematics.py +21 -0
- gnumeric/formula_functions/statistics.py +23 -0
- gnumeric/sheet.py +642 -0
- gnumeric/utils.py +131 -0
- gnumeric/workbook.py +423 -0
- gnumeric-0.1.0.dist-info/METADATA +55 -0
- gnumeric-0.1.0.dist-info/RECORD +17 -0
- gnumeric-0.1.0.dist-info/WHEEL +4 -0
gnumeric/.constants.json
ADDED
gnumeric/__init__.py
ADDED
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Gnumeric-py: Reading and writing gnumeric files with python
|
|
3
|
+
Copyright (C) 2017 Michael Lipschultz
|
|
4
|
+
|
|
5
|
+
This program is free software: you can redistribute it and/or modify
|
|
6
|
+
it under the terms of the GNU General Public License as published by
|
|
7
|
+
the Free Software Foundation, either version 3 of the License, or
|
|
8
|
+
(at your option) any later version.
|
|
9
|
+
|
|
10
|
+
This program is distributed in the hope that it will be useful,
|
|
11
|
+
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
12
|
+
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
13
|
+
GNU General Public License for more details.
|
|
14
|
+
|
|
15
|
+
You should have received a copy of the GNU General Public License
|
|
16
|
+
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
17
|
+
"""
|
|
18
|
+
|
|
19
|
+
import json
|
|
20
|
+
import os
|
|
21
|
+
|
|
22
|
+
try:
|
|
23
|
+
here = os.path.abspath(os.path.dirname(__file__))
|
|
24
|
+
src_file = os.path.join(here, '.constants.json')
|
|
25
|
+
with open(src_file) as src:
|
|
26
|
+
constants = json.load(src)
|
|
27
|
+
__author__ = constants['__author__']
|
|
28
|
+
__author_email__ = constants['__author_email__']
|
|
29
|
+
__license__ = constants['__license__']
|
|
30
|
+
__maintainer_email__ = constants['__maintainer_email__']
|
|
31
|
+
__url__ = constants['__url__']
|
|
32
|
+
__version__ = constants['__version__']
|
|
33
|
+
except IOError:
|
|
34
|
+
# packaged
|
|
35
|
+
pass
|
|
36
|
+
|
|
37
|
+
from gnumeric.workbook import Workbook
|
|
38
|
+
|
|
39
|
+
|
|
40
|
+
def load_workbook(filepath):
|
|
41
|
+
return Workbook.load_workbook(filepath)
|
gnumeric/cell.py
ADDED
|
@@ -0,0 +1,294 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Gnumeric-py: Reading and writing gnumeric files with python
|
|
3
|
+
Copyright (C) 2017 Michael Lipschultz
|
|
4
|
+
|
|
5
|
+
This program is free software: you can redistribute it and/or modify
|
|
6
|
+
it under the terms of the GNU General Public License as published by
|
|
7
|
+
the Free Software Foundation, either version 3 of the License, or
|
|
8
|
+
(at your option) any later version.
|
|
9
|
+
|
|
10
|
+
This program is distributed in the hope that it will be useful,
|
|
11
|
+
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
12
|
+
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
13
|
+
GNU General Public License for more details.
|
|
14
|
+
|
|
15
|
+
You should have received a copy of the GNU General Public License
|
|
16
|
+
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
17
|
+
"""
|
|
18
|
+
|
|
19
|
+
import datetime
|
|
20
|
+
from typing import Optional, Union
|
|
21
|
+
|
|
22
|
+
from lxml import etree
|
|
23
|
+
|
|
24
|
+
from gnumeric.exceptions import UnrecognizedCellTypeException
|
|
25
|
+
from gnumeric.expression import Expression
|
|
26
|
+
from gnumeric.utils import RowColReference
|
|
27
|
+
|
|
28
|
+
VALUE_TYPE_EXPR = -10
|
|
29
|
+
VALUE_TYPE_EMPTY = 10
|
|
30
|
+
VALUE_TYPE_BOOLEAN = 20
|
|
31
|
+
VALUE_TYPE_INTEGER = 30
|
|
32
|
+
VALUE_TYPE_FLOAT = 40
|
|
33
|
+
VALUE_TYPE_ERROR = 50
|
|
34
|
+
VALUE_TYPE_STRING = 60
|
|
35
|
+
VALUE_TYPE_CELLRANGE = 70
|
|
36
|
+
VALUE_TYPE_ARRAY = 80
|
|
37
|
+
|
|
38
|
+
|
|
39
|
+
class Cell:
|
|
40
|
+
_instances = {}
|
|
41
|
+
|
|
42
|
+
_BASE_DATETIME = datetime.datetime(1899, 12, 30)
|
|
43
|
+
|
|
44
|
+
def __new__(cls, cell_element, style_region_element, worksheet, ns):
|
|
45
|
+
key = (cell_element, style_region_element, worksheet)
|
|
46
|
+
instance = cls._instances.get(key)
|
|
47
|
+
if not instance:
|
|
48
|
+
instance = super(Cell, cls).__new__(cls)
|
|
49
|
+
instance.__cached_value = None
|
|
50
|
+
instance.__time_cached_value_set = None
|
|
51
|
+
cls._instances[key] = instance
|
|
52
|
+
return instance
|
|
53
|
+
|
|
54
|
+
def __init__(self, cell_element, style_region_element, worksheet, ns):
|
|
55
|
+
self.__cell = cell_element
|
|
56
|
+
self.__style_region = style_region_element
|
|
57
|
+
self.__worksheet = worksheet
|
|
58
|
+
self.__ns = ns
|
|
59
|
+
|
|
60
|
+
def __get_style_element(self):
|
|
61
|
+
elements = self.__style_region.xpath('./gnm:Style', namespaces=self.__ns)
|
|
62
|
+
return elements[0] if elements else None
|
|
63
|
+
|
|
64
|
+
def __set_expression_id(self, expr_id: str) -> None:
|
|
65
|
+
self.__cell.set('ExprID', expr_id)
|
|
66
|
+
|
|
67
|
+
@property
|
|
68
|
+
def worksheet(self):
|
|
69
|
+
"""
|
|
70
|
+
The worksheet this cell belongs to.
|
|
71
|
+
"""
|
|
72
|
+
return self.__worksheet
|
|
73
|
+
|
|
74
|
+
@property
|
|
75
|
+
def column(self) -> int:
|
|
76
|
+
"""
|
|
77
|
+
The column this cell belongs to (0-indexed).
|
|
78
|
+
"""
|
|
79
|
+
return int(self.__cell.get('Col'))
|
|
80
|
+
|
|
81
|
+
@property
|
|
82
|
+
def row(self) -> int:
|
|
83
|
+
"""
|
|
84
|
+
The row this cell belongs to (0-indexed).
|
|
85
|
+
"""
|
|
86
|
+
return int(self.__cell.get('Row'))
|
|
87
|
+
|
|
88
|
+
@property
|
|
89
|
+
def coordinate(self) -> RowColReference:
|
|
90
|
+
"""
|
|
91
|
+
The (row, column) of the cell (0-indexed)
|
|
92
|
+
"""
|
|
93
|
+
return RowColReference(self.row, self.column)
|
|
94
|
+
|
|
95
|
+
@property
|
|
96
|
+
def text(self) -> Optional[str]:
|
|
97
|
+
"""
|
|
98
|
+
Returns the raw value stored in the cell. The text will be `None` if the cell is empty.
|
|
99
|
+
:return: str or `None`
|
|
100
|
+
"""
|
|
101
|
+
return self.__cell.text
|
|
102
|
+
|
|
103
|
+
@property
|
|
104
|
+
def value_type(self) -> int:
|
|
105
|
+
"""
|
|
106
|
+
Returns the type of value stored in the cell:
|
|
107
|
+
- VALUE_TYPE_EXPR = -10
|
|
108
|
+
- VALUE_TYPE_EMPTY = 10
|
|
109
|
+
- VALUE_TYPE_BOOLEAN = 20
|
|
110
|
+
- VALUE_TYPE_INTEGER = 30
|
|
111
|
+
- VALUE_TYPE_FLOAT = 40
|
|
112
|
+
- VALUE_TYPE_ERROR = 50
|
|
113
|
+
- VALUE_TYPE_STRING = 60
|
|
114
|
+
- VALUE_TYPE_CELLRANGE = 70
|
|
115
|
+
- VALUE_TYPE_ARRAY = 80
|
|
116
|
+
"""
|
|
117
|
+
value_type = self.__cell.get('ValueType')
|
|
118
|
+
if value_type is not None:
|
|
119
|
+
return int(value_type)
|
|
120
|
+
elif self.__cell.get('ExprID') is not None or self.text.startswith('='):
|
|
121
|
+
return VALUE_TYPE_EXPR
|
|
122
|
+
else:
|
|
123
|
+
raise UnrecognizedCellTypeException(
|
|
124
|
+
'Cell is: "' + str(etree.tostring(self.__cell)) + '"'
|
|
125
|
+
)
|
|
126
|
+
|
|
127
|
+
def is_datetime(self) -> bool:
|
|
128
|
+
return (
|
|
129
|
+
self.value_type == VALUE_TYPE_FLOAT
|
|
130
|
+
and self.__cell.get('ValueFormat') == 'yyyy-mmm-dd'
|
|
131
|
+
)
|
|
132
|
+
|
|
133
|
+
def __set_type(self, value_type: int):
|
|
134
|
+
if value_type == VALUE_TYPE_EXPR:
|
|
135
|
+
if 'ValueType' in self.__cell.keys():
|
|
136
|
+
self.__cell.attrib.pop('ValueType')
|
|
137
|
+
else:
|
|
138
|
+
self.__cell.set('ValueType', str(value_type))
|
|
139
|
+
|
|
140
|
+
def get_value(
|
|
141
|
+
self, *, compute_expression: bool = False
|
|
142
|
+
) -> Union[bool, int, float, Expression, str]:
|
|
143
|
+
"""
|
|
144
|
+
Gets the value stored in the cell, converted into the appropriate Python datatype when possible.
|
|
145
|
+
|
|
146
|
+
If the cell is an expression: If `compute_expression` is True, the the result of the expression
|
|
147
|
+
is returned, otherwise an Expression object is returned.
|
|
148
|
+
"""
|
|
149
|
+
value = self.text
|
|
150
|
+
if self.value_type == VALUE_TYPE_BOOLEAN:
|
|
151
|
+
return value.lower() == 'true'
|
|
152
|
+
elif self.value_type == VALUE_TYPE_INTEGER:
|
|
153
|
+
return int(value)
|
|
154
|
+
elif self.value_type == VALUE_TYPE_FLOAT:
|
|
155
|
+
return float(value)
|
|
156
|
+
elif self.value_type == VALUE_TYPE_EXPR:
|
|
157
|
+
expression = Expression(self.__cell.get('ExprID'), self.__worksheet, self)
|
|
158
|
+
if not compute_expression:
|
|
159
|
+
return expression
|
|
160
|
+
else:
|
|
161
|
+
if self.__cached_value is None:
|
|
162
|
+
self.__cached_value = 0
|
|
163
|
+
self.__cached_value = expression.value
|
|
164
|
+
return self.__cached_value
|
|
165
|
+
else:
|
|
166
|
+
return value
|
|
167
|
+
|
|
168
|
+
def set_value(self, value, *, value_type: str = 'infer') -> None:
|
|
169
|
+
"""
|
|
170
|
+
Sets the value stored in the cell.
|
|
171
|
+
|
|
172
|
+
If `value_type` is:
|
|
173
|
+
- one of the `VALUE_TYPE_` constants, then that value type will be used
|
|
174
|
+
- ``keep'`, then the cell will keep its type
|
|
175
|
+
- `'infer'`, then it tries to guess the type based on the value being set:
|
|
176
|
+
- if `value` is of type `bool`, then `VALUE_TYPE_BOOLEAN`
|
|
177
|
+
- if `value` is of type `int`, then `VALUE_TYPE_INTEGER`
|
|
178
|
+
- if `value` is of type `float`, then `VALUE_TYPE_FLOAT`
|
|
179
|
+
- if `value` is an empty string or `None`, then `VALUE_TYPE_EMPTY`
|
|
180
|
+
- if `value` is a string that starts with `=` or is an `Expression` object, then `VALUE_TYPE_EXPR`
|
|
181
|
+
- if `value` is anything else, then `VALUE_TYPE_STRING`
|
|
182
|
+
|
|
183
|
+
The default `value_type` is `'infer'`.
|
|
184
|
+
|
|
185
|
+
Warning: This method does no type checking, so it is possible to save a string into a cell whose type is
|
|
186
|
+
`VALUE_TYPE_INTEGER`. This could result in problems when opening the workbook in Gnumeric.
|
|
187
|
+
"""
|
|
188
|
+
val_types = {
|
|
189
|
+
bool: VALUE_TYPE_BOOLEAN,
|
|
190
|
+
int: VALUE_TYPE_INTEGER,
|
|
191
|
+
float: VALUE_TYPE_FLOAT,
|
|
192
|
+
}
|
|
193
|
+
if value_type == 'infer':
|
|
194
|
+
if type(value) in val_types:
|
|
195
|
+
value_type = val_types[type(value)]
|
|
196
|
+
elif value in ('', None):
|
|
197
|
+
value_type = VALUE_TYPE_EMPTY
|
|
198
|
+
elif isinstance(value, Expression) or value[0] == '=':
|
|
199
|
+
value_type = VALUE_TYPE_EXPR
|
|
200
|
+
elif self.value_type in (
|
|
201
|
+
VALUE_TYPE_EMPTY,
|
|
202
|
+
VALUE_TYPE_BOOLEAN,
|
|
203
|
+
VALUE_TYPE_INTEGER,
|
|
204
|
+
VALUE_TYPE_FLOAT,
|
|
205
|
+
VALUE_TYPE_ERROR,
|
|
206
|
+
):
|
|
207
|
+
value_type = VALUE_TYPE_STRING
|
|
208
|
+
else:
|
|
209
|
+
value_type = self.value_type
|
|
210
|
+
elif value_type == 'keep':
|
|
211
|
+
value_type = self.value_type
|
|
212
|
+
|
|
213
|
+
if value_type == VALUE_TYPE_BOOLEAN:
|
|
214
|
+
self.__cell.text = str(bool(value)).upper()
|
|
215
|
+
elif value_type == VALUE_TYPE_EMPTY:
|
|
216
|
+
self.__cell.text = None
|
|
217
|
+
elif value_type == VALUE_TYPE_EXPR and isinstance(value, Expression):
|
|
218
|
+
if self.__worksheet != value.worksheet:
|
|
219
|
+
raise NotImplementedError(
|
|
220
|
+
'Copying expression to different worksheet is not yet supported'
|
|
221
|
+
) # TODO
|
|
222
|
+
|
|
223
|
+
expr_id = str(value.id)
|
|
224
|
+
if (
|
|
225
|
+
expr_id not in self.__worksheet.get_expression_map()
|
|
226
|
+
and value.id is None
|
|
227
|
+
):
|
|
228
|
+
expr_id = str(
|
|
229
|
+
max(int(k) for k in self.__worksheet.get_expression_map()) + 1
|
|
230
|
+
)
|
|
231
|
+
|
|
232
|
+
if len(value.get_all_cells()) == 1 and value.get_originating_cell() == self:
|
|
233
|
+
# copying expression over itself, so don't add it to cells using this expression
|
|
234
|
+
self.__cell.text = value.value
|
|
235
|
+
else:
|
|
236
|
+
# the expression is shared, so use the expression id
|
|
237
|
+
if len(value.get_all_cells()) == 1:
|
|
238
|
+
value.get_originating_cell().__set_expression_id(expr_id)
|
|
239
|
+
self.__cell.text = None
|
|
240
|
+
self.__set_expression_id(expr_id)
|
|
241
|
+
else:
|
|
242
|
+
self.__cell.text = str(value)
|
|
243
|
+
|
|
244
|
+
self.__set_type(value_type)
|
|
245
|
+
self.__cached_value = self.get_value(compute_expression=True)
|
|
246
|
+
|
|
247
|
+
value = property(
|
|
248
|
+
get_value,
|
|
249
|
+
set_value,
|
|
250
|
+
doc='Get or set the value in the cell, converted into the correct type.',
|
|
251
|
+
)
|
|
252
|
+
|
|
253
|
+
@property
|
|
254
|
+
def result(self):
|
|
255
|
+
"""
|
|
256
|
+
Gets the result of the cell. For expressions, it returns the result of the expression. For literals, it returns the literal.
|
|
257
|
+
"""
|
|
258
|
+
value = self.get_value(compute_expression=True)
|
|
259
|
+
if value is None:
|
|
260
|
+
value = 0
|
|
261
|
+
elif self.is_datetime():
|
|
262
|
+
value = self._BASE_DATETIME + datetime.timedelta(days=value)
|
|
263
|
+
return value
|
|
264
|
+
|
|
265
|
+
@property
|
|
266
|
+
def text_format(self) -> str:
|
|
267
|
+
"""
|
|
268
|
+
The format string used to format the text in the cell for display. This is the "Number Format" in Gnumeric.
|
|
269
|
+
"""
|
|
270
|
+
return str(
|
|
271
|
+
self.__style_region.xpath('./gnm:Style/@Format', namespaces=self.__ns)[0]
|
|
272
|
+
)
|
|
273
|
+
|
|
274
|
+
def __str__(self) -> str:
|
|
275
|
+
return repr(self.value)
|
|
276
|
+
|
|
277
|
+
def __repr__(self) -> str:
|
|
278
|
+
return 'Cell[%s, (%d, %d), ws="%s"]' % (
|
|
279
|
+
str(self),
|
|
280
|
+
self.row,
|
|
281
|
+
self.column,
|
|
282
|
+
self.__worksheet.title,
|
|
283
|
+
)
|
|
284
|
+
|
|
285
|
+
def __eq__(self, other) -> bool:
|
|
286
|
+
return (
|
|
287
|
+
isinstance(other, Cell)
|
|
288
|
+
and self.__worksheet == other.__worksheet
|
|
289
|
+
and self.row == other.row
|
|
290
|
+
and self.column == other.column
|
|
291
|
+
)
|
|
292
|
+
|
|
293
|
+
def __hash__(self) -> int:
|
|
294
|
+
return hash(self.__cell)
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import enum
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
class EvaluationError(enum.Enum):
|
|
5
|
+
DIV0 = '#DIV/0!'
|
|
6
|
+
VALUE = '#VALUE!'
|
|
7
|
+
NA = '#N/A'
|
|
8
|
+
NAME = '#NAME?'
|
|
9
|
+
NUM = '#NUM!' # =10000000000^1000000000
|
|
10
|
+
REF = '#REF!'
|
|
11
|
+
NULL = (
|
|
12
|
+
'#NULL!' # occurs when the intersection of two areas don't actually intersect
|
|
13
|
+
)
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
class ExpressionEvaluationException(Exception):
|
|
17
|
+
"""
|
|
18
|
+
Evaulating the expression has failed.
|
|
19
|
+
"""
|
|
20
|
+
|
|
21
|
+
def __init__(self, error: EvaluationError, msg=None):
|
|
22
|
+
msg = msg or error.value
|
|
23
|
+
super().__init__(msg)
|
|
24
|
+
self.error = error
|
gnumeric/exceptions.py
ADDED
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Gnumeric-py: Reading and writing gnumeric files with python
|
|
3
|
+
Copyright (C) 2017 Michael Lipschultz
|
|
4
|
+
|
|
5
|
+
This program is free software: you can redistribute it and/or modify
|
|
6
|
+
it under the terms of the GNU General Public License as published by
|
|
7
|
+
the Free Software Foundation, either version 3 of the License, or
|
|
8
|
+
(at your option) any later version.
|
|
9
|
+
|
|
10
|
+
This program is distributed in the hope that it will be useful,
|
|
11
|
+
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
12
|
+
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
13
|
+
GNU General Public License for more details.
|
|
14
|
+
|
|
15
|
+
You should have received a copy of the GNU General Public License
|
|
16
|
+
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
17
|
+
"""
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
class DuplicateTitleException(Exception):
|
|
21
|
+
"""
|
|
22
|
+
A workbook cannot contain multiple sheets with the same name/title.
|
|
23
|
+
"""
|
|
24
|
+
|
|
25
|
+
def __init__(self, msg):
|
|
26
|
+
super().__init__(msg)
|
|
27
|
+
|
|
28
|
+
|
|
29
|
+
class WrongWorkbookException(Exception):
|
|
30
|
+
"""
|
|
31
|
+
A sheet or cell from one workbook being using in another workbook.
|
|
32
|
+
"""
|
|
33
|
+
|
|
34
|
+
def __init__(self, msg):
|
|
35
|
+
super().__init__(msg)
|
|
36
|
+
|
|
37
|
+
|
|
38
|
+
class UnsupportedOperationException(Exception):
|
|
39
|
+
def __init__(self, msg):
|
|
40
|
+
super().__init__(msg)
|
|
41
|
+
|
|
42
|
+
|
|
43
|
+
class UnrecognizedCellTypeException(Exception):
|
|
44
|
+
"""
|
|
45
|
+
The type of cell cannot be determined.
|
|
46
|
+
"""
|
|
47
|
+
|
|
48
|
+
def __init__(self, msg):
|
|
49
|
+
super().__init__(msg)
|
gnumeric/expression.py
ADDED
|
@@ -0,0 +1,151 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Gnumeric-py: Reading and writing gnumeric files with python
|
|
3
|
+
Copyright (C) 2017 Michael Lipschultz
|
|
4
|
+
|
|
5
|
+
This program is free software: you can redistribute it and/or modify
|
|
6
|
+
it under the terms of the GNU General Public License as published by
|
|
7
|
+
the Free Software Foundation, either version 3 of the License, or
|
|
8
|
+
(at your option) any later version.
|
|
9
|
+
|
|
10
|
+
This program is distributed in the hope that it will be useful,
|
|
11
|
+
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
12
|
+
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
13
|
+
GNU General Public License for more details.
|
|
14
|
+
|
|
15
|
+
You should have received a copy of the GNU General Public License
|
|
16
|
+
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
17
|
+
"""
|
|
18
|
+
|
|
19
|
+
from typing import Set, Union
|
|
20
|
+
|
|
21
|
+
from gnumeric import expression_evaluation, utils
|
|
22
|
+
from gnumeric.evaluation_errors import EvaluationError
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
class Expression:
|
|
26
|
+
# TODO: what to do if originating cell's text changes or is deleted?
|
|
27
|
+
|
|
28
|
+
def __init__(self, id, worksheet, cell):
|
|
29
|
+
"""
|
|
30
|
+
:param cell: The cell this Expression was created from. It could be the originating cell or just a cell that
|
|
31
|
+
uses this expression. It's used when `id` is `None` (i.e. the expression isn't shared between cells).
|
|
32
|
+
"""
|
|
33
|
+
self.__exprid = id
|
|
34
|
+
self.__worksheet = worksheet
|
|
35
|
+
self.__cell = cell
|
|
36
|
+
|
|
37
|
+
def __get_raw_originating_cell(self):
|
|
38
|
+
"""
|
|
39
|
+
Returns a (int, int, str) tuple, where the values are (row, column, text of the cell).
|
|
40
|
+
"""
|
|
41
|
+
if self.__exprid is not None:
|
|
42
|
+
coords, text = self.__worksheet.get_expression_map()[self.__exprid]
|
|
43
|
+
return int(coords[0]), int(coords[1]), text
|
|
44
|
+
else:
|
|
45
|
+
return self.__cell.row, self.__cell.column, self.__cell.text
|
|
46
|
+
|
|
47
|
+
@property
|
|
48
|
+
def id(self):
|
|
49
|
+
"""
|
|
50
|
+
The expression id used to uniquely identify the expression within the sheet. This will be `None` if the
|
|
51
|
+
expression isn't shared between cells.
|
|
52
|
+
"""
|
|
53
|
+
return self.__exprid
|
|
54
|
+
|
|
55
|
+
@property
|
|
56
|
+
def original_text(self) -> str:
|
|
57
|
+
"""
|
|
58
|
+
Returns the text of the expression, with cell references from the perspective of the cell where the expression
|
|
59
|
+
is stored (i.e. the original cell).
|
|
60
|
+
"""
|
|
61
|
+
return self.__get_raw_originating_cell()[2]
|
|
62
|
+
|
|
63
|
+
@property
|
|
64
|
+
def text(self):
|
|
65
|
+
"""
|
|
66
|
+
Returns the text of the exprsesion, with cell references updated to be from the perspective of the cell using the expression.
|
|
67
|
+
"""
|
|
68
|
+
# TODO: fix this to actually return what it's supposed to
|
|
69
|
+
raise NotImplementedError
|
|
70
|
+
|
|
71
|
+
@property
|
|
72
|
+
def reference_coordinate_offset(self) -> utils.RowColReference:
|
|
73
|
+
"""
|
|
74
|
+
The (row, col) offset to translate the original coordinates into the coordinates based at the current cell.
|
|
75
|
+
"""
|
|
76
|
+
original_coordinates = self.get_originating_cell_coordinate()
|
|
77
|
+
current_coordinates = self.__cell.coordinate
|
|
78
|
+
return utils.RowColReference(
|
|
79
|
+
current_coordinates[0] - original_coordinates[0],
|
|
80
|
+
current_coordinates[1] - original_coordinates[1],
|
|
81
|
+
)
|
|
82
|
+
|
|
83
|
+
@property
|
|
84
|
+
def value(self):
|
|
85
|
+
"""
|
|
86
|
+
Returns the result of the expression's evaluation.
|
|
87
|
+
"""
|
|
88
|
+
return expression_evaluation.evaluate(self.original_text, self.__cell)
|
|
89
|
+
|
|
90
|
+
@property
|
|
91
|
+
def worksheet(self):
|
|
92
|
+
"""
|
|
93
|
+
The worksheet this expression is created in.
|
|
94
|
+
"""
|
|
95
|
+
return self.__worksheet
|
|
96
|
+
|
|
97
|
+
def get_originating_cell_coordinate(
|
|
98
|
+
self, representation_format='index'
|
|
99
|
+
) -> Union[utils.RowColReference, str]:
|
|
100
|
+
"""
|
|
101
|
+
Returns the cell coordinate for the cell Gnumeric is using to store the expression.
|
|
102
|
+
|
|
103
|
+
:param representation_format: For spreadsheet notation, use `'spreadsheet'`, for 0-indexed (row, column)
|
|
104
|
+
notation, use 'index' (default).
|
|
105
|
+
:return: A `str` if `representation_format` is `'spreadsheet'` and a tuple of ints `(int, int)` if 'index'.
|
|
106
|
+
"""
|
|
107
|
+
row, col = self.__get_raw_originating_cell()[:2]
|
|
108
|
+
if representation_format == 'index':
|
|
109
|
+
return utils.RowColReference(row, col)
|
|
110
|
+
elif representation_format == 'spreadsheet':
|
|
111
|
+
return utils.coordinate_to_spreadsheet(row, col)
|
|
112
|
+
|
|
113
|
+
def get_originating_cell(self):
|
|
114
|
+
"""
|
|
115
|
+
Returns the cell Gnumeric is using to store the expression.
|
|
116
|
+
"""
|
|
117
|
+
return self.__worksheet.cell(
|
|
118
|
+
*self.get_originating_cell_coordinate(), create=False
|
|
119
|
+
)
|
|
120
|
+
|
|
121
|
+
def get_all_cells(self, sort=False):
|
|
122
|
+
"""
|
|
123
|
+
Returns a list of all cells using this expression.
|
|
124
|
+
|
|
125
|
+
Use `sort` to specify whether the cells should be sorted. If `False` (default), then no sorting will take
|
|
126
|
+
place. If `sort` is `"row"`, then sorting will occur by row first, then by column within each row. If `sort`
|
|
127
|
+
is `"column"`, then the opposite will happen: first sort by column, then by row within each column.
|
|
128
|
+
"""
|
|
129
|
+
if self.__exprid is None:
|
|
130
|
+
return [self.__cell]
|
|
131
|
+
else:
|
|
132
|
+
return self.__worksheet.get_all_cells_with_expression(
|
|
133
|
+
self.__exprid, sort=sort
|
|
134
|
+
)
|
|
135
|
+
|
|
136
|
+
def get_referenced_cells(self) -> Union[Set, EvaluationError]:
|
|
137
|
+
return expression_evaluation.get_referenced_cells(
|
|
138
|
+
self.original_text, self.__cell
|
|
139
|
+
)
|
|
140
|
+
|
|
141
|
+
def __str__(self):
|
|
142
|
+
return self.original_text
|
|
143
|
+
|
|
144
|
+
def __repr__(self):
|
|
145
|
+
return 'Expression(id=%s, text="%s", ws=%s, cell=(%d, %d))' % (
|
|
146
|
+
self.id,
|
|
147
|
+
self.original_text,
|
|
148
|
+
self.__worksheet,
|
|
149
|
+
self.__cell.row,
|
|
150
|
+
self.__cell.column,
|
|
151
|
+
)
|