Open the guide in a tab, and point at the demo instead of hiding it - #109
Merged
Merged
Conversation
Both surfaces 1.3.0 added to reach documentation and demo data lead somewhere a clinician cannot get back from. Fixed together because they are the same mistake twice: the app knew where something was, and told the user in a way the user could not act on. The Docs link pointed at a GitHub release asset. GitHub serves those with Content-Disposition: attachment, which no attribute on an <a> can override, so every click -- a misclick included -- wrote 1.3 MB to Downloads, needed the network, and served the newest release's guide to whoever clicked, whatever version they were running. The href is now computed at layout time in dash_api/user_guide.py: a frozen bundle already carries the PDF beside its executable and serves that copy inline from the app's own Flask server, so a bundle needs no network at all; everything else opens user_guide.md rendered on GitHub, pinned to the running version. Pinning keys off an exact X.Y.Z rather than "is the version known", because running_version() returns a label rather than None when it cannot tell, and because 1.3.1.dev0 and 1.4.0rc1 are known versions with no tag to name -- a ref we never published is a 404, which is worse than a slightly newer guide. A source checkout falls back to main. The release asset stays published: installs at 1.3.0 and earlier still point their Docs link at that exact name. `clinical-scope --demo` downloaded the dataset into ~/.clinical_scope/, and a leading dot is hidden by default on both macOS and Windows. A clinician who closed the terminal could no longer reach their own demo data, at the first thing they touch. It now prints the link instead: a browser saves it to Downloads, a folder every user can open. --force went with it, having only ever meant "download it again", and demo_data.py with it -- the download machinery had no caller left, and an in-app demo button would want a different shape anyway. The published archive needed a top-level folder to match. build.yml staged into example/ and archived root_dir=staged, so the zip's root was three loose entries; invisible while the app unpacked it, but a person running `unzip` gets three mystery folders in Downloads. Archiving the staged folder by name instead leaves one clinical-scope-example/, which is what the printed paths now assume. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Audit of every comment the previous commit wrote or touched, against the one bar: does it carry what the code cannot say? Five said what the line below them said -- three trailing notes on USER_GUIDE_* constants whose names already carry them, the core_api line narrating the three-line if, and a TestForceIsGone docstring restating its own class name. Fourteen were tightened, and most shared one fault: they explained the change instead of the code. "The app no longer resolves its Docs link here", "The bug being fixed:", "The whole point of the change:", "not three loose folders" -- all of it is the diff's story, which the reader of a file six months from now does not need and cannot check. The tradeoffs those comments were wrapped around stay: why a checkout's PDF does not count, why the pin needs an exact X.Y.Z, why `releases/latest` rather than a version, why the asset name is frozen. The block above USER_GUIDE_* also said in three lines what user_guide.py says in its docstring and again in its two functions. Stating a rule three times means two copies go stale silently. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Addresses #106 and #107 — left for you to close once you have checked the acceptance criteria. Targeting 1.3.1.
Both surfaces 1.3.0 added to reach documentation and demo data lead somewhere a clinician cannot get back from. They are the same mistake twice — the app knew where something was, and told the user in a way the user could not act on — so they ship together.
One commit rather than two: the two fixes interleave in
constants.py,build.yml,CHANGELOG.mdanduser_guide.md, and splitting those hunks would have cost more clarity than it bought. The sections below separate them for review.#106 — Docs link downloaded a 1.3 MB PDF on every click
The link pointed at a GitHub release asset. GitHub serves those with
Content-Disposition: attachment, and no attribute on an<a>overrides a response header — so every click, a misclick included, wrote 1.3 MB to Downloads, needed the network, and served the newest release's guide to whoever clicked, whatever version they were running.The href is now computed at layout time in the new
dash_api/user_guide.py. No new callback — the link stays a plainhtml.A./user-guidepip install…/blob/v1.3.1/docs/user_guide/user_guide.md…/blob/main/docs/user_guide/user_guide.mdWhy pinning keys off an exact
X.Y.Z, not "is the version known".running_version()returns a label rather thanNonewhen it cannot tell, so a null check would not work. More importantly,1.3.1.dev0and1.4.0rc1are perfectly known versions with no published tag — naming a ref that does not exist is a 404, which is worse than a slightly newer guide. So the pin requires a fullX.Y.Zmatch and falls back tomainotherwise.Why a source checkout's PDF does not count as "on disk".
docs/user_guide/ClinicalScope_UserGuide.pdfexists in a checkout, but it is a committed artifact rebuilt once per release — it is expected to laguser_guide.mdonmain. Serving it to a developer would hand them documentation older than the code they are running.USER_GUIDE_URLis removed. The release asset stays published and keeps its name: installs at 1.3.0 and earlier still point their Docs link at that exact asset, andbuild.yml's note now says so.#107 —
--demopointed at a folder users cannot findclinical-scope --demodownloaded the dataset into~/.clinical_scope/, and a leading dot is hidden by default on both macOS and Windows. A clinician who closed the terminal could no longer reach their own demo data — at the first thing they touch.It now prints the link instead, so the browser saves it to Downloads, a folder every user can open:
That last paragraph is beyond what the issue asked for — without those two dates the demo is a second dead end right after the first. It deliberately does not restate them, which would duplicate demo-data literals into
cli.py; it points at where they are written.Maintainer decisions from the issue, both resolved as "remove":
--forceonly ever meant "download it again". Gone, along with theparser.errorbranch;--helpmatches.demo_data.pyhad no caller left. Removed entirely, with its tests. An in-app demo button (No way to load the demo from inside the app #108) would want a different shape anyway, and can be written against what that surface actually needs.The archive now has a top-level folder.
build.ymlstaged intoexample/and archivedroot_dir=staged, so the zip's root was three loose entries. Invisible while the app unpacked it for you — but a person running command-lineunzipgets three mystery folders in Downloads. It now archives the staged folder by name, leaving oneclinical-scope-example/, which is what the printed paths assume.Verified locally against the real
example/tree, not just in principle:namelist()top-level is['clinical-scope-example'](55 members, 2.13 MB),unzip -qinto an empty directory leaves exactly one entry, and both printed paths exist in the unpacked tree. Noclinical_scope_outputor.DS_Storeleaked through the ignore patterns.Checks
ruff check .— all checks passed.ruff format --check .— 123 files already formatted.pytest— 1357 passed in 94s.clinical-scope --demorun for real: output above, exit 0.demo_data,fetch_demo_data,DemoDownloadError,USER_GUIDE_URLand the four deletedDEMO_DOWNLOAD_*constants — clean outside CHANGELOG history.Two things to know before releasing
releases/lateststill serves 1.3.0's assets until 1.3.1 is published — and 1.3.0's archive has the old three-loose-folders layout. So--demo's printed paths will not match a download taken frommainbefore the release goes out. Self-resolving at release, but it meansRELEASING.mdstep 5's single-folder check only passes after the draft release is published, not during the PyPI dry-run.End-to-end bundle behaviour still wants one manual look. The Flask route is unit-tested through a real test client, and the frozen path by monkeypatching
sys.frozen/sys.executable— but that those really land on the bundle root in a shipped PyInstaller build, and that the browser renders the PDF inline, is worth eyeballing once in a real bundle.Docs updated
README.md,docs/user_guide/user_guide.md(both the demo section and the Docs-link description),docs/RELEASING.md(step 5's smoke check),CHANGELOG.md(Fixed×2 plusRemovedfor--force),CLAUDE.md, andbuild_info/README.md.🤖 Generated with Claude Code