measure-evaluate: drive the demo from the installed FHIR package - #44
Merged
Merged
Conversation
The sample hardcoded a 12-measure registry and rebuilt its SQL layer from files in this repo. Both are now read from whatever package Aidbox has installed, so the same app serves a 12-measure package or a larger private one with no code change. Measure catalog from Aidbox - app/catalog.py discovers measures from their SQLQuery `Library` resources (<measure>-summary / -per-patient / -evidence) and resolves each one's ViewDefinition dependencies transitively through `relatedArtifact`, dispatching on the canonical url (/ViewDefinition/ vs /Library/). - `Measure` resources are optional metadata, not a gate: a package that ships Libraries alone still yields a full catalog. - Measure/$evaluate-measure no longer rejects measures missing from service/measures-registry.json; the registry now only supplies optional extras (exc_type, supported_version). Previously any measure outside those 12 returned 404. Materialization from the UI - POST /api/materialize builds the runtime state per measure or for all of them: $materialize the needed ViewDefinitions, run the package's `setup-NN-*` scripts, then flatten far.valueset into sof.concept (the one step no package resource can express, since Aidbox keeps ValueSets in a registry the SoF engine cannot see). - Views already present are skipped, so measures sharing views cost nothing on a second run. Buttons: "Materialize all" in the sidebar and overview, "Rematerialize <MEASURE>" in the measure detail header. - Setup scripts run statement-by-statement so an index for a table this pass did not create cannot roll back the rest. - No wrapper views are created: Aidbox inlines the package's `sql-view` Libraries as CTEs when running a SQLQuery, so the measure SQL's bare `patient_flat` resolves without any database view. Verified by dropping all 22 and re-running measures to identical numbers. Demo app - Serves at http://localhost:8090 (was a separate `python3 -m http.server`). - All local SQL removed; summary, per-patient and evidence now come from the Libraries via $sqlquery-run. demo/measure-sql.json is deleted. - Sidebar filter (id substring + All / Open gaps / Needs setup) for navigating a large measure list; the overview grid follows it. Setup - setup.py is replaced by load-demo-data.py, which only loads the sample bundles. Its other five steps either duplicated the package or actively conflicted with it (recreating the superseded wrapper layer, wiping sof.concept), and it only knew the 12 registry measures. - The init bundle now inlines data/*-clinical-data.json, so a fresh box comes up with patients loaded and needs no data step. It is a `batch`, so one rejected resource cannot sink the load. Build with --no-demo-data for an Aidbox holding real data. - init.json is generated and gitignored (it is multi-megabyte once the demo data is inlined); README step 1 builds it alongside the package .tgz. - sql/01-wrapper-views.sql and sql/02-shared-exclusions.sql are dropped — the package ships both layers now. Fixes two sample-data defects that failed validation on load: a Condition.clinicalStatus display of 'InActive' (should be 'Inactive') and an Organization with neither name nor identifier, which also took two Coverage resources with it. A fresh boot now logs zero batch errors. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
…ge ships sql/03-sof-indexes.sql covered all 22 flat tables, but the package built from this repo ships 10 ViewDefinitions — so 13 of those tables can never exist and 24 of the 39 CREATE INDEX statements targeted nothing. The per-statement execution in run_setup_libraries() kept that from failing the install, but it meant 24 skipped statements on every materialize. Trim the script to the 9 flat tables the shipped ViewDefinitions produce, and narrow the trailing ANALYZE to match (it listed all 22, so as a single statement it would have errored outright). The library's `depends-on` metadata was already correct — _setup_lineage() filters to ViewDefinitions present on disk — so the SQL body and the declared dependencies now agree: 9 tables, 9 ViewDefinitions. Verified: 15 statements, all 16 (with ANALYZE) apply with zero failures against a live box, and a full materialize reports no failed steps with measure results unchanged (cms130 204/80/8, cms131 57/26/11, cms165 75/42/2). Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
The sample was missing from the Aidbox Custom Operations list, so it was not discoverable from the repository index. Placed after the IPS entries, matching the section's existing order. Co-Authored-By: Claude Opus 5 (1M context) <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.
The sample hardcoded a 12-measure registry and rebuilt its SQL layer from files in this repo. Both are now read from whatever package Aidbox has installed, so the same app serves a 12-measure package or a larger private one with no code change.
Measure catalog from Aidbox
app/catalog.pydiscovers measures from their SQLQueryLibraryresources (<measure>-summary/-per-patient/-evidence) and resolves each one's ViewDefinition dependencies transitively throughrelatedArtifact, dispatching on the canonical url (/ViewDefinition/vs/Library/).Measureresources are optional metadata rather than a gate — a package shipping Libraries alone still yields a full catalog.Measure/$evaluate-measureno longer rejects measures missing fromservice/measures-registry.json. Previously any measure outside those 12 returned:The registry now only supplies optional extras (
exc_type,supported_version).Materialization from the UI
POST /api/materializebuilds the runtime state, per measure or for all of them:$materializethe needed ViewDefinitions intosof.*tablessetup-NN-*scripts (terminology scaffolding, indexes)far.valuesetintosof.concept— the one step no package resource can express, since Aidbox keeps ValueSets in a registry the SoF engine cannot seeViews already present are skipped, so measures sharing views cost nothing on a second run. Buttons: Materialize all in the sidebar and overview, Rematerialize <MEASURE> in the measure detail header. Setup scripts run statement-by-statement so an index for a table this pass did not create cannot roll back the rest.
No wrapper views are created. Aidbox inlines the package's
sql-viewLibraries as CTEs when running a SQLQuery, so the measure SQL's barepatient_flatresolves without any database view — verified by dropping all 22 and re-running measures to identical numbers, while raw$sqlon the same name still errors.Demo app
http://localhost:8090by the app container; no separatepython3 -m http.serverstep.$sqlquery-run.demo/measure-sql.jsonis deleted.Setup
setup.pyis replaced byload-demo-data.py, which only loads the sample bundles. Its other five steps either duplicated the package or actively conflicted with it — recreating the superseded wrapper layer, wipingsof.concept— and it only knew the 12 registry measures.The init bundle now inlines
data/*-clinical-data.json, so a fresh box comes up with patients loaded and needs no data step. It is abatch, so one rejected resource cannot sink the load; pass--no-demo-datafor an Aidbox holding real data.init.jsonis generated and gitignored (multi-megabyte once the data is inlined) and README step 1 builds it alongside the package.tgz.sql/01-wrapper-views.sqlandsql/02-shared-exclusions.sqlare dropped — the package ships both layers now.Sample-data fixes
Two resources failed validation on load, and the
Organizationtook twoCoverageresources with it:Condition.clinicalStatus(cms143)'InActive', should be'Inactive'Organization/example(cms165)org-1A fresh boot now logs zero batch errors;
Coveragegoes 31 → 33.Verification
Full teardown (
docker compose down -v), rebuilt both artifacts from the README, fresh boot:Demo driven in a headless browser: measures listed, live gap counts, filter and materialize buttons working, no JS errors.
🤖 Generated with Claude Code