modelwright 0.1.0a4__tar.gz → 0.1.0a6__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 (65) hide show
  1. modelwright-0.1.0a6/MANIFEST.in +1 -0
  2. {modelwright-0.1.0a4/src/modelwright.egg-info → modelwright-0.1.0a6}/PKG-INFO +6 -2
  3. {modelwright-0.1.0a4 → modelwright-0.1.0a6}/README.md +1 -1
  4. modelwright-0.1.0a6/examples/README.md +35 -0
  5. modelwright-0.1.0a6/examples/fable_2020/README.md +21 -0
  6. modelwright-0.1.0a6/examples/fable_2020/generated_fable_2020_model.py.xz +0 -0
  7. modelwright-0.1.0a6/examples/fable_2020/notebook_interface.py +95 -0
  8. modelwright-0.1.0a6/examples/notebooks/README.md +11 -0
  9. modelwright-0.1.0a6/examples/notebooks/fable-2020-notebook-interface.ipynb +300 -0
  10. modelwright-0.1.0a6/examples/notebooks/synthetic-notebook-interface.ipynb +282 -0
  11. modelwright-0.1.0a6/examples/synthetic/notebook_interface.py +62 -0
  12. {modelwright-0.1.0a4 → modelwright-0.1.0a6}/pyproject.toml +6 -1
  13. {modelwright-0.1.0a4 → modelwright-0.1.0a6}/src/modelwright/__init__.py +17 -1
  14. modelwright-0.1.0a6/src/modelwright/notebooks.py +246 -0
  15. {modelwright-0.1.0a4 → modelwright-0.1.0a6/src/modelwright.egg-info}/PKG-INFO +6 -2
  16. {modelwright-0.1.0a4 → modelwright-0.1.0a6}/src/modelwright.egg-info/SOURCES.txt +12 -0
  17. {modelwright-0.1.0a4 → modelwright-0.1.0a6}/src/modelwright.egg-info/requires.txt +5 -0
  18. modelwright-0.1.0a6/tests/test_examples.py +125 -0
  19. {modelwright-0.1.0a4 → modelwright-0.1.0a6}/tests/test_fable_wrapper_benchmark.py +22 -0
  20. {modelwright-0.1.0a4 → modelwright-0.1.0a6}/tests/test_import.py +1 -1
  21. modelwright-0.1.0a6/tests/test_notebooks.py +211 -0
  22. {modelwright-0.1.0a4 → modelwright-0.1.0a6}/tests/test_public_api.py +4 -0
  23. {modelwright-0.1.0a4 → modelwright-0.1.0a6}/LICENSE +0 -0
  24. {modelwright-0.1.0a4 → modelwright-0.1.0a6}/setup.cfg +0 -0
  25. {modelwright-0.1.0a4 → modelwright-0.1.0a6}/src/modelwright/cli.py +0 -0
  26. {modelwright-0.1.0a4 → modelwright-0.1.0a6}/src/modelwright/conversion.py +0 -0
  27. {modelwright-0.1.0a4 → modelwright-0.1.0a6}/src/modelwright/evaluation.py +0 -0
  28. {modelwright-0.1.0a4 → modelwright-0.1.0a6}/src/modelwright/execution.py +0 -0
  29. {modelwright-0.1.0a4 → modelwright-0.1.0a6}/src/modelwright/extraction.py +0 -0
  30. {modelwright-0.1.0a4 → modelwright-0.1.0a6}/src/modelwright/formulas.py +0 -0
  31. {modelwright-0.1.0a4 → modelwright-0.1.0a6}/src/modelwright/formulas_oracle.py +0 -0
  32. {modelwright-0.1.0a4 → modelwright-0.1.0a6}/src/modelwright/generation.py +0 -0
  33. {modelwright-0.1.0a4 → modelwright-0.1.0a6}/src/modelwright/graph.py +0 -0
  34. {modelwright-0.1.0a4 → modelwright-0.1.0a6}/src/modelwright/oracle_validation.py +0 -0
  35. {modelwright-0.1.0a4 → modelwright-0.1.0a6}/src/modelwright/oracles.py +0 -0
  36. {modelwright-0.1.0a4 → modelwright-0.1.0a6}/src/modelwright/references.py +0 -0
  37. {modelwright-0.1.0a4 → modelwright-0.1.0a6}/src/modelwright/validation.py +0 -0
  38. {modelwright-0.1.0a4 → modelwright-0.1.0a6}/src/modelwright/wrappers.py +0 -0
  39. {modelwright-0.1.0a4 → modelwright-0.1.0a6}/src/modelwright.egg-info/dependency_links.txt +0 -0
  40. {modelwright-0.1.0a4 → modelwright-0.1.0a6}/src/modelwright.egg-info/entry_points.txt +0 -0
  41. {modelwright-0.1.0a4 → modelwright-0.1.0a6}/src/modelwright.egg-info/top_level.txt +0 -0
  42. {modelwright-0.1.0a4 → modelwright-0.1.0a6}/tests/test_cli.py +0 -0
  43. {modelwright-0.1.0a4 → modelwright-0.1.0a6}/tests/test_conversion_plan.py +0 -0
  44. {modelwright-0.1.0a4 → modelwright-0.1.0a6}/tests/test_dependency_graph.py +0 -0
  45. {modelwright-0.1.0a4 → modelwright-0.1.0a6}/tests/test_evaluation_orchestration.py +0 -0
  46. {modelwright-0.1.0a4 → modelwright-0.1.0a6}/tests/test_extraction_records.py +0 -0
  47. {modelwright-0.1.0a4 → modelwright-0.1.0a6}/tests/test_formula_expressions.py +0 -0
  48. {modelwright-0.1.0a4 → modelwright-0.1.0a6}/tests/test_formula_translation.py +0 -0
  49. {modelwright-0.1.0a4 → modelwright-0.1.0a6}/tests/test_formulas_oracle.py +0 -0
  50. {modelwright-0.1.0a4 → modelwright-0.1.0a6}/tests/test_generated_execution.py +0 -0
  51. {modelwright-0.1.0a4 → modelwright-0.1.0a6}/tests/test_generation_contract.py +0 -0
  52. {modelwright-0.1.0a4 → modelwright-0.1.0a6}/tests/test_materialize_fable_benchmarks.py +0 -0
  53. {modelwright-0.1.0a4 → modelwright-0.1.0a6}/tests/test_openpyxl_extraction.py +0 -0
  54. {modelwright-0.1.0a4 → modelwright-0.1.0a6}/tests/test_oracle_backed_validation.py +0 -0
  55. {modelwright-0.1.0a4 → modelwright-0.1.0a6}/tests/test_oracle_interface.py +0 -0
  56. {modelwright-0.1.0a4 → modelwright-0.1.0a6}/tests/test_python_generation.py +0 -0
  57. {modelwright-0.1.0a4 → modelwright-0.1.0a6}/tests/test_references.py +0 -0
  58. {modelwright-0.1.0a4 → modelwright-0.1.0a6}/tests/test_scalar_comparison.py +0 -0
  59. {modelwright-0.1.0a4 → modelwright-0.1.0a6}/tests/test_supported_semantics_fixture.py +0 -0
  60. {modelwright-0.1.0a4 → modelwright-0.1.0a6}/tests/test_synthetic_fixture.py +0 -0
  61. {modelwright-0.1.0a4 → modelwright-0.1.0a6}/tests/test_validation.py +0 -0
  62. {modelwright-0.1.0a4 → modelwright-0.1.0a6}/tests/test_validation_regression.py +0 -0
  63. {modelwright-0.1.0a4 → modelwright-0.1.0a6}/tests/test_validation_report_builder.py +0 -0
  64. {modelwright-0.1.0a4 → modelwright-0.1.0a6}/tests/test_validation_scenario.py +0 -0
  65. {modelwright-0.1.0a4 → modelwright-0.1.0a6}/tests/test_wrappers.py +0 -0
