Skip to content

Introduce Pydantic Models for Builtin Models#25

Merged
olivia-banks merged 8 commits intoEpiForeSITE:ob-mvpfrom
olivia-banks:ob-spec-model
Apr 3, 2026
Merged

Introduce Pydantic Models for Builtin Models#25
olivia-banks merged 8 commits intoEpiForeSITE:ob-mvpfrom
olivia-banks:ob-spec-model

Conversation

@olivia-banks
Copy link
Copy Markdown
Member

Introduce Pydantic models for builtin models, and hook this up to the UI. Fixed some reactivity issues, and enabled better diagnostics when a YAML file doesn't look right.

For example, here's what happens when we try to upload Measles parameters to the Tuberculosis model.

image image

As with #24, this is based on #17, and the files to look at are:

modified:   src/epicc/__main__.py
modified:   src/epicc/model/base.py
modified:   src/epicc/models/measles_outbreak.py
modified:   src/epicc/models/tb_isolation.py
modified:   src/epicc/utils/parameter_loader.py

@olivia-banks olivia-banks added this to the MVP milestone Mar 31, 2026
@olivia-banks olivia-banks self-assigned this Mar 31, 2026
Copilot AI review requested due to automatic review settings March 31, 2026 17:46
@olivia-banks olivia-banks added the enhancement New feature or request label Mar 31, 2026
Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR migrates the app from ad-hoc Python/YAML handling to a structured, typed approach: built-in simulation models now declare Pydantic parameter schemas, parameter files are loaded via a unified format layer (YAML/XLSX), and the Streamlit UI is rewired to validate and surface better diagnostics when uploads don’t match a model’s schema.

