Skip to content

Release v 1.1#187

Merged
pendingintent merged 32 commits into
masterfrom
release-v-1.1
Mar 31, 2026
Merged

Release v 1.1#187
pendingintent merged 32 commits into
masterfrom
release-v-1.1

Conversation

@pendingintent

Copy link
Copy Markdown
Owner

Added submodule to support validation against the USDM v4.0 schema .
Added a markdown file to explain the biomedical concept workflow.
Various fixes.

@pendingintent pendingintent added this to the v1.1 milestone Mar 30, 2026
@pendingintent pendingintent self-assigned this Mar 30, 2026
@pendingintent pendingintent added the documentation Improvements or additions to documentation label Mar 30, 2026
Copilot AI review requested due to automatic review settings March 30, 2026 19:54
@pendingintent pendingintent added the enhancement New feature or request label Mar 30, 2026
Comment thread src/soa_builder/web/app.py Fixed
Comment thread src/soa_builder/web/app.py Fixed
Comment thread src/soa_builder/web/app.py Fixed
Comment thread src/soa_builder/web/app.py Fixed
Comment thread src/soa_builder/web/app.py Dismissed
_record_bc_surrogate_audit(
soa_id, "update", surrogate_id, before=before, after=after
)
return RedirectResponse(f"/ui/soa/{soa_id}/activities", status_code=303)

Check warning

Code scanning / CodeQL

URL redirection from remote source Medium

Untrusted URL redirection depends on a
user-provided value
.

Copilot Autofix

AI 2 months ago

Copilot could not generate an autofix suggestion

Copilot could not generate an autofix suggestion for this alert. Try pushing a new commit or if the problem persists contact support.

_record_bc_surrogate_audit(
soa_id, "delete", surrogate_id, before=before, after=None
)
return RedirectResponse(f"/ui/soa/{soa_id}/activities", status_code=303)

Check warning

Code scanning / CodeQL

URL redirection from remote source Medium

Untrusted URL redirection depends on a
user-provided value
.

Copilot Autofix

AI 2 months ago

Copilot could not generate an autofix suggestion

Copilot could not generate an autofix suggestion for this alert. Try pushing a new commit or if the problem persists contact support.

Comment thread src/soa_builder/web/routers/footnotes.py Fixed
Comment thread src/soa_builder/web/routers/footnotes.py Fixed
Comment thread src/soa_builder/web/routers/footnotes.py Fixed

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

Release-oriented update for the SoA Workbench adding USDM v4.0 support pieces (schema validation submodule + USDM output extensions), plus new UI/API capabilities for biomedical concept surrogates and matrix footnotes/superscripts.

Changes:

  • Add Biomedical Concept Surrogates (DB tables, API/UI router, USDM export, tests, and UI integration in concepts cells / XLSX export).
  • Add footnotes + matrix cell superscripts (DB migrations, UI endpoints, edit-page rendering).
  • USDM generator tweaks and performance improvements (prefetching mappings, safer timing code objects, extra indexes).

Reviewed changes

Copilot reviewed 30 out of 46 changed files in this pull request and generated 14 comments.

