twilize 0.13.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.
Files changed (209) hide show
  1. twilize-0.13.0/.dockerignore +14 -0
  2. twilize-0.13.0/.gitignore +60 -0
  3. twilize-0.13.0/CHANGELOG.md +300 -0
  4. twilize-0.13.0/CONTRIBUTING.md +171 -0
  5. twilize-0.13.0/Dockerfile +32 -0
  6. twilize-0.13.0/LICENSE +661 -0
  7. twilize-0.13.0/PKG-INFO +590 -0
  8. twilize-0.13.0/Procfile +1 -0
  9. twilize-0.13.0/README.md +542 -0
  10. twilize-0.13.0/docs/MCP_SERVER.md +744 -0
  11. twilize-0.13.0/docs/ROADMAP.md +426 -0
  12. twilize-0.13.0/docs/tableau_agent.md +1161 -0
  13. twilize-0.13.0/examples/README.md +71 -0
  14. twilize-0.13.0/examples/layouts/layout_c1.json +64 -0
  15. twilize-0.13.0/examples/layouts/layout_c2.json +113 -0
  16. twilize-0.13.0/examples/layouts/layout_executive.json +80 -0
  17. twilize-0.13.0/examples/migrate_workflow/5 KPI Design Ideas (2).twb +9850 -0
  18. twilize-0.13.0/examples/migrate_workflow/README.md +26 -0
  19. twilize-0.13.0/examples/migrate_workflow/Sample - Superstore.xls +0 -0
  20. twilize-0.13.0/examples/migrate_workflow/test_migration_workflow.py +63 -0
  21. twilize-0.13.0/examples/migrate_workflow//347/244/272/344/276/213 - /350/266/205/345/270/202.xls +0 -0
  22. twilize-0.13.0/examples/prompts/README.md +34 -0
  23. twilize-0.13.0/examples/prompts/all_supported_charts_showcase_en.md +37 -0
  24. twilize-0.13.0/examples/prompts/demo_auto_layout4_prompt.md +34 -0
  25. twilize-0.13.0/examples/prompts/demo_auto_layout_prompt.md +20 -0
  26. twilize-0.13.0/examples/prompts/demo_c2_layout_prompt.md +19 -0
  27. twilize-0.13.0/examples/prompts/demo_declarative_layout_prompt.md +34 -0
  28. twilize-0.13.0/examples/prompts/demo_simple.md +20 -0
  29. twilize-0.13.0/examples/prompts/overview_business_demo.md +62 -0
  30. twilize-0.13.0/examples/prompts/overview_natural zh_cn.md +46 -0
  31. twilize-0.13.0/examples/prompts/overview_natural_en.md +113 -0
  32. twilize-0.13.0/examples/prompts/test_parameter_prefix_bug.md +18 -0
  33. twilize-0.13.0/examples/screenshot2layout/dashboard 1.png +0 -0
  34. twilize-0.13.0/examples/screenshot2layout/dashboard 2.png +0 -0
  35. twilize-0.13.0/examples/screenshot2layout/layout_dashboard1.json +114 -0
  36. twilize-0.13.0/examples/screenshot2layout/layout_dashboard2.json +194 -0
  37. twilize-0.13.0/examples/scripts/demo_all_supported_charts.py +128 -0
  38. twilize-0.13.0/examples/scripts/demo_all_supported_charts_mcp.py +125 -0
  39. twilize-0.13.0/examples/scripts/demo_auto_layout4.py +84 -0
  40. twilize-0.13.0/examples/scripts/demo_connections.py +68 -0
  41. twilize-0.13.0/examples/scripts/demo_declarative_layout.py +107 -0
  42. twilize-0.13.0/examples/scripts/demo_e2e_mcp_workflow.py +100 -0
  43. twilize-0.13.0/examples/scripts/demo_hyper_and_new_charts.py +122 -0
  44. twilize-0.13.0/examples/superstore_recreated/Exec Overview Recreated.twb +74 -0
  45. twilize-0.13.0/examples/superstore_recreated/build_exec_overview.py +924 -0
  46. twilize-0.13.0/examples/superstore_recreated/screenshot.png +0 -0
  47. twilize-0.13.0/extension/__init__.py +0 -0
  48. twilize-0.13.0/extension/backend/__init__.py +0 -0
  49. twilize-0.13.0/extension/backend/app.py +228 -0
  50. twilize-0.13.0/extension/backend/chart_suggestion.py +1279 -0
  51. twilize-0.13.0/extension/backend/image_analysis.py +269 -0
  52. twilize-0.13.0/extension/backend/pipeline.py +558 -0
  53. twilize-0.13.0/extension/backend/schema_inference.py +131 -0
  54. twilize-0.13.0/extension/backend/tests/__init__.py +0 -0
  55. twilize-0.13.0/extension/backend/tests/test_schema_inference.py +30 -0
  56. twilize-0.13.0/extension/backend/tests/test_suggestion.py +69 -0
  57. twilize-0.13.0/extension/frontend/index.html +13 -0
  58. twilize-0.13.0/extension/frontend/package.json +24 -0
  59. twilize-0.13.0/extension/frontend/public/tableau.extensions.min.js +2 -0
  60. twilize-0.13.0/extension/frontend/src/App.tsx +152 -0
  61. twilize-0.13.0/extension/frontend/src/components/ApiKeySettings.tsx +131 -0
  62. twilize-0.13.0/extension/frontend/src/components/DataPreview.tsx +88 -0
  63. twilize-0.13.0/extension/frontend/src/components/DownloadPanel.tsx +84 -0
  64. twilize-0.13.0/extension/frontend/src/components/ImageUpload.tsx +111 -0
  65. twilize-0.13.0/extension/frontend/src/components/ProgressIndicator.tsx +72 -0
  66. twilize-0.13.0/extension/frontend/src/components/PromptInput.tsx +40 -0
  67. twilize-0.13.0/extension/frontend/src/components/SuggestionPreview.tsx +118 -0
  68. twilize-0.13.0/extension/frontend/src/hooks/useGeneration.ts +55 -0
  69. twilize-0.13.0/extension/frontend/src/hooks/useTableauData.ts +90 -0
  70. twilize-0.13.0/extension/frontend/src/main.tsx +9 -0
  71. twilize-0.13.0/extension/frontend/src/utils/api.ts +98 -0
  72. twilize-0.13.0/extension/frontend/src/utils/tableauApi.ts +190 -0
  73. twilize-0.13.0/extension/frontend/src/utils/types.ts +28 -0
  74. twilize-0.13.0/extension/frontend/tsconfig.json +21 -0
  75. twilize-0.13.0/extension/frontend/vite.config.ts +19 -0
  76. twilize-0.13.0/extension/manifest/twilize-extension-production.trex +22 -0
  77. twilize-0.13.0/extension/manifest/twilize-extension.trex +22 -0
  78. twilize-0.13.0/extension/scripts/start.py +83 -0
  79. twilize-0.13.0/mcp-server.json +292 -0
  80. twilize-0.13.0/pyproject.toml +86 -0
  81. twilize-0.13.0/requirements.txt +11 -0
  82. twilize-0.13.0/smithery.yaml +131 -0
  83. twilize-0.13.0/src/twilize/__init__.py +55 -0
  84. twilize-0.13.0/src/twilize/__main__.py +5 -0
  85. twilize-0.13.0/src/twilize/c3_layout.py +555 -0
  86. twilize-0.13.0/src/twilize/capability_registry.py +509 -0
  87. twilize-0.13.0/src/twilize/chart_suggester.py +713 -0
  88. twilize-0.13.0/src/twilize/charts/__init__.py +350 -0
  89. twilize-0.13.0/src/twilize/charts/builder_base.py +634 -0
  90. twilize-0.13.0/src/twilize/charts/builder_basic.py +282 -0
  91. twilize-0.13.0/src/twilize/charts/builder_dual_axis.py +677 -0
  92. twilize-0.13.0/src/twilize/charts/builder_maps.py +298 -0
  93. twilize-0.13.0/src/twilize/charts/builder_pie.py +95 -0
  94. twilize-0.13.0/src/twilize/charts/builder_text.py +180 -0
  95. twilize-0.13.0/src/twilize/charts/dispatcher.py +215 -0
  96. twilize-0.13.0/src/twilize/charts/helpers.py +599 -0
  97. twilize-0.13.0/src/twilize/charts/pattern_mapping.py +57 -0
  98. twilize-0.13.0/src/twilize/charts/routing_policy.py +91 -0
  99. twilize-0.13.0/src/twilize/charts/showcase_recipes.py +471 -0
  100. twilize-0.13.0/src/twilize/config.py +26 -0
  101. twilize-0.13.0/src/twilize/connections.py +532 -0
  102. twilize-0.13.0/src/twilize/csv_to_hyper.py +485 -0
  103. twilize-0.13.0/src/twilize/dashboard_actions.py +184 -0
  104. twilize-0.13.0/src/twilize/dashboard_dependencies.py +125 -0
  105. twilize-0.13.0/src/twilize/dashboard_enhancements.py +336 -0
  106. twilize-0.13.0/src/twilize/dashboard_layouts.py +137 -0
  107. twilize-0.13.0/src/twilize/dashboards.py +240 -0
  108. twilize-0.13.0/src/twilize/docapi_bridge.py +220 -0
  109. twilize-0.13.0/src/twilize/field_registry.py +291 -0
  110. twilize-0.13.0/src/twilize/layout.py +6 -0
  111. twilize-0.13.0/src/twilize/layout_model.py +135 -0
  112. twilize-0.13.0/src/twilize/layout_rendering.py +269 -0
  113. twilize-0.13.0/src/twilize/layout_templates.py +587 -0
  114. twilize-0.13.0/src/twilize/mcp/__init__.py +1 -0
  115. twilize-0.13.0/src/twilize/mcp/app.py +27 -0
  116. twilize-0.13.0/src/twilize/mcp/resources.py +93 -0
  117. twilize-0.13.0/src/twilize/mcp/snapshot.py +122 -0
  118. twilize-0.13.0/src/twilize/mcp/state.py +46 -0
  119. twilize-0.13.0/src/twilize/mcp/tools_layout.py +70 -0
  120. twilize-0.13.0/src/twilize/mcp/tools_migration.py +162 -0
  121. twilize-0.13.0/src/twilize/mcp/tools_pipeline.py +129 -0
  122. twilize-0.13.0/src/twilize/mcp/tools_support.py +120 -0
  123. twilize-0.13.0/src/twilize/mcp/tools_workbook.py +672 -0
  124. twilize-0.13.0/src/twilize/migration.py +1312 -0
  125. twilize-0.13.0/src/twilize/parameters.py +165 -0
  126. twilize-0.13.0/src/twilize/pipeline.py +348 -0
  127. twilize-0.13.0/src/twilize/reference_lines.py +159 -0
  128. twilize-0.13.0/src/twilize/references/Sample _ Superstore (Simple).xls +0 -0
  129. twilize-0.13.0/src/twilize/references/empty_template.twb +494 -0
  130. twilize-0.13.0/src/twilize/references/tableau_all_functions.json +1053 -0
  131. twilize-0.13.0/src/twilize/server.py +62 -0
  132. twilize-0.13.0/src/twilize/skills/README.md +41 -0
  133. twilize-0.13.0/src/twilize/skills/calculation_builder.md +126 -0
  134. twilize-0.13.0/src/twilize/skills/chart_builder.md +367 -0
  135. twilize-0.13.0/src/twilize/skills/dashboard_designer.md +256 -0
  136. twilize-0.13.0/src/twilize/skills/formatting.md +167 -0
  137. twilize-0.13.0/src/twilize/style_presets.py +187 -0
  138. twilize-0.13.0/src/twilize/themes.py +153 -0
  139. twilize-0.13.0/src/twilize/trend_lines.py +107 -0
  140. twilize-0.13.0/src/twilize/twb_analyzer.py +478 -0
  141. twilize-0.13.0/src/twilize/twb_editor.py +730 -0
  142. twilize-0.13.0/src/twilize/validator.py +319 -0
  143. twilize-0.13.0/src/twilize/viz_best_practices.py +390 -0
  144. twilize-0.13.0/templates/Sample - Superstore - simple.xls +0 -0
  145. twilize-0.13.0/templates/Sample - Superstore.xls +0 -0
  146. twilize-0.13.0/templates/layout/Tableau Dashboard Layout Templates screenshot.png +0 -0
  147. twilize-0.13.0/templates/layout/Tableau Dashboard Layout Templates.twb +4917 -0
  148. twilize-0.13.0/templates/migrate/5 KPI Design Ideas (2).twb +9803 -0
  149. twilize-0.13.0/templates/migrate/Sample - Superstore.xls +0 -0
  150. twilize-0.13.0/templates/migrate//347/244/272/344/276/213 - /350/266/205/345/270/202.xls +0 -0
  151. twilize-0.13.0/templates/twb/superstore - localmysql.twb +527 -0
  152. twilize-0.13.0/templates/twb/superstore copy.twb +495 -0
  153. twilize-0.13.0/templates/twb/superstore.twb +636 -0
  154. twilize-0.13.0/templates/viz/Tableau Advent Calendar.twb +5511 -0
  155. twilize-0.13.0/templates/viz/pie_chart.twb +686 -0
  156. twilize-0.13.0/tests/README.md +199 -0
  157. twilize-0.13.0/tests/conftest.py +22 -0
  158. twilize-0.13.0/tests/parse_twb.py +27 -0
  159. twilize-0.13.0/tests/test_c2_replica.py +178 -0
  160. twilize-0.13.0/tests/test_capability_registry.py +41 -0
  161. twilize-0.13.0/tests/test_chart_routing.py +140 -0
  162. twilize-0.13.0/tests/test_chart_suggester.py +198 -0
  163. twilize-0.13.0/tests/test_charts.py +60 -0
  164. twilize-0.13.0/tests/test_connections.py +139 -0
  165. twilize-0.13.0/tests/test_csv_schema_inference.py +150 -0
  166. twilize-0.13.0/tests/test_dashboard_action_types.py +238 -0
  167. twilize-0.13.0/tests/test_dashboard_actions.py +65 -0
  168. twilize-0.13.0/tests/test_debug.py +113 -0
  169. twilize-0.13.0/tests/test_declarative_dashboards.py +139 -0
  170. twilize-0.13.0/tests/test_diff.py +4 -0
  171. twilize-0.13.0/tests/test_docapi_bridge.py +41 -0
  172. twilize-0.13.0/tests/test_dual_axis_advanced.py +265 -0
  173. twilize-0.13.0/tests/test_dual_axis_basic.py +160 -0
  174. twilize-0.13.0/tests/test_dual_axis_script.py +5 -0
  175. twilize-0.13.0/tests/test_e2e.py +120 -0
  176. twilize-0.13.0/tests/test_empty.twb +471 -0
  177. twilize-0.13.0/tests/test_error_handling.py +206 -0
  178. twilize-0.13.0/tests/test_existing_workbook_editing.py +200 -0
  179. twilize-0.13.0/tests/test_field_registry.py +59 -0
  180. twilize-0.13.0/tests/test_generate_pie.py +81 -0
  181. twilize-0.13.0/tests/test_hyper_example.py +25 -0
  182. twilize-0.13.0/tests/test_interactive_features.py +60 -0
  183. twilize-0.13.0/tests/test_label_runs.py +234 -0
  184. twilize-0.13.0/tests/test_layout_ascii.py +48 -0
  185. twilize-0.13.0/tests/test_level1_features.py +298 -0
  186. twilize-0.13.0/tests/test_mcp_showcase_workbook.py +291 -0
  187. twilize-0.13.0/tests/test_mcp_tools.py +194 -0
  188. twilize-0.13.0/tests/test_migration_workflow.py +240 -0
  189. twilize-0.13.0/tests/test_overview_full.py +230 -0
  190. twilize-0.13.0/tests/test_overview_replica.py +200 -0
  191. twilize-0.13.0/tests/test_phase10_dashboard_polish.py +410 -0
  192. twilize-0.13.0/tests/test_reference_lines.py +201 -0
  193. twilize-0.13.0/tests/test_screenshot2layout.py +201 -0
  194. twilize-0.13.0/tests/test_template_datasource_structure.py +88 -0
  195. twilize-0.13.0/tests/test_twb_analyzer.py +123 -0
  196. twilize-0.13.0/tests/test_twb_structure.py +288 -0
  197. twilize-0.13.0/tests/test_twbx_support.py +247 -0
  198. twilize-0.13.0/tests/test_worksheet_style.py +274 -0
  199. twilize-0.13.0/tests/twb_assert.py +200 -0
  200. twilize-0.13.0/vendor/tableau-document-schemas/.gitignore +5 -0
  201. twilize-0.13.0/vendor/tableau-document-schemas/CODEOWNERS +3 -0
  202. twilize-0.13.0/vendor/tableau-document-schemas/CODE_OF_CONDUCT.md +105 -0
  203. twilize-0.13.0/vendor/tableau-document-schemas/CONTRIBUTING.md +22 -0
  204. twilize-0.13.0/vendor/tableau-document-schemas/LICENSE.txt +207 -0
  205. twilize-0.13.0/vendor/tableau-document-schemas/README.md +83 -0
  206. twilize-0.13.0/vendor/tableau-document-schemas/SECURITY.md +7 -0
  207. twilize-0.13.0/vendor/tableau-document-schemas/schemas/2026_1/_user_ns_stub.xsd +7 -0
  208. twilize-0.13.0/vendor/tableau-document-schemas/schemas/2026_1/_xml_ns_stub.xsd +15 -0
  209. twilize-0.13.0/vendor/tableau-document-schemas/schemas/2026_1/twb_2026.1.0.xsd +7586 -0