@@ -0,0 +1 @@
1
+ recursive-include examples *.ipynb *.md *.py *.xz
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: modelwright
3
- Version: 0.1.0a4
3
+ Version: 0.1.0a6
4
4
  Summary: Tools for converting spreadsheet workbooks into transparent Python models.
5
5
  Author: UBC FRESH Lab
6
6
  License-Expression: MIT
@@ -32,11 +32,14 @@ Requires-Dist: sphinx-rtd-theme>=2; extra == "docs"
32
32
  Provides-Extra: dev
33
33
  Requires-Dist: build>=1.2; extra == "dev"
34
34
  Requires-Dist: formulas; extra == "dev"
35
+ Requires-Dist: pandas>=2; extra == "dev"
35
36
  Requires-Dist: pytest>=8; extra == "dev"
36
37
  Requires-Dist: ruff>=0.8; extra == "dev"
37
38
  Requires-Dist: sphinx>=7; extra == "dev"
38
39
  Requires-Dist: sphinx-rtd-theme>=2; extra == "dev"
39
40
  Requires-Dist: twine>=5; extra == "dev"
41
+ Provides-Extra: notebook
42
+ Requires-Dist: pandas>=2; extra == "notebook"
40
43
  Provides-Extra: oracle
41
44
  Requires-Dist: formulas; extra == "oracle"
