Introduce Pydantic Models for Builtin Models#25
Introduce Pydantic Models for Builtin Models#25olivia-banks merged 8 commits intoEpiForeSITE:ob-mvpfrom
Conversation
There was a problem hiding this comment.
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
BaseSimulationModelcontract 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) andsrc/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.
|
Blocked by #17. |
There was a problem hiding this comment.
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.
| def run( | ||
| self, | ||
| params: dict[str, Any], | ||
| params: MeaslesOutbreakParams, | ||
| label_overrides: dict[str, str] | None = None, | ||
| ) -> dict[str, Any]: |
There was a problem hiding this comment.
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.
| params: TBIsolationParams, | ||
| label_overrides: dict[str, str] | None = None, | ||
| ) -> dict[str, Any]: |
There was a problem hiding this comment.
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.
| 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()) |
| results = model.run(typed_params, label_overrides=label_overrides) | ||
| render_sections(model.build_sections(results)) | ||
|
|
||
|
|
There was a problem hiding this comment.
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).
| st.set_page_config(page_title=CONFIG.app.title) |
* 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>
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.
As with #24, this is based on #17, and the files to look at are: