dsh-office-kit 0.4.1 → 0.4.3
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.
- package/assets/skills/excel-live-control/SKILL.md +120 -148
- package/assets/skills/excel-live-control/container_tools/live_excel.py +9 -0
- package/assets/skills/excel-live-control/domain_guidance/financial_models.md +42 -58
- package/assets/skills/excel-live-control/domain_guidance/healthcare.md +36 -42
- package/assets/skills/excel-live-control/domain_guidance/marketing_advertising.md +33 -34
- package/assets/skills/excel-live-control/domain_guidance/scientific_research.md +34 -36
- package/assets/skills/excel-live-control/references/office-automation.md +127 -153
- package/assets/skills/excel-live-control/references/read_only_qna.md +40 -45
- package/assets/skills/excel-live-control/style_guidelines.md +56 -64
- package/assets/skills/excel-live-control/workflows/create_workflows.md +40 -47
- package/assets/skills/excel-live-control/workflows/edit_workflows.md +37 -45
- package/assets/skills/office-documents/SKILL.md +24 -60
- package/assets/skills/office-documents/examples/end_to_end_smoke_test.md +5 -5
- package/assets/skills/office-documents/ooxml/comments.md +8 -8
- package/assets/skills/office-documents/ooxml/hyperlinks_and_fields.md +10 -10
- package/assets/skills/office-documents/ooxml/rels_and_content_types.md +7 -7
- package/assets/skills/office-documents/ooxml/tracked_changes.md +12 -12
- package/assets/skills/office-documents/references/docx-structure.md +11 -13
- package/assets/skills/office-documents/references/template-elicitation.md +11 -13
- package/assets/skills/office-documents/references/writing-quality.md +20 -20
- package/assets/skills/office-documents/tasks/accessibility_a11y.md +16 -22
- package/assets/skills/office-documents/tasks/captions_crossrefs.md +23 -26
- package/assets/skills/office-documents/tasks/clean_tracked_changes.md +17 -22
- package/assets/skills/office-documents/tasks/comments_manage.md +9 -11
- package/assets/skills/office-documents/tasks/compare_diff.md +10 -16
- package/assets/skills/office-documents/tasks/create_edit.md +14 -16
- package/assets/skills/office-documents/tasks/equations.md +17 -28
- package/assets/skills/office-documents/tasks/fields_update.md +14 -16
- package/assets/skills/office-documents/tasks/fixtures_edge_cases.md +19 -33
- package/assets/skills/office-documents/tasks/footnotes_endnotes.md +12 -14
- package/assets/skills/office-documents/tasks/forms_content_controls.md +16 -20
- package/assets/skills/office-documents/tasks/headings_numbering.md +17 -22
- package/assets/skills/office-documents/tasks/images_figures.md +15 -25
- package/assets/skills/office-documents/tasks/multi_doc_merge.md +13 -23
- package/assets/skills/office-documents/tasks/navigation_internal_links.md +18 -21
- package/assets/skills/office-documents/tasks/privacy_scrub_metadata.md +16 -22
- package/assets/skills/office-documents/tasks/protection_restrict_editing.md +19 -21
- package/assets/skills/office-documents/tasks/read_review.md +9 -20
- package/assets/skills/office-documents/tasks/redaction_anonymization.md +17 -18
- package/assets/skills/office-documents/tasks/sections_layout.md +16 -23
- package/assets/skills/office-documents/tasks/style_lint_normalize.md +18 -22
- package/assets/skills/office-documents/tasks/tables_spreadsheets.md +21 -31
- package/assets/skills/office-documents/tasks/templates_style_packs.md +17 -21
- package/assets/skills/office-documents/tasks/toc_workflow.md +10 -14
- package/assets/skills/office-documents/tasks/verify_render.md +11 -23
- package/assets/skills/office-documents/tasks/watermarks_background.md +11 -15
- package/assets/skills/office-documents/troubleshooting/libreoffice_headless.md +9 -9
- package/assets/skills/office-documents/troubleshooting/run_splitting.md +12 -12
- package/assets/skills/office-pdf/SKILL.md +36 -51
- package/assets/skills/office-pdf/references/form-fields.md +20 -20
- package/assets/skills/office-pdf/references/geometry.md +10 -10
- package/assets/skills/office-pdf/references/layout-quality.md +13 -13
- package/assets/skills/office-pdf/references/limits.md +12 -56
- package/assets/skills/office-pdf/tasks/annotations.md +5 -5
- package/assets/skills/office-pdf/tasks/create.md +6 -9
- package/assets/skills/office-pdf/tasks/edit-pages.md +14 -14
- package/assets/skills/office-pdf/tasks/forms.md +15 -15
- package/assets/skills/office-pdf/tasks/read.md +9 -9
- package/assets/skills/office-pdf/tasks/redact.md +18 -18
- package/assets/skills/office-pdf/tasks/stamp.md +10 -10
- package/assets/skills/office-presentations/SKILL.md +71 -91
- package/assets/skills/office-presentations/container_tools/render_slides.py +1 -1
- package/assets/skills/office-presentations/domain_guidance/finance.md +16 -16
- package/assets/skills/office-presentations/recipes/README.md +24 -35
- package/assets/skills/office-presentations/recipes/autolayout.md +12 -13
- package/assets/skills/office-presentations/recipes/connectors.md +11 -12
- package/assets/skills/office-presentations/recipes/images.md +11 -12
- package/assets/skills/office-presentations/recipes/master-and-layouts.md +18 -18
- package/assets/skills/office-presentations/recipes/native-charts.md +8 -8
- package/assets/skills/office-presentations/recipes/rich-text-runs.md +13 -13
- package/assets/skills/office-presentations/recipes/speaker-notes.md +7 -7
- package/assets/skills/office-presentations/recipes/tables.md +14 -14
- package/assets/skills/office-presentations/recipes/textbox-at-coordinate.md +9 -9
- package/assets/skills/office-presentations/recipes/theme-tokens.md +7 -8
- package/assets/skills/office-presentations/references/authoring-workflow.md +38 -43
- package/assets/skills/office-presentations/references/finalization.md +10 -12
- package/assets/skills/office-presentations/references/native-bullets.md +14 -18
- package/assets/skills/office-presentations/references/native-evidence.md +18 -32
- package/assets/skills/office-presentations/references/style-guidelines.md +27 -32
- package/assets/skills/office-presentations/references/templates.md +31 -60
- package/assets/skills/office-presentations/routing/google_slides.md +4 -4
- package/assets/skills/office-presentations/tasks/apply-annotations.md +17 -17
- package/assets/skills/office-presentations/tasks/charts-tables-notes.md +20 -23
- package/assets/skills/office-presentations/tasks/create-deck.md +19 -19
- package/assets/skills/office-presentations/tasks/edit-deck.md +14 -16
- package/assets/skills/office-presentations/tasks/inspect-and-verify.md +31 -34
- package/assets/skills/office-presentations/tasks/template-following.md +23 -28
- package/assets/skills/office-spreadsheets/SKILL.md +32 -39
- package/assets/skills/office-spreadsheets/recipes/charts.md +8 -26
- package/assets/skills/office-spreadsheets/recipes/conditional-formats.md +9 -26
- package/assets/skills/office-spreadsheets/recipes/data-tables.md +7 -23
- package/assets/skills/office-spreadsheets/recipes/named-ranges.md +8 -21
- package/assets/skills/office-spreadsheets/recipes/number-formats.md +7 -23
- package/assets/skills/office-spreadsheets/recipes/sparklines.md +7 -20
- package/assets/skills/office-spreadsheets/references/charts.md +15 -34
- package/assets/skills/office-spreadsheets/references/domain/financial-models.md +10 -25
- package/assets/skills/office-spreadsheets/references/domain/healthcare.md +8 -19
- package/assets/skills/office-spreadsheets/references/domain/marketing.md +8 -22
- package/assets/skills/office-spreadsheets/references/domain/research.md +8 -19
- package/assets/skills/office-spreadsheets/references/formula-patterns.md +20 -50
- package/assets/skills/office-spreadsheets/references/style-guidelines.md +19 -44
- package/assets/skills/office-spreadsheets/references/workbook-structure.md +12 -35
- package/assets/skills/office-spreadsheets/routing/google-sheets.md +11 -26
- package/assets/skills/office-spreadsheets/tasks/analyze-workbook.md +10 -25
- package/assets/skills/office-spreadsheets/tasks/annotations.md +12 -30
- package/assets/skills/office-spreadsheets/tasks/create-workbook.md +5 -5
- package/assets/skills/office-spreadsheets/tasks/diff-workbooks.md +2 -2
- package/assets/skills/office-spreadsheets/tasks/edit-workbook.md +5 -5
- package/assets/skills/office-spreadsheets/tasks/inspect-workbook.md +9 -9
- package/assets/skills/office-spreadsheets/tasks/recalc-and-check.md +10 -30
- package/assets/skills/shared/annotation-recipes.md +98 -120
- package/assets/skills/shared/common-workflow.md +70 -80
- package/dist/index.js +105 -29
- package/package.json +1 -1
- package/postinstall.mjs +58 -17
- package/python/office_cli.py +70 -7
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
# Task: inspect and verify a deck
|
|
2
2
|
|
|
3
|
-
Verification is reading the
|
|
4
|
-
|
|
3
|
+
Verification is reading the file back and looking at it. Each tool prints one
|
|
4
|
+
JSON object; `passed` is the decision.
|
|
5
5
|
|
|
6
6
|
## 1. What is in the file
|
|
7
7
|
|
|
@@ -10,8 +10,7 @@ print one JSON object each; `passed` is the decision.
|
|
|
10
10
|
```
|
|
11
11
|
|
|
12
12
|
Per slide: title, all text, tables as rows, charts with type/title/categories/
|
|
13
|
-
series, image count.
|
|
14
|
-
meant to write is the content that was written.
|
|
13
|
+
series, image count. Cheapest check that what was written is what was intended.
|
|
15
14
|
|
|
16
15
|
## 2. Is the package coherent
|
|
17
16
|
|
|
@@ -19,10 +18,10 @@ meant to write is the content that was written.
|
|
|
19
18
|
"<python>" "<skill>/container_tools/inspect_presentation_package_integrity.py" --input deck.pptx
|
|
20
19
|
```
|
|
21
20
|
|
|
22
|
-
Checks required parts, slide/master presence, dangling relationships,
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
21
|
+
Checks required parts, slide/master presence, dangling relationships, unparseable
|
|
22
|
+
XML, slide-count drift, duplicate parts; reports part counts (`slides`, `layouts`,
|
|
23
|
+
`masters`, `charts`, `notesSlides`, `media`, `embeddings`). Zero `embeddings` on a
|
|
24
|
+
chart means lost data.
|
|
26
25
|
|
|
27
26
|
## 3. Does the layout hold
|
|
28
27
|
|
|
@@ -30,13 +29,12 @@ does not parse, slide count drift and duplicate parts, then reports part counts
|
|
|
30
29
|
"<python>" "<skill>/container_tools/inspect_presentation_layout_geometry.py" --input deck.pptx --json-out geometry.json
|
|
31
30
|
```
|
|
32
31
|
|
|
33
|
-
`fail`
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
Add `--strict` to make review findings blocking for a formal deck.
|
|
32
|
+
`fail` blocks delivery: shape outside the slide, degenerate size, text that
|
|
33
|
+
cannot fit its frame, table outside the slide, chart or table covered by another
|
|
34
|
+
shape. `review` needs a decision: text overlap, empty placeholder, title under
|
|
35
|
+
28 pt, body under 14 pt, heading ending in a full stop, connector across text, a
|
|
36
|
+
chart duplicating a table's labels. `--strict` makes review blocking for a formal
|
|
37
|
+
deck.
|
|
40
38
|
|
|
41
39
|
## 4. Are the numbers real
|
|
42
40
|
|
|
@@ -51,11 +49,11 @@ Add `--strict` to make review findings blocking for a formal deck.
|
|
|
51
49
|
- Quantitative charts: every declared chart must be a real chart part with a
|
|
52
50
|
readable series, and in the default `portable` mode must carry its embedded
|
|
53
51
|
workbook.
|
|
54
|
-
- Table arithmetic: only declared totals are enforced,
|
|
55
|
-
|
|
56
|
-
|
|
52
|
+
- Table arithmetic: only declared totals are enforced, against displayed
|
|
53
|
+
rounding; non-additive columns (unit price, ratio, percent) are skipped rather
|
|
54
|
+
than summed wrongly.
|
|
57
55
|
|
|
58
|
-
`totals.json` names the contract
|
|
56
|
+
`totals.json` names the contract:
|
|
59
57
|
|
|
60
58
|
```json
|
|
61
59
|
{ "contracts": [{ "slide": 4, "table": 1, "total_row": 6, "columns": [1, 2] }] }
|
|
@@ -67,32 +65,31 @@ Add `--strict` to make review findings blocking for a formal deck.
|
|
|
67
65
|
"<python>" "<skill>/container_tools/render_slides.py" --input deck.pptx --out-dir preview
|
|
68
66
|
```
|
|
69
67
|
|
|
70
|
-
Writes `slide-NN.png` plus `render.json
|
|
71
|
-
|
|
68
|
+
Writes `slide-NN.png` plus `render.json`; `read_image` every PNG — an unlooked-at
|
|
69
|
+
render is not verification.
|
|
72
70
|
|
|
73
|
-
|
|
71
|
+
Whole deck at once:
|
|
74
72
|
|
|
75
73
|
```bash
|
|
76
74
|
"<python>" "<skill>/template_following_scripts/make_contact_sheet.py" --dir preview --out sheet.png --columns 3
|
|
77
75
|
```
|
|
78
76
|
|
|
79
|
-
|
|
77
|
+
Or `"<skill>/container_tools/create_montage.py` with an explicit file list.
|
|
80
78
|
|
|
81
|
-
Look for: clipped text, overlapping blocks, a title
|
|
82
|
-
|
|
83
|
-
title.
|
|
79
|
+
Look for: clipped text, overlapping blocks, a reflowed title, a chart point
|
|
80
|
+
invisible at thumbnail size, a stretched photograph, a slide with no title.
|
|
84
81
|
|
|
85
82
|
## 6. When nothing can render
|
|
86
83
|
|
|
87
|
-
|
|
88
|
-
`
|
|
89
|
-
PyMuPDF geometry preview
|
|
90
|
-
|
|
91
|
-
|
|
84
|
+
Without the DSH LibreOffice kit (`office_status` → `renderers.available: false`,
|
|
85
|
+
with a `remedy`), `render_slides.py` falls back to `--engine draft`, a
|
|
86
|
+
PyMuPDF geometry preview: placement, overflow, overlap — not typography or theme
|
|
87
|
+
chrome. Say so: "verified as a geometry preview" is honest; "looks great" from an
|
|
88
|
+
approximate render is not.
|
|
92
89
|
|
|
93
|
-
If even the preview cannot run, the
|
|
94
|
-
|
|
95
|
-
|
|
90
|
+
If even the preview cannot run, report the failing command, the slide count from
|
|
91
|
+
`pptx read`, and the gate results; name the renderer to install for a visual
|
|
92
|
+
check.
|
|
96
93
|
|
|
97
94
|
## Gate summary
|
|
98
95
|
|
|
@@ -1,8 +1,7 @@
|
|
|
1
1
|
# Task: follow a supplied template
|
|
2
2
|
|
|
3
3
|
Use this when the user hands over a `.pptx` and says "use our template" or "match
|
|
4
|
-
this deck". The design is
|
|
5
|
-
own slides.
|
|
4
|
+
this deck". The design is decided; put content into its own slides.
|
|
6
5
|
|
|
7
6
|
## 1. Inspect
|
|
8
7
|
|
|
@@ -10,16 +9,14 @@ own slides.
|
|
|
10
9
|
"<python>" "<skill>/template_following_scripts/inspect_template_deck.py" --template brand.pptx --out-dir template-inspect
|
|
11
10
|
```
|
|
12
11
|
|
|
13
|
-
Outputs:
|
|
14
|
-
|
|
15
12
|
- `template-inspect.ndjson` — one JSON line per slide: layout name, layout
|
|
16
13
|
placeholders, slide placeholders (index, kind, rect in inches, text), shape
|
|
17
|
-
counts, text excerpt,
|
|
14
|
+
counts, text excerpt, full-bleed background art flag.
|
|
18
15
|
- `template-manifest.json` — slide size, aspect ratio, layout names, theme font
|
|
19
|
-
names,
|
|
16
|
+
names, `backgroundArtSlides`.
|
|
20
17
|
|
|
21
|
-
Read `backgroundArtSlides`
|
|
22
|
-
|
|
18
|
+
Read `backgroundArtSlides` first: those slides already carry artwork, so a new
|
|
19
|
+
opaque panel covers it.
|
|
23
20
|
|
|
24
21
|
## 2. Plan
|
|
25
22
|
|
|
@@ -65,13 +62,13 @@ Write `frame-map.json`:
|
|
|
65
62
|
|
|
66
63
|
Allowed actions: `rewrite`, `rewrite-and-reposition`, `replace`, `delete`,
|
|
67
64
|
`keep`, `fill-placeholder`. `target.zone` is `[x, y, w, h]` in 0–1 slide
|
|
68
|
-
fractions, used with `rewrite-and-reposition`. Elements whose `role` is
|
|
69
|
-
`
|
|
70
|
-
|
|
65
|
+
fractions, used with `rewrite-and-reposition`. Elements whose `role` is `brand`,
|
|
66
|
+
`logo`, `bumper`, `divider`, `section`, `chrome` or `footer` must stay `keep` —
|
|
67
|
+
they are the template.
|
|
71
68
|
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
69
|
+
New shapes are opt-in: set `newPrimitives.allowed` and justify it. The validator
|
|
70
|
+
rejects additions without that, because "one extra box" is how a branded deck
|
|
71
|
+
stops matching brand.
|
|
75
72
|
|
|
76
73
|
## 3. Validate
|
|
77
74
|
|
|
@@ -79,10 +76,10 @@ box" is how a branded deck stops matching brand.
|
|
|
79
76
|
"<python>" "<skill>/template_following_scripts/validate_template_plan.py" --plan frame-map.json
|
|
80
77
|
```
|
|
81
78
|
|
|
82
|
-
Rejects
|
|
79
|
+
Rejects unknown action, edited preserved role, zone outside 0–1, non-sequential
|
|
83
80
|
slide numbers, missing justification for new primitives, and duplicate source
|
|
84
|
-
slides (a warning
|
|
85
|
-
|
|
81
|
+
slides (a warning: shared artwork — fine for a repeated divider, usually a
|
|
82
|
+
copy-paste mistake otherwise).
|
|
86
83
|
|
|
87
84
|
## 4. Build
|
|
88
85
|
|
|
@@ -90,12 +87,11 @@ repeated section divider, usually a copy-paste mistake otherwise).
|
|
|
90
87
|
"<python>" "<skill>/template_following_scripts/prepare_template_starter_deck.py" --template brand.pptx --plan frame-map.json --out starter.pptx
|
|
91
88
|
```
|
|
92
89
|
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
template has no family for a slide you need.
|
|
90
|
+
Keeps only the mapped source slides in plan order and applies the actions. It
|
|
91
|
+
refuses duplicate source slides (reuses slides rather than cloning parts). The
|
|
92
|
+
report lists applied actions and any empty placeholder. Extend with
|
|
93
|
+
`container_tools/add_slides.py` or the recipes when the template has no family
|
|
94
|
+
for a slide you need.
|
|
99
95
|
|
|
100
96
|
## 5. Check fidelity
|
|
101
97
|
|
|
@@ -118,8 +114,8 @@ template has no family for a slide you need.
|
|
|
118
114
|
}
|
|
119
115
|
```
|
|
120
116
|
|
|
121
|
-
Both print `passed`.
|
|
122
|
-
|
|
117
|
+
Both print `passed`. No requirements → deliberate no-op pass: pass a file or you
|
|
118
|
+
have checked nothing.
|
|
123
119
|
|
|
124
120
|
## 6. Look at it
|
|
125
121
|
|
|
@@ -127,6 +123,5 @@ no-op pass — pass a requirements file or you have not checked anything.
|
|
|
127
123
|
"<python>" "<skill>/container_tools/render_slides.py" --input starter.pptx --out-dir starter-preview
|
|
128
124
|
```
|
|
129
125
|
|
|
130
|
-
Compare
|
|
131
|
-
in the wrong family,
|
|
132
|
-
exists to catch.
|
|
126
|
+
Compare with the source slides: this catches an overflowed placeholder, a title
|
|
127
|
+
in the wrong family, a panel over the corner logo.
|
|
@@ -1,24 +1,24 @@
|
|
|
1
1
|
---
|
|
2
|
-
description: Create, inspect, edit, recalculate, and verify Excel
|
|
2
|
+
description: Create, inspect, edit, recalculate, render, and verify Excel spreadsheets (.xlsx/.csv) with openpyxl recipes, formula patterns, and the bundled Office helper CLI.
|
|
3
3
|
---
|
|
4
4
|
|
|
5
5
|
# Spreadsheets (.xlsx)
|
|
6
6
|
|
|
7
|
-
A
|
|
7
|
+
A cell holds a value, a format, and possibly a formula string. openpyxl writes all three and evaluates none: a new formula has no cached result, so `xlsx read` shows `=SUM(B2:B9)` where the user expects `1,950,400`. Only an engine calculates — `xlsx recalc` through the DSH LibreOffice kit (cross-platform), or the app the user opens the file in. Work in that order: write values and formulas, read back for structure, recalculate for numbers, render for appearance.
|
|
8
8
|
|
|
9
|
-
| User job | What is
|
|
9
|
+
| User job | What is happening | Fast path |
|
|
10
10
|
| --- | --- | --- |
|
|
11
|
-
| Build a new workbook | Cells written
|
|
11
|
+
| Build a new workbook | Cells written from A1; `=`-prefixed strings become formulas; styles are per-cell; sheets get names, widths, freeze panes, merges | `xlsx create` spec, `tasks/create-workbook.md` |
|
|
12
12
|
| Change cells in an existing file | Only the cells you name change; a re-save can drop objects openpyxl does not model (pivots, foreign charts) | `container_tools/set_cells.py` and friends, `tasks/edit-workbook.md` |
|
|
13
|
-
|
|
|
14
|
-
| Inventory a file you did not write |
|
|
15
|
-
| Compare two versions | Cell-by-cell walk
|
|
16
|
-
| Prove the numbers are real | Recalculate
|
|
17
|
-
| Format numbers, conditions, names, charts, tables | Number formats, conditional-formatting rules, defined names, native chart objects, Excel Tables | `recipes/*.md` (openpyxl patterns
|
|
13
|
+
| Workbook open in Excel/WPS must change live | The target is the window, not the file: unsaved user edits are in play and a COM write is not transactional. **Windows only** — on macOS/Linux the live tool exits 3 with a remedy; edit the file instead | `../excel-live-control/SKILL.md` |
|
|
14
|
+
| Inventory a file you did not write | Sheets, used range, formulas vs values, merged ranges, conditional formats, defined names, charts | `container_tools/inspect_workbook.py` |
|
|
15
|
+
| Compare two versions | Cell-by-cell walk at formula-, format-, and structure-aware levels | `container_tools/diff_workbooks.py`, `tasks/diff-workbooks.md` |
|
|
16
|
+
| Prove the numbers are real | Recalculate, then check every formula cell has a value, no error, no dangling sheet | `xlsx recalc` + `container_tools/recalc_gate.py`, `tasks/recalc-and-check.md` |
|
|
17
|
+
| Format numbers, conditions, names, charts, tables | Number formats, conditional-formatting rules, defined names, native chart objects, Excel Tables | `recipes/*.md` (runnable openpyxl patterns) |
|
|
18
18
|
| Answer a question about a file | Read values, not formulas; quote the cells you read | `tasks/analyze-workbook.md` |
|
|
19
19
|
| Spreadsheet lives in Google Sheets | Not this runtime — see `routing/google-sheets.md` | — |
|
|
20
20
|
|
|
21
|
-
Sheet and range semantics
|
|
21
|
+
Sheet and range semantics come before any edit: `B4:D9` is a rectangle, `'Build'!E14` is a cross-sheet reference whose sheet name needs quotes when it contains spaces, and a merged range is one value plus a lot of emptiness. Number formats are display only — the stored value keeps full precision, so never round data to make it look right.
|
|
22
22
|
|
|
23
23
|
## Before any file work
|
|
24
24
|
|
|
@@ -29,54 +29,47 @@ Sheet and range semantics matter before any edit: `B4:D9` is a rectangle, `'Buil
|
|
|
29
29
|
|
|
30
30
|
## Delivery
|
|
31
31
|
|
|
32
|
-
|
|
32
|
+
Hand the finished file to the user with the `present` tool, then summarise in one sentence what changed. A bare path is not delivery: the chat transcript renders `.docx`/`.pptx`/`.xlsx`/`.pdf` links as "二进制文件,无法显示改动" and the user cannot open them.
|
|
33
33
|
|
|
34
34
|
- Creating a file: write it into the user's workspace, verify it, then `present`.
|
|
35
|
-
- Editing an existing file: never overwrite the original. Write `<original>-edited.<ext>` (or
|
|
35
|
+
- Editing an existing file: never overwrite the original. Write `<original>-edited.<ext>` (or the name the user asked for), verify it, `present` it, and state both paths.
|
|
36
36
|
|
|
37
37
|
## Annotations
|
|
38
38
|
|
|
39
|
-
The user may reply with an `office-annotations` block: `[{ page, text, requirement, region: { left, top, width, height } }]`. `region` is normalised 0–1 against the rendered preview page
|
|
39
|
+
The user may reply with an `office-annotations` block: `[{ page, text, requirement, region: { left, top, width, height } }]`. `region` is normalised 0–1 against the rendered preview page; `requirement` is free-form natural language. Treat it as a change request, not editor state.
|
|
40
40
|
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
For xlsx the payload is `{ target, fileName, text: "", requirement, region, imageName, unmapped: true }`: no `page`, no `sheet`, no `range`. The preview client cannot resolve a cell from the drag and refuses to fake one. So:
|
|
44
|
-
|
|
45
|
-
1. `read_image` the attachment named `imageName` — it is a screenshot of the area the user marked. Note visible headers, row labels, and values.
|
|
46
|
-
2. Run `"<python>" "<cli>" xlsx read --input "<file>" --json-out "<dump>.json"` and match those values to sheets and A1 addresses; `container_tools/inspect_workbook.py` adds formulas, merged ranges, and names when the match is ambiguous.
|
|
47
|
-
3. State the resolved sheet and range back to the user before editing ("`Pipeline!D2:D13`, the Amount column"), and ask if two sheets or columns match.
|
|
48
|
-
4. Never convert region pixels into a cell address. Full recipe: `tasks/annotations.md` and `"<shared>/annotation-recipes.md"`.
|
|
41
|
+
For xlsx the payload is `{ target, fileName, text: "", requirement, region, imageName, unmapped: true }`: no `page`, no `sheet`, no `range`, because the preview client cannot resolve a cell from the drag and refuses to fake one. So: `read_image` the attachment named `imageName`, match the visible values to sheets and A1 addresses with `"<python>" "<cli>" xlsx read --input "<file>" --json-out "<dump>.json"`, state the resolved sheet and range before editing ("`Pipeline!D2:D13`, the Amount column"), ask when two sheets or columns match, then apply the smallest edit that satisfies `requirement`, re-render, and confirm the region now looks right. Never convert region pixels into a cell address. When the payload cannot be mapped to a concrete object, do not guess silently — say what you could not locate. Full recipe: `tasks/annotations.md` and `"<shared>/annotation-recipes.md"`.
|
|
49
42
|
|
|
50
43
|
## Verify
|
|
51
44
|
|
|
52
45
|
Three checks, in this order, after every mutation:
|
|
53
46
|
|
|
54
|
-
1. **Structure and values.** `"<python>" "<cli>" xlsx read --input "<file>"` proves the cells you wrote are where you wrote them
|
|
55
|
-
2. **Numbers.** If
|
|
56
|
-
3. **Appearance.** `"<python>" "<cli>" xlsx render --input "<file>" --out-dir "<render-dir>"`, then `read_image` each PNG. Check titles and labels, `###` columns, clipped numbers, charts cut off by the print area, plots with no categories, legends naming `Series 1`, conditional formatting that never fired, and blank pages.
|
|
47
|
+
1. **Structure and values.** `"<python>" "<cli>" xlsx read --input "<file>"` proves the cells you wrote are where you wrote them. `container_tools/inspect_workbook.py` proves charts, tables, conditional formats, names, and merges survived.
|
|
48
|
+
2. **Numbers.** If a formula changed or was added, run `"<python>" "<cli>" xlsx recalc --input "<file>" --out "<file>"`, then `container_tools/recalc_gate.py --input "<file>"`. Recalc fails only when the kit is missing, and `recalc_gate.py` then reports `unverified` — say that plainly instead of presenting an empty cache as a result. Flags, exit codes, and what the gate reports: `tasks/recalc-and-check.md`.
|
|
49
|
+
3. **Appearance.** `"<python>" "<cli>" xlsx render --input "<file>" --out-dir "<render-dir>"`, then `read_image` each PNG. Check titles and labels, `###` columns, clipped numbers, charts cut off by the print area, plots with no categories, legends naming `Series 1`, conditional formatting that never fired, and blank pages. When the LibreOffice kit is missing the command fails explicitly: report the file as structurally verified but visually unchecked, and offer to rebuild from a spec.
|
|
57
50
|
|
|
58
51
|
Two or three render/fix passes are normal. If a defect survives three, say what still looks wrong.
|
|
59
52
|
|
|
60
53
|
## Mutation tools
|
|
61
54
|
|
|
62
|
-
The CLI is read-only for `.xlsx`: `create`, `read`, `render`, `status`, `recalc
|
|
55
|
+
The CLI is read-only for `.xlsx`: `create`, `read`, `render`, `status`, `recalc`, and nothing else. Every change to an existing workbook goes through these openpyxl tools. Each reads `--input`, writes `<input>-edited.xlsx` unless you pass `--out`, prints one JSON object on stdout, and exits non-zero with a JSON `error` on stderr when it refuses.
|
|
63
56
|
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
57
|
+
- `sheet_ops.py` — `--add NAME`, `--rename OLD=NEW`, `--delete NAME`, `--order "A,B,C"`. Renaming a sheet does not update the formulas that point at it; deleting the last sheet is refused.
|
|
58
|
+
- `set_cells.py` — `--sheet S --set B2=42 --set C3="=SUM(C2:C9)"`, or `--spec cells.json`. It does not parse dates for you: write `2026-01-31` and format the cell, or pass a serial.
|
|
59
|
+
- `append_rows.py` — `--sheet S --rows '[[...],[...]]'`, or `--rows-file rows.json`. It does not extend an Excel Table, copy the row above's formula, or grow charts and conditional formats.
|
|
60
|
+
- `style_cells.py` — `--sheet S --range A1:D1 --style '{"bold":true}'`, or `--spec styles.json`. No autofit, and it will not restyle cells outside the range.
|
|
61
|
+
- `add_chart.py` — `--sheet S --type line --data-min-col ... --cat-min-row ...`, or `--spec chart.json`. It cannot set axis number formats, series colours, or a legend position, and a one-row data range is treated as one series of many points.
|
|
62
|
+
- `add_image.py` — `--sheet S --path logo.png --anchor B2 [--width 480]`. It cannot update an embedded image when the file on disk changes.
|
|
63
|
+
|
|
64
|
+
Chain by naming outputs: `set_cells.py --input book.xlsx --out step1.xlsx`, then `style_cells.py --input step1.xlsx --out step2.xlsx`; without `--out` each tool writes `book-edited.xlsx` next to the input and the next step would read the unedited original.
|
|
72
65
|
|
|
73
|
-
|
|
66
|
+
Values: numbers, booleans, `null`, and JSON arrays parse; anything else is text, and a leading `=` is stored as a formula. `--set B3=` stores an empty string, which is not a blank cell — leave a cell out of the assignment to keep it blank.
|
|
74
67
|
|
|
75
|
-
|
|
68
|
+
Runnable invocations with real paths: `tasks/edit-workbook.md` `## Mutation tools`.
|
|
76
69
|
|
|
77
70
|
## Formulas you will write
|
|
78
71
|
|
|
79
|
-
Write the formula text a person would type
|
|
72
|
+
Write the formula text a person would type. openpyxl stores it verbatim and never evaluates it.
|
|
80
73
|
|
|
81
74
|
- Anchoring: `C8` moves when filled, `C$4` pins the row (a period header), `$A8` pins the column (an item label), `$B$3` pins one cell (a rate or case selector). `=SUMIFS(Amount,Month,C$4,Item,$A8)` is the shape a grid needs.
|
|
82
75
|
- Aggregation: `SUM`, `SUMIFS`, `COUNTIFS`, `AVERAGEIFS`, `SUMPRODUCT` for quantity × rate pairs, `PRODUCT` for chained factors (it silently skips blanks, so a missing input looks valid).
|
|
@@ -85,7 +78,7 @@ Write the formula text a person would type; openpyxl stores it verbatim and neve
|
|
|
85
78
|
- No `LET`, no dynamic arrays, no `LAMBDA`: the engines that read these files disagree on them.
|
|
86
79
|
- Cross-sheet references quote the name: `='Build'!E14`, `='Case A'!$B$4`.
|
|
87
80
|
|
|
88
|
-
|
|
81
|
+
Recalculate before anyone reads a number — see `## Verify`. Patterns and dates: `references/formula-patterns.md`.
|
|
89
82
|
|
|
90
83
|
## Tasks
|
|
91
84
|
|
|
@@ -107,8 +100,8 @@ Background: `references/workbook-structure.md` (tab roles and layout), `referenc
|
|
|
107
100
|
|
|
108
101
|
## Edit discipline
|
|
109
102
|
|
|
110
|
-
An existing workbook is the reference: its structure, formula style, formats, and terminology are what the user expects to keep seeing. Change the cells the request names and the formulas those cells feed. Everything else — other sheets, pivot sources, shared formulas, named ranges, conditional formats, data validation, protection, hidden rows, freeze panes, chart contents — stays untouched unless the request or a real dependency forces it. Never flatten formulas to values because it is easier, never autofit whole sheets for a visual tweak, and never quietly repair errors the user did not ask about: list them and ask.
|
|
103
|
+
An existing workbook is the reference: its structure, formula style, formats, and terminology are what the user expects to keep seeing. Change the cells the request names and the formulas those cells feed. Everything else — other sheets, pivot sources, shared formulas, named ranges, conditional formats, data validation, protection, hidden rows, freeze panes, chart contents — stays untouched unless the request or a real dependency forces it. Never flatten formulas to values because it is easier, never autofit whole sheets for a visual tweak, and never quietly repair errors the user did not ask about: list them and ask. Failure modes: `tasks/edit-workbook.md`.
|
|
111
104
|
|
|
112
105
|
## Shared workflow
|
|
113
106
|
|
|
114
|
-
|
|
107
|
+
`"<shared>/common-workflow.md"` owns the create-or-edit decision, the failure ladder, and the edit naming rule. `"<shared>/annotation-recipes.md"` owns the payload shapes the client sends.
|
|
@@ -1,8 +1,6 @@
|
|
|
1
1
|
# Native charts
|
|
2
2
|
|
|
3
|
-
A native chart is an object bound to cell ranges: the numbers stay in cells, the
|
|
4
|
-
chart reads them, and editing a cell moves the bar. That is the whole reason to
|
|
5
|
-
add one instead of pasting an image.
|
|
3
|
+
A native chart is an object bound to cell ranges: the numbers stay in cells, the chart reads them, and editing a cell moves the bar. That is the whole reason to add one instead of pasting an image.
|
|
6
4
|
|
|
7
5
|
```python
|
|
8
6
|
import sys
|
|
@@ -30,37 +28,21 @@ wb.save(sys.argv[2])
|
|
|
30
28
|
|
|
31
29
|
## Orientation is the bug people hit
|
|
32
30
|
|
|
33
|
-
A one-row source is **one series of many points**, not many series of one point.
|
|
34
|
-
`Reference` cannot express that; `add_data(..., from_rows=True)` can. Without it
|
|
35
|
-
a monthly row becomes twelve invisible one-point series and the chart renders
|
|
36
|
-
blank. The mirror case is a one-column source added with `from_rows=True`: you
|
|
37
|
-
get one series per cell row, so only pass it when the data really is row-wise.
|
|
31
|
+
A one-row source is **one series of many points**, not many series of one point. `Reference` cannot express that; `add_data(..., from_rows=True)` can. Without it a monthly row becomes twelve invisible one-point series and the chart renders blank. The mirror case is a one-column source added with `from_rows=True`: you get one series per cell row, so pass it only when the data really is row-wise.
|
|
38
32
|
|
|
39
33
|
## What the helper CLI gives you
|
|
40
34
|
|
|
41
|
-
`xlsx create` accepts `charts[]` with `type`, `title`, `anchor`,
|
|
42
|
-
`height`/`width`, `data {min_col,min_row,max_col,max_row,titles_from_data}` and
|
|
43
|
-
`categories`. `container_tools/add_chart.py` adds the same chart to an existing
|
|
44
|
-
file and handles the row-oriented case. Neither sets axis number formats, series
|
|
45
|
-
colours, legend position, data labels, or gap width — if the chart needs those,
|
|
46
|
-
use a snippet like the one above.
|
|
35
|
+
`xlsx create` accepts `charts[]` with `type`, `title`, `anchor`, `height`/`width`, `data {min_col,min_row,max_col,max_row,titles_from_data}` and `categories`. `container_tools/add_chart.py` adds the same chart to an existing file and handles the row-oriented case. Neither sets axis number formats, series colours, legend position, data labels, or gap width — if the chart needs those, use a snippet like the one above.
|
|
47
36
|
|
|
48
37
|
## Checks before you present
|
|
49
38
|
|
|
50
39
|
- Series count matches the intent (one line per series, not one per point).
|
|
51
40
|
- Categories are labels, not values and not an empty axis with `1, 2, 3, …`.
|
|
52
|
-
- Bar, column, and area axes start at zero; line and scatter may narrow the
|
|
53
|
-
|
|
54
|
-
-
|
|
55
|
-
|
|
56
|
-
- Axis number formats are set on the chart, not inherited from cells — a source
|
|
57
|
-
column formatted as `0.0%` does not format the axis.
|
|
58
|
-
- The chart sits near its data, over empty cells, and is not clipped by the
|
|
59
|
-
print area when the sheet is printed.
|
|
41
|
+
- Bar, column, and area axes start at zero; line and scatter may narrow the range, but the scale must be visible and stated.
|
|
42
|
+
- No default legend on a single series; name each series in `titles_from_data` or the legend reads `Series 1`.
|
|
43
|
+
- Axis number formats are set on the chart, not inherited from cells — a source column formatted as `0.0%` does not format the axis.
|
|
44
|
+
- The chart sits near its data, over empty cells, and is not clipped by the print area when the sheet is printed.
|
|
60
45
|
|
|
61
46
|
## Editing a chart in an existing file
|
|
62
47
|
|
|
63
|
-
Read the object first (`inspect_workbook.py` lists type, anchor, and series
|
|
64
|
-
refs), change the smallest property, and re-render. Moving a chart a few cells is
|
|
65
|
-
fine; rebuilding it with different styling when the user asked for one number to
|
|
66
|
-
be added is not.
|
|
48
|
+
Read the object first (`inspect_workbook.py` lists type, anchor, and series refs), change the smallest property, and re-render. Moving a chart a few cells is fine; rebuilding it with different styling when the user asked for one number to be added is not.
|
|
@@ -1,9 +1,6 @@
|
|
|
1
1
|
# Conditional formats
|
|
2
2
|
|
|
3
|
-
Conditional formatting paints a cell from a rule, not from a value. The rule is
|
|
4
|
-
stored separately from the cell format, so a cell can look red while its stored
|
|
5
|
-
fill stays default — and a reader who copies the value out gets the number, not
|
|
6
|
-
the colour.
|
|
3
|
+
Conditional formatting paints a cell from a rule, not from a value. The rule is stored separately from the cell format, so a cell can look red while its stored fill stays default — and a reader who copies the value out gets the number, not the colour.
|
|
7
4
|
|
|
8
5
|
```python
|
|
9
6
|
import sys
|
|
@@ -39,31 +36,17 @@ ws.conditional_formatting.add(
|
|
|
39
36
|
wb.save(sys.argv[2])
|
|
40
37
|
```
|
|
41
38
|
|
|
42
|
-
- `ColorScaleRule`/`DataBarRule` take a range, not a cell; one rule covers the
|
|
43
|
-
|
|
44
|
-
- `
|
|
45
|
-
|
|
46
|
-
- `
|
|
47
|
-
|
|
48
|
-
- A whole-column range (`D:D`) is cheap to write and slow to render; bound the
|
|
49
|
-
range to the current data (`D2:D13`) and extend it when rows are added.
|
|
50
|
-
- Rules stack in priority order; later rules win only when the earlier ones do
|
|
51
|
-
not match. `stopIfTrue=True` ends the chain — use it when a second rule must
|
|
52
|
-
not repaint an already-flagged cell.
|
|
53
|
-
- Do not also set a static fill on the same cells: the static fill shows through
|
|
54
|
-
where no rule matches and the sheet looks half-formatted.
|
|
39
|
+
- `ColorScaleRule`/`DataBarRule` take a range, not a cell; one rule covers the rectangle and scales against the values inside it.
|
|
40
|
+
- `CellIsRule` compares one cell: `operator` in `lessThan`, `greaterThan`, `between`, `equal`, `notEqual`, …
|
|
41
|
+
- `FormulaRule` evaluates a formula with the range's **top-left** cell, so write the reference for that cell (`$F2` with row relative) and Excel fills the rest.
|
|
42
|
+
- A whole-column range (`D:D`) is cheap to write and slow to render; bound the range to the current data (`D2:D13`) and extend it when rows are added.
|
|
43
|
+
- Rules stack in priority order; later rules win only when the earlier ones do not match. `stopIfTrue=True` ends the chain — use it when a second rule must not repaint an already-flagged cell.
|
|
44
|
+
- Do not also set a static fill on the same cells: the static fill shows through where no rule matches and the sheet looks half-formatted.
|
|
55
45
|
|
|
56
46
|
## Editing an existing file
|
|
57
47
|
|
|
58
|
-
Replace a rule
|
|
59
|
-
`ws.conditional_formatting` and rebuild the collection for that range. Two rules
|
|
60
|
-
covering `D2:D13` with slightly different thresholds give a result nobody can
|
|
61
|
-
explain. Keep the rule count per sheet small — a dashboard with forty rules is a
|
|
62
|
-
compatibility minefield in other engines.
|
|
48
|
+
Replace a rule; do not add a near-duplicate. Iterate `ws.conditional_formatting` and rebuild the collection for that range — two rules covering `D2:D13` with slightly different thresholds give a result nobody can explain. Keep the rule count per sheet small: a dashboard with forty rules is a compatibility minefield in other engines.
|
|
63
49
|
|
|
64
50
|
## Verify
|
|
65
51
|
|
|
66
|
-
Read the file back with `container_tools/inspect_workbook.py`; it lists each
|
|
67
|
-
range and rule type. Then render and look: a data bar that never appears is
|
|
68
|
-
usually a range that does not overlap the data, and a rule that fires on the
|
|
69
|
-
wrong cells is almost always an absolute/relative mistake in `FormulaRule`.
|
|
52
|
+
Read the file back with `container_tools/inspect_workbook.py`; it lists each range and rule type. Then render and look: a data bar that never appears is usually a range that does not overlap the data, and a rule that fires on the wrong cells is almost always an absolute/relative mistake in `FormulaRule`.
|
|
@@ -1,10 +1,6 @@
|
|
|
1
1
|
# Data tables (Excel Tables)
|
|
2
2
|
|
|
3
|
-
An Excel Table (`ListObject`) is the object behind `Insert > Table`: a name, a
|
|
4
|
-
structured reference, banded rows, an optional totals row, and header filters.
|
|
5
|
-
It is not a pivot table and not a formatted range. Use it when the user asks for
|
|
6
|
-
a table, when a filter row helps, or when formulas should read
|
|
7
|
-
`Sales[Amount]` instead of `$D$2:$D$900`.
|
|
3
|
+
An Excel Table (`ListObject`) is the object behind `Insert > Table`: a name, a structured reference, banded rows, an optional totals row, and header filters. It is not a pivot table and not a formatted range. Use it when the user asks for a table, when a filter row helps, or when formulas should read `Sales[Amount]` instead of `$D$2:$D$900`.
|
|
8
4
|
|
|
9
5
|
```python
|
|
10
6
|
import sys
|
|
@@ -23,19 +19,14 @@ wb.save(sys.argv[2])
|
|
|
23
19
|
|
|
24
20
|
Rules that survive contact with the user:
|
|
25
21
|
|
|
26
|
-
- `displayName` has no spaces, no hyphens, and must be unique in the workbook — it
|
|
27
|
-
|
|
28
|
-
- `ref` must start at the header row and match the real data rectangle. A table
|
|
29
|
-
whose `ref` is off by one row shows a phantom header and breaks the filter.
|
|
22
|
+
- `displayName` has no spaces, no hyphens, and must be unique in the workbook — it becomes the structured reference name (`PipelineTable[Amount]`).
|
|
23
|
+
- `ref` must start at the header row and match the real data rectangle. A table whose `ref` is off by one row shows a phantom header and breaks the filter.
|
|
30
24
|
- One table per rectangle, one header row, no merged cells inside it.
|
|
31
|
-
- Give it a style from `TableStyleLight*`/`Medium*`/`Dark*`; a bare table with no
|
|
32
|
-
style looks like an unstyled range and the user asks why the stripes went away.
|
|
25
|
+
- Give it a style from `TableStyleLight*`/`Medium*`/`Dark*`; a bare table with no style looks like an unstyled range and the user asks why the stripes went away.
|
|
33
26
|
|
|
34
27
|
## Appending rows to a table
|
|
35
28
|
|
|
36
|
-
Rows written below the last table row are outside the table until you widen
|
|
37
|
-
`ref`. Widen it in the same pass, or the new records will not filter, will not
|
|
38
|
-
band, and structured references will not see them.
|
|
29
|
+
Rows written below the last table row are outside the table until you widen `ref`. Widen it in the same pass, or the new records will not filter, will not band, and structured references will not see them.
|
|
39
30
|
|
|
40
31
|
```python
|
|
41
32
|
table = ws.tables["PipelineTable"]
|
|
@@ -44,15 +35,8 @@ new_last = first_row + added
|
|
|
44
35
|
table.ref = f"A1:G{new_last}" # same columns, one row longer
|
|
45
36
|
```
|
|
46
37
|
|
|
47
|
-
Copy the format and any formula from the previous row (`ws.cell(r, c)._style =
|
|
48
|
-
ws.cell(r - 1, c)._style`) before widening, so the new row does not appear as a
|
|
49
|
-
plain block inside a banded table.
|
|
38
|
+
Copy the format and any formula from the previous row (`ws.cell(r, c)._style = ws.cell(r - 1, c)._style`) before widening, so the new row does not appear as a plain block inside a banded table.
|
|
50
39
|
|
|
51
40
|
## Totals row
|
|
52
41
|
|
|
53
|
-
`table.totalsRowCount = 1` declares a totals row, and `totalsRowFunction` on a
|
|
54
|
-
column sets `sum`, `count`, `average`, `min`, `max`, `stdDev`, or `var`. The row
|
|
55
|
-
below the last data row must exist and hold the header labels Excel expects; a
|
|
56
|
-
totals row declared without that row renders oddly in some engines. If the user
|
|
57
|
-
only wants a bold total line under a table, a formula row outside the table is
|
|
58
|
-
simpler and less fragile.
|
|
42
|
+
`table.totalsRowCount = 1` declares a totals row, and `totalsRowFunction` on a column sets `sum`, `count`, `average`, `min`, `max`, `stdDev`, or `var`. The row below the last data row must exist and hold the header labels Excel expects; a totals row declared without that row renders oddly in some engines. If the user only wants a bold total line under a table, a formula row outside the table is simpler and less fragile.
|
|
@@ -1,8 +1,6 @@
|
|
|
1
1
|
# Named ranges
|
|
2
2
|
|
|
3
|
-
A defined name is a saved reference with a readable name: `Q4Growth` instead of
|
|
4
|
-
`Model!$B$6`. Formulas that use names survive row and column insertion, and the
|
|
5
|
-
user can select the name box instead of hunting for a cell.
|
|
3
|
+
A defined name is a saved reference with a readable name: `Q4Growth` instead of `Model!$B$6`. Formulas that use names survive row and column insertion, and the user can select the name box instead of hunting for a cell.
|
|
6
4
|
|
|
7
5
|
```python
|
|
8
6
|
import sys
|
|
@@ -15,27 +13,16 @@ wb.defined_names["PlanRevenue"] = DefinedName("PlanRevenue", attr_text="Model!$B
|
|
|
15
13
|
wb.save(sys.argv[2])
|
|
16
14
|
```
|
|
17
15
|
|
|
18
|
-
- The name must be a legal identifier: letters, digits, underscore, not a cell
|
|
19
|
-
|
|
20
|
-
- `
|
|
21
|
-
|
|
22
|
-
-
|
|
23
|
-
put it on the worksheet: `wb["Model"].defined_names["Rate"] = ...` — the same
|
|
24
|
-
name can then mean different cells on different sheets, which confuses readers
|
|
25
|
-
unless the workbook already works that way.
|
|
26
|
-
- Read the existing names with `container_tools/inspect_workbook.py` before
|
|
27
|
-
adding one; a duplicate name is a repair prompt in Excel.
|
|
28
|
-
- Never point a name at a range whose size changes (`A2:A900`) unless the sheet
|
|
29
|
-
really ends there; prefer the exact used range, or a table reference.
|
|
16
|
+
- The name must be a legal identifier: letters, digits, underscore, not a cell address (`Q4` alone is a column, so it is refused).
|
|
17
|
+
- `attr_text` is a full sheet reference; quote sheet names with spaces: `'Case A'!$B$4`.
|
|
18
|
+
- Workbook-level names live in `wb.defined_names`. To scope a name to one sheet, put it on the worksheet: `wb["Model"].defined_names["Rate"] = ...` — the same name can then mean different cells on different sheets, which confuses readers unless the workbook already works that way.
|
|
19
|
+
- Read the existing names with `container_tools/inspect_workbook.py` before adding one; a duplicate name is a repair prompt in Excel.
|
|
20
|
+
- Never point a name at a range whose size changes (`A2:A900`) unless the sheet really ends there; prefer the exact used range, or a table reference.
|
|
30
21
|
|
|
31
22
|
## Using names in formulas
|
|
32
23
|
|
|
33
|
-
`=PlanRevenue*Q4Growth` is more readable than `=Model!$B$11*Model!$B$6`, and it
|
|
34
|
-
does not break when a row is inserted. Paste the formula text exactly as the user
|
|
35
|
-
would type it; openpyxl never resolves the name.
|
|
24
|
+
`=PlanRevenue*Q4Growth` is more readable than `=Model!$B$11*Model!$B$6`, and it does not break when a row is inserted. Paste the formula text exactly as the user would type it; openpyxl never resolves the name.
|
|
36
25
|
|
|
37
26
|
## Removing a name
|
|
38
27
|
|
|
39
|
-
Deleting a name leaves every formula that used it as `#NAME?`. Find the users
|
|
40
|
-
first (formula text is visible in `inspect_workbook.py` output and
|
|
41
|
-
`xlsx read`), then delete, then recalculate and check the gate.
|
|
28
|
+
Deleting a name leaves every formula that used it as `#NAME?`. Find the users first (formula text is visible in `inspect_workbook.py` output and `xlsx read`), then delete, then recalculate and check the gate.
|