42
45
  Provides-Extra: quality
@@ -46,6 +49,7 @@ Requires-Dist: build>=1.2; extra == "release"
46
49
  Requires-Dist: twine>=5; extra == "release"
47
50
  Provides-Extra: test
48
51
  Requires-Dist: formulas; extra == "test"
52
+ Requires-Dist: pandas>=2; extra == "test"
49
53
  Requires-Dist: pytest>=8; extra == "test"
50
54
  Dynamic: license-file
51
55
 
@@ -135,7 +139,7 @@ Restore the public external FABLE benchmark workbooks into ignored local paths:
135
139
  scripts/bootstrap_dev_env.sh --benchmarks
136
140
  ```
137
141
 
138
- `modelwright` is pre-release. The current alpha line is `0.1.0a4`; alpha releases must not be described as full-workbook conversion guarantees.
142
+ `modelwright` is pre-release. The current alpha line is `0.1.0a6`; alpha releases must not be described as full-workbook conversion guarantees.
139
143
 
140
144
  Check release artifacts locally:
141
145
 
@@ -84,7 +84,7 @@ Restore the public external FABLE benchmark workbooks into ignored local paths:
84
84
  scripts/bootstrap_dev_env.sh --benchmarks
85
85
  ```
86
86
 
87
- `modelwright` is pre-release. The current alpha line is `0.1.0a4`; alpha releases must not be described as full-workbook conversion guarantees.
87
+ `modelwright` is pre-release. The current alpha line is `0.1.0a6`; alpha releases must not be described as full-workbook conversion guarantees.
88
88
 
89
89
  Check release artifacts locally:
90
90
 
