gridcalc 0.1.1__tar.gz
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.
- gridcalc-0.1.1/LICENSE +21 -0
- gridcalc-0.1.1/PKG-INFO +290 -0
- gridcalc-0.1.1/README.md +260 -0
- gridcalc-0.1.1/pyproject.toml +71 -0
- gridcalc-0.1.1/src/gridcalc/__init__.py +3 -0
- gridcalc-0.1.1/src/gridcalc/__main__.py +11 -0
- gridcalc-0.1.1/src/gridcalc/config.py +109 -0
- gridcalc-0.1.1/src/gridcalc/engine.py +908 -0
- gridcalc-0.1.1/src/gridcalc/libs/__init__.py +24 -0
- gridcalc-0.1.1/src/gridcalc/libs/xlsx.py +430 -0
- gridcalc-0.1.1/src/gridcalc/sandbox.py +301 -0
- gridcalc-0.1.1/src/gridcalc/tui.py +1609 -0
gridcalc-0.1.1/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Shakeeb Alireza
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
gridcalc-0.1.1/PKG-INFO
ADDED
|
@@ -0,0 +1,290 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: gridcalc
|
|
3
|
+
Version: 0.1.1
|
|
4
|
+
Summary: A terminal spreadsheet powered by Python formulas
|
|
5
|
+
Keywords: spreadsheet,terminal,tui,curses,calculator,visicalc
|
|
6
|
+
Author: Shakeeb Alireza
|
|
7
|
+
Author-email: Shakeeb Alireza <shakfu@users.noreply.github.com>
|
|
8
|
+
License-Expression: MIT
|
|
9
|
+
License-File: LICENSE
|
|
10
|
+
Classifier: Development Status :: 4 - Beta
|
|
11
|
+
Classifier: Environment :: Console :: Curses
|
|
12
|
+
Classifier: Intended Audience :: Developers
|
|
13
|
+
Classifier: Intended Audience :: End Users/Desktop
|
|
14
|
+
Classifier: Operating System :: POSIX
|
|
15
|
+
Classifier: Operating System :: MacOS
|
|
16
|
+
Classifier: Programming Language :: Python :: 3
|
|
17
|
+
Classifier: Programming Language :: Python :: 3.10
|
|
18
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
19
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
20
|
+
Classifier: Programming Language :: Python :: 3.13
|
|
21
|
+
Classifier: Topic :: Office/Business :: Financial :: Spreadsheet
|
|
22
|
+
Classifier: Topic :: Utilities
|
|
23
|
+
Classifier: Typing :: Typed
|
|
24
|
+
Requires-Dist: tomli>=1.0 ; python_full_version < '3.11'
|
|
25
|
+
Requires-Python: >=3.10
|
|
26
|
+
Project-URL: Homepage, https://github.com/shakfu/gridcalc
|
|
27
|
+
Project-URL: Repository, https://github.com/shakfu/gridcalc
|
|
28
|
+
Project-URL: Issues, https://github.com/shakfu/gridcalc/issues
|
|
29
|
+
Description-Content-Type: text/markdown
|
|
30
|
+
|
|
31
|
+
# gridcalc
|
|
32
|
+
|
|
33
|
+
[](https://pypi.org/project/gridcalc/)
|
|
34
|
+
[](https://pypi.org/project/gridcalc/)
|
|
35
|
+
[](LICENSE)
|
|
36
|
+
|
|
37
|
+
A terminal spreadsheet powered by Python formulas. Based on Serge Zaitsev's [kalk](https://github.com/zserge/kalk), reimplemented in pure Python from [pktcalc](https://github.com/sa/pktcalc).
|
|
38
|
+
|
|
39
|
+
Uses Python's `eval()` for formula evaluation. Reads and writes JSON. File-compatible with pktcalc.
|
|
40
|
+
|
|
41
|
+
```sh
|
|
42
|
+
$ gridcalc budget.json
|
|
43
|
+
```
|
|
44
|
+
|
|
45
|
+
## Install
|
|
46
|
+
|
|
47
|
+
From PyPI (requires Python 3.10+):
|
|
48
|
+
|
|
49
|
+
```sh
|
|
50
|
+
pip install gridcalc
|
|
51
|
+
```
|
|
52
|
+
|
|
53
|
+
Or with [uv](https://docs.astral.sh/uv/):
|
|
54
|
+
|
|
55
|
+
```sh
|
|
56
|
+
uv tool install gridcalc
|
|
57
|
+
```
|
|
58
|
+
|
|
59
|
+
Then run:
|
|
60
|
+
|
|
61
|
+
```sh
|
|
62
|
+
gridcalc # new spreadsheet
|
|
63
|
+
gridcalc budget.json # open a file
|
|
64
|
+
```
|
|
65
|
+
|
|
66
|
+
### From source
|
|
67
|
+
|
|
68
|
+
```sh
|
|
69
|
+
git clone https://github.com/shakfu/gridcalc.git
|
|
70
|
+
cd gridcalc
|
|
71
|
+
uv run gridcalc
|
|
72
|
+
```
|
|
73
|
+
|
|
74
|
+
## File format
|
|
75
|
+
|
|
76
|
+
Spreadsheets are stored as JSON:
|
|
77
|
+
|
|
78
|
+
```json
|
|
79
|
+
{
|
|
80
|
+
"code": "def margin(rev, cost):\n return (rev - cost) / rev * 100\n",
|
|
81
|
+
"names": {
|
|
82
|
+
"revenue": "A1:A12",
|
|
83
|
+
"costs": "B1:B12"
|
|
84
|
+
},
|
|
85
|
+
"cells": [
|
|
86
|
+
["Revenue", "Cost", "Margin"],
|
|
87
|
+
[1000, 600, "=margin(A1, B1)"],
|
|
88
|
+
[1200, 700, "=margin(A2, B2)"]
|
|
89
|
+
],
|
|
90
|
+
"format": {
|
|
91
|
+
"width": 10
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
```
|
|
95
|
+
|
|
96
|
+
- **cells**: 2D array of cell values (numbers, strings, formulas, or null)
|
|
97
|
+
- **code** (optional): Python code executed before formulas (functions, imports, constants)
|
|
98
|
+
- **names** (optional): named ranges mapping names to cell ranges
|
|
99
|
+
- **format** (optional): display settings (currently only `width`)
|
|
100
|
+
|
|
101
|
+
## Usage
|
|
102
|
+
|
|
103
|
+
Arrow keys navigate. Type a number or `=` to enter data. Formulas start
|
|
104
|
+
with `=` and are Python expressions. Anything else is a label.
|
|
105
|
+
|
|
106
|
+
Press `:` for the command line (vim-style):
|
|
107
|
+
|
|
108
|
+
:q Quit
|
|
109
|
+
:q! Force quit (no save prompt)
|
|
110
|
+
:w [file] Save
|
|
111
|
+
:wq [file] Save and quit
|
|
112
|
+
:e Edit code block in $EDITOR
|
|
113
|
+
:o [file] Open file
|
|
114
|
+
:b Blank current cell
|
|
115
|
+
:clear Clear entire sheet
|
|
116
|
+
:f <fmt> Format/style cell (b u i L R I G D $ % * or Python spec)
|
|
117
|
+
:gf <fmt> Set global format
|
|
118
|
+
:width <n> Set column width (4-40)
|
|
119
|
+
:dr Delete row
|
|
120
|
+
:dc Delete column
|
|
121
|
+
:ir Insert row
|
|
122
|
+
:ic Insert column
|
|
123
|
+
:m Move row/column (arrow keys to drag)
|
|
124
|
+
:r Replicate (copy with relative refs)
|
|
125
|
+
:name <n> [range] Define named range
|
|
126
|
+
:names List named ranges
|
|
127
|
+
:unname <n> Remove named range
|
|
128
|
+
:tv/:th/:tb/:tn Lock/unlock title rows/columns
|
|
129
|
+
|
|
130
|
+
Other keys:
|
|
131
|
+
|
|
132
|
+
> Go to cell (type reference)
|
|
133
|
+
! Force recalculation
|
|
134
|
+
" Enter label
|
|
135
|
+
Backspace Clear cell
|
|
136
|
+
Tab Next column
|
|
137
|
+
Enter Next row
|
|
138
|
+
Home Jump to A1
|
|
139
|
+
Ctrl-B Toggle bold
|
|
140
|
+
Ctrl-U Toggle underline
|
|
141
|
+
Ctrl-Z Undo
|
|
142
|
+
Ctrl-Y Redo
|
|
143
|
+
Ctrl-C Quit
|
|
144
|
+
|
|
145
|
+
## Formulas
|
|
146
|
+
|
|
147
|
+
Formulas are Python expressions prefixed with `=`. Cell references like
|
|
148
|
+
`A1`, `B3`, `AA10` are available as variables.
|
|
149
|
+
|
|
150
|
+
=A1 + B1 * 2
|
|
151
|
+
=(A1 + A2) / 2
|
|
152
|
+
=A1 ** 2
|
|
153
|
+
=SQRT(A3 + A2)
|
|
154
|
+
|
|
155
|
+
### Range syntax
|
|
156
|
+
|
|
157
|
+
Use `:` to reference a range of cells. Ranges expand into arrays (Vec)
|
|
158
|
+
that support element-wise arithmetic.
|
|
159
|
+
|
|
160
|
+
=SUM(A1:A10)
|
|
161
|
+
=AVG(B1:B3)
|
|
162
|
+
=SUM(A1:A3 * B1:B3)
|
|
163
|
+
|
|
164
|
+
### Named ranges
|
|
165
|
+
|
|
166
|
+
Define a name for a cell range with `:name`, or in the JSON file's
|
|
167
|
+
`names` field. Names are injected as arrays and can be used directly in
|
|
168
|
+
formulas.
|
|
169
|
+
|
|
170
|
+
=SUM(revenue)
|
|
171
|
+
=SUM(revenue - costs)
|
|
172
|
+
=MAX(revenue)
|
|
173
|
+
=sum([x**2 for x in revenue])
|
|
174
|
+
|
|
175
|
+
### Custom functions
|
|
176
|
+
|
|
177
|
+
Use `:e` to open the code block in `$EDITOR`. The editor must block
|
|
178
|
+
until the file is closed (e.g., `vim`, `nano`, or `subl -w` for Sublime
|
|
179
|
+
Text). Define Python functions, import modules, set constants:
|
|
180
|
+
|
|
181
|
+
```python
|
|
182
|
+
import statistics
|
|
183
|
+
|
|
184
|
+
def margin(rev, cost):
|
|
185
|
+
return (rev - cost) / rev * 100
|
|
186
|
+
|
|
187
|
+
def compound(principal, rate, years):
|
|
188
|
+
return principal * (1 + rate) ** years
|
|
189
|
+
|
|
190
|
+
TAX_RATE = 0.21
|
|
191
|
+
```
|
|
192
|
+
|
|
193
|
+
Then use them in formulas: `=margin(A1, B1)`, `=compound(1000, 0.05, 10)`.
|
|
194
|
+
|
|
195
|
+
### Built-in functions
|
|
196
|
+
|
|
197
|
+
SUM(x) Sum of array or scalar
|
|
198
|
+
AVG(x) Average
|
|
199
|
+
MIN(x) Minimum
|
|
200
|
+
MAX(x) Maximum
|
|
201
|
+
COUNT(x) Number of elements
|
|
202
|
+
ABS(x) Absolute value (element-wise for arrays)
|
|
203
|
+
SQRT(x) Square root (element-wise for arrays)
|
|
204
|
+
INT(x) Truncate to integer (element-wise for arrays)
|
|
205
|
+
|
|
206
|
+
Math functions are preloaded: `sin`, `cos`, `tan`, `exp`, `log`,
|
|
207
|
+
`log2`, `log10`, `floor`, `ceil`, `pi`, `e`, `inf`. The `math` module
|
|
208
|
+
is also available for anything else (`=math.factorial(10)`).
|
|
209
|
+
|
|
210
|
+
Python builtins like `sum`, `min`, `max`, `abs`, `len` also work.
|
|
211
|
+
|
|
212
|
+
### Arrays
|
|
213
|
+
|
|
214
|
+
A formula can return an array. The cell displays the first element and
|
|
215
|
+
the count, e.g. `3.0[12]`. The full array is shown in the status bar.
|
|
216
|
+
Element-wise arithmetic works between arrays and scalars:
|
|
217
|
+
|
|
218
|
+
=revenue * 1.1
|
|
219
|
+
=revenue + costs
|
|
220
|
+
|
|
221
|
+
### Cell references
|
|
222
|
+
|
|
223
|
+
References adjust automatically on replicate, insert, and delete.
|
|
224
|
+
Use `$` for absolute references: `$A$1` (fixed), `$A1` (fixed column),
|
|
225
|
+
`A$1` (fixed row).
|
|
226
|
+
|
|
227
|
+
## Formatting
|
|
228
|
+
|
|
229
|
+
Use `:f` to set the display format or style of a cell. All formats
|
|
230
|
+
and styles are persisted when saving.
|
|
231
|
+
|
|
232
|
+
### Text styles
|
|
233
|
+
|
|
234
|
+
Toggle with `:f` or keyboard shortcuts. Styles can be combined
|
|
235
|
+
in a single command:
|
|
236
|
+
|
|
237
|
+
:f b Toggle bold (also Ctrl-B)
|
|
238
|
+
:f u Toggle underline (also Ctrl-U)
|
|
239
|
+
:f i Toggle italic
|
|
240
|
+
:f bi Toggle bold + italic
|
|
241
|
+
:f bui Toggle bold + underline + italic
|
|
242
|
+
|
|
243
|
+
### Number formats
|
|
244
|
+
|
|
245
|
+
:f $ Dollar (2 decimal places)
|
|
246
|
+
:f % Percentage (value * 100, 2 decimal places)
|
|
247
|
+
:f I Integer (truncate decimals)
|
|
248
|
+
:f * Bar chart (asterisks proportional to value)
|
|
249
|
+
:f L Left-align
|
|
250
|
+
:f R Right-align
|
|
251
|
+
:f G General (default)
|
|
252
|
+
:f D Use global format
|
|
253
|
+
|
|
254
|
+
Use `:gf` to set the global default format for all cells.
|
|
255
|
+
|
|
256
|
+
### Python format specs
|
|
257
|
+
|
|
258
|
+
For more control, pass any Python format specification:
|
|
259
|
+
|
|
260
|
+
:f ,.2f 1,234.50 (comma thousands, 2 decimals)
|
|
261
|
+
:f ,.0f 1,234,567 (comma thousands, no decimals)
|
|
262
|
+
:f .1% 15.7% (percentage with 1 decimal)
|
|
263
|
+
:f .4f 3.1416 (fixed 4 decimal places)
|
|
264
|
+
:f .2e 1.23e+04 (scientific notation)
|
|
265
|
+
|
|
266
|
+
These use Python's `format()` builtin. Any valid
|
|
267
|
+
[format spec](https://docs.python.org/3/library/string.html#format-specification-mini-language)
|
|
268
|
+
works.
|
|
269
|
+
|
|
270
|
+
## Development
|
|
271
|
+
|
|
272
|
+
```sh
|
|
273
|
+
make test # run tests
|
|
274
|
+
make lint # ruff check
|
|
275
|
+
make format # ruff format
|
|
276
|
+
make typecheck # mypy
|
|
277
|
+
make qa # lint + typecheck + test + format
|
|
278
|
+
```
|
|
279
|
+
|
|
280
|
+
### Publishing
|
|
281
|
+
|
|
282
|
+
```sh
|
|
283
|
+
make check # build and check dist with twine
|
|
284
|
+
make publish-test # upload to TestPyPI
|
|
285
|
+
make publish # upload to PyPI
|
|
286
|
+
```
|
|
287
|
+
|
|
288
|
+
## License
|
|
289
|
+
|
|
290
|
+
MIT
|
gridcalc-0.1.1/README.md
ADDED
|
@@ -0,0 +1,260 @@
|
|
|
1
|
+
# gridcalc
|
|
2
|
+
|
|
3
|
+
[](https://pypi.org/project/gridcalc/)
|
|
4
|
+
[](https://pypi.org/project/gridcalc/)
|
|
5
|
+
[](LICENSE)
|
|
6
|
+
|
|
7
|
+
A terminal spreadsheet powered by Python formulas. Based on Serge Zaitsev's [kalk](https://github.com/zserge/kalk), reimplemented in pure Python from [pktcalc](https://github.com/sa/pktcalc).
|
|
8
|
+
|
|
9
|
+
Uses Python's `eval()` for formula evaluation. Reads and writes JSON. File-compatible with pktcalc.
|
|
10
|
+
|
|
11
|
+
```sh
|
|
12
|
+
$ gridcalc budget.json
|
|
13
|
+
```
|
|
14
|
+
|
|
15
|
+
## Install
|
|
16
|
+
|
|
17
|
+
From PyPI (requires Python 3.10+):
|
|
18
|
+
|
|
19
|
+
```sh
|
|
20
|
+
pip install gridcalc
|
|
21
|
+
```
|
|
22
|
+
|
|
23
|
+
Or with [uv](https://docs.astral.sh/uv/):
|
|
24
|
+
|
|
25
|
+
```sh
|
|
26
|
+
uv tool install gridcalc
|
|
27
|
+
```
|
|
28
|
+
|
|
29
|
+
Then run:
|
|
30
|
+
|
|
31
|
+
```sh
|
|
32
|
+
gridcalc # new spreadsheet
|
|
33
|
+
gridcalc budget.json # open a file
|
|
34
|
+
```
|
|
35
|
+
|
|
36
|
+
### From source
|
|
37
|
+
|
|
38
|
+
```sh
|
|
39
|
+
git clone https://github.com/shakfu/gridcalc.git
|
|
40
|
+
cd gridcalc
|
|
41
|
+
uv run gridcalc
|
|
42
|
+
```
|
|
43
|
+
|
|
44
|
+
## File format
|
|
45
|
+
|
|
46
|
+
Spreadsheets are stored as JSON:
|
|
47
|
+
|
|
48
|
+
```json
|
|
49
|
+
{
|
|
50
|
+
"code": "def margin(rev, cost):\n return (rev - cost) / rev * 100\n",
|
|
51
|
+
"names": {
|
|
52
|
+
"revenue": "A1:A12",
|
|
53
|
+
"costs": "B1:B12"
|
|
54
|
+
},
|
|
55
|
+
"cells": [
|
|
56
|
+
["Revenue", "Cost", "Margin"],
|
|
57
|
+
[1000, 600, "=margin(A1, B1)"],
|
|
58
|
+
[1200, 700, "=margin(A2, B2)"]
|
|
59
|
+
],
|
|
60
|
+
"format": {
|
|
61
|
+
"width": 10
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
```
|
|
65
|
+
|
|
66
|
+
- **cells**: 2D array of cell values (numbers, strings, formulas, or null)
|
|
67
|
+
- **code** (optional): Python code executed before formulas (functions, imports, constants)
|
|
68
|
+
- **names** (optional): named ranges mapping names to cell ranges
|
|
69
|
+
- **format** (optional): display settings (currently only `width`)
|
|
70
|
+
|
|
71
|
+
## Usage
|
|
72
|
+
|
|
73
|
+
Arrow keys navigate. Type a number or `=` to enter data. Formulas start
|
|
74
|
+
with `=` and are Python expressions. Anything else is a label.
|
|
75
|
+
|
|
76
|
+
Press `:` for the command line (vim-style):
|
|
77
|
+
|
|
78
|
+
:q Quit
|
|
79
|
+
:q! Force quit (no save prompt)
|
|
80
|
+
:w [file] Save
|
|
81
|
+
:wq [file] Save and quit
|
|
82
|
+
:e Edit code block in $EDITOR
|
|
83
|
+
:o [file] Open file
|
|
84
|
+
:b Blank current cell
|
|
85
|
+
:clear Clear entire sheet
|
|
86
|
+
:f <fmt> Format/style cell (b u i L R I G D $ % * or Python spec)
|
|
87
|
+
:gf <fmt> Set global format
|
|
88
|
+
:width <n> Set column width (4-40)
|
|
89
|
+
:dr Delete row
|
|
90
|
+
:dc Delete column
|
|
91
|
+
:ir Insert row
|
|
92
|
+
:ic Insert column
|
|
93
|
+
:m Move row/column (arrow keys to drag)
|
|
94
|
+
:r Replicate (copy with relative refs)
|
|
95
|
+
:name <n> [range] Define named range
|
|
96
|
+
:names List named ranges
|
|
97
|
+
:unname <n> Remove named range
|
|
98
|
+
:tv/:th/:tb/:tn Lock/unlock title rows/columns
|
|
99
|
+
|
|
100
|
+
Other keys:
|
|
101
|
+
|
|
102
|
+
> Go to cell (type reference)
|
|
103
|
+
! Force recalculation
|
|
104
|
+
" Enter label
|
|
105
|
+
Backspace Clear cell
|
|
106
|
+
Tab Next column
|
|
107
|
+
Enter Next row
|
|
108
|
+
Home Jump to A1
|
|
109
|
+
Ctrl-B Toggle bold
|
|
110
|
+
Ctrl-U Toggle underline
|
|
111
|
+
Ctrl-Z Undo
|
|
112
|
+
Ctrl-Y Redo
|
|
113
|
+
Ctrl-C Quit
|
|
114
|
+
|
|
115
|
+
## Formulas
|
|
116
|
+
|
|
117
|
+
Formulas are Python expressions prefixed with `=`. Cell references like
|
|
118
|
+
`A1`, `B3`, `AA10` are available as variables.
|
|
119
|
+
|
|
120
|
+
=A1 + B1 * 2
|
|
121
|
+
=(A1 + A2) / 2
|
|
122
|
+
=A1 ** 2
|
|
123
|
+
=SQRT(A3 + A2)
|
|
124
|
+
|
|
125
|
+
### Range syntax
|
|
126
|
+
|
|
127
|
+
Use `:` to reference a range of cells. Ranges expand into arrays (Vec)
|
|
128
|
+
that support element-wise arithmetic.
|
|
129
|
+
|
|
130
|
+
=SUM(A1:A10)
|
|
131
|
+
=AVG(B1:B3)
|
|
132
|
+
=SUM(A1:A3 * B1:B3)
|
|
133
|
+
|
|
134
|
+
### Named ranges
|
|
135
|
+
|
|
136
|
+
Define a name for a cell range with `:name`, or in the JSON file's
|
|
137
|
+
`names` field. Names are injected as arrays and can be used directly in
|
|
138
|
+
formulas.
|
|
139
|
+
|
|
140
|
+
=SUM(revenue)
|
|
141
|
+
=SUM(revenue - costs)
|
|
142
|
+
=MAX(revenue)
|
|
143
|
+
=sum([x**2 for x in revenue])
|
|
144
|
+
|
|
145
|
+
### Custom functions
|
|
146
|
+
|
|
147
|
+
Use `:e` to open the code block in `$EDITOR`. The editor must block
|
|
148
|
+
until the file is closed (e.g., `vim`, `nano`, or `subl -w` for Sublime
|
|
149
|
+
Text). Define Python functions, import modules, set constants:
|
|
150
|
+
|
|
151
|
+
```python
|
|
152
|
+
import statistics
|
|
153
|
+
|
|
154
|
+
def margin(rev, cost):
|
|
155
|
+
return (rev - cost) / rev * 100
|
|
156
|
+
|
|
157
|
+
def compound(principal, rate, years):
|
|
158
|
+
return principal * (1 + rate) ** years
|
|
159
|
+
|
|
160
|
+
TAX_RATE = 0.21
|
|
161
|
+
```
|
|
162
|
+
|
|
163
|
+
Then use them in formulas: `=margin(A1, B1)`, `=compound(1000, 0.05, 10)`.
|
|
164
|
+
|
|
165
|
+
### Built-in functions
|
|
166
|
+
|
|
167
|
+
SUM(x) Sum of array or scalar
|
|
168
|
+
AVG(x) Average
|
|
169
|
+
MIN(x) Minimum
|
|
170
|
+
MAX(x) Maximum
|
|
171
|
+
COUNT(x) Number of elements
|
|
172
|
+
ABS(x) Absolute value (element-wise for arrays)
|
|
173
|
+
SQRT(x) Square root (element-wise for arrays)
|
|
174
|
+
INT(x) Truncate to integer (element-wise for arrays)
|
|
175
|
+
|
|
176
|
+
Math functions are preloaded: `sin`, `cos`, `tan`, `exp`, `log`,
|
|
177
|
+
`log2`, `log10`, `floor`, `ceil`, `pi`, `e`, `inf`. The `math` module
|
|
178
|
+
is also available for anything else (`=math.factorial(10)`).
|
|
179
|
+
|
|
180
|
+
Python builtins like `sum`, `min`, `max`, `abs`, `len` also work.
|
|
181
|
+
|
|
182
|
+
### Arrays
|
|
183
|
+
|
|
184
|
+
A formula can return an array. The cell displays the first element and
|
|
185
|
+
the count, e.g. `3.0[12]`. The full array is shown in the status bar.
|
|
186
|
+
Element-wise arithmetic works between arrays and scalars:
|
|
187
|
+
|
|
188
|
+
=revenue * 1.1
|
|
189
|
+
=revenue + costs
|
|
190
|
+
|
|
191
|
+
### Cell references
|
|
192
|
+
|
|
193
|
+
References adjust automatically on replicate, insert, and delete.
|
|
194
|
+
Use `$` for absolute references: `$A$1` (fixed), `$A1` (fixed column),
|
|
195
|
+
`A$1` (fixed row).
|
|
196
|
+
|
|
197
|
+
## Formatting
|
|
198
|
+
|
|
199
|
+
Use `:f` to set the display format or style of a cell. All formats
|
|
200
|
+
and styles are persisted when saving.
|
|
201
|
+
|
|
202
|
+
### Text styles
|
|
203
|
+
|
|
204
|
+
Toggle with `:f` or keyboard shortcuts. Styles can be combined
|
|
205
|
+
in a single command:
|
|
206
|
+
|
|
207
|
+
:f b Toggle bold (also Ctrl-B)
|
|
208
|
+
:f u Toggle underline (also Ctrl-U)
|
|
209
|
+
:f i Toggle italic
|
|
210
|
+
:f bi Toggle bold + italic
|
|
211
|
+
:f bui Toggle bold + underline + italic
|
|
212
|
+
|
|
213
|
+
### Number formats
|
|
214
|
+
|
|
215
|
+
:f $ Dollar (2 decimal places)
|
|
216
|
+
:f % Percentage (value * 100, 2 decimal places)
|
|
217
|
+
:f I Integer (truncate decimals)
|
|
218
|
+
:f * Bar chart (asterisks proportional to value)
|
|
219
|
+
:f L Left-align
|
|
220
|
+
:f R Right-align
|
|
221
|
+
:f G General (default)
|
|
222
|
+
:f D Use global format
|
|
223
|
+
|
|
224
|
+
Use `:gf` to set the global default format for all cells.
|
|
225
|
+
|
|
226
|
+
### Python format specs
|
|
227
|
+
|
|
228
|
+
For more control, pass any Python format specification:
|
|
229
|
+
|
|
230
|
+
:f ,.2f 1,234.50 (comma thousands, 2 decimals)
|
|
231
|
+
:f ,.0f 1,234,567 (comma thousands, no decimals)
|
|
232
|
+
:f .1% 15.7% (percentage with 1 decimal)
|
|
233
|
+
:f .4f 3.1416 (fixed 4 decimal places)
|
|
234
|
+
:f .2e 1.23e+04 (scientific notation)
|
|
235
|
+
|
|
236
|
+
These use Python's `format()` builtin. Any valid
|
|
237
|
+
[format spec](https://docs.python.org/3/library/string.html#format-specification-mini-language)
|
|
238
|
+
works.
|
|
239
|
+
|
|
240
|
+
## Development
|
|
241
|
+
|
|
242
|
+
```sh
|
|
243
|
+
make test # run tests
|
|
244
|
+
make lint # ruff check
|
|
245
|
+
make format # ruff format
|
|
246
|
+
make typecheck # mypy
|
|
247
|
+
make qa # lint + typecheck + test + format
|
|
248
|
+
```
|
|
249
|
+
|
|
250
|
+
### Publishing
|
|
251
|
+
|
|
252
|
+
```sh
|
|
253
|
+
make check # build and check dist with twine
|
|
254
|
+
make publish-test # upload to TestPyPI
|
|
255
|
+
make publish # upload to PyPI
|
|
256
|
+
```
|
|
257
|
+
|
|
258
|
+
## License
|
|
259
|
+
|
|
260
|
+
MIT
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
|
|
2
|
+
|
|
3
|
+
[project]
|
|
4
|
+
name = "gridcalc"
|
|
5
|
+
version = "0.1.1"
|
|
6
|
+
description = "A terminal spreadsheet powered by Python formulas"
|
|
7
|
+
readme = "README.md"
|
|
8
|
+
requires-python = ">=3.10"
|
|
9
|
+
license = "MIT"
|
|
10
|
+
license-files = ["LICENSE"]
|
|
11
|
+
authors = [
|
|
12
|
+
{ name = "Shakeeb Alireza", email = "shakfu@users.noreply.github.com" }
|
|
13
|
+
]
|
|
14
|
+
keywords = ["spreadsheet", "terminal", "tui", "curses", "calculator", "visicalc"]
|
|
15
|
+
classifiers = [
|
|
16
|
+
"Development Status :: 4 - Beta",
|
|
17
|
+
"Environment :: Console :: Curses",
|
|
18
|
+
"Intended Audience :: Developers",
|
|
19
|
+
"Intended Audience :: End Users/Desktop",
|
|
20
|
+
"Operating System :: POSIX",
|
|
21
|
+
"Operating System :: MacOS",
|
|
22
|
+
"Programming Language :: Python :: 3",
|
|
23
|
+
"Programming Language :: Python :: 3.10",
|
|
24
|
+
"Programming Language :: Python :: 3.11",
|
|
25
|
+
"Programming Language :: Python :: 3.12",
|
|
26
|
+
"Programming Language :: Python :: 3.13",
|
|
27
|
+
"Topic :: Office/Business :: Financial :: Spreadsheet",
|
|
28
|
+
"Topic :: Utilities",
|
|
29
|
+
"Typing :: Typed",
|
|
30
|
+
]
|
|
31
|
+
dependencies = [
|
|
32
|
+
"tomli>=1.0; python_version < '3.11'",
|
|
33
|
+
]
|
|
34
|
+
|
|
35
|
+
[dependency-groups]
|
|
36
|
+
dev = [
|
|
37
|
+
"mypy>=1.19.1",
|
|
38
|
+
"pytest",
|
|
39
|
+
"ruff>=0.15.7",
|
|
40
|
+
"twine>=6.2.0",
|
|
41
|
+
"build>=1.0",
|
|
42
|
+
]
|
|
43
|
+
|
|
44
|
+
[project.scripts]
|
|
45
|
+
gridcalc = "gridcalc.tui:main"
|
|
46
|
+
|
|
47
|
+
[project.urls]
|
|
48
|
+
Homepage = "https://github.com/shakfu/gridcalc"
|
|
49
|
+
Repository = "https://github.com/shakfu/gridcalc"
|
|
50
|
+
Issues = "https://github.com/shakfu/gridcalc/issues"
|
|
51
|
+
|
|
52
|
+
[build-system]
|
|
53
|
+
requires = ["uv_build>=0.11.2,<0.12"]
|
|
54
|
+
build-backend = "uv_build"
|
|
55
|
+
|
|
56
|
+
[tool.ruff]
|
|
57
|
+
target-version = "py310"
|
|
58
|
+
line-length = 100
|
|
59
|
+
|
|
60
|
+
[tool.ruff.lint]
|
|
61
|
+
select = ["E", "F", "W", "I", "UP", "B", "SIM", "S"]
|
|
62
|
+
ignore = ["S307", "S603", "S605", "S108", "S102", "S110"]
|
|
63
|
+
|
|
64
|
+
[tool.ruff.lint.per-file-ignores]
|
|
65
|
+
"tests/**" = ["S101"]
|
|
66
|
+
|
|
67
|
+
[tool.mypy]
|
|
68
|
+
python_version = "3.10"
|
|
69
|
+
strict = true
|
|
70
|
+
warn_return_any = true
|
|
71
|
+
warn_unused_configs = true
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import os
|
|
2
|
+
import sys
|
|
3
|
+
|
|
4
|
+
# Support both `python -m gridcalc` (relative import) and direct execution
|
|
5
|
+
if __package__:
|
|
6
|
+
from .tui import main
|
|
7
|
+
else:
|
|
8
|
+
sys.path.insert(0, os.path.dirname(os.path.dirname(os.path.abspath(__file__))))
|
|
9
|
+
from gridcalc.tui import main
|
|
10
|
+
|
|
11
|
+
main()
|