Make the docs and the demo data reachable from a pip install - #105
Conversation
A `pip install clinical-scope` puts nothing but `src/` on disk: neither `docs/` nor `example/` is packaged, so the PyPI project page is the only entry point a pip user has. That page rendered the README with six repo-relative links, which PyPI resolves against pypi.org — every one of them 404'd, and the demo GIF did not render at all. The app itself offered no way to reach the tutorial either. - `[project.urls]` gains Documentation, Changelog and Issues, which Warehouse renders as the project page's sidebar. - Every README link is now absolute, so the page works off-repo. - A grey "Docs" pill joins the version badge and settings gear in the top-right stack, opening the online tutorial. - A test fails on any relative link reintroduced into the README. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
The standalone bundle carries `example/` because assemble_bundle.py copies it in, and a source checkout has it by definition. A pip install has neither, so the demo data needs somewhere to be fetched from. Adds an `example` job to build.yml that zips `example/` into `clinical-scope-example.zip` and attaches it to the same draft release as the executables. The tree is staged through a filtered copy first, so the per-run parquet cache and macOS metadata stay out — 2.2 MB rather than the 10 MB an unfiltered archive of the folder would carry. `example/` itself does not move: the bundle keeps its own copy and the test fixtures keep their paths. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
A pip install has no example data and, until now, no way to get any: the Quickstart pointed at a folder only a checkout or the standalone bundle has. `--demo` downloads the release asset into ~/.clinical_scope/example and prints the two paths the app asks for. The parser lives in a new `cli.py` rather than in `core_api.main`, which stays the PyInstaller entry point and must keep ignoring sys.argv — macOS hands a Finder-launched bundle a `-psn_…` argument that argparse would reject. Keeping it separate also means `--help` and `--demo` never build the Dash layout, which happens at import time. `clinical-scope -h` now describes the demo, so the command is discoverable from the terminal. The download is bounded in size, staged in a sibling folder so an interrupted run cannot leave a half-filled demo behind, and refuses any archive member that would write outside its destination. Every failure leaves as one exception: with no asset published yet, `--demo` reports a readable message and exits 1 rather than raising. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
The Quickstart told every reader to point at `demo_database/demo_patient/`, a folder only a source checkout or the standalone application has. For a pip user that was a dead end with no alternative named. - README gains a *Trying the demo* section, and step 3 of the Quickstart sends readers to it instead of promising a bundled folder. - The user guide gains *Trying the Demo Dataset*, covering both routes — it is where the new in-app Docs link lands, so that is where someone who installed with pip will find out the demo is a download away. - The guide's interface tour mentions the Docs link next to Settings. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
The standalone bundle has no `example/` wrapper — assemble_bundle.py copies each asset to the bundle root — but the new demo sections in the README and the user guide sent standalone users to `example/demo_database/`. Both now name `demo_database/`, which is what is actually next to the executable. `--force` was declared at top level and read only inside the `--demo` branch, so `clinical-scope --force` launched the app and dropped the flag. It is now a usage error. `cli.py` also gains the tests it shipped without: the printed paths, both failure exits, and that neither `--demo` nor a refused `--force` ever reaches the dashboard. The demo tree's folder names moved to constants and are now pinned from both ends — test_cli.py asserts the literal paths the command prints, and test_example_assets.py asserts those same literals exist under `example/`. Renaming either side fails a test, instead of surfacing only once a tag is pushed and a user runs `--demo`. Two comments narrated how the code came to look as it does rather than what a reader needs: the docs-link style note restated its own dict, and the demo-download test docstring explained another module's design — untruly, since patching `urlopen` does not depend on that module being Dash-free. The top-right stack's 10/40/70px offsets became a top and a pitch. Also: assemble_bundle.py now filters the same names the release archive does, build.yml names the constant its asset name is pinned by, and the `.gitignore` entry for an `example.zip` nothing produces is gone. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
`ruff check .` and `ruff format --check .` have both been failing since `cli.py` and `demo_data.py` were added, so CI's lint job was red for reasons unrelated to what the code does. `demo_data.py` now raises the way the rest of `src/` does — `msg = …` then `raise DemoDownloadError(msg)` — which is what TRY003 and EM102 are asking for, and incidentally puts the over-long unpack message inside the line limit. The `# noqa: S202` on `extractall` was never doing anything: S202 is a tarfile rule, so the reason it carried is now a plain comment. The one raise that must happen inside the `try`, so the handler deletes the part-file, keeps a narrow `# noqa: TRY301`. `cli.py` gets the per-file ignores the config already grants `scripts/*` and `build_info/*`: a console script prints to stdout by design, and its imports are deferred on purpose so `--help` and `--demo` never build the Dash layout. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
The lint job on this branch was red for four commits without anyone noticing, because running the checks meant remembering three commands and which venv had the pinned ruff. `.github/ci-local.sh` runs what `ci.yml` runs, next to the file it mirrors. It keeps CI's shape: every check runs and the failures are reported together, since CI splits lint and test into two jobs precisely so one cannot hide the other. It prints the ruff version first, as CI does, and says so when that version is outside the capped 0.16.x line — the mismatch CONTRIBUTING already warns about. It covers the active interpreter only; CI still runs the suite on 3.11 and 3.13. The PR checklist now points at it instead of listing the commands. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
AlexisJanin
left a comment
There was a problem hiding this comment.
1 additional comment: i suggest to take this PR opportunity to also clarify the term tutorial vs user-guide. Most obvious conflict is the tutorial.md being transformed to user_guide.pdf
Drops `.github/ci-local.sh` and restores the CONTRIBUTING checklist: running the checks locally already works with the plain commands, and a mirror of ci.yml takes on its job of being one controlled environment for everyone. Corrects the demo walkthrough. The EIT day and the EDF recording start are not there to line up recordings made at different times — neither file carries a recording date at all, so both formats expect one to be supplied. As written it read as a defect in the data. The walkthrough also gains an Inspect data step, which is the cheaper first look at any new folder. Points the in-app Docs link at `ClinicalScope_UserGuide.pdf` on the newest release rather than the markdown on `main`, and adds the build.yml job that attaches it, so a click lands on a released guide instead of edits for a version nobody is running. `TUTORIAL_URL` becomes `USER_GUIDE_URL`. Moves the Docs pill between the version badge and the settings gear. Removes the comments the review called narration, in core_api, styles, pyproject and constants. The release-asset naming warning leaves RELEASING entirely: build.yml already carries it, next to the name it pins, where the person who would break it is actually reading. Rewrites cli.py's docstring to lead with the reason the module exists — the console script and the Finder-launched bundle have different argv contracts — rather than with the startup cost, which only justifies the deferred imports and is now stated where those happen. RELEASING step 5 gets `--demo --force`: without --force the demo folder left by the previous release is kept, so nothing is downloaded and the asset is never actually checked. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
The document already called itself one thing and was filed under another: its own frontmatter title is "Clinical Scope -- User Guide", it builds to ClinicalScope_UserGuide.pdf and README linked it as "the user guide", while the file was tutorial.md and CLAUDE.md, CONTRIBUTING and the skills all called it the tutorial. Two names for one document, and a reader tracing a reference had to work out they were the same. Renames it to docs/user_guide/user_guide.md and settles every reference on "user guide" — links, prose, the build script, three skills, ADR-0008, and the test that reads the file by path. CHANGELOG entries keep saying "tutorial": they record what shipped in past releases, and that is a history rather than an inconsistency. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Review addressedAll fifteen threads answered inline. Two commits, split the way you asked.
|
The tutorial and the example are easy to find with a source checkout (the repo is on disk) and with the standalone application (
assemble_bundle.pycopies both in next to the executable). Apip installis the third channel, and it has neither: the wheel packagessrc/and nothing else. This branch closes that gap.What a pip user had before this
pypi.org, so every one of them 404'd and the demo GIF did not render.demo_database/demo_patient/, a folder they do not have, with no alternative named.Four commits
1. Make the documentation reachable —
[project.urls]gainsDocumentation,ChangelogandIssues(Warehouse renders these as the project page's sidebar); every README link is absolute; a grey 📖 Docs pill joins the version badge and settings gear in the top-right stack. A test fails on any relative link reintroduced into the README.2. Publish the example dataset as a release asset — an
examplejob inbuild.ymlzipsexample/intoclinical-scope-example.zipand attaches it to the same draft release as the executables. The tree is staged through a filtered copy first, so the per-run parquet cache and macOS metadata stay out: 2.2 MB rather than the 10 MB an unfiltered archive would carry.example/itself does not move — the bundle keeps its own copy and the test fixtures keep their paths.3.
clinical-scope --demo— downloads that asset into~/.clinical_scope/example/and prints the data-folder and config paths the app asks for.clinical-scope -hnow describes it, and--versionprints the installed version.4. Document it — a Trying the demo section in the README and Trying the Demo Dataset in the user guide, covering both the bundled copy and the download. The guide is where the new Docs link lands, so that is where a pip user discovers the demo is a download away.
Two design notes
The parser lives in a new
cli.py, not incore_api.main.core_api.pystays the PyInstaller entry point and must keep ignoringsys.argv— macOS hands a Finder-launched bundle a-psn_…argument that argparse would reject. Keeping it separate also means--helpand--demonever build the Dash layout, which happens at import time.Fetch rather than ship in the wheel. Bundling the demo as package data would force
example/undersrc/clinical_scope/, breakingassemble_bundle.pyand the test fixtures, and would add ~6 MB to every install for the majority of users who bring their own ICU data. The download is bounded in size, staged in a sibling folder so an interrupted run cannot leave a half-filled demo behind, and refuses any archive member that would write outside its destination.Testing
tests/unit/test_demo_data.py— 16 tests covering the happy path, idempotency,--force, zip-slip, oversize, bad zip, and offline. No network:urlopenis replaced by a fake serving bytes built in the test.tests/unit/test_readme_links.py— guards the README against relative links.Known limitation
--democannot work until a release ships with the asset —releases/latest/download/clinical-scope-example.zip404s today. Until then the command reports a readable message and exits 1 rather than raising.docs/RELEASING.mdstep 3 now includes checking that the asset is attached, and warns that renaming it breaks the command for every version already installed.The in-app Docs link points at the online tutorial, so it is dead for a standalone-bundle user with no network, even though the PDF sits next to their executable. Left alone for now; say the word if that case is worth handling.
🤖 Generated with Claude Code