Show a summary per file
File Description
tests/test_routers_bc_surrogates.py Adds API + USDM integration tests for BC surrogates.
tests/test_routers_activities.py Updates reorder tests to new request shape.
src/usdm/usdm_utils.py Adjusts BC property output fields and AliasCode typing.
src/usdm/generate_usdm.py Adds bcSurrogates to the full USDM StudyVersion output; improves logging.
src/usdm/generate_study_timings.py Makes timing type / relativeToFrom optional code objects.
src/usdm/generate_encounters.py Prefetches code/rule/timing lookups to reduce per-row queries; tweaks transition rule nullability.
src/usdm/generate_biomedical_concepts.py Provides a default reference URL when missing.
src/usdm/generate_bc_surrogates.py New USDM generator for BC surrogates.
src/usdm/generate_activities.py Prefetches concept/surrogate mappings; emits bcSurrogateIds.
src/soa_builder/web/templates/edit.html Renders superscripts + inline edit controls; adds footnote display and management UI.
src/soa_builder/web/templates/concepts_cell.html Shows surrogate chips alongside concept chips and updates counts/actions.
src/soa_builder/web/templates/activities.html Adds UI for defining/editing BC surrogates and linking them to activities.
src/soa_builder/web/schemas.py Adds Pydantic schemas for BC surrogate create/update payloads.
src/soa_builder/web/routers/visits.py Adjusts transaction commit placement; fixes connection close on 404.
src/soa_builder/web/routers/rollback.py Removes unused DB_PATH constant.
src/soa_builder/web/routers/freezes.py Uses shared _connect(); removes local SQLite wiring.
src/soa_builder/web/routers/footnotes.py New footnotes API/UI router with audit logging.
src/soa_builder/web/routers/bc_surrogates.py New BC surrogate API/UI router + activity linking endpoints + HTMX partial rendering.
src/soa_builder/web/routers/activities.py Makes activity_uid immutable; changes reorder body shape; adds surrogate data for templates.
src/soa_builder/web/migrate_database.py Adds indexes + new tables/columns for footnotes, superscripts, and surrogates.
src/soa_builder/web/initialize_database.py Creates footnote table during fresh DB init.
src/soa_builder/web/db.py Improves PRAGMA failure logging.
src/soa_builder/web/audit.py Adds audit recorders for footnotes and BC surrogates; removes defensive CREATE TABLE blocks.
src/soa_builder/web/app.py Integrates new migrations/routers; adds superscript helpers and surrogate export/XLSX support.
requirements.txt Loosens requests dependency constraint.
README.md Adds submodule clone instructions; updates USDM export section.
output/report/NCT01797120_validation_report.xlsx Adds a generated validation report artifact.
images/image-7.png Documentation screenshot (surrogate selection UI).
images/image-5.png Documentation screenshot.
images/image-4.png Documentation screenshot.
files/NCT01797120_Footnote_2.html Example footnote HTML content.
files/NCT01797120_Footnote_1.html Example footnote HTML content (currently malformed).
docs/BC_WORKFLOW.md New/expanded documentation for BC/DSS workflow and surrogates.
.gitmodules Adds cdisc-json-validation submodule reference.
.gitignore Adjusts ignored output patterns and adds additional local artifact ignores.

Comment on lines +181 to +183
resp = client.post(
f"/soa/{soa_id}/activities/reorder", json={"order": [a3, a1, a2]}
)

Copilot AI Mar 30, 2026

Copy link

Choose a reason for hiding this comment

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

The reorder tests now POST a JSON object {order: [...]} to /soa/{soa_id}/activities/reorder, but the current handler (src/soa_builder/web/app.py at the same route) expects the request body to be a bare JSON array (List[int]). This will result in a 422 and make the test fail.

Align the test payload shape with the actual endpoint contract (or update the endpoint to Body(..., embed=True) consistently).

Copilot uses AI. Check for mistakes.
Comment thread src/soa_builder/web/routers/activities.py
Comment thread src/soa_builder/web/templates/edit.html
Comment thread src/soa_builder/web/templates/edit.html
Comment thread src/soa_builder/web/templates/edit.html
Comment thread src/soa_builder/web/routers/footnotes.py Outdated
Comment thread src/soa_builder/web/routers/footnotes.py
Comment thread README.md Outdated
Comment thread docs/BC_WORKFLOW.md
Comment thread files/NCT01797120_Footnote_1.html
if request.headers.get("HX-Request") == "true":
return HTMLResponse("", headers={"HX-Redirect": redirect_url})

return RedirectResponse(redirect_url, status_code=303)

Check warning

Code scanning / CodeQL

URL redirection from remote source Medium

Untrusted URL redirection depends on a
user-provided value
.

Copilot Autofix

AI 2 months ago

Copilot could not generate an autofix suggestion

Copilot could not generate an autofix suggestion for this alert. Try pushing a new commit or if the problem persists contact support.

