From 5b793bf95634a0a96bf89d94d849ea2040985526 Mon Sep 17 00:00:00 2001 From: Max Ghenis Date: Mon, 23 Feb 2026 12:55:56 -0500 Subject: [PATCH 1/4] Migrate from changelog_entry.yaml to towncrier fragments Co-Authored-By: Claude Opus 4.6 --- .github/bump_version.py | 81 ++++++++++++++++++++ Makefile | 8 +- changelog_entry.yaml => changelog.d/.gitkeep | 0 changelog.d/migrate-to-towncrier.changed.md | 1 + pyproject.toml | 36 ++++++++- 5 files changed, 119 insertions(+), 7 deletions(-) create mode 100644 .github/bump_version.py rename changelog_entry.yaml => changelog.d/.gitkeep (100%) create mode 100644 changelog.d/migrate-to-towncrier.changed.md diff --git a/.github/bump_version.py b/.github/bump_version.py new file mode 100644 index 0000000..19aa790 --- /dev/null +++ b/.github/bump_version.py @@ -0,0 +1,81 @@ +"""Infer semver bump from towncrier fragment types and update version.""" + +import re +import sys +from pathlib import Path + + +def get_current_version(pyproject_path: Path) -> str: + text = pyproject_path.read_text() + match = re.search( + r'^version\s*=\s*"(\d+\.\d+\.\d+)"', text, re.MULTILINE + ) + if not match: + print( + "Could not find version in pyproject.toml", + file=sys.stderr, + ) + sys.exit(1) + return match.group(1) + + +def infer_bump(changelog_dir: Path) -> str: + fragments = [ + f + for f in changelog_dir.iterdir() + if f.is_file() and f.name != ".gitkeep" + ] + if not fragments: + print("No changelog fragments found", file=sys.stderr) + sys.exit(1) + + categories = {f.suffix.lstrip(".") for f in fragments} + for f in fragments: + parts = f.stem.split(".") + if len(parts) >= 2: + categories.add(parts[-1]) + + if "breaking" in categories: + return "major" + if "added" in categories or "removed" in categories: + return "minor" + return "patch" + + +def bump_version(version: str, bump: str) -> str: + major, minor, patch = (int(x) for x in version.split(".")) + if bump == "major": + return f"{major + 1}.0.0" + elif bump == "minor": + return f"{major}.{minor + 1}.0" + else: + return f"{major}.{minor}.{patch + 1}" + + +def update_file(path: Path, old_version: str, new_version: str): + text = path.read_text() + updated = text.replace( + f'version = "{old_version}"', + f'version = "{new_version}"', + ) + if updated != text: + path.write_text(updated) + print(f" Updated {path}") + + +def main(): + root = Path(__file__).resolve().parent.parent + pyproject = root / "pyproject.toml" + changelog_dir = root / "changelog.d" + + current = get_current_version(pyproject) + bump = infer_bump(changelog_dir) + new = bump_version(current, bump) + + print(f"Version: {current} -> {new} ({bump})") + + update_file(pyproject, current, new) + + +if __name__ == "__main__": + main() diff --git a/Makefile b/Makefile index 2187fe9..9c0a7f7 100644 --- a/Makefile +++ b/Makefile @@ -28,12 +28,8 @@ clean: rm -rf docs/_build/ changelog: - build-changelog changelog.yaml --output changelog.yaml --update-last-date --start-from 0.1.5 --append-file changelog_entry.yaml - build-changelog changelog.yaml --org PolicyEngine --repo microimpute --output CHANGELOG.md --template .github/changelog_template.md - bump-version changelog.yaml pyproject.toml - rm changelog_entry.yaml || true - touch changelog_entry.yaml - + python .github/bump_version.py + towncrier build --yes --version $$(python -c "import re; print(re.search(r'version = \"(.+?)\"', open('pyproject.toml').read()).group(1))") # Dashboard commands dashboard-install: cd microimputation-dashboard && npm install diff --git a/changelog_entry.yaml b/changelog.d/.gitkeep similarity index 100% rename from changelog_entry.yaml rename to changelog.d/.gitkeep diff --git a/changelog.d/migrate-to-towncrier.changed.md b/changelog.d/migrate-to-towncrier.changed.md new file mode 100644 index 0000000..865484a --- /dev/null +++ b/changelog.d/migrate-to-towncrier.changed.md @@ -0,0 +1 @@ +Migrated from changelog_entry.yaml to towncrier fragments to eliminate merge conflicts. diff --git a/pyproject.toml b/pyproject.toml index adf7f32..6b163d0 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -40,7 +40,8 @@ dev = [ "isort>=5.13.0,<6.0.0", "mypy>=1.2.3,<2.0.0", "build>=1.2.0,<2.0.0", - "linecheck>=0.1.0,<0.3.0", + "linecheck>=0.1.0,<0.3.0", "towncrier>=24.8.0", + ] matching = [ @@ -78,3 +79,36 @@ line_length = 79 [tool.black] line-length = 79 target-version = ["py312", "py313"] + +[tool.towncrier] +package = "microimpute" +directory = "changelog.d" +filename = "CHANGELOG.md" +title_format = "## [{version}] - {project_date}" +issue_format = "" +underlines = ["", "", ""] + +[[tool.towncrier.type]] +directory = "breaking" +name = "Breaking changes" +showcontent = true + +[[tool.towncrier.type]] +directory = "added" +name = "Added" +showcontent = true + +[[tool.towncrier.type]] +directory = "changed" +name = "Changed" +showcontent = true + +[[tool.towncrier.type]] +directory = "fixed" +name = "Fixed" +showcontent = true + +[[tool.towncrier.type]] +directory = "removed" +name = "Removed" +showcontent = true From cf6eae46235858ab98f44f986b58e31964863859 Mon Sep 17 00:00:00 2001 From: Max Ghenis Date: Mon, 23 Feb 2026 13:01:03 -0500 Subject: [PATCH 2/4] Format bump_version.py with black Co-Authored-By: Claude Opus 4.6 --- .github/bump_version.py | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/.github/bump_version.py b/.github/bump_version.py index 19aa790..bb0fd6d 100644 --- a/.github/bump_version.py +++ b/.github/bump_version.py @@ -7,9 +7,7 @@ def get_current_version(pyproject_path: Path) -> str: text = pyproject_path.read_text() - match = re.search( - r'^version\s*=\s*"(\d+\.\d+\.\d+)"', text, re.MULTILINE - ) + match = re.search(r'^version\s*=\s*"(\d+\.\d+\.\d+)"', text, re.MULTILINE) if not match: print( "Could not find version in pyproject.toml", From d97feb5df8a6c39d23a16a39395e32879695c3fd Mon Sep 17 00:00:00 2001 From: Max Ghenis Date: Mon, 23 Feb 2026 13:06:45 -0500 Subject: [PATCH 3/4] Replace changelog workflows with towncrier fragment checks The old changelog_entry.yaml workflow checked for the deleted changelog_entry.yaml file, causing CI failures. Replace it with a towncrier fragment check in changelog.d/. Update versioning.yaml to use towncrier build instead of yaml-changelog. Co-Authored-By: Claude Opus 4.6 --- .github/workflows/changelog_entry.yaml | 28 +++++++++----------------- .github/workflows/versioning.yaml | 14 ++++++------- 2 files changed, 17 insertions(+), 25 deletions(-) diff --git a/.github/workflows/changelog_entry.yaml b/.github/workflows/changelog_entry.yaml index 911c28e..c74ac3d 100644 --- a/.github/workflows/changelog_entry.yaml +++ b/.github/workflows/changelog_entry.yaml @@ -1,29 +1,21 @@ -name: Versioning +name: Changelog on: pull_request: branches: [ main ] jobs: - check-changelog-entry: - name: Changelog entry check + check-changelog: + name: Check changelog fragment runs-on: ubuntu-latest steps: - - uses: actions/checkout@v3 - - - name: Check for changelog entry + - uses: actions/checkout@v4 + - name: Check for changelog fragment run: | - if [ ! -f "changelog_entry.yaml" ]; then - echo "Error: changelog_entry.yaml file is missing." - echo "Please add a changelog_entry.yaml file at the root of the repository." + FRAGMENTS=$(find changelog.d -type f ! -name '.gitkeep' | wc -l) + if [ "$FRAGMENTS" -eq 0 ]; then + echo "::error::No changelog fragment found in changelog.d/" + echo "Add one with: echo 'Description.' > changelog.d/\$(git branch --show-current)..md" + echo "Types: added, changed, fixed, removed, breaking" exit 1 fi - - # Check if the file is empty - if [ ! -s "changelog_entry.yaml" ]; then - echo "Error: changelog_entry.yaml file is empty." - echo "Please add content to the changelog_entry.yaml file." - exit 1 - fi - - echo "Changelog entry found and is not empty." diff --git a/.github/workflows/versioning.yaml b/.github/workflows/versioning.yaml index 1272f7f..f63ce58 100644 --- a/.github/workflows/versioning.yaml +++ b/.github/workflows/versioning.yaml @@ -7,7 +7,7 @@ on: - main paths: - - changelog_entry.yaml + - "changelog.d/**" - "!pyproject.toml" jobs: @@ -19,17 +19,17 @@ jobs: - name: Checkout repo uses: actions/checkout@v4 with: - repository: ${{ github.event.pull_request.head.repo.full_name }} - ref: ${{ github.event.pull_request.head.ref }} token: ${{ secrets.POLICYENGINE_GITHUB }} + fetch-depth: 0 - name: Setup Python uses: actions/setup-python@v5 with: python-version: 3.13 - - name: Build changelog - run: pip install yaml-changelog && make changelog - - name: Preview changelog update - run: ".github/get-changelog-diff.sh" + - name: Bump version and build changelog + run: | + pip install towncrier + python .github/bump_version.py + towncrier build --yes --version $(python -c "import re; print(re.search(r'version = \"(.+?)\"', open('pyproject.toml').read()).group(1))") - name: Update changelog uses: EndBug/add-and-commit@v9 with: From f713216b649adb87d6ff2d3f8d48e829c43f667f Mon Sep 17 00:00:00 2001 From: Max Ghenis Date: Tue, 24 Feb 2026 05:42:39 -0500 Subject: [PATCH 4/4] Delete old changelog files --- changelog.yaml | 277 ------------------------------------------------- 1 file changed, 277 deletions(-) delete mode 100644 changelog.yaml diff --git a/changelog.yaml b/changelog.yaml deleted file mode 100644 index cb66dcd..0000000 --- a/changelog.yaml +++ /dev/null @@ -1,277 +0,0 @@ -- changes: - added: - - Initialized changelog - date: 2025-06-18 10:44:16 - version: 0.1.5 -- bump: minor - changes: - added: - - Fixed typo in qrf.py. - date: 2025-06-18 13:45:52 -- bump: patch - changes: - added: - - Suppressed warnings. - - Handled edge case in categorical encoding for receiver data. - date: 2025-06-19 14:45:02 -- bump: patch - changes: - changed: - - Making autoimpute return imputations for all models. - date: 2025-06-23 14:45:18 -- bump: patch - changes: - changed: - - Default logging level. - - Autoimpute's output format for imputations - date: 2025-07-07 09:46:21 -- bump: patch - changes: - changed: - - Publishing job to run after versioning in Workflow. - date: 2025-07-24 11:46:11 -- bump: patch - changes: - changed: - - Made kaleido an optional dependency (install with `pip install microimpute[images]`). - - Image export functionality now gracefully handles missing kaleido with informative - warnings. - date: 2025-07-24 12:42:09 -- bump: major - changes: - added: - - Support for Python 3.13. - - Optional `images` extra for kaleido dependency (`pip install microimpute[images]`). - changed: - - Require Python 3.13 (dropped support for Python 3.11 and 3.12). - - Updated CI/CD workflows to test only against Python 3.13. - - Updated Black formatter to target Python 3.13 only. - - Simplified documentation dependencies to let jupyter-book manage its own deps. - - Added furo theme as explicit dependency for documentation builds. - - Updated NumPy from 1.26.x to 2.x (major version upgrade). - - Updated SciPy from 1.14.x to 1.16.x. - - Updated joblib from 1.4.x to 1.5.x. - - Updated flake8 from 6.x to 7.x. - - Updated Black to require version 24.0.0 or newer. - - Updated isort to require version 5.13.0 or newer. - - Allowed statsmodels 0.15.x when released. - - Allowed optuna 4.x versions. - - Made kaleido an optional dependency (moved to `images` extra). - removed: - - kaleido is no longer a required dependency (now optional). - date: 2025-07-24 13:57:14 -- bump: patch - changes: - fixed: - - PyPI deployment workflow now properly defines Python version matrix. - date: 2025-07-26 16:22:56 -- bump: patch - changes: - added: - - Made qrf impute sequentially when multiple imputed_variables are passed. - date: 2025-07-30 23:45:19 -- bump: minor - changes: - added: - - Documentation explaining sequential imputation behavior in QRF - changed: - - Upgraded to JupyterBook 2.0 (beta) for improved documentation builds - date: 2025-07-31 10:14:08 -- bump: patch - changes: - added: - - Extended test coverage for QRF model including edge cases and internal class - testing - changed: - - Removed utils.QRF wrapper to use RandomForestQuantileRegressor directly for - consistency with OLS/QuantReg patterns - - Removed duplicate categorical handling from QRF model as base Imputer class - already handles this - date: 2025-07-31 11:22:52 -- bump: patch - changes: - added: - - Added memory usage logging to QRF. - - Enabled imputation even if there are imputed variables missing when skip_missing - is True. - date: 2025-07-31 23:56:29 -- bump: patch - changes: - added: - - Updated categorical dummy encoding to restore original columns when dummies - are not generated (edge case for when there is a single category). - date: 2025-08-01 14:31:06 -- bump: patch - changes: - changed: - - Add condition to not convert numeric columns to categorical if they have less - than 10 unique values that are not evenly spaced. - date: 2025-08-01 16:36:35 -- bump: patch - changes: - added: - - Moved data loading utilities into utils and removed scf downloading functionality. - - Updated documentation to reflect the new structure and created a myst.yml file - to deploy documentation with new jb v2. - date: 2025-08-05 13:58:25 -- bump: patch - changes: - added: - - Make models return a dataframe directly when no quantiles are specified. - date: 2025-08-08 08:17:09 -- bump: minor - changes: - added: - - Support for Python 3.12 alongside Python 3.13 - - Python 3.12 to CI/CD test matrix for comprehensive testing - - Graceful handling of optional Matching module when R dependencies are unavailable - changed: - - Python version requirement from ">=3.13,<3.14" to ">=3.12,<3.14" - - Black formatter target versions to include both py312 and py313 - - GitHub Actions workflows to test against both Python 3.12 and 3.13 - - Python 3.12 CI tests to run minimal smoke test only (QRF basic functionality) - fixed: - - Issue where predict() returns DataFrame instead of Dict for single quantile - in autoimpute - - Import errors when Matching module is not available due to missing R dependencies - - Unconditional import of rpy2-dependent modules in utils package causing test - failures - date: 2025-09-07 08:07:54 -- bump: patch - changes: - added: - - Improved test coverage. - changed: - - Refactor the Imputer base class. - - Refactor cross-validation, comparison, visualization, and autoimputation functions. - date: 2025-09-14 03:57:42 -- bump: patch - changes: - added: - - RandomForestClassifier and LogisticRegression models for categorical variable - imputation. - date: 2025-09-18 12:24:10 -- bump: patch - changes: - added: - - Log loss metric for evaluating categorical variable imputation. - - Functionality for cross-validation and autoimpute to integrate log loss. - - Visualization utilities for categorical imputation performance. - changed: - - Updated documentation to reflect new methods and log loss features. - date: 2025-09-25 12:33:38 -- bump: minor - changes: - fixed: - - QRF hyperparameter tuning now correctly tunes QRF and RFC separately. - - QRF imputation now correctly handles categorical variables that become predictors - after imputation. - date: 2025-10-02 12:11:14 -- bump: minor - changes: - added: - - Wasserstein distance and Total Variation Distance metrics for distributional - similarity. - fixed: - - Bug in data preprocessing which attempted to normalize categorical variables. - - Bug in loss metric used in Matching hyperparameter tuning. - date: 2025-10-04 01:32:02 -- bump: patch - changes: - fixed: - - Fixed pyproject.toml to avoid dependency versions updating with package. - date: 2025-10-04 02:27:16 -- bump: minor - changes: - added: - - Predictor correlation and sensitivity analysis tools. - - Updated documentation with distributional similarity metrics and predictor analysis. - date: 2025-10-10 08:09:07 -- bump: patch - changes: - fixed: - - Fixed pyproject.toml to avoid dependency versions updating with package again. - date: 2025-10-10 08:33:23 -- bump: patch - changes: - fixed: - - Fixed QRF to encode categorical imputed variables that become predictors correctly. - - Added `not_numeric_categorical` parameter to control whether discrete numeric - variables are treated as categorical. - - Replaced Total Variation Distance with KL-divergence. - date: 2025-10-19 10:08:29 -- bump: minor - changes: - added: - - Created microimputation-dashboard directory with initial dashboard components. - - File upload component to load microimputation results. - - Designed structure required for micorimputation_results.csv to load onto dashboard. - - Created `format_csv` function to format results for dashboard compatibility. - date: 2025-10-20 08:34:22 -- bump: patch - changes: - fixed: - - Add suspense to fix vercel deployment issue. - date: 2025-10-21 03:20:28 -- bump: minor - changes: - added: - - Links to dashboard in README.md and documentation. - - First dashboard visualizations. - date: 2025-10-24 16:06:59 -- bump: minor - changes: - added: - - Distribution comparison histogram of donor and receiver datasets for imputed - variables (to dashboard). - - Log transformation option for numerical variables in data preprocessing. - date: 2025-11-06 04:39:56 -- bump: patch - changes: - fixed: - - Fixed pyproject.toml to avoid dependency versions updating with package again, - again. - date: 2025-11-06 04:49:24 -- bump: minor - changes: - added: - - Mixture Density Network (MDN) model for numeric variable imputation and Neural - Classifier model for categorical variable imputation. - date: 2025-12-03 10:54:44 -- bump: minor - changes: - added: - - Asinh transformation preprocessing option for numeric variables. - - Documentation for MDN model and preprocessing options. - date: 2025-12-04 11:22:49 -- bump: minor - changes: - added: - - Updates to documentation and Myst deployment. - date: 2025-12-05 09:20:27 -- bump: minor - changes: - added: - - Benchmarking experiments for wealth imputation paper draft. - - MDN model to experiments run in imputing-from-scf-to-cps.ipynb. - - Privacy & Terms to microimputation-dashboard. - date: 2025-12-11 14:03:49 -- bump: minor - changes: - updated: - - Introduction, Background and Data sections with more detailed literature review. - date: 2026-01-20 05:25:02 -- bump: minor - changes: - added: - - Error bars and grid lines to visualizations. - - Notebook benchmarking models on additional datasets. - date: 2026-02-07 04:51:52 -- bump: patch - changes: - changed: - - imputing-from-scf-to-cps.ipynb to test SSI policy reforms with different wealth - imputations. - - updated paper/ and main.pdf to capture the new results and discussion from the - notebook as well as general improvements in content and presentation. - date: 2026-02-22 13:05:13