@@ -0,0 +1,35 @@
1
+ # Modelwright Examples
2
+
3
+ These examples show how generated Modelwright Python models can be wrapped with analyst-facing
4
+ facades and notebook-friendly DataFrame helpers.
5
+
6
+ - `synthetic/`: a tiny generated-model example based on the tracked synthetic fixture shape.
7
+ - `fable_2020/`: a production-size example using a compressed generated Python model converted from
8
+ the public 2020 FABLE Calculator benchmark workbook.
9
+ - `notebooks/`: real Jupyter notebooks with markdown explanation, code cells, and stored outputs for
10
+ the synthetic and FABLE examples.
11
+
12
+ The original FABLE workbook is not tracked here. The tracked FABLE example contains Modelwright's
13
+ generated Python output, compressed as `generated_fable_2020_model.py.xz` because the uncompressed
14
+ module is larger than ordinary GitHub per-file limits.
15
+
16
+ ## Importing From Local Notebooks
17
+
18
+ These are source-tree examples. If your notebook file lives under `tmp/notebooks/`, add the repository
19
+ root to `sys.path` before importing from `examples`:
20
+
21
+ ```python
22
+ from pathlib import Path
23
+ import sys
24
+
25
+ repo_root = Path.cwd().resolve()
26
+ while repo_root != repo_root.parent and not (repo_root / "pyproject.toml").exists():
27
+ repo_root = repo_root.parent
28
+
29
+ if not (repo_root / "pyproject.toml").exists():
30
+ raise RuntimeError("Could not find the Modelwright repository root.")
31
+
32
+ sys.path.insert(0, str(repo_root))
33
+ ```
34
+
35
+ Then imports such as `from examples.synthetic.notebook_interface import build_facade` will resolve.
@@ -0,0 +1,21 @@
1
+ # 2020 FABLE Generated Model Example
2
+
3
+ This directory contains a compressed generated Python model produced by Modelwright from the public
4
+ 2020 FABLE Calculator benchmark workbook.
5
+
6
+ The original workbook is not tracked in this repository. The generated model is tracked as
7
+ `generated_fable_2020_model.py.xz` because the uncompressed Python module is about 117 MiB, which is
8
+ larger than ordinary GitHub per-file limits. The example script decompresses it into ignored `tmp/`
9
+ working space before importing it.
10
+
11
+ The generated model preserves the P26 full-validation evidence boundary:
12
+
13
+ - comparable cached outputs: 281,741;
14
+ - matches: 281,741;
15
+ - mismatches: 0.
16
+
17
+ Run from the repository root:
18
+
19
+ ```bash
20
+ python examples/fable_2020/notebook_interface.py
21
+ ```
@@ -0,0 +1,95 @@
1
+ """Notebook-interface example for the generated 2020 FABLE benchmark model."""
2
+
3
+ from __future__ import annotations
4
+
5
+ import importlib.util
6
+ import lzma
7
+ import shutil
8
+ import sys
9
+ from pathlib import Path
10
+ from types import ModuleType
11
+
12
+ from modelwright.notebooks import outputs_frame, report_frames, table_frame
13
+ from modelwright.wrappers import ModelFacade, cell, report, table
14
+
15
+
16
+ EXAMPLE_DIR = Path(__file__).resolve().parent
17
+ ARCHIVE_PATH = EXAMPLE_DIR / "generated_fable_2020_model.py.xz"
18
+ WORK_DIR = Path("tmp/examples/fable_2020")
19
+ MODEL_PATH = WORK_DIR / "generated_fable_2020_model.py"
20
+
21
+ FABLE_SCENARIO_OUTPUTS = {
22
+ "SCENARIOS selection!D20": 2.146115426018433,
23
+ "SCENARIOS selection!D21": 1.8982220554032356,
24
+ "SCENARIOS selection!D22": 1.462761288724012,
25
+ }
26
+
27
+
28
+ def materialize_generated_model() -> Path:
29
+ """Decompress the tracked generated model into ignored local working space."""
30
+
31
+ WORK_DIR.mkdir(parents=True, exist_ok=True)
32
+ if not MODEL_PATH.exists():
33
+ with lzma.open(ARCHIVE_PATH, "rb") as source:
34
+ with MODEL_PATH.open("wb") as target:
35
+ shutil.copyfileobj(source, target)
36
+ return MODEL_PATH
37
+
38
+
39
+ def load_generated_model(path: Path | None = None) -> ModuleType:
40
+ model_path = path or materialize_generated_model()
41
+ spec = importlib.util.spec_from_file_location("modelwright_example_fable_2020", model_path)
42
+ if spec is None or spec.loader is None:
43
+ raise RuntimeError(f"could not load generated model from {model_path}")
44
+ module = importlib.util.module_from_spec(spec)
45
+ sys.modules[spec.name] = module
46
+ spec.loader.exec_module(module)
47
+ return module
48
+
49
+
50
+ def build_facade(module: ModuleType | None = None) -> ModelFacade:
51
+ generated_model = module or load_generated_model()
52
+ return ModelFacade(
53
+ generated_model,
54
+ cells=[
55
+ cell("SCENARIOS selection!D20", name="scenario_metric_1", role="output"),
56
+ cell("SCENARIOS selection!D21", name="scenario_metric_2", role="output"),
57
+ cell("SCENARIOS selection!D22", name="scenario_metric_3", role="output"),
58
+ ],
59
+ tables=[
60
+ table(
61
+ "scenario_selection_slice",
62
+ sheet="SCENARIOS selection",
63
+ range_ref="D20:D22",
64
+ row_labels=["d20", "d21", "d22"],
65
+ column_labels=["value"],
66
+ )
67
+ ],
68
+ reports=[
69
+ report(
70
+ "scenario_selection",
71
+ cells=["scenario_metric_1", "scenario_metric_2", "scenario_metric_3"],
72
+ tables=["scenario_selection_slice"],
73
+ )
74
+ ],
75
+ )
76
+
77
+
78
+ def run_example():
79
+ facade = build_facade()
80
+ values = facade.calculate()
81
+ for cell_ref, expected in FABLE_SCENARIO_OUTPUTS.items():
82
+ observed = values[cell_ref]
83
+ if observed != expected:
84
+ raise RuntimeError(f"{cell_ref} expected {expected!r}, observed {observed!r}")
85
+ return {
86
+ "outputs": outputs_frame(facade),
87
+ "scenario_selection_slice": table_frame(facade, "scenario_selection_slice"),
88
+ "report": report_frames(facade, "scenario_selection"),
89
+ }
90
+
91
+
92
+ if __name__ == "__main__":
93
+ frames = run_example()
94
+ print(frames["outputs"])
95
+ print(frames["scenario_selection_slice"])
@@ -0,0 +1,11 @@
1
+ # Literate Notebook Examples
2
+
3
+ These notebooks are tracked examples for the Sphinx Examples Gallery.
4
+
5
+ - `synthetic-notebook-interface.ipynb` is a tiny, fast notebook that can be checked in default tests.
6
+ - `fable-2020-notebook-interface.ipynb` documents the production-size generated 2020 FABLE model
7
+ workflow. The notebook contains known-valid stored outputs, while full execution remains opt-in
8
+ because the generated model import and calculation are intentionally large.
9
+
10
+ Open them from a source checkout with JupyterLab or Jupyter Notebook. The first code cell adds the
11
+ repository root to `sys.path` so imports from the local `examples/` directory resolve.
@@ -0,0 +1,300 @@
1
+ {
2
+ "cells": [
3
+ {
4
+ "cell_type": "markdown",
5
+ "metadata": {},
6
+ "source": [
7
+ "# 2020 FABLE Notebook Interface Example\n",
8
+ "\n",
9
+ "This notebook shows the same notebook-facing workflow on a production-size generated Modelwright model from the public 2020 FABLE Calculator benchmark workbook.\n",
10
+ "\n",
11
+ "The original workbook is not tracked in this repository. The tracked artifact is Modelwright's generated Python output, compressed as `examples/fable_2020/generated_fable_2020_model.py.xz`. The example script decompresses that generated model into ignored `tmp/` working space before import."
12
+ ]
13
+ },
14
+ {
15
+ "cell_type": "markdown",
16
+ "id": "2ceb2405",
17
+ "metadata": {},
18
+ "source": [
19
+ "## 1. Make The Source Checkout Importable\n",
20
+ "\n",
21
+ "This notebook is meant to be opened from a source checkout. The setup cell finds the repository root and places it on `sys.path`, so imports from `examples/` resolve even if the notebook file is copied under `tmp/notebooks/`.\n",
22
+ "\n",
23
+ "Expected output: the package project name, `modelwright`."
24
+ ]
25
+ },
26
+ {
27
+ "cell_type": "code",
28
+ "execution_count": 1,
29
+ "id": "d40dcd53",
30
+ "metadata": {},
31
+ "outputs": [
32
+ {
33
+ "name": "stdout",
34
+ "output_type": "stream",
35
+ "text": [
36
+ "Using project: modelwright\n"
37
+ ]
38
+ }
39
+ ],
40
+ "source": [
41
+ "from pathlib import Path\n",
42
+ "import sys\n",
43
+ "import tomllib\n",
44
+ "\n",
45
+ "repo_root = Path.cwd().resolve()\n",
46
+ "while repo_root != repo_root.parent and not (repo_root / \"pyproject.toml\").exists():\n",
47
+ " repo_root = repo_root.parent\n",
48
+ "\n",
49
+ "if not (repo_root / \"pyproject.toml\").exists():\n",
50
+ " raise RuntimeError(\"Could not find the Modelwright repository root.\")\n",
51
+ "\n",
52
+ "project_name = tomllib.loads((repo_root / \"pyproject.toml\").read_text())[\"project\"][\"name\"]\n",
53
+ "\n",
54
+ "if str(repo_root) not in sys.path:\n",
55
+ " sys.path.insert(0, str(repo_root))\n",
56
+ "\n",
57
+ "print(f\"Using project: {project_name}\")"
58
+ ]
59
+ },
60
+ {
61
+ "cell_type": "markdown",
62
+ "metadata": {},
63
+ "source": [
64
+ "## 2. Load The Production-Size Example Facade\n",
65
+ "\n",
66
+ "`build_facade()` imports the generated FABLE Python model and declares a small analyst-facing wrapper around three validated `SCENARIOS selection` outputs. The first import may take time because the generated model is intentionally production-sized.\n",
67
+ "\n",
68
+ "Expected output: the compressed archive path, the decompressed generated-model path under `tmp/`, and the declared output names."
69
+ ]
70
+ },
71
+ {
72
+ "cell_type": "code",
73
+ "execution_count": 2,
74
+ "metadata": {},
75
+ "outputs": [
76
+ {
77
+ "name": "stdout",
78
+ "output_type": "stream",
79
+ "text": [
80
+ "Archive: examples/fable_2020/generated_fable_2020_model.py.xz\n",
81
+ "Generated model path: tmp/examples/fable_2020/generated_fable_2020_model.py\n",
82
+ "Declared outputs: ['scenario_metric_1', 'scenario_metric_2', 'scenario_metric_3']\n"
83
+ ]
84
+ }
85
+ ],
86
+ "source": [
87
+ "from examples.fable_2020.notebook_interface import ARCHIVE_PATH, MODEL_PATH, build_facade\n",
88
+ "from modelwright.notebooks import outputs_frame, report_frames, table_frame\n",
89
+ "\n",
90
+ "facade = build_facade()\n",
91
+ "\n",
92
+ "print(f\"Archive: {ARCHIVE_PATH.relative_to(repo_root)}\")\n",
93
+ "print(f\"Generated model path: {MODEL_PATH}\")\n",
94
+ "print(f\"Declared outputs: {list(facade.outputs())}\")"
95
+ ]
96
+ },
97
+ {
98
+ "cell_type": "markdown",
99
+ "metadata": {},
100
+ "source": [
101
+ "## 3. Calculate The Wrapped Model\n",
102
+ "\n",
103
+ "This cell calls the generated model through the facade. The stored output records the three wrapped FABLE outputs. The Phase 26 full-validation evidence for the underlying generated model remains: 281,741 comparable cached outputs, 281,741 matches, and 0 mismatches.\n",
104
+ "\n",
105
+ "Expected output: three `SCENARIOS selection` values."
106
+ ]
107
+ },
108
+ {
109
+ "cell_type": "code",
110
+ "execution_count": 3,
111
+ "metadata": {},
112
+ "outputs": [
113
+ {
114
+ "data": {
115
+ "text/plain": [
116
+ "{'SCENARIOS selection!D20': 2.146115426018433,\n",
117
+ " 'SCENARIOS selection!D21': 1.8982220554032356,\n",
118
+ " 'SCENARIOS selection!D22': 1.462761288724012}"
119
+ ]
120
+ },
121
+ "execution_count": 3,
122
+ "metadata": {},
123
+ "output_type": "execute_result"
124
+ }
125
+ ],
126
+ "source": [
127
+ "values = facade.calculate()\n",
128
+ "{cell_ref: values[cell_ref] for cell_ref in [\n",
129
+ " \"SCENARIOS selection!D20\",\n",
130
+ " \"SCENARIOS selection!D21\",\n",
131
+ " \"SCENARIOS selection!D22\",\n",
132
+ "]}"
133
+ ]
134
+ },
135
+ {
136
+ "cell_type": "markdown",
137
+ "metadata": {},
138
+ "source": [
139
+ "## 4. Display Wrapped Outputs As A DataFrame\n",
140
+ "\n",
141
+ "The notebook helper presents the declared outputs with human names while preserving raw cell references for provenance.\n",
142
+ "\n",
143
+ "Expected output: a three-row DataFrame for the wrapped scenario metrics."
144
+ ]
145
+ },
146
+ {
147
+ "cell_type": "code",
148
+ "execution_count": 4,
149
+ "metadata": {},
150
+ "outputs": [
151
+ {
152
+ "data": {
153
+ "text/plain": [
154
+ " name cell_ref value\n",
155
+ "0 scenario_metric_1 SCENARIOS selection!D20 2.146115\n",
156
+ "1 scenario_metric_2 SCENARIOS selection!D21 1.898222\n",
157
+ "2 scenario_metric_3 SCENARIOS selection!D22 1.462761"
158
+ ]
159
+ },
160
+ "execution_count": 4,
161
+ "metadata": {},
162
+ "output_type": "execute_result"
163
+ }
164
+ ],
165
+ "source": [
166
+ "outputs_frame(facade)[[\"name\", \"cell_ref\", \"value\"]]"
167
+ ]
168
+ },
169
+ {
170
+ "cell_type": "markdown",
171
+ "metadata": {},
172
+ "source": [
173
+ "## 5. Display A Declared FABLE Table Slice\n",
174
+ "\n",
175
+ "The wrapper declares `SCENARIOS selection!D20:D22` as a rectangular table with row labels. This turns a raw output slice into an inspectable table.\n",
176
+ "\n",
177
+ "Expected output: the three selected FABLE values as a one-column DataFrame."
178
+ ]
179
+ },
180
+ {
181
+ "cell_type": "code",
182
+ "execution_count": 5,
183
+ "metadata": {},
184
+ "outputs": [
185
+ {
186
+ "data": {
187
+ "text/plain": [
188
+ " value\n",
189
+ "row \n",
190
+ "d20 2.146115\n",
191
+ "d21 1.898222\n",
192
+ "d22 1.462761"
193
+ ]
194
+ },
195
+ "execution_count": 5,
196
+ "metadata": {},
197
+ "output_type": "execute_result"
198
+ }
199
+ ],
200
+ "source": [
201
+ "table_frame(facade, \"scenario_selection_slice\")"
202
+ ]
203
+ },
204
+ {
205
+ "cell_type": "markdown",
206
+ "metadata": {},
207
+ "source": [
208
+ "## 6. Keep Provenance Available\n",
209
+ "\n",
210
+ "DataFrame display is the humane interface, but workbook provenance is still available. Table frame metadata stores the sheet, range, and cell references used to build the view.\n",
211
+ "\n",
212
+ "Expected output: the original workbook sheet and range."
213
+ ]
214
+ },
215
+ {
216
+ "cell_type": "code",
217
+ "execution_count": 6,
218
+ "metadata": {},
219
+ "outputs": [
220
+ {
221
+ "data": {
222
+ "text/plain": [
223
+ "{'sheet': 'SCENARIOS selection',\n",
224
+ " 'range_ref': 'D20:D22',\n",
225
+ " 'cell_refs': [['SCENARIOS selection!D20'],\n",
226
+ " ['SCENARIOS selection!D21'],\n",
227
+ " ['SCENARIOS selection!D22']]}"
228
+ ]
229
+ },
230
+ "execution_count": 6,
231
+ "metadata": {},
232
+ "output_type": "execute_result"
233
+ }
234
+ ],
235
+ "source": [
236
+ "scenario_table = table_frame(facade, \"scenario_selection_slice\")\n",
237
+ "{\n",
238
+ " \"sheet\": scenario_table.attrs[\"sheet\"],\n",
239
+ " \"range_ref\": scenario_table.attrs[\"range_ref\"],\n",
240
+ " \"cell_refs\": scenario_table.attrs[\"cell_refs\"],\n",
241
+ "}"
242
+ ]
243
+ },
244
+ {
245
+ "cell_type": "markdown",
246
+ "metadata": {},
247
+ "source": [
248
+ "## 7. Bundle Outputs Into A Report\n",
249
+ "\n",
250
+ "Reports collect declared cells and tables under one human-facing name. This gives a notebook user a repeatable reporting boundary without editing the generated model.\n",
251
+ "\n",
252
+ "Expected output: the report's cell DataFrame."
253
+ ]
254
+ },
255
+ {
256
+ "cell_type": "code",
257
+ "execution_count": 7,
258
+ "metadata": {},
259
+ "outputs": [
260
+ {
261
+ "data": {
262
+ "text/plain": [
263
+ " name cell_ref value\n",
264
+ "0 scenario_metric_1 SCENARIOS selection!D20 2.146115\n",
265
+ "1 scenario_metric_2 SCENARIOS selection!D21 1.898222\n",
266
+ "2 scenario_metric_3 SCENARIOS selection!D22 1.462761"
267
+ ]
268
+ },
269
+ "execution_count": 7,
270
+ "metadata": {},
271
+ "output_type": "execute_result"
272
+ }
273
+ ],
274
+ "source": [
275
+ "frames = report_frames(facade, \"scenario_selection\")\n",
276
+ "frames[\"cells\"][[\"name\", \"cell_ref\", \"value\"]]"
277
+ ]
278
+ }
279
+ ],
280
+ "metadata": {
281
+ "kernelspec": {
282
+ "display_name": "Python 3",
283
+ "language": "python",
284
+ "name": "python3"
285
+ },
286
+ "language_info": {
287
+ "codemirror_mode": {
288
+ "name": "ipython",
289
+ "version": 3
290
+ },
291
+ "file_extension": ".py",
292
+ "mimetype": "text/x-python",
293
+ "name": "python",
294
+ "nbconvert_exporter": "python",
295
+ "pygments_lexer": "ipython3"
296
+ }
297
+ },
298
+ "nbformat": 4,
299
+ "nbformat_minor": 5
300
+ }