Skip to content

chore: use uv and ruff for analytics reporting (#4934) - #4936

Open
hunterckx wants to merge 12 commits into
mainfrom
hunter/4934-modernize-analytics-package
Open

chore: use uv and ruff for analytics reporting (#4934)#4936
hunterckx wants to merge 12 commits into
mainfrom
hunter/4934-modernize-analytics-package

Conversation

@hunterckx

Copy link
Copy Markdown
Contributor

Closes #4934

  • Switched to uv for package/dependency management
  • Added pyproject.toml files for the package and for the broader analytics folder
  • Added Ruff formatting/linting with npm scripts for running it
    • Most of the changes in this PR are from formatting being applied
  • Added Python checks to run-checks workflow (Ruff, lockfile consistency, importing the package entry point)

Replace the bare setuptools setup.py with a pyproject.toml (setuptools
backend, so the downstream git+#subdirectory install path is unchanged),
and replace the hand-pinned requirements.txt with a uv workspace and
uv.lock. Adds ruff config and a .python-version.
Auto-fixes for E714 (not-is-test), I001 (import sorting) and trailing
whitespace, plus explicit strict=True on the two zip() calls flagged by
B905 -- both zip sequences that are equal-length by construction.
Whitespace and wrapping only. Notably converts api.py from tab to space
indentation -- it was the only tab-indented file in the tree.
Adds an analytics job to run-checks.yml that syncs the uv environment
against the lockfile, runs ruff check and ruff format --check, and
smoke-imports the package to prove the pyproject packaging installs.
Replaces the venv + pip + requirements.txt instructions with uv sync,
and documents the ruff commands.
@hunterckx
hunterckx force-pushed the hunter/4934-modernize-analytics-package branch from 4e966c9 to 84d3588 Compare August 20, 2026 04:28
lambda m: f"{m[:4]}-{m[4:]}" if len(m) == 6 and "-" not in m else m
)
counts_by_month = dict(zip(grouped[month_col], grouped[count_col].astype(int)))
counts_by_month = grouped.set_index(month_col)[count_col].astype(int).to_dict()

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

One change here that's not just reformatting: there was a linting issue with the zip so this is changed to keep the two columns together in Pandas and then convert to a dict

return dict(
zip([dimension["id"] for dimension in dimensions], [dimension["alias"] for dimension in dimensions])
)
return {field["id"]: field["alias"] for field in fields}

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Another zip issue -- here converted to a notably simpler dict comprehension

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

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 modernizes the analytics/** Python tooling by migrating dependency management to uv, consolidating packaging metadata into pyproject.toml, introducing Ruff linting/formatting, and wiring Python checks into CI to prevent regressions like the prior star-import leak.

Changes:

  • Replace venv + requirements.txt workflow with uv (uv.lock, .python-version) and update setup docs.
  • Add Ruff lint/format configuration and new npm scripts for running Python lint/format.
  • Add a new analytics job to run-checks.yml to run Ruff and validate imports under a locked environment.

Reviewed changes

Copilot reviewed 24 out of 26 changed files in this pull request and generated 3 comments.

Show a summary per file
File Description
package.json Adds npm scripts to run Ruff via uv from the repo root.
analytics/requirements.txt Removes legacy pip requirements file in favor of uv.lock.
analytics/readme.md Updates local setup + lint/format instructions for uv + Ruff.
analytics/pyproject.toml Defines uv workspace, dependency groups (incl. Ruff), and Ruff lint selection.
analytics/uv.lock Adds the locked dependency resolution for the analytics workspace.
analytics/.python-version Pins Python version used by uv.
analytics/lungmap/generate_static_site.py Ruff-driven import and formatting updates.
analytics/lungmap/constants.py Normalizes string quoting/formatting.
analytics/hca-explorer/generate_static_site.py Ruff-driven import and formatting updates.
analytics/hca-explorer/constants.py Reformats nested dict constant for readability.
analytics/anvil-explorer/generate_static_site.py Ruff-driven import and formatting updates.
analytics/anvil-explorer/constants.py Normalizes string quoting/formatting.
analytics/anvil-catalog/generate_static_site.py Ruff-driven import formatting updates.
analytics/analytics_package/setup.py Removes legacy setup.py packaging entrypoint.
analytics/analytics_package/pyproject.toml Introduces setuptools-based packaging metadata via pyproject.toml.
analytics/analytics_package/analytics/static_site/generator.py Import ordering/formatting changes.
analytics/analytics_package/analytics/static_site/fetch.py Formatting plus a small dict construction change for monthly counts.
analytics/analytics_package/analytics/static_site/export.py Formatting changes and minor readability improvements.
analytics/analytics_package/analytics/static_site/charts.py Formats nested chart config structures for readability.
analytics/analytics_package/analytics/static_site/init.py Reorders exports and formats __all__.
analytics/analytics_package/analytics/report_elements.py Import ordering/formatting and readability refactors for chained operations.
analytics/analytics_package/analytics/entities.py Minor formatting normalization for constants/enums.
analytics/analytics_package/analytics/api.py Large formatting/indentation normalization; preserves existing behaviors.
analytics/analytics_package/analytics/_report_utils.py Formatting + small refactor to renaming helper and readability improvements.
.gitignore Switches ignored venv dir to analytics/.venv and ignores Ruff cache.
.github/workflows/run-checks.yml Adds analytics CI job running Ruff and a minimal import check under uv.
Suppressed comments (2)

analytics/analytics_package/analytics/api.py:404

  • Spelling in comments: "nmes" → "names" and "Crete" → "Create".
        # Collect column nmes

analytics/analytics_package/analytics/api.py:287

  • Spelling in comment: "Crete" → "Create".
        # Crete the dataframe

💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.

Comment thread package.json
Comment thread analytics/readme.md
Comment thread analytics/analytics_package/analytics/api.py
@NoopDog NoopDog closed this Aug 20, 2026
@NoopDog NoopDog reopened this Aug 20, 2026

@NoopDog NoopDog left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Reviewed with Claude Code — the Python reformatting itself verified clean (AST-equivalent across all 16 changed files, pins preserved). A handful of tooling-hygiene suggestions, most inline below, plus one that doesn't anchor to the diff:

Pre-commit hook doesn't run the new Python checks.husky/pre-commit gates prettier/eslint/tsc but not lint:python / check-format:python, so an analytics/*.py edit passes the hook and then fails in CI on ruff format --check. Unconditional wiring would break commits for developers without uv, but a guarded invocation (only when uv exists and analytics files are staged) would match how the other checks are integrated.

Comment thread .gitignore
*.egg-info/
/analytics/**/site/
/analytics/venv
/analytics/.venv

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Replacing /analytics/venv with /analytics/.venv un-ignores venvs created under the old documented workflow (python -m venv ./venv inside analytics/). Anyone with such a venv will see it flood git status after pulling, and a stage-all could commit it. Suggest keeping both entries during the transition — it's free.

Comment thread package.json
"start": "npx serve out",
"lint": "eslint .",
"check-format": "prettier --check .",
"lint:python": "uv run --directory analytics ruff check .",

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

These plain uv run invocations auto-sync the full analytics environment (29 packages incl. pandas/numpy/google-api stack) just to run ruff. Adding --only-group dev, as the CI job already does, keeps a first-time npm run lint:python to the single ruff wheel instead of a multi-hundred-MB install.

with:
enable-cache: true
- run: |
uv run --locked --only-group dev ruff check .

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

The ruff invocation is now spelled three different ways in three independently maintained places — here (uv run --locked --only-group dev), package.json (uv run --directory analytics, full sync), and the readme (plain uv run) — so local and CI don't run ruff under identical conditions, and a future flag/path edit to one copy but not the others produces local-pass/CI-fail drift. Consider one canonical entry point, e.g. CI calling the npm scripts, or the scripts adopting CI's flags.

- run: |
uv run --locked --only-group dev ruff check .
uv run --locked --only-group dev ruff format --check .
uv sync --locked

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

This job materializes the uv environment twice: the two --only-group dev runs build a dev-only env for ruff, then uv sync --locked rebuilds the full env in the same job. Equivalent and simpler: one uv sync --locked first, then uv run --no-sync for all commands (ruff is in the default-installed dev group).

Comment thread analytics/pyproject.toml
dev = ["ruff==0.16.3"]

[tool.ruff.lint]
select = ["E4", "E7", "E9", "F", "I", "W", "B"]

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

This select re-spells ruff's default set (E4, E7, E9, F) before appending the real policy; extend-select = ["I", "W", "B"] expresses the same intent in one self-documenting line and tracks default-set updates when the pinned ruff is bumped. Explicit pinning is defensible too — nitpick either way.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Ruff's default rules actually include an assortment of rules not included here, and do not include E4 or all of E7 and F. I don't really have a reason to prefer one choice or the other (or the combination), but a couple things to consider are that Ruff recommends an explicit select, and enabling all of the default rules would require a few more code changes. (On the other hand, using the default rules would be consistent with how we handle TypeScript linting)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

chore: modernize analytics package tooling (uv, ruff, pyproject)

3 participants