if request.headers.get("HX-Request") == "true":
return HTMLResponse("", headers={"HX-Redirect": redirect_url})

return RedirectResponse(redirect_url, status_code=303)

Check warning

Code scanning / CodeQL

URL redirection from remote source Medium

Untrusted URL redirection depends on a
user-provided value
.

Copilot Autofix

AI 2 months ago

Copilot could not generate an autofix suggestion

Copilot could not generate an autofix suggestion for this alert. Try pushing a new commit or if the problem persists contact support.

if request.headers.get("HX-Request") == "true":
return HTMLResponse("", headers={"HX-Redirect": redirect_url})

return RedirectResponse(redirect_url, status_code=303)

Check warning

Code scanning / CodeQL

URL redirection from remote source Medium

Untrusted URL redirection depends on a
user-provided value
.

Copilot Autofix

AI 2 months ago

Copilot could not generate an autofix suggestion

Copilot could not generate an autofix suggestion for this alert. Try pushing a new commit or if the problem persists contact support.

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

Copilot reviewed 30 out of 46 changed files in this pull request and generated 3 comments.

Comment thread src/soa_builder/web/templates/edit.html Outdated
Comment thread src/soa_builder/web/routers/bc_surrogates.py Outdated
Comment thread src/soa_builder/web/routers/activities.py

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

Copilot reviewed 30 out of 46 changed files in this pull request and generated 6 comments.

hx_vals_attr = _html.escape(hx_vals_json, quote=True)