Changes:

  • Introduces a BaseSimulationModel contract plus Pydantic parameter models for the built-in TB Isolation and Measles Outbreak simulations (including default YAML parameter files).
  • Adds a generic “formats” layer (BaseFormat, YAMLFormat, XLSXFormat) with template generation utilities and new unit tests.
  • Replaces the legacy top-level app.py + utils/ loaders with a packaged Streamlit entrypoint (src/epicc/__main__.py) and src/epicc/utils/* helpers.

Reviewed changes

Copilot reviewed 35 out of 43 changed files in this pull request and generated 10 comments.

Show a summary per file
File Description
utils/section_renderer.py Removed legacy non-packaged section renderer (moved under src/epicc/utils).
utils/parameter_ui.py Removed legacy non-packaged sidebar parameter UI (moved under src/epicc/utils).
utils/parameter_loader.py Removed legacy YAML/XLSX parameter loader (replaced by epicc.formats + typed loading).
utils/model_loader.py Removed legacy dynamic module discovery/loader (replaced by explicit built-in registry).
src/epicc/web/sidebar.css Adds sidebar styling used by the packaged Streamlit app.
src/epicc/utils/section_renderer.py New packaged section rendering with safer defaults (get() for missing keys).
src/epicc/utils/parameter_ui.py Packaged sidebar parameter widgets + reset logic.
src/epicc/utils/parameter_loader.py Loads/validates uploaded params via format layer + Pydantic, then flattens for UI.
src/epicc/utils/model_loader.py Defines built-in model registry returned to the UI.
src/epicc/utils/excel_model_runner.py Refactors formatting and reuses shared flatten_dict.
src/epicc/models/tb_isolation.yaml Adds default TB Isolation parameter YAML.
src/epicc/models/tb_isolation.py Adds TB Isolation built-in model + Pydantic parameter schema + YAML defaults loader.
src/epicc/models/measles_outbreak.yaml Adds default Measles Outbreak parameter YAML.
src/epicc/models/measles_outbreak.py Adds Measles Outbreak built-in model + Pydantic parameter schema + YAML defaults loader.
src/epicc/model/schema.py Adds a typed schema for declarative YAML-defined models (Model, Parameter, Equation, etc.).
src/epicc/model/base.py Introduces BaseSimulationModel abstract interface for Python models.
src/epicc/model/init.py Adds load_model() for YAML-defined models using the formats layer.
src/epicc/formats/base.py Adds BaseFormat abstraction for YAML/XLSX IO + template writing.
src/epicc/formats/yaml.py Adds YAML format reader/writer.
src/epicc/formats/xlsx.py Adds XLSX format reader/writer + template writer.
src/epicc/formats/template.py Adds Pydantic-based template generation utilities.
src/epicc/formats/init.py Adds format registry, validation helpers, and read_from_format().
src/epicc/config/schema.py Adds typed config schema (Config, DefaultsConfig, etc.).
src/epicc/config/default.yaml Adds packaged default configuration YAML.
src/epicc/config/init.py Adds packaged config loader and exports CONFIG.
src/epicc/main.py New packaged Streamlit entrypoint wired to built-in models + typed validation errors.
tests/epicc/test_model_loader.py Adds tests for epicc.model.load_model().
tests/epicc/test_formats.py Adds tests for format selection and typed read helper.
tests/epicc/test_formats_yaml.py Adds YAML format read/write tests.
tests/epicc/test_formats_xlsx.py Adds XLSX format read/write tests.
tests/epicc/test_formats_template.py Adds integration tests for template generation.
sample/TB Isolation.xlsx Adds sample TB Isolation workbook.
sample/Measles Outbreak.xlsx Adds sample Measles Outbreak workbook.
sample/~$TB Isolation.xlsx Adds an Excel lock/temp file (should not be committed).
pyproject.toml Adds Pydantic + ruamel.yaml deps, pytest dev dep, and pytest pythonpath config.
models/tb_isolation.py Removes legacy TB Isolation model module (superseded by packaged model).
models/measles_outbreak.py Removes legacy Measles model module (superseded by packaged model).
config/paths.yaml Removes legacy app configuration file (superseded by packaged config).
config/global_defaults.yaml Removes legacy defaults file (superseded by packaged config).
config/app.yaml Removes legacy app config (superseded by packaged config).
app.py Removes legacy top-level Streamlit app entrypoint (replaced by src/epicc/__main__.py).
.gitignore Expands ignored editor/build artifacts.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

@olivia-banks
Copy link
Copy Markdown
Member Author

Blocked by #17.

@gvegayon gvegayon requested a review from EddW1219 April 2, 2026 00:27
@olivia-banks olivia-banks requested a review from Copilot April 3, 2026 22:08
@olivia-banks olivia-banks changed the base branch from main to ob-mvp April 3, 2026 22:09
@olivia-banks olivia-banks merged commit 9c5665c into EpiForeSITE:ob-mvp Apr 3, 2026
3 checks passed
Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 8 out of 11 changed files in this pull request and generated 3 comments.


💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment on lines 71 to 75
def run(
self,
params: dict[str, Any],
params: MeaslesOutbreakParams,
label_overrides: dict[str, str] | None = None,
) -> dict[str, Any]:
Copy link

Copilot AI Apr 3, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

MeaslesOutbreakModel.run() narrows the params argument type to MeaslesOutbreakParams, but BaseSimulationModel.run() now declares params: BaseModel. This override is not type-compatible (LSP) and will likely fail strict type-checking. Consider making BaseSimulationModel generic over a ParamsT: BaseModel (so run() and parameter_model() share the same type), or keep the override signature accepting BaseModel and cast/validate internally.

Copilot uses AI. Check for mistakes.
Comment on lines +107 to 109
params: TBIsolationParams,
label_overrides: dict[str, str] | None = None,
) -> dict[str, Any]:
Copy link

Copilot AI Apr 3, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

TBIsolationModel.run() narrows the params argument type to TBIsolationParams, but BaseSimulationModel.run() declares params: BaseModel. This override is not type-compatible and can break the repository's strict type-checking. Consider making BaseSimulationModel generic over a ParamsT: BaseModel (and returning type[ParamsT] from parameter_model()), or keep run() accepting BaseModel and downcast after validation.

Suggested change
params: TBIsolationParams,
label_overrides: dict[str, str] | None = None,
) -> dict[str, Any]:
params: BaseModel,
label_overrides: dict[str, str] | None = None,
) -> dict[str, Any]:
if isinstance(params, TBIsolationParams):
params = params
else:
params = TBIsolationParams.model_validate(params.model_dump())

Copilot uses AI. Check for mistakes.
results = model.run(typed_params, label_overrides=label_overrides)
render_sections(model.build_sections(results))


Copy link

Copilot AI Apr 3, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

st.set_page_config(...) was removed, and there are no other calls in the codebase. This changes the app's title/layout behavior (e.g., losing layout="wide" and the configured page title) and can’t be added later once Streamlit has emitted elements. If this removal wasn’t intentional, reintroduce st.set_page_config at the top of __main__.py (before _load_styles() or any other st.* calls).

Suggested change
st.set_page_config(page_title=CONFIG.app.title)

Copilot uses AI. Check for mistakes.
gvegayon added a commit that referenced this pull request Apr 10, 2026
* Merge pull request #25 from olivia-banks/ob-spec-model

Introduce Pydantic Models for Builtin Models

* Add Report Generation to PDF (#24)

* [stash] draft

* [stash] draft

* [test] test!

* [format] templating

* [dep] dep

* [report] add report generation button

* Various Fixes (#32)

* [model] make `BaseSimulationModel` generic over `ParamsT`

* [tb-iso] remove unused `hourly_wage_public_health_worker` parameter

* [app] cache uploaded parameter file sha1 in session

* More Various Fixes (#33)

* [model] make `BaseSimulationModel` generic over `ParamsT`

* [tb-iso] remove unused `hourly_wage_public_health_worker` parameter

* [app] cache uploaded parameter file sha1 in session

* [style] hide `.export-hint` in print media

* [branding] persist title

* Even More Fixes! (#34)

* [model] make `BaseSimulationModel` generic over `ParamsT`

* [tb-iso] remove unused `hourly_wage_public_health_worker` parameter

* [app] cache uploaded parameter file sha1 in session

* [style] hide `.export-hint` in print media

* [branding] persist title

* [branding] move to `epicc`

* [gha] overhaul the container image situation

* Fix uv.lock: update project name from epiworld-python-streamlit to epicc

Agent-Logs-Url: https://github.com/EpiForeSITE/epiworldPythonStreamlit/sessions/c0476fd8-31f6-4c9a-bf7c-a585696fa679

Co-authored-by: gvegayon <893619+gvegayon@users.noreply.github.com>

---------

Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com>
Co-authored-by: gvegayon <893619+gvegayon@users.noreply.github.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

enhancement New feature or request

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants