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.
- modelwright-0.1.0a6/MANIFEST.in +1 -0
- {modelwright-0.1.0a4/src/modelwright.egg-info → modelwright-0.1.0a6}/PKG-INFO +6 -2
- {modelwright-0.1.0a4 → modelwright-0.1.0a6}/README.md +1 -1
- modelwright-0.1.0a6/examples/README.md +35 -0
- modelwright-0.1.0a6/examples/fable_2020/README.md +21 -0
- modelwright-0.1.0a6/examples/fable_2020/generated_fable_2020_model.py.xz +0 -0
- modelwright-0.1.0a6/examples/fable_2020/notebook_interface.py +95 -0
- modelwright-0.1.0a6/examples/notebooks/README.md +11 -0
- modelwright-0.1.0a6/examples/notebooks/fable-2020-notebook-interface.ipynb +300 -0
- modelwright-0.1.0a6/examples/notebooks/synthetic-notebook-interface.ipynb +282 -0
- modelwright-0.1.0a6/examples/synthetic/notebook_interface.py +62 -0
- {modelwright-0.1.0a4 → modelwright-0.1.0a6}/pyproject.toml +6 -1
- {modelwright-0.1.0a4 → modelwright-0.1.0a6}/src/modelwright/__init__.py +17 -1
- modelwright-0.1.0a6/src/modelwright/notebooks.py +246 -0
- {modelwright-0.1.0a4 → modelwright-0.1.0a6/src/modelwright.egg-info}/PKG-INFO +6 -2
- {modelwright-0.1.0a4 → modelwright-0.1.0a6}/src/modelwright.egg-info/SOURCES.txt +12 -0
- {modelwright-0.1.0a4 → modelwright-0.1.0a6}/src/modelwright.egg-info/requires.txt +5 -0
- modelwright-0.1.0a6/tests/test_examples.py +125 -0
- {modelwright-0.1.0a4 → modelwright-0.1.0a6}/tests/test_fable_wrapper_benchmark.py +22 -0
- {modelwright-0.1.0a4 → modelwright-0.1.0a6}/tests/test_import.py +1 -1
- modelwright-0.1.0a6/tests/test_notebooks.py +211 -0
- {modelwright-0.1.0a4 → modelwright-0.1.0a6}/tests/test_public_api.py +4 -0
- {modelwright-0.1.0a4 → modelwright-0.1.0a6}/LICENSE +0 -0
- {modelwright-0.1.0a4 → modelwright-0.1.0a6}/setup.cfg +0 -0
- {modelwright-0.1.0a4 → modelwright-0.1.0a6}/src/modelwright/cli.py +0 -0
- {modelwright-0.1.0a4 → modelwright-0.1.0a6}/src/modelwright/conversion.py +0 -0
- {modelwright-0.1.0a4 → modelwright-0.1.0a6}/src/modelwright/evaluation.py +0 -0
- {modelwright-0.1.0a4 → modelwright-0.1.0a6}/src/modelwright/execution.py +0 -0
- {modelwright-0.1.0a4 → modelwright-0.1.0a6}/src/modelwright/extraction.py +0 -0
- {modelwright-0.1.0a4 → modelwright-0.1.0a6}/src/modelwright/formulas.py +0 -0
- {modelwright-0.1.0a4 → modelwright-0.1.0a6}/src/modelwright/formulas_oracle.py +0 -0
- {modelwright-0.1.0a4 → modelwright-0.1.0a6}/src/modelwright/generation.py +0 -0
- {modelwright-0.1.0a4 → modelwright-0.1.0a6}/src/modelwright/graph.py +0 -0
- {modelwright-0.1.0a4 → modelwright-0.1.0a6}/src/modelwright/oracle_validation.py +0 -0
- {modelwright-0.1.0a4 → modelwright-0.1.0a6}/src/modelwright/oracles.py +0 -0
- {modelwright-0.1.0a4 → modelwright-0.1.0a6}/src/modelwright/references.py +0 -0
- {modelwright-0.1.0a4 → modelwright-0.1.0a6}/src/modelwright/validation.py +0 -0
- {modelwright-0.1.0a4 → modelwright-0.1.0a6}/src/modelwright/wrappers.py +0 -0
- {modelwright-0.1.0a4 → modelwright-0.1.0a6}/src/modelwright.egg-info/dependency_links.txt +0 -0
- {modelwright-0.1.0a4 → modelwright-0.1.0a6}/src/modelwright.egg-info/entry_points.txt +0 -0
- {modelwright-0.1.0a4 → modelwright-0.1.0a6}/src/modelwright.egg-info/top_level.txt +0 -0
- {modelwright-0.1.0a4 → modelwright-0.1.0a6}/tests/test_cli.py +0 -0
- {modelwright-0.1.0a4 → modelwright-0.1.0a6}/tests/test_conversion_plan.py +0 -0
- {modelwright-0.1.0a4 → modelwright-0.1.0a6}/tests/test_dependency_graph.py +0 -0
- {modelwright-0.1.0a4 → modelwright-0.1.0a6}/tests/test_evaluation_orchestration.py +0 -0
- {modelwright-0.1.0a4 → modelwright-0.1.0a6}/tests/test_extraction_records.py +0 -0
- {modelwright-0.1.0a4 → modelwright-0.1.0a6}/tests/test_formula_expressions.py +0 -0
- {modelwright-0.1.0a4 → modelwright-0.1.0a6}/tests/test_formula_translation.py +0 -0
- {modelwright-0.1.0a4 → modelwright-0.1.0a6}/tests/test_formulas_oracle.py +0 -0
- {modelwright-0.1.0a4 → modelwright-0.1.0a6}/tests/test_generated_execution.py +0 -0
- {modelwright-0.1.0a4 → modelwright-0.1.0a6}/tests/test_generation_contract.py +0 -0
- {modelwright-0.1.0a4 → modelwright-0.1.0a6}/tests/test_materialize_fable_benchmarks.py +0 -0
- {modelwright-0.1.0a4 → modelwright-0.1.0a6}/tests/test_openpyxl_extraction.py +0 -0
- {modelwright-0.1.0a4 → modelwright-0.1.0a6}/tests/test_oracle_backed_validation.py +0 -0
- {modelwright-0.1.0a4 → modelwright-0.1.0a6}/tests/test_oracle_interface.py +0 -0
- {modelwright-0.1.0a4 → modelwright-0.1.0a6}/tests/test_python_generation.py +0 -0
- {modelwright-0.1.0a4 → modelwright-0.1.0a6}/tests/test_references.py +0 -0
- {modelwright-0.1.0a4 → modelwright-0.1.0a6}/tests/test_scalar_comparison.py +0 -0
- {modelwright-0.1.0a4 → modelwright-0.1.0a6}/tests/test_supported_semantics_fixture.py +0 -0
- {modelwright-0.1.0a4 → modelwright-0.1.0a6}/tests/test_synthetic_fixture.py +0 -0
- {modelwright-0.1.0a4 → modelwright-0.1.0a6}/tests/test_validation.py +0 -0
- {modelwright-0.1.0a4 → modelwright-0.1.0a6}/tests/test_validation_regression.py +0 -0
- {modelwright-0.1.0a4 → modelwright-0.1.0a6}/tests/test_validation_report_builder.py +0 -0
- {modelwright-0.1.0a4 → modelwright-0.1.0a6}/tests/test_validation_scenario.py +0 -0
- {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.
|
|
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.
|
|
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.
|
|
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
|
+
```
|
|
Binary file
|
|
@@ -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
|
+
}
|