Skip to content

Open the guide in a tab, and point at the demo instead of hiding it - #109

Merged
AlexisJanin merged 3 commits into
mainfrom
fix/1.3.1-docs-link-and-demo
Sep 17, 2026
Merged

AlexisJanin merged 3 commits into
mainfrom
fix/1.3.1-docs-link-and-demo

Conversation

@AlexisJanin

@AlexisJanin AlexisJanin commented Sep 17, 2026

Copy link
Copy Markdown
Collaborator

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.md and user_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 plain html.A.

Install href Behaviour
Standalone bundle /user-guide The app serves the PDF it already ships, inline from its own Flask server. No network, nothing written to disk, and it cannot be stale — that PDF shipped with that build.
pip install …/blob/v1.3.1/docs/user_guide/user_guide.md GitHub's rendered Markdown page. Opens in a tab, downloads nothing, pinned to the running version.
Source checkout …/blob/main/docs/user_guide/user_guide.md Fallback when there is no tag to name.

Why pinning keys off an exact X.Y.Z, not "is the version known". running_version() returns a label rather than None when it cannot tell, so a null check would not work. More importantly, 1.3.1.dev0 and 1.4.0rc1 are 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 full X.Y.Z match and falls back to main otherwise.

Why a source checkout's PDF does not count as "on disk". docs/user_guide/ClinicalScope_UserGuide.pdf exists in a checkout, but it is a committed artifact rebuilt once per release — it is expected to lag user_guide.md on main. Serving it to a developer would hand them documentation older than the code they are running.

USER_GUIDE_URL is 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, and build.yml's note now says so.

#107--demo pointed at a folder users cannot find

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, so the browser saves it to Downloads, a folder every user can open:

$ clinical-scope --demo
The demo dataset is a 2 MB download:

  https://github.com/larib-data/clinical-scope/releases/latest/download/clinical-scope-example.zip

Open that link in a browser and unzip the file it saves — your Downloads folder is fine. Then
start the app with `clinical-scope` and fill in, where <unzipped> is where you unzipped it:

  - Data folder:      <unzipped>/clinical-scope-example/demo_database/demo_patient
  - Database options: <unzipped>/clinical-scope-example/demo_database/database_options.json

The demo's EIT and EDF files carry no recording date, so the app asks you for one: the user
guide's "Trying the Demo Dataset" section (the Docs button in the app) gives both values.

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":

  • --force only ever meant "download it again". Gone, along with the parser.error branch; --help matches.
  • demo_data.py had 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.yml staged into example/ and archived root_dir=staged, so the zip's root was three loose entries. Invisible while the app unpacked it for you — but a person running command-line unzip gets three mystery folders in Downloads. It now archives the staged folder by name, leaving one clinical-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 -q into an empty directory leaves exactly one entry, and both printed paths exist in the unpacked tree. No clinical_scope_output or .DS_Store leaked through the ignore patterns.


Checks

  • ruff check . — all checks passed. ruff format --check . — 123 files already formatted.
  • pytest1357 passed in 94s.
  • clinical-scope --demo run for real: output above, exit 0.
  • Repo-wide sweep for dangling references to demo_data, fetch_demo_data, DemoDownloadError, USER_GUIDE_URL and the four deleted DEMO_DOWNLOAD_* constants — clean outside CHANGELOG history.

Two things to know before releasing

  1. releases/latest still 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 from main before the release goes out. Self-resolving at release, but it means RELEASING.md step 5's single-folder check only passes after the draft release is published, not during the PyPI dry-run.

  2. 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 plus Removed for --force), CLAUDE.md, and build_info/README.md.

🤖 Generated with Claude Code

alexisj-inria and others added 3 commits September 17, 2026 15:47
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>
@AlexisJanin
AlexisJanin merged commit bd5069c into main Sep 17, 2026
3 checks passed
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.

2 participants