@@ -0,0 +1,14 @@
1
+ .git
2
+ .venv
3
+ venv
4
+ env
5
+ __pycache__
6
+ *.pyc
7
+ node_modules
8
+ extension/frontend/node_modules
9
+ extension/frontend/dist
10
+ .env
11
+ *.log
12
+ output/
13
+ tests/
14
+ docs/
@@ -0,0 +1,60 @@
1
+ # Python
2
+ __pycache__/
3
+ *.py[cod]
4
+ *$py.class
5
+ *.egg-info/
6
+ *.egg
7
+ dist/
8
+ build/
9
+ *.whl
10
+
11
+ # Virtual environments
12
+ .venv/
13
+ venv/
14
+ env/
15
+ .env
16
+ *.log
17
+ *.logs
18
+
19
+ # IDE
20
+ .vscode/
21
+ .idea/
22
+ *.swp
23
+ *.swo
24
+ *~
25
+ .claude/
26
+
27
+ # OS
28
+ .DS_Store
29
+ Thumbs.db
30
+ desktop.ini
31
+
32
+ # Node
33
+ node_modules/
34
+ package-lock.json
35
+
36
+ # Test output
37
+ output/
38
+
39
+ # Temp files
40
+ temp_template.twb
41
+ test_api.py
42
+ test_env.py
43
+ example_data.csv
44
+
45
+ # Large/binary project files
46
+ backup/
47
+ dashboard/
48
+ *.twbx
49
+ *.hyper
50
+ !src/cwtwb/references/*.hyper
51
+ *.twbr
52
+ tmp/
53
+ templates/migrate/* - migrated to *.twb
54
+ templates/migrate/migration_report.json
55
+ templates/migrate/field_mapping.json
56
+ examples/migrate_workflow/* - migrated to *.twb
57
+ examples/migrate_workflow/* - migrated.twb
58
+ examples/migrate_workflow/migration_report.json
59
+ examples/migrate_workflow/field_mapping.json
60
+ examples/migrate_workflow/prompt.txt
@@ -0,0 +1,300 @@
1
+ # Changelog
2
+
3
+ All notable changes to this project will be documented in this file.
4
+
5
+ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
6
+ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
7
+
8
+ ## [0.16.0] - 2026-03-19
9
+
10
+ ### Fixed
11
+
12
+ - **`FieldRegistry.remove()` missing**: `TWBEditor.remove_calculated_field()` and its `remove_calculated_field` MCP tool called `self.field_registry.remove()`, but `FieldRegistry` only defined `unregister()`. Added the missing `remove()` method; the MCP tool now works correctly instead of raising `AttributeError`.
13
+ - **Dual-axis pane secondary ID mismatch**: `DualAxisChartBuilder` wrote `id="3"` for the secondary pane in both horizontal and vertical configurations, while all test assertions and internal lookups expected `id="2"`. Changed pane_2 id to `"2"` in both the same-measure (lollipop/donut) and different-measure (combo) branches.
14
+ - **`format_capability_catalog()` missing `level_filter` parameter**: The function signature accepted no arguments, but the capability registry design and test suite expected an optional `level_filter: str` parameter to restrict output to a single level (e.g. `"core"`). Added the parameter; calling without it preserves the existing full-catalog behavior.
15
+ - **`inspect_target_schema` crashes on non-Excel paths**: The MCP tool passed any path directly to `xlrd.open_workbook()`, raising `FileNotFoundError` or `XLRDError` for `.csv`, non-existent files, or unsupported formats. Added an extension check up-front and a `try/except` fallback; both now return a readable `"Unsupported…"` string instead of an exception.
16
+ - **`analyze_twb` missing Capability gap section**: `analyze_twb` returned only `report.to_text()`, omitting the decision-oriented gap summary that `diff_template_gap` produces separately. The tool now appends `report.to_gap_text()` so a single `analyze_twb` call includes both the capability catalog and the gap triage.
17
+
18
+ ## [0.15.0] - 2026-03-18
19
+
20
+ ### Added
21
+
22
+ - **XSD schema validation** against the official Tableau TWB XSD (2026.1), vendored at `vendor/tableau-document-schemas/`:
23
+ - `TWBEditor.validate_schema()` — validates the in-memory workbook without saving first; returns a `SchemaValidationResult` with `valid`, `errors`, `schema_available`, and `to_text()` summary.
24
+ - `validate_workbook` MCP tool — validates the current open workbook (in memory) or any `.twb`/`.twbx` file on disk by path. Errors are reported as informational; Tableau itself occasionally generates workbooks that deviate from the schema.
25
+ - `validate_against_schema(root)` — public SDK function in `twilize.validator`, accepts an lxml `_Element` and returns `SchemaValidationResult`.
26
+ - `SchemaValidationResult` exported from the top-level `twilize` package.
27
+ - Two missing XSD imports (`http://www.tableausoftware.com/xml/user` and `http://www.w3.org/XML/1998/namespace`) resolved via local stub XSD files written to `vendor/tableau-document-schemas/schemas/2026_1/` at first use; schema is loaded once and cached for the process lifetime.
28
+
29
+ ## [0.14.0] - 2026-03-17
30
+
31
+ ### Added
32
+
33
+ - **`.twbx` (Packaged Workbook) support**: `TWBEditor` now reads and writes Tableau Packaged Workbook files transparently.
34
+ - **Open**: `TWBEditor("file.twbx")` and `TWBEditor.open_existing("file.twbx")` unzip the archive, locate the embedded `.twb`, and parse it in-memory. The source ZIP path and inner filename are recorded for later re-packing.
35
+ - **Save as `.twbx`**: `editor.save("output.twbx")` serializes the updated XML and re-packs it into a new ZIP, carrying over all bundled assets (`.hyper` data extracts, images, etc.) from the source `.twbx` automatically.
36
+ - **Save as `.twb` from a `.twbx` source**: `editor.save("output.twb")` extracts just the workbook XML, discarding the packaging.
37
+ - **Plain `.twb` → `.twbx`**: any `.twb`-sourced workbook can be packaged by saving with a `.twbx` extension; the result is a single-entry ZIP containing the workbook XML.
38
+ - MCP tools `create_workbook`, `open_workbook`, and `save_workbook` all support `.twbx` paths with no changes to call signatures.
39
+ - **`tests/test_twbx_support.py`**: 25 pytest cases covering open, round-trip save, extract/image preservation, plain-TWB-to-TWBX conversion, modify-and-resave, and MCP tool integration.
40
+
41
+ ## [0.13.0] - 2026-03-17
42
+
43
+ ### Added
44
+
45
+ - **Rich-text `label_runs` in `configure_chart`**: Multi-style labels built from a list of run dicts. Each run supports `text` (literal string), `field` (field expression → `<field_ref>` CDATA), `prefix`, and per-run font attributes (`fontname`, `fontsize`, `fontcolor`, `bold`, `fontalignment`). Use `"\n"` as text to insert a paragraph separator. Pass `"fontalignment": None` to suppress the default alignment attribute. Enables KPI cards with two-line labels, dynamic titles with inline field values, and branded separators.
46
+ - **14 new `configure_worksheet_style` options**:
47
+ - `hide_col_field_labels` / `hide_row_field_labels` — hide column and row field label headers in table/cross-tab views
48
+ - `hide_droplines` — remove drop lines from mark tooltips
49
+ - `hide_table_dividers` — remove row/column divider lines in cross-tab views
50
+ - `hide_reflines` — hide reference lines
51
+ - `disable_tooltip` — disable tooltip entirely (`tooltip-mode='none'`)
52
+ - `pane_cell_style: dict` — pane-level cell text alignment, e.g. `{"text-align": "center", "vertical-align": "center"}`
53
+ - `pane_datalabel_style: dict` — pane-level data label font family, size, and color
54
+ - `pane_mark_style: dict` — pane-level mark color, stroke, transparency, and size (0.0–1.0 scale via `"size"` key)
55
+ - `pane_trendline_hidden: bool` — hide trendline in pane style
56
+ - `label_formats: list[dict]` — per-field label style (font, color, orientation, display toggle)
57
+ - `cell_formats: list[dict]` — per-field table cell style
58
+ - `header_formats: list[dict]` — per-field header height/width
59
+ - `axis_style: dict` — global tick color plus per-field axis display and height control
60
+ - **`mark_color_1` in `configure_dual_axis`**: Explicit hex color for primary-axis marks, symmetric with the existing `mark_color_2`. Useful for pairing a gray bar (`mark_color_1`) against a blue target GanttBar (`mark_color_2`).
61
+ - **`color_map_1` in `configure_dual_axis`**: Datasource-level palette mapping for the primary-axis `color_1` field, using the same mechanism as `configure_chart(color_map=...)`.
62
+ - **`default_format` in `add_calculated_field`**: Optional Tableau number format string written as `default-format` on the column XML, e.g. `'c"$"#,##0,.0K'`.
63
+ - **`color_map` in `configure_dual_axis(extra_axes=[...])`**: Custom palette for `:Measure Names` when used as `"color"` on an extra axis. Each bucket is mapped to a hex color via a datasource-level `<encoding>` element.
64
+ - **`show_title` in dashboard layout nodes**: Pass `show_title: false` in a layout zone dict to suppress the worksheet title bar inside a dashboard zone.
65
+ - **Expanded test suite — 7 new test modules**:
66
+ - `test_worksheet_style.py` — all 18 `configure_worksheet_style` options (hide flags, background, pane styles, per-field formats, axis style)
67
+ - `test_label_runs.py` — text runs, field-ref runs, newline separator, font styling, prefix, fontalignment suppression, KPI card and dynamic title patterns
68
+ - `test_dual_axis_basic.py` — horizontal/vertical dual-axis combos, shared axis, color encoding, filters
69
+ - `test_dual_axis_advanced.py` — `mark_color_1/2`, `color_map_1`, `reverse_axis_1`, `hide_zeroline`, synchronized axis, `show_labels`, `size_value_1/2`
70
+ - `test_dashboard_action_types.py` — highlight action (`tsc:brush`), field-captions param, multiple coexisting actions, error handling (unsupported type, unknown dashboard)
71
+ - `test_mcp_tools.py` — `remove_calculated_field` (add/remove/re-add cycle), connection MCP wrappers, `inspect_target_schema`, `list_capabilities`, `analyze_twb`
72
+ - `test_template_datasource_structure.py` — Superstore template structural sanity: column count, connection class, datasource-dependencies
73
+ - **`tests/README.md`**: Full test suite documentation — run instructions, file index grouped by coverage area, function-to-test mapping table, known gaps.
74
+
75
+ ### Changed
76
+
77
+ - **Examples reorganized**: Scripts moved from `examples/` root into `examples/scripts/` with consistent `demo_` prefix naming. `examples/README.md` updated with a new 7-script progression table and expanded Showcase Projects section.
78
+ - **Exec Overview example refined**: Dashboard header updated to 2023, KPI cards use `pane_cell_style` for center alignment, `show_title: false` on Sales by Sub-Category worksheet, spacer zone added for axis alignment.
79
+
80
+ ## [0.12.0] - 2026-03-13
81
+
82
+ ### Added
83
+ - **Bundled Hyper Extracts**: `Sample - EU Superstore.hyper` and `Sample _ Superstore.hyper` are now included in `src/twilize/references/` and distributed with the wheel. `hyper_and_new_charts.py` and `build_exec_overview.py` no longer require a cloned repository.
84
+ - **Progressive Examples**: All scripts in `examples/scripts/` (5 steps, Beginner → Advanced) and prompts in `examples/prompts/` (10 steps, Beginner → Advanced) now carry explicit step numbers and difficulty labels. `examples/README.md` rewritten with a Quick Start section and full progression tables.
85
+
86
+ ### Fixed
87
+ - **Packaging**: Removed redundant `artifacts` declarations from `pyproject.toml`. All non-Python files under `src/twilize/` are git-tracked and included in the wheel automatically via `packages = ["src/twilize"]`.
88
+ - **`.gitignore`**: Added `!src/twilize/references/*.hyper` exception so bundled Hyper files are tracked by git and always present at wheel build time.
89
+ - **Examples — zero external dependencies**: All scripts in `examples/scripts/` and prompts in `examples/prompts/` updated to use `TWBEditor("")` / `create_workbook("")` (built-in default template) instead of hard-coded paths to `templates/twb/superstore.twb`. All work after a plain `pip install twilize`.
90
+
91
+ ## [0.11.0] - 2026-03-13
92
+
93
+ ### Added
94
+ - **Table Calculation Fields**: `add_calculated_field` now accepts a `table_calc` parameter (e.g. `table_calc="Rows"`) that writes a `<table-calc ordering-type="..."/>` child inside the `<calculation>` element, enabling `RANK_DENSE`, `RUNNING_SUM`, `WINDOW_AVG`, and all other Tableau table calculation functions to work correctly in the generated workbook.
95
+ - **Table Calc Column Instances**: `_setup_datasource_dependencies` in `builder_base` now automatically propagates a `<table-calc ordering-type="Columns"/>` element to any `<column-instance>` whose source column contains a table-calc calculation, matching the pattern Tableau uses for rank and running calculations.
96
+ - **Multi-field Label Support**: `configure_chart` now accepts `label_extra: list[str]` to bind multiple `<text>` encodings to a single mark, enabling combined text labels such as a sales figure plus a state name in one cell.
97
+ - **Row Dimension Label Hiding**: `configure_worksheet_style` now accepts `hide_row_label: str` to suppress the header column that Tableau renders for a rows-shelf dimension (adds `<style-rule element="label"><format attr="display" ... value="false"/></style-rule>`).
98
+ - **Donut Chart via `extra_axes`**: Pie panes in `configure_dual_axis(extra_axes=[...])` now automatically receive a `<size column="[Multiple Values]"/>` encoding when `measure_values` is present, completing the standard Tableau donut chart pattern without manual intervention.
99
+ - **Non-traditional Pie Mark via `BasicChartBuilder`**: `configure_chart(mark_type="Pie")` without `color` or `wedge_size` now routes through `BasicChartBuilder` instead of `PieChartBuilder`, allowing a Pie mark to display a label (e.g. a rank number) on a dimension-shelved view while retaining full rows/sort/filter support.
100
+ - **`selection-relaxation-disallow` on single-pane charts**: All charts built by `BasicChartBuilder` now set `selection-relaxation-option="selection-relaxation-disallow"` on the `<pane>` element, matching Tableau's default for filtered single-view worksheets and preventing click-interaction from relaxing Top N or categorical filters.
101
+
102
+ ### Fixed
103
+ - **Measure Names filter ordering**: In `configure_dual_axis` with `extra_axes` containing `measure_values`, the Measure Names `<filter>` is now inserted into the `<view>` before Top N filters, matching the element order Tableau produces when creating these worksheets interactively.
104
+
105
+ ### Example
106
+ - **Exec Overview Recreated** (`examples/superstore_recreated/`): Added `Rank CY` table calculation field; corrected `Top 5 Locations` to use Pie mark with Rank CY label; corrected `Top 5 Locations text` and `Sales by Sub-Category` to match reference workbook (donut size encoding, label style rules, filter order).
107
+
108
+ ## [0.10.0] - 2026-03-11
109
+
110
+ ### Fixed
111
+ - **XML Schema Conformity**: Fixed strict DTD validation errors related to `<pane>` child element ordering (e.g., `<customized-label>` must precede `<style>`) and `<datasource>` element ordering.
112
+ - **Customized Labels**: Fixed `<customized-label>` generation to correctly wrap dynamic template variables with physical `<` and `>` runs within the XML `<formatted-text>` nodes, complying with strict formatting limits.
113
+ - **Object-Graph Relationship Wiping**: Completely rewrote the `set_hyper_connection` logic for multi-table connections. It now preserves table-level relational links by surgically updating individual pre-existing `<object-graph>/<relation>` attributes correctly matching object definitions instead of flattening them into an unmapped collection.
114
+
115
+ ### Added
116
+ - **Charting Capabilities**: Added parameters `axis_fixed_range` to configure exact visual bounds on measures, `color_map` for granular dataset-level color palette assignments, `mark_sizing_off` to disable auto scaling, `customized_label` for rich template texts, and `text_format` for rapid formatting adjustments.
117
+ - **Dashboard Enhancements**: Added support for explicit `"empty"` layout model objects acting as blank spacers within absolute sizing layouts.
118
+ - **MCP Server Capabilities**: Exposed `configure_worksheet_style` tool expressly to cleanly edit gridlines and aesthetics independently of core configuration.
119
+
120
+ ## [0.9.0] - 2026-03-10
121
+
122
+ ### Added
123
+ - **Unified Recipe Chart API**: Added `configure_chart_recipe` as the single MCP/server entrypoint for showcase recipes, covering `lollipop`, `donut`, `butterfly`, and `calendar` via one registry-driven dispatcher.
124
+ - **Recipe Validation Coverage**: Added regression tests for unknown recipe rejection, required-argument validation, automatic prerequisite field creation, and full `all_supported_charts` showcase reconstruction through the unified recipe API.
125
+
126
+ ### Changed
127
+ - **Recipe Tool Surface**: Replaced the old recipe-specific MCP tools with one `configure_chart_recipe` interface to keep the public API compact as more showcase patterns are added.
128
+ - **Recipe Defaults**: Donut and Calendar recipes now auto-create their standard helper calculations (`min 0` and `Sales Over 400`) when those defaults are used and the fields are missing.
129
+ - **Examples and Prompts**: Updated README, examples, skill docs, and the showcase MCP prompt to teach the unified recipe workflow instead of enumerating one tool per recipe chart.
130
+
131
+ ### Removed
132
+ - **Recipe-Specific MCP Tools**: Removed `configure_lollipop_chart`, `configure_donut_chart`, `configure_butterfly_chart`, `configure_calendar_chart`, and `apply_calendar_chart_layout` from the public MCP/server API.
133
+
134
+ ## [0.8.0] - 2026-03-09
135
+
136
+ ### Added
137
+ - **Capability Registry**: Added a shared capability catalog that classifies chart, encoding, dashboard, action, connection, and feature support into `core`, `advanced`, `recipe`, and `unsupported` tiers.
138
+ - **TWB Gap Analysis**: Added `twb_analyzer.py` plus MCP tools `list_capabilities`, `describe_capability`, `analyze_twb`, and `diff_template_gap` so templates can be evaluated against the declared product boundary before implementation work begins.
139
+ - **Hyper Example Coverage**: Added `tests/test_hyper_example.py` to lock in the Advent Calendar Hyper example's physical `Orders_*` table resolution via Tableau Hyper API.
140
+
141
+ ### Changed
142
+ - **Product Positioning**: Updated the root README and example READMEs to describe `twilize` as a workbook engineering toolkit rather than a conversational analysis competitor.
143
+ - **Chart Architecture**: Refactored chart handling into focused modules under `src/twilize/charts/`, including dispatcher, pattern mapping, routing policy, helpers, and a dedicated text builder while keeping the public `configure_chart` API stable.
144
+ - **Dashboard Architecture**: Split dashboard orchestration, layout resolution, datasource dependency generation, and action creation into dedicated modules while keeping `DashboardsMixin` as a thin compatibility facade.
145
+ - **Layout Architecture**: Split declarative layout computation and XML rendering into `layout_model.py` and `layout_rendering.py`, leaving `layout.py` as a compatibility export layer.
146
+ - **MCP Architecture**: Split the MCP server implementation into `mcp/app.py`, `mcp/state.py`, `mcp/resources.py`, `mcp/tools_support.py`, `mcp/tools_layout.py`, and `mcp/tools_workbook.py`, with `server.py` now acting as a thin compatibility entry point.
147
+ - **Advanced Hyper Example**: Updated `examples/hyper_and_new_charts.py` to prefer the Tableau Advent Calendar `Sample - EU Superstore.hyper` extract and resolve the physical `Orders_*` table name automatically.
148
+
149
+ ### Fixed
150
+ - **Hyper Extract Selection**: The advanced Hyper example no longer picks the first bundled `.hyper` file opportunistically and instead targets the intended Superstore extract.
151
+ - **Hyper Table Resolution**: The advanced Hyper example now resolves the real physical `Orders_*` table name instead of using an incorrect generic table name.
152
+
153
+ ## [0.7.0] - 2026-03-06
154
+
155
+ ### Added
156
+ - **Agent Skills Workflow System**: Introduced 4 specialized skill files that provide expert-level guidance to AI agents during dashboard creation, inspired by Jeffrey Shaffer (Tableau Visionary Hall of Fame).
157
+ - `calculation_builder.md` — Phase 1: Parameters, calculated fields, LOD expressions
158
+ - `chart_builder.md` — Phase 2: Chart type selection, encodings, filter strategy
159
+ - `dashboard_designer.md` — Phase 3: Layout design, filter panels, interaction actions
160
+ - `formatting.md` — Phase 4: Number formats, color strategy, sorting, tooltips
161
+ - **Skills MCP Resources**: Skills are exposed via MCP protocol as `twilize://skills/index` and `twilize://skills/{skill_name}`, allowing AI agents to load domain expertise on demand.
162
+ - **Updated MCP Server Instructions**: Server instructions now prompt AI agents to read skills before each phase for professional-quality output.
163
+
164
+ ### Changed
165
+ - **ROADMAP**: Updated `docs/ROADMAP.md` — marked completed P0 items (module refactor ✅, version sync ✅), added new Skills workflow section.
166
+ - **Package Build**: Added `artifacts` config in `pyproject.toml` to ensure `.md` skill files are distributed with the PyPI wheel.
167
+
168
+ ## [0.6.0] - 2026-03-06
169
+
170
+ ### Added
171
+ - **Runtime TWB Validator**: `save()` now automatically validates TWB XML structure before writing to disk. Fatal errors (missing `<workbook>`, `<datasources>`, `<table>`) raise `TWBValidationError` and block saving; non-fatal warnings are logged. Validation can be disabled via `save(path, validate=False)`.
172
+ - **`map_fields` Parameter**: New parameter for `configure_chart(mark_type="Map", ...)` allowing users to specify additional geographic LOD fields (e.g. `map_fields=["Country/Region", "City"]`).
173
+ - **TWBAssert DSL**: Chainable assertion API (`tests/twb_assert.py`) for structural TWB validation in tests, with 20+ assertion methods covering worksheets, encodings, filters, parameters, calculated fields, dashboards, and maps.
174
+ - **Shared Test Fixtures**: Added `tests/conftest.py` with `editor` and `editor_superstore` pytest fixtures.
175
+ - **Structure Test Suite**: 19 new tests in `tests/test_twb_structure.py` covering Bar, Line, Pie, Area, Map, KPI, Parameters, Calculated Fields, Dashboards, and Filters.
176
+ - **Project Roadmap**: Added `docs/ROADMAP.md` with P0–P3 priority issues and feature plans.
177
+
178
+ ### Changed
179
+ - **Module Architecture**: Refactored `twb_editor.py` (2083→375 lines) into Mixin classes:
180
+ - `charts.py` (ChartsMixin) — `configure_chart` and 9 chart helper methods
181
+ - `dashboards.py` (DashboardsMixin) — `add_dashboard` and dashboard actions
182
+ - `connections.py` (ConnectionsMixin) — MySQL and Tableau Server connections
183
+ - `parameters.py` (ParametersMixin) — parameter management
184
+ - `config.py` — shared constants and `_generate_uuid`
185
+ - **Version Management**: `__init__.py` now dynamically reads the version from `pyproject.toml` via `importlib.metadata` instead of hardcoding it.
186
+ - **API Exports**: `__init__.py` now exports `TWBEditor`, `FieldRegistry`, and `TWBValidationError`.
187
+ - **Worksheet XML Structure**: Improved `add_worksheet` to generate proper `<panes>/<pane>/<view>` hierarchy, `<style>` element, and `<simple-id>` placement per Tableau XSD schema.
188
+
189
+ ### Fixed
190
+ - **Error Handling**: Replaced 6 instances of `except Exception: pass` with specific exception types (`KeyError`, `ValueError`) and proper `logging` output across `twb_editor.py`, `layout.py`.
191
+ - **Circular Import**: Extracted `REFERENCES_DIR` and path constants to `config.py`, eliminating circular imports between `twb_editor.py` and `server.py`.
192
+ - **Redundant Imports**: Removed 4 function-level `import` statements (`re`, `copy`, `dataclasses.replace`) by consolidating them at module level.
193
+
194
+ ### Breaking Changes
195
+ - **Map Charts**: `configure_chart(mark_type="Map")` no longer automatically adds `Country/Region` as an LOD field. Users must now explicitly pass `map_fields=["Country/Region"]` if needed.
196
+
197
+ ## [0.5.3] - 2026-03-05
198
+
199
+ ### Fixed
200
+ - **Calculated Field Parsing**: Improved parameter replacement regex to safely handle both `[ParamName]` and `[Parameters].[ParamName]` formats, preventing double-prefixing and broken expressions.
201
+
202
+ ## [0.5.2] - 2026-03-05
203
+
204
+ ### Added
205
+ - **Business Profitability Overview Prompt**: Added `examples/prompts/overview_business_demo.md` to demonstrate creating an interactive what-if profitability dashboard with parameters and dashboard actions.
206
+
207
+ ### Fixed
208
+ - **Packaging Issues**: Pinned `hatchling<1.27.0` to workaround a `twine` metadata validation error related to `license-files` and fixed Windows encoding issues during package build.
209
+
210
+ ## [0.5.1] - 2026-03-04
211
+
212
+ ### Changed
213
+ - **License**: Updated project license from MIT to AGPL-3.0-or-later.
214
+
215
+ ## [0.5.0] - 2026-03-02
216
+
217
+ ### Added
218
+ - **Zero-Config Blank Templates**: The SDK and MCP server now come with a built-in `empty_template.twb` and a minimal `Sample - Superstore - simple.xls` dataset.
219
+ - **Dynamic Connection Resolution**: When initializing `TWBEditor` without a `template_path`, it automatically resolves and rewrites the internal Excel connection to the runtime absolute path of the bundled sample dataset.
220
+ - **Always-Valid Workbooks**: `clear_worksheets()` now guarantees the creation of at least one default worksheet (`Sheet 1`), ensuring generated TWB files are completely valid and openable in Tableau Desktop immediately upon creation.
221
+
222
+ ### Changed
223
+ - **MCP Tool `create_workbook`**: The `template_path` parameter is now optional. When omitted, it boots up the zero-config blank template.
224
+ - **XML Element Ordering Fix**: `add_worksheet` and `add_dashboard` now strictly enforce the Tableau XSD schema by smartly inserting XML nodes *before* `<windows>`, `<thumbnails>`, and `<external>` tags instead of appending them at the end.
225
+
226
+ ## [0.4.0] - 2026-02-28
227
+
228
+ ### Fixed
229
+ - **Dashboard Sizing Bug**: Added `sizing-mode="fixed"` to the dashboard `<size>` element. This ensures that custom dimensions (width/height) specified in `add_dashboard` are correctly enforced by Tableau Desktop.
230
+
231
+ ### Added
232
+ - **New Showcase Prompt**: Added `examples/prompts/demo_auto_layout4_prompt.md` in English, demonstrating complex nested layouts with fixed headers and KPIs.
233
+ - **Enhanced Testing**: Added assertions to verify `sizing-mode` in `test_declarative_dashboards.py`.
234
+
235
+ ## [0.3.0] - 2026-02-28
236
+
237
+ ### Added
238
+
239
+ - **Agentic UX Tool: `generate_layout_json`**:
240
+ - Introduced a dedicated MCP tool tailored for Language Models to handle complex dashboard layouts gracefully avoiding `EOF` (End of File) payload oversize crashes.
241
+ - Automatically wraps standard nested `layout` dicts inside the payload with a human-readable `_ascii_layout_preview`, persisting an easy-to-review design draft to local disk.
242
+ - Generates perfectly calculated XML `<zone>` absolute coordinates (in Tableau's 100,000 scale) for both relative weighting (`weight`) and exact sizes (`fixed_size`).
243
+ - `TWBEditor.add_dashboard()` intelligent parsing:
244
+ - If given a file path, it now smartly unpacks the layout JSON, automatically extracting `"layout_schema"` and safely discarding extraneous metadata (like the ASCII diagram).
245
+ - **Prompt Strategies (`demo_simple.md`)**:
246
+ - Updated the golden prompt strategy guide showing models how to seamlessly split reasoning logic: Step 1 (Tool: write layout to disk) -> Step 2 (Tool: pass file path to build dashboard).
247
+
248
+ ## [0.2.0] - 2026-02-28
249
+
250
+ ### Added
251
+
252
+ - **Database Connections**:
253
+ - `TWBEditor.set_mysql_connection`: Configure TWB to load data from a Local MySQL database.
254
+ - `TWBEditor.set_tableauserver_connection`: Configure TWB for a Tableau Server hosted datasource.
255
+ - Automatically clears template-included dashboards, worksheets, metadata, and column aliases during config to prevent phantom "ghost" fields.
256
+ - **Dynamic Field Registration**:
257
+ - The `field_registry` will now automatically infer the field type natively via naming heuristics when initializing `configure_chart` on strictly offline unknown schemas.
258
+ - **Declarative JSON Dashboard Layouts**:
259
+ - `add_dashboard` now accepts a deeply nested dictionary (JSON-friendly) `layout` schema, allowing complex, FlexBox-like hierarchical layouts.
260
+ - `add_dashboard` `layout` parameter also directly accepts a file path (string) to a `.json` file, easing the payload size for MCP LLM calls.
261
+ - Generates perfectly calculated XML `<zone>` absolute coordinates (in Tableau's 100,000 scale) for both relative weighting (`weight`) and exact sizes (`fixed_size`).
262
+ - Added `demo_declarative_layout.py` showcasing the JSON engine.
263
+ - **MCP Server Tools**:
264
+ - Exposed `set_mysql_connection` and `set_tableauserver_connection` to the MCP Server.
265
+ - Upgraded `add_dashboard` MCP tool to accept JSON-based dictionaries and JSON file paths for the `layout` schema.
266
+ - **Examples & Documentation**:
267
+ - Reorganized the `examples/` directory into `scripts/` (for Python demos) and `prompts/` (for natural language MCP prompts).
268
+ - Extracted test workflows into runnable demos (`demo_e2e_mcp_workflow.py` and `demo_connections.py`).
269
+
270
+ ## [0.1.0] - 2026-02-27
271
+
272
+ ### Added
273
+
274
+ - **Core library** (`twilize`):
275
+ - `FieldRegistry`: Field name ↔ TWB internal reference mapping with expression parsing (SUM, AVG, COUNT, YEAR, etc.)
276
+ - `TWBEditor`: lxml-based TWB XML editor supporting:
277
+ - Template loading and field initialization
278
+ - Calculated field management (add/remove)
279
+ - Worksheet creation with configurable chart types (Bar, Line, Pie, Area, Circle, Text, Automatic)
280
+ - Color, size, label, detail, and wedge-size encodings
281
+ - Dashboard creation with layout-flow zone structure (vertical, horizontal, grid-2x2)
282
+ - Valid TWB output compatible with Tableau Desktop
283
+
284
+ - **MCP Server** (`server.py`):
285
+ - 8 atomic tools: `create_workbook`, `list_fields`, `add_calculated_field`, `remove_calculated_field`, `add_worksheet`, `configure_chart`, `add_dashboard`, `save_workbook`
286
+ - FastMCP-based stdio transport
287
+
288
+ - **Dashboard layouts**:
289
+ - Verified against Tableau Dashboard Layout Templates (c.2 (2) reference)
290
+ - Dashboard windows use `<viewpoints>` + `<active>` structure (not `<cards>`)
291
+ - Zone structure uses `layout-flow` with `param='vert'/'horz'`
292
+
293
+ - **Tests**:
294
+ - `test_debug.py`: Step-by-step debug test generating intermediate TWB files
295
+ - `test_e2e.py`: End-to-end integration test covering all MCP tools
296
+ - `test_c2_replica.py`: Full replica of c.2 (2) dashboard layout with 8 worksheets
297
+
298
+ - **Package configuration**:
299
+ - `pyproject.toml` with hatchling build backend
300
+ - `twilize` CLI entry point
@@ -0,0 +1,171 @@
1
+ # Contributing to twilize
2
+
3
+ ## Quick start
4
+
5
+ ```bash
6
+ git clone https://github.com/your-org/twilize
7
+ cd twilize
8
+ pip install -e ".[dev]"
9
+ pytest
10
+ ```
11
+
12
+ `[dev]` includes local contributor tooling (test/lint/type-check).
13
+ If you only need runtime usage, `pip install -e .` is enough.
14
+
15
+ All tests should pass before you start. If any fail, check the known-issues
16
+ section in `tests/README.md` first.
17
+
18
+ ---
19
+
20
+ ## Architecture overview
21
+
22
+ ```
23
+ MCP tool call (tools_workbook.py / tools_migration.py / tools_support.py)
24
+
25
+
26
+ ChartsMixin method (src/twilize/charts/__init__.py)
27
+ │ facade: stable public API, no logic
28
+
29
+ Dispatcher function (src/twilize/charts/dispatcher.py)
30
+ │ selects the right builder based on mark_type + routing_policy.py
31
+
32
+ Concrete Builder (builder_basic.py / builder_dual_axis.py / builder_pie.py / …)
33
+ │ constructs the lxml XML subtree for the worksheet
34
+
35
+ TWBEditor XML tree (twb_editor.py via lxml)
36
+
37
+
38
+ save() → validate() → .twb / .twbx
39
+ ```
40
+
41
+ **Rule:** Every new parameter must travel the full chain top to bottom.
42
+ Adding it only to the builder but forgetting the MCP tool (or vice versa)
43
+ causes silent divergence between the Python API and the MCP surface.
44
+
45
+ ---
46
+
47
+ ## Module responsibilities
48
+
49
+ | Module | Role |
50
+ |---|---|
51
+ | `twb_editor.py` | Main editor class. Composed from mixins. Owns the lxml tree. |
52
+ | `charts/__init__.py` | `ChartsMixin` — thin public facade, delegates to dispatcher |
53
+ | `charts/dispatcher.py` | Selects builder, forwards all parameters |
54
+ | `charts/routing_policy.py` | Rules for which builder handles which `mark_type` |
55
+ | `charts/builder_base.py` | Shared XML helpers used by all builders |
56
+ | `charts/builder_basic.py` | Bar, Line, Area, Scatter, Heatmap, and other standard marks |
57
+ | `charts/builder_dual_axis.py` | Dual-axis compositions (two panes, synchronized axes) |
58
+ | `charts/builder_pie.py` | Pie and donut (via extra_axes) |
59
+ | `charts/builder_text.py` | Text / KPI cards with rich-text label support |
60
+ | `charts/builder_maps.py` | Filled and symbol maps |
61
+ | `charts/helpers.py` | `apply_worksheet_style`, `setup_table_style`, shared XML primitives |
62
+ | `field_registry.py` | Tracks calculated fields added in this session; used for datasource-dep injection |
63
+ | `capability_registry.py` | Declares what twilize supports; used by `analyze_twb` and `list_capabilities` |
64
+ | `migration.py` | Field-mapping inference and workbook rewrite for datasource migration |
65
+ | `validator.py` | Structural and XSD validation called by `save()` |
66
+ | `mcp/` | MCP server tool definitions (thin wrappers; no logic lives here) |
67
+
68
+ ---
69
+
70
+ ## Adding a parameter to an existing chart
71
+
72
+ Example: adding `my_param` to `configure_chart`.
73
+
74
+ **Step 1 — Builder** (`builder_basic.py` or whichever builder handles the mark type)
75
+
76
+ ```python
77
+ class BasicChartBuilder:
78
+ def __init__(self, ..., my_param: str | None = None):
79
+ self.my_param = my_param
80
+
81
+ def build(self):
82
+ ...
83
+ if self.my_param:
84
+ # write the relevant XML
85
+ ```
86
+
87
+ **Step 2 — Dispatcher** (`charts/dispatcher.py`)
88
+
89
+ ```python
90
+ def configure_chart(editor, ..., my_param: str | None = None) -> str:
91
+ ...
92
+ builder = BasicChartBuilder(..., my_param=my_param)
93
+ ```
94
+
95
+ **Step 3 — ChartsMixin** (`charts/__init__.py`)
96
+
97
+ ```python
98
+ def configure_chart(self, ..., my_param: str | None = None) -> str:
99
+ return dispatch_configure_chart(self, ..., my_param=my_param)
100
+ ```
101
+
102
+ **Step 4 — MCP tool** (`mcp/tools_workbook.py`)
103
+
104
+ ```python
105
+ @server.tool()
106
+ def configure_chart(..., my_param: str | None = None) -> str:
107
+ return editor.configure_chart(..., my_param=my_param)
108
+ ```
109
+
110
+ **Step 5 — Test**
111
+
112
+ Add a test in `tests/` that calls `configure_chart` with the new parameter and
113
+ asserts the expected XML is present using XPath.
114
+
115
+ ---
116
+
117
+ ## Adding a new chart type
118
+
119
+ 1. Create `src/twilize/charts/builder_mytype.py` subclassing `BaseChartBuilder`
120
+ 2. Add a routing rule in `charts/routing_policy.py`
121
+ 3. Import and dispatch in `charts/dispatcher.py`
122
+ 4. Declare the capability in `capability_registry.py`
123
+ 5. Wire through `ChartsMixin` and the MCP tool if needed
124
+ 6. Add at least one test and one example
125
+
126
+ ---
127
+
128
+ ## Adding a capability to the registry
129
+
130
+ Open `src/twilize/capability_registry.py` and add an entry to `CAPABILITIES`:
131
+
132
+ ```python
133
+ Capability(
134
+ kind="chart",
135
+ name="MyNewChart",
136
+ level="core", # core | advanced | recipe | unsupported
137
+ description="...",
138
+ notes="",
139
+ ),
140
+ ```
141
+
142
+ `level` controls what `list_capabilities` and `analyze_twb` report.
143
+
144
+ ---
145
+
146
+ ## Testing
147
+
148
+ ```bash
149
+ # Full suite
150
+ pytest
151
+
152
+ # Single file
153
+ pytest tests/test_chart_routing.py -v
154
+
155
+ # Keep temp workbooks for inspection
156
+ pytest --basetemp=output/pytest_tmp
157
+ ```
158
+
159
+ Tests are integration tests: they build real `.twb` XML and assert structure
160
+ with XPath. There are no mocks of the lxml tree. This is intentional — mocking
161
+ the XML layer has historically hidden bugs that only appear in Tableau Desktop.
162
+
163
+ ---
164
+
165
+ ## Code style
166
+
167
+ - Python 3.10+, no walrus operator in public API signatures
168
+ - `from __future__ import annotations` at the top of every module
169
+ - No logic in MCP tool functions — they are thin wrappers only
170
+ - No silent `except Exception: pass` — log or re-raise with context
171
+ - Imports at the top of files, not inside function bodies
@@ -0,0 +1,32 @@
1
+ # ---- Stage 1: Build the frontend ----
2
+ FROM node:20-slim AS frontend-builder
3
+
4
+ WORKDIR /app/extension/frontend
5
+ COPY extension/frontend/package.json extension/frontend/package-lock.json* ./
6
+ RUN npm install
7
+ COPY extension/frontend/ ./
8
+ RUN npm run build
9
+
10
+ # ---- Stage 2: Python runtime ----
11
+ FROM python:3.13-slim
12
+
13
+ WORKDIR /app
14
+
15
+ # Install Python dependencies
16
+ COPY requirements.txt ./
17
+ RUN pip install --no-cache-dir -r requirements.txt
18
+
19
+ # Copy the full project
20
+ COPY . .
21
+
22
+ # Install the twilize package in editable mode
23
+ RUN pip install --no-cache-dir -e .
24
+
25
+ # Copy the built frontend from stage 1
26
+ COPY --from=frontend-builder /app/extension/frontend/dist /app/extension/frontend/dist
27
+
28
+ # Railway sets $PORT; default to 8000
29
+ ENV PORT=8000
30
+ EXPOSE ${PORT}
31
+
32
+ CMD python -m uvicorn extension.backend.app:app --host 0.0.0.0 --port ${PORT}