maybeai-sheet-cli 0.6.0__tar.gz → 0.8.0__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.
- {maybeai_sheet_cli-0.6.0 → maybeai_sheet_cli-0.8.0}/PKG-INFO +9 -2
- {maybeai_sheet_cli-0.6.0 → maybeai_sheet_cli-0.8.0}/README.md +8 -1
- {maybeai_sheet_cli-0.6.0 → maybeai_sheet_cli-0.8.0}/pyproject.toml +9 -1
- {maybeai_sheet_cli-0.6.0 → maybeai_sheet_cli-0.8.0}/src/maybeai_sheet/__init__.py +1 -1
- {maybeai_sheet_cli-0.6.0 → maybeai_sheet_cli-0.8.0}/src/maybeai_sheet/cli.py +1 -0
- {maybeai_sheet_cli-0.6.0 → maybeai_sheet_cli-0.8.0}/src/maybeai_sheet/commands/excel_worksheet.py +50 -1
- {maybeai_sheet_cli-0.6.0 → maybeai_sheet_cli-0.8.0}/src/maybeai_sheet/commands/range.py +42 -0
- {maybeai_sheet_cli-0.6.0 → maybeai_sheet_cli-0.8.0}/src/maybeai_sheet/commands/workbook.py +24 -0
- maybeai_sheet_cli-0.8.0/tests/fixtures/benchmarks/build_fixtures.py +213 -0
- maybeai_sheet_cli-0.8.0/tests/fixtures/benchmarks/flat-records.xlsx +0 -0
- maybeai_sheet_cli-0.8.0/tests/fixtures/benchmarks/mixed-engine.xlsx +0 -0
- maybeai_sheet_cli-0.8.0/tests/fixtures/benchmarks/multi-table-layout.xlsx +0 -0
- maybeai_sheet_cli-0.8.0/tests/fixtures/benchmarks/rows.json +12 -0
- maybeai_sheet_cli-0.8.0/tests/fixtures/benchmarks/simple-grid.xlsx +0 -0
- maybeai_sheet_cli-0.8.0/tests/fixtures/benchmarks/simple-values.json +17 -0
- maybeai_sheet_cli-0.8.0/tests/test_benchmark_runner.py +154 -0
- {maybeai_sheet_cli-0.6.0 → maybeai_sheet_cli-0.8.0}/tests/test_cli.py +59 -0
- {maybeai_sheet_cli-0.6.0 → maybeai_sheet_cli-0.8.0}/tests/test_phase2_cli.py +28 -0
- {maybeai_sheet_cli-0.6.0 → maybeai_sheet_cli-0.8.0}/tests/test_two_engine_cli.py +24 -0
- maybeai_sheet_cli-0.6.0/.claude/settings.local.json +0 -9
- maybeai_sheet_cli-0.6.0/CLAUDE.md +0 -46
- maybeai_sheet_cli-0.6.0/cli-packaging-plan.md +0 -279
- maybeai_sheet_cli-0.6.0/cli-plan.md +0 -389
- maybeai_sheet_cli-0.6.0/docs/agent-friendly-command-tree.md +0 -1424
- maybeai_sheet_cli-0.6.0/docs/operation-plan-cli-no-dsl-plan.md +0 -459
- maybeai_sheet_cli-0.6.0/docs/superpowers/plans/2026-07-01-mbs-agent-friendly-command-tree.md +0 -1270
- maybeai_sheet_cli-0.6.0/docs/superpowers/plans/2026-07-01-mbs-agent-friendly-phase-2-routes.md +0 -57
- maybeai_sheet_cli-0.6.0/docs/superpowers/plans/2026-07-01-mbs-agent-friendly-phase-2.md +0 -708
- maybeai_sheet_cli-0.6.0/docs/superpowers/plans/2026-07-01-mbs-be-new-backend-contracts-plan.md +0 -928
- maybeai_sheet_cli-0.6.0/docs/superpowers/plans/2026-07-01-mbs-deferred-table-routing-plan.md +0 -713
- maybeai_sheet_cli-0.6.0/docs/superpowers/plans/2026-07-01-mbs-excel-worksheet-multi-table-plan.md +0 -683
- maybeai_sheet_cli-0.6.0/docs/superpowers/plans/2026-07-01-mbs-two-engine-command-refactor.md +0 -139
- maybeai_sheet_cli-0.6.0/docs/superpowers/reports/2026-07-01-mbs-deferred-routing-e2e-report.html +0 -239
- maybeai_sheet_cli-0.6.0/docs/superpowers/reports/2026-07-01-phase1-test-review.html +0 -326
- maybeai_sheet_cli-0.6.0/docs/superpowers/reports/2026-07-01-phase2-test-review.html +0 -309
- maybeai_sheet_cli-0.6.0/docs/superpowers/reports/20260701-130317-excel-table-empty-range-e2e.html +0 -99
- maybeai_sheet_cli-0.6.0/docs/superpowers/reports/20260701-130447-excel-table-empty-range-e2e.html +0 -23
- maybeai_sheet_cli-0.6.0/docs/superpowers/reports/20260701-130556-excel-table-empty-range-e2e.html +0 -204
- maybeai_sheet_cli-0.6.0/docs/superpowers/reports/20260701-230803-local-cli-endpoint-verification.html +0 -52
- maybeai_sheet_cli-0.6.0/docs/superpowers/reports/20260701-232511-local7011-cli-table-metadata-verification.html +0 -78
- maybeai_sheet_cli-0.6.0/docs/superpowers/reports/e2e-artifacts/e2e-payloads-20260701-130317.json +0 -79
- maybeai_sheet_cli-0.6.0/docs/superpowers/reports/e2e-artifacts/e2e-payloads-20260701-130447.json +0 -82
- maybeai_sheet_cli-0.6.0/docs/superpowers/reports/e2e-artifacts/e2e-payloads-20260701-130556.json +0 -414
- maybeai_sheet_cli-0.6.0/docs/superpowers/reports/e2e-artifacts/multi-table-empty-range-20260701-130317.xlsx +0 -0
- maybeai_sheet_cli-0.6.0/docs/superpowers/reports/e2e-artifacts/openpyxl-multi-table-empty-range-20260701-130447.xlsx +0 -0
- maybeai_sheet_cli-0.6.0/docs/superpowers/reports/import-engine-local-20260702-150800/import_engine_local_report.html +0 -74
- maybeai_sheet_cli-0.6.0/docs/superpowers/reports/import-engine-local-20260702-150800/llm_cost_import_auto.json +0 -479
- maybeai_sheet_cli-0.6.0/docs/superpowers/reports/import-engine-local-20260702-150800/llm_cost_import_mixed_forced.json +0 -0
- maybeai_sheet_cli-0.6.0/docs/superpowers/reports/import-engine-local-20260702-150800/llm_cost_list_worksheets.json +0 -355
- maybeai_sheet_cli-0.6.0/docs/superpowers/reports/import-engine-local-20260702-150800/llm_cost_read_detail_A1_T5.json +0 -549
- maybeai_sheet_cli-0.6.0/docs/superpowers/reports/import-engine-local-20260702-150800/summary.json +0 -79
- maybeai_sheet_cli-0.6.0/docs/superpowers/reports/import-engine-local-20260702-150800/yunling_import_auto.json +0 -602
- maybeai_sheet_cli-0.6.0/docs/superpowers/reports/import-engine-local-20260702-150800/yunling_list_worksheets.json +0 -598
- maybeai_sheet_cli-0.6.0/docs/superpowers/reports/import-engine-local-20260702-150800/yunling_read_T00_A1_F5.json +0 -343
- maybeai_sheet_cli-0.6.0/docs/superpowers/reports/import-engine-merged-20260702-153018/import_auto.json +0 -587
- maybeai_sheet_cli-0.6.0/docs/superpowers/reports/import-engine-merged-20260702-153018/list_worksheets.json +0 -112
- maybeai_sheet_cli-0.6.0/docs/superpowers/reports/import-engine-merged-20260702-153018/merged_two_tables_report.html +0 -12
- maybeai_sheet_cli-0.6.0/docs/superpowers/reports/import-engine-merged-20260702-153018/read_merged_1_A1_T5.json +0 -373
- maybeai_sheet_cli-0.6.0/docs/superpowers/reports/import-engine-merged-20260702-153018/read_merged_2_A1_T5.json +0 -373
- maybeai_sheet_cli-0.6.0/docs/superpowers/reports/import-engine-merged-20260702-153018/summary.json +0 -145
- maybeai_sheet_cli-0.6.0/docs/verification-matrix-fallback.md +0 -35
- maybeai_sheet_cli-0.6.0/uv.lock +0 -415
- {maybeai_sheet_cli-0.6.0 → maybeai_sheet_cli-0.8.0}/.gitignore +0 -0
- {maybeai_sheet_cli-0.6.0 → maybeai_sheet_cli-0.8.0}/src/maybeai_sheet/client.py +0 -0
- {maybeai_sheet_cli-0.6.0 → maybeai_sheet_cli-0.8.0}/src/maybeai_sheet/commands/__init__.py +0 -0
- {maybeai_sheet_cli-0.6.0 → maybeai_sheet_cli-0.8.0}/src/maybeai_sheet/commands/_phase2.py +0 -0
- {maybeai_sheet_cli-0.6.0 → maybeai_sheet_cli-0.8.0}/src/maybeai_sheet/commands/cell.py +0 -0
- {maybeai_sheet_cli-0.6.0 → maybeai_sheet_cli-0.8.0}/src/maybeai_sheet/commands/column.py +0 -0
- {maybeai_sheet_cli-0.6.0 → maybeai_sheet_cli-0.8.0}/src/maybeai_sheet/commands/db_table.py +0 -0
- {maybeai_sheet_cli-0.6.0 → maybeai_sheet_cli-0.8.0}/src/maybeai_sheet/commands/excel_table.py +0 -0
- {maybeai_sheet_cli-0.6.0 → maybeai_sheet_cli-0.8.0}/src/maybeai_sheet/commands/file.py +0 -0
- {maybeai_sheet_cli-0.6.0 → maybeai_sheet_cli-0.8.0}/src/maybeai_sheet/commands/formula.py +0 -0
- {maybeai_sheet_cli-0.6.0 → maybeai_sheet_cli-0.8.0}/src/maybeai_sheet/commands/history.py +0 -0
- {maybeai_sheet_cli-0.6.0 → maybeai_sheet_cli-0.8.0}/src/maybeai_sheet/commands/raw.py +0 -0
- {maybeai_sheet_cli-0.6.0 → maybeai_sheet_cli-0.8.0}/src/maybeai_sheet/commands/row.py +0 -0
- {maybeai_sheet_cli-0.6.0 → maybeai_sheet_cli-0.8.0}/src/maybeai_sheet/commands/share.py +0 -0
- {maybeai_sheet_cli-0.6.0 → maybeai_sheet_cli-0.8.0}/src/maybeai_sheet/commands/sheet.py +0 -0
- {maybeai_sheet_cli-0.6.0 → maybeai_sheet_cli-0.8.0}/src/maybeai_sheet/commands/style.py +0 -0
- {maybeai_sheet_cli-0.6.0 → maybeai_sheet_cli-0.8.0}/src/maybeai_sheet/commands/table.py +0 -0
- {maybeai_sheet_cli-0.6.0 → maybeai_sheet_cli-0.8.0}/src/maybeai_sheet/commands/worksheet.py +0 -0
- {maybeai_sheet_cli-0.6.0 → maybeai_sheet_cli-0.8.0}/src/maybeai_sheet/config.py +0 -0
- {maybeai_sheet_cli-0.6.0 → maybeai_sheet_cli-0.8.0}/src/maybeai_sheet/endpoints.py +0 -0
- {maybeai_sheet_cli-0.6.0 → maybeai_sheet_cli-0.8.0}/src/maybeai_sheet/errors.py +0 -0
- {maybeai_sheet_cli-0.6.0 → maybeai_sheet_cli-0.8.0}/src/maybeai_sheet/formatters.py +0 -0
- {maybeai_sheet_cli-0.6.0 → maybeai_sheet_cli-0.8.0}/src/maybeai_sheet/models/__init__.py +0 -0
- {maybeai_sheet_cli-0.6.0 → maybeai_sheet_cli-0.8.0}/src/maybeai_sheet/models/share.py +0 -0
- {maybeai_sheet_cli-0.6.0 → maybeai_sheet_cli-0.8.0}/src/maybeai_sheet/models/sheet.py +0 -0
- {maybeai_sheet_cli-0.6.0 → maybeai_sheet_cli-0.8.0}/src/maybeai_sheet/models/workbook.py +0 -0
- {maybeai_sheet_cli-0.6.0 → maybeai_sheet_cli-0.8.0}/src/maybeai_sheet/options.py +0 -0
- {maybeai_sheet_cli-0.6.0 → maybeai_sheet_cli-0.8.0}/src/maybeai_sheet/protocol.py +0 -0
- {maybeai_sheet_cli-0.6.0 → maybeai_sheet_cli-0.8.0}/src/maybeai_sheet/resolver.py +0 -0
- {maybeai_sheet_cli-0.6.0 → maybeai_sheet_cli-0.8.0}/src/maybeai_sheet/table_resolver.py +0 -0
- {maybeai_sheet_cli-0.6.0 → maybeai_sheet_cli-0.8.0}/src/maybeai_sheet/update.py +0 -0
- {maybeai_sheet_cli-0.6.0 → maybeai_sheet_cli-0.8.0}/tests/test_endpoints.py +0 -0
- {maybeai_sheet_cli-0.6.0 → maybeai_sheet_cli-0.8.0}/tests/test_formatters.py +0 -0
- {maybeai_sheet_cli-0.6.0 → maybeai_sheet_cli-0.8.0}/tests/test_resolver.py +0 -0
- {maybeai_sheet_cli-0.6.0 → maybeai_sheet_cli-0.8.0}/tests/test_update.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: maybeai-sheet-cli
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.8.0
|
|
4
4
|
Summary: CLI for common MaybeAI spreadsheet operations
|
|
5
5
|
Project-URL: Homepage, https://github.com/OmniMCP-AI/maybeai-sheet-cli
|
|
6
6
|
Project-URL: Repository, https://github.com/OmniMCP-AI/maybeai-sheet-cli
|
|
@@ -167,6 +167,8 @@ Search, style, inspect history, and export:
|
|
|
167
167
|
```bash
|
|
168
168
|
mbs excel-worksheet range search --doc-id abc123 --worksheet-name Sheet1 --query revenue --max-results 20
|
|
169
169
|
mbs excel-worksheet style freeze-panes --doc-id abc123 --worksheet-name Sheet1 --cell B2
|
|
170
|
+
mbs workbook calculate --doc-id abc123
|
|
171
|
+
mbs excel-worksheet calculate --doc-id abc123 --worksheet-name Sheet1
|
|
170
172
|
mbs excel-worksheet range calculate --doc-id abc123 --worksheet-name Sheet1 --cell E2 --formula "=SUM(B2:D2)"
|
|
171
173
|
mbs version list --doc-id abc123 --limit 10
|
|
172
174
|
mbs workbook export --doc-id abc123 --out workbook.xlsx
|
|
@@ -216,6 +218,7 @@ mbs
|
|
|
216
218
|
│ ├── list-user-workbooks API List user workbooks with bounded output.
|
|
217
219
|
| list-worksheets API List worksheets, including engine.
|
|
218
220
|
│ ├── search NOW Search user workbooks with bounded output.
|
|
221
|
+
│ ├── calculate NOW Recalculate formulas across the workbook.
|
|
219
222
|
│ ├── export API Export workbook bytes.
|
|
220
223
|
│ └── copy API Copy a workbook.
|
|
221
224
|
│ ├── upload API Upload a file through workbook import flow.
|
|
@@ -226,7 +229,8 @@ mbs
|
|
|
226
229
|
│
|
|
227
230
|
├── excel_worksheet
|
|
228
231
|
│ ├── read API Read the full range of the worksheet.
|
|
229
|
-
│ ├──
|
|
232
|
+
│ ├── calculate NOW Recalculate formulas in one worksheet.
|
|
233
|
+
│ ├── list-table API Detect content-backed tables in a worksheet.
|
|
230
234
|
│ ├── create API Create a worksheet, optionally with starter values.
|
|
231
235
|
│ ├── update API Update worksheet metadata.
|
|
232
236
|
│ ├── sample PLAN Return bounded worksheet/range sample rows.
|
|
@@ -351,6 +355,7 @@ mbs excel-worksheet read --doc-id abc123 --worksheet-name Actuals --range A1:D20
|
|
|
351
355
|
mbs excel-worksheet create --doc-id abc123 --name Actuals # API
|
|
352
356
|
mbs excel-worksheet update --doc-id abc123 --worksheet-name Actuals --title "Actuals FY26" # API
|
|
353
357
|
mbs excel-worksheet metadata --doc-id abc123 --worksheet-name Actuals # API
|
|
358
|
+
mbs excel-worksheet list-table --doc-id abc123 --gid 3 # API
|
|
354
359
|
mbs excel-worksheet sample --doc-id abc123 --worksheet-name Actuals --range A1:Z5000 --limit 50 # PLAN
|
|
355
360
|
|
|
356
361
|
# excel-worksheet range
|
|
@@ -393,6 +398,8 @@ mbs excel-worksheet column append --doc-id abc123 --worksheet-name Actuals --val
|
|
|
393
398
|
mbs excel-worksheet range lineage --doc-id abc123 --worksheet-name Model --cell E2 --format tree # API
|
|
394
399
|
|
|
395
400
|
# formula
|
|
401
|
+
mbs workbook calculate --doc-id abc123 # API
|
|
402
|
+
mbs excel-worksheet calculate --doc-id abc123 --worksheet-name Model # API
|
|
396
403
|
mbs excel-worksheet range calculate --doc-id abc123 --worksheet-name Model --cell E2 --formula "=SUM(B2:D2)" # API
|
|
397
404
|
mbs excel-worksheet range set-formula --doc-id abc123 --worksheet-name Model --cell E2 --formula "=SUM(B2:D2)" # API
|
|
398
405
|
|
|
@@ -143,6 +143,8 @@ Search, style, inspect history, and export:
|
|
|
143
143
|
```bash
|
|
144
144
|
mbs excel-worksheet range search --doc-id abc123 --worksheet-name Sheet1 --query revenue --max-results 20
|
|
145
145
|
mbs excel-worksheet style freeze-panes --doc-id abc123 --worksheet-name Sheet1 --cell B2
|
|
146
|
+
mbs workbook calculate --doc-id abc123
|
|
147
|
+
mbs excel-worksheet calculate --doc-id abc123 --worksheet-name Sheet1
|
|
146
148
|
mbs excel-worksheet range calculate --doc-id abc123 --worksheet-name Sheet1 --cell E2 --formula "=SUM(B2:D2)"
|
|
147
149
|
mbs version list --doc-id abc123 --limit 10
|
|
148
150
|
mbs workbook export --doc-id abc123 --out workbook.xlsx
|
|
@@ -192,6 +194,7 @@ mbs
|
|
|
192
194
|
│ ├── list-user-workbooks API List user workbooks with bounded output.
|
|
193
195
|
| list-worksheets API List worksheets, including engine.
|
|
194
196
|
│ ├── search NOW Search user workbooks with bounded output.
|
|
197
|
+
│ ├── calculate NOW Recalculate formulas across the workbook.
|
|
195
198
|
│ ├── export API Export workbook bytes.
|
|
196
199
|
│ └── copy API Copy a workbook.
|
|
197
200
|
│ ├── upload API Upload a file through workbook import flow.
|
|
@@ -202,7 +205,8 @@ mbs
|
|
|
202
205
|
│
|
|
203
206
|
├── excel_worksheet
|
|
204
207
|
│ ├── read API Read the full range of the worksheet.
|
|
205
|
-
│ ├──
|
|
208
|
+
│ ├── calculate NOW Recalculate formulas in one worksheet.
|
|
209
|
+
│ ├── list-table API Detect content-backed tables in a worksheet.
|
|
206
210
|
│ ├── create API Create a worksheet, optionally with starter values.
|
|
207
211
|
│ ├── update API Update worksheet metadata.
|
|
208
212
|
│ ├── sample PLAN Return bounded worksheet/range sample rows.
|
|
@@ -327,6 +331,7 @@ mbs excel-worksheet read --doc-id abc123 --worksheet-name Actuals --range A1:D20
|
|
|
327
331
|
mbs excel-worksheet create --doc-id abc123 --name Actuals # API
|
|
328
332
|
mbs excel-worksheet update --doc-id abc123 --worksheet-name Actuals --title "Actuals FY26" # API
|
|
329
333
|
mbs excel-worksheet metadata --doc-id abc123 --worksheet-name Actuals # API
|
|
334
|
+
mbs excel-worksheet list-table --doc-id abc123 --gid 3 # API
|
|
330
335
|
mbs excel-worksheet sample --doc-id abc123 --worksheet-name Actuals --range A1:Z5000 --limit 50 # PLAN
|
|
331
336
|
|
|
332
337
|
# excel-worksheet range
|
|
@@ -369,6 +374,8 @@ mbs excel-worksheet column append --doc-id abc123 --worksheet-name Actuals --val
|
|
|
369
374
|
mbs excel-worksheet range lineage --doc-id abc123 --worksheet-name Model --cell E2 --format tree # API
|
|
370
375
|
|
|
371
376
|
# formula
|
|
377
|
+
mbs workbook calculate --doc-id abc123 # API
|
|
378
|
+
mbs excel-worksheet calculate --doc-id abc123 --worksheet-name Model # API
|
|
372
379
|
mbs excel-worksheet range calculate --doc-id abc123 --worksheet-name Model --cell E2 --formula "=SUM(B2:D2)" # API
|
|
373
380
|
mbs excel-worksheet range set-formula --doc-id abc123 --worksheet-name Model --cell E2 --formula "=SUM(B2:D2)" # API
|
|
374
381
|
|
|
@@ -4,7 +4,7 @@ build-backend = "hatchling.build"
|
|
|
4
4
|
|
|
5
5
|
[project]
|
|
6
6
|
name = "maybeai-sheet-cli"
|
|
7
|
-
version = "0.
|
|
7
|
+
version = "0.8.0"
|
|
8
8
|
description = "CLI for common MaybeAI spreadsheet operations"
|
|
9
9
|
readme = "README.md"
|
|
10
10
|
requires-python = ">=3.10"
|
|
@@ -39,3 +39,11 @@ mbs = "maybeai_sheet.cli:app"
|
|
|
39
39
|
|
|
40
40
|
[tool.hatch.build.targets.wheel]
|
|
41
41
|
packages = ["src/maybeai_sheet"]
|
|
42
|
+
|
|
43
|
+
[tool.hatch.build.targets.sdist]
|
|
44
|
+
include = [
|
|
45
|
+
"/src",
|
|
46
|
+
"/tests",
|
|
47
|
+
"/README.md",
|
|
48
|
+
"/pyproject.toml",
|
|
49
|
+
]
|
{maybeai_sheet_cli-0.6.0 → maybeai_sheet_cli-0.8.0}/src/maybeai_sheet/commands/excel_worksheet.py
RENAMED
|
@@ -16,6 +16,7 @@ from ..table_resolver import filter_content_backed_excel_tables
|
|
|
16
16
|
from .column import app as column_app
|
|
17
17
|
from .range import app as range_app
|
|
18
18
|
from .row import app as row_app
|
|
19
|
+
from .sheet import recalculate as sheet_recalculate
|
|
19
20
|
from .style import app as style_app
|
|
20
21
|
from .worksheet import copy as worksheet_copy
|
|
21
22
|
from .worksheet import create as worksheet_create
|
|
@@ -232,6 +233,49 @@ def move(
|
|
|
232
233
|
)
|
|
233
234
|
|
|
234
235
|
|
|
236
|
+
@app.command("calculate")
|
|
237
|
+
def calculate(
|
|
238
|
+
ctx: typer.Context,
|
|
239
|
+
doc_id: Optional[str] = typer.Option(None, "--doc-id", help="MaybeAI document ID."),
|
|
240
|
+
url: Optional[str] = typer.Option(None, "--url", help="MaybeAI workbook URL."),
|
|
241
|
+
uri: Optional[str] = typer.Option(None, "--uri", help="Resolved workbook URI."),
|
|
242
|
+
gid: Optional[int] = typer.Option(None, "--gid", help="Worksheet gid."),
|
|
243
|
+
worksheet_name: Optional[str] = typer.Option(None, "--worksheet-name", help="Worksheet name."),
|
|
244
|
+
output: Optional[str] = output_option(),
|
|
245
|
+
verbose: Optional[bool] = verbose_option(),
|
|
246
|
+
timeout: Optional[float] = timeout_option(),
|
|
247
|
+
) -> None:
|
|
248
|
+
"""Recalculate formulas in one worksheet."""
|
|
249
|
+
try:
|
|
250
|
+
if gid is not None and worksheet_name is None:
|
|
251
|
+
state = resolve_command_state(
|
|
252
|
+
ctx,
|
|
253
|
+
doc_id=doc_id,
|
|
254
|
+
url=url,
|
|
255
|
+
uri=uri,
|
|
256
|
+
gid=gid,
|
|
257
|
+
output=output,
|
|
258
|
+
verbose=verbose,
|
|
259
|
+
timeout=timeout,
|
|
260
|
+
)
|
|
261
|
+
target = resolve_target(state, force_gid_uri=True)
|
|
262
|
+
worksheet_name = target.worksheet_name
|
|
263
|
+
uri = target.uri
|
|
264
|
+
doc_id = target.document_id
|
|
265
|
+
sheet_recalculate(
|
|
266
|
+
ctx,
|
|
267
|
+
doc_id=doc_id,
|
|
268
|
+
url=url,
|
|
269
|
+
uri=uri,
|
|
270
|
+
worksheet_name=worksheet_name,
|
|
271
|
+
output=output,
|
|
272
|
+
verbose=verbose,
|
|
273
|
+
timeout=timeout,
|
|
274
|
+
)
|
|
275
|
+
except Exception as error: # pragma: no cover - CLI boundary
|
|
276
|
+
handle_cli_error(error)
|
|
277
|
+
|
|
278
|
+
|
|
235
279
|
app.add_typer(range_app, name="range")
|
|
236
280
|
app.add_typer(row_app, name="row")
|
|
237
281
|
app.add_typer(column_app, name="column")
|
|
@@ -288,6 +332,7 @@ def list_table(
|
|
|
288
332
|
doc_id: Optional[str] = typer.Option(None, "--doc-id", help="MaybeAI document ID."),
|
|
289
333
|
url: Optional[str] = typer.Option(None, "--url", help="MaybeAI workbook URL."),
|
|
290
334
|
uri: Optional[str] = typer.Option(None, "--uri", help="Resolved workbook URI."),
|
|
335
|
+
gid: Optional[int] = typer.Option(None, "--gid", help="Worksheet gid."),
|
|
291
336
|
worksheet_name: Optional[str] = typer.Option(None, "--worksheet-name", help="Optional worksheet name filter."),
|
|
292
337
|
output: Optional[str] = output_option(),
|
|
293
338
|
verbose: Optional[bool] = verbose_option(),
|
|
@@ -299,12 +344,16 @@ def list_table(
|
|
|
299
344
|
doc_id=doc_id,
|
|
300
345
|
url=url,
|
|
301
346
|
uri=uri,
|
|
347
|
+
gid=gid,
|
|
302
348
|
worksheet_name=worksheet_name,
|
|
303
349
|
output=output,
|
|
304
350
|
verbose=verbose,
|
|
305
351
|
timeout=timeout,
|
|
306
352
|
)
|
|
307
|
-
target = resolve_target(
|
|
353
|
+
target = resolve_target(
|
|
354
|
+
state,
|
|
355
|
+
force_gid_uri=bool(state.gid is not None and not state.worksheet_name),
|
|
356
|
+
)
|
|
308
357
|
if target.worksheet_name or target.gid is not None:
|
|
309
358
|
endpoint = "/api/v1/excel/table/metadata"
|
|
310
359
|
payload: dict[str, object] = {
|
|
@@ -132,6 +132,7 @@ def search(
|
|
|
132
132
|
verbose: Optional[bool] = verbose_option(),
|
|
133
133
|
timeout: Optional[float] = timeout_option(),
|
|
134
134
|
) -> None:
|
|
135
|
+
"""Search worksheet cells for text."""
|
|
135
136
|
try:
|
|
136
137
|
state = common_options(
|
|
137
138
|
ctx,
|
|
@@ -161,3 +162,44 @@ def search(
|
|
|
161
162
|
)
|
|
162
163
|
except Exception as error: # pragma: no cover - CLI boundary
|
|
163
164
|
handle_cli_error(error)
|
|
165
|
+
|
|
166
|
+
|
|
167
|
+
@app.command("calculate")
|
|
168
|
+
def calculate(
|
|
169
|
+
ctx: typer.Context,
|
|
170
|
+
doc_id: Optional[str] = typer.Option(None, "--doc-id", help="MaybeAI document ID."),
|
|
171
|
+
url: Optional[str] = typer.Option(None, "--url", help="MaybeAI workbook URL."),
|
|
172
|
+
uri: Optional[str] = typer.Option(None, "--uri", help="Resolved workbook URI."),
|
|
173
|
+
worksheet_name: Optional[str] = typer.Option(None, "--worksheet-name", help="Worksheet name."),
|
|
174
|
+
cell: str = typer.Option(..., "--cell", help="A1 cell address to calculate."),
|
|
175
|
+
formula: str = typer.Option(..., "--formula", help="Formula text to calculate."),
|
|
176
|
+
output: Optional[str] = output_option(),
|
|
177
|
+
verbose: Optional[bool] = verbose_option(),
|
|
178
|
+
timeout: Optional[float] = timeout_option(),
|
|
179
|
+
) -> None:
|
|
180
|
+
"""Calculate one formula expression for a worksheet cell."""
|
|
181
|
+
try:
|
|
182
|
+
state = common_options(
|
|
183
|
+
ctx,
|
|
184
|
+
doc_id=doc_id,
|
|
185
|
+
url=url,
|
|
186
|
+
uri=uri,
|
|
187
|
+
gid=None,
|
|
188
|
+
worksheet_name=worksheet_name,
|
|
189
|
+
output=output,
|
|
190
|
+
verbose=verbose,
|
|
191
|
+
timeout=timeout,
|
|
192
|
+
)
|
|
193
|
+
target = command_target(state)
|
|
194
|
+
require_worksheet(target)
|
|
195
|
+
payload = base_payload(target) | {"cellAddress": cell, "formula": formula}
|
|
196
|
+
run_post_command(
|
|
197
|
+
state=state,
|
|
198
|
+
endpoint="/api/v1/excel/calc-formula",
|
|
199
|
+
payload=payload,
|
|
200
|
+
target=target,
|
|
201
|
+
client_class=MaybeAIClient,
|
|
202
|
+
command_id="formula.calculate",
|
|
203
|
+
)
|
|
204
|
+
except Exception as error: # pragma: no cover - CLI boundary
|
|
205
|
+
handle_cli_error(error)
|
|
@@ -14,6 +14,7 @@ from ..models.workbook import CreateWorkbookRequest
|
|
|
14
14
|
from ..options import output_option, resolve_command_state, timeout_option, verbose_option
|
|
15
15
|
from ..resolver import ResolvedTarget, parse_document_target, resolve_target
|
|
16
16
|
from ._phase2 import bounded_limit
|
|
17
|
+
from .sheet import recalculate as sheet_recalculate
|
|
17
18
|
|
|
18
19
|
app = typer.Typer(help="Workbook-level operations.", no_args_is_help=True)
|
|
19
20
|
|
|
@@ -252,6 +253,29 @@ def import_workbook(
|
|
|
252
253
|
)
|
|
253
254
|
|
|
254
255
|
|
|
256
|
+
@app.command("calculate")
|
|
257
|
+
def calculate(
|
|
258
|
+
ctx: typer.Context,
|
|
259
|
+
doc_id: Optional[str] = typer.Option(None, "--doc-id", help="MaybeAI document ID."),
|
|
260
|
+
url: Optional[str] = typer.Option(None, "--url", help="MaybeAI workbook URL."),
|
|
261
|
+
uri: Optional[str] = typer.Option(None, "--uri", help="Resolved workbook URI."),
|
|
262
|
+
output: Optional[str] = output_option(),
|
|
263
|
+
verbose: Optional[bool] = verbose_option(),
|
|
264
|
+
timeout: Optional[float] = timeout_option(),
|
|
265
|
+
) -> None:
|
|
266
|
+
"""Recalculate formulas across the workbook."""
|
|
267
|
+
sheet_recalculate(
|
|
268
|
+
ctx,
|
|
269
|
+
doc_id=doc_id,
|
|
270
|
+
url=url,
|
|
271
|
+
uri=uri,
|
|
272
|
+
worksheet_name=None,
|
|
273
|
+
output=output,
|
|
274
|
+
verbose=verbose,
|
|
275
|
+
timeout=timeout,
|
|
276
|
+
)
|
|
277
|
+
|
|
278
|
+
|
|
255
279
|
@app.command("list")
|
|
256
280
|
def list_workbooks(
|
|
257
281
|
ctx: typer.Context,
|
|
@@ -0,0 +1,213 @@
|
|
|
1
|
+
"""Build deterministic XLSX and JSON fixtures for the mbs benchmark suite."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
import argparse
|
|
6
|
+
import json
|
|
7
|
+
from pathlib import Path
|
|
8
|
+
from zipfile import ZIP_DEFLATED, ZipFile
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
ROOT = Path(__file__).resolve().parent
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
def cell_ref(row: int, col: int) -> str:
|
|
15
|
+
letters = ""
|
|
16
|
+
while col:
|
|
17
|
+
col, rem = divmod(col - 1, 26)
|
|
18
|
+
letters = chr(65 + rem) + letters
|
|
19
|
+
return f"{letters}{row}"
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
def cell_xml(row: int, col: int, value: object) -> str:
|
|
23
|
+
ref = cell_ref(row, col)
|
|
24
|
+
if value is None or value == "":
|
|
25
|
+
return f'<c r="{ref}"/>'
|
|
26
|
+
if isinstance(value, (int, float)):
|
|
27
|
+
return f'<c r="{ref}"><v>{value}</v></c>'
|
|
28
|
+
escaped = (
|
|
29
|
+
str(value)
|
|
30
|
+
.replace("&", "&")
|
|
31
|
+
.replace("<", "<")
|
|
32
|
+
.replace(">", ">")
|
|
33
|
+
.replace('"', """)
|
|
34
|
+
)
|
|
35
|
+
return f'<c r="{ref}" t="inlineStr"><is><t>{escaped}</t></is></c>'
|
|
36
|
+
|
|
37
|
+
|
|
38
|
+
def sheet_xml(rows: list[list[object]]) -> str:
|
|
39
|
+
row_nodes = []
|
|
40
|
+
for row_idx, row in enumerate(rows, start=1):
|
|
41
|
+
cells = "".join(cell_xml(row_idx, col_idx, value) for col_idx, value in enumerate(row, start=1))
|
|
42
|
+
row_nodes.append(f'<row r="{row_idx}">{cells}</row>')
|
|
43
|
+
return (
|
|
44
|
+
'<?xml version="1.0" encoding="UTF-8" standalone="yes"?>'
|
|
45
|
+
'<worksheet xmlns="http://schemas.openxmlformats.org/spreadsheetml/2006/main">'
|
|
46
|
+
f"<sheetData>{''.join(row_nodes)}</sheetData>"
|
|
47
|
+
"</worksheet>"
|
|
48
|
+
)
|
|
49
|
+
|
|
50
|
+
|
|
51
|
+
def workbook_xml(sheet_names: list[str]) -> str:
|
|
52
|
+
sheets = "".join(
|
|
53
|
+
f'<sheet name="{name}" sheetId="{idx}" r:id="rId{idx}"/>'
|
|
54
|
+
for idx, name in enumerate(sheet_names, start=1)
|
|
55
|
+
)
|
|
56
|
+
return (
|
|
57
|
+
'<?xml version="1.0" encoding="UTF-8" standalone="yes"?>'
|
|
58
|
+
'<workbook xmlns="http://schemas.openxmlformats.org/spreadsheetml/2006/main" '
|
|
59
|
+
'xmlns:r="http://schemas.openxmlformats.org/officeDocument/2006/relationships">'
|
|
60
|
+
f"<sheets>{sheets}</sheets>"
|
|
61
|
+
"</workbook>"
|
|
62
|
+
)
|
|
63
|
+
|
|
64
|
+
|
|
65
|
+
def workbook_rels_xml(sheet_names: list[str]) -> str:
|
|
66
|
+
rels = "".join(
|
|
67
|
+
'<Relationship '
|
|
68
|
+
f'Id="rId{idx}" '
|
|
69
|
+
'Type="http://schemas.openxmlformats.org/officeDocument/2006/relationships/worksheet" '
|
|
70
|
+
f'Target="worksheets/sheet{idx}.xml"/>'
|
|
71
|
+
for idx, _ in enumerate(sheet_names, start=1)
|
|
72
|
+
)
|
|
73
|
+
return (
|
|
74
|
+
'<?xml version="1.0" encoding="UTF-8" standalone="yes"?>'
|
|
75
|
+
'<Relationships xmlns="http://schemas.openxmlformats.org/package/2006/relationships">'
|
|
76
|
+
f"{rels}</Relationships>"
|
|
77
|
+
)
|
|
78
|
+
|
|
79
|
+
|
|
80
|
+
def root_rels_xml() -> str:
|
|
81
|
+
return (
|
|
82
|
+
'<?xml version="1.0" encoding="UTF-8" standalone="yes"?>'
|
|
83
|
+
'<Relationships xmlns="http://schemas.openxmlformats.org/package/2006/relationships">'
|
|
84
|
+
'<Relationship Id="rId1" '
|
|
85
|
+
'Type="http://schemas.openxmlformats.org/officeDocument/2006/relationships/officeDocument" '
|
|
86
|
+
'Target="xl/workbook.xml"/>'
|
|
87
|
+
"</Relationships>"
|
|
88
|
+
)
|
|
89
|
+
|
|
90
|
+
|
|
91
|
+
def content_types_xml(sheet_names: list[str]) -> str:
|
|
92
|
+
overrides = "".join(
|
|
93
|
+
'<Override '
|
|
94
|
+
f'PartName="/xl/worksheets/sheet{idx}.xml" '
|
|
95
|
+
'ContentType="application/vnd.openxmlformats-officedocument.spreadsheetml.worksheet+xml"/>'
|
|
96
|
+
for idx, _ in enumerate(sheet_names, start=1)
|
|
97
|
+
)
|
|
98
|
+
return (
|
|
99
|
+
'<?xml version="1.0" encoding="UTF-8" standalone="yes"?>'
|
|
100
|
+
'<Types xmlns="http://schemas.openxmlformats.org/package/2006/content-types">'
|
|
101
|
+
'<Default Extension="rels" ContentType="application/vnd.openxmlformats-package.relationships+xml"/>'
|
|
102
|
+
'<Default Extension="xml" ContentType="application/xml"/>'
|
|
103
|
+
'<Override PartName="/xl/workbook.xml" '
|
|
104
|
+
'ContentType="application/vnd.openxmlformats-officedocument.spreadsheetml.sheet.main+xml"/>'
|
|
105
|
+
f"{overrides}</Types>"
|
|
106
|
+
)
|
|
107
|
+
|
|
108
|
+
|
|
109
|
+
def write_xlsx(path: Path, sheets: dict[str, list[list[object]]]) -> None:
|
|
110
|
+
sheet_names = list(sheets)
|
|
111
|
+
path.parent.mkdir(parents=True, exist_ok=True)
|
|
112
|
+
with ZipFile(path, "w", ZIP_DEFLATED) as zf:
|
|
113
|
+
zf.writestr("[Content_Types].xml", content_types_xml(sheet_names))
|
|
114
|
+
zf.writestr("_rels/.rels", root_rels_xml())
|
|
115
|
+
zf.writestr("xl/workbook.xml", workbook_xml(sheet_names))
|
|
116
|
+
zf.writestr("xl/_rels/workbook.xml.rels", workbook_rels_xml(sheet_names))
|
|
117
|
+
for idx, name in enumerate(sheet_names, start=1):
|
|
118
|
+
zf.writestr(f"xl/worksheets/sheet{idx}.xml", sheet_xml(sheets[name]))
|
|
119
|
+
|
|
120
|
+
|
|
121
|
+
def write_json(path: Path, data: object) -> None:
|
|
122
|
+
path.write_text(json.dumps(data, ensure_ascii=False, indent=2) + "\n", encoding="utf-8")
|
|
123
|
+
|
|
124
|
+
|
|
125
|
+
def build(output_dir: Path = ROOT) -> list[Path]:
|
|
126
|
+
output_dir.mkdir(parents=True, exist_ok=True)
|
|
127
|
+
created: list[Path] = []
|
|
128
|
+
|
|
129
|
+
simple_values = [
|
|
130
|
+
["metric", "Q1", "Q2"],
|
|
131
|
+
["revenue", 100, 120],
|
|
132
|
+
["cost", 40, 55],
|
|
133
|
+
]
|
|
134
|
+
rows = [
|
|
135
|
+
{"metric": "gross_profit", "Q1": 60, "Q2": 65},
|
|
136
|
+
{"metric": "margin", "Q1": "60%", "Q2": "54%"},
|
|
137
|
+
]
|
|
138
|
+
write_json(output_dir / "simple-values.json", simple_values)
|
|
139
|
+
write_json(output_dir / "rows.json", rows)
|
|
140
|
+
created.extend([output_dir / "simple-values.json", output_dir / "rows.json"])
|
|
141
|
+
|
|
142
|
+
write_xlsx(
|
|
143
|
+
output_dir / "simple-grid.xlsx",
|
|
144
|
+
{
|
|
145
|
+
"BenchData": [
|
|
146
|
+
["metric", "Q1", "Q2", "Q3"],
|
|
147
|
+
["revenue", 100, 120, 140],
|
|
148
|
+
["cost", 40, 55, 60],
|
|
149
|
+
["profit", 60, 65, 80],
|
|
150
|
+
]
|
|
151
|
+
},
|
|
152
|
+
)
|
|
153
|
+
created.append(output_dir / "simple-grid.xlsx")
|
|
154
|
+
|
|
155
|
+
write_xlsx(
|
|
156
|
+
output_dir / "flat-records.xlsx",
|
|
157
|
+
{
|
|
158
|
+
"Records": [
|
|
159
|
+
["id", "customer", "amount", "status"],
|
|
160
|
+
[1, "Acme", 120.5, "paid"],
|
|
161
|
+
[2, "Beta", 340.0, "open"],
|
|
162
|
+
[3, "Core", 88.75, "paid"],
|
|
163
|
+
[4, "Delta", 901.1, "open"],
|
|
164
|
+
]
|
|
165
|
+
},
|
|
166
|
+
)
|
|
167
|
+
created.append(output_dir / "flat-records.xlsx")
|
|
168
|
+
|
|
169
|
+
write_xlsx(
|
|
170
|
+
output_dir / "mixed-engine.xlsx",
|
|
171
|
+
{
|
|
172
|
+
"RecordsLarge": [
|
|
173
|
+
["id", "amount", "status"],
|
|
174
|
+
*[[idx, idx * 3, "paid" if idx % 2 else "open"] for idx in range(1, 26)],
|
|
175
|
+
],
|
|
176
|
+
"Cover": [["title", "Mixed engine fixture"], ["owner", "benchmark"]],
|
|
177
|
+
"Model": [["metric", "value"], ["revenue", 1200], ["cost", 700]],
|
|
178
|
+
"Events": [["event_id", "value"], [1, 20], [2, 22], [3, 25]],
|
|
179
|
+
},
|
|
180
|
+
)
|
|
181
|
+
created.append(output_dir / "mixed-engine.xlsx")
|
|
182
|
+
|
|
183
|
+
multi_table_rows = [
|
|
184
|
+
["T01 Revenue Summary", "", "", "", ""],
|
|
185
|
+
["", "", "", "", ""],
|
|
186
|
+
["metric", "Q1", "Q2", "Q3", "total"],
|
|
187
|
+
["revenue", 100, 120, 140, 360],
|
|
188
|
+
["cost", 40, 55, 60, 155],
|
|
189
|
+
["profit", 60, 65, 80, 205],
|
|
190
|
+
["", "", "", "", ""],
|
|
191
|
+
["", "", "", "", ""],
|
|
192
|
+
["ratio", "Q1", "Q2", "Q3", "trend"],
|
|
193
|
+
["margin", "60%", "54%", "57%", "up"],
|
|
194
|
+
["cost_ratio", "40%", "46%", "43%", "down"],
|
|
195
|
+
]
|
|
196
|
+
write_xlsx(output_dir / "multi-table-layout.xlsx", {"MultiTable": multi_table_rows})
|
|
197
|
+
created.append(output_dir / "multi-table-layout.xlsx")
|
|
198
|
+
|
|
199
|
+
return created
|
|
200
|
+
|
|
201
|
+
|
|
202
|
+
def main() -> int:
|
|
203
|
+
parser = argparse.ArgumentParser()
|
|
204
|
+
parser.add_argument("--output-dir", type=Path, default=ROOT)
|
|
205
|
+
args = parser.parse_args()
|
|
206
|
+
created = build(args.output_dir)
|
|
207
|
+
for path in created:
|
|
208
|
+
print(path)
|
|
209
|
+
return 0
|
|
210
|
+
|
|
211
|
+
|
|
212
|
+
if __name__ == "__main__":
|
|
213
|
+
raise SystemExit(main())
|
|
Binary file
|
|
Binary file
|
|
Binary file
|