Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions CHANGE_LOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,10 @@ This file records completed project work in chronological order.

## 2026-06-22

- Activated Phase 30 on `feature/p30-notebook-dataframe-interface`, created GitHub parent issue #174 and child issues #175, #178, #176, #177, #179, and #180, and scoped the next `0.1.0a5` alpha around an optional pandas-backed notebook/DataFrame layer over `modelwright.wrappers` for live-kernel inspection, scenario mutation, table rendering, and baseline-vs-scenario comparison workflows.
- Completed P30.1 through P30.5 by adding `modelwright.notebooks` with lazy pandas-backed DataFrame helpers for declared inputs, outputs, scenarios, tables, reports, and baseline-vs-scenario comparisons; added a `notebook` optional extra with `pandas>=2` while keeping pandas out of core dependencies; documented the notebook workflow in Sphinx; added always-on synthetic notebook tests plus real generated synthetic model coverage; and extended the opt-in FABLE wrapper benchmark so `MODELWRIGHT_RUN_FABLE_BENCHMARKS=1 .venv/bin/python -m pytest -vv tests/test_fable_wrapper_benchmark.py` passed in 149.67 seconds against ignored local 2020 FABLE artifacts while preserving the existing `281,741` matches and `0` mismatches evidence boundary.
- Added P30.6 release examples scope with a tracked `examples/` directory, a tiny synthetic notebook-interface example, a production-size 2020 FABLE notebook-interface example backed by compressed generated Python output rather than the original workbook, Sphinx Examples Gallery pages linked from the docs index, and lightweight tests that verify example integrity without running the expensive FABLE generated-model calculation in default pytest.
- Prepared the `0.1.0a5` release candidate by bumping package/import metadata and release docs, passing repo-local bootstrap, Ruff, default `pytest -vv` with `163` passed and `1` benchmark skip, Sphinx warning-as-error docs, Read the Docs theme verification, and `scripts/check_release_artifacts.sh`; the artifact checker built a roughly `56K` wheel and `2.2M` sdist, included the compressed FABLE generated-model example in the sdist, installed the wheel into a clean ignored environment, imported `modelwright 0.1.0a5`, and smoke-tested the CLI.
- Activated Phase 29 on `feature/p29-model-wrapper-templates`, created GitHub parent issue #167 and child issues #171, #170, #169, #168, and #172, and scoped initial wrapper-template facades for generated Python models with `0.1.0a4` publication as the final phase closeout task.
- Closed P29.1 by expanding `planning/model-wrapper-template-facades.md` into a concrete initial wrapper API contract covering generated-model boundaries, facade records, declaration helpers, table semantics, scenario mutation, report payloads, provenance preservation, error handling, and alpha API limits.
- Completed P29.2 by adding the initial `modelwright.wrappers` module with `ModelFacade`, `Scenario`, cell/table/report declarations, table and report view payloads, wrapper declaration/execution errors, public package exports, and focused tests for scenario overrides, rectangular table reporting, duplicate declarations, report references, and generated-model error wrapping.
Expand Down
1 change: 1 addition & 0 deletions MANIFEST.in
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
recursive-include examples *.md *.py *.xz
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -84,7 +84,7 @@ Restore the public external FABLE benchmark workbooks into ignored local paths:
scripts/bootstrap_dev_env.sh --benchmarks
```

`modelwright` is pre-release. The current alpha line is `0.1.0a4`; alpha releases must not be described as full-workbook conversion guarantees.
`modelwright` is pre-release. The current alpha line is `0.1.0a5`; alpha releases must not be described as full-workbook conversion guarantees.

Check release artifacts locally:

Expand Down
124 changes: 120 additions & 4 deletions ROADMAP.md
Original file line number Diff line number Diff line change
Expand Up @@ -721,9 +721,9 @@ Release claim boundary:

GitHub parent issue: #167

Active branch: `feature/p29-model-wrapper-templates`
Completed branch: `feature/p29-model-wrapper-templates`

Status: active.
Status: complete.

Goal: explore and implement an initial `modelwright` module that helps users build custom wrapper
facades around generated Python models, bridging the gap between raw generated cell-address APIs and
Expand Down Expand Up @@ -794,5 +794,121 @@ Release evidence:

## Current Next Steps

1. Keep Phase 29 closed unless a release defect is discovered.
2. Plan the next roadmap phase before opening a new feature branch.
## Phase 30: Notebook Interface And DataFrame Display Layer

GitHub parent issue: #174

Active branch: `feature/p30-notebook-dataframe-interface`

Status: active.

Goal: add an optional notebook-facing layer on top of `modelwright.wrappers` that exposes wrapped
generated models as Jupyter-friendly, pandas-backed analyst workflows. This phase should support a
live-kernel loop where analysts can inspect declared inputs, outputs, and tables; mutate scenarios;
recalculate; render declared tables as DataFrames; and compare baseline-vs-scenario results without
using raw generated source or `Sheet!A1` dictionaries as the common path.

Planning note: `planning/notebook-dataframe-interface.md`.

Release target: `modelwright==0.1.0a5`.

- [x] P30.1 Define notebook adapter contract. Child issue: #175.
- Status: complete.
- [x] Decide the module boundary for notebook-facing helpers.
- [x] Define the relationship to `ModelFacade` without making pandas a core wrapper dependency.
- [x] Decide the optional dependency policy for `pandas`.
- [x] Define helper API sketches for inputs, outputs, tables, reports, scenarios, and comparisons.
- [x] Define non-goals: no full spreadsheet UI, no dashboard server, no automatic workbook semantic
naming, no widget framework unless explicitly scoped, and no stable public API guarantee yet.
- [x] P30.2 Implement DataFrame view helpers. Child issue: #178.
- Status: complete.
- [x] Add the selected notebook/DataFrame module.
- [x] Add a lazy pandas import and clear missing-dependency error.
- [x] Convert declared inputs and outputs to tidy DataFrames.
- [x] Convert declared table views to DataFrames preserving row labels, column labels, values,
cell refs, and provenance where practical.
- [x] Convert report bundles to DataFrame payloads without changing `ModelFacade` calculation behavior.
- [x] Add focused synthetic tests.
- [x] P30.3 Implement scenario comparison helpers. Child issue: #176.
- Status: complete.
- [x] Define scenario comparison inputs and output columns.
- [x] Include declared name, label, cell ref, baseline value, scenario value, absolute change,
percent change where numeric, unit, role, and provenance/drilldown metadata where practical.
- [x] Preserve generated-model execution errors as wrapper/notebook-layer errors.
- [x] Add synthetic tests for numeric, text, missing, and zero-baseline comparison behavior.
- [x] Keep raw cell refs available without making them the primary notebook-facing interaction.
- [x] P30.4 Add notebook-oriented docs. Child issue: #177.
- Status: complete.
- [x] Add `docs/guides/notebook-interface.rst`.
- [x] Show loading a generated model and wrapping it with `ModelFacade`.
- [x] Show declared inputs, outputs, tables, and reports.
- [x] Show DataFrame rendering helpers.
- [x] Show scenario mutation and scenario comparison.
- [x] Document alpha limits and non-goals clearly.
- [x] Add the guide to the docs index.
- [x] P30.5 Validate synthetic and FABLE notebook workflows. Child issue: #179.
- Status: complete.
- [x] Add always-on synthetic tests for DataFrame conversion and scenario comparison.
- [x] Confirm the notebook layer does not change generated-model calculation behavior.
- [x] Add or extend an opt-in FABLE benchmark-gated test with `MODELWRIGHT_RUN_FABLE_BENCHMARKS=1`
if local artifacts support it.
- [x] Keep generated FABLE models and raw reports under ignored `tmp/`.
- [x] Run full local verification and record evidence in roadmap, changelog, and issue comments.
- [x] P30.6 Add examples gallery. Child issue: #181.
- Status: complete.
- [x] Add an `examples/` directory with a tiny synthetic notebook-interface example.
- [x] Add a production-size 2020 FABLE generated-model example without tracking the original workbook.
- [x] Keep the original FABLE workbook and raw local validation reports out of tracked files.
- [x] Avoid ordinary Git blobs larger than GitHub/PyPI practical limits.
- [x] Add Sphinx Examples Gallery pages linked from the docs index.
- [x] Add lightweight tests for example integrity without running expensive production-size FABLE
calculation in default pytest.
- [ ] P30.7 Publish `modelwright==0.1.0a5`. Child issue: #180.
- Status: active.
- [x] Confirm P30 notebook/DataFrame scope and evidence are complete.
- [x] Bump package/import version and release docs to `0.1.0a5`.
- [x] Run local release checks, including Ruff, pytest, Sphinx docs, docs theme verification, and
release artifact checks.
- [ ] Open and merge the P30 PR to `main`.
- [ ] Create annotated tag `v0.1.0a5`.
- [ ] Publish through the gated release workflow after maintainer approval.
- [ ] Verify PyPI JSON, clean PyPI install, import version, CLI help, GitHub release, and docs deployment.

Acceptance boundary:

- May claim initial Jupyter/DataFrame-facing helpers for wrapped generated models.
- May claim optional pandas-backed display helpers.
- May claim scenario mutation and comparison as notebook-native workflows.
- May claim synthetic and opt-in FABLE evidence that notebook helpers do not change calculation behavior.
- Must not claim a full spreadsheet UI, dashboard application, widget framework, automatic workbook
semantic recovery, stable public API compatibility, or compact runtime IR production readiness.

Implementation evidence:

- Added `modelwright.notebooks` with lazy pandas-backed helpers:
`inputs_frame`, `outputs_frame`, `scenario_frame`, `table_frame`, `report_frames`, and
`compare_scenarios_frame`.
- Added the `notebook` optional extra with `pandas>=2`, while keeping pandas out of core dependencies.
- Added always-on synthetic notebook tests, including real generated synthetic model coverage and
missing-pandas dependency behavior.
- Extended the opt-in FABLE wrapper benchmark to validate notebook DataFrame helpers over the ignored
generated 2020 FABLE model.
- Added `examples/` with a tracked synthetic notebook-interface example and a production-size FABLE
notebook-interface example. The FABLE generated Python output is tracked as a compressed
`generated_fable_2020_model.py.xz` artifact because the uncompressed module is larger than ordinary
GitHub per-file limits.

Verification evidence:

- `scripts/bootstrap_dev_env.sh` passed and installed the updated `dev` extra.
- `.venv/bin/python -m ruff check .` passed.
- `.venv/bin/python -m pytest -vv` passed with `161` passed and `1` skipped benchmark.
- `.venv/bin/sphinx-build -b html docs _build/html -W` passed.
- `.venv/bin/python scripts/verify_docs_theme.py _build/html` passed.
- `MODELWRIGHT_RUN_FABLE_BENCHMARKS=1 .venv/bin/python -m pytest -vv tests/test_fable_wrapper_benchmark.py`
passed in `149.67` seconds, using ignored local FABLE artifacts under `tmp/p26-fable-full-validation/`.
- `scripts/check_release_artifacts.sh` passed for `0.1.0a5`; the clean wheel install imported
`modelwright 0.1.0a5` and the artifact inspection found no forbidden private workbook, ignored
`tmp/`, or source workbook content.
- Release artifacts from the local check were about `56K` for the wheel and `2.2M` for the sdist. The
sdist includes the compressed FABLE generated-model example; the wheel remains package-code only.
11 changes: 11 additions & 0 deletions docs/examples.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
Examples Gallery
================

These examples show the path from generated Python models to analyst-facing wrapper facades and
notebook-friendly DataFrame displays.

.. toctree::
:maxdepth: 1

examples/synthetic-notebook-interface
examples/fable-2020-notebook-interface
23 changes: 23 additions & 0 deletions docs/examples/fable-2020-notebook-interface.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
2020 FABLE Notebook Interface
=============================

This production-size example uses Modelwright's generated Python output from the public 2020 FABLE
Calculator benchmark workbook. The original workbook is not tracked in this repository. The generated
Python model is tracked as ``examples/fable_2020/generated_fable_2020_model.py.xz`` and decompressed
into ignored ``tmp/`` working space before import.

The example wraps three validated ``SCENARIOS selection`` outputs, renders them as DataFrames, and
keeps the validation boundary explicit: the source Phase 26 full-validation report recorded 281,741
comparable cached outputs, 281,741 matches, and 0 mismatches.

Run it from the repository root:

.. code-block:: bash

python examples/fable_2020/notebook_interface.py

Source
------

.. literalinclude:: ../../examples/fable_2020/notebook_interface.py
:language: python
18 changes: 18 additions & 0 deletions docs/examples/synthetic-notebook-interface.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
Synthetic Notebook Interface
============================

This tiny example uses a small generated-model-shaped ``calculate(inputs=None)`` function, wraps it
with ``ModelFacade``, and renders outputs, a declared table, and a baseline-vs-scenario comparison as
DataFrames.

Run it from the repository root:

.. code-block:: bash

python examples/synthetic/notebook_interface.py

Source
------

.. literalinclude:: ../../examples/synthetic/notebook_interface.py
:language: python
159 changes: 159 additions & 0 deletions docs/guides/notebook-interface.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,159 @@
Notebook Interface
==================

The wrapper facade API gives generated models analyst-facing names, tables, reports, and scenarios.
The ``modelwright.notebooks`` module adds the next layer: pandas-backed helpers that display those
facade views naturally in a live Jupyter kernel.

Install the optional notebook dependency when using these helpers:

.. code-block:: bash

python -m pip install 'modelwright[notebook]'

The core package and ``modelwright.wrappers`` do not require pandas.

Boundary
--------

Notebook helpers do not edit generated source code and do not recreate a spreadsheet UI. They convert
declared wrapper views into DataFrames so an analyst can inspect model structure, change scenario
inputs, recalculate, and compare results without manually reading raw ``Sheet!A1`` dictionaries.

Minimal Example
---------------

Assume a generated model module exposes ``calculate(inputs=None)``:

.. code-block:: python

def calculate(inputs=None):
inputs = inputs or {}
base = inputs.get("Inputs!B2", 100)
growth = inputs.get("Inputs!B3", 0.1)
return {
"Summary!B2": base * (1 + growth),
"Summary!C2": base * 2,
"Summary!B3": "ok",
"Summary!C3": base + 5,
}

Declare a facade around the generated model:

.. code-block:: python

from modelwright.wrappers import ModelFacade, cell, report, table

facade = ModelFacade(
generated_model,
cells=[
cell("Inputs!B2", name="base", label="Base volume", role="input", unit="t"),
cell("Inputs!B3", name="growth", label="Growth rate", role="input", unit="fraction"),
cell("Summary!B2", name="projected", label="Projected volume", role="output", unit="t"),
cell("Summary!B3", name="status", label="Status", role="output"),
],
tables=[
table(
"summary_grid",
sheet="Summary",
range_ref="B2:C3",
row_labels=["volume", "status"],
column_labels=["primary", "secondary"],
)
],
reports=[
report("summary", cells=["base", "projected", "status"], tables=["summary_grid"]),
],
)

Render declared inputs and outputs:

.. code-block:: python

from modelwright.notebooks import inputs_frame, outputs_frame

scenario = facade.scenario(name="shock", inputs={"Inputs!B2": 50}).with_input("Inputs!B3", 0.2)

inputs_frame(facade, scenario)
outputs_frame(facade, scenario)

In Jupyter, those calls display tidy DataFrames with names, labels, cell references, roles, units,
values, and value-presence flags.

Render Tables
-------------

Declared rectangular tables become DataFrames whose visible row and column labels come from the
wrapper declaration:

.. code-block:: python

from modelwright.notebooks import table_frame

summary = table_frame(facade, "summary_grid", scenario)
summary

Workbook provenance stays attached to the DataFrame:

.. code-block:: python

summary.attrs["sheet"]
summary.attrs["range_ref"]
summary.attrs["cell_refs"]

Render Reports
--------------

Reports return a small mapping with a cell DataFrame and named table DataFrames:

.. code-block:: python

from modelwright.notebooks import report_frames

frames = report_frames(facade, "summary", scenario)
frames["cells"]
frames["tables"]["summary_grid"]

Compare Scenarios
-----------------

Scenario comparison is the core notebook loop:

.. code-block:: python

from modelwright.notebooks import compare_scenarios_frame

baseline = facade.scenario(name="baseline", inputs={"Inputs!B2": 100, "Inputs!B3": 0.1})
shock = baseline.with_input("Inputs!B2", 120)

compare_scenarios_frame(facade, baseline, shock)

The comparison DataFrame includes declared name, label, cell reference, baseline value, scenario
value, absolute change, percent change where numeric and meaningful, unit, role, and description.
Text comparisons and zero-baseline percent changes remain explicit rather than raising.

Scenario Inputs
---------------

Use ``scenario_frame`` to inspect the exact overrides being sent to the generated model:

.. code-block:: python

from modelwright.notebooks import scenario_frame

scenario_frame(shock)

Alpha Limits
------------

The notebook helpers are provisional. They are intended to make wrapped generated models usable in a
live Python kernel, not to promise a complete end-user application.

Current non-goals:

- full spreadsheet UI recreation;
- dashboard server or widget framework;
- automatic recovery of workbook semantic names or table meanings;
- visual formatting or workbook editing;
- Excel-backed recalculation equivalence;
- stable public API compatibility before real notebook workflows harden the design.
Loading
Loading