return (
f'<td hx-post="/ui/soa/{soa_id_safe}/toggle_cell"'

Copilot AI Mar 31, 2026

Copy link

Choose a reason for hiding this comment

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

_render_cell_td() returns a <td> without data-visit-id / data-instance-id. The matrix reorder JS in edit.html relies on td[data-visit-id] to reorder columns; after an HTMX swap from toggle/superscript-save, those cells will no longer participate in reordering. Include the same data-* attributes in the returned <td> so swapped cells keep working with the existing client-side reorder logic.

Suggested change
f'<td hx-post="/ui/soa/{soa_id_safe}/toggle_cell"'
f'<td data-instance-id="{instance_id_safe}" data-activity-id="{activity_id_safe}"'
f' hx-post="/ui/soa/{soa_id_safe}/toggle_cell"'

Copilot uses AI. Check for mistakes.
Comment on lines +473 to +477
conn.close()
_record_bc_surrogate_audit(
soa_id, "delete", surrogate_id, before=before, after=None
)
return RedirectResponse(f"/ui/soa/{soa_id}/activities", status_code=303)

Copilot AI Mar 31, 2026

Copy link

Choose a reason for hiding this comment

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

_render_concepts_cell() queries activity_concept with ... WHERE activity_id=? AND soa_id=?. Elsewhere the codebase defensively checks whether activity_concept has an soa_id column for backward-compatible databases. To avoid breaking upgraded deployments with legacy schemas, add the same column-existence guard here (or join via activity when soa_id is absent).

Copilot uses AI. Check for mistakes.
Comment on lines +1128 to +1143
"""Add standalone soa_id indexes on high-traffic tables.

The existing UNIQUE constraints cover (soa_id, uid) lookups, but bare
WHERE soa_id=? list queries do full table scans without a leading index.
These indexes cover the ~259 soa_id filter sites in the codebase.
"""
try:
conn = _connect()
cur = conn.cursor()
indexes = [
("idx_activity_soa", "activity", "soa_id"),
("idx_visit_soa", "visit", "soa_id"),
("idx_matrix_cells_soa", "matrix_cells", "soa_id"),
("idx_activity_concept_soa", "activity_concept", "soa_id"),
("idx_instances_soa", "instances", "soa_id"),
("idx_timing_soa", "timing", "soa_id"),

Copilot AI Mar 31, 2026

Copy link

Choose a reason for hiding this comment

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

Most of these new indexes appear redundant because the tables already have UNIQUE constraints whose backing indexes start with soa_id (e.g., UNIQUE(soa_id, activity_uid) on activity, UNIQUE(soa_id, encounter_uid) on visit, etc.), which SQLite can use for WHERE soa_id=? queries. Adding additional single-column soa_id indexes increases write cost and DB size without improving those queries. Consider limiting this migration to tables that don’t already have a leading (soa_id, ...) index (e.g., matrix_cells, activity_concept).

Suggested change
"""Add standalone soa_id indexes on high-traffic tables.
The existing UNIQUE constraints cover (soa_id, uid) lookups, but bare
WHERE soa_id=? list queries do full table scans without a leading index.
These indexes cover the ~259 soa_id filter sites in the codebase.
"""
try:
conn = _connect()
cur = conn.cursor()
indexes = [
("idx_activity_soa", "activity", "soa_id"),
("idx_visit_soa", "visit", "soa_id"),
("idx_matrix_cells_soa", "matrix_cells", "soa_id"),
("idx_activity_concept_soa", "activity_concept", "soa_id"),
("idx_instances_soa", "instances", "soa_id"),
("idx_timing_soa", "timing", "soa_id"),
"""Add standalone soa_id indexes on selected high-traffic tables.
The existing UNIQUE constraints cover (soa_id, uid) lookups, but bare
WHERE soa_id=? list queries do full table scans without a leading index.
These indexes cover the ~259 soa_id filter sites in the codebase, limited
to tables that do not already have a leading (soa_id, ...) index.
"""
try:
conn = _connect()
cur = conn.cursor()
# Only add standalone soa_id indexes for tables that lack an existing
# leading (soa_id, ...) index from a UNIQUE constraint.
indexes = [
("idx_matrix_cells_soa", "matrix_cells", "soa_id"),
("idx_activity_concept_soa", "activity_concept", "soa_id"),

Copilot uses AI. Check for mistakes.
Comment on lines +161 to 163
_type_entry = type_code_map.get(type, ([], [], [], []))
t_code, t_decode, t_codeSystem, t_codeSystemVersion = _type_entry

Copilot AI Mar 31, 2026

Copy link

Choose a reason for hiding this comment

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

type_code_map.get(type, ([],[],[],[])) can yield empty lists when visit.type is NULL/blank or when the code association is missing. The code later indexes into t_code[0] / t_decode[0], which will raise IndexError and fail USDM encounter generation. Treat type as optional (set it to None when missing/unmapped), similar to how timings now handle missing codes.

Copilot uses AI. Check for mistakes.
Comment thread docs/BC_WORKFLOW.md
@@ -0,0 +1,414 @@
# Workflow for linking Biomedical Concept with Scheduled Activity Instance

Assumes that the Activties and Scheduled Activity Instances have been created to form the SOA Matrix.

Copilot AI Mar 31, 2026

Copy link

Choose a reason for hiding this comment

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

Typo: “Activties” → “Activities”. This affects readability and searchability in the documentation.

Suggested change
Assumes that the Activties and Scheduled Activity Instances have been created to form the SOA Matrix.
Assumes that the Activities and Scheduled Activity Instances have been created to form the SOA Matrix.

Copilot uses AI. Check for mistakes.
Comment on lines +71 to +82
cur.execute(
"INSERT INTO footnote (soa_id, footnote_uid, name, label, description, text, dictionary_uid) VALUES (?,?,?,?,?,?,?)",
(
soa_id,
uid,
body.name,
body.label or None,
body.description or None,
body.text or None,
body.dictionary_uid or None,
),
)

Copilot AI Mar 31, 2026

Copy link

Choose a reason for hiding this comment

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

Footnote text is stored verbatim (intended XHTML) and later rendered with | safe in the edit template. Without server-side sanitization/validation, this creates a stored-XSS risk. Consider sanitizing text on create/update (e.g., allowlist tags/attributes) or storing a safe structured representation.

Copilot uses AI. Check for mistakes.
@pendingintent pendingintent merged commit 60ce0d1 into master Mar 31, 2026
9 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

documentation Improvements or additions to documentation enhancement New feature or request

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants