diff --git a/.ua/.trash-1785851745/assemble-review.json b/.ua/.trash-1785851745/assemble-review.json new file mode 100644 index 0000000..47e5130 --- /dev/null +++ b/.ua/.trash-1785851745/assemble-review.json @@ -0,0 +1,16 @@ +{ + "fixedSectionOk": true, + "nodesRecovered": 2, + "edgesRestored": 6, + "crossBatchEdgesAdded": 0, + "typesRemapped": 0, + "complexityRemapped": 0, + "notes": [ + "Fixed section (7 tested_by edges dropped) is a small fraction (~1.2%) of 568 input edges; no systemic issue, no action needed.", + "No nodes-with-no-id, unknown-type, or unknown-complexity issues were reported by the merge script for this run.", + "4 of the 6 dropped 'could not fix' edges (document:files/plan.md -> BC Curation Principles and Completion GLs.xlsx / BC Examples.xlsx / BC DEC Templates.xlsx / BC Governance.docx) were not truly missing data: batch-12 had already created these as file:files/... nodes (type 'file'), but batch-10's edges were authored with a document: prefix. This is a cross-batch prefix-convention mismatch the mechanical merge script's ID-normalization rules didn't cover (not a doubled/missing prefix, but a wrong type-prefix). Rewrote the 4 edge targets to the existing file:files/... node IDs and restored them.", + "The remaining 2 dropped edges targeted files/cdisc_bc_ai_platform.html and files/cdisc_smart_goals_infographic.html. These .html files exist on disk under files/ but were never scanned by project-scanner (absent from scan-result.json's 97-file inventory, filteredByIgnore=0), so no batch ever produced nodes for them -- likely an extension the scanner's Phase 1 doesn't pick up (confirmed present in .html form, not excluded via .understandignore). Recreated them as lightweight file: nodes (summary: 'No summary available', tags: ['untagged'], complexity: 'moderate') and restored the 2 edges from plan.md, since the files are real, referenced reference documents and recovering them was low-risk and valuable for graph completeness.", + "Cross-batch imports check: all 105 importMap entries from scan-result.json already have corresponding imports edges in the assembled graph (0 missing) -- consistent with the merge script's own report of 0 additional imports recovered. No cross-batch edges needed.", + "Post-fix integrity check passed: 281 nodes, 561 edges, 0 dangling edges, 0 duplicate node IDs, 0 duplicate (source,target,type) edge keys." + ] +} diff --git a/.ua/.trash-1785851745/assembled-graph.json b/.ua/.trash-1785851745/assembled-graph.json new file mode 100644 index 0000000..475390e --- /dev/null +++ b/.ua/.trash-1785851745/assembled-graph.json @@ -0,0 +1,8722 @@ +{ + "version": "1.0.0", + "project": { + "name": "cdisc-concept-curation", + "languages": [ + "config", + "css", + "docx", + "html", + "javascript", + "json", + "mako", + "markdown", + "python", + "shell", + "toml", + "txt", + "unknown", + "xlsx", + "yaml" + ], + "frameworks": [ + "Flask", + "SQLAlchemy", + "Alembic", + "pytest", + "GitHub Actions" + ], + "description": "A Flask web application for curating CDISC Biomedical Concepts (BCs), replacing ad hoc spreadsheet and email workflows with a structured pipeline covering ingestion, AI-assisted field mapping, SME review, governance approval, and publishing to the CDISC Library.", + "analyzedAt": "2026-08-04T08:52:00Z", + "gitCommitHash": "8df239f9988f09a9476e634ceb7cac80c29f72fe" + }, + "nodes": [ + { + "id": "file:models/audit.py", + "type": "file", + "name": "audit.py", + "filePath": "models/audit.py", + "summary": "SQLAlchemy model for the AuditLog table that records before/after JSON state for every change made to BCs, DECs, and governance records.", + "tags": [ + "data-model", + "audit-log", + "database", + "orm", + "tested" + ], + "complexity": "simple" + }, + { + "id": "file:models/bc.py", + "type": "file", + "name": "bc.py", + "filePath": "models/bc.py", + "summary": "Core SQLAlchemy models for BiomedicalConcept and DataElementConcept, the central entities of the CDISC BC curation pipeline, including NCIt/LOINC coding fields and governance status.", + "tags": [ + "data-model", + "database", + "orm", + "core-domain", + "tested" + ], + "complexity": "moderate" + }, + { + "id": "file:models/ingestion.py", + "type": "file", + "name": "ingestion.py", + "filePath": "models/ingestion.py", + "summary": "SQLAlchemy model for IngestionRecord, which stages parsed upload rows (with field mappings, confidence scores, and validation errors) in the DB prior to SME approval.", + "tags": [ + "data-model", + "database", + "orm", + "ingestion", + "tested" + ], + "complexity": "simple" + }, + { + "id": "file:routes/audit.py", + "type": "file", + "name": "audit.py", + "filePath": "routes/audit.py", + "summary": "Flask blueprint exposing the read-only audit trail view with filtering by entity type, action, and actor, and pagination.", + "tags": [ + "api-handler", + "audit-log", + "blueprint", + "tested" + ], + "complexity": "simple" + }, + { + "id": "file:routes/ingestion.py", + "type": "file", + "name": "ingestion.py", + "filePath": "routes/ingestion.py", + "summary": "Flask blueprint implementing the file-upload ingestion workflow: parsing uploaded files, staging rows as IngestionRecord, and approving/rejecting them into BiomedicalConcept/DataElementConcept records with audit logging.", + "tags": [ + "api-handler", + "blueprint", + "ingestion", + "workflow", + "tested" + ], + "complexity": "moderate" + }, + { + "id": "file:services/ingestion.py", + "type": "file", + "name": "ingestion.py", + "filePath": "services/ingestion.py", + "summary": "Business logic for the ingestion pipeline: fuzzy field-name mapping via SequenceMatcher similarity scoring, BC/DEC row validation, multi-row grouping by BC ID, and XLSX/CSV/JSON parsers.", + "tags": [ + "service", + "parsing", + "validation", + "fuzzy-matching", + "tested" + ], + "complexity": "complex", + "languageNotes": "Uses difflib.SequenceMatcher similarity ratios against a canonical FIELD_MAP alias dictionary to fuzzily map arbitrary spreadsheet column names to BC/DEC schema fields." + }, + { + "id": "file:tests/conftest.py", + "type": "file", + "name": "conftest.py", + "filePath": "tests/conftest.py", + "summary": "Pytest fixtures shared across the test suite: Flask app/test client factories, a per-test DB reset fixture, API cache clearing, and a sample BiomedicalConcept fixture.", + "tags": [ + "test", + "fixture", + "pytest-config" + ], + "complexity": "moderate" + }, + { + "id": "class:models/audit.py:AuditLog", + "type": "class", + "name": "AuditLog", + "filePath": "models/audit.py", + "lineRange": [ + 7, + 32 + ], + "summary": "ORM model for the immutable audit trail; stores before/after entity state as JSON via property accessors that transparently serialize/deserialize.", + "tags": [ + "data-model", + "audit-log", + "orm" + ], + "complexity": "simple" + }, + { + "id": "class:models/bc.py:BiomedicalConcept", + "type": "class", + "name": "BiomedicalConcept", + "filePath": "models/bc.py", + "lineRange": [ + 6, + 50 + ], + "summary": "Central ORM entity representing a curated CDISC Biomedical Concept, holding NCIt/LOINC coding metadata, governance status, parent/child hierarchy, and related DECs/specializations.", + "tags": [ + "data-model", + "core-domain", + "orm" + ], + "complexity": "moderate" + }, + { + "id": "class:models/bc.py:DataElementConcept", + "type": "class", + "name": "DataElementConcept", + "filePath": "models/bc.py", + "lineRange": [ + 53, + 65 + ], + "summary": "ORM entity for a Data Element Concept (DEC) belonging to a BiomedicalConcept, capturing NCIt DEC code, data type, and example values.", + "tags": [ + "data-model", + "core-domain", + "orm" + ], + "complexity": "simple" + }, + { + "id": "class:models/ingestion.py:IngestionRecord", + "type": "class", + "name": "IngestionRecord", + "filePath": "models/ingestion.py", + "lineRange": [ + 7, + 56 + ], + "summary": "ORM staging model for a parsed upload row awaiting SME approval; exposes JSON-backed properties for field mapping, confidence scores, validation errors, and DEC sub-rows, plus an average-confidence helper.", + "tags": [ + "data-model", + "ingestion", + "orm" + ], + "complexity": "moderate" + }, + { + "id": "function:routes/audit.py:index", + "type": "function", + "name": "index", + "filePath": "routes/audit.py", + "lineRange": [ + 13, + 44 + ], + "summary": "Route handler serving the paginated audit log view, applying optional entity_type/action/actor query filters and ordering by timestamp descending.", + "tags": [ + "api-handler", + "audit-log", + "pagination" + ], + "complexity": "simple" + }, + { + "id": "function:routes/ingestion.py:_bc_from_mapped", + "type": "function", + "name": "_bc_from_mapped", + "filePath": "routes/ingestion.py", + "lineRange": [ + 38, + 54 + ], + "summary": "Builds a BiomedicalConcept instance from a mapped-field dict produced by the ingestion service, pulling canonical field values.", + "tags": [ + "helper", + "data-model", + "ingestion" + ], + "complexity": "simple" + }, + { + "id": "function:routes/ingestion.py:_create_decs", + "type": "function", + "name": "_create_decs", + "filePath": "routes/ingestion.py", + "lineRange": [ + 57, + 68 + ], + "summary": "Creates and persists DataElementConcept rows for a BC from the mapped DEC sub-row list produced during ingestion.", + "tags": [ + "helper", + "data-model", + "ingestion" + ], + "complexity": "simple" + }, + { + "id": "function:routes/ingestion.py:upload", + "type": "function", + "name": "upload", + "filePath": "routes/ingestion.py", + "lineRange": [ + 79, + 118 + ], + "summary": "Route handler that accepts an uploaded XLSX/CSV/JSON file, dispatches to the matching parser, deduplicates against existing BC IDs, and stages the results as pending IngestionRecord rows.", + "tags": [ + "api-handler", + "ingestion", + "file-upload" + ], + "complexity": "moderate" + }, + { + "id": "function:routes/ingestion.py:approve", + "type": "function", + "name": "approve", + "filePath": "routes/ingestion.py", + "lineRange": [ + 122, + 143 + ], + "summary": "Route handler that approves a single pending IngestionRecord, committing it as a new BiomedicalConcept (and DECs) and writing an audit log entry.", + "tags": [ + "api-handler", + "ingestion", + "workflow" + ], + "complexity": "moderate" + }, + { + "id": "function:routes/ingestion.py:approve_all", + "type": "function", + "name": "approve_all", + "filePath": "routes/ingestion.py", + "lineRange": [ + 155, + 186 + ], + "summary": "Route handler that bulk-approves all pending IngestionRecord rows for the current session, creating BCs/DECs and an audit log entry per record.", + "tags": [ + "api-handler", + "ingestion", + "workflow", + "bulk-operation" + ], + "complexity": "moderate" + }, + { + "id": "function:services/ingestion.py:_match_field", + "type": "function", + "name": "_match_field", + "filePath": "services/ingestion.py", + "lineRange": [ + 35, + 45 + ], + "summary": "Scores a raw column name against every canonical field's alias list using similarity ratios and returns the best-matching canonical field name with a confidence score.", + "tags": [ + "helper", + "fuzzy-matching", + "ingestion" + ], + "complexity": "simple" + }, + { + "id": "function:services/ingestion.py:map_fields", + "type": "function", + "name": "map_fields", + "filePath": "services/ingestion.py", + "lineRange": [ + 48, + 62 + ], + "summary": "Maps a raw row dict's column names to canonical BC/DEC field names using _match_field, normalizing values to stripped strings.", + "tags": [ + "fuzzy-matching", + "ingestion", + "data-transformation" + ], + "complexity": "simple" + }, + { + "id": "function:services/ingestion.py:validate_bc", + "type": "function", + "name": "validate_bc", + "filePath": "services/ingestion.py", + "lineRange": [ + 65, + 81 + ], + "summary": "Validates a mapped BC dict, flagging missing short name/definition and requiring at least one of NCIt code or external code with a correct NCIt format.", + "tags": [ + "validation", + "ingestion" + ], + "complexity": "simple" + }, + { + "id": "function:services/ingestion.py:_group_by_bc", + "type": "function", + "name": "_group_by_bc", + "filePath": "services/ingestion.py", + "lineRange": [ + 84, + 137 + ], + "summary": "Groups parsed spreadsheet rows by BC ID into single ingestion records, merging DEC sub-rows and confidences under each parent BC and running validation on the merged result.", + "tags": [ + "data-transformation", + "ingestion", + "grouping" + ], + "complexity": "moderate" + }, + { + "id": "function:services/ingestion.py:parse_xlsx", + "type": "function", + "name": "parse_xlsx", + "filePath": "services/ingestion.py", + "lineRange": [ + 140, + 162 + ], + "summary": "Parses an uploaded XLSX file's first sheet into mapped, grouped BC/DEC records via pandas, capturing per-row parse errors.", + "tags": [ + "parsing", + "ingestion", + "xlsx" + ], + "complexity": "moderate" + }, + { + "id": "function:services/ingestion.py:parse_csv", + "type": "function", + "name": "parse_csv", + "filePath": "services/ingestion.py", + "lineRange": [ + 165, + 188 + ], + "summary": "Parses an uploaded CSV file into mapped and validated BC records via pandas, capturing per-row parse errors.", + "tags": [ + "parsing", + "ingestion", + "csv" + ], + "complexity": "moderate" + }, + { + "id": "function:services/ingestion.py:parse_json", + "type": "function", + "name": "parse_json", + "filePath": "services/ingestion.py", + "lineRange": [ + 191, + 215 + ], + "summary": "Parses an uploaded JSON file (single object or array) into mapped and validated BC records, capturing per-item parse errors.", + "tags": [ + "parsing", + "ingestion", + "json" + ], + "complexity": "moderate" + }, + { + "id": "function:services/ingestion.py:deduplicate", + "type": "function", + "name": "deduplicate", + "filePath": "services/ingestion.py", + "lineRange": [ + 218, + 227 + ], + "summary": "Flags parsed records whose BC ID or NCIt code already exists in the database as duplicates, using case-insensitive comparison.", + "tags": [ + "validation", + "ingestion", + "deduplication" + ], + "complexity": "simple" + }, + { + "id": "function:tests/conftest.py:sample_bc", + "type": "function", + "name": "sample_bc", + "filePath": "tests/conftest.py", + "lineRange": [ + 55, + 68 + ], + "summary": "Pytest fixture that inserts a sample BiomedicalConcept into the test database for use by tests needing an existing BC.", + "tags": [ + "test", + "fixture" + ], + "complexity": "simple" + }, + { + "id": "file:tests/test_audit_routes.py", + "type": "file", + "name": "test_audit_routes.py", + "filePath": "tests/test_audit_routes.py", + "summary": "Test suite for the audit log route, covering empty listing, log rendering, and filtering by entity type, action, actor, and pagination parameters.", + "tags": [ + "test", + "audit", + "route-test" + ], + "complexity": "simple" + }, + { + "id": "class:tests/test_audit_routes.py:TestAuditIndex", + "type": "class", + "name": "TestAuditIndex", + "filePath": "tests/test_audit_routes.py", + "lineRange": [ + 14, + 49 + ], + "summary": "Test class exercising the audit index route's rendering, entity/action/actor filters, and pagination parameter handling.", + "tags": [ + "test", + "audit", + "route-test" + ], + "complexity": "moderate" + }, + { + "id": "file:tests/test_bc_routes.py", + "type": "file", + "name": "test_bc_routes.py", + "filePath": "tests/test_bc_routes.py", + "summary": "Large test suite covering BC CRUD routes: listing/search, creation, detail view (including LOINC lookups), editing, clearing NCIt/LOINC codes, submission for review, deletion, export formats, and the CDISC Library detail page.", + "tags": [ + "test", + "bc", + "route-test" + ], + "complexity": "complex" + }, + { + "id": "function:tests/test_bc_routes.py:_bc_form", + "type": "function", + "name": "_bc_form", + "filePath": "tests/test_bc_routes.py", + "lineRange": [ + 10, + 19 + ], + "summary": "Test helper that builds a default BC creation form payload, allowing field overrides via keyword arguments.", + "tags": [ + "test-helper", + "fixture", + "form-data" + ], + "complexity": "simple" + }, + { + "id": "class:tests/test_bc_routes.py:TestBcIndex", + "type": "class", + "name": "TestBcIndex", + "filePath": "tests/test_bc_routes.py", + "lineRange": [ + 27, + 43 + ], + "summary": "Tests the BC index route's default listing, name search, no-match search, and status filtering.", + "tags": [ + "test", + "bc", + "route-test" + ], + "complexity": "moderate" + }, + { + "id": "class:tests/test_bc_routes.py:TestCreateBc", + "type": "class", + "name": "TestCreateBc", + "filePath": "tests/test_bc_routes.py", + "lineRange": [ + 62, + 109 + ], + "summary": "Tests BC creation: success redirect, missing bc_id validation, duplicate id rejection, audit logging, conditional system/system_name population from a LOINC code, and DEC creation.", + "tags": [ + "test", + "bc", + "route-test", + "validation" + ], + "complexity": "complex" + }, + { + "id": "class:tests/test_bc_routes.py:TestBcDetail", + "type": "class", + "name": "TestBcDetail", + "filePath": "tests/test_bc_routes.py", + "lineRange": [ + 117, + 224 + ], + "summary": "Tests the BC detail page, including 404 handling and conditional LOINC API calls and spinner behavior based on whether a LOINC code is set.", + "tags": [ + "test", + "bc", + "loinc", + "route-test" + ], + "complexity": "complex" + }, + { + "id": "class:tests/test_bc_routes.py:TestEditBc", + "type": "class", + "name": "TestEditBc", + "filePath": "tests/test_bc_routes.py", + "lineRange": [ + 232, + 332 + ], + "summary": "Tests BC editing, covering short name updates, audit logging, clearing NCIt/LOINC/parent BC fields, and consistency between LOINC code and system/system_name fields.", + "tags": [ + "test", + "bc", + "route-test", + "validation" + ], + "complexity": "complex" + }, + { + "id": "class:tests/test_bc_routes.py:TestClearNcitCode", + "type": "class", + "name": "TestClearNcitCode", + "filePath": "tests/test_bc_routes.py", + "lineRange": [ + 340, + 370 + ], + "summary": "Tests the endpoint that clears a BC's NCIt code and associated metadata, including cascading clearing of the parent BC id.", + "tags": [ + "test", + "bc", + "ncit", + "route-test" + ], + "complexity": "moderate" + }, + { + "id": "class:tests/test_bc_routes.py:TestClearLoincCode", + "type": "class", + "name": "TestClearLoincCode", + "filePath": "tests/test_bc_routes.py", + "lineRange": [ + 378, + 416 + ], + "summary": "Tests the endpoint that clears a BC's LOINC code, associated metadata, and dependent system/system_name fields.", + "tags": [ + "test", + "bc", + "loinc", + "route-test" + ], + "complexity": "moderate" + }, + { + "id": "class:tests/test_bc_routes.py:TestSubmitForReview", + "type": "class", + "name": "TestSubmitForReview", + "filePath": "tests/test_bc_routes.py", + "lineRange": [ + 424, + 435 + ], + "summary": "Tests that submitting a BC for review advances its governance status and writes an audit log entry.", + "tags": [ + "test", + "bc", + "governance", + "route-test" + ], + "complexity": "simple" + }, + { + "id": "class:tests/test_bc_routes.py:TestDeleteBc", + "type": "class", + "name": "TestDeleteBc", + "filePath": "tests/test_bc_routes.py", + "lineRange": [ + 443, + 457 + ], + "summary": "Tests BC deletion, audit logging on delete, and 404 handling for nonexistent BCs.", + "tags": [ + "test", + "bc", + "route-test" + ], + "complexity": "moderate" + }, + { + "id": "class:tests/test_bc_routes.py:TestExport", + "type": "class", + "name": "TestExport", + "filePath": "tests/test_bc_routes.py", + "lineRange": [ + 465, + 479 + ], + "summary": "Tests BC export endpoints across JSON, XLSX, and ODM-XML formats.", + "tags": [ + "test", + "bc", + "export", + "route-test" + ], + "complexity": "moderate" + }, + { + "id": "class:tests/test_bc_routes.py:TestLibraryDetail", + "type": "class", + "name": "TestLibraryDetail", + "filePath": "tests/test_bc_routes.py", + "lineRange": [ + 511, + 547 + ], + "summary": "Tests the CDISC Library detail page rendering, API-error redirects, and conditional LOINC API calls based on LOINC coding presence.", + "tags": [ + "test", + "bc", + "loinc", + "cdisc-library", + "route-test" + ], + "complexity": "complex" + }, + { + "id": "file:tests/test_governance_routes.py", + "type": "file", + "name": "test_governance_routes.py", + "filePath": "tests/test_governance_routes.py", + "summary": "Test suite for the governance Kanban board routes: stage advancement, rejection, and XLSX export of stage-3 BCs with LOINC-derived system columns.", + "tags": [ + "test", + "governance", + "route-test" + ], + "complexity": "complex" + }, + { + "id": "class:tests/test_governance_routes.py:TestAdvance", + "type": "class", + "name": "TestAdvance", + "filePath": "tests/test_governance_routes.py", + "lineRange": [ + 17, + 68 + ], + "summary": "Tests governance stage advancement, including multi-stage progression, staying at published, governance record and audit log creation, 404 handling, and AJAX JSON responses.", + "tags": [ + "test", + "governance", + "route-test" + ], + "complexity": "complex" + }, + { + "id": "class:tests/test_governance_routes.py:TestReject", + "type": "class", + "name": "TestReject", + "filePath": "tests/test_governance_routes.py", + "lineRange": [ + 71, + 105 + ], + "summary": "Tests governance rejection back to provisional status, governance record and audit log creation, AJAX responses, and 404 handling.", + "tags": [ + "test", + "governance", + "route-test" + ], + "complexity": "moderate" + }, + { + "id": "class:tests/test_governance_routes.py:TestGovernanceExport", + "type": "class", + "name": "TestGovernanceExport", + "filePath": "tests/test_governance_routes.py", + "lineRange": [ + 108, + 236 + ], + "summary": "Tests the governance XLSX export endpoint: file format enforcement, filtering to stage-3 BCs, and correct population of System/System Name/Code columns based on LOINC code presence.", + "tags": [ + "test", + "governance", + "export", + "route-test" + ], + "complexity": "complex" + }, + { + "id": "file:tests/test_ingestion_routes.py", + "type": "file", + "name": "test_ingestion_routes.py", + "filePath": "tests/test_ingestion_routes.py", + "summary": "Test suite for the ingestion pipeline routes: file upload (CSV/JSON), duplicate BC flagging, per-record approve/reject, and bulk approve-all with audit logging.", + "tags": [ + "test", + "ingestion", + "route-test" + ], + "complexity": "complex" + }, + { + "id": "class:tests/test_ingestion_routes.py:TestUpload", + "type": "class", + "name": "TestUpload", + "filePath": "tests/test_ingestion_routes.py", + "lineRange": [ + 32, + 83 + ], + "summary": "Tests file upload handling for CSV and JSON ingestion, missing-file and wrong-extension errors, and duplicate BC detection.", + "tags": [ + "test", + "ingestion", + "upload", + "route-test" + ], + "complexity": "complex" + }, + { + "id": "class:tests/test_ingestion_routes.py:TestApprove", + "type": "class", + "name": "TestApprove", + "filePath": "tests/test_ingestion_routes.py", + "lineRange": [ + 86, + 131 + ], + "summary": "Tests approving an ingestion record, verifying BC creation, status transition, 404 handling, and duplicate-prevention when the BC already exists.", + "tags": [ + "test", + "ingestion", + "route-test" + ], + "complexity": "complex" + }, + { + "id": "class:tests/test_ingestion_routes.py:TestReject", + "type": "class", + "name": "TestReject", + "filePath": "tests/test_ingestion_routes.py", + "lineRange": [ + 134, + 170 + ], + "summary": "Tests rejecting an ingestion record, ensuring no BC is created and 404 handling for missing records.", + "tags": [ + "test", + "ingestion", + "route-test" + ], + "complexity": "moderate" + }, + { + "id": "class:tests/test_ingestion_routes.py:TestApproveAll", + "type": "class", + "name": "TestApproveAll", + "filePath": "tests/test_ingestion_routes.py", + "lineRange": [ + 173, + 204 + ], + "summary": "Tests bulk approval of all pending ingestion records, skipping records with validation errors and writing an audit log entry per created BC.", + "tags": [ + "test", + "ingestion", + "bulk-approve", + "route-test" + ], + "complexity": "moderate" + }, + { + "id": "file:tests/test_ingestion_service.py", + "type": "file", + "name": "test_ingestion_service.py", + "filePath": "tests/test_ingestion_service.py", + "summary": "Unit test suite for the ingestion service module, covering field similarity scoring, field mapping/matching, BC validation, deduplication, and XLSX/CSV/JSON parsing and grouping logic.", + "tags": [ + "test", + "ingestion", + "service-test", + "parsing" + ], + "complexity": "complex" + }, + { + "id": "class:tests/test_ingestion_service.py:TestSimilarity", + "type": "class", + "name": "TestSimilarity", + "filePath": "tests/test_ingestion_service.py", + "lineRange": [ + 22, + 34 + ], + "summary": "Tests the fuzzy string similarity scorer used for field-name matching, covering identical, case-insensitive, dissimilar, and partial matches.", + "tags": [ + "test", + "ingestion", + "similarity" + ], + "complexity": "simple" + }, + { + "id": "class:tests/test_ingestion_service.py:TestMatchField", + "type": "class", + "name": "TestMatchField", + "filePath": "tests/test_ingestion_service.py", + "lineRange": [ + 42, + 65 + ], + "summary": "Tests matching a raw column name to a canonical field via known aliases, definition synonyms, whitespace normalization, and low-confidence fallback.", + "tags": [ + "test", + "ingestion", + "field-mapping" + ], + "complexity": "moderate" + }, + { + "id": "class:tests/test_ingestion_service.py:TestMapFields", + "type": "class", + "name": "TestMapFields", + "filePath": "tests/test_ingestion_service.py", + "lineRange": [ + 73, + 93 + ], + "summary": "Tests mapping a raw row dict to canonical fields, including handling of None/NaN values, low-confidence exclusion, and value stripping.", + "tags": [ + "test", + "ingestion", + "field-mapping" + ], + "complexity": "moderate" + }, + { + "id": "class:tests/test_ingestion_service.py:TestValidateBc", + "type": "class", + "name": "TestValidateBc", + "filePath": "tests/test_ingestion_service.py", + "lineRange": [ + 101, + 133 + ], + "summary": "Tests BC record validation rules: required short name/definition, requiring at least one of NCIt/LOINC code, and NCIt code format checking.", + "tags": [ + "test", + "ingestion", + "validation" + ], + "complexity": "moderate" + }, + { + "id": "class:tests/test_ingestion_service.py:TestDeduplicate", + "type": "class", + "name": "TestDeduplicate", + "filePath": "tests/test_ingestion_service.py", + "lineRange": [ + 141, + 164 + ], + "summary": "Tests deduplication of parsed ingestion records against existing BC ids, including case-insensitive comparison and NCIt-code fallback matching.", + "tags": [ + "test", + "ingestion", + "deduplication" + ], + "complexity": "moderate" + }, + { + "id": "class:tests/test_ingestion_service.py:TestParseCsv", + "type": "class", + "name": "TestParseCsv", + "filePath": "tests/test_ingestion_service.py", + "lineRange": [ + 172, + 198 + ], + "summary": "Tests CSV parsing into mapped/validated records, including error handling for invalid files and validation-error propagation.", + "tags": [ + "test", + "ingestion", + "parsing", + "csv" + ], + "complexity": "moderate" + }, + { + "id": "class:tests/test_ingestion_service.py:TestParseJson", + "type": "class", + "name": "TestParseJson", + "filePath": "tests/test_ingestion_service.py", + "lineRange": [ + 206, + 223 + ], + "summary": "Tests JSON parsing for both array-of-objects and single-object inputs, and error handling for invalid JSON.", + "tags": [ + "test", + "ingestion", + "parsing", + "json" + ], + "complexity": "simple" + }, + { + "id": "class:tests/test_ingestion_service.py:TestGroupByBc", + "type": "class", + "name": "TestGroupByBc", + "filePath": "tests/test_ingestion_service.py", + "lineRange": [ + 231, + 259 + ], + "summary": "Tests grouping of parsed rows by BC id, including nested DEC sub-rows, multiple distinct BCs, and skipping rows without a BC id.", + "tags": [ + "test", + "ingestion", + "grouping" + ], + "complexity": "moderate" + }, + { + "id": "file:tests/test_models.py", + "type": "file", + "name": "test_models.py", + "filePath": "tests/test_models.py", + "summary": "Unit tests for the AuditLog, IngestionRecord, and BiomedicalConcept ORM models, verifying JSON-backed property round-tripping, computed averages, and dictionary serialization.", + "tags": [ + "test", + "models", + "orm" + ], + "complexity": "moderate" + }, + { + "id": "class:tests/test_models.py:TestAuditLogJsonProperties", + "type": "class", + "name": "TestAuditLogJsonProperties", + "filePath": "tests/test_models.py", + "lineRange": [ + 9, + 44 + ], + "summary": "Tests round-tripping of AuditLog's JSON-backed before_state/after_state properties and correct handling of None values on a persisted record.", + "tags": [ + "test", + "models", + "audit", + "serialization" + ], + "complexity": "moderate" + }, + { + "id": "class:tests/test_models.py:TestIngestionRecordProperties", + "type": "class", + "name": "TestIngestionRecordProperties", + "filePath": "tests/test_models.py", + "lineRange": [ + 47, + 87 + ], + "summary": "Tests round-tripping of IngestionRecord's JSON-backed mapped/confidences/errors/decs properties and the correctness of the computed average confidence.", + "tags": [ + "test", + "models", + "ingestion", + "serialization" + ], + "complexity": "moderate" + }, + { + "id": "class:tests/test_models.py:TestBiomedicalConceptToDict", + "type": "class", + "name": "TestBiomedicalConceptToDict", + "filePath": "tests/test_models.py", + "lineRange": [ + 90, + 116 + ], + "summary": "Tests that BiomedicalConcept.to_dict() includes required keys, matches field values, and defaults status to provisional.", + "tags": [ + "test", + "models", + "bc", + "serialization" + ], + "complexity": "simple" + }, + { + "id": "file:models/governance.py", + "type": "file", + "name": "governance.py", + "filePath": "models/governance.py", + "summary": "SQLAlchemy model for GovernanceRecord, tracking Biomedical Concept approval workflow stage transitions.", + "tags": [ + "data-model", + "database", + "governance", + "tested" + ], + "complexity": "simple" + }, + { + "id": "class:models/governance.py:GovernanceRecord", + "type": "class", + "name": "GovernanceRecord", + "filePath": "models/governance.py", + "lineRange": [ + 6, + 16 + ], + "summary": "ORM model recording a single governance stage transition (status, actor, comment, timestamp) for a Biomedical Concept.", + "tags": [ + "data-model", + "governance", + "audit" + ], + "complexity": "simple" + }, + { + "id": "file:routes/bc.py", + "type": "file", + "name": "bc.py", + "filePath": "routes/bc.py", + "summary": "Flask blueprint implementing BC CRUD, NCIt/LOINC metadata lookup, export, and lifecycle actions (submit for review, delete) for Biomedical Concepts.", + "tags": [ + "api-handler", + "flask-blueprint", + "crud", + "routes", + "tested" + ], + "complexity": "complex" + }, + { + "id": "function:routes/bc.py:index", + "type": "function", + "name": "index", + "filePath": "routes/bc.py", + "lineRange": [ + 23, + 39 + ], + "summary": "Renders the paginated, filterable list of Biomedical Concepts based on search term and status query params.", + "tags": [ + "api-handler", + "listing", + "pagination" + ], + "complexity": "moderate" + }, + { + "id": "function:routes/bc.py:new_bc", + "type": "function", + "name": "new_bc", + "filePath": "routes/bc.py", + "lineRange": [ + 43, + 59 + ], + "summary": "Renders the new-BC creation form, prefilling NCIt fields when arriving from an NCIt search selection.", + "tags": [ + "api-handler", + "form" + ], + "complexity": "moderate" + }, + { + "id": "function:routes/bc.py:export", + "type": "function", + "name": "export", + "filePath": "routes/bc.py", + "lineRange": [ + 63, + 85 + ], + "summary": "Exports all Biomedical Concepts as XLSX, ODM-XML, or JSON depending on the requested format query parameter.", + "tags": [ + "api-handler", + "export" + ], + "complexity": "moderate" + }, + { + "id": "function:routes/bc.py:library_detail", + "type": "function", + "name": "library_detail", + "filePath": "routes/bc.py", + "lineRange": [ + 89, + 114 + ], + "summary": "Displays a published CDISC Library BC's detail, merging in LOINC lookup results for CDASH/QS-style concepts.", + "tags": [ + "api-handler", + "integration" + ], + "complexity": "moderate" + }, + { + "id": "function:routes/bc.py:detail", + "type": "function", + "name": "detail", + "filePath": "routes/bc.py", + "lineRange": [ + 118, + 151 + ], + "summary": "Renders a curated BC's detail page including its Data Element Concepts, and refreshes LOINC lookup data when applicable.", + "tags": [ + "api-handler", + "crud" + ], + "complexity": "complex" + }, + { + "id": "function:routes/bc.py:fetch_metadata", + "type": "function", + "name": "fetch_metadata", + "filePath": "routes/bc.py", + "lineRange": [ + 155, + 182 + ], + "summary": "AJAX endpoint that fetches and caches NCIt and LOINC metadata for a BC and returns it as JSON.", + "tags": [ + "api-handler", + "ajax", + "integration" + ], + "complexity": "moderate" + }, + { + "id": "function:routes/bc.py:create", + "type": "function", + "name": "create", + "filePath": "routes/bc.py", + "lineRange": [ + 186, + 194 + ], + "summary": "Handles the new-BC form submission, delegating persistence and DEC creation to bc_service.", + "tags": [ + "api-handler", + "crud" + ], + "complexity": "simple" + }, + { + "id": "function:routes/bc.py:edit", + "type": "function", + "name": "edit", + "filePath": "routes/bc.py", + "lineRange": [ + 198, + 203 + ], + "summary": "Handles the BC edit form submission, updating fields and DECs via bc_service.", + "tags": [ + "api-handler", + "crud" + ], + "complexity": "simple" + }, + { + "id": "function:routes/bc.py:clear_ncit", + "type": "function", + "name": "clear_ncit", + "filePath": "routes/bc.py", + "lineRange": [ + 207, + 217 + ], + "summary": "Clears a BC's NCIt mapping fields and records the change in the audit log.", + "tags": [ + "api-handler", + "audit" + ], + "complexity": "simple" + }, + { + "id": "function:routes/bc.py:clear_loinc", + "type": "function", + "name": "clear_loinc", + "filePath": "routes/bc.py", + "lineRange": [ + 221, + 232 + ], + "summary": "Clears a BC's LOINC mapping fields and records the change in the audit log.", + "tags": [ + "api-handler", + "audit" + ], + "complexity": "simple" + }, + { + "id": "function:routes/bc.py:submit_for_review", + "type": "function", + "name": "submit_for_review", + "filePath": "routes/bc.py", + "lineRange": [ + 236, + 240 + ], + "summary": "Transitions a BC from draft into the governance review queue.", + "tags": [ + "api-handler", + "governance" + ], + "complexity": "simple" + }, + { + "id": "function:routes/bc.py:delete", + "type": "function", + "name": "delete", + "filePath": "routes/bc.py", + "lineRange": [ + 244, + 255 + ], + "summary": "Deletes a BC along with cascading child BCs and governance records, logging the deletion to the audit trail.", + "tags": [ + "api-handler", + "crud", + "audit" + ], + "complexity": "moderate" + }, + { + "id": "function:routes/bc.py:_decs_from_form", + "type": "function", + "name": "_decs_from_form", + "filePath": "routes/bc.py", + "lineRange": [ + 258, + 276 + ], + "summary": "Private helper that reconstructs the list of Data Element Concept row dicts from parallel multi-value form fields.", + "tags": [ + "utility", + "form-parsing" + ], + "complexity": "moderate" + }, + { + "id": "file:routes/governance.py", + "type": "file", + "name": "governance.py", + "filePath": "routes/governance.py", + "summary": "Flask blueprint for the governance Kanban board \u2014 viewing BCs by stage, exporting the published set, and advancing/rejecting BCs through the approval workflow.", + "tags": [ + "api-handler", + "governance", + "flask-blueprint", + "tested" + ], + "complexity": "moderate" + }, + { + "id": "function:routes/governance.py:board", + "type": "function", + "name": "board", + "filePath": "routes/governance.py", + "lineRange": [ + 17, + 26 + ], + "summary": "Renders the 4-stage governance Kanban board with BCs grouped by their current status.", + "tags": [ + "api-handler", + "governance" + ], + "complexity": "simple" + }, + { + "id": "function:routes/governance.py:export", + "type": "function", + "name": "export", + "filePath": "routes/governance.py", + "lineRange": [ + 30, + 45 + ], + "summary": "Exports the governance workbook for published BCs, sanitizing the requested filename before responding.", + "tags": [ + "api-handler", + "export", + "governance" + ], + "complexity": "moderate" + }, + { + "id": "function:routes/governance.py:advance", + "type": "function", + "name": "advance", + "filePath": "routes/governance.py", + "lineRange": [ + 49, + 58 + ], + "summary": "Advances a BC to the next governance stage and responds with JSON or a redirect depending on the request type.", + "tags": [ + "api-handler", + "governance", + "workflow" + ], + "complexity": "simple" + }, + { + "id": "function:routes/governance.py:reject_bc", + "type": "function", + "name": "reject_bc", + "filePath": "routes/governance.py", + "lineRange": [ + 62, + 68 + ], + "summary": "Rejects a BC back through the governance workflow with an optional comment, supporting JSON or redirect responses.", + "tags": [ + "api-handler", + "governance", + "workflow" + ], + "complexity": "simple" + }, + { + "id": "file:routes/ncit.py", + "type": "file", + "name": "ncit.py", + "filePath": "routes/ncit.py", + "summary": "Flask blueprint for NCI Thesaurus search and mapping to Biomedical Concepts, including AJAX search and single-concept detail lookups.", + "tags": [ + "api-handler", + "ncit", + "flask-blueprint", + "tested" + ], + "complexity": "moderate" + }, + { + "id": "function:routes/ncit.py:index", + "type": "function", + "name": "index", + "filePath": "routes/ncit.py", + "lineRange": [ + 16, + 17 + ], + "summary": "Redirects the NCIt blueprint's index route to the mapping view.", + "tags": [ + "api-handler", + "routing" + ], + "complexity": "simple" + }, + { + "id": "function:routes/ncit.py:mapping", + "type": "function", + "name": "mapping", + "filePath": "routes/ncit.py", + "lineRange": [ + 21, + 30 + ], + "summary": "Lists up to 50 Biomedical Concepts that still lack an NCIt code, for manual mapping.", + "tags": [ + "api-handler", + "ncit" + ], + "complexity": "simple" + }, + { + "id": "function:routes/ncit.py:search_ncit", + "type": "function", + "name": "search_ncit", + "filePath": "routes/ncit.py", + "lineRange": [ + 34, + 61 + ], + "summary": "Searches NCIt concepts by term via the EVS API and returns results as JSON or a rendered partial, depending on the caller.", + "tags": [ + "api-handler", + "ncit", + "ajax" + ], + "complexity": "moderate" + }, + { + "id": "function:routes/ncit.py:concept_detail", + "type": "function", + "name": "concept_detail", + "filePath": "routes/ncit.py", + "lineRange": [ + 65, + 73 + ], + "summary": "Fetches a single NCIt concept's detail metadata by code and returns it as JSON, aborting with 404 on lookup error.", + "tags": [ + "api-handler", + "ncit" + ], + "complexity": "simple" + }, + { + "id": "function:routes/ncit.py:resolve", + "type": "function", + "name": "resolve", + "filePath": "routes/ncit.py", + "lineRange": [ + 77, + 83 + ], + "summary": "Applies a chosen NCIt code to a Biomedical Concept via bc_service and redirects back to the BC detail page.", + "tags": [ + "api-handler", + "ncit" + ], + "complexity": "simple" + }, + { + "id": "file:services/__init__.py", + "type": "file", + "name": "__init__.py", + "filePath": "services/__init__.py", + "summary": "Empty package marker enabling the services directory to be imported as a Python package.", + "tags": [ + "package-init" + ], + "complexity": "simple" + }, + { + "id": "file:services/audit.py", + "type": "file", + "name": "audit.py", + "filePath": "services/audit.py", + "summary": "Provides the log_change helper used across services and routes to persist immutable AuditLog rows for every entity mutation.", + "tags": [ + "audit", + "utility", + "service" + ], + "complexity": "simple" + }, + { + "id": "function:services/audit.py:log_change", + "type": "function", + "name": "log_change", + "filePath": "services/audit.py", + "lineRange": [ + 16, + 32 + ], + "summary": "Creates and persists an AuditLog row capturing entity type/id, action, actor, and before/after JSON snapshots.", + "tags": [ + "audit", + "database", + "logging" + ], + "complexity": "simple" + }, + { + "id": "file:services/bc_service.py", + "type": "file", + "name": "bc_service.py", + "filePath": "services/bc_service.py", + "summary": "Core business logic for creating, updating, and managing Biomedical Concepts and their Data Element Concepts, including NCIt remapping/merging and review submission.", + "tags": [ + "service", + "business-logic", + "crud" + ], + "complexity": "complex" + }, + { + "id": "function:services/bc_service.py:_get_bc_or_raise", + "type": "function", + "name": "_get_bc_or_raise", + "filePath": "services/bc_service.py", + "lineRange": [ + 28, + 32 + ], + "summary": "Looks up a BiomedicalConcept by its bc_id, raising NotFoundError when it does not exist.", + "tags": [ + "utility", + "data-access" + ], + "complexity": "simple" + }, + { + "id": "function:services/bc_service.py:apply_bc_fields", + "type": "function", + "name": "apply_bc_fields", + "filePath": "services/bc_service.py", + "lineRange": [ + 35, + 63 + ], + "summary": "Applies submitted field data onto a BiomedicalConcept instance, handling boolean coercion and LOINC/NCIt/parent-BC field normalization.", + "tags": [ + "business-logic", + "data-model" + ], + "complexity": "moderate" + }, + { + "id": "function:services/bc_service.py:create_bc", + "type": "function", + "name": "create_bc", + "filePath": "services/bc_service.py", + "lineRange": [ + 66, + 85 + ], + "summary": "Creates a new BiomedicalConcept from submitted data, validates uniqueness of the bc_id, persists it, and logs an audit entry.", + "tags": [ + "service", + "crud", + "audit" + ], + "complexity": "moderate" + }, + { + "id": "function:services/bc_service.py:update_bc", + "type": "function", + "name": "update_bc", + "filePath": "services/bc_service.py", + "lineRange": [ + 88, + 95 + ], + "summary": "Updates an existing BiomedicalConcept's fields and records the before/after change in the audit log.", + "tags": [ + "service", + "crud", + "audit" + ], + "complexity": "simple" + }, + { + "id": "function:services/bc_service.py:save_decs", + "type": "function", + "name": "save_decs", + "filePath": "services/bc_service.py", + "lineRange": [ + 98, + 121 + ], + "summary": "Replaces a BC's Data Element Concept rows, deleting existing DECs and inserting the submitted set with sort order.", + "tags": [ + "service", + "crud" + ], + "complexity": "moderate" + }, + { + "id": "function:services/bc_service.py:map_ncit_to_bc", + "type": "function", + "name": "map_ncit_to_bc", + "filePath": "services/bc_service.py", + "lineRange": [ + 124, + 148 + ], + "summary": "Remaps a BC's NCIt code, and when the target NCIt-derived id already exists, merges the current BC's DECs, specializations, and governance history into it.", + "tags": [ + "service", + "ncit", + "merge" + ], + "complexity": "complex" + }, + { + "id": "function:services/bc_service.py:submit_bc_for_review", + "type": "function", + "name": "submit_bc_for_review", + "filePath": "services/bc_service.py", + "lineRange": [ + 151, + 159 + ], + "summary": "Transitions a BC's status to under-review and logs the change to the audit trail.", + "tags": [ + "service", + "governance", + "audit" + ], + "complexity": "simple" + }, + { + "id": "file:services/export.py", + "type": "file", + "name": "export.py", + "filePath": "services/export.py", + "summary": "Exporters that render Biomedical Concept collections to JSON, styled XLSX, governance XLSX, and CDISC ODM-XML formats.", + "tags": [ + "export", + "serialization", + "service", + "tested" + ], + "complexity": "complex" + }, + { + "id": "function:services/export.py:export_json", + "type": "function", + "name": "export_json", + "filePath": "services/export.py", + "lineRange": [ + 37, + 39 + ], + "summary": "Serializes a list of BC dictionaries to a pretty-printed JSON string.", + "tags": [ + "export", + "serialization" + ], + "complexity": "simple" + }, + { + "id": "function:services/export.py:export_xlsx", + "type": "function", + "name": "export_xlsx", + "filePath": "services/export.py", + "lineRange": [ + 42, + 67 + ], + "summary": "Builds a styled openpyxl workbook of BC export fields with a bold header row, falling back to LOINC code when no NCIt code is present.", + "tags": [ + "export", + "xlsx" + ], + "complexity": "moderate" + }, + { + "id": "function:services/export.py:export_governance_xlsx", + "type": "function", + "name": "export_governance_xlsx", + "filePath": "services/export.py", + "lineRange": [ + 88, + 143 + ], + "summary": "Builds a governance-focused workbook containing a BC summary row followed by its Data Element Concept rows for CDISC governance review.", + "tags": [ + "export", + "xlsx", + "governance" + ], + "complexity": "complex" + }, + { + "id": "function:services/export.py:export_odm_xml", + "type": "function", + "name": "export_odm_xml", + "filePath": "services/export.py", + "lineRange": [ + 146, + 185 + ], + "summary": "Builds a CDISC ODM-XML document with ItemDefs and NCIt Aliases from a list of Biomedical Concepts.", + "tags": [ + "export", + "xml", + "odm" + ], + "complexity": "complex" + }, + { + "id": "file:services/governance_service.py", + "type": "file", + "name": "governance_service.py", + "filePath": "services/governance_service.py", + "summary": "Implements governance stage transitions (advance/reject) for Biomedical Concepts, creating GovernanceRecord history rows and audit log entries.", + "tags": [ + "service", + "governance", + "workflow" + ], + "complexity": "moderate" + }, + { + "id": "function:services/governance_service.py:advance_governance", + "type": "function", + "name": "advance_governance", + "filePath": "services/governance_service.py", + "lineRange": [ + 16, + 40 + ], + "summary": "Moves a BC forward through the STATUS_ORDER stage sequence, recording a GovernanceRecord and audit log entry for the transition.", + "tags": [ + "service", + "governance", + "workflow" + ], + "complexity": "moderate" + }, + { + "id": "function:services/governance_service.py:reject_bc", + "type": "function", + "name": "reject_bc", + "filePath": "services/governance_service.py", + "lineRange": [ + 43, + 60 + ], + "summary": "Reverts a BC's governance stage on rejection, recording a GovernanceRecord and audit log entry with the rejection comment.", + "tags": [ + "service", + "governance", + "workflow" + ], + "complexity": "moderate" + }, + { + "id": "file:services/ncit_api.py", + "type": "file", + "name": "ncit_api.py", + "filePath": "services/ncit_api.py", + "summary": "NCI Thesaurus EVS REST API client providing concept search and detailed concept retrieval (parents, semantic type, synonyms, definitions) with caching.", + "tags": [ + "api-client", + "ncit", + "integration", + "service", + "tested" + ], + "complexity": "moderate" + }, + { + "id": "function:services/ncit_api.py:_pick_definition", + "type": "function", + "name": "_pick_definition", + "filePath": "services/ncit_api.py", + "lineRange": [ + 14, + 21 + ], + "summary": "Selects the NCI-source or, failing that, first available definition from a concept's list of definitions.", + "tags": [ + "utility", + "data-transform" + ], + "complexity": "simple" + }, + { + "id": "class:services/ncit_api.py:NCItApiClient", + "type": "class", + "name": "NCItApiClient", + "filePath": "services/ncit_api.py", + "lineRange": [ + 24, + 85 + ], + "summary": "HTTP client wrapping the NCI EVS REST API, exposing concept search and cached, extended concept-detail lookups (parents, semantic type, synonyms, definitions) used throughout the NCIt mapping workflow.", + "tags": [ + "api-client", + "integration", + "caching" + ], + "complexity": "moderate" + }, + { + "id": "file:tests/test_export_service.py", + "type": "file", + "name": "test_export_service.py", + "filePath": "tests/test_export_service.py", + "summary": "Unit tests covering JSON, XLSX, governance XLSX, and ODM-XML export functions in services/export.py, verifying headers, blanking rules, and document structure.", + "tags": [ + "test", + "export", + "unit-test" + ], + "complexity": "complex" + }, + { + "id": "class:tests/test_export_service.py:TestExportJson", + "type": "class", + "name": "TestExportJson", + "filePath": "tests/test_export_service.py", + "lineRange": [ + 55, + 70 + ], + "summary": "Tests export_json's round-trip serialization, empty-list handling, and coercion of non-JSON-serializable values.", + "tags": [ + "test", + "export" + ], + "complexity": "simple" + }, + { + "id": "class:tests/test_export_service.py:TestExportXlsx", + "type": "class", + "name": "TestExportXlsx", + "filePath": "tests/test_export_service.py", + "lineRange": [ + 73, + 104 + ], + "summary": "Tests export_xlsx's header and row output, empty-list behavior, blank rendering of missing fields, and the LOINC-code fallback in the Code column.", + "tags": [ + "test", + "export", + "xlsx" + ], + "complexity": "moderate" + }, + { + "id": "class:tests/test_export_service.py:TestExportGovernanceXlsx", + "type": "class", + "name": "TestExportGovernanceXlsx", + "filePath": "tests/test_export_service.py", + "lineRange": [ + 107, + 180 + ], + "summary": "Tests export_governance_xlsx's BC-row-then-DEC-rows layout and the blanking of system fields for BCs without a LOINC code.", + "tags": [ + "test", + "export", + "governance" + ], + "complexity": "moderate" + }, + { + "id": "class:tests/test_export_service.py:TestExportOdmXml", + "type": "class", + "name": "TestExportOdmXml", + "filePath": "tests/test_export_service.py", + "lineRange": [ + 183, + 207 + ], + "summary": "Tests export_odm_xml's document structure, conditional inclusion of the Alias element only when an NCIt code is present, and empty-list handling.", + "tags": [ + "test", + "export", + "xml" + ], + "complexity": "simple" + }, + { + "id": "file:tests/test_ncit.py", + "type": "file", + "name": "test_ncit.py", + "filePath": "tests/test_ncit.py", + "summary": "Unit tests for NCItApiClient.get_concept's extended metadata extraction and the /ncit concept-detail route's JSON responses and error handling.", + "tags": [ + "test", + "ncit", + "unit-test" + ], + "complexity": "moderate" + }, + { + "id": "class:tests/test_ncit.py:TestNcitGetConceptExtended", + "type": "class", + "name": "TestNcitGetConceptExtended", + "filePath": "tests/test_ncit.py", + "lineRange": [ + 43, + 112 + ], + "summary": "Tests NCItApiClient.get_concept's extraction of parents, semantic type, source synonyms, and full definitions list, plus empty and error-path handling.", + "tags": [ + "test", + "ncit", + "api-client" + ], + "complexity": "moderate" + }, + { + "id": "class:tests/test_ncit.py:TestNcitConceptRoute", + "type": "class", + "name": "TestNcitConceptRoute", + "filePath": "tests/test_ncit.py", + "lineRange": [ + 131, + 156 + ], + "summary": "Tests the /ncit concept-detail route's JSON response shape, correct pass-through of the requested code, and 404 response when the service errors.", + "tags": [ + "test", + "ncit", + "api-handler" + ], + "complexity": "simple" + }, + { + "id": "file:routes/dashboard.py", + "type": "file", + "name": "dashboard.py", + "filePath": "routes/dashboard.py", + "summary": "Flask blueprint for the dashboard home page; fetches CDISC Library BC and specialization counts concurrently via a thread pool and combines them with local KPI stats (BC status counts, recent activity, audit log) for the landing page.", + "tags": [ + "dashboard", + "api-handler", + "kpi", + "route" + ], + "complexity": "moderate" + }, + { + "id": "file:routes/loinc.py", + "type": "file", + "name": "loinc.py", + "filePath": "routes/loinc.py", + "summary": "Flask blueprint exposing a single AJAX search endpoint that proxies term queries to the NLM LOINC API client and returns JSON results.", + "tags": [ + "api-handler", + "loinc", + "search", + "route", + "tested" + ], + "complexity": "simple" + }, + { + "id": "file:routes/specializations.py", + "type": "file", + "name": "specializations.py", + "filePath": "routes/specializations.py", + "summary": "Flask blueprint managing CDISC dataset specializations: lists local and CDISC Library specializations, and supports manual creation, deletion, and auto-generation of variable-level specializations from a BC's data element concepts.", + "tags": [ + "api-handler", + "crud", + "specialization", + "route", + "tested" + ], + "complexity": "complex" + }, + { + "id": "file:services/api_cache.py", + "type": "file", + "name": "api_cache.py", + "filePath": "services/api_cache.py", + "summary": "Shared stale-tolerant in-memory cache helper used by the CDISC Library, NCI EVS, and LOINC API clients; serves fresh data for 5 minutes and falls back to stale data for up to an hour if a refresh fails, so a slow upstream never blanks a page that rendered fine a minute ago.", + "tags": [ + "utility", + "caching", + "resilience", + "service", + "tested" + ], + "complexity": "simple", + "languageNotes": "Stale-while-revalidate cache pattern shared across multiple API client modules via a single module-level dict." + }, + { + "id": "file:services/cdisc_api.py", + "type": "file", + "name": "cdisc_api.py", + "filePath": "services/cdisc_api.py", + "summary": "REST client for the CDISC Library COSMOS API; wraps biomedical concept, specialization, and duplicate-check lookups with a 5-minute in-memory cache and provides a publish_bc method for pushing approved BCs to the Library.", + "tags": [ + "api-client", + "service", + "cdisc-library", + "http", + "tested" + ], + "complexity": "moderate" + }, + { + "id": "file:services/loinc_api.py", + "type": "file", + "name": "loinc_api.py", + "filePath": "services/loinc_api.py", + "summary": "Client for the NLM Clinical Tables LOINC search API; builds the query with a configurable set of extra fields, optionally applies HTTP Basic Auth, and normalizes results through the shared API cache.", + "tags": [ + "api-client", + "service", + "loinc", + "http", + "tested" + ], + "complexity": "moderate" + }, + { + "id": "file:tests/test_cdisc_api_cache.py", + "type": "file", + "name": "test_cdisc_api_cache.py", + "filePath": "tests/test_cdisc_api_cache.py", + "summary": "Unit tests for the shared cache helper and CDISCApiClient cache-key behavior, covering fresh/stale/expired cache states, refresh-failure fallback, and that raw API keys never leak into cache keys.", + "tags": [ + "test", + "caching", + "cdisc-library" + ], + "complexity": "moderate" + }, + { + "id": "file:tests/test_loinc.py", + "type": "file", + "name": "test_loinc.py", + "filePath": "tests/test_loinc.py", + "summary": "Unit tests for LoincApiClient.search normalization, extra-field handling, Basic Auth toggling, and error handling, plus tests for the /loinc/search Flask route's JSON responses.", + "tags": [ + "test", + "loinc", + "route" + ], + "complexity": "complex" + }, + { + "id": "function:routes/dashboard.py:index", + "type": "function", + "name": "index", + "filePath": "routes/dashboard.py", + "lineRange": [ + 17, + 60 + ], + "summary": "Renders the dashboard home page, fetching CDISC Library BC and specialization counts in parallel via a ThreadPoolExecutor and combining them with local BC status counts, recent-activity queries, and audit log entries.", + "tags": [ + "route", + "dashboard", + "concurrency" + ], + "complexity": "moderate" + }, + { + "id": "function:routes/loinc.py:search", + "type": "function", + "name": "search", + "filePath": "routes/loinc.py", + "lineRange": [ + 13, + 23 + ], + "summary": "Handles GET /loinc/search: reads the search term, detects AJAX/JSON requests, and returns LOINC search results as JSON via LoincApiClient.", + "tags": [ + "route", + "api-handler", + "loinc" + ], + "complexity": "simple" + }, + { + "id": "function:routes/specializations.py:_get_bc_options", + "type": "function", + "name": "_get_bc_options", + "filePath": "routes/specializations.py", + "lineRange": [ + 16, + 25 + ], + "summary": "Builds the dropdown option list of approved Biomedical Concepts for specialization forms, merging CDISC Library BC links with locally curated BiomedicalConcept records.", + "tags": [ + "helper", + "dropdown", + "bc" + ], + "complexity": "simple" + }, + { + "id": "function:routes/specializations.py:index", + "type": "function", + "name": "index", + "filePath": "routes/specializations.py", + "lineRange": [ + 29, + 38 + ], + "summary": "Lists all locally stored dataset specializations alongside available BC options for the specializations index page.", + "tags": [ + "route", + "list" + ], + "complexity": "simple" + }, + { + "id": "function:routes/specializations.py:library_detail", + "type": "function", + "name": "library_detail", + "filePath": "routes/specializations.py", + "lineRange": [ + 42, + 52 + ], + "summary": "Fetches and renders a single dataset specialization directly from the CDISC Library by its specialization path, redirecting with a flash message if the lookup fails.", + "tags": [ + "route", + "cdisc-library" + ], + "complexity": "simple" + }, + { + "id": "function:routes/specializations.py:detail", + "type": "function", + "name": "detail", + "filePath": "routes/specializations.py", + "lineRange": [ + 56, + 67 + ], + "summary": "Renders the detail page for a locally stored dataset specialization identified by its VLM group ID.", + "tags": [ + "route", + "detail" + ], + "complexity": "simple" + }, + { + "id": "function:routes/specializations.py:_variables_from_form", + "type": "function", + "name": "_variables_from_form", + "filePath": "routes/specializations.py", + "lineRange": [ + 70, + 87 + ], + "summary": "Parses indexed variables[i][name]/variables[i][label] form fields into a list of variable dicts for specialization create/generate forms.", + "tags": [ + "helper", + "form-parsing" + ], + "complexity": "simple" + }, + { + "id": "function:routes/specializations.py:create", + "type": "function", + "name": "create", + "filePath": "routes/specializations.py", + "lineRange": [ + 91, + 124 + ], + "summary": "Handles manual creation of a dataset specialization from form input, either updating an existing record or inserting a new DatasetSpecialization, and logs the change via the audit service.", + "tags": [ + "route", + "crud", + "audit" + ], + "complexity": "moderate" + }, + { + "id": "function:routes/specializations.py:delete", + "type": "function", + "name": "delete", + "filePath": "routes/specializations.py", + "lineRange": [ + 128, + 134 + ], + "summary": "Deletes a dataset specialization by VLM group ID and records the deletion in the audit log.", + "tags": [ + "route", + "crud", + "audit" + ], + "complexity": "simple" + }, + { + "id": "function:routes/specializations.py:generate_from_dec", + "type": "function", + "name": "generate_from_dec", + "filePath": "routes/specializations.py", + "lineRange": [ + 138, + 146 + ], + "summary": "AJAX endpoint that checks whether a given BC has any Data Element Concepts available to auto-generate a specialization from.", + "tags": [ + "route", + "ajax", + "validation" + ], + "complexity": "simple" + }, + { + "id": "function:routes/specializations.py:generate", + "type": "function", + "name": "generate", + "filePath": "routes/specializations.py", + "lineRange": [ + 150, + 172 + ], + "summary": "Auto-generates a dataset specialization's variables from a BC's Data Element Concepts and saves it as a new DatasetSpecialization, logging the change via the audit service.", + "tags": [ + "route", + "generation", + "audit" + ], + "complexity": "moderate" + }, + { + "id": "function:services/api_cache.py:cached", + "type": "function", + "name": "cached", + "filePath": "services/api_cache.py", + "lineRange": [ + 22, + 42 + ], + "summary": "Core stale-while-revalidate cache primitive: returns a fresh cached value if within CACHE_TTL, otherwise attempts to refetch via the given callable, falling back to a stale value (up to CACHE_STALE_TTL) if the refresh raises.", + "tags": [ + "caching", + "resilience", + "utility" + ], + "complexity": "moderate", + "languageNotes": "The broad except Exception is intentional \u2014 it's the resilience seam preventing upstream failures from blanking a page that previously rendered fine." + }, + { + "id": "function:services/cdisc_api.py:_config_value", + "type": "function", + "name": "_config_value", + "filePath": "services/cdisc_api.py", + "lineRange": [ + 19, + 26 + ], + "summary": "Resolves a configuration value by checking Flask's current_app.config first, then falling back to an environment variable, so config can be read even outside an app context.", + "tags": [ + "helper", + "config" + ], + "complexity": "simple" + }, + { + "id": "class:services/cdisc_api.py:CDISCApiClient", + "type": "class", + "name": "CDISCApiClient", + "filePath": "services/cdisc_api.py", + "lineRange": [ + 29, + 127 + ], + "summary": "REST client wrapping the CDISC Library COSMOS v2 API: fetches biomedical concepts, individual BC/specialization detail, and dataset specializations (cached via the shared api_cache helper), checks for duplicate BC titles, and publishes approved BCs back to the Library.", + "tags": [ + "api-client", + "cdisc-library", + "service", + "http" + ], + "complexity": "complex", + "languageNotes": "Cache keys are derived from a SHA-256 hash of the API key material rather than the raw key, so secrets never end up as cache dictionary keys in memory dumps or logs." + }, + { + "id": "class:services/loinc_api.py:LoincApiClient", + "type": "class", + "name": "LoincApiClient", + "filePath": "services/loinc_api.py", + "lineRange": [ + 18, + 62 + ], + "summary": "Client for the NLM Clinical Tables LOINC /search endpoint; optionally attaches HTTP Basic Auth from LOINC_USER/LOINC_PASSWORD env vars, requests a configurable set of extra fields, normalizes the paginated response into a flat list, and caches results through the shared api_cache helper.", + "tags": [ + "api-client", + "loinc", + "service", + "http" + ], + "complexity": "moderate" + }, + { + "id": "class:tests/test_cdisc_api_cache.py:TestCachedHelper", + "type": "class", + "name": "TestCachedHelper", + "filePath": "tests/test_cdisc_api_cache.py", + "lineRange": [ + 11, + 64 + ], + "summary": "Tests the cached() helper directly: first-call fetch-and-store, serving fresh entries without refetching, refreshing stale entries, falling back to stale data when refresh fails, and raising when there is no fallback available.", + "tags": [ + "test", + "caching" + ], + "complexity": "moderate" + }, + { + "id": "class:tests/test_cdisc_api_cache.py:TestClientCacheKeys", + "type": "class", + "name": "TestClientCacheKeys", + "filePath": "tests/test_cdisc_api_cache.py", + "lineRange": [ + 67, + 120 + ], + "summary": "Tests that CDISCApiClient's cache keys are derived from a hash rather than the raw API key, that errors from get_biomedical_concepts are encoded into the cached result rather than raised, and that a stale error result is replaced once the cache TTL expires.", + "tags": [ + "test", + "caching", + "security" + ], + "complexity": "moderate" + }, + { + "id": "class:tests/test_loinc.py:TestLoincApiClientSearch", + "type": "class", + "name": "TestLoincApiClientSearch", + "filePath": "tests/test_loinc.py", + "lineRange": [ + 44, + 168 + ], + "summary": "Tests LoincApiClient.search's response normalization, presence of all requested extra fields, Basic Auth header toggling based on environment variables, and graceful handling of empty results, missing codes arrays, and network errors.", + "tags": [ + "test", + "loinc", + "api-client" + ], + "complexity": "complex" + }, + { + "id": "class:tests/test_loinc.py:TestLoincSearchRoute", + "type": "class", + "name": "TestLoincSearchRoute", + "filePath": "tests/test_loinc.py", + "lineRange": [ + 176, + 205 + ], + "summary": "Tests the /loinc/search Flask route: JSON response for AJAX requests, empty-term short-circuiting, that the search term is passed through to the client, and that the format=json query param triggers a JSON response.", + "tags": [ + "test", + "route", + "loinc" + ], + "complexity": "simple" + }, + { + "id": "file:app.py", + "type": "file", + "name": "app.py", + "filePath": "app.py", + "summary": "Flask application factory that configures logging, initializes the shared database and migration extensions, registers all eight feature blueprints, and doubles as the dev-server entry point via `python app.py`.", + "tags": [ + "entry-point", + "flask", + "application-factory", + "blueprint-registration" + ], + "complexity": "moderate" + }, + { + "id": "file:config.py", + "type": "file", + "name": "config.py", + "filePath": "config.py", + "summary": "Centralizes all environment-driven configuration for the Flask app, including the secret key, database URI, CDISC/NCIt API base URLs and credentials, upload size limit, and dev server port.", + "tags": [ + "configuration", + "environment-variables", + "flask", + "settings" + ], + "complexity": "simple" + }, + { + "id": "file:db_bootstrap.py", + "type": "file", + "name": "db_bootstrap.py", + "filePath": "db_bootstrap.py", + "summary": "Brings the app's database to the current Alembic migration head at startup, distinguishing fresh, legacy create_all(), and pre-squash-stamped databases, and raising if the schema predates the squashed baseline.", + "tags": [ + "database-migration", + "alembic", + "bootstrap", + "startup", + "tested" + ], + "complexity": "moderate", + "languageNotes": "Encodes a one-time schema-squash migration strategy: legacy databases are schema-validated then stamped rather than replayed through the old revision chain." + }, + { + "id": "file:extensions.py", + "type": "file", + "name": "extensions.py", + "filePath": "extensions.py", + "summary": "Defines the shared SQLAlchemy `db` and Flask-Migrate `migrate` instances used across the app to avoid circular imports, and configures SQLite WAL mode with a busy timeout so the Flask app and MCP server can coexist as concurrent writers.", + "tags": [ + "database", + "sqlalchemy", + "shared-extension", + "sqlite" + ], + "complexity": "simple" + }, + { + "id": "file:models/__init__.py", + "type": "file", + "name": "__init__.py", + "filePath": "models/__init__.py", + "summary": "Package marker for the `models` module that imports the shared `db` instance so all SQLAlchemy model classes register against a single metadata registry.", + "tags": [ + "entry-point", + "package-init", + "barrel" + ], + "complexity": "simple" + }, + { + "id": "file:tests/test_dashboard.py", + "type": "file", + "name": "test_dashboard.py", + "filePath": "tests/test_dashboard.py", + "summary": "Unit tests for the dashboard route, verifying it renders correctly with live CDISC/NCIt API data, degrades gracefully on API errors and empty results, reflects local DB stats accurately, and issues both API calls concurrently.", + "tags": [ + "test", + "dashboard", + "mocking", + "concurrency" + ], + "complexity": "moderate" + }, + { + "id": "file:tests/test_db_bootstrap.py", + "type": "file", + "name": "test_db_bootstrap.py", + "filePath": "tests/test_db_bootstrap.py", + "summary": "Unit tests for `ensure_db()` covering a fresh baseline upgrade, stamping of a legacy create_all() database, restamping of a pre-squash-stamped database, raising on an outdated schema, and idempotency across repeated runs.", + "tags": [ + "test", + "database-migration", + "alembic", + "fixtures" + ], + "complexity": "moderate" + }, + { + "id": "function:app.py:_configure_logging", + "type": "function", + "name": "_configure_logging", + "filePath": "app.py", + "lineRange": [ + 10, + 21 + ], + "summary": "Attaches a basic logging handler to the root logger exactly once, deferring to any handler already installed by pytest, gunicorn, or the MCP server.", + "tags": [ + "logging", + "initialization", + "utility" + ], + "complexity": "simple" + }, + { + "id": "function:app.py:create_app", + "type": "function", + "name": "create_app", + "filePath": "app.py", + "lineRange": [ + 24, + 50 + ], + "summary": "Application factory that builds the Flask app, applies configuration, initializes the `db` and `migrate` extensions, and registers all eight route blueprints under their URL prefixes.", + "tags": [ + "entry-point", + "application-factory", + "flask", + "blueprint-registration" + ], + "complexity": "moderate" + }, + { + "id": "class:config.py:Config", + "type": "class", + "name": "Config", + "filePath": "config.py", + "lineRange": [ + 4, + 14 + ], + "summary": "Flask configuration object sourcing the secret key, database URI, CDISC/NCIt API settings, upload size limit, and dev server port from environment variables with sensible defaults.", + "tags": [ + "configuration", + "environment-variables", + "settings" + ], + "complexity": "simple" + }, + { + "id": "function:db_bootstrap.py:ensure_db", + "type": "function", + "name": "ensure_db", + "filePath": "db_bootstrap.py", + "lineRange": [ + 36, + 71 + ], + "summary": "Idempotently brings the configured database to the current Alembic migration head, handling fresh databases, legacy create_all() databases, and pre-squash-stamped databases, and raising RuntimeError if the schema predates the squashed baseline.", + "tags": [ + "database-migration", + "alembic", + "idempotent", + "startup" + ], + "complexity": "moderate" + }, + { + "id": "function:extensions.py:_set_sqlite_pragmas", + "type": "function", + "name": "_set_sqlite_pragmas", + "filePath": "extensions.py", + "lineRange": [ + 13, + 25 + ], + "summary": "SQLAlchemy connect-event listener that enables WAL journal mode and a 15-second busy timeout on every SQLite connection so the Flask app and MCP server can write concurrently without lock errors.", + "tags": [ + "sqlite", + "event-handler", + "concurrency", + "database" + ], + "complexity": "simple" + }, + { + "id": "class:tests/test_dashboard.py:TestDashboardIndex", + "type": "class", + "name": "TestDashboardIndex", + "filePath": "tests/test_dashboard.py", + "lineRange": [ + 24, + 75 + ], + "summary": "Test suite for the dashboard index route, covering successful rendering with live API stats, graceful handling of API errors and empty results, correctness of locally computed DB stats, and concurrent execution of the two upstream API calls.", + "tags": [ + "test", + "dashboard", + "mocking" + ], + "complexity": "moderate" + }, + { + "id": "function:tests/test_db_bootstrap.py:_make_app", + "type": "function", + "name": "_make_app", + "filePath": "tests/test_db_bootstrap.py", + "lineRange": [ + 20, + 30 + ], + "summary": "Test helper that builds a Flask app configured against a temporary SQLite file so each `ensure_db()` test runs against an isolated on-disk database.", + "tags": [ + "test", + "fixture", + "utility" + ], + "complexity": "simple" + }, + { + "id": "class:tests/test_db_bootstrap.py:TestEnsureDb", + "type": "class", + "name": "TestEnsureDb", + "filePath": "tests/test_db_bootstrap.py", + "lineRange": [ + 43, + 87 + ], + "summary": "Test suite for `ensure_db()` verifying correct behavior across a fresh baseline database, a legacy create_all() database, a pre-squash-stamped database, an outdated/unrecognized schema, and repeated (idempotent) invocations.", + "tags": [ + "test", + "database-migration", + "alembic" + ], + "complexity": "moderate" + }, + { + "id": "file:mcp_server/__main__.py", + "type": "file", + "name": "__main__.py", + "filePath": "mcp_server/__main__.py", + "summary": "Module entry point for `python -m mcp_server`; imports the server's main() coroutine runner and invokes it immediately to start the stdio MCP server loop.", + "tags": [ + "entry-point", + "mcp-server", + "bootstrap" + ], + "complexity": "simple" + }, + { + "id": "file:mcp_server/server.py", + "type": "file", + "name": "server.py", + "filePath": "mcp_server/server.py", + "summary": "MCP server exposing the CDISC curation app's data and write operations as 14 stdio tools (8 read, 6 write); each call is dispatched into a Flask app context shared with the web app so both processes hit the same SQLite database and service layer.", + "tags": [ + "entry-point", + "mcp-server", + "api-handler", + "service", + "tested" + ], + "complexity": "complex", + "languageNotes": "Uses lazy, function-local imports throughout (Flask app, models, service clients) so they only resolve once an app context exists, plus a functools.wraps-based `_with_app_context` decorator that pushes a fresh app context per call since handlers run on asyncio executor worker threads." + }, + { + "id": "function:mcp_server/server.py:_with_app_context", + "type": "function", + "name": "_with_app_context", + "filePath": "mcp_server/server.py", + "lineRange": [ + 60, + 72 + ], + "summary": "Decorator that wraps a tool handler so it runs inside a fresh Flask app context, required because handlers execute on the asyncio executor's worker threads rather than the request thread.", + "tags": [ + "decorator", + "middleware", + "mcp-server" + ], + "complexity": "simple" + }, + { + "id": "function:mcp_server/server.py:_dispatch", + "type": "function", + "name": "_dispatch", + "filePath": "mcp_server/server.py", + "lineRange": [ + 292, + 312 + ], + "summary": "Central dispatch table mapping the 14 MCP tool names to their handler functions and raising ValueError for unknown tool names.", + "tags": [ + "dispatcher", + "mcp-server", + "routing" + ], + "complexity": "moderate" + }, + { + "id": "function:mcp_server/server.py:_bc_full_dict", + "type": "function", + "name": "_bc_full_dict", + "filePath": "mcp_server/server.py", + "lineRange": [ + 320, + 332 + ], + "summary": "Builds the full JSON-serializable dict for a BiomedicalConcept, extending its to_dict() with fields (code, source, history_of_change, timestamps) omitted by the base serializer.", + "tags": [ + "serialization", + "data-model", + "utility" + ], + "complexity": "simple" + }, + { + "id": "function:mcp_server/server.py:_list_bcs", + "type": "function", + "name": "_list_bcs", + "filePath": "mcp_server/server.py", + "lineRange": [ + 336, + 356 + ], + "summary": "Implements the list_bcs tool: paginated, filterable search over BiomedicalConcept by free-text query and status.", + "tags": [ + "api-handler", + "query", + "pagination" + ], + "complexity": "moderate" + }, + { + "id": "function:mcp_server/server.py:_get_bc", + "type": "function", + "name": "_get_bc", + "filePath": "mcp_server/server.py", + "lineRange": [ + 360, + 405 + ], + "summary": "Implements the get_bc tool: fetches full BC detail including its DataElementConcepts, dataset specializations, and governance history.", + "tags": [ + "api-handler", + "query", + "data-model" + ], + "complexity": "moderate" + }, + { + "id": "function:mcp_server/server.py:_search_ncit", + "type": "function", + "name": "_search_ncit", + "filePath": "mcp_server/server.py", + "lineRange": [ + 409, + 416 + ], + "summary": "Implements the search_ncit tool, delegating to NCItApiClient.search_concept with a required search term and bounded result count.", + "tags": [ + "api-handler", + "external-api" + ], + "complexity": "simple" + }, + { + "id": "function:mcp_server/server.py:_get_ncit_concept", + "type": "function", + "name": "_get_ncit_concept", + "filePath": "mcp_server/server.py", + "lineRange": [ + 420, + 426 + ], + "summary": "Implements the get_ncit_concept tool, fetching full concept detail from the NCI Thesaurus via NCItApiClient.", + "tags": [ + "api-handler", + "external-api" + ], + "complexity": "simple" + }, + { + "id": "function:mcp_server/server.py:_search_loinc", + "type": "function", + "name": "_search_loinc", + "filePath": "mcp_server/server.py", + "lineRange": [ + 430, + 437 + ], + "summary": "Implements the search_loinc tool, delegating to LoincApiClient.search for LOINC code lookups.", + "tags": [ + "api-handler", + "external-api" + ], + "complexity": "simple" + }, + { + "id": "function:mcp_server/server.py:_search_cdisc_library", + "type": "function", + "name": "_search_cdisc_library", + "filePath": "mcp_server/server.py", + "lineRange": [ + 441, + 450 + ], + "summary": "Implements the search_cdisc_library tool, querying the live CDISC Library for published Biomedical Concepts and filtering results by title.", + "tags": [ + "api-handler", + "external-api" + ], + "complexity": "simple" + }, + { + "id": "function:mcp_server/server.py:_get_library_bc", + "type": "function", + "name": "_get_library_bc", + "filePath": "mcp_server/server.py", + "lineRange": [ + 454, + 460 + ], + "summary": "Implements the get_library_bc tool, fetching one published BC from the CDISC Library by concept id.", + "tags": [ + "api-handler", + "external-api" + ], + "complexity": "simple" + }, + { + "id": "function:mcp_server/server.py:_list_review_queue", + "type": "function", + "name": "_list_review_queue", + "filePath": "mcp_server/server.py", + "lineRange": [ + 464, + 477 + ], + "summary": "Implements the list_review_queue tool: summarizes the governance Kanban board by status counts plus the count of pending ingestion records.", + "tags": [ + "api-handler", + "governance", + "query" + ], + "complexity": "simple" + }, + { + "id": "function:mcp_server/server.py:_create_bc", + "type": "function", + "name": "_create_bc", + "filePath": "mcp_server/server.py", + "lineRange": [ + 486, + 494 + ], + "summary": "Implements the create_bc write tool, delegating to bc_service.create_bc and optionally bc_service.save_decs before returning the full BC detail.", + "tags": [ + "api-handler", + "write-operation", + "service" + ], + "complexity": "simple" + }, + { + "id": "function:mcp_server/server.py:_update_bc", + "type": "function", + "name": "_update_bc", + "filePath": "mcp_server/server.py", + "lineRange": [ + 498, + 507 + ], + "summary": "Implements the update_bc write tool, applying field updates via bc_service.update_bc and returning the refreshed BC dict.", + "tags": [ + "api-handler", + "write-operation", + "service" + ], + "complexity": "simple" + }, + { + "id": "function:mcp_server/server.py:_map_ncit_to_bc", + "type": "function", + "name": "_map_ncit_to_bc", + "filePath": "mcp_server/server.py", + "lineRange": [ + 511, + 519 + ], + "summary": "Implements the map_ncit_to_bc write tool, attaching an NCIt code to a BC via bc_service.map_ncit_to_bc (which also handles IMPORT_ id promotion).", + "tags": [ + "api-handler", + "write-operation", + "service" + ], + "complexity": "simple" + }, + { + "id": "function:mcp_server/server.py:_submit_bc_for_review", + "type": "function", + "name": "_submit_bc_for_review", + "filePath": "mcp_server/server.py", + "lineRange": [ + 523, + 531 + ], + "summary": "Implements the submit_bc_for_review write tool, moving a BC from provisional into SME review via bc_service.submit_bc_for_review.", + "tags": [ + "api-handler", + "write-operation", + "governance" + ], + "complexity": "simple" + }, + { + "id": "function:mcp_server/server.py:_advance_governance", + "type": "function", + "name": "_advance_governance", + "filePath": "mcp_server/server.py", + "lineRange": [ + 535, + 542 + ], + "summary": "Implements the advance_governance write tool, advancing a BC's governance stage via governance_service.advance_governance.", + "tags": [ + "api-handler", + "write-operation", + "governance" + ], + "complexity": "simple" + }, + { + "id": "function:mcp_server/server.py:_reject_bc", + "type": "function", + "name": "_reject_bc", + "filePath": "mcp_server/server.py", + "lineRange": [ + 546, + 553 + ], + "summary": "Implements the reject_bc write tool, returning a BC to provisional status via governance_service.reject_bc.", + "tags": [ + "api-handler", + "write-operation", + "governance" + ], + "complexity": "simple" + }, + { + "id": "function:mcp_server/server.py:main", + "type": "function", + "name": "main", + "filePath": "mcp_server/server.py", + "lineRange": [ + 561, + 563 + ], + "summary": "Async entry point that configures logging and runs the stdio MCP server loop; invoked by `python -m mcp_server`.", + "tags": [ + "entry-point", + "bootstrap", + "mcp-server" + ], + "complexity": "simple" + }, + { + "id": "function:mcp_server/server.py:_run", + "type": "function", + "name": "_run", + "filePath": "mcp_server/server.py", + "lineRange": [ + 566, + 568 + ], + "summary": "Opens the stdio transport streams and runs the MCP Server with its initialization options.", + "tags": [ + "bootstrap", + "mcp-server" + ], + "complexity": "simple" + }, + { + "id": "file:models/specialization.py", + "type": "file", + "name": "specialization.py", + "filePath": "models/specialization.py", + "summary": "SQLAlchemy model for a dataset specialization (SDTM/CDASH variable-level metadata group) tied to a Biomedical Concept, storing its variable list as a JSON-serialized text column.", + "tags": [ + "data-model", + "orm", + "database" + ], + "complexity": "simple" + }, + { + "id": "class:models/specialization.py:DatasetSpecialization", + "type": "class", + "name": "DatasetSpecialization", + "filePath": "models/specialization.py", + "lineRange": [ + 6, + 30 + ], + "summary": "ORM model mapping the dataset_specializations table; exposes a `variables` property/setter that transparently (de)serializes a JSON text column, plus a to_dict() used by routes and the MCP server.", + "tags": [ + "data-model", + "orm", + "serialization" + ], + "complexity": "simple" + }, + { + "id": "file:tests/test_mcp_server.py", + "type": "file", + "name": "test_mcp_server.py", + "filePath": "tests/test_mcp_server.py", + "summary": "Unit tests for the MCP server's `_dispatch` routing and all 14 tool handlers, exercising them directly against a shared in-memory test Flask app (no stdio transport) and asserting on returned data plus AuditLog/GovernanceRecord side effects.", + "tags": [ + "test", + "mcp-server", + "unit-test" + ], + "complexity": "complex" + }, + { + "id": "class:tests/test_mcp_server.py:TestDispatch", + "type": "class", + "name": "TestDispatch", + "filePath": "tests/test_mcp_server.py", + "lineRange": [ + 32, + 54 + ], + "summary": "Tests the _dispatch routing function: raises for unknown tool names and asserts every tool declared in _TOOLS is actually present in the dispatch handler map.", + "tags": [ + "test", + "mcp-server", + "dispatcher" + ], + "complexity": "simple" + }, + { + "id": "class:tests/test_mcp_server.py:TestListBcs", + "type": "class", + "name": "TestListBcs", + "filePath": "tests/test_mcp_server.py", + "lineRange": [ + 57, + 86 + ], + "summary": "Tests the list_bcs tool against an empty DB, filtering by query/status, and page/per_page pagination behavior.", + "tags": [ + "test", + "mcp-server", + "pagination" + ], + "complexity": "simple" + }, + { + "id": "class:tests/test_mcp_server.py:TestGetBc", + "type": "class", + "name": "TestGetBc", + "filePath": "tests/test_mcp_server.py", + "lineRange": [ + 89, + 109 + ], + "summary": "Tests the get_bc tool: requires a bc_id, raises on missing BC, and returns full detail including DECs, specializations, and governance records.", + "tags": [ + "test", + "mcp-server", + "data-model" + ], + "complexity": "simple" + }, + { + "id": "class:tests/test_mcp_server.py:TestExternalApiTools", + "type": "class", + "name": "TestExternalApiTools", + "filePath": "tests/test_mcp_server.py", + "lineRange": [ + 112, + 154 + ], + "summary": "Tests the external-API-backed tools (search_ncit, get_ncit_concept, search_loinc, search_cdisc_library, get_library_bc), mocking the underlying NCIt/LOINC/CDISC Library clients and asserting call arguments, filtering, and error propagation.", + "tags": [ + "test", + "external-api", + "mocking" + ], + "complexity": "moderate" + }, + { + "id": "class:tests/test_mcp_server.py:TestCreateBc", + "type": "class", + "name": "TestCreateBc", + "filePath": "tests/test_mcp_server.py", + "lineRange": [ + 162, + 188 + ], + "summary": "Tests the create_bc write tool: creates a BC with DECs and an audit trail, rejects a duplicate bc_id, and rejects a missing bc_id.", + "tags": [ + "test", + "write-operation", + "audit" + ], + "complexity": "simple" + }, + { + "id": "class:tests/test_mcp_server.py:TestUpdateBc", + "type": "class", + "name": "TestUpdateBc", + "filePath": "tests/test_mcp_server.py", + "lineRange": [ + 191, + 203 + ], + "summary": "Tests the update_bc write tool: verifies before/after audit logging on update and that updating a missing BC raises.", + "tags": [ + "test", + "write-operation", + "audit" + ], + "complexity": "simple" + }, + { + "id": "class:tests/test_mcp_server.py:TestMapNcit", + "type": "class", + "name": "TestMapNcit", + "filePath": "tests/test_mcp_server.py", + "lineRange": [ + 206, + 266 + ], + "summary": "Tests the map_ncit_to_bc write tool, covering audit logging, IMPORT_ id promotion and its cascading repoint of dependent DECs/specializations/governance records and child BCs, empty-code validation, and collision handling.", + "tags": [ + "test", + "write-operation", + "ncit" + ], + "complexity": "complex" + }, + { + "id": "class:tests/test_mcp_server.py:TestGovernanceWrites", + "type": "class", + "name": "TestGovernanceWrites", + "filePath": "tests/test_mcp_server.py", + "lineRange": [ + 269, + 300 + ], + "summary": "Tests the submit_bc_for_review, advance_governance, and reject_bc write tools end-to-end, asserting GovernanceRecord history and audit trail entries.", + "tags": [ + "test", + "governance", + "write-operation" + ], + "complexity": "moderate" + }, + { + "id": "class:tests/test_mcp_server.py:TestReviewQueue", + "type": "class", + "name": "TestReviewQueue", + "filePath": "tests/test_mcp_server.py", + "lineRange": [ + 303, + 315 + ], + "summary": "Tests the list_review_queue tool's governance-status summary counts and pending ingestion count.", + "tags": [ + "test", + "governance", + "query" + ], + "complexity": "simple" + }, + { + "id": "file:tests/test_specializations_routes.py", + "type": "file", + "name": "test_specializations_routes.py", + "filePath": "tests/test_specializations_routes.py", + "summary": "Route-level tests for the dataset specializations blueprint covering listing, creating, editing/upserting, deleting, detail views, and generating variables from DECs, including audit log verification and CDISC Library error handling.", + "tags": [ + "test", + "route-test", + "unit-test" + ], + "complexity": "complex" + }, + { + "id": "class:tests/test_specializations_routes.py:TestIndex", + "type": "class", + "name": "TestIndex", + "filePath": "tests/test_specializations_routes.py", + "lineRange": [ + 24, + 40 + ], + "summary": "Tests the specializations index route: lists persisted specializations and confirms the page still renders when the CDISC Library client raises an error.", + "tags": [ + "test", + "route-test", + "error-handling" + ], + "complexity": "simple" + }, + { + "id": "class:tests/test_specializations_routes.py:TestCreate", + "type": "class", + "name": "TestCreate", + "filePath": "tests/test_specializations_routes.py", + "lineRange": [ + 43, + 97 + ], + "summary": "Tests creating a dataset specialization via the route: persists the spec, requires bc_id/vlm_group_id, writes an audit log entry, and persists variable rows submitted via the form.", + "tags": [ + "test", + "route-test", + "audit" + ], + "complexity": "moderate" + }, + { + "id": "class:tests/test_specializations_routes.py:TestEditUpsert", + "type": "class", + "name": "TestEditUpsert", + "filePath": "tests/test_specializations_routes.py", + "lineRange": [ + 100, + 141 + ], + "summary": "Tests the edit route's upsert behavior: posting an existing vlm_group_id updates the record instead of duplicating it, and the update writes an audit log entry.", + "tags": [ + "test", + "route-test", + "upsert" + ], + "complexity": "simple" + }, + { + "id": "class:tests/test_specializations_routes.py:TestDelete", + "type": "class", + "name": "TestDelete", + "filePath": "tests/test_specializations_routes.py", + "lineRange": [ + 144, + 172 + ], + "summary": "Tests the delete route: removes the specialization, writes an audit log entry, and returns 404 for an unknown vlm_group_id.", + "tags": [ + "test", + "route-test", + "audit" + ], + "complexity": "simple" + }, + { + "id": "class:tests/test_specializations_routes.py:TestDetail", + "type": "class", + "name": "TestDetail", + "filePath": "tests/test_specializations_routes.py", + "lineRange": [ + 175, + 189 + ], + "summary": "Tests the specialization detail view renders for a known record and returns 404 for an unknown one.", + "tags": [ + "test", + "route-test" + ], + "complexity": "simple" + }, + { + "id": "class:tests/test_specializations_routes.py:TestLibraryDetail", + "type": "class", + "name": "TestLibraryDetail", + "filePath": "tests/test_specializations_routes.py", + "lineRange": [ + 192, + 204 + ], + "summary": "Tests the library-detail view: renders a published CDISC Library specialization and redirects to the dashboard when the Library client errors.", + "tags": [ + "test", + "route-test", + "error-handling" + ], + "complexity": "simple" + }, + { + "id": "class:tests/test_specializations_routes.py:TestGenerate", + "type": "class", + "name": "TestGenerate", + "filePath": "tests/test_specializations_routes.py", + "lineRange": [ + 207, + 245 + ], + "summary": "Tests generating a dataset specialization from a BC's DECs: builds variables from DECs, rejects duplicates, 404s for an unknown BC, and writes an audit log entry.", + "tags": [ + "test", + "route-test", + "generation" + ], + "complexity": "moderate" + }, + { + "id": "class:tests/test_specializations_routes.py:TestGenerateFromDec", + "type": "class", + "name": "TestGenerateFromDec", + "filePath": "tests/test_specializations_routes.py", + "lineRange": [ + 248, + 260 + ], + "summary": "Tests the JSON endpoint that returns generated variables for a single DEC, and its 400 response when bc_id is missing.", + "tags": [ + "test", + "route-test", + "json-api" + ], + "complexity": "simple" + }, + { + "id": "pipeline:.github/workflows/ci.yml", + "type": "pipeline", + "name": "ci.yml", + "filePath": ".github/workflows/ci.yml", + "summary": "GitHub Actions CI workflow that runs on every push and pull request across all branches: installs project and dev dependencies, lints with isort/black/flake8, and runs the pytest suite on Python 3.12.", + "tags": [ + "ci-cd", + "deployment", + "testing", + "linting", + "github-actions" + ], + "complexity": "simple", + "languageNotes": "Single-job workflow (`test`) with pip caching enabled via actions/setup-python; lint and test steps are separate shell steps within the same job rather than a matrix build." + }, + { + "id": "document:.claude/agent-memory/cdisc-frontend-dev/MEMORY.md", + "type": "document", + "name": "MEMORY.md", + "filePath": ".claude/agent-memory/cdisc-frontend-dev/MEMORY.md", + "summary": "Index file for the cdisc-frontend-dev sub-agent's persistent memory, linking to the project foundation and frontend architecture notes.", + "tags": [ + "documentation", + "agent-memory", + "index" + ], + "complexity": "simple" + }, + { + "id": "document:.claude/agent-memory/cdisc-frontend-dev/frontend_architecture.md", + "type": "document", + "name": "frontend_architecture.md", + "filePath": ".claude/agent-memory/cdisc-frontend-dev/frontend_architecture.md", + "summary": "Persistent agent memory documenting the front-end template hierarchy, custom.css class naming conventions, main.js module functions, template-to-route mapping, and Jinja context variable conventions for the CDISC BC Curation UI.", + "tags": [ + "documentation", + "frontend", + "css", + "javascript", + "agent-memory" + ], + "complexity": "moderate" + }, + { + "id": "document:.claude/agent-memory/cdisc-frontend-dev/project_foundation.md", + "type": "document", + "name": "project_foundation.md", + "filePath": ".claude/agent-memory/cdisc-frontend-dev/project_foundation.md", + "summary": "Persistent agent memory describing the Flask application factory pattern, registered blueprints, model and service locations, database setup, and key route implementation decisions for the CDISC BC Curation app.", + "tags": [ + "documentation", + "architecture", + "flask", + "database", + "agent-memory" + ], + "complexity": "moderate" + }, + { + "id": "document:.claude/agents/cdisc-concept-explorer.md", + "type": "document", + "name": "cdisc-concept-explorer.md", + "filePath": ".claude/agents/cdisc-concept-explorer.md", + "summary": "Defines the cdisc-concept-explorer Claude subagent, a CDISC standards specialist that queries the live CDISC Library API to detect duplicate Biomedical Concepts against local drafts, enrich drafts with authoritative metadata, and find dataset specializations for a given BC.", + "tags": [ + "agent-definition", + "documentation", + "cdisc-library", + "api-integration", + "subagent-spec" + ], + "complexity": "simple" + }, + { + "id": "document:.claude/agents/cdisc-frontend-dev.md", + "type": "document", + "name": "cdisc-frontend-dev.md", + "filePath": ".claude/agents/cdisc-frontend-dev.md", + "summary": "Defines the cdisc-frontend-dev Claude subagent, a front-end specialist scoped to Jinja2 templates, CSS, vanilla JavaScript, and Flask view wiring for the BC curation tool, with mandatory CDISC brand-guideline compliance and an extensive persistent project-memory protocol appended to the spec.", + "tags": [ + "agent-definition", + "documentation", + "frontend", + "memory-system", + "brand-guidelines" + ], + "complexity": "moderate", + "languageNotes": "The file embeds a large boilerplate 'Persistent Agent Memory' instructional block (types of memory, save/access rules) alongside the domain-specific agent spec, roughly doubling its length beyond the front-end guidance itself." + }, + { + "id": "document:.claude/agents/mike.md", + "type": "document", + "name": "mike.md", + "filePath": ".claude/agents/mike.md", + "summary": "Defines the mike Claude subagent, a project-manager assistant for cdisc-concept-curation that tracks daily progress in README-PROGRESS.md, prepares git commit messages, flags documentation drift against the code, and always asks before committing or pushing.", + "tags": [ + "agent-definition", + "documentation", + "project-management", + "git-workflow", + "progress-tracking" + ], + "complexity": "simple" + }, + { + "id": "config:.mcp.json", + "type": "config", + "name": ".mcp.json", + "filePath": ".mcp.json", + "summary": "Registers the cdisc-curation MCP server, launching it via the project virtualenv's Python interpreter running the `mcp_server` package as a module.", + "tags": [ + "configuration", + "mcp", + "server-registration" + ], + "complexity": "simple" + }, + { + "id": "config:.pre-commit-config.yaml", + "type": "config", + "name": ".pre-commit-config.yaml", + "filePath": ".pre-commit-config.yaml", + "summary": "Pre-commit hook configuration that runs isort, black, and flake8 on staged files and executes the full pytest suite before every commit.", + "tags": [ + "configuration", + "ci-cd", + "linting", + "build-system" + ], + "complexity": "simple" + }, + { + "id": "document:CLAUDE.md", + "type": "document", + "name": "CLAUDE.md", + "filePath": "CLAUDE.md", + "summary": "Guidance document for Claude Code covering the app's purpose, how to run it, linting and testing commands, the Route-Service-Model architecture with its 8 Flask blueprints and key services, the MCP server, and environment-variable configuration.", + "tags": [ + "documentation", + "architecture", + "entry-point", + "development" + ], + "complexity": "moderate" + }, + { + "id": "document:README-PROGRESS.md", + "type": "document", + "name": "README-PROGRESS.md", + "filePath": "README-PROGRESS.md", + "summary": "Running project progress log tracking feature status and a dated changelog of fixes and workstream completions such as governance export corrections, audit trail coverage, and LOINC API explorer work.", + "tags": [ + "documentation", + "changelog", + "project-status" + ], + "complexity": "moderate" + }, + { + "id": "document:README.md", + "type": "document", + "name": "README.md", + "filePath": "README.md", + "summary": "Primary project README covering prerequisites, installation, environment-variable configuration, running the app and tests, key screens, project structure, and the external CDISC/NCIt/LOINC APIs the platform integrates with.", + "tags": [ + "documentation", + "entry-point", + "installation", + "overview" + ], + "complexity": "moderate" + }, + { + "id": "config:pyproject.toml", + "type": "config", + "name": "pyproject.toml", + "filePath": "pyproject.toml", + "summary": "Tool configuration setting a shared 200-character line length for black (targeting Python 3.9-3.11) and isort (using the black-compatible profile).", + "tags": [ + "configuration", + "build-system", + "formatting" + ], + "complexity": "simple" + }, + { + "id": "document:requirements-dev.txt", + "type": "document", + "name": "requirements-dev.txt", + "filePath": "requirements-dev.txt", + "summary": "Pinned development-only dependency list: black, flake8 (with mccabe/pycodestyle/pyflakes), isort, pre-commit, pytest, and pytest-flask, used for linting and testing.", + "tags": [ + "dependencies", + "development", + "linting", + "testing" + ], + "complexity": "simple" + }, + { + "id": "document:requirements.txt", + "type": "document", + "name": "requirements.txt", + "filePath": "requirements.txt", + "summary": "Pinned runtime dependency list for the Flask application, including Flask, Flask-SQLAlchemy, Flask-Migrate, SQLAlchemy, Alembic, pandas, openpyxl, lxml, requests, and the mcp SDK.", + "tags": [ + "dependencies", + "runtime", + "build-system" + ], + "complexity": "simple" + }, + { + "id": "document:files/plan.md", + "type": "document", + "name": "plan.md", + "filePath": "files/plan.md", + "summary": "Initial high-level project brief for the CDISC Biomedical Concept Curation application, specifying the target UI mockup to match, the key reference files to review, the required agents (cdisc-concept-explorer, ncit-concept-resolver, cdisc-frontend-dev, mike), and the constraint that only the CDISC_API_KEY environment variable be used for API authentication.", + "tags": [ + "documentation", + "planning", + "project-brief", + "requirements" + ], + "complexity": "simple" + }, + { + "id": "document:files/implementation.md", + "type": "document", + "name": "implementation.md", + "filePath": "files/implementation.md", + "summary": "Detailed implementation plan for the Flask/Jinja BC curation app, laying out the directory structure, SQLAlchemy data models, Flask blueprint/route table, service-layer API surface, template inventory, and a four-phase build roadmap from MVP through export and polish.", + "tags": [ + "documentation", + "architecture", + "implementation-plan", + "specification" + ], + "complexity": "moderate", + "languageNotes": "Serves as the architectural blueprint the actual codebase (app.py, models/, routes/, services/, templates/) was built from; nearly every file/module it names exists in the repository today." + }, + { + "id": "document:files/result.md", + "type": "document", + "name": "result.md", + "filePath": "files/result.md", + "summary": "Post-implementation status report confirming all 8 Flask routes return HTTP 200 and enumerating delivered capabilities: file ingestion with AI field mapping, BC CRUD, NCIt mapping, a 4-stage governance Kanban, an immutable audit trail, and JSON/XLSX/ODM-XML export.", + "tags": [ + "documentation", + "summary", + "status-report", + "verification" + ], + "complexity": "simple" + }, + { + "id": "file:templates/base.html", + "type": "file", + "name": "base.html", + "filePath": "templates/base.html", + "summary": "Master Jinja2 layout providing the sidebar navigation, top bar, flash-message rendering, and template blocks (title, page_title, user_info, content, extra_js) that every other page in the app extends.", + "tags": [ + "layout", + "navigation", + "entry-point", + "bootstrap", + "component" + ], + "complexity": "moderate", + "languageNotes": "Jinja2 block-based inheritance root \u2014 defines the extension points (`{% block content %}`, `{% block extra_js %}`, etc.) that all ten other templates in this batch fill in via `{% extends \"base.html\" %}`." + }, + { + "id": "file:templates/audit.html", + "type": "file", + "name": "audit.html", + "filePath": "templates/audit.html", + "summary": "Audit trail page listing all curation actions (create/update/delete/advance/reject/publish/ingest) with actor/entity/date filters, CSV/JSON export links, expandable before/after diff rows, and pagination.", + "tags": [ + "audit-trail", + "data-table", + "filter", + "pagination", + "export" + ], + "complexity": "complex" + }, + { + "id": "file:templates/bc_detail.html", + "type": "file", + "name": "bc_detail.html", + "filePath": "templates/bc_detail.html", + "summary": "Create/edit form for a single Biomedical Concept covering identification, NCIt and LOINC lookups (with async metadata fetch in extra_js), classification, definition, metadata/status, and an editable Data Element Concepts (DECs) table.", + "tags": [ + "form", + "crud", + "ncit-integration", + "loinc-integration", + "dynamic-form" + ], + "complexity": "complex", + "languageNotes": "extra_js block fetches BC metadata via `bc.fetch_metadata` and rebuilds the NCIt/LOINC display grids client-side, mirroring field lists that are also rendered server-side in Jinja \u2014 a duplicated-schema pattern worth keeping in sync if fields change." + }, + { + "id": "file:templates/bc_list.html", + "type": "file", + "name": "bc_list.html", + "filePath": "templates/bc_list.html", + "summary": "Searchable, filterable, paginated table of locally curated Biomedical Concepts with status badges, JSON/XLSX/ODM-XML export links, and a delete-confirmation modal.", + "tags": [ + "list-view", + "search", + "filter", + "pagination", + "export" + ], + "complexity": "complex" + }, + { + "id": "file:templates/dashboard.html", + "type": "file", + "name": "dashboard.html", + "filePath": "templates/dashboard.html", + "summary": "Main landing dashboard showing KPI cards, recent submissions, the governance pipeline snapshot, paginated/searchable views of CDISC Library BCs and dataset specializations, an AI curation pipeline progress tracker, and quick action links.", + "tags": [ + "dashboard", + "kpi-metrics", + "pipeline-status", + "pagination", + "cdisc-library" + ], + "complexity": "complex" + }, + { + "id": "file:templates/governance.html", + "type": "file", + "name": "governance.html", + "filePath": "templates/governance.html", + "summary": "Four-column Kanban board (Provisional -> SME Review -> CDISC Approval -> Published) for advancing or rejecting Biomedical Concepts through the governance workflow, with summary counts and an export-filename modal.", + "tags": [ + "kanban", + "governance", + "workflow", + "modal", + "export" + ], + "complexity": "complex" + }, + { + "id": "file:templates/ingestion.html", + "type": "file", + "name": "ingestion.html", + "filePath": "templates/ingestion.html", + "summary": "File upload page (drag-and-drop or click) for XLSX/CSV/JSON ingestion, with a parsed-results queue table showing field-mapping confidence, validation errors, duplicate flags, and per-row approve/reject actions.", + "tags": [ + "file-upload", + "drag-drop", + "queue", + "validation", + "form" + ], + "complexity": "moderate" + }, + { + "id": "file:templates/library_bc_detail.html", + "type": "file", + "name": "library_bc_detail.html", + "filePath": "templates/library_bc_detail.html", + "summary": "Read-only detail view of a single Biomedical Concept fetched from the published CDISC Library, showing identification, definition, LOINC coding, metadata, and its Data Element Concepts.", + "tags": [ + "read-only-view", + "cdisc-library", + "loinc-integration", + "detail-view" + ], + "complexity": "moderate" + }, + { + "id": "file:templates/library_spec_detail.html", + "type": "file", + "name": "library_spec_detail.html", + "filePath": "templates/library_spec_detail.html", + "summary": "Read-only detail view of a Dataset Specialization from the published CDISC Library, showing identification/domain metadata and a full variables table (role, codelist, assigned term, mandatory flags, relationship).", + "tags": [ + "read-only-view", + "cdisc-library", + "variables-table", + "detail-view" + ], + "complexity": "moderate" + }, + { + "id": "file:templates/ncit_mapping.html", + "type": "file", + "name": "ncit_mapping.html", + "filePath": "templates/ncit_mapping.html", + "summary": "NCI Thesaurus search page plus a conflict-resolution queue listing Biomedical Concepts with unresolved or low-confidence NCIt mappings that block governance advancement, with a confidence-score legend.", + "tags": [ + "ncit-integration", + "search", + "conflict-resolution", + "confidence-score" + ], + "complexity": "moderate" + }, + { + "id": "file:templates/specializations.html", + "type": "file", + "name": "specializations.html", + "filePath": "templates/specializations.html", + "summary": "Dataset Specialization management page with a collapsible create/edit form (VLM group ID, BC selector, SDTM/CDASH domain, dynamic variables table, generate-from-DEC-templates action) and a list/edit/delete table of existing specializations.", + "tags": [ + "crud", + "dynamic-form", + "sdtm", + "cdash", + "variable-table" + ], + "complexity": "complex", + "languageNotes": "extra_js block mixes server-rendered variable rows with client-side row templating (identical markup duplicated in Jinja and JS template-literal form) for the add-row and generate-from-DEC-templates interactions." + }, + { + "id": "document:.claude/agent-memory/cdisc-concept-explorer/MEMORY.md", + "type": "document", + "name": "MEMORY.md", + "filePath": ".claude/agent-memory/cdisc-concept-explorer/MEMORY.md", + "summary": "Single-line agent memory index for the cdisc-concept-explorer agent, pointing to the verified CDISC API reference notes.", + "tags": [ + "documentation", + "agent-memory", + "reference" + ], + "complexity": "simple" + }, + { + "id": "document:.claude/agent-memory/cdisc-concept-explorer/reference_api_endpoints.md", + "type": "document", + "name": "reference_api_endpoints.md", + "filePath": ".claude/agent-memory/cdisc-concept-explorer/reference_api_endpoints.md", + "summary": "Verified reference notes on the CDISC Library COSMOS v2 API: base URL, working endpoints, lack of pagination, and field names for biomedical concept and dataset specialization records.", + "tags": [ + "documentation", + "api-reference", + "cdisc-library", + "agent-memory" + ], + "complexity": "simple" + }, + { + "id": "document:.claude/skills/code-review/SKILL.md", + "type": "document", + "name": "SKILL.md", + "filePath": ".claude/skills/code-review/SKILL.md", + "summary": "Project-specific code review checklist for cdisc-concept-curation covering route/service/model layering, audit log coverage, Alembic migration discipline, API client error handling, test isolation, and TDD conventions.", + "tags": [ + "documentation", + "code-review", + "development", + "checklist" + ], + "complexity": "simple" + }, + { + "id": "document:.claude/skills/run-concept-curation/SKILL.md", + "type": "document", + "name": "SKILL.md", + "filePath": ".claude/skills/run-concept-curation/SKILL.md", + "summary": "Instructions for running and smoke-testing the Flask dev server, including the preferred automated smoke-test script, manual startup steps, and troubleshooting tips.", + "tags": [ + "documentation", + "development", + "smoke-test", + "deployment" + ], + "complexity": "simple" + }, + { + "id": "file:.claude/skills/run-concept-curation/smoke.sh", + "type": "file", + "name": "smoke.sh", + "filePath": ".claude/skills/run-concept-curation/smoke.sh", + "summary": "Shell script that boots the Flask app on a throwaway SQLite database and port, runs a battery of HTTP checks against key routes (dashboard, BC CRUD, governance, NCIt, audit, ingestion, specializations, export), and tears the server down.", + "tags": [ + "script", + "smoke-test", + "automation", + "development" + ], + "complexity": "moderate", + "languageNotes": "Bash script using a trap-based cleanup function and small curl-wrapping helpers (check/get_code/post_code) to assert HTTP status codes." + }, + { + "id": "function:.claude/skills/run-concept-curation/smoke.sh:cleanup", + "type": "function", + "name": "cleanup", + "filePath": ".claude/skills/run-concept-curation/smoke.sh", + "lineRange": [ + 16, + 22 + ], + "summary": "Trap handler that kills the background dev server process and removes the throwaway SQLite database directory on script exit.", + "tags": [ + "cleanup", + "process-management", + "script" + ], + "complexity": "simple" + }, + { + "id": "function:.claude/skills/run-concept-curation/smoke.sh:check", + "type": "function", + "name": "check", + "filePath": ".claude/skills/run-concept-curation/smoke.sh", + "lineRange": [ + 25, + 34 + ], + "summary": "Assertion helper that compares an actual HTTP status code against an expected value and prints a PASS/FAIL line for the smoke test report.", + "tags": [ + "assertion", + "smoke-test", + "validation" + ], + "complexity": "simple" + }, + { + "id": "config:.flake8", + "type": "config", + "name": ".flake8", + "filePath": ".flake8", + "summary": "Flake8 lint configuration setting max line length to 200, ignoring black-conflicting rules (E203/W503) and SQLAlchemy's `== None` idiom (E711), and excluding build/venv directories.", + "tags": [ + "configuration", + "linting", + "build-system", + "python" + ], + "complexity": "simple" + }, + { + "id": "config:.hintrc", + "type": "config", + "name": ".hintrc", + "filePath": ".hintrc", + "summary": "webhint configuration extending the 'development' ruleset and disabling the no-inline-styles rule for HTML/CSS linting.", + "tags": [ + "configuration", + "linting", + "frontend" + ], + "complexity": "simple" + }, + { + "id": "config:.ua/.understandignore", + "type": "config", + "name": ".understandignore", + "filePath": ".ua/.understandignore", + "summary": "Gitignore-style exclusion list for the Understand-Anything analysis tool, with commented-out suggestions mirroring the project's .gitignore (Python caches, virtualenvs, egg metadata) that can be uncommented to activate.", + "tags": [ + "configuration", + "tooling", + "analysis" + ], + "complexity": "simple" + }, + { + "id": "config:.ua/config.json", + "type": "config", + "name": "config.json", + "filePath": ".ua/config.json", + "summary": "Single-key configuration for the Understand-Anything tool setting the output language for generated analysis text to English.", + "tags": [ + "configuration", + "tooling", + "analysis" + ], + "complexity": "simple" + }, + { + "id": "file:files/BC Curation Principles and Completion GLs.xlsx", + "type": "file", + "name": "BC Curation Principles and Completion GLs.xlsx", + "filePath": "files/BC Curation Principles and Completion GLs.xlsx", + "summary": "Reference spreadsheet documenting BC curation principles and field-completion guidelines used by curators when authoring Biomedical Concepts.", + "tags": [ + "reference", + "documentation", + "spreadsheet", + "curation-guidance" + ], + "complexity": "moderate" + }, + { + "id": "file:files/BC DEC Templates.xlsx", + "type": "file", + "name": "BC DEC Templates.xlsx", + "filePath": "files/BC DEC Templates.xlsx", + "summary": "Template spreadsheet defining the standard row/column layout for Data Element Concepts, used as an ingestion source template for curators.", + "tags": [ + "template", + "spreadsheet", + "ingestion", + "reference" + ], + "complexity": "simple" + }, + { + "id": "file:files/BC Examples.xlsx", + "type": "file", + "name": "BC Examples.xlsx", + "filePath": "files/BC Examples.xlsx", + "summary": "Example spreadsheet of fully-populated Biomedical Concepts, used as a reference/sample dataset for the ingestion pipeline and curator training.", + "tags": [ + "reference", + "spreadsheet", + "sample-data", + "ingestion" + ], + "complexity": "moderate" + }, + { + "id": "file:files/BC Governance.docx", + "type": "file", + "name": "BC Governance.docx", + "filePath": "files/BC Governance.docx", + "summary": "Word document describing the governance process and stages (Provisional, SME Review, CDISC Approval, Published) that a curated Biomedical Concept moves through.", + "tags": [ + "documentation", + "governance", + "reference" + ], + "complexity": "simple" + }, + { + "id": "file:mcp_server/__init__.py", + "type": "file", + "name": "__init__.py", + "filePath": "mcp_server/__init__.py", + "summary": "Package marker and docstring for the mcp_server package, noting it is run via `python -m mcp_server` and deliberately named to avoid shadowing the MCP SDK package.", + "tags": [ + "entry-point", + "package-init", + "mcp" + ], + "complexity": "simple" + }, + { + "id": "document:migrations/README", + "type": "document", + "name": "README", + "filePath": "migrations/README", + "summary": "One-line note identifying the migrations directory as a single-database Flask-Migrate/Alembic configuration.", + "tags": [ + "documentation", + "database", + "migration" + ], + "complexity": "simple" + }, + { + "id": "config:migrations/alembic.ini", + "type": "config", + "name": "alembic.ini", + "filePath": "migrations/alembic.ini", + "summary": "Alembic configuration file controlling migration script location, logging setup, and version-file naming for the Flask-Migrate managed schema.", + "tags": [ + "configuration", + "database", + "migration", + "alembic" + ], + "complexity": "simple" + }, + { + "id": "file:migrations/env.py", + "type": "file", + "name": "env.py", + "filePath": "migrations/env.py", + "summary": "Alembic environment script that wires Flask-Migrate's engine/metadata into Alembic's migration context, supporting both offline (SQL-only) and online (live-connection) migration runs.", + "tags": [ + "database", + "migration", + "alembic", + "flask-migrate" + ], + "complexity": "moderate", + "languageNotes": "Handles both Flask-SQLAlchemy <3 and >=3 engine-access APIs via a try/except fallback, and skips autogenerating empty migrations via a process_revision_directives hook." + }, + { + "id": "function:migrations/env.py:get_engine", + "type": "function", + "name": "get_engine", + "filePath": "migrations/env.py", + "lineRange": [ + 17, + 23 + ], + "summary": "Returns the active SQLAlchemy engine from the Flask-Migrate extension, falling back between the Flask-SQLAlchemy 2.x and 3.x APIs.", + "tags": [ + "database", + "migration", + "compatibility" + ], + "complexity": "simple" + }, + { + "id": "function:migrations/env.py:get_engine_url", + "type": "function", + "name": "get_engine_url", + "filePath": "migrations/env.py", + "lineRange": [ + 26, + 30 + ], + "summary": "Renders the database engine's connection URL as a string with percent signs escaped, for use as Alembic's sqlalchemy.url option.", + "tags": [ + "database", + "migration", + "utility" + ], + "complexity": "simple" + }, + { + "id": "function:migrations/env.py:run_migrations_offline", + "type": "function", + "name": "run_migrations_offline", + "filePath": "migrations/env.py", + "lineRange": [ + 52, + 68 + ], + "summary": "Runs Alembic migrations in offline mode by configuring the context with just the database URL and emitting SQL without requiring a live connection.", + "tags": [ + "database", + "migration", + "alembic" + ], + "complexity": "moderate" + }, + { + "id": "function:migrations/env.py:run_migrations_online", + "type": "function", + "name": "run_migrations_online", + "filePath": "migrations/env.py", + "lineRange": [ + 71, + 99 + ], + "summary": "Runs Alembic migrations in online mode against a live database connection, registering a hook that skips autogenerating a migration when no schema changes are detected.", + "tags": [ + "database", + "migration", + "alembic" + ], + "complexity": "moderate" + }, + { + "id": "file:migrations/script.py.mako", + "type": "file", + "name": "script.py.mako", + "filePath": "migrations/script.py.mako", + "summary": "Mako template used by `flask db migrate` to scaffold new Alembic revision files with the standard revision metadata and upgrade/downgrade function stubs.", + "tags": [ + "template", + "database", + "migration", + "code-generation" + ], + "complexity": "simple" + }, + { + "id": "file:migrations/versions/51d4a009d291_baseline_initial_schema.py", + "type": "file", + "name": "51d4a009d291_baseline_initial_schema.py", + "filePath": "migrations/versions/51d4a009d291_baseline_initial_schema.py", + "summary": "Baseline Alembic migration that creates the initial database schema: audit_logs, biomedical_concepts, ingestion_records, data_element_concepts, dataset_specializations, and governance_records tables, with their downgrade counterparts.", + "tags": [ + "database", + "migration", + "schema", + "baseline" + ], + "complexity": "complex", + "languageNotes": "Auto-generated Alembic revision consisting almost entirely of repetitive op.create_table/sa.Column calls plus a batch_alter_table index creation." + }, + { + "id": "function:migrations/versions/51d4a009d291_baseline_initial_schema.py:upgrade", + "type": "function", + "name": "upgrade", + "filePath": "migrations/versions/51d4a009d291_baseline_initial_schema.py", + "lineRange": [ + 19, + 128 + ], + "summary": "Creates the six baseline tables (audit_logs, biomedical_concepts, ingestion_records, data_element_concepts, dataset_specializations, governance_records) with their columns, primary keys, foreign keys, and one index.", + "tags": [ + "database", + "migration", + "schema-creation" + ], + "complexity": "complex" + }, + { + "id": "function:migrations/versions/51d4a009d291_baseline_initial_schema.py:downgrade", + "type": "function", + "name": "downgrade", + "filePath": "migrations/versions/51d4a009d291_baseline_initial_schema.py", + "lineRange": [ + 131, + 142 + ], + "summary": "Reverses the baseline migration by dropping the index and all six tables created in upgrade().", + "tags": [ + "database", + "migration", + "rollback" + ], + "complexity": "simple" + }, + { + "id": "file:routes/__init__.py", + "type": "file", + "name": "__init__.py", + "filePath": "routes/__init__.py", + "summary": "Empty package marker file that makes routes/ a Python package for the Flask blueprint modules it contains.", + "tags": [ + "package-init", + "empty" + ], + "complexity": "simple" + }, + { + "id": "file:static/css/custom.css", + "type": "file", + "name": "custom.css", + "filePath": "static/css/custom.css", + "summary": "Main application stylesheet defining design tokens (colors, spacing), base resets, app shell layout (sidebar, topbar, content area), and component styles (cards, flash messages, nav items, badges) for the CDISC BC curation UI.", + "tags": [ + "stylesheet", + "design-system", + "layout", + "ui" + ], + "complexity": "complex", + "languageNotes": "Organized around CSS custom properties (design tokens) under :root, consistent with the project's CDISC brand color requirements." + }, + { + "id": "file:static/js/main.js", + "type": "file", + "name": "main.js", + "filePath": "static/js/main.js", + "summary": "Client-side interactivity module for the BC curation platform: flash-message dismissal, drag-and-drop file upload, NCIt/LOINC lookup and search panels, dynamic DEC table rows, governance Kanban actions, audit-diff toggles, and shared fetch/toast helpers, all wired up on DOMContentLoaded.", + "tags": [ + "frontend", + "vanilla-js", + "ui-interactivity", + "fetch-api" + ], + "complexity": "complex", + "languageNotes": "Entire file is wrapped in an IIFE for scope isolation; all functions are private and invoked from a single DOMContentLoaded listener at the bottom of the file." + }, + { + "id": "function:static/js/main.js:initFileUpload", + "type": "function", + "name": "initFileUpload", + "filePath": "static/js/main.js", + "lineRange": [ + 25, + 76 + ], + "summary": "Wires up the file-input and drag-and-drop upload zone: updates the displayed filename, toggles drag-over styling, and assigns dropped files to the hidden file input.", + "tags": [ + "ui-interactivity", + "file-upload", + "drag-and-drop" + ], + "complexity": "moderate" + }, + { + "id": "function:static/js/main.js:initNcitLookup", + "type": "function", + "name": "initNcitLookup", + "filePath": "static/js/main.js", + "lineRange": [ + 84, + 131 + ], + "summary": "Handles the NCIt code lookup button: fetches /ncit/search for a typed NCIt code and renders matches into the results panel, or shows an error state on failure.", + "tags": [ + "api-handler", + "fetch-api", + "ncit", + "ui-interactivity" + ], + "complexity": "moderate" + }, + { + "id": "function:static/js/main.js:renderNcitResults", + "type": "function", + "name": "renderNcitResults", + "filePath": "static/js/main.js", + "lineRange": [ + 132, + 207 + ], + "summary": "Renders NCIt lookup result cards into a container and wires each card's 'Use this concept' button to populate the BC form and fetch full concept metadata for display.", + "tags": [ + "rendering", + "ui-interactivity", + "ncit", + "fetch-api" + ], + "complexity": "complex" + }, + { + "id": "function:static/js/main.js:renderNcitMetaDisplay", + "type": "function", + "name": "renderNcitMetaDisplay", + "filePath": "static/js/main.js", + "lineRange": [ + 208, + 265 + ], + "summary": "Formats and displays selected NCIt concept metadata fields (definitions, parents, synonyms, etc.) as a labeled list, handling both scalar and array-shaped fields.", + "tags": [ + "rendering", + "ncit", + "ui-interactivity" + ], + "complexity": "moderate" + }, + { + "id": "function:static/js/main.js:initDecTable", + "type": "function", + "name": "initDecTable", + "filePath": "static/js/main.js", + "lineRange": [ + 266, + 290 + ], + "summary": "Initializes the Data Element Concept table: wires the 'add DEC row' button and delegates row-delete clicks to remove table rows.", + "tags": [ + "ui-interactivity", + "dynamic-table", + "dec" + ], + "complexity": "simple" + }, + { + "id": "function:static/js/main.js:buildDecRow", + "type": "function", + "name": "buildDecRow", + "filePath": "static/js/main.js", + "lineRange": [ + 291, + 331 + ], + "summary": "Builds and returns a new table row element containing the input fields for a Data Element Concept entry, indexed for form submission.", + "tags": [ + "dom-construction", + "dynamic-table", + "dec" + ], + "complexity": "moderate" + }, + { + "id": "function:static/js/main.js:initKanban", + "type": "function", + "name": "initKanban", + "filePath": "static/js/main.js", + "lineRange": [ + 332, + 386 + ], + "summary": "Wires the governance Kanban board's advance and reject buttons to POST stage-transition requests and reload or toast on success/failure.", + "tags": [ + "api-handler", + "governance", + "kanban", + "fetch-api" + ], + "complexity": "moderate" + }, + { + "id": "function:static/js/main.js:initAuditDiff", + "type": "function", + "name": "initAuditDiff", + "filePath": "static/js/main.js", + "lineRange": [ + 387, + 402 + ], + "summary": "Toggles the visibility of before/after diff panels on the audit log page when a diff-toggle button is clicked.", + "tags": [ + "ui-interactivity", + "audit", + "toggle" + ], + "complexity": "simple" + }, + { + "id": "function:static/js/main.js:initNcitSearch", + "type": "function", + "name": "initNcitSearch", + "filePath": "static/js/main.js", + "lineRange": [ + 403, + 444 + ], + "summary": "Wires the NCIt search page's search button and Enter key to trigger a debounced-free search request and render results.", + "tags": [ + "api-handler", + "ncit", + "search", + "ui-interactivity" + ], + "complexity": "moderate" + }, + { + "id": "function:static/js/main.js:renderNcitSearchResults", + "type": "function", + "name": "renderNcitSearchResults", + "filePath": "static/js/main.js", + "lineRange": [ + 445, + 529 + ], + "summary": "Renders NCIt search result cards with synonyms and a 'Use this concept' button that stores the chosen concept in sessionStorage and navigates to the BC creation page.", + "tags": [ + "rendering", + "ncit", + "search", + "ui-interactivity" + ], + "complexity": "complex" + }, + { + "id": "function:static/js/main.js:showInlineToast", + "type": "function", + "name": "showInlineToast", + "filePath": "static/js/main.js", + "lineRange": [ + 537, + 563 + ], + "summary": "Creates and displays a Bootstrap toast notification with the given message and style, appending it to a shared toast container.", + "tags": [ + "ui-interactivity", + "toast", + "notification" + ], + "complexity": "moderate" + }, + { + "id": "function:static/js/main.js:getOrCreateToastContainer", + "type": "function", + "name": "getOrCreateToastContainer", + "filePath": "static/js/main.js", + "lineRange": [ + 564, + 578 + ], + "summary": "Finds or lazily creates the fixed-position container element that hosts stacked toast notifications.", + "tags": [ + "dom-construction", + "toast", + "utility" + ], + "complexity": "simple" + }, + { + "id": "function:static/js/main.js:initLoincLookup", + "type": "function", + "name": "initLoincLookup", + "filePath": "static/js/main.js", + "lineRange": [ + 593, + 680 + ], + "summary": "Handles the LOINC code lookup button: fetches /loinc/search for a typed LOINC code and renders matches, mirroring the NCIt lookup flow.", + "tags": [ + "api-handler", + "fetch-api", + "loinc", + "ui-interactivity" + ], + "complexity": "moderate" + }, + { + "id": "function:static/js/main.js:renderLoincResults", + "type": "function", + "name": "renderLoincResults", + "filePath": "static/js/main.js", + "lineRange": [ + 681, + 755 + ], + "summary": "Renders LOINC lookup result cards into a container and wires each card's selection button to populate form fields and display LOINC metadata.", + "tags": [ + "rendering", + "loinc", + "ui-interactivity", + "fetch-api" + ], + "complexity": "complex" + }, + { + "id": "function:static/js/main.js:renderLoincMetaDisplay", + "type": "function", + "name": "renderLoincMetaDisplay", + "filePath": "static/js/main.js", + "lineRange": [ + 756, + 782 + ], + "summary": "Formats and displays selected LOINC metadata fields as a labeled list on the BC detail page.", + "tags": [ + "rendering", + "loinc", + "ui-interactivity" + ], + "complexity": "moderate" + }, + { + "id": "function:static/js/main.js:initPendingNcitConcept", + "type": "function", + "name": "initPendingNcitConcept", + "filePath": "static/js/main.js", + "lineRange": [ + 783, + 801 + ], + "summary": "On page load, consumes a pending NCIt concept stashed in sessionStorage (from the NCIt search page) and auto-populates the BC detail page's metadata display and hidden field.", + "tags": [ + "ui-interactivity", + "ncit", + "session-storage" + ], + "complexity": "moderate" + }, + { + "id": "file:tests/__init__.py", + "type": "file", + "name": "__init__.py", + "filePath": "tests/__init__.py", + "summary": "Empty package marker file that makes tests/ a discoverable Python package for pytest.", + "tags": [ + "package-init", + "test", + "empty" + ], + "complexity": "simple" + }, + { + "id": "file:files/cdisc_bc_ai_platform.html", + "type": "file", + "name": "cdisc_bc_ai_platform.html", + "filePath": "files/cdisc_bc_ai_platform.html", + "summary": "No summary available", + "tags": [ + "untagged" + ], + "complexity": "moderate" + }, + { + "id": "file:files/cdisc_smart_goals_infographic.html", + "type": "file", + "name": "cdisc_smart_goals_infographic.html", + "filePath": "files/cdisc_smart_goals_infographic.html", + "summary": "No summary available", + "tags": [ + "untagged" + ], + "complexity": "moderate" + } + ], + "edges": [ + { + "source": "file:models/audit.py", + "target": "file:extensions.py", + "type": "imports", + "direction": "forward", + "weight": 0.7 + }, + { + "source": "file:models/bc.py", + "target": "file:extensions.py", + "type": "imports", + "direction": "forward", + "weight": 0.7 + }, + { + "source": "file:models/ingestion.py", + "target": "file:extensions.py", + "type": "imports", + "direction": "forward", + "weight": 0.7 + }, + { + "source": "file:routes/audit.py", + "target": "file:models/audit.py", + "type": "imports", + "direction": "forward", + "weight": 0.7 + }, + { + "source": "file:routes/ingestion.py", + "target": "file:extensions.py", + "type": "imports", + "direction": "forward", + "weight": 0.7 + }, + { + "source": "file:routes/ingestion.py", + "target": "file:models/audit.py", + "type": "imports", + "direction": "forward", + "weight": 0.7 + }, + { + "source": "file:routes/ingestion.py", + "target": "file:models/bc.py", + "type": "imports", + "direction": "forward", + "weight": 0.7 + }, + { + "source": "file:routes/ingestion.py", + "target": "file:models/ingestion.py", + "type": "imports", + "direction": "forward", + "weight": 0.7 + }, + { + "source": "file:routes/ingestion.py", + "target": "file:services/ingestion.py", + "type": "imports", + "direction": "forward", + "weight": 0.7 + }, + { + "source": "file:tests/conftest.py", + "target": "file:app.py", + "type": "imports", + "direction": "forward", + "weight": 0.7 + }, + { + "source": "file:tests/conftest.py", + "target": "file:extensions.py", + "type": "imports", + "direction": "forward", + "weight": 0.7 + }, + { + "source": "file:tests/conftest.py", + "target": "file:models/audit.py", + "type": "imports", + "direction": "forward", + "weight": 0.7 + }, + { + "source": "file:tests/conftest.py", + "target": "file:models/bc.py", + "type": "imports", + "direction": "forward", + "weight": 0.7 + }, + { + "source": "file:tests/conftest.py", + "target": "file:models/governance.py", + "type": "imports", + "direction": "forward", + "weight": 0.7 + }, + { + "source": "file:tests/conftest.py", + "target": "file:models/ingestion.py", + "type": "imports", + "direction": "forward", + "weight": 0.7 + }, + { + "source": "file:models/audit.py", + "target": "class:models/audit.py:AuditLog", + "type": "contains", + "direction": "forward", + "weight": 1.0 + }, + { + "source": "file:models/bc.py", + "target": "class:models/bc.py:BiomedicalConcept", + "type": "contains", + "direction": "forward", + "weight": 1.0 + }, + { + "source": "file:models/bc.py", + "target": "class:models/bc.py:DataElementConcept", + "type": "contains", + "direction": "forward", + "weight": 1.0 + }, + { + "source": "file:models/ingestion.py", + "target": "class:models/ingestion.py:IngestionRecord", + "type": "contains", + "direction": "forward", + "weight": 1.0 + }, + { + "source": "file:routes/audit.py", + "target": "function:routes/audit.py:index", + "type": "contains", + "direction": "forward", + "weight": 1.0 + }, + { + "source": "file:routes/ingestion.py", + "target": "function:routes/ingestion.py:_bc_from_mapped", + "type": "contains", + "direction": "forward", + "weight": 1.0 + }, + { + "source": "file:routes/ingestion.py", + "target": "function:routes/ingestion.py:_create_decs", + "type": "contains", + "direction": "forward", + "weight": 1.0 + }, + { + "source": "file:routes/ingestion.py", + "target": "function:routes/ingestion.py:upload", + "type": "contains", + "direction": "forward", + "weight": 1.0 + }, + { + "source": "file:routes/ingestion.py", + "target": "function:routes/ingestion.py:approve", + "type": "contains", + "direction": "forward", + "weight": 1.0 + }, + { + "source": "file:routes/ingestion.py", + "target": "function:routes/ingestion.py:approve_all", + "type": "contains", + "direction": "forward", + "weight": 1.0 + }, + { + "source": "file:services/ingestion.py", + "target": "function:services/ingestion.py:_match_field", + "type": "contains", + "direction": "forward", + "weight": 1.0 + }, + { + "source": "file:services/ingestion.py", + "target": "function:services/ingestion.py:map_fields", + "type": "contains", + "direction": "forward", + "weight": 1.0 + }, + { + "source": "file:services/ingestion.py", + "target": "function:services/ingestion.py:validate_bc", + "type": "contains", + "direction": "forward", + "weight": 1.0 + }, + { + "source": "file:services/ingestion.py", + "target": "function:services/ingestion.py:_group_by_bc", + "type": "contains", + "direction": "forward", + "weight": 1.0 + }, + { + "source": "file:services/ingestion.py", + "target": "function:services/ingestion.py:parse_xlsx", + "type": "contains", + "direction": "forward", + "weight": 1.0 + }, + { + "source": "file:services/ingestion.py", + "target": "function:services/ingestion.py:parse_csv", + "type": "contains", + "direction": "forward", + "weight": 1.0 + }, + { + "source": "file:services/ingestion.py", + "target": "function:services/ingestion.py:parse_json", + "type": "contains", + "direction": "forward", + "weight": 1.0 + }, + { + "source": "file:services/ingestion.py", + "target": "function:services/ingestion.py:deduplicate", + "type": "contains", + "direction": "forward", + "weight": 1.0 + }, + { + "source": "file:tests/conftest.py", + "target": "function:tests/conftest.py:sample_bc", + "type": "contains", + "direction": "forward", + "weight": 1.0 + }, + { + "source": "file:models/audit.py", + "target": "class:models/audit.py:AuditLog", + "type": "exports", + "direction": "forward", + "weight": 0.8 + }, + { + "source": "file:models/bc.py", + "target": "class:models/bc.py:BiomedicalConcept", + "type": "exports", + "direction": "forward", + "weight": 0.8 + }, + { + "source": "file:models/bc.py", + "target": "class:models/bc.py:DataElementConcept", + "type": "exports", + "direction": "forward", + "weight": 0.8 + }, + { + "source": "file:models/ingestion.py", + "target": "class:models/ingestion.py:IngestionRecord", + "type": "exports", + "direction": "forward", + "weight": 0.8 + }, + { + "source": "file:routes/audit.py", + "target": "function:routes/audit.py:index", + "type": "exports", + "direction": "forward", + "weight": 0.8 + }, + { + "source": "file:routes/ingestion.py", + "target": "function:routes/ingestion.py:_bc_from_mapped", + "type": "exports", + "direction": "forward", + "weight": 0.8 + }, + { + "source": "file:routes/ingestion.py", + "target": "function:routes/ingestion.py:_create_decs", + "type": "exports", + "direction": "forward", + "weight": 0.8 + }, + { + "source": "file:routes/ingestion.py", + "target": "function:routes/ingestion.py:upload", + "type": "exports", + "direction": "forward", + "weight": 0.8 + }, + { + "source": "file:routes/ingestion.py", + "target": "function:routes/ingestion.py:approve", + "type": "exports", + "direction": "forward", + "weight": 0.8 + }, + { + "source": "file:routes/ingestion.py", + "target": "function:routes/ingestion.py:approve_all", + "type": "exports", + "direction": "forward", + "weight": 0.8 + }, + { + "source": "file:services/ingestion.py", + "target": "function:services/ingestion.py:_match_field", + "type": "exports", + "direction": "forward", + "weight": 0.8 + }, + { + "source": "file:services/ingestion.py", + "target": "function:services/ingestion.py:map_fields", + "type": "exports", + "direction": "forward", + "weight": 0.8 + }, + { + "source": "file:services/ingestion.py", + "target": "function:services/ingestion.py:validate_bc", + "type": "exports", + "direction": "forward", + "weight": 0.8 + }, + { + "source": "file:services/ingestion.py", + "target": "function:services/ingestion.py:_group_by_bc", + "type": "exports", + "direction": "forward", + "weight": 0.8 + }, + { + "source": "file:services/ingestion.py", + "target": "function:services/ingestion.py:parse_xlsx", + "type": "exports", + "direction": "forward", + "weight": 0.8 + }, + { + "source": "file:services/ingestion.py", + "target": "function:services/ingestion.py:parse_csv", + "type": "exports", + "direction": "forward", + "weight": 0.8 + }, + { + "source": "file:services/ingestion.py", + "target": "function:services/ingestion.py:parse_json", + "type": "exports", + "direction": "forward", + "weight": 0.8 + }, + { + "source": "file:services/ingestion.py", + "target": "function:services/ingestion.py:deduplicate", + "type": "exports", + "direction": "forward", + "weight": 0.8 + }, + { + "source": "function:routes/ingestion.py:_bc_from_mapped", + "target": "class:models/bc.py:BiomedicalConcept", + "type": "calls", + "direction": "forward", + "weight": 0.8 + }, + { + "source": "function:routes/ingestion.py:_create_decs", + "target": "class:models/bc.py:DataElementConcept", + "type": "calls", + "direction": "forward", + "weight": 0.8 + }, + { + "source": "function:routes/ingestion.py:approve", + "target": "class:models/audit.py:AuditLog", + "type": "calls", + "direction": "forward", + "weight": 0.8 + }, + { + "source": "function:routes/ingestion.py:approve_all", + "target": "class:models/audit.py:AuditLog", + "type": "calls", + "direction": "forward", + "weight": 0.8 + }, + { + "source": "function:routes/ingestion.py:upload", + "target": "function:services/ingestion.py:parse_xlsx", + "type": "calls", + "direction": "forward", + "weight": 0.8 + }, + { + "source": "function:routes/ingestion.py:upload", + "target": "function:services/ingestion.py:parse_csv", + "type": "calls", + "direction": "forward", + "weight": 0.8 + }, + { + "source": "function:routes/ingestion.py:upload", + "target": "function:services/ingestion.py:parse_json", + "type": "calls", + "direction": "forward", + "weight": 0.8 + }, + { + "source": "function:routes/ingestion.py:upload", + "target": "function:services/ingestion.py:deduplicate", + "type": "calls", + "direction": "forward", + "weight": 0.8 + }, + { + "source": "function:routes/audit.py:index", + "target": "class:models/audit.py:AuditLog", + "type": "calls", + "direction": "forward", + "weight": 0.8 + }, + { + "source": "file:models/audit.py", + "target": "file:tests/test_models.py", + "type": "tested_by", + "direction": "forward", + "weight": 0.5 + }, + { + "source": "file:models/bc.py", + "target": "file:tests/test_models.py", + "type": "tested_by", + "direction": "forward", + "weight": 0.5 + }, + { + "source": "file:models/bc.py", + "target": "file:tests/test_bc_routes.py", + "type": "tested_by", + "direction": "forward", + "weight": 0.5 + }, + { + "source": "file:models/ingestion.py", + "target": "file:tests/test_models.py", + "type": "tested_by", + "direction": "forward", + "weight": 0.5 + }, + { + "source": "file:routes/audit.py", + "target": "file:tests/test_audit_routes.py", + "type": "tested_by", + "direction": "forward", + "weight": 0.5 + }, + { + "source": "file:routes/ingestion.py", + "target": "file:tests/test_ingestion_routes.py", + "type": "tested_by", + "direction": "forward", + "weight": 0.5 + }, + { + "source": "file:services/ingestion.py", + "target": "file:tests/test_ingestion_service.py", + "type": "tested_by", + "direction": "forward", + "weight": 0.5 + }, + { + "source": "file:tests/test_audit_routes.py", + "target": "file:extensions.py", + "type": "imports", + "direction": "forward", + "weight": 0.7 + }, + { + "source": "file:tests/test_audit_routes.py", + "target": "file:models/audit.py", + "type": "imports", + "direction": "forward", + "weight": 0.7 + }, + { + "source": "file:tests/test_bc_routes.py", + "target": "file:extensions.py", + "type": "imports", + "direction": "forward", + "weight": 0.7 + }, + { + "source": "file:tests/test_bc_routes.py", + "target": "file:models/audit.py", + "type": "imports", + "direction": "forward", + "weight": 0.7 + }, + { + "source": "file:tests/test_bc_routes.py", + "target": "file:models/bc.py", + "type": "imports", + "direction": "forward", + "weight": 0.7 + }, + { + "source": "file:tests/test_governance_routes.py", + "target": "file:extensions.py", + "type": "imports", + "direction": "forward", + "weight": 0.7 + }, + { + "source": "file:tests/test_governance_routes.py", + "target": "file:models/audit.py", + "type": "imports", + "direction": "forward", + "weight": 0.7 + }, + { + "source": "file:tests/test_governance_routes.py", + "target": "file:models/bc.py", + "type": "imports", + "direction": "forward", + "weight": 0.7 + }, + { + "source": "file:tests/test_governance_routes.py", + "target": "file:models/governance.py", + "type": "imports", + "direction": "forward", + "weight": 0.7 + }, + { + "source": "file:tests/test_ingestion_routes.py", + "target": "file:extensions.py", + "type": "imports", + "direction": "forward", + "weight": 0.7 + }, + { + "source": "file:tests/test_ingestion_routes.py", + "target": "file:models/audit.py", + "type": "imports", + "direction": "forward", + "weight": 0.7 + }, + { + "source": "file:tests/test_ingestion_routes.py", + "target": "file:models/bc.py", + "type": "imports", + "direction": "forward", + "weight": 0.7 + }, + { + "source": "file:tests/test_ingestion_routes.py", + "target": "file:models/ingestion.py", + "type": "imports", + "direction": "forward", + "weight": 0.7 + }, + { + "source": "file:tests/test_ingestion_service.py", + "target": "file:services/ingestion.py", + "type": "imports", + "direction": "forward", + "weight": 0.7 + }, + { + "source": "file:tests/test_models.py", + "target": "file:extensions.py", + "type": "imports", + "direction": "forward", + "weight": 0.7 + }, + { + "source": "file:tests/test_models.py", + "target": "file:models/audit.py", + "type": "imports", + "direction": "forward", + "weight": 0.7 + }, + { + "source": "file:tests/test_models.py", + "target": "file:models/bc.py", + "type": "imports", + "direction": "forward", + "weight": 0.7 + }, + { + "source": "file:tests/test_models.py", + "target": "file:models/ingestion.py", + "type": "imports", + "direction": "forward", + "weight": 0.7 + }, + { + "source": "file:tests/test_audit_routes.py", + "target": "class:tests/test_audit_routes.py:TestAuditIndex", + "type": "contains", + "direction": "forward", + "weight": 1.0 + }, + { + "source": "file:tests/test_bc_routes.py", + "target": "function:tests/test_bc_routes.py:_bc_form", + "type": "contains", + "direction": "forward", + "weight": 1.0 + }, + { + "source": "file:tests/test_bc_routes.py", + "target": "class:tests/test_bc_routes.py:TestBcIndex", + "type": "contains", + "direction": "forward", + "weight": 1.0 + }, + { + "source": "file:tests/test_bc_routes.py", + "target": "class:tests/test_bc_routes.py:TestCreateBc", + "type": "contains", + "direction": "forward", + "weight": 1.0 + }, + { + "source": "file:tests/test_bc_routes.py", + "target": "class:tests/test_bc_routes.py:TestBcDetail", + "type": "contains", + "direction": "forward", + "weight": 1.0 + }, + { + "source": "file:tests/test_bc_routes.py", + "target": "class:tests/test_bc_routes.py:TestEditBc", + "type": "contains", + "direction": "forward", + "weight": 1.0 + }, + { + "source": "file:tests/test_bc_routes.py", + "target": "class:tests/test_bc_routes.py:TestClearNcitCode", + "type": "contains", + "direction": "forward", + "weight": 1.0 + }, + { + "source": "file:tests/test_bc_routes.py", + "target": "class:tests/test_bc_routes.py:TestClearLoincCode", + "type": "contains", + "direction": "forward", + "weight": 1.0 + }, + { + "source": "file:tests/test_bc_routes.py", + "target": "class:tests/test_bc_routes.py:TestSubmitForReview", + "type": "contains", + "direction": "forward", + "weight": 1.0 + }, + { + "source": "file:tests/test_bc_routes.py", + "target": "class:tests/test_bc_routes.py:TestDeleteBc", + "type": "contains", + "direction": "forward", + "weight": 1.0 + }, + { + "source": "file:tests/test_bc_routes.py", + "target": "class:tests/test_bc_routes.py:TestExport", + "type": "contains", + "direction": "forward", + "weight": 1.0 + }, + { + "source": "file:tests/test_bc_routes.py", + "target": "class:tests/test_bc_routes.py:TestLibraryDetail", + "type": "contains", + "direction": "forward", + "weight": 1.0 + }, + { + "source": "file:tests/test_governance_routes.py", + "target": "class:tests/test_governance_routes.py:TestAdvance", + "type": "contains", + "direction": "forward", + "weight": 1.0 + }, + { + "source": "file:tests/test_governance_routes.py", + "target": "class:tests/test_governance_routes.py:TestReject", + "type": "contains", + "direction": "forward", + "weight": 1.0 + }, + { + "source": "file:tests/test_governance_routes.py", + "target": "class:tests/test_governance_routes.py:TestGovernanceExport", + "type": "contains", + "direction": "forward", + "weight": 1.0 + }, + { + "source": "file:tests/test_ingestion_routes.py", + "target": "class:tests/test_ingestion_routes.py:TestUpload", + "type": "contains", + "direction": "forward", + "weight": 1.0 + }, + { + "source": "file:tests/test_ingestion_routes.py", + "target": "class:tests/test_ingestion_routes.py:TestApprove", + "type": "contains", + "direction": "forward", + "weight": 1.0 + }, + { + "source": "file:tests/test_ingestion_routes.py", + "target": "class:tests/test_ingestion_routes.py:TestReject", + "type": "contains", + "direction": "forward", + "weight": 1.0 + }, + { + "source": "file:tests/test_ingestion_routes.py", + "target": "class:tests/test_ingestion_routes.py:TestApproveAll", + "type": "contains", + "direction": "forward", + "weight": 1.0 + }, + { + "source": "file:tests/test_ingestion_service.py", + "target": "class:tests/test_ingestion_service.py:TestSimilarity", + "type": "contains", + "direction": "forward", + "weight": 1.0 + }, + { + "source": "file:tests/test_ingestion_service.py", + "target": "class:tests/test_ingestion_service.py:TestMatchField", + "type": "contains", + "direction": "forward", + "weight": 1.0 + }, + { + "source": "file:tests/test_ingestion_service.py", + "target": "class:tests/test_ingestion_service.py:TestMapFields", + "type": "contains", + "direction": "forward", + "weight": 1.0 + }, + { + "source": "file:tests/test_ingestion_service.py", + "target": "class:tests/test_ingestion_service.py:TestValidateBc", + "type": "contains", + "direction": "forward", + "weight": 1.0 + }, + { + "source": "file:tests/test_ingestion_service.py", + "target": "class:tests/test_ingestion_service.py:TestDeduplicate", + "type": "contains", + "direction": "forward", + "weight": 1.0 + }, + { + "source": "file:tests/test_ingestion_service.py", + "target": "class:tests/test_ingestion_service.py:TestParseCsv", + "type": "contains", + "direction": "forward", + "weight": 1.0 + }, + { + "source": "file:tests/test_ingestion_service.py", + "target": "class:tests/test_ingestion_service.py:TestParseJson", + "type": "contains", + "direction": "forward", + "weight": 1.0 + }, + { + "source": "file:tests/test_ingestion_service.py", + "target": "class:tests/test_ingestion_service.py:TestGroupByBc", + "type": "contains", + "direction": "forward", + "weight": 1.0 + }, + { + "source": "file:tests/test_models.py", + "target": "class:tests/test_models.py:TestAuditLogJsonProperties", + "type": "contains", + "direction": "forward", + "weight": 1.0 + }, + { + "source": "file:tests/test_models.py", + "target": "class:tests/test_models.py:TestIngestionRecordProperties", + "type": "contains", + "direction": "forward", + "weight": 1.0 + }, + { + "source": "file:tests/test_models.py", + "target": "class:tests/test_models.py:TestBiomedicalConceptToDict", + "type": "contains", + "direction": "forward", + "weight": 1.0 + }, + { + "source": "file:models/audit.py", + "target": "file:tests/test_audit_routes.py", + "type": "tested_by", + "direction": "forward", + "weight": 0.5 + }, + { + "source": "file:routes/bc.py", + "target": "file:tests/test_bc_routes.py", + "type": "tested_by", + "direction": "forward", + "weight": 0.5 + }, + { + "source": "file:models/bc.py", + "target": "file:tests/test_governance_routes.py", + "type": "tested_by", + "direction": "forward", + "weight": 0.5 + }, + { + "source": "file:models/governance.py", + "target": "file:tests/test_governance_routes.py", + "type": "tested_by", + "direction": "forward", + "weight": 0.5 + }, + { + "source": "file:routes/governance.py", + "target": "file:tests/test_governance_routes.py", + "type": "tested_by", + "direction": "forward", + "weight": 0.5 + }, + { + "source": "file:models/bc.py", + "target": "file:tests/test_ingestion_routes.py", + "type": "tested_by", + "direction": "forward", + "weight": 0.5 + }, + { + "source": "file:models/ingestion.py", + "target": "file:tests/test_ingestion_routes.py", + "type": "tested_by", + "direction": "forward", + "weight": 0.5 + }, + { + "source": "file:models/governance.py", + "target": "file:extensions.py", + "type": "imports", + "direction": "forward", + "weight": 0.7 + }, + { + "source": "file:routes/bc.py", + "target": "file:extensions.py", + "type": "imports", + "direction": "forward", + "weight": 0.7 + }, + { + "source": "file:routes/bc.py", + "target": "file:models/bc.py", + "type": "imports", + "direction": "forward", + "weight": 0.7 + }, + { + "source": "file:routes/bc.py", + "target": "file:models/governance.py", + "type": "imports", + "direction": "forward", + "weight": 0.7 + }, + { + "source": "file:routes/bc.py", + "target": "file:services/__init__.py", + "type": "imports", + "direction": "forward", + "weight": 0.7 + }, + { + "source": "file:routes/bc.py", + "target": "file:services/audit.py", + "type": "imports", + "direction": "forward", + "weight": 0.7 + }, + { + "source": "file:routes/bc.py", + "target": "file:services/bc_service.py", + "type": "imports", + "direction": "forward", + "weight": 0.7 + }, + { + "source": "file:routes/bc.py", + "target": "file:services/cdisc_api.py", + "type": "imports", + "direction": "forward", + "weight": 0.7 + }, + { + "source": "file:routes/bc.py", + "target": "file:services/export.py", + "type": "imports", + "direction": "forward", + "weight": 0.7 + }, + { + "source": "file:routes/bc.py", + "target": "file:services/loinc_api.py", + "type": "imports", + "direction": "forward", + "weight": 0.7 + }, + { + "source": "file:routes/bc.py", + "target": "file:services/ncit_api.py", + "type": "imports", + "direction": "forward", + "weight": 0.7 + }, + { + "source": "file:routes/governance.py", + "target": "file:extensions.py", + "type": "imports", + "direction": "forward", + "weight": 0.7 + }, + { + "source": "file:routes/governance.py", + "target": "file:models/bc.py", + "type": "imports", + "direction": "forward", + "weight": 0.7 + }, + { + "source": "file:routes/governance.py", + "target": "file:services/__init__.py", + "type": "imports", + "direction": "forward", + "weight": 0.7 + }, + { + "source": "file:routes/governance.py", + "target": "file:services/export.py", + "type": "imports", + "direction": "forward", + "weight": 0.7 + }, + { + "source": "file:routes/governance.py", + "target": "file:services/governance_service.py", + "type": "imports", + "direction": "forward", + "weight": 0.7 + }, + { + "source": "file:routes/ncit.py", + "target": "file:extensions.py", + "type": "imports", + "direction": "forward", + "weight": 0.7 + }, + { + "source": "file:routes/ncit.py", + "target": "file:models/bc.py", + "type": "imports", + "direction": "forward", + "weight": 0.7 + }, + { + "source": "file:routes/ncit.py", + "target": "file:services/__init__.py", + "type": "imports", + "direction": "forward", + "weight": 0.7 + }, + { + "source": "file:routes/ncit.py", + "target": "file:services/bc_service.py", + "type": "imports", + "direction": "forward", + "weight": 0.7 + }, + { + "source": "file:routes/ncit.py", + "target": "file:services/ncit_api.py", + "type": "imports", + "direction": "forward", + "weight": 0.7 + }, + { + "source": "file:services/audit.py", + "target": "file:extensions.py", + "type": "imports", + "direction": "forward", + "weight": 0.7 + }, + { + "source": "file:services/audit.py", + "target": "file:models/audit.py", + "type": "imports", + "direction": "forward", + "weight": 0.7 + }, + { + "source": "file:models/governance.py", + "target": "class:models/governance.py:GovernanceRecord", + "type": "contains", + "direction": "forward", + "weight": 1.0 + }, + { + "source": "file:routes/bc.py", + "target": "function:routes/bc.py:index", + "type": "contains", + "direction": "forward", + "weight": 1.0 + }, + { + "source": "file:routes/bc.py", + "target": "function:routes/bc.py:new_bc", + "type": "contains", + "direction": "forward", + "weight": 1.0 + }, + { + "source": "file:routes/bc.py", + "target": "function:routes/bc.py:export", + "type": "contains", + "direction": "forward", + "weight": 1.0 + }, + { + "source": "file:routes/bc.py", + "target": "function:routes/bc.py:library_detail", + "type": "contains", + "direction": "forward", + "weight": 1.0 + }, + { + "source": "file:routes/bc.py", + "target": "function:routes/bc.py:detail", + "type": "contains", + "direction": "forward", + "weight": 1.0 + }, + { + "source": "file:routes/bc.py", + "target": "function:routes/bc.py:fetch_metadata", + "type": "contains", + "direction": "forward", + "weight": 1.0 + }, + { + "source": "file:routes/bc.py", + "target": "function:routes/bc.py:create", + "type": "contains", + "direction": "forward", + "weight": 1.0 + }, + { + "source": "file:routes/bc.py", + "target": "function:routes/bc.py:edit", + "type": "contains", + "direction": "forward", + "weight": 1.0 + }, + { + "source": "file:routes/bc.py", + "target": "function:routes/bc.py:clear_ncit", + "type": "contains", + "direction": "forward", + "weight": 1.0 + }, + { + "source": "file:routes/bc.py", + "target": "function:routes/bc.py:clear_loinc", + "type": "contains", + "direction": "forward", + "weight": 1.0 + }, + { + "source": "file:routes/bc.py", + "target": "function:routes/bc.py:submit_for_review", + "type": "contains", + "direction": "forward", + "weight": 1.0 + }, + { + "source": "file:routes/bc.py", + "target": "function:routes/bc.py:delete", + "type": "contains", + "direction": "forward", + "weight": 1.0 + }, + { + "source": "file:routes/bc.py", + "target": "function:routes/bc.py:_decs_from_form", + "type": "contains", + "direction": "forward", + "weight": 1.0 + }, + { + "source": "file:routes/governance.py", + "target": "function:routes/governance.py:board", + "type": "contains", + "direction": "forward", + "weight": 1.0 + }, + { + "source": "file:routes/governance.py", + "target": "function:routes/governance.py:export", + "type": "contains", + "direction": "forward", + "weight": 1.0 + }, + { + "source": "file:routes/governance.py", + "target": "function:routes/governance.py:advance", + "type": "contains", + "direction": "forward", + "weight": 1.0 + }, + { + "source": "file:routes/governance.py", + "target": "function:routes/governance.py:reject_bc", + "type": "contains", + "direction": "forward", + "weight": 1.0 + }, + { + "source": "file:routes/ncit.py", + "target": "function:routes/ncit.py:index", + "type": "contains", + "direction": "forward", + "weight": 1.0 + }, + { + "source": "file:routes/ncit.py", + "target": "function:routes/ncit.py:mapping", + "type": "contains", + "direction": "forward", + "weight": 1.0 + }, + { + "source": "file:routes/ncit.py", + "target": "function:routes/ncit.py:search_ncit", + "type": "contains", + "direction": "forward", + "weight": 1.0 + }, + { + "source": "file:routes/ncit.py", + "target": "function:routes/ncit.py:concept_detail", + "type": "contains", + "direction": "forward", + "weight": 1.0 + }, + { + "source": "file:routes/ncit.py", + "target": "function:routes/ncit.py:resolve", + "type": "contains", + "direction": "forward", + "weight": 1.0 + }, + { + "source": "file:services/audit.py", + "target": "function:services/audit.py:log_change", + "type": "contains", + "direction": "forward", + "weight": 1.0 + }, + { + "source": "file:models/governance.py", + "target": "class:models/governance.py:GovernanceRecord", + "type": "exports", + "direction": "forward", + "weight": 0.8 + }, + { + "source": "file:routes/bc.py", + "target": "function:routes/bc.py:index", + "type": "exports", + "direction": "forward", + "weight": 0.8 + }, + { + "source": "file:routes/bc.py", + "target": "function:routes/bc.py:new_bc", + "type": "exports", + "direction": "forward", + "weight": 0.8 + }, + { + "source": "file:routes/bc.py", + "target": "function:routes/bc.py:export", + "type": "exports", + "direction": "forward", + "weight": 0.8 + }, + { + "source": "file:routes/bc.py", + "target": "function:routes/bc.py:library_detail", + "type": "exports", + "direction": "forward", + "weight": 0.8 + }, + { + "source": "file:routes/bc.py", + "target": "function:routes/bc.py:detail", + "type": "exports", + "direction": "forward", + "weight": 0.8 + }, + { + "source": "file:routes/bc.py", + "target": "function:routes/bc.py:fetch_metadata", + "type": "exports", + "direction": "forward", + "weight": 0.8 + }, + { + "source": "file:routes/bc.py", + "target": "function:routes/bc.py:create", + "type": "exports", + "direction": "forward", + "weight": 0.8 + }, + { + "source": "file:routes/bc.py", + "target": "function:routes/bc.py:edit", + "type": "exports", + "direction": "forward", + "weight": 0.8 + }, + { + "source": "file:routes/bc.py", + "target": "function:routes/bc.py:clear_ncit", + "type": "exports", + "direction": "forward", + "weight": 0.8 + }, + { + "source": "file:routes/bc.py", + "target": "function:routes/bc.py:clear_loinc", + "type": "exports", + "direction": "forward", + "weight": 0.8 + }, + { + "source": "file:routes/bc.py", + "target": "function:routes/bc.py:submit_for_review", + "type": "exports", + "direction": "forward", + "weight": 0.8 + }, + { + "source": "file:routes/bc.py", + "target": "function:routes/bc.py:delete", + "type": "exports", + "direction": "forward", + "weight": 0.8 + }, + { + "source": "file:routes/bc.py", + "target": "function:routes/bc.py:_decs_from_form", + "type": "exports", + "direction": "forward", + "weight": 0.8 + }, + { + "source": "file:routes/governance.py", + "target": "function:routes/governance.py:board", + "type": "exports", + "direction": "forward", + "weight": 0.8 + }, + { + "source": "file:routes/governance.py", + "target": "function:routes/governance.py:export", + "type": "exports", + "direction": "forward", + "weight": 0.8 + }, + { + "source": "file:routes/governance.py", + "target": "function:routes/governance.py:advance", + "type": "exports", + "direction": "forward", + "weight": 0.8 + }, + { + "source": "file:routes/governance.py", + "target": "function:routes/governance.py:reject_bc", + "type": "exports", + "direction": "forward", + "weight": 0.8 + }, + { + "source": "file:routes/ncit.py", + "target": "function:routes/ncit.py:index", + "type": "exports", + "direction": "forward", + "weight": 0.8 + }, + { + "source": "file:routes/ncit.py", + "target": "function:routes/ncit.py:mapping", + "type": "exports", + "direction": "forward", + "weight": 0.8 + }, + { + "source": "file:routes/ncit.py", + "target": "function:routes/ncit.py:search_ncit", + "type": "exports", + "direction": "forward", + "weight": 0.8 + }, + { + "source": "file:routes/ncit.py", + "target": "function:routes/ncit.py:concept_detail", + "type": "exports", + "direction": "forward", + "weight": 0.8 + }, + { + "source": "file:routes/ncit.py", + "target": "function:routes/ncit.py:resolve", + "type": "exports", + "direction": "forward", + "weight": 0.8 + }, + { + "source": "file:services/audit.py", + "target": "function:services/audit.py:log_change", + "type": "exports", + "direction": "forward", + "weight": 0.8 + }, + { + "source": "function:routes/bc.py:create", + "target": "function:services/bc_service.py:create_bc", + "type": "calls", + "direction": "forward", + "weight": 0.8 + }, + { + "source": "function:routes/bc.py:create", + "target": "function:services/bc_service.py:save_decs", + "type": "calls", + "direction": "forward", + "weight": 0.8 + }, + { + "source": "function:routes/bc.py:edit", + "target": "function:services/bc_service.py:update_bc", + "type": "calls", + "direction": "forward", + "weight": 0.8 + }, + { + "source": "function:routes/bc.py:edit", + "target": "function:services/bc_service.py:save_decs", + "type": "calls", + "direction": "forward", + "weight": 0.8 + }, + { + "source": "function:routes/bc.py:submit_for_review", + "target": "function:services/bc_service.py:submit_bc_for_review", + "type": "calls", + "direction": "forward", + "weight": 0.8 + }, + { + "source": "function:routes/bc.py:clear_ncit", + "target": "function:services/audit.py:log_change", + "type": "calls", + "direction": "forward", + "weight": 0.8 + }, + { + "source": "function:routes/bc.py:clear_loinc", + "target": "function:services/audit.py:log_change", + "type": "calls", + "direction": "forward", + "weight": 0.8 + }, + { + "source": "function:routes/bc.py:delete", + "target": "function:services/audit.py:log_change", + "type": "calls", + "direction": "forward", + "weight": 0.8 + }, + { + "source": "function:routes/bc.py:library_detail", + "target": "class:services/cdisc_api.py:CDISCApiClient", + "type": "calls", + "direction": "forward", + "weight": 0.8 + }, + { + "source": "function:routes/bc.py:library_detail", + "target": "class:services/loinc_api.py:LoincApiClient", + "type": "calls", + "direction": "forward", + "weight": 0.8 + }, + { + "source": "function:routes/bc.py:detail", + "target": "class:services/loinc_api.py:LoincApiClient", + "type": "calls", + "direction": "forward", + "weight": 0.8 + }, + { + "source": "function:routes/bc.py:fetch_metadata", + "target": "class:services/loinc_api.py:LoincApiClient", + "type": "calls", + "direction": "forward", + "weight": 0.8 + }, + { + "source": "function:routes/bc.py:export", + "target": "function:services/export.py:export_xlsx", + "type": "calls", + "direction": "forward", + "weight": 0.8 + }, + { + "source": "function:routes/bc.py:export", + "target": "function:services/export.py:export_odm_xml", + "type": "calls", + "direction": "forward", + "weight": 0.8 + }, + { + "source": "function:routes/bc.py:export", + "target": "function:services/export.py:export_json", + "type": "calls", + "direction": "forward", + "weight": 0.8 + }, + { + "source": "function:routes/governance.py:export", + "target": "function:services/export.py:export_governance_xlsx", + "type": "calls", + "direction": "forward", + "weight": 0.8 + }, + { + "source": "function:routes/governance.py:advance", + "target": "function:services/governance_service.py:advance_governance", + "type": "calls", + "direction": "forward", + "weight": 0.8 + }, + { + "source": "function:routes/governance.py:reject_bc", + "target": "function:services/governance_service.py:reject_bc", + "type": "calls", + "direction": "forward", + "weight": 0.8 + }, + { + "source": "function:routes/ncit.py:search_ncit", + "target": "class:services/ncit_api.py:NCItApiClient", + "type": "calls", + "direction": "forward", + "weight": 0.8 + }, + { + "source": "function:routes/ncit.py:concept_detail", + "target": "class:services/ncit_api.py:NCItApiClient", + "type": "calls", + "direction": "forward", + "weight": 0.8 + }, + { + "source": "function:routes/ncit.py:resolve", + "target": "function:services/bc_service.py:map_ncit_to_bc", + "type": "calls", + "direction": "forward", + "weight": 0.8 + }, + { + "source": "function:services/audit.py:log_change", + "target": "class:models/audit.py:AuditLog", + "type": "calls", + "direction": "forward", + "weight": 0.8 + }, + { + "source": "file:routes/ncit.py", + "target": "file:tests/test_ncit.py", + "type": "tested_by", + "direction": "forward", + "weight": 0.5 + }, + { + "source": "file:services/bc_service.py", + "target": "file:extensions.py", + "type": "imports", + "direction": "forward", + "weight": 0.7 + }, + { + "source": "file:services/bc_service.py", + "target": "file:models/bc.py", + "type": "imports", + "direction": "forward", + "weight": 0.7 + }, + { + "source": "file:services/bc_service.py", + "target": "file:models/governance.py", + "type": "imports", + "direction": "forward", + "weight": 0.7 + }, + { + "source": "file:services/bc_service.py", + "target": "file:models/specialization.py", + "type": "imports", + "direction": "forward", + "weight": 0.7 + }, + { + "source": "file:services/bc_service.py", + "target": "file:services/audit.py", + "type": "imports", + "direction": "forward", + "weight": 0.7 + }, + { + "source": "file:services/governance_service.py", + "target": "file:extensions.py", + "type": "imports", + "direction": "forward", + "weight": 0.7 + }, + { + "source": "file:services/governance_service.py", + "target": "file:models/governance.py", + "type": "imports", + "direction": "forward", + "weight": 0.7 + }, + { + "source": "file:services/governance_service.py", + "target": "file:services/audit.py", + "type": "imports", + "direction": "forward", + "weight": 0.7 + }, + { + "source": "file:services/governance_service.py", + "target": "file:services/bc_service.py", + "type": "imports", + "direction": "forward", + "weight": 0.7 + }, + { + "source": "file:services/ncit_api.py", + "target": "file:services/api_cache.py", + "type": "imports", + "direction": "forward", + "weight": 0.7 + }, + { + "source": "file:tests/test_export_service.py", + "target": "file:extensions.py", + "type": "imports", + "direction": "forward", + "weight": 0.7 + }, + { + "source": "file:tests/test_export_service.py", + "target": "file:models/bc.py", + "type": "imports", + "direction": "forward", + "weight": 0.7 + }, + { + "source": "file:tests/test_export_service.py", + "target": "file:services/export.py", + "type": "imports", + "direction": "forward", + "weight": 0.7 + }, + { + "source": "file:tests/test_ncit.py", + "target": "file:services/ncit_api.py", + "type": "imports", + "direction": "forward", + "weight": 0.7 + }, + { + "source": "file:services/bc_service.py", + "target": "function:services/bc_service.py:_get_bc_or_raise", + "type": "contains", + "direction": "forward", + "weight": 1.0 + }, + { + "source": "file:services/bc_service.py", + "target": "function:services/bc_service.py:apply_bc_fields", + "type": "contains", + "direction": "forward", + "weight": 1.0 + }, + { + "source": "file:services/bc_service.py", + "target": "function:services/bc_service.py:create_bc", + "type": "contains", + "direction": "forward", + "weight": 1.0 + }, + { + "source": "file:services/bc_service.py", + "target": "function:services/bc_service.py:update_bc", + "type": "contains", + "direction": "forward", + "weight": 1.0 + }, + { + "source": "file:services/bc_service.py", + "target": "function:services/bc_service.py:save_decs", + "type": "contains", + "direction": "forward", + "weight": 1.0 + }, + { + "source": "file:services/bc_service.py", + "target": "function:services/bc_service.py:map_ncit_to_bc", + "type": "contains", + "direction": "forward", + "weight": 1.0 + }, + { + "source": "file:services/bc_service.py", + "target": "function:services/bc_service.py:submit_bc_for_review", + "type": "contains", + "direction": "forward", + "weight": 1.0 + }, + { + "source": "file:services/export.py", + "target": "function:services/export.py:export_json", + "type": "contains", + "direction": "forward", + "weight": 1.0 + }, + { + "source": "file:services/export.py", + "target": "function:services/export.py:export_xlsx", + "type": "contains", + "direction": "forward", + "weight": 1.0 + }, + { + "source": "file:services/export.py", + "target": "function:services/export.py:export_governance_xlsx", + "type": "contains", + "direction": "forward", + "weight": 1.0 + }, + { + "source": "file:services/export.py", + "target": "function:services/export.py:export_odm_xml", + "type": "contains", + "direction": "forward", + "weight": 1.0 + }, + { + "source": "file:services/governance_service.py", + "target": "function:services/governance_service.py:advance_governance", + "type": "contains", + "direction": "forward", + "weight": 1.0 + }, + { + "source": "file:services/governance_service.py", + "target": "function:services/governance_service.py:reject_bc", + "type": "contains", + "direction": "forward", + "weight": 1.0 + }, + { + "source": "file:services/ncit_api.py", + "target": "function:services/ncit_api.py:_pick_definition", + "type": "contains", + "direction": "forward", + "weight": 1.0 + }, + { + "source": "file:services/ncit_api.py", + "target": "class:services/ncit_api.py:NCItApiClient", + "type": "contains", + "direction": "forward", + "weight": 1.0 + }, + { + "source": "file:tests/test_export_service.py", + "target": "class:tests/test_export_service.py:TestExportJson", + "type": "contains", + "direction": "forward", + "weight": 1.0 + }, + { + "source": "file:tests/test_export_service.py", + "target": "class:tests/test_export_service.py:TestExportXlsx", + "type": "contains", + "direction": "forward", + "weight": 1.0 + }, + { + "source": "file:tests/test_export_service.py", + "target": "class:tests/test_export_service.py:TestExportGovernanceXlsx", + "type": "contains", + "direction": "forward", + "weight": 1.0 + }, + { + "source": "file:tests/test_export_service.py", + "target": "class:tests/test_export_service.py:TestExportOdmXml", + "type": "contains", + "direction": "forward", + "weight": 1.0 + }, + { + "source": "file:tests/test_ncit.py", + "target": "class:tests/test_ncit.py:TestNcitGetConceptExtended", + "type": "contains", + "direction": "forward", + "weight": 1.0 + }, + { + "source": "file:tests/test_ncit.py", + "target": "class:tests/test_ncit.py:TestNcitConceptRoute", + "type": "contains", + "direction": "forward", + "weight": 1.0 + }, + { + "source": "file:services/bc_service.py", + "target": "function:services/bc_service.py:_get_bc_or_raise", + "type": "exports", + "direction": "forward", + "weight": 0.8 + }, + { + "source": "file:services/bc_service.py", + "target": "function:services/bc_service.py:apply_bc_fields", + "type": "exports", + "direction": "forward", + "weight": 0.8 + }, + { + "source": "file:services/bc_service.py", + "target": "function:services/bc_service.py:create_bc", + "type": "exports", + "direction": "forward", + "weight": 0.8 + }, + { + "source": "file:services/bc_service.py", + "target": "function:services/bc_service.py:update_bc", + "type": "exports", + "direction": "forward", + "weight": 0.8 + }, + { + "source": "file:services/bc_service.py", + "target": "function:services/bc_service.py:save_decs", + "type": "exports", + "direction": "forward", + "weight": 0.8 + }, + { + "source": "file:services/bc_service.py", + "target": "function:services/bc_service.py:map_ncit_to_bc", + "type": "exports", + "direction": "forward", + "weight": 0.8 + }, + { + "source": "file:services/bc_service.py", + "target": "function:services/bc_service.py:submit_bc_for_review", + "type": "exports", + "direction": "forward", + "weight": 0.8 + }, + { + "source": "file:services/export.py", + "target": "function:services/export.py:export_json", + "type": "exports", + "direction": "forward", + "weight": 0.8 + }, + { + "source": "file:services/export.py", + "target": "function:services/export.py:export_xlsx", + "type": "exports", + "direction": "forward", + "weight": 0.8 + }, + { + "source": "file:services/export.py", + "target": "function:services/export.py:export_governance_xlsx", + "type": "exports", + "direction": "forward", + "weight": 0.8 + }, + { + "source": "file:services/export.py", + "target": "function:services/export.py:export_odm_xml", + "type": "exports", + "direction": "forward", + "weight": 0.8 + }, + { + "source": "file:services/governance_service.py", + "target": "function:services/governance_service.py:advance_governance", + "type": "exports", + "direction": "forward", + "weight": 0.8 + }, + { + "source": "file:services/governance_service.py", + "target": "function:services/governance_service.py:reject_bc", + "type": "exports", + "direction": "forward", + "weight": 0.8 + }, + { + "source": "file:services/ncit_api.py", + "target": "function:services/ncit_api.py:_pick_definition", + "type": "exports", + "direction": "forward", + "weight": 0.8 + }, + { + "source": "file:services/ncit_api.py", + "target": "class:services/ncit_api.py:NCItApiClient", + "type": "exports", + "direction": "forward", + "weight": 0.8 + }, + { + "source": "file:tests/test_export_service.py", + "target": "class:tests/test_export_service.py:TestExportJson", + "type": "exports", + "direction": "forward", + "weight": 0.8 + }, + { + "source": "file:tests/test_export_service.py", + "target": "class:tests/test_export_service.py:TestExportXlsx", + "type": "exports", + "direction": "forward", + "weight": 0.8 + }, + { + "source": "file:tests/test_export_service.py", + "target": "class:tests/test_export_service.py:TestExportGovernanceXlsx", + "type": "exports", + "direction": "forward", + "weight": 0.8 + }, + { + "source": "file:tests/test_export_service.py", + "target": "class:tests/test_export_service.py:TestExportOdmXml", + "type": "exports", + "direction": "forward", + "weight": 0.8 + }, + { + "source": "file:tests/test_ncit.py", + "target": "class:tests/test_ncit.py:TestNcitGetConceptExtended", + "type": "exports", + "direction": "forward", + "weight": 0.8 + }, + { + "source": "file:tests/test_ncit.py", + "target": "class:tests/test_ncit.py:TestNcitConceptRoute", + "type": "exports", + "direction": "forward", + "weight": 0.8 + }, + { + "source": "function:services/bc_service.py:create_bc", + "target": "function:services/audit.py:log_change", + "type": "calls", + "direction": "forward", + "weight": 0.8 + }, + { + "source": "function:services/bc_service.py:update_bc", + "target": "function:services/audit.py:log_change", + "type": "calls", + "direction": "forward", + "weight": 0.8 + }, + { + "source": "function:services/bc_service.py:map_ncit_to_bc", + "target": "function:services/audit.py:log_change", + "type": "calls", + "direction": "forward", + "weight": 0.8 + }, + { + "source": "function:services/bc_service.py:submit_bc_for_review", + "target": "function:services/audit.py:log_change", + "type": "calls", + "direction": "forward", + "weight": 0.8 + }, + { + "source": "function:services/governance_service.py:advance_governance", + "target": "function:services/audit.py:log_change", + "type": "calls", + "direction": "forward", + "weight": 0.8 + }, + { + "source": "function:services/governance_service.py:advance_governance", + "target": "class:models/governance.py:GovernanceRecord", + "type": "calls", + "direction": "forward", + "weight": 0.8 + }, + { + "source": "function:services/governance_service.py:reject_bc", + "target": "function:services/audit.py:log_change", + "type": "calls", + "direction": "forward", + "weight": 0.8 + }, + { + "source": "function:services/governance_service.py:reject_bc", + "target": "class:models/governance.py:GovernanceRecord", + "type": "calls", + "direction": "forward", + "weight": 0.8 + }, + { + "source": "class:services/ncit_api.py:NCItApiClient", + "target": "function:services/api_cache.py:cached", + "type": "calls", + "direction": "forward", + "weight": 0.8 + }, + { + "source": "file:services/export.py", + "target": "file:tests/test_export_service.py", + "type": "tested_by", + "direction": "forward", + "weight": 0.5 + }, + { + "source": "file:services/ncit_api.py", + "target": "file:tests/test_ncit.py", + "type": "tested_by", + "direction": "forward", + "weight": 0.5 + }, + { + "source": "file:routes/dashboard.py", + "target": "file:models/audit.py", + "type": "imports", + "direction": "forward", + "weight": 0.7 + }, + { + "source": "file:routes/dashboard.py", + "target": "file:models/bc.py", + "type": "imports", + "direction": "forward", + "weight": 0.7 + }, + { + "source": "file:routes/dashboard.py", + "target": "file:services/cdisc_api.py", + "type": "imports", + "direction": "forward", + "weight": 0.7 + }, + { + "source": "file:routes/loinc.py", + "target": "file:services/loinc_api.py", + "type": "imports", + "direction": "forward", + "weight": 0.7 + }, + { + "source": "file:routes/specializations.py", + "target": "file:extensions.py", + "type": "imports", + "direction": "forward", + "weight": 0.7 + }, + { + "source": "file:routes/specializations.py", + "target": "file:models/bc.py", + "type": "imports", + "direction": "forward", + "weight": 0.7 + }, + { + "source": "file:routes/specializations.py", + "target": "file:models/specialization.py", + "type": "imports", + "direction": "forward", + "weight": 0.7 + }, + { + "source": "file:routes/specializations.py", + "target": "file:services/audit.py", + "type": "imports", + "direction": "forward", + "weight": 0.7 + }, + { + "source": "file:routes/specializations.py", + "target": "file:services/cdisc_api.py", + "type": "imports", + "direction": "forward", + "weight": 0.7 + }, + { + "source": "file:services/cdisc_api.py", + "target": "file:services/api_cache.py", + "type": "imports", + "direction": "forward", + "weight": 0.7 + }, + { + "source": "file:services/loinc_api.py", + "target": "file:services/api_cache.py", + "type": "imports", + "direction": "forward", + "weight": 0.7 + }, + { + "source": "file:tests/test_cdisc_api_cache.py", + "target": "file:services/cdisc_api.py", + "type": "imports", + "direction": "forward", + "weight": 0.7 + }, + { + "source": "file:tests/test_loinc.py", + "target": "file:services/loinc_api.py", + "type": "imports", + "direction": "forward", + "weight": 0.7 + }, + { + "source": "file:routes/dashboard.py", + "target": "function:routes/dashboard.py:index", + "type": "contains", + "direction": "forward", + "weight": 1.0 + }, + { + "source": "file:routes/loinc.py", + "target": "function:routes/loinc.py:search", + "type": "contains", + "direction": "forward", + "weight": 1.0 + }, + { + "source": "file:routes/specializations.py", + "target": "function:routes/specializations.py:_get_bc_options", + "type": "contains", + "direction": "forward", + "weight": 1.0 + }, + { + "source": "file:routes/specializations.py", + "target": "function:routes/specializations.py:index", + "type": "contains", + "direction": "forward", + "weight": 1.0 + }, + { + "source": "file:routes/specializations.py", + "target": "function:routes/specializations.py:library_detail", + "type": "contains", + "direction": "forward", + "weight": 1.0 + }, + { + "source": "file:routes/specializations.py", + "target": "function:routes/specializations.py:detail", + "type": "contains", + "direction": "forward", + "weight": 1.0 + }, + { + "source": "file:routes/specializations.py", + "target": "function:routes/specializations.py:_variables_from_form", + "type": "contains", + "direction": "forward", + "weight": 1.0 + }, + { + "source": "file:routes/specializations.py", + "target": "function:routes/specializations.py:create", + "type": "contains", + "direction": "forward", + "weight": 1.0 + }, + { + "source": "file:routes/specializations.py", + "target": "function:routes/specializations.py:delete", + "type": "contains", + "direction": "forward", + "weight": 1.0 + }, + { + "source": "file:routes/specializations.py", + "target": "function:routes/specializations.py:generate_from_dec", + "type": "contains", + "direction": "forward", + "weight": 1.0 + }, + { + "source": "file:routes/specializations.py", + "target": "function:routes/specializations.py:generate", + "type": "contains", + "direction": "forward", + "weight": 1.0 + }, + { + "source": "file:services/api_cache.py", + "target": "function:services/api_cache.py:cached", + "type": "contains", + "direction": "forward", + "weight": 1.0 + }, + { + "source": "file:services/cdisc_api.py", + "target": "function:services/cdisc_api.py:_config_value", + "type": "contains", + "direction": "forward", + "weight": 1.0 + }, + { + "source": "file:services/cdisc_api.py", + "target": "class:services/cdisc_api.py:CDISCApiClient", + "type": "contains", + "direction": "forward", + "weight": 1.0 + }, + { + "source": "file:services/loinc_api.py", + "target": "class:services/loinc_api.py:LoincApiClient", + "type": "contains", + "direction": "forward", + "weight": 1.0 + }, + { + "source": "file:tests/test_cdisc_api_cache.py", + "target": "class:tests/test_cdisc_api_cache.py:TestCachedHelper", + "type": "contains", + "direction": "forward", + "weight": 1.0 + }, + { + "source": "file:tests/test_cdisc_api_cache.py", + "target": "class:tests/test_cdisc_api_cache.py:TestClientCacheKeys", + "type": "contains", + "direction": "forward", + "weight": 1.0 + }, + { + "source": "file:tests/test_loinc.py", + "target": "class:tests/test_loinc.py:TestLoincApiClientSearch", + "type": "contains", + "direction": "forward", + "weight": 1.0 + }, + { + "source": "file:tests/test_loinc.py", + "target": "class:tests/test_loinc.py:TestLoincSearchRoute", + "type": "contains", + "direction": "forward", + "weight": 1.0 + }, + { + "source": "file:services/cdisc_api.py", + "target": "class:services/cdisc_api.py:CDISCApiClient", + "type": "exports", + "direction": "forward", + "weight": 0.8 + }, + { + "source": "file:services/loinc_api.py", + "target": "class:services/loinc_api.py:LoincApiClient", + "type": "exports", + "direction": "forward", + "weight": 0.8 + }, + { + "source": "file:services/api_cache.py", + "target": "function:services/api_cache.py:cached", + "type": "exports", + "direction": "forward", + "weight": 0.8 + }, + { + "source": "function:routes/dashboard.py:index", + "target": "class:services/cdisc_api.py:CDISCApiClient", + "type": "calls", + "direction": "forward", + "weight": 0.8 + }, + { + "source": "function:routes/dashboard.py:index", + "target": "class:models/bc.py:BiomedicalConcept", + "type": "calls", + "direction": "forward", + "weight": 0.8 + }, + { + "source": "function:routes/dashboard.py:index", + "target": "class:models/audit.py:AuditLog", + "type": "calls", + "direction": "forward", + "weight": 0.8 + }, + { + "source": "function:routes/loinc.py:search", + "target": "class:services/loinc_api.py:LoincApiClient", + "type": "calls", + "direction": "forward", + "weight": 0.8 + }, + { + "source": "function:routes/specializations.py:_get_bc_options", + "target": "class:services/cdisc_api.py:CDISCApiClient", + "type": "calls", + "direction": "forward", + "weight": 0.8 + }, + { + "source": "function:routes/specializations.py:_get_bc_options", + "target": "class:models/bc.py:BiomedicalConcept", + "type": "calls", + "direction": "forward", + "weight": 0.8 + }, + { + "source": "function:routes/specializations.py:index", + "target": "class:models/specialization.py:DatasetSpecialization", + "type": "calls", + "direction": "forward", + "weight": 0.8 + }, + { + "source": "function:routes/specializations.py:library_detail", + "target": "class:services/cdisc_api.py:CDISCApiClient", + "type": "calls", + "direction": "forward", + "weight": 0.8 + }, + { + "source": "function:routes/specializations.py:detail", + "target": "class:models/specialization.py:DatasetSpecialization", + "type": "calls", + "direction": "forward", + "weight": 0.8 + }, + { + "source": "function:routes/specializations.py:create", + "target": "function:services/audit.py:log_change", + "type": "calls", + "direction": "forward", + "weight": 0.8 + }, + { + "source": "function:routes/specializations.py:create", + "target": "class:models/specialization.py:DatasetSpecialization", + "type": "calls", + "direction": "forward", + "weight": 0.8 + }, + { + "source": "function:routes/specializations.py:delete", + "target": "function:services/audit.py:log_change", + "type": "calls", + "direction": "forward", + "weight": 0.8 + }, + { + "source": "function:routes/specializations.py:generate_from_dec", + "target": "class:models/bc.py:DataElementConcept", + "type": "calls", + "direction": "forward", + "weight": 0.8 + }, + { + "source": "function:routes/specializations.py:generate", + "target": "class:models/bc.py:DataElementConcept", + "type": "calls", + "direction": "forward", + "weight": 0.8 + }, + { + "source": "function:routes/specializations.py:generate", + "target": "class:models/specialization.py:DatasetSpecialization", + "type": "calls", + "direction": "forward", + "weight": 0.8 + }, + { + "source": "function:routes/specializations.py:generate", + "target": "function:services/audit.py:log_change", + "type": "calls", + "direction": "forward", + "weight": 0.8 + }, + { + "source": "class:services/cdisc_api.py:CDISCApiClient", + "target": "function:services/api_cache.py:cached", + "type": "calls", + "direction": "forward", + "weight": 0.8 + }, + { + "source": "class:services/loinc_api.py:LoincApiClient", + "target": "function:services/api_cache.py:cached", + "type": "calls", + "direction": "forward", + "weight": 0.8 + }, + { + "source": "file:services/cdisc_api.py", + "target": "file:tests/test_cdisc_api_cache.py", + "type": "tested_by", + "direction": "forward", + "weight": 0.5 + }, + { + "source": "file:services/api_cache.py", + "target": "file:tests/test_cdisc_api_cache.py", + "type": "tested_by", + "direction": "forward", + "weight": 0.5 + }, + { + "source": "file:services/loinc_api.py", + "target": "file:tests/test_loinc.py", + "type": "tested_by", + "direction": "forward", + "weight": 0.5 + }, + { + "source": "file:routes/loinc.py", + "target": "file:tests/test_loinc.py", + "type": "tested_by", + "direction": "forward", + "weight": 0.5 + }, + { + "source": "file:app.py", + "target": "file:config.py", + "type": "imports", + "direction": "forward", + "weight": 0.7 + }, + { + "source": "file:app.py", + "target": "file:extensions.py", + "type": "imports", + "direction": "forward", + "weight": 0.7 + }, + { + "source": "file:db_bootstrap.py", + "target": "file:extensions.py", + "type": "imports", + "direction": "forward", + "weight": 0.7 + }, + { + "source": "file:models/__init__.py", + "target": "file:extensions.py", + "type": "imports", + "direction": "forward", + "weight": 0.7 + }, + { + "source": "file:tests/test_dashboard.py", + "target": "file:extensions.py", + "type": "imports", + "direction": "forward", + "weight": 0.7 + }, + { + "source": "file:tests/test_dashboard.py", + "target": "file:models/bc.py", + "type": "imports", + "direction": "forward", + "weight": 0.7 + }, + { + "source": "file:tests/test_db_bootstrap.py", + "target": "file:app.py", + "type": "imports", + "direction": "forward", + "weight": 0.7 + }, + { + "source": "file:tests/test_db_bootstrap.py", + "target": "file:db_bootstrap.py", + "type": "imports", + "direction": "forward", + "weight": 0.7 + }, + { + "source": "file:tests/test_db_bootstrap.py", + "target": "file:extensions.py", + "type": "imports", + "direction": "forward", + "weight": 0.7 + }, + { + "source": "file:app.py", + "target": "function:app.py:_configure_logging", + "type": "contains", + "direction": "forward", + "weight": 1.0 + }, + { + "source": "file:app.py", + "target": "function:app.py:create_app", + "type": "contains", + "direction": "forward", + "weight": 1.0 + }, + { + "source": "file:config.py", + "target": "class:config.py:Config", + "type": "contains", + "direction": "forward", + "weight": 1.0 + }, + { + "source": "file:db_bootstrap.py", + "target": "function:db_bootstrap.py:ensure_db", + "type": "contains", + "direction": "forward", + "weight": 1.0 + }, + { + "source": "file:extensions.py", + "target": "function:extensions.py:_set_sqlite_pragmas", + "type": "contains", + "direction": "forward", + "weight": 1.0 + }, + { + "source": "file:tests/test_dashboard.py", + "target": "class:tests/test_dashboard.py:TestDashboardIndex", + "type": "contains", + "direction": "forward", + "weight": 1.0 + }, + { + "source": "file:tests/test_db_bootstrap.py", + "target": "function:tests/test_db_bootstrap.py:_make_app", + "type": "contains", + "direction": "forward", + "weight": 1.0 + }, + { + "source": "file:tests/test_db_bootstrap.py", + "target": "class:tests/test_db_bootstrap.py:TestEnsureDb", + "type": "contains", + "direction": "forward", + "weight": 1.0 + }, + { + "source": "file:app.py", + "target": "function:app.py:create_app", + "type": "exports", + "direction": "forward", + "weight": 0.8 + }, + { + "source": "file:db_bootstrap.py", + "target": "function:db_bootstrap.py:ensure_db", + "type": "exports", + "direction": "forward", + "weight": 0.8 + }, + { + "source": "file:config.py", + "target": "class:config.py:Config", + "type": "exports", + "direction": "forward", + "weight": 0.8 + }, + { + "source": "function:tests/test_db_bootstrap.py:_make_app", + "target": "function:app.py:create_app", + "type": "calls", + "direction": "forward", + "weight": 0.8 + }, + { + "source": "class:tests/test_db_bootstrap.py:TestEnsureDb", + "target": "function:db_bootstrap.py:ensure_db", + "type": "calls", + "direction": "forward", + "weight": 0.8 + }, + { + "source": "class:tests/test_dashboard.py:TestDashboardIndex", + "target": "class:models/bc.py:BiomedicalConcept", + "type": "calls", + "direction": "forward", + "weight": 0.8 + }, + { + "source": "file:app.py", + "target": "file:db_bootstrap.py", + "type": "depends_on", + "direction": "forward", + "weight": 0.6 + }, + { + "source": "file:db_bootstrap.py", + "target": "file:tests/test_db_bootstrap.py", + "type": "tested_by", + "direction": "forward", + "weight": 0.5 + }, + { + "source": "file:mcp_server/__main__.py", + "target": "file:mcp_server/server.py", + "type": "imports", + "direction": "forward", + "weight": 0.7 + }, + { + "source": "file:models/specialization.py", + "target": "file:extensions.py", + "type": "imports", + "direction": "forward", + "weight": 0.7 + }, + { + "source": "file:tests/test_mcp_server.py", + "target": "file:extensions.py", + "type": "imports", + "direction": "forward", + "weight": 0.7 + }, + { + "source": "file:tests/test_mcp_server.py", + "target": "file:mcp_server/server.py", + "type": "imports", + "direction": "forward", + "weight": 0.7 + }, + { + "source": "file:tests/test_mcp_server.py", + "target": "file:models/audit.py", + "type": "imports", + "direction": "forward", + "weight": 0.7 + }, + { + "source": "file:tests/test_mcp_server.py", + "target": "file:models/bc.py", + "type": "imports", + "direction": "forward", + "weight": 0.7 + }, + { + "source": "file:tests/test_mcp_server.py", + "target": "file:models/governance.py", + "type": "imports", + "direction": "forward", + "weight": 0.7 + }, + { + "source": "file:tests/test_mcp_server.py", + "target": "file:models/ingestion.py", + "type": "imports", + "direction": "forward", + "weight": 0.7 + }, + { + "source": "file:tests/test_mcp_server.py", + "target": "file:models/specialization.py", + "type": "imports", + "direction": "forward", + "weight": 0.7 + }, + { + "source": "file:tests/test_specializations_routes.py", + "target": "file:extensions.py", + "type": "imports", + "direction": "forward", + "weight": 0.7 + }, + { + "source": "file:tests/test_specializations_routes.py", + "target": "file:models/audit.py", + "type": "imports", + "direction": "forward", + "weight": 0.7 + }, + { + "source": "file:tests/test_specializations_routes.py", + "target": "file:models/bc.py", + "type": "imports", + "direction": "forward", + "weight": 0.7 + }, + { + "source": "file:tests/test_specializations_routes.py", + "target": "file:models/specialization.py", + "type": "imports", + "direction": "forward", + "weight": 0.7 + }, + { + "source": "file:mcp_server/server.py", + "target": "function:mcp_server/server.py:_with_app_context", + "type": "contains", + "direction": "forward", + "weight": 1.0 + }, + { + "source": "file:mcp_server/server.py", + "target": "function:mcp_server/server.py:_dispatch", + "type": "contains", + "direction": "forward", + "weight": 1.0 + }, + { + "source": "file:mcp_server/server.py", + "target": "function:mcp_server/server.py:_bc_full_dict", + "type": "contains", + "direction": "forward", + "weight": 1.0 + }, + { + "source": "file:mcp_server/server.py", + "target": "function:mcp_server/server.py:_list_bcs", + "type": "contains", + "direction": "forward", + "weight": 1.0 + }, + { + "source": "file:mcp_server/server.py", + "target": "function:mcp_server/server.py:_get_bc", + "type": "contains", + "direction": "forward", + "weight": 1.0 + }, + { + "source": "file:mcp_server/server.py", + "target": "function:mcp_server/server.py:_search_ncit", + "type": "contains", + "direction": "forward", + "weight": 1.0 + }, + { + "source": "file:mcp_server/server.py", + "target": "function:mcp_server/server.py:_get_ncit_concept", + "type": "contains", + "direction": "forward", + "weight": 1.0 + }, + { + "source": "file:mcp_server/server.py", + "target": "function:mcp_server/server.py:_search_loinc", + "type": "contains", + "direction": "forward", + "weight": 1.0 + }, + { + "source": "file:mcp_server/server.py", + "target": "function:mcp_server/server.py:_search_cdisc_library", + "type": "contains", + "direction": "forward", + "weight": 1.0 + }, + { + "source": "file:mcp_server/server.py", + "target": "function:mcp_server/server.py:_get_library_bc", + "type": "contains", + "direction": "forward", + "weight": 1.0 + }, + { + "source": "file:mcp_server/server.py", + "target": "function:mcp_server/server.py:_list_review_queue", + "type": "contains", + "direction": "forward", + "weight": 1.0 + }, + { + "source": "file:mcp_server/server.py", + "target": "function:mcp_server/server.py:_create_bc", + "type": "contains", + "direction": "forward", + "weight": 1.0 + }, + { + "source": "file:mcp_server/server.py", + "target": "function:mcp_server/server.py:_update_bc", + "type": "contains", + "direction": "forward", + "weight": 1.0 + }, + { + "source": "file:mcp_server/server.py", + "target": "function:mcp_server/server.py:_map_ncit_to_bc", + "type": "contains", + "direction": "forward", + "weight": 1.0 + }, + { + "source": "file:mcp_server/server.py", + "target": "function:mcp_server/server.py:_submit_bc_for_review", + "type": "contains", + "direction": "forward", + "weight": 1.0 + }, + { + "source": "file:mcp_server/server.py", + "target": "function:mcp_server/server.py:_advance_governance", + "type": "contains", + "direction": "forward", + "weight": 1.0 + }, + { + "source": "file:mcp_server/server.py", + "target": "function:mcp_server/server.py:_reject_bc", + "type": "contains", + "direction": "forward", + "weight": 1.0 + }, + { + "source": "file:mcp_server/server.py", + "target": "function:mcp_server/server.py:main", + "type": "contains", + "direction": "forward", + "weight": 1.0 + }, + { + "source": "file:mcp_server/server.py", + "target": "function:mcp_server/server.py:_run", + "type": "contains", + "direction": "forward", + "weight": 1.0 + }, + { + "source": "file:models/specialization.py", + "target": "class:models/specialization.py:DatasetSpecialization", + "type": "contains", + "direction": "forward", + "weight": 1.0 + }, + { + "source": "file:tests/test_mcp_server.py", + "target": "class:tests/test_mcp_server.py:TestDispatch", + "type": "contains", + "direction": "forward", + "weight": 1.0 + }, + { + "source": "file:tests/test_mcp_server.py", + "target": "class:tests/test_mcp_server.py:TestListBcs", + "type": "contains", + "direction": "forward", + "weight": 1.0 + }, + { + "source": "file:tests/test_mcp_server.py", + "target": "class:tests/test_mcp_server.py:TestGetBc", + "type": "contains", + "direction": "forward", + "weight": 1.0 + }, + { + "source": "file:tests/test_mcp_server.py", + "target": "class:tests/test_mcp_server.py:TestExternalApiTools", + "type": "contains", + "direction": "forward", + "weight": 1.0 + }, + { + "source": "file:tests/test_mcp_server.py", + "target": "class:tests/test_mcp_server.py:TestCreateBc", + "type": "contains", + "direction": "forward", + "weight": 1.0 + }, + { + "source": "file:tests/test_mcp_server.py", + "target": "class:tests/test_mcp_server.py:TestUpdateBc", + "type": "contains", + "direction": "forward", + "weight": 1.0 + }, + { + "source": "file:tests/test_mcp_server.py", + "target": "class:tests/test_mcp_server.py:TestMapNcit", + "type": "contains", + "direction": "forward", + "weight": 1.0 + }, + { + "source": "file:tests/test_mcp_server.py", + "target": "class:tests/test_mcp_server.py:TestGovernanceWrites", + "type": "contains", + "direction": "forward", + "weight": 1.0 + }, + { + "source": "file:tests/test_mcp_server.py", + "target": "class:tests/test_mcp_server.py:TestReviewQueue", + "type": "contains", + "direction": "forward", + "weight": 1.0 + }, + { + "source": "file:tests/test_specializations_routes.py", + "target": "class:tests/test_specializations_routes.py:TestIndex", + "type": "contains", + "direction": "forward", + "weight": 1.0 + }, + { + "source": "file:tests/test_specializations_routes.py", + "target": "class:tests/test_specializations_routes.py:TestCreate", + "type": "contains", + "direction": "forward", + "weight": 1.0 + }, + { + "source": "file:tests/test_specializations_routes.py", + "target": "class:tests/test_specializations_routes.py:TestEditUpsert", + "type": "contains", + "direction": "forward", + "weight": 1.0 + }, + { + "source": "file:tests/test_specializations_routes.py", + "target": "class:tests/test_specializations_routes.py:TestDelete", + "type": "contains", + "direction": "forward", + "weight": 1.0 + }, + { + "source": "file:tests/test_specializations_routes.py", + "target": "class:tests/test_specializations_routes.py:TestDetail", + "type": "contains", + "direction": "forward", + "weight": 1.0 + }, + { + "source": "file:tests/test_specializations_routes.py", + "target": "class:tests/test_specializations_routes.py:TestLibraryDetail", + "type": "contains", + "direction": "forward", + "weight": 1.0 + }, + { + "source": "file:tests/test_specializations_routes.py", + "target": "class:tests/test_specializations_routes.py:TestGenerate", + "type": "contains", + "direction": "forward", + "weight": 1.0 + }, + { + "source": "file:tests/test_specializations_routes.py", + "target": "class:tests/test_specializations_routes.py:TestGenerateFromDec", + "type": "contains", + "direction": "forward", + "weight": 1.0 + }, + { + "source": "file:models/specialization.py", + "target": "class:models/specialization.py:DatasetSpecialization", + "type": "exports", + "direction": "forward", + "weight": 0.8 + }, + { + "source": "file:mcp_server/server.py", + "target": "function:mcp_server/server.py:_dispatch", + "type": "exports", + "direction": "forward", + "weight": 0.8 + }, + { + "source": "file:mcp_server/server.py", + "target": "function:mcp_server/server.py:main", + "type": "exports", + "direction": "forward", + "weight": 0.8 + }, + { + "source": "file:mcp_server/__main__.py", + "target": "function:mcp_server/server.py:main", + "type": "calls", + "direction": "forward", + "weight": 0.8 + }, + { + "source": "class:tests/test_mcp_server.py:TestDispatch", + "target": "function:mcp_server/server.py:_dispatch", + "type": "calls", + "direction": "forward", + "weight": 0.8 + }, + { + "source": "class:tests/test_mcp_server.py:TestListBcs", + "target": "function:mcp_server/server.py:_list_bcs", + "type": "calls", + "direction": "forward", + "weight": 0.8 + }, + { + "source": "class:tests/test_mcp_server.py:TestGetBc", + "target": "function:mcp_server/server.py:_get_bc", + "type": "calls", + "direction": "forward", + "weight": 0.8 + }, + { + "source": "class:tests/test_mcp_server.py:TestExternalApiTools", + "target": "function:mcp_server/server.py:_search_ncit", + "type": "calls", + "direction": "forward", + "weight": 0.8 + }, + { + "source": "class:tests/test_mcp_server.py:TestExternalApiTools", + "target": "function:mcp_server/server.py:_get_ncit_concept", + "type": "calls", + "direction": "forward", + "weight": 0.8 + }, + { + "source": "class:tests/test_mcp_server.py:TestExternalApiTools", + "target": "function:mcp_server/server.py:_search_loinc", + "type": "calls", + "direction": "forward", + "weight": 0.8 + }, + { + "source": "class:tests/test_mcp_server.py:TestExternalApiTools", + "target": "function:mcp_server/server.py:_search_cdisc_library", + "type": "calls", + "direction": "forward", + "weight": 0.8 + }, + { + "source": "class:tests/test_mcp_server.py:TestExternalApiTools", + "target": "function:mcp_server/server.py:_get_library_bc", + "type": "calls", + "direction": "forward", + "weight": 0.8 + }, + { + "source": "class:tests/test_mcp_server.py:TestCreateBc", + "target": "function:mcp_server/server.py:_create_bc", + "type": "calls", + "direction": "forward", + "weight": 0.8 + }, + { + "source": "class:tests/test_mcp_server.py:TestUpdateBc", + "target": "function:mcp_server/server.py:_update_bc", + "type": "calls", + "direction": "forward", + "weight": 0.8 + }, + { + "source": "class:tests/test_mcp_server.py:TestMapNcit", + "target": "function:mcp_server/server.py:_map_ncit_to_bc", + "type": "calls", + "direction": "forward", + "weight": 0.8 + }, + { + "source": "class:tests/test_mcp_server.py:TestGovernanceWrites", + "target": "function:mcp_server/server.py:_submit_bc_for_review", + "type": "calls", + "direction": "forward", + "weight": 0.8 + }, + { + "source": "class:tests/test_mcp_server.py:TestGovernanceWrites", + "target": "function:mcp_server/server.py:_advance_governance", + "type": "calls", + "direction": "forward", + "weight": 0.8 + }, + { + "source": "class:tests/test_mcp_server.py:TestGovernanceWrites", + "target": "function:mcp_server/server.py:_reject_bc", + "type": "calls", + "direction": "forward", + "weight": 0.8 + }, + { + "source": "class:tests/test_mcp_server.py:TestReviewQueue", + "target": "function:mcp_server/server.py:_list_review_queue", + "type": "calls", + "direction": "forward", + "weight": 0.8 + }, + { + "source": "file:mcp_server/server.py", + "target": "file:app.py", + "type": "depends_on", + "direction": "forward", + "weight": 0.6 + }, + { + "source": "file:mcp_server/server.py", + "target": "file:db_bootstrap.py", + "type": "depends_on", + "direction": "forward", + "weight": 0.6 + }, + { + "source": "file:mcp_server/server.py", + "target": "file:models/bc.py", + "type": "depends_on", + "direction": "forward", + "weight": 0.6 + }, + { + "source": "file:mcp_server/server.py", + "target": "file:models/governance.py", + "type": "depends_on", + "direction": "forward", + "weight": 0.6 + }, + { + "source": "file:mcp_server/server.py", + "target": "file:extensions.py", + "type": "depends_on", + "direction": "forward", + "weight": 0.6 + }, + { + "source": "file:mcp_server/server.py", + "target": "file:models/ingestion.py", + "type": "depends_on", + "direction": "forward", + "weight": 0.6 + }, + { + "source": "file:mcp_server/server.py", + "target": "file:services/ncit_api.py", + "type": "depends_on", + "direction": "forward", + "weight": 0.6 + }, + { + "source": "file:mcp_server/server.py", + "target": "file:services/loinc_api.py", + "type": "depends_on", + "direction": "forward", + "weight": 0.6 + }, + { + "source": "file:mcp_server/server.py", + "target": "file:services/cdisc_api.py", + "type": "depends_on", + "direction": "forward", + "weight": 0.6 + }, + { + "source": "file:mcp_server/server.py", + "target": "file:services/bc_service.py", + "type": "depends_on", + "direction": "forward", + "weight": 0.6 + }, + { + "source": "file:mcp_server/server.py", + "target": "file:services/governance_service.py", + "type": "depends_on", + "direction": "forward", + "weight": 0.6 + }, + { + "source": "file:mcp_server/server.py", + "target": "file:tests/test_mcp_server.py", + "type": "tested_by", + "direction": "forward", + "weight": 0.5 + }, + { + "source": "file:routes/specializations.py", + "target": "file:tests/test_specializations_routes.py", + "type": "tested_by", + "direction": "forward", + "weight": 0.5 + }, + { + "source": "document:.claude/agent-memory/cdisc-frontend-dev/MEMORY.md", + "target": "document:.claude/agent-memory/cdisc-frontend-dev/project_foundation.md", + "type": "related", + "direction": "forward", + "weight": 0.5 + }, + { + "source": "document:.claude/agent-memory/cdisc-frontend-dev/MEMORY.md", + "target": "document:.claude/agent-memory/cdisc-frontend-dev/frontend_architecture.md", + "type": "related", + "direction": "forward", + "weight": 0.5 + }, + { + "source": "document:.claude/agent-memory/cdisc-frontend-dev/frontend_architecture.md", + "target": "file:templates/base.html", + "type": "documents", + "direction": "forward", + "weight": 0.5 + }, + { + "source": "document:.claude/agent-memory/cdisc-frontend-dev/frontend_architecture.md", + "target": "file:static/js/main.js", + "type": "documents", + "direction": "forward", + "weight": 0.5 + }, + { + "source": "document:.claude/agent-memory/cdisc-frontend-dev/frontend_architecture.md", + "target": "file:static/css/custom.css", + "type": "documents", + "direction": "forward", + "weight": 0.5 + }, + { + "source": "document:.claude/agent-memory/cdisc-frontend-dev/project_foundation.md", + "target": "file:app.py", + "type": "documents", + "direction": "forward", + "weight": 0.5 + }, + { + "source": "document:.claude/agent-memory/cdisc-frontend-dev/project_foundation.md", + "target": "file:extensions.py", + "type": "documents", + "direction": "forward", + "weight": 0.5 + }, + { + "source": "document:.claude/agent-memory/cdisc-frontend-dev/project_foundation.md", + "target": "file:models/bc.py", + "type": "documents", + "direction": "forward", + "weight": 0.5 + }, + { + "source": "document:.claude/agent-memory/cdisc-frontend-dev/project_foundation.md", + "target": "file:models/specialization.py", + "type": "documents", + "direction": "forward", + "weight": 0.5 + }, + { + "source": "document:.claude/agent-memory/cdisc-frontend-dev/project_foundation.md", + "target": "file:models/governance.py", + "type": "documents", + "direction": "forward", + "weight": 0.5 + }, + { + "source": "document:.claude/agent-memory/cdisc-frontend-dev/project_foundation.md", + "target": "file:models/audit.py", + "type": "documents", + "direction": "forward", + "weight": 0.5 + }, + { + "source": "document:.claude/agent-memory/cdisc-frontend-dev/project_foundation.md", + "target": "file:models/ingestion.py", + "type": "documents", + "direction": "forward", + "weight": 0.5 + }, + { + "source": "document:.claude/agent-memory/cdisc-frontend-dev/project_foundation.md", + "target": "file:services/ingestion.py", + "type": "documents", + "direction": "forward", + "weight": 0.5 + }, + { + "source": "document:.claude/agent-memory/cdisc-frontend-dev/project_foundation.md", + "target": "file:services/ncit_api.py", + "type": "documents", + "direction": "forward", + "weight": 0.5 + }, + { + "source": "document:.claude/agent-memory/cdisc-frontend-dev/project_foundation.md", + "target": "file:services/loinc_api.py", + "type": "documents", + "direction": "forward", + "weight": 0.5 + }, + { + "source": "document:.claude/agent-memory/cdisc-frontend-dev/project_foundation.md", + "target": "file:services/export.py", + "type": "documents", + "direction": "forward", + "weight": 0.5 + }, + { + "source": "document:.claude/agent-memory/cdisc-frontend-dev/project_foundation.md", + "target": "file:services/cdisc_api.py", + "type": "documents", + "direction": "forward", + "weight": 0.5 + }, + { + "source": "document:.claude/agents/mike.md", + "target": "document:.claude/agents/cdisc-frontend-dev.md", + "type": "related", + "direction": "forward", + "weight": 0.5 + }, + { + "source": "document:.claude/agents/mike.md", + "target": "document:.claude/agents/cdisc-concept-explorer.md", + "type": "related", + "direction": "forward", + "weight": 0.5 + }, + { + "source": "document:README.md", + "target": "document:requirements.txt", + "type": "documents", + "direction": "forward", + "weight": 0.5 + }, + { + "source": "document:README.md", + "target": "config:.pre-commit-config.yaml", + "type": "documents", + "direction": "forward", + "weight": 0.5 + }, + { + "source": "document:README.md", + "target": "config:pyproject.toml", + "type": "documents", + "direction": "forward", + "weight": 0.5 + }, + { + "source": "config:.pre-commit-config.yaml", + "target": "config:pyproject.toml", + "type": "depends_on", + "direction": "forward", + "weight": 0.6 + }, + { + "source": "document:requirements-dev.txt", + "target": "document:requirements.txt", + "type": "related", + "direction": "forward", + "weight": 0.5 + }, + { + "source": "document:README.md", + "target": "document:CLAUDE.md", + "type": "related", + "direction": "forward", + "weight": 0.5 + }, + { + "source": "document:README.md", + "target": "document:README-PROGRESS.md", + "type": "related", + "direction": "forward", + "weight": 0.5 + }, + { + "source": "document:files/plan.md", + "target": "document:files/implementation.md", + "type": "related", + "direction": "forward", + "weight": 0.5 + }, + { + "source": "document:files/implementation.md", + "target": "file:app.py", + "type": "documents", + "direction": "forward", + "weight": 0.5 + }, + { + "source": "document:files/implementation.md", + "target": "file:config.py", + "type": "documents", + "direction": "forward", + "weight": 0.5 + }, + { + "source": "document:files/implementation.md", + "target": "file:models/bc.py", + "type": "documents", + "direction": "forward", + "weight": 0.5 + }, + { + "source": "document:files/implementation.md", + "target": "file:models/specialization.py", + "type": "documents", + "direction": "forward", + "weight": 0.5 + }, + { + "source": "document:files/implementation.md", + "target": "file:models/governance.py", + "type": "documents", + "direction": "forward", + "weight": 0.5 + }, + { + "source": "document:files/implementation.md", + "target": "file:models/audit.py", + "type": "documents", + "direction": "forward", + "weight": 0.5 + }, + { + "source": "document:files/implementation.md", + "target": "file:routes/dashboard.py", + "type": "documents", + "direction": "forward", + "weight": 0.5 + }, + { + "source": "document:files/implementation.md", + "target": "file:routes/ingestion.py", + "type": "documents", + "direction": "forward", + "weight": 0.5 + }, + { + "source": "document:files/implementation.md", + "target": "file:routes/bc.py", + "type": "documents", + "direction": "forward", + "weight": 0.5 + }, + { + "source": "document:files/implementation.md", + "target": "file:routes/ncit.py", + "type": "documents", + "direction": "forward", + "weight": 0.5 + }, + { + "source": "document:files/implementation.md", + "target": "file:routes/specializations.py", + "type": "documents", + "direction": "forward", + "weight": 0.5 + }, + { + "source": "document:files/implementation.md", + "target": "file:routes/governance.py", + "type": "documents", + "direction": "forward", + "weight": 0.5 + }, + { + "source": "document:files/implementation.md", + "target": "file:routes/audit.py", + "type": "documents", + "direction": "forward", + "weight": 0.5 + }, + { + "source": "document:files/implementation.md", + "target": "file:services/cdisc_api.py", + "type": "documents", + "direction": "forward", + "weight": 0.5 + }, + { + "source": "document:files/implementation.md", + "target": "file:services/ncit_api.py", + "type": "documents", + "direction": "forward", + "weight": 0.5 + }, + { + "source": "document:files/implementation.md", + "target": "file:services/ingestion.py", + "type": "documents", + "direction": "forward", + "weight": 0.5 + }, + { + "source": "document:files/implementation.md", + "target": "file:services/export.py", + "type": "documents", + "direction": "forward", + "weight": 0.5 + }, + { + "source": "document:files/implementation.md", + "target": "file:templates/base.html", + "type": "documents", + "direction": "forward", + "weight": 0.5 + }, + { + "source": "document:files/implementation.md", + "target": "document:files/plan.md", + "type": "related", + "direction": "forward", + "weight": 0.5 + }, + { + "source": "document:files/implementation.md", + "target": "document:files/result.md", + "type": "related", + "direction": "forward", + "weight": 0.5 + }, + { + "source": "document:files/result.md", + "target": "file:app.py", + "type": "documents", + "direction": "forward", + "weight": 0.5 + }, + { + "source": "document:files/result.md", + "target": "file:extensions.py", + "type": "documents", + "direction": "forward", + "weight": 0.5 + }, + { + "source": "document:files/result.md", + "target": "file:config.py", + "type": "documents", + "direction": "forward", + "weight": 0.5 + }, + { + "source": "document:files/result.md", + "target": "document:files/implementation.md", + "type": "related", + "direction": "forward", + "weight": 0.5 + }, + { + "source": "file:templates/audit.html", + "target": "file:templates/base.html", + "type": "depends_on", + "direction": "forward", + "weight": 0.6 + }, + { + "source": "file:templates/bc_detail.html", + "target": "file:templates/base.html", + "type": "depends_on", + "direction": "forward", + "weight": 0.6 + }, + { + "source": "file:templates/bc_list.html", + "target": "file:templates/base.html", + "type": "depends_on", + "direction": "forward", + "weight": 0.6 + }, + { + "source": "file:templates/dashboard.html", + "target": "file:templates/base.html", + "type": "depends_on", + "direction": "forward", + "weight": 0.6 + }, + { + "source": "file:templates/governance.html", + "target": "file:templates/base.html", + "type": "depends_on", + "direction": "forward", + "weight": 0.6 + }, + { + "source": "file:templates/ingestion.html", + "target": "file:templates/base.html", + "type": "depends_on", + "direction": "forward", + "weight": 0.6 + }, + { + "source": "file:templates/library_bc_detail.html", + "target": "file:templates/base.html", + "type": "depends_on", + "direction": "forward", + "weight": 0.6 + }, + { + "source": "file:templates/library_spec_detail.html", + "target": "file:templates/base.html", + "type": "depends_on", + "direction": "forward", + "weight": 0.6 + }, + { + "source": "file:templates/ncit_mapping.html", + "target": "file:templates/base.html", + "type": "depends_on", + "direction": "forward", + "weight": 0.6 + }, + { + "source": "file:templates/specializations.html", + "target": "file:templates/base.html", + "type": "depends_on", + "direction": "forward", + "weight": 0.6 + }, + { + "source": "file:.claude/skills/run-concept-curation/smoke.sh", + "target": "function:.claude/skills/run-concept-curation/smoke.sh:cleanup", + "type": "contains", + "direction": "forward", + "weight": 1.0 + }, + { + "source": "file:.claude/skills/run-concept-curation/smoke.sh", + "target": "function:.claude/skills/run-concept-curation/smoke.sh:check", + "type": "contains", + "direction": "forward", + "weight": 1.0 + }, + { + "source": "document:.claude/skills/run-concept-curation/SKILL.md", + "target": "file:.claude/skills/run-concept-curation/smoke.sh", + "type": "documents", + "direction": "forward", + "weight": 0.5 + }, + { + "source": "document:.claude/agent-memory/cdisc-concept-explorer/MEMORY.md", + "target": "document:.claude/agent-memory/cdisc-concept-explorer/reference_api_endpoints.md", + "type": "related", + "direction": "forward", + "weight": 0.5 + }, + { + "source": "config:migrations/alembic.ini", + "target": "file:migrations/env.py", + "type": "configures", + "direction": "forward", + "weight": 0.6 + }, + { + "source": "file:migrations/env.py", + "target": "function:migrations/env.py:get_engine", + "type": "contains", + "direction": "forward", + "weight": 1.0 + }, + { + "source": "file:migrations/env.py", + "target": "function:migrations/env.py:get_engine_url", + "type": "contains", + "direction": "forward", + "weight": 1.0 + }, + { + "source": "file:migrations/env.py", + "target": "function:migrations/env.py:run_migrations_offline", + "type": "contains", + "direction": "forward", + "weight": 1.0 + }, + { + "source": "file:migrations/env.py", + "target": "function:migrations/env.py:run_migrations_online", + "type": "contains", + "direction": "forward", + "weight": 1.0 + }, + { + "source": "file:migrations/env.py", + "target": "function:migrations/env.py:get_engine", + "type": "exports", + "direction": "forward", + "weight": 0.8 + }, + { + "source": "file:migrations/env.py", + "target": "function:migrations/env.py:get_engine_url", + "type": "exports", + "direction": "forward", + "weight": 0.8 + }, + { + "source": "file:migrations/env.py", + "target": "function:migrations/env.py:run_migrations_offline", + "type": "exports", + "direction": "forward", + "weight": 0.8 + }, + { + "source": "file:migrations/env.py", + "target": "function:migrations/env.py:run_migrations_online", + "type": "exports", + "direction": "forward", + "weight": 0.8 + }, + { + "source": "function:migrations/env.py:run_migrations_offline", + "target": "function:migrations/env.py:get_engine_url", + "type": "calls", + "direction": "forward", + "weight": 0.8 + }, + { + "source": "function:migrations/env.py:run_migrations_online", + "target": "function:migrations/env.py:get_engine", + "type": "calls", + "direction": "forward", + "weight": 0.8 + }, + { + "source": "function:migrations/env.py:get_engine_url", + "target": "function:migrations/env.py:get_engine", + "type": "calls", + "direction": "forward", + "weight": 0.8 + }, + { + "source": "file:migrations/script.py.mako", + "target": "file:migrations/versions/51d4a009d291_baseline_initial_schema.py", + "type": "related", + "direction": "forward", + "weight": 0.5 + }, + { + "source": "file:migrations/versions/51d4a009d291_baseline_initial_schema.py", + "target": "function:migrations/versions/51d4a009d291_baseline_initial_schema.py:upgrade", + "type": "contains", + "direction": "forward", + "weight": 1.0 + }, + { + "source": "file:migrations/versions/51d4a009d291_baseline_initial_schema.py", + "target": "function:migrations/versions/51d4a009d291_baseline_initial_schema.py:downgrade", + "type": "contains", + "direction": "forward", + "weight": 1.0 + }, + { + "source": "file:migrations/versions/51d4a009d291_baseline_initial_schema.py", + "target": "function:migrations/versions/51d4a009d291_baseline_initial_schema.py:upgrade", + "type": "exports", + "direction": "forward", + "weight": 0.8 + }, + { + "source": "file:migrations/versions/51d4a009d291_baseline_initial_schema.py", + "target": "function:migrations/versions/51d4a009d291_baseline_initial_schema.py:downgrade", + "type": "exports", + "direction": "forward", + "weight": 0.8 + }, + { + "source": "file:static/js/main.js", + "target": "function:static/js/main.js:initFileUpload", + "type": "contains", + "direction": "forward", + "weight": 1.0 + }, + { + "source": "file:static/js/main.js", + "target": "function:static/js/main.js:initNcitLookup", + "type": "contains", + "direction": "forward", + "weight": 1.0 + }, + { + "source": "file:static/js/main.js", + "target": "function:static/js/main.js:renderNcitResults", + "type": "contains", + "direction": "forward", + "weight": 1.0 + }, + { + "source": "file:static/js/main.js", + "target": "function:static/js/main.js:renderNcitMetaDisplay", + "type": "contains", + "direction": "forward", + "weight": 1.0 + }, + { + "source": "file:static/js/main.js", + "target": "function:static/js/main.js:initDecTable", + "type": "contains", + "direction": "forward", + "weight": 1.0 + }, + { + "source": "file:static/js/main.js", + "target": "function:static/js/main.js:buildDecRow", + "type": "contains", + "direction": "forward", + "weight": 1.0 + }, + { + "source": "file:static/js/main.js", + "target": "function:static/js/main.js:initKanban", + "type": "contains", + "direction": "forward", + "weight": 1.0 + }, + { + "source": "file:static/js/main.js", + "target": "function:static/js/main.js:initAuditDiff", + "type": "contains", + "direction": "forward", + "weight": 1.0 + }, + { + "source": "file:static/js/main.js", + "target": "function:static/js/main.js:initNcitSearch", + "type": "contains", + "direction": "forward", + "weight": 1.0 + }, + { + "source": "file:static/js/main.js", + "target": "function:static/js/main.js:renderNcitSearchResults", + "type": "contains", + "direction": "forward", + "weight": 1.0 + }, + { + "source": "file:static/js/main.js", + "target": "function:static/js/main.js:showInlineToast", + "type": "contains", + "direction": "forward", + "weight": 1.0 + }, + { + "source": "file:static/js/main.js", + "target": "function:static/js/main.js:getOrCreateToastContainer", + "type": "contains", + "direction": "forward", + "weight": 1.0 + }, + { + "source": "file:static/js/main.js", + "target": "function:static/js/main.js:initLoincLookup", + "type": "contains", + "direction": "forward", + "weight": 1.0 + }, + { + "source": "file:static/js/main.js", + "target": "function:static/js/main.js:renderLoincResults", + "type": "contains", + "direction": "forward", + "weight": 1.0 + }, + { + "source": "file:static/js/main.js", + "target": "function:static/js/main.js:renderLoincMetaDisplay", + "type": "contains", + "direction": "forward", + "weight": 1.0 + }, + { + "source": "file:static/js/main.js", + "target": "function:static/js/main.js:initPendingNcitConcept", + "type": "contains", + "direction": "forward", + "weight": 1.0 + }, + { + "source": "function:static/js/main.js:initNcitLookup", + "target": "function:static/js/main.js:renderNcitResults", + "type": "calls", + "direction": "forward", + "weight": 0.8 + }, + { + "source": "function:static/js/main.js:renderNcitResults", + "target": "function:static/js/main.js:renderNcitMetaDisplay", + "type": "calls", + "direction": "forward", + "weight": 0.8 + }, + { + "source": "function:static/js/main.js:initDecTable", + "target": "function:static/js/main.js:buildDecRow", + "type": "calls", + "direction": "forward", + "weight": 0.8 + }, + { + "source": "function:static/js/main.js:initKanban", + "target": "function:static/js/main.js:showInlineToast", + "type": "calls", + "direction": "forward", + "weight": 0.8 + }, + { + "source": "function:static/js/main.js:initNcitSearch", + "target": "function:static/js/main.js:renderNcitSearchResults", + "type": "calls", + "direction": "forward", + "weight": 0.8 + }, + { + "source": "function:static/js/main.js:showInlineToast", + "target": "function:static/js/main.js:getOrCreateToastContainer", + "type": "calls", + "direction": "forward", + "weight": 0.8 + }, + { + "source": "function:static/js/main.js:initPendingNcitConcept", + "target": "function:static/js/main.js:renderNcitMetaDisplay", + "type": "calls", + "direction": "forward", + "weight": 0.8 + }, + { + "source": "file:static/css/custom.css", + "target": "file:static/js/main.js", + "type": "related", + "direction": "forward", + "weight": 0.5 + }, + { + "source": "document:.claude/skills/code-review/SKILL.md", + "target": "file:mcp_server/__init__.py", + "type": "related", + "direction": "forward", + "weight": 0.5 + }, + { + "source": "document:files/plan.md", + "target": "file:files/cdisc_bc_ai_platform.html", + "type": "related", + "direction": "forward", + "weight": 0.5 + }, + { + "source": "document:files/plan.md", + "target": "file:files/cdisc_smart_goals_infographic.html", + "type": "related", + "direction": "forward", + "weight": 0.5 + }, + { + "source": "document:files/plan.md", + "target": "file:files/BC Curation Principles and Completion GLs.xlsx", + "type": "related", + "direction": "forward", + "weight": 0.5 + }, + { + "source": "document:files/plan.md", + "target": "file:files/BC Examples.xlsx", + "type": "related", + "direction": "forward", + "weight": 0.5 + }, + { + "source": "document:files/plan.md", + "target": "file:files/BC DEC Templates.xlsx", + "type": "related", + "direction": "forward", + "weight": 0.5 + }, + { + "source": "document:files/plan.md", + "target": "file:files/BC Governance.docx", + "type": "related", + "direction": "forward", + "weight": 0.5 + } + ], + "layers": [ + { + "id": "layer:api", + "name": "API Layer", + "description": "Flask blueprint route handlers (dashboard, ingestion, bc, ncit, loinc, specializations, governance, audit) plus the MCP server package that exposes the same read/write operations as a second tool-based API surface for AI agents.", + "nodeIds": [ + "file:routes/audit.py", + "file:routes/ingestion.py", + "file:routes/bc.py", + "file:routes/governance.py", + "file:routes/ncit.py", + "file:routes/dashboard.py", + "file:routes/loinc.py", + "file:routes/specializations.py", + "file:routes/__init__.py", + "file:mcp_server/__main__.py", + "file:mcp_server/server.py", + "file:mcp_server/__init__.py" + ] + }, + { + "id": "layer:service", + "name": "Service Layer", + "description": "Business logic and external API clients \u2014 BC/governance/audit orchestration, the ingestion parser and field mapper, the XLSX/JSON/ODM-XML exporter, and the CDISC Library, NCIt, and LOINC API clients (with in-memory caching).", + "nodeIds": [ + "file:services/ingestion.py", + "file:services/__init__.py", + "file:services/audit.py", + "file:services/bc_service.py", + "file:services/export.py", + "file:services/governance_service.py", + "file:services/ncit_api.py", + "file:services/api_cache.py", + "file:services/cdisc_api.py", + "file:services/loinc_api.py" + ] + }, + { + "id": "layer:data", + "name": "Data Layer", + "description": "SQLAlchemy ORM models (BiomedicalConcept, DataElementConcept, IngestionRecord, AuditLog, GovernanceRecord, Specialization) together with the Alembic migration environment and baseline schema revision that bring the SQLite database to head.", + "nodeIds": [ + "file:models/audit.py", + "file:models/bc.py", + "file:models/ingestion.py", + "file:models/governance.py", + "file:models/__init__.py", + "file:models/specialization.py", + "document:migrations/README", + "config:migrations/alembic.ini", + "file:migrations/env.py", + "file:migrations/script.py.mako", + "file:migrations/versions/51d4a009d291_baseline_initial_schema.py" + ] + }, + { + "id": "layer:ui", + "name": "UI Layer", + "description": "Jinja2 + Bootstrap 5 templates for every page (dashboard, ingestion, BC detail/list, governance Kanban, NCIt mapping, specializations, audit log) plus the shared CSS and JavaScript assets that render them in the browser.", + "nodeIds": [ + "file:templates/base.html", + "file:templates/audit.html", + "file:templates/bc_detail.html", + "file:templates/bc_list.html", + "file:templates/dashboard.html", + "file:templates/governance.html", + "file:templates/ingestion.html", + "file:templates/library_bc_detail.html", + "file:templates/library_spec_detail.html", + "file:templates/ncit_mapping.html", + "file:templates/specializations.html", + "file:static/css/custom.css", + "file:static/js/main.js" + ] + }, + { + "id": "layer:config", + "name": "Configuration & Application Setup", + "description": "The application factory, extension singletons, and DB-bootstrap entry points (app.py, extensions.py, db_bootstrap.py) alongside project-level configuration \u2014 config.py, dependency pins, lint/format tool config, and the MCP server registration.", + "nodeIds": [ + "file:app.py", + "file:db_bootstrap.py", + "file:extensions.py", + "file:config.py", + "config:.pre-commit-config.yaml", + "config:.mcp.json", + "config:pyproject.toml", + "document:requirements-dev.txt", + "document:requirements.txt", + "config:.flake8", + "config:.hintrc", + "config:.ua/.understandignore", + "config:.ua/config.json" + ] + }, + { + "id": "layer:test", + "name": "Test Layer", + "description": "The pytest suite covering every blueprint, service, and model, plus shared fixtures in conftest.py, run against the in-memory SQLite TestConfig database used by both CI and local development.", + "nodeIds": [ + "file:tests/conftest.py", + "file:tests/test_audit_routes.py", + "file:tests/test_bc_routes.py", + "file:tests/test_governance_routes.py", + "file:tests/test_ingestion_routes.py", + "file:tests/test_ingestion_service.py", + "file:tests/test_models.py", + "file:tests/test_export_service.py", + "file:tests/test_ncit.py", + "file:tests/test_cdisc_api_cache.py", + "file:tests/test_loinc.py", + "file:tests/test_dashboard.py", + "file:tests/test_db_bootstrap.py", + "file:tests/test_mcp_server.py", + "file:tests/test_specializations_routes.py", + "file:tests/__init__.py" + ] + }, + { + "id": "layer:documentation", + "name": "Documentation & Reference Materials", + "description": "Project-level documentation (CLAUDE.md, README, progress log) and the domain reference materials in files/ \u2014 BC curation principles, DEC templates, worked examples, governance guidance, and platform planning docs \u2014 that ground the curation workflow the app implements.", + "nodeIds": [ + "document:CLAUDE.md", + "document:README-PROGRESS.md", + "document:README.md", + "document:files/plan.md", + "document:files/implementation.md", + "document:files/result.md", + "file:files/BC Curation Principles and Completion GLs.xlsx", + "file:files/BC DEC Templates.xlsx", + "file:files/BC Examples.xlsx", + "file:files/BC Governance.docx", + "file:files/cdisc_bc_ai_platform.html", + "file:files/cdisc_smart_goals_infographic.html" + ] + }, + { + "id": "layer:dev-tooling", + "name": "Dev Tooling & CI/CD", + "description": "The GitHub Actions CI pipeline plus Claude Code agent configuration and memory (.claude/agents, .claude/agent-memory, .claude/skills) that automate linting, testing, and AI-assisted development for this repository.", + "nodeIds": [ + "pipeline:.github/workflows/ci.yml", + "document:.claude/agent-memory/cdisc-frontend-dev/MEMORY.md", + "document:.claude/agent-memory/cdisc-frontend-dev/frontend_architecture.md", + "document:.claude/agent-memory/cdisc-frontend-dev/project_foundation.md", + "document:.claude/agents/cdisc-concept-explorer.md", + "document:.claude/agents/cdisc-frontend-dev.md", + "document:.claude/agents/mike.md", + "document:.claude/agent-memory/cdisc-concept-explorer/MEMORY.md", + "document:.claude/agent-memory/cdisc-concept-explorer/reference_api_endpoints.md", + "document:.claude/skills/code-review/SKILL.md", + "document:.claude/skills/run-concept-curation/SKILL.md", + "file:.claude/skills/run-concept-curation/smoke.sh" + ] + } + ], + "tour": [ + { + "order": 1, + "title": "Project Overview", + "description": "The README explains the platform's purpose: replacing spreadsheet-and-email workflows for curating CDISC Biomedical Concepts (BCs) with a structured pipeline that goes from file ingestion through AI-assisted field mapping, SME review, governance approval, and publication to the CDISC Library. It documents prerequisites, installation, and the external CDISC/NCIt/LOINC APIs the app integrates with, giving you the map before you look at any code.", + "nodeIds": [ + "document:README.md" + ] + }, + { + "order": 2, + "title": "Application Entry Point", + "description": "app.py is the Flask application factory: it configures logging, initializes the shared database and migration extensions, registers all eight feature blueprints, and doubles as the dev-server entry point run via `python app.py`. Every other module you will see in this tour is wired together from this one file, making it the natural starting point for reading the codebase.", + "nodeIds": [ + "file:app.py" + ], + "languageLesson": "Flask's application-factory pattern (a `create_app()` function that builds and configures the app object) keeps extension setup and blueprint registration in one controlled place, which makes the app testable without import-time side effects." + }, + { + "order": 3, + "title": "Shared Extensions and Config", + "description": "extensions.py defines the shared SQLAlchemy `db` and Flask-Migrate `migrate` instances that every model and route imports from, avoiding the circular-import problems that arise when models import an app-bound `db` directly. config.py centralizes every environment-driven setting (secret key, database URI, CDISC/NCIt API bases, upload limits) that app.py loads at startup. Together they are the most depended-upon files in the project \u2014 extensions.py alone is imported by 30 other nodes.", + "nodeIds": [ + "file:extensions.py", + "file:config.py" + ], + "languageLesson": "Splitting shared singletons (db, migrate) into their own module \u2014 rather than defining them alongside the app or the models \u2014 is a common Flask idiom for breaking circular-import cycles between app.py and models/." + }, + { + "order": 4, + "title": "Database Bootstrapping and Migrations", + "description": "db_bootstrap.py brings the SQLite database to the current Alembic migration head on every startup, distinguishing fresh databases from legacy create_all() databases and pre-baseline databases that need stamping. The baseline migration and Alembic's env.py show how that head state is defined and applied \u2014 this is the schema that the models in the next step map onto.", + "nodeIds": [ + "file:db_bootstrap.py", + "config:migrations/alembic.ini", + "file:migrations/env.py", + "file:migrations/versions/51d4a009d291_baseline_initial_schema.py", + "document:migrations/README" + ], + "languageLesson": "Alembic migrations should be incremental and reversible: each revision file pairs an `upgrade()` with a `downgrade()`, letting the schema evolve forward or roll back without hand-editing the database." + }, + { + "order": 5, + "title": "Core Data Models", + "description": "These five SQLAlchemy models are the domain vocabulary of the entire application. BiomedicalConcept and DataElementConcept (models/bc.py) are the central entities being curated; IngestionRecord stages parsed upload rows before approval; GovernanceRecord tracks approval-workflow stage transitions; AuditLog records before/after JSON state for every change; and Specialization stores SDTM/CDASH variable groups. models/bc.py has the highest fan-in of any model (19), reflecting how central it is to routes and services alike.", + "nodeIds": [ + "file:models/bc.py", + "file:models/governance.py", + "file:models/ingestion.py", + "file:models/audit.py", + "file:models/specialization.py" + ] + }, + { + "order": 6, + "title": "API Layer: Flask Blueprints", + "description": "Building on the models from Step 5, these route modules are the HTTP surface of the app. routes/bc.py handles BC CRUD, NCIt/LOINC lookup, and export \u2014 it has the highest fan-out of any route module because it touches nearly every service. routes/ingestion.py drives the upload-to-queue workflow, routes/governance.py implements the Kanban approval board, and routes/dashboard.py aggregates KPI stats from both the local database and the live CDISC Library API.", + "nodeIds": [ + "file:routes/bc.py", + "file:routes/ingestion.py", + "file:routes/governance.py", + "file:routes/dashboard.py" + ], + "languageLesson": "Flask blueprints let each feature area register its own routes, templates, and static assets under a URL prefix, then get wired into the app in one place (app.py) \u2014 a lightweight alternative to a monolithic routes file." + }, + { + "order": 7, + "title": "Service Layer: Core Business Logic", + "description": "Routes stay thin by delegating to the service layer. services/bc_service.py implements creating, updating, and merging Biomedical Concepts, including NCIt remapping and review submission. services/governance_service.py implements the advance/reject transitions that move a BC through Provisional \u2192 SME Review \u2192 CDISC Approval \u2192 Published, and services/audit.py provides the single log_change() helper both of the others call to write immutable AuditLog rows for every mutation.", + "nodeIds": [ + "file:services/bc_service.py", + "file:services/governance_service.py", + "file:services/audit.py" + ] + }, + { + "order": 8, + "title": "The Ingestion Pipeline", + "description": "services/ingestion.py is where the AI-assisted field mapping mentioned in the README actually happens: it parses uploaded XLSX/CSV/JSON files, groups rows by BC ID to handle multi-row DECs, and uses fuzzy string matching to assign canonical field names with confidence scores before rows are staged as IngestionRecord entries (Step 5) for SME approval via routes/ingestion.py (Step 6). This module is the core differentiator that replaces the manual spreadsheet workflow the project was built to eliminate.", + "nodeIds": [ + "file:services/ingestion.py" + ], + "languageLesson": "Python's difflib.SequenceMatcher computes a similarity ratio between strings without any ML model \u2014 a lightweight way to fuzzy-match inconsistent spreadsheet column names against a canonical field vocabulary." + }, + { + "order": 9, + "title": "External API Clients", + "description": "These four modules wrap the platform's three external data sources. services/cdisc_api.py talks to the CDISC Library COSMOS API for BC/specialization lookups and publishing; services/ncit_api.py queries the NCI Thesaurus EVS for concept search; and services/loinc_api.py queries NLM's LOINC search API. All three share services/api_cache.py, a stale-tolerant in-memory cache that serves fresh data for 5 minutes and falls back to stale data for up to an hour if a refresh fails, keeping the UI responsive even when an upstream API is slow.", + "nodeIds": [ + "file:services/cdisc_api.py", + "file:services/ncit_api.py", + "file:services/loinc_api.py", + "file:services/api_cache.py" + ] + }, + { + "order": 10, + "title": "UI Layer: Templates and Styling", + "description": "Every page the routes from Step 6 render extends templates/base.html, the master Jinja2 layout providing sidebar navigation, the top bar, flash-message rendering, and named template blocks that individual pages fill in. static/css/custom.css defines the design tokens (colors, spacing) and component styles \u2014 cards, badges, nav items \u2014 that give those pages a consistent look across the whole app.", + "nodeIds": [ + "file:templates/base.html", + "file:static/css/custom.css" + ], + "languageLesson": "Jinja2 template inheritance (`{% extends %}` plus named `{% block %}` regions) lets every page share one layout while overriding only the content it needs, avoiding duplicated HTML across the eleven page templates." + }, + { + "order": 11, + "title": "The MCP Server: A Second API Surface", + "description": "Alongside the Flask HTTP routes, mcp_server/server.py exposes the same underlying data and write operations as 14 stdio-based MCP tools (8 read, 6 write) for AI agents, dispatching each call into a Flask app context so it shares the exact same SQLite database and service-layer code path as the web app \u2014 writes go through the same services/bc_service.py and services/governance_service.py from Step 7. mcp_server/__main__.py is the process entry point, invoked via `python -m mcp_server`, mirroring app.py's role for the web app in Step 2.", + "nodeIds": [ + "file:mcp_server/server.py", + "file:mcp_server/__main__.py" + ] + }, + { + "order": 12, + "title": "Quality Gates: CI and Pre-commit", + "description": "The GitHub Actions workflow runs on every push and pull request: it installs the project and dev dependencies, lints with isort/black/flake8, and runs the full pytest suite on Python 3.12 \u2014 the same commands documented for local development. .pre-commit-config.yaml enforces the identical checks locally before a commit is even created, so the same rules from Step 2 through Step 11's code all get verified automatically before they ever reach CI.", + "nodeIds": [ + "pipeline:.github/workflows/ci.yml", + "config:.pre-commit-config.yaml" + ], + "languageLesson": "GitHub Actions workflows are declared with `on` triggers, `jobs` (parallel by default), and `steps` (sequential within a job) \u2014 this project's single job runs lint and test steps sequentially on one Python version rather than a matrix build." + } + ] +} \ No newline at end of file diff --git a/.ua/.trash-1785851745/batch-1-part-1.json b/.ua/.trash-1785851745/batch-1-part-1.json new file mode 100644 index 0000000..6321586 --- /dev/null +++ b/.ua/.trash-1785851745/batch-1-part-1.json @@ -0,0 +1,740 @@ +{ + "nodes": [ + { + "id": "file:models/audit.py", + "type": "file", + "name": "audit.py", + "filePath": "models/audit.py", + "summary": "SQLAlchemy model for the AuditLog table that records before/after JSON state for every change made to BCs, DECs, and governance records.", + "tags": ["data-model", "audit-log", "database", "orm"], + "complexity": "simple" + }, + { + "id": "file:models/bc.py", + "type": "file", + "name": "bc.py", + "filePath": "models/bc.py", + "summary": "Core SQLAlchemy models for BiomedicalConcept and DataElementConcept, the central entities of the CDISC BC curation pipeline, including NCIt/LOINC coding fields and governance status.", + "tags": ["data-model", "database", "orm", "core-domain"], + "complexity": "moderate" + }, + { + "id": "file:models/ingestion.py", + "type": "file", + "name": "ingestion.py", + "filePath": "models/ingestion.py", + "summary": "SQLAlchemy model for IngestionRecord, which stages parsed upload rows (with field mappings, confidence scores, and validation errors) in the DB prior to SME approval.", + "tags": ["data-model", "database", "orm", "ingestion"], + "complexity": "simple" + }, + { + "id": "file:routes/audit.py", + "type": "file", + "name": "audit.py", + "filePath": "routes/audit.py", + "summary": "Flask blueprint exposing the read-only audit trail view with filtering by entity type, action, and actor, and pagination.", + "tags": ["api-handler", "audit-log", "blueprint"], + "complexity": "simple" + }, + { + "id": "file:routes/ingestion.py", + "type": "file", + "name": "ingestion.py", + "filePath": "routes/ingestion.py", + "summary": "Flask blueprint implementing the file-upload ingestion workflow: parsing uploaded files, staging rows as IngestionRecord, and approving/rejecting them into BiomedicalConcept/DataElementConcept records with audit logging.", + "tags": ["api-handler", "blueprint", "ingestion", "workflow"], + "complexity": "moderate" + }, + { + "id": "file:services/ingestion.py", + "type": "file", + "name": "ingestion.py", + "filePath": "services/ingestion.py", + "summary": "Business logic for the ingestion pipeline: fuzzy field-name mapping via SequenceMatcher similarity scoring, BC/DEC row validation, multi-row grouping by BC ID, and XLSX/CSV/JSON parsers.", + "tags": ["service", "parsing", "validation", "fuzzy-matching"], + "complexity": "complex", + "languageNotes": "Uses difflib.SequenceMatcher similarity ratios against a canonical FIELD_MAP alias dictionary to fuzzily map arbitrary spreadsheet column names to BC/DEC schema fields." + }, + { + "id": "file:tests/conftest.py", + "type": "file", + "name": "conftest.py", + "filePath": "tests/conftest.py", + "summary": "Pytest fixtures shared across the test suite: Flask app/test client factories, a per-test DB reset fixture, API cache clearing, and a sample BiomedicalConcept fixture.", + "tags": ["test", "fixture", "pytest-config"], + "complexity": "moderate" + }, + { + "id": "class:models/audit.py:AuditLog", + "type": "class", + "name": "AuditLog", + "filePath": "models/audit.py", + "lineRange": [7, 32], + "summary": "ORM model for the immutable audit trail; stores before/after entity state as JSON via property accessors that transparently serialize/deserialize.", + "tags": ["data-model", "audit-log", "orm"], + "complexity": "simple" + }, + { + "id": "class:models/bc.py:BiomedicalConcept", + "type": "class", + "name": "BiomedicalConcept", + "filePath": "models/bc.py", + "lineRange": [6, 50], + "summary": "Central ORM entity representing a curated CDISC Biomedical Concept, holding NCIt/LOINC coding metadata, governance status, parent/child hierarchy, and related DECs/specializations.", + "tags": ["data-model", "core-domain", "orm"], + "complexity": "moderate" + }, + { + "id": "class:models/bc.py:DataElementConcept", + "type": "class", + "name": "DataElementConcept", + "filePath": "models/bc.py", + "lineRange": [53, 65], + "summary": "ORM entity for a Data Element Concept (DEC) belonging to a BiomedicalConcept, capturing NCIt DEC code, data type, and example values.", + "tags": ["data-model", "core-domain", "orm"], + "complexity": "simple" + }, + { + "id": "class:models/ingestion.py:IngestionRecord", + "type": "class", + "name": "IngestionRecord", + "filePath": "models/ingestion.py", + "lineRange": [7, 56], + "summary": "ORM staging model for a parsed upload row awaiting SME approval; exposes JSON-backed properties for field mapping, confidence scores, validation errors, and DEC sub-rows, plus an average-confidence helper.", + "tags": ["data-model", "ingestion", "orm"], + "complexity": "moderate" + }, + { + "id": "function:routes/audit.py:index", + "type": "function", + "name": "index", + "filePath": "routes/audit.py", + "lineRange": [13, 44], + "summary": "Route handler serving the paginated audit log view, applying optional entity_type/action/actor query filters and ordering by timestamp descending.", + "tags": ["api-handler", "audit-log", "pagination"], + "complexity": "simple" + }, + { + "id": "function:routes/ingestion.py:_bc_from_mapped", + "type": "function", + "name": "_bc_from_mapped", + "filePath": "routes/ingestion.py", + "lineRange": [38, 54], + "summary": "Builds a BiomedicalConcept instance from a mapped-field dict produced by the ingestion service, pulling canonical field values.", + "tags": ["helper", "data-model", "ingestion"], + "complexity": "simple" + }, + { + "id": "function:routes/ingestion.py:_create_decs", + "type": "function", + "name": "_create_decs", + "filePath": "routes/ingestion.py", + "lineRange": [57, 68], + "summary": "Creates and persists DataElementConcept rows for a BC from the mapped DEC sub-row list produced during ingestion.", + "tags": ["helper", "data-model", "ingestion"], + "complexity": "simple" + }, + { + "id": "function:routes/ingestion.py:upload", + "type": "function", + "name": "upload", + "filePath": "routes/ingestion.py", + "lineRange": [79, 118], + "summary": "Route handler that accepts an uploaded XLSX/CSV/JSON file, dispatches to the matching parser, deduplicates against existing BC IDs, and stages the results as pending IngestionRecord rows.", + "tags": ["api-handler", "ingestion", "file-upload"], + "complexity": "moderate" + }, + { + "id": "function:routes/ingestion.py:approve", + "type": "function", + "name": "approve", + "filePath": "routes/ingestion.py", + "lineRange": [122, 143], + "summary": "Route handler that approves a single pending IngestionRecord, committing it as a new BiomedicalConcept (and DECs) and writing an audit log entry.", + "tags": ["api-handler", "ingestion", "workflow"], + "complexity": "moderate" + }, + { + "id": "function:routes/ingestion.py:approve_all", + "type": "function", + "name": "approve_all", + "filePath": "routes/ingestion.py", + "lineRange": [155, 186], + "summary": "Route handler that bulk-approves all pending IngestionRecord rows for the current session, creating BCs/DECs and an audit log entry per record.", + "tags": ["api-handler", "ingestion", "workflow", "bulk-operation"], + "complexity": "moderate" + }, + { + "id": "function:services/ingestion.py:_match_field", + "type": "function", + "name": "_match_field", + "filePath": "services/ingestion.py", + "lineRange": [35, 45], + "summary": "Scores a raw column name against every canonical field's alias list using similarity ratios and returns the best-matching canonical field name with a confidence score.", + "tags": ["helper", "fuzzy-matching", "ingestion"], + "complexity": "simple" + }, + { + "id": "function:services/ingestion.py:map_fields", + "type": "function", + "name": "map_fields", + "filePath": "services/ingestion.py", + "lineRange": [48, 62], + "summary": "Maps a raw row dict's column names to canonical BC/DEC field names using _match_field, normalizing values to stripped strings.", + "tags": ["fuzzy-matching", "ingestion", "data-transformation"], + "complexity": "simple" + }, + { + "id": "function:services/ingestion.py:validate_bc", + "type": "function", + "name": "validate_bc", + "filePath": "services/ingestion.py", + "lineRange": [65, 81], + "summary": "Validates a mapped BC dict, flagging missing short name/definition and requiring at least one of NCIt code or external code with a correct NCIt format.", + "tags": ["validation", "ingestion"], + "complexity": "simple" + }, + { + "id": "function:services/ingestion.py:_group_by_bc", + "type": "function", + "name": "_group_by_bc", + "filePath": "services/ingestion.py", + "lineRange": [84, 137], + "summary": "Groups parsed spreadsheet rows by BC ID into single ingestion records, merging DEC sub-rows and confidences under each parent BC and running validation on the merged result.", + "tags": ["data-transformation", "ingestion", "grouping"], + "complexity": "moderate" + }, + { + "id": "function:services/ingestion.py:parse_xlsx", + "type": "function", + "name": "parse_xlsx", + "filePath": "services/ingestion.py", + "lineRange": [140, 162], + "summary": "Parses an uploaded XLSX file's first sheet into mapped, grouped BC/DEC records via pandas, capturing per-row parse errors.", + "tags": ["parsing", "ingestion", "xlsx"], + "complexity": "moderate" + }, + { + "id": "function:services/ingestion.py:parse_csv", + "type": "function", + "name": "parse_csv", + "filePath": "services/ingestion.py", + "lineRange": [165, 188], + "summary": "Parses an uploaded CSV file into mapped and validated BC records via pandas, capturing per-row parse errors.", + "tags": ["parsing", "ingestion", "csv"], + "complexity": "moderate" + }, + { + "id": "function:services/ingestion.py:parse_json", + "type": "function", + "name": "parse_json", + "filePath": "services/ingestion.py", + "lineRange": [191, 215], + "summary": "Parses an uploaded JSON file (single object or array) into mapped and validated BC records, capturing per-item parse errors.", + "tags": ["parsing", "ingestion", "json"], + "complexity": "moderate" + }, + { + "id": "function:services/ingestion.py:deduplicate", + "type": "function", + "name": "deduplicate", + "filePath": "services/ingestion.py", + "lineRange": [218, 227], + "summary": "Flags parsed records whose BC ID or NCIt code already exists in the database as duplicates, using case-insensitive comparison.", + "tags": ["validation", "ingestion", "deduplication"], + "complexity": "simple" + }, + { + "id": "function:tests/conftest.py:sample_bc", + "type": "function", + "name": "sample_bc", + "filePath": "tests/conftest.py", + "lineRange": [55, 68], + "summary": "Pytest fixture that inserts a sample BiomedicalConcept into the test database for use by tests needing an existing BC.", + "tags": ["test", "fixture"], + "complexity": "simple" + } + ], + "edges": [ + { + "source": "file:models/audit.py", + "target": "file:extensions.py", + "type": "imports", + "direction": "forward", + "weight": 0.7 + }, + { + "source": "file:models/bc.py", + "target": "file:extensions.py", + "type": "imports", + "direction": "forward", + "weight": 0.7 + }, + { + "source": "file:models/ingestion.py", + "target": "file:extensions.py", + "type": "imports", + "direction": "forward", + "weight": 0.7 + }, + { + "source": "file:routes/audit.py", + "target": "file:models/audit.py", + "type": "imports", + "direction": "forward", + "weight": 0.7 + }, + { + "source": "file:routes/ingestion.py", + "target": "file:extensions.py", + "type": "imports", + "direction": "forward", + "weight": 0.7 + }, + { + "source": "file:routes/ingestion.py", + "target": "file:models/audit.py", + "type": "imports", + "direction": "forward", + "weight": 0.7 + }, + { + "source": "file:routes/ingestion.py", + "target": "file:models/bc.py", + "type": "imports", + "direction": "forward", + "weight": 0.7 + }, + { + "source": "file:routes/ingestion.py", + "target": "file:models/ingestion.py", + "type": "imports", + "direction": "forward", + "weight": 0.7 + }, + { + "source": "file:routes/ingestion.py", + "target": "file:services/ingestion.py", + "type": "imports", + "direction": "forward", + "weight": 0.7 + }, + { + "source": "file:tests/conftest.py", + "target": "file:app.py", + "type": "imports", + "direction": "forward", + "weight": 0.7 + }, + { + "source": "file:tests/conftest.py", + "target": "file:extensions.py", + "type": "imports", + "direction": "forward", + "weight": 0.7 + }, + { + "source": "file:tests/conftest.py", + "target": "file:models/audit.py", + "type": "imports", + "direction": "forward", + "weight": 0.7 + }, + { + "source": "file:tests/conftest.py", + "target": "file:models/bc.py", + "type": "imports", + "direction": "forward", + "weight": 0.7 + }, + { + "source": "file:tests/conftest.py", + "target": "file:models/governance.py", + "type": "imports", + "direction": "forward", + "weight": 0.7 + }, + { + "source": "file:tests/conftest.py", + "target": "file:models/ingestion.py", + "type": "imports", + "direction": "forward", + "weight": 0.7 + }, + + { + "source": "file:models/audit.py", + "target": "class:models/audit.py:AuditLog", + "type": "contains", + "direction": "forward", + "weight": 1.0 + }, + { + "source": "file:models/bc.py", + "target": "class:models/bc.py:BiomedicalConcept", + "type": "contains", + "direction": "forward", + "weight": 1.0 + }, + { + "source": "file:models/bc.py", + "target": "class:models/bc.py:DataElementConcept", + "type": "contains", + "direction": "forward", + "weight": 1.0 + }, + { + "source": "file:models/ingestion.py", + "target": "class:models/ingestion.py:IngestionRecord", + "type": "contains", + "direction": "forward", + "weight": 1.0 + }, + { + "source": "file:routes/audit.py", + "target": "function:routes/audit.py:index", + "type": "contains", + "direction": "forward", + "weight": 1.0 + }, + { + "source": "file:routes/ingestion.py", + "target": "function:routes/ingestion.py:_bc_from_mapped", + "type": "contains", + "direction": "forward", + "weight": 1.0 + }, + { + "source": "file:routes/ingestion.py", + "target": "function:routes/ingestion.py:_create_decs", + "type": "contains", + "direction": "forward", + "weight": 1.0 + }, + { + "source": "file:routes/ingestion.py", + "target": "function:routes/ingestion.py:upload", + "type": "contains", + "direction": "forward", + "weight": 1.0 + }, + { + "source": "file:routes/ingestion.py", + "target": "function:routes/ingestion.py:approve", + "type": "contains", + "direction": "forward", + "weight": 1.0 + }, + { + "source": "file:routes/ingestion.py", + "target": "function:routes/ingestion.py:approve_all", + "type": "contains", + "direction": "forward", + "weight": 1.0 + }, + { + "source": "file:services/ingestion.py", + "target": "function:services/ingestion.py:_match_field", + "type": "contains", + "direction": "forward", + "weight": 1.0 + }, + { + "source": "file:services/ingestion.py", + "target": "function:services/ingestion.py:map_fields", + "type": "contains", + "direction": "forward", + "weight": 1.0 + }, + { + "source": "file:services/ingestion.py", + "target": "function:services/ingestion.py:validate_bc", + "type": "contains", + "direction": "forward", + "weight": 1.0 + }, + { + "source": "file:services/ingestion.py", + "target": "function:services/ingestion.py:_group_by_bc", + "type": "contains", + "direction": "forward", + "weight": 1.0 + }, + { + "source": "file:services/ingestion.py", + "target": "function:services/ingestion.py:parse_xlsx", + "type": "contains", + "direction": "forward", + "weight": 1.0 + }, + { + "source": "file:services/ingestion.py", + "target": "function:services/ingestion.py:parse_csv", + "type": "contains", + "direction": "forward", + "weight": 1.0 + }, + { + "source": "file:services/ingestion.py", + "target": "function:services/ingestion.py:parse_json", + "type": "contains", + "direction": "forward", + "weight": 1.0 + }, + { + "source": "file:services/ingestion.py", + "target": "function:services/ingestion.py:deduplicate", + "type": "contains", + "direction": "forward", + "weight": 1.0 + }, + { + "source": "file:tests/conftest.py", + "target": "function:tests/conftest.py:sample_bc", + "type": "contains", + "direction": "forward", + "weight": 1.0 + }, + + { + "source": "file:models/audit.py", + "target": "class:models/audit.py:AuditLog", + "type": "exports", + "direction": "forward", + "weight": 0.8 + }, + { + "source": "file:models/bc.py", + "target": "class:models/bc.py:BiomedicalConcept", + "type": "exports", + "direction": "forward", + "weight": 0.8 + }, + { + "source": "file:models/bc.py", + "target": "class:models/bc.py:DataElementConcept", + "type": "exports", + "direction": "forward", + "weight": 0.8 + }, + { + "source": "file:models/ingestion.py", + "target": "class:models/ingestion.py:IngestionRecord", + "type": "exports", + "direction": "forward", + "weight": 0.8 + }, + { + "source": "file:routes/audit.py", + "target": "function:routes/audit.py:index", + "type": "exports", + "direction": "forward", + "weight": 0.8 + }, + { + "source": "file:routes/ingestion.py", + "target": "function:routes/ingestion.py:_bc_from_mapped", + "type": "exports", + "direction": "forward", + "weight": 0.8 + }, + { + "source": "file:routes/ingestion.py", + "target": "function:routes/ingestion.py:_create_decs", + "type": "exports", + "direction": "forward", + "weight": 0.8 + }, + { + "source": "file:routes/ingestion.py", + "target": "function:routes/ingestion.py:upload", + "type": "exports", + "direction": "forward", + "weight": 0.8 + }, + { + "source": "file:routes/ingestion.py", + "target": "function:routes/ingestion.py:approve", + "type": "exports", + "direction": "forward", + "weight": 0.8 + }, + { + "source": "file:routes/ingestion.py", + "target": "function:routes/ingestion.py:approve_all", + "type": "exports", + "direction": "forward", + "weight": 0.8 + }, + { + "source": "file:services/ingestion.py", + "target": "function:services/ingestion.py:_match_field", + "type": "exports", + "direction": "forward", + "weight": 0.8 + }, + { + "source": "file:services/ingestion.py", + "target": "function:services/ingestion.py:map_fields", + "type": "exports", + "direction": "forward", + "weight": 0.8 + }, + { + "source": "file:services/ingestion.py", + "target": "function:services/ingestion.py:validate_bc", + "type": "exports", + "direction": "forward", + "weight": 0.8 + }, + { + "source": "file:services/ingestion.py", + "target": "function:services/ingestion.py:_group_by_bc", + "type": "exports", + "direction": "forward", + "weight": 0.8 + }, + { + "source": "file:services/ingestion.py", + "target": "function:services/ingestion.py:parse_xlsx", + "type": "exports", + "direction": "forward", + "weight": 0.8 + }, + { + "source": "file:services/ingestion.py", + "target": "function:services/ingestion.py:parse_csv", + "type": "exports", + "direction": "forward", + "weight": 0.8 + }, + { + "source": "file:services/ingestion.py", + "target": "function:services/ingestion.py:parse_json", + "type": "exports", + "direction": "forward", + "weight": 0.8 + }, + { + "source": "file:services/ingestion.py", + "target": "function:services/ingestion.py:deduplicate", + "type": "exports", + "direction": "forward", + "weight": 0.8 + }, + + { + "source": "function:routes/ingestion.py:_bc_from_mapped", + "target": "class:models/bc.py:BiomedicalConcept", + "type": "calls", + "direction": "forward", + "weight": 0.8 + }, + { + "source": "function:routes/ingestion.py:_create_decs", + "target": "class:models/bc.py:DataElementConcept", + "type": "calls", + "direction": "forward", + "weight": 0.8 + }, + { + "source": "function:routes/ingestion.py:approve", + "target": "class:models/audit.py:AuditLog", + "type": "calls", + "direction": "forward", + "weight": 0.8 + }, + { + "source": "function:routes/ingestion.py:approve_all", + "target": "class:models/audit.py:AuditLog", + "type": "calls", + "direction": "forward", + "weight": 0.8 + }, + { + "source": "function:routes/ingestion.py:upload", + "target": "function:services/ingestion.py:parse_xlsx", + "type": "calls", + "direction": "forward", + "weight": 0.8 + }, + { + "source": "function:routes/ingestion.py:upload", + "target": "function:services/ingestion.py:parse_csv", + "type": "calls", + "direction": "forward", + "weight": 0.8 + }, + { + "source": "function:routes/ingestion.py:upload", + "target": "function:services/ingestion.py:parse_json", + "type": "calls", + "direction": "forward", + "weight": 0.8 + }, + { + "source": "function:routes/ingestion.py:upload", + "target": "function:services/ingestion.py:deduplicate", + "type": "calls", + "direction": "forward", + "weight": 0.8 + }, + { + "source": "function:routes/audit.py:index", + "target": "class:models/audit.py:AuditLog", + "type": "calls", + "direction": "forward", + "weight": 0.8 + }, + + { + "source": "file:models/audit.py", + "target": "file:tests/test_models.py", + "type": "tested_by", + "direction": "forward", + "weight": 0.5 + }, + { + "source": "file:models/bc.py", + "target": "file:tests/test_models.py", + "type": "tested_by", + "direction": "forward", + "weight": 0.5 + }, + { + "source": "file:models/bc.py", + "target": "file:tests/test_bc_routes.py", + "type": "tested_by", + "direction": "forward", + "weight": 0.5 + }, + { + "source": "file:models/ingestion.py", + "target": "file:tests/test_models.py", + "type": "tested_by", + "direction": "forward", + "weight": 0.5 + }, + { + "source": "file:routes/audit.py", + "target": "file:tests/test_audit_routes.py", + "type": "tested_by", + "direction": "forward", + "weight": 0.5 + }, + { + "source": "file:routes/ingestion.py", + "target": "file:tests/test_ingestion_routes.py", + "type": "tested_by", + "direction": "forward", + "weight": 0.5 + }, + { + "source": "file:services/ingestion.py", + "target": "file:tests/test_ingestion_service.py", + "type": "tested_by", + "direction": "forward", + "weight": 0.5 + } + ] +} diff --git a/.ua/.trash-1785851745/batch-1-part-2.json b/.ua/.trash-1785851745/batch-1-part-2.json new file mode 100644 index 0000000..3bbae21 --- /dev/null +++ b/.ua/.trash-1785851745/batch-1-part-2.json @@ -0,0 +1,806 @@ +{ + "nodes": [ + { + "id": "file:tests/test_audit_routes.py", + "type": "file", + "name": "test_audit_routes.py", + "filePath": "tests/test_audit_routes.py", + "summary": "Test suite for the audit log route, covering empty listing, log rendering, and filtering by entity type, action, actor, and pagination parameters.", + "tags": ["test", "audit", "route-test"], + "complexity": "simple" + }, + { + "id": "class:tests/test_audit_routes.py:TestAuditIndex", + "type": "class", + "name": "TestAuditIndex", + "filePath": "tests/test_audit_routes.py", + "lineRange": [14, 49], + "summary": "Test class exercising the audit index route's rendering, entity/action/actor filters, and pagination parameter handling.", + "tags": ["test", "audit", "route-test"], + "complexity": "moderate" + }, + { + "id": "file:tests/test_bc_routes.py", + "type": "file", + "name": "test_bc_routes.py", + "filePath": "tests/test_bc_routes.py", + "summary": "Large test suite covering BC CRUD routes: listing/search, creation, detail view (including LOINC lookups), editing, clearing NCIt/LOINC codes, submission for review, deletion, export formats, and the CDISC Library detail page.", + "tags": ["test", "bc", "route-test"], + "complexity": "complex" + }, + { + "id": "function:tests/test_bc_routes.py:_bc_form", + "type": "function", + "name": "_bc_form", + "filePath": "tests/test_bc_routes.py", + "lineRange": [10, 19], + "summary": "Test helper that builds a default BC creation form payload, allowing field overrides via keyword arguments.", + "tags": ["test-helper", "fixture", "form-data"], + "complexity": "simple" + }, + { + "id": "class:tests/test_bc_routes.py:TestBcIndex", + "type": "class", + "name": "TestBcIndex", + "filePath": "tests/test_bc_routes.py", + "lineRange": [27, 43], + "summary": "Tests the BC index route's default listing, name search, no-match search, and status filtering.", + "tags": ["test", "bc", "route-test"], + "complexity": "moderate" + }, + { + "id": "class:tests/test_bc_routes.py:TestCreateBc", + "type": "class", + "name": "TestCreateBc", + "filePath": "tests/test_bc_routes.py", + "lineRange": [62, 109], + "summary": "Tests BC creation: success redirect, missing bc_id validation, duplicate id rejection, audit logging, conditional system/system_name population from a LOINC code, and DEC creation.", + "tags": ["test", "bc", "route-test", "validation"], + "complexity": "complex" + }, + { + "id": "class:tests/test_bc_routes.py:TestBcDetail", + "type": "class", + "name": "TestBcDetail", + "filePath": "tests/test_bc_routes.py", + "lineRange": [117, 224], + "summary": "Tests the BC detail page, including 404 handling and conditional LOINC API calls and spinner behavior based on whether a LOINC code is set.", + "tags": ["test", "bc", "loinc", "route-test"], + "complexity": "complex" + }, + { + "id": "class:tests/test_bc_routes.py:TestEditBc", + "type": "class", + "name": "TestEditBc", + "filePath": "tests/test_bc_routes.py", + "lineRange": [232, 332], + "summary": "Tests BC editing, covering short name updates, audit logging, clearing NCIt/LOINC/parent BC fields, and consistency between LOINC code and system/system_name fields.", + "tags": ["test", "bc", "route-test", "validation"], + "complexity": "complex" + }, + { + "id": "class:tests/test_bc_routes.py:TestClearNcitCode", + "type": "class", + "name": "TestClearNcitCode", + "filePath": "tests/test_bc_routes.py", + "lineRange": [340, 370], + "summary": "Tests the endpoint that clears a BC's NCIt code and associated metadata, including cascading clearing of the parent BC id.", + "tags": ["test", "bc", "ncit", "route-test"], + "complexity": "moderate" + }, + { + "id": "class:tests/test_bc_routes.py:TestClearLoincCode", + "type": "class", + "name": "TestClearLoincCode", + "filePath": "tests/test_bc_routes.py", + "lineRange": [378, 416], + "summary": "Tests the endpoint that clears a BC's LOINC code, associated metadata, and dependent system/system_name fields.", + "tags": ["test", "bc", "loinc", "route-test"], + "complexity": "moderate" + }, + { + "id": "class:tests/test_bc_routes.py:TestSubmitForReview", + "type": "class", + "name": "TestSubmitForReview", + "filePath": "tests/test_bc_routes.py", + "lineRange": [424, 435], + "summary": "Tests that submitting a BC for review advances its governance status and writes an audit log entry.", + "tags": ["test", "bc", "governance", "route-test"], + "complexity": "simple" + }, + { + "id": "class:tests/test_bc_routes.py:TestDeleteBc", + "type": "class", + "name": "TestDeleteBc", + "filePath": "tests/test_bc_routes.py", + "lineRange": [443, 457], + "summary": "Tests BC deletion, audit logging on delete, and 404 handling for nonexistent BCs.", + "tags": ["test", "bc", "route-test"], + "complexity": "moderate" + }, + { + "id": "class:tests/test_bc_routes.py:TestExport", + "type": "class", + "name": "TestExport", + "filePath": "tests/test_bc_routes.py", + "lineRange": [465, 479], + "summary": "Tests BC export endpoints across JSON, XLSX, and ODM-XML formats.", + "tags": ["test", "bc", "export", "route-test"], + "complexity": "moderate" + }, + { + "id": "class:tests/test_bc_routes.py:TestLibraryDetail", + "type": "class", + "name": "TestLibraryDetail", + "filePath": "tests/test_bc_routes.py", + "lineRange": [511, 547], + "summary": "Tests the CDISC Library detail page rendering, API-error redirects, and conditional LOINC API calls based on LOINC coding presence.", + "tags": ["test", "bc", "loinc", "cdisc-library", "route-test"], + "complexity": "complex" + }, + { + "id": "file:tests/test_governance_routes.py", + "type": "file", + "name": "test_governance_routes.py", + "filePath": "tests/test_governance_routes.py", + "summary": "Test suite for the governance Kanban board routes: stage advancement, rejection, and XLSX export of stage-3 BCs with LOINC-derived system columns.", + "tags": ["test", "governance", "route-test"], + "complexity": "complex" + }, + { + "id": "class:tests/test_governance_routes.py:TestAdvance", + "type": "class", + "name": "TestAdvance", + "filePath": "tests/test_governance_routes.py", + "lineRange": [17, 68], + "summary": "Tests governance stage advancement, including multi-stage progression, staying at published, governance record and audit log creation, 404 handling, and AJAX JSON responses.", + "tags": ["test", "governance", "route-test"], + "complexity": "complex" + }, + { + "id": "class:tests/test_governance_routes.py:TestReject", + "type": "class", + "name": "TestReject", + "filePath": "tests/test_governance_routes.py", + "lineRange": [71, 105], + "summary": "Tests governance rejection back to provisional status, governance record and audit log creation, AJAX responses, and 404 handling.", + "tags": ["test", "governance", "route-test"], + "complexity": "moderate" + }, + { + "id": "class:tests/test_governance_routes.py:TestGovernanceExport", + "type": "class", + "name": "TestGovernanceExport", + "filePath": "tests/test_governance_routes.py", + "lineRange": [108, 236], + "summary": "Tests the governance XLSX export endpoint: file format enforcement, filtering to stage-3 BCs, and correct population of System/System Name/Code columns based on LOINC code presence.", + "tags": ["test", "governance", "export", "route-test"], + "complexity": "complex" + }, + { + "id": "file:tests/test_ingestion_routes.py", + "type": "file", + "name": "test_ingestion_routes.py", + "filePath": "tests/test_ingestion_routes.py", + "summary": "Test suite for the ingestion pipeline routes: file upload (CSV/JSON), duplicate BC flagging, per-record approve/reject, and bulk approve-all with audit logging.", + "tags": ["test", "ingestion", "route-test"], + "complexity": "complex" + }, + { + "id": "class:tests/test_ingestion_routes.py:TestUpload", + "type": "class", + "name": "TestUpload", + "filePath": "tests/test_ingestion_routes.py", + "lineRange": [32, 83], + "summary": "Tests file upload handling for CSV and JSON ingestion, missing-file and wrong-extension errors, and duplicate BC detection.", + "tags": ["test", "ingestion", "upload", "route-test"], + "complexity": "complex" + }, + { + "id": "class:tests/test_ingestion_routes.py:TestApprove", + "type": "class", + "name": "TestApprove", + "filePath": "tests/test_ingestion_routes.py", + "lineRange": [86, 131], + "summary": "Tests approving an ingestion record, verifying BC creation, status transition, 404 handling, and duplicate-prevention when the BC already exists.", + "tags": ["test", "ingestion", "route-test"], + "complexity": "complex" + }, + { + "id": "class:tests/test_ingestion_routes.py:TestReject", + "type": "class", + "name": "TestReject", + "filePath": "tests/test_ingestion_routes.py", + "lineRange": [134, 170], + "summary": "Tests rejecting an ingestion record, ensuring no BC is created and 404 handling for missing records.", + "tags": ["test", "ingestion", "route-test"], + "complexity": "moderate" + }, + { + "id": "class:tests/test_ingestion_routes.py:TestApproveAll", + "type": "class", + "name": "TestApproveAll", + "filePath": "tests/test_ingestion_routes.py", + "lineRange": [173, 204], + "summary": "Tests bulk approval of all pending ingestion records, skipping records with validation errors and writing an audit log entry per created BC.", + "tags": ["test", "ingestion", "bulk-approve", "route-test"], + "complexity": "moderate" + }, + { + "id": "file:tests/test_ingestion_service.py", + "type": "file", + "name": "test_ingestion_service.py", + "filePath": "tests/test_ingestion_service.py", + "summary": "Unit test suite for the ingestion service module, covering field similarity scoring, field mapping/matching, BC validation, deduplication, and XLSX/CSV/JSON parsing and grouping logic.", + "tags": ["test", "ingestion", "service-test", "parsing"], + "complexity": "complex" + }, + { + "id": "class:tests/test_ingestion_service.py:TestSimilarity", + "type": "class", + "name": "TestSimilarity", + "filePath": "tests/test_ingestion_service.py", + "lineRange": [22, 34], + "summary": "Tests the fuzzy string similarity scorer used for field-name matching, covering identical, case-insensitive, dissimilar, and partial matches.", + "tags": ["test", "ingestion", "similarity"], + "complexity": "simple" + }, + { + "id": "class:tests/test_ingestion_service.py:TestMatchField", + "type": "class", + "name": "TestMatchField", + "filePath": "tests/test_ingestion_service.py", + "lineRange": [42, 65], + "summary": "Tests matching a raw column name to a canonical field via known aliases, definition synonyms, whitespace normalization, and low-confidence fallback.", + "tags": ["test", "ingestion", "field-mapping"], + "complexity": "moderate" + }, + { + "id": "class:tests/test_ingestion_service.py:TestMapFields", + "type": "class", + "name": "TestMapFields", + "filePath": "tests/test_ingestion_service.py", + "lineRange": [73, 93], + "summary": "Tests mapping a raw row dict to canonical fields, including handling of None/NaN values, low-confidence exclusion, and value stripping.", + "tags": ["test", "ingestion", "field-mapping"], + "complexity": "moderate" + }, + { + "id": "class:tests/test_ingestion_service.py:TestValidateBc", + "type": "class", + "name": "TestValidateBc", + "filePath": "tests/test_ingestion_service.py", + "lineRange": [101, 133], + "summary": "Tests BC record validation rules: required short name/definition, requiring at least one of NCIt/LOINC code, and NCIt code format checking.", + "tags": ["test", "ingestion", "validation"], + "complexity": "moderate" + }, + { + "id": "class:tests/test_ingestion_service.py:TestDeduplicate", + "type": "class", + "name": "TestDeduplicate", + "filePath": "tests/test_ingestion_service.py", + "lineRange": [141, 164], + "summary": "Tests deduplication of parsed ingestion records against existing BC ids, including case-insensitive comparison and NCIt-code fallback matching.", + "tags": ["test", "ingestion", "deduplication"], + "complexity": "moderate" + }, + { + "id": "class:tests/test_ingestion_service.py:TestParseCsv", + "type": "class", + "name": "TestParseCsv", + "filePath": "tests/test_ingestion_service.py", + "lineRange": [172, 198], + "summary": "Tests CSV parsing into mapped/validated records, including error handling for invalid files and validation-error propagation.", + "tags": ["test", "ingestion", "parsing", "csv"], + "complexity": "moderate" + }, + { + "id": "class:tests/test_ingestion_service.py:TestParseJson", + "type": "class", + "name": "TestParseJson", + "filePath": "tests/test_ingestion_service.py", + "lineRange": [206, 223], + "summary": "Tests JSON parsing for both array-of-objects and single-object inputs, and error handling for invalid JSON.", + "tags": ["test", "ingestion", "parsing", "json"], + "complexity": "simple" + }, + { + "id": "class:tests/test_ingestion_service.py:TestGroupByBc", + "type": "class", + "name": "TestGroupByBc", + "filePath": "tests/test_ingestion_service.py", + "lineRange": [231, 259], + "summary": "Tests grouping of parsed rows by BC id, including nested DEC sub-rows, multiple distinct BCs, and skipping rows without a BC id.", + "tags": ["test", "ingestion", "grouping"], + "complexity": "moderate" + }, + { + "id": "file:tests/test_models.py", + "type": "file", + "name": "test_models.py", + "filePath": "tests/test_models.py", + "summary": "Unit tests for the AuditLog, IngestionRecord, and BiomedicalConcept ORM models, verifying JSON-backed property round-tripping, computed averages, and dictionary serialization.", + "tags": ["test", "models", "orm"], + "complexity": "moderate" + }, + { + "id": "class:tests/test_models.py:TestAuditLogJsonProperties", + "type": "class", + "name": "TestAuditLogJsonProperties", + "filePath": "tests/test_models.py", + "lineRange": [9, 44], + "summary": "Tests round-tripping of AuditLog's JSON-backed before_state/after_state properties and correct handling of None values on a persisted record.", + "tags": ["test", "models", "audit", "serialization"], + "complexity": "moderate" + }, + { + "id": "class:tests/test_models.py:TestIngestionRecordProperties", + "type": "class", + "name": "TestIngestionRecordProperties", + "filePath": "tests/test_models.py", + "lineRange": [47, 87], + "summary": "Tests round-tripping of IngestionRecord's JSON-backed mapped/confidences/errors/decs properties and the correctness of the computed average confidence.", + "tags": ["test", "models", "ingestion", "serialization"], + "complexity": "moderate" + }, + { + "id": "class:tests/test_models.py:TestBiomedicalConceptToDict", + "type": "class", + "name": "TestBiomedicalConceptToDict", + "filePath": "tests/test_models.py", + "lineRange": [90, 116], + "summary": "Tests that BiomedicalConcept.to_dict() includes required keys, matches field values, and defaults status to provisional.", + "tags": ["test", "models", "bc", "serialization"], + "complexity": "simple" + } + ], + "edges": [ + { + "source": "file:tests/test_audit_routes.py", + "target": "file:extensions.py", + "type": "imports", + "direction": "forward", + "weight": 0.7 + }, + { + "source": "file:tests/test_audit_routes.py", + "target": "file:models/audit.py", + "type": "imports", + "direction": "forward", + "weight": 0.7 + }, + { + "source": "file:tests/test_bc_routes.py", + "target": "file:extensions.py", + "type": "imports", + "direction": "forward", + "weight": 0.7 + }, + { + "source": "file:tests/test_bc_routes.py", + "target": "file:models/audit.py", + "type": "imports", + "direction": "forward", + "weight": 0.7 + }, + { + "source": "file:tests/test_bc_routes.py", + "target": "file:models/bc.py", + "type": "imports", + "direction": "forward", + "weight": 0.7 + }, + { + "source": "file:tests/test_governance_routes.py", + "target": "file:extensions.py", + "type": "imports", + "direction": "forward", + "weight": 0.7 + }, + { + "source": "file:tests/test_governance_routes.py", + "target": "file:models/audit.py", + "type": "imports", + "direction": "forward", + "weight": 0.7 + }, + { + "source": "file:tests/test_governance_routes.py", + "target": "file:models/bc.py", + "type": "imports", + "direction": "forward", + "weight": 0.7 + }, + { + "source": "file:tests/test_governance_routes.py", + "target": "file:models/governance.py", + "type": "imports", + "direction": "forward", + "weight": 0.7 + }, + { + "source": "file:tests/test_ingestion_routes.py", + "target": "file:extensions.py", + "type": "imports", + "direction": "forward", + "weight": 0.7 + }, + { + "source": "file:tests/test_ingestion_routes.py", + "target": "file:models/audit.py", + "type": "imports", + "direction": "forward", + "weight": 0.7 + }, + { + "source": "file:tests/test_ingestion_routes.py", + "target": "file:models/bc.py", + "type": "imports", + "direction": "forward", + "weight": 0.7 + }, + { + "source": "file:tests/test_ingestion_routes.py", + "target": "file:models/ingestion.py", + "type": "imports", + "direction": "forward", + "weight": 0.7 + }, + { + "source": "file:tests/test_ingestion_service.py", + "target": "file:services/ingestion.py", + "type": "imports", + "direction": "forward", + "weight": 0.7 + }, + { + "source": "file:tests/test_models.py", + "target": "file:extensions.py", + "type": "imports", + "direction": "forward", + "weight": 0.7 + }, + { + "source": "file:tests/test_models.py", + "target": "file:models/audit.py", + "type": "imports", + "direction": "forward", + "weight": 0.7 + }, + { + "source": "file:tests/test_models.py", + "target": "file:models/bc.py", + "type": "imports", + "direction": "forward", + "weight": 0.7 + }, + { + "source": "file:tests/test_models.py", + "target": "file:models/ingestion.py", + "type": "imports", + "direction": "forward", + "weight": 0.7 + }, + + { + "source": "file:tests/test_audit_routes.py", + "target": "class:tests/test_audit_routes.py:TestAuditIndex", + "type": "contains", + "direction": "forward", + "weight": 1.0 + }, + + { + "source": "file:tests/test_bc_routes.py", + "target": "function:tests/test_bc_routes.py:_bc_form", + "type": "contains", + "direction": "forward", + "weight": 1.0 + }, + { + "source": "file:tests/test_bc_routes.py", + "target": "class:tests/test_bc_routes.py:TestBcIndex", + "type": "contains", + "direction": "forward", + "weight": 1.0 + }, + { + "source": "file:tests/test_bc_routes.py", + "target": "class:tests/test_bc_routes.py:TestCreateBc", + "type": "contains", + "direction": "forward", + "weight": 1.0 + }, + { + "source": "file:tests/test_bc_routes.py", + "target": "class:tests/test_bc_routes.py:TestBcDetail", + "type": "contains", + "direction": "forward", + "weight": 1.0 + }, + { + "source": "file:tests/test_bc_routes.py", + "target": "class:tests/test_bc_routes.py:TestEditBc", + "type": "contains", + "direction": "forward", + "weight": 1.0 + }, + { + "source": "file:tests/test_bc_routes.py", + "target": "class:tests/test_bc_routes.py:TestClearNcitCode", + "type": "contains", + "direction": "forward", + "weight": 1.0 + }, + { + "source": "file:tests/test_bc_routes.py", + "target": "class:tests/test_bc_routes.py:TestClearLoincCode", + "type": "contains", + "direction": "forward", + "weight": 1.0 + }, + { + "source": "file:tests/test_bc_routes.py", + "target": "class:tests/test_bc_routes.py:TestSubmitForReview", + "type": "contains", + "direction": "forward", + "weight": 1.0 + }, + { + "source": "file:tests/test_bc_routes.py", + "target": "class:tests/test_bc_routes.py:TestDeleteBc", + "type": "contains", + "direction": "forward", + "weight": 1.0 + }, + { + "source": "file:tests/test_bc_routes.py", + "target": "class:tests/test_bc_routes.py:TestExport", + "type": "contains", + "direction": "forward", + "weight": 1.0 + }, + { + "source": "file:tests/test_bc_routes.py", + "target": "class:tests/test_bc_routes.py:TestLibraryDetail", + "type": "contains", + "direction": "forward", + "weight": 1.0 + }, + + { + "source": "file:tests/test_governance_routes.py", + "target": "class:tests/test_governance_routes.py:TestAdvance", + "type": "contains", + "direction": "forward", + "weight": 1.0 + }, + { + "source": "file:tests/test_governance_routes.py", + "target": "class:tests/test_governance_routes.py:TestReject", + "type": "contains", + "direction": "forward", + "weight": 1.0 + }, + { + "source": "file:tests/test_governance_routes.py", + "target": "class:tests/test_governance_routes.py:TestGovernanceExport", + "type": "contains", + "direction": "forward", + "weight": 1.0 + }, + + { + "source": "file:tests/test_ingestion_routes.py", + "target": "class:tests/test_ingestion_routes.py:TestUpload", + "type": "contains", + "direction": "forward", + "weight": 1.0 + }, + { + "source": "file:tests/test_ingestion_routes.py", + "target": "class:tests/test_ingestion_routes.py:TestApprove", + "type": "contains", + "direction": "forward", + "weight": 1.0 + }, + { + "source": "file:tests/test_ingestion_routes.py", + "target": "class:tests/test_ingestion_routes.py:TestReject", + "type": "contains", + "direction": "forward", + "weight": 1.0 + }, + { + "source": "file:tests/test_ingestion_routes.py", + "target": "class:tests/test_ingestion_routes.py:TestApproveAll", + "type": "contains", + "direction": "forward", + "weight": 1.0 + }, + + { + "source": "file:tests/test_ingestion_service.py", + "target": "class:tests/test_ingestion_service.py:TestSimilarity", + "type": "contains", + "direction": "forward", + "weight": 1.0 + }, + { + "source": "file:tests/test_ingestion_service.py", + "target": "class:tests/test_ingestion_service.py:TestMatchField", + "type": "contains", + "direction": "forward", + "weight": 1.0 + }, + { + "source": "file:tests/test_ingestion_service.py", + "target": "class:tests/test_ingestion_service.py:TestMapFields", + "type": "contains", + "direction": "forward", + "weight": 1.0 + }, + { + "source": "file:tests/test_ingestion_service.py", + "target": "class:tests/test_ingestion_service.py:TestValidateBc", + "type": "contains", + "direction": "forward", + "weight": 1.0 + }, + { + "source": "file:tests/test_ingestion_service.py", + "target": "class:tests/test_ingestion_service.py:TestDeduplicate", + "type": "contains", + "direction": "forward", + "weight": 1.0 + }, + { + "source": "file:tests/test_ingestion_service.py", + "target": "class:tests/test_ingestion_service.py:TestParseCsv", + "type": "contains", + "direction": "forward", + "weight": 1.0 + }, + { + "source": "file:tests/test_ingestion_service.py", + "target": "class:tests/test_ingestion_service.py:TestParseJson", + "type": "contains", + "direction": "forward", + "weight": 1.0 + }, + { + "source": "file:tests/test_ingestion_service.py", + "target": "class:tests/test_ingestion_service.py:TestGroupByBc", + "type": "contains", + "direction": "forward", + "weight": 1.0 + }, + + { + "source": "file:tests/test_models.py", + "target": "class:tests/test_models.py:TestAuditLogJsonProperties", + "type": "contains", + "direction": "forward", + "weight": 1.0 + }, + { + "source": "file:tests/test_models.py", + "target": "class:tests/test_models.py:TestIngestionRecordProperties", + "type": "contains", + "direction": "forward", + "weight": 1.0 + }, + { + "source": "file:tests/test_models.py", + "target": "class:tests/test_models.py:TestBiomedicalConceptToDict", + "type": "contains", + "direction": "forward", + "weight": 1.0 + }, + + { + "source": "file:routes/audit.py", + "target": "file:tests/test_audit_routes.py", + "type": "tested_by", + "direction": "forward", + "weight": 0.5 + }, + { + "source": "file:models/audit.py", + "target": "file:tests/test_audit_routes.py", + "type": "tested_by", + "direction": "forward", + "weight": 0.5 + }, + + { + "source": "file:models/bc.py", + "target": "file:tests/test_bc_routes.py", + "type": "tested_by", + "direction": "forward", + "weight": 0.5 + }, + { + "source": "file:routes/bc.py", + "target": "file:tests/test_bc_routes.py", + "type": "tested_by", + "direction": "forward", + "weight": 0.5 + }, + + { + "source": "file:models/bc.py", + "target": "file:tests/test_governance_routes.py", + "type": "tested_by", + "direction": "forward", + "weight": 0.5 + }, + { + "source": "file:models/governance.py", + "target": "file:tests/test_governance_routes.py", + "type": "tested_by", + "direction": "forward", + "weight": 0.5 + }, + { + "source": "file:routes/governance.py", + "target": "file:tests/test_governance_routes.py", + "type": "tested_by", + "direction": "forward", + "weight": 0.5 + }, + + { + "source": "file:routes/ingestion.py", + "target": "file:tests/test_ingestion_routes.py", + "type": "tested_by", + "direction": "forward", + "weight": 0.5 + }, + { + "source": "file:models/bc.py", + "target": "file:tests/test_ingestion_routes.py", + "type": "tested_by", + "direction": "forward", + "weight": 0.5 + }, + { + "source": "file:models/ingestion.py", + "target": "file:tests/test_ingestion_routes.py", + "type": "tested_by", + "direction": "forward", + "weight": 0.5 + }, + + { + "source": "file:services/ingestion.py", + "target": "file:tests/test_ingestion_service.py", + "type": "tested_by", + "direction": "forward", + "weight": 0.5 + }, + + { + "source": "file:models/audit.py", + "target": "file:tests/test_models.py", + "type": "tested_by", + "direction": "forward", + "weight": 0.5 + }, + { + "source": "file:models/bc.py", + "target": "file:tests/test_models.py", + "type": "tested_by", + "direction": "forward", + "weight": 0.5 + }, + { + "source": "file:models/ingestion.py", + "target": "file:tests/test_models.py", + "type": "tested_by", + "direction": "forward", + "weight": 0.5 + } + ] +} diff --git a/.ua/.trash-1785851745/batch-10.json b/.ua/.trash-1785851745/batch-10.json new file mode 100644 index 0000000..3698efc --- /dev/null +++ b/.ua/.trash-1785851745/batch-10.json @@ -0,0 +1,256 @@ +{ + "nodes": [ + { + "id": "document:files/plan.md", + "type": "document", + "name": "plan.md", + "filePath": "files/plan.md", + "summary": "Initial high-level project brief for the CDISC Biomedical Concept Curation application, specifying the target UI mockup to match, the key reference files to review, the required agents (cdisc-concept-explorer, ncit-concept-resolver, cdisc-frontend-dev, mike), and the constraint that only the CDISC_API_KEY environment variable be used for API authentication.", + "tags": ["documentation", "planning", "project-brief", "requirements"], + "complexity": "simple" + }, + { + "id": "document:files/implementation.md", + "type": "document", + "name": "implementation.md", + "filePath": "files/implementation.md", + "summary": "Detailed implementation plan for the Flask/Jinja BC curation app, laying out the directory structure, SQLAlchemy data models, Flask blueprint/route table, service-layer API surface, template inventory, and a four-phase build roadmap from MVP through export and polish.", + "tags": [ + "documentation", + "architecture", + "implementation-plan", + "specification" + ], + "complexity": "moderate", + "languageNotes": "Serves as the architectural blueprint the actual codebase (app.py, models/, routes/, services/, templates/) was built from; nearly every file/module it names exists in the repository today." + }, + { + "id": "document:files/result.md", + "type": "document", + "name": "result.md", + "filePath": "files/result.md", + "summary": "Post-implementation status report confirming all 8 Flask routes return HTTP 200 and enumerating delivered capabilities: file ingestion with AI field mapping, BC CRUD, NCIt mapping, a 4-stage governance Kanban, an immutable audit trail, and JSON/XLSX/ODM-XML export.", + "tags": ["documentation", "summary", "status-report", "verification"], + "complexity": "simple" + } + ], + "edges": [ + { + "source": "document:files/plan.md", + "target": "file:files/cdisc_bc_ai_platform.html", + "type": "related", + "direction": "forward", + "weight": 0.5 + }, + { + "source": "document:files/plan.md", + "target": "file:files/cdisc_smart_goals_infographic.html", + "type": "related", + "direction": "forward", + "weight": 0.5 + }, + { + "source": "document:files/plan.md", + "target": "document:files/BC Curation Principles and Completion GLs.xlsx", + "type": "related", + "direction": "forward", + "weight": 0.5 + }, + { + "source": "document:files/plan.md", + "target": "document:files/BC Examples.xlsx", + "type": "related", + "direction": "forward", + "weight": 0.5 + }, + { + "source": "document:files/plan.md", + "target": "document:files/BC DEC Templates.xlsx", + "type": "related", + "direction": "forward", + "weight": 0.5 + }, + { + "source": "document:files/plan.md", + "target": "document:files/BC Governance.docx", + "type": "related", + "direction": "forward", + "weight": 0.5 + }, + { + "source": "document:files/plan.md", + "target": "document:files/implementation.md", + "type": "related", + "direction": "forward", + "weight": 0.5 + }, + { + "source": "document:files/implementation.md", + "target": "file:app.py", + "type": "documents", + "direction": "forward", + "weight": 0.5 + }, + { + "source": "document:files/implementation.md", + "target": "file:config.py", + "type": "documents", + "direction": "forward", + "weight": 0.5 + }, + { + "source": "document:files/implementation.md", + "target": "file:models/bc.py", + "type": "documents", + "direction": "forward", + "weight": 0.5 + }, + { + "source": "document:files/implementation.md", + "target": "file:models/specialization.py", + "type": "documents", + "direction": "forward", + "weight": 0.5 + }, + { + "source": "document:files/implementation.md", + "target": "file:models/governance.py", + "type": "documents", + "direction": "forward", + "weight": 0.5 + }, + { + "source": "document:files/implementation.md", + "target": "file:models/audit.py", + "type": "documents", + "direction": "forward", + "weight": 0.5 + }, + { + "source": "document:files/implementation.md", + "target": "file:routes/dashboard.py", + "type": "documents", + "direction": "forward", + "weight": 0.5 + }, + { + "source": "document:files/implementation.md", + "target": "file:routes/ingestion.py", + "type": "documents", + "direction": "forward", + "weight": 0.5 + }, + { + "source": "document:files/implementation.md", + "target": "file:routes/bc.py", + "type": "documents", + "direction": "forward", + "weight": 0.5 + }, + { + "source": "document:files/implementation.md", + "target": "file:routes/ncit.py", + "type": "documents", + "direction": "forward", + "weight": 0.5 + }, + { + "source": "document:files/implementation.md", + "target": "file:routes/specializations.py", + "type": "documents", + "direction": "forward", + "weight": 0.5 + }, + { + "source": "document:files/implementation.md", + "target": "file:routes/governance.py", + "type": "documents", + "direction": "forward", + "weight": 0.5 + }, + { + "source": "document:files/implementation.md", + "target": "file:routes/audit.py", + "type": "documents", + "direction": "forward", + "weight": 0.5 + }, + { + "source": "document:files/implementation.md", + "target": "file:services/cdisc_api.py", + "type": "documents", + "direction": "forward", + "weight": 0.5 + }, + { + "source": "document:files/implementation.md", + "target": "file:services/ncit_api.py", + "type": "documents", + "direction": "forward", + "weight": 0.5 + }, + { + "source": "document:files/implementation.md", + "target": "file:services/ingestion.py", + "type": "documents", + "direction": "forward", + "weight": 0.5 + }, + { + "source": "document:files/implementation.md", + "target": "file:services/export.py", + "type": "documents", + "direction": "forward", + "weight": 0.5 + }, + { + "source": "document:files/implementation.md", + "target": "file:templates/base.html", + "type": "documents", + "direction": "forward", + "weight": 0.5 + }, + { + "source": "document:files/implementation.md", + "target": "document:files/plan.md", + "type": "related", + "direction": "forward", + "weight": 0.5 + }, + { + "source": "document:files/implementation.md", + "target": "document:files/result.md", + "type": "related", + "direction": "forward", + "weight": 0.5 + }, + { + "source": "document:files/result.md", + "target": "file:app.py", + "type": "documents", + "direction": "forward", + "weight": 0.5 + }, + { + "source": "document:files/result.md", + "target": "file:extensions.py", + "type": "documents", + "direction": "forward", + "weight": 0.5 + }, + { + "source": "document:files/result.md", + "target": "file:config.py", + "type": "documents", + "direction": "forward", + "weight": 0.5 + }, + { + "source": "document:files/result.md", + "target": "document:files/implementation.md", + "type": "related", + "direction": "forward", + "weight": 0.5 + } + ] +} diff --git a/.ua/.trash-1785851745/batch-11.json b/.ua/.trash-1785851745/batch-11.json new file mode 100644 index 0000000..c81c147 --- /dev/null +++ b/.ua/.trash-1785851745/batch-11.json @@ -0,0 +1,205 @@ +{ + "nodes": [ + { + "id": "file:templates/base.html", + "type": "file", + "name": "base.html", + "filePath": "templates/base.html", + "summary": "Master Jinja2 layout providing the sidebar navigation, top bar, flash-message rendering, and template blocks (title, page_title, user_info, content, extra_js) that every other page in the app extends.", + "tags": ["layout", "navigation", "entry-point", "bootstrap", "component"], + "complexity": "moderate", + "languageNotes": "Jinja2 block-based inheritance root — defines the extension points (`{% block content %}`, `{% block extra_js %}`, etc.) that all ten other templates in this batch fill in via `{% extends \"base.html\" %}`." + }, + { + "id": "file:templates/audit.html", + "type": "file", + "name": "audit.html", + "filePath": "templates/audit.html", + "summary": "Audit trail page listing all curation actions (create/update/delete/advance/reject/publish/ingest) with actor/entity/date filters, CSV/JSON export links, expandable before/after diff rows, and pagination.", + "tags": ["audit-trail", "data-table", "filter", "pagination", "export"], + "complexity": "complex" + }, + { + "id": "file:templates/bc_detail.html", + "type": "file", + "name": "bc_detail.html", + "filePath": "templates/bc_detail.html", + "summary": "Create/edit form for a single Biomedical Concept covering identification, NCIt and LOINC lookups (with async metadata fetch in extra_js), classification, definition, metadata/status, and an editable Data Element Concepts (DECs) table.", + "tags": [ + "form", + "crud", + "ncit-integration", + "loinc-integration", + "dynamic-form" + ], + "complexity": "complex", + "languageNotes": "extra_js block fetches BC metadata via `bc.fetch_metadata` and rebuilds the NCIt/LOINC display grids client-side, mirroring field lists that are also rendered server-side in Jinja — a duplicated-schema pattern worth keeping in sync if fields change." + }, + { + "id": "file:templates/bc_list.html", + "type": "file", + "name": "bc_list.html", + "filePath": "templates/bc_list.html", + "summary": "Searchable, filterable, paginated table of locally curated Biomedical Concepts with status badges, JSON/XLSX/ODM-XML export links, and a delete-confirmation modal.", + "tags": ["list-view", "search", "filter", "pagination", "export"], + "complexity": "complex" + }, + { + "id": "file:templates/dashboard.html", + "type": "file", + "name": "dashboard.html", + "filePath": "templates/dashboard.html", + "summary": "Main landing dashboard showing KPI cards, recent submissions, the governance pipeline snapshot, paginated/searchable views of CDISC Library BCs and dataset specializations, an AI curation pipeline progress tracker, and quick action links.", + "tags": [ + "dashboard", + "kpi-metrics", + "pipeline-status", + "pagination", + "cdisc-library" + ], + "complexity": "complex" + }, + { + "id": "file:templates/governance.html", + "type": "file", + "name": "governance.html", + "filePath": "templates/governance.html", + "summary": "Four-column Kanban board (Provisional -> SME Review -> CDISC Approval -> Published) for advancing or rejecting Biomedical Concepts through the governance workflow, with summary counts and an export-filename modal.", + "tags": ["kanban", "governance", "workflow", "modal", "export"], + "complexity": "complex" + }, + { + "id": "file:templates/ingestion.html", + "type": "file", + "name": "ingestion.html", + "filePath": "templates/ingestion.html", + "summary": "File upload page (drag-and-drop or click) for XLSX/CSV/JSON ingestion, with a parsed-results queue table showing field-mapping confidence, validation errors, duplicate flags, and per-row approve/reject actions.", + "tags": ["file-upload", "drag-drop", "queue", "validation", "form"], + "complexity": "moderate" + }, + { + "id": "file:templates/library_bc_detail.html", + "type": "file", + "name": "library_bc_detail.html", + "filePath": "templates/library_bc_detail.html", + "summary": "Read-only detail view of a single Biomedical Concept fetched from the published CDISC Library, showing identification, definition, LOINC coding, metadata, and its Data Element Concepts.", + "tags": [ + "read-only-view", + "cdisc-library", + "loinc-integration", + "detail-view" + ], + "complexity": "moderate" + }, + { + "id": "file:templates/library_spec_detail.html", + "type": "file", + "name": "library_spec_detail.html", + "filePath": "templates/library_spec_detail.html", + "summary": "Read-only detail view of a Dataset Specialization from the published CDISC Library, showing identification/domain metadata and a full variables table (role, codelist, assigned term, mandatory flags, relationship).", + "tags": [ + "read-only-view", + "cdisc-library", + "variables-table", + "detail-view" + ], + "complexity": "moderate" + }, + { + "id": "file:templates/ncit_mapping.html", + "type": "file", + "name": "ncit_mapping.html", + "filePath": "templates/ncit_mapping.html", + "summary": "NCI Thesaurus search page plus a conflict-resolution queue listing Biomedical Concepts with unresolved or low-confidence NCIt mappings that block governance advancement, with a confidence-score legend.", + "tags": [ + "ncit-integration", + "search", + "conflict-resolution", + "confidence-score" + ], + "complexity": "moderate" + }, + { + "id": "file:templates/specializations.html", + "type": "file", + "name": "specializations.html", + "filePath": "templates/specializations.html", + "summary": "Dataset Specialization management page with a collapsible create/edit form (VLM group ID, BC selector, SDTM/CDASH domain, dynamic variables table, generate-from-DEC-templates action) and a list/edit/delete table of existing specializations.", + "tags": ["crud", "dynamic-form", "sdtm", "cdash", "variable-table"], + "complexity": "complex", + "languageNotes": "extra_js block mixes server-rendered variable rows with client-side row templating (identical markup duplicated in Jinja and JS template-literal form) for the add-row and generate-from-DEC-templates interactions." + } + ], + "edges": [ + { + "source": "file:templates/audit.html", + "target": "file:templates/base.html", + "type": "depends_on", + "direction": "forward", + "weight": 0.6 + }, + { + "source": "file:templates/bc_detail.html", + "target": "file:templates/base.html", + "type": "depends_on", + "direction": "forward", + "weight": 0.6 + }, + { + "source": "file:templates/bc_list.html", + "target": "file:templates/base.html", + "type": "depends_on", + "direction": "forward", + "weight": 0.6 + }, + { + "source": "file:templates/dashboard.html", + "target": "file:templates/base.html", + "type": "depends_on", + "direction": "forward", + "weight": 0.6 + }, + { + "source": "file:templates/governance.html", + "target": "file:templates/base.html", + "type": "depends_on", + "direction": "forward", + "weight": 0.6 + }, + { + "source": "file:templates/ingestion.html", + "target": "file:templates/base.html", + "type": "depends_on", + "direction": "forward", + "weight": 0.6 + }, + { + "source": "file:templates/library_bc_detail.html", + "target": "file:templates/base.html", + "type": "depends_on", + "direction": "forward", + "weight": 0.6 + }, + { + "source": "file:templates/library_spec_detail.html", + "target": "file:templates/base.html", + "type": "depends_on", + "direction": "forward", + "weight": 0.6 + }, + { + "source": "file:templates/ncit_mapping.html", + "target": "file:templates/base.html", + "type": "depends_on", + "direction": "forward", + "weight": 0.6 + }, + { + "source": "file:templates/specializations.html", + "target": "file:templates/base.html", + "type": "depends_on", + "direction": "forward", + "weight": 0.6 + } + ] +} diff --git a/.ua/.trash-1785851745/batch-12.json b/.ua/.trash-1785851745/batch-12.json new file mode 100644 index 0000000..b4e596b --- /dev/null +++ b/.ua/.trash-1785851745/batch-12.json @@ -0,0 +1,790 @@ +{ + "nodes": [ + { + "id": "document:.claude/agent-memory/cdisc-concept-explorer/MEMORY.md", + "type": "document", + "name": "MEMORY.md", + "filePath": ".claude/agent-memory/cdisc-concept-explorer/MEMORY.md", + "summary": "Single-line agent memory index for the cdisc-concept-explorer agent, pointing to the verified CDISC API reference notes.", + "tags": ["documentation", "agent-memory", "reference"], + "complexity": "simple" + }, + { + "id": "document:.claude/agent-memory/cdisc-concept-explorer/reference_api_endpoints.md", + "type": "document", + "name": "reference_api_endpoints.md", + "filePath": ".claude/agent-memory/cdisc-concept-explorer/reference_api_endpoints.md", + "summary": "Verified reference notes on the CDISC Library COSMOS v2 API: base URL, working endpoints, lack of pagination, and field names for biomedical concept and dataset specialization records.", + "tags": [ + "documentation", + "api-reference", + "cdisc-library", + "agent-memory" + ], + "complexity": "simple" + }, + { + "id": "document:.claude/skills/code-review/SKILL.md", + "type": "document", + "name": "SKILL.md", + "filePath": ".claude/skills/code-review/SKILL.md", + "summary": "Project-specific code review checklist for cdisc-concept-curation covering route/service/model layering, audit log coverage, Alembic migration discipline, API client error handling, test isolation, and TDD conventions.", + "tags": ["documentation", "code-review", "development", "checklist"], + "complexity": "simple" + }, + { + "id": "document:.claude/skills/run-concept-curation/SKILL.md", + "type": "document", + "name": "SKILL.md", + "filePath": ".claude/skills/run-concept-curation/SKILL.md", + "summary": "Instructions for running and smoke-testing the Flask dev server, including the preferred automated smoke-test script, manual startup steps, and troubleshooting tips.", + "tags": ["documentation", "development", "smoke-test", "deployment"], + "complexity": "simple" + }, + { + "id": "file:.claude/skills/run-concept-curation/smoke.sh", + "type": "file", + "name": "smoke.sh", + "filePath": ".claude/skills/run-concept-curation/smoke.sh", + "summary": "Shell script that boots the Flask app on a throwaway SQLite database and port, runs a battery of HTTP checks against key routes (dashboard, BC CRUD, governance, NCIt, audit, ingestion, specializations, export), and tears the server down.", + "tags": ["script", "smoke-test", "automation", "development"], + "complexity": "moderate", + "languageNotes": "Bash script using a trap-based cleanup function and small curl-wrapping helpers (check/get_code/post_code) to assert HTTP status codes." + }, + { + "id": "function:.claude/skills/run-concept-curation/smoke.sh:cleanup", + "type": "function", + "name": "cleanup", + "filePath": ".claude/skills/run-concept-curation/smoke.sh", + "lineRange": [16, 22], + "summary": "Trap handler that kills the background dev server process and removes the throwaway SQLite database directory on script exit.", + "tags": ["cleanup", "process-management", "script"], + "complexity": "simple" + }, + { + "id": "function:.claude/skills/run-concept-curation/smoke.sh:check", + "type": "function", + "name": "check", + "filePath": ".claude/skills/run-concept-curation/smoke.sh", + "lineRange": [25, 34], + "summary": "Assertion helper that compares an actual HTTP status code against an expected value and prints a PASS/FAIL line for the smoke test report.", + "tags": ["assertion", "smoke-test", "validation"], + "complexity": "simple" + }, + { + "id": "config:.flake8", + "type": "config", + "name": ".flake8", + "filePath": ".flake8", + "summary": "Flake8 lint configuration setting max line length to 200, ignoring black-conflicting rules (E203/W503) and SQLAlchemy's `== None` idiom (E711), and excluding build/venv directories.", + "tags": ["configuration", "linting", "build-system", "python"], + "complexity": "simple" + }, + { + "id": "config:.hintrc", + "type": "config", + "name": ".hintrc", + "filePath": ".hintrc", + "summary": "webhint configuration extending the 'development' ruleset and disabling the no-inline-styles rule for HTML/CSS linting.", + "tags": ["configuration", "linting", "frontend"], + "complexity": "simple" + }, + { + "id": "config:.ua/.understandignore", + "type": "config", + "name": ".understandignore", + "filePath": ".ua/.understandignore", + "summary": "Gitignore-style exclusion list for the Understand-Anything analysis tool, with commented-out suggestions mirroring the project's .gitignore (Python caches, virtualenvs, egg metadata) that can be uncommented to activate.", + "tags": ["configuration", "tooling", "analysis"], + "complexity": "simple" + }, + { + "id": "config:.ua/config.json", + "type": "config", + "name": "config.json", + "filePath": ".ua/config.json", + "summary": "Single-key configuration for the Understand-Anything tool setting the output language for generated analysis text to English.", + "tags": ["configuration", "tooling", "analysis"], + "complexity": "simple" + }, + { + "id": "file:files/BC Curation Principles and Completion GLs.xlsx", + "type": "file", + "name": "BC Curation Principles and Completion GLs.xlsx", + "filePath": "files/BC Curation Principles and Completion GLs.xlsx", + "summary": "Reference spreadsheet documenting BC curation principles and field-completion guidelines used by curators when authoring Biomedical Concepts.", + "tags": [ + "reference", + "documentation", + "spreadsheet", + "curation-guidance" + ], + "complexity": "moderate" + }, + { + "id": "file:files/BC DEC Templates.xlsx", + "type": "file", + "name": "BC DEC Templates.xlsx", + "filePath": "files/BC DEC Templates.xlsx", + "summary": "Template spreadsheet defining the standard row/column layout for Data Element Concepts, used as an ingestion source template for curators.", + "tags": ["template", "spreadsheet", "ingestion", "reference"], + "complexity": "simple" + }, + { + "id": "file:files/BC Examples.xlsx", + "type": "file", + "name": "BC Examples.xlsx", + "filePath": "files/BC Examples.xlsx", + "summary": "Example spreadsheet of fully-populated Biomedical Concepts, used as a reference/sample dataset for the ingestion pipeline and curator training.", + "tags": ["reference", "spreadsheet", "sample-data", "ingestion"], + "complexity": "moderate" + }, + { + "id": "file:files/BC Governance.docx", + "type": "file", + "name": "BC Governance.docx", + "filePath": "files/BC Governance.docx", + "summary": "Word document describing the governance process and stages (Provisional, SME Review, CDISC Approval, Published) that a curated Biomedical Concept moves through.", + "tags": ["documentation", "governance", "reference"], + "complexity": "simple" + }, + { + "id": "file:mcp_server/__init__.py", + "type": "file", + "name": "__init__.py", + "filePath": "mcp_server/__init__.py", + "summary": "Package marker and docstring for the mcp_server package, noting it is run via `python -m mcp_server` and deliberately named to avoid shadowing the MCP SDK package.", + "tags": ["entry-point", "package-init", "mcp"], + "complexity": "simple" + }, + { + "id": "document:migrations/README", + "type": "document", + "name": "README", + "filePath": "migrations/README", + "summary": "One-line note identifying the migrations directory as a single-database Flask-Migrate/Alembic configuration.", + "tags": ["documentation", "database", "migration"], + "complexity": "simple" + }, + { + "id": "config:migrations/alembic.ini", + "type": "config", + "name": "alembic.ini", + "filePath": "migrations/alembic.ini", + "summary": "Alembic configuration file controlling migration script location, logging setup, and version-file naming for the Flask-Migrate managed schema.", + "tags": ["configuration", "database", "migration", "alembic"], + "complexity": "simple" + }, + { + "id": "file:migrations/env.py", + "type": "file", + "name": "env.py", + "filePath": "migrations/env.py", + "summary": "Alembic environment script that wires Flask-Migrate's engine/metadata into Alembic's migration context, supporting both offline (SQL-only) and online (live-connection) migration runs.", + "tags": ["database", "migration", "alembic", "flask-migrate"], + "complexity": "moderate", + "languageNotes": "Handles both Flask-SQLAlchemy <3 and >=3 engine-access APIs via a try/except fallback, and skips autogenerating empty migrations via a process_revision_directives hook." + }, + { + "id": "function:migrations/env.py:get_engine", + "type": "function", + "name": "get_engine", + "filePath": "migrations/env.py", + "lineRange": [17, 23], + "summary": "Returns the active SQLAlchemy engine from the Flask-Migrate extension, falling back between the Flask-SQLAlchemy 2.x and 3.x APIs.", + "tags": ["database", "migration", "compatibility"], + "complexity": "simple" + }, + { + "id": "function:migrations/env.py:get_engine_url", + "type": "function", + "name": "get_engine_url", + "filePath": "migrations/env.py", + "lineRange": [26, 30], + "summary": "Renders the database engine's connection URL as a string with percent signs escaped, for use as Alembic's sqlalchemy.url option.", + "tags": ["database", "migration", "utility"], + "complexity": "simple" + }, + { + "id": "function:migrations/env.py:run_migrations_offline", + "type": "function", + "name": "run_migrations_offline", + "filePath": "migrations/env.py", + "lineRange": [52, 68], + "summary": "Runs Alembic migrations in offline mode by configuring the context with just the database URL and emitting SQL without requiring a live connection.", + "tags": ["database", "migration", "alembic"], + "complexity": "moderate" + }, + { + "id": "function:migrations/env.py:run_migrations_online", + "type": "function", + "name": "run_migrations_online", + "filePath": "migrations/env.py", + "lineRange": [71, 99], + "summary": "Runs Alembic migrations in online mode against a live database connection, registering a hook that skips autogenerating a migration when no schema changes are detected.", + "tags": ["database", "migration", "alembic"], + "complexity": "moderate" + }, + { + "id": "file:migrations/script.py.mako", + "type": "file", + "name": "script.py.mako", + "filePath": "migrations/script.py.mako", + "summary": "Mako template used by `flask db migrate` to scaffold new Alembic revision files with the standard revision metadata and upgrade/downgrade function stubs.", + "tags": ["template", "database", "migration", "code-generation"], + "complexity": "simple" + }, + { + "id": "file:migrations/versions/51d4a009d291_baseline_initial_schema.py", + "type": "file", + "name": "51d4a009d291_baseline_initial_schema.py", + "filePath": "migrations/versions/51d4a009d291_baseline_initial_schema.py", + "summary": "Baseline Alembic migration that creates the initial database schema: audit_logs, biomedical_concepts, ingestion_records, data_element_concepts, dataset_specializations, and governance_records tables, with their downgrade counterparts.", + "tags": ["database", "migration", "schema", "baseline"], + "complexity": "complex", + "languageNotes": "Auto-generated Alembic revision consisting almost entirely of repetitive op.create_table/sa.Column calls plus a batch_alter_table index creation." + }, + { + "id": "function:migrations/versions/51d4a009d291_baseline_initial_schema.py:upgrade", + "type": "function", + "name": "upgrade", + "filePath": "migrations/versions/51d4a009d291_baseline_initial_schema.py", + "lineRange": [19, 128], + "summary": "Creates the six baseline tables (audit_logs, biomedical_concepts, ingestion_records, data_element_concepts, dataset_specializations, governance_records) with their columns, primary keys, foreign keys, and one index.", + "tags": ["database", "migration", "schema-creation"], + "complexity": "complex" + }, + { + "id": "function:migrations/versions/51d4a009d291_baseline_initial_schema.py:downgrade", + "type": "function", + "name": "downgrade", + "filePath": "migrations/versions/51d4a009d291_baseline_initial_schema.py", + "lineRange": [131, 142], + "summary": "Reverses the baseline migration by dropping the index and all six tables created in upgrade().", + "tags": ["database", "migration", "rollback"], + "complexity": "simple" + }, + { + "id": "file:routes/__init__.py", + "type": "file", + "name": "__init__.py", + "filePath": "routes/__init__.py", + "summary": "Empty package marker file that makes routes/ a Python package for the Flask blueprint modules it contains.", + "tags": ["package-init", "empty"], + "complexity": "simple" + }, + { + "id": "file:static/css/custom.css", + "type": "file", + "name": "custom.css", + "filePath": "static/css/custom.css", + "summary": "Main application stylesheet defining design tokens (colors, spacing), base resets, app shell layout (sidebar, topbar, content area), and component styles (cards, flash messages, nav items, badges) for the CDISC BC curation UI.", + "tags": ["stylesheet", "design-system", "layout", "ui"], + "complexity": "complex", + "languageNotes": "Organized around CSS custom properties (design tokens) under :root, consistent with the project's CDISC brand color requirements." + }, + { + "id": "file:static/js/main.js", + "type": "file", + "name": "main.js", + "filePath": "static/js/main.js", + "summary": "Client-side interactivity module for the BC curation platform: flash-message dismissal, drag-and-drop file upload, NCIt/LOINC lookup and search panels, dynamic DEC table rows, governance Kanban actions, audit-diff toggles, and shared fetch/toast helpers, all wired up on DOMContentLoaded.", + "tags": ["frontend", "vanilla-js", "ui-interactivity", "fetch-api"], + "complexity": "complex", + "languageNotes": "Entire file is wrapped in an IIFE for scope isolation; all functions are private and invoked from a single DOMContentLoaded listener at the bottom of the file." + }, + { + "id": "function:static/js/main.js:initFileUpload", + "type": "function", + "name": "initFileUpload", + "filePath": "static/js/main.js", + "lineRange": [25, 76], + "summary": "Wires up the file-input and drag-and-drop upload zone: updates the displayed filename, toggles drag-over styling, and assigns dropped files to the hidden file input.", + "tags": ["ui-interactivity", "file-upload", "drag-and-drop"], + "complexity": "moderate" + }, + { + "id": "function:static/js/main.js:initNcitLookup", + "type": "function", + "name": "initNcitLookup", + "filePath": "static/js/main.js", + "lineRange": [84, 131], + "summary": "Handles the NCIt code lookup button: fetches /ncit/search for a typed NCIt code and renders matches into the results panel, or shows an error state on failure.", + "tags": ["api-handler", "fetch-api", "ncit", "ui-interactivity"], + "complexity": "moderate" + }, + { + "id": "function:static/js/main.js:renderNcitResults", + "type": "function", + "name": "renderNcitResults", + "filePath": "static/js/main.js", + "lineRange": [132, 207], + "summary": "Renders NCIt lookup result cards into a container and wires each card's 'Use this concept' button to populate the BC form and fetch full concept metadata for display.", + "tags": ["rendering", "ui-interactivity", "ncit", "fetch-api"], + "complexity": "complex" + }, + { + "id": "function:static/js/main.js:renderNcitMetaDisplay", + "type": "function", + "name": "renderNcitMetaDisplay", + "filePath": "static/js/main.js", + "lineRange": [208, 265], + "summary": "Formats and displays selected NCIt concept metadata fields (definitions, parents, synonyms, etc.) as a labeled list, handling both scalar and array-shaped fields.", + "tags": ["rendering", "ncit", "ui-interactivity"], + "complexity": "moderate" + }, + { + "id": "function:static/js/main.js:initDecTable", + "type": "function", + "name": "initDecTable", + "filePath": "static/js/main.js", + "lineRange": [266, 290], + "summary": "Initializes the Data Element Concept table: wires the 'add DEC row' button and delegates row-delete clicks to remove table rows.", + "tags": ["ui-interactivity", "dynamic-table", "dec"], + "complexity": "simple" + }, + { + "id": "function:static/js/main.js:buildDecRow", + "type": "function", + "name": "buildDecRow", + "filePath": "static/js/main.js", + "lineRange": [291, 331], + "summary": "Builds and returns a new table row element containing the input fields for a Data Element Concept entry, indexed for form submission.", + "tags": ["dom-construction", "dynamic-table", "dec"], + "complexity": "moderate" + }, + { + "id": "function:static/js/main.js:initKanban", + "type": "function", + "name": "initKanban", + "filePath": "static/js/main.js", + "lineRange": [332, 386], + "summary": "Wires the governance Kanban board's advance and reject buttons to POST stage-transition requests and reload or toast on success/failure.", + "tags": ["api-handler", "governance", "kanban", "fetch-api"], + "complexity": "moderate" + }, + { + "id": "function:static/js/main.js:initAuditDiff", + "type": "function", + "name": "initAuditDiff", + "filePath": "static/js/main.js", + "lineRange": [387, 402], + "summary": "Toggles the visibility of before/after diff panels on the audit log page when a diff-toggle button is clicked.", + "tags": ["ui-interactivity", "audit", "toggle"], + "complexity": "simple" + }, + { + "id": "function:static/js/main.js:initNcitSearch", + "type": "function", + "name": "initNcitSearch", + "filePath": "static/js/main.js", + "lineRange": [403, 444], + "summary": "Wires the NCIt search page's search button and Enter key to trigger a debounced-free search request and render results.", + "tags": ["api-handler", "ncit", "search", "ui-interactivity"], + "complexity": "moderate" + }, + { + "id": "function:static/js/main.js:renderNcitSearchResults", + "type": "function", + "name": "renderNcitSearchResults", + "filePath": "static/js/main.js", + "lineRange": [445, 529], + "summary": "Renders NCIt search result cards with synonyms and a 'Use this concept' button that stores the chosen concept in sessionStorage and navigates to the BC creation page.", + "tags": ["rendering", "ncit", "search", "ui-interactivity"], + "complexity": "complex" + }, + { + "id": "function:static/js/main.js:showInlineToast", + "type": "function", + "name": "showInlineToast", + "filePath": "static/js/main.js", + "lineRange": [537, 563], + "summary": "Creates and displays a Bootstrap toast notification with the given message and style, appending it to a shared toast container.", + "tags": ["ui-interactivity", "toast", "notification"], + "complexity": "moderate" + }, + { + "id": "function:static/js/main.js:getOrCreateToastContainer", + "type": "function", + "name": "getOrCreateToastContainer", + "filePath": "static/js/main.js", + "lineRange": [564, 578], + "summary": "Finds or lazily creates the fixed-position container element that hosts stacked toast notifications.", + "tags": ["dom-construction", "toast", "utility"], + "complexity": "simple" + }, + { + "id": "function:static/js/main.js:initLoincLookup", + "type": "function", + "name": "initLoincLookup", + "filePath": "static/js/main.js", + "lineRange": [593, 680], + "summary": "Handles the LOINC code lookup button: fetches /loinc/search for a typed LOINC code and renders matches, mirroring the NCIt lookup flow.", + "tags": ["api-handler", "fetch-api", "loinc", "ui-interactivity"], + "complexity": "moderate" + }, + { + "id": "function:static/js/main.js:renderLoincResults", + "type": "function", + "name": "renderLoincResults", + "filePath": "static/js/main.js", + "lineRange": [681, 755], + "summary": "Renders LOINC lookup result cards into a container and wires each card's selection button to populate form fields and display LOINC metadata.", + "tags": ["rendering", "loinc", "ui-interactivity", "fetch-api"], + "complexity": "complex" + }, + { + "id": "function:static/js/main.js:renderLoincMetaDisplay", + "type": "function", + "name": "renderLoincMetaDisplay", + "filePath": "static/js/main.js", + "lineRange": [756, 782], + "summary": "Formats and displays selected LOINC metadata fields as a labeled list on the BC detail page.", + "tags": ["rendering", "loinc", "ui-interactivity"], + "complexity": "moderate" + }, + { + "id": "function:static/js/main.js:initPendingNcitConcept", + "type": "function", + "name": "initPendingNcitConcept", + "filePath": "static/js/main.js", + "lineRange": [783, 801], + "summary": "On page load, consumes a pending NCIt concept stashed in sessionStorage (from the NCIt search page) and auto-populates the BC detail page's metadata display and hidden field.", + "tags": ["ui-interactivity", "ncit", "session-storage"], + "complexity": "moderate" + }, + { + "id": "file:tests/__init__.py", + "type": "file", + "name": "__init__.py", + "filePath": "tests/__init__.py", + "summary": "Empty package marker file that makes tests/ a discoverable Python package for pytest.", + "tags": ["package-init", "test", "empty"], + "complexity": "simple" + } + ], + "edges": [ + { + "source": "file:.claude/skills/run-concept-curation/smoke.sh", + "target": "function:.claude/skills/run-concept-curation/smoke.sh:cleanup", + "type": "contains", + "direction": "forward", + "weight": 1.0 + }, + { + "source": "file:.claude/skills/run-concept-curation/smoke.sh", + "target": "function:.claude/skills/run-concept-curation/smoke.sh:check", + "type": "contains", + "direction": "forward", + "weight": 1.0 + }, + { + "source": "document:.claude/skills/run-concept-curation/SKILL.md", + "target": "file:.claude/skills/run-concept-curation/smoke.sh", + "type": "documents", + "direction": "forward", + "weight": 0.5 + }, + { + "source": "document:.claude/agent-memory/cdisc-concept-explorer/MEMORY.md", + "target": "document:.claude/agent-memory/cdisc-concept-explorer/reference_api_endpoints.md", + "type": "related", + "direction": "forward", + "weight": 0.5 + }, + { + "source": "config:migrations/alembic.ini", + "target": "file:migrations/env.py", + "type": "configures", + "direction": "forward", + "weight": 0.6 + }, + { + "source": "file:migrations/env.py", + "target": "function:migrations/env.py:get_engine", + "type": "contains", + "direction": "forward", + "weight": 1.0 + }, + { + "source": "file:migrations/env.py", + "target": "function:migrations/env.py:get_engine_url", + "type": "contains", + "direction": "forward", + "weight": 1.0 + }, + { + "source": "file:migrations/env.py", + "target": "function:migrations/env.py:run_migrations_offline", + "type": "contains", + "direction": "forward", + "weight": 1.0 + }, + { + "source": "file:migrations/env.py", + "target": "function:migrations/env.py:run_migrations_online", + "type": "contains", + "direction": "forward", + "weight": 1.0 + }, + { + "source": "file:migrations/env.py", + "target": "function:migrations/env.py:get_engine", + "type": "exports", + "direction": "forward", + "weight": 0.8 + }, + { + "source": "file:migrations/env.py", + "target": "function:migrations/env.py:get_engine_url", + "type": "exports", + "direction": "forward", + "weight": 0.8 + }, + { + "source": "file:migrations/env.py", + "target": "function:migrations/env.py:run_migrations_offline", + "type": "exports", + "direction": "forward", + "weight": 0.8 + }, + { + "source": "file:migrations/env.py", + "target": "function:migrations/env.py:run_migrations_online", + "type": "exports", + "direction": "forward", + "weight": 0.8 + }, + { + "source": "function:migrations/env.py:run_migrations_offline", + "target": "function:migrations/env.py:get_engine_url", + "type": "calls", + "direction": "forward", + "weight": 0.8 + }, + { + "source": "function:migrations/env.py:run_migrations_online", + "target": "function:migrations/env.py:get_engine", + "type": "calls", + "direction": "forward", + "weight": 0.8 + }, + { + "source": "function:migrations/env.py:get_engine_url", + "target": "function:migrations/env.py:get_engine", + "type": "calls", + "direction": "forward", + "weight": 0.8 + }, + { + "source": "file:migrations/script.py.mako", + "target": "file:migrations/versions/51d4a009d291_baseline_initial_schema.py", + "type": "related", + "direction": "forward", + "weight": 0.5 + }, + { + "source": "file:migrations/versions/51d4a009d291_baseline_initial_schema.py", + "target": "function:migrations/versions/51d4a009d291_baseline_initial_schema.py:upgrade", + "type": "contains", + "direction": "forward", + "weight": 1.0 + }, + { + "source": "file:migrations/versions/51d4a009d291_baseline_initial_schema.py", + "target": "function:migrations/versions/51d4a009d291_baseline_initial_schema.py:downgrade", + "type": "contains", + "direction": "forward", + "weight": 1.0 + }, + { + "source": "file:migrations/versions/51d4a009d291_baseline_initial_schema.py", + "target": "function:migrations/versions/51d4a009d291_baseline_initial_schema.py:upgrade", + "type": "exports", + "direction": "forward", + "weight": 0.8 + }, + { + "source": "file:migrations/versions/51d4a009d291_baseline_initial_schema.py", + "target": "function:migrations/versions/51d4a009d291_baseline_initial_schema.py:downgrade", + "type": "exports", + "direction": "forward", + "weight": 0.8 + }, + { + "source": "file:static/js/main.js", + "target": "function:static/js/main.js:initFileUpload", + "type": "contains", + "direction": "forward", + "weight": 1.0 + }, + { + "source": "file:static/js/main.js", + "target": "function:static/js/main.js:initNcitLookup", + "type": "contains", + "direction": "forward", + "weight": 1.0 + }, + { + "source": "file:static/js/main.js", + "target": "function:static/js/main.js:renderNcitResults", + "type": "contains", + "direction": "forward", + "weight": 1.0 + }, + { + "source": "file:static/js/main.js", + "target": "function:static/js/main.js:renderNcitMetaDisplay", + "type": "contains", + "direction": "forward", + "weight": 1.0 + }, + { + "source": "file:static/js/main.js", + "target": "function:static/js/main.js:initDecTable", + "type": "contains", + "direction": "forward", + "weight": 1.0 + }, + { + "source": "file:static/js/main.js", + "target": "function:static/js/main.js:buildDecRow", + "type": "contains", + "direction": "forward", + "weight": 1.0 + }, + { + "source": "file:static/js/main.js", + "target": "function:static/js/main.js:initKanban", + "type": "contains", + "direction": "forward", + "weight": 1.0 + }, + { + "source": "file:static/js/main.js", + "target": "function:static/js/main.js:initAuditDiff", + "type": "contains", + "direction": "forward", + "weight": 1.0 + }, + { + "source": "file:static/js/main.js", + "target": "function:static/js/main.js:initNcitSearch", + "type": "contains", + "direction": "forward", + "weight": 1.0 + }, + { + "source": "file:static/js/main.js", + "target": "function:static/js/main.js:renderNcitSearchResults", + "type": "contains", + "direction": "forward", + "weight": 1.0 + }, + { + "source": "file:static/js/main.js", + "target": "function:static/js/main.js:showInlineToast", + "type": "contains", + "direction": "forward", + "weight": 1.0 + }, + { + "source": "file:static/js/main.js", + "target": "function:static/js/main.js:getOrCreateToastContainer", + "type": "contains", + "direction": "forward", + "weight": 1.0 + }, + { + "source": "file:static/js/main.js", + "target": "function:static/js/main.js:initLoincLookup", + "type": "contains", + "direction": "forward", + "weight": 1.0 + }, + { + "source": "file:static/js/main.js", + "target": "function:static/js/main.js:renderLoincResults", + "type": "contains", + "direction": "forward", + "weight": 1.0 + }, + { + "source": "file:static/js/main.js", + "target": "function:static/js/main.js:renderLoincMetaDisplay", + "type": "contains", + "direction": "forward", + "weight": 1.0 + }, + { + "source": "file:static/js/main.js", + "target": "function:static/js/main.js:initPendingNcitConcept", + "type": "contains", + "direction": "forward", + "weight": 1.0 + }, + { + "source": "function:static/js/main.js:initNcitLookup", + "target": "function:static/js/main.js:renderNcitResults", + "type": "calls", + "direction": "forward", + "weight": 0.8 + }, + { + "source": "function:static/js/main.js:renderNcitResults", + "target": "function:static/js/main.js:renderNcitMetaDisplay", + "type": "calls", + "direction": "forward", + "weight": 0.8 + }, + { + "source": "function:static/js/main.js:initDecTable", + "target": "function:static/js/main.js:buildDecRow", + "type": "calls", + "direction": "forward", + "weight": 0.8 + }, + { + "source": "function:static/js/main.js:initKanban", + "target": "function:static/js/main.js:showInlineToast", + "type": "calls", + "direction": "forward", + "weight": 0.8 + }, + { + "source": "function:static/js/main.js:initNcitSearch", + "target": "function:static/js/main.js:renderNcitSearchResults", + "type": "calls", + "direction": "forward", + "weight": 0.8 + }, + { + "source": "function:static/js/main.js:showInlineToast", + "target": "function:static/js/main.js:getOrCreateToastContainer", + "type": "calls", + "direction": "forward", + "weight": 0.8 + }, + { + "source": "function:static/js/main.js:initPendingNcitConcept", + "target": "function:static/js/main.js:renderNcitMetaDisplay", + "type": "calls", + "direction": "forward", + "weight": 0.8 + }, + { + "source": "file:static/css/custom.css", + "target": "file:static/js/main.js", + "type": "related", + "direction": "forward", + "weight": 0.5 + }, + { + "source": "document:.claude/skills/code-review/SKILL.md", + "target": "file:mcp_server/__init__.py", + "type": "related", + "direction": "forward", + "weight": 0.5 + } + ] +} diff --git a/.ua/.trash-1785851745/batch-2-part-1.json b/.ua/.trash-1785851745/batch-2-part-1.json new file mode 100644 index 0000000..4fbe0e1 --- /dev/null +++ b/.ua/.trash-1785851745/batch-2-part-1.json @@ -0,0 +1,965 @@ +{ + "nodes": [ + { + "id": "file:models/governance.py", + "type": "file", + "name": "governance.py", + "filePath": "models/governance.py", + "summary": "SQLAlchemy model for GovernanceRecord, tracking Biomedical Concept approval workflow stage transitions.", + "tags": ["data-model", "database", "governance"], + "complexity": "simple" + }, + { + "id": "class:models/governance.py:GovernanceRecord", + "type": "class", + "name": "GovernanceRecord", + "filePath": "models/governance.py", + "lineRange": [6, 16], + "summary": "ORM model recording a single governance stage transition (status, actor, comment, timestamp) for a Biomedical Concept.", + "tags": ["data-model", "governance", "audit"], + "complexity": "simple" + }, + { + "id": "file:routes/bc.py", + "type": "file", + "name": "bc.py", + "filePath": "routes/bc.py", + "summary": "Flask blueprint implementing BC CRUD, NCIt/LOINC metadata lookup, export, and lifecycle actions (submit for review, delete) for Biomedical Concepts.", + "tags": ["api-handler", "flask-blueprint", "crud", "routes"], + "complexity": "complex" + }, + { + "id": "function:routes/bc.py:index", + "type": "function", + "name": "index", + "filePath": "routes/bc.py", + "lineRange": [23, 39], + "summary": "Renders the paginated, filterable list of Biomedical Concepts based on search term and status query params.", + "tags": ["api-handler", "listing", "pagination"], + "complexity": "moderate" + }, + { + "id": "function:routes/bc.py:new_bc", + "type": "function", + "name": "new_bc", + "filePath": "routes/bc.py", + "lineRange": [43, 59], + "summary": "Renders the new-BC creation form, prefilling NCIt fields when arriving from an NCIt search selection.", + "tags": ["api-handler", "form"], + "complexity": "moderate" + }, + { + "id": "function:routes/bc.py:export", + "type": "function", + "name": "export", + "filePath": "routes/bc.py", + "lineRange": [63, 85], + "summary": "Exports all Biomedical Concepts as XLSX, ODM-XML, or JSON depending on the requested format query parameter.", + "tags": ["api-handler", "export"], + "complexity": "moderate" + }, + { + "id": "function:routes/bc.py:library_detail", + "type": "function", + "name": "library_detail", + "filePath": "routes/bc.py", + "lineRange": [89, 114], + "summary": "Displays a published CDISC Library BC's detail, merging in LOINC lookup results for CDASH/QS-style concepts.", + "tags": ["api-handler", "integration"], + "complexity": "moderate" + }, + { + "id": "function:routes/bc.py:detail", + "type": "function", + "name": "detail", + "filePath": "routes/bc.py", + "lineRange": [118, 151], + "summary": "Renders a curated BC's detail page including its Data Element Concepts, and refreshes LOINC lookup data when applicable.", + "tags": ["api-handler", "crud"], + "complexity": "complex" + }, + { + "id": "function:routes/bc.py:fetch_metadata", + "type": "function", + "name": "fetch_metadata", + "filePath": "routes/bc.py", + "lineRange": [155, 182], + "summary": "AJAX endpoint that fetches and caches NCIt and LOINC metadata for a BC and returns it as JSON.", + "tags": ["api-handler", "ajax", "integration"], + "complexity": "moderate" + }, + { + "id": "function:routes/bc.py:create", + "type": "function", + "name": "create", + "filePath": "routes/bc.py", + "lineRange": [186, 194], + "summary": "Handles the new-BC form submission, delegating persistence and DEC creation to bc_service.", + "tags": ["api-handler", "crud"], + "complexity": "simple" + }, + { + "id": "function:routes/bc.py:edit", + "type": "function", + "name": "edit", + "filePath": "routes/bc.py", + "lineRange": [198, 203], + "summary": "Handles the BC edit form submission, updating fields and DECs via bc_service.", + "tags": ["api-handler", "crud"], + "complexity": "simple" + }, + { + "id": "function:routes/bc.py:clear_ncit", + "type": "function", + "name": "clear_ncit", + "filePath": "routes/bc.py", + "lineRange": [207, 217], + "summary": "Clears a BC's NCIt mapping fields and records the change in the audit log.", + "tags": ["api-handler", "audit"], + "complexity": "simple" + }, + { + "id": "function:routes/bc.py:clear_loinc", + "type": "function", + "name": "clear_loinc", + "filePath": "routes/bc.py", + "lineRange": [221, 232], + "summary": "Clears a BC's LOINC mapping fields and records the change in the audit log.", + "tags": ["api-handler", "audit"], + "complexity": "simple" + }, + { + "id": "function:routes/bc.py:submit_for_review", + "type": "function", + "name": "submit_for_review", + "filePath": "routes/bc.py", + "lineRange": [236, 240], + "summary": "Transitions a BC from draft into the governance review queue.", + "tags": ["api-handler", "governance"], + "complexity": "simple" + }, + { + "id": "function:routes/bc.py:delete", + "type": "function", + "name": "delete", + "filePath": "routes/bc.py", + "lineRange": [244, 255], + "summary": "Deletes a BC along with cascading child BCs and governance records, logging the deletion to the audit trail.", + "tags": ["api-handler", "crud", "audit"], + "complexity": "moderate" + }, + { + "id": "function:routes/bc.py:_decs_from_form", + "type": "function", + "name": "_decs_from_form", + "filePath": "routes/bc.py", + "lineRange": [258, 276], + "summary": "Private helper that reconstructs the list of Data Element Concept row dicts from parallel multi-value form fields.", + "tags": ["utility", "form-parsing"], + "complexity": "moderate" + }, + { + "id": "file:routes/governance.py", + "type": "file", + "name": "governance.py", + "filePath": "routes/governance.py", + "summary": "Flask blueprint for the governance Kanban board — viewing BCs by stage, exporting the published set, and advancing/rejecting BCs through the approval workflow.", + "tags": ["api-handler", "governance", "flask-blueprint"], + "complexity": "moderate" + }, + { + "id": "function:routes/governance.py:board", + "type": "function", + "name": "board", + "filePath": "routes/governance.py", + "lineRange": [17, 26], + "summary": "Renders the 4-stage governance Kanban board with BCs grouped by their current status.", + "tags": ["api-handler", "governance"], + "complexity": "simple" + }, + { + "id": "function:routes/governance.py:export", + "type": "function", + "name": "export", + "filePath": "routes/governance.py", + "lineRange": [30, 45], + "summary": "Exports the governance workbook for published BCs, sanitizing the requested filename before responding.", + "tags": ["api-handler", "export", "governance"], + "complexity": "moderate" + }, + { + "id": "function:routes/governance.py:advance", + "type": "function", + "name": "advance", + "filePath": "routes/governance.py", + "lineRange": [49, 58], + "summary": "Advances a BC to the next governance stage and responds with JSON or a redirect depending on the request type.", + "tags": ["api-handler", "governance", "workflow"], + "complexity": "simple" + }, + { + "id": "function:routes/governance.py:reject_bc", + "type": "function", + "name": "reject_bc", + "filePath": "routes/governance.py", + "lineRange": [62, 68], + "summary": "Rejects a BC back through the governance workflow with an optional comment, supporting JSON or redirect responses.", + "tags": ["api-handler", "governance", "workflow"], + "complexity": "simple" + }, + { + "id": "file:routes/ncit.py", + "type": "file", + "name": "ncit.py", + "filePath": "routes/ncit.py", + "summary": "Flask blueprint for NCI Thesaurus search and mapping to Biomedical Concepts, including AJAX search and single-concept detail lookups.", + "tags": ["api-handler", "ncit", "flask-blueprint"], + "complexity": "moderate" + }, + { + "id": "function:routes/ncit.py:index", + "type": "function", + "name": "index", + "filePath": "routes/ncit.py", + "lineRange": [16, 17], + "summary": "Redirects the NCIt blueprint's index route to the mapping view.", + "tags": ["api-handler", "routing"], + "complexity": "simple" + }, + { + "id": "function:routes/ncit.py:mapping", + "type": "function", + "name": "mapping", + "filePath": "routes/ncit.py", + "lineRange": [21, 30], + "summary": "Lists up to 50 Biomedical Concepts that still lack an NCIt code, for manual mapping.", + "tags": ["api-handler", "ncit"], + "complexity": "simple" + }, + { + "id": "function:routes/ncit.py:search_ncit", + "type": "function", + "name": "search_ncit", + "filePath": "routes/ncit.py", + "lineRange": [34, 61], + "summary": "Searches NCIt concepts by term via the EVS API and returns results as JSON or a rendered partial, depending on the caller.", + "tags": ["api-handler", "ncit", "ajax"], + "complexity": "moderate" + }, + { + "id": "function:routes/ncit.py:concept_detail", + "type": "function", + "name": "concept_detail", + "filePath": "routes/ncit.py", + "lineRange": [65, 73], + "summary": "Fetches a single NCIt concept's detail metadata by code and returns it as JSON, aborting with 404 on lookup error.", + "tags": ["api-handler", "ncit"], + "complexity": "simple" + }, + { + "id": "function:routes/ncit.py:resolve", + "type": "function", + "name": "resolve", + "filePath": "routes/ncit.py", + "lineRange": [77, 83], + "summary": "Applies a chosen NCIt code to a Biomedical Concept via bc_service and redirects back to the BC detail page.", + "tags": ["api-handler", "ncit"], + "complexity": "simple" + }, + { + "id": "file:services/__init__.py", + "type": "file", + "name": "__init__.py", + "filePath": "services/__init__.py", + "summary": "Empty package marker enabling the services directory to be imported as a Python package.", + "tags": ["package-init"], + "complexity": "simple" + }, + { + "id": "file:services/audit.py", + "type": "file", + "name": "audit.py", + "filePath": "services/audit.py", + "summary": "Provides the log_change helper used across services and routes to persist immutable AuditLog rows for every entity mutation.", + "tags": ["audit", "utility", "service"], + "complexity": "simple" + }, + { + "id": "function:services/audit.py:log_change", + "type": "function", + "name": "log_change", + "filePath": "services/audit.py", + "lineRange": [16, 32], + "summary": "Creates and persists an AuditLog row capturing entity type/id, action, actor, and before/after JSON snapshots.", + "tags": ["audit", "database", "logging"], + "complexity": "simple" + } + ], + "edges": [ + { + "source": "file:models/governance.py", + "target": "file:extensions.py", + "type": "imports", + "direction": "forward", + "weight": 0.7 + }, + { + "source": "file:routes/bc.py", + "target": "file:extensions.py", + "type": "imports", + "direction": "forward", + "weight": 0.7 + }, + { + "source": "file:routes/bc.py", + "target": "file:models/bc.py", + "type": "imports", + "direction": "forward", + "weight": 0.7 + }, + { + "source": "file:routes/bc.py", + "target": "file:models/governance.py", + "type": "imports", + "direction": "forward", + "weight": 0.7 + }, + { + "source": "file:routes/bc.py", + "target": "file:services/__init__.py", + "type": "imports", + "direction": "forward", + "weight": 0.7 + }, + { + "source": "file:routes/bc.py", + "target": "file:services/audit.py", + "type": "imports", + "direction": "forward", + "weight": 0.7 + }, + { + "source": "file:routes/bc.py", + "target": "file:services/bc_service.py", + "type": "imports", + "direction": "forward", + "weight": 0.7 + }, + { + "source": "file:routes/bc.py", + "target": "file:services/cdisc_api.py", + "type": "imports", + "direction": "forward", + "weight": 0.7 + }, + { + "source": "file:routes/bc.py", + "target": "file:services/export.py", + "type": "imports", + "direction": "forward", + "weight": 0.7 + }, + { + "source": "file:routes/bc.py", + "target": "file:services/loinc_api.py", + "type": "imports", + "direction": "forward", + "weight": 0.7 + }, + { + "source": "file:routes/bc.py", + "target": "file:services/ncit_api.py", + "type": "imports", + "direction": "forward", + "weight": 0.7 + }, + { + "source": "file:routes/governance.py", + "target": "file:extensions.py", + "type": "imports", + "direction": "forward", + "weight": 0.7 + }, + { + "source": "file:routes/governance.py", + "target": "file:models/bc.py", + "type": "imports", + "direction": "forward", + "weight": 0.7 + }, + { + "source": "file:routes/governance.py", + "target": "file:services/__init__.py", + "type": "imports", + "direction": "forward", + "weight": 0.7 + }, + { + "source": "file:routes/governance.py", + "target": "file:services/export.py", + "type": "imports", + "direction": "forward", + "weight": 0.7 + }, + { + "source": "file:routes/governance.py", + "target": "file:services/governance_service.py", + "type": "imports", + "direction": "forward", + "weight": 0.7 + }, + { + "source": "file:routes/ncit.py", + "target": "file:extensions.py", + "type": "imports", + "direction": "forward", + "weight": 0.7 + }, + { + "source": "file:routes/ncit.py", + "target": "file:models/bc.py", + "type": "imports", + "direction": "forward", + "weight": 0.7 + }, + { + "source": "file:routes/ncit.py", + "target": "file:services/__init__.py", + "type": "imports", + "direction": "forward", + "weight": 0.7 + }, + { + "source": "file:routes/ncit.py", + "target": "file:services/bc_service.py", + "type": "imports", + "direction": "forward", + "weight": 0.7 + }, + { + "source": "file:routes/ncit.py", + "target": "file:services/ncit_api.py", + "type": "imports", + "direction": "forward", + "weight": 0.7 + }, + { + "source": "file:services/audit.py", + "target": "file:extensions.py", + "type": "imports", + "direction": "forward", + "weight": 0.7 + }, + { + "source": "file:services/audit.py", + "target": "file:models/audit.py", + "type": "imports", + "direction": "forward", + "weight": 0.7 + }, + + { + "source": "file:models/governance.py", + "target": "class:models/governance.py:GovernanceRecord", + "type": "contains", + "direction": "forward", + "weight": 1.0 + }, + { + "source": "file:routes/bc.py", + "target": "function:routes/bc.py:index", + "type": "contains", + "direction": "forward", + "weight": 1.0 + }, + { + "source": "file:routes/bc.py", + "target": "function:routes/bc.py:new_bc", + "type": "contains", + "direction": "forward", + "weight": 1.0 + }, + { + "source": "file:routes/bc.py", + "target": "function:routes/bc.py:export", + "type": "contains", + "direction": "forward", + "weight": 1.0 + }, + { + "source": "file:routes/bc.py", + "target": "function:routes/bc.py:library_detail", + "type": "contains", + "direction": "forward", + "weight": 1.0 + }, + { + "source": "file:routes/bc.py", + "target": "function:routes/bc.py:detail", + "type": "contains", + "direction": "forward", + "weight": 1.0 + }, + { + "source": "file:routes/bc.py", + "target": "function:routes/bc.py:fetch_metadata", + "type": "contains", + "direction": "forward", + "weight": 1.0 + }, + { + "source": "file:routes/bc.py", + "target": "function:routes/bc.py:create", + "type": "contains", + "direction": "forward", + "weight": 1.0 + }, + { + "source": "file:routes/bc.py", + "target": "function:routes/bc.py:edit", + "type": "contains", + "direction": "forward", + "weight": 1.0 + }, + { + "source": "file:routes/bc.py", + "target": "function:routes/bc.py:clear_ncit", + "type": "contains", + "direction": "forward", + "weight": 1.0 + }, + { + "source": "file:routes/bc.py", + "target": "function:routes/bc.py:clear_loinc", + "type": "contains", + "direction": "forward", + "weight": 1.0 + }, + { + "source": "file:routes/bc.py", + "target": "function:routes/bc.py:submit_for_review", + "type": "contains", + "direction": "forward", + "weight": 1.0 + }, + { + "source": "file:routes/bc.py", + "target": "function:routes/bc.py:delete", + "type": "contains", + "direction": "forward", + "weight": 1.0 + }, + { + "source": "file:routes/bc.py", + "target": "function:routes/bc.py:_decs_from_form", + "type": "contains", + "direction": "forward", + "weight": 1.0 + }, + { + "source": "file:routes/governance.py", + "target": "function:routes/governance.py:board", + "type": "contains", + "direction": "forward", + "weight": 1.0 + }, + { + "source": "file:routes/governance.py", + "target": "function:routes/governance.py:export", + "type": "contains", + "direction": "forward", + "weight": 1.0 + }, + { + "source": "file:routes/governance.py", + "target": "function:routes/governance.py:advance", + "type": "contains", + "direction": "forward", + "weight": 1.0 + }, + { + "source": "file:routes/governance.py", + "target": "function:routes/governance.py:reject_bc", + "type": "contains", + "direction": "forward", + "weight": 1.0 + }, + { + "source": "file:routes/ncit.py", + "target": "function:routes/ncit.py:index", + "type": "contains", + "direction": "forward", + "weight": 1.0 + }, + { + "source": "file:routes/ncit.py", + "target": "function:routes/ncit.py:mapping", + "type": "contains", + "direction": "forward", + "weight": 1.0 + }, + { + "source": "file:routes/ncit.py", + "target": "function:routes/ncit.py:search_ncit", + "type": "contains", + "direction": "forward", + "weight": 1.0 + }, + { + "source": "file:routes/ncit.py", + "target": "function:routes/ncit.py:concept_detail", + "type": "contains", + "direction": "forward", + "weight": 1.0 + }, + { + "source": "file:routes/ncit.py", + "target": "function:routes/ncit.py:resolve", + "type": "contains", + "direction": "forward", + "weight": 1.0 + }, + { + "source": "file:services/audit.py", + "target": "function:services/audit.py:log_change", + "type": "contains", + "direction": "forward", + "weight": 1.0 + }, + + { + "source": "file:models/governance.py", + "target": "class:models/governance.py:GovernanceRecord", + "type": "exports", + "direction": "forward", + "weight": 0.8 + }, + { + "source": "file:routes/bc.py", + "target": "function:routes/bc.py:index", + "type": "exports", + "direction": "forward", + "weight": 0.8 + }, + { + "source": "file:routes/bc.py", + "target": "function:routes/bc.py:new_bc", + "type": "exports", + "direction": "forward", + "weight": 0.8 + }, + { + "source": "file:routes/bc.py", + "target": "function:routes/bc.py:export", + "type": "exports", + "direction": "forward", + "weight": 0.8 + }, + { + "source": "file:routes/bc.py", + "target": "function:routes/bc.py:library_detail", + "type": "exports", + "direction": "forward", + "weight": 0.8 + }, + { + "source": "file:routes/bc.py", + "target": "function:routes/bc.py:detail", + "type": "exports", + "direction": "forward", + "weight": 0.8 + }, + { + "source": "file:routes/bc.py", + "target": "function:routes/bc.py:fetch_metadata", + "type": "exports", + "direction": "forward", + "weight": 0.8 + }, + { + "source": "file:routes/bc.py", + "target": "function:routes/bc.py:create", + "type": "exports", + "direction": "forward", + "weight": 0.8 + }, + { + "source": "file:routes/bc.py", + "target": "function:routes/bc.py:edit", + "type": "exports", + "direction": "forward", + "weight": 0.8 + }, + { + "source": "file:routes/bc.py", + "target": "function:routes/bc.py:clear_ncit", + "type": "exports", + "direction": "forward", + "weight": 0.8 + }, + { + "source": "file:routes/bc.py", + "target": "function:routes/bc.py:clear_loinc", + "type": "exports", + "direction": "forward", + "weight": 0.8 + }, + { + "source": "file:routes/bc.py", + "target": "function:routes/bc.py:submit_for_review", + "type": "exports", + "direction": "forward", + "weight": 0.8 + }, + { + "source": "file:routes/bc.py", + "target": "function:routes/bc.py:delete", + "type": "exports", + "direction": "forward", + "weight": 0.8 + }, + { + "source": "file:routes/bc.py", + "target": "function:routes/bc.py:_decs_from_form", + "type": "exports", + "direction": "forward", + "weight": 0.8 + }, + { + "source": "file:routes/governance.py", + "target": "function:routes/governance.py:board", + "type": "exports", + "direction": "forward", + "weight": 0.8 + }, + { + "source": "file:routes/governance.py", + "target": "function:routes/governance.py:export", + "type": "exports", + "direction": "forward", + "weight": 0.8 + }, + { + "source": "file:routes/governance.py", + "target": "function:routes/governance.py:advance", + "type": "exports", + "direction": "forward", + "weight": 0.8 + }, + { + "source": "file:routes/governance.py", + "target": "function:routes/governance.py:reject_bc", + "type": "exports", + "direction": "forward", + "weight": 0.8 + }, + { + "source": "file:routes/ncit.py", + "target": "function:routes/ncit.py:index", + "type": "exports", + "direction": "forward", + "weight": 0.8 + }, + { + "source": "file:routes/ncit.py", + "target": "function:routes/ncit.py:mapping", + "type": "exports", + "direction": "forward", + "weight": 0.8 + }, + { + "source": "file:routes/ncit.py", + "target": "function:routes/ncit.py:search_ncit", + "type": "exports", + "direction": "forward", + "weight": 0.8 + }, + { + "source": "file:routes/ncit.py", + "target": "function:routes/ncit.py:concept_detail", + "type": "exports", + "direction": "forward", + "weight": 0.8 + }, + { + "source": "file:routes/ncit.py", + "target": "function:routes/ncit.py:resolve", + "type": "exports", + "direction": "forward", + "weight": 0.8 + }, + { + "source": "file:services/audit.py", + "target": "function:services/audit.py:log_change", + "type": "exports", + "direction": "forward", + "weight": 0.8 + }, + + { + "source": "function:routes/bc.py:create", + "target": "function:services/bc_service.py:create_bc", + "type": "calls", + "direction": "forward", + "weight": 0.8 + }, + { + "source": "function:routes/bc.py:create", + "target": "function:services/bc_service.py:save_decs", + "type": "calls", + "direction": "forward", + "weight": 0.8 + }, + { + "source": "function:routes/bc.py:edit", + "target": "function:services/bc_service.py:update_bc", + "type": "calls", + "direction": "forward", + "weight": 0.8 + }, + { + "source": "function:routes/bc.py:edit", + "target": "function:services/bc_service.py:save_decs", + "type": "calls", + "direction": "forward", + "weight": 0.8 + }, + { + "source": "function:routes/bc.py:submit_for_review", + "target": "function:services/bc_service.py:submit_bc_for_review", + "type": "calls", + "direction": "forward", + "weight": 0.8 + }, + { + "source": "function:routes/bc.py:clear_ncit", + "target": "function:services/audit.py:log_change", + "type": "calls", + "direction": "forward", + "weight": 0.8 + }, + { + "source": "function:routes/bc.py:clear_loinc", + "target": "function:services/audit.py:log_change", + "type": "calls", + "direction": "forward", + "weight": 0.8 + }, + { + "source": "function:routes/bc.py:delete", + "target": "function:services/audit.py:log_change", + "type": "calls", + "direction": "forward", + "weight": 0.8 + }, + { + "source": "function:routes/bc.py:library_detail", + "target": "class:services/cdisc_api.py:CDISCApiClient", + "type": "calls", + "direction": "forward", + "weight": 0.8 + }, + { + "source": "function:routes/bc.py:library_detail", + "target": "class:services/loinc_api.py:LoincApiClient", + "type": "calls", + "direction": "forward", + "weight": 0.8 + }, + { + "source": "function:routes/bc.py:detail", + "target": "class:services/loinc_api.py:LoincApiClient", + "type": "calls", + "direction": "forward", + "weight": 0.8 + }, + { + "source": "function:routes/bc.py:fetch_metadata", + "target": "class:services/loinc_api.py:LoincApiClient", + "type": "calls", + "direction": "forward", + "weight": 0.8 + }, + { + "source": "function:routes/bc.py:export", + "target": "function:services/export.py:export_xlsx", + "type": "calls", + "direction": "forward", + "weight": 0.8 + }, + { + "source": "function:routes/bc.py:export", + "target": "function:services/export.py:export_odm_xml", + "type": "calls", + "direction": "forward", + "weight": 0.8 + }, + { + "source": "function:routes/bc.py:export", + "target": "function:services/export.py:export_json", + "type": "calls", + "direction": "forward", + "weight": 0.8 + }, + + { + "source": "function:routes/governance.py:export", + "target": "function:services/export.py:export_governance_xlsx", + "type": "calls", + "direction": "forward", + "weight": 0.8 + }, + { + "source": "function:routes/governance.py:advance", + "target": "function:services/governance_service.py:advance_governance", + "type": "calls", + "direction": "forward", + "weight": 0.8 + }, + { + "source": "function:routes/governance.py:reject_bc", + "target": "function:services/governance_service.py:reject_bc", + "type": "calls", + "direction": "forward", + "weight": 0.8 + }, + + { + "source": "function:routes/ncit.py:search_ncit", + "target": "class:services/ncit_api.py:NCItApiClient", + "type": "calls", + "direction": "forward", + "weight": 0.8 + }, + { + "source": "function:routes/ncit.py:concept_detail", + "target": "class:services/ncit_api.py:NCItApiClient", + "type": "calls", + "direction": "forward", + "weight": 0.8 + }, + { + "source": "function:routes/ncit.py:resolve", + "target": "function:services/bc_service.py:map_ncit_to_bc", + "type": "calls", + "direction": "forward", + "weight": 0.8 + }, + + { + "source": "function:services/audit.py:log_change", + "target": "class:models/audit.py:AuditLog", + "type": "calls", + "direction": "forward", + "weight": 0.8 + }, + + { + "source": "file:routes/ncit.py", + "target": "file:tests/test_ncit.py", + "type": "tested_by", + "direction": "forward", + "weight": 0.5 + } + ] +} diff --git a/.ua/.trash-1785851745/batch-2-part-2.json b/.ua/.trash-1785851745/batch-2-part-2.json new file mode 100644 index 0000000..661a69e --- /dev/null +++ b/.ua/.trash-1785851745/batch-2-part-2.json @@ -0,0 +1,743 @@ +{ + "nodes": [ + { + "id": "file:services/bc_service.py", + "type": "file", + "name": "bc_service.py", + "filePath": "services/bc_service.py", + "summary": "Core business logic for creating, updating, and managing Biomedical Concepts and their Data Element Concepts, including NCIt remapping/merging and review submission.", + "tags": ["service", "business-logic", "crud"], + "complexity": "complex" + }, + { + "id": "function:services/bc_service.py:_get_bc_or_raise", + "type": "function", + "name": "_get_bc_or_raise", + "filePath": "services/bc_service.py", + "lineRange": [28, 32], + "summary": "Looks up a BiomedicalConcept by its bc_id, raising NotFoundError when it does not exist.", + "tags": ["utility", "data-access"], + "complexity": "simple" + }, + { + "id": "function:services/bc_service.py:apply_bc_fields", + "type": "function", + "name": "apply_bc_fields", + "filePath": "services/bc_service.py", + "lineRange": [35, 63], + "summary": "Applies submitted field data onto a BiomedicalConcept instance, handling boolean coercion and LOINC/NCIt/parent-BC field normalization.", + "tags": ["business-logic", "data-model"], + "complexity": "moderate" + }, + { + "id": "function:services/bc_service.py:create_bc", + "type": "function", + "name": "create_bc", + "filePath": "services/bc_service.py", + "lineRange": [66, 85], + "summary": "Creates a new BiomedicalConcept from submitted data, validates uniqueness of the bc_id, persists it, and logs an audit entry.", + "tags": ["service", "crud", "audit"], + "complexity": "moderate" + }, + { + "id": "function:services/bc_service.py:update_bc", + "type": "function", + "name": "update_bc", + "filePath": "services/bc_service.py", + "lineRange": [88, 95], + "summary": "Updates an existing BiomedicalConcept's fields and records the before/after change in the audit log.", + "tags": ["service", "crud", "audit"], + "complexity": "simple" + }, + { + "id": "function:services/bc_service.py:save_decs", + "type": "function", + "name": "save_decs", + "filePath": "services/bc_service.py", + "lineRange": [98, 121], + "summary": "Replaces a BC's Data Element Concept rows, deleting existing DECs and inserting the submitted set with sort order.", + "tags": ["service", "crud"], + "complexity": "moderate" + }, + { + "id": "function:services/bc_service.py:map_ncit_to_bc", + "type": "function", + "name": "map_ncit_to_bc", + "filePath": "services/bc_service.py", + "lineRange": [124, 148], + "summary": "Remaps a BC's NCIt code, and when the target NCIt-derived id already exists, merges the current BC's DECs, specializations, and governance history into it.", + "tags": ["service", "ncit", "merge"], + "complexity": "complex" + }, + { + "id": "function:services/bc_service.py:submit_bc_for_review", + "type": "function", + "name": "submit_bc_for_review", + "filePath": "services/bc_service.py", + "lineRange": [151, 159], + "summary": "Transitions a BC's status to under-review and logs the change to the audit trail.", + "tags": ["service", "governance", "audit"], + "complexity": "simple" + }, + { + "id": "file:services/export.py", + "type": "file", + "name": "export.py", + "filePath": "services/export.py", + "summary": "Exporters that render Biomedical Concept collections to JSON, styled XLSX, governance XLSX, and CDISC ODM-XML formats.", + "tags": ["export", "serialization", "service"], + "complexity": "complex" + }, + { + "id": "function:services/export.py:export_json", + "type": "function", + "name": "export_json", + "filePath": "services/export.py", + "lineRange": [37, 39], + "summary": "Serializes a list of BC dictionaries to a pretty-printed JSON string.", + "tags": ["export", "serialization"], + "complexity": "simple" + }, + { + "id": "function:services/export.py:export_xlsx", + "type": "function", + "name": "export_xlsx", + "filePath": "services/export.py", + "lineRange": [42, 67], + "summary": "Builds a styled openpyxl workbook of BC export fields with a bold header row, falling back to LOINC code when no NCIt code is present.", + "tags": ["export", "xlsx"], + "complexity": "moderate" + }, + { + "id": "function:services/export.py:export_governance_xlsx", + "type": "function", + "name": "export_governance_xlsx", + "filePath": "services/export.py", + "lineRange": [88, 143], + "summary": "Builds a governance-focused workbook containing a BC summary row followed by its Data Element Concept rows for CDISC governance review.", + "tags": ["export", "xlsx", "governance"], + "complexity": "complex" + }, + { + "id": "function:services/export.py:export_odm_xml", + "type": "function", + "name": "export_odm_xml", + "filePath": "services/export.py", + "lineRange": [146, 185], + "summary": "Builds a CDISC ODM-XML document with ItemDefs and NCIt Aliases from a list of Biomedical Concepts.", + "tags": ["export", "xml", "odm"], + "complexity": "complex" + }, + { + "id": "file:services/governance_service.py", + "type": "file", + "name": "governance_service.py", + "filePath": "services/governance_service.py", + "summary": "Implements governance stage transitions (advance/reject) for Biomedical Concepts, creating GovernanceRecord history rows and audit log entries.", + "tags": ["service", "governance", "workflow"], + "complexity": "moderate" + }, + { + "id": "function:services/governance_service.py:advance_governance", + "type": "function", + "name": "advance_governance", + "filePath": "services/governance_service.py", + "lineRange": [16, 40], + "summary": "Moves a BC forward through the STATUS_ORDER stage sequence, recording a GovernanceRecord and audit log entry for the transition.", + "tags": ["service", "governance", "workflow"], + "complexity": "moderate" + }, + { + "id": "function:services/governance_service.py:reject_bc", + "type": "function", + "name": "reject_bc", + "filePath": "services/governance_service.py", + "lineRange": [43, 60], + "summary": "Reverts a BC's governance stage on rejection, recording a GovernanceRecord and audit log entry with the rejection comment.", + "tags": ["service", "governance", "workflow"], + "complexity": "moderate" + }, + { + "id": "file:services/ncit_api.py", + "type": "file", + "name": "ncit_api.py", + "filePath": "services/ncit_api.py", + "summary": "NCI Thesaurus EVS REST API client providing concept search and detailed concept retrieval (parents, semantic type, synonyms, definitions) with caching.", + "tags": ["api-client", "ncit", "integration", "service"], + "complexity": "moderate" + }, + { + "id": "function:services/ncit_api.py:_pick_definition", + "type": "function", + "name": "_pick_definition", + "filePath": "services/ncit_api.py", + "lineRange": [14, 21], + "summary": "Selects the NCI-source or, failing that, first available definition from a concept's list of definitions.", + "tags": ["utility", "data-transform"], + "complexity": "simple" + }, + { + "id": "class:services/ncit_api.py:NCItApiClient", + "type": "class", + "name": "NCItApiClient", + "filePath": "services/ncit_api.py", + "lineRange": [24, 85], + "summary": "HTTP client wrapping the NCI EVS REST API, exposing concept search and cached, extended concept-detail lookups (parents, semantic type, synonyms, definitions) used throughout the NCIt mapping workflow.", + "tags": ["api-client", "integration", "caching"], + "complexity": "moderate" + }, + { + "id": "file:tests/test_export_service.py", + "type": "file", + "name": "test_export_service.py", + "filePath": "tests/test_export_service.py", + "summary": "Unit tests covering JSON, XLSX, governance XLSX, and ODM-XML export functions in services/export.py, verifying headers, blanking rules, and document structure.", + "tags": ["test", "export", "unit-test"], + "complexity": "complex" + }, + { + "id": "class:tests/test_export_service.py:TestExportJson", + "type": "class", + "name": "TestExportJson", + "filePath": "tests/test_export_service.py", + "lineRange": [55, 70], + "summary": "Tests export_json's round-trip serialization, empty-list handling, and coercion of non-JSON-serializable values.", + "tags": ["test", "export"], + "complexity": "simple" + }, + { + "id": "class:tests/test_export_service.py:TestExportXlsx", + "type": "class", + "name": "TestExportXlsx", + "filePath": "tests/test_export_service.py", + "lineRange": [73, 104], + "summary": "Tests export_xlsx's header and row output, empty-list behavior, blank rendering of missing fields, and the LOINC-code fallback in the Code column.", + "tags": ["test", "export", "xlsx"], + "complexity": "moderate" + }, + { + "id": "class:tests/test_export_service.py:TestExportGovernanceXlsx", + "type": "class", + "name": "TestExportGovernanceXlsx", + "filePath": "tests/test_export_service.py", + "lineRange": [107, 180], + "summary": "Tests export_governance_xlsx's BC-row-then-DEC-rows layout and the blanking of system fields for BCs without a LOINC code.", + "tags": ["test", "export", "governance"], + "complexity": "moderate" + }, + { + "id": "class:tests/test_export_service.py:TestExportOdmXml", + "type": "class", + "name": "TestExportOdmXml", + "filePath": "tests/test_export_service.py", + "lineRange": [183, 207], + "summary": "Tests export_odm_xml's document structure, conditional inclusion of the Alias element only when an NCIt code is present, and empty-list handling.", + "tags": ["test", "export", "xml"], + "complexity": "simple" + }, + { + "id": "file:tests/test_ncit.py", + "type": "file", + "name": "test_ncit.py", + "filePath": "tests/test_ncit.py", + "summary": "Unit tests for NCItApiClient.get_concept's extended metadata extraction and the /ncit concept-detail route's JSON responses and error handling.", + "tags": ["test", "ncit", "unit-test"], + "complexity": "moderate" + }, + { + "id": "class:tests/test_ncit.py:TestNcitGetConceptExtended", + "type": "class", + "name": "TestNcitGetConceptExtended", + "filePath": "tests/test_ncit.py", + "lineRange": [43, 112], + "summary": "Tests NCItApiClient.get_concept's extraction of parents, semantic type, source synonyms, and full definitions list, plus empty and error-path handling.", + "tags": ["test", "ncit", "api-client"], + "complexity": "moderate" + }, + { + "id": "class:tests/test_ncit.py:TestNcitConceptRoute", + "type": "class", + "name": "TestNcitConceptRoute", + "filePath": "tests/test_ncit.py", + "lineRange": [131, 156], + "summary": "Tests the /ncit concept-detail route's JSON response shape, correct pass-through of the requested code, and 404 response when the service errors.", + "tags": ["test", "ncit", "api-handler"], + "complexity": "simple" + } + ], + "edges": [ + { + "source": "file:services/bc_service.py", + "target": "file:extensions.py", + "type": "imports", + "direction": "forward", + "weight": 0.7 + }, + { + "source": "file:services/bc_service.py", + "target": "file:models/bc.py", + "type": "imports", + "direction": "forward", + "weight": 0.7 + }, + { + "source": "file:services/bc_service.py", + "target": "file:models/governance.py", + "type": "imports", + "direction": "forward", + "weight": 0.7 + }, + { + "source": "file:services/bc_service.py", + "target": "file:models/specialization.py", + "type": "imports", + "direction": "forward", + "weight": 0.7 + }, + { + "source": "file:services/bc_service.py", + "target": "file:services/audit.py", + "type": "imports", + "direction": "forward", + "weight": 0.7 + }, + { + "source": "file:services/governance_service.py", + "target": "file:extensions.py", + "type": "imports", + "direction": "forward", + "weight": 0.7 + }, + { + "source": "file:services/governance_service.py", + "target": "file:models/governance.py", + "type": "imports", + "direction": "forward", + "weight": 0.7 + }, + { + "source": "file:services/governance_service.py", + "target": "file:services/audit.py", + "type": "imports", + "direction": "forward", + "weight": 0.7 + }, + { + "source": "file:services/governance_service.py", + "target": "file:services/bc_service.py", + "type": "imports", + "direction": "forward", + "weight": 0.7 + }, + { + "source": "file:services/ncit_api.py", + "target": "file:services/api_cache.py", + "type": "imports", + "direction": "forward", + "weight": 0.7 + }, + { + "source": "file:tests/test_export_service.py", + "target": "file:extensions.py", + "type": "imports", + "direction": "forward", + "weight": 0.7 + }, + { + "source": "file:tests/test_export_service.py", + "target": "file:models/bc.py", + "type": "imports", + "direction": "forward", + "weight": 0.7 + }, + { + "source": "file:tests/test_export_service.py", + "target": "file:services/export.py", + "type": "imports", + "direction": "forward", + "weight": 0.7 + }, + { + "source": "file:tests/test_ncit.py", + "target": "file:services/ncit_api.py", + "type": "imports", + "direction": "forward", + "weight": 0.7 + }, + + { + "source": "file:services/bc_service.py", + "target": "function:services/bc_service.py:_get_bc_or_raise", + "type": "contains", + "direction": "forward", + "weight": 1.0 + }, + { + "source": "file:services/bc_service.py", + "target": "function:services/bc_service.py:apply_bc_fields", + "type": "contains", + "direction": "forward", + "weight": 1.0 + }, + { + "source": "file:services/bc_service.py", + "target": "function:services/bc_service.py:create_bc", + "type": "contains", + "direction": "forward", + "weight": 1.0 + }, + { + "source": "file:services/bc_service.py", + "target": "function:services/bc_service.py:update_bc", + "type": "contains", + "direction": "forward", + "weight": 1.0 + }, + { + "source": "file:services/bc_service.py", + "target": "function:services/bc_service.py:save_decs", + "type": "contains", + "direction": "forward", + "weight": 1.0 + }, + { + "source": "file:services/bc_service.py", + "target": "function:services/bc_service.py:map_ncit_to_bc", + "type": "contains", + "direction": "forward", + "weight": 1.0 + }, + { + "source": "file:services/bc_service.py", + "target": "function:services/bc_service.py:submit_bc_for_review", + "type": "contains", + "direction": "forward", + "weight": 1.0 + }, + { + "source": "file:services/export.py", + "target": "function:services/export.py:export_json", + "type": "contains", + "direction": "forward", + "weight": 1.0 + }, + { + "source": "file:services/export.py", + "target": "function:services/export.py:export_xlsx", + "type": "contains", + "direction": "forward", + "weight": 1.0 + }, + { + "source": "file:services/export.py", + "target": "function:services/export.py:export_governance_xlsx", + "type": "contains", + "direction": "forward", + "weight": 1.0 + }, + { + "source": "file:services/export.py", + "target": "function:services/export.py:export_odm_xml", + "type": "contains", + "direction": "forward", + "weight": 1.0 + }, + { + "source": "file:services/governance_service.py", + "target": "function:services/governance_service.py:advance_governance", + "type": "contains", + "direction": "forward", + "weight": 1.0 + }, + { + "source": "file:services/governance_service.py", + "target": "function:services/governance_service.py:reject_bc", + "type": "contains", + "direction": "forward", + "weight": 1.0 + }, + { + "source": "file:services/ncit_api.py", + "target": "function:services/ncit_api.py:_pick_definition", + "type": "contains", + "direction": "forward", + "weight": 1.0 + }, + { + "source": "file:services/ncit_api.py", + "target": "class:services/ncit_api.py:NCItApiClient", + "type": "contains", + "direction": "forward", + "weight": 1.0 + }, + { + "source": "file:tests/test_export_service.py", + "target": "class:tests/test_export_service.py:TestExportJson", + "type": "contains", + "direction": "forward", + "weight": 1.0 + }, + { + "source": "file:tests/test_export_service.py", + "target": "class:tests/test_export_service.py:TestExportXlsx", + "type": "contains", + "direction": "forward", + "weight": 1.0 + }, + { + "source": "file:tests/test_export_service.py", + "target": "class:tests/test_export_service.py:TestExportGovernanceXlsx", + "type": "contains", + "direction": "forward", + "weight": 1.0 + }, + { + "source": "file:tests/test_export_service.py", + "target": "class:tests/test_export_service.py:TestExportOdmXml", + "type": "contains", + "direction": "forward", + "weight": 1.0 + }, + { + "source": "file:tests/test_ncit.py", + "target": "class:tests/test_ncit.py:TestNcitGetConceptExtended", + "type": "contains", + "direction": "forward", + "weight": 1.0 + }, + { + "source": "file:tests/test_ncit.py", + "target": "class:tests/test_ncit.py:TestNcitConceptRoute", + "type": "contains", + "direction": "forward", + "weight": 1.0 + }, + + { + "source": "file:services/bc_service.py", + "target": "function:services/bc_service.py:_get_bc_or_raise", + "type": "exports", + "direction": "forward", + "weight": 0.8 + }, + { + "source": "file:services/bc_service.py", + "target": "function:services/bc_service.py:apply_bc_fields", + "type": "exports", + "direction": "forward", + "weight": 0.8 + }, + { + "source": "file:services/bc_service.py", + "target": "function:services/bc_service.py:create_bc", + "type": "exports", + "direction": "forward", + "weight": 0.8 + }, + { + "source": "file:services/bc_service.py", + "target": "function:services/bc_service.py:update_bc", + "type": "exports", + "direction": "forward", + "weight": 0.8 + }, + { + "source": "file:services/bc_service.py", + "target": "function:services/bc_service.py:save_decs", + "type": "exports", + "direction": "forward", + "weight": 0.8 + }, + { + "source": "file:services/bc_service.py", + "target": "function:services/bc_service.py:map_ncit_to_bc", + "type": "exports", + "direction": "forward", + "weight": 0.8 + }, + { + "source": "file:services/bc_service.py", + "target": "function:services/bc_service.py:submit_bc_for_review", + "type": "exports", + "direction": "forward", + "weight": 0.8 + }, + { + "source": "file:services/export.py", + "target": "function:services/export.py:export_json", + "type": "exports", + "direction": "forward", + "weight": 0.8 + }, + { + "source": "file:services/export.py", + "target": "function:services/export.py:export_xlsx", + "type": "exports", + "direction": "forward", + "weight": 0.8 + }, + { + "source": "file:services/export.py", + "target": "function:services/export.py:export_governance_xlsx", + "type": "exports", + "direction": "forward", + "weight": 0.8 + }, + { + "source": "file:services/export.py", + "target": "function:services/export.py:export_odm_xml", + "type": "exports", + "direction": "forward", + "weight": 0.8 + }, + { + "source": "file:services/governance_service.py", + "target": "function:services/governance_service.py:advance_governance", + "type": "exports", + "direction": "forward", + "weight": 0.8 + }, + { + "source": "file:services/governance_service.py", + "target": "function:services/governance_service.py:reject_bc", + "type": "exports", + "direction": "forward", + "weight": 0.8 + }, + { + "source": "file:services/ncit_api.py", + "target": "function:services/ncit_api.py:_pick_definition", + "type": "exports", + "direction": "forward", + "weight": 0.8 + }, + { + "source": "file:services/ncit_api.py", + "target": "class:services/ncit_api.py:NCItApiClient", + "type": "exports", + "direction": "forward", + "weight": 0.8 + }, + { + "source": "file:tests/test_export_service.py", + "target": "class:tests/test_export_service.py:TestExportJson", + "type": "exports", + "direction": "forward", + "weight": 0.8 + }, + { + "source": "file:tests/test_export_service.py", + "target": "class:tests/test_export_service.py:TestExportXlsx", + "type": "exports", + "direction": "forward", + "weight": 0.8 + }, + { + "source": "file:tests/test_export_service.py", + "target": "class:tests/test_export_service.py:TestExportGovernanceXlsx", + "type": "exports", + "direction": "forward", + "weight": 0.8 + }, + { + "source": "file:tests/test_export_service.py", + "target": "class:tests/test_export_service.py:TestExportOdmXml", + "type": "exports", + "direction": "forward", + "weight": 0.8 + }, + { + "source": "file:tests/test_ncit.py", + "target": "class:tests/test_ncit.py:TestNcitGetConceptExtended", + "type": "exports", + "direction": "forward", + "weight": 0.8 + }, + { + "source": "file:tests/test_ncit.py", + "target": "class:tests/test_ncit.py:TestNcitConceptRoute", + "type": "exports", + "direction": "forward", + "weight": 0.8 + }, + + { + "source": "function:services/bc_service.py:create_bc", + "target": "function:services/audit.py:log_change", + "type": "calls", + "direction": "forward", + "weight": 0.8 + }, + { + "source": "function:services/bc_service.py:update_bc", + "target": "function:services/audit.py:log_change", + "type": "calls", + "direction": "forward", + "weight": 0.8 + }, + { + "source": "function:services/bc_service.py:map_ncit_to_bc", + "target": "function:services/audit.py:log_change", + "type": "calls", + "direction": "forward", + "weight": 0.8 + }, + { + "source": "function:services/bc_service.py:submit_bc_for_review", + "target": "function:services/audit.py:log_change", + "type": "calls", + "direction": "forward", + "weight": 0.8 + }, + { + "source": "function:services/governance_service.py:advance_governance", + "target": "function:services/audit.py:log_change", + "type": "calls", + "direction": "forward", + "weight": 0.8 + }, + { + "source": "function:services/governance_service.py:advance_governance", + "target": "class:models/governance.py:GovernanceRecord", + "type": "calls", + "direction": "forward", + "weight": 0.8 + }, + { + "source": "function:services/governance_service.py:reject_bc", + "target": "function:services/audit.py:log_change", + "type": "calls", + "direction": "forward", + "weight": 0.8 + }, + { + "source": "function:services/governance_service.py:reject_bc", + "target": "class:models/governance.py:GovernanceRecord", + "type": "calls", + "direction": "forward", + "weight": 0.8 + }, + { + "source": "class:services/ncit_api.py:NCItApiClient", + "target": "function:services/api_cache.py:cached", + "type": "calls", + "direction": "forward", + "weight": 0.8 + }, + + { + "source": "file:services/export.py", + "target": "file:tests/test_export_service.py", + "type": "tested_by", + "direction": "forward", + "weight": 0.5 + }, + { + "source": "file:services/ncit_api.py", + "target": "file:tests/test_ncit.py", + "type": "tested_by", + "direction": "forward", + "weight": 0.5 + } + ] +} diff --git a/.ua/.trash-1785851745/batch-3.json b/.ua/.trash-1785851745/batch-3.json new file mode 100644 index 0000000..f3ba6fa --- /dev/null +++ b/.ua/.trash-1785851745/batch-3.json @@ -0,0 +1,674 @@ +{ + "nodes": [ + { + "id": "file:routes/dashboard.py", + "type": "file", + "name": "dashboard.py", + "filePath": "routes/dashboard.py", + "summary": "Flask blueprint for the dashboard home page; fetches CDISC Library BC and specialization counts concurrently via a thread pool and combines them with local KPI stats (BC status counts, recent activity, audit log) for the landing page.", + "tags": ["dashboard", "api-handler", "kpi", "route"], + "complexity": "moderate" + }, + { + "id": "file:routes/loinc.py", + "type": "file", + "name": "loinc.py", + "filePath": "routes/loinc.py", + "summary": "Flask blueprint exposing a single AJAX search endpoint that proxies term queries to the NLM LOINC API client and returns JSON results.", + "tags": ["api-handler", "loinc", "search", "route"], + "complexity": "simple" + }, + { + "id": "file:routes/specializations.py", + "type": "file", + "name": "specializations.py", + "filePath": "routes/specializations.py", + "summary": "Flask blueprint managing CDISC dataset specializations: lists local and CDISC Library specializations, and supports manual creation, deletion, and auto-generation of variable-level specializations from a BC's data element concepts.", + "tags": ["api-handler", "crud", "specialization", "route"], + "complexity": "complex" + }, + { + "id": "file:services/api_cache.py", + "type": "file", + "name": "api_cache.py", + "filePath": "services/api_cache.py", + "summary": "Shared stale-tolerant in-memory cache helper used by the CDISC Library, NCI EVS, and LOINC API clients; serves fresh data for 5 minutes and falls back to stale data for up to an hour if a refresh fails, so a slow upstream never blanks a page that rendered fine a minute ago.", + "tags": ["utility", "caching", "resilience", "service"], + "complexity": "simple", + "languageNotes": "Stale-while-revalidate cache pattern shared across multiple API client modules via a single module-level dict." + }, + { + "id": "file:services/cdisc_api.py", + "type": "file", + "name": "cdisc_api.py", + "filePath": "services/cdisc_api.py", + "summary": "REST client for the CDISC Library COSMOS API; wraps biomedical concept, specialization, and duplicate-check lookups with a 5-minute in-memory cache and provides a publish_bc method for pushing approved BCs to the Library.", + "tags": ["api-client", "service", "cdisc-library", "http"], + "complexity": "moderate" + }, + { + "id": "file:services/loinc_api.py", + "type": "file", + "name": "loinc_api.py", + "filePath": "services/loinc_api.py", + "summary": "Client for the NLM Clinical Tables LOINC search API; builds the query with a configurable set of extra fields, optionally applies HTTP Basic Auth, and normalizes results through the shared API cache.", + "tags": ["api-client", "service", "loinc", "http"], + "complexity": "moderate" + }, + { + "id": "file:tests/test_cdisc_api_cache.py", + "type": "file", + "name": "test_cdisc_api_cache.py", + "filePath": "tests/test_cdisc_api_cache.py", + "summary": "Unit tests for the shared cache helper and CDISCApiClient cache-key behavior, covering fresh/stale/expired cache states, refresh-failure fallback, and that raw API keys never leak into cache keys.", + "tags": ["test", "caching", "cdisc-library"], + "complexity": "moderate" + }, + { + "id": "file:tests/test_loinc.py", + "type": "file", + "name": "test_loinc.py", + "filePath": "tests/test_loinc.py", + "summary": "Unit tests for LoincApiClient.search normalization, extra-field handling, Basic Auth toggling, and error handling, plus tests for the /loinc/search Flask route's JSON responses.", + "tags": ["test", "loinc", "route"], + "complexity": "complex" + }, + { + "id": "function:routes/dashboard.py:index", + "type": "function", + "name": "index", + "filePath": "routes/dashboard.py", + "lineRange": [17, 60], + "summary": "Renders the dashboard home page, fetching CDISC Library BC and specialization counts in parallel via a ThreadPoolExecutor and combining them with local BC status counts, recent-activity queries, and audit log entries.", + "tags": ["route", "dashboard", "concurrency"], + "complexity": "moderate" + }, + { + "id": "function:routes/loinc.py:search", + "type": "function", + "name": "search", + "filePath": "routes/loinc.py", + "lineRange": [13, 23], + "summary": "Handles GET /loinc/search: reads the search term, detects AJAX/JSON requests, and returns LOINC search results as JSON via LoincApiClient.", + "tags": ["route", "api-handler", "loinc"], + "complexity": "simple" + }, + { + "id": "function:routes/specializations.py:_get_bc_options", + "type": "function", + "name": "_get_bc_options", + "filePath": "routes/specializations.py", + "lineRange": [16, 25], + "summary": "Builds the dropdown option list of approved Biomedical Concepts for specialization forms, merging CDISC Library BC links with locally curated BiomedicalConcept records.", + "tags": ["helper", "dropdown", "bc"], + "complexity": "simple" + }, + { + "id": "function:routes/specializations.py:index", + "type": "function", + "name": "index", + "filePath": "routes/specializations.py", + "lineRange": [29, 38], + "summary": "Lists all locally stored dataset specializations alongside available BC options for the specializations index page.", + "tags": ["route", "list"], + "complexity": "simple" + }, + { + "id": "function:routes/specializations.py:library_detail", + "type": "function", + "name": "library_detail", + "filePath": "routes/specializations.py", + "lineRange": [42, 52], + "summary": "Fetches and renders a single dataset specialization directly from the CDISC Library by its specialization path, redirecting with a flash message if the lookup fails.", + "tags": ["route", "cdisc-library"], + "complexity": "simple" + }, + { + "id": "function:routes/specializations.py:detail", + "type": "function", + "name": "detail", + "filePath": "routes/specializations.py", + "lineRange": [56, 67], + "summary": "Renders the detail page for a locally stored dataset specialization identified by its VLM group ID.", + "tags": ["route", "detail"], + "complexity": "simple" + }, + { + "id": "function:routes/specializations.py:_variables_from_form", + "type": "function", + "name": "_variables_from_form", + "filePath": "routes/specializations.py", + "lineRange": [70, 87], + "summary": "Parses indexed variables[i][name]/variables[i][label] form fields into a list of variable dicts for specialization create/generate forms.", + "tags": ["helper", "form-parsing"], + "complexity": "simple" + }, + { + "id": "function:routes/specializations.py:create", + "type": "function", + "name": "create", + "filePath": "routes/specializations.py", + "lineRange": [91, 124], + "summary": "Handles manual creation of a dataset specialization from form input, either updating an existing record or inserting a new DatasetSpecialization, and logs the change via the audit service.", + "tags": ["route", "crud", "audit"], + "complexity": "moderate" + }, + { + "id": "function:routes/specializations.py:delete", + "type": "function", + "name": "delete", + "filePath": "routes/specializations.py", + "lineRange": [128, 134], + "summary": "Deletes a dataset specialization by VLM group ID and records the deletion in the audit log.", + "tags": ["route", "crud", "audit"], + "complexity": "simple" + }, + { + "id": "function:routes/specializations.py:generate_from_dec", + "type": "function", + "name": "generate_from_dec", + "filePath": "routes/specializations.py", + "lineRange": [138, 146], + "summary": "AJAX endpoint that checks whether a given BC has any Data Element Concepts available to auto-generate a specialization from.", + "tags": ["route", "ajax", "validation"], + "complexity": "simple" + }, + { + "id": "function:routes/specializations.py:generate", + "type": "function", + "name": "generate", + "filePath": "routes/specializations.py", + "lineRange": [150, 172], + "summary": "Auto-generates a dataset specialization's variables from a BC's Data Element Concepts and saves it as a new DatasetSpecialization, logging the change via the audit service.", + "tags": ["route", "generation", "audit"], + "complexity": "moderate" + }, + { + "id": "function:services/api_cache.py:cached", + "type": "function", + "name": "cached", + "filePath": "services/api_cache.py", + "lineRange": [22, 42], + "summary": "Core stale-while-revalidate cache primitive: returns a fresh cached value if within CACHE_TTL, otherwise attempts to refetch via the given callable, falling back to a stale value (up to CACHE_STALE_TTL) if the refresh raises.", + "tags": ["caching", "resilience", "utility"], + "complexity": "moderate", + "languageNotes": "The broad except Exception is intentional — it's the resilience seam preventing upstream failures from blanking a page that previously rendered fine." + }, + { + "id": "function:services/cdisc_api.py:_config_value", + "type": "function", + "name": "_config_value", + "filePath": "services/cdisc_api.py", + "lineRange": [19, 26], + "summary": "Resolves a configuration value by checking Flask's current_app.config first, then falling back to an environment variable, so config can be read even outside an app context.", + "tags": ["helper", "config"], + "complexity": "simple" + }, + { + "id": "class:services/cdisc_api.py:CDISCApiClient", + "type": "class", + "name": "CDISCApiClient", + "filePath": "services/cdisc_api.py", + "lineRange": [29, 127], + "summary": "REST client wrapping the CDISC Library COSMOS v2 API: fetches biomedical concepts, individual BC/specialization detail, and dataset specializations (cached via the shared api_cache helper), checks for duplicate BC titles, and publishes approved BCs back to the Library.", + "tags": ["api-client", "cdisc-library", "service", "http"], + "complexity": "complex", + "languageNotes": "Cache keys are derived from a SHA-256 hash of the API key material rather than the raw key, so secrets never end up as cache dictionary keys in memory dumps or logs." + }, + { + "id": "class:services/loinc_api.py:LoincApiClient", + "type": "class", + "name": "LoincApiClient", + "filePath": "services/loinc_api.py", + "lineRange": [18, 62], + "summary": "Client for the NLM Clinical Tables LOINC /search endpoint; optionally attaches HTTP Basic Auth from LOINC_USER/LOINC_PASSWORD env vars, requests a configurable set of extra fields, normalizes the paginated response into a flat list, and caches results through the shared api_cache helper.", + "tags": ["api-client", "loinc", "service", "http"], + "complexity": "moderate" + }, + { + "id": "class:tests/test_cdisc_api_cache.py:TestCachedHelper", + "type": "class", + "name": "TestCachedHelper", + "filePath": "tests/test_cdisc_api_cache.py", + "lineRange": [11, 64], + "summary": "Tests the cached() helper directly: first-call fetch-and-store, serving fresh entries without refetching, refreshing stale entries, falling back to stale data when refresh fails, and raising when there is no fallback available.", + "tags": ["test", "caching"], + "complexity": "moderate" + }, + { + "id": "class:tests/test_cdisc_api_cache.py:TestClientCacheKeys", + "type": "class", + "name": "TestClientCacheKeys", + "filePath": "tests/test_cdisc_api_cache.py", + "lineRange": [67, 120], + "summary": "Tests that CDISCApiClient's cache keys are derived from a hash rather than the raw API key, that errors from get_biomedical_concepts are encoded into the cached result rather than raised, and that a stale error result is replaced once the cache TTL expires.", + "tags": ["test", "caching", "security"], + "complexity": "moderate" + }, + { + "id": "class:tests/test_loinc.py:TestLoincApiClientSearch", + "type": "class", + "name": "TestLoincApiClientSearch", + "filePath": "tests/test_loinc.py", + "lineRange": [44, 168], + "summary": "Tests LoincApiClient.search's response normalization, presence of all requested extra fields, Basic Auth header toggling based on environment variables, and graceful handling of empty results, missing codes arrays, and network errors.", + "tags": ["test", "loinc", "api-client"], + "complexity": "complex" + }, + { + "id": "class:tests/test_loinc.py:TestLoincSearchRoute", + "type": "class", + "name": "TestLoincSearchRoute", + "filePath": "tests/test_loinc.py", + "lineRange": [176, 205], + "summary": "Tests the /loinc/search Flask route: JSON response for AJAX requests, empty-term short-circuiting, that the search term is passed through to the client, and that the format=json query param triggers a JSON response.", + "tags": ["test", "route", "loinc"], + "complexity": "simple" + } + ], + "edges": [ + { + "source": "file:routes/dashboard.py", + "target": "file:models/audit.py", + "type": "imports", + "direction": "forward", + "weight": 0.7 + }, + { + "source": "file:routes/dashboard.py", + "target": "file:models/bc.py", + "type": "imports", + "direction": "forward", + "weight": 0.7 + }, + { + "source": "file:routes/dashboard.py", + "target": "file:services/cdisc_api.py", + "type": "imports", + "direction": "forward", + "weight": 0.7 + }, + { + "source": "file:routes/loinc.py", + "target": "file:services/loinc_api.py", + "type": "imports", + "direction": "forward", + "weight": 0.7 + }, + { + "source": "file:routes/specializations.py", + "target": "file:extensions.py", + "type": "imports", + "direction": "forward", + "weight": 0.7 + }, + { + "source": "file:routes/specializations.py", + "target": "file:models/bc.py", + "type": "imports", + "direction": "forward", + "weight": 0.7 + }, + { + "source": "file:routes/specializations.py", + "target": "file:models/specialization.py", + "type": "imports", + "direction": "forward", + "weight": 0.7 + }, + { + "source": "file:routes/specializations.py", + "target": "file:services/audit.py", + "type": "imports", + "direction": "forward", + "weight": 0.7 + }, + { + "source": "file:routes/specializations.py", + "target": "file:services/cdisc_api.py", + "type": "imports", + "direction": "forward", + "weight": 0.7 + }, + { + "source": "file:services/cdisc_api.py", + "target": "file:services/api_cache.py", + "type": "imports", + "direction": "forward", + "weight": 0.7 + }, + { + "source": "file:services/loinc_api.py", + "target": "file:services/api_cache.py", + "type": "imports", + "direction": "forward", + "weight": 0.7 + }, + { + "source": "file:tests/test_cdisc_api_cache.py", + "target": "file:services/cdisc_api.py", + "type": "imports", + "direction": "forward", + "weight": 0.7 + }, + { + "source": "file:tests/test_loinc.py", + "target": "file:services/loinc_api.py", + "type": "imports", + "direction": "forward", + "weight": 0.7 + }, + + { + "source": "file:routes/dashboard.py", + "target": "function:routes/dashboard.py:index", + "type": "contains", + "direction": "forward", + "weight": 1.0 + }, + { + "source": "file:routes/loinc.py", + "target": "function:routes/loinc.py:search", + "type": "contains", + "direction": "forward", + "weight": 1.0 + }, + { + "source": "file:routes/specializations.py", + "target": "function:routes/specializations.py:_get_bc_options", + "type": "contains", + "direction": "forward", + "weight": 1.0 + }, + { + "source": "file:routes/specializations.py", + "target": "function:routes/specializations.py:index", + "type": "contains", + "direction": "forward", + "weight": 1.0 + }, + { + "source": "file:routes/specializations.py", + "target": "function:routes/specializations.py:library_detail", + "type": "contains", + "direction": "forward", + "weight": 1.0 + }, + { + "source": "file:routes/specializations.py", + "target": "function:routes/specializations.py:detail", + "type": "contains", + "direction": "forward", + "weight": 1.0 + }, + { + "source": "file:routes/specializations.py", + "target": "function:routes/specializations.py:_variables_from_form", + "type": "contains", + "direction": "forward", + "weight": 1.0 + }, + { + "source": "file:routes/specializations.py", + "target": "function:routes/specializations.py:create", + "type": "contains", + "direction": "forward", + "weight": 1.0 + }, + { + "source": "file:routes/specializations.py", + "target": "function:routes/specializations.py:delete", + "type": "contains", + "direction": "forward", + "weight": 1.0 + }, + { + "source": "file:routes/specializations.py", + "target": "function:routes/specializations.py:generate_from_dec", + "type": "contains", + "direction": "forward", + "weight": 1.0 + }, + { + "source": "file:routes/specializations.py", + "target": "function:routes/specializations.py:generate", + "type": "contains", + "direction": "forward", + "weight": 1.0 + }, + { + "source": "file:services/api_cache.py", + "target": "function:services/api_cache.py:cached", + "type": "contains", + "direction": "forward", + "weight": 1.0 + }, + { + "source": "file:services/cdisc_api.py", + "target": "function:services/cdisc_api.py:_config_value", + "type": "contains", + "direction": "forward", + "weight": 1.0 + }, + { + "source": "file:services/cdisc_api.py", + "target": "class:services/cdisc_api.py:CDISCApiClient", + "type": "contains", + "direction": "forward", + "weight": 1.0 + }, + { + "source": "file:services/loinc_api.py", + "target": "class:services/loinc_api.py:LoincApiClient", + "type": "contains", + "direction": "forward", + "weight": 1.0 + }, + { + "source": "file:tests/test_cdisc_api_cache.py", + "target": "class:tests/test_cdisc_api_cache.py:TestCachedHelper", + "type": "contains", + "direction": "forward", + "weight": 1.0 + }, + { + "source": "file:tests/test_cdisc_api_cache.py", + "target": "class:tests/test_cdisc_api_cache.py:TestClientCacheKeys", + "type": "contains", + "direction": "forward", + "weight": 1.0 + }, + { + "source": "file:tests/test_loinc.py", + "target": "class:tests/test_loinc.py:TestLoincApiClientSearch", + "type": "contains", + "direction": "forward", + "weight": 1.0 + }, + { + "source": "file:tests/test_loinc.py", + "target": "class:tests/test_loinc.py:TestLoincSearchRoute", + "type": "contains", + "direction": "forward", + "weight": 1.0 + }, + + { + "source": "file:services/cdisc_api.py", + "target": "class:services/cdisc_api.py:CDISCApiClient", + "type": "exports", + "direction": "forward", + "weight": 0.8 + }, + { + "source": "file:services/loinc_api.py", + "target": "class:services/loinc_api.py:LoincApiClient", + "type": "exports", + "direction": "forward", + "weight": 0.8 + }, + { + "source": "file:services/api_cache.py", + "target": "function:services/api_cache.py:cached", + "type": "exports", + "direction": "forward", + "weight": 0.8 + }, + + { + "source": "function:routes/dashboard.py:index", + "target": "class:services/cdisc_api.py:CDISCApiClient", + "type": "calls", + "direction": "forward", + "weight": 0.8 + }, + { + "source": "function:routes/dashboard.py:index", + "target": "class:models/bc.py:BiomedicalConcept", + "type": "calls", + "direction": "forward", + "weight": 0.8 + }, + { + "source": "function:routes/dashboard.py:index", + "target": "class:models/audit.py:AuditLog", + "type": "calls", + "direction": "forward", + "weight": 0.8 + }, + { + "source": "function:routes/loinc.py:search", + "target": "class:services/loinc_api.py:LoincApiClient", + "type": "calls", + "direction": "forward", + "weight": 0.8 + }, + { + "source": "function:routes/specializations.py:_get_bc_options", + "target": "class:services/cdisc_api.py:CDISCApiClient", + "type": "calls", + "direction": "forward", + "weight": 0.8 + }, + { + "source": "function:routes/specializations.py:_get_bc_options", + "target": "class:models/bc.py:BiomedicalConcept", + "type": "calls", + "direction": "forward", + "weight": 0.8 + }, + { + "source": "function:routes/specializations.py:index", + "target": "class:models/specialization.py:DatasetSpecialization", + "type": "calls", + "direction": "forward", + "weight": 0.8 + }, + { + "source": "function:routes/specializations.py:library_detail", + "target": "class:services/cdisc_api.py:CDISCApiClient", + "type": "calls", + "direction": "forward", + "weight": 0.8 + }, + { + "source": "function:routes/specializations.py:detail", + "target": "class:models/specialization.py:DatasetSpecialization", + "type": "calls", + "direction": "forward", + "weight": 0.8 + }, + { + "source": "function:routes/specializations.py:create", + "target": "function:services/audit.py:log_change", + "type": "calls", + "direction": "forward", + "weight": 0.8 + }, + { + "source": "function:routes/specializations.py:create", + "target": "class:models/specialization.py:DatasetSpecialization", + "type": "calls", + "direction": "forward", + "weight": 0.8 + }, + { + "source": "function:routes/specializations.py:delete", + "target": "function:services/audit.py:log_change", + "type": "calls", + "direction": "forward", + "weight": 0.8 + }, + { + "source": "function:routes/specializations.py:generate_from_dec", + "target": "class:models/bc.py:DataElementConcept", + "type": "calls", + "direction": "forward", + "weight": 0.8 + }, + { + "source": "function:routes/specializations.py:generate", + "target": "class:models/bc.py:DataElementConcept", + "type": "calls", + "direction": "forward", + "weight": 0.8 + }, + { + "source": "function:routes/specializations.py:generate", + "target": "class:models/specialization.py:DatasetSpecialization", + "type": "calls", + "direction": "forward", + "weight": 0.8 + }, + { + "source": "function:routes/specializations.py:generate", + "target": "function:services/audit.py:log_change", + "type": "calls", + "direction": "forward", + "weight": 0.8 + }, + { + "source": "class:services/cdisc_api.py:CDISCApiClient", + "target": "function:services/api_cache.py:cached", + "type": "calls", + "direction": "forward", + "weight": 0.8 + }, + { + "source": "class:services/loinc_api.py:LoincApiClient", + "target": "function:services/api_cache.py:cached", + "type": "calls", + "direction": "forward", + "weight": 0.8 + }, + + { + "source": "file:services/cdisc_api.py", + "target": "file:tests/test_cdisc_api_cache.py", + "type": "tested_by", + "direction": "forward", + "weight": 0.5 + }, + { + "source": "file:services/api_cache.py", + "target": "file:tests/test_cdisc_api_cache.py", + "type": "tested_by", + "direction": "forward", + "weight": 0.5 + }, + { + "source": "file:services/loinc_api.py", + "target": "file:tests/test_loinc.py", + "type": "tested_by", + "direction": "forward", + "weight": 0.5 + }, + { + "source": "file:routes/loinc.py", + "target": "file:tests/test_loinc.py", + "type": "tested_by", + "direction": "forward", + "weight": 0.5 + } + ] +} diff --git a/.ua/.trash-1785851745/batch-4.json b/.ua/.trash-1785851745/batch-4.json new file mode 100644 index 0000000..7e0e8b8 --- /dev/null +++ b/.ua/.trash-1785851745/batch-4.json @@ -0,0 +1,340 @@ +{ + "nodes": [ + { + "id": "file:app.py", + "type": "file", + "name": "app.py", + "filePath": "app.py", + "summary": "Flask application factory that configures logging, initializes the shared database and migration extensions, registers all eight feature blueprints, and doubles as the dev-server entry point via `python app.py`.", + "tags": [ + "entry-point", + "flask", + "application-factory", + "blueprint-registration" + ], + "complexity": "moderate" + }, + { + "id": "file:config.py", + "type": "file", + "name": "config.py", + "filePath": "config.py", + "summary": "Centralizes all environment-driven configuration for the Flask app, including the secret key, database URI, CDISC/NCIt API base URLs and credentials, upload size limit, and dev server port.", + "tags": ["configuration", "environment-variables", "flask", "settings"], + "complexity": "simple" + }, + { + "id": "file:db_bootstrap.py", + "type": "file", + "name": "db_bootstrap.py", + "filePath": "db_bootstrap.py", + "summary": "Brings the app's database to the current Alembic migration head at startup, distinguishing fresh, legacy create_all(), and pre-squash-stamped databases, and raising if the schema predates the squashed baseline.", + "tags": ["database-migration", "alembic", "bootstrap", "startup"], + "complexity": "moderate", + "languageNotes": "Encodes a one-time schema-squash migration strategy: legacy databases are schema-validated then stamped rather than replayed through the old revision chain." + }, + { + "id": "file:extensions.py", + "type": "file", + "name": "extensions.py", + "filePath": "extensions.py", + "summary": "Defines the shared SQLAlchemy `db` and Flask-Migrate `migrate` instances used across the app to avoid circular imports, and configures SQLite WAL mode with a busy timeout so the Flask app and MCP server can coexist as concurrent writers.", + "tags": ["database", "sqlalchemy", "shared-extension", "sqlite"], + "complexity": "simple" + }, + { + "id": "file:models/__init__.py", + "type": "file", + "name": "__init__.py", + "filePath": "models/__init__.py", + "summary": "Package marker for the `models` module that imports the shared `db` instance so all SQLAlchemy model classes register against a single metadata registry.", + "tags": ["entry-point", "package-init", "barrel"], + "complexity": "simple" + }, + { + "id": "file:tests/test_dashboard.py", + "type": "file", + "name": "test_dashboard.py", + "filePath": "tests/test_dashboard.py", + "summary": "Unit tests for the dashboard route, verifying it renders correctly with live CDISC/NCIt API data, degrades gracefully on API errors and empty results, reflects local DB stats accurately, and issues both API calls concurrently.", + "tags": ["test", "dashboard", "mocking", "concurrency"], + "complexity": "moderate" + }, + { + "id": "file:tests/test_db_bootstrap.py", + "type": "file", + "name": "test_db_bootstrap.py", + "filePath": "tests/test_db_bootstrap.py", + "summary": "Unit tests for `ensure_db()` covering a fresh baseline upgrade, stamping of a legacy create_all() database, restamping of a pre-squash-stamped database, raising on an outdated schema, and idempotency across repeated runs.", + "tags": ["test", "database-migration", "alembic", "fixtures"], + "complexity": "moderate" + }, + { + "id": "function:app.py:_configure_logging", + "type": "function", + "name": "_configure_logging", + "filePath": "app.py", + "lineRange": [10, 21], + "summary": "Attaches a basic logging handler to the root logger exactly once, deferring to any handler already installed by pytest, gunicorn, or the MCP server.", + "tags": ["logging", "initialization", "utility"], + "complexity": "simple" + }, + { + "id": "function:app.py:create_app", + "type": "function", + "name": "create_app", + "filePath": "app.py", + "lineRange": [24, 50], + "summary": "Application factory that builds the Flask app, applies configuration, initializes the `db` and `migrate` extensions, and registers all eight route blueprints under their URL prefixes.", + "tags": [ + "entry-point", + "application-factory", + "flask", + "blueprint-registration" + ], + "complexity": "moderate" + }, + { + "id": "class:config.py:Config", + "type": "class", + "name": "Config", + "filePath": "config.py", + "lineRange": [4, 14], + "summary": "Flask configuration object sourcing the secret key, database URI, CDISC/NCIt API settings, upload size limit, and dev server port from environment variables with sensible defaults.", + "tags": ["configuration", "environment-variables", "settings"], + "complexity": "simple" + }, + { + "id": "function:db_bootstrap.py:ensure_db", + "type": "function", + "name": "ensure_db", + "filePath": "db_bootstrap.py", + "lineRange": [36, 71], + "summary": "Idempotently brings the configured database to the current Alembic migration head, handling fresh databases, legacy create_all() databases, and pre-squash-stamped databases, and raising RuntimeError if the schema predates the squashed baseline.", + "tags": ["database-migration", "alembic", "idempotent", "startup"], + "complexity": "moderate" + }, + { + "id": "function:extensions.py:_set_sqlite_pragmas", + "type": "function", + "name": "_set_sqlite_pragmas", + "filePath": "extensions.py", + "lineRange": [13, 25], + "summary": "SQLAlchemy connect-event listener that enables WAL journal mode and a 15-second busy timeout on every SQLite connection so the Flask app and MCP server can write concurrently without lock errors.", + "tags": ["sqlite", "event-handler", "concurrency", "database"], + "complexity": "simple" + }, + { + "id": "class:tests/test_dashboard.py:TestDashboardIndex", + "type": "class", + "name": "TestDashboardIndex", + "filePath": "tests/test_dashboard.py", + "lineRange": [24, 75], + "summary": "Test suite for the dashboard index route, covering successful rendering with live API stats, graceful handling of API errors and empty results, correctness of locally computed DB stats, and concurrent execution of the two upstream API calls.", + "tags": ["test", "dashboard", "mocking"], + "complexity": "moderate" + }, + { + "id": "function:tests/test_db_bootstrap.py:_make_app", + "type": "function", + "name": "_make_app", + "filePath": "tests/test_db_bootstrap.py", + "lineRange": [20, 30], + "summary": "Test helper that builds a Flask app configured against a temporary SQLite file so each `ensure_db()` test runs against an isolated on-disk database.", + "tags": ["test", "fixture", "utility"], + "complexity": "simple" + }, + { + "id": "class:tests/test_db_bootstrap.py:TestEnsureDb", + "type": "class", + "name": "TestEnsureDb", + "filePath": "tests/test_db_bootstrap.py", + "lineRange": [43, 87], + "summary": "Test suite for `ensure_db()` verifying correct behavior across a fresh baseline database, a legacy create_all() database, a pre-squash-stamped database, an outdated/unrecognized schema, and repeated (idempotent) invocations.", + "tags": ["test", "database-migration", "alembic"], + "complexity": "moderate" + } + ], + "edges": [ + { + "source": "file:app.py", + "target": "file:config.py", + "type": "imports", + "direction": "forward", + "weight": 0.7 + }, + { + "source": "file:app.py", + "target": "file:extensions.py", + "type": "imports", + "direction": "forward", + "weight": 0.7 + }, + { + "source": "file:db_bootstrap.py", + "target": "file:extensions.py", + "type": "imports", + "direction": "forward", + "weight": 0.7 + }, + { + "source": "file:models/__init__.py", + "target": "file:extensions.py", + "type": "imports", + "direction": "forward", + "weight": 0.7 + }, + { + "source": "file:tests/test_dashboard.py", + "target": "file:extensions.py", + "type": "imports", + "direction": "forward", + "weight": 0.7 + }, + { + "source": "file:tests/test_dashboard.py", + "target": "file:models/bc.py", + "type": "imports", + "direction": "forward", + "weight": 0.7 + }, + { + "source": "file:tests/test_db_bootstrap.py", + "target": "file:app.py", + "type": "imports", + "direction": "forward", + "weight": 0.7 + }, + { + "source": "file:tests/test_db_bootstrap.py", + "target": "file:db_bootstrap.py", + "type": "imports", + "direction": "forward", + "weight": 0.7 + }, + { + "source": "file:tests/test_db_bootstrap.py", + "target": "file:extensions.py", + "type": "imports", + "direction": "forward", + "weight": 0.7 + }, + + { + "source": "file:app.py", + "target": "function:app.py:_configure_logging", + "type": "contains", + "direction": "forward", + "weight": 1.0 + }, + { + "source": "file:app.py", + "target": "function:app.py:create_app", + "type": "contains", + "direction": "forward", + "weight": 1.0 + }, + { + "source": "file:config.py", + "target": "class:config.py:Config", + "type": "contains", + "direction": "forward", + "weight": 1.0 + }, + { + "source": "file:db_bootstrap.py", + "target": "function:db_bootstrap.py:ensure_db", + "type": "contains", + "direction": "forward", + "weight": 1.0 + }, + { + "source": "file:extensions.py", + "target": "function:extensions.py:_set_sqlite_pragmas", + "type": "contains", + "direction": "forward", + "weight": 1.0 + }, + { + "source": "file:tests/test_dashboard.py", + "target": "class:tests/test_dashboard.py:TestDashboardIndex", + "type": "contains", + "direction": "forward", + "weight": 1.0 + }, + { + "source": "file:tests/test_db_bootstrap.py", + "target": "function:tests/test_db_bootstrap.py:_make_app", + "type": "contains", + "direction": "forward", + "weight": 1.0 + }, + { + "source": "file:tests/test_db_bootstrap.py", + "target": "class:tests/test_db_bootstrap.py:TestEnsureDb", + "type": "contains", + "direction": "forward", + "weight": 1.0 + }, + + { + "source": "file:app.py", + "target": "function:app.py:create_app", + "type": "exports", + "direction": "forward", + "weight": 0.8 + }, + { + "source": "file:db_bootstrap.py", + "target": "function:db_bootstrap.py:ensure_db", + "type": "exports", + "direction": "forward", + "weight": 0.8 + }, + { + "source": "file:config.py", + "target": "class:config.py:Config", + "type": "exports", + "direction": "forward", + "weight": 0.8 + }, + + { + "source": "function:tests/test_db_bootstrap.py:_make_app", + "target": "function:app.py:create_app", + "type": "calls", + "direction": "forward", + "weight": 0.8 + }, + { + "source": "class:tests/test_db_bootstrap.py:TestEnsureDb", + "target": "function:db_bootstrap.py:ensure_db", + "type": "calls", + "direction": "forward", + "weight": 0.8 + }, + { + "source": "class:tests/test_dashboard.py:TestDashboardIndex", + "target": "class:models/bc.py:BiomedicalConcept", + "type": "calls", + "direction": "forward", + "weight": 0.8 + }, + + { + "source": "file:app.py", + "target": "file:db_bootstrap.py", + "type": "depends_on", + "direction": "forward", + "weight": 0.6 + }, + + { + "source": "file:db_bootstrap.py", + "target": "file:tests/test_db_bootstrap.py", + "type": "tested_by", + "direction": "forward", + "weight": 0.5 + } + ] +} diff --git a/.ua/.trash-1785851745/batch-5.json b/.ua/.trash-1785851745/batch-5.json new file mode 100644 index 0000000..da8add1 --- /dev/null +++ b/.ua/.trash-1785851745/batch-5.json @@ -0,0 +1,1004 @@ +{ + "nodes": [ + { + "id": "file:mcp_server/__main__.py", + "type": "file", + "name": "__main__.py", + "filePath": "mcp_server/__main__.py", + "summary": "Module entry point for `python -m mcp_server`; imports the server's main() coroutine runner and invokes it immediately to start the stdio MCP server loop.", + "tags": ["entry-point", "mcp-server", "bootstrap"], + "complexity": "simple" + }, + { + "id": "file:mcp_server/server.py", + "type": "file", + "name": "server.py", + "filePath": "mcp_server/server.py", + "summary": "MCP server exposing the CDISC curation app's data and write operations as 14 stdio tools (8 read, 6 write); each call is dispatched into a Flask app context shared with the web app so both processes hit the same SQLite database and service layer.", + "tags": ["entry-point", "mcp-server", "api-handler", "service"], + "complexity": "complex", + "languageNotes": "Uses lazy, function-local imports throughout (Flask app, models, service clients) so they only resolve once an app context exists, plus a functools.wraps-based `_with_app_context` decorator that pushes a fresh app context per call since handlers run on asyncio executor worker threads." + }, + { + "id": "function:mcp_server/server.py:_with_app_context", + "type": "function", + "name": "_with_app_context", + "filePath": "mcp_server/server.py", + "lineRange": [60, 72], + "summary": "Decorator that wraps a tool handler so it runs inside a fresh Flask app context, required because handlers execute on the asyncio executor's worker threads rather than the request thread.", + "tags": ["decorator", "middleware", "mcp-server"], + "complexity": "simple" + }, + { + "id": "function:mcp_server/server.py:_dispatch", + "type": "function", + "name": "_dispatch", + "filePath": "mcp_server/server.py", + "lineRange": [292, 312], + "summary": "Central dispatch table mapping the 14 MCP tool names to their handler functions and raising ValueError for unknown tool names.", + "tags": ["dispatcher", "mcp-server", "routing"], + "complexity": "moderate" + }, + { + "id": "function:mcp_server/server.py:_bc_full_dict", + "type": "function", + "name": "_bc_full_dict", + "filePath": "mcp_server/server.py", + "lineRange": [320, 332], + "summary": "Builds the full JSON-serializable dict for a BiomedicalConcept, extending its to_dict() with fields (code, source, history_of_change, timestamps) omitted by the base serializer.", + "tags": ["serialization", "data-model", "utility"], + "complexity": "simple" + }, + { + "id": "function:mcp_server/server.py:_list_bcs", + "type": "function", + "name": "_list_bcs", + "filePath": "mcp_server/server.py", + "lineRange": [336, 356], + "summary": "Implements the list_bcs tool: paginated, filterable search over BiomedicalConcept by free-text query and status.", + "tags": ["api-handler", "query", "pagination"], + "complexity": "moderate" + }, + { + "id": "function:mcp_server/server.py:_get_bc", + "type": "function", + "name": "_get_bc", + "filePath": "mcp_server/server.py", + "lineRange": [360, 405], + "summary": "Implements the get_bc tool: fetches full BC detail including its DataElementConcepts, dataset specializations, and governance history.", + "tags": ["api-handler", "query", "data-model"], + "complexity": "moderate" + }, + { + "id": "function:mcp_server/server.py:_search_ncit", + "type": "function", + "name": "_search_ncit", + "filePath": "mcp_server/server.py", + "lineRange": [409, 416], + "summary": "Implements the search_ncit tool, delegating to NCItApiClient.search_concept with a required search term and bounded result count.", + "tags": ["api-handler", "external-api"], + "complexity": "simple" + }, + { + "id": "function:mcp_server/server.py:_get_ncit_concept", + "type": "function", + "name": "_get_ncit_concept", + "filePath": "mcp_server/server.py", + "lineRange": [420, 426], + "summary": "Implements the get_ncit_concept tool, fetching full concept detail from the NCI Thesaurus via NCItApiClient.", + "tags": ["api-handler", "external-api"], + "complexity": "simple" + }, + { + "id": "function:mcp_server/server.py:_search_loinc", + "type": "function", + "name": "_search_loinc", + "filePath": "mcp_server/server.py", + "lineRange": [430, 437], + "summary": "Implements the search_loinc tool, delegating to LoincApiClient.search for LOINC code lookups.", + "tags": ["api-handler", "external-api"], + "complexity": "simple" + }, + { + "id": "function:mcp_server/server.py:_search_cdisc_library", + "type": "function", + "name": "_search_cdisc_library", + "filePath": "mcp_server/server.py", + "lineRange": [441, 450], + "summary": "Implements the search_cdisc_library tool, querying the live CDISC Library for published Biomedical Concepts and filtering results by title.", + "tags": ["api-handler", "external-api"], + "complexity": "simple" + }, + { + "id": "function:mcp_server/server.py:_get_library_bc", + "type": "function", + "name": "_get_library_bc", + "filePath": "mcp_server/server.py", + "lineRange": [454, 460], + "summary": "Implements the get_library_bc tool, fetching one published BC from the CDISC Library by concept id.", + "tags": ["api-handler", "external-api"], + "complexity": "simple" + }, + { + "id": "function:mcp_server/server.py:_list_review_queue", + "type": "function", + "name": "_list_review_queue", + "filePath": "mcp_server/server.py", + "lineRange": [464, 477], + "summary": "Implements the list_review_queue tool: summarizes the governance Kanban board by status counts plus the count of pending ingestion records.", + "tags": ["api-handler", "governance", "query"], + "complexity": "simple" + }, + { + "id": "function:mcp_server/server.py:_create_bc", + "type": "function", + "name": "_create_bc", + "filePath": "mcp_server/server.py", + "lineRange": [486, 494], + "summary": "Implements the create_bc write tool, delegating to bc_service.create_bc and optionally bc_service.save_decs before returning the full BC detail.", + "tags": ["api-handler", "write-operation", "service"], + "complexity": "simple" + }, + { + "id": "function:mcp_server/server.py:_update_bc", + "type": "function", + "name": "_update_bc", + "filePath": "mcp_server/server.py", + "lineRange": [498, 507], + "summary": "Implements the update_bc write tool, applying field updates via bc_service.update_bc and returning the refreshed BC dict.", + "tags": ["api-handler", "write-operation", "service"], + "complexity": "simple" + }, + { + "id": "function:mcp_server/server.py:_map_ncit_to_bc", + "type": "function", + "name": "_map_ncit_to_bc", + "filePath": "mcp_server/server.py", + "lineRange": [511, 519], + "summary": "Implements the map_ncit_to_bc write tool, attaching an NCIt code to a BC via bc_service.map_ncit_to_bc (which also handles IMPORT_ id promotion).", + "tags": ["api-handler", "write-operation", "service"], + "complexity": "simple" + }, + { + "id": "function:mcp_server/server.py:_submit_bc_for_review", + "type": "function", + "name": "_submit_bc_for_review", + "filePath": "mcp_server/server.py", + "lineRange": [523, 531], + "summary": "Implements the submit_bc_for_review write tool, moving a BC from provisional into SME review via bc_service.submit_bc_for_review.", + "tags": ["api-handler", "write-operation", "governance"], + "complexity": "simple" + }, + { + "id": "function:mcp_server/server.py:_advance_governance", + "type": "function", + "name": "_advance_governance", + "filePath": "mcp_server/server.py", + "lineRange": [535, 542], + "summary": "Implements the advance_governance write tool, advancing a BC's governance stage via governance_service.advance_governance.", + "tags": ["api-handler", "write-operation", "governance"], + "complexity": "simple" + }, + { + "id": "function:mcp_server/server.py:_reject_bc", + "type": "function", + "name": "_reject_bc", + "filePath": "mcp_server/server.py", + "lineRange": [546, 553], + "summary": "Implements the reject_bc write tool, returning a BC to provisional status via governance_service.reject_bc.", + "tags": ["api-handler", "write-operation", "governance"], + "complexity": "simple" + }, + { + "id": "function:mcp_server/server.py:main", + "type": "function", + "name": "main", + "filePath": "mcp_server/server.py", + "lineRange": [561, 563], + "summary": "Async entry point that configures logging and runs the stdio MCP server loop; invoked by `python -m mcp_server`.", + "tags": ["entry-point", "bootstrap", "mcp-server"], + "complexity": "simple" + }, + { + "id": "function:mcp_server/server.py:_run", + "type": "function", + "name": "_run", + "filePath": "mcp_server/server.py", + "lineRange": [566, 568], + "summary": "Opens the stdio transport streams and runs the MCP Server with its initialization options.", + "tags": ["bootstrap", "mcp-server"], + "complexity": "simple" + }, + { + "id": "file:models/specialization.py", + "type": "file", + "name": "specialization.py", + "filePath": "models/specialization.py", + "summary": "SQLAlchemy model for a dataset specialization (SDTM/CDASH variable-level metadata group) tied to a Biomedical Concept, storing its variable list as a JSON-serialized text column.", + "tags": ["data-model", "orm", "database"], + "complexity": "simple" + }, + { + "id": "class:models/specialization.py:DatasetSpecialization", + "type": "class", + "name": "DatasetSpecialization", + "filePath": "models/specialization.py", + "lineRange": [6, 30], + "summary": "ORM model mapping the dataset_specializations table; exposes a `variables` property/setter that transparently (de)serializes a JSON text column, plus a to_dict() used by routes and the MCP server.", + "tags": ["data-model", "orm", "serialization"], + "complexity": "simple" + }, + { + "id": "file:tests/test_mcp_server.py", + "type": "file", + "name": "test_mcp_server.py", + "filePath": "tests/test_mcp_server.py", + "summary": "Unit tests for the MCP server's `_dispatch` routing and all 14 tool handlers, exercising them directly against a shared in-memory test Flask app (no stdio transport) and asserting on returned data plus AuditLog/GovernanceRecord side effects.", + "tags": ["test", "mcp-server", "unit-test"], + "complexity": "complex" + }, + { + "id": "class:tests/test_mcp_server.py:TestDispatch", + "type": "class", + "name": "TestDispatch", + "filePath": "tests/test_mcp_server.py", + "lineRange": [32, 54], + "summary": "Tests the _dispatch routing function: raises for unknown tool names and asserts every tool declared in _TOOLS is actually present in the dispatch handler map.", + "tags": ["test", "mcp-server", "dispatcher"], + "complexity": "simple" + }, + { + "id": "class:tests/test_mcp_server.py:TestListBcs", + "type": "class", + "name": "TestListBcs", + "filePath": "tests/test_mcp_server.py", + "lineRange": [57, 86], + "summary": "Tests the list_bcs tool against an empty DB, filtering by query/status, and page/per_page pagination behavior.", + "tags": ["test", "mcp-server", "pagination"], + "complexity": "simple" + }, + { + "id": "class:tests/test_mcp_server.py:TestGetBc", + "type": "class", + "name": "TestGetBc", + "filePath": "tests/test_mcp_server.py", + "lineRange": [89, 109], + "summary": "Tests the get_bc tool: requires a bc_id, raises on missing BC, and returns full detail including DECs, specializations, and governance records.", + "tags": ["test", "mcp-server", "data-model"], + "complexity": "simple" + }, + { + "id": "class:tests/test_mcp_server.py:TestExternalApiTools", + "type": "class", + "name": "TestExternalApiTools", + "filePath": "tests/test_mcp_server.py", + "lineRange": [112, 154], + "summary": "Tests the external-API-backed tools (search_ncit, get_ncit_concept, search_loinc, search_cdisc_library, get_library_bc), mocking the underlying NCIt/LOINC/CDISC Library clients and asserting call arguments, filtering, and error propagation.", + "tags": ["test", "external-api", "mocking"], + "complexity": "moderate" + }, + { + "id": "class:tests/test_mcp_server.py:TestCreateBc", + "type": "class", + "name": "TestCreateBc", + "filePath": "tests/test_mcp_server.py", + "lineRange": [162, 188], + "summary": "Tests the create_bc write tool: creates a BC with DECs and an audit trail, rejects a duplicate bc_id, and rejects a missing bc_id.", + "tags": ["test", "write-operation", "audit"], + "complexity": "simple" + }, + { + "id": "class:tests/test_mcp_server.py:TestUpdateBc", + "type": "class", + "name": "TestUpdateBc", + "filePath": "tests/test_mcp_server.py", + "lineRange": [191, 203], + "summary": "Tests the update_bc write tool: verifies before/after audit logging on update and that updating a missing BC raises.", + "tags": ["test", "write-operation", "audit"], + "complexity": "simple" + }, + { + "id": "class:tests/test_mcp_server.py:TestMapNcit", + "type": "class", + "name": "TestMapNcit", + "filePath": "tests/test_mcp_server.py", + "lineRange": [206, 266], + "summary": "Tests the map_ncit_to_bc write tool, covering audit logging, IMPORT_ id promotion and its cascading repoint of dependent DECs/specializations/governance records and child BCs, empty-code validation, and collision handling.", + "tags": ["test", "write-operation", "ncit"], + "complexity": "complex" + }, + { + "id": "class:tests/test_mcp_server.py:TestGovernanceWrites", + "type": "class", + "name": "TestGovernanceWrites", + "filePath": "tests/test_mcp_server.py", + "lineRange": [269, 300], + "summary": "Tests the submit_bc_for_review, advance_governance, and reject_bc write tools end-to-end, asserting GovernanceRecord history and audit trail entries.", + "tags": ["test", "governance", "write-operation"], + "complexity": "moderate" + }, + { + "id": "class:tests/test_mcp_server.py:TestReviewQueue", + "type": "class", + "name": "TestReviewQueue", + "filePath": "tests/test_mcp_server.py", + "lineRange": [303, 315], + "summary": "Tests the list_review_queue tool's governance-status summary counts and pending ingestion count.", + "tags": ["test", "governance", "query"], + "complexity": "simple" + }, + { + "id": "file:tests/test_specializations_routes.py", + "type": "file", + "name": "test_specializations_routes.py", + "filePath": "tests/test_specializations_routes.py", + "summary": "Route-level tests for the dataset specializations blueprint covering listing, creating, editing/upserting, deleting, detail views, and generating variables from DECs, including audit log verification and CDISC Library error handling.", + "tags": ["test", "route-test", "unit-test"], + "complexity": "complex" + }, + { + "id": "class:tests/test_specializations_routes.py:TestIndex", + "type": "class", + "name": "TestIndex", + "filePath": "tests/test_specializations_routes.py", + "lineRange": [24, 40], + "summary": "Tests the specializations index route: lists persisted specializations and confirms the page still renders when the CDISC Library client raises an error.", + "tags": ["test", "route-test", "error-handling"], + "complexity": "simple" + }, + { + "id": "class:tests/test_specializations_routes.py:TestCreate", + "type": "class", + "name": "TestCreate", + "filePath": "tests/test_specializations_routes.py", + "lineRange": [43, 97], + "summary": "Tests creating a dataset specialization via the route: persists the spec, requires bc_id/vlm_group_id, writes an audit log entry, and persists variable rows submitted via the form.", + "tags": ["test", "route-test", "audit"], + "complexity": "moderate" + }, + { + "id": "class:tests/test_specializations_routes.py:TestEditUpsert", + "type": "class", + "name": "TestEditUpsert", + "filePath": "tests/test_specializations_routes.py", + "lineRange": [100, 141], + "summary": "Tests the edit route's upsert behavior: posting an existing vlm_group_id updates the record instead of duplicating it, and the update writes an audit log entry.", + "tags": ["test", "route-test", "upsert"], + "complexity": "simple" + }, + { + "id": "class:tests/test_specializations_routes.py:TestDelete", + "type": "class", + "name": "TestDelete", + "filePath": "tests/test_specializations_routes.py", + "lineRange": [144, 172], + "summary": "Tests the delete route: removes the specialization, writes an audit log entry, and returns 404 for an unknown vlm_group_id.", + "tags": ["test", "route-test", "audit"], + "complexity": "simple" + }, + { + "id": "class:tests/test_specializations_routes.py:TestDetail", + "type": "class", + "name": "TestDetail", + "filePath": "tests/test_specializations_routes.py", + "lineRange": [175, 189], + "summary": "Tests the specialization detail view renders for a known record and returns 404 for an unknown one.", + "tags": ["test", "route-test"], + "complexity": "simple" + }, + { + "id": "class:tests/test_specializations_routes.py:TestLibraryDetail", + "type": "class", + "name": "TestLibraryDetail", + "filePath": "tests/test_specializations_routes.py", + "lineRange": [192, 204], + "summary": "Tests the library-detail view: renders a published CDISC Library specialization and redirects to the dashboard when the Library client errors.", + "tags": ["test", "route-test", "error-handling"], + "complexity": "simple" + }, + { + "id": "class:tests/test_specializations_routes.py:TestGenerate", + "type": "class", + "name": "TestGenerate", + "filePath": "tests/test_specializations_routes.py", + "lineRange": [207, 245], + "summary": "Tests generating a dataset specialization from a BC's DECs: builds variables from DECs, rejects duplicates, 404s for an unknown BC, and writes an audit log entry.", + "tags": ["test", "route-test", "generation"], + "complexity": "moderate" + }, + { + "id": "class:tests/test_specializations_routes.py:TestGenerateFromDec", + "type": "class", + "name": "TestGenerateFromDec", + "filePath": "tests/test_specializations_routes.py", + "lineRange": [248, 260], + "summary": "Tests the JSON endpoint that returns generated variables for a single DEC, and its 400 response when bc_id is missing.", + "tags": ["test", "route-test", "json-api"], + "complexity": "simple" + } + ], + "edges": [ + { + "source": "file:mcp_server/__main__.py", + "target": "file:mcp_server/server.py", + "type": "imports", + "direction": "forward", + "weight": 0.7 + }, + { + "source": "file:models/specialization.py", + "target": "file:extensions.py", + "type": "imports", + "direction": "forward", + "weight": 0.7 + }, + { + "source": "file:tests/test_mcp_server.py", + "target": "file:extensions.py", + "type": "imports", + "direction": "forward", + "weight": 0.7 + }, + { + "source": "file:tests/test_mcp_server.py", + "target": "file:mcp_server/server.py", + "type": "imports", + "direction": "forward", + "weight": 0.7 + }, + { + "source": "file:tests/test_mcp_server.py", + "target": "file:models/audit.py", + "type": "imports", + "direction": "forward", + "weight": 0.7 + }, + { + "source": "file:tests/test_mcp_server.py", + "target": "file:models/bc.py", + "type": "imports", + "direction": "forward", + "weight": 0.7 + }, + { + "source": "file:tests/test_mcp_server.py", + "target": "file:models/governance.py", + "type": "imports", + "direction": "forward", + "weight": 0.7 + }, + { + "source": "file:tests/test_mcp_server.py", + "target": "file:models/ingestion.py", + "type": "imports", + "direction": "forward", + "weight": 0.7 + }, + { + "source": "file:tests/test_mcp_server.py", + "target": "file:models/specialization.py", + "type": "imports", + "direction": "forward", + "weight": 0.7 + }, + { + "source": "file:tests/test_specializations_routes.py", + "target": "file:extensions.py", + "type": "imports", + "direction": "forward", + "weight": 0.7 + }, + { + "source": "file:tests/test_specializations_routes.py", + "target": "file:models/audit.py", + "type": "imports", + "direction": "forward", + "weight": 0.7 + }, + { + "source": "file:tests/test_specializations_routes.py", + "target": "file:models/bc.py", + "type": "imports", + "direction": "forward", + "weight": 0.7 + }, + { + "source": "file:tests/test_specializations_routes.py", + "target": "file:models/specialization.py", + "type": "imports", + "direction": "forward", + "weight": 0.7 + }, + + { + "source": "file:mcp_server/server.py", + "target": "function:mcp_server/server.py:_with_app_context", + "type": "contains", + "direction": "forward", + "weight": 1.0 + }, + { + "source": "file:mcp_server/server.py", + "target": "function:mcp_server/server.py:_dispatch", + "type": "contains", + "direction": "forward", + "weight": 1.0 + }, + { + "source": "file:mcp_server/server.py", + "target": "function:mcp_server/server.py:_bc_full_dict", + "type": "contains", + "direction": "forward", + "weight": 1.0 + }, + { + "source": "file:mcp_server/server.py", + "target": "function:mcp_server/server.py:_list_bcs", + "type": "contains", + "direction": "forward", + "weight": 1.0 + }, + { + "source": "file:mcp_server/server.py", + "target": "function:mcp_server/server.py:_get_bc", + "type": "contains", + "direction": "forward", + "weight": 1.0 + }, + { + "source": "file:mcp_server/server.py", + "target": "function:mcp_server/server.py:_search_ncit", + "type": "contains", + "direction": "forward", + "weight": 1.0 + }, + { + "source": "file:mcp_server/server.py", + "target": "function:mcp_server/server.py:_get_ncit_concept", + "type": "contains", + "direction": "forward", + "weight": 1.0 + }, + { + "source": "file:mcp_server/server.py", + "target": "function:mcp_server/server.py:_search_loinc", + "type": "contains", + "direction": "forward", + "weight": 1.0 + }, + { + "source": "file:mcp_server/server.py", + "target": "function:mcp_server/server.py:_search_cdisc_library", + "type": "contains", + "direction": "forward", + "weight": 1.0 + }, + { + "source": "file:mcp_server/server.py", + "target": "function:mcp_server/server.py:_get_library_bc", + "type": "contains", + "direction": "forward", + "weight": 1.0 + }, + { + "source": "file:mcp_server/server.py", + "target": "function:mcp_server/server.py:_list_review_queue", + "type": "contains", + "direction": "forward", + "weight": 1.0 + }, + { + "source": "file:mcp_server/server.py", + "target": "function:mcp_server/server.py:_create_bc", + "type": "contains", + "direction": "forward", + "weight": 1.0 + }, + { + "source": "file:mcp_server/server.py", + "target": "function:mcp_server/server.py:_update_bc", + "type": "contains", + "direction": "forward", + "weight": 1.0 + }, + { + "source": "file:mcp_server/server.py", + "target": "function:mcp_server/server.py:_map_ncit_to_bc", + "type": "contains", + "direction": "forward", + "weight": 1.0 + }, + { + "source": "file:mcp_server/server.py", + "target": "function:mcp_server/server.py:_submit_bc_for_review", + "type": "contains", + "direction": "forward", + "weight": 1.0 + }, + { + "source": "file:mcp_server/server.py", + "target": "function:mcp_server/server.py:_advance_governance", + "type": "contains", + "direction": "forward", + "weight": 1.0 + }, + { + "source": "file:mcp_server/server.py", + "target": "function:mcp_server/server.py:_reject_bc", + "type": "contains", + "direction": "forward", + "weight": 1.0 + }, + { + "source": "file:mcp_server/server.py", + "target": "function:mcp_server/server.py:main", + "type": "contains", + "direction": "forward", + "weight": 1.0 + }, + { + "source": "file:mcp_server/server.py", + "target": "function:mcp_server/server.py:_run", + "type": "contains", + "direction": "forward", + "weight": 1.0 + }, + + { + "source": "file:models/specialization.py", + "target": "class:models/specialization.py:DatasetSpecialization", + "type": "contains", + "direction": "forward", + "weight": 1.0 + }, + + { + "source": "file:tests/test_mcp_server.py", + "target": "class:tests/test_mcp_server.py:TestDispatch", + "type": "contains", + "direction": "forward", + "weight": 1.0 + }, + { + "source": "file:tests/test_mcp_server.py", + "target": "class:tests/test_mcp_server.py:TestListBcs", + "type": "contains", + "direction": "forward", + "weight": 1.0 + }, + { + "source": "file:tests/test_mcp_server.py", + "target": "class:tests/test_mcp_server.py:TestGetBc", + "type": "contains", + "direction": "forward", + "weight": 1.0 + }, + { + "source": "file:tests/test_mcp_server.py", + "target": "class:tests/test_mcp_server.py:TestExternalApiTools", + "type": "contains", + "direction": "forward", + "weight": 1.0 + }, + { + "source": "file:tests/test_mcp_server.py", + "target": "class:tests/test_mcp_server.py:TestCreateBc", + "type": "contains", + "direction": "forward", + "weight": 1.0 + }, + { + "source": "file:tests/test_mcp_server.py", + "target": "class:tests/test_mcp_server.py:TestUpdateBc", + "type": "contains", + "direction": "forward", + "weight": 1.0 + }, + { + "source": "file:tests/test_mcp_server.py", + "target": "class:tests/test_mcp_server.py:TestMapNcit", + "type": "contains", + "direction": "forward", + "weight": 1.0 + }, + { + "source": "file:tests/test_mcp_server.py", + "target": "class:tests/test_mcp_server.py:TestGovernanceWrites", + "type": "contains", + "direction": "forward", + "weight": 1.0 + }, + { + "source": "file:tests/test_mcp_server.py", + "target": "class:tests/test_mcp_server.py:TestReviewQueue", + "type": "contains", + "direction": "forward", + "weight": 1.0 + }, + + { + "source": "file:tests/test_specializations_routes.py", + "target": "class:tests/test_specializations_routes.py:TestIndex", + "type": "contains", + "direction": "forward", + "weight": 1.0 + }, + { + "source": "file:tests/test_specializations_routes.py", + "target": "class:tests/test_specializations_routes.py:TestCreate", + "type": "contains", + "direction": "forward", + "weight": 1.0 + }, + { + "source": "file:tests/test_specializations_routes.py", + "target": "class:tests/test_specializations_routes.py:TestEditUpsert", + "type": "contains", + "direction": "forward", + "weight": 1.0 + }, + { + "source": "file:tests/test_specializations_routes.py", + "target": "class:tests/test_specializations_routes.py:TestDelete", + "type": "contains", + "direction": "forward", + "weight": 1.0 + }, + { + "source": "file:tests/test_specializations_routes.py", + "target": "class:tests/test_specializations_routes.py:TestDetail", + "type": "contains", + "direction": "forward", + "weight": 1.0 + }, + { + "source": "file:tests/test_specializations_routes.py", + "target": "class:tests/test_specializations_routes.py:TestLibraryDetail", + "type": "contains", + "direction": "forward", + "weight": 1.0 + }, + { + "source": "file:tests/test_specializations_routes.py", + "target": "class:tests/test_specializations_routes.py:TestGenerate", + "type": "contains", + "direction": "forward", + "weight": 1.0 + }, + { + "source": "file:tests/test_specializations_routes.py", + "target": "class:tests/test_specializations_routes.py:TestGenerateFromDec", + "type": "contains", + "direction": "forward", + "weight": 1.0 + }, + + { + "source": "file:models/specialization.py", + "target": "class:models/specialization.py:DatasetSpecialization", + "type": "exports", + "direction": "forward", + "weight": 0.8 + }, + { + "source": "file:mcp_server/server.py", + "target": "function:mcp_server/server.py:_dispatch", + "type": "exports", + "direction": "forward", + "weight": 0.8 + }, + { + "source": "file:mcp_server/server.py", + "target": "function:mcp_server/server.py:main", + "type": "exports", + "direction": "forward", + "weight": 0.8 + }, + + { + "source": "file:mcp_server/__main__.py", + "target": "function:mcp_server/server.py:main", + "type": "calls", + "direction": "forward", + "weight": 0.8 + }, + { + "source": "class:tests/test_mcp_server.py:TestDispatch", + "target": "function:mcp_server/server.py:_dispatch", + "type": "calls", + "direction": "forward", + "weight": 0.8 + }, + { + "source": "class:tests/test_mcp_server.py:TestListBcs", + "target": "function:mcp_server/server.py:_list_bcs", + "type": "calls", + "direction": "forward", + "weight": 0.8 + }, + { + "source": "class:tests/test_mcp_server.py:TestGetBc", + "target": "function:mcp_server/server.py:_get_bc", + "type": "calls", + "direction": "forward", + "weight": 0.8 + }, + { + "source": "class:tests/test_mcp_server.py:TestExternalApiTools", + "target": "function:mcp_server/server.py:_search_ncit", + "type": "calls", + "direction": "forward", + "weight": 0.8 + }, + { + "source": "class:tests/test_mcp_server.py:TestExternalApiTools", + "target": "function:mcp_server/server.py:_get_ncit_concept", + "type": "calls", + "direction": "forward", + "weight": 0.8 + }, + { + "source": "class:tests/test_mcp_server.py:TestExternalApiTools", + "target": "function:mcp_server/server.py:_search_loinc", + "type": "calls", + "direction": "forward", + "weight": 0.8 + }, + { + "source": "class:tests/test_mcp_server.py:TestExternalApiTools", + "target": "function:mcp_server/server.py:_search_cdisc_library", + "type": "calls", + "direction": "forward", + "weight": 0.8 + }, + { + "source": "class:tests/test_mcp_server.py:TestExternalApiTools", + "target": "function:mcp_server/server.py:_get_library_bc", + "type": "calls", + "direction": "forward", + "weight": 0.8 + }, + { + "source": "class:tests/test_mcp_server.py:TestCreateBc", + "target": "function:mcp_server/server.py:_create_bc", + "type": "calls", + "direction": "forward", + "weight": 0.8 + }, + { + "source": "class:tests/test_mcp_server.py:TestUpdateBc", + "target": "function:mcp_server/server.py:_update_bc", + "type": "calls", + "direction": "forward", + "weight": 0.8 + }, + { + "source": "class:tests/test_mcp_server.py:TestMapNcit", + "target": "function:mcp_server/server.py:_map_ncit_to_bc", + "type": "calls", + "direction": "forward", + "weight": 0.8 + }, + { + "source": "class:tests/test_mcp_server.py:TestGovernanceWrites", + "target": "function:mcp_server/server.py:_submit_bc_for_review", + "type": "calls", + "direction": "forward", + "weight": 0.8 + }, + { + "source": "class:tests/test_mcp_server.py:TestGovernanceWrites", + "target": "function:mcp_server/server.py:_advance_governance", + "type": "calls", + "direction": "forward", + "weight": 0.8 + }, + { + "source": "class:tests/test_mcp_server.py:TestGovernanceWrites", + "target": "function:mcp_server/server.py:_reject_bc", + "type": "calls", + "direction": "forward", + "weight": 0.8 + }, + { + "source": "class:tests/test_mcp_server.py:TestReviewQueue", + "target": "function:mcp_server/server.py:_list_review_queue", + "type": "calls", + "direction": "forward", + "weight": 0.8 + }, + + { + "source": "file:mcp_server/server.py", + "target": "file:app.py", + "type": "depends_on", + "direction": "forward", + "weight": 0.6 + }, + { + "source": "file:mcp_server/server.py", + "target": "file:db_bootstrap.py", + "type": "depends_on", + "direction": "forward", + "weight": 0.6 + }, + { + "source": "file:mcp_server/server.py", + "target": "file:models/bc.py", + "type": "depends_on", + "direction": "forward", + "weight": 0.6 + }, + { + "source": "file:mcp_server/server.py", + "target": "file:models/governance.py", + "type": "depends_on", + "direction": "forward", + "weight": 0.6 + }, + { + "source": "file:mcp_server/server.py", + "target": "file:extensions.py", + "type": "depends_on", + "direction": "forward", + "weight": 0.6 + }, + { + "source": "file:mcp_server/server.py", + "target": "file:models/ingestion.py", + "type": "depends_on", + "direction": "forward", + "weight": 0.6 + }, + { + "source": "file:mcp_server/server.py", + "target": "file:services/ncit_api.py", + "type": "depends_on", + "direction": "forward", + "weight": 0.6 + }, + { + "source": "file:mcp_server/server.py", + "target": "file:services/loinc_api.py", + "type": "depends_on", + "direction": "forward", + "weight": 0.6 + }, + { + "source": "file:mcp_server/server.py", + "target": "file:services/cdisc_api.py", + "type": "depends_on", + "direction": "forward", + "weight": 0.6 + }, + { + "source": "file:mcp_server/server.py", + "target": "file:services/bc_service.py", + "type": "depends_on", + "direction": "forward", + "weight": 0.6 + }, + { + "source": "file:mcp_server/server.py", + "target": "file:services/governance_service.py", + "type": "depends_on", + "direction": "forward", + "weight": 0.6 + }, + + { + "source": "file:mcp_server/server.py", + "target": "file:tests/test_mcp_server.py", + "type": "tested_by", + "direction": "forward", + "weight": 0.5 + }, + { + "source": "file:routes/specializations.py", + "target": "file:tests/test_specializations_routes.py", + "type": "tested_by", + "direction": "forward", + "weight": 0.5 + } + ] +} diff --git a/.ua/.trash-1785851745/batch-6.json b/.ua/.trash-1785851745/batch-6.json new file mode 100644 index 0000000..8941777 --- /dev/null +++ b/.ua/.trash-1785851745/batch-6.json @@ -0,0 +1,15 @@ +{ + "nodes": [ + { + "id": "pipeline:.github/workflows/ci.yml", + "type": "pipeline", + "name": "ci.yml", + "filePath": ".github/workflows/ci.yml", + "summary": "GitHub Actions CI workflow that runs on every push and pull request across all branches: installs project and dev dependencies, lints with isort/black/flake8, and runs the pytest suite on Python 3.12.", + "tags": ["ci-cd", "deployment", "testing", "linting", "github-actions"], + "complexity": "simple", + "languageNotes": "Single-job workflow (`test`) with pip caching enabled via actions/setup-python; lint and test steps are separate shell steps within the same job rather than a matrix build." + } + ], + "edges": [] +} diff --git a/.ua/.trash-1785851745/batch-7.json b/.ua/.trash-1785851745/batch-7.json new file mode 100644 index 0000000..7f9270b --- /dev/null +++ b/.ua/.trash-1785851745/batch-7.json @@ -0,0 +1,164 @@ +{ + "nodes": [ + { + "id": "document:.claude/agent-memory/cdisc-frontend-dev/MEMORY.md", + "type": "document", + "name": "MEMORY.md", + "filePath": ".claude/agent-memory/cdisc-frontend-dev/MEMORY.md", + "summary": "Index file for the cdisc-frontend-dev sub-agent's persistent memory, linking to the project foundation and frontend architecture notes.", + "tags": ["documentation", "agent-memory", "index"], + "complexity": "simple" + }, + { + "id": "document:.claude/agent-memory/cdisc-frontend-dev/frontend_architecture.md", + "type": "document", + "name": "frontend_architecture.md", + "filePath": ".claude/agent-memory/cdisc-frontend-dev/frontend_architecture.md", + "summary": "Persistent agent memory documenting the front-end template hierarchy, custom.css class naming conventions, main.js module functions, template-to-route mapping, and Jinja context variable conventions for the CDISC BC Curation UI.", + "tags": [ + "documentation", + "frontend", + "css", + "javascript", + "agent-memory" + ], + "complexity": "moderate" + }, + { + "id": "document:.claude/agent-memory/cdisc-frontend-dev/project_foundation.md", + "type": "document", + "name": "project_foundation.md", + "filePath": ".claude/agent-memory/cdisc-frontend-dev/project_foundation.md", + "summary": "Persistent agent memory describing the Flask application factory pattern, registered blueprints, model and service locations, database setup, and key route implementation decisions for the CDISC BC Curation app.", + "tags": [ + "documentation", + "architecture", + "flask", + "database", + "agent-memory" + ], + "complexity": "moderate" + } + ], + "edges": [ + { + "source": "document:.claude/agent-memory/cdisc-frontend-dev/MEMORY.md", + "target": "document:.claude/agent-memory/cdisc-frontend-dev/project_foundation.md", + "type": "related", + "direction": "forward", + "weight": 0.5 + }, + { + "source": "document:.claude/agent-memory/cdisc-frontend-dev/MEMORY.md", + "target": "document:.claude/agent-memory/cdisc-frontend-dev/frontend_architecture.md", + "type": "related", + "direction": "forward", + "weight": 0.5 + }, + { + "source": "document:.claude/agent-memory/cdisc-frontend-dev/frontend_architecture.md", + "target": "file:templates/base.html", + "type": "documents", + "direction": "forward", + "weight": 0.5 + }, + { + "source": "document:.claude/agent-memory/cdisc-frontend-dev/frontend_architecture.md", + "target": "file:static/js/main.js", + "type": "documents", + "direction": "forward", + "weight": 0.5 + }, + { + "source": "document:.claude/agent-memory/cdisc-frontend-dev/frontend_architecture.md", + "target": "file:static/css/custom.css", + "type": "documents", + "direction": "forward", + "weight": 0.5 + }, + { + "source": "document:.claude/agent-memory/cdisc-frontend-dev/project_foundation.md", + "target": "file:app.py", + "type": "documents", + "direction": "forward", + "weight": 0.5 + }, + { + "source": "document:.claude/agent-memory/cdisc-frontend-dev/project_foundation.md", + "target": "file:extensions.py", + "type": "documents", + "direction": "forward", + "weight": 0.5 + }, + { + "source": "document:.claude/agent-memory/cdisc-frontend-dev/project_foundation.md", + "target": "file:models/bc.py", + "type": "documents", + "direction": "forward", + "weight": 0.5 + }, + { + "source": "document:.claude/agent-memory/cdisc-frontend-dev/project_foundation.md", + "target": "file:models/specialization.py", + "type": "documents", + "direction": "forward", + "weight": 0.5 + }, + { + "source": "document:.claude/agent-memory/cdisc-frontend-dev/project_foundation.md", + "target": "file:models/governance.py", + "type": "documents", + "direction": "forward", + "weight": 0.5 + }, + { + "source": "document:.claude/agent-memory/cdisc-frontend-dev/project_foundation.md", + "target": "file:models/audit.py", + "type": "documents", + "direction": "forward", + "weight": 0.5 + }, + { + "source": "document:.claude/agent-memory/cdisc-frontend-dev/project_foundation.md", + "target": "file:models/ingestion.py", + "type": "documents", + "direction": "forward", + "weight": 0.5 + }, + { + "source": "document:.claude/agent-memory/cdisc-frontend-dev/project_foundation.md", + "target": "file:services/ingestion.py", + "type": "documents", + "direction": "forward", + "weight": 0.5 + }, + { + "source": "document:.claude/agent-memory/cdisc-frontend-dev/project_foundation.md", + "target": "file:services/ncit_api.py", + "type": "documents", + "direction": "forward", + "weight": 0.5 + }, + { + "source": "document:.claude/agent-memory/cdisc-frontend-dev/project_foundation.md", + "target": "file:services/loinc_api.py", + "type": "documents", + "direction": "forward", + "weight": 0.5 + }, + { + "source": "document:.claude/agent-memory/cdisc-frontend-dev/project_foundation.md", + "target": "file:services/export.py", + "type": "documents", + "direction": "forward", + "weight": 0.5 + }, + { + "source": "document:.claude/agent-memory/cdisc-frontend-dev/project_foundation.md", + "target": "file:services/cdisc_api.py", + "type": "documents", + "direction": "forward", + "weight": 0.5 + } + ] +} diff --git a/.ua/.trash-1785851745/batch-8.json b/.ua/.trash-1785851745/batch-8.json new file mode 100644 index 0000000..e80337e --- /dev/null +++ b/.ua/.trash-1785851745/batch-8.json @@ -0,0 +1,66 @@ +{ + "nodes": [ + { + "id": "document:.claude/agents/cdisc-concept-explorer.md", + "type": "document", + "name": "cdisc-concept-explorer.md", + "filePath": ".claude/agents/cdisc-concept-explorer.md", + "summary": "Defines the cdisc-concept-explorer Claude subagent, a CDISC standards specialist that queries the live CDISC Library API to detect duplicate Biomedical Concepts against local drafts, enrich drafts with authoritative metadata, and find dataset specializations for a given BC.", + "tags": [ + "agent-definition", + "documentation", + "cdisc-library", + "api-integration", + "subagent-spec" + ], + "complexity": "simple" + }, + { + "id": "document:.claude/agents/cdisc-frontend-dev.md", + "type": "document", + "name": "cdisc-frontend-dev.md", + "filePath": ".claude/agents/cdisc-frontend-dev.md", + "summary": "Defines the cdisc-frontend-dev Claude subagent, a front-end specialist scoped to Jinja2 templates, CSS, vanilla JavaScript, and Flask view wiring for the BC curation tool, with mandatory CDISC brand-guideline compliance and an extensive persistent project-memory protocol appended to the spec.", + "tags": [ + "agent-definition", + "documentation", + "frontend", + "memory-system", + "brand-guidelines" + ], + "complexity": "moderate", + "languageNotes": "The file embeds a large boilerplate 'Persistent Agent Memory' instructional block (types of memory, save/access rules) alongside the domain-specific agent spec, roughly doubling its length beyond the front-end guidance itself." + }, + { + "id": "document:.claude/agents/mike.md", + "type": "document", + "name": "mike.md", + "filePath": ".claude/agents/mike.md", + "summary": "Defines the mike Claude subagent, a project-manager assistant for cdisc-concept-curation that tracks daily progress in README-PROGRESS.md, prepares git commit messages, flags documentation drift against the code, and always asks before committing or pushing.", + "tags": [ + "agent-definition", + "documentation", + "project-management", + "git-workflow", + "progress-tracking" + ], + "complexity": "simple" + } + ], + "edges": [ + { + "source": "document:.claude/agents/mike.md", + "target": "document:.claude/agents/cdisc-frontend-dev.md", + "type": "related", + "direction": "forward", + "weight": 0.5 + }, + { + "source": "document:.claude/agents/mike.md", + "target": "document:.claude/agents/cdisc-concept-explorer.md", + "type": "related", + "direction": "forward", + "weight": 0.5 + } + ] +} diff --git a/.ua/.trash-1785851745/batch-9.json b/.ua/.trash-1785851745/batch-9.json new file mode 100644 index 0000000..3292344 --- /dev/null +++ b/.ua/.trash-1785851745/batch-9.json @@ -0,0 +1,127 @@ +{ + "nodes": [ + { + "id": "config:.mcp.json", + "type": "config", + "name": ".mcp.json", + "filePath": ".mcp.json", + "summary": "Registers the cdisc-curation MCP server, launching it via the project virtualenv's Python interpreter running the `mcp_server` package as a module.", + "tags": ["configuration", "mcp", "server-registration"], + "complexity": "simple" + }, + { + "id": "config:.pre-commit-config.yaml", + "type": "config", + "name": ".pre-commit-config.yaml", + "filePath": ".pre-commit-config.yaml", + "summary": "Pre-commit hook configuration that runs isort, black, and flake8 on staged files and executes the full pytest suite before every commit.", + "tags": ["configuration", "ci-cd", "linting", "build-system"], + "complexity": "simple" + }, + { + "id": "document:CLAUDE.md", + "type": "document", + "name": "CLAUDE.md", + "filePath": "CLAUDE.md", + "summary": "Guidance document for Claude Code covering the app's purpose, how to run it, linting and testing commands, the Route-Service-Model architecture with its 8 Flask blueprints and key services, the MCP server, and environment-variable configuration.", + "tags": ["documentation", "architecture", "entry-point", "development"], + "complexity": "moderate" + }, + { + "id": "document:README-PROGRESS.md", + "type": "document", + "name": "README-PROGRESS.md", + "filePath": "README-PROGRESS.md", + "summary": "Running project progress log tracking feature status and a dated changelog of fixes and workstream completions such as governance export corrections, audit trail coverage, and LOINC API explorer work.", + "tags": ["documentation", "changelog", "project-status"], + "complexity": "moderate" + }, + { + "id": "document:README.md", + "type": "document", + "name": "README.md", + "filePath": "README.md", + "summary": "Primary project README covering prerequisites, installation, environment-variable configuration, running the app and tests, key screens, project structure, and the external CDISC/NCIt/LOINC APIs the platform integrates with.", + "tags": ["documentation", "entry-point", "installation", "overview"], + "complexity": "moderate" + }, + { + "id": "config:pyproject.toml", + "type": "config", + "name": "pyproject.toml", + "filePath": "pyproject.toml", + "summary": "Tool configuration setting a shared 200-character line length for black (targeting Python 3.9-3.11) and isort (using the black-compatible profile).", + "tags": ["configuration", "build-system", "formatting"], + "complexity": "simple" + }, + { + "id": "document:requirements-dev.txt", + "type": "document", + "name": "requirements-dev.txt", + "filePath": "requirements-dev.txt", + "summary": "Pinned development-only dependency list: black, flake8 (with mccabe/pycodestyle/pyflakes), isort, pre-commit, pytest, and pytest-flask, used for linting and testing.", + "tags": ["dependencies", "development", "linting", "testing"], + "complexity": "simple" + }, + { + "id": "document:requirements.txt", + "type": "document", + "name": "requirements.txt", + "filePath": "requirements.txt", + "summary": "Pinned runtime dependency list for the Flask application, including Flask, Flask-SQLAlchemy, Flask-Migrate, SQLAlchemy, Alembic, pandas, openpyxl, lxml, requests, and the mcp SDK.", + "tags": ["dependencies", "runtime", "build-system"], + "complexity": "simple" + } + ], + "edges": [ + { + "source": "document:README.md", + "target": "document:requirements.txt", + "type": "documents", + "direction": "forward", + "weight": 0.5 + }, + { + "source": "document:README.md", + "target": "config:.pre-commit-config.yaml", + "type": "documents", + "direction": "forward", + "weight": 0.5 + }, + { + "source": "document:README.md", + "target": "config:pyproject.toml", + "type": "documents", + "direction": "forward", + "weight": 0.5 + }, + { + "source": "config:.pre-commit-config.yaml", + "target": "config:pyproject.toml", + "type": "depends_on", + "direction": "forward", + "weight": 0.6 + }, + { + "source": "document:requirements-dev.txt", + "target": "document:requirements.txt", + "type": "related", + "direction": "forward", + "weight": 0.5 + }, + { + "source": "document:README.md", + "target": "document:CLAUDE.md", + "type": "related", + "direction": "forward", + "weight": 0.5 + }, + { + "source": "document:README.md", + "target": "document:README-PROGRESS.md", + "type": "related", + "direction": "forward", + "weight": 0.5 + } + ] +} diff --git a/.ua/.trash-1785851745/batches.json b/.ua/.trash-1785851745/batches.json new file mode 100644 index 0000000..1a3f402 --- /dev/null +++ b/.ua/.trash-1785851745/batches.json @@ -0,0 +1,2213 @@ +{ + "schemaVersion": 1, + "algorithm": "louvain", + "totalFiles": 97, + "totalBatches": 12, + "exportsByPath": { + ".flake8": [], + ".hintrc": [], + ".ua/.understandignore": [], + "app.py": [ + "_configure_logging", + "create_app" + ], + "config.py": [ + "Config" + ], + "db_bootstrap.py": [ + "_schema_is_current", + "ensure_db" + ], + "extensions.py": [ + "_set_sqlite_pragmas" + ], + "files/BC Curation Principles and Completion GLs.xlsx": [], + "files/BC DEC Templates.xlsx": [], + "files/BC Examples.xlsx": [], + "files/BC Governance.docx": [], + "mcp_server/__init__.py": [], + "mcp_server/__main__.py": [], + "mcp_server/server.py": [ + "_get_app", + "_with_app_context", + "list_tools", + "call_tool", + "_dispatch", + "_bc_full_dict", + "_list_bcs", + "_get_bc", + "_search_ncit", + "_get_ncit_concept", + "_search_loinc", + "_search_cdisc_library", + "_get_library_bc", + "_list_review_queue", + "_create_bc", + "_update_bc", + "_map_ncit_to_bc", + "_submit_bc_for_review", + "_advance_governance", + "_reject_bc", + "main", + "_run" + ], + "migrations/README": [], + "migrations/env.py": [ + "get_engine", + "get_engine_url", + "get_metadata", + "run_migrations_offline", + "run_migrations_online" + ], + "migrations/script.py.mako": [], + "migrations/versions/51d4a009d291_baseline_initial_schema.py": [ + "upgrade", + "downgrade" + ], + "models/__init__.py": [], + "models/audit.py": [ + "AuditLog" + ], + "models/bc.py": [ + "BiomedicalConcept", + "DataElementConcept" + ], + "models/governance.py": [ + "GovernanceRecord" + ], + "models/ingestion.py": [ + "IngestionRecord" + ], + "models/specialization.py": [ + "DatasetSpecialization" + ], + "routes/__init__.py": [], + "routes/audit.py": [ + "index" + ], + "routes/bc.py": [ + "index", + "new_bc", + "export", + "library_detail", + "detail", + "fetch_metadata", + "create", + "edit", + "clear_ncit", + "clear_loinc", + "submit_for_review", + "delete", + "_decs_from_form" + ], + "routes/dashboard.py": [ + "index" + ], + "routes/governance.py": [ + "board", + "export", + "advance", + "reject_bc" + ], + "routes/ingestion.py": [ + "_allowed_file", + "_get_session_key", + "_bc_from_mapped", + "_create_decs", + "index", + "upload", + "approve", + "reject", + "approve_all" + ], + "routes/loinc.py": [ + "search" + ], + "routes/ncit.py": [ + "index", + "mapping", + "search_ncit", + "concept_detail", + "resolve" + ], + "routes/specializations.py": [ + "_get_bc_options", + "index", + "library_detail", + "detail", + "_variables_from_form", + "create", + "delete", + "generate_from_dec", + "generate" + ], + "services/__init__.py": [], + "services/api_cache.py": [ + "cached" + ], + "services/audit.py": [ + "log_change" + ], + "services/bc_service.py": [ + "NotFoundError", + "_get_bc_or_raise", + "apply_bc_fields", + "create_bc", + "update_bc", + "save_decs", + "map_ncit_to_bc", + "submit_bc_for_review" + ], + "services/cdisc_api.py": [ + "_config_value", + "CDISCApiClient" + ], + "services/export.py": [ + "export_json", + "export_xlsx", + "export_governance_xlsx", + "export_odm_xml" + ], + "services/governance_service.py": [ + "advance_governance", + "reject_bc" + ], + "services/ingestion.py": [ + "_similarity", + "_match_field", + "map_fields", + "validate_bc", + "_group_by_bc", + "parse_xlsx", + "parse_csv", + "parse_json", + "deduplicate" + ], + "services/loinc_api.py": [ + "LoincApiClient" + ], + "services/ncit_api.py": [ + "_pick_definition", + "NCItApiClient" + ], + "static/js/main.js": [], + "tests/__init__.py": [], + "tests/conftest.py": [ + "TestConfig", + "app", + "client", + "clean_db", + "clear_api_cache", + "sample_bc" + ], + "tests/test_audit_routes.py": [ + "_add_log", + "TestAuditIndex" + ], + "tests/test_bc_routes.py": [ + "_bc_form", + "TestBcIndex", + "TestNewBc", + "TestCreateBc", + "TestBcDetail", + "TestEditBc", + "TestClearNcitCode", + "TestClearLoincCode", + "TestSubmitForReview", + "TestDeleteBc", + "TestExport", + "TestLibraryDetail" + ], + "tests/test_cdisc_api_cache.py": [ + "TestCachedHelper", + "TestClientCacheKeys" + ], + "tests/test_dashboard.py": [ + "_mock_client", + "TestDashboardIndex" + ], + "tests/test_db_bootstrap.py": [ + "_make_app", + "_tables", + "_stamped_revision", + "TestEnsureDb" + ], + "tests/test_export_service.py": [ + "TestExportJson", + "TestExportXlsx", + "TestExportGovernanceXlsx", + "TestExportOdmXml" + ], + "tests/test_governance_routes.py": [ + "TestGovernanceBoard", + "TestAdvance", + "TestReject", + "TestGovernanceExport" + ], + "tests/test_ingestion_routes.py": [ + "_csv_file", + "_json_file", + "TestIngestionIndex", + "TestUpload", + "TestApprove", + "TestReject", + "TestApproveAll" + ], + "tests/test_ingestion_service.py": [ + "TestSimilarity", + "TestMatchField", + "TestMapFields", + "TestValidateBc", + "TestDeduplicate", + "TestParseCsv", + "TestParseJson", + "TestGroupByBc" + ], + "tests/test_loinc.py": [ + "TestLoincApiClientSearch", + "TestLoincSearchRoute" + ], + "tests/test_mcp_server.py": [ + "inject_test_app", + "_dispatch", + "TestDispatch", + "TestListBcs", + "TestGetBc", + "TestExternalApiTools", + "_audit_rows", + "TestCreateBc", + "TestUpdateBc", + "TestMapNcit", + "TestGovernanceWrites", + "TestReviewQueue" + ], + "tests/test_models.py": [ + "TestAuditLogJsonProperties", + "TestIngestionRecordProperties", + "TestBiomedicalConceptToDict" + ], + "tests/test_ncit.py": [ + "TestNcitGetConceptExtended", + "TestNcitConceptRoute" + ], + "tests/test_specializations_routes.py": [ + "_patch_client", + "TestIndex", + "TestCreate", + "TestEditUpsert", + "TestDelete", + "TestDetail", + "TestLibraryDetail", + "TestGenerate", + "TestGenerateFromDec" + ] + }, + "batches": [ + { + "batchIndex": 1, + "files": [ + { + "path": "models/audit.py", + "language": "python", + "sizeLines": 32, + "fileCategory": "code" + }, + { + "path": "models/bc.py", + "language": "python", + "sizeLines": 65, + "fileCategory": "code" + }, + { + "path": "models/ingestion.py", + "language": "python", + "sizeLines": 56, + "fileCategory": "code" + }, + { + "path": "routes/audit.py", + "language": "python", + "sizeLines": 44, + "fileCategory": "code" + }, + { + "path": "routes/ingestion.py", + "language": "python", + "sizeLines": 186, + "fileCategory": "code" + }, + { + "path": "services/ingestion.py", + "language": "python", + "sizeLines": 227, + "fileCategory": "code" + }, + { + "path": "tests/conftest.py", + "language": "python", + "sizeLines": 68, + "fileCategory": "code" + }, + { + "path": "tests/test_audit_routes.py", + "language": "python", + "sizeLines": 49, + "fileCategory": "code" + }, + { + "path": "tests/test_bc_routes.py", + "language": "python", + "sizeLines": 547, + "fileCategory": "code" + }, + { + "path": "tests/test_governance_routes.py", + "language": "python", + "sizeLines": 236, + "fileCategory": "code" + }, + { + "path": "tests/test_ingestion_routes.py", + "language": "python", + "sizeLines": 204, + "fileCategory": "code" + }, + { + "path": "tests/test_ingestion_service.py", + "language": "python", + "sizeLines": 259, + "fileCategory": "code" + }, + { + "path": "tests/test_models.py", + "language": "python", + "sizeLines": 116, + "fileCategory": "code" + } + ], + "batchImportData": { + "models/audit.py": [ + "extensions.py" + ], + "models/bc.py": [ + "extensions.py" + ], + "models/ingestion.py": [ + "extensions.py" + ], + "routes/audit.py": [ + "models/audit.py" + ], + "routes/ingestion.py": [ + "extensions.py", + "models/audit.py", + "models/bc.py", + "models/ingestion.py", + "services/ingestion.py" + ], + "services/ingestion.py": [], + "tests/conftest.py": [ + "app.py", + "extensions.py", + "models/audit.py", + "models/bc.py", + "models/governance.py", + "models/ingestion.py" + ], + "tests/test_audit_routes.py": [ + "extensions.py", + "models/audit.py" + ], + "tests/test_bc_routes.py": [ + "extensions.py", + "models/audit.py", + "models/bc.py" + ], + "tests/test_governance_routes.py": [ + "extensions.py", + "models/audit.py", + "models/bc.py", + "models/governance.py" + ], + "tests/test_ingestion_routes.py": [ + "extensions.py", + "models/audit.py", + "models/bc.py", + "models/ingestion.py" + ], + "tests/test_ingestion_service.py": [ + "services/ingestion.py" + ], + "tests/test_models.py": [ + "extensions.py", + "models/audit.py", + "models/bc.py", + "models/ingestion.py" + ] + }, + "neighborMap": { + "models/audit.py": [ + { + "path": "extensions.py", + "batchIndex": 4, + "symbols": [ + "_set_sqlite_pragmas" + ] + }, + { + "path": "routes/dashboard.py", + "batchIndex": 3, + "symbols": [ + "index" + ] + }, + { + "path": "services/audit.py", + "batchIndex": 2, + "symbols": [ + "log_change" + ] + }, + { + "path": "tests/test_mcp_server.py", + "batchIndex": 5, + "symbols": [ + "inject_test_app", + "_dispatch", + "TestDispatch", + "TestListBcs", + "TestGetBc", + "TestExternalApiTools", + "_audit_rows", + "TestCreateBc", + "TestUpdateBc", + "TestMapNcit", + "TestGovernanceWrites", + "TestReviewQueue" + ] + }, + { + "path": "tests/test_specializations_routes.py", + "batchIndex": 5, + "symbols": [ + "_patch_client", + "TestIndex", + "TestCreate", + "TestEditUpsert", + "TestDelete", + "TestDetail", + "TestLibraryDetail", + "TestGenerate", + "TestGenerateFromDec" + ] + } + ], + "models/bc.py": [ + { + "path": "extensions.py", + "batchIndex": 4, + "symbols": [ + "_set_sqlite_pragmas" + ] + }, + { + "path": "routes/bc.py", + "batchIndex": 2, + "symbols": [ + "index", + "new_bc", + "export", + "library_detail", + "detail", + "fetch_metadata", + "create", + "edit", + "clear_ncit", + "clear_loinc", + "submit_for_review", + "delete", + "_decs_from_form" + ] + }, + { + "path": "routes/dashboard.py", + "batchIndex": 3, + "symbols": [ + "index" + ] + }, + { + "path": "routes/governance.py", + "batchIndex": 2, + "symbols": [ + "board", + "export", + "advance", + "reject_bc" + ] + }, + { + "path": "routes/ncit.py", + "batchIndex": 2, + "symbols": [ + "index", + "mapping", + "search_ncit", + "concept_detail", + "resolve" + ] + }, + { + "path": "routes/specializations.py", + "batchIndex": 3, + "symbols": [ + "_get_bc_options", + "index", + "library_detail", + "detail", + "_variables_from_form", + "create", + "delete", + "generate_from_dec", + "generate" + ] + }, + { + "path": "services/bc_service.py", + "batchIndex": 2, + "symbols": [ + "NotFoundError", + "_get_bc_or_raise", + "apply_bc_fields", + "create_bc", + "update_bc", + "save_decs", + "map_ncit_to_bc", + "submit_bc_for_review" + ] + }, + { + "path": "tests/test_dashboard.py", + "batchIndex": 4, + "symbols": [ + "_mock_client", + "TestDashboardIndex" + ] + }, + { + "path": "tests/test_export_service.py", + "batchIndex": 2, + "symbols": [ + "TestExportJson", + "TestExportXlsx", + "TestExportGovernanceXlsx", + "TestExportOdmXml" + ] + }, + { + "path": "tests/test_mcp_server.py", + "batchIndex": 5, + "symbols": [ + "inject_test_app", + "_dispatch", + "TestDispatch", + "TestListBcs", + "TestGetBc", + "TestExternalApiTools", + "_audit_rows", + "TestCreateBc", + "TestUpdateBc", + "TestMapNcit", + "TestGovernanceWrites", + "TestReviewQueue" + ] + }, + { + "path": "tests/test_specializations_routes.py", + "batchIndex": 5, + "symbols": [ + "_patch_client", + "TestIndex", + "TestCreate", + "TestEditUpsert", + "TestDelete", + "TestDetail", + "TestLibraryDetail", + "TestGenerate", + "TestGenerateFromDec" + ] + } + ], + "models/ingestion.py": [ + { + "path": "extensions.py", + "batchIndex": 4, + "symbols": [ + "_set_sqlite_pragmas" + ] + }, + { + "path": "tests/test_mcp_server.py", + "batchIndex": 5, + "symbols": [ + "inject_test_app", + "_dispatch", + "TestDispatch", + "TestListBcs", + "TestGetBc", + "TestExternalApiTools", + "_audit_rows", + "TestCreateBc", + "TestUpdateBc", + "TestMapNcit", + "TestGovernanceWrites", + "TestReviewQueue" + ] + } + ], + "routes/ingestion.py": [ + { + "path": "extensions.py", + "batchIndex": 4, + "symbols": [ + "_set_sqlite_pragmas" + ] + } + ], + "tests/conftest.py": [ + { + "path": "app.py", + "batchIndex": 4, + "symbols": [ + "_configure_logging", + "create_app" + ] + }, + { + "path": "extensions.py", + "batchIndex": 4, + "symbols": [ + "_set_sqlite_pragmas" + ] + }, + { + "path": "models/governance.py", + "batchIndex": 2, + "symbols": [ + "GovernanceRecord" + ] + } + ], + "tests/test_audit_routes.py": [ + { + "path": "extensions.py", + "batchIndex": 4, + "symbols": [ + "_set_sqlite_pragmas" + ] + } + ], + "tests/test_bc_routes.py": [ + { + "path": "extensions.py", + "batchIndex": 4, + "symbols": [ + "_set_sqlite_pragmas" + ] + } + ], + "tests/test_governance_routes.py": [ + { + "path": "extensions.py", + "batchIndex": 4, + "symbols": [ + "_set_sqlite_pragmas" + ] + }, + { + "path": "models/governance.py", + "batchIndex": 2, + "symbols": [ + "GovernanceRecord" + ] + } + ], + "tests/test_ingestion_routes.py": [ + { + "path": "extensions.py", + "batchIndex": 4, + "symbols": [ + "_set_sqlite_pragmas" + ] + } + ], + "tests/test_models.py": [ + { + "path": "extensions.py", + "batchIndex": 4, + "symbols": [ + "_set_sqlite_pragmas" + ] + } + ] + } + }, + { + "batchIndex": 2, + "files": [ + { + "path": "models/governance.py", + "language": "python", + "sizeLines": 16, + "fileCategory": "code" + }, + { + "path": "routes/bc.py", + "language": "python", + "sizeLines": 276, + "fileCategory": "code" + }, + { + "path": "routes/governance.py", + "language": "python", + "sizeLines": 68, + "fileCategory": "code" + }, + { + "path": "routes/ncit.py", + "language": "python", + "sizeLines": 83, + "fileCategory": "code" + }, + { + "path": "services/__init__.py", + "language": "python", + "sizeLines": 0, + "fileCategory": "code" + }, + { + "path": "services/audit.py", + "language": "python", + "sizeLines": 32, + "fileCategory": "code" + }, + { + "path": "services/bc_service.py", + "language": "python", + "sizeLines": 159, + "fileCategory": "code" + }, + { + "path": "services/export.py", + "language": "python", + "sizeLines": 185, + "fileCategory": "code" + }, + { + "path": "services/governance_service.py", + "language": "python", + "sizeLines": 60, + "fileCategory": "code" + }, + { + "path": "services/ncit_api.py", + "language": "python", + "sizeLines": 85, + "fileCategory": "code" + }, + { + "path": "tests/test_export_service.py", + "language": "python", + "sizeLines": 207, + "fileCategory": "code" + }, + { + "path": "tests/test_ncit.py", + "language": "python", + "sizeLines": 156, + "fileCategory": "code" + } + ], + "batchImportData": { + "models/governance.py": [ + "extensions.py" + ], + "routes/bc.py": [ + "extensions.py", + "models/bc.py", + "models/governance.py", + "services/__init__.py", + "services/audit.py", + "services/bc_service.py", + "services/cdisc_api.py", + "services/export.py", + "services/loinc_api.py", + "services/ncit_api.py" + ], + "routes/governance.py": [ + "extensions.py", + "models/bc.py", + "services/__init__.py", + "services/export.py", + "services/governance_service.py" + ], + "routes/ncit.py": [ + "extensions.py", + "models/bc.py", + "services/__init__.py", + "services/bc_service.py", + "services/ncit_api.py" + ], + "services/__init__.py": [], + "services/audit.py": [ + "extensions.py", + "models/audit.py" + ], + "services/bc_service.py": [ + "extensions.py", + "models/bc.py", + "models/governance.py", + "models/specialization.py", + "services/audit.py" + ], + "services/export.py": [], + "services/governance_service.py": [ + "extensions.py", + "models/governance.py", + "services/audit.py", + "services/bc_service.py" + ], + "services/ncit_api.py": [ + "services/api_cache.py" + ], + "tests/test_export_service.py": [ + "extensions.py", + "models/bc.py", + "services/export.py" + ], + "tests/test_ncit.py": [ + "services/ncit_api.py" + ] + }, + "neighborMap": { + "models/governance.py": [ + { + "path": "extensions.py", + "batchIndex": 4, + "symbols": [ + "_set_sqlite_pragmas" + ] + }, + { + "path": "tests/conftest.py", + "batchIndex": 1, + "symbols": [ + "TestConfig", + "app", + "client", + "clean_db", + "clear_api_cache", + "sample_bc" + ] + }, + { + "path": "tests/test_governance_routes.py", + "batchIndex": 1, + "symbols": [ + "TestGovernanceBoard", + "TestAdvance", + "TestReject", + "TestGovernanceExport" + ] + }, + { + "path": "tests/test_mcp_server.py", + "batchIndex": 5, + "symbols": [ + "inject_test_app", + "_dispatch", + "TestDispatch", + "TestListBcs", + "TestGetBc", + "TestExternalApiTools", + "_audit_rows", + "TestCreateBc", + "TestUpdateBc", + "TestMapNcit", + "TestGovernanceWrites", + "TestReviewQueue" + ] + } + ], + "routes/bc.py": [ + { + "path": "extensions.py", + "batchIndex": 4, + "symbols": [ + "_set_sqlite_pragmas" + ] + }, + { + "path": "models/bc.py", + "batchIndex": 1, + "symbols": [ + "BiomedicalConcept", + "DataElementConcept" + ] + }, + { + "path": "services/cdisc_api.py", + "batchIndex": 3, + "symbols": [ + "_config_value", + "CDISCApiClient" + ] + }, + { + "path": "services/loinc_api.py", + "batchIndex": 3, + "symbols": [ + "LoincApiClient" + ] + } + ], + "routes/governance.py": [ + { + "path": "extensions.py", + "batchIndex": 4, + "symbols": [ + "_set_sqlite_pragmas" + ] + }, + { + "path": "models/bc.py", + "batchIndex": 1, + "symbols": [ + "BiomedicalConcept", + "DataElementConcept" + ] + } + ], + "routes/ncit.py": [ + { + "path": "extensions.py", + "batchIndex": 4, + "symbols": [ + "_set_sqlite_pragmas" + ] + }, + { + "path": "models/bc.py", + "batchIndex": 1, + "symbols": [ + "BiomedicalConcept", + "DataElementConcept" + ] + } + ], + "services/audit.py": [ + { + "path": "extensions.py", + "batchIndex": 4, + "symbols": [ + "_set_sqlite_pragmas" + ] + }, + { + "path": "models/audit.py", + "batchIndex": 1, + "symbols": [ + "AuditLog" + ] + }, + { + "path": "routes/specializations.py", + "batchIndex": 3, + "symbols": [ + "_get_bc_options", + "index", + "library_detail", + "detail", + "_variables_from_form", + "create", + "delete", + "generate_from_dec", + "generate" + ] + } + ], + "services/bc_service.py": [ + { + "path": "extensions.py", + "batchIndex": 4, + "symbols": [ + "_set_sqlite_pragmas" + ] + }, + { + "path": "models/bc.py", + "batchIndex": 1, + "symbols": [ + "BiomedicalConcept", + "DataElementConcept" + ] + }, + { + "path": "models/specialization.py", + "batchIndex": 5, + "symbols": [ + "DatasetSpecialization" + ] + } + ], + "services/governance_service.py": [ + { + "path": "extensions.py", + "batchIndex": 4, + "symbols": [ + "_set_sqlite_pragmas" + ] + } + ], + "services/ncit_api.py": [ + { + "path": "services/api_cache.py", + "batchIndex": 3, + "symbols": [ + "cached" + ] + } + ], + "tests/test_export_service.py": [ + { + "path": "extensions.py", + "batchIndex": 4, + "symbols": [ + "_set_sqlite_pragmas" + ] + }, + { + "path": "models/bc.py", + "batchIndex": 1, + "symbols": [ + "BiomedicalConcept", + "DataElementConcept" + ] + } + ] + } + }, + { + "batchIndex": 3, + "files": [ + { + "path": "routes/dashboard.py", + "language": "python", + "sizeLines": 60, + "fileCategory": "code" + }, + { + "path": "routes/loinc.py", + "language": "python", + "sizeLines": 23, + "fileCategory": "code" + }, + { + "path": "routes/specializations.py", + "language": "python", + "sizeLines": 172, + "fileCategory": "code" + }, + { + "path": "services/api_cache.py", + "language": "python", + "sizeLines": 42, + "fileCategory": "code" + }, + { + "path": "services/cdisc_api.py", + "language": "python", + "sizeLines": 127, + "fileCategory": "code" + }, + { + "path": "services/loinc_api.py", + "language": "python", + "sizeLines": 62, + "fileCategory": "code" + }, + { + "path": "tests/test_cdisc_api_cache.py", + "language": "python", + "sizeLines": 120, + "fileCategory": "code" + }, + { + "path": "tests/test_loinc.py", + "language": "python", + "sizeLines": 205, + "fileCategory": "code" + } + ], + "batchImportData": { + "routes/dashboard.py": [ + "models/audit.py", + "models/bc.py", + "services/cdisc_api.py" + ], + "routes/loinc.py": [ + "services/loinc_api.py" + ], + "routes/specializations.py": [ + "extensions.py", + "models/bc.py", + "models/specialization.py", + "services/audit.py", + "services/cdisc_api.py" + ], + "services/api_cache.py": [], + "services/cdisc_api.py": [ + "services/api_cache.py" + ], + "services/loinc_api.py": [ + "services/api_cache.py" + ], + "tests/test_cdisc_api_cache.py": [ + "services/cdisc_api.py" + ], + "tests/test_loinc.py": [ + "services/loinc_api.py" + ] + }, + "neighborMap": { + "routes/dashboard.py": [ + { + "path": "models/audit.py", + "batchIndex": 1, + "symbols": [ + "AuditLog" + ] + }, + { + "path": "models/bc.py", + "batchIndex": 1, + "symbols": [ + "BiomedicalConcept", + "DataElementConcept" + ] + } + ], + "routes/specializations.py": [ + { + "path": "extensions.py", + "batchIndex": 4, + "symbols": [ + "_set_sqlite_pragmas" + ] + }, + { + "path": "models/bc.py", + "batchIndex": 1, + "symbols": [ + "BiomedicalConcept", + "DataElementConcept" + ] + }, + { + "path": "models/specialization.py", + "batchIndex": 5, + "symbols": [ + "DatasetSpecialization" + ] + }, + { + "path": "services/audit.py", + "batchIndex": 2, + "symbols": [ + "log_change" + ] + } + ], + "services/api_cache.py": [ + { + "path": "services/ncit_api.py", + "batchIndex": 2, + "symbols": [ + "_pick_definition", + "NCItApiClient" + ] + } + ], + "services/cdisc_api.py": [ + { + "path": "routes/bc.py", + "batchIndex": 2, + "symbols": [ + "index", + "new_bc", + "export", + "library_detail", + "detail", + "fetch_metadata", + "create", + "edit", + "clear_ncit", + "clear_loinc", + "submit_for_review", + "delete", + "_decs_from_form" + ] + } + ], + "services/loinc_api.py": [ + { + "path": "routes/bc.py", + "batchIndex": 2, + "symbols": [ + "index", + "new_bc", + "export", + "library_detail", + "detail", + "fetch_metadata", + "create", + "edit", + "clear_ncit", + "clear_loinc", + "submit_for_review", + "delete", + "_decs_from_form" + ] + } + ] + } + }, + { + "batchIndex": 4, + "files": [ + { + "path": "app.py", + "language": "python", + "sizeLines": 59, + "fileCategory": "code" + }, + { + "path": "config.py", + "language": "python", + "sizeLines": 14, + "fileCategory": "code" + }, + { + "path": "db_bootstrap.py", + "language": "python", + "sizeLines": 71, + "fileCategory": "code" + }, + { + "path": "extensions.py", + "language": "python", + "sizeLines": 25, + "fileCategory": "code" + }, + { + "path": "models/__init__.py", + "language": "python", + "sizeLines": 1, + "fileCategory": "code" + }, + { + "path": "tests/test_dashboard.py", + "language": "python", + "sizeLines": 75, + "fileCategory": "code" + }, + { + "path": "tests/test_db_bootstrap.py", + "language": "python", + "sizeLines": 87, + "fileCategory": "code" + } + ], + "batchImportData": { + "app.py": [ + "config.py", + "extensions.py" + ], + "config.py": [], + "db_bootstrap.py": [ + "extensions.py" + ], + "extensions.py": [], + "models/__init__.py": [ + "extensions.py" + ], + "tests/test_dashboard.py": [ + "extensions.py", + "models/bc.py" + ], + "tests/test_db_bootstrap.py": [ + "app.py", + "db_bootstrap.py", + "extensions.py" + ] + }, + "neighborMap": { + "app.py": [ + { + "path": "tests/conftest.py", + "batchIndex": 1, + "symbols": [ + "TestConfig", + "app", + "client", + "clean_db", + "clear_api_cache", + "sample_bc" + ] + } + ], + "extensions.py": [ + { + "path": "models/audit.py", + "batchIndex": 1, + "symbols": [ + "AuditLog" + ] + }, + { + "path": "models/bc.py", + "batchIndex": 1, + "symbols": [ + "BiomedicalConcept", + "DataElementConcept" + ] + }, + { + "path": "models/governance.py", + "batchIndex": 2, + "symbols": [ + "GovernanceRecord" + ] + }, + { + "path": "models/ingestion.py", + "batchIndex": 1, + "symbols": [ + "IngestionRecord" + ] + }, + { + "path": "models/specialization.py", + "batchIndex": 5, + "symbols": [ + "DatasetSpecialization" + ] + }, + { + "path": "routes/bc.py", + "batchIndex": 2, + "symbols": [ + "index", + "new_bc", + "export", + "library_detail", + "detail", + "fetch_metadata", + "create", + "edit", + "clear_ncit", + "clear_loinc", + "submit_for_review", + "delete", + "_decs_from_form" + ] + }, + { + "path": "routes/governance.py", + "batchIndex": 2, + "symbols": [ + "board", + "export", + "advance", + "reject_bc" + ] + }, + { + "path": "routes/ingestion.py", + "batchIndex": 1, + "symbols": [ + "_allowed_file", + "_get_session_key", + "_bc_from_mapped", + "_create_decs", + "index", + "upload", + "approve", + "reject", + "approve_all" + ] + }, + { + "path": "routes/ncit.py", + "batchIndex": 2, + "symbols": [ + "index", + "mapping", + "search_ncit", + "concept_detail", + "resolve" + ] + }, + { + "path": "routes/specializations.py", + "batchIndex": 3, + "symbols": [ + "_get_bc_options", + "index", + "library_detail", + "detail", + "_variables_from_form", + "create", + "delete", + "generate_from_dec", + "generate" + ] + }, + { + "path": "services/audit.py", + "batchIndex": 2, + "symbols": [ + "log_change" + ] + }, + { + "path": "services/bc_service.py", + "batchIndex": 2, + "symbols": [ + "NotFoundError", + "_get_bc_or_raise", + "apply_bc_fields", + "create_bc", + "update_bc", + "save_decs", + "map_ncit_to_bc", + "submit_bc_for_review" + ] + }, + { + "path": "services/governance_service.py", + "batchIndex": 2, + "symbols": [ + "advance_governance", + "reject_bc" + ] + }, + { + "path": "tests/conftest.py", + "batchIndex": 1, + "symbols": [ + "TestConfig", + "app", + "client", + "clean_db", + "clear_api_cache", + "sample_bc" + ] + }, + { + "path": "tests/test_audit_routes.py", + "batchIndex": 1, + "symbols": [ + "_add_log", + "TestAuditIndex" + ] + }, + { + "path": "tests/test_bc_routes.py", + "batchIndex": 1, + "symbols": [ + "_bc_form", + "TestBcIndex", + "TestNewBc", + "TestCreateBc", + "TestBcDetail", + "TestEditBc", + "TestClearNcitCode", + "TestClearLoincCode", + "TestSubmitForReview", + "TestDeleteBc", + "TestExport", + "TestLibraryDetail" + ] + }, + { + "path": "tests/test_export_service.py", + "batchIndex": 2, + "symbols": [ + "TestExportJson", + "TestExportXlsx", + "TestExportGovernanceXlsx", + "TestExportOdmXml" + ] + }, + { + "path": "tests/test_governance_routes.py", + "batchIndex": 1, + "symbols": [ + "TestGovernanceBoard", + "TestAdvance", + "TestReject", + "TestGovernanceExport" + ] + }, + { + "path": "tests/test_ingestion_routes.py", + "batchIndex": 1, + "symbols": [ + "_csv_file", + "_json_file", + "TestIngestionIndex", + "TestUpload", + "TestApprove", + "TestReject", + "TestApproveAll" + ] + }, + { + "path": "tests/test_mcp_server.py", + "batchIndex": 5, + "symbols": [ + "inject_test_app", + "_dispatch", + "TestDispatch", + "TestListBcs", + "TestGetBc", + "TestExternalApiTools", + "_audit_rows", + "TestCreateBc", + "TestUpdateBc", + "TestMapNcit", + "TestGovernanceWrites", + "TestReviewQueue" + ] + }, + { + "path": "tests/test_models.py", + "batchIndex": 1, + "symbols": [ + "TestAuditLogJsonProperties", + "TestIngestionRecordProperties", + "TestBiomedicalConceptToDict" + ] + }, + { + "path": "tests/test_specializations_routes.py", + "batchIndex": 5, + "symbols": [ + "_patch_client", + "TestIndex", + "TestCreate", + "TestEditUpsert", + "TestDelete", + "TestDetail", + "TestLibraryDetail", + "TestGenerate", + "TestGenerateFromDec" + ] + } + ], + "tests/test_dashboard.py": [ + { + "path": "models/bc.py", + "batchIndex": 1, + "symbols": [ + "BiomedicalConcept", + "DataElementConcept" + ] + } + ] + } + }, + { + "batchIndex": 5, + "files": [ + { + "path": "mcp_server/__main__.py", + "language": "python", + "sizeLines": 3, + "fileCategory": "code" + }, + { + "path": "mcp_server/server.py", + "language": "python", + "sizeLines": 572, + "fileCategory": "code" + }, + { + "path": "models/specialization.py", + "language": "python", + "sizeLines": 30, + "fileCategory": "code" + }, + { + "path": "tests/test_mcp_server.py", + "language": "python", + "sizeLines": 315, + "fileCategory": "code" + }, + { + "path": "tests/test_specializations_routes.py", + "language": "python", + "sizeLines": 260, + "fileCategory": "code" + } + ], + "batchImportData": { + "mcp_server/__main__.py": [ + "mcp_server/server.py" + ], + "mcp_server/server.py": [], + "models/specialization.py": [ + "extensions.py" + ], + "tests/test_mcp_server.py": [ + "extensions.py", + "mcp_server/server.py", + "models/audit.py", + "models/bc.py", + "models/governance.py", + "models/ingestion.py", + "models/specialization.py" + ], + "tests/test_specializations_routes.py": [ + "extensions.py", + "models/audit.py", + "models/bc.py", + "models/specialization.py" + ] + }, + "neighborMap": { + "models/specialization.py": [ + { + "path": "extensions.py", + "batchIndex": 4, + "symbols": [ + "_set_sqlite_pragmas" + ] + }, + { + "path": "routes/specializations.py", + "batchIndex": 3, + "symbols": [ + "_get_bc_options", + "index", + "library_detail", + "detail", + "_variables_from_form", + "create", + "delete", + "generate_from_dec", + "generate" + ] + }, + { + "path": "services/bc_service.py", + "batchIndex": 2, + "symbols": [ + "NotFoundError", + "_get_bc_or_raise", + "apply_bc_fields", + "create_bc", + "update_bc", + "save_decs", + "map_ncit_to_bc", + "submit_bc_for_review" + ] + } + ], + "tests/test_mcp_server.py": [ + { + "path": "extensions.py", + "batchIndex": 4, + "symbols": [ + "_set_sqlite_pragmas" + ] + }, + { + "path": "models/audit.py", + "batchIndex": 1, + "symbols": [ + "AuditLog" + ] + }, + { + "path": "models/bc.py", + "batchIndex": 1, + "symbols": [ + "BiomedicalConcept", + "DataElementConcept" + ] + }, + { + "path": "models/governance.py", + "batchIndex": 2, + "symbols": [ + "GovernanceRecord" + ] + }, + { + "path": "models/ingestion.py", + "batchIndex": 1, + "symbols": [ + "IngestionRecord" + ] + } + ], + "tests/test_specializations_routes.py": [ + { + "path": "extensions.py", + "batchIndex": 4, + "symbols": [ + "_set_sqlite_pragmas" + ] + }, + { + "path": "models/audit.py", + "batchIndex": 1, + "symbols": [ + "AuditLog" + ] + }, + { + "path": "models/bc.py", + "batchIndex": 1, + "symbols": [ + "BiomedicalConcept", + "DataElementConcept" + ] + } + ] + } + }, + { + "batchIndex": 6, + "files": [ + { + "path": ".github/workflows/ci.yml", + "language": "yaml", + "sizeLines": 26, + "fileCategory": "infra" + } + ], + "batchImportData": { + ".github/workflows/ci.yml": [] + }, + "neighborMap": {} + }, + { + "batchIndex": 7, + "files": [ + { + "path": ".claude/agent-memory/cdisc-frontend-dev/MEMORY.md", + "language": "markdown", + "sizeLines": 2, + "fileCategory": "docs" + }, + { + "path": ".claude/agent-memory/cdisc-frontend-dev/frontend_architecture.md", + "language": "markdown", + "sizeLines": 65, + "fileCategory": "docs" + }, + { + "path": ".claude/agent-memory/cdisc-frontend-dev/project_foundation.md", + "language": "markdown", + "sizeLines": 54, + "fileCategory": "docs" + } + ], + "batchImportData": { + ".claude/agent-memory/cdisc-frontend-dev/MEMORY.md": [], + ".claude/agent-memory/cdisc-frontend-dev/frontend_architecture.md": [], + ".claude/agent-memory/cdisc-frontend-dev/project_foundation.md": [] + }, + "neighborMap": {} + }, + { + "batchIndex": 8, + "files": [ + { + "path": ".claude/agents/cdisc-concept-explorer.md", + "language": "markdown", + "sizeLines": 67, + "fileCategory": "docs" + }, + { + "path": ".claude/agents/cdisc-frontend-dev.md", + "language": "markdown", + "sizeLines": 207, + "fileCategory": "docs" + }, + { + "path": ".claude/agents/mike.md", + "language": "markdown", + "sizeLines": 108, + "fileCategory": "docs" + } + ], + "batchImportData": { + ".claude/agents/cdisc-concept-explorer.md": [], + ".claude/agents/cdisc-frontend-dev.md": [], + ".claude/agents/mike.md": [] + }, + "neighborMap": {} + }, + { + "batchIndex": 9, + "files": [ + { + "path": ".mcp.json", + "language": "json", + "sizeLines": 9, + "fileCategory": "config" + }, + { + "path": ".pre-commit-config.yaml", + "language": "yaml", + "sizeLines": 24, + "fileCategory": "config" + }, + { + "path": "CLAUDE.md", + "language": "markdown", + "sizeLines": 128, + "fileCategory": "docs" + }, + { + "path": "README-PROGRESS.md", + "language": "markdown", + "sizeLines": 147, + "fileCategory": "docs" + }, + { + "path": "README.md", + "language": "markdown", + "sizeLines": 185, + "fileCategory": "docs" + }, + { + "path": "pyproject.toml", + "language": "toml", + "sizeLines": 7, + "fileCategory": "config" + }, + { + "path": "requirements-dev.txt", + "language": "txt", + "sizeLines": 9, + "fileCategory": "docs" + }, + { + "path": "requirements.txt", + "language": "txt", + "sizeLines": 28, + "fileCategory": "docs" + } + ], + "batchImportData": { + ".mcp.json": [], + ".pre-commit-config.yaml": [], + "CLAUDE.md": [], + "README-PROGRESS.md": [], + "README.md": [], + "pyproject.toml": [], + "requirements-dev.txt": [], + "requirements.txt": [] + }, + "neighborMap": {} + }, + { + "batchIndex": 10, + "files": [ + { + "path": "files/implementation.md", + "language": "markdown", + "sizeLines": 205, + "fileCategory": "docs" + }, + { + "path": "files/plan.md", + "language": "markdown", + "sizeLines": 47, + "fileCategory": "docs" + }, + { + "path": "files/result.md", + "language": "markdown", + "sizeLines": 27, + "fileCategory": "docs" + } + ], + "batchImportData": { + "files/implementation.md": [], + "files/plan.md": [], + "files/result.md": [] + }, + "neighborMap": {} + }, + { + "batchIndex": 11, + "files": [ + { + "path": "templates/audit.html", + "language": "html", + "sizeLines": 213, + "fileCategory": "markup" + }, + { + "path": "templates/base.html", + "language": "html", + "sizeLines": 130, + "fileCategory": "markup" + }, + { + "path": "templates/bc_detail.html", + "language": "html", + "sizeLines": 533, + "fileCategory": "markup" + }, + { + "path": "templates/bc_list.html", + "language": "html", + "sizeLines": 238, + "fileCategory": "markup" + }, + { + "path": "templates/dashboard.html", + "language": "html", + "sizeLines": 392, + "fileCategory": "markup" + }, + { + "path": "templates/governance.html", + "language": "html", + "sizeLines": 243, + "fileCategory": "markup" + }, + { + "path": "templates/ingestion.html", + "language": "html", + "sizeLines": 174, + "fileCategory": "markup" + }, + { + "path": "templates/library_bc_detail.html", + "language": "html", + "sizeLines": 162, + "fileCategory": "markup" + }, + { + "path": "templates/library_spec_detail.html", + "language": "html", + "sizeLines": 197, + "fileCategory": "markup" + }, + { + "path": "templates/ncit_mapping.html", + "language": "html", + "sizeLines": 135, + "fileCategory": "markup" + }, + { + "path": "templates/specializations.html", + "language": "html", + "sizeLines": 394, + "fileCategory": "markup" + } + ], + "batchImportData": { + "templates/audit.html": [], + "templates/base.html": [], + "templates/bc_detail.html": [], + "templates/bc_list.html": [], + "templates/dashboard.html": [], + "templates/governance.html": [], + "templates/ingestion.html": [], + "templates/library_bc_detail.html": [], + "templates/library_spec_detail.html": [], + "templates/ncit_mapping.html": [], + "templates/specializations.html": [] + }, + "neighborMap": {} + }, + { + "batchIndex": 12, + "files": [ + { + "path": ".claude/agent-memory/cdisc-concept-explorer/MEMORY.md", + "language": "markdown", + "sizeLines": 1, + "fileCategory": "docs" + }, + { + "path": ".claude/agent-memory/cdisc-concept-explorer/reference_api_endpoints.md", + "language": "markdown", + "sizeLines": 36, + "fileCategory": "docs" + }, + { + "path": ".claude/skills/code-review/SKILL.md", + "language": "markdown", + "sizeLines": 59, + "fileCategory": "docs" + }, + { + "path": ".claude/skills/run-concept-curation/SKILL.md", + "language": "markdown", + "sizeLines": 49, + "fileCategory": "docs" + }, + { + "path": ".claude/skills/run-concept-curation/smoke.sh", + "language": "shell", + "sizeLines": 90, + "fileCategory": "script" + }, + { + "path": ".flake8", + "language": "unknown", + "sizeLines": 16, + "fileCategory": "code" + }, + { + "path": ".hintrc", + "language": "unknown", + "sizeLines": 7, + "fileCategory": "code" + }, + { + "path": ".ua/.understandignore", + "language": "unknown", + "sizeLines": 110, + "fileCategory": "code" + }, + { + "path": ".ua/config.json", + "language": "json", + "sizeLines": 1, + "fileCategory": "config" + }, + { + "path": "files/BC Curation Principles and Completion GLs.xlsx", + "language": "xlsx", + "sizeLines": 182, + "fileCategory": "code" + }, + { + "path": "files/BC DEC Templates.xlsx", + "language": "xlsx", + "sizeLines": 94, + "fileCategory": "code" + }, + { + "path": "files/BC Examples.xlsx", + "language": "xlsx", + "sizeLines": 332, + "fileCategory": "code" + }, + { + "path": "files/BC Governance.docx", + "language": "docx", + "sizeLines": 54, + "fileCategory": "code" + }, + { + "path": "mcp_server/__init__.py", + "language": "python", + "sizeLines": 5, + "fileCategory": "code" + }, + { + "path": "migrations/README", + "language": "unknown", + "sizeLines": 1, + "fileCategory": "code" + }, + { + "path": "migrations/alembic.ini", + "language": "config", + "sizeLines": 50, + "fileCategory": "config" + }, + { + "path": "migrations/env.py", + "language": "python", + "sizeLines": 105, + "fileCategory": "code" + }, + { + "path": "migrations/script.py.mako", + "language": "mako", + "sizeLines": 24, + "fileCategory": "code" + }, + { + "path": "migrations/versions/51d4a009d291_baseline_initial_schema.py", + "language": "python", + "sizeLines": 142, + "fileCategory": "code" + }, + { + "path": "routes/__init__.py", + "language": "python", + "sizeLines": 0, + "fileCategory": "code" + }, + { + "path": "static/css/custom.css", + "language": "css", + "sizeLines": 1096, + "fileCategory": "markup" + }, + { + "path": "static/js/main.js", + "language": "javascript", + "sizeLines": 819, + "fileCategory": "code" + }, + { + "path": "tests/__init__.py", + "language": "python", + "sizeLines": 0, + "fileCategory": "code" + } + ], + "batchImportData": { + ".claude/agent-memory/cdisc-concept-explorer/MEMORY.md": [], + ".claude/agent-memory/cdisc-concept-explorer/reference_api_endpoints.md": [], + ".claude/skills/code-review/SKILL.md": [], + ".claude/skills/run-concept-curation/SKILL.md": [], + ".claude/skills/run-concept-curation/smoke.sh": [], + ".flake8": [], + ".hintrc": [], + ".ua/.understandignore": [], + ".ua/config.json": [], + "files/BC Curation Principles and Completion GLs.xlsx": [], + "files/BC DEC Templates.xlsx": [], + "files/BC Examples.xlsx": [], + "files/BC Governance.docx": [], + "mcp_server/__init__.py": [], + "migrations/README": [], + "migrations/alembic.ini": [], + "migrations/env.py": [], + "migrations/script.py.mako": [], + "migrations/versions/51d4a009d291_baseline_initial_schema.py": [], + "routes/__init__.py": [], + "static/css/custom.css": [], + "static/js/main.js": [], + "tests/__init__.py": [] + }, + "neighborMap": {} + } + ] +} \ No newline at end of file diff --git a/.ua/.trash-1785851745/fingerprint-input.json b/.ua/.trash-1785851745/fingerprint-input.json new file mode 100644 index 0000000..4b11381 --- /dev/null +++ b/.ua/.trash-1785851745/fingerprint-input.json @@ -0,0 +1,103 @@ +{ + "projectRoot": "/Users/dmoreland/projects/cdisc-concept-curation", + "sourceFilePaths": [ + ".claude/agent-memory/cdisc-concept-explorer/MEMORY.md", + ".claude/agent-memory/cdisc-concept-explorer/reference_api_endpoints.md", + ".claude/agent-memory/cdisc-frontend-dev/MEMORY.md", + ".claude/agent-memory/cdisc-frontend-dev/frontend_architecture.md", + ".claude/agent-memory/cdisc-frontend-dev/project_foundation.md", + ".claude/agents/cdisc-concept-explorer.md", + ".claude/agents/cdisc-frontend-dev.md", + ".claude/agents/mike.md", + ".claude/skills/code-review/SKILL.md", + ".claude/skills/run-concept-curation/SKILL.md", + ".claude/skills/run-concept-curation/smoke.sh", + ".flake8", + ".github/workflows/ci.yml", + ".hintrc", + ".mcp.json", + ".pre-commit-config.yaml", + ".ua/.understandignore", + ".ua/config.json", + "CLAUDE.md", + "README-PROGRESS.md", + "README.md", + "app.py", + "config.py", + "db_bootstrap.py", + "extensions.py", + "files/BC Curation Principles and Completion GLs.xlsx", + "files/BC DEC Templates.xlsx", + "files/BC Examples.xlsx", + "files/BC Governance.docx", + "files/implementation.md", + "files/plan.md", + "files/result.md", + "mcp_server/__init__.py", + "mcp_server/__main__.py", + "mcp_server/server.py", + "migrations/README", + "migrations/alembic.ini", + "migrations/env.py", + "migrations/script.py.mako", + "migrations/versions/51d4a009d291_baseline_initial_schema.py", + "models/__init__.py", + "models/audit.py", + "models/bc.py", + "models/governance.py", + "models/ingestion.py", + "models/specialization.py", + "pyproject.toml", + "requirements-dev.txt", + "requirements.txt", + "routes/__init__.py", + "routes/audit.py", + "routes/bc.py", + "routes/dashboard.py", + "routes/governance.py", + "routes/ingestion.py", + "routes/loinc.py", + "routes/ncit.py", + "routes/specializations.py", + "services/__init__.py", + "services/api_cache.py", + "services/audit.py", + "services/bc_service.py", + "services/cdisc_api.py", + "services/export.py", + "services/governance_service.py", + "services/ingestion.py", + "services/loinc_api.py", + "services/ncit_api.py", + "static/css/custom.css", + "static/js/main.js", + "templates/audit.html", + "templates/base.html", + "templates/bc_detail.html", + "templates/bc_list.html", + "templates/dashboard.html", + "templates/governance.html", + "templates/ingestion.html", + "templates/library_bc_detail.html", + "templates/library_spec_detail.html", + "templates/ncit_mapping.html", + "templates/specializations.html", + "tests/__init__.py", + "tests/conftest.py", + "tests/test_audit_routes.py", + "tests/test_bc_routes.py", + "tests/test_cdisc_api_cache.py", + "tests/test_dashboard.py", + "tests/test_db_bootstrap.py", + "tests/test_export_service.py", + "tests/test_governance_routes.py", + "tests/test_ingestion_routes.py", + "tests/test_ingestion_service.py", + "tests/test_loinc.py", + "tests/test_mcp_server.py", + "tests/test_models.py", + "tests/test_ncit.py", + "tests/test_specializations_routes.py" + ], + "gitCommitHash": "8df239f9988f09a9476e634ceb7cac80c29f72fe" +} \ No newline at end of file diff --git a/.ua/.trash-1785851745/layers.json b/.ua/.trash-1785851745/layers.json new file mode 100644 index 0000000..590e3f7 --- /dev/null +++ b/.ua/.trash-1785851745/layers.json @@ -0,0 +1,157 @@ +[ + { + "id": "layer:api", + "name": "API Layer", + "description": "Flask blueprint route handlers (dashboard, ingestion, bc, ncit, loinc, specializations, governance, audit) plus the MCP server package that exposes the same read/write operations as a second tool-based API surface for AI agents.", + "nodeIds": [ + "file:routes/audit.py", + "file:routes/ingestion.py", + "file:routes/bc.py", + "file:routes/governance.py", + "file:routes/ncit.py", + "file:routes/dashboard.py", + "file:routes/loinc.py", + "file:routes/specializations.py", + "file:routes/__init__.py", + "file:mcp_server/__main__.py", + "file:mcp_server/server.py", + "file:mcp_server/__init__.py" + ] + }, + { + "id": "layer:service", + "name": "Service Layer", + "description": "Business logic and external API clients — BC/governance/audit orchestration, the ingestion parser and field mapper, the XLSX/JSON/ODM-XML exporter, and the CDISC Library, NCIt, and LOINC API clients (with in-memory caching).", + "nodeIds": [ + "file:services/ingestion.py", + "file:services/__init__.py", + "file:services/audit.py", + "file:services/bc_service.py", + "file:services/export.py", + "file:services/governance_service.py", + "file:services/ncit_api.py", + "file:services/api_cache.py", + "file:services/cdisc_api.py", + "file:services/loinc_api.py" + ] + }, + { + "id": "layer:data", + "name": "Data Layer", + "description": "SQLAlchemy ORM models (BiomedicalConcept, DataElementConcept, IngestionRecord, AuditLog, GovernanceRecord, Specialization) together with the Alembic migration environment and baseline schema revision that bring the SQLite database to head.", + "nodeIds": [ + "file:models/audit.py", + "file:models/bc.py", + "file:models/ingestion.py", + "file:models/governance.py", + "file:models/__init__.py", + "file:models/specialization.py", + "document:migrations/README", + "config:migrations/alembic.ini", + "file:migrations/env.py", + "file:migrations/script.py.mako", + "file:migrations/versions/51d4a009d291_baseline_initial_schema.py" + ] + }, + { + "id": "layer:ui", + "name": "UI Layer", + "description": "Jinja2 + Bootstrap 5 templates for every page (dashboard, ingestion, BC detail/list, governance Kanban, NCIt mapping, specializations, audit log) plus the shared CSS and JavaScript assets that render them in the browser.", + "nodeIds": [ + "file:templates/base.html", + "file:templates/audit.html", + "file:templates/bc_detail.html", + "file:templates/bc_list.html", + "file:templates/dashboard.html", + "file:templates/governance.html", + "file:templates/ingestion.html", + "file:templates/library_bc_detail.html", + "file:templates/library_spec_detail.html", + "file:templates/ncit_mapping.html", + "file:templates/specializations.html", + "file:static/css/custom.css", + "file:static/js/main.js" + ] + }, + { + "id": "layer:config", + "name": "Configuration & Application Setup", + "description": "The application factory, extension singletons, and DB-bootstrap entry points (app.py, extensions.py, db_bootstrap.py) alongside project-level configuration — config.py, dependency pins, lint/format tool config, and the MCP server registration.", + "nodeIds": [ + "file:app.py", + "file:db_bootstrap.py", + "file:extensions.py", + "file:config.py", + "config:.pre-commit-config.yaml", + "config:.mcp.json", + "config:pyproject.toml", + "document:requirements-dev.txt", + "document:requirements.txt", + "config:.flake8", + "config:.hintrc", + "config:.ua/.understandignore", + "config:.ua/config.json" + ] + }, + { + "id": "layer:test", + "name": "Test Layer", + "description": "The pytest suite covering every blueprint, service, and model, plus shared fixtures in conftest.py, run against the in-memory SQLite TestConfig database used by both CI and local development.", + "nodeIds": [ + "file:tests/conftest.py", + "file:tests/test_audit_routes.py", + "file:tests/test_bc_routes.py", + "file:tests/test_governance_routes.py", + "file:tests/test_ingestion_routes.py", + "file:tests/test_ingestion_service.py", + "file:tests/test_models.py", + "file:tests/test_export_service.py", + "file:tests/test_ncit.py", + "file:tests/test_cdisc_api_cache.py", + "file:tests/test_loinc.py", + "file:tests/test_dashboard.py", + "file:tests/test_db_bootstrap.py", + "file:tests/test_mcp_server.py", + "file:tests/test_specializations_routes.py", + "file:tests/__init__.py" + ] + }, + { + "id": "layer:documentation", + "name": "Documentation & Reference Materials", + "description": "Project-level documentation (CLAUDE.md, README, progress log) and the domain reference materials in files/ — BC curation principles, DEC templates, worked examples, governance guidance, and platform planning docs — that ground the curation workflow the app implements.", + "nodeIds": [ + "document:CLAUDE.md", + "document:README-PROGRESS.md", + "document:README.md", + "document:files/plan.md", + "document:files/implementation.md", + "document:files/result.md", + "file:files/BC Curation Principles and Completion GLs.xlsx", + "file:files/BC DEC Templates.xlsx", + "file:files/BC Examples.xlsx", + "file:files/BC Governance.docx", + "file:files/cdisc_bc_ai_platform.html", + "file:files/cdisc_smart_goals_infographic.html" + ] + }, + { + "id": "layer:dev-tooling", + "name": "Dev Tooling & CI/CD", + "description": "The GitHub Actions CI pipeline plus Claude Code agent configuration and memory (.claude/agents, .claude/agent-memory, .claude/skills) that automate linting, testing, and AI-assisted development for this repository.", + "nodeIds": [ + "pipeline:.github/workflows/ci.yml", + "document:.claude/agent-memory/cdisc-frontend-dev/MEMORY.md", + "document:.claude/agent-memory/cdisc-frontend-dev/frontend_architecture.md", + "document:.claude/agent-memory/cdisc-frontend-dev/project_foundation.md", + "document:.claude/agents/cdisc-concept-explorer.md", + "document:.claude/agents/cdisc-frontend-dev.md", + "document:.claude/agents/mike.md", + "document:.claude/agent-memory/cdisc-concept-explorer/MEMORY.md", + "document:.claude/agent-memory/cdisc-concept-explorer/reference_api_endpoints.md", + "document:.claude/skills/code-review/SKILL.md", + "document:.claude/skills/run-concept-curation/SKILL.md", + "file:.claude/skills/run-concept-curation/smoke.sh" + ] + } +] diff --git a/.ua/.trash-1785851745/review.json b/.ua/.trash-1785851745/review.json new file mode 100644 index 0000000..200da95 --- /dev/null +++ b/.ua/.trash-1785851745/review.json @@ -0,0 +1,39 @@ +{ + "issues": [], + "warnings": [ + "Node 'pipeline:.github/workflows/ci.yml' has no edges (orphan)", + "Node 'config:.mcp.json' has no edges (orphan)", + "Node 'config:.flake8' has no edges (orphan)", + "Node 'config:.hintrc' has no edges (orphan)", + "Node 'config:.ua/.understandignore' has no edges (orphan)", + "Node 'config:.ua/config.json' has no edges (orphan)", + "Node 'document:migrations/README' has no edges (orphan)", + "Node 'file:routes/__init__.py' has no edges (orphan)", + "Node 'file:tests/__init__.py' has no edges (orphan)" + ], + "stats": { + "totalNodes": 281, + "totalEdges": 561, + "totalLayers": 8, + "tourSteps": 12, + "nodeTypes": { + "file": 71, + "class": 68, + "function": 114, + "pipeline": 1, + "document": 19, + "config": 8 + }, + "edgeTypes": { + "imports": 105, + "contains": 182, + "exports": 78, + "calls": 87, + "tested_by": 24, + "depends_on": 23, + "related": 21, + "documents": 40, + "configures": 1 + } + } +} \ No newline at end of file diff --git a/.ua/.trash-1785851745/tmp/batch3_full.json b/.ua/.trash-1785851745/tmp/batch3_full.json new file mode 100644 index 0000000..f8ed9b0 --- /dev/null +++ b/.ua/.trash-1785851745/tmp/batch3_full.json @@ -0,0 +1 @@ +{"batchIndex": 3, "files": [{"path": "routes/dashboard.py", "language": "python", "sizeLines": 60, "fileCategory": "code"}, {"path": "routes/loinc.py", "language": "python", "sizeLines": 23, "fileCategory": "code"}, {"path": "routes/specializations.py", "language": "python", "sizeLines": 172, "fileCategory": "code"}, {"path": "services/api_cache.py", "language": "python", "sizeLines": 42, "fileCategory": "code"}, {"path": "services/cdisc_api.py", "language": "python", "sizeLines": 127, "fileCategory": "code"}, {"path": "services/loinc_api.py", "language": "python", "sizeLines": 62, "fileCategory": "code"}, {"path": "tests/test_cdisc_api_cache.py", "language": "python", "sizeLines": 120, "fileCategory": "code"}, {"path": "tests/test_loinc.py", "language": "python", "sizeLines": 205, "fileCategory": "code"}], "batchImportData": {"routes/dashboard.py": ["models/audit.py", "models/bc.py", "services/cdisc_api.py"], "routes/loinc.py": ["services/loinc_api.py"], "routes/specializations.py": ["extensions.py", "models/bc.py", "models/specialization.py", "services/audit.py", "services/cdisc_api.py"], "services/api_cache.py": [], "services/cdisc_api.py": ["services/api_cache.py"], "services/loinc_api.py": ["services/api_cache.py"], "tests/test_cdisc_api_cache.py": ["services/cdisc_api.py"], "tests/test_loinc.py": ["services/loinc_api.py"]}, "neighborMap": {"routes/dashboard.py": [{"path": "models/audit.py", "batchIndex": 1, "symbols": ["AuditLog"]}, {"path": "models/bc.py", "batchIndex": 1, "symbols": ["BiomedicalConcept", "DataElementConcept"]}], "routes/specializations.py": [{"path": "extensions.py", "batchIndex": 4, "symbols": ["_set_sqlite_pragmas"]}, {"path": "models/bc.py", "batchIndex": 1, "symbols": ["BiomedicalConcept", "DataElementConcept"]}, {"path": "models/specialization.py", "batchIndex": 5, "symbols": ["DatasetSpecialization"]}, {"path": "services/audit.py", "batchIndex": 2, "symbols": ["log_change"]}], "services/api_cache.py": [{"path": "services/ncit_api.py", "batchIndex": 2, "symbols": ["_pick_definition", "NCItApiClient"]}], "services/cdisc_api.py": [{"path": "routes/bc.py", "batchIndex": 2, "symbols": ["index", "new_bc", "export", "library_detail", "detail", "fetch_metadata", "create", "edit", "clear_ncit", "clear_loinc", "submit_for_review", "delete", "_decs_from_form"]}], "services/loinc_api.py": [{"path": "routes/bc.py", "batchIndex": 2, "symbols": ["index", "new_bc", "export", "library_detail", "detail", "fetch_metadata", "create", "edit", "clear_ncit", "clear_loinc", "submit_for_review", "delete", "_decs_from_form"]}]}} diff --git a/.ua/.trash-1785851745/tmp/ua-arch-analyze.js b/.ua/.trash-1785851745/tmp/ua-arch-analyze.js new file mode 100644 index 0000000..8ec1d80 --- /dev/null +++ b/.ua/.trash-1785851745/tmp/ua-arch-analyze.js @@ -0,0 +1,463 @@ +#!/usr/bin/env node +"use strict"; + +const fs = require("fs"); +const path = require("path"); + +function main() { + const inputPath = process.argv[2]; + const outputPath = process.argv[3]; + if (!inputPath || !outputPath) { + console.error("Usage: node ua-arch-analyze.js "); + process.exit(1); + } + + let raw; + try { + raw = JSON.parse(fs.readFileSync(inputPath, "utf8")); + } catch (e) { + console.error("Failed to read/parse input JSON: " + e.message); + process.exit(1); + } + + const fileNodes = raw.fileNodes || []; + const importEdges = raw.importEdges || []; + const allEdges = raw.allEdges || []; + + const nodeById = new Map(); + for (const n of fileNodes) nodeById.set(n.id, n); + + // ---------- A. Directory Grouping ---------- + function dirOf(fp) { + if (!fp) return ""; + const idx = fp.lastIndexOf("/"); + return idx === -1 ? "" : fp.substring(0, idx); + } + + const paths = fileNodes + .map((n) => n.filePath || n.name || "") + .filter(Boolean); + + function commonPrefix(strs) { + if (strs.length === 0) return ""; + const splitPaths = strs.map((s) => s.split("/")); + const minLen = Math.min(...splitPaths.map((p) => p.length)); + const prefixParts = []; + for (let i = 0; i < minLen - 1; i++) { + const seg = splitPaths[0][i]; + if (splitPaths.every((p) => p[i] === seg)) { + prefixParts.push(seg); + } else { + break; + } + } + return prefixParts.length ? prefixParts.join("/") + "/" : ""; + } + + const prefix = commonPrefix(paths); + + function firstSegmentAfterPrefix(fp) { + let rest = fp; + if (prefix && fp.startsWith(prefix)) { + rest = fp.substring(prefix.length); + } + const parts = rest.split("/"); + if (parts.length > 1) { + return parts[0]; + } + // flat file directly under prefix (or root) - group by extension pattern + const fname = parts[0]; + return classifyFlatFile(fname); + } + + function classifyFlatFile(fname) { + if ( + /\.(test|spec)\./.test(fname) || + /^test_/.test(fname) || + /_test\.go$/.test(fname) + ) + return "test"; + if (/\.config\./.test(fname) || /config/i.test(fname)) return "config"; + const ext = fname.includes(".") + ? fname.substring(fname.lastIndexOf(".") + 1) + : "noext"; + return ext || "root"; + } + + const directoryGroups = {}; + for (const n of fileNodes) { + const fp = n.filePath || n.name || n.id; + const group = firstSegmentAfterPrefix(fp) || "root"; + if (!directoryGroups[group]) directoryGroups[group] = []; + directoryGroups[group].push(n.id); + } + + // ---------- B. Node Type Grouping ---------- + const nodeTypeGroups = {}; + for (const n of fileNodes) { + const t = n.type || "unknown"; + if (!nodeTypeGroups[t]) nodeTypeGroups[t] = []; + nodeTypeGroups[t].push(n.id); + } + + // ---------- C. Import Adjacency Matrix ---------- + const fanOut = {}; + const fanIn = {}; + for (const n of fileNodes) { + fanOut[n.id] = 0; + fanIn[n.id] = 0; + } + const groupOfId = {}; + for (const [g, ids] of Object.entries(directoryGroups)) { + for (const id of ids) groupOfId[id] = g; + } + + for (const e of importEdges) { + if (fanOut.hasOwnProperty(e.source)) fanOut[e.source]++; + if (fanIn.hasOwnProperty(e.target)) fanIn[e.target]++; + } + + // ---------- D. Cross-Category Dependency Analysis ---------- + const crossCategoryMap = new Map(); + for (const e of allEdges) { + const s = nodeById.get(e.source); + const t = nodeById.get(e.target); + if (!s || !t) continue; + if (s.type === t.type) continue; // cross-category only per spec examples; but keep general + const key = `${s.type}|${t.type}|${e.type}`; + crossCategoryMap.set(key, (crossCategoryMap.get(key) || 0) + 1); + } + const crossCategoryEdges = []; + for (const [key, count] of crossCategoryMap.entries()) { + const [fromType, toType, edgeType] = key.split("|"); + crossCategoryEdges.push({ fromType, toType, edgeType, count }); + } + + // ---------- E. Inter-Group Import Frequency ---------- + const interGroupMap = new Map(); + for (const e of importEdges) { + const g1 = groupOfId[e.source]; + const g2 = groupOfId[e.target]; + if (!g1 || !g2 || g1 === g2) continue; + const key = `${g1}|${g2}`; + interGroupMap.set(key, (interGroupMap.get(key) || 0) + 1); + } + const interGroupImports = []; + for (const [key, count] of interGroupMap.entries()) { + const [from, to] = key.split("|"); + interGroupImports.push({ from, to, count }); + } + + // ---------- F. Intra-Group Import Density ---------- + const intraGroupDensity = {}; + for (const g of Object.keys(directoryGroups)) { + let internal = 0; + let total = 0; + for (const e of importEdges) { + const g1 = groupOfId[e.source]; + const g2 = groupOfId[e.target]; + if (g1 === g || g2 === g) { + total++; + if (g1 === g && g2 === g) internal++; + } + } + intraGroupDensity[g] = { + internalEdges: internal, + totalEdges: total, + density: total > 0 ? internal / total : 0, + }; + } + + // ---------- G. Directory Pattern Matching ---------- + const dirPatternTable = [ + { + pats: ["routes", "api", "controllers", "endpoints", "handlers"], + label: "api", + }, + { pats: ["services", "core", "lib", "domain", "logic"], label: "service" }, + { + pats: ["models", "db", "data", "persistence", "repository", "entities"], + label: "data", + }, + { + pats: ["components", "views", "pages", "ui", "layouts", "screens"], + label: "ui", + }, + { + pats: ["middleware", "plugins", "interceptors", "guards"], + label: "middleware", + }, + { + pats: ["utils", "helpers", "common", "shared", "tools"], + label: "utility", + }, + { pats: ["config", "constants", "env", "settings"], label: "config" }, + { pats: ["__tests__", "test", "tests", "spec", "specs"], label: "test" }, + { + pats: ["types", "interfaces", "schemas", "contracts", "dtos"], + label: "types", + }, + { pats: ["hooks"], label: "hooks" }, + { + pats: ["store", "state", "reducers", "actions", "slices"], + label: "state", + }, + { pats: ["assets", "static", "public"], label: "assets" }, + { pats: ["migrations"], label: "data" }, + { pats: ["management", "commands"], label: "config" }, + { pats: ["templatetags"], label: "utility" }, + { pats: ["signals"], label: "service" }, + { pats: ["serializers"], label: "api" }, + { pats: ["cmd"], label: "entry" }, + { pats: ["internal"], label: "service" }, + { pats: ["pkg"], label: "utility" }, + { pats: ["dto", "request", "response"], label: "types" }, + { pats: ["entity"], label: "data" }, + { pats: ["controller"], label: "api" }, + { pats: ["routers"], label: "api" }, + { pats: ["composables"], label: "service" }, + { pats: ["blueprints"], label: "api" }, + { pats: ["mailers", "jobs", "channels"], label: "service" }, + { pats: ["bin"], label: "entry" }, + { pats: ["docs", "documentation", "wiki"], label: "documentation" }, + { + pats: ["deploy", "deployment", "infra", "infrastructure"], + label: "infrastructure", + }, + { pats: [".github", ".gitlab", ".circleci"], label: "ci-cd" }, + { pats: ["k8s", "kubernetes", "helm", "charts"], label: "infrastructure" }, + { pats: ["terraform", "tf"], label: "infrastructure" }, + { pats: ["docker"], label: "infrastructure" }, + { pats: ["sql", "database", "schema"], label: "data" }, + ]; + + function matchDirPattern(dirName) { + const lower = (dirName || "").toLowerCase(); + for (const row of dirPatternTable) { + if (row.pats.includes(lower)) return row.label; + } + return null; + } + + const patternMatches = {}; + for (const g of Object.keys(directoryGroups)) { + const m = matchDirPattern(g); + if (m) patternMatches[g] = m; + } + + // File-level patterns + function matchFilePattern(n) { + const fp = n.filePath || n.name || ""; + const base = path.basename(fp); + if ( + /\.(test|spec)\./.test(base) || + /^test_/.test(base) || + /_test\.go$/.test(base) || + /Test\.java$/.test(base) || + /_spec\.rb$/.test(base) || + /Test\.php$/.test(base) || + /Tests\.cs$/.test(base) + ) { + return "test"; + } + if (/\.d\.ts$/.test(base)) return "types"; + if (base === "index.ts" || base === "index.js" || base === "__init__.py") + return "entry"; + if (base === "manage.py") return "entry"; + if (base === "wsgi.py" || base === "asgi.py") return "config"; + if (base === "main.go" && /cmd\//.test(fp)) return "entry"; + if ((base === "main.rs" || base === "lib.rs") && /^src\//.test(fp)) + return "entry"; + if (base === "Application.java" || base === "Program.cs") return "entry"; + if (base === "config.ru") return "entry"; + if ( + [ + "Cargo.toml", + "go.mod", + "Gemfile", + "pom.xml", + "build.gradle", + "composer.json", + ].includes(base) + ) + return "config"; + if (base === "Dockerfile" || /^docker-compose\./.test(base)) + return "infrastructure"; + if (/\.tf$/.test(base) || /\.tfvars$/.test(base)) return "infrastructure"; + if ( + /^\.github\/workflows\//.test(fp) || + base === ".gitlab-ci.yml" || + base === "Jenkinsfile" + ) + return "ci-cd"; + if (/\.sql$/.test(base)) return "data"; + if (/\.(graphql|gql|proto)$/.test(base)) return "types"; + if (/\.(md|rst)$/.test(base)) return "documentation"; + if (base === "Makefile") return "infrastructure"; + return null; + } + + const filePatternMatches = {}; + for (const n of fileNodes) { + const m = matchFilePattern(n); + if (m) filePatternMatches[n.id] = m; + } + + // ---------- H. Deployment Topology Detection ---------- + const infraFiles = []; + let hasDockerfile = false; + let hasCompose = false; + let hasK8s = false; + let hasTerraform = false; + let hasCI = false; + for (const n of fileNodes) { + const fp = n.filePath || n.name || ""; + const base = path.basename(fp); + if (base === "Dockerfile" || /^Dockerfile\./.test(base)) { + hasDockerfile = true; + infraFiles.push(fp); + } else if (/^docker-compose/.test(base)) { + hasCompose = true; + infraFiles.push(fp); + } else if (/\.tf$/.test(base) || /\.tfvars$/.test(base)) { + hasTerraform = true; + infraFiles.push(fp); + } else if ( + /^\.github\/workflows\//.test(fp) || + base === ".gitlab-ci.yml" || + base === "Jenkinsfile" || + n.type === "pipeline" + ) { + hasCI = true; + infraFiles.push(fp); + } else if (/k8s|kubernetes|helm/i.test(fp)) { + hasK8s = true; + infraFiles.push(fp); + } + } + + const deploymentTopology = { + hasDockerfile, + hasCompose, + hasK8s, + hasTerraform, + hasCI, + infraFiles, + }; + + // ---------- I. Data Pipeline Detection ---------- + const schemaFiles = []; + const migrationFiles = []; + const dataModelFiles = []; + const apiHandlerFiles = []; + for (const n of fileNodes) { + const fp = n.filePath || n.name || ""; + if ( + /\.sql$/.test(fp) || + /\.(graphql|gql|proto)$/.test(fp) || + n.type === "schema" + ) + schemaFiles.push(fp); + if (/migrations\//.test(fp)) migrationFiles.push(fp); + if (/models\//.test(fp) || n.type === "table") dataModelFiles.push(fp); + if (/routes\//.test(fp) || n.type === "endpoint") apiHandlerFiles.push(fp); + } + + const dataPipeline = { + schemaFiles, + migrationFiles, + dataModelFiles, + apiHandlerFiles, + }; + + // ---------- J. Documentation Coverage ---------- + const docFilePaths = fileNodes + .filter((n) => n.type === "document") + .map((n) => n.filePath || n.name || ""); + let groupsWithDocs = 0; + const undocumentedGroups = []; + const groupNames = Object.keys(directoryGroups); + for (const g of groupNames) { + const hasReadme = directoryGroups[g].some((id) => { + const n = nodeById.get(id); + const base = path.basename(n.filePath || n.name || ""); + return /^README/i.test(base); + }); + const referencedByDocs = docFilePaths.some((dfp) => + dfp.toLowerCase().includes(g.toLowerCase()), + ); + if (hasReadme || referencedByDocs) { + groupsWithDocs++; + } else { + undocumentedGroups.push(g); + } + } + const totalGroups = groupNames.length; + const docCoverage = { + groupsWithDocs, + totalGroups, + coverageRatio: + totalGroups > 0 ? +(groupsWithDocs / totalGroups).toFixed(2) : 0, + undocumentedGroups, + }; + + // ---------- K. Dependency Direction ---------- + const pairSeen = new Set(); + const dependencyDirection = []; + for (const { from, to, count } of interGroupImports) { + const key = [from, to].sort().join("|"); + if (pairSeen.has(key)) continue; + pairSeen.add(key); + const reverse = interGroupImports.find( + (x) => x.from === to && x.to === from, + ); + const reverseCount = reverse ? reverse.count : 0; + if (count > reverseCount) { + dependencyDirection.push({ dependent: from, dependsOn: to }); + } else if (reverseCount > count) { + dependencyDirection.push({ dependent: to, dependsOn: from }); + } + } + + // ---------- fileStats ---------- + const filesPerGroup = {}; + for (const [g, ids] of Object.entries(directoryGroups)) + filesPerGroup[g] = ids.length; + const nodeTypeCounts = {}; + for (const [t, ids] of Object.entries(nodeTypeGroups)) + nodeTypeCounts[t] = ids.length; + + const fileStats = { + totalFileNodes: fileNodes.length, + filesPerGroup, + nodeTypeCounts, + }; + + const result = { + scriptCompleted: true, + directoryGroups, + nodeTypeGroups, + crossCategoryEdges, + interGroupImports, + intraGroupDensity, + patternMatches, + filePatternMatches, + deploymentTopology, + dataPipeline, + docCoverage, + dependencyDirection, + fileStats, + fileFanIn: fanIn, + fileFanOut: fanOut, + }; + + fs.writeFileSync(outputPath, JSON.stringify(result, null, 2)); + process.exit(0); +} + +try { + main(); +} catch (e) { + console.error("Fatal error: " + (e && e.stack ? e.stack : e)); + process.exit(1); +} diff --git a/.ua/.trash-1785851745/tmp/ua-arch-input.json b/.ua/.trash-1785851745/tmp/ua-arch-input.json new file mode 100644 index 0000000..afae7c6 --- /dev/null +++ b/.ua/.trash-1785851745/tmp/ua-arch-input.json @@ -0,0 +1 @@ +{"fileNodes": [{"id": "file:models/audit.py", "type": "file", "name": "audit.py", "filePath": "models/audit.py", "summary": "SQLAlchemy model for the AuditLog table that records before/after JSON state for every change made to BCs, DECs, and governance records.", "tags": ["data-model", "audit-log", "database", "orm", "tested"]}, {"id": "file:models/bc.py", "type": "file", "name": "bc.py", "filePath": "models/bc.py", "summary": "Core SQLAlchemy models for BiomedicalConcept and DataElementConcept, the central entities of the CDISC BC curation pipeline, including NCIt/LOINC coding fields and governance status.", "tags": ["data-model", "database", "orm", "core-domain", "tested"]}, {"id": "file:models/ingestion.py", "type": "file", "name": "ingestion.py", "filePath": "models/ingestion.py", "summary": "SQLAlchemy model for IngestionRecord, which stages parsed upload rows (with field mappings, confidence scores, and validation errors) in the DB prior to SME approval.", "tags": ["data-model", "database", "orm", "ingestion", "tested"]}, {"id": "file:routes/audit.py", "type": "file", "name": "audit.py", "filePath": "routes/audit.py", "summary": "Flask blueprint exposing the read-only audit trail view with filtering by entity type, action, and actor, and pagination.", "tags": ["api-handler", "audit-log", "blueprint", "tested"]}, {"id": "file:routes/ingestion.py", "type": "file", "name": "ingestion.py", "filePath": "routes/ingestion.py", "summary": "Flask blueprint implementing the file-upload ingestion workflow: parsing uploaded files, staging rows as IngestionRecord, and approving/rejecting them into BiomedicalConcept/DataElementConcept records with audit logging.", "tags": ["api-handler", "blueprint", "ingestion", "workflow", "tested"]}, {"id": "file:services/ingestion.py", "type": "file", "name": "ingestion.py", "filePath": "services/ingestion.py", "summary": "Business logic for the ingestion pipeline: fuzzy field-name mapping via SequenceMatcher similarity scoring, BC/DEC row validation, multi-row grouping by BC ID, and XLSX/CSV/JSON parsers.", "tags": ["service", "parsing", "validation", "fuzzy-matching", "tested"]}, {"id": "file:tests/conftest.py", "type": "file", "name": "conftest.py", "filePath": "tests/conftest.py", "summary": "Pytest fixtures shared across the test suite: Flask app/test client factories, a per-test DB reset fixture, API cache clearing, and a sample BiomedicalConcept fixture.", "tags": ["test", "fixture", "pytest-config"]}, {"id": "file:tests/test_audit_routes.py", "type": "file", "name": "test_audit_routes.py", "filePath": "tests/test_audit_routes.py", "summary": "Test suite for the audit log route, covering empty listing, log rendering, and filtering by entity type, action, actor, and pagination parameters.", "tags": ["test", "audit", "route-test"]}, {"id": "file:tests/test_bc_routes.py", "type": "file", "name": "test_bc_routes.py", "filePath": "tests/test_bc_routes.py", "summary": "Large test suite covering BC CRUD routes: listing/search, creation, detail view (including LOINC lookups), editing, clearing NCIt/LOINC codes, submission for review, deletion, export formats, and the CDISC Library detail page.", "tags": ["test", "bc", "route-test"]}, {"id": "file:tests/test_governance_routes.py", "type": "file", "name": "test_governance_routes.py", "filePath": "tests/test_governance_routes.py", "summary": "Test suite for the governance Kanban board routes: stage advancement, rejection, and XLSX export of stage-3 BCs with LOINC-derived system columns.", "tags": ["test", "governance", "route-test"]}, {"id": "file:tests/test_ingestion_routes.py", "type": "file", "name": "test_ingestion_routes.py", "filePath": "tests/test_ingestion_routes.py", "summary": "Test suite for the ingestion pipeline routes: file upload (CSV/JSON), duplicate BC flagging, per-record approve/reject, and bulk approve-all with audit logging.", "tags": ["test", "ingestion", "route-test"]}, {"id": "file:tests/test_ingestion_service.py", "type": "file", "name": "test_ingestion_service.py", "filePath": "tests/test_ingestion_service.py", "summary": "Unit test suite for the ingestion service module, covering field similarity scoring, field mapping/matching, BC validation, deduplication, and XLSX/CSV/JSON parsing and grouping logic.", "tags": ["test", "ingestion", "service-test", "parsing"]}, {"id": "file:tests/test_models.py", "type": "file", "name": "test_models.py", "filePath": "tests/test_models.py", "summary": "Unit tests for the AuditLog, IngestionRecord, and BiomedicalConcept ORM models, verifying JSON-backed property round-tripping, computed averages, and dictionary serialization.", "tags": ["test", "models", "orm"]}, {"id": "file:models/governance.py", "type": "file", "name": "governance.py", "filePath": "models/governance.py", "summary": "SQLAlchemy model for GovernanceRecord, tracking Biomedical Concept approval workflow stage transitions.", "tags": ["data-model", "database", "governance", "tested"]}, {"id": "file:routes/bc.py", "type": "file", "name": "bc.py", "filePath": "routes/bc.py", "summary": "Flask blueprint implementing BC CRUD, NCIt/LOINC metadata lookup, export, and lifecycle actions (submit for review, delete) for Biomedical Concepts.", "tags": ["api-handler", "flask-blueprint", "crud", "routes", "tested"]}, {"id": "file:routes/governance.py", "type": "file", "name": "governance.py", "filePath": "routes/governance.py", "summary": "Flask blueprint for the governance Kanban board \u2014 viewing BCs by stage, exporting the published set, and advancing/rejecting BCs through the approval workflow.", "tags": ["api-handler", "governance", "flask-blueprint", "tested"]}, {"id": "file:routes/ncit.py", "type": "file", "name": "ncit.py", "filePath": "routes/ncit.py", "summary": "Flask blueprint for NCI Thesaurus search and mapping to Biomedical Concepts, including AJAX search and single-concept detail lookups.", "tags": ["api-handler", "ncit", "flask-blueprint", "tested"]}, {"id": "file:services/__init__.py", "type": "file", "name": "__init__.py", "filePath": "services/__init__.py", "summary": "Empty package marker enabling the services directory to be imported as a Python package.", "tags": ["package-init"]}, {"id": "file:services/audit.py", "type": "file", "name": "audit.py", "filePath": "services/audit.py", "summary": "Provides the log_change helper used across services and routes to persist immutable AuditLog rows for every entity mutation.", "tags": ["audit", "utility", "service"]}, {"id": "file:services/bc_service.py", "type": "file", "name": "bc_service.py", "filePath": "services/bc_service.py", "summary": "Core business logic for creating, updating, and managing Biomedical Concepts and their Data Element Concepts, including NCIt remapping/merging and review submission.", "tags": ["service", "business-logic", "crud"]}, {"id": "file:services/export.py", "type": "file", "name": "export.py", "filePath": "services/export.py", "summary": "Exporters that render Biomedical Concept collections to JSON, styled XLSX, governance XLSX, and CDISC ODM-XML formats.", "tags": ["export", "serialization", "service", "tested"]}, {"id": "file:services/governance_service.py", "type": "file", "name": "governance_service.py", "filePath": "services/governance_service.py", "summary": "Implements governance stage transitions (advance/reject) for Biomedical Concepts, creating GovernanceRecord history rows and audit log entries.", "tags": ["service", "governance", "workflow"]}, {"id": "file:services/ncit_api.py", "type": "file", "name": "ncit_api.py", "filePath": "services/ncit_api.py", "summary": "NCI Thesaurus EVS REST API client providing concept search and detailed concept retrieval (parents, semantic type, synonyms, definitions) with caching.", "tags": ["api-client", "ncit", "integration", "service", "tested"]}, {"id": "file:tests/test_export_service.py", "type": "file", "name": "test_export_service.py", "filePath": "tests/test_export_service.py", "summary": "Unit tests covering JSON, XLSX, governance XLSX, and ODM-XML export functions in services/export.py, verifying headers, blanking rules, and document structure.", "tags": ["test", "export", "unit-test"]}, {"id": "file:tests/test_ncit.py", "type": "file", "name": "test_ncit.py", "filePath": "tests/test_ncit.py", "summary": "Unit tests for NCItApiClient.get_concept's extended metadata extraction and the /ncit concept-detail route's JSON responses and error handling.", "tags": ["test", "ncit", "unit-test"]}, {"id": "file:routes/dashboard.py", "type": "file", "name": "dashboard.py", "filePath": "routes/dashboard.py", "summary": "Flask blueprint for the dashboard home page; fetches CDISC Library BC and specialization counts concurrently via a thread pool and combines them with local KPI stats (BC status counts, recent activity, audit log) for the landing page.", "tags": ["dashboard", "api-handler", "kpi", "route"]}, {"id": "file:routes/loinc.py", "type": "file", "name": "loinc.py", "filePath": "routes/loinc.py", "summary": "Flask blueprint exposing a single AJAX search endpoint that proxies term queries to the NLM LOINC API client and returns JSON results.", "tags": ["api-handler", "loinc", "search", "route", "tested"]}, {"id": "file:routes/specializations.py", "type": "file", "name": "specializations.py", "filePath": "routes/specializations.py", "summary": "Flask blueprint managing CDISC dataset specializations: lists local and CDISC Library specializations, and supports manual creation, deletion, and auto-generation of variable-level specializations from a BC's data element concepts.", "tags": ["api-handler", "crud", "specialization", "route", "tested"]}, {"id": "file:services/api_cache.py", "type": "file", "name": "api_cache.py", "filePath": "services/api_cache.py", "summary": "Shared stale-tolerant in-memory cache helper used by the CDISC Library, NCI EVS, and LOINC API clients; serves fresh data for 5 minutes and falls back to stale data for up to an hour if a refresh fails, so a slow upstream never blanks a page that rendered fine a minute ago.", "tags": ["utility", "caching", "resilience", "service", "tested"]}, {"id": "file:services/cdisc_api.py", "type": "file", "name": "cdisc_api.py", "filePath": "services/cdisc_api.py", "summary": "REST client for the CDISC Library COSMOS API; wraps biomedical concept, specialization, and duplicate-check lookups with a 5-minute in-memory cache and provides a publish_bc method for pushing approved BCs to the Library.", "tags": ["api-client", "service", "cdisc-library", "http", "tested"]}, {"id": "file:services/loinc_api.py", "type": "file", "name": "loinc_api.py", "filePath": "services/loinc_api.py", "summary": "Client for the NLM Clinical Tables LOINC search API; builds the query with a configurable set of extra fields, optionally applies HTTP Basic Auth, and normalizes results through the shared API cache.", "tags": ["api-client", "service", "loinc", "http", "tested"]}, {"id": "file:tests/test_cdisc_api_cache.py", "type": "file", "name": "test_cdisc_api_cache.py", "filePath": "tests/test_cdisc_api_cache.py", "summary": "Unit tests for the shared cache helper and CDISCApiClient cache-key behavior, covering fresh/stale/expired cache states, refresh-failure fallback, and that raw API keys never leak into cache keys.", "tags": ["test", "caching", "cdisc-library"]}, {"id": "file:tests/test_loinc.py", "type": "file", "name": "test_loinc.py", "filePath": "tests/test_loinc.py", "summary": "Unit tests for LoincApiClient.search normalization, extra-field handling, Basic Auth toggling, and error handling, plus tests for the /loinc/search Flask route's JSON responses.", "tags": ["test", "loinc", "route"]}, {"id": "file:app.py", "type": "file", "name": "app.py", "filePath": "app.py", "summary": "Flask application factory that configures logging, initializes the shared database and migration extensions, registers all eight feature blueprints, and doubles as the dev-server entry point via `python app.py`.", "tags": ["entry-point", "flask", "application-factory", "blueprint-registration"]}, {"id": "file:config.py", "type": "file", "name": "config.py", "filePath": "config.py", "summary": "Centralizes all environment-driven configuration for the Flask app, including the secret key, database URI, CDISC/NCIt API base URLs and credentials, upload size limit, and dev server port.", "tags": ["configuration", "environment-variables", "flask", "settings"]}, {"id": "file:db_bootstrap.py", "type": "file", "name": "db_bootstrap.py", "filePath": "db_bootstrap.py", "summary": "Brings the app's database to the current Alembic migration head at startup, distinguishing fresh, legacy create_all(), and pre-squash-stamped databases, and raising if the schema predates the squashed baseline.", "tags": ["database-migration", "alembic", "bootstrap", "startup", "tested"]}, {"id": "file:extensions.py", "type": "file", "name": "extensions.py", "filePath": "extensions.py", "summary": "Defines the shared SQLAlchemy `db` and Flask-Migrate `migrate` instances used across the app to avoid circular imports, and configures SQLite WAL mode with a busy timeout so the Flask app and MCP server can coexist as concurrent writers.", "tags": ["database", "sqlalchemy", "shared-extension", "sqlite"]}, {"id": "file:models/__init__.py", "type": "file", "name": "__init__.py", "filePath": "models/__init__.py", "summary": "Package marker for the `models` module that imports the shared `db` instance so all SQLAlchemy model classes register against a single metadata registry.", "tags": ["entry-point", "package-init", "barrel"]}, {"id": "file:tests/test_dashboard.py", "type": "file", "name": "test_dashboard.py", "filePath": "tests/test_dashboard.py", "summary": "Unit tests for the dashboard route, verifying it renders correctly with live CDISC/NCIt API data, degrades gracefully on API errors and empty results, reflects local DB stats accurately, and issues both API calls concurrently.", "tags": ["test", "dashboard", "mocking", "concurrency"]}, {"id": "file:tests/test_db_bootstrap.py", "type": "file", "name": "test_db_bootstrap.py", "filePath": "tests/test_db_bootstrap.py", "summary": "Unit tests for `ensure_db()` covering a fresh baseline upgrade, stamping of a legacy create_all() database, restamping of a pre-squash-stamped database, raising on an outdated schema, and idempotency across repeated runs.", "tags": ["test", "database-migration", "alembic", "fixtures"]}, {"id": "file:mcp_server/__main__.py", "type": "file", "name": "__main__.py", "filePath": "mcp_server/__main__.py", "summary": "Module entry point for `python -m mcp_server`; imports the server's main() coroutine runner and invokes it immediately to start the stdio MCP server loop.", "tags": ["entry-point", "mcp-server", "bootstrap"]}, {"id": "file:mcp_server/server.py", "type": "file", "name": "server.py", "filePath": "mcp_server/server.py", "summary": "MCP server exposing the CDISC curation app's data and write operations as 14 stdio tools (8 read, 6 write); each call is dispatched into a Flask app context shared with the web app so both processes hit the same SQLite database and service layer.", "tags": ["entry-point", "mcp-server", "api-handler", "service", "tested"]}, {"id": "file:models/specialization.py", "type": "file", "name": "specialization.py", "filePath": "models/specialization.py", "summary": "SQLAlchemy model for a dataset specialization (SDTM/CDASH variable-level metadata group) tied to a Biomedical Concept, storing its variable list as a JSON-serialized text column.", "tags": ["data-model", "orm", "database"]}, {"id": "file:tests/test_mcp_server.py", "type": "file", "name": "test_mcp_server.py", "filePath": "tests/test_mcp_server.py", "summary": "Unit tests for the MCP server's `_dispatch` routing and all 14 tool handlers, exercising them directly against a shared in-memory test Flask app (no stdio transport) and asserting on returned data plus AuditLog/GovernanceRecord side effects.", "tags": ["test", "mcp-server", "unit-test"]}, {"id": "file:tests/test_specializations_routes.py", "type": "file", "name": "test_specializations_routes.py", "filePath": "tests/test_specializations_routes.py", "summary": "Route-level tests for the dataset specializations blueprint covering listing, creating, editing/upserting, deleting, detail views, and generating variables from DECs, including audit log verification and CDISC Library error handling.", "tags": ["test", "route-test", "unit-test"]}, {"id": "pipeline:.github/workflows/ci.yml", "type": "pipeline", "name": "ci.yml", "filePath": ".github/workflows/ci.yml", "summary": "GitHub Actions CI workflow that runs on every push and pull request across all branches: installs project and dev dependencies, lints with isort/black/flake8, and runs the pytest suite on Python 3.12.", "tags": ["ci-cd", "deployment", "testing", "linting", "github-actions"]}, {"id": "document:.claude/agent-memory/cdisc-frontend-dev/MEMORY.md", "type": "document", "name": "MEMORY.md", "filePath": ".claude/agent-memory/cdisc-frontend-dev/MEMORY.md", "summary": "Index file for the cdisc-frontend-dev sub-agent's persistent memory, linking to the project foundation and frontend architecture notes.", "tags": ["documentation", "agent-memory", "index"]}, {"id": "document:.claude/agent-memory/cdisc-frontend-dev/frontend_architecture.md", "type": "document", "name": "frontend_architecture.md", "filePath": ".claude/agent-memory/cdisc-frontend-dev/frontend_architecture.md", "summary": "Persistent agent memory documenting the front-end template hierarchy, custom.css class naming conventions, main.js module functions, template-to-route mapping, and Jinja context variable conventions for the CDISC BC Curation UI.", "tags": ["documentation", "frontend", "css", "javascript", "agent-memory"]}, {"id": "document:.claude/agent-memory/cdisc-frontend-dev/project_foundation.md", "type": "document", "name": "project_foundation.md", "filePath": ".claude/agent-memory/cdisc-frontend-dev/project_foundation.md", "summary": "Persistent agent memory describing the Flask application factory pattern, registered blueprints, model and service locations, database setup, and key route implementation decisions for the CDISC BC Curation app.", "tags": ["documentation", "architecture", "flask", "database", "agent-memory"]}, {"id": "document:.claude/agents/cdisc-concept-explorer.md", "type": "document", "name": "cdisc-concept-explorer.md", "filePath": ".claude/agents/cdisc-concept-explorer.md", "summary": "Defines the cdisc-concept-explorer Claude subagent, a CDISC standards specialist that queries the live CDISC Library API to detect duplicate Biomedical Concepts against local drafts, enrich drafts with authoritative metadata, and find dataset specializations for a given BC.", "tags": ["agent-definition", "documentation", "cdisc-library", "api-integration", "subagent-spec"]}, {"id": "document:.claude/agents/cdisc-frontend-dev.md", "type": "document", "name": "cdisc-frontend-dev.md", "filePath": ".claude/agents/cdisc-frontend-dev.md", "summary": "Defines the cdisc-frontend-dev Claude subagent, a front-end specialist scoped to Jinja2 templates, CSS, vanilla JavaScript, and Flask view wiring for the BC curation tool, with mandatory CDISC brand-guideline compliance and an extensive persistent project-memory protocol appended to the spec.", "tags": ["agent-definition", "documentation", "frontend", "memory-system", "brand-guidelines"]}, {"id": "document:.claude/agents/mike.md", "type": "document", "name": "mike.md", "filePath": ".claude/agents/mike.md", "summary": "Defines the mike Claude subagent, a project-manager assistant for cdisc-concept-curation that tracks daily progress in README-PROGRESS.md, prepares git commit messages, flags documentation drift against the code, and always asks before committing or pushing.", "tags": ["agent-definition", "documentation", "project-management", "git-workflow", "progress-tracking"]}, {"id": "config:.mcp.json", "type": "config", "name": ".mcp.json", "filePath": ".mcp.json", "summary": "Registers the cdisc-curation MCP server, launching it via the project virtualenv's Python interpreter running the `mcp_server` package as a module.", "tags": ["configuration", "mcp", "server-registration"]}, {"id": "config:.pre-commit-config.yaml", "type": "config", "name": ".pre-commit-config.yaml", "filePath": ".pre-commit-config.yaml", "summary": "Pre-commit hook configuration that runs isort, black, and flake8 on staged files and executes the full pytest suite before every commit.", "tags": ["configuration", "ci-cd", "linting", "build-system"]}, {"id": "document:CLAUDE.md", "type": "document", "name": "CLAUDE.md", "filePath": "CLAUDE.md", "summary": "Guidance document for Claude Code covering the app's purpose, how to run it, linting and testing commands, the Route-Service-Model architecture with its 8 Flask blueprints and key services, the MCP server, and environment-variable configuration.", "tags": ["documentation", "architecture", "entry-point", "development"]}, {"id": "document:README-PROGRESS.md", "type": "document", "name": "README-PROGRESS.md", "filePath": "README-PROGRESS.md", "summary": "Running project progress log tracking feature status and a dated changelog of fixes and workstream completions such as governance export corrections, audit trail coverage, and LOINC API explorer work.", "tags": ["documentation", "changelog", "project-status"]}, {"id": "document:README.md", "type": "document", "name": "README.md", "filePath": "README.md", "summary": "Primary project README covering prerequisites, installation, environment-variable configuration, running the app and tests, key screens, project structure, and the external CDISC/NCIt/LOINC APIs the platform integrates with.", "tags": ["documentation", "entry-point", "installation", "overview"]}, {"id": "config:pyproject.toml", "type": "config", "name": "pyproject.toml", "filePath": "pyproject.toml", "summary": "Tool configuration setting a shared 200-character line length for black (targeting Python 3.9-3.11) and isort (using the black-compatible profile).", "tags": ["configuration", "build-system", "formatting"]}, {"id": "document:requirements-dev.txt", "type": "document", "name": "requirements-dev.txt", "filePath": "requirements-dev.txt", "summary": "Pinned development-only dependency list: black, flake8 (with mccabe/pycodestyle/pyflakes), isort, pre-commit, pytest, and pytest-flask, used for linting and testing.", "tags": ["dependencies", "development", "linting", "testing"]}, {"id": "document:requirements.txt", "type": "document", "name": "requirements.txt", "filePath": "requirements.txt", "summary": "Pinned runtime dependency list for the Flask application, including Flask, Flask-SQLAlchemy, Flask-Migrate, SQLAlchemy, Alembic, pandas, openpyxl, lxml, requests, and the mcp SDK.", "tags": ["dependencies", "runtime", "build-system"]}, {"id": "document:files/plan.md", "type": "document", "name": "plan.md", "filePath": "files/plan.md", "summary": "Initial high-level project brief for the CDISC Biomedical Concept Curation application, specifying the target UI mockup to match, the key reference files to review, the required agents (cdisc-concept-explorer, ncit-concept-resolver, cdisc-frontend-dev, mike), and the constraint that only the CDISC_API_KEY environment variable be used for API authentication.", "tags": ["documentation", "planning", "project-brief", "requirements"]}, {"id": "document:files/implementation.md", "type": "document", "name": "implementation.md", "filePath": "files/implementation.md", "summary": "Detailed implementation plan for the Flask/Jinja BC curation app, laying out the directory structure, SQLAlchemy data models, Flask blueprint/route table, service-layer API surface, template inventory, and a four-phase build roadmap from MVP through export and polish.", "tags": ["documentation", "architecture", "implementation-plan", "specification"]}, {"id": "document:files/result.md", "type": "document", "name": "result.md", "filePath": "files/result.md", "summary": "Post-implementation status report confirming all 8 Flask routes return HTTP 200 and enumerating delivered capabilities: file ingestion with AI field mapping, BC CRUD, NCIt mapping, a 4-stage governance Kanban, an immutable audit trail, and JSON/XLSX/ODM-XML export.", "tags": ["documentation", "summary", "status-report", "verification"]}, {"id": "file:templates/base.html", "type": "file", "name": "base.html", "filePath": "templates/base.html", "summary": "Master Jinja2 layout providing the sidebar navigation, top bar, flash-message rendering, and template blocks (title, page_title, user_info, content, extra_js) that every other page in the app extends.", "tags": ["layout", "navigation", "entry-point", "bootstrap", "component"]}, {"id": "file:templates/audit.html", "type": "file", "name": "audit.html", "filePath": "templates/audit.html", "summary": "Audit trail page listing all curation actions (create/update/delete/advance/reject/publish/ingest) with actor/entity/date filters, CSV/JSON export links, expandable before/after diff rows, and pagination.", "tags": ["audit-trail", "data-table", "filter", "pagination", "export"]}, {"id": "file:templates/bc_detail.html", "type": "file", "name": "bc_detail.html", "filePath": "templates/bc_detail.html", "summary": "Create/edit form for a single Biomedical Concept covering identification, NCIt and LOINC lookups (with async metadata fetch in extra_js), classification, definition, metadata/status, and an editable Data Element Concepts (DECs) table.", "tags": ["form", "crud", "ncit-integration", "loinc-integration", "dynamic-form"]}, {"id": "file:templates/bc_list.html", "type": "file", "name": "bc_list.html", "filePath": "templates/bc_list.html", "summary": "Searchable, filterable, paginated table of locally curated Biomedical Concepts with status badges, JSON/XLSX/ODM-XML export links, and a delete-confirmation modal.", "tags": ["list-view", "search", "filter", "pagination", "export"]}, {"id": "file:templates/dashboard.html", "type": "file", "name": "dashboard.html", "filePath": "templates/dashboard.html", "summary": "Main landing dashboard showing KPI cards, recent submissions, the governance pipeline snapshot, paginated/searchable views of CDISC Library BCs and dataset specializations, an AI curation pipeline progress tracker, and quick action links.", "tags": ["dashboard", "kpi-metrics", "pipeline-status", "pagination", "cdisc-library"]}, {"id": "file:templates/governance.html", "type": "file", "name": "governance.html", "filePath": "templates/governance.html", "summary": "Four-column Kanban board (Provisional -> SME Review -> CDISC Approval -> Published) for advancing or rejecting Biomedical Concepts through the governance workflow, with summary counts and an export-filename modal.", "tags": ["kanban", "governance", "workflow", "modal", "export"]}, {"id": "file:templates/ingestion.html", "type": "file", "name": "ingestion.html", "filePath": "templates/ingestion.html", "summary": "File upload page (drag-and-drop or click) for XLSX/CSV/JSON ingestion, with a parsed-results queue table showing field-mapping confidence, validation errors, duplicate flags, and per-row approve/reject actions.", "tags": ["file-upload", "drag-drop", "queue", "validation", "form"]}, {"id": "file:templates/library_bc_detail.html", "type": "file", "name": "library_bc_detail.html", "filePath": "templates/library_bc_detail.html", "summary": "Read-only detail view of a single Biomedical Concept fetched from the published CDISC Library, showing identification, definition, LOINC coding, metadata, and its Data Element Concepts.", "tags": ["read-only-view", "cdisc-library", "loinc-integration", "detail-view"]}, {"id": "file:templates/library_spec_detail.html", "type": "file", "name": "library_spec_detail.html", "filePath": "templates/library_spec_detail.html", "summary": "Read-only detail view of a Dataset Specialization from the published CDISC Library, showing identification/domain metadata and a full variables table (role, codelist, assigned term, mandatory flags, relationship).", "tags": ["read-only-view", "cdisc-library", "variables-table", "detail-view"]}, {"id": "file:templates/ncit_mapping.html", "type": "file", "name": "ncit_mapping.html", "filePath": "templates/ncit_mapping.html", "summary": "NCI Thesaurus search page plus a conflict-resolution queue listing Biomedical Concepts with unresolved or low-confidence NCIt mappings that block governance advancement, with a confidence-score legend.", "tags": ["ncit-integration", "search", "conflict-resolution", "confidence-score"]}, {"id": "file:templates/specializations.html", "type": "file", "name": "specializations.html", "filePath": "templates/specializations.html", "summary": "Dataset Specialization management page with a collapsible create/edit form (VLM group ID, BC selector, SDTM/CDASH domain, dynamic variables table, generate-from-DEC-templates action) and a list/edit/delete table of existing specializations.", "tags": ["crud", "dynamic-form", "sdtm", "cdash", "variable-table"]}, {"id": "document:.claude/agent-memory/cdisc-concept-explorer/MEMORY.md", "type": "document", "name": "MEMORY.md", "filePath": ".claude/agent-memory/cdisc-concept-explorer/MEMORY.md", "summary": "Single-line agent memory index for the cdisc-concept-explorer agent, pointing to the verified CDISC API reference notes.", "tags": ["documentation", "agent-memory", "reference"]}, {"id": "document:.claude/agent-memory/cdisc-concept-explorer/reference_api_endpoints.md", "type": "document", "name": "reference_api_endpoints.md", "filePath": ".claude/agent-memory/cdisc-concept-explorer/reference_api_endpoints.md", "summary": "Verified reference notes on the CDISC Library COSMOS v2 API: base URL, working endpoints, lack of pagination, and field names for biomedical concept and dataset specialization records.", "tags": ["documentation", "api-reference", "cdisc-library", "agent-memory"]}, {"id": "document:.claude/skills/code-review/SKILL.md", "type": "document", "name": "SKILL.md", "filePath": ".claude/skills/code-review/SKILL.md", "summary": "Project-specific code review checklist for cdisc-concept-curation covering route/service/model layering, audit log coverage, Alembic migration discipline, API client error handling, test isolation, and TDD conventions.", "tags": ["documentation", "code-review", "development", "checklist"]}, {"id": "document:.claude/skills/run-concept-curation/SKILL.md", "type": "document", "name": "SKILL.md", "filePath": ".claude/skills/run-concept-curation/SKILL.md", "summary": "Instructions for running and smoke-testing the Flask dev server, including the preferred automated smoke-test script, manual startup steps, and troubleshooting tips.", "tags": ["documentation", "development", "smoke-test", "deployment"]}, {"id": "file:.claude/skills/run-concept-curation/smoke.sh", "type": "file", "name": "smoke.sh", "filePath": ".claude/skills/run-concept-curation/smoke.sh", "summary": "Shell script that boots the Flask app on a throwaway SQLite database and port, runs a battery of HTTP checks against key routes (dashboard, BC CRUD, governance, NCIt, audit, ingestion, specializations, export), and tears the server down.", "tags": ["script", "smoke-test", "automation", "development"]}, {"id": "config:.flake8", "type": "config", "name": ".flake8", "filePath": ".flake8", "summary": "Flake8 lint configuration setting max line length to 200, ignoring black-conflicting rules (E203/W503) and SQLAlchemy's `== None` idiom (E711), and excluding build/venv directories.", "tags": ["configuration", "linting", "build-system", "python"]}, {"id": "config:.hintrc", "type": "config", "name": ".hintrc", "filePath": ".hintrc", "summary": "webhint configuration extending the 'development' ruleset and disabling the no-inline-styles rule for HTML/CSS linting.", "tags": ["configuration", "linting", "frontend"]}, {"id": "config:.ua/.understandignore", "type": "config", "name": ".understandignore", "filePath": ".ua/.understandignore", "summary": "Gitignore-style exclusion list for the Understand-Anything analysis tool, with commented-out suggestions mirroring the project's .gitignore (Python caches, virtualenvs, egg metadata) that can be uncommented to activate.", "tags": ["configuration", "tooling", "analysis"]}, {"id": "config:.ua/config.json", "type": "config", "name": "config.json", "filePath": ".ua/config.json", "summary": "Single-key configuration for the Understand-Anything tool setting the output language for generated analysis text to English.", "tags": ["configuration", "tooling", "analysis"]}, {"id": "file:files/BC Curation Principles and Completion GLs.xlsx", "type": "file", "name": "BC Curation Principles and Completion GLs.xlsx", "filePath": "files/BC Curation Principles and Completion GLs.xlsx", "summary": "Reference spreadsheet documenting BC curation principles and field-completion guidelines used by curators when authoring Biomedical Concepts.", "tags": ["reference", "documentation", "spreadsheet", "curation-guidance"]}, {"id": "file:files/BC DEC Templates.xlsx", "type": "file", "name": "BC DEC Templates.xlsx", "filePath": "files/BC DEC Templates.xlsx", "summary": "Template spreadsheet defining the standard row/column layout for Data Element Concepts, used as an ingestion source template for curators.", "tags": ["template", "spreadsheet", "ingestion", "reference"]}, {"id": "file:files/BC Examples.xlsx", "type": "file", "name": "BC Examples.xlsx", "filePath": "files/BC Examples.xlsx", "summary": "Example spreadsheet of fully-populated Biomedical Concepts, used as a reference/sample dataset for the ingestion pipeline and curator training.", "tags": ["reference", "spreadsheet", "sample-data", "ingestion"]}, {"id": "file:files/BC Governance.docx", "type": "file", "name": "BC Governance.docx", "filePath": "files/BC Governance.docx", "summary": "Word document describing the governance process and stages (Provisional, SME Review, CDISC Approval, Published) that a curated Biomedical Concept moves through.", "tags": ["documentation", "governance", "reference"]}, {"id": "file:mcp_server/__init__.py", "type": "file", "name": "__init__.py", "filePath": "mcp_server/__init__.py", "summary": "Package marker and docstring for the mcp_server package, noting it is run via `python -m mcp_server` and deliberately named to avoid shadowing the MCP SDK package.", "tags": ["entry-point", "package-init", "mcp"]}, {"id": "document:migrations/README", "type": "document", "name": "README", "filePath": "migrations/README", "summary": "One-line note identifying the migrations directory as a single-database Flask-Migrate/Alembic configuration.", "tags": ["documentation", "database", "migration"]}, {"id": "config:migrations/alembic.ini", "type": "config", "name": "alembic.ini", "filePath": "migrations/alembic.ini", "summary": "Alembic configuration file controlling migration script location, logging setup, and version-file naming for the Flask-Migrate managed schema.", "tags": ["configuration", "database", "migration", "alembic"]}, {"id": "file:migrations/env.py", "type": "file", "name": "env.py", "filePath": "migrations/env.py", "summary": "Alembic environment script that wires Flask-Migrate's engine/metadata into Alembic's migration context, supporting both offline (SQL-only) and online (live-connection) migration runs.", "tags": ["database", "migration", "alembic", "flask-migrate"]}, {"id": "file:migrations/script.py.mako", "type": "file", "name": "script.py.mako", "filePath": "migrations/script.py.mako", "summary": "Mako template used by `flask db migrate` to scaffold new Alembic revision files with the standard revision metadata and upgrade/downgrade function stubs.", "tags": ["template", "database", "migration", "code-generation"]}, {"id": "file:migrations/versions/51d4a009d291_baseline_initial_schema.py", "type": "file", "name": "51d4a009d291_baseline_initial_schema.py", "filePath": "migrations/versions/51d4a009d291_baseline_initial_schema.py", "summary": "Baseline Alembic migration that creates the initial database schema: audit_logs, biomedical_concepts, ingestion_records, data_element_concepts, dataset_specializations, and governance_records tables, with their downgrade counterparts.", "tags": ["database", "migration", "schema", "baseline"]}, {"id": "file:routes/__init__.py", "type": "file", "name": "__init__.py", "filePath": "routes/__init__.py", "summary": "Empty package marker file that makes routes/ a Python package for the Flask blueprint modules it contains.", "tags": ["package-init", "empty"]}, {"id": "file:static/css/custom.css", "type": "file", "name": "custom.css", "filePath": "static/css/custom.css", "summary": "Main application stylesheet defining design tokens (colors, spacing), base resets, app shell layout (sidebar, topbar, content area), and component styles (cards, flash messages, nav items, badges) for the CDISC BC curation UI.", "tags": ["stylesheet", "design-system", "layout", "ui"]}, {"id": "file:static/js/main.js", "type": "file", "name": "main.js", "filePath": "static/js/main.js", "summary": "Client-side interactivity module for the BC curation platform: flash-message dismissal, drag-and-drop file upload, NCIt/LOINC lookup and search panels, dynamic DEC table rows, governance Kanban actions, audit-diff toggles, and shared fetch/toast helpers, all wired up on DOMContentLoaded.", "tags": ["frontend", "vanilla-js", "ui-interactivity", "fetch-api"]}, {"id": "file:tests/__init__.py", "type": "file", "name": "__init__.py", "filePath": "tests/__init__.py", "summary": "Empty package marker file that makes tests/ a discoverable Python package for pytest.", "tags": ["package-init", "test", "empty"]}, {"id": "file:files/cdisc_bc_ai_platform.html", "type": "file", "name": "cdisc_bc_ai_platform.html", "filePath": "files/cdisc_bc_ai_platform.html", "summary": "No summary available", "tags": ["untagged"]}, {"id": "file:files/cdisc_smart_goals_infographic.html", "type": "file", "name": "cdisc_smart_goals_infographic.html", "filePath": "files/cdisc_smart_goals_infographic.html", "summary": "No summary available", "tags": ["untagged"]}], "importEdges": [{"source": "file:models/audit.py", "target": "file:extensions.py", "type": "imports", "direction": "forward", "weight": 0.7}, {"source": "file:models/bc.py", "target": "file:extensions.py", "type": "imports", "direction": "forward", "weight": 0.7}, {"source": "file:models/ingestion.py", "target": "file:extensions.py", "type": "imports", "direction": "forward", "weight": 0.7}, {"source": "file:routes/audit.py", "target": "file:models/audit.py", "type": "imports", "direction": "forward", "weight": 0.7}, {"source": "file:routes/ingestion.py", "target": "file:extensions.py", "type": "imports", "direction": "forward", "weight": 0.7}, {"source": "file:routes/ingestion.py", "target": "file:models/audit.py", "type": "imports", "direction": "forward", "weight": 0.7}, {"source": "file:routes/ingestion.py", "target": "file:models/bc.py", "type": "imports", "direction": "forward", "weight": 0.7}, {"source": "file:routes/ingestion.py", "target": "file:models/ingestion.py", "type": "imports", "direction": "forward", "weight": 0.7}, {"source": "file:routes/ingestion.py", "target": "file:services/ingestion.py", "type": "imports", "direction": "forward", "weight": 0.7}, {"source": "file:tests/conftest.py", "target": "file:app.py", "type": "imports", "direction": "forward", "weight": 0.7}, {"source": "file:tests/conftest.py", "target": "file:extensions.py", "type": "imports", "direction": "forward", "weight": 0.7}, {"source": "file:tests/conftest.py", "target": "file:models/audit.py", "type": "imports", "direction": "forward", "weight": 0.7}, {"source": "file:tests/conftest.py", "target": "file:models/bc.py", "type": "imports", "direction": "forward", "weight": 0.7}, {"source": "file:tests/conftest.py", "target": "file:models/governance.py", "type": "imports", "direction": "forward", "weight": 0.7}, {"source": "file:tests/conftest.py", "target": "file:models/ingestion.py", "type": "imports", "direction": "forward", "weight": 0.7}, {"source": "file:tests/test_audit_routes.py", "target": "file:extensions.py", "type": "imports", "direction": "forward", "weight": 0.7}, {"source": "file:tests/test_audit_routes.py", "target": "file:models/audit.py", "type": "imports", "direction": "forward", "weight": 0.7}, {"source": "file:tests/test_bc_routes.py", "target": "file:extensions.py", "type": "imports", "direction": "forward", "weight": 0.7}, {"source": "file:tests/test_bc_routes.py", "target": "file:models/audit.py", "type": "imports", "direction": "forward", "weight": 0.7}, {"source": "file:tests/test_bc_routes.py", "target": "file:models/bc.py", "type": "imports", "direction": "forward", "weight": 0.7}, {"source": "file:tests/test_governance_routes.py", "target": "file:extensions.py", "type": "imports", "direction": "forward", "weight": 0.7}, {"source": "file:tests/test_governance_routes.py", "target": "file:models/audit.py", "type": "imports", "direction": "forward", "weight": 0.7}, {"source": "file:tests/test_governance_routes.py", "target": "file:models/bc.py", "type": "imports", "direction": "forward", "weight": 0.7}, {"source": "file:tests/test_governance_routes.py", "target": "file:models/governance.py", "type": "imports", "direction": "forward", "weight": 0.7}, {"source": "file:tests/test_ingestion_routes.py", "target": "file:extensions.py", "type": "imports", "direction": "forward", "weight": 0.7}, {"source": "file:tests/test_ingestion_routes.py", "target": "file:models/audit.py", "type": "imports", "direction": "forward", "weight": 0.7}, {"source": "file:tests/test_ingestion_routes.py", "target": "file:models/bc.py", "type": "imports", "direction": "forward", "weight": 0.7}, {"source": "file:tests/test_ingestion_routes.py", "target": "file:models/ingestion.py", "type": "imports", "direction": "forward", "weight": 0.7}, {"source": "file:tests/test_ingestion_service.py", "target": "file:services/ingestion.py", "type": "imports", "direction": "forward", "weight": 0.7}, {"source": "file:tests/test_models.py", "target": "file:extensions.py", "type": "imports", "direction": "forward", "weight": 0.7}, {"source": "file:tests/test_models.py", "target": "file:models/audit.py", "type": "imports", "direction": "forward", "weight": 0.7}, {"source": "file:tests/test_models.py", "target": "file:models/bc.py", "type": "imports", "direction": "forward", "weight": 0.7}, {"source": "file:tests/test_models.py", "target": "file:models/ingestion.py", "type": "imports", "direction": "forward", "weight": 0.7}, {"source": "file:models/governance.py", "target": "file:extensions.py", "type": "imports", "direction": "forward", "weight": 0.7}, {"source": "file:routes/bc.py", "target": "file:extensions.py", "type": "imports", "direction": "forward", "weight": 0.7}, {"source": "file:routes/bc.py", "target": "file:models/bc.py", "type": "imports", "direction": "forward", "weight": 0.7}, {"source": "file:routes/bc.py", "target": "file:models/governance.py", "type": "imports", "direction": "forward", "weight": 0.7}, {"source": "file:routes/bc.py", "target": "file:services/__init__.py", "type": "imports", "direction": "forward", "weight": 0.7}, {"source": "file:routes/bc.py", "target": "file:services/audit.py", "type": "imports", "direction": "forward", "weight": 0.7}, {"source": "file:routes/bc.py", "target": "file:services/bc_service.py", "type": "imports", "direction": "forward", "weight": 0.7}, {"source": "file:routes/bc.py", "target": "file:services/cdisc_api.py", "type": "imports", "direction": "forward", "weight": 0.7}, {"source": "file:routes/bc.py", "target": "file:services/export.py", "type": "imports", "direction": "forward", "weight": 0.7}, {"source": "file:routes/bc.py", "target": "file:services/loinc_api.py", "type": "imports", "direction": "forward", "weight": 0.7}, {"source": "file:routes/bc.py", "target": "file:services/ncit_api.py", "type": "imports", "direction": "forward", "weight": 0.7}, {"source": "file:routes/governance.py", "target": "file:extensions.py", "type": "imports", "direction": "forward", "weight": 0.7}, {"source": "file:routes/governance.py", "target": "file:models/bc.py", "type": "imports", "direction": "forward", "weight": 0.7}, {"source": "file:routes/governance.py", "target": "file:services/__init__.py", "type": "imports", "direction": "forward", "weight": 0.7}, {"source": "file:routes/governance.py", "target": "file:services/export.py", "type": "imports", "direction": "forward", "weight": 0.7}, {"source": "file:routes/governance.py", "target": "file:services/governance_service.py", "type": "imports", "direction": "forward", "weight": 0.7}, {"source": "file:routes/ncit.py", "target": "file:extensions.py", "type": "imports", "direction": "forward", "weight": 0.7}, {"source": "file:routes/ncit.py", "target": "file:models/bc.py", "type": "imports", "direction": "forward", "weight": 0.7}, {"source": "file:routes/ncit.py", "target": "file:services/__init__.py", "type": "imports", "direction": "forward", "weight": 0.7}, {"source": "file:routes/ncit.py", "target": "file:services/bc_service.py", "type": "imports", "direction": "forward", "weight": 0.7}, {"source": "file:routes/ncit.py", "target": "file:services/ncit_api.py", "type": "imports", "direction": "forward", "weight": 0.7}, {"source": "file:services/audit.py", "target": "file:extensions.py", "type": "imports", "direction": "forward", "weight": 0.7}, {"source": "file:services/audit.py", "target": "file:models/audit.py", "type": "imports", "direction": "forward", "weight": 0.7}, {"source": "file:services/bc_service.py", "target": "file:extensions.py", "type": "imports", "direction": "forward", "weight": 0.7}, {"source": "file:services/bc_service.py", "target": "file:models/bc.py", "type": "imports", "direction": "forward", "weight": 0.7}, {"source": "file:services/bc_service.py", "target": "file:models/governance.py", "type": "imports", "direction": "forward", "weight": 0.7}, {"source": "file:services/bc_service.py", "target": "file:models/specialization.py", "type": "imports", "direction": "forward", "weight": 0.7}, {"source": "file:services/bc_service.py", "target": "file:services/audit.py", "type": "imports", "direction": "forward", "weight": 0.7}, {"source": "file:services/governance_service.py", "target": "file:extensions.py", "type": "imports", "direction": "forward", "weight": 0.7}, {"source": "file:services/governance_service.py", "target": "file:models/governance.py", "type": "imports", "direction": "forward", "weight": 0.7}, {"source": "file:services/governance_service.py", "target": "file:services/audit.py", "type": "imports", "direction": "forward", "weight": 0.7}, {"source": "file:services/governance_service.py", "target": "file:services/bc_service.py", "type": "imports", "direction": "forward", "weight": 0.7}, {"source": "file:services/ncit_api.py", "target": "file:services/api_cache.py", "type": "imports", "direction": "forward", "weight": 0.7}, {"source": "file:tests/test_export_service.py", "target": "file:extensions.py", "type": "imports", "direction": "forward", "weight": 0.7}, {"source": "file:tests/test_export_service.py", "target": "file:models/bc.py", "type": "imports", "direction": "forward", "weight": 0.7}, {"source": "file:tests/test_export_service.py", "target": "file:services/export.py", "type": "imports", "direction": "forward", "weight": 0.7}, {"source": "file:tests/test_ncit.py", "target": "file:services/ncit_api.py", "type": "imports", "direction": "forward", "weight": 0.7}, {"source": "file:routes/dashboard.py", "target": "file:models/audit.py", "type": "imports", "direction": "forward", "weight": 0.7}, {"source": "file:routes/dashboard.py", "target": "file:models/bc.py", "type": "imports", "direction": "forward", "weight": 0.7}, {"source": "file:routes/dashboard.py", "target": "file:services/cdisc_api.py", "type": "imports", "direction": "forward", "weight": 0.7}, {"source": "file:routes/loinc.py", "target": "file:services/loinc_api.py", "type": "imports", "direction": "forward", "weight": 0.7}, {"source": "file:routes/specializations.py", "target": "file:extensions.py", "type": "imports", "direction": "forward", "weight": 0.7}, {"source": "file:routes/specializations.py", "target": "file:models/bc.py", "type": "imports", "direction": "forward", "weight": 0.7}, {"source": "file:routes/specializations.py", "target": "file:models/specialization.py", "type": "imports", "direction": "forward", "weight": 0.7}, {"source": "file:routes/specializations.py", "target": "file:services/audit.py", "type": "imports", "direction": "forward", "weight": 0.7}, {"source": "file:routes/specializations.py", "target": "file:services/cdisc_api.py", "type": "imports", "direction": "forward", "weight": 0.7}, {"source": "file:services/cdisc_api.py", "target": "file:services/api_cache.py", "type": "imports", "direction": "forward", "weight": 0.7}, {"source": "file:services/loinc_api.py", "target": "file:services/api_cache.py", "type": "imports", "direction": "forward", "weight": 0.7}, {"source": "file:tests/test_cdisc_api_cache.py", "target": "file:services/cdisc_api.py", "type": "imports", "direction": "forward", "weight": 0.7}, {"source": "file:tests/test_loinc.py", "target": "file:services/loinc_api.py", "type": "imports", "direction": "forward", "weight": 0.7}, {"source": "file:app.py", "target": "file:config.py", "type": "imports", "direction": "forward", "weight": 0.7}, {"source": "file:app.py", "target": "file:extensions.py", "type": "imports", "direction": "forward", "weight": 0.7}, {"source": "file:db_bootstrap.py", "target": "file:extensions.py", "type": "imports", "direction": "forward", "weight": 0.7}, {"source": "file:models/__init__.py", "target": "file:extensions.py", "type": "imports", "direction": "forward", "weight": 0.7}, {"source": "file:tests/test_dashboard.py", "target": "file:extensions.py", "type": "imports", "direction": "forward", "weight": 0.7}, {"source": "file:tests/test_dashboard.py", "target": "file:models/bc.py", "type": "imports", "direction": "forward", "weight": 0.7}, {"source": "file:tests/test_db_bootstrap.py", "target": "file:app.py", "type": "imports", "direction": "forward", "weight": 0.7}, {"source": "file:tests/test_db_bootstrap.py", "target": "file:db_bootstrap.py", "type": "imports", "direction": "forward", "weight": 0.7}, {"source": "file:tests/test_db_bootstrap.py", "target": "file:extensions.py", "type": "imports", "direction": "forward", "weight": 0.7}, {"source": "file:mcp_server/__main__.py", "target": "file:mcp_server/server.py", "type": "imports", "direction": "forward", "weight": 0.7}, {"source": "file:models/specialization.py", "target": "file:extensions.py", "type": "imports", "direction": "forward", "weight": 0.7}, {"source": "file:tests/test_mcp_server.py", "target": "file:extensions.py", "type": "imports", "direction": "forward", "weight": 0.7}, {"source": "file:tests/test_mcp_server.py", "target": "file:mcp_server/server.py", "type": "imports", "direction": "forward", "weight": 0.7}, {"source": "file:tests/test_mcp_server.py", "target": "file:models/audit.py", "type": "imports", "direction": "forward", "weight": 0.7}, {"source": "file:tests/test_mcp_server.py", "target": "file:models/bc.py", "type": "imports", "direction": "forward", "weight": 0.7}, {"source": "file:tests/test_mcp_server.py", "target": "file:models/governance.py", "type": "imports", "direction": "forward", "weight": 0.7}, {"source": "file:tests/test_mcp_server.py", "target": "file:models/ingestion.py", "type": "imports", "direction": "forward", "weight": 0.7}, {"source": "file:tests/test_mcp_server.py", "target": "file:models/specialization.py", "type": "imports", "direction": "forward", "weight": 0.7}, {"source": "file:tests/test_specializations_routes.py", "target": "file:extensions.py", "type": "imports", "direction": "forward", "weight": 0.7}, {"source": "file:tests/test_specializations_routes.py", "target": "file:models/audit.py", "type": "imports", "direction": "forward", "weight": 0.7}, {"source": "file:tests/test_specializations_routes.py", "target": "file:models/bc.py", "type": "imports", "direction": "forward", "weight": 0.7}, {"source": "file:tests/test_specializations_routes.py", "target": "file:models/specialization.py", "type": "imports", "direction": "forward", "weight": 0.7}], "allEdges": [{"source": "file:models/audit.py", "target": "file:extensions.py", "type": "imports", "direction": "forward", "weight": 0.7}, {"source": "file:models/bc.py", "target": "file:extensions.py", "type": "imports", "direction": "forward", "weight": 0.7}, {"source": "file:models/ingestion.py", "target": "file:extensions.py", "type": "imports", "direction": "forward", "weight": 0.7}, {"source": "file:routes/audit.py", "target": "file:models/audit.py", "type": "imports", "direction": "forward", "weight": 0.7}, {"source": "file:routes/ingestion.py", "target": "file:extensions.py", "type": "imports", "direction": "forward", "weight": 0.7}, {"source": "file:routes/ingestion.py", "target": "file:models/audit.py", "type": "imports", "direction": "forward", "weight": 0.7}, {"source": "file:routes/ingestion.py", "target": "file:models/bc.py", "type": "imports", "direction": "forward", "weight": 0.7}, {"source": "file:routes/ingestion.py", "target": "file:models/ingestion.py", "type": "imports", "direction": "forward", "weight": 0.7}, {"source": "file:routes/ingestion.py", "target": "file:services/ingestion.py", "type": "imports", "direction": "forward", "weight": 0.7}, {"source": "file:tests/conftest.py", "target": "file:app.py", "type": "imports", "direction": "forward", "weight": 0.7}, {"source": "file:tests/conftest.py", "target": "file:extensions.py", "type": "imports", "direction": "forward", "weight": 0.7}, {"source": "file:tests/conftest.py", "target": "file:models/audit.py", "type": "imports", "direction": "forward", "weight": 0.7}, {"source": "file:tests/conftest.py", "target": "file:models/bc.py", "type": "imports", "direction": "forward", "weight": 0.7}, {"source": "file:tests/conftest.py", "target": "file:models/governance.py", "type": "imports", "direction": "forward", "weight": 0.7}, {"source": "file:tests/conftest.py", "target": "file:models/ingestion.py", "type": "imports", "direction": "forward", "weight": 0.7}, {"source": "file:models/audit.py", "target": "file:tests/test_models.py", "type": "tested_by", "direction": "forward", "weight": 0.5}, {"source": "file:models/bc.py", "target": "file:tests/test_models.py", "type": "tested_by", "direction": "forward", "weight": 0.5}, {"source": "file:models/bc.py", "target": "file:tests/test_bc_routes.py", "type": "tested_by", "direction": "forward", "weight": 0.5}, {"source": "file:models/ingestion.py", "target": "file:tests/test_models.py", "type": "tested_by", "direction": "forward", "weight": 0.5}, {"source": "file:routes/audit.py", "target": "file:tests/test_audit_routes.py", "type": "tested_by", "direction": "forward", "weight": 0.5}, {"source": "file:routes/ingestion.py", "target": "file:tests/test_ingestion_routes.py", "type": "tested_by", "direction": "forward", "weight": 0.5}, {"source": "file:services/ingestion.py", "target": "file:tests/test_ingestion_service.py", "type": "tested_by", "direction": "forward", "weight": 0.5}, {"source": "file:tests/test_audit_routes.py", "target": "file:extensions.py", "type": "imports", "direction": "forward", "weight": 0.7}, {"source": "file:tests/test_audit_routes.py", "target": "file:models/audit.py", "type": "imports", "direction": "forward", "weight": 0.7}, {"source": "file:tests/test_bc_routes.py", "target": "file:extensions.py", "type": "imports", "direction": "forward", "weight": 0.7}, {"source": "file:tests/test_bc_routes.py", "target": "file:models/audit.py", "type": "imports", "direction": "forward", "weight": 0.7}, {"source": "file:tests/test_bc_routes.py", "target": "file:models/bc.py", "type": "imports", "direction": "forward", "weight": 0.7}, {"source": "file:tests/test_governance_routes.py", "target": "file:extensions.py", "type": "imports", "direction": "forward", "weight": 0.7}, {"source": "file:tests/test_governance_routes.py", "target": "file:models/audit.py", "type": "imports", "direction": "forward", "weight": 0.7}, {"source": "file:tests/test_governance_routes.py", "target": "file:models/bc.py", "type": "imports", "direction": "forward", "weight": 0.7}, {"source": "file:tests/test_governance_routes.py", "target": "file:models/governance.py", "type": "imports", "direction": "forward", "weight": 0.7}, {"source": "file:tests/test_ingestion_routes.py", "target": "file:extensions.py", "type": "imports", "direction": "forward", "weight": 0.7}, {"source": "file:tests/test_ingestion_routes.py", "target": "file:models/audit.py", "type": "imports", "direction": "forward", "weight": 0.7}, {"source": "file:tests/test_ingestion_routes.py", "target": "file:models/bc.py", "type": "imports", "direction": "forward", "weight": 0.7}, {"source": "file:tests/test_ingestion_routes.py", "target": "file:models/ingestion.py", "type": "imports", "direction": "forward", "weight": 0.7}, {"source": "file:tests/test_ingestion_service.py", "target": "file:services/ingestion.py", "type": "imports", "direction": "forward", "weight": 0.7}, {"source": "file:tests/test_models.py", "target": "file:extensions.py", "type": "imports", "direction": "forward", "weight": 0.7}, {"source": "file:tests/test_models.py", "target": "file:models/audit.py", "type": "imports", "direction": "forward", "weight": 0.7}, {"source": "file:tests/test_models.py", "target": "file:models/bc.py", "type": "imports", "direction": "forward", "weight": 0.7}, {"source": "file:tests/test_models.py", "target": "file:models/ingestion.py", "type": "imports", "direction": "forward", "weight": 0.7}, {"source": "file:models/audit.py", "target": "file:tests/test_audit_routes.py", "type": "tested_by", "direction": "forward", "weight": 0.5}, {"source": "file:routes/bc.py", "target": "file:tests/test_bc_routes.py", "type": "tested_by", "direction": "forward", "weight": 0.5}, {"source": "file:models/bc.py", "target": "file:tests/test_governance_routes.py", "type": "tested_by", "direction": "forward", "weight": 0.5}, {"source": "file:models/governance.py", "target": "file:tests/test_governance_routes.py", "type": "tested_by", "direction": "forward", "weight": 0.5}, {"source": "file:routes/governance.py", "target": "file:tests/test_governance_routes.py", "type": "tested_by", "direction": "forward", "weight": 0.5}, {"source": "file:models/bc.py", "target": "file:tests/test_ingestion_routes.py", "type": "tested_by", "direction": "forward", "weight": 0.5}, {"source": "file:models/ingestion.py", "target": "file:tests/test_ingestion_routes.py", "type": "tested_by", "direction": "forward", "weight": 0.5}, {"source": "file:models/governance.py", "target": "file:extensions.py", "type": "imports", "direction": "forward", "weight": 0.7}, {"source": "file:routes/bc.py", "target": "file:extensions.py", "type": "imports", "direction": "forward", "weight": 0.7}, {"source": "file:routes/bc.py", "target": "file:models/bc.py", "type": "imports", "direction": "forward", "weight": 0.7}, {"source": "file:routes/bc.py", "target": "file:models/governance.py", "type": "imports", "direction": "forward", "weight": 0.7}, {"source": "file:routes/bc.py", "target": "file:services/__init__.py", "type": "imports", "direction": "forward", "weight": 0.7}, {"source": "file:routes/bc.py", "target": "file:services/audit.py", "type": "imports", "direction": "forward", "weight": 0.7}, {"source": "file:routes/bc.py", "target": "file:services/bc_service.py", "type": "imports", "direction": "forward", "weight": 0.7}, {"source": "file:routes/bc.py", "target": "file:services/cdisc_api.py", "type": "imports", "direction": "forward", "weight": 0.7}, {"source": "file:routes/bc.py", "target": "file:services/export.py", "type": "imports", "direction": "forward", "weight": 0.7}, {"source": "file:routes/bc.py", "target": "file:services/loinc_api.py", "type": "imports", "direction": "forward", "weight": 0.7}, {"source": "file:routes/bc.py", "target": "file:services/ncit_api.py", "type": "imports", "direction": "forward", "weight": 0.7}, {"source": "file:routes/governance.py", "target": "file:extensions.py", "type": "imports", "direction": "forward", "weight": 0.7}, {"source": "file:routes/governance.py", "target": "file:models/bc.py", "type": "imports", "direction": "forward", "weight": 0.7}, {"source": "file:routes/governance.py", "target": "file:services/__init__.py", "type": "imports", "direction": "forward", "weight": 0.7}, {"source": "file:routes/governance.py", "target": "file:services/export.py", "type": "imports", "direction": "forward", "weight": 0.7}, {"source": "file:routes/governance.py", "target": "file:services/governance_service.py", "type": "imports", "direction": "forward", "weight": 0.7}, {"source": "file:routes/ncit.py", "target": "file:extensions.py", "type": "imports", "direction": "forward", "weight": 0.7}, {"source": "file:routes/ncit.py", "target": "file:models/bc.py", "type": "imports", "direction": "forward", "weight": 0.7}, {"source": "file:routes/ncit.py", "target": "file:services/__init__.py", "type": "imports", "direction": "forward", "weight": 0.7}, {"source": "file:routes/ncit.py", "target": "file:services/bc_service.py", "type": "imports", "direction": "forward", "weight": 0.7}, {"source": "file:routes/ncit.py", "target": "file:services/ncit_api.py", "type": "imports", "direction": "forward", "weight": 0.7}, {"source": "file:services/audit.py", "target": "file:extensions.py", "type": "imports", "direction": "forward", "weight": 0.7}, {"source": "file:services/audit.py", "target": "file:models/audit.py", "type": "imports", "direction": "forward", "weight": 0.7}, {"source": "file:routes/ncit.py", "target": "file:tests/test_ncit.py", "type": "tested_by", "direction": "forward", "weight": 0.5}, {"source": "file:services/bc_service.py", "target": "file:extensions.py", "type": "imports", "direction": "forward", "weight": 0.7}, {"source": "file:services/bc_service.py", "target": "file:models/bc.py", "type": "imports", "direction": "forward", "weight": 0.7}, {"source": "file:services/bc_service.py", "target": "file:models/governance.py", "type": "imports", "direction": "forward", "weight": 0.7}, {"source": "file:services/bc_service.py", "target": "file:models/specialization.py", "type": "imports", "direction": "forward", "weight": 0.7}, {"source": "file:services/bc_service.py", "target": "file:services/audit.py", "type": "imports", "direction": "forward", "weight": 0.7}, {"source": "file:services/governance_service.py", "target": "file:extensions.py", "type": "imports", "direction": "forward", "weight": 0.7}, {"source": "file:services/governance_service.py", "target": "file:models/governance.py", "type": "imports", "direction": "forward", "weight": 0.7}, {"source": "file:services/governance_service.py", "target": "file:services/audit.py", "type": "imports", "direction": "forward", "weight": 0.7}, {"source": "file:services/governance_service.py", "target": "file:services/bc_service.py", "type": "imports", "direction": "forward", "weight": 0.7}, {"source": "file:services/ncit_api.py", "target": "file:services/api_cache.py", "type": "imports", "direction": "forward", "weight": 0.7}, {"source": "file:tests/test_export_service.py", "target": "file:extensions.py", "type": "imports", "direction": "forward", "weight": 0.7}, {"source": "file:tests/test_export_service.py", "target": "file:models/bc.py", "type": "imports", "direction": "forward", "weight": 0.7}, {"source": "file:tests/test_export_service.py", "target": "file:services/export.py", "type": "imports", "direction": "forward", "weight": 0.7}, {"source": "file:tests/test_ncit.py", "target": "file:services/ncit_api.py", "type": "imports", "direction": "forward", "weight": 0.7}, {"source": "file:services/export.py", "target": "file:tests/test_export_service.py", "type": "tested_by", "direction": "forward", "weight": 0.5}, {"source": "file:services/ncit_api.py", "target": "file:tests/test_ncit.py", "type": "tested_by", "direction": "forward", "weight": 0.5}, {"source": "file:routes/dashboard.py", "target": "file:models/audit.py", "type": "imports", "direction": "forward", "weight": 0.7}, {"source": "file:routes/dashboard.py", "target": "file:models/bc.py", "type": "imports", "direction": "forward", "weight": 0.7}, {"source": "file:routes/dashboard.py", "target": "file:services/cdisc_api.py", "type": "imports", "direction": "forward", "weight": 0.7}, {"source": "file:routes/loinc.py", "target": "file:services/loinc_api.py", "type": "imports", "direction": "forward", "weight": 0.7}, {"source": "file:routes/specializations.py", "target": "file:extensions.py", "type": "imports", "direction": "forward", "weight": 0.7}, {"source": "file:routes/specializations.py", "target": "file:models/bc.py", "type": "imports", "direction": "forward", "weight": 0.7}, {"source": "file:routes/specializations.py", "target": "file:models/specialization.py", "type": "imports", "direction": "forward", "weight": 0.7}, {"source": "file:routes/specializations.py", "target": "file:services/audit.py", "type": "imports", "direction": "forward", "weight": 0.7}, {"source": "file:routes/specializations.py", "target": "file:services/cdisc_api.py", "type": "imports", "direction": "forward", "weight": 0.7}, {"source": "file:services/cdisc_api.py", "target": "file:services/api_cache.py", "type": "imports", "direction": "forward", "weight": 0.7}, {"source": "file:services/loinc_api.py", "target": "file:services/api_cache.py", "type": "imports", "direction": "forward", "weight": 0.7}, {"source": "file:tests/test_cdisc_api_cache.py", "target": "file:services/cdisc_api.py", "type": "imports", "direction": "forward", "weight": 0.7}, {"source": "file:tests/test_loinc.py", "target": "file:services/loinc_api.py", "type": "imports", "direction": "forward", "weight": 0.7}, {"source": "file:services/cdisc_api.py", "target": "file:tests/test_cdisc_api_cache.py", "type": "tested_by", "direction": "forward", "weight": 0.5}, {"source": "file:services/api_cache.py", "target": "file:tests/test_cdisc_api_cache.py", "type": "tested_by", "direction": "forward", "weight": 0.5}, {"source": "file:services/loinc_api.py", "target": "file:tests/test_loinc.py", "type": "tested_by", "direction": "forward", "weight": 0.5}, {"source": "file:routes/loinc.py", "target": "file:tests/test_loinc.py", "type": "tested_by", "direction": "forward", "weight": 0.5}, {"source": "file:app.py", "target": "file:config.py", "type": "imports", "direction": "forward", "weight": 0.7}, {"source": "file:app.py", "target": "file:extensions.py", "type": "imports", "direction": "forward", "weight": 0.7}, {"source": "file:db_bootstrap.py", "target": "file:extensions.py", "type": "imports", "direction": "forward", "weight": 0.7}, {"source": "file:models/__init__.py", "target": "file:extensions.py", "type": "imports", "direction": "forward", "weight": 0.7}, {"source": "file:tests/test_dashboard.py", "target": "file:extensions.py", "type": "imports", "direction": "forward", "weight": 0.7}, {"source": "file:tests/test_dashboard.py", "target": "file:models/bc.py", "type": "imports", "direction": "forward", "weight": 0.7}, {"source": "file:tests/test_db_bootstrap.py", "target": "file:app.py", "type": "imports", "direction": "forward", "weight": 0.7}, {"source": "file:tests/test_db_bootstrap.py", "target": "file:db_bootstrap.py", "type": "imports", "direction": "forward", "weight": 0.7}, {"source": "file:tests/test_db_bootstrap.py", "target": "file:extensions.py", "type": "imports", "direction": "forward", "weight": 0.7}, {"source": "file:app.py", "target": "file:db_bootstrap.py", "type": "depends_on", "direction": "forward", "weight": 0.6}, {"source": "file:db_bootstrap.py", "target": "file:tests/test_db_bootstrap.py", "type": "tested_by", "direction": "forward", "weight": 0.5}, {"source": "file:mcp_server/__main__.py", "target": "file:mcp_server/server.py", "type": "imports", "direction": "forward", "weight": 0.7}, {"source": "file:models/specialization.py", "target": "file:extensions.py", "type": "imports", "direction": "forward", "weight": 0.7}, {"source": "file:tests/test_mcp_server.py", "target": "file:extensions.py", "type": "imports", "direction": "forward", "weight": 0.7}, {"source": "file:tests/test_mcp_server.py", "target": "file:mcp_server/server.py", "type": "imports", "direction": "forward", "weight": 0.7}, {"source": "file:tests/test_mcp_server.py", "target": "file:models/audit.py", "type": "imports", "direction": "forward", "weight": 0.7}, {"source": "file:tests/test_mcp_server.py", "target": "file:models/bc.py", "type": "imports", "direction": "forward", "weight": 0.7}, {"source": "file:tests/test_mcp_server.py", "target": "file:models/governance.py", "type": "imports", "direction": "forward", "weight": 0.7}, {"source": "file:tests/test_mcp_server.py", "target": "file:models/ingestion.py", "type": "imports", "direction": "forward", "weight": 0.7}, {"source": "file:tests/test_mcp_server.py", "target": "file:models/specialization.py", "type": "imports", "direction": "forward", "weight": 0.7}, {"source": "file:tests/test_specializations_routes.py", "target": "file:extensions.py", "type": "imports", "direction": "forward", "weight": 0.7}, {"source": "file:tests/test_specializations_routes.py", "target": "file:models/audit.py", "type": "imports", "direction": "forward", "weight": 0.7}, {"source": "file:tests/test_specializations_routes.py", "target": "file:models/bc.py", "type": "imports", "direction": "forward", "weight": 0.7}, {"source": "file:tests/test_specializations_routes.py", "target": "file:models/specialization.py", "type": "imports", "direction": "forward", "weight": 0.7}, {"source": "file:mcp_server/server.py", "target": "file:app.py", "type": "depends_on", "direction": "forward", "weight": 0.6}, {"source": "file:mcp_server/server.py", "target": "file:db_bootstrap.py", "type": "depends_on", "direction": "forward", "weight": 0.6}, {"source": "file:mcp_server/server.py", "target": "file:models/bc.py", "type": "depends_on", "direction": "forward", "weight": 0.6}, {"source": "file:mcp_server/server.py", "target": "file:models/governance.py", "type": "depends_on", "direction": "forward", "weight": 0.6}, {"source": "file:mcp_server/server.py", "target": "file:extensions.py", "type": "depends_on", "direction": "forward", "weight": 0.6}, {"source": "file:mcp_server/server.py", "target": "file:models/ingestion.py", "type": "depends_on", "direction": "forward", "weight": 0.6}, {"source": "file:mcp_server/server.py", "target": "file:services/ncit_api.py", "type": "depends_on", "direction": "forward", "weight": 0.6}, {"source": "file:mcp_server/server.py", "target": "file:services/loinc_api.py", "type": "depends_on", "direction": "forward", "weight": 0.6}, {"source": "file:mcp_server/server.py", "target": "file:services/cdisc_api.py", "type": "depends_on", "direction": "forward", "weight": 0.6}, {"source": "file:mcp_server/server.py", "target": "file:services/bc_service.py", "type": "depends_on", "direction": "forward", "weight": 0.6}, {"source": "file:mcp_server/server.py", "target": "file:services/governance_service.py", "type": "depends_on", "direction": "forward", "weight": 0.6}, {"source": "file:mcp_server/server.py", "target": "file:tests/test_mcp_server.py", "type": "tested_by", "direction": "forward", "weight": 0.5}, {"source": "file:routes/specializations.py", "target": "file:tests/test_specializations_routes.py", "type": "tested_by", "direction": "forward", "weight": 0.5}, {"source": "document:.claude/agent-memory/cdisc-frontend-dev/MEMORY.md", "target": "document:.claude/agent-memory/cdisc-frontend-dev/project_foundation.md", "type": "related", "direction": "forward", "weight": 0.5}, {"source": "document:.claude/agent-memory/cdisc-frontend-dev/MEMORY.md", "target": "document:.claude/agent-memory/cdisc-frontend-dev/frontend_architecture.md", "type": "related", "direction": "forward", "weight": 0.5}, {"source": "document:.claude/agent-memory/cdisc-frontend-dev/frontend_architecture.md", "target": "file:templates/base.html", "type": "documents", "direction": "forward", "weight": 0.5}, {"source": "document:.claude/agent-memory/cdisc-frontend-dev/frontend_architecture.md", "target": "file:static/js/main.js", "type": "documents", "direction": "forward", "weight": 0.5}, {"source": "document:.claude/agent-memory/cdisc-frontend-dev/frontend_architecture.md", "target": "file:static/css/custom.css", "type": "documents", "direction": "forward", "weight": 0.5}, {"source": "document:.claude/agent-memory/cdisc-frontend-dev/project_foundation.md", "target": "file:app.py", "type": "documents", "direction": "forward", "weight": 0.5}, {"source": "document:.claude/agent-memory/cdisc-frontend-dev/project_foundation.md", "target": "file:extensions.py", "type": "documents", "direction": "forward", "weight": 0.5}, {"source": "document:.claude/agent-memory/cdisc-frontend-dev/project_foundation.md", "target": "file:models/bc.py", "type": "documents", "direction": "forward", "weight": 0.5}, {"source": "document:.claude/agent-memory/cdisc-frontend-dev/project_foundation.md", "target": "file:models/specialization.py", "type": "documents", "direction": "forward", "weight": 0.5}, {"source": "document:.claude/agent-memory/cdisc-frontend-dev/project_foundation.md", "target": "file:models/governance.py", "type": "documents", "direction": "forward", "weight": 0.5}, {"source": "document:.claude/agent-memory/cdisc-frontend-dev/project_foundation.md", "target": "file:models/audit.py", "type": "documents", "direction": "forward", "weight": 0.5}, {"source": "document:.claude/agent-memory/cdisc-frontend-dev/project_foundation.md", "target": "file:models/ingestion.py", "type": "documents", "direction": "forward", "weight": 0.5}, {"source": "document:.claude/agent-memory/cdisc-frontend-dev/project_foundation.md", "target": "file:services/ingestion.py", "type": "documents", "direction": "forward", "weight": 0.5}, {"source": "document:.claude/agent-memory/cdisc-frontend-dev/project_foundation.md", "target": "file:services/ncit_api.py", "type": "documents", "direction": "forward", "weight": 0.5}, {"source": "document:.claude/agent-memory/cdisc-frontend-dev/project_foundation.md", "target": "file:services/loinc_api.py", "type": "documents", "direction": "forward", "weight": 0.5}, {"source": "document:.claude/agent-memory/cdisc-frontend-dev/project_foundation.md", "target": "file:services/export.py", "type": "documents", "direction": "forward", "weight": 0.5}, {"source": "document:.claude/agent-memory/cdisc-frontend-dev/project_foundation.md", "target": "file:services/cdisc_api.py", "type": "documents", "direction": "forward", "weight": 0.5}, {"source": "document:.claude/agents/mike.md", "target": "document:.claude/agents/cdisc-frontend-dev.md", "type": "related", "direction": "forward", "weight": 0.5}, {"source": "document:.claude/agents/mike.md", "target": "document:.claude/agents/cdisc-concept-explorer.md", "type": "related", "direction": "forward", "weight": 0.5}, {"source": "document:README.md", "target": "document:requirements.txt", "type": "documents", "direction": "forward", "weight": 0.5}, {"source": "document:README.md", "target": "config:.pre-commit-config.yaml", "type": "documents", "direction": "forward", "weight": 0.5}, {"source": "document:README.md", "target": "config:pyproject.toml", "type": "documents", "direction": "forward", "weight": 0.5}, {"source": "config:.pre-commit-config.yaml", "target": "config:pyproject.toml", "type": "depends_on", "direction": "forward", "weight": 0.6}, {"source": "document:requirements-dev.txt", "target": "document:requirements.txt", "type": "related", "direction": "forward", "weight": 0.5}, {"source": "document:README.md", "target": "document:CLAUDE.md", "type": "related", "direction": "forward", "weight": 0.5}, {"source": "document:README.md", "target": "document:README-PROGRESS.md", "type": "related", "direction": "forward", "weight": 0.5}, {"source": "document:files/plan.md", "target": "document:files/implementation.md", "type": "related", "direction": "forward", "weight": 0.5}, {"source": "document:files/implementation.md", "target": "file:app.py", "type": "documents", "direction": "forward", "weight": 0.5}, {"source": "document:files/implementation.md", "target": "file:config.py", "type": "documents", "direction": "forward", "weight": 0.5}, {"source": "document:files/implementation.md", "target": "file:models/bc.py", "type": "documents", "direction": "forward", "weight": 0.5}, {"source": "document:files/implementation.md", "target": "file:models/specialization.py", "type": "documents", "direction": "forward", "weight": 0.5}, {"source": "document:files/implementation.md", "target": "file:models/governance.py", "type": "documents", "direction": "forward", "weight": 0.5}, {"source": "document:files/implementation.md", "target": "file:models/audit.py", "type": "documents", "direction": "forward", "weight": 0.5}, {"source": "document:files/implementation.md", "target": "file:routes/dashboard.py", "type": "documents", "direction": "forward", "weight": 0.5}, {"source": "document:files/implementation.md", "target": "file:routes/ingestion.py", "type": "documents", "direction": "forward", "weight": 0.5}, {"source": "document:files/implementation.md", "target": "file:routes/bc.py", "type": "documents", "direction": "forward", "weight": 0.5}, {"source": "document:files/implementation.md", "target": "file:routes/ncit.py", "type": "documents", "direction": "forward", "weight": 0.5}, {"source": "document:files/implementation.md", "target": "file:routes/specializations.py", "type": "documents", "direction": "forward", "weight": 0.5}, {"source": "document:files/implementation.md", "target": "file:routes/governance.py", "type": "documents", "direction": "forward", "weight": 0.5}, {"source": "document:files/implementation.md", "target": "file:routes/audit.py", "type": "documents", "direction": "forward", "weight": 0.5}, {"source": "document:files/implementation.md", "target": "file:services/cdisc_api.py", "type": "documents", "direction": "forward", "weight": 0.5}, {"source": "document:files/implementation.md", "target": "file:services/ncit_api.py", "type": "documents", "direction": "forward", "weight": 0.5}, {"source": "document:files/implementation.md", "target": "file:services/ingestion.py", "type": "documents", "direction": "forward", "weight": 0.5}, {"source": "document:files/implementation.md", "target": "file:services/export.py", "type": "documents", "direction": "forward", "weight": 0.5}, {"source": "document:files/implementation.md", "target": "file:templates/base.html", "type": "documents", "direction": "forward", "weight": 0.5}, {"source": "document:files/implementation.md", "target": "document:files/plan.md", "type": "related", "direction": "forward", "weight": 0.5}, {"source": "document:files/implementation.md", "target": "document:files/result.md", "type": "related", "direction": "forward", "weight": 0.5}, {"source": "document:files/result.md", "target": "file:app.py", "type": "documents", "direction": "forward", "weight": 0.5}, {"source": "document:files/result.md", "target": "file:extensions.py", "type": "documents", "direction": "forward", "weight": 0.5}, {"source": "document:files/result.md", "target": "file:config.py", "type": "documents", "direction": "forward", "weight": 0.5}, {"source": "document:files/result.md", "target": "document:files/implementation.md", "type": "related", "direction": "forward", "weight": 0.5}, {"source": "file:templates/audit.html", "target": "file:templates/base.html", "type": "depends_on", "direction": "forward", "weight": 0.6}, {"source": "file:templates/bc_detail.html", "target": "file:templates/base.html", "type": "depends_on", "direction": "forward", "weight": 0.6}, {"source": "file:templates/bc_list.html", "target": "file:templates/base.html", "type": "depends_on", "direction": "forward", "weight": 0.6}, {"source": "file:templates/dashboard.html", "target": "file:templates/base.html", "type": "depends_on", "direction": "forward", "weight": 0.6}, {"source": "file:templates/governance.html", "target": "file:templates/base.html", "type": "depends_on", "direction": "forward", "weight": 0.6}, {"source": "file:templates/ingestion.html", "target": "file:templates/base.html", "type": "depends_on", "direction": "forward", "weight": 0.6}, {"source": "file:templates/library_bc_detail.html", "target": "file:templates/base.html", "type": "depends_on", "direction": "forward", "weight": 0.6}, {"source": "file:templates/library_spec_detail.html", "target": "file:templates/base.html", "type": "depends_on", "direction": "forward", "weight": 0.6}, {"source": "file:templates/ncit_mapping.html", "target": "file:templates/base.html", "type": "depends_on", "direction": "forward", "weight": 0.6}, {"source": "file:templates/specializations.html", "target": "file:templates/base.html", "type": "depends_on", "direction": "forward", "weight": 0.6}, {"source": "document:.claude/skills/run-concept-curation/SKILL.md", "target": "file:.claude/skills/run-concept-curation/smoke.sh", "type": "documents", "direction": "forward", "weight": 0.5}, {"source": "document:.claude/agent-memory/cdisc-concept-explorer/MEMORY.md", "target": "document:.claude/agent-memory/cdisc-concept-explorer/reference_api_endpoints.md", "type": "related", "direction": "forward", "weight": 0.5}, {"source": "config:migrations/alembic.ini", "target": "file:migrations/env.py", "type": "configures", "direction": "forward", "weight": 0.6}, {"source": "file:migrations/script.py.mako", "target": "file:migrations/versions/51d4a009d291_baseline_initial_schema.py", "type": "related", "direction": "forward", "weight": 0.5}, {"source": "file:static/css/custom.css", "target": "file:static/js/main.js", "type": "related", "direction": "forward", "weight": 0.5}, {"source": "document:.claude/skills/code-review/SKILL.md", "target": "file:mcp_server/__init__.py", "type": "related", "direction": "forward", "weight": 0.5}, {"source": "document:files/plan.md", "target": "file:files/cdisc_bc_ai_platform.html", "type": "related", "direction": "forward", "weight": 0.5}, {"source": "document:files/plan.md", "target": "file:files/cdisc_smart_goals_infographic.html", "type": "related", "direction": "forward", "weight": 0.5}, {"source": "document:files/plan.md", "target": "file:files/BC Curation Principles and Completion GLs.xlsx", "type": "related", "direction": "forward", "weight": 0.5}, {"source": "document:files/plan.md", "target": "file:files/BC Examples.xlsx", "type": "related", "direction": "forward", "weight": 0.5}, {"source": "document:files/plan.md", "target": "file:files/BC DEC Templates.xlsx", "type": "related", "direction": "forward", "weight": 0.5}, {"source": "document:files/plan.md", "target": "file:files/BC Governance.docx", "type": "related", "direction": "forward", "weight": 0.5}]} \ No newline at end of file diff --git a/.ua/.trash-1785851745/tmp/ua-arch-results.json b/.ua/.trash-1785851745/tmp/ua-arch-results.json new file mode 100644 index 0000000..1ce7acc --- /dev/null +++ b/.ua/.trash-1785851745/tmp/ua-arch-results.json @@ -0,0 +1,826 @@ +{ + "scriptCompleted": true, + "directoryGroups": { + "models": [ + "file:models/audit.py", + "file:models/bc.py", + "file:models/ingestion.py", + "file:models/governance.py", + "file:models/__init__.py", + "file:models/specialization.py" + ], + "routes": [ + "file:routes/audit.py", + "file:routes/ingestion.py", + "file:routes/bc.py", + "file:routes/governance.py", + "file:routes/ncit.py", + "file:routes/dashboard.py", + "file:routes/loinc.py", + "file:routes/specializations.py", + "file:routes/__init__.py" + ], + "services": [ + "file:services/ingestion.py", + "file:services/__init__.py", + "file:services/audit.py", + "file:services/bc_service.py", + "file:services/export.py", + "file:services/governance_service.py", + "file:services/ncit_api.py", + "file:services/api_cache.py", + "file:services/cdisc_api.py", + "file:services/loinc_api.py" + ], + "tests": [ + "file:tests/conftest.py", + "file:tests/test_audit_routes.py", + "file:tests/test_bc_routes.py", + "file:tests/test_governance_routes.py", + "file:tests/test_ingestion_routes.py", + "file:tests/test_ingestion_service.py", + "file:tests/test_models.py", + "file:tests/test_export_service.py", + "file:tests/test_ncit.py", + "file:tests/test_cdisc_api_cache.py", + "file:tests/test_loinc.py", + "file:tests/test_dashboard.py", + "file:tests/test_db_bootstrap.py", + "file:tests/test_mcp_server.py", + "file:tests/test_specializations_routes.py", + "file:tests/__init__.py" + ], + "py": [ + "file:app.py", + "file:db_bootstrap.py", + "file:extensions.py" + ], + "config": [ + "file:config.py", + "config:.pre-commit-config.yaml" + ], + "mcp_server": [ + "file:mcp_server/__main__.py", + "file:mcp_server/server.py", + "file:mcp_server/__init__.py" + ], + ".github": [ + "pipeline:.github/workflows/ci.yml" + ], + ".claude": [ + "document:.claude/agent-memory/cdisc-frontend-dev/MEMORY.md", + "document:.claude/agent-memory/cdisc-frontend-dev/frontend_architecture.md", + "document:.claude/agent-memory/cdisc-frontend-dev/project_foundation.md", + "document:.claude/agents/cdisc-concept-explorer.md", + "document:.claude/agents/cdisc-frontend-dev.md", + "document:.claude/agents/mike.md", + "document:.claude/agent-memory/cdisc-concept-explorer/MEMORY.md", + "document:.claude/agent-memory/cdisc-concept-explorer/reference_api_endpoints.md", + "document:.claude/skills/code-review/SKILL.md", + "document:.claude/skills/run-concept-curation/SKILL.md", + "file:.claude/skills/run-concept-curation/smoke.sh" + ], + "json": [ + "config:.mcp.json" + ], + "md": [ + "document:CLAUDE.md", + "document:README-PROGRESS.md", + "document:README.md" + ], + "toml": [ + "config:pyproject.toml" + ], + "txt": [ + "document:requirements-dev.txt", + "document:requirements.txt" + ], + "files": [ + "document:files/plan.md", + "document:files/implementation.md", + "document:files/result.md", + "file:files/BC Curation Principles and Completion GLs.xlsx", + "file:files/BC DEC Templates.xlsx", + "file:files/BC Examples.xlsx", + "file:files/BC Governance.docx", + "file:files/cdisc_bc_ai_platform.html", + "file:files/cdisc_smart_goals_infographic.html" + ], + "templates": [ + "file:templates/base.html", + "file:templates/audit.html", + "file:templates/bc_detail.html", + "file:templates/bc_list.html", + "file:templates/dashboard.html", + "file:templates/governance.html", + "file:templates/ingestion.html", + "file:templates/library_bc_detail.html", + "file:templates/library_spec_detail.html", + "file:templates/ncit_mapping.html", + "file:templates/specializations.html" + ], + "flake8": [ + "config:.flake8" + ], + "hintrc": [ + "config:.hintrc" + ], + ".ua": [ + "config:.ua/.understandignore", + "config:.ua/config.json" + ], + "migrations": [ + "document:migrations/README", + "config:migrations/alembic.ini", + "file:migrations/env.py", + "file:migrations/script.py.mako", + "file:migrations/versions/51d4a009d291_baseline_initial_schema.py" + ], + "static": [ + "file:static/css/custom.css", + "file:static/js/main.js" + ] + }, + "nodeTypeGroups": { + "file": [ + "file:models/audit.py", + "file:models/bc.py", + "file:models/ingestion.py", + "file:routes/audit.py", + "file:routes/ingestion.py", + "file:services/ingestion.py", + "file:tests/conftest.py", + "file:tests/test_audit_routes.py", + "file:tests/test_bc_routes.py", + "file:tests/test_governance_routes.py", + "file:tests/test_ingestion_routes.py", + "file:tests/test_ingestion_service.py", + "file:tests/test_models.py", + "file:models/governance.py", + "file:routes/bc.py", + "file:routes/governance.py", + "file:routes/ncit.py", + "file:services/__init__.py", + "file:services/audit.py", + "file:services/bc_service.py", + "file:services/export.py", + "file:services/governance_service.py", + "file:services/ncit_api.py", + "file:tests/test_export_service.py", + "file:tests/test_ncit.py", + "file:routes/dashboard.py", + "file:routes/loinc.py", + "file:routes/specializations.py", + "file:services/api_cache.py", + "file:services/cdisc_api.py", + "file:services/loinc_api.py", + "file:tests/test_cdisc_api_cache.py", + "file:tests/test_loinc.py", + "file:app.py", + "file:config.py", + "file:db_bootstrap.py", + "file:extensions.py", + "file:models/__init__.py", + "file:tests/test_dashboard.py", + "file:tests/test_db_bootstrap.py", + "file:mcp_server/__main__.py", + "file:mcp_server/server.py", + "file:models/specialization.py", + "file:tests/test_mcp_server.py", + "file:tests/test_specializations_routes.py", + "file:templates/base.html", + "file:templates/audit.html", + "file:templates/bc_detail.html", + "file:templates/bc_list.html", + "file:templates/dashboard.html", + "file:templates/governance.html", + "file:templates/ingestion.html", + "file:templates/library_bc_detail.html", + "file:templates/library_spec_detail.html", + "file:templates/ncit_mapping.html", + "file:templates/specializations.html", + "file:.claude/skills/run-concept-curation/smoke.sh", + "file:files/BC Curation Principles and Completion GLs.xlsx", + "file:files/BC DEC Templates.xlsx", + "file:files/BC Examples.xlsx", + "file:files/BC Governance.docx", + "file:mcp_server/__init__.py", + "file:migrations/env.py", + "file:migrations/script.py.mako", + "file:migrations/versions/51d4a009d291_baseline_initial_schema.py", + "file:routes/__init__.py", + "file:static/css/custom.css", + "file:static/js/main.js", + "file:tests/__init__.py", + "file:files/cdisc_bc_ai_platform.html", + "file:files/cdisc_smart_goals_infographic.html" + ], + "pipeline": [ + "pipeline:.github/workflows/ci.yml" + ], + "document": [ + "document:.claude/agent-memory/cdisc-frontend-dev/MEMORY.md", + "document:.claude/agent-memory/cdisc-frontend-dev/frontend_architecture.md", + "document:.claude/agent-memory/cdisc-frontend-dev/project_foundation.md", + "document:.claude/agents/cdisc-concept-explorer.md", + "document:.claude/agents/cdisc-frontend-dev.md", + "document:.claude/agents/mike.md", + "document:CLAUDE.md", + "document:README-PROGRESS.md", + "document:README.md", + "document:requirements-dev.txt", + "document:requirements.txt", + "document:files/plan.md", + "document:files/implementation.md", + "document:files/result.md", + "document:.claude/agent-memory/cdisc-concept-explorer/MEMORY.md", + "document:.claude/agent-memory/cdisc-concept-explorer/reference_api_endpoints.md", + "document:.claude/skills/code-review/SKILL.md", + "document:.claude/skills/run-concept-curation/SKILL.md", + "document:migrations/README" + ], + "config": [ + "config:.mcp.json", + "config:.pre-commit-config.yaml", + "config:pyproject.toml", + "config:.flake8", + "config:.hintrc", + "config:.ua/.understandignore", + "config:.ua/config.json", + "config:migrations/alembic.ini" + ] + }, + "crossCategoryEdges": [ + { + "fromType": "document", + "toType": "file", + "edgeType": "documents", + "count": 37 + }, + { + "fromType": "document", + "toType": "config", + "edgeType": "documents", + "count": 2 + }, + { + "fromType": "config", + "toType": "file", + "edgeType": "configures", + "count": 1 + }, + { + "fromType": "document", + "toType": "file", + "edgeType": "related", + "count": 7 + } + ], + "interGroupImports": [ + { + "from": "models", + "to": "py", + "count": 6 + }, + { + "from": "routes", + "to": "models", + "count": 12 + }, + { + "from": "routes", + "to": "py", + "count": 5 + }, + { + "from": "routes", + "to": "services", + "count": 18 + }, + { + "from": "tests", + "to": "py", + "count": 14 + }, + { + "from": "tests", + "to": "models", + "count": 26 + }, + { + "from": "tests", + "to": "services", + "count": 5 + }, + { + "from": "services", + "to": "py", + "count": 3 + }, + { + "from": "services", + "to": "models", + "count": 5 + }, + { + "from": "py", + "to": "config", + "count": 1 + }, + { + "from": "tests", + "to": "mcp_server", + "count": 1 + } + ], + "intraGroupDensity": { + "models": { + "internalEdges": 0, + "totalEdges": 49, + "density": 0 + }, + "routes": { + "internalEdges": 0, + "totalEdges": 35, + "density": 0 + }, + "services": { + "internalEdges": 6, + "totalEdges": 37, + "density": 0.16216216216216217 + }, + "tests": { + "internalEdges": 0, + "totalEdges": 46, + "density": 0 + }, + "py": { + "internalEdges": 2, + "totalEdges": 31, + "density": 0.06451612903225806 + }, + "config": { + "internalEdges": 0, + "totalEdges": 1, + "density": 0 + }, + "mcp_server": { + "internalEdges": 1, + "totalEdges": 2, + "density": 0.5 + }, + ".github": { + "internalEdges": 0, + "totalEdges": 0, + "density": 0 + }, + ".claude": { + "internalEdges": 0, + "totalEdges": 0, + "density": 0 + }, + "json": { + "internalEdges": 0, + "totalEdges": 0, + "density": 0 + }, + "md": { + "internalEdges": 0, + "totalEdges": 0, + "density": 0 + }, + "toml": { + "internalEdges": 0, + "totalEdges": 0, + "density": 0 + }, + "txt": { + "internalEdges": 0, + "totalEdges": 0, + "density": 0 + }, + "files": { + "internalEdges": 0, + "totalEdges": 0, + "density": 0 + }, + "templates": { + "internalEdges": 0, + "totalEdges": 0, + "density": 0 + }, + "flake8": { + "internalEdges": 0, + "totalEdges": 0, + "density": 0 + }, + "hintrc": { + "internalEdges": 0, + "totalEdges": 0, + "density": 0 + }, + ".ua": { + "internalEdges": 0, + "totalEdges": 0, + "density": 0 + }, + "migrations": { + "internalEdges": 0, + "totalEdges": 0, + "density": 0 + }, + "static": { + "internalEdges": 0, + "totalEdges": 0, + "density": 0 + } + }, + "patternMatches": { + "models": "data", + "routes": "api", + "services": "service", + "tests": "test", + "config": "config", + ".github": "ci-cd", + "migrations": "data", + "static": "assets" + }, + "filePatternMatches": { + "file:tests/test_audit_routes.py": "test", + "file:tests/test_bc_routes.py": "test", + "file:tests/test_governance_routes.py": "test", + "file:tests/test_ingestion_routes.py": "test", + "file:tests/test_ingestion_service.py": "test", + "file:tests/test_models.py": "test", + "file:services/__init__.py": "entry", + "file:tests/test_export_service.py": "test", + "file:tests/test_ncit.py": "test", + "file:tests/test_cdisc_api_cache.py": "test", + "file:tests/test_loinc.py": "test", + "file:models/__init__.py": "entry", + "file:tests/test_dashboard.py": "test", + "file:tests/test_db_bootstrap.py": "test", + "file:tests/test_mcp_server.py": "test", + "file:tests/test_specializations_routes.py": "test", + "pipeline:.github/workflows/ci.yml": "ci-cd", + "document:.claude/agent-memory/cdisc-frontend-dev/MEMORY.md": "documentation", + "document:.claude/agent-memory/cdisc-frontend-dev/frontend_architecture.md": "documentation", + "document:.claude/agent-memory/cdisc-frontend-dev/project_foundation.md": "documentation", + "document:.claude/agents/cdisc-concept-explorer.md": "documentation", + "document:.claude/agents/cdisc-frontend-dev.md": "documentation", + "document:.claude/agents/mike.md": "documentation", + "document:CLAUDE.md": "documentation", + "document:README-PROGRESS.md": "documentation", + "document:README.md": "documentation", + "document:files/plan.md": "documentation", + "document:files/implementation.md": "documentation", + "document:files/result.md": "documentation", + "document:.claude/agent-memory/cdisc-concept-explorer/MEMORY.md": "documentation", + "document:.claude/agent-memory/cdisc-concept-explorer/reference_api_endpoints.md": "documentation", + "document:.claude/skills/code-review/SKILL.md": "documentation", + "document:.claude/skills/run-concept-curation/SKILL.md": "documentation", + "file:mcp_server/__init__.py": "entry", + "file:routes/__init__.py": "entry", + "file:tests/__init__.py": "entry" + }, + "deploymentTopology": { + "hasDockerfile": false, + "hasCompose": false, + "hasK8s": false, + "hasTerraform": false, + "hasCI": true, + "infraFiles": [ + ".github/workflows/ci.yml" + ] + }, + "dataPipeline": { + "schemaFiles": [], + "migrationFiles": [ + "migrations/README", + "migrations/alembic.ini", + "migrations/env.py", + "migrations/script.py.mako", + "migrations/versions/51d4a009d291_baseline_initial_schema.py" + ], + "dataModelFiles": [ + "models/audit.py", + "models/bc.py", + "models/ingestion.py", + "models/governance.py", + "models/__init__.py", + "models/specialization.py" + ], + "apiHandlerFiles": [ + "routes/audit.py", + "routes/ingestion.py", + "routes/bc.py", + "routes/governance.py", + "routes/ncit.py", + "routes/dashboard.py", + "routes/loinc.py", + "routes/specializations.py", + "routes/__init__.py" + ] + }, + "docCoverage": { + "groupsWithDocs": 5, + "totalGroups": 20, + "coverageRatio": 0.25, + "undocumentedGroups": [ + "models", + "routes", + "services", + "tests", + "py", + "config", + "mcp_server", + ".github", + "json", + "toml", + "templates", + "flake8", + "hintrc", + ".ua", + "static" + ] + }, + "dependencyDirection": [ + { + "dependent": "models", + "dependsOn": "py" + }, + { + "dependent": "routes", + "dependsOn": "models" + }, + { + "dependent": "routes", + "dependsOn": "py" + }, + { + "dependent": "routes", + "dependsOn": "services" + }, + { + "dependent": "tests", + "dependsOn": "py" + }, + { + "dependent": "tests", + "dependsOn": "models" + }, + { + "dependent": "tests", + "dependsOn": "services" + }, + { + "dependent": "services", + "dependsOn": "py" + }, + { + "dependent": "services", + "dependsOn": "models" + }, + { + "dependent": "py", + "dependsOn": "config" + }, + { + "dependent": "tests", + "dependsOn": "mcp_server" + } + ], + "fileStats": { + "totalFileNodes": 99, + "filesPerGroup": { + "models": 6, + "routes": 9, + "services": 10, + "tests": 16, + "py": 3, + "config": 2, + "mcp_server": 3, + ".github": 1, + ".claude": 11, + "json": 1, + "md": 3, + "toml": 1, + "txt": 2, + "files": 9, + "templates": 11, + "flake8": 1, + "hintrc": 1, + ".ua": 2, + "migrations": 5, + "static": 2 + }, + "nodeTypeCounts": { + "file": 71, + "pipeline": 1, + "document": 19, + "config": 8 + } + }, + "fileFanIn": { + "file:models/audit.py": 12, + "file:models/bc.py": 16, + "file:models/ingestion.py": 5, + "file:routes/audit.py": 0, + "file:routes/ingestion.py": 0, + "file:services/ingestion.py": 2, + "file:tests/conftest.py": 0, + "file:tests/test_audit_routes.py": 0, + "file:tests/test_bc_routes.py": 0, + "file:tests/test_governance_routes.py": 0, + "file:tests/test_ingestion_routes.py": 0, + "file:tests/test_ingestion_service.py": 0, + "file:tests/test_models.py": 0, + "file:models/governance.py": 6, + "file:routes/bc.py": 0, + "file:routes/governance.py": 0, + "file:routes/ncit.py": 0, + "file:services/__init__.py": 3, + "file:services/audit.py": 4, + "file:services/bc_service.py": 3, + "file:services/export.py": 3, + "file:services/governance_service.py": 1, + "file:services/ncit_api.py": 3, + "file:tests/test_export_service.py": 0, + "file:tests/test_ncit.py": 0, + "file:routes/dashboard.py": 0, + "file:routes/loinc.py": 0, + "file:routes/specializations.py": 0, + "file:services/api_cache.py": 3, + "file:services/cdisc_api.py": 4, + "file:services/loinc_api.py": 3, + "file:tests/test_cdisc_api_cache.py": 0, + "file:tests/test_loinc.py": 0, + "file:app.py": 2, + "file:config.py": 1, + "file:db_bootstrap.py": 1, + "file:extensions.py": 27, + "file:models/__init__.py": 0, + "file:tests/test_dashboard.py": 0, + "file:tests/test_db_bootstrap.py": 0, + "file:mcp_server/__main__.py": 0, + "file:mcp_server/server.py": 2, + "file:models/specialization.py": 4, + "file:tests/test_mcp_server.py": 0, + "file:tests/test_specializations_routes.py": 0, + "pipeline:.github/workflows/ci.yml": 0, + "document:.claude/agent-memory/cdisc-frontend-dev/MEMORY.md": 0, + "document:.claude/agent-memory/cdisc-frontend-dev/frontend_architecture.md": 0, + "document:.claude/agent-memory/cdisc-frontend-dev/project_foundation.md": 0, + "document:.claude/agents/cdisc-concept-explorer.md": 0, + "document:.claude/agents/cdisc-frontend-dev.md": 0, + "document:.claude/agents/mike.md": 0, + "config:.mcp.json": 0, + "config:.pre-commit-config.yaml": 0, + "document:CLAUDE.md": 0, + "document:README-PROGRESS.md": 0, + "document:README.md": 0, + "config:pyproject.toml": 0, + "document:requirements-dev.txt": 0, + "document:requirements.txt": 0, + "document:files/plan.md": 0, + "document:files/implementation.md": 0, + "document:files/result.md": 0, + "file:templates/base.html": 0, + "file:templates/audit.html": 0, + "file:templates/bc_detail.html": 0, + "file:templates/bc_list.html": 0, + "file:templates/dashboard.html": 0, + "file:templates/governance.html": 0, + "file:templates/ingestion.html": 0, + "file:templates/library_bc_detail.html": 0, + "file:templates/library_spec_detail.html": 0, + "file:templates/ncit_mapping.html": 0, + "file:templates/specializations.html": 0, + "document:.claude/agent-memory/cdisc-concept-explorer/MEMORY.md": 0, + "document:.claude/agent-memory/cdisc-concept-explorer/reference_api_endpoints.md": 0, + "document:.claude/skills/code-review/SKILL.md": 0, + "document:.claude/skills/run-concept-curation/SKILL.md": 0, + "file:.claude/skills/run-concept-curation/smoke.sh": 0, + "config:.flake8": 0, + "config:.hintrc": 0, + "config:.ua/.understandignore": 0, + "config:.ua/config.json": 0, + "file:files/BC Curation Principles and Completion GLs.xlsx": 0, + "file:files/BC DEC Templates.xlsx": 0, + "file:files/BC Examples.xlsx": 0, + "file:files/BC Governance.docx": 0, + "file:mcp_server/__init__.py": 0, + "document:migrations/README": 0, + "config:migrations/alembic.ini": 0, + "file:migrations/env.py": 0, + "file:migrations/script.py.mako": 0, + "file:migrations/versions/51d4a009d291_baseline_initial_schema.py": 0, + "file:routes/__init__.py": 0, + "file:static/css/custom.css": 0, + "file:static/js/main.js": 0, + "file:tests/__init__.py": 0, + "file:files/cdisc_bc_ai_platform.html": 0, + "file:files/cdisc_smart_goals_infographic.html": 0 + }, + "fileFanOut": { + "file:models/audit.py": 1, + "file:models/bc.py": 1, + "file:models/ingestion.py": 1, + "file:routes/audit.py": 1, + "file:routes/ingestion.py": 5, + "file:services/ingestion.py": 0, + "file:tests/conftest.py": 6, + "file:tests/test_audit_routes.py": 2, + "file:tests/test_bc_routes.py": 3, + "file:tests/test_governance_routes.py": 4, + "file:tests/test_ingestion_routes.py": 4, + "file:tests/test_ingestion_service.py": 1, + "file:tests/test_models.py": 4, + "file:models/governance.py": 1, + "file:routes/bc.py": 10, + "file:routes/governance.py": 5, + "file:routes/ncit.py": 5, + "file:services/__init__.py": 0, + "file:services/audit.py": 2, + "file:services/bc_service.py": 5, + "file:services/export.py": 0, + "file:services/governance_service.py": 4, + "file:services/ncit_api.py": 1, + "file:tests/test_export_service.py": 3, + "file:tests/test_ncit.py": 1, + "file:routes/dashboard.py": 3, + "file:routes/loinc.py": 1, + "file:routes/specializations.py": 5, + "file:services/api_cache.py": 0, + "file:services/cdisc_api.py": 1, + "file:services/loinc_api.py": 1, + "file:tests/test_cdisc_api_cache.py": 1, + "file:tests/test_loinc.py": 1, + "file:app.py": 2, + "file:config.py": 0, + "file:db_bootstrap.py": 1, + "file:extensions.py": 0, + "file:models/__init__.py": 1, + "file:tests/test_dashboard.py": 2, + "file:tests/test_db_bootstrap.py": 3, + "file:mcp_server/__main__.py": 1, + "file:mcp_server/server.py": 0, + "file:models/specialization.py": 1, + "file:tests/test_mcp_server.py": 7, + "file:tests/test_specializations_routes.py": 4, + "pipeline:.github/workflows/ci.yml": 0, + "document:.claude/agent-memory/cdisc-frontend-dev/MEMORY.md": 0, + "document:.claude/agent-memory/cdisc-frontend-dev/frontend_architecture.md": 0, + "document:.claude/agent-memory/cdisc-frontend-dev/project_foundation.md": 0, + "document:.claude/agents/cdisc-concept-explorer.md": 0, + "document:.claude/agents/cdisc-frontend-dev.md": 0, + "document:.claude/agents/mike.md": 0, + "config:.mcp.json": 0, + "config:.pre-commit-config.yaml": 0, + "document:CLAUDE.md": 0, + "document:README-PROGRESS.md": 0, + "document:README.md": 0, + "config:pyproject.toml": 0, + "document:requirements-dev.txt": 0, + "document:requirements.txt": 0, + "document:files/plan.md": 0, + "document:files/implementation.md": 0, + "document:files/result.md": 0, + "file:templates/base.html": 0, + "file:templates/audit.html": 0, + "file:templates/bc_detail.html": 0, + "file:templates/bc_list.html": 0, + "file:templates/dashboard.html": 0, + "file:templates/governance.html": 0, + "file:templates/ingestion.html": 0, + "file:templates/library_bc_detail.html": 0, + "file:templates/library_spec_detail.html": 0, + "file:templates/ncit_mapping.html": 0, + "file:templates/specializations.html": 0, + "document:.claude/agent-memory/cdisc-concept-explorer/MEMORY.md": 0, + "document:.claude/agent-memory/cdisc-concept-explorer/reference_api_endpoints.md": 0, + "document:.claude/skills/code-review/SKILL.md": 0, + "document:.claude/skills/run-concept-curation/SKILL.md": 0, + "file:.claude/skills/run-concept-curation/smoke.sh": 0, + "config:.flake8": 0, + "config:.hintrc": 0, + "config:.ua/.understandignore": 0, + "config:.ua/config.json": 0, + "file:files/BC Curation Principles and Completion GLs.xlsx": 0, + "file:files/BC DEC Templates.xlsx": 0, + "file:files/BC Examples.xlsx": 0, + "file:files/BC Governance.docx": 0, + "file:mcp_server/__init__.py": 0, + "document:migrations/README": 0, + "config:migrations/alembic.ini": 0, + "file:migrations/env.py": 0, + "file:migrations/script.py.mako": 0, + "file:migrations/versions/51d4a009d291_baseline_initial_schema.py": 0, + "file:routes/__init__.py": 0, + "file:static/css/custom.css": 0, + "file:static/js/main.js": 0, + "file:tests/__init__.py": 0, + "file:files/cdisc_bc_ai_platform.html": 0, + "file:files/cdisc_smart_goals_infographic.html": 0 + } +} \ No newline at end of file diff --git a/.ua/.trash-1785851745/tmp/ua-file-analyzer-input-1.json b/.ua/.trash-1785851745/tmp/ua-file-analyzer-input-1.json new file mode 100644 index 0000000..118a5a4 --- /dev/null +++ b/.ua/.trash-1785851745/tmp/ua-file-analyzer-input-1.json @@ -0,0 +1,143 @@ +{ + "projectRoot": "/Users/dmoreland/projects/cdisc-concept-curation", + "batchFiles": [ + { + "path": "models/audit.py", + "language": "python", + "sizeLines": 32, + "fileCategory": "code" + }, + { + "path": "models/bc.py", + "language": "python", + "sizeLines": 65, + "fileCategory": "code" + }, + { + "path": "models/ingestion.py", + "language": "python", + "sizeLines": 56, + "fileCategory": "code" + }, + { + "path": "routes/audit.py", + "language": "python", + "sizeLines": 44, + "fileCategory": "code" + }, + { + "path": "routes/ingestion.py", + "language": "python", + "sizeLines": 186, + "fileCategory": "code" + }, + { + "path": "services/ingestion.py", + "language": "python", + "sizeLines": 227, + "fileCategory": "code" + }, + { + "path": "tests/conftest.py", + "language": "python", + "sizeLines": 68, + "fileCategory": "code" + }, + { + "path": "tests/test_audit_routes.py", + "language": "python", + "sizeLines": 49, + "fileCategory": "code" + }, + { + "path": "tests/test_bc_routes.py", + "language": "python", + "sizeLines": 547, + "fileCategory": "code" + }, + { + "path": "tests/test_governance_routes.py", + "language": "python", + "sizeLines": 236, + "fileCategory": "code" + }, + { + "path": "tests/test_ingestion_routes.py", + "language": "python", + "sizeLines": 204, + "fileCategory": "code" + }, + { + "path": "tests/test_ingestion_service.py", + "language": "python", + "sizeLines": 259, + "fileCategory": "code" + }, + { + "path": "tests/test_models.py", + "language": "python", + "sizeLines": 116, + "fileCategory": "code" + } + ], + "batchImportData": { + "models/audit.py": [ + "extensions.py" + ], + "models/bc.py": [ + "extensions.py" + ], + "models/ingestion.py": [ + "extensions.py" + ], + "routes/audit.py": [ + "models/audit.py" + ], + "routes/ingestion.py": [ + "extensions.py", + "models/audit.py", + "models/bc.py", + "models/ingestion.py", + "services/ingestion.py" + ], + "services/ingestion.py": [], + "tests/conftest.py": [ + "app.py", + "extensions.py", + "models/audit.py", + "models/bc.py", + "models/governance.py", + "models/ingestion.py" + ], + "tests/test_audit_routes.py": [ + "extensions.py", + "models/audit.py" + ], + "tests/test_bc_routes.py": [ + "extensions.py", + "models/audit.py", + "models/bc.py" + ], + "tests/test_governance_routes.py": [ + "extensions.py", + "models/audit.py", + "models/bc.py", + "models/governance.py" + ], + "tests/test_ingestion_routes.py": [ + "extensions.py", + "models/audit.py", + "models/bc.py", + "models/ingestion.py" + ], + "tests/test_ingestion_service.py": [ + "services/ingestion.py" + ], + "tests/test_models.py": [ + "extensions.py", + "models/audit.py", + "models/bc.py", + "models/ingestion.py" + ] + } +} \ No newline at end of file diff --git a/.ua/.trash-1785851745/tmp/ua-file-analyzer-input-10.json b/.ua/.trash-1785851745/tmp/ua-file-analyzer-input-10.json new file mode 100644 index 0000000..f9b330c --- /dev/null +++ b/.ua/.trash-1785851745/tmp/ua-file-analyzer-input-10.json @@ -0,0 +1,28 @@ +{ + "projectRoot": "/Users/dmoreland/projects/cdisc-concept-curation", + "batchFiles": [ + { + "path": "files/implementation.md", + "language": "markdown", + "sizeLines": 205, + "fileCategory": "docs" + }, + { + "path": "files/plan.md", + "language": "markdown", + "sizeLines": 47, + "fileCategory": "docs" + }, + { + "path": "files/result.md", + "language": "markdown", + "sizeLines": 27, + "fileCategory": "docs" + } + ], + "batchImportData": { + "files/implementation.md": [], + "files/plan.md": [], + "files/result.md": [] + } +} diff --git a/.ua/.trash-1785851745/tmp/ua-file-analyzer-input-11.json b/.ua/.trash-1785851745/tmp/ua-file-analyzer-input-11.json new file mode 100644 index 0000000..68eb4a6 --- /dev/null +++ b/.ua/.trash-1785851745/tmp/ua-file-analyzer-input-11.json @@ -0,0 +1,84 @@ +{ + "projectRoot": "/Users/dmoreland/projects/cdisc-concept-curation", + "batchFiles": [ + { + "path": "templates/audit.html", + "language": "html", + "sizeLines": 213, + "fileCategory": "markup" + }, + { + "path": "templates/base.html", + "language": "html", + "sizeLines": 130, + "fileCategory": "markup" + }, + { + "path": "templates/bc_detail.html", + "language": "html", + "sizeLines": 533, + "fileCategory": "markup" + }, + { + "path": "templates/bc_list.html", + "language": "html", + "sizeLines": 238, + "fileCategory": "markup" + }, + { + "path": "templates/dashboard.html", + "language": "html", + "sizeLines": 392, + "fileCategory": "markup" + }, + { + "path": "templates/governance.html", + "language": "html", + "sizeLines": 243, + "fileCategory": "markup" + }, + { + "path": "templates/ingestion.html", + "language": "html", + "sizeLines": 174, + "fileCategory": "markup" + }, + { + "path": "templates/library_bc_detail.html", + "language": "html", + "sizeLines": 162, + "fileCategory": "markup" + }, + { + "path": "templates/library_spec_detail.html", + "language": "html", + "sizeLines": 197, + "fileCategory": "markup" + }, + { + "path": "templates/ncit_mapping.html", + "language": "html", + "sizeLines": 135, + "fileCategory": "markup" + }, + { + "path": "templates/specializations.html", + "language": "html", + "sizeLines": 394, + "fileCategory": "markup" + } + ], + "batchImportData": { + "templates/audit.html": [], + "templates/base.html": [], + "templates/bc_detail.html": [], + "templates/bc_list.html": [], + "templates/dashboard.html": [], + "templates/governance.html": [], + "templates/ingestion.html": [], + "templates/library_bc_detail.html": [], + "templates/library_spec_detail.html": [], + "templates/ncit_mapping.html": [], + "templates/specializations.html": [] + } +} \ No newline at end of file diff --git a/.ua/.trash-1785851745/tmp/ua-file-analyzer-input-12.json b/.ua/.trash-1785851745/tmp/ua-file-analyzer-input-12.json new file mode 100644 index 0000000..a5b5eba --- /dev/null +++ b/.ua/.trash-1785851745/tmp/ua-file-analyzer-input-12.json @@ -0,0 +1,168 @@ +{ + "projectRoot": "/Users/dmoreland/projects/cdisc-concept-curation", + "batchFiles": [ + { + "path": ".claude/agent-memory/cdisc-concept-explorer/MEMORY.md", + "language": "markdown", + "sizeLines": 1, + "fileCategory": "docs" + }, + { + "path": ".claude/agent-memory/cdisc-concept-explorer/reference_api_endpoints.md", + "language": "markdown", + "sizeLines": 36, + "fileCategory": "docs" + }, + { + "path": ".claude/skills/code-review/SKILL.md", + "language": "markdown", + "sizeLines": 59, + "fileCategory": "docs" + }, + { + "path": ".claude/skills/run-concept-curation/SKILL.md", + "language": "markdown", + "sizeLines": 49, + "fileCategory": "docs" + }, + { + "path": ".claude/skills/run-concept-curation/smoke.sh", + "language": "shell", + "sizeLines": 90, + "fileCategory": "script" + }, + { + "path": ".flake8", + "language": "unknown", + "sizeLines": 16, + "fileCategory": "code" + }, + { + "path": ".hintrc", + "language": "unknown", + "sizeLines": 7, + "fileCategory": "code" + }, + { + "path": ".ua/.understandignore", + "language": "unknown", + "sizeLines": 110, + "fileCategory": "code" + }, + { + "path": ".ua/config.json", + "language": "json", + "sizeLines": 1, + "fileCategory": "config" + }, + { + "path": "files/BC Curation Principles and Completion GLs.xlsx", + "language": "xlsx", + "sizeLines": 182, + "fileCategory": "code" + }, + { + "path": "files/BC DEC Templates.xlsx", + "language": "xlsx", + "sizeLines": 94, + "fileCategory": "code" + }, + { + "path": "files/BC Examples.xlsx", + "language": "xlsx", + "sizeLines": 332, + "fileCategory": "code" + }, + { + "path": "files/BC Governance.docx", + "language": "docx", + "sizeLines": 54, + "fileCategory": "code" + }, + { + "path": "mcp_server/__init__.py", + "language": "python", + "sizeLines": 5, + "fileCategory": "code" + }, + { + "path": "migrations/README", + "language": "unknown", + "sizeLines": 1, + "fileCategory": "code" + }, + { + "path": "migrations/alembic.ini", + "language": "config", + "sizeLines": 50, + "fileCategory": "config" + }, + { + "path": "migrations/env.py", + "language": "python", + "sizeLines": 105, + "fileCategory": "code" + }, + { + "path": "migrations/script.py.mako", + "language": "mako", + "sizeLines": 24, + "fileCategory": "code" + }, + { + "path": "migrations/versions/51d4a009d291_baseline_initial_schema.py", + "language": "python", + "sizeLines": 142, + "fileCategory": "code" + }, + { + "path": "routes/__init__.py", + "language": "python", + "sizeLines": 0, + "fileCategory": "code" + }, + { + "path": "static/css/custom.css", + "language": "css", + "sizeLines": 1096, + "fileCategory": "markup" + }, + { + "path": "static/js/main.js", + "language": "javascript", + "sizeLines": 819, + "fileCategory": "code" + }, + { + "path": "tests/__init__.py", + "language": "python", + "sizeLines": 0, + "fileCategory": "code" + } + ], + "batchImportData": { + ".claude/agent-memory/cdisc-concept-explorer/MEMORY.md": [], + ".claude/agent-memory/cdisc-concept-explorer/reference_api_endpoints.md": [], + ".claude/skills/code-review/SKILL.md": [], + ".claude/skills/run-concept-curation/SKILL.md": [], + ".claude/skills/run-concept-curation/smoke.sh": [], + ".flake8": [], + ".hintrc": [], + ".ua/.understandignore": [], + ".ua/config.json": [], + "files/BC Curation Principles and Completion GLs.xlsx": [], + "files/BC DEC Templates.xlsx": [], + "files/BC Examples.xlsx": [], + "files/BC Governance.docx": [], + "mcp_server/__init__.py": [], + "migrations/README": [], + "migrations/alembic.ini": [], + "migrations/env.py": [], + "migrations/script.py.mako": [], + "migrations/versions/51d4a009d291_baseline_initial_schema.py": [], + "routes/__init__.py": [], + "static/css/custom.css": [], + "static/js/main.js": [], + "tests/__init__.py": [] + } +} \ No newline at end of file diff --git a/.ua/.trash-1785851745/tmp/ua-file-analyzer-input-3.json b/.ua/.trash-1785851745/tmp/ua-file-analyzer-input-3.json new file mode 100644 index 0000000..64b5f0a --- /dev/null +++ b/.ua/.trash-1785851745/tmp/ua-file-analyzer-input-3.json @@ -0,0 +1,83 @@ +{ + "projectRoot": "/Users/dmoreland/projects/cdisc-concept-curation", + "batchFiles": [ + { + "path": "routes/dashboard.py", + "language": "python", + "sizeLines": 60, + "fileCategory": "code" + }, + { + "path": "routes/loinc.py", + "language": "python", + "sizeLines": 23, + "fileCategory": "code" + }, + { + "path": "routes/specializations.py", + "language": "python", + "sizeLines": 172, + "fileCategory": "code" + }, + { + "path": "services/api_cache.py", + "language": "python", + "sizeLines": 42, + "fileCategory": "code" + }, + { + "path": "services/cdisc_api.py", + "language": "python", + "sizeLines": 127, + "fileCategory": "code" + }, + { + "path": "services/loinc_api.py", + "language": "python", + "sizeLines": 62, + "fileCategory": "code" + }, + { + "path": "tests/test_cdisc_api_cache.py", + "language": "python", + "sizeLines": 120, + "fileCategory": "code" + }, + { + "path": "tests/test_loinc.py", + "language": "python", + "sizeLines": 205, + "fileCategory": "code" + } + ], + "batchImportData": { + "routes/dashboard.py": [ + "models/audit.py", + "models/bc.py", + "services/cdisc_api.py" + ], + "routes/loinc.py": [ + "services/loinc_api.py" + ], + "routes/specializations.py": [ + "extensions.py", + "models/bc.py", + "models/specialization.py", + "services/audit.py", + "services/cdisc_api.py" + ], + "services/api_cache.py": [], + "services/cdisc_api.py": [ + "services/api_cache.py" + ], + "services/loinc_api.py": [ + "services/api_cache.py" + ], + "tests/test_cdisc_api_cache.py": [ + "services/cdisc_api.py" + ], + "tests/test_loinc.py": [ + "services/loinc_api.py" + ] + } +} \ No newline at end of file diff --git a/.ua/.trash-1785851745/tmp/ua-file-analyzer-input-4.json b/.ua/.trash-1785851745/tmp/ua-file-analyzer-input-4.json new file mode 100644 index 0000000..4e725ac --- /dev/null +++ b/.ua/.trash-1785851745/tmp/ua-file-analyzer-input-4.json @@ -0,0 +1,70 @@ +{ + "projectRoot": "/Users/dmoreland/projects/cdisc-concept-curation", + "batchFiles": [ + { + "path": "app.py", + "language": "python", + "sizeLines": 59, + "fileCategory": "code" + }, + { + "path": "config.py", + "language": "python", + "sizeLines": 14, + "fileCategory": "code" + }, + { + "path": "db_bootstrap.py", + "language": "python", + "sizeLines": 71, + "fileCategory": "code" + }, + { + "path": "extensions.py", + "language": "python", + "sizeLines": 25, + "fileCategory": "code" + }, + { + "path": "models/__init__.py", + "language": "python", + "sizeLines": 1, + "fileCategory": "code" + }, + { + "path": "tests/test_dashboard.py", + "language": "python", + "sizeLines": 75, + "fileCategory": "code" + }, + { + "path": "tests/test_db_bootstrap.py", + "language": "python", + "sizeLines": 87, + "fileCategory": "code" + } + ], + "batchImportData": { + "app.py": [ + "config.py", + "extensions.py" + ], + "config.py": [], + "db_bootstrap.py": [ + "extensions.py" + ], + "extensions.py": [], + "models/__init__.py": [ + "extensions.py" + ], + "tests/test_dashboard.py": [ + "extensions.py", + "models/bc.py" + ], + "tests/test_db_bootstrap.py": [ + "app.py", + "db_bootstrap.py", + "extensions.py" + ] + } +} \ No newline at end of file diff --git a/.ua/.trash-1785851745/tmp/ua-file-analyzer-input-5.json b/.ua/.trash-1785851745/tmp/ua-file-analyzer-input-5.json new file mode 100644 index 0000000..c0662e7 --- /dev/null +++ b/.ua/.trash-1785851745/tmp/ua-file-analyzer-input-5.json @@ -0,0 +1,59 @@ +{ + "projectRoot": "/Users/dmoreland/projects/cdisc-concept-curation", + "batchFiles": [ + { + "path": "mcp_server/__main__.py", + "language": "python", + "sizeLines": 3, + "fileCategory": "code" + }, + { + "path": "mcp_server/server.py", + "language": "python", + "sizeLines": 572, + "fileCategory": "code" + }, + { + "path": "models/specialization.py", + "language": "python", + "sizeLines": 30, + "fileCategory": "code" + }, + { + "path": "tests/test_mcp_server.py", + "language": "python", + "sizeLines": 315, + "fileCategory": "code" + }, + { + "path": "tests/test_specializations_routes.py", + "language": "python", + "sizeLines": 260, + "fileCategory": "code" + } + ], + "batchImportData": { + "mcp_server/__main__.py": [ + "mcp_server/server.py" + ], + "mcp_server/server.py": [], + "models/specialization.py": [ + "extensions.py" + ], + "tests/test_mcp_server.py": [ + "extensions.py", + "mcp_server/server.py", + "models/audit.py", + "models/bc.py", + "models/governance.py", + "models/ingestion.py", + "models/specialization.py" + ], + "tests/test_specializations_routes.py": [ + "extensions.py", + "models/audit.py", + "models/bc.py", + "models/specialization.py" + ] + } +} \ No newline at end of file diff --git a/.ua/.trash-1785851745/tmp/ua-file-analyzer-input-6.json b/.ua/.trash-1785851745/tmp/ua-file-analyzer-input-6.json new file mode 100644 index 0000000..01c503d --- /dev/null +++ b/.ua/.trash-1785851745/tmp/ua-file-analyzer-input-6.json @@ -0,0 +1,14 @@ +{ + "projectRoot": "/Users/dmoreland/projects/cdisc-concept-curation", + "batchFiles": [ + { + "path": ".github/workflows/ci.yml", + "language": "yaml", + "sizeLines": 26, + "fileCategory": "infra" + } + ], + "batchImportData": { + ".github/workflows/ci.yml": [] + } +} diff --git a/.ua/.trash-1785851745/tmp/ua-file-analyzer-input-7.json b/.ua/.trash-1785851745/tmp/ua-file-analyzer-input-7.json new file mode 100644 index 0000000..5ef9140 --- /dev/null +++ b/.ua/.trash-1785851745/tmp/ua-file-analyzer-input-7.json @@ -0,0 +1,28 @@ +{ + "projectRoot": "/Users/dmoreland/projects/cdisc-concept-curation", + "batchFiles": [ + { + "path": ".claude/agent-memory/cdisc-frontend-dev/MEMORY.md", + "language": "markdown", + "sizeLines": 2, + "fileCategory": "docs" + }, + { + "path": ".claude/agent-memory/cdisc-frontend-dev/frontend_architecture.md", + "language": "markdown", + "sizeLines": 65, + "fileCategory": "docs" + }, + { + "path": ".claude/agent-memory/cdisc-frontend-dev/project_foundation.md", + "language": "markdown", + "sizeLines": 54, + "fileCategory": "docs" + } + ], + "batchImportData": { + ".claude/agent-memory/cdisc-frontend-dev/MEMORY.md": [], + ".claude/agent-memory/cdisc-frontend-dev/frontend_architecture.md": [], + ".claude/agent-memory/cdisc-frontend-dev/project_foundation.md": [] + } +} \ No newline at end of file diff --git a/.ua/.trash-1785851745/tmp/ua-file-analyzer-input-8.json b/.ua/.trash-1785851745/tmp/ua-file-analyzer-input-8.json new file mode 100644 index 0000000..47ac3fc --- /dev/null +++ b/.ua/.trash-1785851745/tmp/ua-file-analyzer-input-8.json @@ -0,0 +1,13 @@ +{ + "projectRoot": "/Users/dmoreland/projects/cdisc-concept-curation", + "batchFiles": [ + {"path": ".claude/agents/cdisc-concept-explorer.md", "language": "markdown", "sizeLines": 67, "fileCategory": "docs"}, + {"path": ".claude/agents/cdisc-frontend-dev.md", "language": "markdown", "sizeLines": 207, "fileCategory": "docs"}, + {"path": ".claude/agents/mike.md", "language": "markdown", "sizeLines": 108, "fileCategory": "docs"} + ], + "batchImportData": { + ".claude/agents/cdisc-concept-explorer.md": [], + ".claude/agents/cdisc-frontend-dev.md": [], + ".claude/agents/mike.md": [] + } +} diff --git a/.ua/.trash-1785851745/tmp/ua-file-analyzer-input-9.json b/.ua/.trash-1785851745/tmp/ua-file-analyzer-input-9.json new file mode 100644 index 0000000..73640e8 --- /dev/null +++ b/.ua/.trash-1785851745/tmp/ua-file-analyzer-input-9.json @@ -0,0 +1,63 @@ +{ + "projectRoot": "/Users/dmoreland/projects/cdisc-concept-curation", + "batchFiles": [ + { + "path": ".mcp.json", + "language": "json", + "sizeLines": 9, + "fileCategory": "config" + }, + { + "path": ".pre-commit-config.yaml", + "language": "yaml", + "sizeLines": 24, + "fileCategory": "config" + }, + { + "path": "CLAUDE.md", + "language": "markdown", + "sizeLines": 128, + "fileCategory": "docs" + }, + { + "path": "README-PROGRESS.md", + "language": "markdown", + "sizeLines": 147, + "fileCategory": "docs" + }, + { + "path": "README.md", + "language": "markdown", + "sizeLines": 185, + "fileCategory": "docs" + }, + { + "path": "pyproject.toml", + "language": "toml", + "sizeLines": 7, + "fileCategory": "config" + }, + { + "path": "requirements-dev.txt", + "language": "txt", + "sizeLines": 9, + "fileCategory": "docs" + }, + { + "path": "requirements.txt", + "language": "txt", + "sizeLines": 28, + "fileCategory": "docs" + } + ], + "batchImportData": { + ".mcp.json": [], + ".pre-commit-config.yaml": [], + "CLAUDE.md": [], + "README-PROGRESS.md": [], + "README.md": [], + "pyproject.toml": [], + "requirements-dev.txt": [], + "requirements.txt": [] + } +} diff --git a/.ua/.trash-1785851745/tmp/ua-file-extract-results-1.json b/.ua/.trash-1785851745/tmp/ua-file-extract-results-1.json new file mode 100644 index 0000000..c77ea95 --- /dev/null +++ b/.ua/.trash-1785851745/tmp/ua-file-extract-results-1.json @@ -0,0 +1,4964 @@ +{ + "scriptCompleted": true, + "filesAnalyzed": 13, + "filesSkipped": [], + "analysisOutcomes": { + "structure": { + "succeeded": 13, + "failed": 0 + }, + "callGraph": { + "succeeded": 13, + "failed": 0, + "skipped": 0 + } + }, + "results": [ + { + "path": "models/audit.py", + "language": "python", + "fileCategory": "code", + "totalLines": 32, + "nonEmptyLines": 25, + "classes": [ + { + "name": "AuditLog", + "startLine": 7, + "endLine": 32, + "methods": [ + "before_state", + "before_state", + "after_state", + "after_state" + ], + "properties": [] + } + ], + "exports": [ + { + "name": "AuditLog", + "line": 7, + "isDefault": false + } + ], + "callGraph": [ + { + "caller": "before_state", + "callee": "json.loads", + "lineNumber": 20 + }, + { + "caller": "before_state", + "callee": "json.dumps", + "lineNumber": 24 + }, + { + "caller": "after_state", + "callee": "json.loads", + "lineNumber": 28 + }, + { + "caller": "after_state", + "callee": "json.dumps", + "lineNumber": 32 + } + ], + "metrics": { + "importCount": 1, + "exportCount": 1, + "functionCount": 0, + "classCount": 1 + } + }, + { + "path": "models/bc.py", + "language": "python", + "fileCategory": "code", + "totalLines": 65, + "nonEmptyLines": 58, + "classes": [ + { + "name": "BiomedicalConcept", + "startLine": 6, + "endLine": 50, + "methods": [ + "to_dict" + ], + "properties": [] + }, + { + "name": "DataElementConcept", + "startLine": 53, + "endLine": 65, + "methods": [], + "properties": [] + } + ], + "exports": [ + { + "name": "BiomedicalConcept", + "line": 6, + "isDefault": false + }, + { + "name": "DataElementConcept", + "line": 53, + "isDefault": false + } + ], + "metrics": { + "importCount": 1, + "exportCount": 2, + "functionCount": 0, + "classCount": 2 + } + }, + { + "path": "models/ingestion.py", + "language": "python", + "fileCategory": "code", + "totalLines": 56, + "nonEmptyLines": 44, + "classes": [ + { + "name": "IngestionRecord", + "startLine": 7, + "endLine": 56, + "methods": [ + "mapped", + "mapped", + "confidences", + "confidences", + "errors", + "errors", + "decs", + "decs", + "avg_confidence" + ], + "properties": [] + } + ], + "exports": [ + { + "name": "IngestionRecord", + "line": 7, + "isDefault": false + } + ], + "callGraph": [ + { + "caller": "mapped", + "callee": "json.loads", + "lineNumber": 23 + }, + { + "caller": "mapped", + "callee": "json.dumps", + "lineNumber": 27 + }, + { + "caller": "confidences", + "callee": "json.loads", + "lineNumber": 31 + }, + { + "caller": "confidences", + "callee": "json.dumps", + "lineNumber": 35 + }, + { + "caller": "errors", + "callee": "json.loads", + "lineNumber": 39 + }, + { + "caller": "errors", + "callee": "json.dumps", + "lineNumber": 43 + }, + { + "caller": "decs", + "callee": "json.loads", + "lineNumber": 47 + }, + { + "caller": "decs", + "callee": "json.dumps", + "lineNumber": 51 + }, + { + "caller": "avg_confidence", + "callee": "self.confidences.values", + "lineNumber": 55 + }, + { + "caller": "avg_confidence", + "callee": "isinstance", + "lineNumber": 55 + }, + { + "caller": "avg_confidence", + "callee": "round", + "lineNumber": 56 + }, + { + "caller": "avg_confidence", + "callee": "sum", + "lineNumber": 56 + }, + { + "caller": "avg_confidence", + "callee": "len", + "lineNumber": 56 + } + ], + "metrics": { + "importCount": 1, + "exportCount": 1, + "functionCount": 0, + "classCount": 1 + } + }, + { + "path": "routes/audit.py", + "language": "python", + "fileCategory": "code", + "totalLines": 44, + "nonEmptyLines": 36, + "functions": [ + { + "name": "index", + "startLine": 13, + "endLine": 44, + "params": [] + } + ], + "exports": [ + { + "name": "index", + "line": 12, + "isDefault": false + } + ], + "callGraph": [ + { + "caller": "index", + "callee": "request.args.get", + "lineNumber": 14 + }, + { + "caller": "index", + "callee": "request.args.get", + "lineNumber": 15 + }, + { + "caller": "index", + "callee": "request.args.get", + "lineNumber": 16 + }, + { + "caller": "index", + "callee": "request.args.get", + "lineNumber": 17 + }, + { + "caller": "index", + "callee": "request.args.get", + "lineNumber": 18 + }, + { + "caller": "index", + "callee": "request.args.get", + "lineNumber": 19 + }, + { + "caller": "index", + "callee": "query.filter_by", + "lineNumber": 23 + }, + { + "caller": "index", + "callee": "query.filter", + "lineNumber": 25 + }, + { + "caller": "index", + "callee": "AuditLog.action.ilike", + "lineNumber": 25 + }, + { + "caller": "index", + "callee": "query.filter", + "lineNumber": 27 + }, + { + "caller": "index", + "callee": "AuditLog.actor.ilike", + "lineNumber": 27 + }, + { + "caller": "index", + "callee": "query.filter", + "lineNumber": 29 + }, + { + "caller": "index", + "callee": "query.filter", + "lineNumber": 31 + }, + { + "caller": "index", + "callee": "query.order_by(AuditLog.timestamp.desc()).paginate", + "lineNumber": 33 + }, + { + "caller": "index", + "callee": "query.order_by", + "lineNumber": 33 + }, + { + "caller": "index", + "callee": "AuditLog.timestamp.desc", + "lineNumber": 33 + }, + { + "caller": "index", + "callee": "render_template", + "lineNumber": 34 + } + ], + "metrics": { + "importCount": 1, + "exportCount": 1, + "functionCount": 1, + "classCount": 0 + } + }, + { + "path": "routes/ingestion.py", + "language": "python", + "fileCategory": "code", + "totalLines": 186, + "nonEmptyLines": 157, + "functions": [ + { + "name": "_allowed_file", + "startLine": 27, + "endLine": 29, + "params": [ + "filename" + ] + }, + { + "name": "_get_session_key", + "startLine": 32, + "endLine": 35, + "params": [] + }, + { + "name": "_bc_from_mapped", + "startLine": 38, + "endLine": 54, + "params": [ + "bc_id", + "mapped" + ] + }, + { + "name": "_create_decs", + "startLine": 57, + "endLine": 68, + "params": [ + "bc_id", + "decs" + ] + }, + { + "name": "index", + "startLine": 72, + "endLine": 75, + "params": [] + }, + { + "name": "upload", + "startLine": 79, + "endLine": 118, + "params": [] + }, + { + "name": "approve", + "startLine": 122, + "endLine": 143, + "params": [ + "record_id" + ] + }, + { + "name": "reject", + "startLine": 147, + "endLine": 151, + "params": [ + "record_id" + ] + }, + { + "name": "approve_all", + "startLine": 155, + "endLine": 186, + "params": [] + } + ], + "exports": [ + { + "name": "_allowed_file", + "line": 27, + "isDefault": false + }, + { + "name": "_get_session_key", + "line": 32, + "isDefault": false + }, + { + "name": "_bc_from_mapped", + "line": 38, + "isDefault": false + }, + { + "name": "_create_decs", + "line": 57, + "isDefault": false + }, + { + "name": "index", + "line": 71, + "isDefault": false + }, + { + "name": "upload", + "line": 78, + "isDefault": false + }, + { + "name": "approve", + "line": 121, + "isDefault": false + }, + { + "name": "reject", + "line": 146, + "isDefault": false + }, + { + "name": "approve_all", + "line": 154, + "isDefault": false + } + ], + "callGraph": [ + { + "caller": "_allowed_file", + "callee": "filename.rsplit(\".\", 1)[1].lower", + "lineNumber": 28 + }, + { + "caller": "_allowed_file", + "callee": "filename.rsplit", + "lineNumber": 28 + }, + { + "caller": "_get_session_key", + "callee": "uuid.uuid4", + "lineNumber": 34 + }, + { + "caller": "_bc_from_mapped", + "callee": "BiomedicalConcept", + "lineNumber": 39 + }, + { + "caller": "_bc_from_mapped", + "callee": "mapped.get", + "lineNumber": 41 + }, + { + "caller": "_bc_from_mapped", + "callee": "mapped.get", + "lineNumber": 42 + }, + { + "caller": "_bc_from_mapped", + "callee": "mapped.get", + "lineNumber": 43 + }, + { + "caller": "_bc_from_mapped", + "callee": "mapped.get", + "lineNumber": 44 + }, + { + "caller": "_bc_from_mapped", + "callee": "mapped.get", + "lineNumber": 45 + }, + { + "caller": "_bc_from_mapped", + "callee": "mapped.get", + "lineNumber": 46 + }, + { + "caller": "_bc_from_mapped", + "callee": "mapped.get", + "lineNumber": 47 + }, + { + "caller": "_bc_from_mapped", + "callee": "mapped.get", + "lineNumber": 48 + }, + { + "caller": "_bc_from_mapped", + "callee": "mapped.get", + "lineNumber": 49 + }, + { + "caller": "_bc_from_mapped", + "callee": "mapped.get", + "lineNumber": 50 + }, + { + "caller": "_bc_from_mapped", + "callee": "mapped.get", + "lineNumber": 51 + }, + { + "caller": "_create_decs", + "callee": "enumerate", + "lineNumber": 58 + }, + { + "caller": "_create_decs", + "callee": "DataElementConcept", + "lineNumber": 59 + }, + { + "caller": "_create_decs", + "callee": "d.get", + "lineNumber": 60 + }, + { + "caller": "_create_decs", + "callee": "d.get", + "lineNumber": 62 + }, + { + "caller": "_create_decs", + "callee": "d.get", + "lineNumber": 63 + }, + { + "caller": "_create_decs", + "callee": "d.get", + "lineNumber": 64 + }, + { + "caller": "_create_decs", + "callee": "d.get", + "lineNumber": 65 + }, + { + "caller": "_create_decs", + "callee": "db.session.add", + "lineNumber": 68 + }, + { + "caller": "index", + "callee": "session.get", + "lineNumber": 73 + }, + { + "caller": "index", + "callee": "IngestionRecord.query.filter_by(session_key=key, status=\"pending\").all", + "lineNumber": 74 + }, + { + "caller": "index", + "callee": "IngestionRecord.query.filter_by", + "lineNumber": 74 + }, + { + "caller": "index", + "callee": "render_template", + "lineNumber": 75 + }, + { + "caller": "upload", + "callee": "flash", + "lineNumber": 81 + }, + { + "caller": "upload", + "callee": "redirect", + "lineNumber": 82 + }, + { + "caller": "upload", + "callee": "url_for", + "lineNumber": 82 + }, + { + "caller": "upload", + "callee": "_allowed_file", + "lineNumber": 84 + }, + { + "caller": "upload", + "callee": "flash", + "lineNumber": 85 + }, + { + "caller": "upload", + "callee": "redirect", + "lineNumber": 86 + }, + { + "caller": "upload", + "callee": "url_for", + "lineNumber": 86 + }, + { + "caller": "upload", + "callee": "f.filename.rsplit(\".\", 1)[1].lower", + "lineNumber": 88 + }, + { + "caller": "upload", + "callee": "f.filename.rsplit", + "lineNumber": 88 + }, + { + "caller": "upload", + "callee": "BiomedicalConcept.query.with_entities(BiomedicalConcept.bc_id).all", + "lineNumber": 89 + }, + { + "caller": "upload", + "callee": "BiomedicalConcept.query.with_entities", + "lineNumber": 89 + }, + { + "caller": "upload", + "callee": "parse_xlsx", + "lineNumber": 92 + }, + { + "caller": "upload", + "callee": "parse_csv", + "lineNumber": 94 + }, + { + "caller": "upload", + "callee": "parse_json", + "lineNumber": 96 + }, + { + "caller": "upload", + "callee": "deduplicate", + "lineNumber": 98 + }, + { + "caller": "upload", + "callee": "_get_session_key", + "lineNumber": 100 + }, + { + "caller": "upload", + "callee": "IngestionRecord.query.filter_by(session_key=key, status=\"pending\").delete", + "lineNumber": 101 + }, + { + "caller": "upload", + "callee": "IngestionRecord.query.filter_by", + "lineNumber": 101 + }, + { + "caller": "upload", + "callee": "IngestionRecord", + "lineNumber": 104 + }, + { + "caller": "upload", + "callee": "rec.get", + "lineNumber": 107 + }, + { + "caller": "upload", + "callee": "rec.get", + "lineNumber": 108 + }, + { + "caller": "upload", + "callee": "rec.get", + "lineNumber": 110 + }, + { + "caller": "upload", + "callee": "rec.get", + "lineNumber": 111 + }, + { + "caller": "upload", + "callee": "rec.get", + "lineNumber": 112 + }, + { + "caller": "upload", + "callee": "rec.get", + "lineNumber": 113 + }, + { + "caller": "upload", + "callee": "db.session.add", + "lineNumber": 114 + }, + { + "caller": "upload", + "callee": "db.session.commit", + "lineNumber": 116 + }, + { + "caller": "upload", + "callee": "flash", + "lineNumber": 117 + }, + { + "caller": "upload", + "callee": "len", + "lineNumber": 117 + }, + { + "caller": "upload", + "callee": "redirect", + "lineNumber": 118 + }, + { + "caller": "upload", + "callee": "url_for", + "lineNumber": 118 + }, + { + "caller": "approve", + "callee": "db.get_or_404", + "lineNumber": 123 + }, + { + "caller": "approve", + "callee": "mapped.get", + "lineNumber": 125 + }, + { + "caller": "approve", + "callee": "mapped.get", + "lineNumber": 125 + }, + { + "caller": "approve", + "callee": "db.session.get", + "lineNumber": 126 + }, + { + "caller": "approve", + "callee": "_bc_from_mapped", + "lineNumber": 127 + }, + { + "caller": "approve", + "callee": "db.session.add", + "lineNumber": 128 + }, + { + "caller": "approve", + "callee": "_create_decs", + "lineNumber": 129 + }, + { + "caller": "approve", + "callee": "AuditLog", + "lineNumber": 130 + }, + { + "caller": "approve", + "callee": "db.session.add", + "lineNumber": 137 + }, + { + "caller": "approve", + "callee": "flash", + "lineNumber": 138 + }, + { + "caller": "approve", + "callee": "flash", + "lineNumber": 140 + }, + { + "caller": "approve", + "callee": "db.session.commit", + "lineNumber": 142 + }, + { + "caller": "approve", + "callee": "redirect", + "lineNumber": 143 + }, + { + "caller": "approve", + "callee": "url_for", + "lineNumber": 143 + }, + { + "caller": "reject", + "callee": "db.get_or_404", + "lineNumber": 148 + }, + { + "caller": "reject", + "callee": "db.session.commit", + "lineNumber": 150 + }, + { + "caller": "reject", + "callee": "redirect", + "lineNumber": 151 + }, + { + "caller": "reject", + "callee": "url_for", + "lineNumber": 151 + }, + { + "caller": "approve_all", + "callee": "session.get", + "lineNumber": 156 + }, + { + "caller": "approve_all", + "callee": "redirect", + "lineNumber": 158 + }, + { + "caller": "approve_all", + "callee": "url_for", + "lineNumber": 158 + }, + { + "caller": "approve_all", + "callee": "IngestionRecord.query.filter_by(session_key=key, status=\"pending\").all", + "lineNumber": 159 + }, + { + "caller": "approve_all", + "callee": "IngestionRecord.query.filter_by", + "lineNumber": 159 + }, + { + "caller": "approve_all", + "callee": "mapped.get", + "lineNumber": 166 + }, + { + "caller": "approve_all", + "callee": "mapped.get", + "lineNumber": 166 + }, + { + "caller": "approve_all", + "callee": "db.session.get", + "lineNumber": 167 + }, + { + "caller": "approve_all", + "callee": "_bc_from_mapped", + "lineNumber": 168 + }, + { + "caller": "approve_all", + "callee": "db.session.add", + "lineNumber": 169 + }, + { + "caller": "approve_all", + "callee": "_create_decs", + "lineNumber": 170 + }, + { + "caller": "approve_all", + "callee": "db.session.add", + "lineNumber": 171 + }, + { + "caller": "approve_all", + "callee": "AuditLog", + "lineNumber": 172 + }, + { + "caller": "approve_all", + "callee": "db.session.commit", + "lineNumber": 184 + }, + { + "caller": "approve_all", + "callee": "flash", + "lineNumber": 185 + }, + { + "caller": "approve_all", + "callee": "redirect", + "lineNumber": 186 + }, + { + "caller": "approve_all", + "callee": "url_for", + "lineNumber": 186 + } + ], + "metrics": { + "importCount": 5, + "exportCount": 9, + "functionCount": 9, + "classCount": 0 + } + }, + { + "path": "services/ingestion.py", + "language": "python", + "fileCategory": "code", + "totalLines": 227, + "nonEmptyLines": 204, + "functions": [ + { + "name": "_similarity", + "startLine": 31, + "endLine": 32, + "params": [ + "a", + "b" + ] + }, + { + "name": "_match_field", + "startLine": 35, + "endLine": 45, + "params": [ + "col_name" + ] + }, + { + "name": "map_fields", + "startLine": 48, + "endLine": 62, + "params": [ + "raw_dict" + ] + }, + { + "name": "validate_bc", + "startLine": 65, + "endLine": 81, + "params": [ + "bc_dict" + ] + }, + { + "name": "_group_by_bc", + "startLine": 84, + "endLine": 137, + "params": [ + "rows", + "sheet" + ] + }, + { + "name": "parse_xlsx", + "startLine": 140, + "endLine": 162, + "params": [ + "file_obj" + ] + }, + { + "name": "parse_csv", + "startLine": 165, + "endLine": 188, + "params": [ + "file_obj" + ] + }, + { + "name": "parse_json", + "startLine": 191, + "endLine": 215, + "params": [ + "file_obj" + ] + }, + { + "name": "deduplicate", + "startLine": 218, + "endLine": 227, + "params": [ + "parsed_records", + "existing_ids" + ] + } + ], + "exports": [ + { + "name": "_similarity", + "line": 31, + "isDefault": false + }, + { + "name": "_match_field", + "line": 35, + "isDefault": false + }, + { + "name": "map_fields", + "line": 48, + "isDefault": false + }, + { + "name": "validate_bc", + "line": 65, + "isDefault": false + }, + { + "name": "_group_by_bc", + "line": 84, + "isDefault": false + }, + { + "name": "parse_xlsx", + "line": 140, + "isDefault": false + }, + { + "name": "parse_csv", + "line": 165, + "isDefault": false + }, + { + "name": "parse_json", + "line": 191, + "isDefault": false + }, + { + "name": "deduplicate", + "line": 218, + "isDefault": false + } + ], + "callGraph": [ + { + "caller": "_similarity", + "callee": "SequenceMatcher(None, a.lower().strip(), b.lower().strip()).ratio", + "lineNumber": 32 + }, + { + "caller": "_similarity", + "callee": "SequenceMatcher", + "lineNumber": 32 + }, + { + "caller": "_similarity", + "callee": "a.lower().strip", + "lineNumber": 32 + }, + { + "caller": "_similarity", + "callee": "a.lower", + "lineNumber": 32 + }, + { + "caller": "_similarity", + "callee": "b.lower().strip", + "lineNumber": 32 + }, + { + "caller": "_similarity", + "callee": "b.lower", + "lineNumber": 32 + }, + { + "caller": "_match_field", + "callee": "col_name.lower().replace(\" \", \"_\").replace", + "lineNumber": 37 + }, + { + "caller": "_match_field", + "callee": "col_name.lower().replace", + "lineNumber": 37 + }, + { + "caller": "_match_field", + "callee": "col_name.lower", + "lineNumber": 37 + }, + { + "caller": "_match_field", + "callee": "FIELD_MAP.items", + "lineNumber": 39 + }, + { + "caller": "_match_field", + "callee": "_similarity", + "lineNumber": 41 + }, + { + "caller": "_match_field", + "callee": "round", + "lineNumber": 45 + }, + { + "caller": "map_fields", + "callee": "raw_dict.items", + "lineNumber": 55 + }, + { + "caller": "map_fields", + "callee": "isinstance", + "lineNumber": 56 + }, + { + "caller": "map_fields", + "callee": "str", + "lineNumber": 56 + }, + { + "caller": "map_fields", + "callee": "_match_field", + "lineNumber": 58 + }, + { + "caller": "map_fields", + "callee": "str", + "lineNumber": 58 + }, + { + "caller": "map_fields", + "callee": "str(value).strip", + "lineNumber": 60 + }, + { + "caller": "map_fields", + "callee": "str", + "lineNumber": 60 + }, + { + "caller": "validate_bc", + "callee": "bc_dict.get", + "lineNumber": 71 + }, + { + "caller": "validate_bc", + "callee": "errors.append", + "lineNumber": 72 + }, + { + "caller": "validate_bc", + "callee": "bc_dict.get", + "lineNumber": 73 + }, + { + "caller": "validate_bc", + "callee": "errors.append", + "lineNumber": 74 + }, + { + "caller": "validate_bc", + "callee": "bc_dict.get", + "lineNumber": 75 + }, + { + "caller": "validate_bc", + "callee": "bc_dict.get", + "lineNumber": 75 + }, + { + "caller": "validate_bc", + "callee": "errors.append", + "lineNumber": 76 + }, + { + "caller": "validate_bc", + "callee": "bc_dict.get", + "lineNumber": 78 + }, + { + "caller": "validate_bc", + "callee": "bc_dict.get", + "lineNumber": 78 + }, + { + "caller": "validate_bc", + "callee": "ncit.upper().startswith", + "lineNumber": 79 + }, + { + "caller": "validate_bc", + "callee": "ncit.upper", + "lineNumber": 79 + }, + { + "caller": "validate_bc", + "callee": "errors.append", + "lineNumber": 80 + }, + { + "caller": "_group_by_bc", + "callee": "OrderedDict", + "lineNumber": 93 + }, + { + "caller": "_group_by_bc", + "callee": "mapped.get", + "lineNumber": 95 + }, + { + "caller": "_group_by_bc", + "callee": "mapped.get", + "lineNumber": 95 + }, + { + "caller": "_group_by_bc", + "callee": "mapped.get", + "lineNumber": 101 + }, + { + "caller": "_group_by_bc", + "callee": "g[\"mapped\"].get", + "lineNumber": 101 + }, + { + "caller": "_group_by_bc", + "callee": "mapped.items", + "lineNumber": 103 + }, + { + "caller": "_group_by_bc", + "callee": "g[\"confidences\"].update", + "lineNumber": 106 + }, + { + "caller": "_group_by_bc", + "callee": "mapped.get", + "lineNumber": 107 + }, + { + "caller": "_group_by_bc", + "callee": "mapped.get", + "lineNumber": 107 + }, + { + "caller": "_group_by_bc", + "callee": "g[\"decs\"].append", + "lineNumber": 108 + }, + { + "caller": "_group_by_bc", + "callee": "mapped.get", + "lineNumber": 110 + }, + { + "caller": "_group_by_bc", + "callee": "mapped.get", + "lineNumber": 111 + }, + { + "caller": "_group_by_bc", + "callee": "mapped.get", + "lineNumber": 112 + }, + { + "caller": "_group_by_bc", + "callee": "mapped.get", + "lineNumber": 113 + }, + { + "caller": "_group_by_bc", + "callee": "mapped.get", + "lineNumber": 114 + }, + { + "caller": "_group_by_bc", + "callee": "g[\"mapped\"].update", + "lineNumber": 119 + }, + { + "caller": "_group_by_bc", + "callee": "g[\"confidences\"].update", + "lineNumber": 120 + }, + { + "caller": "_group_by_bc", + "callee": "groups.items", + "lineNumber": 123 + }, + { + "caller": "_group_by_bc", + "callee": "mapped.get", + "lineNumber": 125 + }, + { + "caller": "_group_by_bc", + "callee": "validate_bc", + "lineNumber": 127 + }, + { + "caller": "_group_by_bc", + "callee": "results.append", + "lineNumber": 128 + }, + { + "caller": "parse_xlsx", + "callee": "pd.ExcelFile", + "lineNumber": 147 + }, + { + "caller": "parse_xlsx", + "callee": "xl.parse", + "lineNumber": 149 + }, + { + "caller": "parse_xlsx", + "callee": "df.iterrows", + "lineNumber": 151 + }, + { + "caller": "parse_xlsx", + "callee": "row.to_dict", + "lineNumber": 152 + }, + { + "caller": "parse_xlsx", + "callee": "map_fields", + "lineNumber": 153 + }, + { + "caller": "parse_xlsx", + "callee": "rows.append", + "lineNumber": 155 + }, + { + "caller": "parse_xlsx", + "callee": "results.extend", + "lineNumber": 156 + }, + { + "caller": "parse_xlsx", + "callee": "_group_by_bc", + "lineNumber": 156 + }, + { + "caller": "parse_xlsx", + "callee": "logger.error", + "lineNumber": 160 + }, + { + "caller": "parse_xlsx", + "callee": "results.append", + "lineNumber": 161 + }, + { + "caller": "parse_xlsx", + "callee": "str", + "lineNumber": 161 + }, + { + "caller": "parse_xlsx", + "callee": "str", + "lineNumber": 161 + }, + { + "caller": "parse_csv", + "callee": "pd.read_csv", + "lineNumber": 169 + }, + { + "caller": "parse_csv", + "callee": "df.iterrows", + "lineNumber": 170 + }, + { + "caller": "parse_csv", + "callee": "row.to_dict", + "lineNumber": 171 + }, + { + "caller": "parse_csv", + "callee": "map_fields", + "lineNumber": 172 + }, + { + "caller": "parse_csv", + "callee": "validate_bc", + "lineNumber": 175 + }, + { + "caller": "parse_csv", + "callee": "results.append", + "lineNumber": 176 + }, + { + "caller": "parse_csv", + "callee": "str", + "lineNumber": 178 + }, + { + "caller": "parse_csv", + "callee": "raw.items", + "lineNumber": 178 + }, + { + "caller": "parse_csv", + "callee": "logger.error", + "lineNumber": 186 + }, + { + "caller": "parse_csv", + "callee": "results.append", + "lineNumber": 187 + }, + { + "caller": "parse_csv", + "callee": "str", + "lineNumber": 187 + }, + { + "caller": "parse_csv", + "callee": "str", + "lineNumber": 187 + }, + { + "caller": "parse_json", + "callee": "json.load", + "lineNumber": 195 + }, + { + "caller": "parse_json", + "callee": "isinstance", + "lineNumber": 196 + }, + { + "caller": "parse_json", + "callee": "map_fields", + "lineNumber": 199 + }, + { + "caller": "parse_json", + "callee": "validate_bc", + "lineNumber": 202 + }, + { + "caller": "parse_json", + "callee": "results.append", + "lineNumber": 203 + }, + { + "caller": "parse_json", + "callee": "str", + "lineNumber": 205 + }, + { + "caller": "parse_json", + "callee": "item.items", + "lineNumber": 205 + }, + { + "caller": "parse_json", + "callee": "logger.error", + "lineNumber": 213 + }, + { + "caller": "parse_json", + "callee": "results.append", + "lineNumber": 214 + }, + { + "caller": "parse_json", + "callee": "str", + "lineNumber": 214 + }, + { + "caller": "parse_json", + "callee": "str", + "lineNumber": 214 + }, + { + "caller": "deduplicate", + "callee": "rec.get(\"mapped\", {}).get", + "lineNumber": 225 + }, + { + "caller": "deduplicate", + "callee": "rec.get", + "lineNumber": 225 + }, + { + "caller": "deduplicate", + "callee": "rec.get(\"mapped\", {}).get", + "lineNumber": 225 + }, + { + "caller": "deduplicate", + "callee": "rec.get", + "lineNumber": 225 + }, + { + "caller": "deduplicate", + "callee": "bc_id.upper", + "lineNumber": 226 + }, + { + "caller": "deduplicate", + "callee": "e.upper", + "lineNumber": 226 + } + ], + "metrics": { + "importCount": 0, + "exportCount": 9, + "functionCount": 9, + "classCount": 0 + } + }, + { + "path": "tests/conftest.py", + "language": "python", + "fileCategory": "code", + "totalLines": 68, + "nonEmptyLines": 54, + "functions": [ + { + "name": "app", + "startLine": 24, + "endLine": 26, + "params": [] + }, + { + "name": "client", + "startLine": 30, + "endLine": 31, + "params": [ + "app" + ] + }, + { + "name": "clean_db", + "startLine": 35, + "endLine": 41, + "params": [ + "app" + ] + }, + { + "name": "clear_api_cache", + "startLine": 45, + "endLine": 51, + "params": [] + }, + { + "name": "sample_bc", + "startLine": 55, + "endLine": 68, + "params": [ + "app" + ] + } + ], + "classes": [ + { + "name": "TestConfig", + "startLine": 11, + "endLine": 20, + "methods": [], + "properties": [] + } + ], + "exports": [ + { + "name": "TestConfig", + "line": 11, + "isDefault": false + }, + { + "name": "app", + "line": 23, + "isDefault": false + }, + { + "name": "client", + "line": 29, + "isDefault": false + }, + { + "name": "clean_db", + "line": 34, + "isDefault": false + }, + { + "name": "clear_api_cache", + "line": 44, + "isDefault": false + }, + { + "name": "sample_bc", + "line": 54, + "isDefault": false + } + ], + "callGraph": [ + { + "caller": "app", + "callee": "create_app", + "lineNumber": 25 + }, + { + "caller": "client", + "callee": "app.test_client", + "lineNumber": 31 + }, + { + "caller": "clean_db", + "callee": "app.app_context", + "lineNumber": 37 + }, + { + "caller": "clean_db", + "callee": "_db.drop_all", + "lineNumber": 38 + }, + { + "caller": "clean_db", + "callee": "_db.create_all", + "lineNumber": 39 + }, + { + "caller": "clean_db", + "callee": "_db.session.remove", + "lineNumber": 41 + }, + { + "caller": "clear_api_cache", + "callee": "api_cache._cache.clear", + "lineNumber": 50 + }, + { + "caller": "sample_bc", + "callee": "app.app_context", + "lineNumber": 57 + }, + { + "caller": "sample_bc", + "callee": "BiomedicalConcept", + "lineNumber": 58 + }, + { + "caller": "sample_bc", + "callee": "_db.session.add", + "lineNumber": 66 + }, + { + "caller": "sample_bc", + "callee": "_db.session.commit", + "lineNumber": 67 + } + ], + "metrics": { + "importCount": 6, + "exportCount": 6, + "functionCount": 5, + "classCount": 1 + } + }, + { + "path": "tests/test_audit_routes.py", + "language": "python", + "fileCategory": "code", + "totalLines": 49, + "nonEmptyLines": 39, + "functions": [ + { + "name": "_add_log", + "startLine": 7, + "endLine": 11, + "params": [ + "app", + "entity_type", + "entity_id", + "action", + "actor" + ] + } + ], + "classes": [ + { + "name": "TestAuditIndex", + "startLine": 14, + "endLine": 49, + "methods": [ + "test_returns_200_empty", + "test_shows_log_entries", + "test_filter_by_entity_type", + "test_filter_by_action", + "test_filter_by_actor", + "test_pagination_param_accepted" + ], + "properties": [] + } + ], + "exports": [ + { + "name": "_add_log", + "line": 7, + "isDefault": false + }, + { + "name": "TestAuditIndex", + "line": 14, + "isDefault": false + } + ], + "callGraph": [ + { + "caller": "_add_log", + "callee": "app.app_context", + "lineNumber": 8 + }, + { + "caller": "_add_log", + "callee": "AuditLog", + "lineNumber": 9 + }, + { + "caller": "_add_log", + "callee": "db.session.add", + "lineNumber": 10 + }, + { + "caller": "_add_log", + "callee": "db.session.commit", + "lineNumber": 11 + }, + { + "caller": "test_returns_200_empty", + "callee": "client.get", + "lineNumber": 16 + }, + { + "caller": "test_shows_log_entries", + "callee": "_add_log", + "lineNumber": 20 + }, + { + "caller": "test_shows_log_entries", + "callee": "client.get", + "lineNumber": 21 + }, + { + "caller": "test_filter_by_entity_type", + "callee": "_add_log", + "lineNumber": 26 + }, + { + "caller": "test_filter_by_entity_type", + "callee": "_add_log", + "lineNumber": 27 + }, + { + "caller": "test_filter_by_entity_type", + "callee": "client.get", + "lineNumber": 28 + }, + { + "caller": "test_filter_by_action", + "callee": "_add_log", + "lineNumber": 33 + }, + { + "caller": "test_filter_by_action", + "callee": "_add_log", + "lineNumber": 34 + }, + { + "caller": "test_filter_by_action", + "callee": "client.get", + "lineNumber": 35 + }, + { + "caller": "test_filter_by_actor", + "callee": "_add_log", + "lineNumber": 40 + }, + { + "caller": "test_filter_by_actor", + "callee": "_add_log", + "lineNumber": 41 + }, + { + "caller": "test_filter_by_actor", + "callee": "client.get", + "lineNumber": 42 + }, + { + "caller": "test_pagination_param_accepted", + "callee": "_add_log", + "lineNumber": 47 + }, + { + "caller": "test_pagination_param_accepted", + "callee": "client.get", + "lineNumber": 48 + } + ], + "metrics": { + "importCount": 2, + "exportCount": 2, + "functionCount": 1, + "classCount": 1 + } + }, + { + "path": "tests/test_bc_routes.py", + "language": "python", + "fileCategory": "code", + "totalLines": 547, + "nonEmptyLines": 446, + "functions": [ + { + "name": "_bc_form", + "startLine": 10, + "endLine": 19, + "params": [ + "**kwargs" + ] + } + ], + "classes": [ + { + "name": "TestBcIndex", + "startLine": 27, + "endLine": 43, + "methods": [ + "test_returns_200", + "test_search_by_name", + "test_search_no_match", + "test_filter_by_status" + ], + "properties": [] + }, + { + "name": "TestNewBc", + "startLine": 51, + "endLine": 54, + "methods": [ + "test_returns_200" + ], + "properties": [] + }, + { + "name": "TestCreateBc", + "startLine": 62, + "endLine": 109, + "methods": [ + "test_creates_bc_and_redirects", + "test_missing_bc_id_redirects_with_error", + "test_duplicate_bc_id_rejected", + "test_create_writes_audit_log", + "test_create_does_not_set_system_without_loinc_code", + "test_create_sets_system_when_loinc_code_provided", + "test_create_with_decs" + ], + "properties": [] + }, + { + "name": "TestBcDetail", + "startLine": 117, + "endLine": 224, + "methods": [ + "test_existing_bc_returns_200", + "test_missing_bc_returns_404", + "test_loinc_api_called_when_code_set", + "test_loinc_api_not_called_when_no_code", + "test_loinc_api_error_does_not_break_page", + "test_loinc_spinner_not_shown_when_loinc_fetched_server_side", + "test_loinc_metadata_saved_when_fetched_in_detail" + ], + "properties": [] + }, + { + "name": "TestEditBc", + "startLine": 232, + "endLine": 332, + "methods": [ + "test_updates_short_name", + "test_edit_writes_audit_log", + "test_nonexistent_bc_returns_404", + "test_edit_clears_ncit_code_when_submitted_empty", + "test_edit_clears_ncit_metadata_when_ncit_code_cleared", + "test_edit_clears_parent_bc_id_when_submitted_empty", + "test_edit_clears_loinc_code_and_metadata_when_submitted_empty", + "test_edit_clears_system_and_system_name_when_loinc_code_cleared", + "test_edit_preserves_system_when_loinc_code_present", + "test_edit_strips_whitespace_from_ncit_code", + "test_detail_renders_empty_ncit_code_when_none" + ], + "properties": [] + }, + { + "name": "TestClearNcitCode", + "startLine": 340, + "endLine": 370, + "methods": [ + "test_clears_ncit_code_and_metadata", + "test_clear_ncit_writes_audit_log", + "test_clear_ncit_nonexistent_bc_returns_404", + "test_clear_ncit_also_clears_parent_bc_id" + ], + "properties": [] + }, + { + "name": "TestClearLoincCode", + "startLine": 378, + "endLine": 416, + "methods": [ + "test_clears_loinc_code_and_metadata", + "test_clear_loinc_writes_audit_log", + "test_clear_loinc_also_clears_system_and_system_name", + "test_clear_loinc_nonexistent_bc_returns_404" + ], + "properties": [] + }, + { + "name": "TestSubmitForReview", + "startLine": 424, + "endLine": 435, + "methods": [ + "test_advances_status_to_sme_review", + "test_submit_writes_audit_log" + ], + "properties": [] + }, + { + "name": "TestDeleteBc", + "startLine": 443, + "endLine": 457, + "methods": [ + "test_deletes_bc", + "test_delete_writes_audit_log", + "test_nonexistent_bc_returns_404" + ], + "properties": [] + }, + { + "name": "TestExport", + "startLine": 465, + "endLine": 479, + "methods": [ + "test_json_export", + "test_xlsx_export", + "test_odm_xml_export" + ], + "properties": [] + }, + { + "name": "TestLibraryDetail", + "startLine": 511, + "endLine": 547, + "methods": [ + "test_renders_page_for_valid_concept", + "test_redirects_on_api_error", + "test_loinc_api_called_when_loinc_coding_present", + "test_loinc_api_not_called_when_no_loinc_coding", + "test_loinc_api_error_does_not_break_page" + ], + "properties": [] + } + ], + "exports": [ + { + "name": "_bc_form", + "line": 10, + "isDefault": false + }, + { + "name": "TestBcIndex", + "line": 27, + "isDefault": false + }, + { + "name": "TestNewBc", + "line": 51, + "isDefault": false + }, + { + "name": "TestCreateBc", + "line": 62, + "isDefault": false + }, + { + "name": "TestBcDetail", + "line": 117, + "isDefault": false + }, + { + "name": "TestEditBc", + "line": 232, + "isDefault": false + }, + { + "name": "TestClearNcitCode", + "line": 340, + "isDefault": false + }, + { + "name": "TestClearLoincCode", + "line": 378, + "isDefault": false + }, + { + "name": "TestSubmitForReview", + "line": 424, + "isDefault": false + }, + { + "name": "TestDeleteBc", + "line": 443, + "isDefault": false + }, + { + "name": "TestExport", + "line": 465, + "isDefault": false + }, + { + "name": "TestLibraryDetail", + "line": 511, + "isDefault": false + } + ], + "callGraph": [ + { + "caller": "_bc_form", + "callee": "defaults.update", + "lineNumber": 18 + }, + { + "caller": "test_returns_200", + "callee": "client.get", + "lineNumber": 29 + }, + { + "caller": "test_search_by_name", + "callee": "client.get", + "lineNumber": 33 + }, + { + "caller": "test_search_no_match", + "callee": "client.get", + "lineNumber": 38 + }, + { + "caller": "test_filter_by_status", + "callee": "client.get", + "lineNumber": 42 + }, + { + "caller": "test_returns_200", + "callee": "client.get", + "lineNumber": 53 + }, + { + "caller": "test_creates_bc_and_redirects", + "callee": "client.post", + "lineNumber": 64 + }, + { + "caller": "test_creates_bc_and_redirects", + "callee": "_bc_form", + "lineNumber": 64 + }, + { + "caller": "test_creates_bc_and_redirects", + "callee": "app.app_context", + "lineNumber": 66 + }, + { + "caller": "test_creates_bc_and_redirects", + "callee": "db.session.get", + "lineNumber": 67 + }, + { + "caller": "test_missing_bc_id_redirects_with_error", + "callee": "client.post", + "lineNumber": 70 + }, + { + "caller": "test_missing_bc_id_redirects_with_error", + "callee": "_bc_form", + "lineNumber": 70 + }, + { + "caller": "test_missing_bc_id_redirects_with_error", + "callee": "r.data.lower", + "lineNumber": 71 + }, + { + "caller": "test_duplicate_bc_id_rejected", + "callee": "client.post", + "lineNumber": 75 + }, + { + "caller": "test_duplicate_bc_id_rejected", + "callee": "_bc_form", + "lineNumber": 75 + }, + { + "caller": "test_create_writes_audit_log", + "callee": "client.post", + "lineNumber": 79 + }, + { + "caller": "test_create_writes_audit_log", + "callee": "_bc_form", + "lineNumber": 79 + }, + { + "caller": "test_create_writes_audit_log", + "callee": "app.app_context", + "lineNumber": 80 + }, + { + "caller": "test_create_writes_audit_log", + "callee": "AuditLog.query.filter_by(entity_id=\"C00001\", action=\"created\").first", + "lineNumber": 81 + }, + { + "caller": "test_create_writes_audit_log", + "callee": "AuditLog.query.filter_by", + "lineNumber": 81 + }, + { + "caller": "test_create_does_not_set_system_without_loinc_code", + "callee": "client.post", + "lineNumber": 85 + }, + { + "caller": "test_create_does_not_set_system_without_loinc_code", + "callee": "_bc_form", + "lineNumber": 85 + }, + { + "caller": "test_create_does_not_set_system_without_loinc_code", + "callee": "app.app_context", + "lineNumber": 86 + }, + { + "caller": "test_create_does_not_set_system_without_loinc_code", + "callee": "db.session.get", + "lineNumber": 87 + }, + { + "caller": "test_create_sets_system_when_loinc_code_provided", + "callee": "client.post", + "lineNumber": 92 + }, + { + "caller": "test_create_sets_system_when_loinc_code_provided", + "callee": "_bc_form", + "lineNumber": 92 + }, + { + "caller": "test_create_sets_system_when_loinc_code_provided", + "callee": "app.app_context", + "lineNumber": 93 + }, + { + "caller": "test_create_sets_system_when_loinc_code_provided", + "callee": "db.session.get", + "lineNumber": 94 + }, + { + "caller": "test_create_with_decs", + "callee": "_bc_form", + "lineNumber": 99 + }, + { + "caller": "test_create_with_decs", + "callee": "client.post", + "lineNumber": 105 + }, + { + "caller": "test_create_with_decs", + "callee": "app.app_context", + "lineNumber": 106 + }, + { + "caller": "test_create_with_decs", + "callee": "DataElementConcept.query.filter_by(bc_id=\"C00001\").all", + "lineNumber": 107 + }, + { + "caller": "test_create_with_decs", + "callee": "DataElementConcept.query.filter_by", + "lineNumber": 107 + }, + { + "caller": "test_create_with_decs", + "callee": "len", + "lineNumber": 108 + }, + { + "caller": "test_existing_bc_returns_200", + "callee": "patch", + "lineNumber": 119 + }, + { + "caller": "test_existing_bc_returns_200", + "callee": "client.get", + "lineNumber": 121 + }, + { + "caller": "test_missing_bc_returns_404", + "callee": "client.get", + "lineNumber": 125 + }, + { + "caller": "test_loinc_api_called_when_code_set", + "callee": "app.app_context", + "lineNumber": 129 + }, + { + "caller": "test_loinc_api_called_when_code_set", + "callee": "BiomedicalConcept", + "lineNumber": 130 + }, + { + "caller": "test_loinc_api_called_when_code_set", + "callee": "db.session.add", + "lineNumber": 137 + }, + { + "caller": "test_loinc_api_called_when_code_set", + "callee": "db.session.commit", + "lineNumber": 138 + }, + { + "caller": "test_loinc_api_called_when_code_set", + "callee": "patch", + "lineNumber": 141 + }, + { + "caller": "test_loinc_api_called_when_code_set", + "callee": "client.get", + "lineNumber": 143 + }, + { + "caller": "test_loinc_api_called_when_code_set", + "callee": "MockLoinc.return_value.search.assert_called_once_with", + "lineNumber": 146 + }, + { + "caller": "test_loinc_api_not_called_when_no_code", + "callee": "app.app_context", + "lineNumber": 150 + }, + { + "caller": "test_loinc_api_not_called_when_no_code", + "callee": "BiomedicalConcept", + "lineNumber": 151 + }, + { + "caller": "test_loinc_api_not_called_when_no_code", + "callee": "db.session.add", + "lineNumber": 157 + }, + { + "caller": "test_loinc_api_not_called_when_no_code", + "callee": "db.session.commit", + "lineNumber": 158 + }, + { + "caller": "test_loinc_api_not_called_when_no_code", + "callee": "patch", + "lineNumber": 160 + }, + { + "caller": "test_loinc_api_not_called_when_no_code", + "callee": "client.get", + "lineNumber": 161 + }, + { + "caller": "test_loinc_api_not_called_when_no_code", + "callee": "MockLoinc.return_value.search.assert_not_called", + "lineNumber": 164 + }, + { + "caller": "test_loinc_api_error_does_not_break_page", + "callee": "app.app_context", + "lineNumber": 167 + }, + { + "caller": "test_loinc_api_error_does_not_break_page", + "callee": "BiomedicalConcept", + "lineNumber": 168 + }, + { + "caller": "test_loinc_api_error_does_not_break_page", + "callee": "db.session.add", + "lineNumber": 175 + }, + { + "caller": "test_loinc_api_error_does_not_break_page", + "callee": "db.session.commit", + "lineNumber": 176 + }, + { + "caller": "test_loinc_api_error_does_not_break_page", + "callee": "patch", + "lineNumber": 178 + }, + { + "caller": "test_loinc_api_error_does_not_break_page", + "callee": "client.get", + "lineNumber": 180 + }, + { + "caller": "test_loinc_spinner_not_shown_when_loinc_fetched_server_side", + "callee": "app.app_context", + "lineNumber": 185 + }, + { + "caller": "test_loinc_spinner_not_shown_when_loinc_fetched_server_side", + "callee": "BiomedicalConcept", + "lineNumber": 186 + }, + { + "caller": "test_loinc_spinner_not_shown_when_loinc_fetched_server_side", + "callee": "db.session.add", + "lineNumber": 193 + }, + { + "caller": "test_loinc_spinner_not_shown_when_loinc_fetched_server_side", + "callee": "db.session.commit", + "lineNumber": 194 + }, + { + "caller": "test_loinc_spinner_not_shown_when_loinc_fetched_server_side", + "callee": "patch", + "lineNumber": 197 + }, + { + "caller": "test_loinc_spinner_not_shown_when_loinc_fetched_server_side", + "callee": "client.get", + "lineNumber": 199 + }, + { + "caller": "test_loinc_metadata_saved_when_fetched_in_detail", + "callee": "app.app_context", + "lineNumber": 205 + }, + { + "caller": "test_loinc_metadata_saved_when_fetched_in_detail", + "callee": "BiomedicalConcept", + "lineNumber": 206 + }, + { + "caller": "test_loinc_metadata_saved_when_fetched_in_detail", + "callee": "db.session.add", + "lineNumber": 213 + }, + { + "caller": "test_loinc_metadata_saved_when_fetched_in_detail", + "callee": "db.session.commit", + "lineNumber": 214 + }, + { + "caller": "test_loinc_metadata_saved_when_fetched_in_detail", + "callee": "patch", + "lineNumber": 217 + }, + { + "caller": "test_loinc_metadata_saved_when_fetched_in_detail", + "callee": "client.get", + "lineNumber": 219 + }, + { + "caller": "test_loinc_metadata_saved_when_fetched_in_detail", + "callee": "app.app_context", + "lineNumber": 221 + }, + { + "caller": "test_loinc_metadata_saved_when_fetched_in_detail", + "callee": "db.session.get", + "lineNumber": 222 + }, + { + "caller": "test_updates_short_name", + "callee": "client.post", + "lineNumber": 234 + }, + { + "caller": "test_updates_short_name", + "callee": "app.app_context", + "lineNumber": 235 + }, + { + "caller": "test_updates_short_name", + "callee": "db.session.get", + "lineNumber": 236 + }, + { + "caller": "test_edit_writes_audit_log", + "callee": "client.post", + "lineNumber": 240 + }, + { + "caller": "test_edit_writes_audit_log", + "callee": "app.app_context", + "lineNumber": 241 + }, + { + "caller": "test_edit_writes_audit_log", + "callee": "AuditLog.query.filter_by(entity_id=\"C12345\", action=\"updated\").first", + "lineNumber": 242 + }, + { + "caller": "test_edit_writes_audit_log", + "callee": "AuditLog.query.filter_by", + "lineNumber": 242 + }, + { + "caller": "test_nonexistent_bc_returns_404", + "callee": "client.post", + "lineNumber": 247 + }, + { + "caller": "test_edit_clears_ncit_code_when_submitted_empty", + "callee": "client.post", + "lineNumber": 251 + }, + { + "caller": "test_edit_clears_ncit_code_when_submitted_empty", + "callee": "app.app_context", + "lineNumber": 252 + }, + { + "caller": "test_edit_clears_ncit_code_when_submitted_empty", + "callee": "db.session.get", + "lineNumber": 253 + }, + { + "caller": "test_edit_clears_ncit_metadata_when_ncit_code_cleared", + "callee": "app.app_context", + "lineNumber": 257 + }, + { + "caller": "test_edit_clears_ncit_metadata_when_ncit_code_cleared", + "callee": "db.session.get", + "lineNumber": 258 + }, + { + "caller": "test_edit_clears_ncit_metadata_when_ncit_code_cleared", + "callee": "db.session.commit", + "lineNumber": 260 + }, + { + "caller": "test_edit_clears_ncit_metadata_when_ncit_code_cleared", + "callee": "client.post", + "lineNumber": 261 + }, + { + "caller": "test_edit_clears_ncit_metadata_when_ncit_code_cleared", + "callee": "app.app_context", + "lineNumber": 262 + }, + { + "caller": "test_edit_clears_ncit_metadata_when_ncit_code_cleared", + "callee": "db.session.get", + "lineNumber": 263 + }, + { + "caller": "test_edit_clears_parent_bc_id_when_submitted_empty", + "callee": "app.app_context", + "lineNumber": 267 + }, + { + "caller": "test_edit_clears_parent_bc_id_when_submitted_empty", + "callee": "db.session.get", + "lineNumber": 268 + }, + { + "caller": "test_edit_clears_parent_bc_id_when_submitted_empty", + "callee": "db.session.commit", + "lineNumber": 270 + }, + { + "caller": "test_edit_clears_parent_bc_id_when_submitted_empty", + "callee": "client.post", + "lineNumber": 271 + }, + { + "caller": "test_edit_clears_parent_bc_id_when_submitted_empty", + "callee": "app.app_context", + "lineNumber": 272 + }, + { + "caller": "test_edit_clears_parent_bc_id_when_submitted_empty", + "callee": "db.session.get", + "lineNumber": 273 + }, + { + "caller": "test_edit_clears_loinc_code_and_metadata_when_submitted_empty", + "callee": "app.app_context", + "lineNumber": 277 + }, + { + "caller": "test_edit_clears_loinc_code_and_metadata_when_submitted_empty", + "callee": "db.session.get", + "lineNumber": 278 + }, + { + "caller": "test_edit_clears_loinc_code_and_metadata_when_submitted_empty", + "callee": "db.session.commit", + "lineNumber": 281 + }, + { + "caller": "test_edit_clears_loinc_code_and_metadata_when_submitted_empty", + "callee": "client.post", + "lineNumber": 282 + }, + { + "caller": "test_edit_clears_loinc_code_and_metadata_when_submitted_empty", + "callee": "app.app_context", + "lineNumber": 283 + }, + { + "caller": "test_edit_clears_loinc_code_and_metadata_when_submitted_empty", + "callee": "db.session.get", + "lineNumber": 284 + }, + { + "caller": "test_edit_clears_system_and_system_name_when_loinc_code_cleared", + "callee": "app.app_context", + "lineNumber": 289 + }, + { + "caller": "test_edit_clears_system_and_system_name_when_loinc_code_cleared", + "callee": "db.session.get", + "lineNumber": 290 + }, + { + "caller": "test_edit_clears_system_and_system_name_when_loinc_code_cleared", + "callee": "db.session.commit", + "lineNumber": 294 + }, + { + "caller": "test_edit_clears_system_and_system_name_when_loinc_code_cleared", + "callee": "client.post", + "lineNumber": 295 + }, + { + "caller": "test_edit_clears_system_and_system_name_when_loinc_code_cleared", + "callee": "app.app_context", + "lineNumber": 296 + }, + { + "caller": "test_edit_clears_system_and_system_name_when_loinc_code_cleared", + "callee": "db.session.get", + "lineNumber": 297 + }, + { + "caller": "test_edit_preserves_system_when_loinc_code_present", + "callee": "app.app_context", + "lineNumber": 302 + }, + { + "caller": "test_edit_preserves_system_when_loinc_code_present", + "callee": "db.session.get", + "lineNumber": 303 + }, + { + "caller": "test_edit_preserves_system_when_loinc_code_present", + "callee": "db.session.commit", + "lineNumber": 307 + }, + { + "caller": "test_edit_preserves_system_when_loinc_code_present", + "callee": "client.post", + "lineNumber": 308 + }, + { + "caller": "test_edit_preserves_system_when_loinc_code_present", + "callee": "app.app_context", + "lineNumber": 309 + }, + { + "caller": "test_edit_preserves_system_when_loinc_code_present", + "callee": "db.session.get", + "lineNumber": 310 + }, + { + "caller": "test_edit_strips_whitespace_from_ncit_code", + "callee": "client.post", + "lineNumber": 315 + }, + { + "caller": "test_edit_strips_whitespace_from_ncit_code", + "callee": "app.app_context", + "lineNumber": 316 + }, + { + "caller": "test_edit_strips_whitespace_from_ncit_code", + "callee": "db.session.get", + "lineNumber": 317 + }, + { + "caller": "test_detail_renders_empty_ncit_code_when_none", + "callee": "app.app_context", + "lineNumber": 324 + }, + { + "caller": "test_detail_renders_empty_ncit_code_when_none", + "callee": "db.session.get", + "lineNumber": 325 + }, + { + "caller": "test_detail_renders_empty_ncit_code_when_none", + "callee": "db.session.commit", + "lineNumber": 327 + }, + { + "caller": "test_detail_renders_empty_ncit_code_when_none", + "callee": "patch", + "lineNumber": 328 + }, + { + "caller": "test_detail_renders_empty_ncit_code_when_none", + "callee": "client.get", + "lineNumber": 330 + }, + { + "caller": "test_clears_ncit_code_and_metadata", + "callee": "app.app_context", + "lineNumber": 342 + }, + { + "caller": "test_clears_ncit_code_and_metadata", + "callee": "db.session.get", + "lineNumber": 343 + }, + { + "caller": "test_clears_ncit_code_and_metadata", + "callee": "db.session.commit", + "lineNumber": 345 + }, + { + "caller": "test_clears_ncit_code_and_metadata", + "callee": "client.post", + "lineNumber": 346 + }, + { + "caller": "test_clears_ncit_code_and_metadata", + "callee": "app.app_context", + "lineNumber": 347 + }, + { + "caller": "test_clears_ncit_code_and_metadata", + "callee": "db.session.get", + "lineNumber": 348 + }, + { + "caller": "test_clear_ncit_writes_audit_log", + "callee": "client.post", + "lineNumber": 353 + }, + { + "caller": "test_clear_ncit_writes_audit_log", + "callee": "app.app_context", + "lineNumber": 354 + }, + { + "caller": "test_clear_ncit_writes_audit_log", + "callee": "AuditLog.query.filter_by(entity_id=\"C12345\", action=\"ncit_cleared\").first", + "lineNumber": 355 + }, + { + "caller": "test_clear_ncit_writes_audit_log", + "callee": "AuditLog.query.filter_by", + "lineNumber": 355 + }, + { + "caller": "test_clear_ncit_nonexistent_bc_returns_404", + "callee": "client.post", + "lineNumber": 359 + }, + { + "caller": "test_clear_ncit_also_clears_parent_bc_id", + "callee": "app.app_context", + "lineNumber": 363 + }, + { + "caller": "test_clear_ncit_also_clears_parent_bc_id", + "callee": "db.session.get", + "lineNumber": 364 + }, + { + "caller": "test_clear_ncit_also_clears_parent_bc_id", + "callee": "db.session.commit", + "lineNumber": 366 + }, + { + "caller": "test_clear_ncit_also_clears_parent_bc_id", + "callee": "client.post", + "lineNumber": 367 + }, + { + "caller": "test_clear_ncit_also_clears_parent_bc_id", + "callee": "app.app_context", + "lineNumber": 368 + }, + { + "caller": "test_clear_ncit_also_clears_parent_bc_id", + "callee": "db.session.get", + "lineNumber": 369 + }, + { + "caller": "test_clears_loinc_code_and_metadata", + "callee": "app.app_context", + "lineNumber": 380 + }, + { + "caller": "test_clears_loinc_code_and_metadata", + "callee": "db.session.get", + "lineNumber": 381 + }, + { + "caller": "test_clears_loinc_code_and_metadata", + "callee": "db.session.commit", + "lineNumber": 384 + }, + { + "caller": "test_clears_loinc_code_and_metadata", + "callee": "client.post", + "lineNumber": 385 + }, + { + "caller": "test_clears_loinc_code_and_metadata", + "callee": "app.app_context", + "lineNumber": 386 + }, + { + "caller": "test_clears_loinc_code_and_metadata", + "callee": "db.session.get", + "lineNumber": 387 + }, + { + "caller": "test_clear_loinc_writes_audit_log", + "callee": "app.app_context", + "lineNumber": 392 + }, + { + "caller": "test_clear_loinc_writes_audit_log", + "callee": "db.session.get", + "lineNumber": 393 + }, + { + "caller": "test_clear_loinc_writes_audit_log", + "callee": "db.session.commit", + "lineNumber": 395 + }, + { + "caller": "test_clear_loinc_writes_audit_log", + "callee": "client.post", + "lineNumber": 396 + }, + { + "caller": "test_clear_loinc_writes_audit_log", + "callee": "app.app_context", + "lineNumber": 397 + }, + { + "caller": "test_clear_loinc_writes_audit_log", + "callee": "AuditLog.query.filter_by(entity_id=\"C12345\", action=\"loinc_cleared\").first", + "lineNumber": 398 + }, + { + "caller": "test_clear_loinc_writes_audit_log", + "callee": "AuditLog.query.filter_by", + "lineNumber": 398 + }, + { + "caller": "test_clear_loinc_also_clears_system_and_system_name", + "callee": "app.app_context", + "lineNumber": 402 + }, + { + "caller": "test_clear_loinc_also_clears_system_and_system_name", + "callee": "db.session.get", + "lineNumber": 403 + }, + { + "caller": "test_clear_loinc_also_clears_system_and_system_name", + "callee": "db.session.commit", + "lineNumber": 407 + }, + { + "caller": "test_clear_loinc_also_clears_system_and_system_name", + "callee": "client.post", + "lineNumber": 408 + }, + { + "caller": "test_clear_loinc_also_clears_system_and_system_name", + "callee": "app.app_context", + "lineNumber": 409 + }, + { + "caller": "test_clear_loinc_also_clears_system_and_system_name", + "callee": "db.session.get", + "lineNumber": 410 + }, + { + "caller": "test_clear_loinc_nonexistent_bc_returns_404", + "callee": "client.post", + "lineNumber": 415 + }, + { + "caller": "test_advances_status_to_sme_review", + "callee": "client.post", + "lineNumber": 426 + }, + { + "caller": "test_advances_status_to_sme_review", + "callee": "app.app_context", + "lineNumber": 427 + }, + { + "caller": "test_advances_status_to_sme_review", + "callee": "db.session.get", + "lineNumber": 428 + }, + { + "caller": "test_submit_writes_audit_log", + "callee": "client.post", + "lineNumber": 432 + }, + { + "caller": "test_submit_writes_audit_log", + "callee": "app.app_context", + "lineNumber": 433 + }, + { + "caller": "test_submit_writes_audit_log", + "callee": "AuditLog.query.filter_by(entity_id=\"C12345\", action=\"submitted_for_review\").first", + "lineNumber": 434 + }, + { + "caller": "test_submit_writes_audit_log", + "callee": "AuditLog.query.filter_by", + "lineNumber": 434 + }, + { + "caller": "test_deletes_bc", + "callee": "client.post", + "lineNumber": 445 + }, + { + "caller": "test_deletes_bc", + "callee": "app.app_context", + "lineNumber": 446 + }, + { + "caller": "test_deletes_bc", + "callee": "db.session.get", + "lineNumber": 447 + }, + { + "caller": "test_delete_writes_audit_log", + "callee": "client.post", + "lineNumber": 450 + }, + { + "caller": "test_delete_writes_audit_log", + "callee": "app.app_context", + "lineNumber": 451 + }, + { + "caller": "test_delete_writes_audit_log", + "callee": "AuditLog.query.filter_by(entity_id=\"C12345\", action=\"deleted\").first", + "lineNumber": 452 + }, + { + "caller": "test_delete_writes_audit_log", + "callee": "AuditLog.query.filter_by", + "lineNumber": 452 + }, + { + "caller": "test_nonexistent_bc_returns_404", + "callee": "client.post", + "lineNumber": 456 + }, + { + "caller": "test_json_export", + "callee": "client.get", + "lineNumber": 467 + }, + { + "caller": "test_xlsx_export", + "callee": "client.get", + "lineNumber": 472 + }, + { + "caller": "test_odm_xml_export", + "callee": "client.get", + "lineNumber": 477 + }, + { + "caller": "test_renders_page_for_valid_concept", + "callee": "patch", + "lineNumber": 513 + }, + { + "caller": "test_renders_page_for_valid_concept", + "callee": "patch", + "lineNumber": 513 + }, + { + "caller": "test_renders_page_for_valid_concept", + "callee": "client.get", + "lineNumber": 516 + }, + { + "caller": "test_redirects_on_api_error", + "callee": "patch", + "lineNumber": 521 + }, + { + "caller": "test_redirects_on_api_error", + "callee": "client.get", + "lineNumber": 523 + }, + { + "caller": "test_loinc_api_called_when_loinc_coding_present", + "callee": "patch", + "lineNumber": 527 + }, + { + "caller": "test_loinc_api_called_when_loinc_coding_present", + "callee": "patch", + "lineNumber": 527 + }, + { + "caller": "test_loinc_api_called_when_loinc_coding_present", + "callee": "client.get", + "lineNumber": 530 + }, + { + "caller": "test_loinc_api_called_when_loinc_coding_present", + "callee": "MockLoinc.return_value.search.assert_called_once_with", + "lineNumber": 532 + }, + { + "caller": "test_loinc_api_not_called_when_no_loinc_coding", + "callee": "patch", + "lineNumber": 536 + }, + { + "caller": "test_loinc_api_not_called_when_no_loinc_coding", + "callee": "patch", + "lineNumber": 536 + }, + { + "caller": "test_loinc_api_not_called_when_no_loinc_coding", + "callee": "client.get", + "lineNumber": 538 + }, + { + "caller": "test_loinc_api_not_called_when_no_loinc_coding", + "callee": "MockLoinc.return_value.search.assert_not_called", + "lineNumber": 540 + }, + { + "caller": "test_loinc_api_error_does_not_break_page", + "callee": "patch", + "lineNumber": 543 + }, + { + "caller": "test_loinc_api_error_does_not_break_page", + "callee": "patch", + "lineNumber": 543 + }, + { + "caller": "test_loinc_api_error_does_not_break_page", + "callee": "client.get", + "lineNumber": 546 + } + ], + "metrics": { + "importCount": 3, + "exportCount": 12, + "functionCount": 1, + "classCount": 11 + } + }, + { + "path": "tests/test_governance_routes.py", + "language": "python", + "fileCategory": "code", + "totalLines": 236, + "nonEmptyLines": 196, + "classes": [ + { + "name": "TestGovernanceBoard", + "startLine": 11, + "endLine": 14, + "methods": [ + "test_board_returns_200" + ], + "properties": [] + }, + { + "name": "TestAdvance", + "startLine": 17, + "endLine": 68, + "methods": [ + "test_advances_provisional_to_sme_review", + "test_advance_through_all_stages", + "test_already_published_stays_published", + "test_advance_creates_governance_record", + "test_advance_writes_audit_log", + "test_advance_nonexistent_bc_returns_404", + "test_advance_ajax_returns_json" + ], + "properties": [] + }, + { + "name": "TestReject", + "startLine": 71, + "endLine": 105, + "methods": [ + "test_reject_returns_to_provisional", + "test_reject_creates_governance_record", + "test_reject_writes_audit_log", + "test_reject_ajax_returns_json", + "test_reject_nonexistent_bc_returns_404" + ], + "properties": [] + }, + { + "name": "TestGovernanceExport", + "startLine": 108, + "endLine": 236, + "methods": [ + "test_export_returns_xlsx", + "test_export_filename_in_content_disposition", + "test_export_enforces_xlsx_extension", + "test_export_excludes_non_stage3_bcs", + "test_export_includes_stage3_bcs", + "test_export_system_columns_blank_without_loinc_code", + "test_export_system_columns_populated_with_loinc_code", + "test_export_code_column_uses_loinc_code" + ], + "properties": [] + } + ], + "exports": [ + { + "name": "TestGovernanceBoard", + "line": 11, + "isDefault": false + }, + { + "name": "TestAdvance", + "line": 17, + "isDefault": false + }, + { + "name": "TestReject", + "line": 71, + "isDefault": false + }, + { + "name": "TestGovernanceExport", + "line": 108, + "isDefault": false + } + ], + "callGraph": [ + { + "caller": "test_board_returns_200", + "callee": "client.get", + "lineNumber": 13 + }, + { + "caller": "test_advances_provisional_to_sme_review", + "callee": "client.post", + "lineNumber": 19 + }, + { + "caller": "test_advances_provisional_to_sme_review", + "callee": "app.app_context", + "lineNumber": 20 + }, + { + "caller": "test_advances_provisional_to_sme_review", + "callee": "db.session.get", + "lineNumber": 21 + }, + { + "caller": "test_advance_through_all_stages", + "callee": "client.post", + "lineNumber": 26 + }, + { + "caller": "test_advance_through_all_stages", + "callee": "app.app_context", + "lineNumber": 27 + }, + { + "caller": "test_advance_through_all_stages", + "callee": "db.session.get", + "lineNumber": 28 + }, + { + "caller": "test_already_published_stays_published", + "callee": "range", + "lineNumber": 33 + }, + { + "caller": "test_already_published_stays_published", + "callee": "client.post", + "lineNumber": 34 + }, + { + "caller": "test_already_published_stays_published", + "callee": "client.post", + "lineNumber": 36 + }, + { + "caller": "test_already_published_stays_published", + "callee": "app.app_context", + "lineNumber": 38 + }, + { + "caller": "test_already_published_stays_published", + "callee": "db.session.get", + "lineNumber": 39 + }, + { + "caller": "test_advance_creates_governance_record", + "callee": "client.post", + "lineNumber": 43 + }, + { + "caller": "test_advance_creates_governance_record", + "callee": "app.app_context", + "lineNumber": 44 + }, + { + "caller": "test_advance_creates_governance_record", + "callee": "GovernanceRecord.query.filter_by(bc_id=\"C12345\", action=\"advanced\").first", + "lineNumber": 45 + }, + { + "caller": "test_advance_creates_governance_record", + "callee": "GovernanceRecord.query.filter_by", + "lineNumber": 45 + }, + { + "caller": "test_advance_writes_audit_log", + "callee": "client.post", + "lineNumber": 49 + }, + { + "caller": "test_advance_writes_audit_log", + "callee": "app.app_context", + "lineNumber": 50 + }, + { + "caller": "test_advance_writes_audit_log", + "callee": "AuditLog.query.filter_by(entity_id=\"C12345\", action=\"status_changed\").first", + "lineNumber": 51 + }, + { + "caller": "test_advance_writes_audit_log", + "callee": "AuditLog.query.filter_by", + "lineNumber": 51 + }, + { + "caller": "test_advance_nonexistent_bc_returns_404", + "callee": "client.post", + "lineNumber": 57 + }, + { + "caller": "test_advance_ajax_returns_json", + "callee": "client.post", + "lineNumber": 61 + }, + { + "caller": "test_advance_ajax_returns_json", + "callee": "r.get_json", + "lineNumber": 66 + }, + { + "caller": "test_reject_returns_to_provisional", + "callee": "client.post", + "lineNumber": 74 + }, + { + "caller": "test_reject_returns_to_provisional", + "callee": "client.post", + "lineNumber": 75 + }, + { + "caller": "test_reject_returns_to_provisional", + "callee": "app.app_context", + "lineNumber": 76 + }, + { + "caller": "test_reject_returns_to_provisional", + "callee": "db.session.get", + "lineNumber": 77 + }, + { + "caller": "test_reject_creates_governance_record", + "callee": "client.post", + "lineNumber": 81 + }, + { + "caller": "test_reject_creates_governance_record", + "callee": "app.app_context", + "lineNumber": 82 + }, + { + "caller": "test_reject_creates_governance_record", + "callee": "GovernanceRecord.query.filter_by(bc_id=\"C12345\", action=\"rejected\").first", + "lineNumber": 83 + }, + { + "caller": "test_reject_creates_governance_record", + "callee": "GovernanceRecord.query.filter_by", + "lineNumber": 83 + }, + { + "caller": "test_reject_writes_audit_log", + "callee": "client.post", + "lineNumber": 87 + }, + { + "caller": "test_reject_writes_audit_log", + "callee": "client.post", + "lineNumber": 88 + }, + { + "caller": "test_reject_writes_audit_log", + "callee": "app.app_context", + "lineNumber": 89 + }, + { + "caller": "test_reject_writes_audit_log", + "callee": "AuditLog.query.filter_by(entity_id=\"C12345\", action=\"rejected\").first", + "lineNumber": 90 + }, + { + "caller": "test_reject_writes_audit_log", + "callee": "AuditLog.query.filter_by", + "lineNumber": 90 + }, + { + "caller": "test_reject_ajax_returns_json", + "callee": "client.post", + "lineNumber": 95 + }, + { + "caller": "test_reject_ajax_returns_json", + "callee": "r.get_json", + "lineNumber": 100 + }, + { + "caller": "test_reject_nonexistent_bc_returns_404", + "callee": "client.post", + "lineNumber": 104 + }, + { + "caller": "test_export_returns_xlsx", + "callee": "range", + "lineNumber": 110 + }, + { + "caller": "test_export_returns_xlsx", + "callee": "client.post", + "lineNumber": 111 + }, + { + "caller": "test_export_returns_xlsx", + "callee": "client.get", + "lineNumber": 112 + }, + { + "caller": "test_export_filename_in_content_disposition", + "callee": "range", + "lineNumber": 117 + }, + { + "caller": "test_export_filename_in_content_disposition", + "callee": "client.post", + "lineNumber": 118 + }, + { + "caller": "test_export_filename_in_content_disposition", + "callee": "client.get", + "lineNumber": 119 + }, + { + "caller": "test_export_enforces_xlsx_extension", + "callee": "range", + "lineNumber": 123 + }, + { + "caller": "test_export_enforces_xlsx_extension", + "callee": "client.post", + "lineNumber": 124 + }, + { + "caller": "test_export_enforces_xlsx_extension", + "callee": "client.get", + "lineNumber": 125 + }, + { + "caller": "test_export_excludes_non_stage3_bcs", + "callee": "client.get", + "lineNumber": 130 + }, + { + "caller": "test_export_excludes_non_stage3_bcs", + "callee": "openpyxl.load_workbook", + "lineNumber": 136 + }, + { + "caller": "test_export_excludes_non_stage3_bcs", + "callee": "io.BytesIO", + "lineNumber": 136 + }, + { + "caller": "test_export_includes_stage3_bcs", + "callee": "range", + "lineNumber": 141 + }, + { + "caller": "test_export_includes_stage3_bcs", + "callee": "client.post", + "lineNumber": 142 + }, + { + "caller": "test_export_includes_stage3_bcs", + "callee": "client.get", + "lineNumber": 143 + }, + { + "caller": "test_export_includes_stage3_bcs", + "callee": "openpyxl.load_workbook", + "lineNumber": 148 + }, + { + "caller": "test_export_includes_stage3_bcs", + "callee": "io.BytesIO", + "lineNumber": 148 + }, + { + "caller": "test_export_system_columns_blank_without_loinc_code", + "callee": "app.app_context", + "lineNumber": 153 + }, + { + "caller": "test_export_system_columns_blank_without_loinc_code", + "callee": "BiomedicalConcept", + "lineNumber": 156 + }, + { + "caller": "test_export_system_columns_blank_without_loinc_code", + "callee": "_db.session.add", + "lineNumber": 164 + }, + { + "caller": "test_export_system_columns_blank_without_loinc_code", + "callee": "_db.session.commit", + "lineNumber": 165 + }, + { + "caller": "test_export_system_columns_blank_without_loinc_code", + "callee": "range", + "lineNumber": 166 + }, + { + "caller": "test_export_system_columns_blank_without_loinc_code", + "callee": "client.post", + "lineNumber": 167 + }, + { + "caller": "test_export_system_columns_blank_without_loinc_code", + "callee": "client.get", + "lineNumber": 168 + }, + { + "caller": "test_export_system_columns_blank_without_loinc_code", + "callee": "openpyxl.load_workbook", + "lineNumber": 173 + }, + { + "caller": "test_export_system_columns_blank_without_loinc_code", + "callee": "io.BytesIO", + "lineNumber": 173 + }, + { + "caller": "test_export_system_columns_blank_without_loinc_code", + "callee": "ws.cell", + "lineNumber": 175 + }, + { + "caller": "test_export_system_columns_blank_without_loinc_code", + "callee": "range", + "lineNumber": 175 + }, + { + "caller": "test_export_system_columns_blank_without_loinc_code", + "callee": "headers.index", + "lineNumber": 176 + }, + { + "caller": "test_export_system_columns_blank_without_loinc_code", + "callee": "headers.index", + "lineNumber": 177 + }, + { + "caller": "test_export_system_columns_blank_without_loinc_code", + "callee": "ws.cell", + "lineNumber": 178 + }, + { + "caller": "test_export_system_columns_blank_without_loinc_code", + "callee": "ws.cell", + "lineNumber": 179 + }, + { + "caller": "test_export_system_columns_populated_with_loinc_code", + "callee": "app.app_context", + "lineNumber": 182 + }, + { + "caller": "test_export_system_columns_populated_with_loinc_code", + "callee": "BiomedicalConcept", + "lineNumber": 185 + }, + { + "caller": "test_export_system_columns_populated_with_loinc_code", + "callee": "_db.session.add", + "lineNumber": 194 + }, + { + "caller": "test_export_system_columns_populated_with_loinc_code", + "callee": "_db.session.commit", + "lineNumber": 195 + }, + { + "caller": "test_export_system_columns_populated_with_loinc_code", + "callee": "range", + "lineNumber": 196 + }, + { + "caller": "test_export_system_columns_populated_with_loinc_code", + "callee": "client.post", + "lineNumber": 197 + }, + { + "caller": "test_export_system_columns_populated_with_loinc_code", + "callee": "client.get", + "lineNumber": 198 + }, + { + "caller": "test_export_system_columns_populated_with_loinc_code", + "callee": "openpyxl.load_workbook", + "lineNumber": 203 + }, + { + "caller": "test_export_system_columns_populated_with_loinc_code", + "callee": "io.BytesIO", + "lineNumber": 203 + }, + { + "caller": "test_export_system_columns_populated_with_loinc_code", + "callee": "ws.cell", + "lineNumber": 205 + }, + { + "caller": "test_export_system_columns_populated_with_loinc_code", + "callee": "range", + "lineNumber": 205 + }, + { + "caller": "test_export_system_columns_populated_with_loinc_code", + "callee": "headers.index", + "lineNumber": 206 + }, + { + "caller": "test_export_system_columns_populated_with_loinc_code", + "callee": "headers.index", + "lineNumber": 207 + }, + { + "caller": "test_export_system_columns_populated_with_loinc_code", + "callee": "ws.cell", + "lineNumber": 208 + }, + { + "caller": "test_export_system_columns_populated_with_loinc_code", + "callee": "ws.cell", + "lineNumber": 209 + }, + { + "caller": "test_export_code_column_uses_loinc_code", + "callee": "app.app_context", + "lineNumber": 212 + }, + { + "caller": "test_export_code_column_uses_loinc_code", + "callee": "BiomedicalConcept", + "lineNumber": 215 + }, + { + "caller": "test_export_code_column_uses_loinc_code", + "callee": "_db.session.add", + "lineNumber": 222 + }, + { + "caller": "test_export_code_column_uses_loinc_code", + "callee": "_db.session.commit", + "lineNumber": 223 + }, + { + "caller": "test_export_code_column_uses_loinc_code", + "callee": "range", + "lineNumber": 224 + }, + { + "caller": "test_export_code_column_uses_loinc_code", + "callee": "client.post", + "lineNumber": 225 + }, + { + "caller": "test_export_code_column_uses_loinc_code", + "callee": "client.get", + "lineNumber": 226 + }, + { + "caller": "test_export_code_column_uses_loinc_code", + "callee": "openpyxl.load_workbook", + "lineNumber": 231 + }, + { + "caller": "test_export_code_column_uses_loinc_code", + "callee": "io.BytesIO", + "lineNumber": 231 + }, + { + "caller": "test_export_code_column_uses_loinc_code", + "callee": "ws.cell", + "lineNumber": 233 + }, + { + "caller": "test_export_code_column_uses_loinc_code", + "callee": "range", + "lineNumber": 233 + }, + { + "caller": "test_export_code_column_uses_loinc_code", + "callee": "headers.index", + "lineNumber": 234 + }, + { + "caller": "test_export_code_column_uses_loinc_code", + "callee": "ws.cell", + "lineNumber": 235 + } + ], + "metrics": { + "importCount": 4, + "exportCount": 4, + "functionCount": 0, + "classCount": 4 + } + }, + { + "path": "tests/test_ingestion_routes.py", + "language": "python", + "fileCategory": "code", + "totalLines": 204, + "nonEmptyLines": 175, + "functions": [ + { + "name": "_csv_file", + "startLine": 13, + "endLine": 19, + "params": [ + "rows" + ] + }, + { + "name": "_json_file", + "startLine": 22, + "endLine": 23, + "params": [ + "data" + ] + } + ], + "classes": [ + { + "name": "TestIngestionIndex", + "startLine": 26, + "endLine": 29, + "methods": [ + "test_returns_200" + ], + "properties": [] + }, + { + "name": "TestUpload", + "startLine": 32, + "endLine": 83, + "methods": [ + "test_upload_csv_creates_ingestion_records", + "test_upload_json_creates_ingestion_records", + "test_no_file_redirects_with_error", + "test_wrong_extension_rejected", + "test_duplicate_bc_flagged" + ], + "properties": [] + }, + { + "name": "TestApprove", + "startLine": 86, + "endLine": 131, + "methods": [ + "_upload_and_get_record_id", + "test_approve_creates_bc", + "test_approve_sets_status_approved", + "test_approve_nonexistent_record_returns_404", + "test_approve_already_existing_bc_does_not_duplicate" + ], + "properties": [] + }, + { + "name": "TestReject", + "startLine": 134, + "endLine": 170, + "methods": [ + "test_reject_sets_status_rejected", + "test_reject_does_not_create_bc", + "test_reject_nonexistent_record_returns_404" + ], + "properties": [] + }, + { + "name": "TestApproveAll", + "startLine": 173, + "endLine": 204, + "methods": [ + "test_approve_all_skips_records_with_errors", + "test_approve_all_writes_audit_log_per_bc" + ], + "properties": [] + } + ], + "exports": [ + { + "name": "_csv_file", + "line": 13, + "isDefault": false + }, + { + "name": "_json_file", + "line": 22, + "isDefault": false + }, + { + "name": "TestIngestionIndex", + "line": 26, + "isDefault": false + }, + { + "name": "TestUpload", + "line": 32, + "isDefault": false + }, + { + "name": "TestApprove", + "line": 86, + "isDefault": false + }, + { + "name": "TestReject", + "line": 134, + "isDefault": false + }, + { + "name": "TestApproveAll", + "line": 173, + "isDefault": false + } + ], + "callGraph": [ + { + "caller": "_csv_file", + "callee": "io.StringIO", + "lineNumber": 15 + }, + { + "caller": "_csv_file", + "callee": "csv.DictWriter", + "lineNumber": 16 + }, + { + "caller": "_csv_file", + "callee": "rows[0].keys", + "lineNumber": 16 + }, + { + "caller": "_csv_file", + "callee": "writer.writeheader", + "lineNumber": 17 + }, + { + "caller": "_csv_file", + "callee": "writer.writerows", + "lineNumber": 18 + }, + { + "caller": "_csv_file", + "callee": "io.BytesIO", + "lineNumber": 19 + }, + { + "caller": "_csv_file", + "callee": "buf.getvalue().encode", + "lineNumber": 19 + }, + { + "caller": "_csv_file", + "callee": "buf.getvalue", + "lineNumber": 19 + }, + { + "caller": "_json_file", + "callee": "io.BytesIO", + "lineNumber": 23 + }, + { + "caller": "_json_file", + "callee": "json.dumps(data).encode", + "lineNumber": 23 + }, + { + "caller": "_json_file", + "callee": "json.dumps", + "lineNumber": 23 + }, + { + "caller": "test_returns_200", + "callee": "client.get", + "lineNumber": 28 + }, + { + "caller": "test_upload_csv_creates_ingestion_records", + "callee": "_csv_file", + "lineNumber": 35 + }, + { + "caller": "test_upload_csv_creates_ingestion_records", + "callee": "client.post", + "lineNumber": 36 + }, + { + "caller": "test_upload_csv_creates_ingestion_records", + "callee": "app.app_context", + "lineNumber": 43 + }, + { + "caller": "test_upload_csv_creates_ingestion_records", + "callee": "IngestionRecord.query.count", + "lineNumber": 44 + }, + { + "caller": "test_upload_json_creates_ingestion_records", + "callee": "_json_file", + "lineNumber": 48 + }, + { + "caller": "test_upload_json_creates_ingestion_records", + "callee": "client.post", + "lineNumber": 49 + }, + { + "caller": "test_upload_json_creates_ingestion_records", + "callee": "app.app_context", + "lineNumber": 56 + }, + { + "caller": "test_upload_json_creates_ingestion_records", + "callee": "IngestionRecord.query.count", + "lineNumber": 57 + }, + { + "caller": "test_no_file_redirects_with_error", + "callee": "client.post", + "lineNumber": 60 + }, + { + "caller": "test_wrong_extension_rejected", + "callee": "client.post", + "lineNumber": 64 + }, + { + "caller": "test_wrong_extension_rejected", + "callee": "io.BytesIO", + "lineNumber": 66 + }, + { + "caller": "test_duplicate_bc_flagged", + "callee": "_csv_file", + "lineNumber": 74 + }, + { + "caller": "test_duplicate_bc_flagged", + "callee": "client.post", + "lineNumber": 75 + }, + { + "caller": "test_duplicate_bc_flagged", + "callee": "app.app_context", + "lineNumber": 80 + }, + { + "caller": "test_duplicate_bc_flagged", + "callee": "IngestionRecord.query.first", + "lineNumber": 81 + }, + { + "caller": "_upload_and_get_record_id", + "callee": "_csv_file", + "lineNumber": 89 + }, + { + "caller": "_upload_and_get_record_id", + "callee": "client.post", + "lineNumber": 90 + }, + { + "caller": "_upload_and_get_record_id", + "callee": "app.app_context", + "lineNumber": 95 + }, + { + "caller": "_upload_and_get_record_id", + "callee": "IngestionRecord.query.filter_by(status=\"pending\").first", + "lineNumber": 96 + }, + { + "caller": "_upload_and_get_record_id", + "callee": "IngestionRecord.query.filter_by", + "lineNumber": 96 + }, + { + "caller": "test_approve_creates_bc", + "callee": "self._upload_and_get_record_id", + "lineNumber": 100 + }, + { + "caller": "test_approve_creates_bc", + "callee": "client.post", + "lineNumber": 102 + }, + { + "caller": "test_approve_creates_bc", + "callee": "app.app_context", + "lineNumber": 103 + }, + { + "caller": "test_approve_creates_bc", + "callee": "db.session.get", + "lineNumber": 104 + }, + { + "caller": "test_approve_sets_status_approved", + "callee": "self._upload_and_get_record_id", + "lineNumber": 107 + }, + { + "caller": "test_approve_sets_status_approved", + "callee": "client.post", + "lineNumber": 108 + }, + { + "caller": "test_approve_sets_status_approved", + "callee": "app.app_context", + "lineNumber": 109 + }, + { + "caller": "test_approve_sets_status_approved", + "callee": "db.session.get", + "lineNumber": 110 + }, + { + "caller": "test_approve_nonexistent_record_returns_404", + "callee": "client.post", + "lineNumber": 114 + }, + { + "caller": "test_approve_already_existing_bc_does_not_duplicate", + "callee": "_csv_file", + "lineNumber": 119 + }, + { + "caller": "test_approve_already_existing_bc_does_not_duplicate", + "callee": "client.post", + "lineNumber": 120 + }, + { + "caller": "test_approve_already_existing_bc_does_not_duplicate", + "callee": "app.app_context", + "lineNumber": 125 + }, + { + "caller": "test_approve_already_existing_bc_does_not_duplicate", + "callee": "IngestionRecord.query.filter_by(status=\"pending\").first", + "lineNumber": 126 + }, + { + "caller": "test_approve_already_existing_bc_does_not_duplicate", + "callee": "IngestionRecord.query.filter_by", + "lineNumber": 126 + }, + { + "caller": "test_approve_already_existing_bc_does_not_duplicate", + "callee": "client.post", + "lineNumber": 128 + }, + { + "caller": "test_approve_already_existing_bc_does_not_duplicate", + "callee": "app.app_context", + "lineNumber": 129 + }, + { + "caller": "test_approve_already_existing_bc_does_not_duplicate", + "callee": "BiomedicalConcept.query.filter_by(bc_id=\"C12345\").count", + "lineNumber": 131 + }, + { + "caller": "test_approve_already_existing_bc_does_not_duplicate", + "callee": "BiomedicalConcept.query.filter_by", + "lineNumber": 131 + }, + { + "caller": "test_reject_sets_status_rejected", + "callee": "_csv_file", + "lineNumber": 137 + }, + { + "caller": "test_reject_sets_status_rejected", + "callee": "client.post", + "lineNumber": 138 + }, + { + "caller": "test_reject_sets_status_rejected", + "callee": "app.app_context", + "lineNumber": 143 + }, + { + "caller": "test_reject_sets_status_rejected", + "callee": "IngestionRecord.query.filter_by(status=\"pending\").first", + "lineNumber": 144 + }, + { + "caller": "test_reject_sets_status_rejected", + "callee": "IngestionRecord.query.filter_by", + "lineNumber": 144 + }, + { + "caller": "test_reject_sets_status_rejected", + "callee": "client.post", + "lineNumber": 147 + }, + { + "caller": "test_reject_sets_status_rejected", + "callee": "app.app_context", + "lineNumber": 148 + }, + { + "caller": "test_reject_sets_status_rejected", + "callee": "db.session.get", + "lineNumber": 149 + }, + { + "caller": "test_reject_does_not_create_bc", + "callee": "_csv_file", + "lineNumber": 154 + }, + { + "caller": "test_reject_does_not_create_bc", + "callee": "client.post", + "lineNumber": 155 + }, + { + "caller": "test_reject_does_not_create_bc", + "callee": "app.app_context", + "lineNumber": 160 + }, + { + "caller": "test_reject_does_not_create_bc", + "callee": "IngestionRecord.query.filter_by(status=\"pending\").first", + "lineNumber": 161 + }, + { + "caller": "test_reject_does_not_create_bc", + "callee": "IngestionRecord.query.filter_by", + "lineNumber": 161 + }, + { + "caller": "test_reject_does_not_create_bc", + "callee": "client.post", + "lineNumber": 164 + }, + { + "caller": "test_reject_does_not_create_bc", + "callee": "app.app_context", + "lineNumber": 165 + }, + { + "caller": "test_reject_does_not_create_bc", + "callee": "db.session.get", + "lineNumber": 166 + }, + { + "caller": "test_reject_nonexistent_record_returns_404", + "callee": "client.post", + "lineNumber": 169 + }, + { + "caller": "test_approve_all_skips_records_with_errors", + "callee": "_csv_file", + "lineNumber": 178 + }, + { + "caller": "test_approve_all_skips_records_with_errors", + "callee": "client.session_transaction", + "lineNumber": 179 + }, + { + "caller": "test_approve_all_skips_records_with_errors", + "callee": "client.post", + "lineNumber": 181 + }, + { + "caller": "test_approve_all_skips_records_with_errors", + "callee": "client.post", + "lineNumber": 186 + }, + { + "caller": "test_approve_all_skips_records_with_errors", + "callee": "app.app_context", + "lineNumber": 187 + }, + { + "caller": "test_approve_all_skips_records_with_errors", + "callee": "db.session.get", + "lineNumber": 188 + }, + { + "caller": "test_approve_all_writes_audit_log_per_bc", + "callee": "_csv_file", + "lineNumber": 192 + }, + { + "caller": "test_approve_all_writes_audit_log_per_bc", + "callee": "client.session_transaction", + "lineNumber": 193 + }, + { + "caller": "test_approve_all_writes_audit_log_per_bc", + "callee": "client.post", + "lineNumber": 195 + }, + { + "caller": "test_approve_all_writes_audit_log_per_bc", + "callee": "client.post", + "lineNumber": 200 + }, + { + "caller": "test_approve_all_writes_audit_log_per_bc", + "callee": "app.app_context", + "lineNumber": 201 + }, + { + "caller": "test_approve_all_writes_audit_log_per_bc", + "callee": "db.session.get", + "lineNumber": 202 + }, + { + "caller": "test_approve_all_writes_audit_log_per_bc", + "callee": "AuditLog.query.filter_by(entity_type=\"BiomedicalConcept\", entity_id=\"C001\", action=\"created_via_ingestion\").first", + "lineNumber": 203 + }, + { + "caller": "test_approve_all_writes_audit_log_per_bc", + "callee": "AuditLog.query.filter_by", + "lineNumber": 203 + } + ], + "metrics": { + "importCount": 4, + "exportCount": 7, + "functionCount": 2, + "classCount": 5 + } + }, + { + "path": "tests/test_ingestion_service.py", + "language": "python", + "fileCategory": "code", + "totalLines": 259, + "nonEmptyLines": 196, + "classes": [ + { + "name": "TestSimilarity", + "startLine": 22, + "endLine": 34, + "methods": [ + "test_identical_strings", + "test_case_insensitive", + "test_completely_different", + "test_partial_match" + ], + "properties": [] + }, + { + "name": "TestMatchField", + "startLine": 42, + "endLine": 65, + "methods": [ + "test_exact_alias_match", + "test_known_alias", + "test_definition_alias", + "test_spaces_normalised", + "test_unknown_column_returns_low_score" + ], + "properties": [] + }, + { + "name": "TestMapFields", + "startLine": 73, + "endLine": 93, + "methods": [ + "test_canonical_columns_mapped_at_full_confidence", + "test_none_values_skipped", + "test_nan_values_skipped", + "test_low_confidence_columns_excluded", + "test_values_stripped" + ], + "properties": [] + }, + { + "name": "TestValidateBc", + "startLine": 101, + "endLine": 133, + "methods": [ + "_valid", + "test_valid_record_has_no_errors", + "test_missing_short_name", + "test_missing_definition", + "test_missing_both_ids", + "test_invalid_ncit_format", + "test_ncit_starting_with_c_accepted" + ], + "properties": [] + }, + { + "name": "TestDeduplicate", + "startLine": 141, + "endLine": 164, + "methods": [ + "_record", + "test_marks_existing_ids_as_duplicate", + "test_case_insensitive_comparison", + "test_empty_existing_ids", + "test_falls_back_to_ncit_code" + ], + "properties": [] + }, + { + "name": "TestParseCsv", + "startLine": 172, + "endLine": 198, + "methods": [ + "_make_csv", + "test_valid_row_parsed", + "test_invalid_file_returns_error_record", + "test_missing_definition_produces_validation_error" + ], + "properties": [] + }, + { + "name": "TestParseJson", + "startLine": 206, + "endLine": 223, + "methods": [ + "test_array_of_objects", + "test_single_object_wrapped", + "test_invalid_json_returns_error" + ], + "properties": [] + }, + { + "name": "TestGroupByBc", + "startLine": 231, + "endLine": 259, + "methods": [ + "test_single_row_becomes_one_record", + "test_dec_sub_rows_grouped_under_parent", + "test_multiple_bcs_produce_separate_records", + "test_row_without_bc_id_skipped" + ], + "properties": [] + } + ], + "exports": [ + { + "name": "TestSimilarity", + "line": 22, + "isDefault": false + }, + { + "name": "TestMatchField", + "line": 42, + "isDefault": false + }, + { + "name": "TestMapFields", + "line": 73, + "isDefault": false + }, + { + "name": "TestValidateBc", + "line": 101, + "isDefault": false + }, + { + "name": "TestDeduplicate", + "line": 141, + "isDefault": false + }, + { + "name": "TestParseCsv", + "line": 172, + "isDefault": false + }, + { + "name": "TestParseJson", + "line": 206, + "isDefault": false + }, + { + "name": "TestGroupByBc", + "line": 231, + "isDefault": false + } + ], + "callGraph": [ + { + "caller": "test_identical_strings", + "callee": "_similarity", + "lineNumber": 24 + }, + { + "caller": "test_case_insensitive", + "callee": "_similarity", + "lineNumber": 27 + }, + { + "caller": "test_completely_different", + "callee": "_similarity", + "lineNumber": 30 + }, + { + "caller": "test_partial_match", + "callee": "_similarity", + "lineNumber": 33 + }, + { + "caller": "test_exact_alias_match", + "callee": "_match_field", + "lineNumber": 44 + }, + { + "caller": "test_known_alias", + "callee": "_match_field", + "lineNumber": 49 + }, + { + "caller": "test_definition_alias", + "callee": "_match_field", + "lineNumber": 54 + }, + { + "caller": "test_spaces_normalised", + "callee": "_match_field", + "lineNumber": 59 + }, + { + "caller": "test_unknown_column_returns_low_score", + "callee": "_match_field", + "lineNumber": 64 + }, + { + "caller": "test_canonical_columns_mapped_at_full_confidence", + "callee": "map_fields", + "lineNumber": 75 + }, + { + "caller": "test_none_values_skipped", + "callee": "map_fields", + "lineNumber": 80 + }, + { + "caller": "test_nan_values_skipped", + "callee": "map_fields", + "lineNumber": 84 + }, + { + "caller": "test_nan_values_skipped", + "callee": "float", + "lineNumber": 84 + }, + { + "caller": "test_low_confidence_columns_excluded", + "callee": "map_fields", + "lineNumber": 88 + }, + { + "caller": "test_values_stripped", + "callee": "map_fields", + "lineNumber": 92 + }, + { + "caller": "test_valid_record_has_no_errors", + "callee": "validate_bc", + "lineNumber": 106 + }, + { + "caller": "test_valid_record_has_no_errors", + "callee": "self._valid", + "lineNumber": 106 + }, + { + "caller": "test_missing_short_name", + "callee": "self._valid", + "lineNumber": 109 + }, + { + "caller": "test_missing_short_name", + "callee": "validate_bc", + "lineNumber": 111 + }, + { + "caller": "test_missing_short_name", + "callee": "any", + "lineNumber": 112 + }, + { + "caller": "test_missing_definition", + "callee": "self._valid", + "lineNumber": 115 + }, + { + "caller": "test_missing_definition", + "callee": "validate_bc", + "lineNumber": 117 + }, + { + "caller": "test_missing_definition", + "callee": "any", + "lineNumber": 118 + }, + { + "caller": "test_missing_both_ids", + "callee": "validate_bc", + "lineNumber": 121 + }, + { + "caller": "test_missing_both_ids", + "callee": "any", + "lineNumber": 122 + }, + { + "caller": "test_invalid_ncit_format", + "callee": "self._valid", + "lineNumber": 125 + }, + { + "caller": "test_invalid_ncit_format", + "callee": "validate_bc", + "lineNumber": 127 + }, + { + "caller": "test_invalid_ncit_format", + "callee": "any", + "lineNumber": 128 + }, + { + "caller": "test_ncit_starting_with_c_accepted", + "callee": "self._valid", + "lineNumber": 131 + }, + { + "caller": "test_ncit_starting_with_c_accepted", + "callee": "validate_bc", + "lineNumber": 133 + }, + { + "caller": "test_marks_existing_ids_as_duplicate", + "callee": "self._record", + "lineNumber": 146 + }, + { + "caller": "test_marks_existing_ids_as_duplicate", + "callee": "self._record", + "lineNumber": 146 + }, + { + "caller": "test_marks_existing_ids_as_duplicate", + "callee": "deduplicate", + "lineNumber": 147 + }, + { + "caller": "test_case_insensitive_comparison", + "callee": "self._record", + "lineNumber": 152 + }, + { + "caller": "test_case_insensitive_comparison", + "callee": "deduplicate", + "lineNumber": 153 + }, + { + "caller": "test_empty_existing_ids", + "callee": "self._record", + "lineNumber": 157 + }, + { + "caller": "test_empty_existing_ids", + "callee": "deduplicate", + "lineNumber": 158 + }, + { + "caller": "test_empty_existing_ids", + "callee": "set", + "lineNumber": 158 + }, + { + "caller": "test_falls_back_to_ncit_code", + "callee": "deduplicate", + "lineNumber": 163 + }, + { + "caller": "_make_csv", + "callee": "io.StringIO", + "lineNumber": 177 + }, + { + "caller": "_make_csv", + "callee": "csv.DictWriter", + "lineNumber": 179 + }, + { + "caller": "_make_csv", + "callee": "rows[0].keys", + "lineNumber": 179 + }, + { + "caller": "_make_csv", + "callee": "writer.writeheader", + "lineNumber": 180 + }, + { + "caller": "_make_csv", + "callee": "writer.writerows", + "lineNumber": 181 + }, + { + "caller": "_make_csv", + "callee": "io.BytesIO", + "lineNumber": 182 + }, + { + "caller": "_make_csv", + "callee": "buf.getvalue().encode", + "lineNumber": 182 + }, + { + "caller": "_make_csv", + "callee": "buf.getvalue", + "lineNumber": 182 + }, + { + "caller": "test_valid_row_parsed", + "callee": "self._make_csv", + "lineNumber": 185 + }, + { + "caller": "test_valid_row_parsed", + "callee": "parse_csv", + "lineNumber": 186 + }, + { + "caller": "test_valid_row_parsed", + "callee": "len", + "lineNumber": 187 + }, + { + "caller": "test_invalid_file_returns_error_record", + "callee": "parse_csv", + "lineNumber": 191 + }, + { + "caller": "test_invalid_file_returns_error_record", + "callee": "io.BytesIO", + "lineNumber": 191 + }, + { + "caller": "test_invalid_file_returns_error_record", + "callee": "isinstance", + "lineNumber": 193 + }, + { + "caller": "test_missing_definition_produces_validation_error", + "callee": "self._make_csv", + "lineNumber": 196 + }, + { + "caller": "test_missing_definition_produces_validation_error", + "callee": "parse_csv", + "lineNumber": 197 + }, + { + "caller": "test_missing_definition_produces_validation_error", + "callee": "any", + "lineNumber": 198 + }, + { + "caller": "test_missing_definition_produces_validation_error", + "callee": "any", + "lineNumber": 198 + }, + { + "caller": "test_missing_definition_produces_validation_error", + "callee": "r.get", + "lineNumber": 198 + }, + { + "caller": "test_array_of_objects", + "callee": "io.BytesIO", + "lineNumber": 209 + }, + { + "caller": "test_array_of_objects", + "callee": "json.dumps(data).encode", + "lineNumber": 209 + }, + { + "caller": "test_array_of_objects", + "callee": "json.dumps", + "lineNumber": 209 + }, + { + "caller": "test_array_of_objects", + "callee": "parse_json", + "lineNumber": 210 + }, + { + "caller": "test_array_of_objects", + "callee": "len", + "lineNumber": 211 + }, + { + "caller": "test_single_object_wrapped", + "callee": "io.BytesIO", + "lineNumber": 216 + }, + { + "caller": "test_single_object_wrapped", + "callee": "json.dumps(data).encode", + "lineNumber": 216 + }, + { + "caller": "test_single_object_wrapped", + "callee": "json.dumps", + "lineNumber": 216 + }, + { + "caller": "test_single_object_wrapped", + "callee": "parse_json", + "lineNumber": 217 + }, + { + "caller": "test_single_object_wrapped", + "callee": "len", + "lineNumber": 218 + }, + { + "caller": "test_invalid_json_returns_error", + "callee": "parse_json", + "lineNumber": 221 + }, + { + "caller": "test_invalid_json_returns_error", + "callee": "io.BytesIO", + "lineNumber": 221 + }, + { + "caller": "test_invalid_json_returns_error", + "callee": "len", + "lineNumber": 222 + }, + { + "caller": "test_invalid_json_returns_error", + "callee": "records[0].get", + "lineNumber": 223 + }, + { + "caller": "test_invalid_json_returns_error", + "callee": "records[0].get", + "lineNumber": 223 + }, + { + "caller": "test_single_row_becomes_one_record", + "callee": "_group_by_bc", + "lineNumber": 234 + }, + { + "caller": "test_single_row_becomes_one_record", + "callee": "len", + "lineNumber": 235 + }, + { + "caller": "test_dec_sub_rows_grouped_under_parent", + "callee": "_group_by_bc", + "lineNumber": 243 + }, + { + "caller": "test_dec_sub_rows_grouped_under_parent", + "callee": "len", + "lineNumber": 244 + }, + { + "caller": "test_dec_sub_rows_grouped_under_parent", + "callee": "len", + "lineNumber": 245 + }, + { + "caller": "test_multiple_bcs_produce_separate_records", + "callee": "_group_by_bc", + "lineNumber": 253 + }, + { + "caller": "test_multiple_bcs_produce_separate_records", + "callee": "len", + "lineNumber": 254 + }, + { + "caller": "test_row_without_bc_id_skipped", + "callee": "_group_by_bc", + "lineNumber": 258 + } + ], + "metrics": { + "importCount": 1, + "exportCount": 8, + "functionCount": 0, + "classCount": 8 + } + }, + { + "path": "tests/test_models.py", + "language": "python", + "fileCategory": "code", + "totalLines": 116, + "nonEmptyLines": 97, + "classes": [ + { + "name": "TestAuditLogJsonProperties", + "startLine": 9, + "endLine": 44, + "methods": [ + "test_before_state_round_trips", + "test_after_state_round_trips", + "test_none_before_state_returns_none", + "test_none_after_state_returns_none", + "test_persisted_log_retrieves_state" + ], + "properties": [] + }, + { + "name": "TestIngestionRecordProperties", + "startLine": 47, + "endLine": 87, + "methods": [ + "test_mapped_round_trips", + "test_confidences_round_trips", + "test_errors_round_trips", + "test_decs_round_trips", + "test_empty_mapped_returns_empty_dict", + "test_avg_confidence_computed_correctly", + "test_avg_confidence_empty_confidences" + ], + "properties": [] + }, + { + "name": "TestBiomedicalConceptToDict", + "startLine": 90, + "endLine": 116, + "methods": [ + "test_to_dict_contains_required_keys", + "test_to_dict_values_match", + "test_default_status_is_provisional" + ], + "properties": [] + } + ], + "exports": [ + { + "name": "TestAuditLogJsonProperties", + "line": 9, + "isDefault": false + }, + { + "name": "TestIngestionRecordProperties", + "line": 47, + "isDefault": false + }, + { + "name": "TestBiomedicalConceptToDict", + "line": 90, + "isDefault": false + } + ], + "callGraph": [ + { + "caller": "test_before_state_round_trips", + "callee": "app.app_context", + "lineNumber": 11 + }, + { + "caller": "test_before_state_round_trips", + "callee": "AuditLog", + "lineNumber": 12 + }, + { + "caller": "test_after_state_round_trips", + "callee": "app.app_context", + "lineNumber": 17 + }, + { + "caller": "test_after_state_round_trips", + "callee": "AuditLog", + "lineNumber": 18 + }, + { + "caller": "test_none_before_state_returns_none", + "callee": "app.app_context", + "lineNumber": 23 + }, + { + "caller": "test_none_before_state_returns_none", + "callee": "AuditLog", + "lineNumber": 24 + }, + { + "caller": "test_none_after_state_returns_none", + "callee": "app.app_context", + "lineNumber": 28 + }, + { + "caller": "test_none_after_state_returns_none", + "callee": "AuditLog", + "lineNumber": 29 + }, + { + "caller": "test_persisted_log_retrieves_state", + "callee": "app.app_context", + "lineNumber": 33 + }, + { + "caller": "test_persisted_log_retrieves_state", + "callee": "AuditLog", + "lineNumber": 34 + }, + { + "caller": "test_persisted_log_retrieves_state", + "callee": "db.session.add", + "lineNumber": 41 + }, + { + "caller": "test_persisted_log_retrieves_state", + "callee": "db.session.commit", + "lineNumber": 42 + }, + { + "caller": "test_persisted_log_retrieves_state", + "callee": "AuditLog.query.first", + "lineNumber": 43 + }, + { + "caller": "test_mapped_round_trips", + "callee": "app.app_context", + "lineNumber": 49 + }, + { + "caller": "test_mapped_round_trips", + "callee": "IngestionRecord", + "lineNumber": 50 + }, + { + "caller": "test_confidences_round_trips", + "callee": "app.app_context", + "lineNumber": 55 + }, + { + "caller": "test_confidences_round_trips", + "callee": "IngestionRecord", + "lineNumber": 56 + }, + { + "caller": "test_errors_round_trips", + "callee": "app.app_context", + "lineNumber": 61 + }, + { + "caller": "test_errors_round_trips", + "callee": "IngestionRecord", + "lineNumber": 62 + }, + { + "caller": "test_decs_round_trips", + "callee": "app.app_context", + "lineNumber": 67 + }, + { + "caller": "test_decs_round_trips", + "callee": "IngestionRecord", + "lineNumber": 68 + }, + { + "caller": "test_empty_mapped_returns_empty_dict", + "callee": "app.app_context", + "lineNumber": 73 + }, + { + "caller": "test_empty_mapped_returns_empty_dict", + "callee": "IngestionRecord", + "lineNumber": 74 + }, + { + "caller": "test_avg_confidence_computed_correctly", + "callee": "app.app_context", + "lineNumber": 78 + }, + { + "caller": "test_avg_confidence_computed_correctly", + "callee": "IngestionRecord", + "lineNumber": 79 + }, + { + "caller": "test_avg_confidence_computed_correctly", + "callee": "round", + "lineNumber": 81 + }, + { + "caller": "test_avg_confidence_empty_confidences", + "callee": "app.app_context", + "lineNumber": 84 + }, + { + "caller": "test_avg_confidence_empty_confidences", + "callee": "IngestionRecord", + "lineNumber": 85 + }, + { + "caller": "test_to_dict_contains_required_keys", + "callee": "app.app_context", + "lineNumber": 92 + }, + { + "caller": "test_to_dict_contains_required_keys", + "callee": "BiomedicalConcept", + "lineNumber": 93 + }, + { + "caller": "test_to_dict_contains_required_keys", + "callee": "bc.to_dict", + "lineNumber": 100 + }, + { + "caller": "test_to_dict_values_match", + "callee": "app.app_context", + "lineNumber": 105 + }, + { + "caller": "test_to_dict_values_match", + "callee": "BiomedicalConcept", + "lineNumber": 106 + }, + { + "caller": "test_to_dict_values_match", + "callee": "bc.to_dict", + "lineNumber": 107 + }, + { + "caller": "test_default_status_is_provisional", + "callee": "app.app_context", + "lineNumber": 112 + }, + { + "caller": "test_default_status_is_provisional", + "callee": "BiomedicalConcept", + "lineNumber": 113 + }, + { + "caller": "test_default_status_is_provisional", + "callee": "db.session.add", + "lineNumber": 114 + }, + { + "caller": "test_default_status_is_provisional", + "callee": "db.session.commit", + "lineNumber": 115 + } + ], + "metrics": { + "importCount": 4, + "exportCount": 3, + "functionCount": 0, + "classCount": 3 + } + } + ] +} \ No newline at end of file diff --git a/.ua/.trash-1785851745/tmp/ua-file-extract-results-10.json b/.ua/.trash-1785851745/tmp/ua-file-extract-results-10.json new file mode 100644 index 0000000..fb2af57 --- /dev/null +++ b/.ua/.trash-1785851745/tmp/ua-file-extract-results-10.json @@ -0,0 +1,226 @@ +{ + "scriptCompleted": true, + "filesAnalyzed": 3, + "filesSkipped": [], + "analysisOutcomes": { + "structure": { + "succeeded": 3, + "failed": 0 + }, + "callGraph": { + "succeeded": 0, + "failed": 0, + "skipped": 3 + } + }, + "results": [ + { + "path": "files/implementation.md", + "language": "markdown", + "fileCategory": "docs", + "totalLines": 205, + "nonEmptyLines": 167, + "sections": [ + { + "heading": "CDISC Biomedical Concept Curation Application", + "level": 1, + "line": 1 + }, + { + "heading": "Context", + "level": 2, + "line": 3 + }, + { + "heading": "Directory Structure", + "level": 2, + "line": 9 + }, + { + "heading": "Data Models (SQLAlchemy + SQLite)", + "level": 2, + "line": 52 + }, + { + "heading": "BiomedicalConcept", + "level": 3, + "line": 54 + }, + { + "heading": "DataElementConcept", + "level": 3, + "line": 62 + }, + { + "heading": "DatasetSpecialization", + "level": 3, + "line": 67 + }, + { + "heading": "GovernanceRecord", + "level": 3, + "line": 72 + }, + { + "heading": "AuditLog", + "level": 3, + "line": 77 + }, + { + "heading": "Flask Blueprints", + "level": 2, + "line": 84 + }, + { + "heading": "Services", + "level": 2, + "line": 98 + }, + { + "heading": "`services/cdisc_api.py`", + "level": 3, + "line": 100 + }, + { + "heading": "`services/ncit_api.py`", + "level": 3, + "line": 107 + }, + { + "heading": "`services/ingestion.py`", + "level": 3, + "line": 113 + }, + { + "heading": "`services/export.py`", + "level": 3, + "line": 121 + }, + { + "heading": "Templates", + "level": 2, + "line": 128 + }, + { + "heading": "`base.html`", + "level": 3, + "line": 130 + }, + { + "heading": "Per-screen templates", + "level": 3, + "line": 135 + }, + { + "heading": "Implementation Phases", + "level": 2, + "line": 146 + }, + { + "heading": "Phase 1 — Core Foundation (MVP)", + "level": 3, + "line": 148 + }, + { + "heading": "Phase 2 — Ingestion + NCIt", + "level": 3, + "line": 156 + }, + { + "heading": "Phase 3 — Governance + Specializations", + "level": 3, + "line": 163 + }, + { + "heading": "Phase 4 — Export + Polish", + "level": 3, + "line": 170 + }, + { + "heading": "Critical Files to Create/Modify", + "level": 2, + "line": 179 + }, + { + "heading": "Reference Files (read-only)", + "level": 2, + "line": 188 + }, + { + "heading": "Verification", + "level": 2, + "line": 197 + } + ], + "metrics": { + "importCount": 0, + "exportCount": 0, + "functionCount": 0, + "classCount": 0, + "sectionCount": 26 + } + }, + { + "path": "files/plan.md", + "language": "markdown", + "fileCategory": "docs", + "totalLines": 47, + "nonEmptyLines": 28, + "sections": [ + { + "heading": "High level overview", + "level": 1, + "line": 1 + }, + { + "heading": "Files to review", + "level": 1, + "line": 33 + }, + { + "heading": "Agents", + "level": 1, + "line": 43 + } + ], + "metrics": { + "importCount": 0, + "exportCount": 0, + "functionCount": 0, + "classCount": 0, + "sectionCount": 3 + } + }, + { + "path": "files/result.md", + "language": "markdown", + "fileCategory": "docs", + "totalLines": 27, + "nonEmptyLines": 24, + "sections": [ + { + "heading": "What was built", + "level": 1, + "line": 1 + }, + { + "heading": "Structure", + "level": 3, + "line": 5 + }, + { + "heading": "Key capabilities", + "level": 3, + "line": 20 + } + ], + "metrics": { + "importCount": 0, + "exportCount": 0, + "functionCount": 0, + "classCount": 0, + "sectionCount": 3 + } + } + ] +} \ No newline at end of file diff --git a/.ua/.trash-1785851745/tmp/ua-file-extract-results-11.json b/.ua/.trash-1785851745/tmp/ua-file-extract-results-11.json new file mode 100644 index 0000000..7c8dd11 --- /dev/null +++ b/.ua/.trash-1785851745/tmp/ua-file-extract-results-11.json @@ -0,0 +1,29 @@ +{ + "scriptCompleted": true, + "filesAnalyzed": 0, + "filesSkipped": [ + "templates/audit.html", + "templates/base.html", + "templates/bc_detail.html", + "templates/bc_list.html", + "templates/dashboard.html", + "templates/governance.html", + "templates/ingestion.html", + "templates/library_bc_detail.html", + "templates/library_spec_detail.html", + "templates/ncit_mapping.html", + "templates/specializations.html" + ], + "analysisOutcomes": { + "structure": { + "succeeded": 0, + "failed": 0 + }, + "callGraph": { + "succeeded": 0, + "failed": 0, + "skipped": 0 + } + }, + "results": [] +} \ No newline at end of file diff --git a/.ua/.trash-1785851745/tmp/ua-file-extract-results-12.json b/.ua/.trash-1785851745/tmp/ua-file-extract-results-12.json new file mode 100644 index 0000000..08270c9 --- /dev/null +++ b/.ua/.trash-1785851745/tmp/ua-file-extract-results-12.json @@ -0,0 +1,2566 @@ +{ + "scriptCompleted": true, + "filesAnalyzed": 12, + "filesSkipped": [ + ".flake8", + ".hintrc", + ".ua/.understandignore", + "files/BC Curation Principles and Completion GLs.xlsx", + "files/BC DEC Templates.xlsx", + "files/BC Examples.xlsx", + "files/BC Governance.docx", + "migrations/README", + "migrations/alembic.ini", + "migrations/script.py.mako", + "static/css/custom.css" + ], + "analysisOutcomes": { + "structure": { + "succeeded": 12, + "failed": 0 + }, + "callGraph": { + "succeeded": 6, + "failed": 0, + "skipped": 6 + } + }, + "results": [ + { + "path": ".claude/agent-memory/cdisc-concept-explorer/MEMORY.md", + "language": "markdown", + "fileCategory": "docs", + "totalLines": 1, + "nonEmptyLines": 1, + "metrics": { + "importCount": 0, + "exportCount": 0, + "functionCount": 0, + "classCount": 0, + "sectionCount": 0 + } + }, + { + "path": ".claude/agent-memory/cdisc-concept-explorer/reference_api_endpoints.md", + "language": "markdown", + "fileCategory": "docs", + "totalLines": 36, + "nonEmptyLines": 27, + "sections": [ + { + "heading": "Base URL", + "level": 2, + "line": 7 + }, + { + "heading": "Working Endpoints", + "level": 2, + "line": 11 + }, + { + "heading": "Pagination", + "level": 2, + "line": 22 + }, + { + "heading": "List Response Shape", + "level": 2, + "line": 25 + }, + { + "heading": "Single BC Record Fields", + "level": 2, + "line": 30 + }, + { + "heading": "Single Dataset Specialization Record Fields", + "level": 2, + "line": 33 + } + ], + "metrics": { + "importCount": 0, + "exportCount": 0, + "functionCount": 0, + "classCount": 0, + "sectionCount": 6 + } + }, + { + "path": ".claude/skills/code-review/SKILL.md", + "language": "markdown", + "fileCategory": "docs", + "totalLines": 59, + "nonEmptyLines": 50, + "sections": [ + { + "heading": "Code review for cdisc-concept-curation", + "level": 1, + "line": 6 + }, + { + "heading": "Project-specific checks (highest value)", + "level": 2, + "line": 12 + }, + { + "heading": "General checks", + "level": 2, + "line": 40 + }, + { + "heading": "Verification", + "level": 2, + "line": 51 + } + ], + "metrics": { + "importCount": 0, + "exportCount": 0, + "functionCount": 0, + "classCount": 0, + "sectionCount": 4 + } + }, + { + "path": ".claude/skills/run-concept-curation/SKILL.md", + "language": "markdown", + "fileCategory": "docs", + "totalLines": 49, + "nonEmptyLines": 39, + "sections": [ + { + "heading": "Run the cdisc-concept-curation app", + "level": 1, + "line": 6 + }, + { + "heading": "Quick smoke test (preferred)", + "level": 2, + "line": 8 + }, + { + "heading": "Manual dev server", + "level": 2, + "line": 21 + }, + { + "heading": "Rules", + "level": 2, + "line": 29 + }, + { + "heading": "Troubleshooting", + "level": 2, + "line": 42 + } + ], + "metrics": { + "importCount": 0, + "exportCount": 0, + "functionCount": 0, + "classCount": 0, + "sectionCount": 5 + } + }, + { + "path": ".claude/skills/run-concept-curation/smoke.sh", + "language": "shell", + "fileCategory": "script", + "totalLines": 90, + "nonEmptyLines": 80, + "functions": [ + { + "name": "cleanup", + "startLine": 16, + "endLine": 22, + "params": [] + }, + { + "name": "check", + "startLine": 25, + "endLine": 34, + "params": [] + }, + { + "name": "get_code", + "startLine": 36, + "endLine": 38, + "params": [] + }, + { + "name": "post_code", + "startLine": 40, + "endLine": 43, + "params": [] + } + ], + "metrics": { + "importCount": 0, + "exportCount": 0, + "functionCount": 4, + "classCount": 0 + } + }, + { + "path": ".ua/config.json", + "language": "json", + "fileCategory": "config", + "totalLines": 1, + "nonEmptyLines": 1, + "sections": [ + { + "heading": "outputLanguage", + "level": 1, + "line": 1 + } + ], + "metrics": { + "importCount": 0, + "exportCount": 0, + "functionCount": 0, + "classCount": 0, + "sectionCount": 1 + } + }, + { + "path": "mcp_server/__init__.py", + "language": "python", + "fileCategory": "code", + "totalLines": 5, + "nonEmptyLines": 4, + "metrics": { + "importCount": 0, + "exportCount": 0, + "functionCount": 0, + "classCount": 0 + } + }, + { + "path": "migrations/env.py", + "language": "python", + "fileCategory": "code", + "totalLines": 105, + "nonEmptyLines": 76, + "functions": [ + { + "name": "get_engine", + "startLine": 17, + "endLine": 23, + "params": [] + }, + { + "name": "get_engine_url", + "startLine": 26, + "endLine": 30, + "params": [] + }, + { + "name": "get_metadata", + "startLine": 46, + "endLine": 49, + "params": [] + }, + { + "name": "run_migrations_offline", + "startLine": 52, + "endLine": 68, + "params": [] + }, + { + "name": "run_migrations_online", + "startLine": 71, + "endLine": 99, + "params": [] + } + ], + "exports": [ + { + "name": "get_engine", + "line": 17, + "isDefault": false + }, + { + "name": "get_engine_url", + "line": 26, + "isDefault": false + }, + { + "name": "get_metadata", + "line": 46, + "isDefault": false + }, + { + "name": "run_migrations_offline", + "line": 52, + "isDefault": false + }, + { + "name": "run_migrations_online", + "line": 71, + "isDefault": false + } + ], + "callGraph": [ + { + "caller": "get_engine", + "callee": "current_app.extensions[\"migrate\"].db.get_engine", + "lineNumber": 20 + }, + { + "caller": "get_engine_url", + "callee": "get_engine().url.render_as_string(hide_password=False).replace", + "lineNumber": 28 + }, + { + "caller": "get_engine_url", + "callee": "get_engine().url.render_as_string", + "lineNumber": 28 + }, + { + "caller": "get_engine_url", + "callee": "get_engine", + "lineNumber": 28 + }, + { + "caller": "get_engine_url", + "callee": "str(get_engine().url).replace", + "lineNumber": 30 + }, + { + "caller": "get_engine_url", + "callee": "str", + "lineNumber": 30 + }, + { + "caller": "get_engine_url", + "callee": "get_engine", + "lineNumber": 30 + }, + { + "caller": "get_metadata", + "callee": "hasattr", + "lineNumber": 47 + }, + { + "caller": "run_migrations_offline", + "callee": "config.get_main_option", + "lineNumber": 64 + }, + { + "caller": "run_migrations_offline", + "callee": "context.configure", + "lineNumber": 65 + }, + { + "caller": "run_migrations_offline", + "callee": "get_metadata", + "lineNumber": 65 + }, + { + "caller": "run_migrations_offline", + "callee": "context.begin_transaction", + "lineNumber": 67 + }, + { + "caller": "run_migrations_offline", + "callee": "context.run_migrations", + "lineNumber": 68 + }, + { + "caller": "process_revision_directives", + "callee": "getattr", + "lineNumber": 83 + }, + { + "caller": "process_revision_directives", + "callee": "script.upgrade_ops.is_empty", + "lineNumber": 85 + }, + { + "caller": "process_revision_directives", + "callee": "logger.info", + "lineNumber": 87 + }, + { + "caller": "run_migrations_online", + "callee": "conf_args.get", + "lineNumber": 90 + }, + { + "caller": "run_migrations_online", + "callee": "get_engine", + "lineNumber": 93 + }, + { + "caller": "run_migrations_online", + "callee": "connectable.connect", + "lineNumber": 95 + }, + { + "caller": "run_migrations_online", + "callee": "context.configure", + "lineNumber": 96 + }, + { + "caller": "run_migrations_online", + "callee": "get_metadata", + "lineNumber": 96 + }, + { + "caller": "run_migrations_online", + "callee": "context.begin_transaction", + "lineNumber": 98 + }, + { + "caller": "run_migrations_online", + "callee": "context.run_migrations", + "lineNumber": 99 + } + ], + "metrics": { + "importCount": 0, + "exportCount": 5, + "functionCount": 5, + "classCount": 0 + } + }, + { + "path": "migrations/versions/51d4a009d291_baseline_initial_schema.py", + "language": "python", + "fileCategory": "code", + "totalLines": 142, + "nonEmptyLines": 132, + "functions": [ + { + "name": "upgrade", + "startLine": 19, + "endLine": 128, + "params": [] + }, + { + "name": "downgrade", + "startLine": 131, + "endLine": 142, + "params": [] + } + ], + "exports": [ + { + "name": "upgrade", + "line": 19, + "isDefault": false + }, + { + "name": "downgrade", + "line": 131, + "isDefault": false + } + ], + "callGraph": [ + { + "caller": "upgrade", + "callee": "op.create_table", + "lineNumber": 21 + }, + { + "caller": "upgrade", + "callee": "sa.Column", + "lineNumber": 23 + }, + { + "caller": "upgrade", + "callee": "sa.Integer", + "lineNumber": 23 + }, + { + "caller": "upgrade", + "callee": "sa.Column", + "lineNumber": 24 + }, + { + "caller": "upgrade", + "callee": "sa.String", + "lineNumber": 24 + }, + { + "caller": "upgrade", + "callee": "sa.Column", + "lineNumber": 25 + }, + { + "caller": "upgrade", + "callee": "sa.String", + "lineNumber": 25 + }, + { + "caller": "upgrade", + "callee": "sa.Column", + "lineNumber": 26 + }, + { + "caller": "upgrade", + "callee": "sa.String", + "lineNumber": 26 + }, + { + "caller": "upgrade", + "callee": "sa.Column", + "lineNumber": 27 + }, + { + "caller": "upgrade", + "callee": "sa.String", + "lineNumber": 27 + }, + { + "caller": "upgrade", + "callee": "sa.Column", + "lineNumber": 28 + }, + { + "caller": "upgrade", + "callee": "sa.Text", + "lineNumber": 28 + }, + { + "caller": "upgrade", + "callee": "sa.Column", + "lineNumber": 29 + }, + { + "caller": "upgrade", + "callee": "sa.Text", + "lineNumber": 29 + }, + { + "caller": "upgrade", + "callee": "sa.Column", + "lineNumber": 30 + }, + { + "caller": "upgrade", + "callee": "sa.DateTime", + "lineNumber": 30 + }, + { + "caller": "upgrade", + "callee": "sa.PrimaryKeyConstraint", + "lineNumber": 31 + }, + { + "caller": "upgrade", + "callee": "op.create_table", + "lineNumber": 33 + }, + { + "caller": "upgrade", + "callee": "sa.Column", + "lineNumber": 35 + }, + { + "caller": "upgrade", + "callee": "sa.String", + "lineNumber": 35 + }, + { + "caller": "upgrade", + "callee": "sa.Column", + "lineNumber": 36 + }, + { + "caller": "upgrade", + "callee": "sa.String", + "lineNumber": 36 + }, + { + "caller": "upgrade", + "callee": "sa.Column", + "lineNumber": 37 + }, + { + "caller": "upgrade", + "callee": "sa.Text", + "lineNumber": 37 + }, + { + "caller": "upgrade", + "callee": "sa.Column", + "lineNumber": 38 + }, + { + "caller": "upgrade", + "callee": "sa.String", + "lineNumber": 38 + }, + { + "caller": "upgrade", + "callee": "sa.Column", + "lineNumber": 39 + }, + { + "caller": "upgrade", + "callee": "sa.String", + "lineNumber": 39 + }, + { + "caller": "upgrade", + "callee": "sa.Column", + "lineNumber": 40 + }, + { + "caller": "upgrade", + "callee": "sa.String", + "lineNumber": 40 + }, + { + "caller": "upgrade", + "callee": "sa.Column", + "lineNumber": 41 + }, + { + "caller": "upgrade", + "callee": "sa.Text", + "lineNumber": 41 + }, + { + "caller": "upgrade", + "callee": "sa.Column", + "lineNumber": 42 + }, + { + "caller": "upgrade", + "callee": "sa.String", + "lineNumber": 42 + }, + { + "caller": "upgrade", + "callee": "sa.Column", + "lineNumber": 43 + }, + { + "caller": "upgrade", + "callee": "sa.String", + "lineNumber": 43 + }, + { + "caller": "upgrade", + "callee": "sa.Column", + "lineNumber": 44 + }, + { + "caller": "upgrade", + "callee": "sa.String", + "lineNumber": 44 + }, + { + "caller": "upgrade", + "callee": "sa.Column", + "lineNumber": 45 + }, + { + "caller": "upgrade", + "callee": "sa.String", + "lineNumber": 45 + }, + { + "caller": "upgrade", + "callee": "sa.Column", + "lineNumber": 46 + }, + { + "caller": "upgrade", + "callee": "sa.String", + "lineNumber": 46 + }, + { + "caller": "upgrade", + "callee": "sa.Column", + "lineNumber": 47 + }, + { + "caller": "upgrade", + "callee": "sa.Text", + "lineNumber": 47 + }, + { + "caller": "upgrade", + "callee": "sa.Column", + "lineNumber": 48 + }, + { + "caller": "upgrade", + "callee": "sa.Text", + "lineNumber": 48 + }, + { + "caller": "upgrade", + "callee": "sa.Column", + "lineNumber": 49 + }, + { + "caller": "upgrade", + "callee": "sa.String", + "lineNumber": 49 + }, + { + "caller": "upgrade", + "callee": "sa.Column", + "lineNumber": 50 + }, + { + "caller": "upgrade", + "callee": "sa.String", + "lineNumber": 50 + }, + { + "caller": "upgrade", + "callee": "sa.Column", + "lineNumber": 51 + }, + { + "caller": "upgrade", + "callee": "sa.String", + "lineNumber": 51 + }, + { + "caller": "upgrade", + "callee": "sa.Column", + "lineNumber": 52 + }, + { + "caller": "upgrade", + "callee": "sa.DateTime", + "lineNumber": 52 + }, + { + "caller": "upgrade", + "callee": "sa.Column", + "lineNumber": 53 + }, + { + "caller": "upgrade", + "callee": "sa.DateTime", + "lineNumber": 53 + }, + { + "caller": "upgrade", + "callee": "sa.Column", + "lineNumber": 54 + }, + { + "caller": "upgrade", + "callee": "sa.Text", + "lineNumber": 54 + }, + { + "caller": "upgrade", + "callee": "sa.Column", + "lineNumber": 55 + }, + { + "caller": "upgrade", + "callee": "sa.String", + "lineNumber": 55 + }, + { + "caller": "upgrade", + "callee": "sa.ForeignKeyConstraint", + "lineNumber": 56 + }, + { + "caller": "upgrade", + "callee": "sa.PrimaryKeyConstraint", + "lineNumber": 60 + }, + { + "caller": "upgrade", + "callee": "op.create_table", + "lineNumber": 62 + }, + { + "caller": "upgrade", + "callee": "sa.Column", + "lineNumber": 64 + }, + { + "caller": "upgrade", + "callee": "sa.Integer", + "lineNumber": 64 + }, + { + "caller": "upgrade", + "callee": "sa.Column", + "lineNumber": 65 + }, + { + "caller": "upgrade", + "callee": "sa.String", + "lineNumber": 65 + }, + { + "caller": "upgrade", + "callee": "sa.Column", + "lineNumber": 66 + }, + { + "caller": "upgrade", + "callee": "sa.String", + "lineNumber": 66 + }, + { + "caller": "upgrade", + "callee": "sa.Column", + "lineNumber": 67 + }, + { + "caller": "upgrade", + "callee": "sa.String", + "lineNumber": 67 + }, + { + "caller": "upgrade", + "callee": "sa.Column", + "lineNumber": 68 + }, + { + "caller": "upgrade", + "callee": "sa.Text", + "lineNumber": 68 + }, + { + "caller": "upgrade", + "callee": "sa.Column", + "lineNumber": 69 + }, + { + "caller": "upgrade", + "callee": "sa.Text", + "lineNumber": 69 + }, + { + "caller": "upgrade", + "callee": "sa.Column", + "lineNumber": 70 + }, + { + "caller": "upgrade", + "callee": "sa.Text", + "lineNumber": 70 + }, + { + "caller": "upgrade", + "callee": "sa.Column", + "lineNumber": 71 + }, + { + "caller": "upgrade", + "callee": "sa.Text", + "lineNumber": 71 + }, + { + "caller": "upgrade", + "callee": "sa.Column", + "lineNumber": 72 + }, + { + "caller": "upgrade", + "callee": "sa.Boolean", + "lineNumber": 72 + }, + { + "caller": "upgrade", + "callee": "sa.Column", + "lineNumber": 73 + }, + { + "caller": "upgrade", + "callee": "sa.String", + "lineNumber": 73 + }, + { + "caller": "upgrade", + "callee": "sa.Column", + "lineNumber": 74 + }, + { + "caller": "upgrade", + "callee": "sa.DateTime", + "lineNumber": 74 + }, + { + "caller": "upgrade", + "callee": "sa.PrimaryKeyConstraint", + "lineNumber": 75 + }, + { + "caller": "upgrade", + "callee": "op.batch_alter_table", + "lineNumber": 77 + }, + { + "caller": "upgrade", + "callee": "batch_op.create_index", + "lineNumber": 78 + }, + { + "caller": "upgrade", + "callee": "batch_op.f", + "lineNumber": 78 + }, + { + "caller": "upgrade", + "callee": "op.create_table", + "lineNumber": 80 + }, + { + "caller": "upgrade", + "callee": "sa.Column", + "lineNumber": 82 + }, + { + "caller": "upgrade", + "callee": "sa.Integer", + "lineNumber": 82 + }, + { + "caller": "upgrade", + "callee": "sa.Column", + "lineNumber": 83 + }, + { + "caller": "upgrade", + "callee": "sa.String", + "lineNumber": 83 + }, + { + "caller": "upgrade", + "callee": "sa.Column", + "lineNumber": 84 + }, + { + "caller": "upgrade", + "callee": "sa.String", + "lineNumber": 84 + }, + { + "caller": "upgrade", + "callee": "sa.Column", + "lineNumber": 85 + }, + { + "caller": "upgrade", + "callee": "sa.String", + "lineNumber": 85 + }, + { + "caller": "upgrade", + "callee": "sa.Column", + "lineNumber": 86 + }, + { + "caller": "upgrade", + "callee": "sa.String", + "lineNumber": 86 + }, + { + "caller": "upgrade", + "callee": "sa.Column", + "lineNumber": 87 + }, + { + "caller": "upgrade", + "callee": "sa.String", + "lineNumber": 87 + }, + { + "caller": "upgrade", + "callee": "sa.Column", + "lineNumber": 88 + }, + { + "caller": "upgrade", + "callee": "sa.Text", + "lineNumber": 88 + }, + { + "caller": "upgrade", + "callee": "sa.Column", + "lineNumber": 89 + }, + { + "caller": "upgrade", + "callee": "sa.Boolean", + "lineNumber": 89 + }, + { + "caller": "upgrade", + "callee": "sa.Column", + "lineNumber": 90 + }, + { + "caller": "upgrade", + "callee": "sa.Boolean", + "lineNumber": 90 + }, + { + "caller": "upgrade", + "callee": "sa.Column", + "lineNumber": 91 + }, + { + "caller": "upgrade", + "callee": "sa.String", + "lineNumber": 91 + }, + { + "caller": "upgrade", + "callee": "sa.Column", + "lineNumber": 92 + }, + { + "caller": "upgrade", + "callee": "sa.Integer", + "lineNumber": 92 + }, + { + "caller": "upgrade", + "callee": "sa.ForeignKeyConstraint", + "lineNumber": 93 + }, + { + "caller": "upgrade", + "callee": "sa.PrimaryKeyConstraint", + "lineNumber": 97 + }, + { + "caller": "upgrade", + "callee": "op.create_table", + "lineNumber": 99 + }, + { + "caller": "upgrade", + "callee": "sa.Column", + "lineNumber": 101 + }, + { + "caller": "upgrade", + "callee": "sa.String", + "lineNumber": 101 + }, + { + "caller": "upgrade", + "callee": "sa.Column", + "lineNumber": 102 + }, + { + "caller": "upgrade", + "callee": "sa.String", + "lineNumber": 102 + }, + { + "caller": "upgrade", + "callee": "sa.Column", + "lineNumber": 103 + }, + { + "caller": "upgrade", + "callee": "sa.String", + "lineNumber": 103 + }, + { + "caller": "upgrade", + "callee": "sa.Column", + "lineNumber": 104 + }, + { + "caller": "upgrade", + "callee": "sa.String", + "lineNumber": 104 + }, + { + "caller": "upgrade", + "callee": "sa.Column", + "lineNumber": 105 + }, + { + "caller": "upgrade", + "callee": "sa.Text", + "lineNumber": 105 + }, + { + "caller": "upgrade", + "callee": "sa.Column", + "lineNumber": 106 + }, + { + "caller": "upgrade", + "callee": "sa.DateTime", + "lineNumber": 106 + }, + { + "caller": "upgrade", + "callee": "sa.ForeignKeyConstraint", + "lineNumber": 107 + }, + { + "caller": "upgrade", + "callee": "sa.PrimaryKeyConstraint", + "lineNumber": 111 + }, + { + "caller": "upgrade", + "callee": "op.create_table", + "lineNumber": 113 + }, + { + "caller": "upgrade", + "callee": "sa.Column", + "lineNumber": 115 + }, + { + "caller": "upgrade", + "callee": "sa.Integer", + "lineNumber": 115 + }, + { + "caller": "upgrade", + "callee": "sa.Column", + "lineNumber": 116 + }, + { + "caller": "upgrade", + "callee": "sa.String", + "lineNumber": 116 + }, + { + "caller": "upgrade", + "callee": "sa.Column", + "lineNumber": 117 + }, + { + "caller": "upgrade", + "callee": "sa.Integer", + "lineNumber": 117 + }, + { + "caller": "upgrade", + "callee": "sa.Column", + "lineNumber": 118 + }, + { + "caller": "upgrade", + "callee": "sa.String", + "lineNumber": 118 + }, + { + "caller": "upgrade", + "callee": "sa.Column", + "lineNumber": 119 + }, + { + "caller": "upgrade", + "callee": "sa.String", + "lineNumber": 119 + }, + { + "caller": "upgrade", + "callee": "sa.Column", + "lineNumber": 120 + }, + { + "caller": "upgrade", + "callee": "sa.Text", + "lineNumber": 120 + }, + { + "caller": "upgrade", + "callee": "sa.Column", + "lineNumber": 121 + }, + { + "caller": "upgrade", + "callee": "sa.DateTime", + "lineNumber": 121 + }, + { + "caller": "upgrade", + "callee": "sa.ForeignKeyConstraint", + "lineNumber": 122 + }, + { + "caller": "upgrade", + "callee": "sa.PrimaryKeyConstraint", + "lineNumber": 126 + }, + { + "caller": "downgrade", + "callee": "op.drop_table", + "lineNumber": 133 + }, + { + "caller": "downgrade", + "callee": "op.drop_table", + "lineNumber": 134 + }, + { + "caller": "downgrade", + "callee": "op.drop_table", + "lineNumber": 135 + }, + { + "caller": "downgrade", + "callee": "op.batch_alter_table", + "lineNumber": 136 + }, + { + "caller": "downgrade", + "callee": "batch_op.drop_index", + "lineNumber": 137 + }, + { + "caller": "downgrade", + "callee": "batch_op.f", + "lineNumber": 137 + }, + { + "caller": "downgrade", + "callee": "op.drop_table", + "lineNumber": 139 + }, + { + "caller": "downgrade", + "callee": "op.drop_table", + "lineNumber": 140 + }, + { + "caller": "downgrade", + "callee": "op.drop_table", + "lineNumber": 141 + } + ], + "metrics": { + "importCount": 0, + "exportCount": 2, + "functionCount": 2, + "classCount": 0 + } + }, + { + "path": "routes/__init__.py", + "language": "python", + "fileCategory": "code", + "totalLines": 1, + "nonEmptyLines": 0, + "metrics": { + "importCount": 0, + "exportCount": 0, + "functionCount": 0, + "classCount": 0 + } + }, + { + "path": "static/js/main.js", + "language": "javascript", + "fileCategory": "code", + "totalLines": 819, + "nonEmptyLines": 723, + "callGraph": [ + { + "caller": "initFlashDismiss", + "callee": "document.querySelectorAll", + "lineNumber": 13 + }, + { + "caller": "initFlashDismiss", + "callee": "flashMessages.forEach", + "lineNumber": 14 + }, + { + "caller": "initFlashDismiss", + "callee": "setTimeout", + "lineNumber": 15 + }, + { + "caller": "initFlashDismiss", + "callee": "bootstrap.Alert.getOrCreateInstance", + "lineNumber": 16 + }, + { + "caller": "initFlashDismiss", + "callee": "bsAlert.close", + "lineNumber": 17 + }, + { + "caller": "initFileUpload", + "callee": "document.getElementById", + "lineNumber": 26 + }, + { + "caller": "initFileUpload", + "callee": "document.getElementById", + "lineNumber": 27 + }, + { + "caller": "initFileUpload", + "callee": "document.getElementById", + "lineNumber": 28 + }, + { + "caller": "initFileUpload", + "callee": "fileInput.addEventListener", + "lineNumber": 31 + }, + { + "caller": "initFileUpload", + "callee": "fileNameDisplay.classList.remove", + "lineNumber": 35 + }, + { + "caller": "initFileUpload", + "callee": "uploadZone.addEventListener", + "lineNumber": 42 + }, + { + "caller": "initFileUpload", + "callee": "e.target.closest", + "lineNumber": 43 + }, + { + "caller": "initFileUpload", + "callee": "e.target.closest", + "lineNumber": 43 + }, + { + "caller": "initFileUpload", + "callee": "fileInput.click", + "lineNumber": 44 + }, + { + "caller": "initFileUpload", + "callee": "uploadZone.addEventListener", + "lineNumber": 48 + }, + { + "caller": "initFileUpload", + "callee": "e.preventDefault", + "lineNumber": 49 + }, + { + "caller": "initFileUpload", + "callee": "uploadZone.classList.add", + "lineNumber": 50 + }, + { + "caller": "initFileUpload", + "callee": "uploadZone.addEventListener", + "lineNumber": 53 + }, + { + "caller": "initFileUpload", + "callee": "uploadZone.classList.remove", + "lineNumber": 54 + }, + { + "caller": "initFileUpload", + "callee": "uploadZone.addEventListener", + "lineNumber": 57 + }, + { + "caller": "initFileUpload", + "callee": "e.preventDefault", + "lineNumber": 58 + }, + { + "caller": "initFileUpload", + "callee": "uploadZone.classList.remove", + "lineNumber": 59 + }, + { + "caller": "initFileUpload", + "callee": "dt.items.add", + "lineNumber": 64 + }, + { + "caller": "initFileUpload", + "callee": "fileNameDisplay.classList.remove", + "lineNumber": 68 + }, + { + "caller": "initNcitLookup", + "callee": "document.getElementById", + "lineNumber": 85 + }, + { + "caller": "initNcitLookup", + "callee": "document.getElementById", + "lineNumber": 86 + }, + { + "caller": "initNcitLookup", + "callee": "document.getElementById", + "lineNumber": 87 + }, + { + "caller": "initNcitLookup", + "callee": "document.getElementById", + "lineNumber": 88 + }, + { + "caller": "initNcitLookup", + "callee": "lookupBtn.addEventListener", + "lineNumber": 92 + }, + { + "caller": "initNcitLookup", + "callee": "ncitCodeInput.value.trim", + "lineNumber": 93 + }, + { + "caller": "initNcitLookup", + "callee": "ncitCodeInput.focus", + "lineNumber": 95 + }, + { + "caller": "initNcitLookup", + "callee": "url.searchParams.set", + "lineNumber": 104 + }, + { + "caller": "initNcitLookup", + "callee": "fetch", + "lineNumber": 106 + }, + { + "caller": "initNcitLookup", + "callee": "url.toString", + "lineNumber": 106 + }, + { + "caller": "initNcitLookup", + "callee": "response.json", + "lineNumber": 112 + }, + { + "caller": "initNcitLookup", + "callee": "renderNcitResults", + "lineNumber": 113 + }, + { + "caller": "initNcitLookup", + "callee": "resultsPanel.classList.remove", + "lineNumber": 114 + }, + { + "caller": "initNcitLookup", + "callee": "resultsPanel.classList.remove", + "lineNumber": 119 + }, + { + "caller": "initNcitLookup", + "callee": "console.error", + "lineNumber": 120 + }, + { + "caller": "renderNcitResults", + "callee": "data.map(function (item) {\n const synonymList = Array.isArray(item.synonyms) ? item.synonyms.join('; ') : (item.synonyms || '');\n const safeCode = escapeHtml(item.code || '');\n const safeName = escapeHtml(item.preferred_name || '');\n const safeDef = escapeHtml(item.definition || '');\n const safeSyn = escapeHtml(synonymList);\n\n return `\n
\n
\n ${safeCode}\n \n
\n
${safeName}
\n
${safeDef}
\n ${safeSyn ? `
Synonyms: ${safeSyn}
` : ''}\n
\n `;\n }).join", + "lineNumber": 140 + }, + { + "caller": "renderNcitResults", + "callee": "data.map", + "lineNumber": 140 + }, + { + "caller": "renderNcitResults", + "callee": "Array.isArray", + "lineNumber": 141 + }, + { + "caller": "renderNcitResults", + "callee": "item.synonyms.join", + "lineNumber": 141 + }, + { + "caller": "renderNcitResults", + "callee": "escapeHtml", + "lineNumber": 142 + }, + { + "caller": "renderNcitResults", + "callee": "escapeHtml", + "lineNumber": 143 + }, + { + "caller": "renderNcitResults", + "callee": "escapeHtml", + "lineNumber": 144 + }, + { + "caller": "renderNcitResults", + "callee": "escapeHtml", + "lineNumber": 145 + }, + { + "caller": "renderNcitResults", + "callee": "container.querySelectorAll('.use-ncit-btn').forEach", + "lineNumber": 166 + }, + { + "caller": "renderNcitResults", + "callee": "container.querySelectorAll", + "lineNumber": 166 + }, + { + "caller": "renderNcitResults", + "callee": "btn.addEventListener", + "lineNumber": 167 + }, + { + "caller": "renderNcitResults", + "callee": "document.getElementById", + "lineNumber": 171 + }, + { + "caller": "renderNcitResults", + "callee": "shortNameInput.value.trim", + "lineNumber": 172 + }, + { + "caller": "renderNcitResults", + "callee": "document.getElementById", + "lineNumber": 176 + }, + { + "caller": "renderNcitResults", + "callee": "panel.classList.add", + "lineNumber": 177 + }, + { + "caller": "renderNcitResults", + "callee": "document.getElementById", + "lineNumber": 180 + }, + { + "caller": "renderNcitResults", + "callee": "metaDisplay.classList.remove", + "lineNumber": 183 + }, + { + "caller": "renderNcitResults", + "callee": "encodeURIComponent", + "lineNumber": 186 + }, + { + "caller": "renderNcitResults", + "callee": "fetch", + "lineNumber": 187 + }, + { + "caller": "renderNcitResults", + "callee": "url.toString", + "lineNumber": 187 + }, + { + "caller": "renderNcitResults", + "callee": "response.json", + "lineNumber": 189 + }, + { + "caller": "renderNcitResults", + "callee": "renderNcitMetaDisplay", + "lineNumber": 190 + }, + { + "caller": "renderNcitResults", + "callee": "document.getElementById", + "lineNumber": 191 + }, + { + "caller": "renderNcitResults", + "callee": "JSON.stringify", + "lineNumber": 192 + }, + { + "caller": "renderNcitResults", + "callee": "console.error", + "lineNumber": 198 + }, + { + "caller": "renderNcitMetaDisplay", + "callee": "document.getElementById", + "lineNumber": 209 + }, + { + "caller": "renderNcitMetaDisplay", + "callee": "fields.filter(function (f) {\n var v = item[f.key];\n return v && (!Array.isArray(v) || v.length > 0);\n }).map", + "lineNumber": 222 + }, + { + "caller": "renderNcitMetaDisplay", + "callee": "fields.filter", + "lineNumber": 222 + }, + { + "caller": "renderNcitMetaDisplay", + "callee": "Array.isArray", + "lineNumber": 224 + }, + { + "caller": "renderNcitMetaDisplay", + "callee": "v.map(function (d) { return '[' + (d.source || '') + '] ' + (d.definition || ''); }).filter(Boolean).join", + "lineNumber": 229 + }, + { + "caller": "renderNcitMetaDisplay", + "callee": "v.map(function (d) { return '[' + (d.source || '') + '] ' + (d.definition || ''); }).filter", + "lineNumber": 229 + }, + { + "caller": "renderNcitMetaDisplay", + "callee": "v.map", + "lineNumber": 229 + }, + { + "caller": "renderNcitMetaDisplay", + "callee": "v.map(function (p) { return p.name + ' (' + p.code + ')'; }).join", + "lineNumber": 231 + }, + { + "caller": "renderNcitMetaDisplay", + "callee": "v.map", + "lineNumber": 231 + }, + { + "caller": "renderNcitMetaDisplay", + "callee": "Array.isArray", + "lineNumber": 232 + }, + { + "caller": "renderNcitMetaDisplay", + "callee": "v.join", + "lineNumber": 233 + }, + { + "caller": "renderNcitMetaDisplay", + "callee": "String", + "lineNumber": 235 + }, + { + "caller": "renderNcitMetaDisplay", + "callee": "escapeHtml", + "lineNumber": 238 + }, + { + "caller": "renderNcitMetaDisplay", + "callee": "escapeHtml", + "lineNumber": 239 + }, + { + "caller": "renderNcitMetaDisplay", + "callee": "items.push", + "lineNumber": 245 + }, + { + "caller": "renderNcitMetaDisplay", + "callee": "escapeHtml", + "lineNumber": 247 + }, + { + "caller": "renderNcitMetaDisplay", + "callee": "items.join", + "lineNumber": 252 + }, + { + "caller": "renderNcitMetaDisplay", + "callee": "display.classList.toggle", + "lineNumber": 254 + }, + { + "caller": "renderNcitMetaDisplay", + "callee": "document.getElementById", + "lineNumber": 257 + }, + { + "caller": "renderNcitMetaDisplay", + "callee": "parentInput.value.trim", + "lineNumber": 258 + }, + { + "caller": "renderNcitMetaDisplay", + "callee": "Array.isArray", + "lineNumber": 258 + }, + { + "caller": "initDecTable", + "callee": "document.getElementById", + "lineNumber": 267 + }, + { + "caller": "initDecTable", + "callee": "document.getElementById", + "lineNumber": 268 + }, + { + "caller": "initDecTable", + "callee": "addDecBtn.addEventListener", + "lineNumber": 272 + }, + { + "caller": "initDecTable", + "callee": "decTableBody.querySelectorAll", + "lineNumber": 273 + }, + { + "caller": "initDecTable", + "callee": "buildDecRow", + "lineNumber": 274 + }, + { + "caller": "initDecTable", + "callee": "decTableBody.appendChild", + "lineNumber": 275 + }, + { + "caller": "initDecTable", + "callee": "decTableBody.addEventListener", + "lineNumber": 279 + }, + { + "caller": "initDecTable", + "callee": "e.target.closest", + "lineNumber": 280 + }, + { + "caller": "initDecTable", + "callee": "deleteBtn.closest", + "lineNumber": 282 + }, + { + "caller": "initDecTable", + "callee": "row.remove", + "lineNumber": 283 + }, + { + "caller": "buildDecRow", + "callee": "document.createElement", + "lineNumber": 292 + }, + { + "caller": "initKanban", + "callee": "document.querySelectorAll('.kanban-advance-btn').forEach", + "lineNumber": 334 + }, + { + "caller": "initKanban", + "callee": "document.querySelectorAll", + "lineNumber": 334 + }, + { + "caller": "initKanban", + "callee": "btn.addEventListener", + "lineNumber": 335 + }, + { + "caller": "initKanban", + "callee": "e.stopPropagation", + "lineNumber": 336 + }, + { + "caller": "initKanban", + "callee": "postJson", + "lineNumber": 342 + }, + { + "caller": "initKanban", + "callee": "encodeURIComponent", + "lineNumber": 342 + }, + { + "caller": "initKanban", + "callee": "location.reload", + "lineNumber": 344 + }, + { + "caller": "initKanban", + "callee": "showInlineToast", + "lineNumber": 346 + }, + { + "caller": "initKanban", + "callee": "showInlineToast", + "lineNumber": 350 + }, + { + "caller": "initKanban", + "callee": "console.error", + "lineNumber": 351 + }, + { + "caller": "initKanban", + "callee": "document.querySelectorAll('.kanban-reject-btn').forEach", + "lineNumber": 358 + }, + { + "caller": "initKanban", + "callee": "document.querySelectorAll", + "lineNumber": 358 + }, + { + "caller": "initKanban", + "callee": "btn.addEventListener", + "lineNumber": 359 + }, + { + "caller": "initKanban", + "callee": "e.stopPropagation", + "lineNumber": 360 + }, + { + "caller": "initKanban", + "callee": "confirm", + "lineNumber": 364 + }, + { + "caller": "initKanban", + "callee": "postJson", + "lineNumber": 368 + }, + { + "caller": "initKanban", + "callee": "encodeURIComponent", + "lineNumber": 368 + }, + { + "caller": "initKanban", + "callee": "location.reload", + "lineNumber": 370 + }, + { + "caller": "initKanban", + "callee": "showInlineToast", + "lineNumber": 372 + }, + { + "caller": "initKanban", + "callee": "showInlineToast", + "lineNumber": 376 + }, + { + "caller": "initKanban", + "callee": "console.error", + "lineNumber": 377 + }, + { + "caller": "initAuditDiff", + "callee": "document.querySelectorAll('.audit-diff-toggle').forEach", + "lineNumber": 388 + }, + { + "caller": "initAuditDiff", + "callee": "document.querySelectorAll", + "lineNumber": 388 + }, + { + "caller": "initAuditDiff", + "callee": "btn.addEventListener", + "lineNumber": 389 + }, + { + "caller": "initAuditDiff", + "callee": "btn.closest", + "lineNumber": 390 + }, + { + "caller": "initAuditDiff", + "callee": "panel.classList.contains", + "lineNumber": 391 + }, + { + "caller": "initAuditDiff", + "callee": "panel.classList.contains", + "lineNumber": 392 + }, + { + "caller": "initAuditDiff", + "callee": "panel.classList.toggle", + "lineNumber": 393 + }, + { + "caller": "initAuditDiff", + "callee": "btn.setAttribute", + "lineNumber": 394 + }, + { + "caller": "initNcitSearch", + "callee": "document.getElementById", + "lineNumber": 404 + }, + { + "caller": "initNcitSearch", + "callee": "document.getElementById", + "lineNumber": 405 + }, + { + "caller": "initNcitSearch", + "callee": "document.getElementById", + "lineNumber": 406 + }, + { + "caller": "doSearch", + "callee": "searchInput.value.trim", + "lineNumber": 411 + }, + { + "caller": "doSearch", + "callee": "url.searchParams.set", + "lineNumber": 420 + }, + { + "caller": "doSearch", + "callee": "fetch", + "lineNumber": 422 + }, + { + "caller": "doSearch", + "callee": "url.toString", + "lineNumber": 422 + }, + { + "caller": "doSearch", + "callee": "response.json", + "lineNumber": 428 + }, + { + "caller": "doSearch", + "callee": "renderNcitSearchResults", + "lineNumber": 429 + }, + { + "caller": "doSearch", + "callee": "console.error", + "lineNumber": 432 + }, + { + "caller": "initNcitSearch", + "callee": "searchBtn.addEventListener", + "lineNumber": 439 + }, + { + "caller": "initNcitSearch", + "callee": "searchInput.addEventListener", + "lineNumber": 440 + }, + { + "caller": "initNcitSearch", + "callee": "doSearch", + "lineNumber": 441 + }, + { + "caller": "renderNcitSearchResults", + "callee": "data.map(function (item) {\n const safeCode = escapeHtml(item.code || '');\n const safeName = escapeHtml(item.preferred_name || '');\n const safeDef = escapeHtml(item.definition || '');\n const synonymList = Array.isArray(item.synonyms) ? item.synonyms : (item.synonyms ? [item.synonyms] : []);\n const safeSyns = synonymList.map(escapeHtml);\n\n return `\n
\n
\n ${safeCode}\n
\n
${safeName}
\n

${safeDef}

\n ${safeSyns.length > 0 ? `\n
\n Synonyms:\n
    \n ${safeSyns.map(s => `
  • ${s}
  • `).join('')}\n
\n
` : ''}\n \n
\n `;\n }).join", + "lineNumber": 451 + }, + { + "caller": "renderNcitSearchResults", + "callee": "data.map", + "lineNumber": 451 + }, + { + "caller": "renderNcitSearchResults", + "callee": "escapeHtml", + "lineNumber": 452 + }, + { + "caller": "renderNcitSearchResults", + "callee": "escapeHtml", + "lineNumber": 453 + }, + { + "caller": "renderNcitSearchResults", + "callee": "escapeHtml", + "lineNumber": 454 + }, + { + "caller": "renderNcitSearchResults", + "callee": "Array.isArray", + "lineNumber": 455 + }, + { + "caller": "renderNcitSearchResults", + "callee": "synonymList.map", + "lineNumber": 456 + }, + { + "caller": "renderNcitSearchResults", + "callee": "safeSyns.map(s => `
  • ${s}
  • `).join", + "lineNumber": 469 + }, + { + "caller": "renderNcitSearchResults", + "callee": "safeSyns.map", + "lineNumber": 469 + }, + { + "caller": "renderNcitSearchResults", + "callee": "container.querySelectorAll('.use-ncit-in-search-btn').forEach", + "lineNumber": 484 + }, + { + "caller": "renderNcitSearchResults", + "callee": "container.querySelectorAll", + "lineNumber": 484 + }, + { + "caller": "renderNcitSearchResults", + "callee": "btn.addEventListener", + "lineNumber": 485 + }, + { + "caller": "renderNcitSearchResults", + "callee": "encodeURIComponent", + "lineNumber": 494 + }, + { + "caller": "renderNcitSearchResults", + "callee": "fetch", + "lineNumber": 495 + }, + { + "caller": "renderNcitSearchResults", + "callee": "url.toString", + "lineNumber": 495 + }, + { + "caller": "renderNcitSearchResults", + "callee": "response.json", + "lineNumber": 497 + }, + { + "caller": "renderNcitSearchResults", + "callee": "sessionStorage.setItem", + "lineNumber": 498 + }, + { + "caller": "renderNcitSearchResults", + "callee": "JSON.stringify", + "lineNumber": 498 + }, + { + "caller": "renderNcitSearchResults", + "callee": "console.error", + "lineNumber": 501 + }, + { + "caller": "renderNcitSearchResults", + "callee": "encodeURIComponent", + "lineNumber": 504 + }, + { + "caller": "renderNcitSearchResults", + "callee": "encodeURIComponent", + "lineNumber": 504 + }, + { + "caller": "renderNcitSearchResults", + "callee": "encodeURIComponent", + "lineNumber": 504 + }, + { + "caller": "postJson", + "callee": "document.querySelector", + "lineNumber": 519 + }, + { + "caller": "postJson", + "callee": "csrfMeta.getAttribute", + "lineNumber": 520 + }, + { + "caller": "postJson", + "callee": "fetch", + "lineNumber": 522 + }, + { + "caller": "postJson", + "callee": "JSON.stringify", + "lineNumber": 525 + }, + { + "caller": "escapeHtml", + "callee": "document.createElement", + "lineNumber": 531 + }, + { + "caller": "escapeHtml", + "callee": "div.appendChild", + "lineNumber": 532 + }, + { + "caller": "escapeHtml", + "callee": "document.createTextNode", + "lineNumber": 532 + }, + { + "caller": "showInlineToast", + "callee": "getOrCreateToastContainer", + "lineNumber": 541 + }, + { + "caller": "showInlineToast", + "callee": "document.createElement", + "lineNumber": 542 + }, + { + "caller": "showInlineToast", + "callee": "toastEl.setAttribute", + "lineNumber": 544 + }, + { + "caller": "showInlineToast", + "callee": "toastEl.setAttribute", + "lineNumber": 545 + }, + { + "caller": "showInlineToast", + "callee": "toastEl.setAttribute", + "lineNumber": 546 + }, + { + "caller": "showInlineToast", + "callee": "escapeHtml", + "lineNumber": 549 + }, + { + "caller": "showInlineToast", + "callee": "container.appendChild", + "lineNumber": 554 + }, + { + "caller": "showInlineToast", + "callee": "toast.show", + "lineNumber": 557 + }, + { + "caller": "showInlineToast", + "callee": "toastEl.addEventListener", + "lineNumber": 559 + }, + { + "caller": "showInlineToast", + "callee": "toastEl.remove", + "lineNumber": 560 + }, + { + "caller": "getOrCreateToastContainer", + "callee": "document.getElementById", + "lineNumber": 565 + }, + { + "caller": "getOrCreateToastContainer", + "callee": "document.createElement", + "lineNumber": 567 + }, + { + "caller": "getOrCreateToastContainer", + "callee": "document.body.appendChild", + "lineNumber": 571 + }, + { + "caller": "initClickableRows", + "callee": "document.querySelectorAll('tr[data-href]').forEach", + "lineNumber": 580 + }, + { + "caller": "initClickableRows", + "callee": "document.querySelectorAll", + "lineNumber": 580 + }, + { + "caller": "initClickableRows", + "callee": "row.addEventListener", + "lineNumber": 581 + }, + { + "caller": "initLoincLookup", + "callee": "document.getElementById", + "lineNumber": 594 + }, + { + "caller": "initLoincLookup", + "callee": "document.getElementById", + "lineNumber": 595 + }, + { + "caller": "initLoincLookup", + "callee": "document.getElementById", + "lineNumber": 596 + }, + { + "caller": "initLoincLookup", + "callee": "document.getElementById", + "lineNumber": 597 + }, + { + "caller": "initLoincLookup", + "callee": "document.getElementById", + "lineNumber": 598 + }, + { + "caller": "doLoincSearch", + "callee": "url.searchParams.set", + "lineNumber": 608 + }, + { + "caller": "doLoincSearch", + "callee": "fetch", + "lineNumber": 609 + }, + { + "caller": "doLoincSearch", + "callee": "url.toString", + "lineNumber": 609 + }, + { + "caller": "doLoincSearch", + "callee": "response.json", + "lineNumber": 611 + }, + { + "caller": "doLoincSearch", + "callee": "renderLoincResults", + "lineNumber": 612 + }, + { + "caller": "doLoincSearch", + "callee": "resultsPanel.classList.remove", + "lineNumber": 613 + }, + { + "caller": "doLoincSearch", + "callee": "resultsPanel.classList.remove", + "lineNumber": 618 + }, + { + "caller": "doLoincSearch", + "callee": "console.error", + "lineNumber": 619 + }, + { + "caller": "initLoincLookup", + "callee": "lookupBtn.addEventListener", + "lineNumber": 627 + }, + { + "caller": "initLoincLookup", + "callee": "codeInput.value.trim", + "lineNumber": 628 + }, + { + "caller": "initLoincLookup", + "callee": "nameInput.value.trim", + "lineNumber": 628 + }, + { + "caller": "initLoincLookup", + "callee": "codeInput.focus", + "lineNumber": 629 + }, + { + "caller": "initLoincLookup", + "callee": "doLoincSearch", + "lineNumber": 630 + }, + { + "caller": "initLoincLookup", + "callee": "codeInput.addEventListener", + "lineNumber": 634 + }, + { + "caller": "initLoincLookup", + "callee": "e.preventDefault", + "lineNumber": 635 + }, + { + "caller": "initLoincLookup", + "callee": "doLoincSearch", + "lineNumber": 635 + }, + { + "caller": "initLoincLookup", + "callee": "codeInput.value.trim", + "lineNumber": 635 + }, + { + "caller": "initLoincLookup", + "callee": "nameInput.addEventListener", + "lineNumber": 641 + }, + { + "caller": "initLoincLookup", + "callee": "clearTimeout", + "lineNumber": 642 + }, + { + "caller": "initLoincLookup", + "callee": "nameInput.value.trim", + "lineNumber": 643 + }, + { + "caller": "initLoincLookup", + "callee": "resultsPanel.classList.add", + "lineNumber": 644 + }, + { + "caller": "initLoincLookup", + "callee": "setTimeout", + "lineNumber": 645 + }, + { + "caller": "initLoincLookup", + "callee": "doLoincSearch", + "lineNumber": 645 + }, + { + "caller": "initLoincLookup", + "callee": "nameInput.addEventListener", + "lineNumber": 647 + }, + { + "caller": "initLoincLookup", + "callee": "e.preventDefault", + "lineNumber": 649 + }, + { + "caller": "initLoincLookup", + "callee": "clearTimeout", + "lineNumber": 650 + }, + { + "caller": "initLoincLookup", + "callee": "doLoincSearch", + "lineNumber": 651 + }, + { + "caller": "initLoincLookup", + "callee": "nameInput.value.trim", + "lineNumber": 651 + }, + { + "caller": "renderLoincResults", + "callee": "escapeHtml", + "lineNumber": 690 + }, + { + "caller": "renderLoincResults", + "callee": "data.map(function (item, idx) {\n const safeCode = escapeHtml(item.LOINC_NUM || '');\n const safeName = escapeHtml(item.LONG_COMMON_NAME || '');\n\n // Build metadata rows for all non-empty fields except LOINC_NUM\n // (shown in the header row; LONG_COMMON_NAME is included in the grid)\n var metaRows = Object.keys(LOINC_FIELD_LABELS)\n .filter(function (k) { return k !== 'LOINC_NUM' && item[k]; })\n .map(function (k) {\n return `
    \n ${escapeHtml(LOINC_FIELD_LABELS[k])}\n ${escapeHtml(String(item[k]))}\n
    `;\n }).join('');\n\n return `\n
    \n
    \n ${safeCode}\n \n
    \n
    ${safeName}
    \n ${metaRows ? `
    ${metaRows}
    ` : ''}\n
    \n `;\n }).join", + "lineNumber": 694 + }, + { + "caller": "renderLoincResults", + "callee": "data.map", + "lineNumber": 694 + }, + { + "caller": "renderLoincResults", + "callee": "escapeHtml", + "lineNumber": 695 + }, + { + "caller": "renderLoincResults", + "callee": "escapeHtml", + "lineNumber": 696 + }, + { + "caller": "renderLoincResults", + "callee": "Object.keys(LOINC_FIELD_LABELS)\n .filter(function (k) { return k !== 'LOINC_NUM' && item[k]; })\n .map(function (k) {\n return `
    \n ${escapeHtml(LOINC_FIELD_LABELS[k])}\n ${escapeHtml(String(item[k]))}\n
    `;\n }).join", + "lineNumber": 700 + }, + { + "caller": "renderLoincResults", + "callee": "Object.keys(LOINC_FIELD_LABELS)\n .filter(function (k) { return k !== 'LOINC_NUM' && item[k]; })\n .map", + "lineNumber": 700 + }, + { + "caller": "renderLoincResults", + "callee": "Object.keys(LOINC_FIELD_LABELS)\n .filter", + "lineNumber": 700 + }, + { + "caller": "renderLoincResults", + "callee": "Object.keys", + "lineNumber": 700 + }, + { + "caller": "renderLoincResults", + "callee": "escapeHtml", + "lineNumber": 704 + }, + { + "caller": "renderLoincResults", + "callee": "escapeHtml", + "lineNumber": 705 + }, + { + "caller": "renderLoincResults", + "callee": "String", + "lineNumber": 705 + }, + { + "caller": "renderLoincResults", + "callee": "container.querySelectorAll('.use-loinc-btn').forEach", + "lineNumber": 729 + }, + { + "caller": "renderLoincResults", + "callee": "container.querySelectorAll", + "lineNumber": 729 + }, + { + "caller": "renderLoincResults", + "callee": "btn.addEventListener", + "lineNumber": 730 + }, + { + "caller": "renderLoincResults", + "callee": "parseInt", + "lineNumber": 731 + }, + { + "caller": "renderLoincResults", + "callee": "document.getElementById", + "lineNumber": 735 + }, + { + "caller": "renderLoincResults", + "callee": "document.getElementById", + "lineNumber": 736 + }, + { + "caller": "renderLoincResults", + "callee": "document.getElementById", + "lineNumber": 737 + }, + { + "caller": "renderLoincResults", + "callee": "JSON.stringify", + "lineNumber": 741 + }, + { + "caller": "renderLoincResults", + "callee": "renderLoincMetaDisplay", + "lineNumber": 743 + }, + { + "caller": "renderLoincResults", + "callee": "document.getElementById", + "lineNumber": 745 + }, + { + "caller": "renderLoincResults", + "callee": "panel.classList.add", + "lineNumber": 746 + }, + { + "caller": "renderLoincMetaDisplay", + "callee": "document.getElementById", + "lineNumber": 757 + }, + { + "caller": "renderLoincMetaDisplay", + "callee": "displayFields\n .filter(function (k) { return item[k]; })\n .map(function (k) {\n return `
    \n ${escapeHtml(LOINC_FIELD_LABELS[k] || k)}\n ${escapeHtml(String(item[k]))}\n
    `;\n }).join", + "lineNumber": 766 + }, + { + "caller": "renderLoincMetaDisplay", + "callee": "displayFields\n .filter(function (k) { return item[k]; })\n .map", + "lineNumber": 766 + }, + { + "caller": "renderLoincMetaDisplay", + "callee": "displayFields\n .filter", + "lineNumber": 766 + }, + { + "caller": "renderLoincMetaDisplay", + "callee": "escapeHtml", + "lineNumber": 770 + }, + { + "caller": "renderLoincMetaDisplay", + "callee": "escapeHtml", + "lineNumber": 771 + }, + { + "caller": "renderLoincMetaDisplay", + "callee": "String", + "lineNumber": 771 + }, + { + "caller": "renderLoincMetaDisplay", + "callee": "display.classList.toggle", + "lineNumber": 776 + }, + { + "caller": "initPendingNcitConcept", + "callee": "sessionStorage.getItem", + "lineNumber": 784 + }, + { + "caller": "initPendingNcitConcept", + "callee": "document.getElementById", + "lineNumber": 787 + }, + { + "caller": "initPendingNcitConcept", + "callee": "document.getElementById", + "lineNumber": 788 + }, + { + "caller": "initPendingNcitConcept", + "callee": "sessionStorage.removeItem", + "lineNumber": 792 + }, + { + "caller": "initPendingNcitConcept", + "callee": "JSON.parse", + "lineNumber": 795 + }, + { + "caller": "initPendingNcitConcept", + "callee": "renderNcitMetaDisplay", + "lineNumber": 797 + }, + { + "caller": "initPendingNcitConcept", + "callee": "document.getElementById", + "lineNumber": 799 + }, + { + "caller": "initPendingNcitConcept", + "callee": "JSON.stringify", + "lineNumber": 800 + } + ], + "metrics": { + "importCount": 0, + "exportCount": 0, + "functionCount": 0, + "classCount": 0 + } + }, + { + "path": "tests/__init__.py", + "language": "python", + "fileCategory": "code", + "totalLines": 1, + "nonEmptyLines": 0, + "metrics": { + "importCount": 0, + "exportCount": 0, + "functionCount": 0, + "classCount": 0 + } + } + ] +} \ No newline at end of file diff --git a/.ua/.trash-1785851745/tmp/ua-file-extract-results-3.json b/.ua/.trash-1785851745/tmp/ua-file-extract-results-3.json new file mode 100644 index 0000000..7270bb4 --- /dev/null +++ b/.ua/.trash-1785851745/tmp/ua-file-extract-results-3.json @@ -0,0 +1,1935 @@ +{ + "scriptCompleted": true, + "filesAnalyzed": 8, + "filesSkipped": [], + "analysisOutcomes": { + "structure": { + "succeeded": 8, + "failed": 0 + }, + "callGraph": { + "succeeded": 8, + "failed": 0, + "skipped": 0 + } + }, + "results": [ + { + "path": "routes/dashboard.py", + "language": "python", + "fileCategory": "code", + "totalLines": 60, + "nonEmptyLines": 49, + "functions": [ + { + "name": "index", + "startLine": 17, + "endLine": 60, + "params": [] + } + ], + "exports": [ + { + "name": "index", + "line": 16, + "isDefault": false + } + ], + "callGraph": [ + { + "caller": "index", + "callee": "CDISCApiClient", + "lineNumber": 19 + }, + { + "caller": "index", + "callee": "ThreadPoolExecutor", + "lineNumber": 20 + }, + { + "caller": "index", + "callee": "pool.submit", + "lineNumber": 21 + }, + { + "caller": "index", + "callee": "pool.submit", + "lineNumber": 22 + }, + { + "caller": "index", + "callee": "fut_bcs.result", + "lineNumber": 23 + }, + { + "caller": "index", + "callee": "fut_specs.result", + "lineNumber": 24 + }, + { + "caller": "index", + "callee": "api_bcs[0].get", + "lineNumber": 26 + }, + { + "caller": "index", + "callee": "api_specs[0].get", + "lineNumber": 27 + }, + { + "caller": "index", + "callee": "len", + "lineNumber": 28 + }, + { + "caller": "index", + "callee": "len", + "lineNumber": 29 + }, + { + "caller": "index", + "callee": "BiomedicalConcept.query.count", + "lineNumber": 32 + }, + { + "caller": "index", + "callee": "BiomedicalConcept.query.filter(BiomedicalConcept.status.in_([\"provisional\", \"sme_review\", \"cdisc_approval\"])).count", + "lineNumber": 33 + }, + { + "caller": "index", + "callee": "BiomedicalConcept.query.filter", + "lineNumber": 33 + }, + { + "caller": "index", + "callee": "BiomedicalConcept.status.in_", + "lineNumber": 33 + }, + { + "caller": "index", + "callee": "BiomedicalConcept.query.filter(BiomedicalConcept.created_at >= datetime.now(timezone.utc) - timedelta(days=7)).count", + "lineNumber": 34 + }, + { + "caller": "index", + "callee": "BiomedicalConcept.query.filter", + "lineNumber": 34 + }, + { + "caller": "index", + "callee": "datetime.now", + "lineNumber": 34 + }, + { + "caller": "index", + "callee": "timedelta", + "lineNumber": 34 + }, + { + "caller": "index", + "callee": "BiomedicalConcept.query.filter(BiomedicalConcept.status != \"published\").order_by(BiomedicalConcept.updated_at.desc()).limit(10).all", + "lineNumber": 36 + }, + { + "caller": "index", + "callee": "BiomedicalConcept.query.filter(BiomedicalConcept.status != \"published\").order_by(BiomedicalConcept.updated_at.desc()).limit", + "lineNumber": 36 + }, + { + "caller": "index", + "callee": "BiomedicalConcept.query.filter(BiomedicalConcept.status != \"published\").order_by", + "lineNumber": 36 + }, + { + "caller": "index", + "callee": "BiomedicalConcept.query.filter", + "lineNumber": 36 + }, + { + "caller": "index", + "callee": "BiomedicalConcept.updated_at.desc", + "lineNumber": 36 + }, + { + "caller": "index", + "callee": "AuditLog.query.order_by(AuditLog.timestamp.desc()).limit(10).all", + "lineNumber": 37 + }, + { + "caller": "index", + "callee": "AuditLog.query.order_by(AuditLog.timestamp.desc()).limit", + "lineNumber": 37 + }, + { + "caller": "index", + "callee": "AuditLog.query.order_by", + "lineNumber": 37 + }, + { + "caller": "index", + "callee": "AuditLog.timestamp.desc", + "lineNumber": 37 + }, + { + "caller": "index", + "callee": "render_template", + "lineNumber": 47 + }, + { + "caller": "index", + "callee": "sorted", + "lineNumber": 50 + }, + { + "caller": "index", + "callee": "(x.get(\"title\") or \"\").lower", + "lineNumber": 50 + }, + { + "caller": "index", + "callee": "x.get", + "lineNumber": 50 + }, + { + "caller": "index", + "callee": "sorted", + "lineNumber": 51 + }, + { + "caller": "index", + "callee": "(x.get(\"title\") or \"\").lower", + "lineNumber": 51 + }, + { + "caller": "index", + "callee": "x.get", + "lineNumber": 51 + }, + { + "caller": "index", + "callee": "BiomedicalConcept.query.order_by(BiomedicalConcept.created_at.desc()).limit(10).all", + "lineNumber": 56 + }, + { + "caller": "index", + "callee": "BiomedicalConcept.query.order_by(BiomedicalConcept.created_at.desc()).limit", + "lineNumber": 56 + }, + { + "caller": "index", + "callee": "BiomedicalConcept.query.order_by", + "lineNumber": 56 + }, + { + "caller": "index", + "callee": "BiomedicalConcept.created_at.desc", + "lineNumber": 56 + } + ], + "metrics": { + "importCount": 3, + "exportCount": 1, + "functionCount": 1, + "classCount": 0 + } + }, + { + "path": "routes/loinc.py", + "language": "python", + "fileCategory": "code", + "totalLines": 23, + "nonEmptyLines": 16, + "functions": [ + { + "name": "search", + "startLine": 13, + "endLine": 23, + "params": [] + } + ], + "exports": [ + { + "name": "search", + "line": 12, + "isDefault": false + } + ], + "callGraph": [ + { + "caller": "search", + "callee": "request.args.get(\"term\", \"\").strip", + "lineNumber": 14 + }, + { + "caller": "search", + "callee": "request.args.get", + "lineNumber": 14 + }, + { + "caller": "search", + "callee": "request.headers.get", + "lineNumber": 15 + }, + { + "caller": "search", + "callee": "request.args.get", + "lineNumber": 15 + }, + { + "caller": "search", + "callee": "request.headers.get", + "lineNumber": 15 + }, + { + "caller": "search", + "callee": "jsonify", + "lineNumber": 18 + }, + { + "caller": "search", + "callee": "jsonify", + "lineNumber": 19 + }, + { + "caller": "search", + "callee": "LoincApiClient", + "lineNumber": 21 + }, + { + "caller": "search", + "callee": "client.search", + "lineNumber": 22 + }, + { + "caller": "search", + "callee": "jsonify", + "lineNumber": 23 + } + ], + "metrics": { + "importCount": 1, + "exportCount": 1, + "functionCount": 1, + "classCount": 0 + } + }, + { + "path": "routes/specializations.py", + "language": "python", + "fileCategory": "code", + "totalLines": 172, + "nonEmptyLines": 147, + "functions": [ + { + "name": "_get_bc_options", + "startLine": 16, + "endLine": 25, + "params": [] + }, + { + "name": "index", + "startLine": 29, + "endLine": 38, + "params": [] + }, + { + "name": "library_detail", + "startLine": 42, + "endLine": 52, + "params": [ + "spec_path" + ] + }, + { + "name": "detail", + "startLine": 56, + "endLine": 67, + "params": [ + "vlm_group_id" + ] + }, + { + "name": "_variables_from_form", + "startLine": 70, + "endLine": 87, + "params": [ + "form" + ] + }, + { + "name": "create", + "startLine": 91, + "endLine": 124, + "params": [] + }, + { + "name": "delete", + "startLine": 128, + "endLine": 134, + "params": [ + "vlm_group_id" + ] + }, + { + "name": "generate_from_dec", + "startLine": 138, + "endLine": 146, + "params": [] + }, + { + "name": "generate", + "startLine": 150, + "endLine": 172, + "params": [ + "bc_id" + ] + } + ], + "exports": [ + { + "name": "_get_bc_options", + "line": 16, + "isDefault": false + }, + { + "name": "index", + "line": 28, + "isDefault": false + }, + { + "name": "library_detail", + "line": 41, + "isDefault": false + }, + { + "name": "detail", + "line": 55, + "isDefault": false + }, + { + "name": "_variables_from_form", + "line": 70, + "isDefault": false + }, + { + "name": "create", + "line": 90, + "isDefault": false + }, + { + "name": "delete", + "line": 127, + "isDefault": false + }, + { + "name": "generate_from_dec", + "line": 137, + "isDefault": false + }, + { + "name": "generate", + "line": 149, + "isDefault": false + } + ], + "callGraph": [ + { + "caller": "_get_bc_options", + "callee": "CDISCApiClient().get_biomedical_concepts", + "lineNumber": 22 + }, + { + "caller": "_get_bc_options", + "callee": "CDISCApiClient", + "lineNumber": 22 + }, + { + "caller": "_get_bc_options", + "callee": "lnk[\"href\"].rstrip(\"/\").split", + "lineNumber": 23 + }, + { + "caller": "_get_bc_options", + "callee": "lnk[\"href\"].rstrip", + "lineNumber": 23 + }, + { + "caller": "_get_bc_options", + "callee": "lnk.get", + "lineNumber": 23 + }, + { + "caller": "_get_bc_options", + "callee": "BiomedicalConcept.query.order_by(BiomedicalConcept.short_name).all", + "lineNumber": 24 + }, + { + "caller": "_get_bc_options", + "callee": "BiomedicalConcept.query.order_by", + "lineNumber": 24 + }, + { + "caller": "index", + "callee": "DatasetSpecialization.query.all", + "lineNumber": 30 + }, + { + "caller": "index", + "callee": "_get_bc_options", + "lineNumber": 31 + }, + { + "caller": "index", + "callee": "render_template", + "lineNumber": 32 + }, + { + "caller": "library_detail", + "callee": "CDISCApiClient", + "lineNumber": 43 + }, + { + "caller": "library_detail", + "callee": "client.get_specialization", + "lineNumber": 44 + }, + { + "caller": "library_detail", + "callee": "flash", + "lineNumber": 46 + }, + { + "caller": "library_detail", + "callee": "redirect", + "lineNumber": 47 + }, + { + "caller": "library_detail", + "callee": "url_for", + "lineNumber": 47 + }, + { + "caller": "library_detail", + "callee": "render_template", + "lineNumber": 48 + }, + { + "caller": "library_detail", + "callee": "spec.get", + "lineNumber": 51 + }, + { + "caller": "library_detail", + "callee": "spec.get", + "lineNumber": 51 + }, + { + "caller": "library_detail", + "callee": "spec_path.split", + "lineNumber": 51 + }, + { + "caller": "detail", + "callee": "db.get_or_404", + "lineNumber": 57 + }, + { + "caller": "detail", + "callee": "DatasetSpecialization.query.all", + "lineNumber": 58 + }, + { + "caller": "detail", + "callee": "_get_bc_options", + "lineNumber": 59 + }, + { + "caller": "detail", + "callee": "render_template", + "lineNumber": 60 + }, + { + "caller": "_variables_from_form", + "callee": "(form.get(f\"variables[{i}][name]\") or \"\").strip", + "lineNumber": 75 + }, + { + "caller": "_variables_from_form", + "callee": "form.get", + "lineNumber": 75 + }, + { + "caller": "_variables_from_form", + "callee": "(form.get(f\"variables[{i}][label]\") or \"\").strip", + "lineNumber": 76 + }, + { + "caller": "_variables_from_form", + "callee": "form.get", + "lineNumber": 76 + }, + { + "caller": "_variables_from_form", + "callee": "variables.append", + "lineNumber": 78 + }, + { + "caller": "_variables_from_form", + "callee": "form.get", + "lineNumber": 82 + }, + { + "caller": "_variables_from_form", + "callee": "form.get", + "lineNumber": 83 + }, + { + "caller": "create", + "callee": "request.form.get(\"vlm_group_id\", \"\").strip", + "lineNumber": 92 + }, + { + "caller": "create", + "callee": "request.form.get", + "lineNumber": 92 + }, + { + "caller": "create", + "callee": "request.form.get(\"bc_id\", \"\").strip", + "lineNumber": 93 + }, + { + "caller": "create", + "callee": "request.form.get", + "lineNumber": 93 + }, + { + "caller": "create", + "callee": "flash", + "lineNumber": 95 + }, + { + "caller": "create", + "callee": "redirect", + "lineNumber": 96 + }, + { + "caller": "create", + "callee": "url_for", + "lineNumber": 96 + }, + { + "caller": "create", + "callee": "request.form.get", + "lineNumber": 97 + }, + { + "caller": "create", + "callee": "request.form.get", + "lineNumber": 98 + }, + { + "caller": "create", + "callee": "_variables_from_form", + "lineNumber": 99 + }, + { + "caller": "create", + "callee": "db.session.get", + "lineNumber": 101 + }, + { + "caller": "create", + "callee": "spec.to_dict", + "lineNumber": 103 + }, + { + "caller": "create", + "callee": "log_change", + "lineNumber": 108 + }, + { + "caller": "create", + "callee": "spec.to_dict", + "lineNumber": 108 + }, + { + "caller": "create", + "callee": "db.session.commit", + "lineNumber": 109 + }, + { + "caller": "create", + "callee": "flash", + "lineNumber": 110 + }, + { + "caller": "create", + "callee": "redirect", + "lineNumber": 111 + }, + { + "caller": "create", + "callee": "url_for", + "lineNumber": 111 + }, + { + "caller": "create", + "callee": "DatasetSpecialization", + "lineNumber": 113 + }, + { + "caller": "create", + "callee": "db.session.add", + "lineNumber": 120 + }, + { + "caller": "create", + "callee": "log_change", + "lineNumber": 121 + }, + { + "caller": "create", + "callee": "spec.to_dict", + "lineNumber": 121 + }, + { + "caller": "create", + "callee": "db.session.commit", + "lineNumber": 122 + }, + { + "caller": "create", + "callee": "flash", + "lineNumber": 123 + }, + { + "caller": "create", + "callee": "redirect", + "lineNumber": 124 + }, + { + "caller": "create", + "callee": "url_for", + "lineNumber": 124 + }, + { + "caller": "delete", + "callee": "db.get_or_404", + "lineNumber": 129 + }, + { + "caller": "delete", + "callee": "log_change", + "lineNumber": 130 + }, + { + "caller": "delete", + "callee": "spec.to_dict", + "lineNumber": 130 + }, + { + "caller": "delete", + "callee": "db.session.delete", + "lineNumber": 131 + }, + { + "caller": "delete", + "callee": "db.session.commit", + "lineNumber": 132 + }, + { + "caller": "delete", + "callee": "flash", + "lineNumber": 133 + }, + { + "caller": "delete", + "callee": "redirect", + "lineNumber": 134 + }, + { + "caller": "delete", + "callee": "url_for", + "lineNumber": 134 + }, + { + "caller": "generate_from_dec", + "callee": "request.get_json", + "lineNumber": 140 + }, + { + "caller": "generate_from_dec", + "callee": "data.get(\"bc_id\", \"\").strip", + "lineNumber": 141 + }, + { + "caller": "generate_from_dec", + "callee": "data.get", + "lineNumber": 141 + }, + { + "caller": "generate_from_dec", + "callee": "jsonify", + "lineNumber": 143 + }, + { + "caller": "generate_from_dec", + "callee": "DataElementConcept.query.filter_by(bc_id=bc_id).all", + "lineNumber": 144 + }, + { + "caller": "generate_from_dec", + "callee": "DataElementConcept.query.filter_by", + "lineNumber": 144 + }, + { + "caller": "generate_from_dec", + "callee": "bool", + "lineNumber": 145 + }, + { + "caller": "generate_from_dec", + "callee": "jsonify", + "lineNumber": 146 + }, + { + "caller": "generate", + "callee": "db.get_or_404", + "lineNumber": 152 + }, + { + "caller": "generate", + "callee": "DataElementConcept.query.filter_by(bc_id=bc_id).all", + "lineNumber": 153 + }, + { + "caller": "generate", + "callee": "DataElementConcept.query.filter_by", + "lineNumber": 153 + }, + { + "caller": "generate", + "callee": "request.form.get", + "lineNumber": 154 + }, + { + "caller": "generate", + "callee": "db.session.get", + "lineNumber": 156 + }, + { + "caller": "generate", + "callee": "flash", + "lineNumber": 158 + }, + { + "caller": "generate", + "callee": "redirect", + "lineNumber": 159 + }, + { + "caller": "generate", + "callee": "url_for", + "lineNumber": 159 + }, + { + "caller": "generate", + "callee": "DatasetSpecialization", + "lineNumber": 161 + }, + { + "caller": "generate", + "callee": "db.session.add", + "lineNumber": 168 + }, + { + "caller": "generate", + "callee": "log_change", + "lineNumber": 169 + }, + { + "caller": "generate", + "callee": "spec.to_dict", + "lineNumber": 169 + }, + { + "caller": "generate", + "callee": "db.session.commit", + "lineNumber": 170 + }, + { + "caller": "generate", + "callee": "flash", + "lineNumber": 171 + }, + { + "caller": "generate", + "callee": "redirect", + "lineNumber": 172 + }, + { + "caller": "generate", + "callee": "url_for", + "lineNumber": 172 + } + ], + "metrics": { + "importCount": 5, + "exportCount": 9, + "functionCount": 9, + "classCount": 0 + } + }, + { + "path": "services/api_cache.py", + "language": "python", + "fileCategory": "code", + "totalLines": 42, + "nonEmptyLines": 34, + "functions": [ + { + "name": "cached", + "startLine": 22, + "endLine": 42, + "params": [ + "cache_key", + "fn" + ] + } + ], + "exports": [ + { + "name": "cached", + "line": 22, + "isDefault": false + } + ], + "callGraph": [ + { + "caller": "cached", + "callee": "time.time", + "lineNumber": 25 + }, + { + "caller": "cached", + "callee": "_cache.get", + "lineNumber": 26 + }, + { + "caller": "cached", + "callee": "fn", + "lineNumber": 34 + }, + { + "caller": "cached", + "callee": "logger.warning", + "lineNumber": 39 + }, + { + "caller": "cached", + "callee": "logger.error", + "lineNumber": 41 + } + ], + "metrics": { + "importCount": 0, + "exportCount": 1, + "functionCount": 1, + "classCount": 0 + } + }, + { + "path": "services/cdisc_api.py", + "language": "python", + "fileCategory": "code", + "totalLines": 127, + "nonEmptyLines": 107, + "functions": [ + { + "name": "_config_value", + "startLine": 19, + "endLine": 26, + "params": [ + "name", + "default" + ] + } + ], + "classes": [ + { + "name": "CDISCApiClient", + "startLine": 29, + "endLine": 127, + "methods": [ + "__init__", + "_cache_key", + "_get", + "get_biomedical_concepts", + "get_bc", + "get_specialization", + "get_dataset_specializations", + "check_duplicate", + "publish_bc" + ], + "properties": [] + } + ], + "exports": [ + { + "name": "_config_value", + "line": 19, + "isDefault": false + }, + { + "name": "CDISCApiClient", + "line": 29, + "isDefault": false + } + ], + "callGraph": [ + { + "caller": "_config_value", + "callee": "current_app.config.get", + "lineNumber": 24 + }, + { + "caller": "_config_value", + "callee": "os.environ.get", + "lineNumber": 24 + }, + { + "caller": "_config_value", + "callee": "os.environ.get", + "lineNumber": 26 + }, + { + "caller": "__init__", + "callee": "_config_value", + "lineNumber": 31 + }, + { + "caller": "__init__", + "callee": "_config_value", + "lineNumber": 32 + }, + { + "caller": "__init__", + "callee": "_config_value", + "lineNumber": 33 + }, + { + "caller": "__init__", + "callee": "hashlib.sha256(key_material.encode()).hexdigest", + "lineNumber": 44 + }, + { + "caller": "__init__", + "callee": "hashlib.sha256", + "lineNumber": 44 + }, + { + "caller": "__init__", + "callee": "key_material.encode", + "lineNumber": 44 + }, + { + "caller": "_get", + "callee": "requests.get", + "lineNumber": 51 + }, + { + "caller": "_get", + "callee": "response.raise_for_status", + "lineNumber": 52 + }, + { + "caller": "_get", + "callee": "response.json", + "lineNumber": 53 + }, + { + "caller": "_fetch", + "callee": "self._get", + "lineNumber": 65 + }, + { + "caller": "_fetch", + "callee": "data.get(\"_links\", {}).get", + "lineNumber": 66 + }, + { + "caller": "_fetch", + "callee": "data.get", + "lineNumber": 66 + }, + { + "caller": "_fetch", + "callee": "logger.error", + "lineNumber": 68 + }, + { + "caller": "_fetch", + "callee": "str", + "lineNumber": 69 + }, + { + "caller": "get_biomedical_concepts", + "callee": "_cached", + "lineNumber": 71 + }, + { + "caller": "get_biomedical_concepts", + "callee": "self._cache_key", + "lineNumber": 71 + }, + { + "caller": "get_bc", + "callee": "self._get", + "lineNumber": 76 + }, + { + "caller": "get_bc", + "callee": "logger.error", + "lineNumber": 78 + }, + { + "caller": "get_bc", + "callee": "str", + "lineNumber": 79 + }, + { + "caller": "get_specialization", + "callee": "self._get", + "lineNumber": 84 + }, + { + "caller": "get_specialization", + "callee": "logger.error", + "lineNumber": 86 + }, + { + "caller": "get_specialization", + "callee": "str", + "lineNumber": 87 + }, + { + "caller": "_fetch", + "callee": "self._get", + "lineNumber": 99 + }, + { + "caller": "_fetch", + "callee": "data.get(\"_links\", {}).get", + "lineNumber": 100 + }, + { + "caller": "_fetch", + "callee": "data.get", + "lineNumber": 100 + }, + { + "caller": "_fetch", + "callee": "isinstance", + "lineNumber": 102 + }, + { + "caller": "_fetch", + "callee": "links.values", + "lineNumber": 104 + }, + { + "caller": "_fetch", + "callee": "isinstance", + "lineNumber": 104 + }, + { + "caller": "_fetch", + "callee": "logger.error", + "lineNumber": 106 + }, + { + "caller": "_fetch", + "callee": "str", + "lineNumber": 107 + }, + { + "caller": "get_dataset_specializations", + "callee": "_cached", + "lineNumber": 109 + }, + { + "caller": "get_dataset_specializations", + "callee": "self._cache_key", + "lineNumber": 109 + }, + { + "caller": "check_duplicate", + "callee": "self.get_biomedical_concepts", + "lineNumber": 116 + }, + { + "caller": "check_duplicate", + "callee": "any", + "lineNumber": 117 + }, + { + "caller": "check_duplicate", + "callee": "bc.get(\"title\", \"\").lower", + "lineNumber": 117 + }, + { + "caller": "check_duplicate", + "callee": "bc.get", + "lineNumber": 117 + }, + { + "caller": "check_duplicate", + "callee": "short_name.lower", + "lineNumber": 117 + }, + { + "caller": "check_duplicate", + "callee": "logger.error", + "lineNumber": 119 + }, + { + "caller": "publish_bc", + "callee": "requests.post", + "lineNumber": 125 + }, + { + "caller": "publish_bc", + "callee": "response.raise_for_status", + "lineNumber": 126 + }, + { + "caller": "publish_bc", + "callee": "response.json", + "lineNumber": 127 + } + ], + "metrics": { + "importCount": 1, + "exportCount": 2, + "functionCount": 1, + "classCount": 1 + } + }, + { + "path": "services/loinc_api.py", + "language": "python", + "fileCategory": "code", + "totalLines": 62, + "nonEmptyLines": 51, + "classes": [ + { + "name": "LoincApiClient", + "startLine": 18, + "endLine": 62, + "methods": [ + "_auth", + "search" + ], + "properties": [] + } + ], + "exports": [ + { + "name": "LoincApiClient", + "line": 18, + "isDefault": false + } + ], + "callGraph": [ + { + "caller": "_auth", + "callee": "os.environ.get", + "lineNumber": 22 + }, + { + "caller": "_auth", + "callee": "os.environ.get", + "lineNumber": 23 + }, + { + "caller": "_fetch", + "callee": "requests.get", + "lineNumber": 36 + }, + { + "caller": "_fetch", + "callee": "self._auth", + "lineNumber": 39 + }, + { + "caller": "_fetch", + "callee": "response.raise_for_status", + "lineNumber": 42 + }, + { + "caller": "_fetch", + "callee": "response.json", + "lineNumber": 43 + }, + { + "caller": "_fetch", + "callee": "len", + "lineNumber": 46 + }, + { + "caller": "_fetch", + "callee": "len", + "lineNumber": 47 + }, + { + "caller": "_fetch", + "callee": "range", + "lineNumber": 49 + }, + { + "caller": "_fetch", + "callee": "len", + "lineNumber": 49 + }, + { + "caller": "_fetch", + "callee": "extra.items", + "lineNumber": 51 + }, + { + "caller": "_fetch", + "callee": "len", + "lineNumber": 52 + }, + { + "caller": "_fetch", + "callee": "results.append", + "lineNumber": 53 + }, + { + "caller": "search", + "callee": "cached", + "lineNumber": 57 + }, + { + "caller": "search", + "callee": "logger.error", + "lineNumber": 61 + }, + { + "caller": "search", + "callee": "str", + "lineNumber": 62 + } + ], + "metrics": { + "importCount": 1, + "exportCount": 1, + "functionCount": 0, + "classCount": 1 + } + }, + { + "path": "tests/test_cdisc_api_cache.py", + "language": "python", + "fileCategory": "code", + "totalLines": 120, + "nonEmptyLines": 86, + "classes": [ + { + "name": "TestCachedHelper", + "startLine": 11, + "endLine": 64, + "methods": [ + "setup_method", + "test_first_call_fetches_and_stores", + "test_fresh_entry_served_without_refetch", + "test_stale_entry_refreshes_successfully", + "test_stale_entry_served_when_refresh_fails", + "test_expired_entry_raises_when_refresh_fails", + "test_no_entry_raises_when_fetch_fails" + ], + "properties": [] + }, + { + "name": "TestClientCacheKeys", + "startLine": 67, + "endLine": 120, + "methods": [ + "setup_method", + "test_cache_key_excludes_raw_api_key", + "test_get_biomedical_concepts_error_encoded_not_raised", + "test_error_result_replaced_after_ttl" + ], + "properties": [] + } + ], + "exports": [ + { + "name": "TestCachedHelper", + "line": 11, + "isDefault": false + }, + { + "name": "TestClientCacheKeys", + "line": 67, + "isDefault": false + } + ], + "callGraph": [ + { + "caller": "setup_method", + "callee": "cdisc_api._cache.clear", + "lineNumber": 13 + }, + { + "caller": "fn", + "callee": "calls.append", + "lineNumber": 19 + }, + { + "caller": "test_first_call_fetches_and_stores", + "callee": "_cached", + "lineNumber": 22 + }, + { + "caller": "test_first_call_fetches_and_stores", + "callee": "len", + "lineNumber": 23 + }, + { + "caller": "fn", + "callee": "calls.append", + "lineNumber": 30 + }, + { + "caller": "test_fresh_entry_served_without_refetch", + "callee": "_cached", + "lineNumber": 33 + }, + { + "caller": "test_fresh_entry_served_without_refetch", + "callee": "_cached", + "lineNumber": 34 + }, + { + "caller": "test_fresh_entry_served_without_refetch", + "callee": "len", + "lineNumber": 35 + }, + { + "caller": "test_stale_entry_refreshes_successfully", + "callee": "time.time", + "lineNumber": 38 + }, + { + "caller": "test_stale_entry_refreshes_successfully", + "callee": "_cached", + "lineNumber": 39 + }, + { + "caller": "test_stale_entry_served_when_refresh_fails", + "callee": "time.time", + "lineNumber": 43 + }, + { + "caller": "failing", + "callee": "ConnectionError", + "lineNumber": 46 + }, + { + "caller": "test_stale_entry_served_when_refresh_fails", + "callee": "_cached", + "lineNumber": 48 + }, + { + "caller": "test_expired_entry_raises_when_refresh_fails", + "callee": "time.time", + "lineNumber": 51 + }, + { + "caller": "failing", + "callee": "ConnectionError", + "lineNumber": 54 + }, + { + "caller": "test_expired_entry_raises_when_refresh_fails", + "callee": "pytest.raises", + "lineNumber": 56 + }, + { + "caller": "test_expired_entry_raises_when_refresh_fails", + "callee": "_cached", + "lineNumber": 57 + }, + { + "caller": "failing", + "callee": "ConnectionError", + "lineNumber": 61 + }, + { + "caller": "test_no_entry_raises_when_fetch_fails", + "callee": "pytest.raises", + "lineNumber": 63 + }, + { + "caller": "test_no_entry_raises_when_fetch_fails", + "callee": "_cached", + "lineNumber": 64 + }, + { + "caller": "setup_method", + "callee": "cdisc_api._cache.clear", + "lineNumber": 69 + }, + { + "caller": "test_cache_key_excludes_raw_api_key", + "callee": "app.app_context", + "lineNumber": 72 + }, + { + "caller": "test_cache_key_excludes_raw_api_key", + "callee": "CDISCApiClient", + "lineNumber": 73 + }, + { + "caller": "test_cache_key_excludes_raw_api_key", + "callee": "client._cache_key", + "lineNumber": 74 + }, + { + "caller": "boom", + "callee": "requests.ConnectionError", + "lineNumber": 83 + }, + { + "caller": "test_get_biomedical_concepts_error_encoded_not_raised", + "callee": "monkeypatch.setattr", + "lineNumber": 85 + }, + { + "caller": "test_get_biomedical_concepts_error_encoded_not_raised", + "callee": "app.app_context", + "lineNumber": 86 + }, + { + "caller": "test_get_biomedical_concepts_error_encoded_not_raised", + "callee": "CDISCApiClient().get_biomedical_concepts", + "lineNumber": 87 + }, + { + "caller": "test_get_biomedical_concepts_error_encoded_not_raised", + "callee": "CDISCApiClient", + "lineNumber": 87 + }, + { + "caller": "test_get_biomedical_concepts_error_encoded_not_raised", + "callee": "len", + "lineNumber": 88 + }, + { + "caller": "test_error_result_replaced_after_ttl", + "callee": "app.app_context", + "lineNumber": 95 + }, + { + "caller": "test_error_result_replaced_after_ttl", + "callee": "CDISCApiClient", + "lineNumber": 96 + }, + { + "caller": "boom", + "callee": "requests.ConnectionError", + "lineNumber": 99 + }, + { + "caller": "test_error_result_replaced_after_ttl", + "callee": "monkeypatch.setattr", + "lineNumber": 101 + }, + { + "caller": "test_error_result_replaced_after_ttl", + "callee": "app.app_context", + "lineNumber": 102 + }, + { + "caller": "test_error_result_replaced_after_ttl", + "callee": "CDISCApiClient().get_biomedical_concepts", + "lineNumber": 103 + }, + { + "caller": "test_error_result_replaced_after_ttl", + "callee": "CDISCApiClient", + "lineNumber": 103 + }, + { + "caller": "test_error_result_replaced_after_ttl", + "callee": "client._cache_key", + "lineNumber": 106 + }, + { + "caller": "test_error_result_replaced_after_ttl", + "callee": "monkeypatch.setattr", + "lineNumber": 117 + }, + { + "caller": "test_error_result_replaced_after_ttl", + "callee": "FakeResp", + "lineNumber": 117 + }, + { + "caller": "test_error_result_replaced_after_ttl", + "callee": "app.app_context", + "lineNumber": 118 + }, + { + "caller": "test_error_result_replaced_after_ttl", + "callee": "CDISCApiClient().get_biomedical_concepts", + "lineNumber": 119 + }, + { + "caller": "test_error_result_replaced_after_ttl", + "callee": "CDISCApiClient", + "lineNumber": 119 + } + ], + "metrics": { + "importCount": 1, + "exportCount": 2, + "functionCount": 0, + "classCount": 2 + } + }, + { + "path": "tests/test_loinc.py", + "language": "python", + "fileCategory": "code", + "totalLines": 205, + "nonEmptyLines": 169, + "classes": [ + { + "name": "TestLoincApiClientSearch", + "startLine": 44, + "endLine": 168, + "methods": [ + "_mock_response", + "test_returns_normalized_list", + "test_all_ef_fields_present_in_result", + "test_uses_ef_parameter", + "test_ef_fields_constant_contains_all_required_fields", + "test_uses_basic_auth_when_env_vars_set", + "test_no_auth_when_env_vars_missing", + "test_empty_results", + "test_missing_codes_array_returns_empty", + "test_network_error_returns_error_entry" + ], + "properties": [] + }, + { + "name": "TestLoincSearchRoute", + "startLine": 176, + "endLine": 205, + "methods": [ + "test_returns_json_for_ajax", + "test_empty_term_returns_empty_list", + "test_calls_client_with_term", + "test_format_json_param_triggers_json_response" + ], + "properties": [] + } + ], + "exports": [ + { + "name": "TestLoincApiClientSearch", + "line": 44, + "isDefault": false + }, + { + "name": "TestLoincSearchRoute", + "line": 176, + "isDefault": false + } + ], + "callGraph": [ + { + "caller": "_mock_response", + "callee": "MagicMock", + "lineNumber": 46 + }, + { + "caller": "_mock_response", + "callee": "MagicMock", + "lineNumber": 49 + }, + { + "caller": "test_returns_normalized_list", + "callee": "patch", + "lineNumber": 53 + }, + { + "caller": "test_returns_normalized_list", + "callee": "self._mock_response", + "lineNumber": 54 + }, + { + "caller": "test_returns_normalized_list", + "callee": "LoincApiClient().search", + "lineNumber": 55 + }, + { + "caller": "test_returns_normalized_list", + "callee": "LoincApiClient", + "lineNumber": 55 + }, + { + "caller": "test_returns_normalized_list", + "callee": "len", + "lineNumber": 57 + }, + { + "caller": "test_all_ef_fields_present_in_result", + "callee": "patch", + "lineNumber": 69 + }, + { + "caller": "test_all_ef_fields_present_in_result", + "callee": "self._mock_response", + "lineNumber": 70 + }, + { + "caller": "test_all_ef_fields_present_in_result", + "callee": "LoincApiClient().search", + "lineNumber": 71 + }, + { + "caller": "test_all_ef_fields_present_in_result", + "callee": "LoincApiClient", + "lineNumber": 71 + }, + { + "caller": "test_uses_ef_parameter", + "callee": "patch", + "lineNumber": 94 + }, + { + "caller": "test_uses_ef_parameter", + "callee": "self._mock_response", + "lineNumber": 95 + }, + { + "caller": "test_uses_ef_parameter", + "callee": "LoincApiClient().search", + "lineNumber": 96 + }, + { + "caller": "test_uses_ef_parameter", + "callee": "LoincApiClient", + "lineNumber": 96 + }, + { + "caller": "test_uses_basic_auth_when_env_vars_set", + "callee": "monkeypatch.setenv", + "lineNumber": 129 + }, + { + "caller": "test_uses_basic_auth_when_env_vars_set", + "callee": "monkeypatch.setenv", + "lineNumber": 130 + }, + { + "caller": "test_uses_basic_auth_when_env_vars_set", + "callee": "patch", + "lineNumber": 131 + }, + { + "caller": "test_uses_basic_auth_when_env_vars_set", + "callee": "self._mock_response", + "lineNumber": 132 + }, + { + "caller": "test_uses_basic_auth_when_env_vars_set", + "callee": "LoincApiClient().search", + "lineNumber": 133 + }, + { + "caller": "test_uses_basic_auth_when_env_vars_set", + "callee": "LoincApiClient", + "lineNumber": 133 + }, + { + "caller": "test_uses_basic_auth_when_env_vars_set", + "callee": "mock_get.call_args[1].get", + "lineNumber": 135 + }, + { + "caller": "test_no_auth_when_env_vars_missing", + "callee": "monkeypatch.delenv", + "lineNumber": 139 + }, + { + "caller": "test_no_auth_when_env_vars_missing", + "callee": "monkeypatch.delenv", + "lineNumber": 140 + }, + { + "caller": "test_no_auth_when_env_vars_missing", + "callee": "patch", + "lineNumber": 141 + }, + { + "caller": "test_no_auth_when_env_vars_missing", + "callee": "self._mock_response", + "lineNumber": 142 + }, + { + "caller": "test_no_auth_when_env_vars_missing", + "callee": "LoincApiClient().search", + "lineNumber": 143 + }, + { + "caller": "test_no_auth_when_env_vars_missing", + "callee": "LoincApiClient", + "lineNumber": 143 + }, + { + "caller": "test_no_auth_when_env_vars_missing", + "callee": "mock_get.call_args[1].get", + "lineNumber": 145 + }, + { + "caller": "test_empty_results", + "callee": "patch", + "lineNumber": 149 + }, + { + "caller": "test_empty_results", + "callee": "self._mock_response", + "lineNumber": 150 + }, + { + "caller": "test_empty_results", + "callee": "LoincApiClient().search", + "lineNumber": 151 + }, + { + "caller": "test_empty_results", + "callee": "LoincApiClient", + "lineNumber": 151 + }, + { + "caller": "test_missing_codes_array_returns_empty", + "callee": "patch", + "lineNumber": 156 + }, + { + "caller": "test_missing_codes_array_returns_empty", + "callee": "self._mock_response", + "lineNumber": 157 + }, + { + "caller": "test_missing_codes_array_returns_empty", + "callee": "LoincApiClient().search", + "lineNumber": 158 + }, + { + "caller": "test_missing_codes_array_returns_empty", + "callee": "LoincApiClient", + "lineNumber": 158 + }, + { + "caller": "test_network_error_returns_error_entry", + "callee": "patch", + "lineNumber": 163 + }, + { + "caller": "test_network_error_returns_error_entry", + "callee": "requests.RequestException", + "lineNumber": 163 + }, + { + "caller": "test_network_error_returns_error_entry", + "callee": "LoincApiClient().search", + "lineNumber": 164 + }, + { + "caller": "test_network_error_returns_error_entry", + "callee": "LoincApiClient", + "lineNumber": 164 + }, + { + "caller": "test_network_error_returns_error_entry", + "callee": "len", + "lineNumber": 166 + }, + { + "caller": "test_returns_json_for_ajax", + "callee": "patch", + "lineNumber": 178 + }, + { + "caller": "test_returns_json_for_ajax", + "callee": "client.get", + "lineNumber": 180 + }, + { + "caller": "test_returns_json_for_ajax", + "callee": "json.loads", + "lineNumber": 183 + }, + { + "caller": "test_returns_json_for_ajax", + "callee": "isinstance", + "lineNumber": 184 + }, + { + "caller": "test_empty_term_returns_empty_list", + "callee": "client.get", + "lineNumber": 188 + }, + { + "caller": "test_empty_term_returns_empty_list", + "callee": "json.loads", + "lineNumber": 190 + }, + { + "caller": "test_calls_client_with_term", + "callee": "patch", + "lineNumber": 193 + }, + { + "caller": "test_calls_client_with_term", + "callee": "client.get", + "lineNumber": 195 + }, + { + "caller": "test_calls_client_with_term", + "callee": "MockClient.return_value.search.assert_called_once_with", + "lineNumber": 197 + }, + { + "caller": "test_format_json_param_triggers_json_response", + "callee": "patch", + "lineNumber": 200 + }, + { + "caller": "test_format_json_param_triggers_json_response", + "callee": "client.get", + "lineNumber": 202 + }, + { + "caller": "test_format_json_param_triggers_json_response", + "callee": "r.content_type.startswith", + "lineNumber": 205 + } + ], + "metrics": { + "importCount": 1, + "exportCount": 2, + "functionCount": 0, + "classCount": 2 + } + } + ] +} \ No newline at end of file diff --git a/.ua/.trash-1785851745/tmp/ua-file-extract-results-4.json b/.ua/.trash-1785851745/tmp/ua-file-extract-results-4.json new file mode 100644 index 0000000..8288e29 --- /dev/null +++ b/.ua/.trash-1785851745/tmp/ua-file-extract-results-4.json @@ -0,0 +1,843 @@ +{ + "scriptCompleted": true, + "filesAnalyzed": 7, + "filesSkipped": [], + "analysisOutcomes": { + "structure": { + "succeeded": 7, + "failed": 0 + }, + "callGraph": { + "succeeded": 7, + "failed": 0, + "skipped": 0 + } + }, + "results": [ + { + "path": "app.py", + "language": "python", + "fileCategory": "code", + "totalLines": 59, + "nonEmptyLines": 45, + "functions": [ + { + "name": "_configure_logging", + "startLine": 10, + "endLine": 21, + "params": [] + }, + { + "name": "create_app", + "startLine": 24, + "endLine": 50, + "params": [ + "config_class" + ] + } + ], + "exports": [ + { + "name": "_configure_logging", + "line": 10, + "isDefault": false + }, + { + "name": "create_app", + "line": 24, + "isDefault": false + } + ], + "callGraph": [ + { + "caller": "_configure_logging", + "callee": "logging.getLogger", + "lineNumber": 16 + }, + { + "caller": "_configure_logging", + "callee": "logging.basicConfig", + "lineNumber": 18 + }, + { + "caller": "create_app", + "callee": "_configure_logging", + "lineNumber": 25 + }, + { + "caller": "create_app", + "callee": "Flask", + "lineNumber": 26 + }, + { + "caller": "create_app", + "callee": "app.config.from_object", + "lineNumber": 27 + }, + { + "caller": "create_app", + "callee": "db.init_app", + "lineNumber": 29 + }, + { + "caller": "create_app", + "callee": "migrate.init_app", + "lineNumber": 30 + }, + { + "caller": "create_app", + "callee": "app.register_blueprint", + "lineNumber": 41 + }, + { + "caller": "create_app", + "callee": "app.register_blueprint", + "lineNumber": 42 + }, + { + "caller": "create_app", + "callee": "app.register_blueprint", + "lineNumber": 43 + }, + { + "caller": "create_app", + "callee": "app.register_blueprint", + "lineNumber": 44 + }, + { + "caller": "create_app", + "callee": "app.register_blueprint", + "lineNumber": 45 + }, + { + "caller": "create_app", + "callee": "app.register_blueprint", + "lineNumber": 46 + }, + { + "caller": "create_app", + "callee": "app.register_blueprint", + "lineNumber": 47 + }, + { + "caller": "create_app", + "callee": "app.register_blueprint", + "lineNumber": 48 + } + ], + "metrics": { + "importCount": 2, + "exportCount": 2, + "functionCount": 2, + "classCount": 0 + } + }, + { + "path": "config.py", + "language": "python", + "fileCategory": "code", + "totalLines": 14, + "nonEmptyLines": 12, + "classes": [ + { + "name": "Config", + "startLine": 4, + "endLine": 14, + "methods": [], + "properties": [] + } + ], + "exports": [ + { + "name": "Config", + "line": 4, + "isDefault": false + } + ], + "metrics": { + "importCount": 0, + "exportCount": 1, + "functionCount": 0, + "classCount": 1 + } + }, + { + "path": "db_bootstrap.py", + "language": "python", + "fileCategory": "code", + "totalLines": 71, + "nonEmptyLines": 55, + "functions": [ + { + "name": "_schema_is_current", + "startLine": 31, + "endLine": 33, + "params": [ + "inspector" + ] + }, + { + "name": "ensure_db", + "startLine": 36, + "endLine": 71, + "params": [ + "app" + ] + } + ], + "exports": [ + { + "name": "_schema_is_current", + "line": 31, + "isDefault": false + }, + { + "name": "ensure_db", + "line": 36, + "isDefault": false + } + ], + "callGraph": [ + { + "caller": "_schema_is_current", + "callee": "inspector.get_columns", + "lineNumber": 32 + }, + { + "caller": "ensure_db", + "callee": "app.app_context", + "lineNumber": 48 + }, + { + "caller": "ensure_db", + "callee": "inspect", + "lineNumber": 49 + }, + { + "caller": "ensure_db", + "callee": "set", + "lineNumber": 50 + }, + { + "caller": "ensure_db", + "callee": "inspector.get_table_names", + "lineNumber": 50 + }, + { + "caller": "ensure_db", + "callee": "_schema_is_current", + "lineNumber": 53 + }, + { + "caller": "ensure_db", + "callee": "RuntimeError", + "lineNumber": 54 + }, + { + "caller": "ensure_db", + "callee": "logger.info", + "lineNumber": 61 + }, + { + "caller": "ensure_db", + "callee": "stamp", + "lineNumber": 62 + }, + { + "caller": "ensure_db", + "callee": "db.session.execute(text(\"SELECT version_num FROM alembic_version\")).scalar", + "lineNumber": 64 + }, + { + "caller": "ensure_db", + "callee": "db.session.execute", + "lineNumber": 64 + }, + { + "caller": "ensure_db", + "callee": "text", + "lineNumber": 64 + }, + { + "caller": "ensure_db", + "callee": "logger.info", + "lineNumber": 66 + }, + { + "caller": "ensure_db", + "callee": "db.session.execute", + "lineNumber": 67 + }, + { + "caller": "ensure_db", + "callee": "text", + "lineNumber": 67 + }, + { + "caller": "ensure_db", + "callee": "db.session.commit", + "lineNumber": 68 + }, + { + "caller": "ensure_db", + "callee": "stamp", + "lineNumber": 69 + }, + { + "caller": "ensure_db", + "callee": "upgrade", + "lineNumber": 71 + } + ], + "metrics": { + "importCount": 1, + "exportCount": 2, + "functionCount": 2, + "classCount": 0 + } + }, + { + "path": "extensions.py", + "language": "python", + "fileCategory": "code", + "totalLines": 25, + "nonEmptyLines": 20, + "functions": [ + { + "name": "_set_sqlite_pragmas", + "startLine": 13, + "endLine": 25, + "params": [ + "dbapi_connection", + "connection_record" + ] + } + ], + "exports": [ + { + "name": "_set_sqlite_pragmas", + "line": 12, + "isDefault": false + } + ], + "callGraph": [ + { + "caller": "_set_sqlite_pragmas", + "callee": "isinstance", + "lineNumber": 21 + }, + { + "caller": "_set_sqlite_pragmas", + "callee": "dbapi_connection.cursor", + "lineNumber": 22 + }, + { + "caller": "_set_sqlite_pragmas", + "callee": "cursor.execute", + "lineNumber": 23 + }, + { + "caller": "_set_sqlite_pragmas", + "callee": "cursor.execute", + "lineNumber": 24 + }, + { + "caller": "_set_sqlite_pragmas", + "callee": "cursor.close", + "lineNumber": 25 + } + ], + "metrics": { + "importCount": 0, + "exportCount": 1, + "functionCount": 1, + "classCount": 0 + } + }, + { + "path": "models/__init__.py", + "language": "python", + "fileCategory": "code", + "totalLines": 1, + "nonEmptyLines": 1, + "metrics": { + "importCount": 1, + "exportCount": 0, + "functionCount": 0, + "classCount": 0 + } + }, + { + "path": "tests/test_dashboard.py", + "language": "python", + "fileCategory": "code", + "totalLines": 75, + "nonEmptyLines": 64, + "functions": [ + { + "name": "_mock_client", + "startLine": 17, + "endLine": 21, + "params": [ + "bcs", + "specs" + ] + } + ], + "classes": [ + { + "name": "TestDashboardIndex", + "startLine": 24, + "endLine": 75, + "methods": [ + "test_renders_with_api_data", + "test_renders_when_api_errors", + "test_renders_with_empty_api_results", + "test_local_stats_reflect_db", + "test_both_api_calls_made_concurrently" + ], + "properties": [] + } + ], + "exports": [ + { + "name": "_mock_client", + "line": 17, + "isDefault": false + }, + { + "name": "TestDashboardIndex", + "line": 24, + "isDefault": false + } + ], + "callGraph": [ + { + "caller": "_mock_client", + "callee": "MagicMock", + "lineNumber": 18 + }, + { + "caller": "test_renders_with_api_data", + "callee": "patch", + "lineNumber": 26 + }, + { + "caller": "test_renders_with_api_data", + "callee": "_mock_client", + "lineNumber": 27 + }, + { + "caller": "test_renders_with_api_data", + "callee": "client.get", + "lineNumber": 28 + }, + { + "caller": "test_renders_when_api_errors", + "callee": "patch", + "lineNumber": 34 + }, + { + "caller": "test_renders_when_api_errors", + "callee": "_mock_client", + "lineNumber": 35 + }, + { + "caller": "test_renders_when_api_errors", + "callee": "client.get", + "lineNumber": 39 + }, + { + "caller": "test_renders_with_empty_api_results", + "callee": "patch", + "lineNumber": 44 + }, + { + "caller": "test_renders_with_empty_api_results", + "callee": "_mock_client", + "lineNumber": 45 + }, + { + "caller": "test_renders_with_empty_api_results", + "callee": "client.get", + "lineNumber": 46 + }, + { + "caller": "test_local_stats_reflect_db", + "callee": "app.app_context", + "lineNumber": 50 + }, + { + "caller": "test_local_stats_reflect_db", + "callee": "db.session.add", + "lineNumber": 51 + }, + { + "caller": "test_local_stats_reflect_db", + "callee": "BiomedicalConcept", + "lineNumber": 52 + }, + { + "caller": "test_local_stats_reflect_db", + "callee": "db.session.commit", + "lineNumber": 59 + }, + { + "caller": "test_local_stats_reflect_db", + "callee": "patch", + "lineNumber": 60 + }, + { + "caller": "test_local_stats_reflect_db", + "callee": "_mock_client", + "lineNumber": 61 + }, + { + "caller": "test_local_stats_reflect_db", + "callee": "client.get", + "lineNumber": 62 + }, + { + "caller": "test_both_api_calls_made_concurrently", + "callee": "_mock_client", + "lineNumber": 70 + }, + { + "caller": "test_both_api_calls_made_concurrently", + "callee": "patch", + "lineNumber": 71 + }, + { + "caller": "test_both_api_calls_made_concurrently", + "callee": "client.get", + "lineNumber": 73 + } + ], + "metrics": { + "importCount": 2, + "exportCount": 2, + "functionCount": 1, + "classCount": 1 + } + }, + { + "path": "tests/test_db_bootstrap.py", + "language": "python", + "fileCategory": "code", + "totalLines": 87, + "nonEmptyLines": 71, + "functions": [ + { + "name": "_make_app", + "startLine": 20, + "endLine": 30, + "params": [ + "tmp_path", + "name" + ] + }, + { + "name": "_tables", + "startLine": 33, + "endLine": 35, + "params": [ + "app" + ] + }, + { + "name": "_stamped_revision", + "startLine": 38, + "endLine": 40, + "params": [ + "app" + ] + } + ], + "classes": [ + { + "name": "TestEnsureDb", + "startLine": 43, + "endLine": 87, + "methods": [ + "test_fresh_db_upgraded_from_baseline", + "test_legacy_create_all_db_is_stamped", + "test_pre_squash_stamped_db_is_restamped", + "test_outdated_schema_raises", + "test_idempotent_second_run" + ], + "properties": [] + } + ], + "exports": [ + { + "name": "_make_app", + "line": 20, + "isDefault": false + }, + { + "name": "_tables", + "line": 33, + "isDefault": false + }, + { + "name": "_stamped_revision", + "line": 38, + "isDefault": false + }, + { + "name": "TestEnsureDb", + "line": 43, + "isDefault": false + } + ], + "callGraph": [ + { + "caller": "_make_app", + "callee": "create_app", + "lineNumber": 30 + }, + { + "caller": "_tables", + "callee": "app.app_context", + "lineNumber": 34 + }, + { + "caller": "_tables", + "callee": "set", + "lineNumber": 35 + }, + { + "caller": "_tables", + "callee": "inspect(db.engine).get_table_names", + "lineNumber": 35 + }, + { + "caller": "_tables", + "callee": "inspect", + "lineNumber": 35 + }, + { + "caller": "_stamped_revision", + "callee": "app.app_context", + "lineNumber": 39 + }, + { + "caller": "_stamped_revision", + "callee": "db.session.execute(text(\"SELECT version_num FROM alembic_version\")).scalar", + "lineNumber": 40 + }, + { + "caller": "_stamped_revision", + "callee": "db.session.execute", + "lineNumber": 40 + }, + { + "caller": "_stamped_revision", + "callee": "text", + "lineNumber": 40 + }, + { + "caller": "test_fresh_db_upgraded_from_baseline", + "callee": "_make_app", + "lineNumber": 45 + }, + { + "caller": "test_fresh_db_upgraded_from_baseline", + "callee": "ensure_db", + "lineNumber": 46 + }, + { + "caller": "test_fresh_db_upgraded_from_baseline", + "callee": "_tables", + "lineNumber": 47 + }, + { + "caller": "test_fresh_db_upgraded_from_baseline", + "callee": "_stamped_revision", + "lineNumber": 48 + }, + { + "caller": "test_legacy_create_all_db_is_stamped", + "callee": "_make_app", + "lineNumber": 51 + }, + { + "caller": "test_legacy_create_all_db_is_stamped", + "callee": "app.app_context", + "lineNumber": 52 + }, + { + "caller": "test_legacy_create_all_db_is_stamped", + "callee": "db.create_all", + "lineNumber": 53 + }, + { + "caller": "test_legacy_create_all_db_is_stamped", + "callee": "_tables", + "lineNumber": 54 + }, + { + "caller": "test_legacy_create_all_db_is_stamped", + "callee": "ensure_db", + "lineNumber": 55 + }, + { + "caller": "test_legacy_create_all_db_is_stamped", + "callee": "_tables", + "lineNumber": 56 + }, + { + "caller": "test_legacy_create_all_db_is_stamped", + "callee": "_stamped_revision", + "lineNumber": 57 + }, + { + "caller": "test_pre_squash_stamped_db_is_restamped", + "callee": "_make_app", + "lineNumber": 60 + }, + { + "caller": "test_pre_squash_stamped_db_is_restamped", + "callee": "app.app_context", + "lineNumber": 61 + }, + { + "caller": "test_pre_squash_stamped_db_is_restamped", + "callee": "db.create_all", + "lineNumber": 62 + }, + { + "caller": "test_pre_squash_stamped_db_is_restamped", + "callee": "db.session.execute", + "lineNumber": 63 + }, + { + "caller": "test_pre_squash_stamped_db_is_restamped", + "callee": "text", + "lineNumber": 63 + }, + { + "caller": "test_pre_squash_stamped_db_is_restamped", + "callee": "db.session.execute", + "lineNumber": 64 + }, + { + "caller": "test_pre_squash_stamped_db_is_restamped", + "callee": "text", + "lineNumber": 64 + }, + { + "caller": "test_pre_squash_stamped_db_is_restamped", + "callee": "db.session.execute", + "lineNumber": 66 + }, + { + "caller": "test_pre_squash_stamped_db_is_restamped", + "callee": "text", + "lineNumber": 66 + }, + { + "caller": "test_pre_squash_stamped_db_is_restamped", + "callee": "db.session.commit", + "lineNumber": 67 + }, + { + "caller": "test_pre_squash_stamped_db_is_restamped", + "callee": "ensure_db", + "lineNumber": 68 + }, + { + "caller": "test_pre_squash_stamped_db_is_restamped", + "callee": "_stamped_revision", + "lineNumber": 69 + }, + { + "caller": "test_pre_squash_stamped_db_is_restamped", + "callee": "app.app_context", + "lineNumber": 70 + }, + { + "caller": "test_pre_squash_stamped_db_is_restamped", + "callee": "db.session.execute(text(\"SELECT count(*) FROM biomedical_concepts\")).scalar", + "lineNumber": 71 + }, + { + "caller": "test_pre_squash_stamped_db_is_restamped", + "callee": "db.session.execute", + "lineNumber": 71 + }, + { + "caller": "test_pre_squash_stamped_db_is_restamped", + "callee": "text", + "lineNumber": 71 + }, + { + "caller": "test_outdated_schema_raises", + "callee": "_make_app", + "lineNumber": 74 + }, + { + "caller": "test_outdated_schema_raises", + "callee": "app.app_context", + "lineNumber": 75 + }, + { + "caller": "test_outdated_schema_raises", + "callee": "db.session.execute", + "lineNumber": 77 + }, + { + "caller": "test_outdated_schema_raises", + "callee": "text", + "lineNumber": 77 + }, + { + "caller": "test_outdated_schema_raises", + "callee": "db.session.commit", + "lineNumber": 78 + }, + { + "caller": "test_outdated_schema_raises", + "callee": "pytest.raises", + "lineNumber": 79 + }, + { + "caller": "test_outdated_schema_raises", + "callee": "ensure_db", + "lineNumber": 80 + }, + { + "caller": "test_idempotent_second_run", + "callee": "_make_app", + "lineNumber": 83 + }, + { + "caller": "test_idempotent_second_run", + "callee": "ensure_db", + "lineNumber": 84 + }, + { + "caller": "test_idempotent_second_run", + "callee": "_stamped_revision", + "lineNumber": 85 + }, + { + "caller": "test_idempotent_second_run", + "callee": "ensure_db", + "lineNumber": 86 + }, + { + "caller": "test_idempotent_second_run", + "callee": "_stamped_revision", + "lineNumber": 87 + } + ], + "metrics": { + "importCount": 3, + "exportCount": 4, + "functionCount": 3, + "classCount": 1 + } + } + ] +} \ No newline at end of file diff --git a/.ua/.trash-1785851745/tmp/ua-file-extract-results-5.json b/.ua/.trash-1785851745/tmp/ua-file-extract-results-5.json new file mode 100644 index 0000000..f49623c --- /dev/null +++ b/.ua/.trash-1785851745/tmp/ua-file-extract-results-5.json @@ -0,0 +1,2688 @@ +{ + "scriptCompleted": true, + "filesAnalyzed": 5, + "filesSkipped": [], + "analysisOutcomes": { + "structure": { + "succeeded": 5, + "failed": 0 + }, + "callGraph": { + "succeeded": 5, + "failed": 0, + "skipped": 0 + } + }, + "results": [ + { + "path": "mcp_server/__main__.py", + "language": "python", + "fileCategory": "code", + "totalLines": 3, + "nonEmptyLines": 2, + "metrics": { + "importCount": 1, + "exportCount": 0, + "functionCount": 0, + "classCount": 0 + } + }, + { + "path": "mcp_server/server.py", + "language": "python", + "fileCategory": "code", + "totalLines": 572, + "nonEmptyLines": 486, + "functions": [ + { + "name": "_get_app", + "startLine": 49, + "endLine": 57, + "params": [] + }, + { + "name": "_with_app_context", + "startLine": 60, + "endLine": 72, + "params": [ + "fn" + ] + }, + { + "name": "list_tools", + "startLine": 276, + "endLine": 277, + "params": [] + }, + { + "name": "call_tool", + "startLine": 281, + "endLine": 284, + "params": [ + "name", + "arguments" + ] + }, + { + "name": "_dispatch", + "startLine": 292, + "endLine": 312, + "params": [ + "name", + "args" + ] + }, + { + "name": "_bc_full_dict", + "startLine": 320, + "endLine": 332, + "params": [ + "bc" + ] + }, + { + "name": "_list_bcs", + "startLine": 336, + "endLine": 356, + "params": [ + "args" + ] + }, + { + "name": "_get_bc", + "startLine": 360, + "endLine": 405, + "params": [ + "args" + ] + }, + { + "name": "_search_ncit", + "startLine": 409, + "endLine": 416, + "params": [ + "args" + ] + }, + { + "name": "_get_ncit_concept", + "startLine": 420, + "endLine": 426, + "params": [ + "args" + ] + }, + { + "name": "_search_loinc", + "startLine": 430, + "endLine": 437, + "params": [ + "args" + ] + }, + { + "name": "_search_cdisc_library", + "startLine": 441, + "endLine": 450, + "params": [ + "args" + ] + }, + { + "name": "_get_library_bc", + "startLine": 454, + "endLine": 460, + "params": [ + "args" + ] + }, + { + "name": "_list_review_queue", + "startLine": 464, + "endLine": 477, + "params": [ + "_args" + ] + }, + { + "name": "_create_bc", + "startLine": 486, + "endLine": 494, + "params": [ + "args" + ] + }, + { + "name": "_update_bc", + "startLine": 498, + "endLine": 507, + "params": [ + "args" + ] + }, + { + "name": "_map_ncit_to_bc", + "startLine": 511, + "endLine": 519, + "params": [ + "args" + ] + }, + { + "name": "_submit_bc_for_review", + "startLine": 523, + "endLine": 531, + "params": [ + "args" + ] + }, + { + "name": "_advance_governance", + "startLine": 535, + "endLine": 542, + "params": [ + "args" + ] + }, + { + "name": "_reject_bc", + "startLine": 546, + "endLine": 553, + "params": [ + "args" + ] + }, + { + "name": "main", + "startLine": 561, + "endLine": 563, + "params": [] + }, + { + "name": "_run", + "startLine": 566, + "endLine": 568, + "params": [] + } + ], + "exports": [ + { + "name": "_get_app", + "line": 49, + "isDefault": false + }, + { + "name": "_with_app_context", + "line": 60, + "isDefault": false + }, + { + "name": "list_tools", + "line": 275, + "isDefault": false + }, + { + "name": "call_tool", + "line": 280, + "isDefault": false + }, + { + "name": "_dispatch", + "line": 292, + "isDefault": false + }, + { + "name": "_bc_full_dict", + "line": 320, + "isDefault": false + }, + { + "name": "_list_bcs", + "line": 335, + "isDefault": false + }, + { + "name": "_get_bc", + "line": 359, + "isDefault": false + }, + { + "name": "_search_ncit", + "line": 408, + "isDefault": false + }, + { + "name": "_get_ncit_concept", + "line": 419, + "isDefault": false + }, + { + "name": "_search_loinc", + "line": 429, + "isDefault": false + }, + { + "name": "_search_cdisc_library", + "line": 440, + "isDefault": false + }, + { + "name": "_get_library_bc", + "line": 453, + "isDefault": false + }, + { + "name": "_list_review_queue", + "line": 463, + "isDefault": false + }, + { + "name": "_create_bc", + "line": 485, + "isDefault": false + }, + { + "name": "_update_bc", + "line": 497, + "isDefault": false + }, + { + "name": "_map_ncit_to_bc", + "line": 510, + "isDefault": false + }, + { + "name": "_submit_bc_for_review", + "line": 522, + "isDefault": false + }, + { + "name": "_advance_governance", + "line": 534, + "isDefault": false + }, + { + "name": "_reject_bc", + "line": 545, + "isDefault": false + }, + { + "name": "main", + "line": 561, + "isDefault": false + }, + { + "name": "_run", + "line": 566, + "isDefault": false + } + ], + "callGraph": [ + { + "caller": "_get_app", + "callee": "create_app", + "lineNumber": 55 + }, + { + "caller": "_get_app", + "callee": "ensure_db", + "lineNumber": 56 + }, + { + "caller": "_with_app_context", + "callee": "functools.wraps", + "lineNumber": 67 + }, + { + "caller": "wrapper", + "callee": "_get_app().app_context", + "lineNumber": 69 + }, + { + "caller": "wrapper", + "callee": "_get_app", + "lineNumber": 69 + }, + { + "caller": "wrapper", + "callee": "fn", + "lineNumber": 70 + }, + { + "caller": "call_tool", + "callee": "asyncio.get_event_loop", + "lineNumber": 282 + }, + { + "caller": "call_tool", + "callee": "loop.run_in_executor", + "lineNumber": 283 + }, + { + "caller": "call_tool", + "callee": "types.TextContent", + "lineNumber": 284 + }, + { + "caller": "call_tool", + "callee": "json.dumps", + "lineNumber": 284 + }, + { + "caller": "_dispatch", + "callee": "handlers.get", + "lineNumber": 309 + }, + { + "caller": "_dispatch", + "callee": "ValueError", + "lineNumber": 311 + }, + { + "caller": "_dispatch", + "callee": "fn", + "lineNumber": 312 + }, + { + "caller": "_bc_full_dict", + "callee": "bc.to_dict", + "lineNumber": 322 + }, + { + "caller": "_bc_full_dict", + "callee": "data.update", + "lineNumber": 323 + }, + { + "caller": "_list_bcs", + "callee": "str(args.get(\"q\") or \"\").strip", + "lineNumber": 339 + }, + { + "caller": "_list_bcs", + "callee": "str", + "lineNumber": 339 + }, + { + "caller": "_list_bcs", + "callee": "args.get", + "lineNumber": 339 + }, + { + "caller": "_list_bcs", + "callee": "str(args.get(\"status\") or \"\").strip", + "lineNumber": 340 + }, + { + "caller": "_list_bcs", + "callee": "str", + "lineNumber": 340 + }, + { + "caller": "_list_bcs", + "callee": "args.get", + "lineNumber": 340 + }, + { + "caller": "_list_bcs", + "callee": "max", + "lineNumber": 341 + }, + { + "caller": "_list_bcs", + "callee": "int", + "lineNumber": 341 + }, + { + "caller": "_list_bcs", + "callee": "args.get", + "lineNumber": 341 + }, + { + "caller": "_list_bcs", + "callee": "min", + "lineNumber": 342 + }, + { + "caller": "_list_bcs", + "callee": "max", + "lineNumber": 342 + }, + { + "caller": "_list_bcs", + "callee": "int", + "lineNumber": 342 + }, + { + "caller": "_list_bcs", + "callee": "args.get", + "lineNumber": 342 + }, + { + "caller": "_list_bcs", + "callee": "query.filter", + "lineNumber": 346 + }, + { + "caller": "_list_bcs", + "callee": "BiomedicalConcept.short_name.ilike", + "lineNumber": 346 + }, + { + "caller": "_list_bcs", + "callee": "BiomedicalConcept.bc_id.ilike", + "lineNumber": 346 + }, + { + "caller": "_list_bcs", + "callee": "BiomedicalConcept.ncit_code.ilike", + "lineNumber": 346 + }, + { + "caller": "_list_bcs", + "callee": "query.filter_by", + "lineNumber": 348 + }, + { + "caller": "_list_bcs", + "callee": "query.order_by(BiomedicalConcept.updated_at.desc()).paginate", + "lineNumber": 349 + }, + { + "caller": "_list_bcs", + "callee": "query.order_by", + "lineNumber": 349 + }, + { + "caller": "_list_bcs", + "callee": "BiomedicalConcept.updated_at.desc", + "lineNumber": 349 + }, + { + "caller": "_list_bcs", + "callee": "_bc_full_dict", + "lineNumber": 351 + }, + { + "caller": "_get_bc", + "callee": "str(args.get(\"bc_id\") or \"\").strip", + "lineNumber": 365 + }, + { + "caller": "_get_bc", + "callee": "str", + "lineNumber": 365 + }, + { + "caller": "_get_bc", + "callee": "args.get", + "lineNumber": 365 + }, + { + "caller": "_get_bc", + "callee": "ValueError", + "lineNumber": 367 + }, + { + "caller": "_get_bc", + "callee": "db.session.get", + "lineNumber": 368 + }, + { + "caller": "_get_bc", + "callee": "ValueError", + "lineNumber": 370 + }, + { + "caller": "_get_bc", + "callee": "DataElementConcept.query.filter_by(bc_id=bc_id).order_by(DataElementConcept.sort_order).all", + "lineNumber": 372 + }, + { + "caller": "_get_bc", + "callee": "DataElementConcept.query.filter_by(bc_id=bc_id).order_by", + "lineNumber": 372 + }, + { + "caller": "_get_bc", + "callee": "DataElementConcept.query.filter_by", + "lineNumber": 372 + }, + { + "caller": "_get_bc", + "callee": "_bc_full_dict", + "lineNumber": 373 + }, + { + "caller": "_get_bc", + "callee": "GovernanceRecord.query.filter_by(bc_id=bc_id).order_by(GovernanceRecord.id).all", + "lineNumber": 403 + }, + { + "caller": "_get_bc", + "callee": "GovernanceRecord.query.filter_by(bc_id=bc_id).order_by", + "lineNumber": 403 + }, + { + "caller": "_get_bc", + "callee": "GovernanceRecord.query.filter_by", + "lineNumber": 403 + }, + { + "caller": "_search_ncit", + "callee": "str(args.get(\"term\") or \"\").strip", + "lineNumber": 412 + }, + { + "caller": "_search_ncit", + "callee": "str", + "lineNumber": 412 + }, + { + "caller": "_search_ncit", + "callee": "args.get", + "lineNumber": 412 + }, + { + "caller": "_search_ncit", + "callee": "ValueError", + "lineNumber": 414 + }, + { + "caller": "_search_ncit", + "callee": "min", + "lineNumber": 415 + }, + { + "caller": "_search_ncit", + "callee": "max", + "lineNumber": 415 + }, + { + "caller": "_search_ncit", + "callee": "int", + "lineNumber": 415 + }, + { + "caller": "_search_ncit", + "callee": "args.get", + "lineNumber": 415 + }, + { + "caller": "_search_ncit", + "callee": "NCItApiClient().search_concept", + "lineNumber": 416 + }, + { + "caller": "_search_ncit", + "callee": "NCItApiClient", + "lineNumber": 416 + }, + { + "caller": "_get_ncit_concept", + "callee": "str(args.get(\"ncit_code\") or \"\").strip", + "lineNumber": 423 + }, + { + "caller": "_get_ncit_concept", + "callee": "str", + "lineNumber": 423 + }, + { + "caller": "_get_ncit_concept", + "callee": "args.get", + "lineNumber": 423 + }, + { + "caller": "_get_ncit_concept", + "callee": "ValueError", + "lineNumber": 425 + }, + { + "caller": "_get_ncit_concept", + "callee": "NCItApiClient().get_concept", + "lineNumber": 426 + }, + { + "caller": "_get_ncit_concept", + "callee": "NCItApiClient", + "lineNumber": 426 + }, + { + "caller": "_search_loinc", + "callee": "str(args.get(\"term\") or \"\").strip", + "lineNumber": 433 + }, + { + "caller": "_search_loinc", + "callee": "str", + "lineNumber": 433 + }, + { + "caller": "_search_loinc", + "callee": "args.get", + "lineNumber": 433 + }, + { + "caller": "_search_loinc", + "callee": "ValueError", + "lineNumber": 435 + }, + { + "caller": "_search_loinc", + "callee": "min", + "lineNumber": 436 + }, + { + "caller": "_search_loinc", + "callee": "max", + "lineNumber": 436 + }, + { + "caller": "_search_loinc", + "callee": "int", + "lineNumber": 436 + }, + { + "caller": "_search_loinc", + "callee": "args.get", + "lineNumber": 436 + }, + { + "caller": "_search_loinc", + "callee": "LoincApiClient().search", + "lineNumber": 437 + }, + { + "caller": "_search_loinc", + "callee": "LoincApiClient", + "lineNumber": 437 + }, + { + "caller": "_search_cdisc_library", + "callee": "str(args.get(\"q\") or \"\").strip().lower", + "lineNumber": 444 + }, + { + "caller": "_search_cdisc_library", + "callee": "str(args.get(\"q\") or \"\").strip", + "lineNumber": 444 + }, + { + "caller": "_search_cdisc_library", + "callee": "str", + "lineNumber": 444 + }, + { + "caller": "_search_cdisc_library", + "callee": "args.get", + "lineNumber": 444 + }, + { + "caller": "_search_cdisc_library", + "callee": "CDISCApiClient().get_biomedical_concepts", + "lineNumber": 445 + }, + { + "caller": "_search_cdisc_library", + "callee": "CDISCApiClient", + "lineNumber": 445 + }, + { + "caller": "_search_cdisc_library", + "callee": "(lnk.get(\"title\") or \"\").lower", + "lineNumber": 449 + }, + { + "caller": "_search_cdisc_library", + "callee": "lnk.get", + "lineNumber": 449 + }, + { + "caller": "_get_library_bc", + "callee": "str(args.get(\"concept_id\") or \"\").strip", + "lineNumber": 457 + }, + { + "caller": "_get_library_bc", + "callee": "str", + "lineNumber": 457 + }, + { + "caller": "_get_library_bc", + "callee": "args.get", + "lineNumber": 457 + }, + { + "caller": "_get_library_bc", + "callee": "ValueError", + "lineNumber": 459 + }, + { + "caller": "_get_library_bc", + "callee": "CDISCApiClient().get_bc", + "lineNumber": 460 + }, + { + "caller": "_get_library_bc", + "callee": "CDISCApiClient", + "lineNumber": 460 + }, + { + "caller": "_list_review_queue", + "callee": "BiomedicalConcept.query.filter_by(status=status).order_by(BiomedicalConcept.updated_at.desc()).all", + "lineNumber": 470 + }, + { + "caller": "_list_review_queue", + "callee": "BiomedicalConcept.query.filter_by(status=status).order_by", + "lineNumber": 470 + }, + { + "caller": "_list_review_queue", + "callee": "BiomedicalConcept.query.filter_by", + "lineNumber": 470 + }, + { + "caller": "_list_review_queue", + "callee": "BiomedicalConcept.updated_at.desc", + "lineNumber": 470 + }, + { + "caller": "_list_review_queue", + "callee": "IngestionRecord.query.filter_by(status=\"pending\").count", + "lineNumber": 472 + }, + { + "caller": "_list_review_queue", + "callee": "IngestionRecord.query.filter_by", + "lineNumber": 472 + }, + { + "caller": "_create_bc", + "callee": "str", + "lineNumber": 489 + }, + { + "caller": "_create_bc", + "callee": "args.get", + "lineNumber": 489 + }, + { + "caller": "_create_bc", + "callee": "bc_service.create_bc", + "lineNumber": 490 + }, + { + "caller": "_create_bc", + "callee": "args.get", + "lineNumber": 491 + }, + { + "caller": "_create_bc", + "callee": "bc_service.save_decs", + "lineNumber": 493 + }, + { + "caller": "_create_bc", + "callee": "_get_bc.__wrapped__", + "lineNumber": 494 + }, + { + "caller": "_update_bc", + "callee": "str(args.get(\"bc_id\") or \"\").strip", + "lineNumber": 501 + }, + { + "caller": "_update_bc", + "callee": "str", + "lineNumber": 501 + }, + { + "caller": "_update_bc", + "callee": "args.get", + "lineNumber": 501 + }, + { + "caller": "_update_bc", + "callee": "ValueError", + "lineNumber": 503 + }, + { + "caller": "_update_bc", + "callee": "str", + "lineNumber": 504 + }, + { + "caller": "_update_bc", + "callee": "args.get", + "lineNumber": 504 + }, + { + "caller": "_update_bc", + "callee": "bc_service.update_bc", + "lineNumber": 506 + }, + { + "caller": "_update_bc", + "callee": "_bc_full_dict", + "lineNumber": 507 + }, + { + "caller": "_map_ncit_to_bc", + "callee": "str(args.get(\"bc_id\") or \"\").strip", + "lineNumber": 514 + }, + { + "caller": "_map_ncit_to_bc", + "callee": "str", + "lineNumber": 514 + }, + { + "caller": "_map_ncit_to_bc", + "callee": "args.get", + "lineNumber": 514 + }, + { + "caller": "_map_ncit_to_bc", + "callee": "ValueError", + "lineNumber": 516 + }, + { + "caller": "_map_ncit_to_bc", + "callee": "str", + "lineNumber": 517 + }, + { + "caller": "_map_ncit_to_bc", + "callee": "args.get", + "lineNumber": 517 + }, + { + "caller": "_map_ncit_to_bc", + "callee": "bc_service.map_ncit_to_bc", + "lineNumber": 518 + }, + { + "caller": "_map_ncit_to_bc", + "callee": "args.get", + "lineNumber": 518 + }, + { + "caller": "_map_ncit_to_bc", + "callee": "_bc_full_dict", + "lineNumber": 519 + }, + { + "caller": "_submit_bc_for_review", + "callee": "str(args.get(\"bc_id\") or \"\").strip", + "lineNumber": 526 + }, + { + "caller": "_submit_bc_for_review", + "callee": "str", + "lineNumber": 526 + }, + { + "caller": "_submit_bc_for_review", + "callee": "args.get", + "lineNumber": 526 + }, + { + "caller": "_submit_bc_for_review", + "callee": "ValueError", + "lineNumber": 528 + }, + { + "caller": "_submit_bc_for_review", + "callee": "str", + "lineNumber": 529 + }, + { + "caller": "_submit_bc_for_review", + "callee": "args.get", + "lineNumber": 529 + }, + { + "caller": "_submit_bc_for_review", + "callee": "bc_service.submit_bc_for_review", + "lineNumber": 530 + }, + { + "caller": "_submit_bc_for_review", + "callee": "_bc_full_dict", + "lineNumber": 531 + }, + { + "caller": "_advance_governance", + "callee": "str(args.get(\"bc_id\") or \"\").strip", + "lineNumber": 538 + }, + { + "caller": "_advance_governance", + "callee": "str", + "lineNumber": 538 + }, + { + "caller": "_advance_governance", + "callee": "args.get", + "lineNumber": 538 + }, + { + "caller": "_advance_governance", + "callee": "ValueError", + "lineNumber": 540 + }, + { + "caller": "_advance_governance", + "callee": "str", + "lineNumber": 541 + }, + { + "caller": "_advance_governance", + "callee": "args.get", + "lineNumber": 541 + }, + { + "caller": "_advance_governance", + "callee": "governance_service.advance_governance", + "lineNumber": 542 + }, + { + "caller": "_advance_governance", + "callee": "str", + "lineNumber": 542 + }, + { + "caller": "_advance_governance", + "callee": "args.get", + "lineNumber": 542 + }, + { + "caller": "_reject_bc", + "callee": "str(args.get(\"bc_id\") or \"\").strip", + "lineNumber": 549 + }, + { + "caller": "_reject_bc", + "callee": "str", + "lineNumber": 549 + }, + { + "caller": "_reject_bc", + "callee": "args.get", + "lineNumber": 549 + }, + { + "caller": "_reject_bc", + "callee": "ValueError", + "lineNumber": 551 + }, + { + "caller": "_reject_bc", + "callee": "str", + "lineNumber": 552 + }, + { + "caller": "_reject_bc", + "callee": "args.get", + "lineNumber": 552 + }, + { + "caller": "_reject_bc", + "callee": "governance_service.reject_bc", + "lineNumber": 553 + }, + { + "caller": "_reject_bc", + "callee": "str", + "lineNumber": 553 + }, + { + "caller": "_reject_bc", + "callee": "args.get", + "lineNumber": 553 + }, + { + "caller": "main", + "callee": "logging.basicConfig", + "lineNumber": 562 + }, + { + "caller": "main", + "callee": "asyncio.run", + "lineNumber": 563 + }, + { + "caller": "main", + "callee": "_run", + "lineNumber": 563 + }, + { + "caller": "_run", + "callee": "stdio_server", + "lineNumber": 567 + }, + { + "caller": "_run", + "callee": "server.run", + "lineNumber": 568 + }, + { + "caller": "_run", + "callee": "server.create_initialization_options", + "lineNumber": 568 + } + ], + "metrics": { + "importCount": 0, + "exportCount": 22, + "functionCount": 22, + "classCount": 0 + } + }, + { + "path": "models/specialization.py", + "language": "python", + "fileCategory": "code", + "totalLines": 30, + "nonEmptyLines": 24, + "classes": [ + { + "name": "DatasetSpecialization", + "startLine": 6, + "endLine": 30, + "methods": [ + "variables", + "variables", + "to_dict" + ], + "properties": [] + } + ], + "exports": [ + { + "name": "DatasetSpecialization", + "line": 6, + "isDefault": false + } + ], + "callGraph": [ + { + "caller": "variables", + "callee": "json.loads", + "lineNumber": 17 + }, + { + "caller": "variables", + "callee": "json.dumps", + "lineNumber": 21 + } + ], + "metrics": { + "importCount": 1, + "exportCount": 1, + "functionCount": 0, + "classCount": 1 + } + }, + { + "path": "tests/test_mcp_server.py", + "language": "python", + "fileCategory": "code", + "totalLines": 315, + "nonEmptyLines": 253, + "functions": [ + { + "name": "inject_test_app", + "startLine": 21, + "endLine": 25, + "params": [ + "app" + ] + }, + { + "name": "_dispatch", + "startLine": 28, + "endLine": 29, + "params": [ + "name", + "args" + ] + }, + { + "name": "_audit_rows", + "startLine": 157, + "endLine": 159, + "params": [ + "app", + "action" + ] + } + ], + "classes": [ + { + "name": "TestDispatch", + "startLine": 32, + "endLine": 54, + "methods": [ + "test_unknown_tool_raises", + "test_all_declared_tools_are_dispatchable" + ], + "properties": [] + }, + { + "name": "TestListBcs", + "startLine": 57, + "endLine": 86, + "methods": [ + "test_empty_db", + "test_lists_and_filters", + "test_pagination" + ], + "properties": [] + }, + { + "name": "TestGetBc", + "startLine": 89, + "endLine": 109, + "methods": [ + "test_requires_bc_id", + "test_missing_bc_raises", + "test_full_detail" + ], + "properties": [] + }, + { + "name": "TestExternalApiTools", + "startLine": 112, + "endLine": 154, + "methods": [ + "test_search_ncit_requires_term", + "test_search_ncit", + "test_get_ncit_concept", + "test_search_loinc", + "test_search_cdisc_library_filters_by_title", + "test_search_cdisc_library_passes_error_through", + "test_get_library_bc" + ], + "properties": [] + }, + { + "name": "TestCreateBc", + "startLine": 162, + "endLine": 188, + "methods": [ + "test_creates_with_decs_and_audit", + "test_duplicate_raises", + "test_missing_bc_id_raises" + ], + "properties": [] + }, + { + "name": "TestUpdateBc", + "startLine": 191, + "endLine": 203, + "methods": [ + "test_updates_and_audits_before_after", + "test_missing_bc_raises" + ], + "properties": [] + }, + { + "name": "TestMapNcit", + "startLine": 206, + "endLine": 266, + "methods": [ + "test_maps_and_audits", + "test_promotes_import_id", + "test_empty_code_raises", + "test_promotes_import_id_repoints_dependents", + "test_promotes_import_id_repoints_child_bcs", + "test_promotes_import_id_collision_raises" + ], + "properties": [] + }, + { + "name": "TestGovernanceWrites", + "startLine": 269, + "endLine": 300, + "methods": [ + "test_submit_then_advance_to_published", + "test_reject_returns_to_provisional" + ], + "properties": [] + }, + { + "name": "TestReviewQueue", + "startLine": 303, + "endLine": 315, + "methods": [ + "test_queue_summary" + ], + "properties": [] + } + ], + "exports": [ + { + "name": "inject_test_app", + "line": 20, + "isDefault": false + }, + { + "name": "_dispatch", + "line": 28, + "isDefault": false + }, + { + "name": "TestDispatch", + "line": 32, + "isDefault": false + }, + { + "name": "TestListBcs", + "line": 57, + "isDefault": false + }, + { + "name": "TestGetBc", + "line": 89, + "isDefault": false + }, + { + "name": "TestExternalApiTools", + "line": 112, + "isDefault": false + }, + { + "name": "_audit_rows", + "line": 157, + "isDefault": false + }, + { + "name": "TestCreateBc", + "line": 162, + "isDefault": false + }, + { + "name": "TestUpdateBc", + "line": 191, + "isDefault": false + }, + { + "name": "TestMapNcit", + "line": 206, + "isDefault": false + }, + { + "name": "TestGovernanceWrites", + "line": 269, + "isDefault": false + }, + { + "name": "TestReviewQueue", + "line": 303, + "isDefault": false + } + ], + "callGraph": [ + { + "caller": "_dispatch", + "callee": "mcp_srv._dispatch", + "lineNumber": 29 + }, + { + "caller": "test_unknown_tool_raises", + "callee": "pytest.raises", + "lineNumber": 34 + }, + { + "caller": "test_unknown_tool_raises", + "callee": "_dispatch", + "lineNumber": 35 + }, + { + "caller": "test_empty_db", + "callee": "_dispatch", + "lineNumber": 59 + }, + { + "caller": "test_lists_and_filters", + "callee": "app.app_context", + "lineNumber": 63 + }, + { + "caller": "test_lists_and_filters", + "callee": "db.session.add", + "lineNumber": 64 + }, + { + "caller": "test_lists_and_filters", + "callee": "BiomedicalConcept", + "lineNumber": 64 + }, + { + "caller": "test_lists_and_filters", + "callee": "db.session.commit", + "lineNumber": 65 + }, + { + "caller": "test_lists_and_filters", + "callee": "_dispatch", + "lineNumber": 67 + }, + { + "caller": "test_lists_and_filters", + "callee": "_dispatch", + "lineNumber": 70 + }, + { + "caller": "test_lists_and_filters", + "callee": "_dispatch", + "lineNumber": 74 + }, + { + "caller": "test_pagination", + "callee": "app.app_context", + "lineNumber": 79 + }, + { + "caller": "test_pagination", + "callee": "range", + "lineNumber": 80 + }, + { + "caller": "test_pagination", + "callee": "db.session.add", + "lineNumber": 81 + }, + { + "caller": "test_pagination", + "callee": "BiomedicalConcept", + "lineNumber": 81 + }, + { + "caller": "test_pagination", + "callee": "db.session.commit", + "lineNumber": 82 + }, + { + "caller": "test_pagination", + "callee": "_dispatch", + "lineNumber": 83 + }, + { + "caller": "test_pagination", + "callee": "len", + "lineNumber": 85 + }, + { + "caller": "test_requires_bc_id", + "callee": "pytest.raises", + "lineNumber": 91 + }, + { + "caller": "test_requires_bc_id", + "callee": "_dispatch", + "lineNumber": 92 + }, + { + "caller": "test_missing_bc_raises", + "callee": "pytest.raises", + "lineNumber": 95 + }, + { + "caller": "test_missing_bc_raises", + "callee": "_dispatch", + "lineNumber": 96 + }, + { + "caller": "test_full_detail", + "callee": "app.app_context", + "lineNumber": 99 + }, + { + "caller": "test_full_detail", + "callee": "db.session.add", + "lineNumber": 100 + }, + { + "caller": "test_full_detail", + "callee": "DataElementConcept", + "lineNumber": 100 + }, + { + "caller": "test_full_detail", + "callee": "db.session.add", + "lineNumber": 101 + }, + { + "caller": "test_full_detail", + "callee": "DatasetSpecialization", + "lineNumber": 101 + }, + { + "caller": "test_full_detail", + "callee": "db.session.add", + "lineNumber": 102 + }, + { + "caller": "test_full_detail", + "callee": "GovernanceRecord", + "lineNumber": 102 + }, + { + "caller": "test_full_detail", + "callee": "db.session.commit", + "lineNumber": 103 + }, + { + "caller": "test_full_detail", + "callee": "_dispatch", + "lineNumber": 105 + }, + { + "caller": "test_search_ncit_requires_term", + "callee": "pytest.raises", + "lineNumber": 114 + }, + { + "caller": "test_search_ncit_requires_term", + "callee": "_dispatch", + "lineNumber": 115 + }, + { + "caller": "test_search_ncit", + "callee": "patch", + "lineNumber": 118 + }, + { + "caller": "test_search_ncit", + "callee": "_dispatch", + "lineNumber": 120 + }, + { + "caller": "test_search_ncit", + "callee": "mock_search.assert_called_once_with", + "lineNumber": 122 + }, + { + "caller": "test_get_ncit_concept", + "callee": "patch", + "lineNumber": 125 + }, + { + "caller": "test_get_ncit_concept", + "callee": "_dispatch", + "lineNumber": 127 + }, + { + "caller": "test_search_loinc", + "callee": "patch", + "lineNumber": 131 + }, + { + "caller": "test_search_loinc", + "callee": "_dispatch", + "lineNumber": 133 + }, + { + "caller": "test_search_cdisc_library_filters_by_title", + "callee": "patch", + "lineNumber": 141 + }, + { + "caller": "test_search_cdisc_library_filters_by_title", + "callee": "_dispatch", + "lineNumber": 142 + }, + { + "caller": "test_search_cdisc_library_filters_by_title", + "callee": "len", + "lineNumber": 143 + }, + { + "caller": "test_search_cdisc_library_passes_error_through", + "callee": "patch", + "lineNumber": 147 + }, + { + "caller": "test_search_cdisc_library_passes_error_through", + "callee": "_dispatch", + "lineNumber": 148 + }, + { + "caller": "test_get_library_bc", + "callee": "patch", + "lineNumber": 152 + }, + { + "caller": "test_get_library_bc", + "callee": "_dispatch", + "lineNumber": 153 + }, + { + "caller": "_audit_rows", + "callee": "app.app_context", + "lineNumber": 158 + }, + { + "caller": "_audit_rows", + "callee": "AuditLog.query.filter_by(action=action).all", + "lineNumber": 159 + }, + { + "caller": "_audit_rows", + "callee": "AuditLog.query.filter_by", + "lineNumber": 159 + }, + { + "caller": "test_creates_with_decs_and_audit", + "callee": "_dispatch", + "lineNumber": 164 + }, + { + "caller": "test_creates_with_decs_and_audit", + "callee": "_audit_rows", + "lineNumber": 177 + }, + { + "caller": "test_creates_with_decs_and_audit", + "callee": "len", + "lineNumber": 178 + }, + { + "caller": "test_duplicate_raises", + "callee": "pytest.raises", + "lineNumber": 183 + }, + { + "caller": "test_duplicate_raises", + "callee": "_dispatch", + "lineNumber": 184 + }, + { + "caller": "test_missing_bc_id_raises", + "callee": "pytest.raises", + "lineNumber": 187 + }, + { + "caller": "test_missing_bc_id_raises", + "callee": "_dispatch", + "lineNumber": 188 + }, + { + "caller": "test_updates_and_audits_before_after", + "callee": "_dispatch", + "lineNumber": 193 + }, + { + "caller": "test_updates_and_audits_before_after", + "callee": "_audit_rows", + "lineNumber": 195 + }, + { + "caller": "test_updates_and_audits_before_after", + "callee": "len", + "lineNumber": 196 + }, + { + "caller": "test_missing_bc_raises", + "callee": "pytest.raises", + "lineNumber": 202 + }, + { + "caller": "test_missing_bc_raises", + "callee": "_dispatch", + "lineNumber": 203 + }, + { + "caller": "test_maps_and_audits", + "callee": "_dispatch", + "lineNumber": 208 + }, + { + "caller": "test_maps_and_audits", + "callee": "_audit_rows", + "lineNumber": 210 + }, + { + "caller": "test_maps_and_audits", + "callee": "len", + "lineNumber": 211 + }, + { + "caller": "test_promotes_import_id", + "callee": "app.app_context", + "lineNumber": 215 + }, + { + "caller": "test_promotes_import_id", + "callee": "db.session.add", + "lineNumber": 216 + }, + { + "caller": "test_promotes_import_id", + "callee": "BiomedicalConcept", + "lineNumber": 216 + }, + { + "caller": "test_promotes_import_id", + "callee": "db.session.commit", + "lineNumber": 217 + }, + { + "caller": "test_promotes_import_id", + "callee": "_dispatch", + "lineNumber": 218 + }, + { + "caller": "test_promotes_import_id", + "callee": "app.app_context", + "lineNumber": 220 + }, + { + "caller": "test_promotes_import_id", + "callee": "db.session.get", + "lineNumber": 221 + }, + { + "caller": "test_promotes_import_id", + "callee": "db.session.get", + "lineNumber": 222 + }, + { + "caller": "test_empty_code_raises", + "callee": "pytest.raises", + "lineNumber": 225 + }, + { + "caller": "test_empty_code_raises", + "callee": "_dispatch", + "lineNumber": 226 + }, + { + "caller": "test_promotes_import_id_repoints_dependents", + "callee": "app.app_context", + "lineNumber": 229 + }, + { + "caller": "test_promotes_import_id_repoints_dependents", + "callee": "db.session.add", + "lineNumber": 230 + }, + { + "caller": "test_promotes_import_id_repoints_dependents", + "callee": "BiomedicalConcept", + "lineNumber": 230 + }, + { + "caller": "test_promotes_import_id_repoints_dependents", + "callee": "db.session.add", + "lineNumber": 231 + }, + { + "caller": "test_promotes_import_id_repoints_dependents", + "callee": "DataElementConcept", + "lineNumber": 231 + }, + { + "caller": "test_promotes_import_id_repoints_dependents", + "callee": "db.session.add", + "lineNumber": 232 + }, + { + "caller": "test_promotes_import_id_repoints_dependents", + "callee": "DatasetSpecialization", + "lineNumber": 232 + }, + { + "caller": "test_promotes_import_id_repoints_dependents", + "callee": "db.session.add", + "lineNumber": 233 + }, + { + "caller": "test_promotes_import_id_repoints_dependents", + "callee": "GovernanceRecord", + "lineNumber": 233 + }, + { + "caller": "test_promotes_import_id_repoints_dependents", + "callee": "db.session.commit", + "lineNumber": 234 + }, + { + "caller": "test_promotes_import_id_repoints_dependents", + "callee": "_dispatch", + "lineNumber": 236 + }, + { + "caller": "test_promotes_import_id_repoints_dependents", + "callee": "app.app_context", + "lineNumber": 239 + }, + { + "caller": "test_promotes_import_id_repoints_dependents", + "callee": "db.session.get", + "lineNumber": 240 + }, + { + "caller": "test_promotes_import_id_repoints_dependents", + "callee": "db.session.get", + "lineNumber": 241 + }, + { + "caller": "test_promotes_import_id_repoints_child_bcs", + "callee": "app.app_context", + "lineNumber": 248 + }, + { + "caller": "test_promotes_import_id_repoints_child_bcs", + "callee": "db.session.add", + "lineNumber": 249 + }, + { + "caller": "test_promotes_import_id_repoints_child_bcs", + "callee": "BiomedicalConcept", + "lineNumber": 249 + }, + { + "caller": "test_promotes_import_id_repoints_child_bcs", + "callee": "db.session.add", + "lineNumber": 250 + }, + { + "caller": "test_promotes_import_id_repoints_child_bcs", + "callee": "BiomedicalConcept", + "lineNumber": 250 + }, + { + "caller": "test_promotes_import_id_repoints_child_bcs", + "callee": "db.session.commit", + "lineNumber": 251 + }, + { + "caller": "test_promotes_import_id_repoints_child_bcs", + "callee": "_dispatch", + "lineNumber": 253 + }, + { + "caller": "test_promotes_import_id_repoints_child_bcs", + "callee": "app.app_context", + "lineNumber": 255 + }, + { + "caller": "test_promotes_import_id_repoints_child_bcs", + "callee": "db.session.get", + "lineNumber": 256 + }, + { + "caller": "test_promotes_import_id_collision_raises", + "callee": "app.app_context", + "lineNumber": 260 + }, + { + "caller": "test_promotes_import_id_collision_raises", + "callee": "db.session.add", + "lineNumber": 261 + }, + { + "caller": "test_promotes_import_id_collision_raises", + "callee": "BiomedicalConcept", + "lineNumber": 261 + }, + { + "caller": "test_promotes_import_id_collision_raises", + "callee": "db.session.add", + "lineNumber": 262 + }, + { + "caller": "test_promotes_import_id_collision_raises", + "callee": "BiomedicalConcept", + "lineNumber": 262 + }, + { + "caller": "test_promotes_import_id_collision_raises", + "callee": "db.session.commit", + "lineNumber": 263 + }, + { + "caller": "test_promotes_import_id_collision_raises", + "callee": "pytest.raises", + "lineNumber": 265 + }, + { + "caller": "test_promotes_import_id_collision_raises", + "callee": "_dispatch", + "lineNumber": 266 + }, + { + "caller": "test_submit_then_advance_to_published", + "callee": "_dispatch", + "lineNumber": 271 + }, + { + "caller": "test_submit_then_advance_to_published", + "callee": "_dispatch", + "lineNumber": 274 + }, + { + "caller": "test_submit_then_advance_to_published", + "callee": "_dispatch", + "lineNumber": 277 + }, + { + "caller": "test_submit_then_advance_to_published", + "callee": "_dispatch", + "lineNumber": 280 + }, + { + "caller": "test_submit_then_advance_to_published", + "callee": "app.app_context", + "lineNumber": 284 + }, + { + "caller": "test_submit_then_advance_to_published", + "callee": "GovernanceRecord.query.filter_by(bc_id=sample_bc).order_by(GovernanceRecord.id).all", + "lineNumber": 285 + }, + { + "caller": "test_submit_then_advance_to_published", + "callee": "GovernanceRecord.query.filter_by(bc_id=sample_bc).order_by", + "lineNumber": 285 + }, + { + "caller": "test_submit_then_advance_to_published", + "callee": "GovernanceRecord.query.filter_by", + "lineNumber": 285 + }, + { + "caller": "test_submit_then_advance_to_published", + "callee": "len", + "lineNumber": 289 + }, + { + "caller": "test_submit_then_advance_to_published", + "callee": "_audit_rows", + "lineNumber": 289 + }, + { + "caller": "test_submit_then_advance_to_published", + "callee": "len", + "lineNumber": 290 + }, + { + "caller": "test_submit_then_advance_to_published", + "callee": "_audit_rows", + "lineNumber": 290 + }, + { + "caller": "test_reject_returns_to_provisional", + "callee": "_dispatch", + "lineNumber": 293 + }, + { + "caller": "test_reject_returns_to_provisional", + "callee": "_dispatch", + "lineNumber": 294 + }, + { + "caller": "test_reject_returns_to_provisional", + "callee": "app.app_context", + "lineNumber": 296 + }, + { + "caller": "test_reject_returns_to_provisional", + "callee": "GovernanceRecord.query.filter_by(bc_id=sample_bc, action=\"rejected\").one", + "lineNumber": 297 + }, + { + "caller": "test_reject_returns_to_provisional", + "callee": "GovernanceRecord.query.filter_by", + "lineNumber": 297 + }, + { + "caller": "test_reject_returns_to_provisional", + "callee": "len", + "lineNumber": 300 + }, + { + "caller": "test_reject_returns_to_provisional", + "callee": "_audit_rows", + "lineNumber": 300 + }, + { + "caller": "test_queue_summary", + "callee": "app.app_context", + "lineNumber": 305 + }, + { + "caller": "test_queue_summary", + "callee": "db.session.get", + "lineNumber": 306 + }, + { + "caller": "test_queue_summary", + "callee": "db.session.add", + "lineNumber": 308 + }, + { + "caller": "test_queue_summary", + "callee": "IngestionRecord", + "lineNumber": 308 + }, + { + "caller": "test_queue_summary", + "callee": "db.session.add", + "lineNumber": 309 + }, + { + "caller": "test_queue_summary", + "callee": "IngestionRecord", + "lineNumber": 309 + }, + { + "caller": "test_queue_summary", + "callee": "db.session.commit", + "lineNumber": 310 + }, + { + "caller": "test_queue_summary", + "callee": "_dispatch", + "lineNumber": 312 + } + ], + "metrics": { + "importCount": 7, + "exportCount": 12, + "functionCount": 3, + "classCount": 9 + } + }, + { + "path": "tests/test_specializations_routes.py", + "language": "python", + "fileCategory": "code", + "totalLines": 260, + "nonEmptyLines": 225, + "functions": [ + { + "name": "_patch_client", + "startLine": 15, + "endLine": 21, + "params": [ + "**kwargs" + ] + } + ], + "classes": [ + { + "name": "TestIndex", + "startLine": 24, + "endLine": 40, + "methods": [ + "test_lists_specializations", + "test_index_survives_library_error" + ], + "properties": [] + }, + { + "name": "TestCreate", + "startLine": 43, + "endLine": 97, + "methods": [ + "test_create_persists_spec", + "test_create_requires_ids", + "test_create_writes_audit_log", + "test_create_persists_variable_rows_from_form" + ], + "properties": [] + }, + { + "name": "TestEditUpsert", + "startLine": 100, + "endLine": 141, + "methods": [ + "test_posting_existing_vlm_group_id_updates_instead_of_duplicating", + "test_edit_writes_updated_audit_log" + ], + "properties": [] + }, + { + "name": "TestDelete", + "startLine": 144, + "endLine": 172, + "methods": [ + "test_delete_removes_spec", + "test_delete_writes_audit_log", + "test_delete_unknown_404" + ], + "properties": [] + }, + { + "name": "TestDetail", + "startLine": 175, + "endLine": 189, + "methods": [ + "test_detail_renders", + "test_detail_404_for_unknown" + ], + "properties": [] + }, + { + "name": "TestLibraryDetail", + "startLine": 192, + "endLine": 204, + "methods": [ + "test_renders_library_spec", + "test_error_redirects_to_dashboard" + ], + "properties": [] + }, + { + "name": "TestGenerate", + "startLine": 207, + "endLine": 245, + "methods": [ + "_add_decs", + "test_generate_builds_variables_from_decs", + "test_generate_duplicate_is_rejected", + "test_generate_unknown_bc_404", + "test_generate_writes_audit_log" + ], + "properties": [] + }, + { + "name": "TestGenerateFromDec", + "startLine": 248, + "endLine": 260, + "methods": [ + "test_returns_variables_json", + "test_missing_bc_id_returns_400" + ], + "properties": [] + } + ], + "exports": [ + { + "name": "_patch_client", + "line": 15, + "isDefault": false + }, + { + "name": "TestIndex", + "line": 24, + "isDefault": false + }, + { + "name": "TestCreate", + "line": 43, + "isDefault": false + }, + { + "name": "TestEditUpsert", + "line": 100, + "isDefault": false + }, + { + "name": "TestDelete", + "line": 144, + "isDefault": false + }, + { + "name": "TestDetail", + "line": 175, + "isDefault": false + }, + { + "name": "TestLibraryDetail", + "line": 192, + "isDefault": false + }, + { + "name": "TestGenerate", + "line": 207, + "isDefault": false + }, + { + "name": "TestGenerateFromDec", + "line": 248, + "isDefault": false + } + ], + "callGraph": [ + { + "caller": "_patch_client", + "callee": "MagicMock", + "lineNumber": 17 + }, + { + "caller": "_patch_client", + "callee": "kwargs.get", + "lineNumber": 18 + }, + { + "caller": "_patch_client", + "callee": "kwargs.get", + "lineNumber": 19 + }, + { + "caller": "_patch_client", + "callee": "patch", + "lineNumber": 20 + }, + { + "caller": "test_lists_specializations", + "callee": "app.app_context", + "lineNumber": 26 + }, + { + "caller": "test_lists_specializations", + "callee": "DatasetSpecialization", + "lineNumber": 27 + }, + { + "caller": "test_lists_specializations", + "callee": "db.session.add", + "lineNumber": 28 + }, + { + "caller": "test_lists_specializations", + "callee": "db.session.commit", + "lineNumber": 29 + }, + { + "caller": "test_lists_specializations", + "callee": "_patch_client", + "lineNumber": 30 + }, + { + "caller": "test_lists_specializations", + "callee": "client.get", + "lineNumber": 32 + }, + { + "caller": "test_index_survives_library_error", + "callee": "_patch_client", + "lineNumber": 37 + }, + { + "caller": "test_index_survives_library_error", + "callee": "client.get", + "lineNumber": 39 + }, + { + "caller": "test_create_persists_spec", + "callee": "_patch_client", + "lineNumber": 45 + }, + { + "caller": "test_create_persists_spec", + "callee": "client.post", + "lineNumber": 47 + }, + { + "caller": "test_create_persists_spec", + "callee": "app.app_context", + "lineNumber": 52 + }, + { + "caller": "test_create_persists_spec", + "callee": "db.session.get", + "lineNumber": 53 + }, + { + "caller": "test_create_requires_ids", + "callee": "_patch_client", + "lineNumber": 59 + }, + { + "caller": "test_create_requires_ids", + "callee": "client.post", + "lineNumber": 61 + }, + { + "caller": "test_create_requires_ids", + "callee": "app.app_context", + "lineNumber": 63 + }, + { + "caller": "test_create_requires_ids", + "callee": "DatasetSpecialization.query.count", + "lineNumber": 64 + }, + { + "caller": "test_create_writes_audit_log", + "callee": "_patch_client", + "lineNumber": 67 + }, + { + "caller": "test_create_writes_audit_log", + "callee": "client.post", + "lineNumber": 69 + }, + { + "caller": "test_create_writes_audit_log", + "callee": "app.app_context", + "lineNumber": 73 + }, + { + "caller": "test_create_writes_audit_log", + "callee": "AuditLog.query.filter_by(entity_type=\"DatasetSpecialization\", entity_id=\"VLM1\", action=\"created\").first", + "lineNumber": 74 + }, + { + "caller": "test_create_writes_audit_log", + "callee": "AuditLog.query.filter_by", + "lineNumber": 74 + }, + { + "caller": "test_create_persists_variable_rows_from_form", + "callee": "_patch_client", + "lineNumber": 79 + }, + { + "caller": "test_create_persists_variable_rows_from_form", + "callee": "client.post", + "lineNumber": 81 + }, + { + "caller": "test_create_persists_variable_rows_from_form", + "callee": "app.app_context", + "lineNumber": 95 + }, + { + "caller": "test_create_persists_variable_rows_from_form", + "callee": "db.session.get", + "lineNumber": 96 + }, + { + "caller": "test_posting_existing_vlm_group_id_updates_instead_of_duplicating", + "callee": "app.app_context", + "lineNumber": 102 + }, + { + "caller": "test_posting_existing_vlm_group_id_updates_instead_of_duplicating", + "callee": "db.session.add", + "lineNumber": 103 + }, + { + "caller": "test_posting_existing_vlm_group_id_updates_instead_of_duplicating", + "callee": "DatasetSpecialization", + "lineNumber": 103 + }, + { + "caller": "test_posting_existing_vlm_group_id_updates_instead_of_duplicating", + "callee": "db.session.commit", + "lineNumber": 104 + }, + { + "caller": "test_posting_existing_vlm_group_id_updates_instead_of_duplicating", + "callee": "_patch_client", + "lineNumber": 105 + }, + { + "caller": "test_posting_existing_vlm_group_id_updates_instead_of_duplicating", + "callee": "client.post", + "lineNumber": 107 + }, + { + "caller": "test_posting_existing_vlm_group_id_updates_instead_of_duplicating", + "callee": "app.app_context", + "lineNumber": 120 + }, + { + "caller": "test_posting_existing_vlm_group_id_updates_instead_of_duplicating", + "callee": "DatasetSpecialization.query.count", + "lineNumber": 121 + }, + { + "caller": "test_posting_existing_vlm_group_id_updates_instead_of_duplicating", + "callee": "db.session.get", + "lineNumber": 122 + }, + { + "caller": "test_edit_writes_updated_audit_log", + "callee": "app.app_context", + "lineNumber": 128 + }, + { + "caller": "test_edit_writes_updated_audit_log", + "callee": "db.session.add", + "lineNumber": 129 + }, + { + "caller": "test_edit_writes_updated_audit_log", + "callee": "DatasetSpecialization", + "lineNumber": 129 + }, + { + "caller": "test_edit_writes_updated_audit_log", + "callee": "db.session.commit", + "lineNumber": 130 + }, + { + "caller": "test_edit_writes_updated_audit_log", + "callee": "_patch_client", + "lineNumber": 131 + }, + { + "caller": "test_edit_writes_updated_audit_log", + "callee": "client.post", + "lineNumber": 133 + }, + { + "caller": "test_edit_writes_updated_audit_log", + "callee": "app.app_context", + "lineNumber": 137 + }, + { + "caller": "test_edit_writes_updated_audit_log", + "callee": "AuditLog.query.filter_by(entity_type=\"DatasetSpecialization\", entity_id=\"VLM1\", action=\"updated\").first", + "lineNumber": 138 + }, + { + "caller": "test_edit_writes_updated_audit_log", + "callee": "AuditLog.query.filter_by", + "lineNumber": 138 + }, + { + "caller": "test_delete_removes_spec", + "callee": "app.app_context", + "lineNumber": 146 + }, + { + "caller": "test_delete_removes_spec", + "callee": "db.session.add", + "lineNumber": 147 + }, + { + "caller": "test_delete_removes_spec", + "callee": "DatasetSpecialization", + "lineNumber": 147 + }, + { + "caller": "test_delete_removes_spec", + "callee": "db.session.commit", + "lineNumber": 148 + }, + { + "caller": "test_delete_removes_spec", + "callee": "_patch_client", + "lineNumber": 149 + }, + { + "caller": "test_delete_removes_spec", + "callee": "client.post", + "lineNumber": 151 + }, + { + "caller": "test_delete_removes_spec", + "callee": "app.app_context", + "lineNumber": 153 + }, + { + "caller": "test_delete_removes_spec", + "callee": "db.session.get", + "lineNumber": 154 + }, + { + "caller": "test_delete_writes_audit_log", + "callee": "app.app_context", + "lineNumber": 157 + }, + { + "caller": "test_delete_writes_audit_log", + "callee": "db.session.add", + "lineNumber": 158 + }, + { + "caller": "test_delete_writes_audit_log", + "callee": "DatasetSpecialization", + "lineNumber": 158 + }, + { + "caller": "test_delete_writes_audit_log", + "callee": "db.session.commit", + "lineNumber": 159 + }, + { + "caller": "test_delete_writes_audit_log", + "callee": "_patch_client", + "lineNumber": 160 + }, + { + "caller": "test_delete_writes_audit_log", + "callee": "client.post", + "lineNumber": 162 + }, + { + "caller": "test_delete_writes_audit_log", + "callee": "app.app_context", + "lineNumber": 163 + }, + { + "caller": "test_delete_writes_audit_log", + "callee": "AuditLog.query.filter_by(entity_type=\"DatasetSpecialization\", entity_id=\"VLM1\", action=\"deleted\").first", + "lineNumber": 164 + }, + { + "caller": "test_delete_writes_audit_log", + "callee": "AuditLog.query.filter_by", + "lineNumber": 164 + }, + { + "caller": "test_delete_unknown_404", + "callee": "_patch_client", + "lineNumber": 169 + }, + { + "caller": "test_delete_unknown_404", + "callee": "client.post", + "lineNumber": 171 + }, + { + "caller": "test_detail_renders", + "callee": "app.app_context", + "lineNumber": 177 + }, + { + "caller": "test_detail_renders", + "callee": "db.session.add", + "lineNumber": 178 + }, + { + "caller": "test_detail_renders", + "callee": "DatasetSpecialization", + "lineNumber": 178 + }, + { + "caller": "test_detail_renders", + "callee": "db.session.commit", + "lineNumber": 179 + }, + { + "caller": "test_detail_renders", + "callee": "_patch_client", + "lineNumber": 180 + }, + { + "caller": "test_detail_renders", + "callee": "client.get", + "lineNumber": 182 + }, + { + "caller": "test_detail_404_for_unknown", + "callee": "_patch_client", + "lineNumber": 186 + }, + { + "caller": "test_detail_404_for_unknown", + "callee": "client.get", + "lineNumber": 188 + }, + { + "caller": "test_renders_library_spec", + "callee": "_patch_client", + "lineNumber": 194 + }, + { + "caller": "test_renders_library_spec", + "callee": "client.get", + "lineNumber": 196 + }, + { + "caller": "test_error_redirects_to_dashboard", + "callee": "_patch_client", + "lineNumber": 200 + }, + { + "caller": "test_error_redirects_to_dashboard", + "callee": "client.get", + "lineNumber": 202 + }, + { + "caller": "_add_decs", + "callee": "app.app_context", + "lineNumber": 209 + }, + { + "caller": "_add_decs", + "callee": "db.session.add_all", + "lineNumber": 210 + }, + { + "caller": "_add_decs", + "callee": "DataElementConcept", + "lineNumber": 212 + }, + { + "caller": "_add_decs", + "callee": "DataElementConcept", + "lineNumber": 213 + }, + { + "caller": "_add_decs", + "callee": "db.session.commit", + "lineNumber": 216 + }, + { + "caller": "test_generate_builds_variables_from_decs", + "callee": "self._add_decs", + "lineNumber": 219 + }, + { + "caller": "test_generate_builds_variables_from_decs", + "callee": "client.post", + "lineNumber": 220 + }, + { + "caller": "test_generate_builds_variables_from_decs", + "callee": "app.app_context", + "lineNumber": 222 + }, + { + "caller": "test_generate_builds_variables_from_decs", + "callee": "db.session.get", + "lineNumber": 223 + }, + { + "caller": "test_generate_duplicate_is_rejected", + "callee": "self._add_decs", + "lineNumber": 229 + }, + { + "caller": "test_generate_duplicate_is_rejected", + "callee": "client.post", + "lineNumber": 230 + }, + { + "caller": "test_generate_duplicate_is_rejected", + "callee": "client.post", + "lineNumber": 231 + }, + { + "caller": "test_generate_duplicate_is_rejected", + "callee": "app.app_context", + "lineNumber": 233 + }, + { + "caller": "test_generate_duplicate_is_rejected", + "callee": "DatasetSpecialization.query.count", + "lineNumber": 234 + }, + { + "caller": "test_generate_unknown_bc_404", + "callee": "client.post", + "lineNumber": 237 + }, + { + "caller": "test_generate_writes_audit_log", + "callee": "self._add_decs", + "lineNumber": 241 + }, + { + "caller": "test_generate_writes_audit_log", + "callee": "client.post", + "lineNumber": 242 + }, + { + "caller": "test_generate_writes_audit_log", + "callee": "app.app_context", + "lineNumber": 243 + }, + { + "caller": "test_generate_writes_audit_log", + "callee": "AuditLog.query.filter_by(entity_type=\"DatasetSpecialization\", entity_id=f\"{sample_bc}.SDTM\", action=\"created\").first", + "lineNumber": 244 + }, + { + "caller": "test_generate_writes_audit_log", + "callee": "AuditLog.query.filter_by", + "lineNumber": 244 + }, + { + "caller": "test_returns_variables_json", + "callee": "app.app_context", + "lineNumber": 250 + }, + { + "caller": "test_returns_variables_json", + "callee": "db.session.add", + "lineNumber": 251 + }, + { + "caller": "test_returns_variables_json", + "callee": "DataElementConcept", + "lineNumber": 251 + }, + { + "caller": "test_returns_variables_json", + "callee": "db.session.commit", + "lineNumber": 252 + }, + { + "caller": "test_returns_variables_json", + "callee": "client.post", + "lineNumber": 253 + }, + { + "caller": "test_returns_variables_json", + "callee": "resp.get_json", + "lineNumber": 255 + }, + { + "caller": "test_missing_bc_id_returns_400", + "callee": "client.post", + "lineNumber": 259 + } + ], + "metrics": { + "importCount": 4, + "exportCount": 9, + "functionCount": 1, + "classCount": 8 + } + } + ] +} \ No newline at end of file diff --git a/.ua/.trash-1785851745/tmp/ua-file-extract-results-6.json b/.ua/.trash-1785851745/tmp/ua-file-extract-results-6.json new file mode 100644 index 0000000..5678e62 --- /dev/null +++ b/.ua/.trash-1785851745/tmp/ua-file-extract-results-6.json @@ -0,0 +1,49 @@ +{ + "scriptCompleted": true, + "filesAnalyzed": 1, + "filesSkipped": [], + "analysisOutcomes": { + "structure": { + "succeeded": 1, + "failed": 0 + }, + "callGraph": { + "succeeded": 0, + "failed": 0, + "skipped": 1 + } + }, + "results": [ + { + "path": ".github/workflows/ci.yml", + "language": "yaml", + "fileCategory": "infra", + "totalLines": 26, + "nonEmptyLines": 24, + "sections": [ + { + "heading": "name", + "level": 1, + "line": 1 + }, + { + "heading": "on", + "level": 1, + "line": 3 + }, + { + "heading": "jobs", + "level": 1, + "line": 9 + } + ], + "metrics": { + "importCount": 0, + "exportCount": 0, + "functionCount": 0, + "classCount": 0, + "sectionCount": 3 + } + } + ] +} \ No newline at end of file diff --git a/.ua/.trash-1785851745/tmp/ua-file-extract-results-7.json b/.ua/.trash-1785851745/tmp/ua-file-extract-results-7.json new file mode 100644 index 0000000..a04f664 --- /dev/null +++ b/.ua/.trash-1785851745/tmp/ua-file-extract-results-7.json @@ -0,0 +1,60 @@ +{ + "scriptCompleted": true, + "filesAnalyzed": 3, + "filesSkipped": [], + "analysisOutcomes": { + "structure": { + "succeeded": 3, + "failed": 0 + }, + "callGraph": { + "succeeded": 0, + "failed": 0, + "skipped": 3 + } + }, + "results": [ + { + "path": ".claude/agent-memory/cdisc-frontend-dev/MEMORY.md", + "language": "markdown", + "fileCategory": "docs", + "totalLines": 2, + "nonEmptyLines": 2, + "metrics": { + "importCount": 0, + "exportCount": 0, + "functionCount": 0, + "classCount": 0, + "sectionCount": 0 + } + }, + { + "path": ".claude/agent-memory/cdisc-frontend-dev/frontend_architecture.md", + "language": "markdown", + "fileCategory": "docs", + "totalLines": 65, + "nonEmptyLines": 56, + "metrics": { + "importCount": 0, + "exportCount": 0, + "functionCount": 0, + "classCount": 0, + "sectionCount": 0 + } + }, + { + "path": ".claude/agent-memory/cdisc-frontend-dev/project_foundation.md", + "language": "markdown", + "fileCategory": "docs", + "totalLines": 54, + "nonEmptyLines": 42, + "metrics": { + "importCount": 0, + "exportCount": 0, + "functionCount": 0, + "classCount": 0, + "sectionCount": 0 + } + } + ] +} \ No newline at end of file diff --git a/.ua/.trash-1785851745/tmp/ua-file-extract-results-8.json b/.ua/.trash-1785851745/tmp/ua-file-extract-results-8.json new file mode 100644 index 0000000..ab0c53c --- /dev/null +++ b/.ua/.trash-1785851745/tmp/ua-file-extract-results-8.json @@ -0,0 +1,231 @@ +{ + "scriptCompleted": true, + "filesAnalyzed": 3, + "filesSkipped": [], + "analysisOutcomes": { + "structure": { + "succeeded": 3, + "failed": 0 + }, + "callGraph": { + "succeeded": 0, + "failed": 0, + "skipped": 3 + } + }, + "results": [ + { + "path": ".claude/agents/cdisc-concept-explorer.md", + "language": "markdown", + "fileCategory": "docs", + "totalLines": 67, + "nonEmptyLines": 59, + "sections": [ + { + "heading": "Your Core Mission", + "level": 2, + "line": 15 + }, + { + "heading": "Environment & API Access", + "level": 2, + "line": 21 + }, + { + "heading": "Project Integration Points", + "level": 2, + "line": 35 + }, + { + "heading": "CRITICAL: API-First Policy", + "level": 2, + "line": 44 + }, + { + "heading": "Output Format", + "level": 2, + "line": 53 + }, + { + "heading": "Search Results — candidate BCs with key details", + "level": 3, + "line": 54 + }, + { + "heading": "Recommendation — primary pick with rationale, alternatives with when-to-prefer", + "level": 3, + "line": 55 + }, + { + "heading": "Curation Notes — duplicate risk vs local drafts, metadata worth copying into the draft, deprecation flags", + "level": 3, + "line": 56 + }, + { + "heading": "Quality Standards", + "level": 2, + "line": 58 + } + ], + "metrics": { + "importCount": 0, + "exportCount": 0, + "functionCount": 0, + "classCount": 0, + "sectionCount": 9 + } + }, + { + "path": ".claude/agents/cdisc-frontend-dev.md", + "language": "markdown", + "fileCategory": "docs", + "totalLines": 207, + "nonEmptyLines": 160, + "sections": [ + { + "heading": "Persistent Agent Memory", + "level": 1, + "line": 74 + }, + { + "heading": "Types of memory", + "level": 2, + "line": 82 + }, + { + "heading": "What NOT to save in memory", + "level": 2, + "line": 146 + }, + { + "heading": "How to save memories", + "level": 2, + "line": 156 + }, + { + "heading": "When to access memories", + "level": 2, + "line": 180 + }, + { + "heading": "Before recommending from memory", + "level": 2, + "line": 186 + }, + { + "heading": "Memory and other forms of persistence", + "level": 2, + "line": 198 + }, + { + "heading": "MEMORY.md", + "level": 2, + "line": 205 + } + ], + "metrics": { + "importCount": 0, + "exportCount": 0, + "functionCount": 0, + "classCount": 0, + "sectionCount": 8 + } + }, + { + "path": ".claude/agents/mike.md", + "language": "markdown", + "fileCategory": "docs", + "totalLines": 108, + "nonEmptyLines": 86, + "sections": [ + { + "heading": "Your Responsibilities", + "level": 2, + "line": 14 + }, + { + "heading": "1. Progress Tracking", + "level": 3, + "line": 16 + }, + { + "heading": "2. Git Operations", + "level": 3, + "line": 22 + }, + { + "heading": "3. Documentation Management", + "level": 3, + "line": 27 + }, + { + "heading": "Project Structure Knowledge", + "level": 2, + "line": 33 + }, + { + "heading": "Progress & docs files", + "level": 3, + "line": 35 + }, + { + "heading": "Agent team", + "level": 3, + "line": 44 + }, + { + "heading": "App shape (for status summaries)", + "level": 3, + "line": 52 + }, + { + "heading": "README-PROGRESS.md Format", + "level": 2, + "line": 58 + }, + { + "heading": "Git Commit Guidelines", + "level": 2, + "line": 75 + }, + { + "heading": "Before Committing", + "level": 3, + "line": 77 + }, + { + "heading": "Commit Message Format", + "level": 3, + "line": 83 + }, + { + "heading": "Workflow Patterns", + "level": 2, + "line": 93 + }, + { + "heading": "After Work Session", + "level": 3, + "line": 95 + }, + { + "heading": "Always Ask Before", + "level": 3, + "line": 100 + }, + { + "heading": "Communication Style", + "level": 2, + "line": 105 + } + ], + "metrics": { + "importCount": 0, + "exportCount": 0, + "functionCount": 0, + "classCount": 0, + "sectionCount": 16 + } + } + ] +} \ No newline at end of file diff --git a/.ua/.trash-1785851745/tmp/ua-file-extract-results-9.json b/.ua/.trash-1785851745/tmp/ua-file-extract-results-9.json new file mode 100644 index 0000000..481bef1 --- /dev/null +++ b/.ua/.trash-1785851745/tmp/ua-file-extract-results-9.json @@ -0,0 +1,322 @@ +{ + "scriptCompleted": true, + "filesAnalyzed": 6, + "filesSkipped": [ + "requirements-dev.txt", + "requirements.txt" + ], + "analysisOutcomes": { + "structure": { + "succeeded": 6, + "failed": 0 + }, + "callGraph": { + "succeeded": 0, + "failed": 0, + "skipped": 6 + } + }, + "results": [ + { + "path": ".mcp.json", + "language": "json", + "fileCategory": "config", + "totalLines": 9, + "nonEmptyLines": 9, + "sections": [ + { + "heading": "mcpServers", + "level": 1, + "line": 2 + } + ], + "metrics": { + "importCount": 0, + "exportCount": 0, + "functionCount": 0, + "classCount": 0, + "sectionCount": 1 + } + }, + { + "path": ".pre-commit-config.yaml", + "language": "yaml", + "fileCategory": "config", + "totalLines": 24, + "nonEmptyLines": 21, + "sections": [ + { + "heading": "repos", + "level": 1, + "line": 1 + } + ], + "metrics": { + "importCount": 0, + "exportCount": 0, + "functionCount": 0, + "classCount": 0, + "sectionCount": 1 + } + }, + { + "path": "CLAUDE.md", + "language": "markdown", + "fileCategory": "docs", + "totalLines": 129, + "nonEmptyLines": 101, + "sections": [ + { + "heading": "CLAUDE.md", + "level": 1, + "line": 1 + }, + { + "heading": "What This App Does", + "level": 2, + "line": 5 + }, + { + "heading": "Running the App", + "level": 2, + "line": 9 + }, + { + "heading": "Linting", + "level": 2, + "line": 29 + }, + { + "heading": "Testing", + "level": 2, + "line": 42 + }, + { + "heading": "Architecture", + "level": 2, + "line": 56 + }, + { + "heading": "Config", + "level": 2, + "line": 109 + }, + { + "heading": "Conventions", + "level": 2, + "line": 125 + } + ], + "metrics": { + "importCount": 0, + "exportCount": 0, + "functionCount": 0, + "classCount": 0, + "sectionCount": 8 + } + }, + { + "path": "README-PROGRESS.md", + "language": "markdown", + "fileCategory": "docs", + "totalLines": 147, + "nonEmptyLines": 120, + "sections": [ + { + "heading": "Project Progress", + "level": 1, + "line": 1 + }, + { + "heading": "Overview", + "level": 2, + "line": 3 + }, + { + "heading": "Feature Status", + "level": 2, + "line": 7 + }, + { + "heading": "Daily Changelog", + "level": 2, + "line": 29 + }, + { + "heading": "2026-08-03", + "level": 3, + "line": 31 + }, + { + "heading": "Fixed New Copilot Comment on PR #39 — \"Code\" Column Still Blank in `export_xlsx()`", + "level": 4, + "line": 33 + }, + { + "heading": "Closed Out Workstream B.1 — Module-Level Loggers", + "level": 4, + "line": 40 + }, + { + "heading": "Fixed Broken Governance Export Fix-Up Commits from PR #39", + "level": 4, + "line": 46 + }, + { + "heading": "Fixed BC ID Promotion Orphaning Dependent Rows", + "level": 4, + "line": 54 + }, + { + "heading": "Resolved Remaining Copilot Review Comments on PR #39", + "level": 4, + "line": 61 + }, + { + "heading": "2026-04-15", + "level": 3, + "line": 69 + }, + { + "heading": "Audit Trail Coverage Completion + Specialization Delete Route", + "level": 4, + "line": 71 + }, + { + "heading": "2026-04-14", + "level": 3, + "line": 81 + }, + { + "heading": "LOINC API Explorer + BC Detail Performance + Specializations + Config", + "level": 4, + "line": 83 + }, + { + "heading": "2026-03-30", + "level": 3, + "line": 100 + }, + { + "heading": "Dashboard: API Integration + BC/Specialization Display", + "level": 4, + "line": 102 + }, + { + "heading": "2026-03-27", + "level": 3, + "line": 114 + } + ], + "metrics": { + "importCount": 0, + "exportCount": 0, + "functionCount": 0, + "classCount": 0, + "sectionCount": 17 + } + }, + { + "path": "README.md", + "language": "markdown", + "fileCategory": "docs", + "totalLines": 185, + "nonEmptyLines": 136, + "sections": [ + { + "heading": "CDISC Biomedical Concept Curation Platform", + "level": 1, + "line": 1 + }, + { + "heading": "Prerequisites", + "level": 2, + "line": 7 + }, + { + "heading": "Installation", + "level": 2, + "line": 14 + }, + { + "heading": "Dependencies installed", + "level": 3, + "line": 35 + }, + { + "heading": "Configuration", + "level": 2, + "line": 54 + }, + { + "heading": "Environment Variables", + "level": 3, + "line": 56 + }, + { + "heading": "Code Quality", + "level": 3, + "line": 75 + }, + { + "heading": "Running the App", + "level": 2, + "line": 86 + }, + { + "heading": "Running Tests", + "level": 2, + "line": 100 + }, + { + "heading": "Key Screens", + "level": 2, + "line": 122 + }, + { + "heading": "Project Structure", + "level": 2, + "line": 138 + }, + { + "heading": "External APIs", + "level": 2, + "line": 179 + } + ], + "metrics": { + "importCount": 0, + "exportCount": 0, + "functionCount": 0, + "classCount": 0, + "sectionCount": 12 + } + }, + { + "path": "pyproject.toml", + "language": "toml", + "fileCategory": "config", + "totalLines": 7, + "nonEmptyLines": 6, + "sections": [ + { + "heading": "tool.black", + "level": 2, + "line": 1 + }, + { + "heading": "tool.isort", + "level": 2, + "line": 5 + } + ], + "metrics": { + "importCount": 0, + "exportCount": 0, + "functionCount": 0, + "classCount": 0, + "sectionCount": 2 + } + } + ] +} \ No newline at end of file diff --git a/.ua/.trash-1785851745/tmp/ua-import-map-input.json b/.ua/.trash-1785851745/tmp/ua-import-map-input.json new file mode 100644 index 0000000..b5fccce --- /dev/null +++ b/.ua/.trash-1785851745/tmp/ua-import-map-input.json @@ -0,0 +1 @@ +{"projectRoot": "/Users/dmoreland/projects/cdisc-concept-curation", "files": [{"path": ".claude/agent-memory/cdisc-concept-explorer/MEMORY.md", "language": "markdown", "fileCategory": "docs"}, {"path": ".claude/agent-memory/cdisc-concept-explorer/reference_api_endpoints.md", "language": "markdown", "fileCategory": "docs"}, {"path": ".claude/agent-memory/cdisc-frontend-dev/MEMORY.md", "language": "markdown", "fileCategory": "docs"}, {"path": ".claude/agent-memory/cdisc-frontend-dev/frontend_architecture.md", "language": "markdown", "fileCategory": "docs"}, {"path": ".claude/agent-memory/cdisc-frontend-dev/project_foundation.md", "language": "markdown", "fileCategory": "docs"}, {"path": ".claude/agents/cdisc-concept-explorer.md", "language": "markdown", "fileCategory": "docs"}, {"path": ".claude/agents/cdisc-frontend-dev.md", "language": "markdown", "fileCategory": "docs"}, {"path": ".claude/agents/mike.md", "language": "markdown", "fileCategory": "docs"}, {"path": ".claude/skills/code-review/SKILL.md", "language": "markdown", "fileCategory": "docs"}, {"path": ".claude/skills/run-concept-curation/SKILL.md", "language": "markdown", "fileCategory": "docs"}, {"path": ".claude/skills/run-concept-curation/smoke.sh", "language": "shell", "fileCategory": "script"}, {"path": ".flake8", "language": "unknown", "fileCategory": "code"}, {"path": ".github/workflows/ci.yml", "language": "yaml", "fileCategory": "infra"}, {"path": ".hintrc", "language": "unknown", "fileCategory": "code"}, {"path": ".mcp.json", "language": "json", "fileCategory": "config"}, {"path": ".pre-commit-config.yaml", "language": "yaml", "fileCategory": "config"}, {"path": ".ua/.understandignore", "language": "unknown", "fileCategory": "code"}, {"path": ".ua/config.json", "language": "json", "fileCategory": "config"}, {"path": "CLAUDE.md", "language": "markdown", "fileCategory": "docs"}, {"path": "README-PROGRESS.md", "language": "markdown", "fileCategory": "docs"}, {"path": "README.md", "language": "markdown", "fileCategory": "docs"}, {"path": "app.py", "language": "python", "fileCategory": "code"}, {"path": "config.py", "language": "python", "fileCategory": "code"}, {"path": "db_bootstrap.py", "language": "python", "fileCategory": "code"}, {"path": "extensions.py", "language": "python", "fileCategory": "code"}, {"path": "files/BC Curation Principles and Completion GLs.xlsx", "language": "xlsx", "fileCategory": "code"}, {"path": "files/BC DEC Templates.xlsx", "language": "xlsx", "fileCategory": "code"}, {"path": "files/BC Examples.xlsx", "language": "xlsx", "fileCategory": "code"}, {"path": "files/BC Governance.docx", "language": "docx", "fileCategory": "code"}, {"path": "files/implementation.md", "language": "markdown", "fileCategory": "docs"}, {"path": "files/plan.md", "language": "markdown", "fileCategory": "docs"}, {"path": "files/result.md", "language": "markdown", "fileCategory": "docs"}, {"path": "mcp_server/__init__.py", "language": "python", "fileCategory": "code"}, {"path": "mcp_server/__main__.py", "language": "python", "fileCategory": "code"}, {"path": "mcp_server/server.py", "language": "python", "fileCategory": "code"}, {"path": "migrations/README", "language": "unknown", "fileCategory": "code"}, {"path": "migrations/alembic.ini", "language": "config", "fileCategory": "config"}, {"path": "migrations/env.py", "language": "python", "fileCategory": "code"}, {"path": "migrations/script.py.mako", "language": "mako", "fileCategory": "code"}, {"path": "migrations/versions/51d4a009d291_baseline_initial_schema.py", "language": "python", "fileCategory": "code"}, {"path": "models/__init__.py", "language": "python", "fileCategory": "code"}, {"path": "models/audit.py", "language": "python", "fileCategory": "code"}, {"path": "models/bc.py", "language": "python", "fileCategory": "code"}, {"path": "models/governance.py", "language": "python", "fileCategory": "code"}, {"path": "models/ingestion.py", "language": "python", "fileCategory": "code"}, {"path": "models/specialization.py", "language": "python", "fileCategory": "code"}, {"path": "pyproject.toml", "language": "toml", "fileCategory": "config"}, {"path": "requirements-dev.txt", "language": "txt", "fileCategory": "docs"}, {"path": "requirements.txt", "language": "txt", "fileCategory": "docs"}, {"path": "routes/__init__.py", "language": "python", "fileCategory": "code"}, {"path": "routes/audit.py", "language": "python", "fileCategory": "code"}, {"path": "routes/bc.py", "language": "python", "fileCategory": "code"}, {"path": "routes/dashboard.py", "language": "python", "fileCategory": "code"}, {"path": "routes/governance.py", "language": "python", "fileCategory": "code"}, {"path": "routes/ingestion.py", "language": "python", "fileCategory": "code"}, {"path": "routes/loinc.py", "language": "python", "fileCategory": "code"}, {"path": "routes/ncit.py", "language": "python", "fileCategory": "code"}, {"path": "routes/specializations.py", "language": "python", "fileCategory": "code"}, {"path": "services/__init__.py", "language": "python", "fileCategory": "code"}, {"path": "services/api_cache.py", "language": "python", "fileCategory": "code"}, {"path": "services/audit.py", "language": "python", "fileCategory": "code"}, {"path": "services/bc_service.py", "language": "python", "fileCategory": "code"}, {"path": "services/cdisc_api.py", "language": "python", "fileCategory": "code"}, {"path": "services/export.py", "language": "python", "fileCategory": "code"}, {"path": "services/governance_service.py", "language": "python", "fileCategory": "code"}, {"path": "services/ingestion.py", "language": "python", "fileCategory": "code"}, {"path": "services/loinc_api.py", "language": "python", "fileCategory": "code"}, {"path": "services/ncit_api.py", "language": "python", "fileCategory": "code"}, {"path": "static/css/custom.css", "language": "css", "fileCategory": "markup"}, {"path": "static/js/main.js", "language": "javascript", "fileCategory": "code"}, {"path": "templates/audit.html", "language": "html", "fileCategory": "markup"}, {"path": "templates/base.html", "language": "html", "fileCategory": "markup"}, {"path": "templates/bc_detail.html", "language": "html", "fileCategory": "markup"}, {"path": "templates/bc_list.html", "language": "html", "fileCategory": "markup"}, {"path": "templates/dashboard.html", "language": "html", "fileCategory": "markup"}, {"path": "templates/governance.html", "language": "html", "fileCategory": "markup"}, {"path": "templates/ingestion.html", "language": "html", "fileCategory": "markup"}, {"path": "templates/library_bc_detail.html", "language": "html", "fileCategory": "markup"}, {"path": "templates/library_spec_detail.html", "language": "html", "fileCategory": "markup"}, {"path": "templates/ncit_mapping.html", "language": "html", "fileCategory": "markup"}, {"path": "templates/specializations.html", "language": "html", "fileCategory": "markup"}, {"path": "tests/__init__.py", "language": "python", "fileCategory": "code"}, {"path": "tests/conftest.py", "language": "python", "fileCategory": "code"}, {"path": "tests/test_audit_routes.py", "language": "python", "fileCategory": "code"}, {"path": "tests/test_bc_routes.py", "language": "python", "fileCategory": "code"}, {"path": "tests/test_cdisc_api_cache.py", "language": "python", "fileCategory": "code"}, {"path": "tests/test_dashboard.py", "language": "python", "fileCategory": "code"}, {"path": "tests/test_db_bootstrap.py", "language": "python", "fileCategory": "code"}, {"path": "tests/test_export_service.py", "language": "python", "fileCategory": "code"}, {"path": "tests/test_governance_routes.py", "language": "python", "fileCategory": "code"}, {"path": "tests/test_ingestion_routes.py", "language": "python", "fileCategory": "code"}, {"path": "tests/test_ingestion_service.py", "language": "python", "fileCategory": "code"}, {"path": "tests/test_loinc.py", "language": "python", "fileCategory": "code"}, {"path": "tests/test_mcp_server.py", "language": "python", "fileCategory": "code"}, {"path": "tests/test_models.py", "language": "python", "fileCategory": "code"}, {"path": "tests/test_ncit.py", "language": "python", "fileCategory": "code"}, {"path": "tests/test_specializations_routes.py", "language": "python", "fileCategory": "code"}]} \ No newline at end of file diff --git a/.ua/.trash-1785851745/tmp/ua-import-map-output.json b/.ua/.trash-1785851745/tmp/ua-import-map-output.json new file mode 100644 index 0000000..eccc542 --- /dev/null +++ b/.ua/.trash-1785851745/tmp/ua-import-map-output.json @@ -0,0 +1,250 @@ +{ + "scriptCompleted": true, + "stats": { + "filesScanned": 97, + "filesWithImports": 38, + "totalEdges": 105 + }, + "importMap": { + ".claude/agent-memory/cdisc-concept-explorer/MEMORY.md": [], + ".claude/agent-memory/cdisc-concept-explorer/reference_api_endpoints.md": [], + ".claude/agent-memory/cdisc-frontend-dev/MEMORY.md": [], + ".claude/agent-memory/cdisc-frontend-dev/frontend_architecture.md": [], + ".claude/agent-memory/cdisc-frontend-dev/project_foundation.md": [], + ".claude/agents/cdisc-concept-explorer.md": [], + ".claude/agents/cdisc-frontend-dev.md": [], + ".claude/agents/mike.md": [], + ".claude/skills/code-review/SKILL.md": [], + ".claude/skills/run-concept-curation/SKILL.md": [], + ".claude/skills/run-concept-curation/smoke.sh": [], + ".flake8": [], + ".github/workflows/ci.yml": [], + ".hintrc": [], + ".mcp.json": [], + ".pre-commit-config.yaml": [], + ".ua/.understandignore": [], + ".ua/config.json": [], + "CLAUDE.md": [], + "README-PROGRESS.md": [], + "README.md": [], + "app.py": [ + "config.py", + "extensions.py" + ], + "config.py": [], + "db_bootstrap.py": [ + "extensions.py" + ], + "extensions.py": [], + "files/BC Curation Principles and Completion GLs.xlsx": [], + "files/BC DEC Templates.xlsx": [], + "files/BC Examples.xlsx": [], + "files/BC Governance.docx": [], + "files/implementation.md": [], + "files/plan.md": [], + "files/result.md": [], + "mcp_server/__init__.py": [], + "mcp_server/__main__.py": [ + "mcp_server/server.py" + ], + "mcp_server/server.py": [], + "migrations/README": [], + "migrations/alembic.ini": [], + "migrations/env.py": [], + "migrations/script.py.mako": [], + "migrations/versions/51d4a009d291_baseline_initial_schema.py": [], + "models/__init__.py": [ + "extensions.py" + ], + "models/audit.py": [ + "extensions.py" + ], + "models/bc.py": [ + "extensions.py" + ], + "models/governance.py": [ + "extensions.py" + ], + "models/ingestion.py": [ + "extensions.py" + ], + "models/specialization.py": [ + "extensions.py" + ], + "pyproject.toml": [], + "requirements-dev.txt": [], + "requirements.txt": [], + "routes/__init__.py": [], + "routes/audit.py": [ + "models/audit.py" + ], + "routes/bc.py": [ + "extensions.py", + "models/bc.py", + "models/governance.py", + "services/__init__.py", + "services/audit.py", + "services/bc_service.py", + "services/cdisc_api.py", + "services/export.py", + "services/loinc_api.py", + "services/ncit_api.py" + ], + "routes/dashboard.py": [ + "models/audit.py", + "models/bc.py", + "services/cdisc_api.py" + ], + "routes/governance.py": [ + "extensions.py", + "models/bc.py", + "services/__init__.py", + "services/export.py", + "services/governance_service.py" + ], + "routes/ingestion.py": [ + "extensions.py", + "models/audit.py", + "models/bc.py", + "models/ingestion.py", + "services/ingestion.py" + ], + "routes/loinc.py": [ + "services/loinc_api.py" + ], + "routes/ncit.py": [ + "extensions.py", + "models/bc.py", + "services/__init__.py", + "services/bc_service.py", + "services/ncit_api.py" + ], + "routes/specializations.py": [ + "extensions.py", + "models/bc.py", + "models/specialization.py", + "services/audit.py", + "services/cdisc_api.py" + ], + "services/__init__.py": [], + "services/api_cache.py": [], + "services/audit.py": [ + "extensions.py", + "models/audit.py" + ], + "services/bc_service.py": [ + "extensions.py", + "models/bc.py", + "models/governance.py", + "models/specialization.py", + "services/audit.py" + ], + "services/cdisc_api.py": [ + "services/api_cache.py" + ], + "services/export.py": [], + "services/governance_service.py": [ + "extensions.py", + "models/governance.py", + "services/audit.py", + "services/bc_service.py" + ], + "services/ingestion.py": [], + "services/loinc_api.py": [ + "services/api_cache.py" + ], + "services/ncit_api.py": [ + "services/api_cache.py" + ], + "static/css/custom.css": [], + "static/js/main.js": [], + "templates/audit.html": [], + "templates/base.html": [], + "templates/bc_detail.html": [], + "templates/bc_list.html": [], + "templates/dashboard.html": [], + "templates/governance.html": [], + "templates/ingestion.html": [], + "templates/library_bc_detail.html": [], + "templates/library_spec_detail.html": [], + "templates/ncit_mapping.html": [], + "templates/specializations.html": [], + "tests/__init__.py": [], + "tests/conftest.py": [ + "app.py", + "extensions.py", + "models/audit.py", + "models/bc.py", + "models/governance.py", + "models/ingestion.py" + ], + "tests/test_audit_routes.py": [ + "extensions.py", + "models/audit.py" + ], + "tests/test_bc_routes.py": [ + "extensions.py", + "models/audit.py", + "models/bc.py" + ], + "tests/test_cdisc_api_cache.py": [ + "services/cdisc_api.py" + ], + "tests/test_dashboard.py": [ + "extensions.py", + "models/bc.py" + ], + "tests/test_db_bootstrap.py": [ + "app.py", + "db_bootstrap.py", + "extensions.py" + ], + "tests/test_export_service.py": [ + "extensions.py", + "models/bc.py", + "services/export.py" + ], + "tests/test_governance_routes.py": [ + "extensions.py", + "models/audit.py", + "models/bc.py", + "models/governance.py" + ], + "tests/test_ingestion_routes.py": [ + "extensions.py", + "models/audit.py", + "models/bc.py", + "models/ingestion.py" + ], + "tests/test_ingestion_service.py": [ + "services/ingestion.py" + ], + "tests/test_loinc.py": [ + "services/loinc_api.py" + ], + "tests/test_mcp_server.py": [ + "extensions.py", + "mcp_server/server.py", + "models/audit.py", + "models/bc.py", + "models/governance.py", + "models/ingestion.py", + "models/specialization.py" + ], + "tests/test_models.py": [ + "extensions.py", + "models/audit.py", + "models/bc.py", + "models/ingestion.py" + ], + "tests/test_ncit.py": [ + "services/ncit_api.py" + ], + "tests/test_specializations_routes.py": [ + "extensions.py", + "models/audit.py", + "models/bc.py", + "models/specialization.py" + ] + } +} \ No newline at end of file diff --git a/.ua/.trash-1785851745/tmp/ua-inline-validate.cjs b/.ua/.trash-1785851745/tmp/ua-inline-validate.cjs new file mode 100644 index 0000000..49c388e --- /dev/null +++ b/.ua/.trash-1785851745/tmp/ua-inline-validate.cjs @@ -0,0 +1,113 @@ +#!/usr/bin/env node +const fs = require("fs"); +const graphPath = process.argv[2]; +const outputPath = process.argv[3]; +try { + const graph = JSON.parse(fs.readFileSync(graphPath, "utf8")); + const issues = [], + warnings = []; + if (!Array.isArray(graph.nodes)) { + issues.push("graph.nodes is missing or not an array"); + graph.nodes = []; + } + if (!Array.isArray(graph.edges)) { + issues.push("graph.edges is missing or not an array"); + graph.edges = []; + } + const nodeIds = new Set(); + const seen = new Map(); + graph.nodes.forEach((n, i) => { + if (!n.id) { + issues.push(`Node[${i}] missing id`); + return; + } + if (!n.type) issues.push(`Node[${i}] '${n.id}' missing type`); + if (!n.name) issues.push(`Node[${i}] '${n.id}' missing name`); + if (!n.summary) issues.push(`Node[${i}] '${n.id}' missing summary`); + if (!n.tags || !n.tags.length) + issues.push(`Node[${i}] '${n.id}' missing tags`); + if (seen.has(n.id)) + issues.push( + `Duplicate node ID '${n.id}' at indices ${seen.get(n.id)} and ${i}`, + ); + else seen.set(n.id, i); + nodeIds.add(n.id); + }); + graph.edges.forEach((e, i) => { + if (!nodeIds.has(e.source)) + issues.push(`Edge[${i}] source '${e.source}' not found`); + if (!nodeIds.has(e.target)) + issues.push(`Edge[${i}] target '${e.target}' not found`); + }); + const fileLevelTypes = new Set([ + "file", + "config", + "document", + "service", + "pipeline", + "table", + "schema", + "resource", + "endpoint", + ]); + const fileNodes = graph.nodes + .filter((n) => fileLevelTypes.has(n.type)) + .map((n) => n.id); + const assigned = new Map(); + if (!Array.isArray(graph.layers)) { + if (graph.layers) warnings.push("graph.layers is not an array"); + graph.layers = []; + } + if (!Array.isArray(graph.tour)) { + if (graph.tour) warnings.push("graph.tour is not an array"); + graph.tour = []; + } + graph.layers.forEach((layer) => { + (layer.nodeIds || []).forEach((id) => { + if (!nodeIds.has(id)) + issues.push(`Layer '${layer.id}' refs missing node '${id}'`); + if (assigned.has(id)) + issues.push(`Node '${id}' appears in multiple layers`); + assigned.set(id, layer.id); + }); + }); + fileNodes.forEach((id) => { + if (!assigned.has(id)) issues.push(`File node '${id}' not in any layer`); + }); + graph.tour.forEach((step, i) => { + (step.nodeIds || []).forEach((id) => { + if (!nodeIds.has(id)) + issues.push(`Tour step[${i}] refs missing node '${id}'`); + }); + }); + const withEdges = new Set([ + ...graph.edges.map((e) => e.source), + ...graph.edges.map((e) => e.target), + ]); + graph.nodes.forEach((n) => { + if (!withEdges.has(n.id)) + warnings.push(`Node '${n.id}' has no edges (orphan)`); + }); + const stats = { + totalNodes: graph.nodes.length, + totalEdges: graph.edges.length, + totalLayers: graph.layers.length, + tourSteps: graph.tour.length, + nodeTypes: graph.nodes.reduce((a, n) => { + a[n.type] = (a[n.type] || 0) + 1; + return a; + }, {}), + edgeTypes: graph.edges.reduce((a, e) => { + a[e.type] = (a[e.type] || 0) + 1; + return a; + }, {}), + }; + fs.writeFileSync( + outputPath, + JSON.stringify({ issues, warnings, stats }, null, 2), + ); + process.exit(0); +} catch (err) { + process.stderr.write(err.message + "\n"); + process.exit(1); +} diff --git a/.ua/.trash-1785851745/tmp/ua-scan-files.json b/.ua/.trash-1785851745/tmp/ua-scan-files.json new file mode 100644 index 0000000..e3c7264 --- /dev/null +++ b/.ua/.trash-1785851745/tmp/ua-scan-files.json @@ -0,0 +1,619 @@ +{ + "scriptCompleted": true, + "contentDigest": "0a9ba49c5bc042cece36d511f05493d3fdd446017db4e2daf65e679a7f4faa3f", + "files": [ + { + "path": ".claude/agent-memory/cdisc-concept-explorer/MEMORY.md", + "language": "markdown", + "sizeLines": 1, + "fileCategory": "docs" + }, + { + "path": ".claude/agent-memory/cdisc-concept-explorer/reference_api_endpoints.md", + "language": "markdown", + "sizeLines": 36, + "fileCategory": "docs" + }, + { + "path": ".claude/agent-memory/cdisc-frontend-dev/MEMORY.md", + "language": "markdown", + "sizeLines": 2, + "fileCategory": "docs" + }, + { + "path": ".claude/agent-memory/cdisc-frontend-dev/frontend_architecture.md", + "language": "markdown", + "sizeLines": 65, + "fileCategory": "docs" + }, + { + "path": ".claude/agent-memory/cdisc-frontend-dev/project_foundation.md", + "language": "markdown", + "sizeLines": 54, + "fileCategory": "docs" + }, + { + "path": ".claude/agents/cdisc-concept-explorer.md", + "language": "markdown", + "sizeLines": 67, + "fileCategory": "docs" + }, + { + "path": ".claude/agents/cdisc-frontend-dev.md", + "language": "markdown", + "sizeLines": 207, + "fileCategory": "docs" + }, + { + "path": ".claude/agents/mike.md", + "language": "markdown", + "sizeLines": 108, + "fileCategory": "docs" + }, + { + "path": ".claude/skills/code-review/SKILL.md", + "language": "markdown", + "sizeLines": 59, + "fileCategory": "docs" + }, + { + "path": ".claude/skills/run-concept-curation/SKILL.md", + "language": "markdown", + "sizeLines": 49, + "fileCategory": "docs" + }, + { + "path": ".claude/skills/run-concept-curation/smoke.sh", + "language": "shell", + "sizeLines": 90, + "fileCategory": "script" + }, + { + "path": ".flake8", + "language": "unknown", + "sizeLines": 16, + "fileCategory": "code" + }, + { + "path": ".github/workflows/ci.yml", + "language": "yaml", + "sizeLines": 26, + "fileCategory": "infra" + }, + { + "path": ".hintrc", + "language": "unknown", + "sizeLines": 7, + "fileCategory": "code" + }, + { + "path": ".mcp.json", + "language": "json", + "sizeLines": 9, + "fileCategory": "config" + }, + { + "path": ".pre-commit-config.yaml", + "language": "yaml", + "sizeLines": 24, + "fileCategory": "config" + }, + { + "path": ".ua/.understandignore", + "language": "unknown", + "sizeLines": 110, + "fileCategory": "code" + }, + { + "path": ".ua/config.json", + "language": "json", + "sizeLines": 1, + "fileCategory": "config" + }, + { + "path": "CLAUDE.md", + "language": "markdown", + "sizeLines": 128, + "fileCategory": "docs" + }, + { + "path": "README-PROGRESS.md", + "language": "markdown", + "sizeLines": 147, + "fileCategory": "docs" + }, + { + "path": "README.md", + "language": "markdown", + "sizeLines": 185, + "fileCategory": "docs" + }, + { + "path": "app.py", + "language": "python", + "sizeLines": 59, + "fileCategory": "code" + }, + { + "path": "config.py", + "language": "python", + "sizeLines": 14, + "fileCategory": "code" + }, + { + "path": "db_bootstrap.py", + "language": "python", + "sizeLines": 71, + "fileCategory": "code" + }, + { + "path": "extensions.py", + "language": "python", + "sizeLines": 25, + "fileCategory": "code" + }, + { + "path": "files/BC Curation Principles and Completion GLs.xlsx", + "language": "xlsx", + "sizeLines": 182, + "fileCategory": "code" + }, + { + "path": "files/BC DEC Templates.xlsx", + "language": "xlsx", + "sizeLines": 94, + "fileCategory": "code" + }, + { + "path": "files/BC Examples.xlsx", + "language": "xlsx", + "sizeLines": 332, + "fileCategory": "code" + }, + { + "path": "files/BC Governance.docx", + "language": "docx", + "sizeLines": 54, + "fileCategory": "code" + }, + { + "path": "files/implementation.md", + "language": "markdown", + "sizeLines": 205, + "fileCategory": "docs" + }, + { + "path": "files/plan.md", + "language": "markdown", + "sizeLines": 47, + "fileCategory": "docs" + }, + { + "path": "files/result.md", + "language": "markdown", + "sizeLines": 27, + "fileCategory": "docs" + }, + { + "path": "mcp_server/__init__.py", + "language": "python", + "sizeLines": 5, + "fileCategory": "code" + }, + { + "path": "mcp_server/__main__.py", + "language": "python", + "sizeLines": 3, + "fileCategory": "code" + }, + { + "path": "mcp_server/server.py", + "language": "python", + "sizeLines": 572, + "fileCategory": "code" + }, + { + "path": "migrations/README", + "language": "unknown", + "sizeLines": 1, + "fileCategory": "code" + }, + { + "path": "migrations/alembic.ini", + "language": "config", + "sizeLines": 50, + "fileCategory": "config" + }, + { + "path": "migrations/env.py", + "language": "python", + "sizeLines": 105, + "fileCategory": "code" + }, + { + "path": "migrations/script.py.mako", + "language": "mako", + "sizeLines": 24, + "fileCategory": "code" + }, + { + "path": "migrations/versions/51d4a009d291_baseline_initial_schema.py", + "language": "python", + "sizeLines": 142, + "fileCategory": "code" + }, + { + "path": "models/__init__.py", + "language": "python", + "sizeLines": 1, + "fileCategory": "code" + }, + { + "path": "models/audit.py", + "language": "python", + "sizeLines": 32, + "fileCategory": "code" + }, + { + "path": "models/bc.py", + "language": "python", + "sizeLines": 65, + "fileCategory": "code" + }, + { + "path": "models/governance.py", + "language": "python", + "sizeLines": 16, + "fileCategory": "code" + }, + { + "path": "models/ingestion.py", + "language": "python", + "sizeLines": 56, + "fileCategory": "code" + }, + { + "path": "models/specialization.py", + "language": "python", + "sizeLines": 30, + "fileCategory": "code" + }, + { + "path": "pyproject.toml", + "language": "toml", + "sizeLines": 7, + "fileCategory": "config" + }, + { + "path": "requirements-dev.txt", + "language": "txt", + "sizeLines": 9, + "fileCategory": "docs" + }, + { + "path": "requirements.txt", + "language": "txt", + "sizeLines": 28, + "fileCategory": "docs" + }, + { + "path": "routes/__init__.py", + "language": "python", + "sizeLines": 0, + "fileCategory": "code" + }, + { + "path": "routes/audit.py", + "language": "python", + "sizeLines": 44, + "fileCategory": "code" + }, + { + "path": "routes/bc.py", + "language": "python", + "sizeLines": 276, + "fileCategory": "code" + }, + { + "path": "routes/dashboard.py", + "language": "python", + "sizeLines": 60, + "fileCategory": "code" + }, + { + "path": "routes/governance.py", + "language": "python", + "sizeLines": 68, + "fileCategory": "code" + }, + { + "path": "routes/ingestion.py", + "language": "python", + "sizeLines": 186, + "fileCategory": "code" + }, + { + "path": "routes/loinc.py", + "language": "python", + "sizeLines": 23, + "fileCategory": "code" + }, + { + "path": "routes/ncit.py", + "language": "python", + "sizeLines": 83, + "fileCategory": "code" + }, + { + "path": "routes/specializations.py", + "language": "python", + "sizeLines": 172, + "fileCategory": "code" + }, + { + "path": "services/__init__.py", + "language": "python", + "sizeLines": 0, + "fileCategory": "code" + }, + { + "path": "services/api_cache.py", + "language": "python", + "sizeLines": 42, + "fileCategory": "code" + }, + { + "path": "services/audit.py", + "language": "python", + "sizeLines": 32, + "fileCategory": "code" + }, + { + "path": "services/bc_service.py", + "language": "python", + "sizeLines": 159, + "fileCategory": "code" + }, + { + "path": "services/cdisc_api.py", + "language": "python", + "sizeLines": 127, + "fileCategory": "code" + }, + { + "path": "services/export.py", + "language": "python", + "sizeLines": 185, + "fileCategory": "code" + }, + { + "path": "services/governance_service.py", + "language": "python", + "sizeLines": 60, + "fileCategory": "code" + }, + { + "path": "services/ingestion.py", + "language": "python", + "sizeLines": 227, + "fileCategory": "code" + }, + { + "path": "services/loinc_api.py", + "language": "python", + "sizeLines": 62, + "fileCategory": "code" + }, + { + "path": "services/ncit_api.py", + "language": "python", + "sizeLines": 85, + "fileCategory": "code" + }, + { + "path": "static/css/custom.css", + "language": "css", + "sizeLines": 1096, + "fileCategory": "markup" + }, + { + "path": "static/js/main.js", + "language": "javascript", + "sizeLines": 819, + "fileCategory": "code" + }, + { + "path": "templates/audit.html", + "language": "html", + "sizeLines": 213, + "fileCategory": "markup" + }, + { + "path": "templates/base.html", + "language": "html", + "sizeLines": 130, + "fileCategory": "markup" + }, + { + "path": "templates/bc_detail.html", + "language": "html", + "sizeLines": 533, + "fileCategory": "markup" + }, + { + "path": "templates/bc_list.html", + "language": "html", + "sizeLines": 238, + "fileCategory": "markup" + }, + { + "path": "templates/dashboard.html", + "language": "html", + "sizeLines": 392, + "fileCategory": "markup" + }, + { + "path": "templates/governance.html", + "language": "html", + "sizeLines": 243, + "fileCategory": "markup" + }, + { + "path": "templates/ingestion.html", + "language": "html", + "sizeLines": 174, + "fileCategory": "markup" + }, + { + "path": "templates/library_bc_detail.html", + "language": "html", + "sizeLines": 162, + "fileCategory": "markup" + }, + { + "path": "templates/library_spec_detail.html", + "language": "html", + "sizeLines": 197, + "fileCategory": "markup" + }, + { + "path": "templates/ncit_mapping.html", + "language": "html", + "sizeLines": 135, + "fileCategory": "markup" + }, + { + "path": "templates/specializations.html", + "language": "html", + "sizeLines": 394, + "fileCategory": "markup" + }, + { + "path": "tests/__init__.py", + "language": "python", + "sizeLines": 0, + "fileCategory": "code" + }, + { + "path": "tests/conftest.py", + "language": "python", + "sizeLines": 68, + "fileCategory": "code" + }, + { + "path": "tests/test_audit_routes.py", + "language": "python", + "sizeLines": 49, + "fileCategory": "code" + }, + { + "path": "tests/test_bc_routes.py", + "language": "python", + "sizeLines": 547, + "fileCategory": "code" + }, + { + "path": "tests/test_cdisc_api_cache.py", + "language": "python", + "sizeLines": 120, + "fileCategory": "code" + }, + { + "path": "tests/test_dashboard.py", + "language": "python", + "sizeLines": 75, + "fileCategory": "code" + }, + { + "path": "tests/test_db_bootstrap.py", + "language": "python", + "sizeLines": 87, + "fileCategory": "code" + }, + { + "path": "tests/test_export_service.py", + "language": "python", + "sizeLines": 207, + "fileCategory": "code" + }, + { + "path": "tests/test_governance_routes.py", + "language": "python", + "sizeLines": 236, + "fileCategory": "code" + }, + { + "path": "tests/test_ingestion_routes.py", + "language": "python", + "sizeLines": 204, + "fileCategory": "code" + }, + { + "path": "tests/test_ingestion_service.py", + "language": "python", + "sizeLines": 259, + "fileCategory": "code" + }, + { + "path": "tests/test_loinc.py", + "language": "python", + "sizeLines": 205, + "fileCategory": "code" + }, + { + "path": "tests/test_mcp_server.py", + "language": "python", + "sizeLines": 315, + "fileCategory": "code" + }, + { + "path": "tests/test_models.py", + "language": "python", + "sizeLines": 116, + "fileCategory": "code" + }, + { + "path": "tests/test_ncit.py", + "language": "python", + "sizeLines": 156, + "fileCategory": "code" + }, + { + "path": "tests/test_specializations_routes.py", + "language": "python", + "sizeLines": 260, + "fileCategory": "code" + } + ], + "totalFiles": 97, + "filteredByIgnore": 0, + "estimatedComplexity": "moderate", + "stats": { + "filesScanned": 97, + "byCategory": { + "docs": 18, + "script": 1, + "code": 60, + "infra": 1, + "config": 5, + "markup": 12 + }, + "byLanguage": { + "markdown": 16, + "shell": 1, + "unknown": 4, + "yaml": 2, + "json": 2, + "python": 50, + "xlsx": 3, + "docx": 1, + "config": 1, + "mako": 1, + "toml": 1, + "txt": 2, + "css": 1, + "javascript": 1, + "html": 11 + } + } +} \ No newline at end of file diff --git a/.ua/.trash-1785851745/tmp/ua-tour-analyze.js b/.ua/.trash-1785851745/tmp/ua-tour-analyze.js new file mode 100644 index 0000000..9a28c13 --- /dev/null +++ b/.ua/.trash-1785851745/tmp/ua-tour-analyze.js @@ -0,0 +1,404 @@ +#!/usr/bin/env node +"use strict"; + +const fs = require("fs"); +const path = require("path"); + +function fail(msg) { + console.error("ERROR: " + msg); + process.exit(1); +} + +const inputPath = process.argv[2]; +const outputPath = process.argv[3]; + +if (!inputPath || !outputPath) { + fail("Usage: node ua-tour-analyze.js "); +} + +let raw; +try { + raw = fs.readFileSync(inputPath, "utf8"); +} catch (e) { + fail("Could not read input file: " + e.message); +} + +let data; +try { + data = JSON.parse(raw); +} catch (e) { + fail("Invalid JSON input: " + e.message); +} + +const nodes = Array.isArray(data.nodes) ? data.nodes : []; +const edges = Array.isArray(data.edges) ? data.edges : []; +const layers = Array.isArray(data.layers) ? data.layers : []; + +const nodeById = new Map(); +for (const n of nodes) { + nodeById.set(n.id, n); +} + +// Filter edges to only those with valid source/target +const validEdges = edges.filter( + (e) => nodeById.has(e.source) && nodeById.has(e.target), +); + +// ---------- A & B: Fan-in / Fan-out ---------- +const fanIn = new Map(); +const fanOut = new Map(); +for (const n of nodes) { + fanIn.set(n.id, 0); + fanOut.set(n.id, 0); +} +for (const e of validEdges) { + fanOut.set(e.source, (fanOut.get(e.source) || 0) + 1); + fanIn.set(e.target, (fanIn.get(e.target) || 0) + 1); +} + +const fanInRanking = nodes + .map((n) => ({ id: n.id, fanIn: fanIn.get(n.id) || 0, name: n.name })) + .sort((a, b) => b.fanIn - a.fanIn) + .slice(0, 20); + +const fanOutRanking = nodes + .map((n) => ({ id: n.id, fanOut: fanOut.get(n.id) || 0, name: n.name })) + .sort((a, b) => b.fanOut - a.fanOut) + .slice(0, 20); + +// ---------- C: Entry point candidates ---------- +const ENTRY_FILENAMES = new Set([ + "index.ts", + "index.js", + "main.ts", + "main.js", + "app.ts", + "app.js", + "server.ts", + "server.js", + "mod.rs", + "main.go", + "main.py", + "main.rs", + "manage.py", + "app.py", + "wsgi.py", + "asgi.py", + "run.py", + "__main__.py", + "Application.java", + "Main.java", + "Program.cs", + "config.ru", + "index.php", + "App.swift", + "Application.kt", + "main.cpp", + "main.c", +]); + +const fanOutValues = nodes + .map((n) => fanOut.get(n.id) || 0) + .sort((a, b) => a - b); +const fanInValues = nodes + .map((n) => fanIn.get(n.id) || 0) + .sort((a, b) => a - b); + +function percentileThreshold(sortedValues, percentileFromTop) { + if (sortedValues.length === 0) return 0; + const idx = Math.max( + 0, + Math.floor(sortedValues.length * (1 - percentileFromTop)), + ); + return sortedValues[idx]; +} + +const fanOutTop10Threshold = percentileThreshold(fanOutValues, 0.1); +const fanInBottom25Threshold = (sorted) => + sorted[Math.min(sorted.length - 1, Math.floor(sorted.length * 0.25))]; +const fanInBottomThreshold = fanInBottom25Threshold(fanInValues); + +function depthFromRoot(filePath) { + if (!filePath) return 99; + const parts = filePath.split("/").filter(Boolean); + return parts.length; // 1 = root file, 2 = one level deep +} + +const entryScores = []; +for (const n of nodes) { + let score = 0; + const fp = n.filePath || ""; + const baseName = path.basename(fp || n.name || ""); + + if (n.type === "document") { + const depth = depthFromRoot(fp); + if (baseName.toLowerCase() === "readme.md" && depth <= 1) { + score += 5; + } else if (baseName.toLowerCase().endsWith(".md") && depth <= 1) { + score += 2; + } + } else if (n.type === "file") { + if (ENTRY_FILENAMES.has(baseName)) { + score += 3; + } + const depth = depthFromRoot(fp); + if (depth <= 2) { + score += 1; + } + const fo = fanOut.get(n.id) || 0; + const fi = fanIn.get(n.id) || 0; + if (fo >= fanOutTop10Threshold && fo > 0) { + score += 1; + } + if (fi <= fanInBottomThreshold) { + score += 1; + } + } + + if (score > 0) { + entryScores.push({ + id: n.id, + score, + name: n.name, + summary: n.summary || "", + }); + } +} + +entryScores.sort((a, b) => b.score - a.score); +const entryPointCandidates = entryScores.slice(0, 5); + +// ---------- D: BFS from top code entry point ---------- +// Skip documentation nodes for BFS start; find top-scoring 'file' type candidate +function findTopCodeEntry() { + for (const cand of entryScores) { + const n = nodeById.get(cand.id); + if (n && n.type !== "document") { + return cand.id; + } + } + // fallback: any file node with app.py/main.py etc, else first file node + for (const n of nodes) { + if ( + n.type === "file" && + ENTRY_FILENAMES.has(path.basename(n.filePath || n.name || "")) + ) { + return n.id; + } + } + const firstFile = nodes.find((n) => n.type === "file"); + return firstFile ? firstFile.id : nodes[0] ? nodes[0].id : null; +} + +const bfsStart = findTopCodeEntry(); + +const adjacency = new Map(); +for (const n of nodes) adjacency.set(n.id, []); +for (const e of validEdges) { + if (e.type === "imports" || e.type === "calls") { + adjacency.get(e.source).push(e.target); + } +} + +const bfsOrder = []; +const depthMap = {}; +const byDepth = {}; + +if (bfsStart) { + const visited = new Set([bfsStart]); + const queue = [[bfsStart, 0]]; + while (queue.length > 0) { + const [cur, depth] = queue.shift(); + bfsOrder.push(cur); + depthMap[cur] = depth; + if (!byDepth[depth]) byDepth[depth] = []; + byDepth[depth].push(cur); + const neighbors = adjacency.get(cur) || []; + for (const nb of neighbors) { + if (!visited.has(nb)) { + visited.add(nb); + queue.push([nb, depth + 1]); + } + } + } +} + +// ---------- E: Non-code file inventory ---------- +const nonCodeFiles = { + documentation: [], + infrastructure: [], + data: [], + config: [], +}; + +for (const n of nodes) { + const entry = { id: n.id, name: n.name, summary: n.summary || "" }; + if (n.type === "document") { + nonCodeFiles.documentation.push(entry); + } else if ( + n.type === "service" || + n.type === "pipeline" || + n.type === "resource" + ) { + nonCodeFiles.infrastructure.push({ ...entry, type: n.type }); + } else if ( + n.type === "table" || + n.type === "schema" || + n.type === "endpoint" + ) { + nonCodeFiles.data.push({ ...entry, type: n.type }); + } else if (n.type === "config") { + nonCodeFiles.config.push(entry); + } +} + +// ---------- F: Tightly coupled clusters ---------- +const edgeKey = (a, b) => a + "||" + b; +const edgeSet = new Set( + validEdges + .filter((e) => e.type === "imports" || e.type === "calls") + .map((e) => edgeKey(e.source, e.target)), +); + +const bidirectionalPairs = []; +for (const e of validEdges) { + if (e.type !== "imports" && e.type !== "calls") continue; + if (e.source === e.target) continue; + if (edgeSet.has(edgeKey(e.target, e.source))) { + const pairKey = [e.source, e.target].sort().join("||"); + bidirectionalPairs.push(pairKey); + } +} +const uniquePairs = Array.from(new Set(bidirectionalPairs)).map((p) => + p.split("||"), +); + +// Union-find to merge overlapping pairs into clusters, then expand +const parent = new Map(); +function find(x) { + if (!parent.has(x)) parent.set(x, x); + let root = x; + while (parent.get(root) !== root) root = parent.get(root); + parent.set(x, root); + return root; +} +function union(a, b) { + const ra = find(a), + rb = find(b); + if (ra !== rb) parent.set(ra, rb); +} + +for (const [a, b] of uniquePairs) { + find(a); + find(b); + union(a, b); +} + +const clusterMap = new Map(); +for (const [a] of uniquePairs) { + const root = find(a); + if (!clusterMap.has(root)) clusterMap.set(root, new Set()); +} +for (const [a, b] of uniquePairs) { + const root = find(a); + clusterMap.get(root).add(a); + clusterMap.get(root).add(b); +} + +// Expand: add nodes connecting to 2+ existing cluster members (edges either direction, imports/calls) +const allRelEdges = validEdges.filter( + (e) => e.type === "imports" || e.type === "calls", +); +for (const [root, members] of clusterMap.entries()) { + if (members.size >= 5) continue; + let changed = true; + while (changed && members.size < 5) { + changed = false; + const connectionCount = new Map(); + for (const e of allRelEdges) { + if (members.has(e.source) && !members.has(e.target)) { + connectionCount.set(e.target, (connectionCount.get(e.target) || 0) + 1); + } else if (members.has(e.target) && !members.has(e.source)) { + connectionCount.set(e.source, (connectionCount.get(e.source) || 0) + 1); + } + } + let bestNode = null, + bestCount = 0; + for (const [node, count] of connectionCount.entries()) { + if (count >= 2 && count > bestCount) { + bestNode = node; + bestCount = count; + } + } + if (bestNode && members.size < 5) { + members.add(bestNode); + changed = true; + } + } +} + +function edgeCountWithin(members) { + let count = 0; + for (const e of allRelEdges) { + if (members.has(e.source) && members.has(e.target)) count++; + } + return count; +} + +let clusters = Array.from(clusterMap.values()) + .filter((members) => members.size >= 2 && members.size <= 5) + .map((members) => ({ + nodes: Array.from(members), + edgeCount: edgeCountWithin(members), + })) + .sort((a, b) => b.edgeCount - a.edgeCount) + .slice(0, 10); + +// ---------- G: Layer list ---------- +const layersOut = { + count: layers.length, + list: layers.map((l) => ({ + id: l.id, + name: l.name, + description: l.description || "", + })), +}; + +// ---------- H: Node summary index ---------- +const nodeSummaryIndex = {}; +for (const n of nodes) { + nodeSummaryIndex[n.id] = { + name: n.name, + type: n.type, + summary: n.summary || "", + }; +} + +// ---------- Output ---------- +const result = { + scriptCompleted: true, + entryPointCandidates, + fanInRanking, + fanOutRanking, + bfsTraversal: { + startNode: bfsStart, + order: bfsOrder, + depthMap, + byDepth, + }, + nonCodeFiles, + clusters, + layers: layersOut, + nodeSummaryIndex, + totalNodes: nodes.length, + totalEdges: validEdges.length, +}; + +try { + fs.writeFileSync(outputPath, JSON.stringify(result, null, 2)); +} catch (e) { + fail("Could not write output file: " + e.message); +} + +console.log("Analysis complete. Output written to " + outputPath); +process.exit(0); diff --git a/.ua/.trash-1785851745/tmp/ua-tour-input.json b/.ua/.trash-1785851745/tmp/ua-tour-input.json new file mode 100644 index 0000000..b916e5f --- /dev/null +++ b/.ua/.trash-1785851745/tmp/ua-tour-input.json @@ -0,0 +1 @@ +{"nodes": [{"id": "file:models/audit.py", "name": "audit.py", "filePath": "models/audit.py", "summary": "SQLAlchemy model for the AuditLog table that records before/after JSON state for every change made to BCs, DECs, and governance records.", "type": "file"}, {"id": "file:models/bc.py", "name": "bc.py", "filePath": "models/bc.py", "summary": "Core SQLAlchemy models for BiomedicalConcept and DataElementConcept, the central entities of the CDISC BC curation pipeline, including NCIt/LOINC coding fields and governance status.", "type": "file"}, {"id": "file:models/ingestion.py", "name": "ingestion.py", "filePath": "models/ingestion.py", "summary": "SQLAlchemy model for IngestionRecord, which stages parsed upload rows (with field mappings, confidence scores, and validation errors) in the DB prior to SME approval.", "type": "file"}, {"id": "file:routes/audit.py", "name": "audit.py", "filePath": "routes/audit.py", "summary": "Flask blueprint exposing the read-only audit trail view with filtering by entity type, action, and actor, and pagination.", "type": "file"}, {"id": "file:routes/ingestion.py", "name": "ingestion.py", "filePath": "routes/ingestion.py", "summary": "Flask blueprint implementing the file-upload ingestion workflow: parsing uploaded files, staging rows as IngestionRecord, and approving/rejecting them into BiomedicalConcept/DataElementConcept records with audit logging.", "type": "file"}, {"id": "file:services/ingestion.py", "name": "ingestion.py", "filePath": "services/ingestion.py", "summary": "Business logic for the ingestion pipeline: fuzzy field-name mapping via SequenceMatcher similarity scoring, BC/DEC row validation, multi-row grouping by BC ID, and XLSX/CSV/JSON parsers.", "type": "file"}, {"id": "file:tests/conftest.py", "name": "conftest.py", "filePath": "tests/conftest.py", "summary": "Pytest fixtures shared across the test suite: Flask app/test client factories, a per-test DB reset fixture, API cache clearing, and a sample BiomedicalConcept fixture.", "type": "file"}, {"id": "file:tests/test_audit_routes.py", "name": "test_audit_routes.py", "filePath": "tests/test_audit_routes.py", "summary": "Test suite for the audit log route, covering empty listing, log rendering, and filtering by entity type, action, actor, and pagination parameters.", "type": "file"}, {"id": "file:tests/test_bc_routes.py", "name": "test_bc_routes.py", "filePath": "tests/test_bc_routes.py", "summary": "Large test suite covering BC CRUD routes: listing/search, creation, detail view (including LOINC lookups), editing, clearing NCIt/LOINC codes, submission for review, deletion, export formats, and the CDISC Library detail page.", "type": "file"}, {"id": "file:tests/test_governance_routes.py", "name": "test_governance_routes.py", "filePath": "tests/test_governance_routes.py", "summary": "Test suite for the governance Kanban board routes: stage advancement, rejection, and XLSX export of stage-3 BCs with LOINC-derived system columns.", "type": "file"}, {"id": "file:tests/test_ingestion_routes.py", "name": "test_ingestion_routes.py", "filePath": "tests/test_ingestion_routes.py", "summary": "Test suite for the ingestion pipeline routes: file upload (CSV/JSON), duplicate BC flagging, per-record approve/reject, and bulk approve-all with audit logging.", "type": "file"}, {"id": "file:tests/test_ingestion_service.py", "name": "test_ingestion_service.py", "filePath": "tests/test_ingestion_service.py", "summary": "Unit test suite for the ingestion service module, covering field similarity scoring, field mapping/matching, BC validation, deduplication, and XLSX/CSV/JSON parsing and grouping logic.", "type": "file"}, {"id": "file:tests/test_models.py", "name": "test_models.py", "filePath": "tests/test_models.py", "summary": "Unit tests for the AuditLog, IngestionRecord, and BiomedicalConcept ORM models, verifying JSON-backed property round-tripping, computed averages, and dictionary serialization.", "type": "file"}, {"id": "file:models/governance.py", "name": "governance.py", "filePath": "models/governance.py", "summary": "SQLAlchemy model for GovernanceRecord, tracking Biomedical Concept approval workflow stage transitions.", "type": "file"}, {"id": "file:routes/bc.py", "name": "bc.py", "filePath": "routes/bc.py", "summary": "Flask blueprint implementing BC CRUD, NCIt/LOINC metadata lookup, export, and lifecycle actions (submit for review, delete) for Biomedical Concepts.", "type": "file"}, {"id": "file:routes/governance.py", "name": "governance.py", "filePath": "routes/governance.py", "summary": "Flask blueprint for the governance Kanban board \u2014 viewing BCs by stage, exporting the published set, and advancing/rejecting BCs through the approval workflow.", "type": "file"}, {"id": "file:routes/ncit.py", "name": "ncit.py", "filePath": "routes/ncit.py", "summary": "Flask blueprint for NCI Thesaurus search and mapping to Biomedical Concepts, including AJAX search and single-concept detail lookups.", "type": "file"}, {"id": "file:services/__init__.py", "name": "__init__.py", "filePath": "services/__init__.py", "summary": "Empty package marker enabling the services directory to be imported as a Python package.", "type": "file"}, {"id": "file:services/audit.py", "name": "audit.py", "filePath": "services/audit.py", "summary": "Provides the log_change helper used across services and routes to persist immutable AuditLog rows for every entity mutation.", "type": "file"}, {"id": "file:services/bc_service.py", "name": "bc_service.py", "filePath": "services/bc_service.py", "summary": "Core business logic for creating, updating, and managing Biomedical Concepts and their Data Element Concepts, including NCIt remapping/merging and review submission.", "type": "file"}, {"id": "file:services/export.py", "name": "export.py", "filePath": "services/export.py", "summary": "Exporters that render Biomedical Concept collections to JSON, styled XLSX, governance XLSX, and CDISC ODM-XML formats.", "type": "file"}, {"id": "file:services/governance_service.py", "name": "governance_service.py", "filePath": "services/governance_service.py", "summary": "Implements governance stage transitions (advance/reject) for Biomedical Concepts, creating GovernanceRecord history rows and audit log entries.", "type": "file"}, {"id": "file:services/ncit_api.py", "name": "ncit_api.py", "filePath": "services/ncit_api.py", "summary": "NCI Thesaurus EVS REST API client providing concept search and detailed concept retrieval (parents, semantic type, synonyms, definitions) with caching.", "type": "file"}, {"id": "file:tests/test_export_service.py", "name": "test_export_service.py", "filePath": "tests/test_export_service.py", "summary": "Unit tests covering JSON, XLSX, governance XLSX, and ODM-XML export functions in services/export.py, verifying headers, blanking rules, and document structure.", "type": "file"}, {"id": "file:tests/test_ncit.py", "name": "test_ncit.py", "filePath": "tests/test_ncit.py", "summary": "Unit tests for NCItApiClient.get_concept's extended metadata extraction and the /ncit concept-detail route's JSON responses and error handling.", "type": "file"}, {"id": "file:routes/dashboard.py", "name": "dashboard.py", "filePath": "routes/dashboard.py", "summary": "Flask blueprint for the dashboard home page; fetches CDISC Library BC and specialization counts concurrently via a thread pool and combines them with local KPI stats (BC status counts, recent activity, audit log) for the landing page.", "type": "file"}, {"id": "file:routes/loinc.py", "name": "loinc.py", "filePath": "routes/loinc.py", "summary": "Flask blueprint exposing a single AJAX search endpoint that proxies term queries to the NLM LOINC API client and returns JSON results.", "type": "file"}, {"id": "file:routes/specializations.py", "name": "specializations.py", "filePath": "routes/specializations.py", "summary": "Flask blueprint managing CDISC dataset specializations: lists local and CDISC Library specializations, and supports manual creation, deletion, and auto-generation of variable-level specializations from a BC's data element concepts.", "type": "file"}, {"id": "file:services/api_cache.py", "name": "api_cache.py", "filePath": "services/api_cache.py", "summary": "Shared stale-tolerant in-memory cache helper used by the CDISC Library, NCI EVS, and LOINC API clients; serves fresh data for 5 minutes and falls back to stale data for up to an hour if a refresh fails, so a slow upstream never blanks a page that rendered fine a minute ago.", "type": "file"}, {"id": "file:services/cdisc_api.py", "name": "cdisc_api.py", "filePath": "services/cdisc_api.py", "summary": "REST client for the CDISC Library COSMOS API; wraps biomedical concept, specialization, and duplicate-check lookups with a 5-minute in-memory cache and provides a publish_bc method for pushing approved BCs to the Library.", "type": "file"}, {"id": "file:services/loinc_api.py", "name": "loinc_api.py", "filePath": "services/loinc_api.py", "summary": "Client for the NLM Clinical Tables LOINC search API; builds the query with a configurable set of extra fields, optionally applies HTTP Basic Auth, and normalizes results through the shared API cache.", "type": "file"}, {"id": "file:tests/test_cdisc_api_cache.py", "name": "test_cdisc_api_cache.py", "filePath": "tests/test_cdisc_api_cache.py", "summary": "Unit tests for the shared cache helper and CDISCApiClient cache-key behavior, covering fresh/stale/expired cache states, refresh-failure fallback, and that raw API keys never leak into cache keys.", "type": "file"}, {"id": "file:tests/test_loinc.py", "name": "test_loinc.py", "filePath": "tests/test_loinc.py", "summary": "Unit tests for LoincApiClient.search normalization, extra-field handling, Basic Auth toggling, and error handling, plus tests for the /loinc/search Flask route's JSON responses.", "type": "file"}, {"id": "file:app.py", "name": "app.py", "filePath": "app.py", "summary": "Flask application factory that configures logging, initializes the shared database and migration extensions, registers all eight feature blueprints, and doubles as the dev-server entry point via `python app.py`.", "type": "file"}, {"id": "file:config.py", "name": "config.py", "filePath": "config.py", "summary": "Centralizes all environment-driven configuration for the Flask app, including the secret key, database URI, CDISC/NCIt API base URLs and credentials, upload size limit, and dev server port.", "type": "file"}, {"id": "file:db_bootstrap.py", "name": "db_bootstrap.py", "filePath": "db_bootstrap.py", "summary": "Brings the app's database to the current Alembic migration head at startup, distinguishing fresh, legacy create_all(), and pre-squash-stamped databases, and raising if the schema predates the squashed baseline.", "type": "file"}, {"id": "file:extensions.py", "name": "extensions.py", "filePath": "extensions.py", "summary": "Defines the shared SQLAlchemy `db` and Flask-Migrate `migrate` instances used across the app to avoid circular imports, and configures SQLite WAL mode with a busy timeout so the Flask app and MCP server can coexist as concurrent writers.", "type": "file"}, {"id": "file:models/__init__.py", "name": "__init__.py", "filePath": "models/__init__.py", "summary": "Package marker for the `models` module that imports the shared `db` instance so all SQLAlchemy model classes register against a single metadata registry.", "type": "file"}, {"id": "file:tests/test_dashboard.py", "name": "test_dashboard.py", "filePath": "tests/test_dashboard.py", "summary": "Unit tests for the dashboard route, verifying it renders correctly with live CDISC/NCIt API data, degrades gracefully on API errors and empty results, reflects local DB stats accurately, and issues both API calls concurrently.", "type": "file"}, {"id": "file:tests/test_db_bootstrap.py", "name": "test_db_bootstrap.py", "filePath": "tests/test_db_bootstrap.py", "summary": "Unit tests for `ensure_db()` covering a fresh baseline upgrade, stamping of a legacy create_all() database, restamping of a pre-squash-stamped database, raising on an outdated schema, and idempotency across repeated runs.", "type": "file"}, {"id": "file:mcp_server/__main__.py", "name": "__main__.py", "filePath": "mcp_server/__main__.py", "summary": "Module entry point for `python -m mcp_server`; imports the server's main() coroutine runner and invokes it immediately to start the stdio MCP server loop.", "type": "file"}, {"id": "file:mcp_server/server.py", "name": "server.py", "filePath": "mcp_server/server.py", "summary": "MCP server exposing the CDISC curation app's data and write operations as 14 stdio tools (8 read, 6 write); each call is dispatched into a Flask app context shared with the web app so both processes hit the same SQLite database and service layer.", "type": "file"}, {"id": "file:models/specialization.py", "name": "specialization.py", "filePath": "models/specialization.py", "summary": "SQLAlchemy model for a dataset specialization (SDTM/CDASH variable-level metadata group) tied to a Biomedical Concept, storing its variable list as a JSON-serialized text column.", "type": "file"}, {"id": "file:tests/test_mcp_server.py", "name": "test_mcp_server.py", "filePath": "tests/test_mcp_server.py", "summary": "Unit tests for the MCP server's `_dispatch` routing and all 14 tool handlers, exercising them directly against a shared in-memory test Flask app (no stdio transport) and asserting on returned data plus AuditLog/GovernanceRecord side effects.", "type": "file"}, {"id": "file:tests/test_specializations_routes.py", "name": "test_specializations_routes.py", "filePath": "tests/test_specializations_routes.py", "summary": "Route-level tests for the dataset specializations blueprint covering listing, creating, editing/upserting, deleting, detail views, and generating variables from DECs, including audit log verification and CDISC Library error handling.", "type": "file"}, {"id": "pipeline:.github/workflows/ci.yml", "name": "ci.yml", "filePath": ".github/workflows/ci.yml", "summary": "GitHub Actions CI workflow that runs on every push and pull request across all branches: installs project and dev dependencies, lints with isort/black/flake8, and runs the pytest suite on Python 3.12.", "type": "pipeline"}, {"id": "document:.claude/agent-memory/cdisc-frontend-dev/MEMORY.md", "name": "MEMORY.md", "filePath": ".claude/agent-memory/cdisc-frontend-dev/MEMORY.md", "summary": "Index file for the cdisc-frontend-dev sub-agent's persistent memory, linking to the project foundation and frontend architecture notes.", "type": "document"}, {"id": "document:.claude/agent-memory/cdisc-frontend-dev/frontend_architecture.md", "name": "frontend_architecture.md", "filePath": ".claude/agent-memory/cdisc-frontend-dev/frontend_architecture.md", "summary": "Persistent agent memory documenting the front-end template hierarchy, custom.css class naming conventions, main.js module functions, template-to-route mapping, and Jinja context variable conventions for the CDISC BC Curation UI.", "type": "document"}, {"id": "document:.claude/agent-memory/cdisc-frontend-dev/project_foundation.md", "name": "project_foundation.md", "filePath": ".claude/agent-memory/cdisc-frontend-dev/project_foundation.md", "summary": "Persistent agent memory describing the Flask application factory pattern, registered blueprints, model and service locations, database setup, and key route implementation decisions for the CDISC BC Curation app.", "type": "document"}, {"id": "document:.claude/agents/cdisc-concept-explorer.md", "name": "cdisc-concept-explorer.md", "filePath": ".claude/agents/cdisc-concept-explorer.md", "summary": "Defines the cdisc-concept-explorer Claude subagent, a CDISC standards specialist that queries the live CDISC Library API to detect duplicate Biomedical Concepts against local drafts, enrich drafts with authoritative metadata, and find dataset specializations for a given BC.", "type": "document"}, {"id": "document:.claude/agents/cdisc-frontend-dev.md", "name": "cdisc-frontend-dev.md", "filePath": ".claude/agents/cdisc-frontend-dev.md", "summary": "Defines the cdisc-frontend-dev Claude subagent, a front-end specialist scoped to Jinja2 templates, CSS, vanilla JavaScript, and Flask view wiring for the BC curation tool, with mandatory CDISC brand-guideline compliance and an extensive persistent project-memory protocol appended to the spec.", "type": "document"}, {"id": "document:.claude/agents/mike.md", "name": "mike.md", "filePath": ".claude/agents/mike.md", "summary": "Defines the mike Claude subagent, a project-manager assistant for cdisc-concept-curation that tracks daily progress in README-PROGRESS.md, prepares git commit messages, flags documentation drift against the code, and always asks before committing or pushing.", "type": "document"}, {"id": "config:.mcp.json", "name": ".mcp.json", "filePath": ".mcp.json", "summary": "Registers the cdisc-curation MCP server, launching it via the project virtualenv's Python interpreter running the `mcp_server` package as a module.", "type": "config"}, {"id": "config:.pre-commit-config.yaml", "name": ".pre-commit-config.yaml", "filePath": ".pre-commit-config.yaml", "summary": "Pre-commit hook configuration that runs isort, black, and flake8 on staged files and executes the full pytest suite before every commit.", "type": "config"}, {"id": "document:CLAUDE.md", "name": "CLAUDE.md", "filePath": "CLAUDE.md", "summary": "Guidance document for Claude Code covering the app's purpose, how to run it, linting and testing commands, the Route-Service-Model architecture with its 8 Flask blueprints and key services, the MCP server, and environment-variable configuration.", "type": "document"}, {"id": "document:README-PROGRESS.md", "name": "README-PROGRESS.md", "filePath": "README-PROGRESS.md", "summary": "Running project progress log tracking feature status and a dated changelog of fixes and workstream completions such as governance export corrections, audit trail coverage, and LOINC API explorer work.", "type": "document"}, {"id": "document:README.md", "name": "README.md", "filePath": "README.md", "summary": "Primary project README covering prerequisites, installation, environment-variable configuration, running the app and tests, key screens, project structure, and the external CDISC/NCIt/LOINC APIs the platform integrates with.", "type": "document"}, {"id": "config:pyproject.toml", "name": "pyproject.toml", "filePath": "pyproject.toml", "summary": "Tool configuration setting a shared 200-character line length for black (targeting Python 3.9-3.11) and isort (using the black-compatible profile).", "type": "config"}, {"id": "document:requirements-dev.txt", "name": "requirements-dev.txt", "filePath": "requirements-dev.txt", "summary": "Pinned development-only dependency list: black, flake8 (with mccabe/pycodestyle/pyflakes), isort, pre-commit, pytest, and pytest-flask, used for linting and testing.", "type": "document"}, {"id": "document:requirements.txt", "name": "requirements.txt", "filePath": "requirements.txt", "summary": "Pinned runtime dependency list for the Flask application, including Flask, Flask-SQLAlchemy, Flask-Migrate, SQLAlchemy, Alembic, pandas, openpyxl, lxml, requests, and the mcp SDK.", "type": "document"}, {"id": "document:files/plan.md", "name": "plan.md", "filePath": "files/plan.md", "summary": "Initial high-level project brief for the CDISC Biomedical Concept Curation application, specifying the target UI mockup to match, the key reference files to review, the required agents (cdisc-concept-explorer, ncit-concept-resolver, cdisc-frontend-dev, mike), and the constraint that only the CDISC_API_KEY environment variable be used for API authentication.", "type": "document"}, {"id": "document:files/implementation.md", "name": "implementation.md", "filePath": "files/implementation.md", "summary": "Detailed implementation plan for the Flask/Jinja BC curation app, laying out the directory structure, SQLAlchemy data models, Flask blueprint/route table, service-layer API surface, template inventory, and a four-phase build roadmap from MVP through export and polish.", "type": "document"}, {"id": "document:files/result.md", "name": "result.md", "filePath": "files/result.md", "summary": "Post-implementation status report confirming all 8 Flask routes return HTTP 200 and enumerating delivered capabilities: file ingestion with AI field mapping, BC CRUD, NCIt mapping, a 4-stage governance Kanban, an immutable audit trail, and JSON/XLSX/ODM-XML export.", "type": "document"}, {"id": "file:templates/base.html", "name": "base.html", "filePath": "templates/base.html", "summary": "Master Jinja2 layout providing the sidebar navigation, top bar, flash-message rendering, and template blocks (title, page_title, user_info, content, extra_js) that every other page in the app extends.", "type": "file"}, {"id": "file:templates/audit.html", "name": "audit.html", "filePath": "templates/audit.html", "summary": "Audit trail page listing all curation actions (create/update/delete/advance/reject/publish/ingest) with actor/entity/date filters, CSV/JSON export links, expandable before/after diff rows, and pagination.", "type": "file"}, {"id": "file:templates/bc_detail.html", "name": "bc_detail.html", "filePath": "templates/bc_detail.html", "summary": "Create/edit form for a single Biomedical Concept covering identification, NCIt and LOINC lookups (with async metadata fetch in extra_js), classification, definition, metadata/status, and an editable Data Element Concepts (DECs) table.", "type": "file"}, {"id": "file:templates/bc_list.html", "name": "bc_list.html", "filePath": "templates/bc_list.html", "summary": "Searchable, filterable, paginated table of locally curated Biomedical Concepts with status badges, JSON/XLSX/ODM-XML export links, and a delete-confirmation modal.", "type": "file"}, {"id": "file:templates/dashboard.html", "name": "dashboard.html", "filePath": "templates/dashboard.html", "summary": "Main landing dashboard showing KPI cards, recent submissions, the governance pipeline snapshot, paginated/searchable views of CDISC Library BCs and dataset specializations, an AI curation pipeline progress tracker, and quick action links.", "type": "file"}, {"id": "file:templates/governance.html", "name": "governance.html", "filePath": "templates/governance.html", "summary": "Four-column Kanban board (Provisional -> SME Review -> CDISC Approval -> Published) for advancing or rejecting Biomedical Concepts through the governance workflow, with summary counts and an export-filename modal.", "type": "file"}, {"id": "file:templates/ingestion.html", "name": "ingestion.html", "filePath": "templates/ingestion.html", "summary": "File upload page (drag-and-drop or click) for XLSX/CSV/JSON ingestion, with a parsed-results queue table showing field-mapping confidence, validation errors, duplicate flags, and per-row approve/reject actions.", "type": "file"}, {"id": "file:templates/library_bc_detail.html", "name": "library_bc_detail.html", "filePath": "templates/library_bc_detail.html", "summary": "Read-only detail view of a single Biomedical Concept fetched from the published CDISC Library, showing identification, definition, LOINC coding, metadata, and its Data Element Concepts.", "type": "file"}, {"id": "file:templates/library_spec_detail.html", "name": "library_spec_detail.html", "filePath": "templates/library_spec_detail.html", "summary": "Read-only detail view of a Dataset Specialization from the published CDISC Library, showing identification/domain metadata and a full variables table (role, codelist, assigned term, mandatory flags, relationship).", "type": "file"}, {"id": "file:templates/ncit_mapping.html", "name": "ncit_mapping.html", "filePath": "templates/ncit_mapping.html", "summary": "NCI Thesaurus search page plus a conflict-resolution queue listing Biomedical Concepts with unresolved or low-confidence NCIt mappings that block governance advancement, with a confidence-score legend.", "type": "file"}, {"id": "file:templates/specializations.html", "name": "specializations.html", "filePath": "templates/specializations.html", "summary": "Dataset Specialization management page with a collapsible create/edit form (VLM group ID, BC selector, SDTM/CDASH domain, dynamic variables table, generate-from-DEC-templates action) and a list/edit/delete table of existing specializations.", "type": "file"}, {"id": "document:.claude/agent-memory/cdisc-concept-explorer/MEMORY.md", "name": "MEMORY.md", "filePath": ".claude/agent-memory/cdisc-concept-explorer/MEMORY.md", "summary": "Single-line agent memory index for the cdisc-concept-explorer agent, pointing to the verified CDISC API reference notes.", "type": "document"}, {"id": "document:.claude/agent-memory/cdisc-concept-explorer/reference_api_endpoints.md", "name": "reference_api_endpoints.md", "filePath": ".claude/agent-memory/cdisc-concept-explorer/reference_api_endpoints.md", "summary": "Verified reference notes on the CDISC Library COSMOS v2 API: base URL, working endpoints, lack of pagination, and field names for biomedical concept and dataset specialization records.", "type": "document"}, {"id": "document:.claude/skills/code-review/SKILL.md", "name": "SKILL.md", "filePath": ".claude/skills/code-review/SKILL.md", "summary": "Project-specific code review checklist for cdisc-concept-curation covering route/service/model layering, audit log coverage, Alembic migration discipline, API client error handling, test isolation, and TDD conventions.", "type": "document"}, {"id": "document:.claude/skills/run-concept-curation/SKILL.md", "name": "SKILL.md", "filePath": ".claude/skills/run-concept-curation/SKILL.md", "summary": "Instructions for running and smoke-testing the Flask dev server, including the preferred automated smoke-test script, manual startup steps, and troubleshooting tips.", "type": "document"}, {"id": "file:.claude/skills/run-concept-curation/smoke.sh", "name": "smoke.sh", "filePath": ".claude/skills/run-concept-curation/smoke.sh", "summary": "Shell script that boots the Flask app on a throwaway SQLite database and port, runs a battery of HTTP checks against key routes (dashboard, BC CRUD, governance, NCIt, audit, ingestion, specializations, export), and tears the server down.", "type": "file"}, {"id": "config:.flake8", "name": ".flake8", "filePath": ".flake8", "summary": "Flake8 lint configuration setting max line length to 200, ignoring black-conflicting rules (E203/W503) and SQLAlchemy's `== None` idiom (E711), and excluding build/venv directories.", "type": "config"}, {"id": "config:.hintrc", "name": ".hintrc", "filePath": ".hintrc", "summary": "webhint configuration extending the 'development' ruleset and disabling the no-inline-styles rule for HTML/CSS linting.", "type": "config"}, {"id": "config:.ua/.understandignore", "name": ".understandignore", "filePath": ".ua/.understandignore", "summary": "Gitignore-style exclusion list for the Understand-Anything analysis tool, with commented-out suggestions mirroring the project's .gitignore (Python caches, virtualenvs, egg metadata) that can be uncommented to activate.", "type": "config"}, {"id": "config:.ua/config.json", "name": "config.json", "filePath": ".ua/config.json", "summary": "Single-key configuration for the Understand-Anything tool setting the output language for generated analysis text to English.", "type": "config"}, {"id": "file:files/BC Curation Principles and Completion GLs.xlsx", "name": "BC Curation Principles and Completion GLs.xlsx", "filePath": "files/BC Curation Principles and Completion GLs.xlsx", "summary": "Reference spreadsheet documenting BC curation principles and field-completion guidelines used by curators when authoring Biomedical Concepts.", "type": "file"}, {"id": "file:files/BC DEC Templates.xlsx", "name": "BC DEC Templates.xlsx", "filePath": "files/BC DEC Templates.xlsx", "summary": "Template spreadsheet defining the standard row/column layout for Data Element Concepts, used as an ingestion source template for curators.", "type": "file"}, {"id": "file:files/BC Examples.xlsx", "name": "BC Examples.xlsx", "filePath": "files/BC Examples.xlsx", "summary": "Example spreadsheet of fully-populated Biomedical Concepts, used as a reference/sample dataset for the ingestion pipeline and curator training.", "type": "file"}, {"id": "file:files/BC Governance.docx", "name": "BC Governance.docx", "filePath": "files/BC Governance.docx", "summary": "Word document describing the governance process and stages (Provisional, SME Review, CDISC Approval, Published) that a curated Biomedical Concept moves through.", "type": "file"}, {"id": "file:mcp_server/__init__.py", "name": "__init__.py", "filePath": "mcp_server/__init__.py", "summary": "Package marker and docstring for the mcp_server package, noting it is run via `python -m mcp_server` and deliberately named to avoid shadowing the MCP SDK package.", "type": "file"}, {"id": "document:migrations/README", "name": "README", "filePath": "migrations/README", "summary": "One-line note identifying the migrations directory as a single-database Flask-Migrate/Alembic configuration.", "type": "document"}, {"id": "config:migrations/alembic.ini", "name": "alembic.ini", "filePath": "migrations/alembic.ini", "summary": "Alembic configuration file controlling migration script location, logging setup, and version-file naming for the Flask-Migrate managed schema.", "type": "config"}, {"id": "file:migrations/env.py", "name": "env.py", "filePath": "migrations/env.py", "summary": "Alembic environment script that wires Flask-Migrate's engine/metadata into Alembic's migration context, supporting both offline (SQL-only) and online (live-connection) migration runs.", "type": "file"}, {"id": "file:migrations/script.py.mako", "name": "script.py.mako", "filePath": "migrations/script.py.mako", "summary": "Mako template used by `flask db migrate` to scaffold new Alembic revision files with the standard revision metadata and upgrade/downgrade function stubs.", "type": "file"}, {"id": "file:migrations/versions/51d4a009d291_baseline_initial_schema.py", "name": "51d4a009d291_baseline_initial_schema.py", "filePath": "migrations/versions/51d4a009d291_baseline_initial_schema.py", "summary": "Baseline Alembic migration that creates the initial database schema: audit_logs, biomedical_concepts, ingestion_records, data_element_concepts, dataset_specializations, and governance_records tables, with their downgrade counterparts.", "type": "file"}, {"id": "file:routes/__init__.py", "name": "__init__.py", "filePath": "routes/__init__.py", "summary": "Empty package marker file that makes routes/ a Python package for the Flask blueprint modules it contains.", "type": "file"}, {"id": "file:static/css/custom.css", "name": "custom.css", "filePath": "static/css/custom.css", "summary": "Main application stylesheet defining design tokens (colors, spacing), base resets, app shell layout (sidebar, topbar, content area), and component styles (cards, flash messages, nav items, badges) for the CDISC BC curation UI.", "type": "file"}, {"id": "file:static/js/main.js", "name": "main.js", "filePath": "static/js/main.js", "summary": "Client-side interactivity module for the BC curation platform: flash-message dismissal, drag-and-drop file upload, NCIt/LOINC lookup and search panels, dynamic DEC table rows, governance Kanban actions, audit-diff toggles, and shared fetch/toast helpers, all wired up on DOMContentLoaded.", "type": "file"}, {"id": "file:tests/__init__.py", "name": "__init__.py", "filePath": "tests/__init__.py", "summary": "Empty package marker file that makes tests/ a discoverable Python package for pytest.", "type": "file"}, {"id": "file:files/cdisc_bc_ai_platform.html", "name": "cdisc_bc_ai_platform.html", "filePath": "files/cdisc_bc_ai_platform.html", "summary": "No summary available", "type": "file"}, {"id": "file:files/cdisc_smart_goals_infographic.html", "name": "cdisc_smart_goals_infographic.html", "filePath": "files/cdisc_smart_goals_infographic.html", "summary": "No summary available", "type": "file"}], "edges": [{"source": "file:models/audit.py", "target": "file:extensions.py", "type": "imports"}, {"source": "file:models/bc.py", "target": "file:extensions.py", "type": "imports"}, {"source": "file:models/ingestion.py", "target": "file:extensions.py", "type": "imports"}, {"source": "file:routes/audit.py", "target": "file:models/audit.py", "type": "imports"}, {"source": "file:routes/ingestion.py", "target": "file:extensions.py", "type": "imports"}, {"source": "file:routes/ingestion.py", "target": "file:models/audit.py", "type": "imports"}, {"source": "file:routes/ingestion.py", "target": "file:models/bc.py", "type": "imports"}, {"source": "file:routes/ingestion.py", "target": "file:models/ingestion.py", "type": "imports"}, {"source": "file:routes/ingestion.py", "target": "file:services/ingestion.py", "type": "imports"}, {"source": "file:tests/conftest.py", "target": "file:app.py", "type": "imports"}, {"source": "file:tests/conftest.py", "target": "file:extensions.py", "type": "imports"}, {"source": "file:tests/conftest.py", "target": "file:models/audit.py", "type": "imports"}, {"source": "file:tests/conftest.py", "target": "file:models/bc.py", "type": "imports"}, {"source": "file:tests/conftest.py", "target": "file:models/governance.py", "type": "imports"}, {"source": "file:tests/conftest.py", "target": "file:models/ingestion.py", "type": "imports"}, {"source": "file:models/audit.py", "target": "file:tests/test_models.py", "type": "tested_by"}, {"source": "file:models/bc.py", "target": "file:tests/test_models.py", "type": "tested_by"}, {"source": "file:models/bc.py", "target": "file:tests/test_bc_routes.py", "type": "tested_by"}, {"source": "file:models/ingestion.py", "target": "file:tests/test_models.py", "type": "tested_by"}, {"source": "file:routes/audit.py", "target": "file:tests/test_audit_routes.py", "type": "tested_by"}, {"source": "file:routes/ingestion.py", "target": "file:tests/test_ingestion_routes.py", "type": "tested_by"}, {"source": "file:services/ingestion.py", "target": "file:tests/test_ingestion_service.py", "type": "tested_by"}, {"source": "file:tests/test_audit_routes.py", "target": "file:extensions.py", "type": "imports"}, {"source": "file:tests/test_audit_routes.py", "target": "file:models/audit.py", "type": "imports"}, {"source": "file:tests/test_bc_routes.py", "target": "file:extensions.py", "type": "imports"}, {"source": "file:tests/test_bc_routes.py", "target": "file:models/audit.py", "type": "imports"}, {"source": "file:tests/test_bc_routes.py", "target": "file:models/bc.py", "type": "imports"}, {"source": "file:tests/test_governance_routes.py", "target": "file:extensions.py", "type": "imports"}, {"source": "file:tests/test_governance_routes.py", "target": "file:models/audit.py", "type": "imports"}, {"source": "file:tests/test_governance_routes.py", "target": "file:models/bc.py", "type": "imports"}, {"source": "file:tests/test_governance_routes.py", "target": "file:models/governance.py", "type": "imports"}, {"source": "file:tests/test_ingestion_routes.py", "target": "file:extensions.py", "type": "imports"}, {"source": "file:tests/test_ingestion_routes.py", "target": "file:models/audit.py", "type": "imports"}, {"source": "file:tests/test_ingestion_routes.py", "target": "file:models/bc.py", "type": "imports"}, {"source": "file:tests/test_ingestion_routes.py", "target": "file:models/ingestion.py", "type": "imports"}, {"source": "file:tests/test_ingestion_service.py", "target": "file:services/ingestion.py", "type": "imports"}, {"source": "file:tests/test_models.py", "target": "file:extensions.py", "type": "imports"}, {"source": "file:tests/test_models.py", "target": "file:models/audit.py", "type": "imports"}, {"source": "file:tests/test_models.py", "target": "file:models/bc.py", "type": "imports"}, {"source": "file:tests/test_models.py", "target": "file:models/ingestion.py", "type": "imports"}, {"source": "file:models/audit.py", "target": "file:tests/test_audit_routes.py", "type": "tested_by"}, {"source": "file:routes/bc.py", "target": "file:tests/test_bc_routes.py", "type": "tested_by"}, {"source": "file:models/bc.py", "target": "file:tests/test_governance_routes.py", "type": "tested_by"}, {"source": "file:models/governance.py", "target": "file:tests/test_governance_routes.py", "type": "tested_by"}, {"source": "file:routes/governance.py", "target": "file:tests/test_governance_routes.py", "type": "tested_by"}, {"source": "file:models/bc.py", "target": "file:tests/test_ingestion_routes.py", "type": "tested_by"}, {"source": "file:models/ingestion.py", "target": "file:tests/test_ingestion_routes.py", "type": "tested_by"}, {"source": "file:models/governance.py", "target": "file:extensions.py", "type": "imports"}, {"source": "file:routes/bc.py", "target": "file:extensions.py", "type": "imports"}, {"source": "file:routes/bc.py", "target": "file:models/bc.py", "type": "imports"}, {"source": "file:routes/bc.py", "target": "file:models/governance.py", "type": "imports"}, {"source": "file:routes/bc.py", "target": "file:services/__init__.py", "type": "imports"}, {"source": "file:routes/bc.py", "target": "file:services/audit.py", "type": "imports"}, {"source": "file:routes/bc.py", "target": "file:services/bc_service.py", "type": "imports"}, {"source": "file:routes/bc.py", "target": "file:services/cdisc_api.py", "type": "imports"}, {"source": "file:routes/bc.py", "target": "file:services/export.py", "type": "imports"}, {"source": "file:routes/bc.py", "target": "file:services/loinc_api.py", "type": "imports"}, {"source": "file:routes/bc.py", "target": "file:services/ncit_api.py", "type": "imports"}, {"source": "file:routes/governance.py", "target": "file:extensions.py", "type": "imports"}, {"source": "file:routes/governance.py", "target": "file:models/bc.py", "type": "imports"}, {"source": "file:routes/governance.py", "target": "file:services/__init__.py", "type": "imports"}, {"source": "file:routes/governance.py", "target": "file:services/export.py", "type": "imports"}, {"source": "file:routes/governance.py", "target": "file:services/governance_service.py", "type": "imports"}, {"source": "file:routes/ncit.py", "target": "file:extensions.py", "type": "imports"}, {"source": "file:routes/ncit.py", "target": "file:models/bc.py", "type": "imports"}, {"source": "file:routes/ncit.py", "target": "file:services/__init__.py", "type": "imports"}, {"source": "file:routes/ncit.py", "target": "file:services/bc_service.py", "type": "imports"}, {"source": "file:routes/ncit.py", "target": "file:services/ncit_api.py", "type": "imports"}, {"source": "file:services/audit.py", "target": "file:extensions.py", "type": "imports"}, {"source": "file:services/audit.py", "target": "file:models/audit.py", "type": "imports"}, {"source": "file:routes/ncit.py", "target": "file:tests/test_ncit.py", "type": "tested_by"}, {"source": "file:services/bc_service.py", "target": "file:extensions.py", "type": "imports"}, {"source": "file:services/bc_service.py", "target": "file:models/bc.py", "type": "imports"}, {"source": "file:services/bc_service.py", "target": "file:models/governance.py", "type": "imports"}, {"source": "file:services/bc_service.py", "target": "file:models/specialization.py", "type": "imports"}, {"source": "file:services/bc_service.py", "target": "file:services/audit.py", "type": "imports"}, {"source": "file:services/governance_service.py", "target": "file:extensions.py", "type": "imports"}, {"source": "file:services/governance_service.py", "target": "file:models/governance.py", "type": "imports"}, {"source": "file:services/governance_service.py", "target": "file:services/audit.py", "type": "imports"}, {"source": "file:services/governance_service.py", "target": "file:services/bc_service.py", "type": "imports"}, {"source": "file:services/ncit_api.py", "target": "file:services/api_cache.py", "type": "imports"}, {"source": "file:tests/test_export_service.py", "target": "file:extensions.py", "type": "imports"}, {"source": "file:tests/test_export_service.py", "target": "file:models/bc.py", "type": "imports"}, {"source": "file:tests/test_export_service.py", "target": "file:services/export.py", "type": "imports"}, {"source": "file:tests/test_ncit.py", "target": "file:services/ncit_api.py", "type": "imports"}, {"source": "file:services/export.py", "target": "file:tests/test_export_service.py", "type": "tested_by"}, {"source": "file:services/ncit_api.py", "target": "file:tests/test_ncit.py", "type": "tested_by"}, {"source": "file:routes/dashboard.py", "target": "file:models/audit.py", "type": "imports"}, {"source": "file:routes/dashboard.py", "target": "file:models/bc.py", "type": "imports"}, {"source": "file:routes/dashboard.py", "target": "file:services/cdisc_api.py", "type": "imports"}, {"source": "file:routes/loinc.py", "target": "file:services/loinc_api.py", "type": "imports"}, {"source": "file:routes/specializations.py", "target": "file:extensions.py", "type": "imports"}, {"source": "file:routes/specializations.py", "target": "file:models/bc.py", "type": "imports"}, {"source": "file:routes/specializations.py", "target": "file:models/specialization.py", "type": "imports"}, {"source": "file:routes/specializations.py", "target": "file:services/audit.py", "type": "imports"}, {"source": "file:routes/specializations.py", "target": "file:services/cdisc_api.py", "type": "imports"}, {"source": "file:services/cdisc_api.py", "target": "file:services/api_cache.py", "type": "imports"}, {"source": "file:services/loinc_api.py", "target": "file:services/api_cache.py", "type": "imports"}, {"source": "file:tests/test_cdisc_api_cache.py", "target": "file:services/cdisc_api.py", "type": "imports"}, {"source": "file:tests/test_loinc.py", "target": "file:services/loinc_api.py", "type": "imports"}, {"source": "file:services/cdisc_api.py", "target": "file:tests/test_cdisc_api_cache.py", "type": "tested_by"}, {"source": "file:services/api_cache.py", "target": "file:tests/test_cdisc_api_cache.py", "type": "tested_by"}, {"source": "file:services/loinc_api.py", "target": "file:tests/test_loinc.py", "type": "tested_by"}, {"source": "file:routes/loinc.py", "target": "file:tests/test_loinc.py", "type": "tested_by"}, {"source": "file:app.py", "target": "file:config.py", "type": "imports"}, {"source": "file:app.py", "target": "file:extensions.py", "type": "imports"}, {"source": "file:db_bootstrap.py", "target": "file:extensions.py", "type": "imports"}, {"source": "file:models/__init__.py", "target": "file:extensions.py", "type": "imports"}, {"source": "file:tests/test_dashboard.py", "target": "file:extensions.py", "type": "imports"}, {"source": "file:tests/test_dashboard.py", "target": "file:models/bc.py", "type": "imports"}, {"source": "file:tests/test_db_bootstrap.py", "target": "file:app.py", "type": "imports"}, {"source": "file:tests/test_db_bootstrap.py", "target": "file:db_bootstrap.py", "type": "imports"}, {"source": "file:tests/test_db_bootstrap.py", "target": "file:extensions.py", "type": "imports"}, {"source": "file:app.py", "target": "file:db_bootstrap.py", "type": "depends_on"}, {"source": "file:db_bootstrap.py", "target": "file:tests/test_db_bootstrap.py", "type": "tested_by"}, {"source": "file:mcp_server/__main__.py", "target": "file:mcp_server/server.py", "type": "imports"}, {"source": "file:models/specialization.py", "target": "file:extensions.py", "type": "imports"}, {"source": "file:tests/test_mcp_server.py", "target": "file:extensions.py", "type": "imports"}, {"source": "file:tests/test_mcp_server.py", "target": "file:mcp_server/server.py", "type": "imports"}, {"source": "file:tests/test_mcp_server.py", "target": "file:models/audit.py", "type": "imports"}, {"source": "file:tests/test_mcp_server.py", "target": "file:models/bc.py", "type": "imports"}, {"source": "file:tests/test_mcp_server.py", "target": "file:models/governance.py", "type": "imports"}, {"source": "file:tests/test_mcp_server.py", "target": "file:models/ingestion.py", "type": "imports"}, {"source": "file:tests/test_mcp_server.py", "target": "file:models/specialization.py", "type": "imports"}, {"source": "file:tests/test_specializations_routes.py", "target": "file:extensions.py", "type": "imports"}, {"source": "file:tests/test_specializations_routes.py", "target": "file:models/audit.py", "type": "imports"}, {"source": "file:tests/test_specializations_routes.py", "target": "file:models/bc.py", "type": "imports"}, {"source": "file:tests/test_specializations_routes.py", "target": "file:models/specialization.py", "type": "imports"}, {"source": "file:mcp_server/server.py", "target": "file:app.py", "type": "depends_on"}, {"source": "file:mcp_server/server.py", "target": "file:db_bootstrap.py", "type": "depends_on"}, {"source": "file:mcp_server/server.py", "target": "file:models/bc.py", "type": "depends_on"}, {"source": "file:mcp_server/server.py", "target": "file:models/governance.py", "type": "depends_on"}, {"source": "file:mcp_server/server.py", "target": "file:extensions.py", "type": "depends_on"}, {"source": "file:mcp_server/server.py", "target": "file:models/ingestion.py", "type": "depends_on"}, {"source": "file:mcp_server/server.py", "target": "file:services/ncit_api.py", "type": "depends_on"}, {"source": "file:mcp_server/server.py", "target": "file:services/loinc_api.py", "type": "depends_on"}, {"source": "file:mcp_server/server.py", "target": "file:services/cdisc_api.py", "type": "depends_on"}, {"source": "file:mcp_server/server.py", "target": "file:services/bc_service.py", "type": "depends_on"}, {"source": "file:mcp_server/server.py", "target": "file:services/governance_service.py", "type": "depends_on"}, {"source": "file:mcp_server/server.py", "target": "file:tests/test_mcp_server.py", "type": "tested_by"}, {"source": "file:routes/specializations.py", "target": "file:tests/test_specializations_routes.py", "type": "tested_by"}, {"source": "document:.claude/agent-memory/cdisc-frontend-dev/MEMORY.md", "target": "document:.claude/agent-memory/cdisc-frontend-dev/project_foundation.md", "type": "related"}, {"source": "document:.claude/agent-memory/cdisc-frontend-dev/MEMORY.md", "target": "document:.claude/agent-memory/cdisc-frontend-dev/frontend_architecture.md", "type": "related"}, {"source": "document:.claude/agent-memory/cdisc-frontend-dev/frontend_architecture.md", "target": "file:templates/base.html", "type": "documents"}, {"source": "document:.claude/agent-memory/cdisc-frontend-dev/frontend_architecture.md", "target": "file:static/js/main.js", "type": "documents"}, {"source": "document:.claude/agent-memory/cdisc-frontend-dev/frontend_architecture.md", "target": "file:static/css/custom.css", "type": "documents"}, {"source": "document:.claude/agent-memory/cdisc-frontend-dev/project_foundation.md", "target": "file:app.py", "type": "documents"}, {"source": "document:.claude/agent-memory/cdisc-frontend-dev/project_foundation.md", "target": "file:extensions.py", "type": "documents"}, {"source": "document:.claude/agent-memory/cdisc-frontend-dev/project_foundation.md", "target": "file:models/bc.py", "type": "documents"}, {"source": "document:.claude/agent-memory/cdisc-frontend-dev/project_foundation.md", "target": "file:models/specialization.py", "type": "documents"}, {"source": "document:.claude/agent-memory/cdisc-frontend-dev/project_foundation.md", "target": "file:models/governance.py", "type": "documents"}, {"source": "document:.claude/agent-memory/cdisc-frontend-dev/project_foundation.md", "target": "file:models/audit.py", "type": "documents"}, {"source": "document:.claude/agent-memory/cdisc-frontend-dev/project_foundation.md", "target": "file:models/ingestion.py", "type": "documents"}, {"source": "document:.claude/agent-memory/cdisc-frontend-dev/project_foundation.md", "target": "file:services/ingestion.py", "type": "documents"}, {"source": "document:.claude/agent-memory/cdisc-frontend-dev/project_foundation.md", "target": "file:services/ncit_api.py", "type": "documents"}, {"source": "document:.claude/agent-memory/cdisc-frontend-dev/project_foundation.md", "target": "file:services/loinc_api.py", "type": "documents"}, {"source": "document:.claude/agent-memory/cdisc-frontend-dev/project_foundation.md", "target": "file:services/export.py", "type": "documents"}, {"source": "document:.claude/agent-memory/cdisc-frontend-dev/project_foundation.md", "target": "file:services/cdisc_api.py", "type": "documents"}, {"source": "document:.claude/agents/mike.md", "target": "document:.claude/agents/cdisc-frontend-dev.md", "type": "related"}, {"source": "document:.claude/agents/mike.md", "target": "document:.claude/agents/cdisc-concept-explorer.md", "type": "related"}, {"source": "document:README.md", "target": "document:requirements.txt", "type": "documents"}, {"source": "document:README.md", "target": "config:.pre-commit-config.yaml", "type": "documents"}, {"source": "document:README.md", "target": "config:pyproject.toml", "type": "documents"}, {"source": "config:.pre-commit-config.yaml", "target": "config:pyproject.toml", "type": "depends_on"}, {"source": "document:requirements-dev.txt", "target": "document:requirements.txt", "type": "related"}, {"source": "document:README.md", "target": "document:CLAUDE.md", "type": "related"}, {"source": "document:README.md", "target": "document:README-PROGRESS.md", "type": "related"}, {"source": "document:files/plan.md", "target": "document:files/implementation.md", "type": "related"}, {"source": "document:files/implementation.md", "target": "file:app.py", "type": "documents"}, {"source": "document:files/implementation.md", "target": "file:config.py", "type": "documents"}, {"source": "document:files/implementation.md", "target": "file:models/bc.py", "type": "documents"}, {"source": "document:files/implementation.md", "target": "file:models/specialization.py", "type": "documents"}, {"source": "document:files/implementation.md", "target": "file:models/governance.py", "type": "documents"}, {"source": "document:files/implementation.md", "target": "file:models/audit.py", "type": "documents"}, {"source": "document:files/implementation.md", "target": "file:routes/dashboard.py", "type": "documents"}, {"source": "document:files/implementation.md", "target": "file:routes/ingestion.py", "type": "documents"}, {"source": "document:files/implementation.md", "target": "file:routes/bc.py", "type": "documents"}, {"source": "document:files/implementation.md", "target": "file:routes/ncit.py", "type": "documents"}, {"source": "document:files/implementation.md", "target": "file:routes/specializations.py", "type": "documents"}, {"source": "document:files/implementation.md", "target": "file:routes/governance.py", "type": "documents"}, {"source": "document:files/implementation.md", "target": "file:routes/audit.py", "type": "documents"}, {"source": "document:files/implementation.md", "target": "file:services/cdisc_api.py", "type": "documents"}, {"source": "document:files/implementation.md", "target": "file:services/ncit_api.py", "type": "documents"}, {"source": "document:files/implementation.md", "target": "file:services/ingestion.py", "type": "documents"}, {"source": "document:files/implementation.md", "target": "file:services/export.py", "type": "documents"}, {"source": "document:files/implementation.md", "target": "file:templates/base.html", "type": "documents"}, {"source": "document:files/implementation.md", "target": "document:files/plan.md", "type": "related"}, {"source": "document:files/implementation.md", "target": "document:files/result.md", "type": "related"}, {"source": "document:files/result.md", "target": "file:app.py", "type": "documents"}, {"source": "document:files/result.md", "target": "file:extensions.py", "type": "documents"}, {"source": "document:files/result.md", "target": "file:config.py", "type": "documents"}, {"source": "document:files/result.md", "target": "document:files/implementation.md", "type": "related"}, {"source": "file:templates/audit.html", "target": "file:templates/base.html", "type": "depends_on"}, {"source": "file:templates/bc_detail.html", "target": "file:templates/base.html", "type": "depends_on"}, {"source": "file:templates/bc_list.html", "target": "file:templates/base.html", "type": "depends_on"}, {"source": "file:templates/dashboard.html", "target": "file:templates/base.html", "type": "depends_on"}, {"source": "file:templates/governance.html", "target": "file:templates/base.html", "type": "depends_on"}, {"source": "file:templates/ingestion.html", "target": "file:templates/base.html", "type": "depends_on"}, {"source": "file:templates/library_bc_detail.html", "target": "file:templates/base.html", "type": "depends_on"}, {"source": "file:templates/library_spec_detail.html", "target": "file:templates/base.html", "type": "depends_on"}, {"source": "file:templates/ncit_mapping.html", "target": "file:templates/base.html", "type": "depends_on"}, {"source": "file:templates/specializations.html", "target": "file:templates/base.html", "type": "depends_on"}, {"source": "document:.claude/skills/run-concept-curation/SKILL.md", "target": "file:.claude/skills/run-concept-curation/smoke.sh", "type": "documents"}, {"source": "document:.claude/agent-memory/cdisc-concept-explorer/MEMORY.md", "target": "document:.claude/agent-memory/cdisc-concept-explorer/reference_api_endpoints.md", "type": "related"}, {"source": "config:migrations/alembic.ini", "target": "file:migrations/env.py", "type": "configures"}, {"source": "file:migrations/script.py.mako", "target": "file:migrations/versions/51d4a009d291_baseline_initial_schema.py", "type": "related"}, {"source": "file:static/css/custom.css", "target": "file:static/js/main.js", "type": "related"}, {"source": "document:.claude/skills/code-review/SKILL.md", "target": "file:mcp_server/__init__.py", "type": "related"}, {"source": "document:files/plan.md", "target": "file:files/cdisc_bc_ai_platform.html", "type": "related"}, {"source": "document:files/plan.md", "target": "file:files/cdisc_smart_goals_infographic.html", "type": "related"}, {"source": "document:files/plan.md", "target": "file:files/BC Curation Principles and Completion GLs.xlsx", "type": "related"}, {"source": "document:files/plan.md", "target": "file:files/BC Examples.xlsx", "type": "related"}, {"source": "document:files/plan.md", "target": "file:files/BC DEC Templates.xlsx", "type": "related"}, {"source": "document:files/plan.md", "target": "file:files/BC Governance.docx", "type": "related"}], "layers": [{"id": "layer:api", "name": "API Layer", "description": "Flask blueprint route handlers (dashboard, ingestion, bc, ncit, loinc, specializations, governance, audit) plus the MCP server package that exposes the same read/write operations as a second tool-based API surface for AI agents."}, {"id": "layer:service", "name": "Service Layer", "description": "Business logic and external API clients \u2014 BC/governance/audit orchestration, the ingestion parser and field mapper, the XLSX/JSON/ODM-XML exporter, and the CDISC Library, NCIt, and LOINC API clients (with in-memory caching)."}, {"id": "layer:data", "name": "Data Layer", "description": "SQLAlchemy ORM models (BiomedicalConcept, DataElementConcept, IngestionRecord, AuditLog, GovernanceRecord, Specialization) together with the Alembic migration environment and baseline schema revision that bring the SQLite database to head."}, {"id": "layer:ui", "name": "UI Layer", "description": "Jinja2 + Bootstrap 5 templates for every page (dashboard, ingestion, BC detail/list, governance Kanban, NCIt mapping, specializations, audit log) plus the shared CSS and JavaScript assets that render them in the browser."}, {"id": "layer:config", "name": "Configuration & Application Setup", "description": "The application factory, extension singletons, and DB-bootstrap entry points (app.py, extensions.py, db_bootstrap.py) alongside project-level configuration \u2014 config.py, dependency pins, lint/format tool config, and the MCP server registration."}, {"id": "layer:test", "name": "Test Layer", "description": "The pytest suite covering every blueprint, service, and model, plus shared fixtures in conftest.py, run against the in-memory SQLite TestConfig database used by both CI and local development."}, {"id": "layer:documentation", "name": "Documentation & Reference Materials", "description": "Project-level documentation (CLAUDE.md, README, progress log) and the domain reference materials in files/ \u2014 BC curation principles, DEC templates, worked examples, governance guidance, and platform planning docs \u2014 that ground the curation workflow the app implements."}, {"id": "layer:dev-tooling", "name": "Dev Tooling & CI/CD", "description": "The GitHub Actions CI pipeline plus Claude Code agent configuration and memory (.claude/agents, .claude/agent-memory, .claude/skills) that automate linting, testing, and AI-assisted development for this repository."}]} \ No newline at end of file diff --git a/.ua/.trash-1785851745/tmp/ua-tour-results.json b/.ua/.trash-1785851745/tmp/ua-tour-results.json new file mode 100644 index 0000000..6fd7c2d --- /dev/null +++ b/.ua/.trash-1785851745/tmp/ua-tour-results.json @@ -0,0 +1,953 @@ +{ + "scriptCompleted": true, + "entryPointCandidates": [ + { + "id": "file:mcp_server/__main__.py", + "score": 5, + "name": "__main__.py", + "summary": "Module entry point for `python -m mcp_server`; imports the server's main() coroutine runner and invokes it immediately to start the stdio MCP server loop." + }, + { + "id": "document:README.md", + "score": 5, + "name": "README.md", + "summary": "Primary project README covering prerequisites, installation, environment-variable configuration, running the app and tests, key screens, project structure, and the external CDISC/NCIt/LOINC APIs the platform integrates with." + }, + { + "id": "file:app.py", + "score": 4, + "name": "app.py", + "summary": "Flask application factory that configures logging, initializes the shared database and migration extensions, registers all eight feature blueprints, and doubles as the dev-server entry point via `python app.py`." + }, + { + "id": "file:tests/conftest.py", + "score": 3, + "name": "conftest.py", + "summary": "Pytest fixtures shared across the test suite: Flask app/test client factories, a per-test DB reset fixture, API cache clearing, and a sample BiomedicalConcept fixture." + }, + { + "id": "file:static/js/main.js", + "score": 3, + "name": "main.js", + "summary": "Client-side interactivity module for the BC curation platform: flash-message dismissal, drag-and-drop file upload, NCIt/LOINC lookup and search panels, dynamic DEC table rows, governance Kanban actions, audit-diff toggles, and shared fetch/toast helpers, all wired up on DOMContentLoaded." + } + ], + "fanInRanking": [ + { + "id": "file:extensions.py", + "fanIn": 30, + "name": "extensions.py" + }, + { + "id": "file:models/bc.py", + "fanIn": 19, + "name": "bc.py" + }, + { + "id": "file:models/audit.py", + "fanIn": 14, + "name": "audit.py" + }, + { + "id": "file:templates/base.html", + "fanIn": 12, + "name": "base.html" + }, + { + "id": "file:models/governance.py", + "fanIn": 9, + "name": "governance.py" + }, + { + "id": "file:models/ingestion.py", + "fanIn": 7, + "name": "ingestion.py" + }, + { + "id": "file:services/cdisc_api.py", + "fanIn": 7, + "name": "cdisc_api.py" + }, + { + "id": "file:services/ncit_api.py", + "fanIn": 6, + "name": "ncit_api.py" + }, + { + "id": "file:app.py", + "fanIn": 6, + "name": "app.py" + }, + { + "id": "file:models/specialization.py", + "fanIn": 6, + "name": "specialization.py" + }, + { + "id": "file:services/export.py", + "fanIn": 5, + "name": "export.py" + }, + { + "id": "file:services/loinc_api.py", + "fanIn": 5, + "name": "loinc_api.py" + }, + { + "id": "file:services/ingestion.py", + "fanIn": 4, + "name": "ingestion.py" + }, + { + "id": "file:services/audit.py", + "fanIn": 4, + "name": "audit.py" + }, + { + "id": "file:services/bc_service.py", + "fanIn": 4, + "name": "bc_service.py" + }, + { + "id": "file:tests/test_governance_routes.py", + "fanIn": 3, + "name": "test_governance_routes.py" + }, + { + "id": "file:tests/test_ingestion_routes.py", + "fanIn": 3, + "name": "test_ingestion_routes.py" + }, + { + "id": "file:tests/test_models.py", + "fanIn": 3, + "name": "test_models.py" + }, + { + "id": "file:services/__init__.py", + "fanIn": 3, + "name": "__init__.py" + }, + { + "id": "file:services/api_cache.py", + "fanIn": 3, + "name": "api_cache.py" + } + ], + "fanOutRanking": [ + { + "id": "document:files/implementation.md", + "fanOut": 20, + "name": "implementation.md" + }, + { + "id": "file:mcp_server/server.py", + "fanOut": 12, + "name": "server.py" + }, + { + "id": "document:.claude/agent-memory/cdisc-frontend-dev/project_foundation.md", + "fanOut": 12, + "name": "project_foundation.md" + }, + { + "id": "file:routes/bc.py", + "fanOut": 11, + "name": "bc.py" + }, + { + "id": "file:tests/test_mcp_server.py", + "fanOut": 7, + "name": "test_mcp_server.py" + }, + { + "id": "document:files/plan.md", + "fanOut": 7, + "name": "plan.md" + }, + { + "id": "file:routes/ingestion.py", + "fanOut": 6, + "name": "ingestion.py" + }, + { + "id": "file:tests/conftest.py", + "fanOut": 6, + "name": "conftest.py" + }, + { + "id": "file:routes/governance.py", + "fanOut": 6, + "name": "governance.py" + }, + { + "id": "file:routes/ncit.py", + "fanOut": 6, + "name": "ncit.py" + }, + { + "id": "file:routes/specializations.py", + "fanOut": 6, + "name": "specializations.py" + }, + { + "id": "file:models/bc.py", + "fanOut": 5, + "name": "bc.py" + }, + { + "id": "file:services/bc_service.py", + "fanOut": 5, + "name": "bc_service.py" + }, + { + "id": "document:README.md", + "fanOut": 5, + "name": "README.md" + }, + { + "id": "file:tests/test_governance_routes.py", + "fanOut": 4, + "name": "test_governance_routes.py" + }, + { + "id": "file:tests/test_ingestion_routes.py", + "fanOut": 4, + "name": "test_ingestion_routes.py" + }, + { + "id": "file:tests/test_models.py", + "fanOut": 4, + "name": "test_models.py" + }, + { + "id": "file:services/governance_service.py", + "fanOut": 4, + "name": "governance_service.py" + }, + { + "id": "file:tests/test_specializations_routes.py", + "fanOut": 4, + "name": "test_specializations_routes.py" + }, + { + "id": "document:files/result.md", + "fanOut": 4, + "name": "result.md" + } + ], + "bfsTraversal": { + "startNode": "file:mcp_server/__main__.py", + "order": [ + "file:mcp_server/__main__.py", + "file:mcp_server/server.py" + ], + "depthMap": { + "file:mcp_server/__main__.py": 0, + "file:mcp_server/server.py": 1 + }, + "byDepth": { + "0": [ + "file:mcp_server/__main__.py" + ], + "1": [ + "file:mcp_server/server.py" + ] + } + }, + "nonCodeFiles": { + "documentation": [ + { + "id": "document:.claude/agent-memory/cdisc-frontend-dev/MEMORY.md", + "name": "MEMORY.md", + "summary": "Index file for the cdisc-frontend-dev sub-agent's persistent memory, linking to the project foundation and frontend architecture notes." + }, + { + "id": "document:.claude/agent-memory/cdisc-frontend-dev/frontend_architecture.md", + "name": "frontend_architecture.md", + "summary": "Persistent agent memory documenting the front-end template hierarchy, custom.css class naming conventions, main.js module functions, template-to-route mapping, and Jinja context variable conventions for the CDISC BC Curation UI." + }, + { + "id": "document:.claude/agent-memory/cdisc-frontend-dev/project_foundation.md", + "name": "project_foundation.md", + "summary": "Persistent agent memory describing the Flask application factory pattern, registered blueprints, model and service locations, database setup, and key route implementation decisions for the CDISC BC Curation app." + }, + { + "id": "document:.claude/agents/cdisc-concept-explorer.md", + "name": "cdisc-concept-explorer.md", + "summary": "Defines the cdisc-concept-explorer Claude subagent, a CDISC standards specialist that queries the live CDISC Library API to detect duplicate Biomedical Concepts against local drafts, enrich drafts with authoritative metadata, and find dataset specializations for a given BC." + }, + { + "id": "document:.claude/agents/cdisc-frontend-dev.md", + "name": "cdisc-frontend-dev.md", + "summary": "Defines the cdisc-frontend-dev Claude subagent, a front-end specialist scoped to Jinja2 templates, CSS, vanilla JavaScript, and Flask view wiring for the BC curation tool, with mandatory CDISC brand-guideline compliance and an extensive persistent project-memory protocol appended to the spec." + }, + { + "id": "document:.claude/agents/mike.md", + "name": "mike.md", + "summary": "Defines the mike Claude subagent, a project-manager assistant for cdisc-concept-curation that tracks daily progress in README-PROGRESS.md, prepares git commit messages, flags documentation drift against the code, and always asks before committing or pushing." + }, + { + "id": "document:CLAUDE.md", + "name": "CLAUDE.md", + "summary": "Guidance document for Claude Code covering the app's purpose, how to run it, linting and testing commands, the Route-Service-Model architecture with its 8 Flask blueprints and key services, the MCP server, and environment-variable configuration." + }, + { + "id": "document:README-PROGRESS.md", + "name": "README-PROGRESS.md", + "summary": "Running project progress log tracking feature status and a dated changelog of fixes and workstream completions such as governance export corrections, audit trail coverage, and LOINC API explorer work." + }, + { + "id": "document:README.md", + "name": "README.md", + "summary": "Primary project README covering prerequisites, installation, environment-variable configuration, running the app and tests, key screens, project structure, and the external CDISC/NCIt/LOINC APIs the platform integrates with." + }, + { + "id": "document:requirements-dev.txt", + "name": "requirements-dev.txt", + "summary": "Pinned development-only dependency list: black, flake8 (with mccabe/pycodestyle/pyflakes), isort, pre-commit, pytest, and pytest-flask, used for linting and testing." + }, + { + "id": "document:requirements.txt", + "name": "requirements.txt", + "summary": "Pinned runtime dependency list for the Flask application, including Flask, Flask-SQLAlchemy, Flask-Migrate, SQLAlchemy, Alembic, pandas, openpyxl, lxml, requests, and the mcp SDK." + }, + { + "id": "document:files/plan.md", + "name": "plan.md", + "summary": "Initial high-level project brief for the CDISC Biomedical Concept Curation application, specifying the target UI mockup to match, the key reference files to review, the required agents (cdisc-concept-explorer, ncit-concept-resolver, cdisc-frontend-dev, mike), and the constraint that only the CDISC_API_KEY environment variable be used for API authentication." + }, + { + "id": "document:files/implementation.md", + "name": "implementation.md", + "summary": "Detailed implementation plan for the Flask/Jinja BC curation app, laying out the directory structure, SQLAlchemy data models, Flask blueprint/route table, service-layer API surface, template inventory, and a four-phase build roadmap from MVP through export and polish." + }, + { + "id": "document:files/result.md", + "name": "result.md", + "summary": "Post-implementation status report confirming all 8 Flask routes return HTTP 200 and enumerating delivered capabilities: file ingestion with AI field mapping, BC CRUD, NCIt mapping, a 4-stage governance Kanban, an immutable audit trail, and JSON/XLSX/ODM-XML export." + }, + { + "id": "document:.claude/agent-memory/cdisc-concept-explorer/MEMORY.md", + "name": "MEMORY.md", + "summary": "Single-line agent memory index for the cdisc-concept-explorer agent, pointing to the verified CDISC API reference notes." + }, + { + "id": "document:.claude/agent-memory/cdisc-concept-explorer/reference_api_endpoints.md", + "name": "reference_api_endpoints.md", + "summary": "Verified reference notes on the CDISC Library COSMOS v2 API: base URL, working endpoints, lack of pagination, and field names for biomedical concept and dataset specialization records." + }, + { + "id": "document:.claude/skills/code-review/SKILL.md", + "name": "SKILL.md", + "summary": "Project-specific code review checklist for cdisc-concept-curation covering route/service/model layering, audit log coverage, Alembic migration discipline, API client error handling, test isolation, and TDD conventions." + }, + { + "id": "document:.claude/skills/run-concept-curation/SKILL.md", + "name": "SKILL.md", + "summary": "Instructions for running and smoke-testing the Flask dev server, including the preferred automated smoke-test script, manual startup steps, and troubleshooting tips." + }, + { + "id": "document:migrations/README", + "name": "README", + "summary": "One-line note identifying the migrations directory as a single-database Flask-Migrate/Alembic configuration." + } + ], + "infrastructure": [ + { + "id": "pipeline:.github/workflows/ci.yml", + "name": "ci.yml", + "summary": "GitHub Actions CI workflow that runs on every push and pull request across all branches: installs project and dev dependencies, lints with isort/black/flake8, and runs the pytest suite on Python 3.12.", + "type": "pipeline" + } + ], + "data": [], + "config": [ + { + "id": "config:.mcp.json", + "name": ".mcp.json", + "summary": "Registers the cdisc-curation MCP server, launching it via the project virtualenv's Python interpreter running the `mcp_server` package as a module." + }, + { + "id": "config:.pre-commit-config.yaml", + "name": ".pre-commit-config.yaml", + "summary": "Pre-commit hook configuration that runs isort, black, and flake8 on staged files and executes the full pytest suite before every commit." + }, + { + "id": "config:pyproject.toml", + "name": "pyproject.toml", + "summary": "Tool configuration setting a shared 200-character line length for black (targeting Python 3.9-3.11) and isort (using the black-compatible profile)." + }, + { + "id": "config:.flake8", + "name": ".flake8", + "summary": "Flake8 lint configuration setting max line length to 200, ignoring black-conflicting rules (E203/W503) and SQLAlchemy's `== None` idiom (E711), and excluding build/venv directories." + }, + { + "id": "config:.hintrc", + "name": ".hintrc", + "summary": "webhint configuration extending the 'development' ruleset and disabling the no-inline-styles rule for HTML/CSS linting." + }, + { + "id": "config:.ua/.understandignore", + "name": ".understandignore", + "summary": "Gitignore-style exclusion list for the Understand-Anything analysis tool, with commented-out suggestions mirroring the project's .gitignore (Python caches, virtualenvs, egg metadata) that can be uncommented to activate." + }, + { + "id": "config:.ua/config.json", + "name": "config.json", + "summary": "Single-key configuration for the Understand-Anything tool setting the output language for generated analysis text to English." + }, + { + "id": "config:migrations/alembic.ini", + "name": "alembic.ini", + "summary": "Alembic configuration file controlling migration script location, logging setup, and version-file naming for the Flask-Migrate managed schema." + } + ] + }, + "clusters": [], + "layers": { + "count": 8, + "list": [ + { + "id": "layer:api", + "name": "API Layer", + "description": "Flask blueprint route handlers (dashboard, ingestion, bc, ncit, loinc, specializations, governance, audit) plus the MCP server package that exposes the same read/write operations as a second tool-based API surface for AI agents." + }, + { + "id": "layer:service", + "name": "Service Layer", + "description": "Business logic and external API clients — BC/governance/audit orchestration, the ingestion parser and field mapper, the XLSX/JSON/ODM-XML exporter, and the CDISC Library, NCIt, and LOINC API clients (with in-memory caching)." + }, + { + "id": "layer:data", + "name": "Data Layer", + "description": "SQLAlchemy ORM models (BiomedicalConcept, DataElementConcept, IngestionRecord, AuditLog, GovernanceRecord, Specialization) together with the Alembic migration environment and baseline schema revision that bring the SQLite database to head." + }, + { + "id": "layer:ui", + "name": "UI Layer", + "description": "Jinja2 + Bootstrap 5 templates for every page (dashboard, ingestion, BC detail/list, governance Kanban, NCIt mapping, specializations, audit log) plus the shared CSS and JavaScript assets that render them in the browser." + }, + { + "id": "layer:config", + "name": "Configuration & Application Setup", + "description": "The application factory, extension singletons, and DB-bootstrap entry points (app.py, extensions.py, db_bootstrap.py) alongside project-level configuration — config.py, dependency pins, lint/format tool config, and the MCP server registration." + }, + { + "id": "layer:test", + "name": "Test Layer", + "description": "The pytest suite covering every blueprint, service, and model, plus shared fixtures in conftest.py, run against the in-memory SQLite TestConfig database used by both CI and local development." + }, + { + "id": "layer:documentation", + "name": "Documentation & Reference Materials", + "description": "Project-level documentation (CLAUDE.md, README, progress log) and the domain reference materials in files/ — BC curation principles, DEC templates, worked examples, governance guidance, and platform planning docs — that ground the curation workflow the app implements." + }, + { + "id": "layer:dev-tooling", + "name": "Dev Tooling & CI/CD", + "description": "The GitHub Actions CI pipeline plus Claude Code agent configuration and memory (.claude/agents, .claude/agent-memory, .claude/skills) that automate linting, testing, and AI-assisted development for this repository." + } + ] + }, + "nodeSummaryIndex": { + "file:models/audit.py": { + "name": "audit.py", + "type": "file", + "summary": "SQLAlchemy model for the AuditLog table that records before/after JSON state for every change made to BCs, DECs, and governance records." + }, + "file:models/bc.py": { + "name": "bc.py", + "type": "file", + "summary": "Core SQLAlchemy models for BiomedicalConcept and DataElementConcept, the central entities of the CDISC BC curation pipeline, including NCIt/LOINC coding fields and governance status." + }, + "file:models/ingestion.py": { + "name": "ingestion.py", + "type": "file", + "summary": "SQLAlchemy model for IngestionRecord, which stages parsed upload rows (with field mappings, confidence scores, and validation errors) in the DB prior to SME approval." + }, + "file:routes/audit.py": { + "name": "audit.py", + "type": "file", + "summary": "Flask blueprint exposing the read-only audit trail view with filtering by entity type, action, and actor, and pagination." + }, + "file:routes/ingestion.py": { + "name": "ingestion.py", + "type": "file", + "summary": "Flask blueprint implementing the file-upload ingestion workflow: parsing uploaded files, staging rows as IngestionRecord, and approving/rejecting them into BiomedicalConcept/DataElementConcept records with audit logging." + }, + "file:services/ingestion.py": { + "name": "ingestion.py", + "type": "file", + "summary": "Business logic for the ingestion pipeline: fuzzy field-name mapping via SequenceMatcher similarity scoring, BC/DEC row validation, multi-row grouping by BC ID, and XLSX/CSV/JSON parsers." + }, + "file:tests/conftest.py": { + "name": "conftest.py", + "type": "file", + "summary": "Pytest fixtures shared across the test suite: Flask app/test client factories, a per-test DB reset fixture, API cache clearing, and a sample BiomedicalConcept fixture." + }, + "file:tests/test_audit_routes.py": { + "name": "test_audit_routes.py", + "type": "file", + "summary": "Test suite for the audit log route, covering empty listing, log rendering, and filtering by entity type, action, actor, and pagination parameters." + }, + "file:tests/test_bc_routes.py": { + "name": "test_bc_routes.py", + "type": "file", + "summary": "Large test suite covering BC CRUD routes: listing/search, creation, detail view (including LOINC lookups), editing, clearing NCIt/LOINC codes, submission for review, deletion, export formats, and the CDISC Library detail page." + }, + "file:tests/test_governance_routes.py": { + "name": "test_governance_routes.py", + "type": "file", + "summary": "Test suite for the governance Kanban board routes: stage advancement, rejection, and XLSX export of stage-3 BCs with LOINC-derived system columns." + }, + "file:tests/test_ingestion_routes.py": { + "name": "test_ingestion_routes.py", + "type": "file", + "summary": "Test suite for the ingestion pipeline routes: file upload (CSV/JSON), duplicate BC flagging, per-record approve/reject, and bulk approve-all with audit logging." + }, + "file:tests/test_ingestion_service.py": { + "name": "test_ingestion_service.py", + "type": "file", + "summary": "Unit test suite for the ingestion service module, covering field similarity scoring, field mapping/matching, BC validation, deduplication, and XLSX/CSV/JSON parsing and grouping logic." + }, + "file:tests/test_models.py": { + "name": "test_models.py", + "type": "file", + "summary": "Unit tests for the AuditLog, IngestionRecord, and BiomedicalConcept ORM models, verifying JSON-backed property round-tripping, computed averages, and dictionary serialization." + }, + "file:models/governance.py": { + "name": "governance.py", + "type": "file", + "summary": "SQLAlchemy model for GovernanceRecord, tracking Biomedical Concept approval workflow stage transitions." + }, + "file:routes/bc.py": { + "name": "bc.py", + "type": "file", + "summary": "Flask blueprint implementing BC CRUD, NCIt/LOINC metadata lookup, export, and lifecycle actions (submit for review, delete) for Biomedical Concepts." + }, + "file:routes/governance.py": { + "name": "governance.py", + "type": "file", + "summary": "Flask blueprint for the governance Kanban board — viewing BCs by stage, exporting the published set, and advancing/rejecting BCs through the approval workflow." + }, + "file:routes/ncit.py": { + "name": "ncit.py", + "type": "file", + "summary": "Flask blueprint for NCI Thesaurus search and mapping to Biomedical Concepts, including AJAX search and single-concept detail lookups." + }, + "file:services/__init__.py": { + "name": "__init__.py", + "type": "file", + "summary": "Empty package marker enabling the services directory to be imported as a Python package." + }, + "file:services/audit.py": { + "name": "audit.py", + "type": "file", + "summary": "Provides the log_change helper used across services and routes to persist immutable AuditLog rows for every entity mutation." + }, + "file:services/bc_service.py": { + "name": "bc_service.py", + "type": "file", + "summary": "Core business logic for creating, updating, and managing Biomedical Concepts and their Data Element Concepts, including NCIt remapping/merging and review submission." + }, + "file:services/export.py": { + "name": "export.py", + "type": "file", + "summary": "Exporters that render Biomedical Concept collections to JSON, styled XLSX, governance XLSX, and CDISC ODM-XML formats." + }, + "file:services/governance_service.py": { + "name": "governance_service.py", + "type": "file", + "summary": "Implements governance stage transitions (advance/reject) for Biomedical Concepts, creating GovernanceRecord history rows and audit log entries." + }, + "file:services/ncit_api.py": { + "name": "ncit_api.py", + "type": "file", + "summary": "NCI Thesaurus EVS REST API client providing concept search and detailed concept retrieval (parents, semantic type, synonyms, definitions) with caching." + }, + "file:tests/test_export_service.py": { + "name": "test_export_service.py", + "type": "file", + "summary": "Unit tests covering JSON, XLSX, governance XLSX, and ODM-XML export functions in services/export.py, verifying headers, blanking rules, and document structure." + }, + "file:tests/test_ncit.py": { + "name": "test_ncit.py", + "type": "file", + "summary": "Unit tests for NCItApiClient.get_concept's extended metadata extraction and the /ncit concept-detail route's JSON responses and error handling." + }, + "file:routes/dashboard.py": { + "name": "dashboard.py", + "type": "file", + "summary": "Flask blueprint for the dashboard home page; fetches CDISC Library BC and specialization counts concurrently via a thread pool and combines them with local KPI stats (BC status counts, recent activity, audit log) for the landing page." + }, + "file:routes/loinc.py": { + "name": "loinc.py", + "type": "file", + "summary": "Flask blueprint exposing a single AJAX search endpoint that proxies term queries to the NLM LOINC API client and returns JSON results." + }, + "file:routes/specializations.py": { + "name": "specializations.py", + "type": "file", + "summary": "Flask blueprint managing CDISC dataset specializations: lists local and CDISC Library specializations, and supports manual creation, deletion, and auto-generation of variable-level specializations from a BC's data element concepts." + }, + "file:services/api_cache.py": { + "name": "api_cache.py", + "type": "file", + "summary": "Shared stale-tolerant in-memory cache helper used by the CDISC Library, NCI EVS, and LOINC API clients; serves fresh data for 5 minutes and falls back to stale data for up to an hour if a refresh fails, so a slow upstream never blanks a page that rendered fine a minute ago." + }, + "file:services/cdisc_api.py": { + "name": "cdisc_api.py", + "type": "file", + "summary": "REST client for the CDISC Library COSMOS API; wraps biomedical concept, specialization, and duplicate-check lookups with a 5-minute in-memory cache and provides a publish_bc method for pushing approved BCs to the Library." + }, + "file:services/loinc_api.py": { + "name": "loinc_api.py", + "type": "file", + "summary": "Client for the NLM Clinical Tables LOINC search API; builds the query with a configurable set of extra fields, optionally applies HTTP Basic Auth, and normalizes results through the shared API cache." + }, + "file:tests/test_cdisc_api_cache.py": { + "name": "test_cdisc_api_cache.py", + "type": "file", + "summary": "Unit tests for the shared cache helper and CDISCApiClient cache-key behavior, covering fresh/stale/expired cache states, refresh-failure fallback, and that raw API keys never leak into cache keys." + }, + "file:tests/test_loinc.py": { + "name": "test_loinc.py", + "type": "file", + "summary": "Unit tests for LoincApiClient.search normalization, extra-field handling, Basic Auth toggling, and error handling, plus tests for the /loinc/search Flask route's JSON responses." + }, + "file:app.py": { + "name": "app.py", + "type": "file", + "summary": "Flask application factory that configures logging, initializes the shared database and migration extensions, registers all eight feature blueprints, and doubles as the dev-server entry point via `python app.py`." + }, + "file:config.py": { + "name": "config.py", + "type": "file", + "summary": "Centralizes all environment-driven configuration for the Flask app, including the secret key, database URI, CDISC/NCIt API base URLs and credentials, upload size limit, and dev server port." + }, + "file:db_bootstrap.py": { + "name": "db_bootstrap.py", + "type": "file", + "summary": "Brings the app's database to the current Alembic migration head at startup, distinguishing fresh, legacy create_all(), and pre-squash-stamped databases, and raising if the schema predates the squashed baseline." + }, + "file:extensions.py": { + "name": "extensions.py", + "type": "file", + "summary": "Defines the shared SQLAlchemy `db` and Flask-Migrate `migrate` instances used across the app to avoid circular imports, and configures SQLite WAL mode with a busy timeout so the Flask app and MCP server can coexist as concurrent writers." + }, + "file:models/__init__.py": { + "name": "__init__.py", + "type": "file", + "summary": "Package marker for the `models` module that imports the shared `db` instance so all SQLAlchemy model classes register against a single metadata registry." + }, + "file:tests/test_dashboard.py": { + "name": "test_dashboard.py", + "type": "file", + "summary": "Unit tests for the dashboard route, verifying it renders correctly with live CDISC/NCIt API data, degrades gracefully on API errors and empty results, reflects local DB stats accurately, and issues both API calls concurrently." + }, + "file:tests/test_db_bootstrap.py": { + "name": "test_db_bootstrap.py", + "type": "file", + "summary": "Unit tests for `ensure_db()` covering a fresh baseline upgrade, stamping of a legacy create_all() database, restamping of a pre-squash-stamped database, raising on an outdated schema, and idempotency across repeated runs." + }, + "file:mcp_server/__main__.py": { + "name": "__main__.py", + "type": "file", + "summary": "Module entry point for `python -m mcp_server`; imports the server's main() coroutine runner and invokes it immediately to start the stdio MCP server loop." + }, + "file:mcp_server/server.py": { + "name": "server.py", + "type": "file", + "summary": "MCP server exposing the CDISC curation app's data and write operations as 14 stdio tools (8 read, 6 write); each call is dispatched into a Flask app context shared with the web app so both processes hit the same SQLite database and service layer." + }, + "file:models/specialization.py": { + "name": "specialization.py", + "type": "file", + "summary": "SQLAlchemy model for a dataset specialization (SDTM/CDASH variable-level metadata group) tied to a Biomedical Concept, storing its variable list as a JSON-serialized text column." + }, + "file:tests/test_mcp_server.py": { + "name": "test_mcp_server.py", + "type": "file", + "summary": "Unit tests for the MCP server's `_dispatch` routing and all 14 tool handlers, exercising them directly against a shared in-memory test Flask app (no stdio transport) and asserting on returned data plus AuditLog/GovernanceRecord side effects." + }, + "file:tests/test_specializations_routes.py": { + "name": "test_specializations_routes.py", + "type": "file", + "summary": "Route-level tests for the dataset specializations blueprint covering listing, creating, editing/upserting, deleting, detail views, and generating variables from DECs, including audit log verification and CDISC Library error handling." + }, + "pipeline:.github/workflows/ci.yml": { + "name": "ci.yml", + "type": "pipeline", + "summary": "GitHub Actions CI workflow that runs on every push and pull request across all branches: installs project and dev dependencies, lints with isort/black/flake8, and runs the pytest suite on Python 3.12." + }, + "document:.claude/agent-memory/cdisc-frontend-dev/MEMORY.md": { + "name": "MEMORY.md", + "type": "document", + "summary": "Index file for the cdisc-frontend-dev sub-agent's persistent memory, linking to the project foundation and frontend architecture notes." + }, + "document:.claude/agent-memory/cdisc-frontend-dev/frontend_architecture.md": { + "name": "frontend_architecture.md", + "type": "document", + "summary": "Persistent agent memory documenting the front-end template hierarchy, custom.css class naming conventions, main.js module functions, template-to-route mapping, and Jinja context variable conventions for the CDISC BC Curation UI." + }, + "document:.claude/agent-memory/cdisc-frontend-dev/project_foundation.md": { + "name": "project_foundation.md", + "type": "document", + "summary": "Persistent agent memory describing the Flask application factory pattern, registered blueprints, model and service locations, database setup, and key route implementation decisions for the CDISC BC Curation app." + }, + "document:.claude/agents/cdisc-concept-explorer.md": { + "name": "cdisc-concept-explorer.md", + "type": "document", + "summary": "Defines the cdisc-concept-explorer Claude subagent, a CDISC standards specialist that queries the live CDISC Library API to detect duplicate Biomedical Concepts against local drafts, enrich drafts with authoritative metadata, and find dataset specializations for a given BC." + }, + "document:.claude/agents/cdisc-frontend-dev.md": { + "name": "cdisc-frontend-dev.md", + "type": "document", + "summary": "Defines the cdisc-frontend-dev Claude subagent, a front-end specialist scoped to Jinja2 templates, CSS, vanilla JavaScript, and Flask view wiring for the BC curation tool, with mandatory CDISC brand-guideline compliance and an extensive persistent project-memory protocol appended to the spec." + }, + "document:.claude/agents/mike.md": { + "name": "mike.md", + "type": "document", + "summary": "Defines the mike Claude subagent, a project-manager assistant for cdisc-concept-curation that tracks daily progress in README-PROGRESS.md, prepares git commit messages, flags documentation drift against the code, and always asks before committing or pushing." + }, + "config:.mcp.json": { + "name": ".mcp.json", + "type": "config", + "summary": "Registers the cdisc-curation MCP server, launching it via the project virtualenv's Python interpreter running the `mcp_server` package as a module." + }, + "config:.pre-commit-config.yaml": { + "name": ".pre-commit-config.yaml", + "type": "config", + "summary": "Pre-commit hook configuration that runs isort, black, and flake8 on staged files and executes the full pytest suite before every commit." + }, + "document:CLAUDE.md": { + "name": "CLAUDE.md", + "type": "document", + "summary": "Guidance document for Claude Code covering the app's purpose, how to run it, linting and testing commands, the Route-Service-Model architecture with its 8 Flask blueprints and key services, the MCP server, and environment-variable configuration." + }, + "document:README-PROGRESS.md": { + "name": "README-PROGRESS.md", + "type": "document", + "summary": "Running project progress log tracking feature status and a dated changelog of fixes and workstream completions such as governance export corrections, audit trail coverage, and LOINC API explorer work." + }, + "document:README.md": { + "name": "README.md", + "type": "document", + "summary": "Primary project README covering prerequisites, installation, environment-variable configuration, running the app and tests, key screens, project structure, and the external CDISC/NCIt/LOINC APIs the platform integrates with." + }, + "config:pyproject.toml": { + "name": "pyproject.toml", + "type": "config", + "summary": "Tool configuration setting a shared 200-character line length for black (targeting Python 3.9-3.11) and isort (using the black-compatible profile)." + }, + "document:requirements-dev.txt": { + "name": "requirements-dev.txt", + "type": "document", + "summary": "Pinned development-only dependency list: black, flake8 (with mccabe/pycodestyle/pyflakes), isort, pre-commit, pytest, and pytest-flask, used for linting and testing." + }, + "document:requirements.txt": { + "name": "requirements.txt", + "type": "document", + "summary": "Pinned runtime dependency list for the Flask application, including Flask, Flask-SQLAlchemy, Flask-Migrate, SQLAlchemy, Alembic, pandas, openpyxl, lxml, requests, and the mcp SDK." + }, + "document:files/plan.md": { + "name": "plan.md", + "type": "document", + "summary": "Initial high-level project brief for the CDISC Biomedical Concept Curation application, specifying the target UI mockup to match, the key reference files to review, the required agents (cdisc-concept-explorer, ncit-concept-resolver, cdisc-frontend-dev, mike), and the constraint that only the CDISC_API_KEY environment variable be used for API authentication." + }, + "document:files/implementation.md": { + "name": "implementation.md", + "type": "document", + "summary": "Detailed implementation plan for the Flask/Jinja BC curation app, laying out the directory structure, SQLAlchemy data models, Flask blueprint/route table, service-layer API surface, template inventory, and a four-phase build roadmap from MVP through export and polish." + }, + "document:files/result.md": { + "name": "result.md", + "type": "document", + "summary": "Post-implementation status report confirming all 8 Flask routes return HTTP 200 and enumerating delivered capabilities: file ingestion with AI field mapping, BC CRUD, NCIt mapping, a 4-stage governance Kanban, an immutable audit trail, and JSON/XLSX/ODM-XML export." + }, + "file:templates/base.html": { + "name": "base.html", + "type": "file", + "summary": "Master Jinja2 layout providing the sidebar navigation, top bar, flash-message rendering, and template blocks (title, page_title, user_info, content, extra_js) that every other page in the app extends." + }, + "file:templates/audit.html": { + "name": "audit.html", + "type": "file", + "summary": "Audit trail page listing all curation actions (create/update/delete/advance/reject/publish/ingest) with actor/entity/date filters, CSV/JSON export links, expandable before/after diff rows, and pagination." + }, + "file:templates/bc_detail.html": { + "name": "bc_detail.html", + "type": "file", + "summary": "Create/edit form for a single Biomedical Concept covering identification, NCIt and LOINC lookups (with async metadata fetch in extra_js), classification, definition, metadata/status, and an editable Data Element Concepts (DECs) table." + }, + "file:templates/bc_list.html": { + "name": "bc_list.html", + "type": "file", + "summary": "Searchable, filterable, paginated table of locally curated Biomedical Concepts with status badges, JSON/XLSX/ODM-XML export links, and a delete-confirmation modal." + }, + "file:templates/dashboard.html": { + "name": "dashboard.html", + "type": "file", + "summary": "Main landing dashboard showing KPI cards, recent submissions, the governance pipeline snapshot, paginated/searchable views of CDISC Library BCs and dataset specializations, an AI curation pipeline progress tracker, and quick action links." + }, + "file:templates/governance.html": { + "name": "governance.html", + "type": "file", + "summary": "Four-column Kanban board (Provisional -> SME Review -> CDISC Approval -> Published) for advancing or rejecting Biomedical Concepts through the governance workflow, with summary counts and an export-filename modal." + }, + "file:templates/ingestion.html": { + "name": "ingestion.html", + "type": "file", + "summary": "File upload page (drag-and-drop or click) for XLSX/CSV/JSON ingestion, with a parsed-results queue table showing field-mapping confidence, validation errors, duplicate flags, and per-row approve/reject actions." + }, + "file:templates/library_bc_detail.html": { + "name": "library_bc_detail.html", + "type": "file", + "summary": "Read-only detail view of a single Biomedical Concept fetched from the published CDISC Library, showing identification, definition, LOINC coding, metadata, and its Data Element Concepts." + }, + "file:templates/library_spec_detail.html": { + "name": "library_spec_detail.html", + "type": "file", + "summary": "Read-only detail view of a Dataset Specialization from the published CDISC Library, showing identification/domain metadata and a full variables table (role, codelist, assigned term, mandatory flags, relationship)." + }, + "file:templates/ncit_mapping.html": { + "name": "ncit_mapping.html", + "type": "file", + "summary": "NCI Thesaurus search page plus a conflict-resolution queue listing Biomedical Concepts with unresolved or low-confidence NCIt mappings that block governance advancement, with a confidence-score legend." + }, + "file:templates/specializations.html": { + "name": "specializations.html", + "type": "file", + "summary": "Dataset Specialization management page with a collapsible create/edit form (VLM group ID, BC selector, SDTM/CDASH domain, dynamic variables table, generate-from-DEC-templates action) and a list/edit/delete table of existing specializations." + }, + "document:.claude/agent-memory/cdisc-concept-explorer/MEMORY.md": { + "name": "MEMORY.md", + "type": "document", + "summary": "Single-line agent memory index for the cdisc-concept-explorer agent, pointing to the verified CDISC API reference notes." + }, + "document:.claude/agent-memory/cdisc-concept-explorer/reference_api_endpoints.md": { + "name": "reference_api_endpoints.md", + "type": "document", + "summary": "Verified reference notes on the CDISC Library COSMOS v2 API: base URL, working endpoints, lack of pagination, and field names for biomedical concept and dataset specialization records." + }, + "document:.claude/skills/code-review/SKILL.md": { + "name": "SKILL.md", + "type": "document", + "summary": "Project-specific code review checklist for cdisc-concept-curation covering route/service/model layering, audit log coverage, Alembic migration discipline, API client error handling, test isolation, and TDD conventions." + }, + "document:.claude/skills/run-concept-curation/SKILL.md": { + "name": "SKILL.md", + "type": "document", + "summary": "Instructions for running and smoke-testing the Flask dev server, including the preferred automated smoke-test script, manual startup steps, and troubleshooting tips." + }, + "file:.claude/skills/run-concept-curation/smoke.sh": { + "name": "smoke.sh", + "type": "file", + "summary": "Shell script that boots the Flask app on a throwaway SQLite database and port, runs a battery of HTTP checks against key routes (dashboard, BC CRUD, governance, NCIt, audit, ingestion, specializations, export), and tears the server down." + }, + "config:.flake8": { + "name": ".flake8", + "type": "config", + "summary": "Flake8 lint configuration setting max line length to 200, ignoring black-conflicting rules (E203/W503) and SQLAlchemy's `== None` idiom (E711), and excluding build/venv directories." + }, + "config:.hintrc": { + "name": ".hintrc", + "type": "config", + "summary": "webhint configuration extending the 'development' ruleset and disabling the no-inline-styles rule for HTML/CSS linting." + }, + "config:.ua/.understandignore": { + "name": ".understandignore", + "type": "config", + "summary": "Gitignore-style exclusion list for the Understand-Anything analysis tool, with commented-out suggestions mirroring the project's .gitignore (Python caches, virtualenvs, egg metadata) that can be uncommented to activate." + }, + "config:.ua/config.json": { + "name": "config.json", + "type": "config", + "summary": "Single-key configuration for the Understand-Anything tool setting the output language for generated analysis text to English." + }, + "file:files/BC Curation Principles and Completion GLs.xlsx": { + "name": "BC Curation Principles and Completion GLs.xlsx", + "type": "file", + "summary": "Reference spreadsheet documenting BC curation principles and field-completion guidelines used by curators when authoring Biomedical Concepts." + }, + "file:files/BC DEC Templates.xlsx": { + "name": "BC DEC Templates.xlsx", + "type": "file", + "summary": "Template spreadsheet defining the standard row/column layout for Data Element Concepts, used as an ingestion source template for curators." + }, + "file:files/BC Examples.xlsx": { + "name": "BC Examples.xlsx", + "type": "file", + "summary": "Example spreadsheet of fully-populated Biomedical Concepts, used as a reference/sample dataset for the ingestion pipeline and curator training." + }, + "file:files/BC Governance.docx": { + "name": "BC Governance.docx", + "type": "file", + "summary": "Word document describing the governance process and stages (Provisional, SME Review, CDISC Approval, Published) that a curated Biomedical Concept moves through." + }, + "file:mcp_server/__init__.py": { + "name": "__init__.py", + "type": "file", + "summary": "Package marker and docstring for the mcp_server package, noting it is run via `python -m mcp_server` and deliberately named to avoid shadowing the MCP SDK package." + }, + "document:migrations/README": { + "name": "README", + "type": "document", + "summary": "One-line note identifying the migrations directory as a single-database Flask-Migrate/Alembic configuration." + }, + "config:migrations/alembic.ini": { + "name": "alembic.ini", + "type": "config", + "summary": "Alembic configuration file controlling migration script location, logging setup, and version-file naming for the Flask-Migrate managed schema." + }, + "file:migrations/env.py": { + "name": "env.py", + "type": "file", + "summary": "Alembic environment script that wires Flask-Migrate's engine/metadata into Alembic's migration context, supporting both offline (SQL-only) and online (live-connection) migration runs." + }, + "file:migrations/script.py.mako": { + "name": "script.py.mako", + "type": "file", + "summary": "Mako template used by `flask db migrate` to scaffold new Alembic revision files with the standard revision metadata and upgrade/downgrade function stubs." + }, + "file:migrations/versions/51d4a009d291_baseline_initial_schema.py": { + "name": "51d4a009d291_baseline_initial_schema.py", + "type": "file", + "summary": "Baseline Alembic migration that creates the initial database schema: audit_logs, biomedical_concepts, ingestion_records, data_element_concepts, dataset_specializations, and governance_records tables, with their downgrade counterparts." + }, + "file:routes/__init__.py": { + "name": "__init__.py", + "type": "file", + "summary": "Empty package marker file that makes routes/ a Python package for the Flask blueprint modules it contains." + }, + "file:static/css/custom.css": { + "name": "custom.css", + "type": "file", + "summary": "Main application stylesheet defining design tokens (colors, spacing), base resets, app shell layout (sidebar, topbar, content area), and component styles (cards, flash messages, nav items, badges) for the CDISC BC curation UI." + }, + "file:static/js/main.js": { + "name": "main.js", + "type": "file", + "summary": "Client-side interactivity module for the BC curation platform: flash-message dismissal, drag-and-drop file upload, NCIt/LOINC lookup and search panels, dynamic DEC table rows, governance Kanban actions, audit-diff toggles, and shared fetch/toast helpers, all wired up on DOMContentLoaded." + }, + "file:tests/__init__.py": { + "name": "__init__.py", + "type": "file", + "summary": "Empty package marker file that makes tests/ a discoverable Python package for pytest." + }, + "file:files/cdisc_bc_ai_platform.html": { + "name": "cdisc_bc_ai_platform.html", + "type": "file", + "summary": "No summary available" + }, + "file:files/cdisc_smart_goals_infographic.html": { + "name": "cdisc_smart_goals_infographic.html", + "type": "file", + "summary": "No summary available" + } + }, + "totalNodes": 99, + "totalEdges": 214 +} \ No newline at end of file diff --git a/.ua/.trash-1785851745/tour.json b/.ua/.trash-1785851745/tour.json new file mode 100644 index 0000000..2d24b16 --- /dev/null +++ b/.ua/.trash-1785851745/tour.json @@ -0,0 +1,110 @@ +[ + { + "order": 1, + "title": "Project Overview", + "description": "The README explains the platform's purpose: replacing spreadsheet-and-email workflows for curating CDISC Biomedical Concepts (BCs) with a structured pipeline that goes from file ingestion through AI-assisted field mapping, SME review, governance approval, and publication to the CDISC Library. It documents prerequisites, installation, and the external CDISC/NCIt/LOINC APIs the app integrates with, giving you the map before you look at any code.", + "nodeIds": ["document:README.md"] + }, + { + "order": 2, + "title": "Application Entry Point", + "description": "app.py is the Flask application factory: it configures logging, initializes the shared database and migration extensions, registers all eight feature blueprints, and doubles as the dev-server entry point run via `python app.py`. Every other module you will see in this tour is wired together from this one file, making it the natural starting point for reading the codebase.", + "nodeIds": ["file:app.py"], + "languageLesson": "Flask's application-factory pattern (a `create_app()` function that builds and configures the app object) keeps extension setup and blueprint registration in one controlled place, which makes the app testable without import-time side effects." + }, + { + "order": 3, + "title": "Shared Extensions and Config", + "description": "extensions.py defines the shared SQLAlchemy `db` and Flask-Migrate `migrate` instances that every model and route imports from, avoiding the circular-import problems that arise when models import an app-bound `db` directly. config.py centralizes every environment-driven setting (secret key, database URI, CDISC/NCIt API bases, upload limits) that app.py loads at startup. Together they are the most depended-upon files in the project — extensions.py alone is imported by 30 other nodes.", + "nodeIds": ["file:extensions.py", "file:config.py"], + "languageLesson": "Splitting shared singletons (db, migrate) into their own module — rather than defining them alongside the app or the models — is a common Flask idiom for breaking circular-import cycles between app.py and models/." + }, + { + "order": 4, + "title": "Database Bootstrapping and Migrations", + "description": "db_bootstrap.py brings the SQLite database to the current Alembic migration head on every startup, distinguishing fresh databases from legacy create_all() databases and pre-baseline databases that need stamping. The baseline migration and Alembic's env.py show how that head state is defined and applied — this is the schema that the models in the next step map onto.", + "nodeIds": [ + "file:db_bootstrap.py", + "config:migrations/alembic.ini", + "file:migrations/env.py", + "file:migrations/versions/51d4a009d291_baseline_initial_schema.py", + "document:migrations/README" + ], + "languageLesson": "Alembic migrations should be incremental and reversible: each revision file pairs an `upgrade()` with a `downgrade()`, letting the schema evolve forward or roll back without hand-editing the database." + }, + { + "order": 5, + "title": "Core Data Models", + "description": "These five SQLAlchemy models are the domain vocabulary of the entire application. BiomedicalConcept and DataElementConcept (models/bc.py) are the central entities being curated; IngestionRecord stages parsed upload rows before approval; GovernanceRecord tracks approval-workflow stage transitions; AuditLog records before/after JSON state for every change; and Specialization stores SDTM/CDASH variable groups. models/bc.py has the highest fan-in of any model (19), reflecting how central it is to routes and services alike.", + "nodeIds": [ + "file:models/bc.py", + "file:models/governance.py", + "file:models/ingestion.py", + "file:models/audit.py", + "file:models/specialization.py" + ] + }, + { + "order": 6, + "title": "API Layer: Flask Blueprints", + "description": "Building on the models from Step 5, these route modules are the HTTP surface of the app. routes/bc.py handles BC CRUD, NCIt/LOINC lookup, and export — it has the highest fan-out of any route module because it touches nearly every service. routes/ingestion.py drives the upload-to-queue workflow, routes/governance.py implements the Kanban approval board, and routes/dashboard.py aggregates KPI stats from both the local database and the live CDISC Library API.", + "nodeIds": [ + "file:routes/bc.py", + "file:routes/ingestion.py", + "file:routes/governance.py", + "file:routes/dashboard.py" + ], + "languageLesson": "Flask blueprints let each feature area register its own routes, templates, and static assets under a URL prefix, then get wired into the app in one place (app.py) — a lightweight alternative to a monolithic routes file." + }, + { + "order": 7, + "title": "Service Layer: Core Business Logic", + "description": "Routes stay thin by delegating to the service layer. services/bc_service.py implements creating, updating, and merging Biomedical Concepts, including NCIt remapping and review submission. services/governance_service.py implements the advance/reject transitions that move a BC through Provisional → SME Review → CDISC Approval → Published, and services/audit.py provides the single log_change() helper both of the others call to write immutable AuditLog rows for every mutation.", + "nodeIds": [ + "file:services/bc_service.py", + "file:services/governance_service.py", + "file:services/audit.py" + ] + }, + { + "order": 8, + "title": "The Ingestion Pipeline", + "description": "services/ingestion.py is where the AI-assisted field mapping mentioned in the README actually happens: it parses uploaded XLSX/CSV/JSON files, groups rows by BC ID to handle multi-row DECs, and uses fuzzy string matching to assign canonical field names with confidence scores before rows are staged as IngestionRecord entries (Step 5) for SME approval via routes/ingestion.py (Step 6). This module is the core differentiator that replaces the manual spreadsheet workflow the project was built to eliminate.", + "nodeIds": ["file:services/ingestion.py"], + "languageLesson": "Python's difflib.SequenceMatcher computes a similarity ratio between strings without any ML model — a lightweight way to fuzzy-match inconsistent spreadsheet column names against a canonical field vocabulary." + }, + { + "order": 9, + "title": "External API Clients", + "description": "These four modules wrap the platform's three external data sources. services/cdisc_api.py talks to the CDISC Library COSMOS API for BC/specialization lookups and publishing; services/ncit_api.py queries the NCI Thesaurus EVS for concept search; and services/loinc_api.py queries NLM's LOINC search API. All three share services/api_cache.py, a stale-tolerant in-memory cache that serves fresh data for 5 minutes and falls back to stale data for up to an hour if a refresh fails, keeping the UI responsive even when an upstream API is slow.", + "nodeIds": [ + "file:services/cdisc_api.py", + "file:services/ncit_api.py", + "file:services/loinc_api.py", + "file:services/api_cache.py" + ] + }, + { + "order": 10, + "title": "UI Layer: Templates and Styling", + "description": "Every page the routes from Step 6 render extends templates/base.html, the master Jinja2 layout providing sidebar navigation, the top bar, flash-message rendering, and named template blocks that individual pages fill in. static/css/custom.css defines the design tokens (colors, spacing) and component styles — cards, badges, nav items — that give those pages a consistent look across the whole app.", + "nodeIds": ["file:templates/base.html", "file:static/css/custom.css"], + "languageLesson": "Jinja2 template inheritance (`{% extends %}` plus named `{% block %}` regions) lets every page share one layout while overriding only the content it needs, avoiding duplicated HTML across the eleven page templates." + }, + { + "order": 11, + "title": "The MCP Server: A Second API Surface", + "description": "Alongside the Flask HTTP routes, mcp_server/server.py exposes the same underlying data and write operations as 14 stdio-based MCP tools (8 read, 6 write) for AI agents, dispatching each call into a Flask app context so it shares the exact same SQLite database and service-layer code path as the web app — writes go through the same services/bc_service.py and services/governance_service.py from Step 7. mcp_server/__main__.py is the process entry point, invoked via `python -m mcp_server`, mirroring app.py's role for the web app in Step 2.", + "nodeIds": ["file:mcp_server/server.py", "file:mcp_server/__main__.py"] + }, + { + "order": 12, + "title": "Quality Gates: CI and Pre-commit", + "description": "The GitHub Actions workflow runs on every push and pull request: it installs the project and dev dependencies, lints with isort/black/flake8, and runs the full pytest suite on Python 3.12 — the same commands documented for local development. .pre-commit-config.yaml enforces the identical checks locally before a commit is even created, so the same rules from Step 2 through Step 11's code all get verified automatically before they ever reach CI.", + "nodeIds": [ + "pipeline:.github/workflows/ci.yml", + "config:.pre-commit-config.yaml" + ], + "languageLesson": "GitHub Actions workflows are declared with `on` triggers, `jobs` (parallel by default), and `steps` (sequential within a job) — this project's single job runs lint and test steps sequentially on one Python version rather than a matrix build." + } +] diff --git a/.ua/.understandignore b/.ua/.understandignore new file mode 100644 index 0000000..c31a5f6 --- /dev/null +++ b/.ua/.understandignore @@ -0,0 +1,110 @@ +# .understandignore — patterns for files/dirs to exclude from analysis +# Syntax: same as .gitignore (globs, # comments, ! negation, trailing / for dirs) +# Lines below are suggestions — uncomment to activate. +# Use ! prefix to force-include something excluded by defaults. +# +# Built-in defaults (always excluded unless negated): +# node_modules/, .git/, dist/, build/, obj/, *.lock, *.min.js, etc. +# + +# --- From .gitignore (uncomment to exclude) --- + +**/__pycache__/ +*.py[cod] +*$py.class +ENV/ +env/ +develop-eggs/ +.eggs/ +*.egg-info/ +*.egg +pip-log.txt +pip-delete-this-directory.txt +.coverage* +coverage.xml +htmlcov/ +.tox/ +.nox/ +.pytest_cache/ +.mypy_cache/ +.pyre/ +.dmypy.json +.ruff_cache/ +.ipynb_checkpoints/ +.DS_Store +Thumbs.db +*.db +*.db-shm +*.db-wal +*.sqlite +.env +.env.* +# normalized/schedule_instances.csv +# normalized/*.json +# *.tmp +# *.bak +# .prof +# *.manifest +# *.spec +# *.whl +# .python-version +# files/cdisc_bc_ai_platform.html +# files/cdisc_smart_goals_infographic.html +# .claude/settings.json +# .claude/settings.local.json + +# --- Detected directories (uncomment to exclude) --- + +# migrations/ +# tests/ + +# --- Test file patterns (uncomment to exclude) --- + +# JS / TS +# *.test.* +# *.spec.* +# *.snap +# C# / .NET +# **/*Tests.cs +# **/*Test.cs +# **/*Fixture.cs +# **/*.Tests.csproj +# Java / Kotlin +# **/src/test/** +# **/*Test.java +# **/*IT.java +# **/*Spec.kt +# Go +# **/*_test.go +# C++ +# **/*_test.cc +# **/*_test.cpp +# **/*_test.cxx +# **/*Test.cc +# **/*Test.cpp +# **/*_unittest.cc +# **/*_unittest.cpp +# **/*_browsertest.cc +# **/*_benchmark.cc +# **/*Benchmark.cpp +# Python +# **/test_*.py +# **/*_test.py +# **/tests.py +# **/conftest.py +# Rust +# **/tests.rs +# **/test_*.rs +# **/*_test.rs +# **/bench_*.rs +# **/*_bench.rs +# Ruby +# **/*_spec.rb +# **/*_test.rb +# **/test_*.rb +# **/spec_helper.rb +# **/test_helper.rb +# **/rails_helper.rb +# Swift +# **/Tests/**/*.swift +# **/Specs/**/*.swift diff --git a/.ua/config.json b/.ua/config.json new file mode 100644 index 0000000..36b8b38 --- /dev/null +++ b/.ua/config.json @@ -0,0 +1 @@ +{"outputLanguage": "en"} diff --git a/.ua/fingerprints.json b/.ua/fingerprints.json new file mode 100644 index 0000000..49659b5 --- /dev/null +++ b/.ua/fingerprints.json @@ -0,0 +1,4505 @@ +{ + "version": "1.0.0", + "gitCommitHash": "8df239f9988f09a9476e634ceb7cac80c29f72fe", + "generatedAt": "2026-08-04T12:51:44.114Z", + "files": { + ".claude/agent-memory/cdisc-concept-explorer/MEMORY.md": { + "filePath": ".claude/agent-memory/cdisc-concept-explorer/MEMORY.md", + "contentHash": "ee417c274518ef2aacebe6beedbd4cdb922f4bbbe70fc17aa9097b09559de658", + "functions": [], + "classes": [], + "imports": [], + "exports": [], + "totalLines": 2, + "hasStructuralAnalysis": true + }, + ".claude/agent-memory/cdisc-concept-explorer/reference_api_endpoints.md": { + "filePath": ".claude/agent-memory/cdisc-concept-explorer/reference_api_endpoints.md", + "contentHash": "5c481ee629243bd21285e2598ee6020d3067d2a4b847309416bc4ab6e8297f99", + "functions": [], + "classes": [], + "imports": [], + "exports": [], + "totalLines": 37, + "hasStructuralAnalysis": true + }, + ".claude/agent-memory/cdisc-frontend-dev/MEMORY.md": { + "filePath": ".claude/agent-memory/cdisc-frontend-dev/MEMORY.md", + "contentHash": "9cfc5fe6aaa907e2cdc699cfd2603999336adbd10903d1f0a8603977dd4873fe", + "functions": [], + "classes": [], + "imports": [], + "exports": [], + "totalLines": 3, + "hasStructuralAnalysis": true + }, + ".claude/agent-memory/cdisc-frontend-dev/frontend_architecture.md": { + "filePath": ".claude/agent-memory/cdisc-frontend-dev/frontend_architecture.md", + "contentHash": "3b05b5895c38aad4b300845ff70e1ca4c32677c7dcbed8d89d9f7ae1473961ca", + "functions": [], + "classes": [], + "imports": [], + "exports": [], + "totalLines": 66, + "hasStructuralAnalysis": true + }, + ".claude/agent-memory/cdisc-frontend-dev/project_foundation.md": { + "filePath": ".claude/agent-memory/cdisc-frontend-dev/project_foundation.md", + "contentHash": "e8eb26ab98afe12f21d9c17642ace96e6a8c9aa4b17401fb554388b4e58f6891", + "functions": [], + "classes": [], + "imports": [], + "exports": [], + "totalLines": 55, + "hasStructuralAnalysis": true + }, + ".claude/agents/cdisc-concept-explorer.md": { + "filePath": ".claude/agents/cdisc-concept-explorer.md", + "contentHash": "f907dbdf954f653dd35e03e0d7b6fffbb38c1c490365a4554b7650eea7821f9b", + "functions": [], + "classes": [], + "imports": [], + "exports": [], + "totalLines": 68, + "hasStructuralAnalysis": true + }, + ".claude/agents/cdisc-frontend-dev.md": { + "filePath": ".claude/agents/cdisc-frontend-dev.md", + "contentHash": "573541a9606aa19a355ee658d195192d4eaa6fa100144c687ada0eafd0d0a31d", + "functions": [], + "classes": [], + "imports": [], + "exports": [], + "totalLines": 208, + "hasStructuralAnalysis": true + }, + ".claude/agents/mike.md": { + "filePath": ".claude/agents/mike.md", + "contentHash": "588c5031dd5cf81a75989f12a447bf96e326219a64f90bcf173fa52d97178073", + "functions": [], + "classes": [], + "imports": [], + "exports": [], + "totalLines": 109, + "hasStructuralAnalysis": true + }, + ".claude/skills/code-review/SKILL.md": { + "filePath": ".claude/skills/code-review/SKILL.md", + "contentHash": "349bb5d0210ddbd16465458913f4b7bc8d69c3b4ac4618fd6669e8211370520d", + "functions": [], + "classes": [], + "imports": [], + "exports": [], + "totalLines": 60, + "hasStructuralAnalysis": true + }, + ".claude/skills/run-concept-curation/SKILL.md": { + "filePath": ".claude/skills/run-concept-curation/SKILL.md", + "contentHash": "9165f7fa93e935395a2284ee844fd74d73f849d90cec18ac8b83f706b8956a66", + "functions": [], + "classes": [], + "imports": [], + "exports": [], + "totalLines": 50, + "hasStructuralAnalysis": true + }, + ".claude/skills/run-concept-curation/smoke.sh": { + "filePath": ".claude/skills/run-concept-curation/smoke.sh", + "contentHash": "3ebfae9eed4b581af79400d637b56a2fd9d304838c802a92e7a61c00ecb77785", + "functions": [ + { + "name": "cleanup", + "params": [], + "exported": false, + "lineCount": 7 + }, + { + "name": "check", + "params": [], + "exported": false, + "lineCount": 10 + }, + { + "name": "get_code", + "params": [], + "exported": false, + "lineCount": 3 + }, + { + "name": "post_code", + "params": [], + "exported": false, + "lineCount": 4 + } + ], + "classes": [], + "imports": [], + "exports": [], + "totalLines": 91, + "hasStructuralAnalysis": true + }, + ".flake8": { + "filePath": ".flake8", + "contentHash": "50e829a2c17f0fbf402c844a6f69e9951ea71d000d06f2f02db81e3d844d34ef", + "functions": [], + "classes": [], + "imports": [], + "exports": [], + "totalLines": 17, + "hasStructuralAnalysis": false + }, + ".github/workflows/ci.yml": { + "filePath": ".github/workflows/ci.yml", + "contentHash": "f32bc27dcfc08f2c7689eba8e3061540c2c36a55ed61ab6e2648efbf2aa6a7a1", + "functions": [], + "classes": [], + "imports": [], + "exports": [], + "totalLines": 27, + "hasStructuralAnalysis": true + }, + ".hintrc": { + "filePath": ".hintrc", + "contentHash": "8eb612a502d2b7e4802fa225cf718b1f5a4c0c2522218a0c74ae443add44d90b", + "functions": [], + "classes": [], + "imports": [], + "exports": [], + "totalLines": 8, + "hasStructuralAnalysis": false + }, + ".mcp.json": { + "filePath": ".mcp.json", + "contentHash": "22dbf241a5b03191333d6e14f2663e82feed1f38c5376e344fb5f8e8f3671288", + "functions": [], + "classes": [], + "imports": [], + "exports": [], + "totalLines": 10, + "hasStructuralAnalysis": true + }, + ".pre-commit-config.yaml": { + "filePath": ".pre-commit-config.yaml", + "contentHash": "9477fce18a5b3b6145e4971ea89b78919283632dbfc98226861cb757b637e0bb", + "functions": [], + "classes": [], + "imports": [], + "exports": [], + "totalLines": 25, + "hasStructuralAnalysis": true + }, + ".ua/.understandignore": { + "filePath": ".ua/.understandignore", + "contentHash": "121b228fa2b76f7ac38af1fa7d27cf3adf72f84fd8f02e1c2e760ba67889eb2e", + "functions": [], + "classes": [], + "imports": [], + "exports": [], + "totalLines": 111, + "hasStructuralAnalysis": false + }, + ".ua/config.json": { + "filePath": ".ua/config.json", + "contentHash": "e6e1895a8e3cb71f76daebccd84c4fc9877d8a88fb75e476ca22a320e69d4995", + "functions": [], + "classes": [], + "imports": [], + "exports": [], + "totalLines": 2, + "hasStructuralAnalysis": true + }, + "CLAUDE.md": { + "filePath": "CLAUDE.md", + "contentHash": "b5e73debeb62dbb875b1c392949ee2b17335d15f89c6a47ea9c80dbe422ad5a0", + "functions": [], + "classes": [], + "imports": [], + "exports": [], + "totalLines": 129, + "hasStructuralAnalysis": true + }, + "README-PROGRESS.md": { + "filePath": "README-PROGRESS.md", + "contentHash": "0e85f64fe2ea380bb65d159d2a439ed5a8f6d369883e6fadc2000f827b5e93a6", + "functions": [], + "classes": [], + "imports": [], + "exports": [], + "totalLines": 148, + "hasStructuralAnalysis": true + }, + "README.md": { + "filePath": "README.md", + "contentHash": "d590246a42fa91bf021240bc7d3290e5a6d12d3f9160682c2a68fa26f9e069d8", + "functions": [], + "classes": [], + "imports": [], + "exports": [], + "totalLines": 186, + "hasStructuralAnalysis": true + }, + "app.py": { + "filePath": "app.py", + "contentHash": "b7ebda67cac87f9e627d9425dd04b869da56df48fdaa564c6c0c4380fdcf02cd", + "functions": [ + { + "name": "_configure_logging", + "params": [], + "exported": true, + "lineCount": 12 + }, + { + "name": "create_app", + "params": [ + "config_class" + ], + "exported": true, + "lineCount": 27 + } + ], + "classes": [], + "imports": [ + { + "source": "logging", + "specifiers": [ + "logging" + ] + }, + { + "source": "os", + "specifiers": [ + "os" + ] + }, + { + "source": "flask", + "specifiers": [ + "Flask" + ] + }, + { + "source": "config", + "specifiers": [ + "Config" + ] + }, + { + "source": "extensions", + "specifiers": [ + "db", + "migrate" + ] + } + ], + "exports": [ + "_configure_logging", + "create_app" + ], + "totalLines": 60, + "hasStructuralAnalysis": true + }, + "config.py": { + "filePath": "config.py", + "contentHash": "920708fe5857bc3bd8adb9cde0ad718dbf83a21e1ab53562956ddb10200190b2", + "functions": [], + "classes": [ + { + "name": "Config", + "methods": [], + "properties": [], + "exported": true, + "lineCount": 11 + } + ], + "imports": [ + { + "source": "os", + "specifiers": [ + "os" + ] + } + ], + "exports": [ + "Config" + ], + "totalLines": 15, + "hasStructuralAnalysis": true + }, + "db_bootstrap.py": { + "filePath": "db_bootstrap.py", + "contentHash": "1d43ffaa14c25aeffa4fd9239e2553d8d26360f5a94d102a7d06d60665ecc613", + "functions": [ + { + "name": "_schema_is_current", + "params": [ + "inspector" + ], + "exported": true, + "lineCount": 3 + }, + { + "name": "ensure_db", + "params": [ + "app" + ], + "exported": true, + "lineCount": 36 + } + ], + "classes": [], + "imports": [ + { + "source": "logging", + "specifiers": [ + "logging" + ] + }, + { + "source": "flask_migrate", + "specifiers": [ + "stamp", + "upgrade" + ] + }, + { + "source": "sqlalchemy", + "specifiers": [ + "inspect", + "text" + ] + }, + { + "source": "extensions", + "specifiers": [ + "db" + ] + } + ], + "exports": [ + "_schema_is_current", + "ensure_db" + ], + "totalLines": 72, + "hasStructuralAnalysis": true + }, + "extensions.py": { + "filePath": "extensions.py", + "contentHash": "a9f773e061558d1cab03e9b47d20cc956bfbe0ce685afddac72da466abd28d0a", + "functions": [ + { + "name": "_set_sqlite_pragmas", + "params": [ + "dbapi_connection", + "connection_record" + ], + "exported": true, + "lineCount": 13 + } + ], + "classes": [], + "imports": [ + { + "source": "sqlite3", + "specifiers": [ + "sqlite3" + ] + }, + { + "source": "flask_migrate", + "specifiers": [ + "Migrate" + ] + }, + { + "source": "flask_sqlalchemy", + "specifiers": [ + "SQLAlchemy" + ] + }, + { + "source": "sqlalchemy", + "specifiers": [ + "event" + ] + }, + { + "source": "sqlalchemy.engine", + "specifiers": [ + "Engine" + ] + } + ], + "exports": [ + "_set_sqlite_pragmas" + ], + "totalLines": 26, + "hasStructuralAnalysis": true + }, + "files/BC Curation Principles and Completion GLs.xlsx": { + "filePath": "files/BC Curation Principles and Completion GLs.xlsx", + "contentHash": "f88a580abd70d7d5ea448a51a53354511cad86b5e8e7f0a23dff33f8bc5e8f6b", + "functions": [], + "classes": [], + "imports": [], + "exports": [], + "totalLines": 183, + "hasStructuralAnalysis": false + }, + "files/BC DEC Templates.xlsx": { + "filePath": "files/BC DEC Templates.xlsx", + "contentHash": "307bcc306a110b44187885c1cf7647721c3757bb146d1881516c6ef819722247", + "functions": [], + "classes": [], + "imports": [], + "exports": [], + "totalLines": 95, + "hasStructuralAnalysis": false + }, + "files/BC Examples.xlsx": { + "filePath": "files/BC Examples.xlsx", + "contentHash": "b73c17e958fb8ea05ce4455c7d97863099841930fee5104eaff831c1992a36c8", + "functions": [], + "classes": [], + "imports": [], + "exports": [], + "totalLines": 333, + "hasStructuralAnalysis": false + }, + "files/BC Governance.docx": { + "filePath": "files/BC Governance.docx", + "contentHash": "5f2bbfc7ab96f59e3be77a623c6bf7685ecdacbdea292595a439579cbd90e6bf", + "functions": [], + "classes": [], + "imports": [], + "exports": [], + "totalLines": 55, + "hasStructuralAnalysis": false + }, + "files/implementation.md": { + "filePath": "files/implementation.md", + "contentHash": "1c0944818a2b8137476499791c5d9001b1f727985cd95fcb4a6ba2f7b46e8669", + "functions": [], + "classes": [], + "imports": [], + "exports": [], + "totalLines": 206, + "hasStructuralAnalysis": true + }, + "files/plan.md": { + "filePath": "files/plan.md", + "contentHash": "00dc791078a0ea8662ee8742894ea677f4a9c628886a01b81543c1fd9404b3a5", + "functions": [], + "classes": [], + "imports": [], + "exports": [], + "totalLines": 48, + "hasStructuralAnalysis": true + }, + "files/result.md": { + "filePath": "files/result.md", + "contentHash": "3437e47769e0f7ed3cc128456fae9e4ee1a03787e9761a74b061a178d787d53d", + "functions": [], + "classes": [], + "imports": [], + "exports": [], + "totalLines": 28, + "hasStructuralAnalysis": true + }, + "mcp_server/__init__.py": { + "filePath": "mcp_server/__init__.py", + "contentHash": "7dd8aa0295c37b61193777401163e286d48d8f105cda0268504ea5834ad64a99", + "functions": [], + "classes": [], + "imports": [], + "exports": [], + "totalLines": 6, + "hasStructuralAnalysis": true + }, + "mcp_server/__main__.py": { + "filePath": "mcp_server/__main__.py", + "contentHash": "425871ea4a94a774abe503f0f3ba0b46a45e352d5154fd5abba2b486fa2fa7d3", + "functions": [], + "classes": [], + "imports": [ + { + "source": "mcp_server.server", + "specifiers": [ + "main" + ] + } + ], + "exports": [], + "totalLines": 4, + "hasStructuralAnalysis": true + }, + "mcp_server/server.py": { + "filePath": "mcp_server/server.py", + "contentHash": "9f2497cc3778a3fe9ae96817297a9d47bc2f7756067c537c971befe2c9b78100", + "functions": [ + { + "name": "_get_app", + "params": [], + "exported": true, + "lineCount": 9 + }, + { + "name": "_with_app_context", + "params": [ + "fn" + ], + "exported": true, + "lineCount": 13 + }, + { + "name": "list_tools", + "params": [], + "returnType": "list[types.Tool]", + "exported": true, + "lineCount": 2 + }, + { + "name": "call_tool", + "params": [ + "name", + "arguments" + ], + "returnType": "list[types.TextContent]", + "exported": true, + "lineCount": 4 + }, + { + "name": "_dispatch", + "params": [ + "name", + "args" + ], + "returnType": "Any", + "exported": true, + "lineCount": 21 + }, + { + "name": "_bc_full_dict", + "params": [ + "bc" + ], + "exported": true, + "lineCount": 13 + }, + { + "name": "_list_bcs", + "params": [ + "args" + ], + "returnType": "dict", + "exported": true, + "lineCount": 21 + }, + { + "name": "_get_bc", + "params": [ + "args" + ], + "returnType": "dict", + "exported": true, + "lineCount": 46 + }, + { + "name": "_search_ncit", + "params": [ + "args" + ], + "returnType": "list", + "exported": true, + "lineCount": 8 + }, + { + "name": "_get_ncit_concept", + "params": [ + "args" + ], + "returnType": "dict", + "exported": true, + "lineCount": 7 + }, + { + "name": "_search_loinc", + "params": [ + "args" + ], + "returnType": "list", + "exported": true, + "lineCount": 8 + }, + { + "name": "_search_cdisc_library", + "params": [ + "args" + ], + "returnType": "list", + "exported": true, + "lineCount": 10 + }, + { + "name": "_get_library_bc", + "params": [ + "args" + ], + "returnType": "dict", + "exported": true, + "lineCount": 7 + }, + { + "name": "_list_review_queue", + "params": [ + "_args" + ], + "returnType": "dict", + "exported": true, + "lineCount": 14 + }, + { + "name": "_create_bc", + "params": [ + "args" + ], + "returnType": "dict", + "exported": true, + "lineCount": 9 + }, + { + "name": "_update_bc", + "params": [ + "args" + ], + "returnType": "dict", + "exported": true, + "lineCount": 10 + }, + { + "name": "_map_ncit_to_bc", + "params": [ + "args" + ], + "returnType": "dict", + "exported": true, + "lineCount": 9 + }, + { + "name": "_submit_bc_for_review", + "params": [ + "args" + ], + "returnType": "dict", + "exported": true, + "lineCount": 9 + }, + { + "name": "_advance_governance", + "params": [ + "args" + ], + "returnType": "dict", + "exported": true, + "lineCount": 8 + }, + { + "name": "_reject_bc", + "params": [ + "args" + ], + "returnType": "dict", + "exported": true, + "lineCount": 8 + }, + { + "name": "main", + "params": [], + "returnType": "None", + "exported": true, + "lineCount": 3 + }, + { + "name": "_run", + "params": [], + "returnType": "None", + "exported": true, + "lineCount": 3 + } + ], + "classes": [], + "imports": [ + { + "source": "asyncio", + "specifiers": [ + "asyncio" + ] + }, + { + "source": "functools", + "specifiers": [ + "functools" + ] + }, + { + "source": "json", + "specifiers": [ + "json" + ] + }, + { + "source": "logging", + "specifiers": [ + "logging" + ] + }, + { + "source": "typing", + "specifiers": [ + "Any" + ] + }, + { + "source": "mcp.types", + "specifiers": [ + "types" + ] + }, + { + "source": "mcp.server", + "specifiers": [ + "Server" + ] + }, + { + "source": "mcp.server.stdio", + "specifiers": [ + "stdio_server" + ] + } + ], + "exports": [ + "_get_app", + "_with_app_context", + "list_tools", + "call_tool", + "_dispatch", + "_bc_full_dict", + "_list_bcs", + "_get_bc", + "_search_ncit", + "_get_ncit_concept", + "_search_loinc", + "_search_cdisc_library", + "_get_library_bc", + "_list_review_queue", + "_create_bc", + "_update_bc", + "_map_ncit_to_bc", + "_submit_bc_for_review", + "_advance_governance", + "_reject_bc", + "main", + "_run" + ], + "totalLines": 573, + "hasStructuralAnalysis": true + }, + "migrations/README": { + "filePath": "migrations/README", + "contentHash": "24bd0dae33abb1c3dc2a0466421d51de196fd7fae8843a2dd13be999d336ec99", + "functions": [], + "classes": [], + "imports": [], + "exports": [], + "totalLines": 2, + "hasStructuralAnalysis": false + }, + "migrations/alembic.ini": { + "filePath": "migrations/alembic.ini", + "contentHash": "4a36049892b3cfa2bc41fb995ad2c900959c08a39d45b7d486c565a60bfca1c9", + "functions": [], + "classes": [], + "imports": [], + "exports": [], + "totalLines": 51, + "hasStructuralAnalysis": false + }, + "migrations/env.py": { + "filePath": "migrations/env.py", + "contentHash": "92689d7fd7f97985620061033b64aa7142753f6298c57c7f5eb60e268334beb4", + "functions": [ + { + "name": "get_engine", + "params": [], + "exported": true, + "lineCount": 7 + }, + { + "name": "get_engine_url", + "params": [], + "exported": true, + "lineCount": 5 + }, + { + "name": "get_metadata", + "params": [], + "exported": true, + "lineCount": 4 + }, + { + "name": "run_migrations_offline", + "params": [], + "exported": true, + "lineCount": 17 + }, + { + "name": "run_migrations_online", + "params": [], + "exported": true, + "lineCount": 29 + } + ], + "classes": [], + "imports": [ + { + "source": "logging", + "specifiers": [ + "logging" + ] + }, + { + "source": "logging.config", + "specifiers": [ + "fileConfig" + ] + }, + { + "source": "alembic", + "specifiers": [ + "context" + ] + }, + { + "source": "flask", + "specifiers": [ + "current_app" + ] + } + ], + "exports": [ + "get_engine", + "get_engine_url", + "get_metadata", + "run_migrations_offline", + "run_migrations_online" + ], + "totalLines": 106, + "hasStructuralAnalysis": true + }, + "migrations/script.py.mako": { + "filePath": "migrations/script.py.mako", + "contentHash": "f3fc6003e826fce85e8673bb0a22228d68279b4d1996ccc41ed207ac0605265d", + "functions": [], + "classes": [], + "imports": [], + "exports": [], + "totalLines": 25, + "hasStructuralAnalysis": false + }, + "migrations/versions/51d4a009d291_baseline_initial_schema.py": { + "filePath": "migrations/versions/51d4a009d291_baseline_initial_schema.py", + "contentHash": "edf3daaa1612275a98cf23d62847b43b815346756996b53c43ce94d2ac8003b2", + "functions": [ + { + "name": "upgrade", + "params": [], + "exported": true, + "lineCount": 110 + }, + { + "name": "downgrade", + "params": [], + "exported": true, + "lineCount": 12 + } + ], + "classes": [], + "imports": [ + { + "source": "sqlalchemy", + "specifiers": [ + "sa" + ] + }, + { + "source": "alembic", + "specifiers": [ + "op" + ] + } + ], + "exports": [ + "upgrade", + "downgrade" + ], + "totalLines": 143, + "hasStructuralAnalysis": true + }, + "models/__init__.py": { + "filePath": "models/__init__.py", + "contentHash": "273825544f98b1156d301faba2f8d77e630da0cf94894bcc32377930215c193d", + "functions": [], + "classes": [], + "imports": [ + { + "source": "extensions", + "specifiers": [ + "db" + ] + } + ], + "exports": [], + "totalLines": 2, + "hasStructuralAnalysis": true + }, + "models/audit.py": { + "filePath": "models/audit.py", + "contentHash": "f67bd0fe5e9cbf356609e39e6fe3b3e73cc5d7fffc4d2c00b3b54248101c8571", + "functions": [], + "classes": [ + { + "name": "AuditLog", + "methods": [ + "before_state", + "before_state", + "after_state", + "after_state" + ], + "properties": [], + "exported": true, + "lineCount": 26 + } + ], + "imports": [ + { + "source": "json", + "specifiers": [ + "json" + ] + }, + { + "source": "datetime", + "specifiers": [ + "datetime", + "timezone" + ] + }, + { + "source": "extensions", + "specifiers": [ + "db" + ] + } + ], + "exports": [ + "AuditLog" + ], + "totalLines": 33, + "hasStructuralAnalysis": true + }, + "models/bc.py": { + "filePath": "models/bc.py", + "contentHash": "f8c9a5096da1fc72a59e4ef79834525f19b739a965eb4fbaa2be927f46dfff5b", + "functions": [], + "classes": [ + { + "name": "BiomedicalConcept", + "methods": [ + "to_dict" + ], + "properties": [], + "exported": true, + "lineCount": 45 + }, + { + "name": "DataElementConcept", + "methods": [], + "properties": [], + "exported": true, + "lineCount": 13 + } + ], + "imports": [ + { + "source": "datetime", + "specifiers": [ + "datetime", + "timezone" + ] + }, + { + "source": "extensions", + "specifiers": [ + "db" + ] + } + ], + "exports": [ + "BiomedicalConcept", + "DataElementConcept" + ], + "totalLines": 66, + "hasStructuralAnalysis": true + }, + "models/governance.py": { + "filePath": "models/governance.py", + "contentHash": "88d4dadcfeed93ca7bc1d069ab68578ebe3850af8a7321d5b79efc96ce1df19e", + "functions": [], + "classes": [ + { + "name": "GovernanceRecord", + "methods": [], + "properties": [], + "exported": true, + "lineCount": 11 + } + ], + "imports": [ + { + "source": "datetime", + "specifiers": [ + "datetime", + "timezone" + ] + }, + { + "source": "extensions", + "specifiers": [ + "db" + ] + } + ], + "exports": [ + "GovernanceRecord" + ], + "totalLines": 17, + "hasStructuralAnalysis": true + }, + "models/ingestion.py": { + "filePath": "models/ingestion.py", + "contentHash": "b51f60feedd01d30bc9b3ce674e2b87a390129ff5dcca8eee62936901be5919f", + "functions": [], + "classes": [ + { + "name": "IngestionRecord", + "methods": [ + "mapped", + "mapped", + "confidences", + "confidences", + "errors", + "errors", + "decs", + "decs", + "avg_confidence" + ], + "properties": [], + "exported": true, + "lineCount": 50 + } + ], + "imports": [ + { + "source": "json", + "specifiers": [ + "json" + ] + }, + { + "source": "datetime", + "specifiers": [ + "datetime", + "timezone" + ] + }, + { + "source": "extensions", + "specifiers": [ + "db" + ] + } + ], + "exports": [ + "IngestionRecord" + ], + "totalLines": 57, + "hasStructuralAnalysis": true + }, + "models/specialization.py": { + "filePath": "models/specialization.py", + "contentHash": "7964823cf0cd491b3f3505045cb38ecddab606fe481b60366277717ac0214278", + "functions": [], + "classes": [ + { + "name": "DatasetSpecialization", + "methods": [ + "variables", + "variables", + "to_dict" + ], + "properties": [], + "exported": true, + "lineCount": 25 + } + ], + "imports": [ + { + "source": "json", + "specifiers": [ + "json" + ] + }, + { + "source": "extensions", + "specifiers": [ + "db" + ] + } + ], + "exports": [ + "DatasetSpecialization" + ], + "totalLines": 31, + "hasStructuralAnalysis": true + }, + "pyproject.toml": { + "filePath": "pyproject.toml", + "contentHash": "7891009aeb7a54c053c9742a767b8a430c054b0af785ae80c892dcf1c21ab24a", + "functions": [], + "classes": [], + "imports": [], + "exports": [], + "totalLines": 8, + "hasStructuralAnalysis": true + }, + "requirements-dev.txt": { + "filePath": "requirements-dev.txt", + "contentHash": "144cb029a25e18c30aa58e37d283c9e054a8ba598c06aa61dd49bbc4c41cdb3e", + "functions": [], + "classes": [], + "imports": [], + "exports": [], + "totalLines": 10, + "hasStructuralAnalysis": false + }, + "requirements.txt": { + "filePath": "requirements.txt", + "contentHash": "999c15143ead873cb4b4fd52b765b0af47d954d2f20e198dc15d1c59cb433fc8", + "functions": [], + "classes": [], + "imports": [], + "exports": [], + "totalLines": 29, + "hasStructuralAnalysis": false + }, + "routes/__init__.py": { + "filePath": "routes/__init__.py", + "contentHash": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855", + "functions": [], + "classes": [], + "imports": [], + "exports": [], + "totalLines": 1, + "hasStructuralAnalysis": true + }, + "routes/audit.py": { + "filePath": "routes/audit.py", + "contentHash": "d188f43a629a8f7504ab84021f69b05a4feebf5980f550bafbdeb23137bf5c5c", + "functions": [ + { + "name": "index", + "params": [], + "exported": true, + "lineCount": 32 + } + ], + "classes": [], + "imports": [ + { + "source": "logging", + "specifiers": [ + "logging" + ] + }, + { + "source": "flask", + "specifiers": [ + "Blueprint", + "render_template", + "request" + ] + }, + { + "source": "models.audit", + "specifiers": [ + "AuditLog" + ] + } + ], + "exports": [ + "index" + ], + "totalLines": 45, + "hasStructuralAnalysis": true + }, + "routes/bc.py": { + "filePath": "routes/bc.py", + "contentHash": "45037137be470c61cf1def15b342a9529c16a7d03e71ff11287014d0bef07aeb", + "functions": [ + { + "name": "index", + "params": [], + "exported": true, + "lineCount": 17 + }, + { + "name": "new_bc", + "params": [], + "exported": true, + "lineCount": 17 + }, + { + "name": "export", + "params": [], + "exported": true, + "lineCount": 23 + }, + { + "name": "library_detail", + "params": [ + "concept_id" + ], + "exported": true, + "lineCount": 26 + }, + { + "name": "detail", + "params": [ + "bc_id" + ], + "exported": true, + "lineCount": 34 + }, + { + "name": "fetch_metadata", + "params": [ + "bc_id" + ], + "exported": true, + "lineCount": 28 + }, + { + "name": "create", + "params": [], + "exported": true, + "lineCount": 9 + }, + { + "name": "edit", + "params": [ + "bc_id" + ], + "exported": true, + "lineCount": 6 + }, + { + "name": "clear_ncit", + "params": [ + "bc_id" + ], + "exported": true, + "lineCount": 11 + }, + { + "name": "clear_loinc", + "params": [ + "bc_id" + ], + "exported": true, + "lineCount": 12 + }, + { + "name": "submit_for_review", + "params": [ + "bc_id" + ], + "exported": true, + "lineCount": 5 + }, + { + "name": "delete", + "params": [ + "bc_id" + ], + "exported": true, + "lineCount": 12 + }, + { + "name": "_decs_from_form", + "params": [ + "form" + ], + "exported": true, + "lineCount": 19 + } + ], + "classes": [], + "imports": [ + { + "source": "json", + "specifiers": [ + "json" + ] + }, + { + "source": "logging", + "specifiers": [ + "logging" + ] + }, + { + "source": "datetime", + "specifiers": [ + "datetime", + "timezone" + ] + }, + { + "source": "flask", + "specifiers": [ + "Blueprint", + "Response", + "flash", + "redirect", + "render_template", + "request", + "url_for" + ] + }, + { + "source": "extensions", + "specifiers": [ + "db" + ] + }, + { + "source": "models.bc", + "specifiers": [ + "BiomedicalConcept", + "DataElementConcept" + ] + }, + { + "source": "models.governance", + "specifiers": [ + "GovernanceRecord" + ] + }, + { + "source": "services", + "specifiers": [ + "bc_service" + ] + }, + { + "source": "services.audit", + "specifiers": [ + "log_change" + ] + }, + { + "source": "services.cdisc_api", + "specifiers": [ + "CDISCApiClient" + ] + }, + { + "source": "services.export", + "specifiers": [ + "export_json", + "export_odm_xml", + "export_xlsx" + ] + }, + { + "source": "services.loinc_api", + "specifiers": [ + "LoincApiClient" + ] + }, + { + "source": "services.ncit_api", + "specifiers": [ + "NCItApiClient" + ] + } + ], + "exports": [ + "index", + "new_bc", + "export", + "library_detail", + "detail", + "fetch_metadata", + "create", + "edit", + "clear_ncit", + "clear_loinc", + "submit_for_review", + "delete", + "_decs_from_form" + ], + "totalLines": 277, + "hasStructuralAnalysis": true + }, + "routes/dashboard.py": { + "filePath": "routes/dashboard.py", + "contentHash": "de080bfd5e5df9378eeea01f24592e878e42b27de874726c6ee65d3439d9b588", + "functions": [ + { + "name": "index", + "params": [], + "exported": true, + "lineCount": 44 + } + ], + "classes": [], + "imports": [ + { + "source": "logging", + "specifiers": [ + "logging" + ] + }, + { + "source": "concurrent.futures", + "specifiers": [ + "ThreadPoolExecutor" + ] + }, + { + "source": "datetime", + "specifiers": [ + "datetime", + "timedelta", + "timezone" + ] + }, + { + "source": "flask", + "specifiers": [ + "Blueprint", + "render_template" + ] + }, + { + "source": "models.audit", + "specifiers": [ + "AuditLog" + ] + }, + { + "source": "models.bc", + "specifiers": [ + "BiomedicalConcept" + ] + }, + { + "source": "services.cdisc_api", + "specifiers": [ + "CDISCApiClient" + ] + } + ], + "exports": [ + "index" + ], + "totalLines": 61, + "hasStructuralAnalysis": true + }, + "routes/governance.py": { + "filePath": "routes/governance.py", + "contentHash": "f3a4e9fdf44b9cb3170ad88f6d0a756ac7e29941a06ca6927997d39af5a830cb", + "functions": [ + { + "name": "board", + "params": [], + "exported": true, + "lineCount": 10 + }, + { + "name": "export", + "params": [], + "exported": true, + "lineCount": 16 + }, + { + "name": "advance", + "params": [ + "bc_id" + ], + "exported": true, + "lineCount": 10 + }, + { + "name": "reject_bc", + "params": [ + "bc_id" + ], + "exported": true, + "lineCount": 7 + } + ], + "classes": [], + "imports": [ + { + "source": "logging", + "specifiers": [ + "logging" + ] + }, + { + "source": "flask", + "specifiers": [ + "Blueprint", + "Response", + "flash", + "jsonify", + "redirect", + "render_template", + "request", + "url_for" + ] + }, + { + "source": "extensions", + "specifiers": [ + "db" + ] + }, + { + "source": "models.bc", + "specifiers": [ + "BiomedicalConcept" + ] + }, + { + "source": "services", + "specifiers": [ + "governance_service" + ] + }, + { + "source": "services.export", + "specifiers": [ + "export_governance_xlsx" + ] + }, + { + "source": "services.governance_service", + "specifiers": [ + "STATUS_ORDER" + ] + } + ], + "exports": [ + "board", + "export", + "advance", + "reject_bc" + ], + "totalLines": 69, + "hasStructuralAnalysis": true + }, + "routes/ingestion.py": { + "filePath": "routes/ingestion.py", + "contentHash": "7554db3e76c74e77bd81761bee4bbf46fb10c7a7516cf6797d1cbe985bad2e57", + "functions": [ + { + "name": "_allowed_file", + "params": [ + "filename" + ], + "exported": true, + "lineCount": 3 + }, + { + "name": "_get_session_key", + "params": [], + "exported": true, + "lineCount": 4 + }, + { + "name": "_bc_from_mapped", + "params": [ + "bc_id", + "mapped" + ], + "exported": true, + "lineCount": 17 + }, + { + "name": "_create_decs", + "params": [ + "bc_id", + "decs" + ], + "exported": true, + "lineCount": 12 + }, + { + "name": "index", + "params": [], + "exported": true, + "lineCount": 4 + }, + { + "name": "upload", + "params": [], + "exported": true, + "lineCount": 40 + }, + { + "name": "approve", + "params": [ + "record_id" + ], + "exported": true, + "lineCount": 22 + }, + { + "name": "reject", + "params": [ + "record_id" + ], + "exported": true, + "lineCount": 5 + }, + { + "name": "approve_all", + "params": [], + "exported": true, + "lineCount": 32 + } + ], + "classes": [], + "imports": [ + { + "source": "logging", + "specifiers": [ + "logging" + ] + }, + { + "source": "uuid", + "specifiers": [ + "uuid" + ] + }, + { + "source": "flask", + "specifiers": [ + "Blueprint", + "flash", + "redirect", + "render_template", + "request", + "session", + "url_for" + ] + }, + { + "source": "extensions", + "specifiers": [ + "db" + ] + }, + { + "source": "models.audit", + "specifiers": [ + "AuditLog" + ] + }, + { + "source": "models.bc", + "specifiers": [ + "BiomedicalConcept", + "DataElementConcept" + ] + }, + { + "source": "models.ingestion", + "specifiers": [ + "IngestionRecord" + ] + }, + { + "source": "services.ingestion", + "specifiers": [ + "deduplicate", + "parse_csv", + "parse_json", + "parse_xlsx" + ] + } + ], + "exports": [ + "_allowed_file", + "_get_session_key", + "_bc_from_mapped", + "_create_decs", + "index", + "upload", + "approve", + "reject", + "approve_all" + ], + "totalLines": 187, + "hasStructuralAnalysis": true + }, + "routes/loinc.py": { + "filePath": "routes/loinc.py", + "contentHash": "658f533968b7d9f4866058d9a7d410c85d53573e9866838a5a56aa14b988a4b3", + "functions": [ + { + "name": "search", + "params": [], + "exported": true, + "lineCount": 11 + } + ], + "classes": [], + "imports": [ + { + "source": "logging", + "specifiers": [ + "logging" + ] + }, + { + "source": "flask", + "specifiers": [ + "Blueprint", + "jsonify", + "request" + ] + }, + { + "source": "services.loinc_api", + "specifiers": [ + "LoincApiClient" + ] + } + ], + "exports": [ + "search" + ], + "totalLines": 24, + "hasStructuralAnalysis": true + }, + "routes/ncit.py": { + "filePath": "routes/ncit.py", + "contentHash": "134c08ee841cd2a20ecc9b87a07af703baa72db7d9ef56d895901147c3003b83", + "functions": [ + { + "name": "index", + "params": [], + "exported": true, + "lineCount": 2 + }, + { + "name": "mapping", + "params": [], + "exported": true, + "lineCount": 10 + }, + { + "name": "search_ncit", + "params": [], + "exported": true, + "lineCount": 28 + }, + { + "name": "concept_detail", + "params": [ + "ncit_code" + ], + "exported": true, + "lineCount": 9 + }, + { + "name": "resolve", + "params": [ + "bc_id" + ], + "exported": true, + "lineCount": 7 + } + ], + "classes": [], + "imports": [ + { + "source": "logging", + "specifiers": [ + "logging" + ] + }, + { + "source": "flask", + "specifiers": [ + "Blueprint", + "flash", + "jsonify", + "redirect", + "render_template", + "request", + "url_for" + ] + }, + { + "source": "extensions", + "specifiers": [ + "db" + ] + }, + { + "source": "models.bc", + "specifiers": [ + "BiomedicalConcept" + ] + }, + { + "source": "services", + "specifiers": [ + "bc_service" + ] + }, + { + "source": "services.ncit_api", + "specifiers": [ + "NCItApiClient" + ] + } + ], + "exports": [ + "index", + "mapping", + "search_ncit", + "concept_detail", + "resolve" + ], + "totalLines": 84, + "hasStructuralAnalysis": true + }, + "routes/specializations.py": { + "filePath": "routes/specializations.py", + "contentHash": "40d13cc0af73d20e8b93f2b835a8f68ee18f77cf34100acc35a171ab2d40d9c7", + "functions": [ + { + "name": "_get_bc_options", + "params": [], + "exported": true, + "lineCount": 10 + }, + { + "name": "index", + "params": [], + "exported": true, + "lineCount": 10 + }, + { + "name": "library_detail", + "params": [ + "spec_path" + ], + "exported": true, + "lineCount": 11 + }, + { + "name": "detail", + "params": [ + "vlm_group_id" + ], + "exported": true, + "lineCount": 12 + }, + { + "name": "_variables_from_form", + "params": [ + "form" + ], + "exported": true, + "lineCount": 18 + }, + { + "name": "create", + "params": [], + "exported": true, + "lineCount": 34 + }, + { + "name": "delete", + "params": [ + "vlm_group_id" + ], + "exported": true, + "lineCount": 7 + }, + { + "name": "generate_from_dec", + "params": [], + "exported": true, + "lineCount": 9 + }, + { + "name": "generate", + "params": [ + "bc_id" + ], + "exported": true, + "lineCount": 23 + } + ], + "classes": [], + "imports": [ + { + "source": "logging", + "specifiers": [ + "logging" + ] + }, + { + "source": "flask", + "specifiers": [ + "Blueprint", + "flash", + "jsonify", + "redirect", + "render_template", + "request", + "url_for" + ] + }, + { + "source": "extensions", + "specifiers": [ + "db" + ] + }, + { + "source": "models.bc", + "specifiers": [ + "BiomedicalConcept", + "DataElementConcept" + ] + }, + { + "source": "models.specialization", + "specifiers": [ + "DatasetSpecialization" + ] + }, + { + "source": "services.audit", + "specifiers": [ + "log_change" + ] + }, + { + "source": "services.cdisc_api", + "specifiers": [ + "CDISCApiClient" + ] + } + ], + "exports": [ + "_get_bc_options", + "index", + "library_detail", + "detail", + "_variables_from_form", + "create", + "delete", + "generate_from_dec", + "generate" + ], + "totalLines": 173, + "hasStructuralAnalysis": true + }, + "services/__init__.py": { + "filePath": "services/__init__.py", + "contentHash": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855", + "functions": [], + "classes": [], + "imports": [], + "exports": [], + "totalLines": 1, + "hasStructuralAnalysis": true + }, + "services/api_cache.py": { + "filePath": "services/api_cache.py", + "contentHash": "ae642e2006c93f59753144f998bbe5284396e1be939f9fa9265242813823473c", + "functions": [ + { + "name": "cached", + "params": [ + "cache_key", + "fn" + ], + "exported": true, + "lineCount": 21 + } + ], + "classes": [], + "imports": [ + { + "source": "logging", + "specifiers": [ + "logging" + ] + }, + { + "source": "time", + "specifiers": [ + "time" + ] + } + ], + "exports": [ + "cached" + ], + "totalLines": 43, + "hasStructuralAnalysis": true + }, + "services/audit.py": { + "filePath": "services/audit.py", + "contentHash": "54f49edc0b14634d80f3131b2b8e6cee20f5b6df28ca6b6af1a79d208d9bd435", + "functions": [ + { + "name": "log_change", + "params": [ + "entity_type", + "entity_id", + "action", + "actor", + "before", + "after" + ], + "exported": true, + "lineCount": 17 + } + ], + "classes": [], + "imports": [ + { + "source": "logging", + "specifiers": [ + "logging" + ] + }, + { + "source": "extensions", + "specifiers": [ + "db" + ] + }, + { + "source": "models.audit", + "specifiers": [ + "AuditLog" + ] + } + ], + "exports": [ + "log_change" + ], + "totalLines": 33, + "hasStructuralAnalysis": true + }, + "services/bc_service.py": { + "filePath": "services/bc_service.py", + "contentHash": "ae90c7ea4c439a757e44d67e6a2355dc7ed025c8892a0f8316401e38ed2530c6", + "functions": [ + { + "name": "_get_bc_or_raise", + "params": [ + "bc_id" + ], + "exported": true, + "lineCount": 5 + }, + { + "name": "apply_bc_fields", + "params": [ + "bc", + "data", + "is_new" + ], + "exported": true, + "lineCount": 29 + }, + { + "name": "create_bc", + "params": [ + "data", + "actor" + ], + "exported": true, + "lineCount": 20 + }, + { + "name": "update_bc", + "params": [ + "bc_id", + "data", + "actor" + ], + "exported": true, + "lineCount": 8 + }, + { + "name": "save_decs", + "params": [ + "bc_id", + "decs" + ], + "exported": true, + "lineCount": 24 + }, + { + "name": "map_ncit_to_bc", + "params": [ + "bc_id", + "ncit_code", + "actor" + ], + "exported": true, + "lineCount": 25 + }, + { + "name": "submit_bc_for_review", + "params": [ + "bc_id", + "actor" + ], + "exported": true, + "lineCount": 9 + } + ], + "classes": [ + { + "name": "NotFoundError", + "methods": [], + "properties": [], + "exported": true, + "lineCount": 2 + } + ], + "imports": [ + { + "source": "logging", + "specifiers": [ + "logging" + ] + }, + { + "source": "datetime", + "specifiers": [ + "datetime", + "timezone" + ] + }, + { + "source": "extensions", + "specifiers": [ + "db" + ] + }, + { + "source": "models.bc", + "specifiers": [ + "BiomedicalConcept", + "DataElementConcept" + ] + }, + { + "source": "models.governance", + "specifiers": [ + "GovernanceRecord" + ] + }, + { + "source": "models.specialization", + "specifiers": [ + "DatasetSpecialization" + ] + }, + { + "source": "services.audit", + "specifiers": [ + "log_change" + ] + } + ], + "exports": [ + "NotFoundError", + "_get_bc_or_raise", + "apply_bc_fields", + "create_bc", + "update_bc", + "save_decs", + "map_ncit_to_bc", + "submit_bc_for_review" + ], + "totalLines": 160, + "hasStructuralAnalysis": true + }, + "services/cdisc_api.py": { + "filePath": "services/cdisc_api.py", + "contentHash": "d18305bc14699506ba9700f76a9e378007a9d81733a6cd4c9c4a25edd60a86ad", + "functions": [ + { + "name": "_config_value", + "params": [ + "name", + "default" + ], + "exported": true, + "lineCount": 8 + } + ], + "classes": [ + { + "name": "CDISCApiClient", + "methods": [ + "__init__", + "_cache_key", + "_get", + "get_biomedical_concepts", + "get_bc", + "get_specialization", + "get_dataset_specializations", + "check_duplicate", + "publish_bc" + ], + "properties": [], + "exported": true, + "lineCount": 99 + } + ], + "imports": [ + { + "source": "hashlib", + "specifiers": [ + "hashlib" + ] + }, + { + "source": "logging", + "specifiers": [ + "logging" + ] + }, + { + "source": "os", + "specifiers": [ + "os" + ] + }, + { + "source": "requests", + "specifiers": [ + "requests" + ] + }, + { + "source": "flask", + "specifiers": [ + "current_app" + ] + }, + { + "source": "services.api_cache", + "specifiers": [ + "_CACHE_STALE_TTL" + ] + }, + { + "source": "services.api_cache", + "specifiers": [ + "_CACHE_TTL" + ] + }, + { + "source": "services.api_cache", + "specifiers": [ + "_cache", + "cached" + ] + } + ], + "exports": [ + "_config_value", + "CDISCApiClient" + ], + "totalLines": 128, + "hasStructuralAnalysis": true + }, + "services/export.py": { + "filePath": "services/export.py", + "contentHash": "8a7fc3b0568a340e7ee13309e95fb384ef4ea85dfe00d0dc108d880d3db03e85", + "functions": [ + { + "name": "export_json", + "params": [ + "bc_list" + ], + "exported": true, + "lineCount": 3 + }, + { + "name": "export_xlsx", + "params": [ + "bc_list" + ], + "exported": true, + "lineCount": 26 + }, + { + "name": "export_governance_xlsx", + "params": [ + "bc_objects" + ], + "exported": true, + "lineCount": 56 + }, + { + "name": "export_odm_xml", + "params": [ + "bc_list" + ], + "exported": true, + "lineCount": 40 + } + ], + "classes": [], + "imports": [ + { + "source": "io", + "specifiers": [ + "io" + ] + }, + { + "source": "json", + "specifiers": [ + "json" + ] + }, + { + "source": "logging", + "specifiers": [ + "logging" + ] + }, + { + "source": "datetime", + "specifiers": [ + "datetime", + "timezone" + ] + } + ], + "exports": [ + "export_json", + "export_xlsx", + "export_governance_xlsx", + "export_odm_xml" + ], + "totalLines": 186, + "hasStructuralAnalysis": true + }, + "services/governance_service.py": { + "filePath": "services/governance_service.py", + "contentHash": "6a62bb7150c5ee6e91eba0875e616f1b8f0050c9fc0867b49d5673e7b76e5473", + "functions": [ + { + "name": "advance_governance", + "params": [ + "bc_id", + "actor", + "comment" + ], + "exported": true, + "lineCount": 25 + }, + { + "name": "reject_bc", + "params": [ + "bc_id", + "actor", + "comment" + ], + "exported": true, + "lineCount": 18 + } + ], + "classes": [], + "imports": [ + { + "source": "logging", + "specifiers": [ + "logging" + ] + }, + { + "source": "datetime", + "specifiers": [ + "datetime", + "timezone" + ] + }, + { + "source": "extensions", + "specifiers": [ + "db" + ] + }, + { + "source": "models.governance", + "specifiers": [ + "GovernanceRecord" + ] + }, + { + "source": "services.audit", + "specifiers": [ + "log_change" + ] + }, + { + "source": "services.bc_service", + "specifiers": [ + "_get_bc_or_raise" + ] + } + ], + "exports": [ + "advance_governance", + "reject_bc" + ], + "totalLines": 61, + "hasStructuralAnalysis": true + }, + "services/ingestion.py": { + "filePath": "services/ingestion.py", + "contentHash": "0167eca1feb3312c2378e931a090a335bf57bb544214e219dc607c9a3e69b96f", + "functions": [ + { + "name": "_similarity", + "params": [ + "a", + "b" + ], + "exported": true, + "lineCount": 2 + }, + { + "name": "_match_field", + "params": [ + "col_name" + ], + "exported": true, + "lineCount": 11 + }, + { + "name": "map_fields", + "params": [ + "raw_dict" + ], + "exported": true, + "lineCount": 15 + }, + { + "name": "validate_bc", + "params": [ + "bc_dict" + ], + "exported": true, + "lineCount": 17 + }, + { + "name": "_group_by_bc", + "params": [ + "rows", + "sheet" + ], + "exported": true, + "lineCount": 54 + }, + { + "name": "parse_xlsx", + "params": [ + "file_obj" + ], + "exported": true, + "lineCount": 23 + }, + { + "name": "parse_csv", + "params": [ + "file_obj" + ], + "exported": true, + "lineCount": 24 + }, + { + "name": "parse_json", + "params": [ + "file_obj" + ], + "exported": true, + "lineCount": 25 + }, + { + "name": "deduplicate", + "params": [ + "parsed_records", + "existing_ids" + ], + "exported": true, + "lineCount": 10 + } + ], + "classes": [], + "imports": [ + { + "source": "json", + "specifiers": [ + "json" + ] + }, + { + "source": "logging", + "specifiers": [ + "logging" + ] + }, + { + "source": "difflib", + "specifiers": [ + "SequenceMatcher" + ] + }, + { + "source": "pandas", + "specifiers": [ + "pd" + ] + } + ], + "exports": [ + "_similarity", + "_match_field", + "map_fields", + "validate_bc", + "_group_by_bc", + "parse_xlsx", + "parse_csv", + "parse_json", + "deduplicate" + ], + "totalLines": 228, + "hasStructuralAnalysis": true + }, + "services/loinc_api.py": { + "filePath": "services/loinc_api.py", + "contentHash": "3721953b89408afb4e88d17531e45dbba4450f8d507123447f60097919a4197f", + "functions": [], + "classes": [ + { + "name": "LoincApiClient", + "methods": [ + "_auth", + "search" + ], + "properties": [], + "exported": true, + "lineCount": 45 + } + ], + "imports": [ + { + "source": "logging", + "specifiers": [ + "logging" + ] + }, + { + "source": "os", + "specifiers": [ + "os" + ] + }, + { + "source": "requests", + "specifiers": [ + "requests" + ] + }, + { + "source": "services.api_cache", + "specifiers": [ + "cached" + ] + } + ], + "exports": [ + "LoincApiClient" + ], + "totalLines": 63, + "hasStructuralAnalysis": true + }, + "services/ncit_api.py": { + "filePath": "services/ncit_api.py", + "contentHash": "57b47561c153e0fa364bc504b8d77cb5484447243a5e93b65a05fb5b7639f3e4", + "functions": [ + { + "name": "_pick_definition", + "params": [ + "definitions" + ], + "exported": true, + "lineCount": 8 + } + ], + "classes": [ + { + "name": "NCItApiClient", + "methods": [ + "__init__", + "_get", + "search_concept", + "get_concept", + "get_preferred_name" + ], + "properties": [], + "exported": true, + "lineCount": 62 + } + ], + "imports": [ + { + "source": "logging", + "specifiers": [ + "logging" + ] + }, + { + "source": "os", + "specifiers": [ + "os" + ] + }, + { + "source": "requests", + "specifiers": [ + "requests" + ] + }, + { + "source": "flask", + "specifiers": [ + "current_app" + ] + }, + { + "source": "services.api_cache", + "specifiers": [ + "cached" + ] + } + ], + "exports": [ + "_pick_definition", + "NCItApiClient" + ], + "totalLines": 86, + "hasStructuralAnalysis": true + }, + "static/css/custom.css": { + "filePath": "static/css/custom.css", + "contentHash": "61fe20679445ac825b7b8be238374a72935d29c7a83715d4bb59a8ac98fbe71d", + "functions": [], + "classes": [], + "imports": [], + "exports": [], + "totalLines": 1097, + "hasStructuralAnalysis": false + }, + "static/js/main.js": { + "filePath": "static/js/main.js", + "contentHash": "52f07e8e97b921aa15cf4232d720b84b484d272dcaad46d647dc1b19527abbed", + "functions": [], + "classes": [], + "imports": [], + "exports": [], + "totalLines": 820, + "hasStructuralAnalysis": true + }, + "templates/audit.html": { + "filePath": "templates/audit.html", + "contentHash": "8caa4e6e4d29be0f03d0bb84310c968d63fcf1757c4cce75cbe8cb391372a535", + "functions": [], + "classes": [], + "imports": [], + "exports": [], + "totalLines": 214, + "hasStructuralAnalysis": false + }, + "templates/base.html": { + "filePath": "templates/base.html", + "contentHash": "ae7e1ed9f173f9c8a968789b3674945f1fd62b17ed8f267daf2a29cece1016ba", + "functions": [], + "classes": [], + "imports": [], + "exports": [], + "totalLines": 131, + "hasStructuralAnalysis": false + }, + "templates/bc_detail.html": { + "filePath": "templates/bc_detail.html", + "contentHash": "51bc60ce834cc904d783297bf3ca5fda52ff1f8b396fa3795280e14e6a42cb51", + "functions": [], + "classes": [], + "imports": [], + "exports": [], + "totalLines": 534, + "hasStructuralAnalysis": false + }, + "templates/bc_list.html": { + "filePath": "templates/bc_list.html", + "contentHash": "f04c173eacaf83502a489c42e467b3d88edd3d3f7d511cf17bd9e415c978b602", + "functions": [], + "classes": [], + "imports": [], + "exports": [], + "totalLines": 239, + "hasStructuralAnalysis": false + }, + "templates/dashboard.html": { + "filePath": "templates/dashboard.html", + "contentHash": "df7e92c5b82100baf0279761e27424ec926644eda4a937171845fdbb9e859deb", + "functions": [], + "classes": [], + "imports": [], + "exports": [], + "totalLines": 393, + "hasStructuralAnalysis": false + }, + "templates/governance.html": { + "filePath": "templates/governance.html", + "contentHash": "3e52302cff2946df0da808d8b013b6673afcceddbf84b5f46589aef223fb88c6", + "functions": [], + "classes": [], + "imports": [], + "exports": [], + "totalLines": 244, + "hasStructuralAnalysis": false + }, + "templates/ingestion.html": { + "filePath": "templates/ingestion.html", + "contentHash": "7a78c465c79edcd552b3344a4fe0834d01710c724875d5cb7412b0f5a59a6106", + "functions": [], + "classes": [], + "imports": [], + "exports": [], + "totalLines": 175, + "hasStructuralAnalysis": false + }, + "templates/library_bc_detail.html": { + "filePath": "templates/library_bc_detail.html", + "contentHash": "e9283447bada3f960efa471e8c38515beebebc1b1faf583f852aabf919377494", + "functions": [], + "classes": [], + "imports": [], + "exports": [], + "totalLines": 163, + "hasStructuralAnalysis": false + }, + "templates/library_spec_detail.html": { + "filePath": "templates/library_spec_detail.html", + "contentHash": "4850d7885fb08dfefbe8eb478720b8b560e248e43bd844e57a3ebbee8cfce560", + "functions": [], + "classes": [], + "imports": [], + "exports": [], + "totalLines": 198, + "hasStructuralAnalysis": false + }, + "templates/ncit_mapping.html": { + "filePath": "templates/ncit_mapping.html", + "contentHash": "a1e1d73e4d18f92bcc7dc8a252bb71dab1692a00f866b5140d542bcacdefd0d4", + "functions": [], + "classes": [], + "imports": [], + "exports": [], + "totalLines": 136, + "hasStructuralAnalysis": false + }, + "templates/specializations.html": { + "filePath": "templates/specializations.html", + "contentHash": "b8ec28d214b216400093b36585910c871fb2fe70749e0f792c15d2e8dd578a49", + "functions": [], + "classes": [], + "imports": [], + "exports": [], + "totalLines": 395, + "hasStructuralAnalysis": false + }, + "tests/__init__.py": { + "filePath": "tests/__init__.py", + "contentHash": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855", + "functions": [], + "classes": [], + "imports": [], + "exports": [], + "totalLines": 1, + "hasStructuralAnalysis": true + }, + "tests/conftest.py": { + "filePath": "tests/conftest.py", + "contentHash": "ce9fd20760a20dda13f8e8ec1731dd16dc53dd0b9383a303a718cb77f9fbe0a8", + "functions": [ + { + "name": "app", + "params": [], + "exported": true, + "lineCount": 3 + }, + { + "name": "client", + "params": [ + "app" + ], + "exported": true, + "lineCount": 2 + }, + { + "name": "clean_db", + "params": [ + "app" + ], + "exported": true, + "lineCount": 7 + }, + { + "name": "clear_api_cache", + "params": [], + "exported": true, + "lineCount": 7 + }, + { + "name": "sample_bc", + "params": [ + "app" + ], + "exported": true, + "lineCount": 14 + } + ], + "classes": [ + { + "name": "TestConfig", + "methods": [], + "properties": [], + "exported": true, + "lineCount": 10 + } + ], + "imports": [ + { + "source": "pytest", + "specifiers": [ + "pytest" + ] + }, + { + "source": "app", + "specifiers": [ + "create_app" + ] + }, + { + "source": "extensions", + "specifiers": [ + "_db" + ] + }, + { + "source": "models.audit", + "specifiers": [ + "AuditLog" + ] + }, + { + "source": "models.bc", + "specifiers": [ + "BiomedicalConcept", + "DataElementConcept" + ] + }, + { + "source": "models.governance", + "specifiers": [ + "GovernanceRecord" + ] + }, + { + "source": "models.ingestion", + "specifiers": [ + "IngestionRecord" + ] + } + ], + "exports": [ + "TestConfig", + "app", + "client", + "clean_db", + "clear_api_cache", + "sample_bc" + ], + "totalLines": 69, + "hasStructuralAnalysis": true + }, + "tests/test_audit_routes.py": { + "filePath": "tests/test_audit_routes.py", + "contentHash": "8013f2c2340ecbe6f0e28bdf714b38e8dbabde5697034a9a2e85feacc6129d57", + "functions": [ + { + "name": "_add_log", + "params": [ + "app", + "entity_type", + "entity_id", + "action", + "actor" + ], + "exported": true, + "lineCount": 5 + } + ], + "classes": [ + { + "name": "TestAuditIndex", + "methods": [ + "test_returns_200_empty", + "test_shows_log_entries", + "test_filter_by_entity_type", + "test_filter_by_action", + "test_filter_by_actor", + "test_pagination_param_accepted" + ], + "properties": [], + "exported": true, + "lineCount": 36 + } + ], + "imports": [ + { + "source": "extensions", + "specifiers": [ + "db" + ] + }, + { + "source": "models.audit", + "specifiers": [ + "AuditLog" + ] + } + ], + "exports": [ + "_add_log", + "TestAuditIndex" + ], + "totalLines": 50, + "hasStructuralAnalysis": true + }, + "tests/test_bc_routes.py": { + "filePath": "tests/test_bc_routes.py", + "contentHash": "5f48f82a7e30df683e33af91cc19fc91712cbfaf4d9a9180cf5cef9efbd13c43", + "functions": [ + { + "name": "_bc_form", + "params": [ + "**kwargs" + ], + "exported": true, + "lineCount": 10 + } + ], + "classes": [ + { + "name": "TestBcIndex", + "methods": [ + "test_returns_200", + "test_search_by_name", + "test_search_no_match", + "test_filter_by_status" + ], + "properties": [], + "exported": true, + "lineCount": 17 + }, + { + "name": "TestNewBc", + "methods": [ + "test_returns_200" + ], + "properties": [], + "exported": true, + "lineCount": 4 + }, + { + "name": "TestCreateBc", + "methods": [ + "test_creates_bc_and_redirects", + "test_missing_bc_id_redirects_with_error", + "test_duplicate_bc_id_rejected", + "test_create_writes_audit_log", + "test_create_does_not_set_system_without_loinc_code", + "test_create_sets_system_when_loinc_code_provided", + "test_create_with_decs" + ], + "properties": [], + "exported": true, + "lineCount": 48 + }, + { + "name": "TestBcDetail", + "methods": [ + "test_existing_bc_returns_200", + "test_missing_bc_returns_404", + "test_loinc_api_called_when_code_set", + "test_loinc_api_not_called_when_no_code", + "test_loinc_api_error_does_not_break_page", + "test_loinc_spinner_not_shown_when_loinc_fetched_server_side", + "test_loinc_metadata_saved_when_fetched_in_detail" + ], + "properties": [], + "exported": true, + "lineCount": 108 + }, + { + "name": "TestEditBc", + "methods": [ + "test_updates_short_name", + "test_edit_writes_audit_log", + "test_nonexistent_bc_returns_404", + "test_edit_clears_ncit_code_when_submitted_empty", + "test_edit_clears_ncit_metadata_when_ncit_code_cleared", + "test_edit_clears_parent_bc_id_when_submitted_empty", + "test_edit_clears_loinc_code_and_metadata_when_submitted_empty", + "test_edit_clears_system_and_system_name_when_loinc_code_cleared", + "test_edit_preserves_system_when_loinc_code_present", + "test_edit_strips_whitespace_from_ncit_code", + "test_detail_renders_empty_ncit_code_when_none" + ], + "properties": [], + "exported": true, + "lineCount": 101 + }, + { + "name": "TestClearNcitCode", + "methods": [ + "test_clears_ncit_code_and_metadata", + "test_clear_ncit_writes_audit_log", + "test_clear_ncit_nonexistent_bc_returns_404", + "test_clear_ncit_also_clears_parent_bc_id" + ], + "properties": [], + "exported": true, + "lineCount": 31 + }, + { + "name": "TestClearLoincCode", + "methods": [ + "test_clears_loinc_code_and_metadata", + "test_clear_loinc_writes_audit_log", + "test_clear_loinc_also_clears_system_and_system_name", + "test_clear_loinc_nonexistent_bc_returns_404" + ], + "properties": [], + "exported": true, + "lineCount": 39 + }, + { + "name": "TestSubmitForReview", + "methods": [ + "test_advances_status_to_sme_review", + "test_submit_writes_audit_log" + ], + "properties": [], + "exported": true, + "lineCount": 12 + }, + { + "name": "TestDeleteBc", + "methods": [ + "test_deletes_bc", + "test_delete_writes_audit_log", + "test_nonexistent_bc_returns_404" + ], + "properties": [], + "exported": true, + "lineCount": 15 + }, + { + "name": "TestExport", + "methods": [ + "test_json_export", + "test_xlsx_export", + "test_odm_xml_export" + ], + "properties": [], + "exported": true, + "lineCount": 15 + }, + { + "name": "TestLibraryDetail", + "methods": [ + "test_renders_page_for_valid_concept", + "test_redirects_on_api_error", + "test_loinc_api_called_when_loinc_coding_present", + "test_loinc_api_not_called_when_no_loinc_coding", + "test_loinc_api_error_does_not_break_page" + ], + "properties": [], + "exported": true, + "lineCount": 37 + } + ], + "imports": [ + { + "source": "unittest.mock", + "specifiers": [ + "patch" + ] + }, + { + "source": "extensions", + "specifiers": [ + "db" + ] + }, + { + "source": "models.audit", + "specifiers": [ + "AuditLog" + ] + }, + { + "source": "models.bc", + "specifiers": [ + "BiomedicalConcept", + "DataElementConcept" + ] + } + ], + "exports": [ + "_bc_form", + "TestBcIndex", + "TestNewBc", + "TestCreateBc", + "TestBcDetail", + "TestEditBc", + "TestClearNcitCode", + "TestClearLoincCode", + "TestSubmitForReview", + "TestDeleteBc", + "TestExport", + "TestLibraryDetail" + ], + "totalLines": 548, + "hasStructuralAnalysis": true + }, + "tests/test_cdisc_api_cache.py": { + "filePath": "tests/test_cdisc_api_cache.py", + "contentHash": "9d7e123e8aff8f05db55a0bf4c081949a3d0a2372615a008afdbcfebc085f8d0", + "functions": [], + "classes": [ + { + "name": "TestCachedHelper", + "methods": [ + "setup_method", + "test_first_call_fetches_and_stores", + "test_fresh_entry_served_without_refetch", + "test_stale_entry_refreshes_successfully", + "test_stale_entry_served_when_refresh_fails", + "test_expired_entry_raises_when_refresh_fails", + "test_no_entry_raises_when_fetch_fails" + ], + "properties": [], + "exported": true, + "lineCount": 54 + }, + { + "name": "TestClientCacheKeys", + "methods": [ + "setup_method", + "test_cache_key_excludes_raw_api_key", + "test_get_biomedical_concepts_error_encoded_not_raised", + "test_error_result_replaced_after_ttl" + ], + "properties": [], + "exported": true, + "lineCount": 54 + } + ], + "imports": [ + { + "source": "time", + "specifiers": [ + "time" + ] + }, + { + "source": "pytest", + "specifiers": [ + "pytest" + ] + }, + { + "source": "services.cdisc_api", + "specifiers": [ + "cdisc_api" + ] + }, + { + "source": "services.cdisc_api", + "specifiers": [ + "CDISCApiClient", + "_cached" + ] + } + ], + "exports": [ + "TestCachedHelper", + "TestClientCacheKeys" + ], + "totalLines": 121, + "hasStructuralAnalysis": true + }, + "tests/test_dashboard.py": { + "filePath": "tests/test_dashboard.py", + "contentHash": "f3e92dfc86cf756cc94bbe5f72e25c1a75f431037dd952111b751c7bfdaa8398", + "functions": [ + { + "name": "_mock_client", + "params": [ + "bcs", + "specs" + ], + "exported": true, + "lineCount": 5 + } + ], + "classes": [ + { + "name": "TestDashboardIndex", + "methods": [ + "test_renders_with_api_data", + "test_renders_when_api_errors", + "test_renders_with_empty_api_results", + "test_local_stats_reflect_db", + "test_both_api_calls_made_concurrently" + ], + "properties": [], + "exported": true, + "lineCount": 52 + } + ], + "imports": [ + { + "source": "unittest.mock", + "specifiers": [ + "MagicMock", + "patch" + ] + }, + { + "source": "extensions", + "specifiers": [ + "db" + ] + }, + { + "source": "models.bc", + "specifiers": [ + "BiomedicalConcept" + ] + } + ], + "exports": [ + "_mock_client", + "TestDashboardIndex" + ], + "totalLines": 76, + "hasStructuralAnalysis": true + }, + "tests/test_db_bootstrap.py": { + "filePath": "tests/test_db_bootstrap.py", + "contentHash": "31893a83fea3420c9dd3dbdb19f488586f11c9a3f89d1c658fa381e87c7150c5", + "functions": [ + { + "name": "_make_app", + "params": [ + "tmp_path", + "name" + ], + "exported": true, + "lineCount": 11 + }, + { + "name": "_tables", + "params": [ + "app" + ], + "exported": true, + "lineCount": 3 + }, + { + "name": "_stamped_revision", + "params": [ + "app" + ], + "exported": true, + "lineCount": 3 + } + ], + "classes": [ + { + "name": "TestEnsureDb", + "methods": [ + "test_fresh_db_upgraded_from_baseline", + "test_legacy_create_all_db_is_stamped", + "test_pre_squash_stamped_db_is_restamped", + "test_outdated_schema_raises", + "test_idempotent_second_run" + ], + "properties": [], + "exported": true, + "lineCount": 45 + } + ], + "imports": [ + { + "source": "pytest", + "specifiers": [ + "pytest" + ] + }, + { + "source": "sqlalchemy", + "specifiers": [ + "inspect", + "text" + ] + }, + { + "source": "app", + "specifiers": [ + "create_app" + ] + }, + { + "source": "db_bootstrap", + "specifiers": [ + "LEGACY_REVISIONS", + "ensure_db" + ] + }, + { + "source": "extensions", + "specifiers": [ + "db" + ] + } + ], + "exports": [ + "_make_app", + "_tables", + "_stamped_revision", + "TestEnsureDb" + ], + "totalLines": 88, + "hasStructuralAnalysis": true + }, + "tests/test_export_service.py": { + "filePath": "tests/test_export_service.py", + "contentHash": "8a8dca02d5af71e736751928345e9c0e218eba429eb3b73077f8c5be7e9fa50f", + "functions": [], + "classes": [ + { + "name": "TestExportJson", + "methods": [ + "test_round_trip", + "test_empty_list", + "test_non_serializable_values_coerced" + ], + "properties": [], + "exported": true, + "lineCount": 16 + }, + { + "name": "TestExportXlsx", + "methods": [ + "test_returns_workbook_with_headers_and_rows", + "test_empty_list_has_header_only", + "test_missing_fields_render_blank", + "test_code_column_uses_loinc_code" + ], + "properties": [], + "exported": true, + "lineCount": 32 + }, + { + "name": "TestExportGovernanceXlsx", + "methods": [ + "_make_bc_with_decs", + "test_bc_row_then_dec_rows", + "test_bc_without_loinc_blanks_system_fields" + ], + "properties": [], + "exported": true, + "lineCount": 74 + }, + { + "name": "TestExportOdmXml", + "methods": [ + "test_valid_odm_structure", + "test_alias_only_when_ncit_code_present", + "test_empty_list" + ], + "properties": [], + "exported": true, + "lineCount": 25 + } + ], + "imports": [ + { + "source": "json", + "specifiers": [ + "json" + ] + }, + { + "source": "openpyxl", + "specifiers": [ + "openpyxl" + ] + }, + { + "source": "lxml", + "specifiers": [ + "etree" + ] + }, + { + "source": "extensions", + "specifiers": [ + "db" + ] + }, + { + "source": "models.bc", + "specifiers": [ + "BiomedicalConcept", + "DataElementConcept" + ] + }, + { + "source": "services.export", + "specifiers": [ + "BC_EXPORT_FIELDS", + "GOVERNANCE_HEADERS", + "export_governance_xlsx", + "export_json", + "export_odm_xml", + "export_xlsx" + ] + } + ], + "exports": [ + "TestExportJson", + "TestExportXlsx", + "TestExportGovernanceXlsx", + "TestExportOdmXml" + ], + "totalLines": 208, + "hasStructuralAnalysis": true + }, + "tests/test_governance_routes.py": { + "filePath": "tests/test_governance_routes.py", + "contentHash": "c0e5a5fa592e675783523d9900710fa2b7ead002b4f131256a1b1d5e060ebc07", + "functions": [], + "classes": [ + { + "name": "TestGovernanceBoard", + "methods": [ + "test_board_returns_200" + ], + "properties": [], + "exported": true, + "lineCount": 4 + }, + { + "name": "TestAdvance", + "methods": [ + "test_advances_provisional_to_sme_review", + "test_advance_through_all_stages", + "test_already_published_stays_published", + "test_advance_creates_governance_record", + "test_advance_writes_audit_log", + "test_advance_nonexistent_bc_returns_404", + "test_advance_ajax_returns_json" + ], + "properties": [], + "exported": true, + "lineCount": 52 + }, + { + "name": "TestReject", + "methods": [ + "test_reject_returns_to_provisional", + "test_reject_creates_governance_record", + "test_reject_writes_audit_log", + "test_reject_ajax_returns_json", + "test_reject_nonexistent_bc_returns_404" + ], + "properties": [], + "exported": true, + "lineCount": 35 + }, + { + "name": "TestGovernanceExport", + "methods": [ + "test_export_returns_xlsx", + "test_export_filename_in_content_disposition", + "test_export_enforces_xlsx_extension", + "test_export_excludes_non_stage3_bcs", + "test_export_includes_stage3_bcs", + "test_export_system_columns_blank_without_loinc_code", + "test_export_system_columns_populated_with_loinc_code", + "test_export_code_column_uses_loinc_code" + ], + "properties": [], + "exported": true, + "lineCount": 129 + } + ], + "imports": [ + { + "source": "extensions", + "specifiers": [ + "db" + ] + }, + { + "source": "models.audit", + "specifiers": [ + "AuditLog" + ] + }, + { + "source": "models.bc", + "specifiers": [ + "BiomedicalConcept" + ] + }, + { + "source": "models.governance", + "specifiers": [ + "GovernanceRecord" + ] + } + ], + "exports": [ + "TestGovernanceBoard", + "TestAdvance", + "TestReject", + "TestGovernanceExport" + ], + "totalLines": 237, + "hasStructuralAnalysis": true + }, + "tests/test_ingestion_routes.py": { + "filePath": "tests/test_ingestion_routes.py", + "contentHash": "79d59da2cb71886a311c02c94a3a922fe456f7b3477c35b4eb622d5511533271", + "functions": [ + { + "name": "_csv_file", + "params": [ + "rows" + ], + "exported": true, + "lineCount": 7 + }, + { + "name": "_json_file", + "params": [ + "data" + ], + "exported": true, + "lineCount": 2 + } + ], + "classes": [ + { + "name": "TestIngestionIndex", + "methods": [ + "test_returns_200" + ], + "properties": [], + "exported": true, + "lineCount": 4 + }, + { + "name": "TestUpload", + "methods": [ + "test_upload_csv_creates_ingestion_records", + "test_upload_json_creates_ingestion_records", + "test_no_file_redirects_with_error", + "test_wrong_extension_rejected", + "test_duplicate_bc_flagged" + ], + "properties": [], + "exported": true, + "lineCount": 52 + }, + { + "name": "TestApprove", + "methods": [ + "_upload_and_get_record_id", + "test_approve_creates_bc", + "test_approve_sets_status_approved", + "test_approve_nonexistent_record_returns_404", + "test_approve_already_existing_bc_does_not_duplicate" + ], + "properties": [], + "exported": true, + "lineCount": 46 + }, + { + "name": "TestReject", + "methods": [ + "test_reject_sets_status_rejected", + "test_reject_does_not_create_bc", + "test_reject_nonexistent_record_returns_404" + ], + "properties": [], + "exported": true, + "lineCount": 37 + }, + { + "name": "TestApproveAll", + "methods": [ + "test_approve_all_skips_records_with_errors", + "test_approve_all_writes_audit_log_per_bc" + ], + "properties": [], + "exported": true, + "lineCount": 32 + } + ], + "imports": [ + { + "source": "csv", + "specifiers": [ + "csv" + ] + }, + { + "source": "io", + "specifiers": [ + "io" + ] + }, + { + "source": "json", + "specifiers": [ + "json" + ] + }, + { + "source": "extensions", + "specifiers": [ + "db" + ] + }, + { + "source": "models.audit", + "specifiers": [ + "AuditLog" + ] + }, + { + "source": "models.bc", + "specifiers": [ + "BiomedicalConcept" + ] + }, + { + "source": "models.ingestion", + "specifiers": [ + "IngestionRecord" + ] + } + ], + "exports": [ + "_csv_file", + "_json_file", + "TestIngestionIndex", + "TestUpload", + "TestApprove", + "TestReject", + "TestApproveAll" + ], + "totalLines": 205, + "hasStructuralAnalysis": true + }, + "tests/test_ingestion_service.py": { + "filePath": "tests/test_ingestion_service.py", + "contentHash": "4c435290fbff3c641fd0af82defbd764898d8d61f745419a6eccb9a6d3ff5131", + "functions": [], + "classes": [ + { + "name": "TestSimilarity", + "methods": [ + "test_identical_strings", + "test_case_insensitive", + "test_completely_different", + "test_partial_match" + ], + "properties": [], + "exported": true, + "lineCount": 13 + }, + { + "name": "TestMatchField", + "methods": [ + "test_exact_alias_match", + "test_known_alias", + "test_definition_alias", + "test_spaces_normalised", + "test_unknown_column_returns_low_score" + ], + "properties": [], + "exported": true, + "lineCount": 24 + }, + { + "name": "TestMapFields", + "methods": [ + "test_canonical_columns_mapped_at_full_confidence", + "test_none_values_skipped", + "test_nan_values_skipped", + "test_low_confidence_columns_excluded", + "test_values_stripped" + ], + "properties": [], + "exported": true, + "lineCount": 21 + }, + { + "name": "TestValidateBc", + "methods": [ + "_valid", + "test_valid_record_has_no_errors", + "test_missing_short_name", + "test_missing_definition", + "test_missing_both_ids", + "test_invalid_ncit_format", + "test_ncit_starting_with_c_accepted" + ], + "properties": [], + "exported": true, + "lineCount": 33 + }, + { + "name": "TestDeduplicate", + "methods": [ + "_record", + "test_marks_existing_ids_as_duplicate", + "test_case_insensitive_comparison", + "test_empty_existing_ids", + "test_falls_back_to_ncit_code" + ], + "properties": [], + "exported": true, + "lineCount": 24 + }, + { + "name": "TestParseCsv", + "methods": [ + "_make_csv", + "test_valid_row_parsed", + "test_invalid_file_returns_error_record", + "test_missing_definition_produces_validation_error" + ], + "properties": [], + "exported": true, + "lineCount": 27 + }, + { + "name": "TestParseJson", + "methods": [ + "test_array_of_objects", + "test_single_object_wrapped", + "test_invalid_json_returns_error" + ], + "properties": [], + "exported": true, + "lineCount": 18 + }, + { + "name": "TestGroupByBc", + "methods": [ + "test_single_row_becomes_one_record", + "test_dec_sub_rows_grouped_under_parent", + "test_multiple_bcs_produce_separate_records", + "test_row_without_bc_id_skipped" + ], + "properties": [], + "exported": true, + "lineCount": 29 + } + ], + "imports": [ + { + "source": "io", + "specifiers": [ + "io" + ] + }, + { + "source": "json", + "specifiers": [ + "json" + ] + }, + { + "source": "services.ingestion", + "specifiers": [ + "_group_by_bc", + "_match_field", + "_similarity", + "deduplicate", + "map_fields", + "parse_csv", + "parse_json", + "validate_bc" + ] + } + ], + "exports": [ + "TestSimilarity", + "TestMatchField", + "TestMapFields", + "TestValidateBc", + "TestDeduplicate", + "TestParseCsv", + "TestParseJson", + "TestGroupByBc" + ], + "totalLines": 260, + "hasStructuralAnalysis": true + }, + "tests/test_loinc.py": { + "filePath": "tests/test_loinc.py", + "contentHash": "3d4b82abeb234340727868b8efca4afd28123be231132025844129f54294c95d", + "functions": [], + "classes": [ + { + "name": "TestLoincApiClientSearch", + "methods": [ + "_mock_response", + "test_returns_normalized_list", + "test_all_ef_fields_present_in_result", + "test_uses_ef_parameter", + "test_ef_fields_constant_contains_all_required_fields", + "test_uses_basic_auth_when_env_vars_set", + "test_no_auth_when_env_vars_missing", + "test_empty_results", + "test_missing_codes_array_returns_empty", + "test_network_error_returns_error_entry" + ], + "properties": [], + "exported": true, + "lineCount": 125 + }, + { + "name": "TestLoincSearchRoute", + "methods": [ + "test_returns_json_for_ajax", + "test_empty_term_returns_empty_list", + "test_calls_client_with_term", + "test_format_json_param_triggers_json_response" + ], + "properties": [], + "exported": true, + "lineCount": 30 + } + ], + "imports": [ + { + "source": "json", + "specifiers": [ + "json" + ] + }, + { + "source": "unittest.mock", + "specifiers": [ + "MagicMock", + "patch" + ] + }, + { + "source": "requests", + "specifiers": [ + "requests" + ] + }, + { + "source": "services.loinc_api", + "specifiers": [ + "LOINC_EF_FIELDS", + "LoincApiClient" + ] + } + ], + "exports": [ + "TestLoincApiClientSearch", + "TestLoincSearchRoute" + ], + "totalLines": 206, + "hasStructuralAnalysis": true + }, + "tests/test_mcp_server.py": { + "filePath": "tests/test_mcp_server.py", + "contentHash": "916868d3e977f2c9af27dfbbeb390d4de45aa6dc0d2d1a227267a48c2d4efb27", + "functions": [ + { + "name": "inject_test_app", + "params": [ + "app" + ], + "exported": true, + "lineCount": 5 + }, + { + "name": "_dispatch", + "params": [ + "name", + "args" + ], + "exported": true, + "lineCount": 2 + }, + { + "name": "_audit_rows", + "params": [ + "app", + "action" + ], + "exported": true, + "lineCount": 3 + } + ], + "classes": [ + { + "name": "TestDispatch", + "methods": [ + "test_unknown_tool_raises", + "test_all_declared_tools_are_dispatchable" + ], + "properties": [], + "exported": true, + "lineCount": 23 + }, + { + "name": "TestListBcs", + "methods": [ + "test_empty_db", + "test_lists_and_filters", + "test_pagination" + ], + "properties": [], + "exported": true, + "lineCount": 30 + }, + { + "name": "TestGetBc", + "methods": [ + "test_requires_bc_id", + "test_missing_bc_raises", + "test_full_detail" + ], + "properties": [], + "exported": true, + "lineCount": 21 + }, + { + "name": "TestExternalApiTools", + "methods": [ + "test_search_ncit_requires_term", + "test_search_ncit", + "test_get_ncit_concept", + "test_search_loinc", + "test_search_cdisc_library_filters_by_title", + "test_search_cdisc_library_passes_error_through", + "test_get_library_bc" + ], + "properties": [], + "exported": true, + "lineCount": 43 + }, + { + "name": "TestCreateBc", + "methods": [ + "test_creates_with_decs_and_audit", + "test_duplicate_raises", + "test_missing_bc_id_raises" + ], + "properties": [], + "exported": true, + "lineCount": 27 + }, + { + "name": "TestUpdateBc", + "methods": [ + "test_updates_and_audits_before_after", + "test_missing_bc_raises" + ], + "properties": [], + "exported": true, + "lineCount": 13 + }, + { + "name": "TestMapNcit", + "methods": [ + "test_maps_and_audits", + "test_promotes_import_id", + "test_empty_code_raises", + "test_promotes_import_id_repoints_dependents", + "test_promotes_import_id_repoints_child_bcs", + "test_promotes_import_id_collision_raises" + ], + "properties": [], + "exported": true, + "lineCount": 61 + }, + { + "name": "TestGovernanceWrites", + "methods": [ + "test_submit_then_advance_to_published", + "test_reject_returns_to_provisional" + ], + "properties": [], + "exported": true, + "lineCount": 32 + }, + { + "name": "TestReviewQueue", + "methods": [ + "test_queue_summary" + ], + "properties": [], + "exported": true, + "lineCount": 13 + } + ], + "imports": [ + { + "source": "unittest.mock", + "specifiers": [ + "patch" + ] + }, + { + "source": "pytest", + "specifiers": [ + "pytest" + ] + }, + { + "source": "mcp_server.server", + "specifiers": [ + "mcp_srv" + ] + }, + { + "source": "extensions", + "specifiers": [ + "db" + ] + }, + { + "source": "models.audit", + "specifiers": [ + "AuditLog" + ] + }, + { + "source": "models.bc", + "specifiers": [ + "BiomedicalConcept", + "DataElementConcept" + ] + }, + { + "source": "models.governance", + "specifiers": [ + "GovernanceRecord" + ] + }, + { + "source": "models.ingestion", + "specifiers": [ + "IngestionRecord" + ] + }, + { + "source": "models.specialization", + "specifiers": [ + "DatasetSpecialization" + ] + } + ], + "exports": [ + "inject_test_app", + "_dispatch", + "TestDispatch", + "TestListBcs", + "TestGetBc", + "TestExternalApiTools", + "_audit_rows", + "TestCreateBc", + "TestUpdateBc", + "TestMapNcit", + "TestGovernanceWrites", + "TestReviewQueue" + ], + "totalLines": 316, + "hasStructuralAnalysis": true + }, + "tests/test_models.py": { + "filePath": "tests/test_models.py", + "contentHash": "4c9a8ca039ac1d5aea47c02831eaf8b169055235059425b6492066979d8e4818", + "functions": [], + "classes": [ + { + "name": "TestAuditLogJsonProperties", + "methods": [ + "test_before_state_round_trips", + "test_after_state_round_trips", + "test_none_before_state_returns_none", + "test_none_after_state_returns_none", + "test_persisted_log_retrieves_state" + ], + "properties": [], + "exported": true, + "lineCount": 36 + }, + { + "name": "TestIngestionRecordProperties", + "methods": [ + "test_mapped_round_trips", + "test_confidences_round_trips", + "test_errors_round_trips", + "test_decs_round_trips", + "test_empty_mapped_returns_empty_dict", + "test_avg_confidence_computed_correctly", + "test_avg_confidence_empty_confidences" + ], + "properties": [], + "exported": true, + "lineCount": 41 + }, + { + "name": "TestBiomedicalConceptToDict", + "methods": [ + "test_to_dict_contains_required_keys", + "test_to_dict_values_match", + "test_default_status_is_provisional" + ], + "properties": [], + "exported": true, + "lineCount": 27 + } + ], + "imports": [ + { + "source": "extensions", + "specifiers": [ + "db" + ] + }, + { + "source": "models.audit", + "specifiers": [ + "AuditLog" + ] + }, + { + "source": "models.bc", + "specifiers": [ + "BiomedicalConcept" + ] + }, + { + "source": "models.ingestion", + "specifiers": [ + "IngestionRecord" + ] + } + ], + "exports": [ + "TestAuditLogJsonProperties", + "TestIngestionRecordProperties", + "TestBiomedicalConceptToDict" + ], + "totalLines": 117, + "hasStructuralAnalysis": true + }, + "tests/test_ncit.py": { + "filePath": "tests/test_ncit.py", + "contentHash": "72dd3cce91530eecfcde2157da49f56e744d7fde3ae986302f3adaa7482c37a2", + "functions": [], + "classes": [ + { + "name": "TestNcitGetConceptExtended", + "methods": [ + "setup_method", + "_mock_get", + "test_returns_parents", + "test_returns_semantic_type", + "test_returns_source_synonyms", + "test_returns_all_definitions", + "test_empty_parents_returns_empty_list", + "test_missing_semantic_type_returns_empty_list", + "test_error_returns_error_dict" + ], + "properties": [], + "exported": true, + "lineCount": 70 + }, + { + "name": "TestNcitConceptRoute", + "methods": [ + "test_returns_json", + "test_calls_get_concept_with_code", + "test_error_from_service_returns_404" + ], + "properties": [], + "exported": true, + "lineCount": 26 + } + ], + "imports": [ + { + "source": "json", + "specifiers": [ + "json" + ] + }, + { + "source": "unittest.mock", + "specifiers": [ + "MagicMock", + "patch" + ] + }, + { + "source": "requests", + "specifiers": [ + "requests" + ] + }, + { + "source": "services.ncit_api", + "specifiers": [ + "NCItApiClient" + ] + } + ], + "exports": [ + "TestNcitGetConceptExtended", + "TestNcitConceptRoute" + ], + "totalLines": 157, + "hasStructuralAnalysis": true + }, + "tests/test_specializations_routes.py": { + "filePath": "tests/test_specializations_routes.py", + "contentHash": "8166664408d6bdeac94371f94e2b8a3efbd92259cfce5439fa45dd5721c80eb1", + "functions": [ + { + "name": "_patch_client", + "params": [ + "**kwargs" + ], + "exported": true, + "lineCount": 7 + } + ], + "classes": [ + { + "name": "TestIndex", + "methods": [ + "test_lists_specializations", + "test_index_survives_library_error" + ], + "properties": [], + "exported": true, + "lineCount": 17 + }, + { + "name": "TestCreate", + "methods": [ + "test_create_persists_spec", + "test_create_requires_ids", + "test_create_writes_audit_log", + "test_create_persists_variable_rows_from_form" + ], + "properties": [], + "exported": true, + "lineCount": 55 + }, + { + "name": "TestEditUpsert", + "methods": [ + "test_posting_existing_vlm_group_id_updates_instead_of_duplicating", + "test_edit_writes_updated_audit_log" + ], + "properties": [], + "exported": true, + "lineCount": 42 + }, + { + "name": "TestDelete", + "methods": [ + "test_delete_removes_spec", + "test_delete_writes_audit_log", + "test_delete_unknown_404" + ], + "properties": [], + "exported": true, + "lineCount": 29 + }, + { + "name": "TestDetail", + "methods": [ + "test_detail_renders", + "test_detail_404_for_unknown" + ], + "properties": [], + "exported": true, + "lineCount": 15 + }, + { + "name": "TestLibraryDetail", + "methods": [ + "test_renders_library_spec", + "test_error_redirects_to_dashboard" + ], + "properties": [], + "exported": true, + "lineCount": 13 + }, + { + "name": "TestGenerate", + "methods": [ + "_add_decs", + "test_generate_builds_variables_from_decs", + "test_generate_duplicate_is_rejected", + "test_generate_unknown_bc_404", + "test_generate_writes_audit_log" + ], + "properties": [], + "exported": true, + "lineCount": 39 + }, + { + "name": "TestGenerateFromDec", + "methods": [ + "test_returns_variables_json", + "test_missing_bc_id_returns_400" + ], + "properties": [], + "exported": true, + "lineCount": 13 + } + ], + "imports": [ + { + "source": "unittest.mock", + "specifiers": [ + "MagicMock", + "patch" + ] + }, + { + "source": "extensions", + "specifiers": [ + "db" + ] + }, + { + "source": "models.audit", + "specifiers": [ + "AuditLog" + ] + }, + { + "source": "models.bc", + "specifiers": [ + "DataElementConcept" + ] + }, + { + "source": "models.specialization", + "specifiers": [ + "DatasetSpecialization" + ] + } + ], + "exports": [ + "_patch_client", + "TestIndex", + "TestCreate", + "TestEditUpsert", + "TestDelete", + "TestDetail", + "TestLibraryDetail", + "TestGenerate", + "TestGenerateFromDec" + ], + "totalLines": 261, + "hasStructuralAnalysis": true + } + } +} \ No newline at end of file diff --git a/.ua/intermediate/scan-result.json b/.ua/intermediate/scan-result.json new file mode 100644 index 0000000..0657800 --- /dev/null +++ b/.ua/intermediate/scan-result.json @@ -0,0 +1,857 @@ +{ + "name": "cdisc-concept-curation", + "description": "A Flask web application for curating CDISC Biomedical Concepts (BCs), replacing ad hoc spreadsheet and email workflows with a structured pipeline covering ingestion, AI-assisted field mapping, SME review, governance approval, and publishing to the CDISC Library.", + "languages": [ + "config", + "css", + "docx", + "html", + "javascript", + "json", + "mako", + "markdown", + "python", + "shell", + "toml", + "txt", + "unknown", + "xlsx", + "yaml" + ], + "frameworks": [ + "Flask", + "SQLAlchemy", + "Alembic", + "pytest", + "GitHub Actions" + ], + "files": [ + { + "path": ".claude/agent-memory/cdisc-concept-explorer/MEMORY.md", + "language": "markdown", + "sizeLines": 1, + "fileCategory": "docs" + }, + { + "path": ".claude/agent-memory/cdisc-concept-explorer/reference_api_endpoints.md", + "language": "markdown", + "sizeLines": 36, + "fileCategory": "docs" + }, + { + "path": ".claude/agent-memory/cdisc-frontend-dev/MEMORY.md", + "language": "markdown", + "sizeLines": 2, + "fileCategory": "docs" + }, + { + "path": ".claude/agent-memory/cdisc-frontend-dev/frontend_architecture.md", + "language": "markdown", + "sizeLines": 65, + "fileCategory": "docs" + }, + { + "path": ".claude/agent-memory/cdisc-frontend-dev/project_foundation.md", + "language": "markdown", + "sizeLines": 54, + "fileCategory": "docs" + }, + { + "path": ".claude/agents/cdisc-concept-explorer.md", + "language": "markdown", + "sizeLines": 67, + "fileCategory": "docs" + }, + { + "path": ".claude/agents/cdisc-frontend-dev.md", + "language": "markdown", + "sizeLines": 207, + "fileCategory": "docs" + }, + { + "path": ".claude/agents/mike.md", + "language": "markdown", + "sizeLines": 108, + "fileCategory": "docs" + }, + { + "path": ".claude/skills/code-review/SKILL.md", + "language": "markdown", + "sizeLines": 59, + "fileCategory": "docs" + }, + { + "path": ".claude/skills/run-concept-curation/SKILL.md", + "language": "markdown", + "sizeLines": 49, + "fileCategory": "docs" + }, + { + "path": ".claude/skills/run-concept-curation/smoke.sh", + "language": "shell", + "sizeLines": 90, + "fileCategory": "script" + }, + { + "path": ".flake8", + "language": "unknown", + "sizeLines": 16, + "fileCategory": "code" + }, + { + "path": ".github/workflows/ci.yml", + "language": "yaml", + "sizeLines": 26, + "fileCategory": "infra" + }, + { + "path": ".hintrc", + "language": "unknown", + "sizeLines": 7, + "fileCategory": "code" + }, + { + "path": ".mcp.json", + "language": "json", + "sizeLines": 9, + "fileCategory": "config" + }, + { + "path": ".pre-commit-config.yaml", + "language": "yaml", + "sizeLines": 24, + "fileCategory": "config" + }, + { + "path": ".ua/.understandignore", + "language": "unknown", + "sizeLines": 110, + "fileCategory": "code" + }, + { + "path": ".ua/config.json", + "language": "json", + "sizeLines": 1, + "fileCategory": "config" + }, + { + "path": "CLAUDE.md", + "language": "markdown", + "sizeLines": 128, + "fileCategory": "docs" + }, + { + "path": "README-PROGRESS.md", + "language": "markdown", + "sizeLines": 147, + "fileCategory": "docs" + }, + { + "path": "README.md", + "language": "markdown", + "sizeLines": 185, + "fileCategory": "docs" + }, + { + "path": "app.py", + "language": "python", + "sizeLines": 59, + "fileCategory": "code" + }, + { + "path": "config.py", + "language": "python", + "sizeLines": 14, + "fileCategory": "code" + }, + { + "path": "db_bootstrap.py", + "language": "python", + "sizeLines": 71, + "fileCategory": "code" + }, + { + "path": "extensions.py", + "language": "python", + "sizeLines": 25, + "fileCategory": "code" + }, + { + "path": "files/BC Curation Principles and Completion GLs.xlsx", + "language": "xlsx", + "sizeLines": 182, + "fileCategory": "code" + }, + { + "path": "files/BC DEC Templates.xlsx", + "language": "xlsx", + "sizeLines": 94, + "fileCategory": "code" + }, + { + "path": "files/BC Examples.xlsx", + "language": "xlsx", + "sizeLines": 332, + "fileCategory": "code" + }, + { + "path": "files/BC Governance.docx", + "language": "docx", + "sizeLines": 54, + "fileCategory": "code" + }, + { + "path": "files/implementation.md", + "language": "markdown", + "sizeLines": 205, + "fileCategory": "docs" + }, + { + "path": "files/plan.md", + "language": "markdown", + "sizeLines": 47, + "fileCategory": "docs" + }, + { + "path": "files/result.md", + "language": "markdown", + "sizeLines": 27, + "fileCategory": "docs" + }, + { + "path": "mcp_server/__init__.py", + "language": "python", + "sizeLines": 5, + "fileCategory": "code" + }, + { + "path": "mcp_server/__main__.py", + "language": "python", + "sizeLines": 3, + "fileCategory": "code" + }, + { + "path": "mcp_server/server.py", + "language": "python", + "sizeLines": 572, + "fileCategory": "code" + }, + { + "path": "migrations/README", + "language": "unknown", + "sizeLines": 1, + "fileCategory": "code" + }, + { + "path": "migrations/alembic.ini", + "language": "config", + "sizeLines": 50, + "fileCategory": "config" + }, + { + "path": "migrations/env.py", + "language": "python", + "sizeLines": 105, + "fileCategory": "code" + }, + { + "path": "migrations/script.py.mako", + "language": "mako", + "sizeLines": 24, + "fileCategory": "code" + }, + { + "path": "migrations/versions/51d4a009d291_baseline_initial_schema.py", + "language": "python", + "sizeLines": 142, + "fileCategory": "code" + }, + { + "path": "models/__init__.py", + "language": "python", + "sizeLines": 1, + "fileCategory": "code" + }, + { + "path": "models/audit.py", + "language": "python", + "sizeLines": 32, + "fileCategory": "code" + }, + { + "path": "models/bc.py", + "language": "python", + "sizeLines": 65, + "fileCategory": "code" + }, + { + "path": "models/governance.py", + "language": "python", + "sizeLines": 16, + "fileCategory": "code" + }, + { + "path": "models/ingestion.py", + "language": "python", + "sizeLines": 56, + "fileCategory": "code" + }, + { + "path": "models/specialization.py", + "language": "python", + "sizeLines": 30, + "fileCategory": "code" + }, + { + "path": "pyproject.toml", + "language": "toml", + "sizeLines": 7, + "fileCategory": "config" + }, + { + "path": "requirements-dev.txt", + "language": "txt", + "sizeLines": 9, + "fileCategory": "docs" + }, + { + "path": "requirements.txt", + "language": "txt", + "sizeLines": 28, + "fileCategory": "docs" + }, + { + "path": "routes/__init__.py", + "language": "python", + "sizeLines": 0, + "fileCategory": "code" + }, + { + "path": "routes/audit.py", + "language": "python", + "sizeLines": 44, + "fileCategory": "code" + }, + { + "path": "routes/bc.py", + "language": "python", + "sizeLines": 276, + "fileCategory": "code" + }, + { + "path": "routes/dashboard.py", + "language": "python", + "sizeLines": 60, + "fileCategory": "code" + }, + { + "path": "routes/governance.py", + "language": "python", + "sizeLines": 68, + "fileCategory": "code" + }, + { + "path": "routes/ingestion.py", + "language": "python", + "sizeLines": 186, + "fileCategory": "code" + }, + { + "path": "routes/loinc.py", + "language": "python", + "sizeLines": 23, + "fileCategory": "code" + }, + { + "path": "routes/ncit.py", + "language": "python", + "sizeLines": 83, + "fileCategory": "code" + }, + { + "path": "routes/specializations.py", + "language": "python", + "sizeLines": 172, + "fileCategory": "code" + }, + { + "path": "services/__init__.py", + "language": "python", + "sizeLines": 0, + "fileCategory": "code" + }, + { + "path": "services/api_cache.py", + "language": "python", + "sizeLines": 42, + "fileCategory": "code" + }, + { + "path": "services/audit.py", + "language": "python", + "sizeLines": 32, + "fileCategory": "code" + }, + { + "path": "services/bc_service.py", + "language": "python", + "sizeLines": 159, + "fileCategory": "code" + }, + { + "path": "services/cdisc_api.py", + "language": "python", + "sizeLines": 127, + "fileCategory": "code" + }, + { + "path": "services/export.py", + "language": "python", + "sizeLines": 185, + "fileCategory": "code" + }, + { + "path": "services/governance_service.py", + "language": "python", + "sizeLines": 60, + "fileCategory": "code" + }, + { + "path": "services/ingestion.py", + "language": "python", + "sizeLines": 227, + "fileCategory": "code" + }, + { + "path": "services/loinc_api.py", + "language": "python", + "sizeLines": 62, + "fileCategory": "code" + }, + { + "path": "services/ncit_api.py", + "language": "python", + "sizeLines": 85, + "fileCategory": "code" + }, + { + "path": "static/css/custom.css", + "language": "css", + "sizeLines": 1096, + "fileCategory": "markup" + }, + { + "path": "static/js/main.js", + "language": "javascript", + "sizeLines": 819, + "fileCategory": "code" + }, + { + "path": "templates/audit.html", + "language": "html", + "sizeLines": 213, + "fileCategory": "markup" + }, + { + "path": "templates/base.html", + "language": "html", + "sizeLines": 130, + "fileCategory": "markup" + }, + { + "path": "templates/bc_detail.html", + "language": "html", + "sizeLines": 533, + "fileCategory": "markup" + }, + { + "path": "templates/bc_list.html", + "language": "html", + "sizeLines": 238, + "fileCategory": "markup" + }, + { + "path": "templates/dashboard.html", + "language": "html", + "sizeLines": 392, + "fileCategory": "markup" + }, + { + "path": "templates/governance.html", + "language": "html", + "sizeLines": 243, + "fileCategory": "markup" + }, + { + "path": "templates/ingestion.html", + "language": "html", + "sizeLines": 174, + "fileCategory": "markup" + }, + { + "path": "templates/library_bc_detail.html", + "language": "html", + "sizeLines": 162, + "fileCategory": "markup" + }, + { + "path": "templates/library_spec_detail.html", + "language": "html", + "sizeLines": 197, + "fileCategory": "markup" + }, + { + "path": "templates/ncit_mapping.html", + "language": "html", + "sizeLines": 135, + "fileCategory": "markup" + }, + { + "path": "templates/specializations.html", + "language": "html", + "sizeLines": 394, + "fileCategory": "markup" + }, + { + "path": "tests/__init__.py", + "language": "python", + "sizeLines": 0, + "fileCategory": "code" + }, + { + "path": "tests/conftest.py", + "language": "python", + "sizeLines": 68, + "fileCategory": "code" + }, + { + "path": "tests/test_audit_routes.py", + "language": "python", + "sizeLines": 49, + "fileCategory": "code" + }, + { + "path": "tests/test_bc_routes.py", + "language": "python", + "sizeLines": 547, + "fileCategory": "code" + }, + { + "path": "tests/test_cdisc_api_cache.py", + "language": "python", + "sizeLines": 120, + "fileCategory": "code" + }, + { + "path": "tests/test_dashboard.py", + "language": "python", + "sizeLines": 75, + "fileCategory": "code" + }, + { + "path": "tests/test_db_bootstrap.py", + "language": "python", + "sizeLines": 87, + "fileCategory": "code" + }, + { + "path": "tests/test_export_service.py", + "language": "python", + "sizeLines": 207, + "fileCategory": "code" + }, + { + "path": "tests/test_governance_routes.py", + "language": "python", + "sizeLines": 236, + "fileCategory": "code" + }, + { + "path": "tests/test_ingestion_routes.py", + "language": "python", + "sizeLines": 204, + "fileCategory": "code" + }, + { + "path": "tests/test_ingestion_service.py", + "language": "python", + "sizeLines": 259, + "fileCategory": "code" + }, + { + "path": "tests/test_loinc.py", + "language": "python", + "sizeLines": 205, + "fileCategory": "code" + }, + { + "path": "tests/test_mcp_server.py", + "language": "python", + "sizeLines": 315, + "fileCategory": "code" + }, + { + "path": "tests/test_models.py", + "language": "python", + "sizeLines": 116, + "fileCategory": "code" + }, + { + "path": "tests/test_ncit.py", + "language": "python", + "sizeLines": 156, + "fileCategory": "code" + }, + { + "path": "tests/test_specializations_routes.py", + "language": "python", + "sizeLines": 260, + "fileCategory": "code" + } + ], + "totalFiles": 97, + "filteredByIgnore": 0, + "estimatedComplexity": "moderate", + "importMap": { + ".claude/agent-memory/cdisc-concept-explorer/MEMORY.md": [], + ".claude/agent-memory/cdisc-concept-explorer/reference_api_endpoints.md": [], + ".claude/agent-memory/cdisc-frontend-dev/MEMORY.md": [], + ".claude/agent-memory/cdisc-frontend-dev/frontend_architecture.md": [], + ".claude/agent-memory/cdisc-frontend-dev/project_foundation.md": [], + ".claude/agents/cdisc-concept-explorer.md": [], + ".claude/agents/cdisc-frontend-dev.md": [], + ".claude/agents/mike.md": [], + ".claude/skills/code-review/SKILL.md": [], + ".claude/skills/run-concept-curation/SKILL.md": [], + ".claude/skills/run-concept-curation/smoke.sh": [], + ".flake8": [], + ".github/workflows/ci.yml": [], + ".hintrc": [], + ".mcp.json": [], + ".pre-commit-config.yaml": [], + ".ua/.understandignore": [], + ".ua/config.json": [], + "CLAUDE.md": [], + "README-PROGRESS.md": [], + "README.md": [], + "app.py": [ + "config.py", + "extensions.py" + ], + "config.py": [], + "db_bootstrap.py": [ + "extensions.py" + ], + "extensions.py": [], + "files/BC Curation Principles and Completion GLs.xlsx": [], + "files/BC DEC Templates.xlsx": [], + "files/BC Examples.xlsx": [], + "files/BC Governance.docx": [], + "files/implementation.md": [], + "files/plan.md": [], + "files/result.md": [], + "mcp_server/__init__.py": [], + "mcp_server/__main__.py": [ + "mcp_server/server.py" + ], + "mcp_server/server.py": [], + "migrations/README": [], + "migrations/alembic.ini": [], + "migrations/env.py": [], + "migrations/script.py.mako": [], + "migrations/versions/51d4a009d291_baseline_initial_schema.py": [], + "models/__init__.py": [ + "extensions.py" + ], + "models/audit.py": [ + "extensions.py" + ], + "models/bc.py": [ + "extensions.py" + ], + "models/governance.py": [ + "extensions.py" + ], + "models/ingestion.py": [ + "extensions.py" + ], + "models/specialization.py": [ + "extensions.py" + ], + "pyproject.toml": [], + "requirements-dev.txt": [], + "requirements.txt": [], + "routes/__init__.py": [], + "routes/audit.py": [ + "models/audit.py" + ], + "routes/bc.py": [ + "extensions.py", + "models/bc.py", + "models/governance.py", + "services/__init__.py", + "services/audit.py", + "services/bc_service.py", + "services/cdisc_api.py", + "services/export.py", + "services/loinc_api.py", + "services/ncit_api.py" + ], + "routes/dashboard.py": [ + "models/audit.py", + "models/bc.py", + "services/cdisc_api.py" + ], + "routes/governance.py": [ + "extensions.py", + "models/bc.py", + "services/__init__.py", + "services/export.py", + "services/governance_service.py" + ], + "routes/ingestion.py": [ + "extensions.py", + "models/audit.py", + "models/bc.py", + "models/ingestion.py", + "services/ingestion.py" + ], + "routes/loinc.py": [ + "services/loinc_api.py" + ], + "routes/ncit.py": [ + "extensions.py", + "models/bc.py", + "services/__init__.py", + "services/bc_service.py", + "services/ncit_api.py" + ], + "routes/specializations.py": [ + "extensions.py", + "models/bc.py", + "models/specialization.py", + "services/audit.py", + "services/cdisc_api.py" + ], + "services/__init__.py": [], + "services/api_cache.py": [], + "services/audit.py": [ + "extensions.py", + "models/audit.py" + ], + "services/bc_service.py": [ + "extensions.py", + "models/bc.py", + "models/governance.py", + "models/specialization.py", + "services/audit.py" + ], + "services/cdisc_api.py": [ + "services/api_cache.py" + ], + "services/export.py": [], + "services/governance_service.py": [ + "extensions.py", + "models/governance.py", + "services/audit.py", + "services/bc_service.py" + ], + "services/ingestion.py": [], + "services/loinc_api.py": [ + "services/api_cache.py" + ], + "services/ncit_api.py": [ + "services/api_cache.py" + ], + "static/css/custom.css": [], + "static/js/main.js": [], + "templates/audit.html": [], + "templates/base.html": [], + "templates/bc_detail.html": [], + "templates/bc_list.html": [], + "templates/dashboard.html": [], + "templates/governance.html": [], + "templates/ingestion.html": [], + "templates/library_bc_detail.html": [], + "templates/library_spec_detail.html": [], + "templates/ncit_mapping.html": [], + "templates/specializations.html": [], + "tests/__init__.py": [], + "tests/conftest.py": [ + "app.py", + "extensions.py", + "models/audit.py", + "models/bc.py", + "models/governance.py", + "models/ingestion.py" + ], + "tests/test_audit_routes.py": [ + "extensions.py", + "models/audit.py" + ], + "tests/test_bc_routes.py": [ + "extensions.py", + "models/audit.py", + "models/bc.py" + ], + "tests/test_cdisc_api_cache.py": [ + "services/cdisc_api.py" + ], + "tests/test_dashboard.py": [ + "extensions.py", + "models/bc.py" + ], + "tests/test_db_bootstrap.py": [ + "app.py", + "db_bootstrap.py", + "extensions.py" + ], + "tests/test_export_service.py": [ + "extensions.py", + "models/bc.py", + "services/export.py" + ], + "tests/test_governance_routes.py": [ + "extensions.py", + "models/audit.py", + "models/bc.py", + "models/governance.py" + ], + "tests/test_ingestion_routes.py": [ + "extensions.py", + "models/audit.py", + "models/bc.py", + "models/ingestion.py" + ], + "tests/test_ingestion_service.py": [ + "services/ingestion.py" + ], + "tests/test_loinc.py": [ + "services/loinc_api.py" + ], + "tests/test_mcp_server.py": [ + "extensions.py", + "mcp_server/server.py", + "models/audit.py", + "models/bc.py", + "models/governance.py", + "models/ingestion.py", + "models/specialization.py" + ], + "tests/test_models.py": [ + "extensions.py", + "models/audit.py", + "models/bc.py", + "models/ingestion.py" + ], + "tests/test_ncit.py": [ + "services/ncit_api.py" + ], + "tests/test_specializations_routes.py": [ + "extensions.py", + "models/audit.py", + "models/bc.py", + "models/specialization.py" + ] + } +} \ No newline at end of file diff --git a/.ua/knowledge-graph.json b/.ua/knowledge-graph.json new file mode 100644 index 0000000..475390e --- /dev/null +++ b/.ua/knowledge-graph.json @@ -0,0 +1,8722 @@ +{ + "version": "1.0.0", + "project": { + "name": "cdisc-concept-curation", + "languages": [ + "config", + "css", + "docx", + "html", + "javascript", + "json", + "mako", + "markdown", + "python", + "shell", + "toml", + "txt", + "unknown", + "xlsx", + "yaml" + ], + "frameworks": [ + "Flask", + "SQLAlchemy", + "Alembic", + "pytest", + "GitHub Actions" + ], + "description": "A Flask web application for curating CDISC Biomedical Concepts (BCs), replacing ad hoc spreadsheet and email workflows with a structured pipeline covering ingestion, AI-assisted field mapping, SME review, governance approval, and publishing to the CDISC Library.", + "analyzedAt": "2026-08-04T08:52:00Z", + "gitCommitHash": "8df239f9988f09a9476e634ceb7cac80c29f72fe" + }, + "nodes": [ + { + "id": "file:models/audit.py", + "type": "file", + "name": "audit.py", + "filePath": "models/audit.py", + "summary": "SQLAlchemy model for the AuditLog table that records before/after JSON state for every change made to BCs, DECs, and governance records.", + "tags": [ + "data-model", + "audit-log", + "database", + "orm", + "tested" + ], + "complexity": "simple" + }, + { + "id": "file:models/bc.py", + "type": "file", + "name": "bc.py", + "filePath": "models/bc.py", + "summary": "Core SQLAlchemy models for BiomedicalConcept and DataElementConcept, the central entities of the CDISC BC curation pipeline, including NCIt/LOINC coding fields and governance status.", + "tags": [ + "data-model", + "database", + "orm", + "core-domain", + "tested" + ], + "complexity": "moderate" + }, + { + "id": "file:models/ingestion.py", + "type": "file", + "name": "ingestion.py", + "filePath": "models/ingestion.py", + "summary": "SQLAlchemy model for IngestionRecord, which stages parsed upload rows (with field mappings, confidence scores, and validation errors) in the DB prior to SME approval.", + "tags": [ + "data-model", + "database", + "orm", + "ingestion", + "tested" + ], + "complexity": "simple" + }, + { + "id": "file:routes/audit.py", + "type": "file", + "name": "audit.py", + "filePath": "routes/audit.py", + "summary": "Flask blueprint exposing the read-only audit trail view with filtering by entity type, action, and actor, and pagination.", + "tags": [ + "api-handler", + "audit-log", + "blueprint", + "tested" + ], + "complexity": "simple" + }, + { + "id": "file:routes/ingestion.py", + "type": "file", + "name": "ingestion.py", + "filePath": "routes/ingestion.py", + "summary": "Flask blueprint implementing the file-upload ingestion workflow: parsing uploaded files, staging rows as IngestionRecord, and approving/rejecting them into BiomedicalConcept/DataElementConcept records with audit logging.", + "tags": [ + "api-handler", + "blueprint", + "ingestion", + "workflow", + "tested" + ], + "complexity": "moderate" + }, + { + "id": "file:services/ingestion.py", + "type": "file", + "name": "ingestion.py", + "filePath": "services/ingestion.py", + "summary": "Business logic for the ingestion pipeline: fuzzy field-name mapping via SequenceMatcher similarity scoring, BC/DEC row validation, multi-row grouping by BC ID, and XLSX/CSV/JSON parsers.", + "tags": [ + "service", + "parsing", + "validation", + "fuzzy-matching", + "tested" + ], + "complexity": "complex", + "languageNotes": "Uses difflib.SequenceMatcher similarity ratios against a canonical FIELD_MAP alias dictionary to fuzzily map arbitrary spreadsheet column names to BC/DEC schema fields." + }, + { + "id": "file:tests/conftest.py", + "type": "file", + "name": "conftest.py", + "filePath": "tests/conftest.py", + "summary": "Pytest fixtures shared across the test suite: Flask app/test client factories, a per-test DB reset fixture, API cache clearing, and a sample BiomedicalConcept fixture.", + "tags": [ + "test", + "fixture", + "pytest-config" + ], + "complexity": "moderate" + }, + { + "id": "class:models/audit.py:AuditLog", + "type": "class", + "name": "AuditLog", + "filePath": "models/audit.py", + "lineRange": [ + 7, + 32 + ], + "summary": "ORM model for the immutable audit trail; stores before/after entity state as JSON via property accessors that transparently serialize/deserialize.", + "tags": [ + "data-model", + "audit-log", + "orm" + ], + "complexity": "simple" + }, + { + "id": "class:models/bc.py:BiomedicalConcept", + "type": "class", + "name": "BiomedicalConcept", + "filePath": "models/bc.py", + "lineRange": [ + 6, + 50 + ], + "summary": "Central ORM entity representing a curated CDISC Biomedical Concept, holding NCIt/LOINC coding metadata, governance status, parent/child hierarchy, and related DECs/specializations.", + "tags": [ + "data-model", + "core-domain", + "orm" + ], + "complexity": "moderate" + }, + { + "id": "class:models/bc.py:DataElementConcept", + "type": "class", + "name": "DataElementConcept", + "filePath": "models/bc.py", + "lineRange": [ + 53, + 65 + ], + "summary": "ORM entity for a Data Element Concept (DEC) belonging to a BiomedicalConcept, capturing NCIt DEC code, data type, and example values.", + "tags": [ + "data-model", + "core-domain", + "orm" + ], + "complexity": "simple" + }, + { + "id": "class:models/ingestion.py:IngestionRecord", + "type": "class", + "name": "IngestionRecord", + "filePath": "models/ingestion.py", + "lineRange": [ + 7, + 56 + ], + "summary": "ORM staging model for a parsed upload row awaiting SME approval; exposes JSON-backed properties for field mapping, confidence scores, validation errors, and DEC sub-rows, plus an average-confidence helper.", + "tags": [ + "data-model", + "ingestion", + "orm" + ], + "complexity": "moderate" + }, + { + "id": "function:routes/audit.py:index", + "type": "function", + "name": "index", + "filePath": "routes/audit.py", + "lineRange": [ + 13, + 44 + ], + "summary": "Route handler serving the paginated audit log view, applying optional entity_type/action/actor query filters and ordering by timestamp descending.", + "tags": [ + "api-handler", + "audit-log", + "pagination" + ], + "complexity": "simple" + }, + { + "id": "function:routes/ingestion.py:_bc_from_mapped", + "type": "function", + "name": "_bc_from_mapped", + "filePath": "routes/ingestion.py", + "lineRange": [ + 38, + 54 + ], + "summary": "Builds a BiomedicalConcept instance from a mapped-field dict produced by the ingestion service, pulling canonical field values.", + "tags": [ + "helper", + "data-model", + "ingestion" + ], + "complexity": "simple" + }, + { + "id": "function:routes/ingestion.py:_create_decs", + "type": "function", + "name": "_create_decs", + "filePath": "routes/ingestion.py", + "lineRange": [ + 57, + 68 + ], + "summary": "Creates and persists DataElementConcept rows for a BC from the mapped DEC sub-row list produced during ingestion.", + "tags": [ + "helper", + "data-model", + "ingestion" + ], + "complexity": "simple" + }, + { + "id": "function:routes/ingestion.py:upload", + "type": "function", + "name": "upload", + "filePath": "routes/ingestion.py", + "lineRange": [ + 79, + 118 + ], + "summary": "Route handler that accepts an uploaded XLSX/CSV/JSON file, dispatches to the matching parser, deduplicates against existing BC IDs, and stages the results as pending IngestionRecord rows.", + "tags": [ + "api-handler", + "ingestion", + "file-upload" + ], + "complexity": "moderate" + }, + { + "id": "function:routes/ingestion.py:approve", + "type": "function", + "name": "approve", + "filePath": "routes/ingestion.py", + "lineRange": [ + 122, + 143 + ], + "summary": "Route handler that approves a single pending IngestionRecord, committing it as a new BiomedicalConcept (and DECs) and writing an audit log entry.", + "tags": [ + "api-handler", + "ingestion", + "workflow" + ], + "complexity": "moderate" + }, + { + "id": "function:routes/ingestion.py:approve_all", + "type": "function", + "name": "approve_all", + "filePath": "routes/ingestion.py", + "lineRange": [ + 155, + 186 + ], + "summary": "Route handler that bulk-approves all pending IngestionRecord rows for the current session, creating BCs/DECs and an audit log entry per record.", + "tags": [ + "api-handler", + "ingestion", + "workflow", + "bulk-operation" + ], + "complexity": "moderate" + }, + { + "id": "function:services/ingestion.py:_match_field", + "type": "function", + "name": "_match_field", + "filePath": "services/ingestion.py", + "lineRange": [ + 35, + 45 + ], + "summary": "Scores a raw column name against every canonical field's alias list using similarity ratios and returns the best-matching canonical field name with a confidence score.", + "tags": [ + "helper", + "fuzzy-matching", + "ingestion" + ], + "complexity": "simple" + }, + { + "id": "function:services/ingestion.py:map_fields", + "type": "function", + "name": "map_fields", + "filePath": "services/ingestion.py", + "lineRange": [ + 48, + 62 + ], + "summary": "Maps a raw row dict's column names to canonical BC/DEC field names using _match_field, normalizing values to stripped strings.", + "tags": [ + "fuzzy-matching", + "ingestion", + "data-transformation" + ], + "complexity": "simple" + }, + { + "id": "function:services/ingestion.py:validate_bc", + "type": "function", + "name": "validate_bc", + "filePath": "services/ingestion.py", + "lineRange": [ + 65, + 81 + ], + "summary": "Validates a mapped BC dict, flagging missing short name/definition and requiring at least one of NCIt code or external code with a correct NCIt format.", + "tags": [ + "validation", + "ingestion" + ], + "complexity": "simple" + }, + { + "id": "function:services/ingestion.py:_group_by_bc", + "type": "function", + "name": "_group_by_bc", + "filePath": "services/ingestion.py", + "lineRange": [ + 84, + 137 + ], + "summary": "Groups parsed spreadsheet rows by BC ID into single ingestion records, merging DEC sub-rows and confidences under each parent BC and running validation on the merged result.", + "tags": [ + "data-transformation", + "ingestion", + "grouping" + ], + "complexity": "moderate" + }, + { + "id": "function:services/ingestion.py:parse_xlsx", + "type": "function", + "name": "parse_xlsx", + "filePath": "services/ingestion.py", + "lineRange": [ + 140, + 162 + ], + "summary": "Parses an uploaded XLSX file's first sheet into mapped, grouped BC/DEC records via pandas, capturing per-row parse errors.", + "tags": [ + "parsing", + "ingestion", + "xlsx" + ], + "complexity": "moderate" + }, + { + "id": "function:services/ingestion.py:parse_csv", + "type": "function", + "name": "parse_csv", + "filePath": "services/ingestion.py", + "lineRange": [ + 165, + 188 + ], + "summary": "Parses an uploaded CSV file into mapped and validated BC records via pandas, capturing per-row parse errors.", + "tags": [ + "parsing", + "ingestion", + "csv" + ], + "complexity": "moderate" + }, + { + "id": "function:services/ingestion.py:parse_json", + "type": "function", + "name": "parse_json", + "filePath": "services/ingestion.py", + "lineRange": [ + 191, + 215 + ], + "summary": "Parses an uploaded JSON file (single object or array) into mapped and validated BC records, capturing per-item parse errors.", + "tags": [ + "parsing", + "ingestion", + "json" + ], + "complexity": "moderate" + }, + { + "id": "function:services/ingestion.py:deduplicate", + "type": "function", + "name": "deduplicate", + "filePath": "services/ingestion.py", + "lineRange": [ + 218, + 227 + ], + "summary": "Flags parsed records whose BC ID or NCIt code already exists in the database as duplicates, using case-insensitive comparison.", + "tags": [ + "validation", + "ingestion", + "deduplication" + ], + "complexity": "simple" + }, + { + "id": "function:tests/conftest.py:sample_bc", + "type": "function", + "name": "sample_bc", + "filePath": "tests/conftest.py", + "lineRange": [ + 55, + 68 + ], + "summary": "Pytest fixture that inserts a sample BiomedicalConcept into the test database for use by tests needing an existing BC.", + "tags": [ + "test", + "fixture" + ], + "complexity": "simple" + }, + { + "id": "file:tests/test_audit_routes.py", + "type": "file", + "name": "test_audit_routes.py", + "filePath": "tests/test_audit_routes.py", + "summary": "Test suite for the audit log route, covering empty listing, log rendering, and filtering by entity type, action, actor, and pagination parameters.", + "tags": [ + "test", + "audit", + "route-test" + ], + "complexity": "simple" + }, + { + "id": "class:tests/test_audit_routes.py:TestAuditIndex", + "type": "class", + "name": "TestAuditIndex", + "filePath": "tests/test_audit_routes.py", + "lineRange": [ + 14, + 49 + ], + "summary": "Test class exercising the audit index route's rendering, entity/action/actor filters, and pagination parameter handling.", + "tags": [ + "test", + "audit", + "route-test" + ], + "complexity": "moderate" + }, + { + "id": "file:tests/test_bc_routes.py", + "type": "file", + "name": "test_bc_routes.py", + "filePath": "tests/test_bc_routes.py", + "summary": "Large test suite covering BC CRUD routes: listing/search, creation, detail view (including LOINC lookups), editing, clearing NCIt/LOINC codes, submission for review, deletion, export formats, and the CDISC Library detail page.", + "tags": [ + "test", + "bc", + "route-test" + ], + "complexity": "complex" + }, + { + "id": "function:tests/test_bc_routes.py:_bc_form", + "type": "function", + "name": "_bc_form", + "filePath": "tests/test_bc_routes.py", + "lineRange": [ + 10, + 19 + ], + "summary": "Test helper that builds a default BC creation form payload, allowing field overrides via keyword arguments.", + "tags": [ + "test-helper", + "fixture", + "form-data" + ], + "complexity": "simple" + }, + { + "id": "class:tests/test_bc_routes.py:TestBcIndex", + "type": "class", + "name": "TestBcIndex", + "filePath": "tests/test_bc_routes.py", + "lineRange": [ + 27, + 43 + ], + "summary": "Tests the BC index route's default listing, name search, no-match search, and status filtering.", + "tags": [ + "test", + "bc", + "route-test" + ], + "complexity": "moderate" + }, + { + "id": "class:tests/test_bc_routes.py:TestCreateBc", + "type": "class", + "name": "TestCreateBc", + "filePath": "tests/test_bc_routes.py", + "lineRange": [ + 62, + 109 + ], + "summary": "Tests BC creation: success redirect, missing bc_id validation, duplicate id rejection, audit logging, conditional system/system_name population from a LOINC code, and DEC creation.", + "tags": [ + "test", + "bc", + "route-test", + "validation" + ], + "complexity": "complex" + }, + { + "id": "class:tests/test_bc_routes.py:TestBcDetail", + "type": "class", + "name": "TestBcDetail", + "filePath": "tests/test_bc_routes.py", + "lineRange": [ + 117, + 224 + ], + "summary": "Tests the BC detail page, including 404 handling and conditional LOINC API calls and spinner behavior based on whether a LOINC code is set.", + "tags": [ + "test", + "bc", + "loinc", + "route-test" + ], + "complexity": "complex" + }, + { + "id": "class:tests/test_bc_routes.py:TestEditBc", + "type": "class", + "name": "TestEditBc", + "filePath": "tests/test_bc_routes.py", + "lineRange": [ + 232, + 332 + ], + "summary": "Tests BC editing, covering short name updates, audit logging, clearing NCIt/LOINC/parent BC fields, and consistency between LOINC code and system/system_name fields.", + "tags": [ + "test", + "bc", + "route-test", + "validation" + ], + "complexity": "complex" + }, + { + "id": "class:tests/test_bc_routes.py:TestClearNcitCode", + "type": "class", + "name": "TestClearNcitCode", + "filePath": "tests/test_bc_routes.py", + "lineRange": [ + 340, + 370 + ], + "summary": "Tests the endpoint that clears a BC's NCIt code and associated metadata, including cascading clearing of the parent BC id.", + "tags": [ + "test", + "bc", + "ncit", + "route-test" + ], + "complexity": "moderate" + }, + { + "id": "class:tests/test_bc_routes.py:TestClearLoincCode", + "type": "class", + "name": "TestClearLoincCode", + "filePath": "tests/test_bc_routes.py", + "lineRange": [ + 378, + 416 + ], + "summary": "Tests the endpoint that clears a BC's LOINC code, associated metadata, and dependent system/system_name fields.", + "tags": [ + "test", + "bc", + "loinc", + "route-test" + ], + "complexity": "moderate" + }, + { + "id": "class:tests/test_bc_routes.py:TestSubmitForReview", + "type": "class", + "name": "TestSubmitForReview", + "filePath": "tests/test_bc_routes.py", + "lineRange": [ + 424, + 435 + ], + "summary": "Tests that submitting a BC for review advances its governance status and writes an audit log entry.", + "tags": [ + "test", + "bc", + "governance", + "route-test" + ], + "complexity": "simple" + }, + { + "id": "class:tests/test_bc_routes.py:TestDeleteBc", + "type": "class", + "name": "TestDeleteBc", + "filePath": "tests/test_bc_routes.py", + "lineRange": [ + 443, + 457 + ], + "summary": "Tests BC deletion, audit logging on delete, and 404 handling for nonexistent BCs.", + "tags": [ + "test", + "bc", + "route-test" + ], + "complexity": "moderate" + }, + { + "id": "class:tests/test_bc_routes.py:TestExport", + "type": "class", + "name": "TestExport", + "filePath": "tests/test_bc_routes.py", + "lineRange": [ + 465, + 479 + ], + "summary": "Tests BC export endpoints across JSON, XLSX, and ODM-XML formats.", + "tags": [ + "test", + "bc", + "export", + "route-test" + ], + "complexity": "moderate" + }, + { + "id": "class:tests/test_bc_routes.py:TestLibraryDetail", + "type": "class", + "name": "TestLibraryDetail", + "filePath": "tests/test_bc_routes.py", + "lineRange": [ + 511, + 547 + ], + "summary": "Tests the CDISC Library detail page rendering, API-error redirects, and conditional LOINC API calls based on LOINC coding presence.", + "tags": [ + "test", + "bc", + "loinc", + "cdisc-library", + "route-test" + ], + "complexity": "complex" + }, + { + "id": "file:tests/test_governance_routes.py", + "type": "file", + "name": "test_governance_routes.py", + "filePath": "tests/test_governance_routes.py", + "summary": "Test suite for the governance Kanban board routes: stage advancement, rejection, and XLSX export of stage-3 BCs with LOINC-derived system columns.", + "tags": [ + "test", + "governance", + "route-test" + ], + "complexity": "complex" + }, + { + "id": "class:tests/test_governance_routes.py:TestAdvance", + "type": "class", + "name": "TestAdvance", + "filePath": "tests/test_governance_routes.py", + "lineRange": [ + 17, + 68 + ], + "summary": "Tests governance stage advancement, including multi-stage progression, staying at published, governance record and audit log creation, 404 handling, and AJAX JSON responses.", + "tags": [ + "test", + "governance", + "route-test" + ], + "complexity": "complex" + }, + { + "id": "class:tests/test_governance_routes.py:TestReject", + "type": "class", + "name": "TestReject", + "filePath": "tests/test_governance_routes.py", + "lineRange": [ + 71, + 105 + ], + "summary": "Tests governance rejection back to provisional status, governance record and audit log creation, AJAX responses, and 404 handling.", + "tags": [ + "test", + "governance", + "route-test" + ], + "complexity": "moderate" + }, + { + "id": "class:tests/test_governance_routes.py:TestGovernanceExport", + "type": "class", + "name": "TestGovernanceExport", + "filePath": "tests/test_governance_routes.py", + "lineRange": [ + 108, + 236 + ], + "summary": "Tests the governance XLSX export endpoint: file format enforcement, filtering to stage-3 BCs, and correct population of System/System Name/Code columns based on LOINC code presence.", + "tags": [ + "test", + "governance", + "export", + "route-test" + ], + "complexity": "complex" + }, + { + "id": "file:tests/test_ingestion_routes.py", + "type": "file", + "name": "test_ingestion_routes.py", + "filePath": "tests/test_ingestion_routes.py", + "summary": "Test suite for the ingestion pipeline routes: file upload (CSV/JSON), duplicate BC flagging, per-record approve/reject, and bulk approve-all with audit logging.", + "tags": [ + "test", + "ingestion", + "route-test" + ], + "complexity": "complex" + }, + { + "id": "class:tests/test_ingestion_routes.py:TestUpload", + "type": "class", + "name": "TestUpload", + "filePath": "tests/test_ingestion_routes.py", + "lineRange": [ + 32, + 83 + ], + "summary": "Tests file upload handling for CSV and JSON ingestion, missing-file and wrong-extension errors, and duplicate BC detection.", + "tags": [ + "test", + "ingestion", + "upload", + "route-test" + ], + "complexity": "complex" + }, + { + "id": "class:tests/test_ingestion_routes.py:TestApprove", + "type": "class", + "name": "TestApprove", + "filePath": "tests/test_ingestion_routes.py", + "lineRange": [ + 86, + 131 + ], + "summary": "Tests approving an ingestion record, verifying BC creation, status transition, 404 handling, and duplicate-prevention when the BC already exists.", + "tags": [ + "test", + "ingestion", + "route-test" + ], + "complexity": "complex" + }, + { + "id": "class:tests/test_ingestion_routes.py:TestReject", + "type": "class", + "name": "TestReject", + "filePath": "tests/test_ingestion_routes.py", + "lineRange": [ + 134, + 170 + ], + "summary": "Tests rejecting an ingestion record, ensuring no BC is created and 404 handling for missing records.", + "tags": [ + "test", + "ingestion", + "route-test" + ], + "complexity": "moderate" + }, + { + "id": "class:tests/test_ingestion_routes.py:TestApproveAll", + "type": "class", + "name": "TestApproveAll", + "filePath": "tests/test_ingestion_routes.py", + "lineRange": [ + 173, + 204 + ], + "summary": "Tests bulk approval of all pending ingestion records, skipping records with validation errors and writing an audit log entry per created BC.", + "tags": [ + "test", + "ingestion", + "bulk-approve", + "route-test" + ], + "complexity": "moderate" + }, + { + "id": "file:tests/test_ingestion_service.py", + "type": "file", + "name": "test_ingestion_service.py", + "filePath": "tests/test_ingestion_service.py", + "summary": "Unit test suite for the ingestion service module, covering field similarity scoring, field mapping/matching, BC validation, deduplication, and XLSX/CSV/JSON parsing and grouping logic.", + "tags": [ + "test", + "ingestion", + "service-test", + "parsing" + ], + "complexity": "complex" + }, + { + "id": "class:tests/test_ingestion_service.py:TestSimilarity", + "type": "class", + "name": "TestSimilarity", + "filePath": "tests/test_ingestion_service.py", + "lineRange": [ + 22, + 34 + ], + "summary": "Tests the fuzzy string similarity scorer used for field-name matching, covering identical, case-insensitive, dissimilar, and partial matches.", + "tags": [ + "test", + "ingestion", + "similarity" + ], + "complexity": "simple" + }, + { + "id": "class:tests/test_ingestion_service.py:TestMatchField", + "type": "class", + "name": "TestMatchField", + "filePath": "tests/test_ingestion_service.py", + "lineRange": [ + 42, + 65 + ], + "summary": "Tests matching a raw column name to a canonical field via known aliases, definition synonyms, whitespace normalization, and low-confidence fallback.", + "tags": [ + "test", + "ingestion", + "field-mapping" + ], + "complexity": "moderate" + }, + { + "id": "class:tests/test_ingestion_service.py:TestMapFields", + "type": "class", + "name": "TestMapFields", + "filePath": "tests/test_ingestion_service.py", + "lineRange": [ + 73, + 93 + ], + "summary": "Tests mapping a raw row dict to canonical fields, including handling of None/NaN values, low-confidence exclusion, and value stripping.", + "tags": [ + "test", + "ingestion", + "field-mapping" + ], + "complexity": "moderate" + }, + { + "id": "class:tests/test_ingestion_service.py:TestValidateBc", + "type": "class", + "name": "TestValidateBc", + "filePath": "tests/test_ingestion_service.py", + "lineRange": [ + 101, + 133 + ], + "summary": "Tests BC record validation rules: required short name/definition, requiring at least one of NCIt/LOINC code, and NCIt code format checking.", + "tags": [ + "test", + "ingestion", + "validation" + ], + "complexity": "moderate" + }, + { + "id": "class:tests/test_ingestion_service.py:TestDeduplicate", + "type": "class", + "name": "TestDeduplicate", + "filePath": "tests/test_ingestion_service.py", + "lineRange": [ + 141, + 164 + ], + "summary": "Tests deduplication of parsed ingestion records against existing BC ids, including case-insensitive comparison and NCIt-code fallback matching.", + "tags": [ + "test", + "ingestion", + "deduplication" + ], + "complexity": "moderate" + }, + { + "id": "class:tests/test_ingestion_service.py:TestParseCsv", + "type": "class", + "name": "TestParseCsv", + "filePath": "tests/test_ingestion_service.py", + "lineRange": [ + 172, + 198 + ], + "summary": "Tests CSV parsing into mapped/validated records, including error handling for invalid files and validation-error propagation.", + "tags": [ + "test", + "ingestion", + "parsing", + "csv" + ], + "complexity": "moderate" + }, + { + "id": "class:tests/test_ingestion_service.py:TestParseJson", + "type": "class", + "name": "TestParseJson", + "filePath": "tests/test_ingestion_service.py", + "lineRange": [ + 206, + 223 + ], + "summary": "Tests JSON parsing for both array-of-objects and single-object inputs, and error handling for invalid JSON.", + "tags": [ + "test", + "ingestion", + "parsing", + "json" + ], + "complexity": "simple" + }, + { + "id": "class:tests/test_ingestion_service.py:TestGroupByBc", + "type": "class", + "name": "TestGroupByBc", + "filePath": "tests/test_ingestion_service.py", + "lineRange": [ + 231, + 259 + ], + "summary": "Tests grouping of parsed rows by BC id, including nested DEC sub-rows, multiple distinct BCs, and skipping rows without a BC id.", + "tags": [ + "test", + "ingestion", + "grouping" + ], + "complexity": "moderate" + }, + { + "id": "file:tests/test_models.py", + "type": "file", + "name": "test_models.py", + "filePath": "tests/test_models.py", + "summary": "Unit tests for the AuditLog, IngestionRecord, and BiomedicalConcept ORM models, verifying JSON-backed property round-tripping, computed averages, and dictionary serialization.", + "tags": [ + "test", + "models", + "orm" + ], + "complexity": "moderate" + }, + { + "id": "class:tests/test_models.py:TestAuditLogJsonProperties", + "type": "class", + "name": "TestAuditLogJsonProperties", + "filePath": "tests/test_models.py", + "lineRange": [ + 9, + 44 + ], + "summary": "Tests round-tripping of AuditLog's JSON-backed before_state/after_state properties and correct handling of None values on a persisted record.", + "tags": [ + "test", + "models", + "audit", + "serialization" + ], + "complexity": "moderate" + }, + { + "id": "class:tests/test_models.py:TestIngestionRecordProperties", + "type": "class", + "name": "TestIngestionRecordProperties", + "filePath": "tests/test_models.py", + "lineRange": [ + 47, + 87 + ], + "summary": "Tests round-tripping of IngestionRecord's JSON-backed mapped/confidences/errors/decs properties and the correctness of the computed average confidence.", + "tags": [ + "test", + "models", + "ingestion", + "serialization" + ], + "complexity": "moderate" + }, + { + "id": "class:tests/test_models.py:TestBiomedicalConceptToDict", + "type": "class", + "name": "TestBiomedicalConceptToDict", + "filePath": "tests/test_models.py", + "lineRange": [ + 90, + 116 + ], + "summary": "Tests that BiomedicalConcept.to_dict() includes required keys, matches field values, and defaults status to provisional.", + "tags": [ + "test", + "models", + "bc", + "serialization" + ], + "complexity": "simple" + }, + { + "id": "file:models/governance.py", + "type": "file", + "name": "governance.py", + "filePath": "models/governance.py", + "summary": "SQLAlchemy model for GovernanceRecord, tracking Biomedical Concept approval workflow stage transitions.", + "tags": [ + "data-model", + "database", + "governance", + "tested" + ], + "complexity": "simple" + }, + { + "id": "class:models/governance.py:GovernanceRecord", + "type": "class", + "name": "GovernanceRecord", + "filePath": "models/governance.py", + "lineRange": [ + 6, + 16 + ], + "summary": "ORM model recording a single governance stage transition (status, actor, comment, timestamp) for a Biomedical Concept.", + "tags": [ + "data-model", + "governance", + "audit" + ], + "complexity": "simple" + }, + { + "id": "file:routes/bc.py", + "type": "file", + "name": "bc.py", + "filePath": "routes/bc.py", + "summary": "Flask blueprint implementing BC CRUD, NCIt/LOINC metadata lookup, export, and lifecycle actions (submit for review, delete) for Biomedical Concepts.", + "tags": [ + "api-handler", + "flask-blueprint", + "crud", + "routes", + "tested" + ], + "complexity": "complex" + }, + { + "id": "function:routes/bc.py:index", + "type": "function", + "name": "index", + "filePath": "routes/bc.py", + "lineRange": [ + 23, + 39 + ], + "summary": "Renders the paginated, filterable list of Biomedical Concepts based on search term and status query params.", + "tags": [ + "api-handler", + "listing", + "pagination" + ], + "complexity": "moderate" + }, + { + "id": "function:routes/bc.py:new_bc", + "type": "function", + "name": "new_bc", + "filePath": "routes/bc.py", + "lineRange": [ + 43, + 59 + ], + "summary": "Renders the new-BC creation form, prefilling NCIt fields when arriving from an NCIt search selection.", + "tags": [ + "api-handler", + "form" + ], + "complexity": "moderate" + }, + { + "id": "function:routes/bc.py:export", + "type": "function", + "name": "export", + "filePath": "routes/bc.py", + "lineRange": [ + 63, + 85 + ], + "summary": "Exports all Biomedical Concepts as XLSX, ODM-XML, or JSON depending on the requested format query parameter.", + "tags": [ + "api-handler", + "export" + ], + "complexity": "moderate" + }, + { + "id": "function:routes/bc.py:library_detail", + "type": "function", + "name": "library_detail", + "filePath": "routes/bc.py", + "lineRange": [ + 89, + 114 + ], + "summary": "Displays a published CDISC Library BC's detail, merging in LOINC lookup results for CDASH/QS-style concepts.", + "tags": [ + "api-handler", + "integration" + ], + "complexity": "moderate" + }, + { + "id": "function:routes/bc.py:detail", + "type": "function", + "name": "detail", + "filePath": "routes/bc.py", + "lineRange": [ + 118, + 151 + ], + "summary": "Renders a curated BC's detail page including its Data Element Concepts, and refreshes LOINC lookup data when applicable.", + "tags": [ + "api-handler", + "crud" + ], + "complexity": "complex" + }, + { + "id": "function:routes/bc.py:fetch_metadata", + "type": "function", + "name": "fetch_metadata", + "filePath": "routes/bc.py", + "lineRange": [ + 155, + 182 + ], + "summary": "AJAX endpoint that fetches and caches NCIt and LOINC metadata for a BC and returns it as JSON.", + "tags": [ + "api-handler", + "ajax", + "integration" + ], + "complexity": "moderate" + }, + { + "id": "function:routes/bc.py:create", + "type": "function", + "name": "create", + "filePath": "routes/bc.py", + "lineRange": [ + 186, + 194 + ], + "summary": "Handles the new-BC form submission, delegating persistence and DEC creation to bc_service.", + "tags": [ + "api-handler", + "crud" + ], + "complexity": "simple" + }, + { + "id": "function:routes/bc.py:edit", + "type": "function", + "name": "edit", + "filePath": "routes/bc.py", + "lineRange": [ + 198, + 203 + ], + "summary": "Handles the BC edit form submission, updating fields and DECs via bc_service.", + "tags": [ + "api-handler", + "crud" + ], + "complexity": "simple" + }, + { + "id": "function:routes/bc.py:clear_ncit", + "type": "function", + "name": "clear_ncit", + "filePath": "routes/bc.py", + "lineRange": [ + 207, + 217 + ], + "summary": "Clears a BC's NCIt mapping fields and records the change in the audit log.", + "tags": [ + "api-handler", + "audit" + ], + "complexity": "simple" + }, + { + "id": "function:routes/bc.py:clear_loinc", + "type": "function", + "name": "clear_loinc", + "filePath": "routes/bc.py", + "lineRange": [ + 221, + 232 + ], + "summary": "Clears a BC's LOINC mapping fields and records the change in the audit log.", + "tags": [ + "api-handler", + "audit" + ], + "complexity": "simple" + }, + { + "id": "function:routes/bc.py:submit_for_review", + "type": "function", + "name": "submit_for_review", + "filePath": "routes/bc.py", + "lineRange": [ + 236, + 240 + ], + "summary": "Transitions a BC from draft into the governance review queue.", + "tags": [ + "api-handler", + "governance" + ], + "complexity": "simple" + }, + { + "id": "function:routes/bc.py:delete", + "type": "function", + "name": "delete", + "filePath": "routes/bc.py", + "lineRange": [ + 244, + 255 + ], + "summary": "Deletes a BC along with cascading child BCs and governance records, logging the deletion to the audit trail.", + "tags": [ + "api-handler", + "crud", + "audit" + ], + "complexity": "moderate" + }, + { + "id": "function:routes/bc.py:_decs_from_form", + "type": "function", + "name": "_decs_from_form", + "filePath": "routes/bc.py", + "lineRange": [ + 258, + 276 + ], + "summary": "Private helper that reconstructs the list of Data Element Concept row dicts from parallel multi-value form fields.", + "tags": [ + "utility", + "form-parsing" + ], + "complexity": "moderate" + }, + { + "id": "file:routes/governance.py", + "type": "file", + "name": "governance.py", + "filePath": "routes/governance.py", + "summary": "Flask blueprint for the governance Kanban board \u2014 viewing BCs by stage, exporting the published set, and advancing/rejecting BCs through the approval workflow.", + "tags": [ + "api-handler", + "governance", + "flask-blueprint", + "tested" + ], + "complexity": "moderate" + }, + { + "id": "function:routes/governance.py:board", + "type": "function", + "name": "board", + "filePath": "routes/governance.py", + "lineRange": [ + 17, + 26 + ], + "summary": "Renders the 4-stage governance Kanban board with BCs grouped by their current status.", + "tags": [ + "api-handler", + "governance" + ], + "complexity": "simple" + }, + { + "id": "function:routes/governance.py:export", + "type": "function", + "name": "export", + "filePath": "routes/governance.py", + "lineRange": [ + 30, + 45 + ], + "summary": "Exports the governance workbook for published BCs, sanitizing the requested filename before responding.", + "tags": [ + "api-handler", + "export", + "governance" + ], + "complexity": "moderate" + }, + { + "id": "function:routes/governance.py:advance", + "type": "function", + "name": "advance", + "filePath": "routes/governance.py", + "lineRange": [ + 49, + 58 + ], + "summary": "Advances a BC to the next governance stage and responds with JSON or a redirect depending on the request type.", + "tags": [ + "api-handler", + "governance", + "workflow" + ], + "complexity": "simple" + }, + { + "id": "function:routes/governance.py:reject_bc", + "type": "function", + "name": "reject_bc", + "filePath": "routes/governance.py", + "lineRange": [ + 62, + 68 + ], + "summary": "Rejects a BC back through the governance workflow with an optional comment, supporting JSON or redirect responses.", + "tags": [ + "api-handler", + "governance", + "workflow" + ], + "complexity": "simple" + }, + { + "id": "file:routes/ncit.py", + "type": "file", + "name": "ncit.py", + "filePath": "routes/ncit.py", + "summary": "Flask blueprint for NCI Thesaurus search and mapping to Biomedical Concepts, including AJAX search and single-concept detail lookups.", + "tags": [ + "api-handler", + "ncit", + "flask-blueprint", + "tested" + ], + "complexity": "moderate" + }, + { + "id": "function:routes/ncit.py:index", + "type": "function", + "name": "index", + "filePath": "routes/ncit.py", + "lineRange": [ + 16, + 17 + ], + "summary": "Redirects the NCIt blueprint's index route to the mapping view.", + "tags": [ + "api-handler", + "routing" + ], + "complexity": "simple" + }, + { + "id": "function:routes/ncit.py:mapping", + "type": "function", + "name": "mapping", + "filePath": "routes/ncit.py", + "lineRange": [ + 21, + 30 + ], + "summary": "Lists up to 50 Biomedical Concepts that still lack an NCIt code, for manual mapping.", + "tags": [ + "api-handler", + "ncit" + ], + "complexity": "simple" + }, + { + "id": "function:routes/ncit.py:search_ncit", + "type": "function", + "name": "search_ncit", + "filePath": "routes/ncit.py", + "lineRange": [ + 34, + 61 + ], + "summary": "Searches NCIt concepts by term via the EVS API and returns results as JSON or a rendered partial, depending on the caller.", + "tags": [ + "api-handler", + "ncit", + "ajax" + ], + "complexity": "moderate" + }, + { + "id": "function:routes/ncit.py:concept_detail", + "type": "function", + "name": "concept_detail", + "filePath": "routes/ncit.py", + "lineRange": [ + 65, + 73 + ], + "summary": "Fetches a single NCIt concept's detail metadata by code and returns it as JSON, aborting with 404 on lookup error.", + "tags": [ + "api-handler", + "ncit" + ], + "complexity": "simple" + }, + { + "id": "function:routes/ncit.py:resolve", + "type": "function", + "name": "resolve", + "filePath": "routes/ncit.py", + "lineRange": [ + 77, + 83 + ], + "summary": "Applies a chosen NCIt code to a Biomedical Concept via bc_service and redirects back to the BC detail page.", + "tags": [ + "api-handler", + "ncit" + ], + "complexity": "simple" + }, + { + "id": "file:services/__init__.py", + "type": "file", + "name": "__init__.py", + "filePath": "services/__init__.py", + "summary": "Empty package marker enabling the services directory to be imported as a Python package.", + "tags": [ + "package-init" + ], + "complexity": "simple" + }, + { + "id": "file:services/audit.py", + "type": "file", + "name": "audit.py", + "filePath": "services/audit.py", + "summary": "Provides the log_change helper used across services and routes to persist immutable AuditLog rows for every entity mutation.", + "tags": [ + "audit", + "utility", + "service" + ], + "complexity": "simple" + }, + { + "id": "function:services/audit.py:log_change", + "type": "function", + "name": "log_change", + "filePath": "services/audit.py", + "lineRange": [ + 16, + 32 + ], + "summary": "Creates and persists an AuditLog row capturing entity type/id, action, actor, and before/after JSON snapshots.", + "tags": [ + "audit", + "database", + "logging" + ], + "complexity": "simple" + }, + { + "id": "file:services/bc_service.py", + "type": "file", + "name": "bc_service.py", + "filePath": "services/bc_service.py", + "summary": "Core business logic for creating, updating, and managing Biomedical Concepts and their Data Element Concepts, including NCIt remapping/merging and review submission.", + "tags": [ + "service", + "business-logic", + "crud" + ], + "complexity": "complex" + }, + { + "id": "function:services/bc_service.py:_get_bc_or_raise", + "type": "function", + "name": "_get_bc_or_raise", + "filePath": "services/bc_service.py", + "lineRange": [ + 28, + 32 + ], + "summary": "Looks up a BiomedicalConcept by its bc_id, raising NotFoundError when it does not exist.", + "tags": [ + "utility", + "data-access" + ], + "complexity": "simple" + }, + { + "id": "function:services/bc_service.py:apply_bc_fields", + "type": "function", + "name": "apply_bc_fields", + "filePath": "services/bc_service.py", + "lineRange": [ + 35, + 63 + ], + "summary": "Applies submitted field data onto a BiomedicalConcept instance, handling boolean coercion and LOINC/NCIt/parent-BC field normalization.", + "tags": [ + "business-logic", + "data-model" + ], + "complexity": "moderate" + }, + { + "id": "function:services/bc_service.py:create_bc", + "type": "function", + "name": "create_bc", + "filePath": "services/bc_service.py", + "lineRange": [ + 66, + 85 + ], + "summary": "Creates a new BiomedicalConcept from submitted data, validates uniqueness of the bc_id, persists it, and logs an audit entry.", + "tags": [ + "service", + "crud", + "audit" + ], + "complexity": "moderate" + }, + { + "id": "function:services/bc_service.py:update_bc", + "type": "function", + "name": "update_bc", + "filePath": "services/bc_service.py", + "lineRange": [ + 88, + 95 + ], + "summary": "Updates an existing BiomedicalConcept's fields and records the before/after change in the audit log.", + "tags": [ + "service", + "crud", + "audit" + ], + "complexity": "simple" + }, + { + "id": "function:services/bc_service.py:save_decs", + "type": "function", + "name": "save_decs", + "filePath": "services/bc_service.py", + "lineRange": [ + 98, + 121 + ], + "summary": "Replaces a BC's Data Element Concept rows, deleting existing DECs and inserting the submitted set with sort order.", + "tags": [ + "service", + "crud" + ], + "complexity": "moderate" + }, + { + "id": "function:services/bc_service.py:map_ncit_to_bc", + "type": "function", + "name": "map_ncit_to_bc", + "filePath": "services/bc_service.py", + "lineRange": [ + 124, + 148 + ], + "summary": "Remaps a BC's NCIt code, and when the target NCIt-derived id already exists, merges the current BC's DECs, specializations, and governance history into it.", + "tags": [ + "service", + "ncit", + "merge" + ], + "complexity": "complex" + }, + { + "id": "function:services/bc_service.py:submit_bc_for_review", + "type": "function", + "name": "submit_bc_for_review", + "filePath": "services/bc_service.py", + "lineRange": [ + 151, + 159 + ], + "summary": "Transitions a BC's status to under-review and logs the change to the audit trail.", + "tags": [ + "service", + "governance", + "audit" + ], + "complexity": "simple" + }, + { + "id": "file:services/export.py", + "type": "file", + "name": "export.py", + "filePath": "services/export.py", + "summary": "Exporters that render Biomedical Concept collections to JSON, styled XLSX, governance XLSX, and CDISC ODM-XML formats.", + "tags": [ + "export", + "serialization", + "service", + "tested" + ], + "complexity": "complex" + }, + { + "id": "function:services/export.py:export_json", + "type": "function", + "name": "export_json", + "filePath": "services/export.py", + "lineRange": [ + 37, + 39 + ], + "summary": "Serializes a list of BC dictionaries to a pretty-printed JSON string.", + "tags": [ + "export", + "serialization" + ], + "complexity": "simple" + }, + { + "id": "function:services/export.py:export_xlsx", + "type": "function", + "name": "export_xlsx", + "filePath": "services/export.py", + "lineRange": [ + 42, + 67 + ], + "summary": "Builds a styled openpyxl workbook of BC export fields with a bold header row, falling back to LOINC code when no NCIt code is present.", + "tags": [ + "export", + "xlsx" + ], + "complexity": "moderate" + }, + { + "id": "function:services/export.py:export_governance_xlsx", + "type": "function", + "name": "export_governance_xlsx", + "filePath": "services/export.py", + "lineRange": [ + 88, + 143 + ], + "summary": "Builds a governance-focused workbook containing a BC summary row followed by its Data Element Concept rows for CDISC governance review.", + "tags": [ + "export", + "xlsx", + "governance" + ], + "complexity": "complex" + }, + { + "id": "function:services/export.py:export_odm_xml", + "type": "function", + "name": "export_odm_xml", + "filePath": "services/export.py", + "lineRange": [ + 146, + 185 + ], + "summary": "Builds a CDISC ODM-XML document with ItemDefs and NCIt Aliases from a list of Biomedical Concepts.", + "tags": [ + "export", + "xml", + "odm" + ], + "complexity": "complex" + }, + { + "id": "file:services/governance_service.py", + "type": "file", + "name": "governance_service.py", + "filePath": "services/governance_service.py", + "summary": "Implements governance stage transitions (advance/reject) for Biomedical Concepts, creating GovernanceRecord history rows and audit log entries.", + "tags": [ + "service", + "governance", + "workflow" + ], + "complexity": "moderate" + }, + { + "id": "function:services/governance_service.py:advance_governance", + "type": "function", + "name": "advance_governance", + "filePath": "services/governance_service.py", + "lineRange": [ + 16, + 40 + ], + "summary": "Moves a BC forward through the STATUS_ORDER stage sequence, recording a GovernanceRecord and audit log entry for the transition.", + "tags": [ + "service", + "governance", + "workflow" + ], + "complexity": "moderate" + }, + { + "id": "function:services/governance_service.py:reject_bc", + "type": "function", + "name": "reject_bc", + "filePath": "services/governance_service.py", + "lineRange": [ + 43, + 60 + ], + "summary": "Reverts a BC's governance stage on rejection, recording a GovernanceRecord and audit log entry with the rejection comment.", + "tags": [ + "service", + "governance", + "workflow" + ], + "complexity": "moderate" + }, + { + "id": "file:services/ncit_api.py", + "type": "file", + "name": "ncit_api.py", + "filePath": "services/ncit_api.py", + "summary": "NCI Thesaurus EVS REST API client providing concept search and detailed concept retrieval (parents, semantic type, synonyms, definitions) with caching.", + "tags": [ + "api-client", + "ncit", + "integration", + "service", + "tested" + ], + "complexity": "moderate" + }, + { + "id": "function:services/ncit_api.py:_pick_definition", + "type": "function", + "name": "_pick_definition", + "filePath": "services/ncit_api.py", + "lineRange": [ + 14, + 21 + ], + "summary": "Selects the NCI-source or, failing that, first available definition from a concept's list of definitions.", + "tags": [ + "utility", + "data-transform" + ], + "complexity": "simple" + }, + { + "id": "class:services/ncit_api.py:NCItApiClient", + "type": "class", + "name": "NCItApiClient", + "filePath": "services/ncit_api.py", + "lineRange": [ + 24, + 85 + ], + "summary": "HTTP client wrapping the NCI EVS REST API, exposing concept search and cached, extended concept-detail lookups (parents, semantic type, synonyms, definitions) used throughout the NCIt mapping workflow.", + "tags": [ + "api-client", + "integration", + "caching" + ], + "complexity": "moderate" + }, + { + "id": "file:tests/test_export_service.py", + "type": "file", + "name": "test_export_service.py", + "filePath": "tests/test_export_service.py", + "summary": "Unit tests covering JSON, XLSX, governance XLSX, and ODM-XML export functions in services/export.py, verifying headers, blanking rules, and document structure.", + "tags": [ + "test", + "export", + "unit-test" + ], + "complexity": "complex" + }, + { + "id": "class:tests/test_export_service.py:TestExportJson", + "type": "class", + "name": "TestExportJson", + "filePath": "tests/test_export_service.py", + "lineRange": [ + 55, + 70 + ], + "summary": "Tests export_json's round-trip serialization, empty-list handling, and coercion of non-JSON-serializable values.", + "tags": [ + "test", + "export" + ], + "complexity": "simple" + }, + { + "id": "class:tests/test_export_service.py:TestExportXlsx", + "type": "class", + "name": "TestExportXlsx", + "filePath": "tests/test_export_service.py", + "lineRange": [ + 73, + 104 + ], + "summary": "Tests export_xlsx's header and row output, empty-list behavior, blank rendering of missing fields, and the LOINC-code fallback in the Code column.", + "tags": [ + "test", + "export", + "xlsx" + ], + "complexity": "moderate" + }, + { + "id": "class:tests/test_export_service.py:TestExportGovernanceXlsx", + "type": "class", + "name": "TestExportGovernanceXlsx", + "filePath": "tests/test_export_service.py", + "lineRange": [ + 107, + 180 + ], + "summary": "Tests export_governance_xlsx's BC-row-then-DEC-rows layout and the blanking of system fields for BCs without a LOINC code.", + "tags": [ + "test", + "export", + "governance" + ], + "complexity": "moderate" + }, + { + "id": "class:tests/test_export_service.py:TestExportOdmXml", + "type": "class", + "name": "TestExportOdmXml", + "filePath": "tests/test_export_service.py", + "lineRange": [ + 183, + 207 + ], + "summary": "Tests export_odm_xml's document structure, conditional inclusion of the Alias element only when an NCIt code is present, and empty-list handling.", + "tags": [ + "test", + "export", + "xml" + ], + "complexity": "simple" + }, + { + "id": "file:tests/test_ncit.py", + "type": "file", + "name": "test_ncit.py", + "filePath": "tests/test_ncit.py", + "summary": "Unit tests for NCItApiClient.get_concept's extended metadata extraction and the /ncit concept-detail route's JSON responses and error handling.", + "tags": [ + "test", + "ncit", + "unit-test" + ], + "complexity": "moderate" + }, + { + "id": "class:tests/test_ncit.py:TestNcitGetConceptExtended", + "type": "class", + "name": "TestNcitGetConceptExtended", + "filePath": "tests/test_ncit.py", + "lineRange": [ + 43, + 112 + ], + "summary": "Tests NCItApiClient.get_concept's extraction of parents, semantic type, source synonyms, and full definitions list, plus empty and error-path handling.", + "tags": [ + "test", + "ncit", + "api-client" + ], + "complexity": "moderate" + }, + { + "id": "class:tests/test_ncit.py:TestNcitConceptRoute", + "type": "class", + "name": "TestNcitConceptRoute", + "filePath": "tests/test_ncit.py", + "lineRange": [ + 131, + 156 + ], + "summary": "Tests the /ncit concept-detail route's JSON response shape, correct pass-through of the requested code, and 404 response when the service errors.", + "tags": [ + "test", + "ncit", + "api-handler" + ], + "complexity": "simple" + }, + { + "id": "file:routes/dashboard.py", + "type": "file", + "name": "dashboard.py", + "filePath": "routes/dashboard.py", + "summary": "Flask blueprint for the dashboard home page; fetches CDISC Library BC and specialization counts concurrently via a thread pool and combines them with local KPI stats (BC status counts, recent activity, audit log) for the landing page.", + "tags": [ + "dashboard", + "api-handler", + "kpi", + "route" + ], + "complexity": "moderate" + }, + { + "id": "file:routes/loinc.py", + "type": "file", + "name": "loinc.py", + "filePath": "routes/loinc.py", + "summary": "Flask blueprint exposing a single AJAX search endpoint that proxies term queries to the NLM LOINC API client and returns JSON results.", + "tags": [ + "api-handler", + "loinc", + "search", + "route", + "tested" + ], + "complexity": "simple" + }, + { + "id": "file:routes/specializations.py", + "type": "file", + "name": "specializations.py", + "filePath": "routes/specializations.py", + "summary": "Flask blueprint managing CDISC dataset specializations: lists local and CDISC Library specializations, and supports manual creation, deletion, and auto-generation of variable-level specializations from a BC's data element concepts.", + "tags": [ + "api-handler", + "crud", + "specialization", + "route", + "tested" + ], + "complexity": "complex" + }, + { + "id": "file:services/api_cache.py", + "type": "file", + "name": "api_cache.py", + "filePath": "services/api_cache.py", + "summary": "Shared stale-tolerant in-memory cache helper used by the CDISC Library, NCI EVS, and LOINC API clients; serves fresh data for 5 minutes and falls back to stale data for up to an hour if a refresh fails, so a slow upstream never blanks a page that rendered fine a minute ago.", + "tags": [ + "utility", + "caching", + "resilience", + "service", + "tested" + ], + "complexity": "simple", + "languageNotes": "Stale-while-revalidate cache pattern shared across multiple API client modules via a single module-level dict." + }, + { + "id": "file:services/cdisc_api.py", + "type": "file", + "name": "cdisc_api.py", + "filePath": "services/cdisc_api.py", + "summary": "REST client for the CDISC Library COSMOS API; wraps biomedical concept, specialization, and duplicate-check lookups with a 5-minute in-memory cache and provides a publish_bc method for pushing approved BCs to the Library.", + "tags": [ + "api-client", + "service", + "cdisc-library", + "http", + "tested" + ], + "complexity": "moderate" + }, + { + "id": "file:services/loinc_api.py", + "type": "file", + "name": "loinc_api.py", + "filePath": "services/loinc_api.py", + "summary": "Client for the NLM Clinical Tables LOINC search API; builds the query with a configurable set of extra fields, optionally applies HTTP Basic Auth, and normalizes results through the shared API cache.", + "tags": [ + "api-client", + "service", + "loinc", + "http", + "tested" + ], + "complexity": "moderate" + }, + { + "id": "file:tests/test_cdisc_api_cache.py", + "type": "file", + "name": "test_cdisc_api_cache.py", + "filePath": "tests/test_cdisc_api_cache.py", + "summary": "Unit tests for the shared cache helper and CDISCApiClient cache-key behavior, covering fresh/stale/expired cache states, refresh-failure fallback, and that raw API keys never leak into cache keys.", + "tags": [ + "test", + "caching", + "cdisc-library" + ], + "complexity": "moderate" + }, + { + "id": "file:tests/test_loinc.py", + "type": "file", + "name": "test_loinc.py", + "filePath": "tests/test_loinc.py", + "summary": "Unit tests for LoincApiClient.search normalization, extra-field handling, Basic Auth toggling, and error handling, plus tests for the /loinc/search Flask route's JSON responses.", + "tags": [ + "test", + "loinc", + "route" + ], + "complexity": "complex" + }, + { + "id": "function:routes/dashboard.py:index", + "type": "function", + "name": "index", + "filePath": "routes/dashboard.py", + "lineRange": [ + 17, + 60 + ], + "summary": "Renders the dashboard home page, fetching CDISC Library BC and specialization counts in parallel via a ThreadPoolExecutor and combining them with local BC status counts, recent-activity queries, and audit log entries.", + "tags": [ + "route", + "dashboard", + "concurrency" + ], + "complexity": "moderate" + }, + { + "id": "function:routes/loinc.py:search", + "type": "function", + "name": "search", + "filePath": "routes/loinc.py", + "lineRange": [ + 13, + 23 + ], + "summary": "Handles GET /loinc/search: reads the search term, detects AJAX/JSON requests, and returns LOINC search results as JSON via LoincApiClient.", + "tags": [ + "route", + "api-handler", + "loinc" + ], + "complexity": "simple" + }, + { + "id": "function:routes/specializations.py:_get_bc_options", + "type": "function", + "name": "_get_bc_options", + "filePath": "routes/specializations.py", + "lineRange": [ + 16, + 25 + ], + "summary": "Builds the dropdown option list of approved Biomedical Concepts for specialization forms, merging CDISC Library BC links with locally curated BiomedicalConcept records.", + "tags": [ + "helper", + "dropdown", + "bc" + ], + "complexity": "simple" + }, + { + "id": "function:routes/specializations.py:index", + "type": "function", + "name": "index", + "filePath": "routes/specializations.py", + "lineRange": [ + 29, + 38 + ], + "summary": "Lists all locally stored dataset specializations alongside available BC options for the specializations index page.", + "tags": [ + "route", + "list" + ], + "complexity": "simple" + }, + { + "id": "function:routes/specializations.py:library_detail", + "type": "function", + "name": "library_detail", + "filePath": "routes/specializations.py", + "lineRange": [ + 42, + 52 + ], + "summary": "Fetches and renders a single dataset specialization directly from the CDISC Library by its specialization path, redirecting with a flash message if the lookup fails.", + "tags": [ + "route", + "cdisc-library" + ], + "complexity": "simple" + }, + { + "id": "function:routes/specializations.py:detail", + "type": "function", + "name": "detail", + "filePath": "routes/specializations.py", + "lineRange": [ + 56, + 67 + ], + "summary": "Renders the detail page for a locally stored dataset specialization identified by its VLM group ID.", + "tags": [ + "route", + "detail" + ], + "complexity": "simple" + }, + { + "id": "function:routes/specializations.py:_variables_from_form", + "type": "function", + "name": "_variables_from_form", + "filePath": "routes/specializations.py", + "lineRange": [ + 70, + 87 + ], + "summary": "Parses indexed variables[i][name]/variables[i][label] form fields into a list of variable dicts for specialization create/generate forms.", + "tags": [ + "helper", + "form-parsing" + ], + "complexity": "simple" + }, + { + "id": "function:routes/specializations.py:create", + "type": "function", + "name": "create", + "filePath": "routes/specializations.py", + "lineRange": [ + 91, + 124 + ], + "summary": "Handles manual creation of a dataset specialization from form input, either updating an existing record or inserting a new DatasetSpecialization, and logs the change via the audit service.", + "tags": [ + "route", + "crud", + "audit" + ], + "complexity": "moderate" + }, + { + "id": "function:routes/specializations.py:delete", + "type": "function", + "name": "delete", + "filePath": "routes/specializations.py", + "lineRange": [ + 128, + 134 + ], + "summary": "Deletes a dataset specialization by VLM group ID and records the deletion in the audit log.", + "tags": [ + "route", + "crud", + "audit" + ], + "complexity": "simple" + }, + { + "id": "function:routes/specializations.py:generate_from_dec", + "type": "function", + "name": "generate_from_dec", + "filePath": "routes/specializations.py", + "lineRange": [ + 138, + 146 + ], + "summary": "AJAX endpoint that checks whether a given BC has any Data Element Concepts available to auto-generate a specialization from.", + "tags": [ + "route", + "ajax", + "validation" + ], + "complexity": "simple" + }, + { + "id": "function:routes/specializations.py:generate", + "type": "function", + "name": "generate", + "filePath": "routes/specializations.py", + "lineRange": [ + 150, + 172 + ], + "summary": "Auto-generates a dataset specialization's variables from a BC's Data Element Concepts and saves it as a new DatasetSpecialization, logging the change via the audit service.", + "tags": [ + "route", + "generation", + "audit" + ], + "complexity": "moderate" + }, + { + "id": "function:services/api_cache.py:cached", + "type": "function", + "name": "cached", + "filePath": "services/api_cache.py", + "lineRange": [ + 22, + 42 + ], + "summary": "Core stale-while-revalidate cache primitive: returns a fresh cached value if within CACHE_TTL, otherwise attempts to refetch via the given callable, falling back to a stale value (up to CACHE_STALE_TTL) if the refresh raises.", + "tags": [ + "caching", + "resilience", + "utility" + ], + "complexity": "moderate", + "languageNotes": "The broad except Exception is intentional \u2014 it's the resilience seam preventing upstream failures from blanking a page that previously rendered fine." + }, + { + "id": "function:services/cdisc_api.py:_config_value", + "type": "function", + "name": "_config_value", + "filePath": "services/cdisc_api.py", + "lineRange": [ + 19, + 26 + ], + "summary": "Resolves a configuration value by checking Flask's current_app.config first, then falling back to an environment variable, so config can be read even outside an app context.", + "tags": [ + "helper", + "config" + ], + "complexity": "simple" + }, + { + "id": "class:services/cdisc_api.py:CDISCApiClient", + "type": "class", + "name": "CDISCApiClient", + "filePath": "services/cdisc_api.py", + "lineRange": [ + 29, + 127 + ], + "summary": "REST client wrapping the CDISC Library COSMOS v2 API: fetches biomedical concepts, individual BC/specialization detail, and dataset specializations (cached via the shared api_cache helper), checks for duplicate BC titles, and publishes approved BCs back to the Library.", + "tags": [ + "api-client", + "cdisc-library", + "service", + "http" + ], + "complexity": "complex", + "languageNotes": "Cache keys are derived from a SHA-256 hash of the API key material rather than the raw key, so secrets never end up as cache dictionary keys in memory dumps or logs." + }, + { + "id": "class:services/loinc_api.py:LoincApiClient", + "type": "class", + "name": "LoincApiClient", + "filePath": "services/loinc_api.py", + "lineRange": [ + 18, + 62 + ], + "summary": "Client for the NLM Clinical Tables LOINC /search endpoint; optionally attaches HTTP Basic Auth from LOINC_USER/LOINC_PASSWORD env vars, requests a configurable set of extra fields, normalizes the paginated response into a flat list, and caches results through the shared api_cache helper.", + "tags": [ + "api-client", + "loinc", + "service", + "http" + ], + "complexity": "moderate" + }, + { + "id": "class:tests/test_cdisc_api_cache.py:TestCachedHelper", + "type": "class", + "name": "TestCachedHelper", + "filePath": "tests/test_cdisc_api_cache.py", + "lineRange": [ + 11, + 64 + ], + "summary": "Tests the cached() helper directly: first-call fetch-and-store, serving fresh entries without refetching, refreshing stale entries, falling back to stale data when refresh fails, and raising when there is no fallback available.", + "tags": [ + "test", + "caching" + ], + "complexity": "moderate" + }, + { + "id": "class:tests/test_cdisc_api_cache.py:TestClientCacheKeys", + "type": "class", + "name": "TestClientCacheKeys", + "filePath": "tests/test_cdisc_api_cache.py", + "lineRange": [ + 67, + 120 + ], + "summary": "Tests that CDISCApiClient's cache keys are derived from a hash rather than the raw API key, that errors from get_biomedical_concepts are encoded into the cached result rather than raised, and that a stale error result is replaced once the cache TTL expires.", + "tags": [ + "test", + "caching", + "security" + ], + "complexity": "moderate" + }, + { + "id": "class:tests/test_loinc.py:TestLoincApiClientSearch", + "type": "class", + "name": "TestLoincApiClientSearch", + "filePath": "tests/test_loinc.py", + "lineRange": [ + 44, + 168 + ], + "summary": "Tests LoincApiClient.search's response normalization, presence of all requested extra fields, Basic Auth header toggling based on environment variables, and graceful handling of empty results, missing codes arrays, and network errors.", + "tags": [ + "test", + "loinc", + "api-client" + ], + "complexity": "complex" + }, + { + "id": "class:tests/test_loinc.py:TestLoincSearchRoute", + "type": "class", + "name": "TestLoincSearchRoute", + "filePath": "tests/test_loinc.py", + "lineRange": [ + 176, + 205 + ], + "summary": "Tests the /loinc/search Flask route: JSON response for AJAX requests, empty-term short-circuiting, that the search term is passed through to the client, and that the format=json query param triggers a JSON response.", + "tags": [ + "test", + "route", + "loinc" + ], + "complexity": "simple" + }, + { + "id": "file:app.py", + "type": "file", + "name": "app.py", + "filePath": "app.py", + "summary": "Flask application factory that configures logging, initializes the shared database and migration extensions, registers all eight feature blueprints, and doubles as the dev-server entry point via `python app.py`.", + "tags": [ + "entry-point", + "flask", + "application-factory", + "blueprint-registration" + ], + "complexity": "moderate" + }, + { + "id": "file:config.py", + "type": "file", + "name": "config.py", + "filePath": "config.py", + "summary": "Centralizes all environment-driven configuration for the Flask app, including the secret key, database URI, CDISC/NCIt API base URLs and credentials, upload size limit, and dev server port.", + "tags": [ + "configuration", + "environment-variables", + "flask", + "settings" + ], + "complexity": "simple" + }, + { + "id": "file:db_bootstrap.py", + "type": "file", + "name": "db_bootstrap.py", + "filePath": "db_bootstrap.py", + "summary": "Brings the app's database to the current Alembic migration head at startup, distinguishing fresh, legacy create_all(), and pre-squash-stamped databases, and raising if the schema predates the squashed baseline.", + "tags": [ + "database-migration", + "alembic", + "bootstrap", + "startup", + "tested" + ], + "complexity": "moderate", + "languageNotes": "Encodes a one-time schema-squash migration strategy: legacy databases are schema-validated then stamped rather than replayed through the old revision chain." + }, + { + "id": "file:extensions.py", + "type": "file", + "name": "extensions.py", + "filePath": "extensions.py", + "summary": "Defines the shared SQLAlchemy `db` and Flask-Migrate `migrate` instances used across the app to avoid circular imports, and configures SQLite WAL mode with a busy timeout so the Flask app and MCP server can coexist as concurrent writers.", + "tags": [ + "database", + "sqlalchemy", + "shared-extension", + "sqlite" + ], + "complexity": "simple" + }, + { + "id": "file:models/__init__.py", + "type": "file", + "name": "__init__.py", + "filePath": "models/__init__.py", + "summary": "Package marker for the `models` module that imports the shared `db` instance so all SQLAlchemy model classes register against a single metadata registry.", + "tags": [ + "entry-point", + "package-init", + "barrel" + ], + "complexity": "simple" + }, + { + "id": "file:tests/test_dashboard.py", + "type": "file", + "name": "test_dashboard.py", + "filePath": "tests/test_dashboard.py", + "summary": "Unit tests for the dashboard route, verifying it renders correctly with live CDISC/NCIt API data, degrades gracefully on API errors and empty results, reflects local DB stats accurately, and issues both API calls concurrently.", + "tags": [ + "test", + "dashboard", + "mocking", + "concurrency" + ], + "complexity": "moderate" + }, + { + "id": "file:tests/test_db_bootstrap.py", + "type": "file", + "name": "test_db_bootstrap.py", + "filePath": "tests/test_db_bootstrap.py", + "summary": "Unit tests for `ensure_db()` covering a fresh baseline upgrade, stamping of a legacy create_all() database, restamping of a pre-squash-stamped database, raising on an outdated schema, and idempotency across repeated runs.", + "tags": [ + "test", + "database-migration", + "alembic", + "fixtures" + ], + "complexity": "moderate" + }, + { + "id": "function:app.py:_configure_logging", + "type": "function", + "name": "_configure_logging", + "filePath": "app.py", + "lineRange": [ + 10, + 21 + ], + "summary": "Attaches a basic logging handler to the root logger exactly once, deferring to any handler already installed by pytest, gunicorn, or the MCP server.", + "tags": [ + "logging", + "initialization", + "utility" + ], + "complexity": "simple" + }, + { + "id": "function:app.py:create_app", + "type": "function", + "name": "create_app", + "filePath": "app.py", + "lineRange": [ + 24, + 50 + ], + "summary": "Application factory that builds the Flask app, applies configuration, initializes the `db` and `migrate` extensions, and registers all eight route blueprints under their URL prefixes.", + "tags": [ + "entry-point", + "application-factory", + "flask", + "blueprint-registration" + ], + "complexity": "moderate" + }, + { + "id": "class:config.py:Config", + "type": "class", + "name": "Config", + "filePath": "config.py", + "lineRange": [ + 4, + 14 + ], + "summary": "Flask configuration object sourcing the secret key, database URI, CDISC/NCIt API settings, upload size limit, and dev server port from environment variables with sensible defaults.", + "tags": [ + "configuration", + "environment-variables", + "settings" + ], + "complexity": "simple" + }, + { + "id": "function:db_bootstrap.py:ensure_db", + "type": "function", + "name": "ensure_db", + "filePath": "db_bootstrap.py", + "lineRange": [ + 36, + 71 + ], + "summary": "Idempotently brings the configured database to the current Alembic migration head, handling fresh databases, legacy create_all() databases, and pre-squash-stamped databases, and raising RuntimeError if the schema predates the squashed baseline.", + "tags": [ + "database-migration", + "alembic", + "idempotent", + "startup" + ], + "complexity": "moderate" + }, + { + "id": "function:extensions.py:_set_sqlite_pragmas", + "type": "function", + "name": "_set_sqlite_pragmas", + "filePath": "extensions.py", + "lineRange": [ + 13, + 25 + ], + "summary": "SQLAlchemy connect-event listener that enables WAL journal mode and a 15-second busy timeout on every SQLite connection so the Flask app and MCP server can write concurrently without lock errors.", + "tags": [ + "sqlite", + "event-handler", + "concurrency", + "database" + ], + "complexity": "simple" + }, + { + "id": "class:tests/test_dashboard.py:TestDashboardIndex", + "type": "class", + "name": "TestDashboardIndex", + "filePath": "tests/test_dashboard.py", + "lineRange": [ + 24, + 75 + ], + "summary": "Test suite for the dashboard index route, covering successful rendering with live API stats, graceful handling of API errors and empty results, correctness of locally computed DB stats, and concurrent execution of the two upstream API calls.", + "tags": [ + "test", + "dashboard", + "mocking" + ], + "complexity": "moderate" + }, + { + "id": "function:tests/test_db_bootstrap.py:_make_app", + "type": "function", + "name": "_make_app", + "filePath": "tests/test_db_bootstrap.py", + "lineRange": [ + 20, + 30 + ], + "summary": "Test helper that builds a Flask app configured against a temporary SQLite file so each `ensure_db()` test runs against an isolated on-disk database.", + "tags": [ + "test", + "fixture", + "utility" + ], + "complexity": "simple" + }, + { + "id": "class:tests/test_db_bootstrap.py:TestEnsureDb", + "type": "class", + "name": "TestEnsureDb", + "filePath": "tests/test_db_bootstrap.py", + "lineRange": [ + 43, + 87 + ], + "summary": "Test suite for `ensure_db()` verifying correct behavior across a fresh baseline database, a legacy create_all() database, a pre-squash-stamped database, an outdated/unrecognized schema, and repeated (idempotent) invocations.", + "tags": [ + "test", + "database-migration", + "alembic" + ], + "complexity": "moderate" + }, + { + "id": "file:mcp_server/__main__.py", + "type": "file", + "name": "__main__.py", + "filePath": "mcp_server/__main__.py", + "summary": "Module entry point for `python -m mcp_server`; imports the server's main() coroutine runner and invokes it immediately to start the stdio MCP server loop.", + "tags": [ + "entry-point", + "mcp-server", + "bootstrap" + ], + "complexity": "simple" + }, + { + "id": "file:mcp_server/server.py", + "type": "file", + "name": "server.py", + "filePath": "mcp_server/server.py", + "summary": "MCP server exposing the CDISC curation app's data and write operations as 14 stdio tools (8 read, 6 write); each call is dispatched into a Flask app context shared with the web app so both processes hit the same SQLite database and service layer.", + "tags": [ + "entry-point", + "mcp-server", + "api-handler", + "service", + "tested" + ], + "complexity": "complex", + "languageNotes": "Uses lazy, function-local imports throughout (Flask app, models, service clients) so they only resolve once an app context exists, plus a functools.wraps-based `_with_app_context` decorator that pushes a fresh app context per call since handlers run on asyncio executor worker threads." + }, + { + "id": "function:mcp_server/server.py:_with_app_context", + "type": "function", + "name": "_with_app_context", + "filePath": "mcp_server/server.py", + "lineRange": [ + 60, + 72 + ], + "summary": "Decorator that wraps a tool handler so it runs inside a fresh Flask app context, required because handlers execute on the asyncio executor's worker threads rather than the request thread.", + "tags": [ + "decorator", + "middleware", + "mcp-server" + ], + "complexity": "simple" + }, + { + "id": "function:mcp_server/server.py:_dispatch", + "type": "function", + "name": "_dispatch", + "filePath": "mcp_server/server.py", + "lineRange": [ + 292, + 312 + ], + "summary": "Central dispatch table mapping the 14 MCP tool names to their handler functions and raising ValueError for unknown tool names.", + "tags": [ + "dispatcher", + "mcp-server", + "routing" + ], + "complexity": "moderate" + }, + { + "id": "function:mcp_server/server.py:_bc_full_dict", + "type": "function", + "name": "_bc_full_dict", + "filePath": "mcp_server/server.py", + "lineRange": [ + 320, + 332 + ], + "summary": "Builds the full JSON-serializable dict for a BiomedicalConcept, extending its to_dict() with fields (code, source, history_of_change, timestamps) omitted by the base serializer.", + "tags": [ + "serialization", + "data-model", + "utility" + ], + "complexity": "simple" + }, + { + "id": "function:mcp_server/server.py:_list_bcs", + "type": "function", + "name": "_list_bcs", + "filePath": "mcp_server/server.py", + "lineRange": [ + 336, + 356 + ], + "summary": "Implements the list_bcs tool: paginated, filterable search over BiomedicalConcept by free-text query and status.", + "tags": [ + "api-handler", + "query", + "pagination" + ], + "complexity": "moderate" + }, + { + "id": "function:mcp_server/server.py:_get_bc", + "type": "function", + "name": "_get_bc", + "filePath": "mcp_server/server.py", + "lineRange": [ + 360, + 405 + ], + "summary": "Implements the get_bc tool: fetches full BC detail including its DataElementConcepts, dataset specializations, and governance history.", + "tags": [ + "api-handler", + "query", + "data-model" + ], + "complexity": "moderate" + }, + { + "id": "function:mcp_server/server.py:_search_ncit", + "type": "function", + "name": "_search_ncit", + "filePath": "mcp_server/server.py", + "lineRange": [ + 409, + 416 + ], + "summary": "Implements the search_ncit tool, delegating to NCItApiClient.search_concept with a required search term and bounded result count.", + "tags": [ + "api-handler", + "external-api" + ], + "complexity": "simple" + }, + { + "id": "function:mcp_server/server.py:_get_ncit_concept", + "type": "function", + "name": "_get_ncit_concept", + "filePath": "mcp_server/server.py", + "lineRange": [ + 420, + 426 + ], + "summary": "Implements the get_ncit_concept tool, fetching full concept detail from the NCI Thesaurus via NCItApiClient.", + "tags": [ + "api-handler", + "external-api" + ], + "complexity": "simple" + }, + { + "id": "function:mcp_server/server.py:_search_loinc", + "type": "function", + "name": "_search_loinc", + "filePath": "mcp_server/server.py", + "lineRange": [ + 430, + 437 + ], + "summary": "Implements the search_loinc tool, delegating to LoincApiClient.search for LOINC code lookups.", + "tags": [ + "api-handler", + "external-api" + ], + "complexity": "simple" + }, + { + "id": "function:mcp_server/server.py:_search_cdisc_library", + "type": "function", + "name": "_search_cdisc_library", + "filePath": "mcp_server/server.py", + "lineRange": [ + 441, + 450 + ], + "summary": "Implements the search_cdisc_library tool, querying the live CDISC Library for published Biomedical Concepts and filtering results by title.", + "tags": [ + "api-handler", + "external-api" + ], + "complexity": "simple" + }, + { + "id": "function:mcp_server/server.py:_get_library_bc", + "type": "function", + "name": "_get_library_bc", + "filePath": "mcp_server/server.py", + "lineRange": [ + 454, + 460 + ], + "summary": "Implements the get_library_bc tool, fetching one published BC from the CDISC Library by concept id.", + "tags": [ + "api-handler", + "external-api" + ], + "complexity": "simple" + }, + { + "id": "function:mcp_server/server.py:_list_review_queue", + "type": "function", + "name": "_list_review_queue", + "filePath": "mcp_server/server.py", + "lineRange": [ + 464, + 477 + ], + "summary": "Implements the list_review_queue tool: summarizes the governance Kanban board by status counts plus the count of pending ingestion records.", + "tags": [ + "api-handler", + "governance", + "query" + ], + "complexity": "simple" + }, + { + "id": "function:mcp_server/server.py:_create_bc", + "type": "function", + "name": "_create_bc", + "filePath": "mcp_server/server.py", + "lineRange": [ + 486, + 494 + ], + "summary": "Implements the create_bc write tool, delegating to bc_service.create_bc and optionally bc_service.save_decs before returning the full BC detail.", + "tags": [ + "api-handler", + "write-operation", + "service" + ], + "complexity": "simple" + }, + { + "id": "function:mcp_server/server.py:_update_bc", + "type": "function", + "name": "_update_bc", + "filePath": "mcp_server/server.py", + "lineRange": [ + 498, + 507 + ], + "summary": "Implements the update_bc write tool, applying field updates via bc_service.update_bc and returning the refreshed BC dict.", + "tags": [ + "api-handler", + "write-operation", + "service" + ], + "complexity": "simple" + }, + { + "id": "function:mcp_server/server.py:_map_ncit_to_bc", + "type": "function", + "name": "_map_ncit_to_bc", + "filePath": "mcp_server/server.py", + "lineRange": [ + 511, + 519 + ], + "summary": "Implements the map_ncit_to_bc write tool, attaching an NCIt code to a BC via bc_service.map_ncit_to_bc (which also handles IMPORT_ id promotion).", + "tags": [ + "api-handler", + "write-operation", + "service" + ], + "complexity": "simple" + }, + { + "id": "function:mcp_server/server.py:_submit_bc_for_review", + "type": "function", + "name": "_submit_bc_for_review", + "filePath": "mcp_server/server.py", + "lineRange": [ + 523, + 531 + ], + "summary": "Implements the submit_bc_for_review write tool, moving a BC from provisional into SME review via bc_service.submit_bc_for_review.", + "tags": [ + "api-handler", + "write-operation", + "governance" + ], + "complexity": "simple" + }, + { + "id": "function:mcp_server/server.py:_advance_governance", + "type": "function", + "name": "_advance_governance", + "filePath": "mcp_server/server.py", + "lineRange": [ + 535, + 542 + ], + "summary": "Implements the advance_governance write tool, advancing a BC's governance stage via governance_service.advance_governance.", + "tags": [ + "api-handler", + "write-operation", + "governance" + ], + "complexity": "simple" + }, + { + "id": "function:mcp_server/server.py:_reject_bc", + "type": "function", + "name": "_reject_bc", + "filePath": "mcp_server/server.py", + "lineRange": [ + 546, + 553 + ], + "summary": "Implements the reject_bc write tool, returning a BC to provisional status via governance_service.reject_bc.", + "tags": [ + "api-handler", + "write-operation", + "governance" + ], + "complexity": "simple" + }, + { + "id": "function:mcp_server/server.py:main", + "type": "function", + "name": "main", + "filePath": "mcp_server/server.py", + "lineRange": [ + 561, + 563 + ], + "summary": "Async entry point that configures logging and runs the stdio MCP server loop; invoked by `python -m mcp_server`.", + "tags": [ + "entry-point", + "bootstrap", + "mcp-server" + ], + "complexity": "simple" + }, + { + "id": "function:mcp_server/server.py:_run", + "type": "function", + "name": "_run", + "filePath": "mcp_server/server.py", + "lineRange": [ + 566, + 568 + ], + "summary": "Opens the stdio transport streams and runs the MCP Server with its initialization options.", + "tags": [ + "bootstrap", + "mcp-server" + ], + "complexity": "simple" + }, + { + "id": "file:models/specialization.py", + "type": "file", + "name": "specialization.py", + "filePath": "models/specialization.py", + "summary": "SQLAlchemy model for a dataset specialization (SDTM/CDASH variable-level metadata group) tied to a Biomedical Concept, storing its variable list as a JSON-serialized text column.", + "tags": [ + "data-model", + "orm", + "database" + ], + "complexity": "simple" + }, + { + "id": "class:models/specialization.py:DatasetSpecialization", + "type": "class", + "name": "DatasetSpecialization", + "filePath": "models/specialization.py", + "lineRange": [ + 6, + 30 + ], + "summary": "ORM model mapping the dataset_specializations table; exposes a `variables` property/setter that transparently (de)serializes a JSON text column, plus a to_dict() used by routes and the MCP server.", + "tags": [ + "data-model", + "orm", + "serialization" + ], + "complexity": "simple" + }, + { + "id": "file:tests/test_mcp_server.py", + "type": "file", + "name": "test_mcp_server.py", + "filePath": "tests/test_mcp_server.py", + "summary": "Unit tests for the MCP server's `_dispatch` routing and all 14 tool handlers, exercising them directly against a shared in-memory test Flask app (no stdio transport) and asserting on returned data plus AuditLog/GovernanceRecord side effects.", + "tags": [ + "test", + "mcp-server", + "unit-test" + ], + "complexity": "complex" + }, + { + "id": "class:tests/test_mcp_server.py:TestDispatch", + "type": "class", + "name": "TestDispatch", + "filePath": "tests/test_mcp_server.py", + "lineRange": [ + 32, + 54 + ], + "summary": "Tests the _dispatch routing function: raises for unknown tool names and asserts every tool declared in _TOOLS is actually present in the dispatch handler map.", + "tags": [ + "test", + "mcp-server", + "dispatcher" + ], + "complexity": "simple" + }, + { + "id": "class:tests/test_mcp_server.py:TestListBcs", + "type": "class", + "name": "TestListBcs", + "filePath": "tests/test_mcp_server.py", + "lineRange": [ + 57, + 86 + ], + "summary": "Tests the list_bcs tool against an empty DB, filtering by query/status, and page/per_page pagination behavior.", + "tags": [ + "test", + "mcp-server", + "pagination" + ], + "complexity": "simple" + }, + { + "id": "class:tests/test_mcp_server.py:TestGetBc", + "type": "class", + "name": "TestGetBc", + "filePath": "tests/test_mcp_server.py", + "lineRange": [ + 89, + 109 + ], + "summary": "Tests the get_bc tool: requires a bc_id, raises on missing BC, and returns full detail including DECs, specializations, and governance records.", + "tags": [ + "test", + "mcp-server", + "data-model" + ], + "complexity": "simple" + }, + { + "id": "class:tests/test_mcp_server.py:TestExternalApiTools", + "type": "class", + "name": "TestExternalApiTools", + "filePath": "tests/test_mcp_server.py", + "lineRange": [ + 112, + 154 + ], + "summary": "Tests the external-API-backed tools (search_ncit, get_ncit_concept, search_loinc, search_cdisc_library, get_library_bc), mocking the underlying NCIt/LOINC/CDISC Library clients and asserting call arguments, filtering, and error propagation.", + "tags": [ + "test", + "external-api", + "mocking" + ], + "complexity": "moderate" + }, + { + "id": "class:tests/test_mcp_server.py:TestCreateBc", + "type": "class", + "name": "TestCreateBc", + "filePath": "tests/test_mcp_server.py", + "lineRange": [ + 162, + 188 + ], + "summary": "Tests the create_bc write tool: creates a BC with DECs and an audit trail, rejects a duplicate bc_id, and rejects a missing bc_id.", + "tags": [ + "test", + "write-operation", + "audit" + ], + "complexity": "simple" + }, + { + "id": "class:tests/test_mcp_server.py:TestUpdateBc", + "type": "class", + "name": "TestUpdateBc", + "filePath": "tests/test_mcp_server.py", + "lineRange": [ + 191, + 203 + ], + "summary": "Tests the update_bc write tool: verifies before/after audit logging on update and that updating a missing BC raises.", + "tags": [ + "test", + "write-operation", + "audit" + ], + "complexity": "simple" + }, + { + "id": "class:tests/test_mcp_server.py:TestMapNcit", + "type": "class", + "name": "TestMapNcit", + "filePath": "tests/test_mcp_server.py", + "lineRange": [ + 206, + 266 + ], + "summary": "Tests the map_ncit_to_bc write tool, covering audit logging, IMPORT_ id promotion and its cascading repoint of dependent DECs/specializations/governance records and child BCs, empty-code validation, and collision handling.", + "tags": [ + "test", + "write-operation", + "ncit" + ], + "complexity": "complex" + }, + { + "id": "class:tests/test_mcp_server.py:TestGovernanceWrites", + "type": "class", + "name": "TestGovernanceWrites", + "filePath": "tests/test_mcp_server.py", + "lineRange": [ + 269, + 300 + ], + "summary": "Tests the submit_bc_for_review, advance_governance, and reject_bc write tools end-to-end, asserting GovernanceRecord history and audit trail entries.", + "tags": [ + "test", + "governance", + "write-operation" + ], + "complexity": "moderate" + }, + { + "id": "class:tests/test_mcp_server.py:TestReviewQueue", + "type": "class", + "name": "TestReviewQueue", + "filePath": "tests/test_mcp_server.py", + "lineRange": [ + 303, + 315 + ], + "summary": "Tests the list_review_queue tool's governance-status summary counts and pending ingestion count.", + "tags": [ + "test", + "governance", + "query" + ], + "complexity": "simple" + }, + { + "id": "file:tests/test_specializations_routes.py", + "type": "file", + "name": "test_specializations_routes.py", + "filePath": "tests/test_specializations_routes.py", + "summary": "Route-level tests for the dataset specializations blueprint covering listing, creating, editing/upserting, deleting, detail views, and generating variables from DECs, including audit log verification and CDISC Library error handling.", + "tags": [ + "test", + "route-test", + "unit-test" + ], + "complexity": "complex" + }, + { + "id": "class:tests/test_specializations_routes.py:TestIndex", + "type": "class", + "name": "TestIndex", + "filePath": "tests/test_specializations_routes.py", + "lineRange": [ + 24, + 40 + ], + "summary": "Tests the specializations index route: lists persisted specializations and confirms the page still renders when the CDISC Library client raises an error.", + "tags": [ + "test", + "route-test", + "error-handling" + ], + "complexity": "simple" + }, + { + "id": "class:tests/test_specializations_routes.py:TestCreate", + "type": "class", + "name": "TestCreate", + "filePath": "tests/test_specializations_routes.py", + "lineRange": [ + 43, + 97 + ], + "summary": "Tests creating a dataset specialization via the route: persists the spec, requires bc_id/vlm_group_id, writes an audit log entry, and persists variable rows submitted via the form.", + "tags": [ + "test", + "route-test", + "audit" + ], + "complexity": "moderate" + }, + { + "id": "class:tests/test_specializations_routes.py:TestEditUpsert", + "type": "class", + "name": "TestEditUpsert", + "filePath": "tests/test_specializations_routes.py", + "lineRange": [ + 100, + 141 + ], + "summary": "Tests the edit route's upsert behavior: posting an existing vlm_group_id updates the record instead of duplicating it, and the update writes an audit log entry.", + "tags": [ + "test", + "route-test", + "upsert" + ], + "complexity": "simple" + }, + { + "id": "class:tests/test_specializations_routes.py:TestDelete", + "type": "class", + "name": "TestDelete", + "filePath": "tests/test_specializations_routes.py", + "lineRange": [ + 144, + 172 + ], + "summary": "Tests the delete route: removes the specialization, writes an audit log entry, and returns 404 for an unknown vlm_group_id.", + "tags": [ + "test", + "route-test", + "audit" + ], + "complexity": "simple" + }, + { + "id": "class:tests/test_specializations_routes.py:TestDetail", + "type": "class", + "name": "TestDetail", + "filePath": "tests/test_specializations_routes.py", + "lineRange": [ + 175, + 189 + ], + "summary": "Tests the specialization detail view renders for a known record and returns 404 for an unknown one.", + "tags": [ + "test", + "route-test" + ], + "complexity": "simple" + }, + { + "id": "class:tests/test_specializations_routes.py:TestLibraryDetail", + "type": "class", + "name": "TestLibraryDetail", + "filePath": "tests/test_specializations_routes.py", + "lineRange": [ + 192, + 204 + ], + "summary": "Tests the library-detail view: renders a published CDISC Library specialization and redirects to the dashboard when the Library client errors.", + "tags": [ + "test", + "route-test", + "error-handling" + ], + "complexity": "simple" + }, + { + "id": "class:tests/test_specializations_routes.py:TestGenerate", + "type": "class", + "name": "TestGenerate", + "filePath": "tests/test_specializations_routes.py", + "lineRange": [ + 207, + 245 + ], + "summary": "Tests generating a dataset specialization from a BC's DECs: builds variables from DECs, rejects duplicates, 404s for an unknown BC, and writes an audit log entry.", + "tags": [ + "test", + "route-test", + "generation" + ], + "complexity": "moderate" + }, + { + "id": "class:tests/test_specializations_routes.py:TestGenerateFromDec", + "type": "class", + "name": "TestGenerateFromDec", + "filePath": "tests/test_specializations_routes.py", + "lineRange": [ + 248, + 260 + ], + "summary": "Tests the JSON endpoint that returns generated variables for a single DEC, and its 400 response when bc_id is missing.", + "tags": [ + "test", + "route-test", + "json-api" + ], + "complexity": "simple" + }, + { + "id": "pipeline:.github/workflows/ci.yml", + "type": "pipeline", + "name": "ci.yml", + "filePath": ".github/workflows/ci.yml", + "summary": "GitHub Actions CI workflow that runs on every push and pull request across all branches: installs project and dev dependencies, lints with isort/black/flake8, and runs the pytest suite on Python 3.12.", + "tags": [ + "ci-cd", + "deployment", + "testing", + "linting", + "github-actions" + ], + "complexity": "simple", + "languageNotes": "Single-job workflow (`test`) with pip caching enabled via actions/setup-python; lint and test steps are separate shell steps within the same job rather than a matrix build." + }, + { + "id": "document:.claude/agent-memory/cdisc-frontend-dev/MEMORY.md", + "type": "document", + "name": "MEMORY.md", + "filePath": ".claude/agent-memory/cdisc-frontend-dev/MEMORY.md", + "summary": "Index file for the cdisc-frontend-dev sub-agent's persistent memory, linking to the project foundation and frontend architecture notes.", + "tags": [ + "documentation", + "agent-memory", + "index" + ], + "complexity": "simple" + }, + { + "id": "document:.claude/agent-memory/cdisc-frontend-dev/frontend_architecture.md", + "type": "document", + "name": "frontend_architecture.md", + "filePath": ".claude/agent-memory/cdisc-frontend-dev/frontend_architecture.md", + "summary": "Persistent agent memory documenting the front-end template hierarchy, custom.css class naming conventions, main.js module functions, template-to-route mapping, and Jinja context variable conventions for the CDISC BC Curation UI.", + "tags": [ + "documentation", + "frontend", + "css", + "javascript", + "agent-memory" + ], + "complexity": "moderate" + }, + { + "id": "document:.claude/agent-memory/cdisc-frontend-dev/project_foundation.md", + "type": "document", + "name": "project_foundation.md", + "filePath": ".claude/agent-memory/cdisc-frontend-dev/project_foundation.md", + "summary": "Persistent agent memory describing the Flask application factory pattern, registered blueprints, model and service locations, database setup, and key route implementation decisions for the CDISC BC Curation app.", + "tags": [ + "documentation", + "architecture", + "flask", + "database", + "agent-memory" + ], + "complexity": "moderate" + }, + { + "id": "document:.claude/agents/cdisc-concept-explorer.md", + "type": "document", + "name": "cdisc-concept-explorer.md", + "filePath": ".claude/agents/cdisc-concept-explorer.md", + "summary": "Defines the cdisc-concept-explorer Claude subagent, a CDISC standards specialist that queries the live CDISC Library API to detect duplicate Biomedical Concepts against local drafts, enrich drafts with authoritative metadata, and find dataset specializations for a given BC.", + "tags": [ + "agent-definition", + "documentation", + "cdisc-library", + "api-integration", + "subagent-spec" + ], + "complexity": "simple" + }, + { + "id": "document:.claude/agents/cdisc-frontend-dev.md", + "type": "document", + "name": "cdisc-frontend-dev.md", + "filePath": ".claude/agents/cdisc-frontend-dev.md", + "summary": "Defines the cdisc-frontend-dev Claude subagent, a front-end specialist scoped to Jinja2 templates, CSS, vanilla JavaScript, and Flask view wiring for the BC curation tool, with mandatory CDISC brand-guideline compliance and an extensive persistent project-memory protocol appended to the spec.", + "tags": [ + "agent-definition", + "documentation", + "frontend", + "memory-system", + "brand-guidelines" + ], + "complexity": "moderate", + "languageNotes": "The file embeds a large boilerplate 'Persistent Agent Memory' instructional block (types of memory, save/access rules) alongside the domain-specific agent spec, roughly doubling its length beyond the front-end guidance itself." + }, + { + "id": "document:.claude/agents/mike.md", + "type": "document", + "name": "mike.md", + "filePath": ".claude/agents/mike.md", + "summary": "Defines the mike Claude subagent, a project-manager assistant for cdisc-concept-curation that tracks daily progress in README-PROGRESS.md, prepares git commit messages, flags documentation drift against the code, and always asks before committing or pushing.", + "tags": [ + "agent-definition", + "documentation", + "project-management", + "git-workflow", + "progress-tracking" + ], + "complexity": "simple" + }, + { + "id": "config:.mcp.json", + "type": "config", + "name": ".mcp.json", + "filePath": ".mcp.json", + "summary": "Registers the cdisc-curation MCP server, launching it via the project virtualenv's Python interpreter running the `mcp_server` package as a module.", + "tags": [ + "configuration", + "mcp", + "server-registration" + ], + "complexity": "simple" + }, + { + "id": "config:.pre-commit-config.yaml", + "type": "config", + "name": ".pre-commit-config.yaml", + "filePath": ".pre-commit-config.yaml", + "summary": "Pre-commit hook configuration that runs isort, black, and flake8 on staged files and executes the full pytest suite before every commit.", + "tags": [ + "configuration", + "ci-cd", + "linting", + "build-system" + ], + "complexity": "simple" + }, + { + "id": "document:CLAUDE.md", + "type": "document", + "name": "CLAUDE.md", + "filePath": "CLAUDE.md", + "summary": "Guidance document for Claude Code covering the app's purpose, how to run it, linting and testing commands, the Route-Service-Model architecture with its 8 Flask blueprints and key services, the MCP server, and environment-variable configuration.", + "tags": [ + "documentation", + "architecture", + "entry-point", + "development" + ], + "complexity": "moderate" + }, + { + "id": "document:README-PROGRESS.md", + "type": "document", + "name": "README-PROGRESS.md", + "filePath": "README-PROGRESS.md", + "summary": "Running project progress log tracking feature status and a dated changelog of fixes and workstream completions such as governance export corrections, audit trail coverage, and LOINC API explorer work.", + "tags": [ + "documentation", + "changelog", + "project-status" + ], + "complexity": "moderate" + }, + { + "id": "document:README.md", + "type": "document", + "name": "README.md", + "filePath": "README.md", + "summary": "Primary project README covering prerequisites, installation, environment-variable configuration, running the app and tests, key screens, project structure, and the external CDISC/NCIt/LOINC APIs the platform integrates with.", + "tags": [ + "documentation", + "entry-point", + "installation", + "overview" + ], + "complexity": "moderate" + }, + { + "id": "config:pyproject.toml", + "type": "config", + "name": "pyproject.toml", + "filePath": "pyproject.toml", + "summary": "Tool configuration setting a shared 200-character line length for black (targeting Python 3.9-3.11) and isort (using the black-compatible profile).", + "tags": [ + "configuration", + "build-system", + "formatting" + ], + "complexity": "simple" + }, + { + "id": "document:requirements-dev.txt", + "type": "document", + "name": "requirements-dev.txt", + "filePath": "requirements-dev.txt", + "summary": "Pinned development-only dependency list: black, flake8 (with mccabe/pycodestyle/pyflakes), isort, pre-commit, pytest, and pytest-flask, used for linting and testing.", + "tags": [ + "dependencies", + "development", + "linting", + "testing" + ], + "complexity": "simple" + }, + { + "id": "document:requirements.txt", + "type": "document", + "name": "requirements.txt", + "filePath": "requirements.txt", + "summary": "Pinned runtime dependency list for the Flask application, including Flask, Flask-SQLAlchemy, Flask-Migrate, SQLAlchemy, Alembic, pandas, openpyxl, lxml, requests, and the mcp SDK.", + "tags": [ + "dependencies", + "runtime", + "build-system" + ], + "complexity": "simple" + }, + { + "id": "document:files/plan.md", + "type": "document", + "name": "plan.md", + "filePath": "files/plan.md", + "summary": "Initial high-level project brief for the CDISC Biomedical Concept Curation application, specifying the target UI mockup to match, the key reference files to review, the required agents (cdisc-concept-explorer, ncit-concept-resolver, cdisc-frontend-dev, mike), and the constraint that only the CDISC_API_KEY environment variable be used for API authentication.", + "tags": [ + "documentation", + "planning", + "project-brief", + "requirements" + ], + "complexity": "simple" + }, + { + "id": "document:files/implementation.md", + "type": "document", + "name": "implementation.md", + "filePath": "files/implementation.md", + "summary": "Detailed implementation plan for the Flask/Jinja BC curation app, laying out the directory structure, SQLAlchemy data models, Flask blueprint/route table, service-layer API surface, template inventory, and a four-phase build roadmap from MVP through export and polish.", + "tags": [ + "documentation", + "architecture", + "implementation-plan", + "specification" + ], + "complexity": "moderate", + "languageNotes": "Serves as the architectural blueprint the actual codebase (app.py, models/, routes/, services/, templates/) was built from; nearly every file/module it names exists in the repository today." + }, + { + "id": "document:files/result.md", + "type": "document", + "name": "result.md", + "filePath": "files/result.md", + "summary": "Post-implementation status report confirming all 8 Flask routes return HTTP 200 and enumerating delivered capabilities: file ingestion with AI field mapping, BC CRUD, NCIt mapping, a 4-stage governance Kanban, an immutable audit trail, and JSON/XLSX/ODM-XML export.", + "tags": [ + "documentation", + "summary", + "status-report", + "verification" + ], + "complexity": "simple" + }, + { + "id": "file:templates/base.html", + "type": "file", + "name": "base.html", + "filePath": "templates/base.html", + "summary": "Master Jinja2 layout providing the sidebar navigation, top bar, flash-message rendering, and template blocks (title, page_title, user_info, content, extra_js) that every other page in the app extends.", + "tags": [ + "layout", + "navigation", + "entry-point", + "bootstrap", + "component" + ], + "complexity": "moderate", + "languageNotes": "Jinja2 block-based inheritance root \u2014 defines the extension points (`{% block content %}`, `{% block extra_js %}`, etc.) that all ten other templates in this batch fill in via `{% extends \"base.html\" %}`." + }, + { + "id": "file:templates/audit.html", + "type": "file", + "name": "audit.html", + "filePath": "templates/audit.html", + "summary": "Audit trail page listing all curation actions (create/update/delete/advance/reject/publish/ingest) with actor/entity/date filters, CSV/JSON export links, expandable before/after diff rows, and pagination.", + "tags": [ + "audit-trail", + "data-table", + "filter", + "pagination", + "export" + ], + "complexity": "complex" + }, + { + "id": "file:templates/bc_detail.html", + "type": "file", + "name": "bc_detail.html", + "filePath": "templates/bc_detail.html", + "summary": "Create/edit form for a single Biomedical Concept covering identification, NCIt and LOINC lookups (with async metadata fetch in extra_js), classification, definition, metadata/status, and an editable Data Element Concepts (DECs) table.", + "tags": [ + "form", + "crud", + "ncit-integration", + "loinc-integration", + "dynamic-form" + ], + "complexity": "complex", + "languageNotes": "extra_js block fetches BC metadata via `bc.fetch_metadata` and rebuilds the NCIt/LOINC display grids client-side, mirroring field lists that are also rendered server-side in Jinja \u2014 a duplicated-schema pattern worth keeping in sync if fields change." + }, + { + "id": "file:templates/bc_list.html", + "type": "file", + "name": "bc_list.html", + "filePath": "templates/bc_list.html", + "summary": "Searchable, filterable, paginated table of locally curated Biomedical Concepts with status badges, JSON/XLSX/ODM-XML export links, and a delete-confirmation modal.", + "tags": [ + "list-view", + "search", + "filter", + "pagination", + "export" + ], + "complexity": "complex" + }, + { + "id": "file:templates/dashboard.html", + "type": "file", + "name": "dashboard.html", + "filePath": "templates/dashboard.html", + "summary": "Main landing dashboard showing KPI cards, recent submissions, the governance pipeline snapshot, paginated/searchable views of CDISC Library BCs and dataset specializations, an AI curation pipeline progress tracker, and quick action links.", + "tags": [ + "dashboard", + "kpi-metrics", + "pipeline-status", + "pagination", + "cdisc-library" + ], + "complexity": "complex" + }, + { + "id": "file:templates/governance.html", + "type": "file", + "name": "governance.html", + "filePath": "templates/governance.html", + "summary": "Four-column Kanban board (Provisional -> SME Review -> CDISC Approval -> Published) for advancing or rejecting Biomedical Concepts through the governance workflow, with summary counts and an export-filename modal.", + "tags": [ + "kanban", + "governance", + "workflow", + "modal", + "export" + ], + "complexity": "complex" + }, + { + "id": "file:templates/ingestion.html", + "type": "file", + "name": "ingestion.html", + "filePath": "templates/ingestion.html", + "summary": "File upload page (drag-and-drop or click) for XLSX/CSV/JSON ingestion, with a parsed-results queue table showing field-mapping confidence, validation errors, duplicate flags, and per-row approve/reject actions.", + "tags": [ + "file-upload", + "drag-drop", + "queue", + "validation", + "form" + ], + "complexity": "moderate" + }, + { + "id": "file:templates/library_bc_detail.html", + "type": "file", + "name": "library_bc_detail.html", + "filePath": "templates/library_bc_detail.html", + "summary": "Read-only detail view of a single Biomedical Concept fetched from the published CDISC Library, showing identification, definition, LOINC coding, metadata, and its Data Element Concepts.", + "tags": [ + "read-only-view", + "cdisc-library", + "loinc-integration", + "detail-view" + ], + "complexity": "moderate" + }, + { + "id": "file:templates/library_spec_detail.html", + "type": "file", + "name": "library_spec_detail.html", + "filePath": "templates/library_spec_detail.html", + "summary": "Read-only detail view of a Dataset Specialization from the published CDISC Library, showing identification/domain metadata and a full variables table (role, codelist, assigned term, mandatory flags, relationship).", + "tags": [ + "read-only-view", + "cdisc-library", + "variables-table", + "detail-view" + ], + "complexity": "moderate" + }, + { + "id": "file:templates/ncit_mapping.html", + "type": "file", + "name": "ncit_mapping.html", + "filePath": "templates/ncit_mapping.html", + "summary": "NCI Thesaurus search page plus a conflict-resolution queue listing Biomedical Concepts with unresolved or low-confidence NCIt mappings that block governance advancement, with a confidence-score legend.", + "tags": [ + "ncit-integration", + "search", + "conflict-resolution", + "confidence-score" + ], + "complexity": "moderate" + }, + { + "id": "file:templates/specializations.html", + "type": "file", + "name": "specializations.html", + "filePath": "templates/specializations.html", + "summary": "Dataset Specialization management page with a collapsible create/edit form (VLM group ID, BC selector, SDTM/CDASH domain, dynamic variables table, generate-from-DEC-templates action) and a list/edit/delete table of existing specializations.", + "tags": [ + "crud", + "dynamic-form", + "sdtm", + "cdash", + "variable-table" + ], + "complexity": "complex", + "languageNotes": "extra_js block mixes server-rendered variable rows with client-side row templating (identical markup duplicated in Jinja and JS template-literal form) for the add-row and generate-from-DEC-templates interactions." + }, + { + "id": "document:.claude/agent-memory/cdisc-concept-explorer/MEMORY.md", + "type": "document", + "name": "MEMORY.md", + "filePath": ".claude/agent-memory/cdisc-concept-explorer/MEMORY.md", + "summary": "Single-line agent memory index for the cdisc-concept-explorer agent, pointing to the verified CDISC API reference notes.", + "tags": [ + "documentation", + "agent-memory", + "reference" + ], + "complexity": "simple" + }, + { + "id": "document:.claude/agent-memory/cdisc-concept-explorer/reference_api_endpoints.md", + "type": "document", + "name": "reference_api_endpoints.md", + "filePath": ".claude/agent-memory/cdisc-concept-explorer/reference_api_endpoints.md", + "summary": "Verified reference notes on the CDISC Library COSMOS v2 API: base URL, working endpoints, lack of pagination, and field names for biomedical concept and dataset specialization records.", + "tags": [ + "documentation", + "api-reference", + "cdisc-library", + "agent-memory" + ], + "complexity": "simple" + }, + { + "id": "document:.claude/skills/code-review/SKILL.md", + "type": "document", + "name": "SKILL.md", + "filePath": ".claude/skills/code-review/SKILL.md", + "summary": "Project-specific code review checklist for cdisc-concept-curation covering route/service/model layering, audit log coverage, Alembic migration discipline, API client error handling, test isolation, and TDD conventions.", + "tags": [ + "documentation", + "code-review", + "development", + "checklist" + ], + "complexity": "simple" + }, + { + "id": "document:.claude/skills/run-concept-curation/SKILL.md", + "type": "document", + "name": "SKILL.md", + "filePath": ".claude/skills/run-concept-curation/SKILL.md", + "summary": "Instructions for running and smoke-testing the Flask dev server, including the preferred automated smoke-test script, manual startup steps, and troubleshooting tips.", + "tags": [ + "documentation", + "development", + "smoke-test", + "deployment" + ], + "complexity": "simple" + }, + { + "id": "file:.claude/skills/run-concept-curation/smoke.sh", + "type": "file", + "name": "smoke.sh", + "filePath": ".claude/skills/run-concept-curation/smoke.sh", + "summary": "Shell script that boots the Flask app on a throwaway SQLite database and port, runs a battery of HTTP checks against key routes (dashboard, BC CRUD, governance, NCIt, audit, ingestion, specializations, export), and tears the server down.", + "tags": [ + "script", + "smoke-test", + "automation", + "development" + ], + "complexity": "moderate", + "languageNotes": "Bash script using a trap-based cleanup function and small curl-wrapping helpers (check/get_code/post_code) to assert HTTP status codes." + }, + { + "id": "function:.claude/skills/run-concept-curation/smoke.sh:cleanup", + "type": "function", + "name": "cleanup", + "filePath": ".claude/skills/run-concept-curation/smoke.sh", + "lineRange": [ + 16, + 22 + ], + "summary": "Trap handler that kills the background dev server process and removes the throwaway SQLite database directory on script exit.", + "tags": [ + "cleanup", + "process-management", + "script" + ], + "complexity": "simple" + }, + { + "id": "function:.claude/skills/run-concept-curation/smoke.sh:check", + "type": "function", + "name": "check", + "filePath": ".claude/skills/run-concept-curation/smoke.sh", + "lineRange": [ + 25, + 34 + ], + "summary": "Assertion helper that compares an actual HTTP status code against an expected value and prints a PASS/FAIL line for the smoke test report.", + "tags": [ + "assertion", + "smoke-test", + "validation" + ], + "complexity": "simple" + }, + { + "id": "config:.flake8", + "type": "config", + "name": ".flake8", + "filePath": ".flake8", + "summary": "Flake8 lint configuration setting max line length to 200, ignoring black-conflicting rules (E203/W503) and SQLAlchemy's `== None` idiom (E711), and excluding build/venv directories.", + "tags": [ + "configuration", + "linting", + "build-system", + "python" + ], + "complexity": "simple" + }, + { + "id": "config:.hintrc", + "type": "config", + "name": ".hintrc", + "filePath": ".hintrc", + "summary": "webhint configuration extending the 'development' ruleset and disabling the no-inline-styles rule for HTML/CSS linting.", + "tags": [ + "configuration", + "linting", + "frontend" + ], + "complexity": "simple" + }, + { + "id": "config:.ua/.understandignore", + "type": "config", + "name": ".understandignore", + "filePath": ".ua/.understandignore", + "summary": "Gitignore-style exclusion list for the Understand-Anything analysis tool, with commented-out suggestions mirroring the project's .gitignore (Python caches, virtualenvs, egg metadata) that can be uncommented to activate.", + "tags": [ + "configuration", + "tooling", + "analysis" + ], + "complexity": "simple" + }, + { + "id": "config:.ua/config.json", + "type": "config", + "name": "config.json", + "filePath": ".ua/config.json", + "summary": "Single-key configuration for the Understand-Anything tool setting the output language for generated analysis text to English.", + "tags": [ + "configuration", + "tooling", + "analysis" + ], + "complexity": "simple" + }, + { + "id": "file:files/BC Curation Principles and Completion GLs.xlsx", + "type": "file", + "name": "BC Curation Principles and Completion GLs.xlsx", + "filePath": "files/BC Curation Principles and Completion GLs.xlsx", + "summary": "Reference spreadsheet documenting BC curation principles and field-completion guidelines used by curators when authoring Biomedical Concepts.", + "tags": [ + "reference", + "documentation", + "spreadsheet", + "curation-guidance" + ], + "complexity": "moderate" + }, + { + "id": "file:files/BC DEC Templates.xlsx", + "type": "file", + "name": "BC DEC Templates.xlsx", + "filePath": "files/BC DEC Templates.xlsx", + "summary": "Template spreadsheet defining the standard row/column layout for Data Element Concepts, used as an ingestion source template for curators.", + "tags": [ + "template", + "spreadsheet", + "ingestion", + "reference" + ], + "complexity": "simple" + }, + { + "id": "file:files/BC Examples.xlsx", + "type": "file", + "name": "BC Examples.xlsx", + "filePath": "files/BC Examples.xlsx", + "summary": "Example spreadsheet of fully-populated Biomedical Concepts, used as a reference/sample dataset for the ingestion pipeline and curator training.", + "tags": [ + "reference", + "spreadsheet", + "sample-data", + "ingestion" + ], + "complexity": "moderate" + }, + { + "id": "file:files/BC Governance.docx", + "type": "file", + "name": "BC Governance.docx", + "filePath": "files/BC Governance.docx", + "summary": "Word document describing the governance process and stages (Provisional, SME Review, CDISC Approval, Published) that a curated Biomedical Concept moves through.", + "tags": [ + "documentation", + "governance", + "reference" + ], + "complexity": "simple" + }, + { + "id": "file:mcp_server/__init__.py", + "type": "file", + "name": "__init__.py", + "filePath": "mcp_server/__init__.py", + "summary": "Package marker and docstring for the mcp_server package, noting it is run via `python -m mcp_server` and deliberately named to avoid shadowing the MCP SDK package.", + "tags": [ + "entry-point", + "package-init", + "mcp" + ], + "complexity": "simple" + }, + { + "id": "document:migrations/README", + "type": "document", + "name": "README", + "filePath": "migrations/README", + "summary": "One-line note identifying the migrations directory as a single-database Flask-Migrate/Alembic configuration.", + "tags": [ + "documentation", + "database", + "migration" + ], + "complexity": "simple" + }, + { + "id": "config:migrations/alembic.ini", + "type": "config", + "name": "alembic.ini", + "filePath": "migrations/alembic.ini", + "summary": "Alembic configuration file controlling migration script location, logging setup, and version-file naming for the Flask-Migrate managed schema.", + "tags": [ + "configuration", + "database", + "migration", + "alembic" + ], + "complexity": "simple" + }, + { + "id": "file:migrations/env.py", + "type": "file", + "name": "env.py", + "filePath": "migrations/env.py", + "summary": "Alembic environment script that wires Flask-Migrate's engine/metadata into Alembic's migration context, supporting both offline (SQL-only) and online (live-connection) migration runs.", + "tags": [ + "database", + "migration", + "alembic", + "flask-migrate" + ], + "complexity": "moderate", + "languageNotes": "Handles both Flask-SQLAlchemy <3 and >=3 engine-access APIs via a try/except fallback, and skips autogenerating empty migrations via a process_revision_directives hook." + }, + { + "id": "function:migrations/env.py:get_engine", + "type": "function", + "name": "get_engine", + "filePath": "migrations/env.py", + "lineRange": [ + 17, + 23 + ], + "summary": "Returns the active SQLAlchemy engine from the Flask-Migrate extension, falling back between the Flask-SQLAlchemy 2.x and 3.x APIs.", + "tags": [ + "database", + "migration", + "compatibility" + ], + "complexity": "simple" + }, + { + "id": "function:migrations/env.py:get_engine_url", + "type": "function", + "name": "get_engine_url", + "filePath": "migrations/env.py", + "lineRange": [ + 26, + 30 + ], + "summary": "Renders the database engine's connection URL as a string with percent signs escaped, for use as Alembic's sqlalchemy.url option.", + "tags": [ + "database", + "migration", + "utility" + ], + "complexity": "simple" + }, + { + "id": "function:migrations/env.py:run_migrations_offline", + "type": "function", + "name": "run_migrations_offline", + "filePath": "migrations/env.py", + "lineRange": [ + 52, + 68 + ], + "summary": "Runs Alembic migrations in offline mode by configuring the context with just the database URL and emitting SQL without requiring a live connection.", + "tags": [ + "database", + "migration", + "alembic" + ], + "complexity": "moderate" + }, + { + "id": "function:migrations/env.py:run_migrations_online", + "type": "function", + "name": "run_migrations_online", + "filePath": "migrations/env.py", + "lineRange": [ + 71, + 99 + ], + "summary": "Runs Alembic migrations in online mode against a live database connection, registering a hook that skips autogenerating a migration when no schema changes are detected.", + "tags": [ + "database", + "migration", + "alembic" + ], + "complexity": "moderate" + }, + { + "id": "file:migrations/script.py.mako", + "type": "file", + "name": "script.py.mako", + "filePath": "migrations/script.py.mako", + "summary": "Mako template used by `flask db migrate` to scaffold new Alembic revision files with the standard revision metadata and upgrade/downgrade function stubs.", + "tags": [ + "template", + "database", + "migration", + "code-generation" + ], + "complexity": "simple" + }, + { + "id": "file:migrations/versions/51d4a009d291_baseline_initial_schema.py", + "type": "file", + "name": "51d4a009d291_baseline_initial_schema.py", + "filePath": "migrations/versions/51d4a009d291_baseline_initial_schema.py", + "summary": "Baseline Alembic migration that creates the initial database schema: audit_logs, biomedical_concepts, ingestion_records, data_element_concepts, dataset_specializations, and governance_records tables, with their downgrade counterparts.", + "tags": [ + "database", + "migration", + "schema", + "baseline" + ], + "complexity": "complex", + "languageNotes": "Auto-generated Alembic revision consisting almost entirely of repetitive op.create_table/sa.Column calls plus a batch_alter_table index creation." + }, + { + "id": "function:migrations/versions/51d4a009d291_baseline_initial_schema.py:upgrade", + "type": "function", + "name": "upgrade", + "filePath": "migrations/versions/51d4a009d291_baseline_initial_schema.py", + "lineRange": [ + 19, + 128 + ], + "summary": "Creates the six baseline tables (audit_logs, biomedical_concepts, ingestion_records, data_element_concepts, dataset_specializations, governance_records) with their columns, primary keys, foreign keys, and one index.", + "tags": [ + "database", + "migration", + "schema-creation" + ], + "complexity": "complex" + }, + { + "id": "function:migrations/versions/51d4a009d291_baseline_initial_schema.py:downgrade", + "type": "function", + "name": "downgrade", + "filePath": "migrations/versions/51d4a009d291_baseline_initial_schema.py", + "lineRange": [ + 131, + 142 + ], + "summary": "Reverses the baseline migration by dropping the index and all six tables created in upgrade().", + "tags": [ + "database", + "migration", + "rollback" + ], + "complexity": "simple" + }, + { + "id": "file:routes/__init__.py", + "type": "file", + "name": "__init__.py", + "filePath": "routes/__init__.py", + "summary": "Empty package marker file that makes routes/ a Python package for the Flask blueprint modules it contains.", + "tags": [ + "package-init", + "empty" + ], + "complexity": "simple" + }, + { + "id": "file:static/css/custom.css", + "type": "file", + "name": "custom.css", + "filePath": "static/css/custom.css", + "summary": "Main application stylesheet defining design tokens (colors, spacing), base resets, app shell layout (sidebar, topbar, content area), and component styles (cards, flash messages, nav items, badges) for the CDISC BC curation UI.", + "tags": [ + "stylesheet", + "design-system", + "layout", + "ui" + ], + "complexity": "complex", + "languageNotes": "Organized around CSS custom properties (design tokens) under :root, consistent with the project's CDISC brand color requirements." + }, + { + "id": "file:static/js/main.js", + "type": "file", + "name": "main.js", + "filePath": "static/js/main.js", + "summary": "Client-side interactivity module for the BC curation platform: flash-message dismissal, drag-and-drop file upload, NCIt/LOINC lookup and search panels, dynamic DEC table rows, governance Kanban actions, audit-diff toggles, and shared fetch/toast helpers, all wired up on DOMContentLoaded.", + "tags": [ + "frontend", + "vanilla-js", + "ui-interactivity", + "fetch-api" + ], + "complexity": "complex", + "languageNotes": "Entire file is wrapped in an IIFE for scope isolation; all functions are private and invoked from a single DOMContentLoaded listener at the bottom of the file." + }, + { + "id": "function:static/js/main.js:initFileUpload", + "type": "function", + "name": "initFileUpload", + "filePath": "static/js/main.js", + "lineRange": [ + 25, + 76 + ], + "summary": "Wires up the file-input and drag-and-drop upload zone: updates the displayed filename, toggles drag-over styling, and assigns dropped files to the hidden file input.", + "tags": [ + "ui-interactivity", + "file-upload", + "drag-and-drop" + ], + "complexity": "moderate" + }, + { + "id": "function:static/js/main.js:initNcitLookup", + "type": "function", + "name": "initNcitLookup", + "filePath": "static/js/main.js", + "lineRange": [ + 84, + 131 + ], + "summary": "Handles the NCIt code lookup button: fetches /ncit/search for a typed NCIt code and renders matches into the results panel, or shows an error state on failure.", + "tags": [ + "api-handler", + "fetch-api", + "ncit", + "ui-interactivity" + ], + "complexity": "moderate" + }, + { + "id": "function:static/js/main.js:renderNcitResults", + "type": "function", + "name": "renderNcitResults", + "filePath": "static/js/main.js", + "lineRange": [ + 132, + 207 + ], + "summary": "Renders NCIt lookup result cards into a container and wires each card's 'Use this concept' button to populate the BC form and fetch full concept metadata for display.", + "tags": [ + "rendering", + "ui-interactivity", + "ncit", + "fetch-api" + ], + "complexity": "complex" + }, + { + "id": "function:static/js/main.js:renderNcitMetaDisplay", + "type": "function", + "name": "renderNcitMetaDisplay", + "filePath": "static/js/main.js", + "lineRange": [ + 208, + 265 + ], + "summary": "Formats and displays selected NCIt concept metadata fields (definitions, parents, synonyms, etc.) as a labeled list, handling both scalar and array-shaped fields.", + "tags": [ + "rendering", + "ncit", + "ui-interactivity" + ], + "complexity": "moderate" + }, + { + "id": "function:static/js/main.js:initDecTable", + "type": "function", + "name": "initDecTable", + "filePath": "static/js/main.js", + "lineRange": [ + 266, + 290 + ], + "summary": "Initializes the Data Element Concept table: wires the 'add DEC row' button and delegates row-delete clicks to remove table rows.", + "tags": [ + "ui-interactivity", + "dynamic-table", + "dec" + ], + "complexity": "simple" + }, + { + "id": "function:static/js/main.js:buildDecRow", + "type": "function", + "name": "buildDecRow", + "filePath": "static/js/main.js", + "lineRange": [ + 291, + 331 + ], + "summary": "Builds and returns a new table row element containing the input fields for a Data Element Concept entry, indexed for form submission.", + "tags": [ + "dom-construction", + "dynamic-table", + "dec" + ], + "complexity": "moderate" + }, + { + "id": "function:static/js/main.js:initKanban", + "type": "function", + "name": "initKanban", + "filePath": "static/js/main.js", + "lineRange": [ + 332, + 386 + ], + "summary": "Wires the governance Kanban board's advance and reject buttons to POST stage-transition requests and reload or toast on success/failure.", + "tags": [ + "api-handler", + "governance", + "kanban", + "fetch-api" + ], + "complexity": "moderate" + }, + { + "id": "function:static/js/main.js:initAuditDiff", + "type": "function", + "name": "initAuditDiff", + "filePath": "static/js/main.js", + "lineRange": [ + 387, + 402 + ], + "summary": "Toggles the visibility of before/after diff panels on the audit log page when a diff-toggle button is clicked.", + "tags": [ + "ui-interactivity", + "audit", + "toggle" + ], + "complexity": "simple" + }, + { + "id": "function:static/js/main.js:initNcitSearch", + "type": "function", + "name": "initNcitSearch", + "filePath": "static/js/main.js", + "lineRange": [ + 403, + 444 + ], + "summary": "Wires the NCIt search page's search button and Enter key to trigger a debounced-free search request and render results.", + "tags": [ + "api-handler", + "ncit", + "search", + "ui-interactivity" + ], + "complexity": "moderate" + }, + { + "id": "function:static/js/main.js:renderNcitSearchResults", + "type": "function", + "name": "renderNcitSearchResults", + "filePath": "static/js/main.js", + "lineRange": [ + 445, + 529 + ], + "summary": "Renders NCIt search result cards with synonyms and a 'Use this concept' button that stores the chosen concept in sessionStorage and navigates to the BC creation page.", + "tags": [ + "rendering", + "ncit", + "search", + "ui-interactivity" + ], + "complexity": "complex" + }, + { + "id": "function:static/js/main.js:showInlineToast", + "type": "function", + "name": "showInlineToast", + "filePath": "static/js/main.js", + "lineRange": [ + 537, + 563 + ], + "summary": "Creates and displays a Bootstrap toast notification with the given message and style, appending it to a shared toast container.", + "tags": [ + "ui-interactivity", + "toast", + "notification" + ], + "complexity": "moderate" + }, + { + "id": "function:static/js/main.js:getOrCreateToastContainer", + "type": "function", + "name": "getOrCreateToastContainer", + "filePath": "static/js/main.js", + "lineRange": [ + 564, + 578 + ], + "summary": "Finds or lazily creates the fixed-position container element that hosts stacked toast notifications.", + "tags": [ + "dom-construction", + "toast", + "utility" + ], + "complexity": "simple" + }, + { + "id": "function:static/js/main.js:initLoincLookup", + "type": "function", + "name": "initLoincLookup", + "filePath": "static/js/main.js", + "lineRange": [ + 593, + 680 + ], + "summary": "Handles the LOINC code lookup button: fetches /loinc/search for a typed LOINC code and renders matches, mirroring the NCIt lookup flow.", + "tags": [ + "api-handler", + "fetch-api", + "loinc", + "ui-interactivity" + ], + "complexity": "moderate" + }, + { + "id": "function:static/js/main.js:renderLoincResults", + "type": "function", + "name": "renderLoincResults", + "filePath": "static/js/main.js", + "lineRange": [ + 681, + 755 + ], + "summary": "Renders LOINC lookup result cards into a container and wires each card's selection button to populate form fields and display LOINC metadata.", + "tags": [ + "rendering", + "loinc", + "ui-interactivity", + "fetch-api" + ], + "complexity": "complex" + }, + { + "id": "function:static/js/main.js:renderLoincMetaDisplay", + "type": "function", + "name": "renderLoincMetaDisplay", + "filePath": "static/js/main.js", + "lineRange": [ + 756, + 782 + ], + "summary": "Formats and displays selected LOINC metadata fields as a labeled list on the BC detail page.", + "tags": [ + "rendering", + "loinc", + "ui-interactivity" + ], + "complexity": "moderate" + }, + { + "id": "function:static/js/main.js:initPendingNcitConcept", + "type": "function", + "name": "initPendingNcitConcept", + "filePath": "static/js/main.js", + "lineRange": [ + 783, + 801 + ], + "summary": "On page load, consumes a pending NCIt concept stashed in sessionStorage (from the NCIt search page) and auto-populates the BC detail page's metadata display and hidden field.", + "tags": [ + "ui-interactivity", + "ncit", + "session-storage" + ], + "complexity": "moderate" + }, + { + "id": "file:tests/__init__.py", + "type": "file", + "name": "__init__.py", + "filePath": "tests/__init__.py", + "summary": "Empty package marker file that makes tests/ a discoverable Python package for pytest.", + "tags": [ + "package-init", + "test", + "empty" + ], + "complexity": "simple" + }, + { + "id": "file:files/cdisc_bc_ai_platform.html", + "type": "file", + "name": "cdisc_bc_ai_platform.html", + "filePath": "files/cdisc_bc_ai_platform.html", + "summary": "No summary available", + "tags": [ + "untagged" + ], + "complexity": "moderate" + }, + { + "id": "file:files/cdisc_smart_goals_infographic.html", + "type": "file", + "name": "cdisc_smart_goals_infographic.html", + "filePath": "files/cdisc_smart_goals_infographic.html", + "summary": "No summary available", + "tags": [ + "untagged" + ], + "complexity": "moderate" + } + ], + "edges": [ + { + "source": "file:models/audit.py", + "target": "file:extensions.py", + "type": "imports", + "direction": "forward", + "weight": 0.7 + }, + { + "source": "file:models/bc.py", + "target": "file:extensions.py", + "type": "imports", + "direction": "forward", + "weight": 0.7 + }, + { + "source": "file:models/ingestion.py", + "target": "file:extensions.py", + "type": "imports", + "direction": "forward", + "weight": 0.7 + }, + { + "source": "file:routes/audit.py", + "target": "file:models/audit.py", + "type": "imports", + "direction": "forward", + "weight": 0.7 + }, + { + "source": "file:routes/ingestion.py", + "target": "file:extensions.py", + "type": "imports", + "direction": "forward", + "weight": 0.7 + }, + { + "source": "file:routes/ingestion.py", + "target": "file:models/audit.py", + "type": "imports", + "direction": "forward", + "weight": 0.7 + }, + { + "source": "file:routes/ingestion.py", + "target": "file:models/bc.py", + "type": "imports", + "direction": "forward", + "weight": 0.7 + }, + { + "source": "file:routes/ingestion.py", + "target": "file:models/ingestion.py", + "type": "imports", + "direction": "forward", + "weight": 0.7 + }, + { + "source": "file:routes/ingestion.py", + "target": "file:services/ingestion.py", + "type": "imports", + "direction": "forward", + "weight": 0.7 + }, + { + "source": "file:tests/conftest.py", + "target": "file:app.py", + "type": "imports", + "direction": "forward", + "weight": 0.7 + }, + { + "source": "file:tests/conftest.py", + "target": "file:extensions.py", + "type": "imports", + "direction": "forward", + "weight": 0.7 + }, + { + "source": "file:tests/conftest.py", + "target": "file:models/audit.py", + "type": "imports", + "direction": "forward", + "weight": 0.7 + }, + { + "source": "file:tests/conftest.py", + "target": "file:models/bc.py", + "type": "imports", + "direction": "forward", + "weight": 0.7 + }, + { + "source": "file:tests/conftest.py", + "target": "file:models/governance.py", + "type": "imports", + "direction": "forward", + "weight": 0.7 + }, + { + "source": "file:tests/conftest.py", + "target": "file:models/ingestion.py", + "type": "imports", + "direction": "forward", + "weight": 0.7 + }, + { + "source": "file:models/audit.py", + "target": "class:models/audit.py:AuditLog", + "type": "contains", + "direction": "forward", + "weight": 1.0 + }, + { + "source": "file:models/bc.py", + "target": "class:models/bc.py:BiomedicalConcept", + "type": "contains", + "direction": "forward", + "weight": 1.0 + }, + { + "source": "file:models/bc.py", + "target": "class:models/bc.py:DataElementConcept", + "type": "contains", + "direction": "forward", + "weight": 1.0 + }, + { + "source": "file:models/ingestion.py", + "target": "class:models/ingestion.py:IngestionRecord", + "type": "contains", + "direction": "forward", + "weight": 1.0 + }, + { + "source": "file:routes/audit.py", + "target": "function:routes/audit.py:index", + "type": "contains", + "direction": "forward", + "weight": 1.0 + }, + { + "source": "file:routes/ingestion.py", + "target": "function:routes/ingestion.py:_bc_from_mapped", + "type": "contains", + "direction": "forward", + "weight": 1.0 + }, + { + "source": "file:routes/ingestion.py", + "target": "function:routes/ingestion.py:_create_decs", + "type": "contains", + "direction": "forward", + "weight": 1.0 + }, + { + "source": "file:routes/ingestion.py", + "target": "function:routes/ingestion.py:upload", + "type": "contains", + "direction": "forward", + "weight": 1.0 + }, + { + "source": "file:routes/ingestion.py", + "target": "function:routes/ingestion.py:approve", + "type": "contains", + "direction": "forward", + "weight": 1.0 + }, + { + "source": "file:routes/ingestion.py", + "target": "function:routes/ingestion.py:approve_all", + "type": "contains", + "direction": "forward", + "weight": 1.0 + }, + { + "source": "file:services/ingestion.py", + "target": "function:services/ingestion.py:_match_field", + "type": "contains", + "direction": "forward", + "weight": 1.0 + }, + { + "source": "file:services/ingestion.py", + "target": "function:services/ingestion.py:map_fields", + "type": "contains", + "direction": "forward", + "weight": 1.0 + }, + { + "source": "file:services/ingestion.py", + "target": "function:services/ingestion.py:validate_bc", + "type": "contains", + "direction": "forward", + "weight": 1.0 + }, + { + "source": "file:services/ingestion.py", + "target": "function:services/ingestion.py:_group_by_bc", + "type": "contains", + "direction": "forward", + "weight": 1.0 + }, + { + "source": "file:services/ingestion.py", + "target": "function:services/ingestion.py:parse_xlsx", + "type": "contains", + "direction": "forward", + "weight": 1.0 + }, + { + "source": "file:services/ingestion.py", + "target": "function:services/ingestion.py:parse_csv", + "type": "contains", + "direction": "forward", + "weight": 1.0 + }, + { + "source": "file:services/ingestion.py", + "target": "function:services/ingestion.py:parse_json", + "type": "contains", + "direction": "forward", + "weight": 1.0 + }, + { + "source": "file:services/ingestion.py", + "target": "function:services/ingestion.py:deduplicate", + "type": "contains", + "direction": "forward", + "weight": 1.0 + }, + { + "source": "file:tests/conftest.py", + "target": "function:tests/conftest.py:sample_bc", + "type": "contains", + "direction": "forward", + "weight": 1.0 + }, + { + "source": "file:models/audit.py", + "target": "class:models/audit.py:AuditLog", + "type": "exports", + "direction": "forward", + "weight": 0.8 + }, + { + "source": "file:models/bc.py", + "target": "class:models/bc.py:BiomedicalConcept", + "type": "exports", + "direction": "forward", + "weight": 0.8 + }, + { + "source": "file:models/bc.py", + "target": "class:models/bc.py:DataElementConcept", + "type": "exports", + "direction": "forward", + "weight": 0.8 + }, + { + "source": "file:models/ingestion.py", + "target": "class:models/ingestion.py:IngestionRecord", + "type": "exports", + "direction": "forward", + "weight": 0.8 + }, + { + "source": "file:routes/audit.py", + "target": "function:routes/audit.py:index", + "type": "exports", + "direction": "forward", + "weight": 0.8 + }, + { + "source": "file:routes/ingestion.py", + "target": "function:routes/ingestion.py:_bc_from_mapped", + "type": "exports", + "direction": "forward", + "weight": 0.8 + }, + { + "source": "file:routes/ingestion.py", + "target": "function:routes/ingestion.py:_create_decs", + "type": "exports", + "direction": "forward", + "weight": 0.8 + }, + { + "source": "file:routes/ingestion.py", + "target": "function:routes/ingestion.py:upload", + "type": "exports", + "direction": "forward", + "weight": 0.8 + }, + { + "source": "file:routes/ingestion.py", + "target": "function:routes/ingestion.py:approve", + "type": "exports", + "direction": "forward", + "weight": 0.8 + }, + { + "source": "file:routes/ingestion.py", + "target": "function:routes/ingestion.py:approve_all", + "type": "exports", + "direction": "forward", + "weight": 0.8 + }, + { + "source": "file:services/ingestion.py", + "target": "function:services/ingestion.py:_match_field", + "type": "exports", + "direction": "forward", + "weight": 0.8 + }, + { + "source": "file:services/ingestion.py", + "target": "function:services/ingestion.py:map_fields", + "type": "exports", + "direction": "forward", + "weight": 0.8 + }, + { + "source": "file:services/ingestion.py", + "target": "function:services/ingestion.py:validate_bc", + "type": "exports", + "direction": "forward", + "weight": 0.8 + }, + { + "source": "file:services/ingestion.py", + "target": "function:services/ingestion.py:_group_by_bc", + "type": "exports", + "direction": "forward", + "weight": 0.8 + }, + { + "source": "file:services/ingestion.py", + "target": "function:services/ingestion.py:parse_xlsx", + "type": "exports", + "direction": "forward", + "weight": 0.8 + }, + { + "source": "file:services/ingestion.py", + "target": "function:services/ingestion.py:parse_csv", + "type": "exports", + "direction": "forward", + "weight": 0.8 + }, + { + "source": "file:services/ingestion.py", + "target": "function:services/ingestion.py:parse_json", + "type": "exports", + "direction": "forward", + "weight": 0.8 + }, + { + "source": "file:services/ingestion.py", + "target": "function:services/ingestion.py:deduplicate", + "type": "exports", + "direction": "forward", + "weight": 0.8 + }, + { + "source": "function:routes/ingestion.py:_bc_from_mapped", + "target": "class:models/bc.py:BiomedicalConcept", + "type": "calls", + "direction": "forward", + "weight": 0.8 + }, + { + "source": "function:routes/ingestion.py:_create_decs", + "target": "class:models/bc.py:DataElementConcept", + "type": "calls", + "direction": "forward", + "weight": 0.8 + }, + { + "source": "function:routes/ingestion.py:approve", + "target": "class:models/audit.py:AuditLog", + "type": "calls", + "direction": "forward", + "weight": 0.8 + }, + { + "source": "function:routes/ingestion.py:approve_all", + "target": "class:models/audit.py:AuditLog", + "type": "calls", + "direction": "forward", + "weight": 0.8 + }, + { + "source": "function:routes/ingestion.py:upload", + "target": "function:services/ingestion.py:parse_xlsx", + "type": "calls", + "direction": "forward", + "weight": 0.8 + }, + { + "source": "function:routes/ingestion.py:upload", + "target": "function:services/ingestion.py:parse_csv", + "type": "calls", + "direction": "forward", + "weight": 0.8 + }, + { + "source": "function:routes/ingestion.py:upload", + "target": "function:services/ingestion.py:parse_json", + "type": "calls", + "direction": "forward", + "weight": 0.8 + }, + { + "source": "function:routes/ingestion.py:upload", + "target": "function:services/ingestion.py:deduplicate", + "type": "calls", + "direction": "forward", + "weight": 0.8 + }, + { + "source": "function:routes/audit.py:index", + "target": "class:models/audit.py:AuditLog", + "type": "calls", + "direction": "forward", + "weight": 0.8 + }, + { + "source": "file:models/audit.py", + "target": "file:tests/test_models.py", + "type": "tested_by", + "direction": "forward", + "weight": 0.5 + }, + { + "source": "file:models/bc.py", + "target": "file:tests/test_models.py", + "type": "tested_by", + "direction": "forward", + "weight": 0.5 + }, + { + "source": "file:models/bc.py", + "target": "file:tests/test_bc_routes.py", + "type": "tested_by", + "direction": "forward", + "weight": 0.5 + }, + { + "source": "file:models/ingestion.py", + "target": "file:tests/test_models.py", + "type": "tested_by", + "direction": "forward", + "weight": 0.5 + }, + { + "source": "file:routes/audit.py", + "target": "file:tests/test_audit_routes.py", + "type": "tested_by", + "direction": "forward", + "weight": 0.5 + }, + { + "source": "file:routes/ingestion.py", + "target": "file:tests/test_ingestion_routes.py", + "type": "tested_by", + "direction": "forward", + "weight": 0.5 + }, + { + "source": "file:services/ingestion.py", + "target": "file:tests/test_ingestion_service.py", + "type": "tested_by", + "direction": "forward", + "weight": 0.5 + }, + { + "source": "file:tests/test_audit_routes.py", + "target": "file:extensions.py", + "type": "imports", + "direction": "forward", + "weight": 0.7 + }, + { + "source": "file:tests/test_audit_routes.py", + "target": "file:models/audit.py", + "type": "imports", + "direction": "forward", + "weight": 0.7 + }, + { + "source": "file:tests/test_bc_routes.py", + "target": "file:extensions.py", + "type": "imports", + "direction": "forward", + "weight": 0.7 + }, + { + "source": "file:tests/test_bc_routes.py", + "target": "file:models/audit.py", + "type": "imports", + "direction": "forward", + "weight": 0.7 + }, + { + "source": "file:tests/test_bc_routes.py", + "target": "file:models/bc.py", + "type": "imports", + "direction": "forward", + "weight": 0.7 + }, + { + "source": "file:tests/test_governance_routes.py", + "target": "file:extensions.py", + "type": "imports", + "direction": "forward", + "weight": 0.7 + }, + { + "source": "file:tests/test_governance_routes.py", + "target": "file:models/audit.py", + "type": "imports", + "direction": "forward", + "weight": 0.7 + }, + { + "source": "file:tests/test_governance_routes.py", + "target": "file:models/bc.py", + "type": "imports", + "direction": "forward", + "weight": 0.7 + }, + { + "source": "file:tests/test_governance_routes.py", + "target": "file:models/governance.py", + "type": "imports", + "direction": "forward", + "weight": 0.7 + }, + { + "source": "file:tests/test_ingestion_routes.py", + "target": "file:extensions.py", + "type": "imports", + "direction": "forward", + "weight": 0.7 + }, + { + "source": "file:tests/test_ingestion_routes.py", + "target": "file:models/audit.py", + "type": "imports", + "direction": "forward", + "weight": 0.7 + }, + { + "source": "file:tests/test_ingestion_routes.py", + "target": "file:models/bc.py", + "type": "imports", + "direction": "forward", + "weight": 0.7 + }, + { + "source": "file:tests/test_ingestion_routes.py", + "target": "file:models/ingestion.py", + "type": "imports", + "direction": "forward", + "weight": 0.7 + }, + { + "source": "file:tests/test_ingestion_service.py", + "target": "file:services/ingestion.py", + "type": "imports", + "direction": "forward", + "weight": 0.7 + }, + { + "source": "file:tests/test_models.py", + "target": "file:extensions.py", + "type": "imports", + "direction": "forward", + "weight": 0.7 + }, + { + "source": "file:tests/test_models.py", + "target": "file:models/audit.py", + "type": "imports", + "direction": "forward", + "weight": 0.7 + }, + { + "source": "file:tests/test_models.py", + "target": "file:models/bc.py", + "type": "imports", + "direction": "forward", + "weight": 0.7 + }, + { + "source": "file:tests/test_models.py", + "target": "file:models/ingestion.py", + "type": "imports", + "direction": "forward", + "weight": 0.7 + }, + { + "source": "file:tests/test_audit_routes.py", + "target": "class:tests/test_audit_routes.py:TestAuditIndex", + "type": "contains", + "direction": "forward", + "weight": 1.0 + }, + { + "source": "file:tests/test_bc_routes.py", + "target": "function:tests/test_bc_routes.py:_bc_form", + "type": "contains", + "direction": "forward", + "weight": 1.0 + }, + { + "source": "file:tests/test_bc_routes.py", + "target": "class:tests/test_bc_routes.py:TestBcIndex", + "type": "contains", + "direction": "forward", + "weight": 1.0 + }, + { + "source": "file:tests/test_bc_routes.py", + "target": "class:tests/test_bc_routes.py:TestCreateBc", + "type": "contains", + "direction": "forward", + "weight": 1.0 + }, + { + "source": "file:tests/test_bc_routes.py", + "target": "class:tests/test_bc_routes.py:TestBcDetail", + "type": "contains", + "direction": "forward", + "weight": 1.0 + }, + { + "source": "file:tests/test_bc_routes.py", + "target": "class:tests/test_bc_routes.py:TestEditBc", + "type": "contains", + "direction": "forward", + "weight": 1.0 + }, + { + "source": "file:tests/test_bc_routes.py", + "target": "class:tests/test_bc_routes.py:TestClearNcitCode", + "type": "contains", + "direction": "forward", + "weight": 1.0 + }, + { + "source": "file:tests/test_bc_routes.py", + "target": "class:tests/test_bc_routes.py:TestClearLoincCode", + "type": "contains", + "direction": "forward", + "weight": 1.0 + }, + { + "source": "file:tests/test_bc_routes.py", + "target": "class:tests/test_bc_routes.py:TestSubmitForReview", + "type": "contains", + "direction": "forward", + "weight": 1.0 + }, + { + "source": "file:tests/test_bc_routes.py", + "target": "class:tests/test_bc_routes.py:TestDeleteBc", + "type": "contains", + "direction": "forward", + "weight": 1.0 + }, + { + "source": "file:tests/test_bc_routes.py", + "target": "class:tests/test_bc_routes.py:TestExport", + "type": "contains", + "direction": "forward", + "weight": 1.0 + }, + { + "source": "file:tests/test_bc_routes.py", + "target": "class:tests/test_bc_routes.py:TestLibraryDetail", + "type": "contains", + "direction": "forward", + "weight": 1.0 + }, + { + "source": "file:tests/test_governance_routes.py", + "target": "class:tests/test_governance_routes.py:TestAdvance", + "type": "contains", + "direction": "forward", + "weight": 1.0 + }, + { + "source": "file:tests/test_governance_routes.py", + "target": "class:tests/test_governance_routes.py:TestReject", + "type": "contains", + "direction": "forward", + "weight": 1.0 + }, + { + "source": "file:tests/test_governance_routes.py", + "target": "class:tests/test_governance_routes.py:TestGovernanceExport", + "type": "contains", + "direction": "forward", + "weight": 1.0 + }, + { + "source": "file:tests/test_ingestion_routes.py", + "target": "class:tests/test_ingestion_routes.py:TestUpload", + "type": "contains", + "direction": "forward", + "weight": 1.0 + }, + { + "source": "file:tests/test_ingestion_routes.py", + "target": "class:tests/test_ingestion_routes.py:TestApprove", + "type": "contains", + "direction": "forward", + "weight": 1.0 + }, + { + "source": "file:tests/test_ingestion_routes.py", + "target": "class:tests/test_ingestion_routes.py:TestReject", + "type": "contains", + "direction": "forward", + "weight": 1.0 + }, + { + "source": "file:tests/test_ingestion_routes.py", + "target": "class:tests/test_ingestion_routes.py:TestApproveAll", + "type": "contains", + "direction": "forward", + "weight": 1.0 + }, + { + "source": "file:tests/test_ingestion_service.py", + "target": "class:tests/test_ingestion_service.py:TestSimilarity", + "type": "contains", + "direction": "forward", + "weight": 1.0 + }, + { + "source": "file:tests/test_ingestion_service.py", + "target": "class:tests/test_ingestion_service.py:TestMatchField", + "type": "contains", + "direction": "forward", + "weight": 1.0 + }, + { + "source": "file:tests/test_ingestion_service.py", + "target": "class:tests/test_ingestion_service.py:TestMapFields", + "type": "contains", + "direction": "forward", + "weight": 1.0 + }, + { + "source": "file:tests/test_ingestion_service.py", + "target": "class:tests/test_ingestion_service.py:TestValidateBc", + "type": "contains", + "direction": "forward", + "weight": 1.0 + }, + { + "source": "file:tests/test_ingestion_service.py", + "target": "class:tests/test_ingestion_service.py:TestDeduplicate", + "type": "contains", + "direction": "forward", + "weight": 1.0 + }, + { + "source": "file:tests/test_ingestion_service.py", + "target": "class:tests/test_ingestion_service.py:TestParseCsv", + "type": "contains", + "direction": "forward", + "weight": 1.0 + }, + { + "source": "file:tests/test_ingestion_service.py", + "target": "class:tests/test_ingestion_service.py:TestParseJson", + "type": "contains", + "direction": "forward", + "weight": 1.0 + }, + { + "source": "file:tests/test_ingestion_service.py", + "target": "class:tests/test_ingestion_service.py:TestGroupByBc", + "type": "contains", + "direction": "forward", + "weight": 1.0 + }, + { + "source": "file:tests/test_models.py", + "target": "class:tests/test_models.py:TestAuditLogJsonProperties", + "type": "contains", + "direction": "forward", + "weight": 1.0 + }, + { + "source": "file:tests/test_models.py", + "target": "class:tests/test_models.py:TestIngestionRecordProperties", + "type": "contains", + "direction": "forward", + "weight": 1.0 + }, + { + "source": "file:tests/test_models.py", + "target": "class:tests/test_models.py:TestBiomedicalConceptToDict", + "type": "contains", + "direction": "forward", + "weight": 1.0 + }, + { + "source": "file:models/audit.py", + "target": "file:tests/test_audit_routes.py", + "type": "tested_by", + "direction": "forward", + "weight": 0.5 + }, + { + "source": "file:routes/bc.py", + "target": "file:tests/test_bc_routes.py", + "type": "tested_by", + "direction": "forward", + "weight": 0.5 + }, + { + "source": "file:models/bc.py", + "target": "file:tests/test_governance_routes.py", + "type": "tested_by", + "direction": "forward", + "weight": 0.5 + }, + { + "source": "file:models/governance.py", + "target": "file:tests/test_governance_routes.py", + "type": "tested_by", + "direction": "forward", + "weight": 0.5 + }, + { + "source": "file:routes/governance.py", + "target": "file:tests/test_governance_routes.py", + "type": "tested_by", + "direction": "forward", + "weight": 0.5 + }, + { + "source": "file:models/bc.py", + "target": "file:tests/test_ingestion_routes.py", + "type": "tested_by", + "direction": "forward", + "weight": 0.5 + }, + { + "source": "file:models/ingestion.py", + "target": "file:tests/test_ingestion_routes.py", + "type": "tested_by", + "direction": "forward", + "weight": 0.5 + }, + { + "source": "file:models/governance.py", + "target": "file:extensions.py", + "type": "imports", + "direction": "forward", + "weight": 0.7 + }, + { + "source": "file:routes/bc.py", + "target": "file:extensions.py", + "type": "imports", + "direction": "forward", + "weight": 0.7 + }, + { + "source": "file:routes/bc.py", + "target": "file:models/bc.py", + "type": "imports", + "direction": "forward", + "weight": 0.7 + }, + { + "source": "file:routes/bc.py", + "target": "file:models/governance.py", + "type": "imports", + "direction": "forward", + "weight": 0.7 + }, + { + "source": "file:routes/bc.py", + "target": "file:services/__init__.py", + "type": "imports", + "direction": "forward", + "weight": 0.7 + }, + { + "source": "file:routes/bc.py", + "target": "file:services/audit.py", + "type": "imports", + "direction": "forward", + "weight": 0.7 + }, + { + "source": "file:routes/bc.py", + "target": "file:services/bc_service.py", + "type": "imports", + "direction": "forward", + "weight": 0.7 + }, + { + "source": "file:routes/bc.py", + "target": "file:services/cdisc_api.py", + "type": "imports", + "direction": "forward", + "weight": 0.7 + }, + { + "source": "file:routes/bc.py", + "target": "file:services/export.py", + "type": "imports", + "direction": "forward", + "weight": 0.7 + }, + { + "source": "file:routes/bc.py", + "target": "file:services/loinc_api.py", + "type": "imports", + "direction": "forward", + "weight": 0.7 + }, + { + "source": "file:routes/bc.py", + "target": "file:services/ncit_api.py", + "type": "imports", + "direction": "forward", + "weight": 0.7 + }, + { + "source": "file:routes/governance.py", + "target": "file:extensions.py", + "type": "imports", + "direction": "forward", + "weight": 0.7 + }, + { + "source": "file:routes/governance.py", + "target": "file:models/bc.py", + "type": "imports", + "direction": "forward", + "weight": 0.7 + }, + { + "source": "file:routes/governance.py", + "target": "file:services/__init__.py", + "type": "imports", + "direction": "forward", + "weight": 0.7 + }, + { + "source": "file:routes/governance.py", + "target": "file:services/export.py", + "type": "imports", + "direction": "forward", + "weight": 0.7 + }, + { + "source": "file:routes/governance.py", + "target": "file:services/governance_service.py", + "type": "imports", + "direction": "forward", + "weight": 0.7 + }, + { + "source": "file:routes/ncit.py", + "target": "file:extensions.py", + "type": "imports", + "direction": "forward", + "weight": 0.7 + }, + { + "source": "file:routes/ncit.py", + "target": "file:models/bc.py", + "type": "imports", + "direction": "forward", + "weight": 0.7 + }, + { + "source": "file:routes/ncit.py", + "target": "file:services/__init__.py", + "type": "imports", + "direction": "forward", + "weight": 0.7 + }, + { + "source": "file:routes/ncit.py", + "target": "file:services/bc_service.py", + "type": "imports", + "direction": "forward", + "weight": 0.7 + }, + { + "source": "file:routes/ncit.py", + "target": "file:services/ncit_api.py", + "type": "imports", + "direction": "forward", + "weight": 0.7 + }, + { + "source": "file:services/audit.py", + "target": "file:extensions.py", + "type": "imports", + "direction": "forward", + "weight": 0.7 + }, + { + "source": "file:services/audit.py", + "target": "file:models/audit.py", + "type": "imports", + "direction": "forward", + "weight": 0.7 + }, + { + "source": "file:models/governance.py", + "target": "class:models/governance.py:GovernanceRecord", + "type": "contains", + "direction": "forward", + "weight": 1.0 + }, + { + "source": "file:routes/bc.py", + "target": "function:routes/bc.py:index", + "type": "contains", + "direction": "forward", + "weight": 1.0 + }, + { + "source": "file:routes/bc.py", + "target": "function:routes/bc.py:new_bc", + "type": "contains", + "direction": "forward", + "weight": 1.0 + }, + { + "source": "file:routes/bc.py", + "target": "function:routes/bc.py:export", + "type": "contains", + "direction": "forward", + "weight": 1.0 + }, + { + "source": "file:routes/bc.py", + "target": "function:routes/bc.py:library_detail", + "type": "contains", + "direction": "forward", + "weight": 1.0 + }, + { + "source": "file:routes/bc.py", + "target": "function:routes/bc.py:detail", + "type": "contains", + "direction": "forward", + "weight": 1.0 + }, + { + "source": "file:routes/bc.py", + "target": "function:routes/bc.py:fetch_metadata", + "type": "contains", + "direction": "forward", + "weight": 1.0 + }, + { + "source": "file:routes/bc.py", + "target": "function:routes/bc.py:create", + "type": "contains", + "direction": "forward", + "weight": 1.0 + }, + { + "source": "file:routes/bc.py", + "target": "function:routes/bc.py:edit", + "type": "contains", + "direction": "forward", + "weight": 1.0 + }, + { + "source": "file:routes/bc.py", + "target": "function:routes/bc.py:clear_ncit", + "type": "contains", + "direction": "forward", + "weight": 1.0 + }, + { + "source": "file:routes/bc.py", + "target": "function:routes/bc.py:clear_loinc", + "type": "contains", + "direction": "forward", + "weight": 1.0 + }, + { + "source": "file:routes/bc.py", + "target": "function:routes/bc.py:submit_for_review", + "type": "contains", + "direction": "forward", + "weight": 1.0 + }, + { + "source": "file:routes/bc.py", + "target": "function:routes/bc.py:delete", + "type": "contains", + "direction": "forward", + "weight": 1.0 + }, + { + "source": "file:routes/bc.py", + "target": "function:routes/bc.py:_decs_from_form", + "type": "contains", + "direction": "forward", + "weight": 1.0 + }, + { + "source": "file:routes/governance.py", + "target": "function:routes/governance.py:board", + "type": "contains", + "direction": "forward", + "weight": 1.0 + }, + { + "source": "file:routes/governance.py", + "target": "function:routes/governance.py:export", + "type": "contains", + "direction": "forward", + "weight": 1.0 + }, + { + "source": "file:routes/governance.py", + "target": "function:routes/governance.py:advance", + "type": "contains", + "direction": "forward", + "weight": 1.0 + }, + { + "source": "file:routes/governance.py", + "target": "function:routes/governance.py:reject_bc", + "type": "contains", + "direction": "forward", + "weight": 1.0 + }, + { + "source": "file:routes/ncit.py", + "target": "function:routes/ncit.py:index", + "type": "contains", + "direction": "forward", + "weight": 1.0 + }, + { + "source": "file:routes/ncit.py", + "target": "function:routes/ncit.py:mapping", + "type": "contains", + "direction": "forward", + "weight": 1.0 + }, + { + "source": "file:routes/ncit.py", + "target": "function:routes/ncit.py:search_ncit", + "type": "contains", + "direction": "forward", + "weight": 1.0 + }, + { + "source": "file:routes/ncit.py", + "target": "function:routes/ncit.py:concept_detail", + "type": "contains", + "direction": "forward", + "weight": 1.0 + }, + { + "source": "file:routes/ncit.py", + "target": "function:routes/ncit.py:resolve", + "type": "contains", + "direction": "forward", + "weight": 1.0 + }, + { + "source": "file:services/audit.py", + "target": "function:services/audit.py:log_change", + "type": "contains", + "direction": "forward", + "weight": 1.0 + }, + { + "source": "file:models/governance.py", + "target": "class:models/governance.py:GovernanceRecord", + "type": "exports", + "direction": "forward", + "weight": 0.8 + }, + { + "source": "file:routes/bc.py", + "target": "function:routes/bc.py:index", + "type": "exports", + "direction": "forward", + "weight": 0.8 + }, + { + "source": "file:routes/bc.py", + "target": "function:routes/bc.py:new_bc", + "type": "exports", + "direction": "forward", + "weight": 0.8 + }, + { + "source": "file:routes/bc.py", + "target": "function:routes/bc.py:export", + "type": "exports", + "direction": "forward", + "weight": 0.8 + }, + { + "source": "file:routes/bc.py", + "target": "function:routes/bc.py:library_detail", + "type": "exports", + "direction": "forward", + "weight": 0.8 + }, + { + "source": "file:routes/bc.py", + "target": "function:routes/bc.py:detail", + "type": "exports", + "direction": "forward", + "weight": 0.8 + }, + { + "source": "file:routes/bc.py", + "target": "function:routes/bc.py:fetch_metadata", + "type": "exports", + "direction": "forward", + "weight": 0.8 + }, + { + "source": "file:routes/bc.py", + "target": "function:routes/bc.py:create", + "type": "exports", + "direction": "forward", + "weight": 0.8 + }, + { + "source": "file:routes/bc.py", + "target": "function:routes/bc.py:edit", + "type": "exports", + "direction": "forward", + "weight": 0.8 + }, + { + "source": "file:routes/bc.py", + "target": "function:routes/bc.py:clear_ncit", + "type": "exports", + "direction": "forward", + "weight": 0.8 + }, + { + "source": "file:routes/bc.py", + "target": "function:routes/bc.py:clear_loinc", + "type": "exports", + "direction": "forward", + "weight": 0.8 + }, + { + "source": "file:routes/bc.py", + "target": "function:routes/bc.py:submit_for_review", + "type": "exports", + "direction": "forward", + "weight": 0.8 + }, + { + "source": "file:routes/bc.py", + "target": "function:routes/bc.py:delete", + "type": "exports", + "direction": "forward", + "weight": 0.8 + }, + { + "source": "file:routes/bc.py", + "target": "function:routes/bc.py:_decs_from_form", + "type": "exports", + "direction": "forward", + "weight": 0.8 + }, + { + "source": "file:routes/governance.py", + "target": "function:routes/governance.py:board", + "type": "exports", + "direction": "forward", + "weight": 0.8 + }, + { + "source": "file:routes/governance.py", + "target": "function:routes/governance.py:export", + "type": "exports", + "direction": "forward", + "weight": 0.8 + }, + { + "source": "file:routes/governance.py", + "target": "function:routes/governance.py:advance", + "type": "exports", + "direction": "forward", + "weight": 0.8 + }, + { + "source": "file:routes/governance.py", + "target": "function:routes/governance.py:reject_bc", + "type": "exports", + "direction": "forward", + "weight": 0.8 + }, + { + "source": "file:routes/ncit.py", + "target": "function:routes/ncit.py:index", + "type": "exports", + "direction": "forward", + "weight": 0.8 + }, + { + "source": "file:routes/ncit.py", + "target": "function:routes/ncit.py:mapping", + "type": "exports", + "direction": "forward", + "weight": 0.8 + }, + { + "source": "file:routes/ncit.py", + "target": "function:routes/ncit.py:search_ncit", + "type": "exports", + "direction": "forward", + "weight": 0.8 + }, + { + "source": "file:routes/ncit.py", + "target": "function:routes/ncit.py:concept_detail", + "type": "exports", + "direction": "forward", + "weight": 0.8 + }, + { + "source": "file:routes/ncit.py", + "target": "function:routes/ncit.py:resolve", + "type": "exports", + "direction": "forward", + "weight": 0.8 + }, + { + "source": "file:services/audit.py", + "target": "function:services/audit.py:log_change", + "type": "exports", + "direction": "forward", + "weight": 0.8 + }, + { + "source": "function:routes/bc.py:create", + "target": "function:services/bc_service.py:create_bc", + "type": "calls", + "direction": "forward", + "weight": 0.8 + }, + { + "source": "function:routes/bc.py:create", + "target": "function:services/bc_service.py:save_decs", + "type": "calls", + "direction": "forward", + "weight": 0.8 + }, + { + "source": "function:routes/bc.py:edit", + "target": "function:services/bc_service.py:update_bc", + "type": "calls", + "direction": "forward", + "weight": 0.8 + }, + { + "source": "function:routes/bc.py:edit", + "target": "function:services/bc_service.py:save_decs", + "type": "calls", + "direction": "forward", + "weight": 0.8 + }, + { + "source": "function:routes/bc.py:submit_for_review", + "target": "function:services/bc_service.py:submit_bc_for_review", + "type": "calls", + "direction": "forward", + "weight": 0.8 + }, + { + "source": "function:routes/bc.py:clear_ncit", + "target": "function:services/audit.py:log_change", + "type": "calls", + "direction": "forward", + "weight": 0.8 + }, + { + "source": "function:routes/bc.py:clear_loinc", + "target": "function:services/audit.py:log_change", + "type": "calls", + "direction": "forward", + "weight": 0.8 + }, + { + "source": "function:routes/bc.py:delete", + "target": "function:services/audit.py:log_change", + "type": "calls", + "direction": "forward", + "weight": 0.8 + }, + { + "source": "function:routes/bc.py:library_detail", + "target": "class:services/cdisc_api.py:CDISCApiClient", + "type": "calls", + "direction": "forward", + "weight": 0.8 + }, + { + "source": "function:routes/bc.py:library_detail", + "target": "class:services/loinc_api.py:LoincApiClient", + "type": "calls", + "direction": "forward", + "weight": 0.8 + }, + { + "source": "function:routes/bc.py:detail", + "target": "class:services/loinc_api.py:LoincApiClient", + "type": "calls", + "direction": "forward", + "weight": 0.8 + }, + { + "source": "function:routes/bc.py:fetch_metadata", + "target": "class:services/loinc_api.py:LoincApiClient", + "type": "calls", + "direction": "forward", + "weight": 0.8 + }, + { + "source": "function:routes/bc.py:export", + "target": "function:services/export.py:export_xlsx", + "type": "calls", + "direction": "forward", + "weight": 0.8 + }, + { + "source": "function:routes/bc.py:export", + "target": "function:services/export.py:export_odm_xml", + "type": "calls", + "direction": "forward", + "weight": 0.8 + }, + { + "source": "function:routes/bc.py:export", + "target": "function:services/export.py:export_json", + "type": "calls", + "direction": "forward", + "weight": 0.8 + }, + { + "source": "function:routes/governance.py:export", + "target": "function:services/export.py:export_governance_xlsx", + "type": "calls", + "direction": "forward", + "weight": 0.8 + }, + { + "source": "function:routes/governance.py:advance", + "target": "function:services/governance_service.py:advance_governance", + "type": "calls", + "direction": "forward", + "weight": 0.8 + }, + { + "source": "function:routes/governance.py:reject_bc", + "target": "function:services/governance_service.py:reject_bc", + "type": "calls", + "direction": "forward", + "weight": 0.8 + }, + { + "source": "function:routes/ncit.py:search_ncit", + "target": "class:services/ncit_api.py:NCItApiClient", + "type": "calls", + "direction": "forward", + "weight": 0.8 + }, + { + "source": "function:routes/ncit.py:concept_detail", + "target": "class:services/ncit_api.py:NCItApiClient", + "type": "calls", + "direction": "forward", + "weight": 0.8 + }, + { + "source": "function:routes/ncit.py:resolve", + "target": "function:services/bc_service.py:map_ncit_to_bc", + "type": "calls", + "direction": "forward", + "weight": 0.8 + }, + { + "source": "function:services/audit.py:log_change", + "target": "class:models/audit.py:AuditLog", + "type": "calls", + "direction": "forward", + "weight": 0.8 + }, + { + "source": "file:routes/ncit.py", + "target": "file:tests/test_ncit.py", + "type": "tested_by", + "direction": "forward", + "weight": 0.5 + }, + { + "source": "file:services/bc_service.py", + "target": "file:extensions.py", + "type": "imports", + "direction": "forward", + "weight": 0.7 + }, + { + "source": "file:services/bc_service.py", + "target": "file:models/bc.py", + "type": "imports", + "direction": "forward", + "weight": 0.7 + }, + { + "source": "file:services/bc_service.py", + "target": "file:models/governance.py", + "type": "imports", + "direction": "forward", + "weight": 0.7 + }, + { + "source": "file:services/bc_service.py", + "target": "file:models/specialization.py", + "type": "imports", + "direction": "forward", + "weight": 0.7 + }, + { + "source": "file:services/bc_service.py", + "target": "file:services/audit.py", + "type": "imports", + "direction": "forward", + "weight": 0.7 + }, + { + "source": "file:services/governance_service.py", + "target": "file:extensions.py", + "type": "imports", + "direction": "forward", + "weight": 0.7 + }, + { + "source": "file:services/governance_service.py", + "target": "file:models/governance.py", + "type": "imports", + "direction": "forward", + "weight": 0.7 + }, + { + "source": "file:services/governance_service.py", + "target": "file:services/audit.py", + "type": "imports", + "direction": "forward", + "weight": 0.7 + }, + { + "source": "file:services/governance_service.py", + "target": "file:services/bc_service.py", + "type": "imports", + "direction": "forward", + "weight": 0.7 + }, + { + "source": "file:services/ncit_api.py", + "target": "file:services/api_cache.py", + "type": "imports", + "direction": "forward", + "weight": 0.7 + }, + { + "source": "file:tests/test_export_service.py", + "target": "file:extensions.py", + "type": "imports", + "direction": "forward", + "weight": 0.7 + }, + { + "source": "file:tests/test_export_service.py", + "target": "file:models/bc.py", + "type": "imports", + "direction": "forward", + "weight": 0.7 + }, + { + "source": "file:tests/test_export_service.py", + "target": "file:services/export.py", + "type": "imports", + "direction": "forward", + "weight": 0.7 + }, + { + "source": "file:tests/test_ncit.py", + "target": "file:services/ncit_api.py", + "type": "imports", + "direction": "forward", + "weight": 0.7 + }, + { + "source": "file:services/bc_service.py", + "target": "function:services/bc_service.py:_get_bc_or_raise", + "type": "contains", + "direction": "forward", + "weight": 1.0 + }, + { + "source": "file:services/bc_service.py", + "target": "function:services/bc_service.py:apply_bc_fields", + "type": "contains", + "direction": "forward", + "weight": 1.0 + }, + { + "source": "file:services/bc_service.py", + "target": "function:services/bc_service.py:create_bc", + "type": "contains", + "direction": "forward", + "weight": 1.0 + }, + { + "source": "file:services/bc_service.py", + "target": "function:services/bc_service.py:update_bc", + "type": "contains", + "direction": "forward", + "weight": 1.0 + }, + { + "source": "file:services/bc_service.py", + "target": "function:services/bc_service.py:save_decs", + "type": "contains", + "direction": "forward", + "weight": 1.0 + }, + { + "source": "file:services/bc_service.py", + "target": "function:services/bc_service.py:map_ncit_to_bc", + "type": "contains", + "direction": "forward", + "weight": 1.0 + }, + { + "source": "file:services/bc_service.py", + "target": "function:services/bc_service.py:submit_bc_for_review", + "type": "contains", + "direction": "forward", + "weight": 1.0 + }, + { + "source": "file:services/export.py", + "target": "function:services/export.py:export_json", + "type": "contains", + "direction": "forward", + "weight": 1.0 + }, + { + "source": "file:services/export.py", + "target": "function:services/export.py:export_xlsx", + "type": "contains", + "direction": "forward", + "weight": 1.0 + }, + { + "source": "file:services/export.py", + "target": "function:services/export.py:export_governance_xlsx", + "type": "contains", + "direction": "forward", + "weight": 1.0 + }, + { + "source": "file:services/export.py", + "target": "function:services/export.py:export_odm_xml", + "type": "contains", + "direction": "forward", + "weight": 1.0 + }, + { + "source": "file:services/governance_service.py", + "target": "function:services/governance_service.py:advance_governance", + "type": "contains", + "direction": "forward", + "weight": 1.0 + }, + { + "source": "file:services/governance_service.py", + "target": "function:services/governance_service.py:reject_bc", + "type": "contains", + "direction": "forward", + "weight": 1.0 + }, + { + "source": "file:services/ncit_api.py", + "target": "function:services/ncit_api.py:_pick_definition", + "type": "contains", + "direction": "forward", + "weight": 1.0 + }, + { + "source": "file:services/ncit_api.py", + "target": "class:services/ncit_api.py:NCItApiClient", + "type": "contains", + "direction": "forward", + "weight": 1.0 + }, + { + "source": "file:tests/test_export_service.py", + "target": "class:tests/test_export_service.py:TestExportJson", + "type": "contains", + "direction": "forward", + "weight": 1.0 + }, + { + "source": "file:tests/test_export_service.py", + "target": "class:tests/test_export_service.py:TestExportXlsx", + "type": "contains", + "direction": "forward", + "weight": 1.0 + }, + { + "source": "file:tests/test_export_service.py", + "target": "class:tests/test_export_service.py:TestExportGovernanceXlsx", + "type": "contains", + "direction": "forward", + "weight": 1.0 + }, + { + "source": "file:tests/test_export_service.py", + "target": "class:tests/test_export_service.py:TestExportOdmXml", + "type": "contains", + "direction": "forward", + "weight": 1.0 + }, + { + "source": "file:tests/test_ncit.py", + "target": "class:tests/test_ncit.py:TestNcitGetConceptExtended", + "type": "contains", + "direction": "forward", + "weight": 1.0 + }, + { + "source": "file:tests/test_ncit.py", + "target": "class:tests/test_ncit.py:TestNcitConceptRoute", + "type": "contains", + "direction": "forward", + "weight": 1.0 + }, + { + "source": "file:services/bc_service.py", + "target": "function:services/bc_service.py:_get_bc_or_raise", + "type": "exports", + "direction": "forward", + "weight": 0.8 + }, + { + "source": "file:services/bc_service.py", + "target": "function:services/bc_service.py:apply_bc_fields", + "type": "exports", + "direction": "forward", + "weight": 0.8 + }, + { + "source": "file:services/bc_service.py", + "target": "function:services/bc_service.py:create_bc", + "type": "exports", + "direction": "forward", + "weight": 0.8 + }, + { + "source": "file:services/bc_service.py", + "target": "function:services/bc_service.py:update_bc", + "type": "exports", + "direction": "forward", + "weight": 0.8 + }, + { + "source": "file:services/bc_service.py", + "target": "function:services/bc_service.py:save_decs", + "type": "exports", + "direction": "forward", + "weight": 0.8 + }, + { + "source": "file:services/bc_service.py", + "target": "function:services/bc_service.py:map_ncit_to_bc", + "type": "exports", + "direction": "forward", + "weight": 0.8 + }, + { + "source": "file:services/bc_service.py", + "target": "function:services/bc_service.py:submit_bc_for_review", + "type": "exports", + "direction": "forward", + "weight": 0.8 + }, + { + "source": "file:services/export.py", + "target": "function:services/export.py:export_json", + "type": "exports", + "direction": "forward", + "weight": 0.8 + }, + { + "source": "file:services/export.py", + "target": "function:services/export.py:export_xlsx", + "type": "exports", + "direction": "forward", + "weight": 0.8 + }, + { + "source": "file:services/export.py", + "target": "function:services/export.py:export_governance_xlsx", + "type": "exports", + "direction": "forward", + "weight": 0.8 + }, + { + "source": "file:services/export.py", + "target": "function:services/export.py:export_odm_xml", + "type": "exports", + "direction": "forward", + "weight": 0.8 + }, + { + "source": "file:services/governance_service.py", + "target": "function:services/governance_service.py:advance_governance", + "type": "exports", + "direction": "forward", + "weight": 0.8 + }, + { + "source": "file:services/governance_service.py", + "target": "function:services/governance_service.py:reject_bc", + "type": "exports", + "direction": "forward", + "weight": 0.8 + }, + { + "source": "file:services/ncit_api.py", + "target": "function:services/ncit_api.py:_pick_definition", + "type": "exports", + "direction": "forward", + "weight": 0.8 + }, + { + "source": "file:services/ncit_api.py", + "target": "class:services/ncit_api.py:NCItApiClient", + "type": "exports", + "direction": "forward", + "weight": 0.8 + }, + { + "source": "file:tests/test_export_service.py", + "target": "class:tests/test_export_service.py:TestExportJson", + "type": "exports", + "direction": "forward", + "weight": 0.8 + }, + { + "source": "file:tests/test_export_service.py", + "target": "class:tests/test_export_service.py:TestExportXlsx", + "type": "exports", + "direction": "forward", + "weight": 0.8 + }, + { + "source": "file:tests/test_export_service.py", + "target": "class:tests/test_export_service.py:TestExportGovernanceXlsx", + "type": "exports", + "direction": "forward", + "weight": 0.8 + }, + { + "source": "file:tests/test_export_service.py", + "target": "class:tests/test_export_service.py:TestExportOdmXml", + "type": "exports", + "direction": "forward", + "weight": 0.8 + }, + { + "source": "file:tests/test_ncit.py", + "target": "class:tests/test_ncit.py:TestNcitGetConceptExtended", + "type": "exports", + "direction": "forward", + "weight": 0.8 + }, + { + "source": "file:tests/test_ncit.py", + "target": "class:tests/test_ncit.py:TestNcitConceptRoute", + "type": "exports", + "direction": "forward", + "weight": 0.8 + }, + { + "source": "function:services/bc_service.py:create_bc", + "target": "function:services/audit.py:log_change", + "type": "calls", + "direction": "forward", + "weight": 0.8 + }, + { + "source": "function:services/bc_service.py:update_bc", + "target": "function:services/audit.py:log_change", + "type": "calls", + "direction": "forward", + "weight": 0.8 + }, + { + "source": "function:services/bc_service.py:map_ncit_to_bc", + "target": "function:services/audit.py:log_change", + "type": "calls", + "direction": "forward", + "weight": 0.8 + }, + { + "source": "function:services/bc_service.py:submit_bc_for_review", + "target": "function:services/audit.py:log_change", + "type": "calls", + "direction": "forward", + "weight": 0.8 + }, + { + "source": "function:services/governance_service.py:advance_governance", + "target": "function:services/audit.py:log_change", + "type": "calls", + "direction": "forward", + "weight": 0.8 + }, + { + "source": "function:services/governance_service.py:advance_governance", + "target": "class:models/governance.py:GovernanceRecord", + "type": "calls", + "direction": "forward", + "weight": 0.8 + }, + { + "source": "function:services/governance_service.py:reject_bc", + "target": "function:services/audit.py:log_change", + "type": "calls", + "direction": "forward", + "weight": 0.8 + }, + { + "source": "function:services/governance_service.py:reject_bc", + "target": "class:models/governance.py:GovernanceRecord", + "type": "calls", + "direction": "forward", + "weight": 0.8 + }, + { + "source": "class:services/ncit_api.py:NCItApiClient", + "target": "function:services/api_cache.py:cached", + "type": "calls", + "direction": "forward", + "weight": 0.8 + }, + { + "source": "file:services/export.py", + "target": "file:tests/test_export_service.py", + "type": "tested_by", + "direction": "forward", + "weight": 0.5 + }, + { + "source": "file:services/ncit_api.py", + "target": "file:tests/test_ncit.py", + "type": "tested_by", + "direction": "forward", + "weight": 0.5 + }, + { + "source": "file:routes/dashboard.py", + "target": "file:models/audit.py", + "type": "imports", + "direction": "forward", + "weight": 0.7 + }, + { + "source": "file:routes/dashboard.py", + "target": "file:models/bc.py", + "type": "imports", + "direction": "forward", + "weight": 0.7 + }, + { + "source": "file:routes/dashboard.py", + "target": "file:services/cdisc_api.py", + "type": "imports", + "direction": "forward", + "weight": 0.7 + }, + { + "source": "file:routes/loinc.py", + "target": "file:services/loinc_api.py", + "type": "imports", + "direction": "forward", + "weight": 0.7 + }, + { + "source": "file:routes/specializations.py", + "target": "file:extensions.py", + "type": "imports", + "direction": "forward", + "weight": 0.7 + }, + { + "source": "file:routes/specializations.py", + "target": "file:models/bc.py", + "type": "imports", + "direction": "forward", + "weight": 0.7 + }, + { + "source": "file:routes/specializations.py", + "target": "file:models/specialization.py", + "type": "imports", + "direction": "forward", + "weight": 0.7 + }, + { + "source": "file:routes/specializations.py", + "target": "file:services/audit.py", + "type": "imports", + "direction": "forward", + "weight": 0.7 + }, + { + "source": "file:routes/specializations.py", + "target": "file:services/cdisc_api.py", + "type": "imports", + "direction": "forward", + "weight": 0.7 + }, + { + "source": "file:services/cdisc_api.py", + "target": "file:services/api_cache.py", + "type": "imports", + "direction": "forward", + "weight": 0.7 + }, + { + "source": "file:services/loinc_api.py", + "target": "file:services/api_cache.py", + "type": "imports", + "direction": "forward", + "weight": 0.7 + }, + { + "source": "file:tests/test_cdisc_api_cache.py", + "target": "file:services/cdisc_api.py", + "type": "imports", + "direction": "forward", + "weight": 0.7 + }, + { + "source": "file:tests/test_loinc.py", + "target": "file:services/loinc_api.py", + "type": "imports", + "direction": "forward", + "weight": 0.7 + }, + { + "source": "file:routes/dashboard.py", + "target": "function:routes/dashboard.py:index", + "type": "contains", + "direction": "forward", + "weight": 1.0 + }, + { + "source": "file:routes/loinc.py", + "target": "function:routes/loinc.py:search", + "type": "contains", + "direction": "forward", + "weight": 1.0 + }, + { + "source": "file:routes/specializations.py", + "target": "function:routes/specializations.py:_get_bc_options", + "type": "contains", + "direction": "forward", + "weight": 1.0 + }, + { + "source": "file:routes/specializations.py", + "target": "function:routes/specializations.py:index", + "type": "contains", + "direction": "forward", + "weight": 1.0 + }, + { + "source": "file:routes/specializations.py", + "target": "function:routes/specializations.py:library_detail", + "type": "contains", + "direction": "forward", + "weight": 1.0 + }, + { + "source": "file:routes/specializations.py", + "target": "function:routes/specializations.py:detail", + "type": "contains", + "direction": "forward", + "weight": 1.0 + }, + { + "source": "file:routes/specializations.py", + "target": "function:routes/specializations.py:_variables_from_form", + "type": "contains", + "direction": "forward", + "weight": 1.0 + }, + { + "source": "file:routes/specializations.py", + "target": "function:routes/specializations.py:create", + "type": "contains", + "direction": "forward", + "weight": 1.0 + }, + { + "source": "file:routes/specializations.py", + "target": "function:routes/specializations.py:delete", + "type": "contains", + "direction": "forward", + "weight": 1.0 + }, + { + "source": "file:routes/specializations.py", + "target": "function:routes/specializations.py:generate_from_dec", + "type": "contains", + "direction": "forward", + "weight": 1.0 + }, + { + "source": "file:routes/specializations.py", + "target": "function:routes/specializations.py:generate", + "type": "contains", + "direction": "forward", + "weight": 1.0 + }, + { + "source": "file:services/api_cache.py", + "target": "function:services/api_cache.py:cached", + "type": "contains", + "direction": "forward", + "weight": 1.0 + }, + { + "source": "file:services/cdisc_api.py", + "target": "function:services/cdisc_api.py:_config_value", + "type": "contains", + "direction": "forward", + "weight": 1.0 + }, + { + "source": "file:services/cdisc_api.py", + "target": "class:services/cdisc_api.py:CDISCApiClient", + "type": "contains", + "direction": "forward", + "weight": 1.0 + }, + { + "source": "file:services/loinc_api.py", + "target": "class:services/loinc_api.py:LoincApiClient", + "type": "contains", + "direction": "forward", + "weight": 1.0 + }, + { + "source": "file:tests/test_cdisc_api_cache.py", + "target": "class:tests/test_cdisc_api_cache.py:TestCachedHelper", + "type": "contains", + "direction": "forward", + "weight": 1.0 + }, + { + "source": "file:tests/test_cdisc_api_cache.py", + "target": "class:tests/test_cdisc_api_cache.py:TestClientCacheKeys", + "type": "contains", + "direction": "forward", + "weight": 1.0 + }, + { + "source": "file:tests/test_loinc.py", + "target": "class:tests/test_loinc.py:TestLoincApiClientSearch", + "type": "contains", + "direction": "forward", + "weight": 1.0 + }, + { + "source": "file:tests/test_loinc.py", + "target": "class:tests/test_loinc.py:TestLoincSearchRoute", + "type": "contains", + "direction": "forward", + "weight": 1.0 + }, + { + "source": "file:services/cdisc_api.py", + "target": "class:services/cdisc_api.py:CDISCApiClient", + "type": "exports", + "direction": "forward", + "weight": 0.8 + }, + { + "source": "file:services/loinc_api.py", + "target": "class:services/loinc_api.py:LoincApiClient", + "type": "exports", + "direction": "forward", + "weight": 0.8 + }, + { + "source": "file:services/api_cache.py", + "target": "function:services/api_cache.py:cached", + "type": "exports", + "direction": "forward", + "weight": 0.8 + }, + { + "source": "function:routes/dashboard.py:index", + "target": "class:services/cdisc_api.py:CDISCApiClient", + "type": "calls", + "direction": "forward", + "weight": 0.8 + }, + { + "source": "function:routes/dashboard.py:index", + "target": "class:models/bc.py:BiomedicalConcept", + "type": "calls", + "direction": "forward", + "weight": 0.8 + }, + { + "source": "function:routes/dashboard.py:index", + "target": "class:models/audit.py:AuditLog", + "type": "calls", + "direction": "forward", + "weight": 0.8 + }, + { + "source": "function:routes/loinc.py:search", + "target": "class:services/loinc_api.py:LoincApiClient", + "type": "calls", + "direction": "forward", + "weight": 0.8 + }, + { + "source": "function:routes/specializations.py:_get_bc_options", + "target": "class:services/cdisc_api.py:CDISCApiClient", + "type": "calls", + "direction": "forward", + "weight": 0.8 + }, + { + "source": "function:routes/specializations.py:_get_bc_options", + "target": "class:models/bc.py:BiomedicalConcept", + "type": "calls", + "direction": "forward", + "weight": 0.8 + }, + { + "source": "function:routes/specializations.py:index", + "target": "class:models/specialization.py:DatasetSpecialization", + "type": "calls", + "direction": "forward", + "weight": 0.8 + }, + { + "source": "function:routes/specializations.py:library_detail", + "target": "class:services/cdisc_api.py:CDISCApiClient", + "type": "calls", + "direction": "forward", + "weight": 0.8 + }, + { + "source": "function:routes/specializations.py:detail", + "target": "class:models/specialization.py:DatasetSpecialization", + "type": "calls", + "direction": "forward", + "weight": 0.8 + }, + { + "source": "function:routes/specializations.py:create", + "target": "function:services/audit.py:log_change", + "type": "calls", + "direction": "forward", + "weight": 0.8 + }, + { + "source": "function:routes/specializations.py:create", + "target": "class:models/specialization.py:DatasetSpecialization", + "type": "calls", + "direction": "forward", + "weight": 0.8 + }, + { + "source": "function:routes/specializations.py:delete", + "target": "function:services/audit.py:log_change", + "type": "calls", + "direction": "forward", + "weight": 0.8 + }, + { + "source": "function:routes/specializations.py:generate_from_dec", + "target": "class:models/bc.py:DataElementConcept", + "type": "calls", + "direction": "forward", + "weight": 0.8 + }, + { + "source": "function:routes/specializations.py:generate", + "target": "class:models/bc.py:DataElementConcept", + "type": "calls", + "direction": "forward", + "weight": 0.8 + }, + { + "source": "function:routes/specializations.py:generate", + "target": "class:models/specialization.py:DatasetSpecialization", + "type": "calls", + "direction": "forward", + "weight": 0.8 + }, + { + "source": "function:routes/specializations.py:generate", + "target": "function:services/audit.py:log_change", + "type": "calls", + "direction": "forward", + "weight": 0.8 + }, + { + "source": "class:services/cdisc_api.py:CDISCApiClient", + "target": "function:services/api_cache.py:cached", + "type": "calls", + "direction": "forward", + "weight": 0.8 + }, + { + "source": "class:services/loinc_api.py:LoincApiClient", + "target": "function:services/api_cache.py:cached", + "type": "calls", + "direction": "forward", + "weight": 0.8 + }, + { + "source": "file:services/cdisc_api.py", + "target": "file:tests/test_cdisc_api_cache.py", + "type": "tested_by", + "direction": "forward", + "weight": 0.5 + }, + { + "source": "file:services/api_cache.py", + "target": "file:tests/test_cdisc_api_cache.py", + "type": "tested_by", + "direction": "forward", + "weight": 0.5 + }, + { + "source": "file:services/loinc_api.py", + "target": "file:tests/test_loinc.py", + "type": "tested_by", + "direction": "forward", + "weight": 0.5 + }, + { + "source": "file:routes/loinc.py", + "target": "file:tests/test_loinc.py", + "type": "tested_by", + "direction": "forward", + "weight": 0.5 + }, + { + "source": "file:app.py", + "target": "file:config.py", + "type": "imports", + "direction": "forward", + "weight": 0.7 + }, + { + "source": "file:app.py", + "target": "file:extensions.py", + "type": "imports", + "direction": "forward", + "weight": 0.7 + }, + { + "source": "file:db_bootstrap.py", + "target": "file:extensions.py", + "type": "imports", + "direction": "forward", + "weight": 0.7 + }, + { + "source": "file:models/__init__.py", + "target": "file:extensions.py", + "type": "imports", + "direction": "forward", + "weight": 0.7 + }, + { + "source": "file:tests/test_dashboard.py", + "target": "file:extensions.py", + "type": "imports", + "direction": "forward", + "weight": 0.7 + }, + { + "source": "file:tests/test_dashboard.py", + "target": "file:models/bc.py", + "type": "imports", + "direction": "forward", + "weight": 0.7 + }, + { + "source": "file:tests/test_db_bootstrap.py", + "target": "file:app.py", + "type": "imports", + "direction": "forward", + "weight": 0.7 + }, + { + "source": "file:tests/test_db_bootstrap.py", + "target": "file:db_bootstrap.py", + "type": "imports", + "direction": "forward", + "weight": 0.7 + }, + { + "source": "file:tests/test_db_bootstrap.py", + "target": "file:extensions.py", + "type": "imports", + "direction": "forward", + "weight": 0.7 + }, + { + "source": "file:app.py", + "target": "function:app.py:_configure_logging", + "type": "contains", + "direction": "forward", + "weight": 1.0 + }, + { + "source": "file:app.py", + "target": "function:app.py:create_app", + "type": "contains", + "direction": "forward", + "weight": 1.0 + }, + { + "source": "file:config.py", + "target": "class:config.py:Config", + "type": "contains", + "direction": "forward", + "weight": 1.0 + }, + { + "source": "file:db_bootstrap.py", + "target": "function:db_bootstrap.py:ensure_db", + "type": "contains", + "direction": "forward", + "weight": 1.0 + }, + { + "source": "file:extensions.py", + "target": "function:extensions.py:_set_sqlite_pragmas", + "type": "contains", + "direction": "forward", + "weight": 1.0 + }, + { + "source": "file:tests/test_dashboard.py", + "target": "class:tests/test_dashboard.py:TestDashboardIndex", + "type": "contains", + "direction": "forward", + "weight": 1.0 + }, + { + "source": "file:tests/test_db_bootstrap.py", + "target": "function:tests/test_db_bootstrap.py:_make_app", + "type": "contains", + "direction": "forward", + "weight": 1.0 + }, + { + "source": "file:tests/test_db_bootstrap.py", + "target": "class:tests/test_db_bootstrap.py:TestEnsureDb", + "type": "contains", + "direction": "forward", + "weight": 1.0 + }, + { + "source": "file:app.py", + "target": "function:app.py:create_app", + "type": "exports", + "direction": "forward", + "weight": 0.8 + }, + { + "source": "file:db_bootstrap.py", + "target": "function:db_bootstrap.py:ensure_db", + "type": "exports", + "direction": "forward", + "weight": 0.8 + }, + { + "source": "file:config.py", + "target": "class:config.py:Config", + "type": "exports", + "direction": "forward", + "weight": 0.8 + }, + { + "source": "function:tests/test_db_bootstrap.py:_make_app", + "target": "function:app.py:create_app", + "type": "calls", + "direction": "forward", + "weight": 0.8 + }, + { + "source": "class:tests/test_db_bootstrap.py:TestEnsureDb", + "target": "function:db_bootstrap.py:ensure_db", + "type": "calls", + "direction": "forward", + "weight": 0.8 + }, + { + "source": "class:tests/test_dashboard.py:TestDashboardIndex", + "target": "class:models/bc.py:BiomedicalConcept", + "type": "calls", + "direction": "forward", + "weight": 0.8 + }, + { + "source": "file:app.py", + "target": "file:db_bootstrap.py", + "type": "depends_on", + "direction": "forward", + "weight": 0.6 + }, + { + "source": "file:db_bootstrap.py", + "target": "file:tests/test_db_bootstrap.py", + "type": "tested_by", + "direction": "forward", + "weight": 0.5 + }, + { + "source": "file:mcp_server/__main__.py", + "target": "file:mcp_server/server.py", + "type": "imports", + "direction": "forward", + "weight": 0.7 + }, + { + "source": "file:models/specialization.py", + "target": "file:extensions.py", + "type": "imports", + "direction": "forward", + "weight": 0.7 + }, + { + "source": "file:tests/test_mcp_server.py", + "target": "file:extensions.py", + "type": "imports", + "direction": "forward", + "weight": 0.7 + }, + { + "source": "file:tests/test_mcp_server.py", + "target": "file:mcp_server/server.py", + "type": "imports", + "direction": "forward", + "weight": 0.7 + }, + { + "source": "file:tests/test_mcp_server.py", + "target": "file:models/audit.py", + "type": "imports", + "direction": "forward", + "weight": 0.7 + }, + { + "source": "file:tests/test_mcp_server.py", + "target": "file:models/bc.py", + "type": "imports", + "direction": "forward", + "weight": 0.7 + }, + { + "source": "file:tests/test_mcp_server.py", + "target": "file:models/governance.py", + "type": "imports", + "direction": "forward", + "weight": 0.7 + }, + { + "source": "file:tests/test_mcp_server.py", + "target": "file:models/ingestion.py", + "type": "imports", + "direction": "forward", + "weight": 0.7 + }, + { + "source": "file:tests/test_mcp_server.py", + "target": "file:models/specialization.py", + "type": "imports", + "direction": "forward", + "weight": 0.7 + }, + { + "source": "file:tests/test_specializations_routes.py", + "target": "file:extensions.py", + "type": "imports", + "direction": "forward", + "weight": 0.7 + }, + { + "source": "file:tests/test_specializations_routes.py", + "target": "file:models/audit.py", + "type": "imports", + "direction": "forward", + "weight": 0.7 + }, + { + "source": "file:tests/test_specializations_routes.py", + "target": "file:models/bc.py", + "type": "imports", + "direction": "forward", + "weight": 0.7 + }, + { + "source": "file:tests/test_specializations_routes.py", + "target": "file:models/specialization.py", + "type": "imports", + "direction": "forward", + "weight": 0.7 + }, + { + "source": "file:mcp_server/server.py", + "target": "function:mcp_server/server.py:_with_app_context", + "type": "contains", + "direction": "forward", + "weight": 1.0 + }, + { + "source": "file:mcp_server/server.py", + "target": "function:mcp_server/server.py:_dispatch", + "type": "contains", + "direction": "forward", + "weight": 1.0 + }, + { + "source": "file:mcp_server/server.py", + "target": "function:mcp_server/server.py:_bc_full_dict", + "type": "contains", + "direction": "forward", + "weight": 1.0 + }, + { + "source": "file:mcp_server/server.py", + "target": "function:mcp_server/server.py:_list_bcs", + "type": "contains", + "direction": "forward", + "weight": 1.0 + }, + { + "source": "file:mcp_server/server.py", + "target": "function:mcp_server/server.py:_get_bc", + "type": "contains", + "direction": "forward", + "weight": 1.0 + }, + { + "source": "file:mcp_server/server.py", + "target": "function:mcp_server/server.py:_search_ncit", + "type": "contains", + "direction": "forward", + "weight": 1.0 + }, + { + "source": "file:mcp_server/server.py", + "target": "function:mcp_server/server.py:_get_ncit_concept", + "type": "contains", + "direction": "forward", + "weight": 1.0 + }, + { + "source": "file:mcp_server/server.py", + "target": "function:mcp_server/server.py:_search_loinc", + "type": "contains", + "direction": "forward", + "weight": 1.0 + }, + { + "source": "file:mcp_server/server.py", + "target": "function:mcp_server/server.py:_search_cdisc_library", + "type": "contains", + "direction": "forward", + "weight": 1.0 + }, + { + "source": "file:mcp_server/server.py", + "target": "function:mcp_server/server.py:_get_library_bc", + "type": "contains", + "direction": "forward", + "weight": 1.0 + }, + { + "source": "file:mcp_server/server.py", + "target": "function:mcp_server/server.py:_list_review_queue", + "type": "contains", + "direction": "forward", + "weight": 1.0 + }, + { + "source": "file:mcp_server/server.py", + "target": "function:mcp_server/server.py:_create_bc", + "type": "contains", + "direction": "forward", + "weight": 1.0 + }, + { + "source": "file:mcp_server/server.py", + "target": "function:mcp_server/server.py:_update_bc", + "type": "contains", + "direction": "forward", + "weight": 1.0 + }, + { + "source": "file:mcp_server/server.py", + "target": "function:mcp_server/server.py:_map_ncit_to_bc", + "type": "contains", + "direction": "forward", + "weight": 1.0 + }, + { + "source": "file:mcp_server/server.py", + "target": "function:mcp_server/server.py:_submit_bc_for_review", + "type": "contains", + "direction": "forward", + "weight": 1.0 + }, + { + "source": "file:mcp_server/server.py", + "target": "function:mcp_server/server.py:_advance_governance", + "type": "contains", + "direction": "forward", + "weight": 1.0 + }, + { + "source": "file:mcp_server/server.py", + "target": "function:mcp_server/server.py:_reject_bc", + "type": "contains", + "direction": "forward", + "weight": 1.0 + }, + { + "source": "file:mcp_server/server.py", + "target": "function:mcp_server/server.py:main", + "type": "contains", + "direction": "forward", + "weight": 1.0 + }, + { + "source": "file:mcp_server/server.py", + "target": "function:mcp_server/server.py:_run", + "type": "contains", + "direction": "forward", + "weight": 1.0 + }, + { + "source": "file:models/specialization.py", + "target": "class:models/specialization.py:DatasetSpecialization", + "type": "contains", + "direction": "forward", + "weight": 1.0 + }, + { + "source": "file:tests/test_mcp_server.py", + "target": "class:tests/test_mcp_server.py:TestDispatch", + "type": "contains", + "direction": "forward", + "weight": 1.0 + }, + { + "source": "file:tests/test_mcp_server.py", + "target": "class:tests/test_mcp_server.py:TestListBcs", + "type": "contains", + "direction": "forward", + "weight": 1.0 + }, + { + "source": "file:tests/test_mcp_server.py", + "target": "class:tests/test_mcp_server.py:TestGetBc", + "type": "contains", + "direction": "forward", + "weight": 1.0 + }, + { + "source": "file:tests/test_mcp_server.py", + "target": "class:tests/test_mcp_server.py:TestExternalApiTools", + "type": "contains", + "direction": "forward", + "weight": 1.0 + }, + { + "source": "file:tests/test_mcp_server.py", + "target": "class:tests/test_mcp_server.py:TestCreateBc", + "type": "contains", + "direction": "forward", + "weight": 1.0 + }, + { + "source": "file:tests/test_mcp_server.py", + "target": "class:tests/test_mcp_server.py:TestUpdateBc", + "type": "contains", + "direction": "forward", + "weight": 1.0 + }, + { + "source": "file:tests/test_mcp_server.py", + "target": "class:tests/test_mcp_server.py:TestMapNcit", + "type": "contains", + "direction": "forward", + "weight": 1.0 + }, + { + "source": "file:tests/test_mcp_server.py", + "target": "class:tests/test_mcp_server.py:TestGovernanceWrites", + "type": "contains", + "direction": "forward", + "weight": 1.0 + }, + { + "source": "file:tests/test_mcp_server.py", + "target": "class:tests/test_mcp_server.py:TestReviewQueue", + "type": "contains", + "direction": "forward", + "weight": 1.0 + }, + { + "source": "file:tests/test_specializations_routes.py", + "target": "class:tests/test_specializations_routes.py:TestIndex", + "type": "contains", + "direction": "forward", + "weight": 1.0 + }, + { + "source": "file:tests/test_specializations_routes.py", + "target": "class:tests/test_specializations_routes.py:TestCreate", + "type": "contains", + "direction": "forward", + "weight": 1.0 + }, + { + "source": "file:tests/test_specializations_routes.py", + "target": "class:tests/test_specializations_routes.py:TestEditUpsert", + "type": "contains", + "direction": "forward", + "weight": 1.0 + }, + { + "source": "file:tests/test_specializations_routes.py", + "target": "class:tests/test_specializations_routes.py:TestDelete", + "type": "contains", + "direction": "forward", + "weight": 1.0 + }, + { + "source": "file:tests/test_specializations_routes.py", + "target": "class:tests/test_specializations_routes.py:TestDetail", + "type": "contains", + "direction": "forward", + "weight": 1.0 + }, + { + "source": "file:tests/test_specializations_routes.py", + "target": "class:tests/test_specializations_routes.py:TestLibraryDetail", + "type": "contains", + "direction": "forward", + "weight": 1.0 + }, + { + "source": "file:tests/test_specializations_routes.py", + "target": "class:tests/test_specializations_routes.py:TestGenerate", + "type": "contains", + "direction": "forward", + "weight": 1.0 + }, + { + "source": "file:tests/test_specializations_routes.py", + "target": "class:tests/test_specializations_routes.py:TestGenerateFromDec", + "type": "contains", + "direction": "forward", + "weight": 1.0 + }, + { + "source": "file:models/specialization.py", + "target": "class:models/specialization.py:DatasetSpecialization", + "type": "exports", + "direction": "forward", + "weight": 0.8 + }, + { + "source": "file:mcp_server/server.py", + "target": "function:mcp_server/server.py:_dispatch", + "type": "exports", + "direction": "forward", + "weight": 0.8 + }, + { + "source": "file:mcp_server/server.py", + "target": "function:mcp_server/server.py:main", + "type": "exports", + "direction": "forward", + "weight": 0.8 + }, + { + "source": "file:mcp_server/__main__.py", + "target": "function:mcp_server/server.py:main", + "type": "calls", + "direction": "forward", + "weight": 0.8 + }, + { + "source": "class:tests/test_mcp_server.py:TestDispatch", + "target": "function:mcp_server/server.py:_dispatch", + "type": "calls", + "direction": "forward", + "weight": 0.8 + }, + { + "source": "class:tests/test_mcp_server.py:TestListBcs", + "target": "function:mcp_server/server.py:_list_bcs", + "type": "calls", + "direction": "forward", + "weight": 0.8 + }, + { + "source": "class:tests/test_mcp_server.py:TestGetBc", + "target": "function:mcp_server/server.py:_get_bc", + "type": "calls", + "direction": "forward", + "weight": 0.8 + }, + { + "source": "class:tests/test_mcp_server.py:TestExternalApiTools", + "target": "function:mcp_server/server.py:_search_ncit", + "type": "calls", + "direction": "forward", + "weight": 0.8 + }, + { + "source": "class:tests/test_mcp_server.py:TestExternalApiTools", + "target": "function:mcp_server/server.py:_get_ncit_concept", + "type": "calls", + "direction": "forward", + "weight": 0.8 + }, + { + "source": "class:tests/test_mcp_server.py:TestExternalApiTools", + "target": "function:mcp_server/server.py:_search_loinc", + "type": "calls", + "direction": "forward", + "weight": 0.8 + }, + { + "source": "class:tests/test_mcp_server.py:TestExternalApiTools", + "target": "function:mcp_server/server.py:_search_cdisc_library", + "type": "calls", + "direction": "forward", + "weight": 0.8 + }, + { + "source": "class:tests/test_mcp_server.py:TestExternalApiTools", + "target": "function:mcp_server/server.py:_get_library_bc", + "type": "calls", + "direction": "forward", + "weight": 0.8 + }, + { + "source": "class:tests/test_mcp_server.py:TestCreateBc", + "target": "function:mcp_server/server.py:_create_bc", + "type": "calls", + "direction": "forward", + "weight": 0.8 + }, + { + "source": "class:tests/test_mcp_server.py:TestUpdateBc", + "target": "function:mcp_server/server.py:_update_bc", + "type": "calls", + "direction": "forward", + "weight": 0.8 + }, + { + "source": "class:tests/test_mcp_server.py:TestMapNcit", + "target": "function:mcp_server/server.py:_map_ncit_to_bc", + "type": "calls", + "direction": "forward", + "weight": 0.8 + }, + { + "source": "class:tests/test_mcp_server.py:TestGovernanceWrites", + "target": "function:mcp_server/server.py:_submit_bc_for_review", + "type": "calls", + "direction": "forward", + "weight": 0.8 + }, + { + "source": "class:tests/test_mcp_server.py:TestGovernanceWrites", + "target": "function:mcp_server/server.py:_advance_governance", + "type": "calls", + "direction": "forward", + "weight": 0.8 + }, + { + "source": "class:tests/test_mcp_server.py:TestGovernanceWrites", + "target": "function:mcp_server/server.py:_reject_bc", + "type": "calls", + "direction": "forward", + "weight": 0.8 + }, + { + "source": "class:tests/test_mcp_server.py:TestReviewQueue", + "target": "function:mcp_server/server.py:_list_review_queue", + "type": "calls", + "direction": "forward", + "weight": 0.8 + }, + { + "source": "file:mcp_server/server.py", + "target": "file:app.py", + "type": "depends_on", + "direction": "forward", + "weight": 0.6 + }, + { + "source": "file:mcp_server/server.py", + "target": "file:db_bootstrap.py", + "type": "depends_on", + "direction": "forward", + "weight": 0.6 + }, + { + "source": "file:mcp_server/server.py", + "target": "file:models/bc.py", + "type": "depends_on", + "direction": "forward", + "weight": 0.6 + }, + { + "source": "file:mcp_server/server.py", + "target": "file:models/governance.py", + "type": "depends_on", + "direction": "forward", + "weight": 0.6 + }, + { + "source": "file:mcp_server/server.py", + "target": "file:extensions.py", + "type": "depends_on", + "direction": "forward", + "weight": 0.6 + }, + { + "source": "file:mcp_server/server.py", + "target": "file:models/ingestion.py", + "type": "depends_on", + "direction": "forward", + "weight": 0.6 + }, + { + "source": "file:mcp_server/server.py", + "target": "file:services/ncit_api.py", + "type": "depends_on", + "direction": "forward", + "weight": 0.6 + }, + { + "source": "file:mcp_server/server.py", + "target": "file:services/loinc_api.py", + "type": "depends_on", + "direction": "forward", + "weight": 0.6 + }, + { + "source": "file:mcp_server/server.py", + "target": "file:services/cdisc_api.py", + "type": "depends_on", + "direction": "forward", + "weight": 0.6 + }, + { + "source": "file:mcp_server/server.py", + "target": "file:services/bc_service.py", + "type": "depends_on", + "direction": "forward", + "weight": 0.6 + }, + { + "source": "file:mcp_server/server.py", + "target": "file:services/governance_service.py", + "type": "depends_on", + "direction": "forward", + "weight": 0.6 + }, + { + "source": "file:mcp_server/server.py", + "target": "file:tests/test_mcp_server.py", + "type": "tested_by", + "direction": "forward", + "weight": 0.5 + }, + { + "source": "file:routes/specializations.py", + "target": "file:tests/test_specializations_routes.py", + "type": "tested_by", + "direction": "forward", + "weight": 0.5 + }, + { + "source": "document:.claude/agent-memory/cdisc-frontend-dev/MEMORY.md", + "target": "document:.claude/agent-memory/cdisc-frontend-dev/project_foundation.md", + "type": "related", + "direction": "forward", + "weight": 0.5 + }, + { + "source": "document:.claude/agent-memory/cdisc-frontend-dev/MEMORY.md", + "target": "document:.claude/agent-memory/cdisc-frontend-dev/frontend_architecture.md", + "type": "related", + "direction": "forward", + "weight": 0.5 + }, + { + "source": "document:.claude/agent-memory/cdisc-frontend-dev/frontend_architecture.md", + "target": "file:templates/base.html", + "type": "documents", + "direction": "forward", + "weight": 0.5 + }, + { + "source": "document:.claude/agent-memory/cdisc-frontend-dev/frontend_architecture.md", + "target": "file:static/js/main.js", + "type": "documents", + "direction": "forward", + "weight": 0.5 + }, + { + "source": "document:.claude/agent-memory/cdisc-frontend-dev/frontend_architecture.md", + "target": "file:static/css/custom.css", + "type": "documents", + "direction": "forward", + "weight": 0.5 + }, + { + "source": "document:.claude/agent-memory/cdisc-frontend-dev/project_foundation.md", + "target": "file:app.py", + "type": "documents", + "direction": "forward", + "weight": 0.5 + }, + { + "source": "document:.claude/agent-memory/cdisc-frontend-dev/project_foundation.md", + "target": "file:extensions.py", + "type": "documents", + "direction": "forward", + "weight": 0.5 + }, + { + "source": "document:.claude/agent-memory/cdisc-frontend-dev/project_foundation.md", + "target": "file:models/bc.py", + "type": "documents", + "direction": "forward", + "weight": 0.5 + }, + { + "source": "document:.claude/agent-memory/cdisc-frontend-dev/project_foundation.md", + "target": "file:models/specialization.py", + "type": "documents", + "direction": "forward", + "weight": 0.5 + }, + { + "source": "document:.claude/agent-memory/cdisc-frontend-dev/project_foundation.md", + "target": "file:models/governance.py", + "type": "documents", + "direction": "forward", + "weight": 0.5 + }, + { + "source": "document:.claude/agent-memory/cdisc-frontend-dev/project_foundation.md", + "target": "file:models/audit.py", + "type": "documents", + "direction": "forward", + "weight": 0.5 + }, + { + "source": "document:.claude/agent-memory/cdisc-frontend-dev/project_foundation.md", + "target": "file:models/ingestion.py", + "type": "documents", + "direction": "forward", + "weight": 0.5 + }, + { + "source": "document:.claude/agent-memory/cdisc-frontend-dev/project_foundation.md", + "target": "file:services/ingestion.py", + "type": "documents", + "direction": "forward", + "weight": 0.5 + }, + { + "source": "document:.claude/agent-memory/cdisc-frontend-dev/project_foundation.md", + "target": "file:services/ncit_api.py", + "type": "documents", + "direction": "forward", + "weight": 0.5 + }, + { + "source": "document:.claude/agent-memory/cdisc-frontend-dev/project_foundation.md", + "target": "file:services/loinc_api.py", + "type": "documents", + "direction": "forward", + "weight": 0.5 + }, + { + "source": "document:.claude/agent-memory/cdisc-frontend-dev/project_foundation.md", + "target": "file:services/export.py", + "type": "documents", + "direction": "forward", + "weight": 0.5 + }, + { + "source": "document:.claude/agent-memory/cdisc-frontend-dev/project_foundation.md", + "target": "file:services/cdisc_api.py", + "type": "documents", + "direction": "forward", + "weight": 0.5 + }, + { + "source": "document:.claude/agents/mike.md", + "target": "document:.claude/agents/cdisc-frontend-dev.md", + "type": "related", + "direction": "forward", + "weight": 0.5 + }, + { + "source": "document:.claude/agents/mike.md", + "target": "document:.claude/agents/cdisc-concept-explorer.md", + "type": "related", + "direction": "forward", + "weight": 0.5 + }, + { + "source": "document:README.md", + "target": "document:requirements.txt", + "type": "documents", + "direction": "forward", + "weight": 0.5 + }, + { + "source": "document:README.md", + "target": "config:.pre-commit-config.yaml", + "type": "documents", + "direction": "forward", + "weight": 0.5 + }, + { + "source": "document:README.md", + "target": "config:pyproject.toml", + "type": "documents", + "direction": "forward", + "weight": 0.5 + }, + { + "source": "config:.pre-commit-config.yaml", + "target": "config:pyproject.toml", + "type": "depends_on", + "direction": "forward", + "weight": 0.6 + }, + { + "source": "document:requirements-dev.txt", + "target": "document:requirements.txt", + "type": "related", + "direction": "forward", + "weight": 0.5 + }, + { + "source": "document:README.md", + "target": "document:CLAUDE.md", + "type": "related", + "direction": "forward", + "weight": 0.5 + }, + { + "source": "document:README.md", + "target": "document:README-PROGRESS.md", + "type": "related", + "direction": "forward", + "weight": 0.5 + }, + { + "source": "document:files/plan.md", + "target": "document:files/implementation.md", + "type": "related", + "direction": "forward", + "weight": 0.5 + }, + { + "source": "document:files/implementation.md", + "target": "file:app.py", + "type": "documents", + "direction": "forward", + "weight": 0.5 + }, + { + "source": "document:files/implementation.md", + "target": "file:config.py", + "type": "documents", + "direction": "forward", + "weight": 0.5 + }, + { + "source": "document:files/implementation.md", + "target": "file:models/bc.py", + "type": "documents", + "direction": "forward", + "weight": 0.5 + }, + { + "source": "document:files/implementation.md", + "target": "file:models/specialization.py", + "type": "documents", + "direction": "forward", + "weight": 0.5 + }, + { + "source": "document:files/implementation.md", + "target": "file:models/governance.py", + "type": "documents", + "direction": "forward", + "weight": 0.5 + }, + { + "source": "document:files/implementation.md", + "target": "file:models/audit.py", + "type": "documents", + "direction": "forward", + "weight": 0.5 + }, + { + "source": "document:files/implementation.md", + "target": "file:routes/dashboard.py", + "type": "documents", + "direction": "forward", + "weight": 0.5 + }, + { + "source": "document:files/implementation.md", + "target": "file:routes/ingestion.py", + "type": "documents", + "direction": "forward", + "weight": 0.5 + }, + { + "source": "document:files/implementation.md", + "target": "file:routes/bc.py", + "type": "documents", + "direction": "forward", + "weight": 0.5 + }, + { + "source": "document:files/implementation.md", + "target": "file:routes/ncit.py", + "type": "documents", + "direction": "forward", + "weight": 0.5 + }, + { + "source": "document:files/implementation.md", + "target": "file:routes/specializations.py", + "type": "documents", + "direction": "forward", + "weight": 0.5 + }, + { + "source": "document:files/implementation.md", + "target": "file:routes/governance.py", + "type": "documents", + "direction": "forward", + "weight": 0.5 + }, + { + "source": "document:files/implementation.md", + "target": "file:routes/audit.py", + "type": "documents", + "direction": "forward", + "weight": 0.5 + }, + { + "source": "document:files/implementation.md", + "target": "file:services/cdisc_api.py", + "type": "documents", + "direction": "forward", + "weight": 0.5 + }, + { + "source": "document:files/implementation.md", + "target": "file:services/ncit_api.py", + "type": "documents", + "direction": "forward", + "weight": 0.5 + }, + { + "source": "document:files/implementation.md", + "target": "file:services/ingestion.py", + "type": "documents", + "direction": "forward", + "weight": 0.5 + }, + { + "source": "document:files/implementation.md", + "target": "file:services/export.py", + "type": "documents", + "direction": "forward", + "weight": 0.5 + }, + { + "source": "document:files/implementation.md", + "target": "file:templates/base.html", + "type": "documents", + "direction": "forward", + "weight": 0.5 + }, + { + "source": "document:files/implementation.md", + "target": "document:files/plan.md", + "type": "related", + "direction": "forward", + "weight": 0.5 + }, + { + "source": "document:files/implementation.md", + "target": "document:files/result.md", + "type": "related", + "direction": "forward", + "weight": 0.5 + }, + { + "source": "document:files/result.md", + "target": "file:app.py", + "type": "documents", + "direction": "forward", + "weight": 0.5 + }, + { + "source": "document:files/result.md", + "target": "file:extensions.py", + "type": "documents", + "direction": "forward", + "weight": 0.5 + }, + { + "source": "document:files/result.md", + "target": "file:config.py", + "type": "documents", + "direction": "forward", + "weight": 0.5 + }, + { + "source": "document:files/result.md", + "target": "document:files/implementation.md", + "type": "related", + "direction": "forward", + "weight": 0.5 + }, + { + "source": "file:templates/audit.html", + "target": "file:templates/base.html", + "type": "depends_on", + "direction": "forward", + "weight": 0.6 + }, + { + "source": "file:templates/bc_detail.html", + "target": "file:templates/base.html", + "type": "depends_on", + "direction": "forward", + "weight": 0.6 + }, + { + "source": "file:templates/bc_list.html", + "target": "file:templates/base.html", + "type": "depends_on", + "direction": "forward", + "weight": 0.6 + }, + { + "source": "file:templates/dashboard.html", + "target": "file:templates/base.html", + "type": "depends_on", + "direction": "forward", + "weight": 0.6 + }, + { + "source": "file:templates/governance.html", + "target": "file:templates/base.html", + "type": "depends_on", + "direction": "forward", + "weight": 0.6 + }, + { + "source": "file:templates/ingestion.html", + "target": "file:templates/base.html", + "type": "depends_on", + "direction": "forward", + "weight": 0.6 + }, + { + "source": "file:templates/library_bc_detail.html", + "target": "file:templates/base.html", + "type": "depends_on", + "direction": "forward", + "weight": 0.6 + }, + { + "source": "file:templates/library_spec_detail.html", + "target": "file:templates/base.html", + "type": "depends_on", + "direction": "forward", + "weight": 0.6 + }, + { + "source": "file:templates/ncit_mapping.html", + "target": "file:templates/base.html", + "type": "depends_on", + "direction": "forward", + "weight": 0.6 + }, + { + "source": "file:templates/specializations.html", + "target": "file:templates/base.html", + "type": "depends_on", + "direction": "forward", + "weight": 0.6 + }, + { + "source": "file:.claude/skills/run-concept-curation/smoke.sh", + "target": "function:.claude/skills/run-concept-curation/smoke.sh:cleanup", + "type": "contains", + "direction": "forward", + "weight": 1.0 + }, + { + "source": "file:.claude/skills/run-concept-curation/smoke.sh", + "target": "function:.claude/skills/run-concept-curation/smoke.sh:check", + "type": "contains", + "direction": "forward", + "weight": 1.0 + }, + { + "source": "document:.claude/skills/run-concept-curation/SKILL.md", + "target": "file:.claude/skills/run-concept-curation/smoke.sh", + "type": "documents", + "direction": "forward", + "weight": 0.5 + }, + { + "source": "document:.claude/agent-memory/cdisc-concept-explorer/MEMORY.md", + "target": "document:.claude/agent-memory/cdisc-concept-explorer/reference_api_endpoints.md", + "type": "related", + "direction": "forward", + "weight": 0.5 + }, + { + "source": "config:migrations/alembic.ini", + "target": "file:migrations/env.py", + "type": "configures", + "direction": "forward", + "weight": 0.6 + }, + { + "source": "file:migrations/env.py", + "target": "function:migrations/env.py:get_engine", + "type": "contains", + "direction": "forward", + "weight": 1.0 + }, + { + "source": "file:migrations/env.py", + "target": "function:migrations/env.py:get_engine_url", + "type": "contains", + "direction": "forward", + "weight": 1.0 + }, + { + "source": "file:migrations/env.py", + "target": "function:migrations/env.py:run_migrations_offline", + "type": "contains", + "direction": "forward", + "weight": 1.0 + }, + { + "source": "file:migrations/env.py", + "target": "function:migrations/env.py:run_migrations_online", + "type": "contains", + "direction": "forward", + "weight": 1.0 + }, + { + "source": "file:migrations/env.py", + "target": "function:migrations/env.py:get_engine", + "type": "exports", + "direction": "forward", + "weight": 0.8 + }, + { + "source": "file:migrations/env.py", + "target": "function:migrations/env.py:get_engine_url", + "type": "exports", + "direction": "forward", + "weight": 0.8 + }, + { + "source": "file:migrations/env.py", + "target": "function:migrations/env.py:run_migrations_offline", + "type": "exports", + "direction": "forward", + "weight": 0.8 + }, + { + "source": "file:migrations/env.py", + "target": "function:migrations/env.py:run_migrations_online", + "type": "exports", + "direction": "forward", + "weight": 0.8 + }, + { + "source": "function:migrations/env.py:run_migrations_offline", + "target": "function:migrations/env.py:get_engine_url", + "type": "calls", + "direction": "forward", + "weight": 0.8 + }, + { + "source": "function:migrations/env.py:run_migrations_online", + "target": "function:migrations/env.py:get_engine", + "type": "calls", + "direction": "forward", + "weight": 0.8 + }, + { + "source": "function:migrations/env.py:get_engine_url", + "target": "function:migrations/env.py:get_engine", + "type": "calls", + "direction": "forward", + "weight": 0.8 + }, + { + "source": "file:migrations/script.py.mako", + "target": "file:migrations/versions/51d4a009d291_baseline_initial_schema.py", + "type": "related", + "direction": "forward", + "weight": 0.5 + }, + { + "source": "file:migrations/versions/51d4a009d291_baseline_initial_schema.py", + "target": "function:migrations/versions/51d4a009d291_baseline_initial_schema.py:upgrade", + "type": "contains", + "direction": "forward", + "weight": 1.0 + }, + { + "source": "file:migrations/versions/51d4a009d291_baseline_initial_schema.py", + "target": "function:migrations/versions/51d4a009d291_baseline_initial_schema.py:downgrade", + "type": "contains", + "direction": "forward", + "weight": 1.0 + }, + { + "source": "file:migrations/versions/51d4a009d291_baseline_initial_schema.py", + "target": "function:migrations/versions/51d4a009d291_baseline_initial_schema.py:upgrade", + "type": "exports", + "direction": "forward", + "weight": 0.8 + }, + { + "source": "file:migrations/versions/51d4a009d291_baseline_initial_schema.py", + "target": "function:migrations/versions/51d4a009d291_baseline_initial_schema.py:downgrade", + "type": "exports", + "direction": "forward", + "weight": 0.8 + }, + { + "source": "file:static/js/main.js", + "target": "function:static/js/main.js:initFileUpload", + "type": "contains", + "direction": "forward", + "weight": 1.0 + }, + { + "source": "file:static/js/main.js", + "target": "function:static/js/main.js:initNcitLookup", + "type": "contains", + "direction": "forward", + "weight": 1.0 + }, + { + "source": "file:static/js/main.js", + "target": "function:static/js/main.js:renderNcitResults", + "type": "contains", + "direction": "forward", + "weight": 1.0 + }, + { + "source": "file:static/js/main.js", + "target": "function:static/js/main.js:renderNcitMetaDisplay", + "type": "contains", + "direction": "forward", + "weight": 1.0 + }, + { + "source": "file:static/js/main.js", + "target": "function:static/js/main.js:initDecTable", + "type": "contains", + "direction": "forward", + "weight": 1.0 + }, + { + "source": "file:static/js/main.js", + "target": "function:static/js/main.js:buildDecRow", + "type": "contains", + "direction": "forward", + "weight": 1.0 + }, + { + "source": "file:static/js/main.js", + "target": "function:static/js/main.js:initKanban", + "type": "contains", + "direction": "forward", + "weight": 1.0 + }, + { + "source": "file:static/js/main.js", + "target": "function:static/js/main.js:initAuditDiff", + "type": "contains", + "direction": "forward", + "weight": 1.0 + }, + { + "source": "file:static/js/main.js", + "target": "function:static/js/main.js:initNcitSearch", + "type": "contains", + "direction": "forward", + "weight": 1.0 + }, + { + "source": "file:static/js/main.js", + "target": "function:static/js/main.js:renderNcitSearchResults", + "type": "contains", + "direction": "forward", + "weight": 1.0 + }, + { + "source": "file:static/js/main.js", + "target": "function:static/js/main.js:showInlineToast", + "type": "contains", + "direction": "forward", + "weight": 1.0 + }, + { + "source": "file:static/js/main.js", + "target": "function:static/js/main.js:getOrCreateToastContainer", + "type": "contains", + "direction": "forward", + "weight": 1.0 + }, + { + "source": "file:static/js/main.js", + "target": "function:static/js/main.js:initLoincLookup", + "type": "contains", + "direction": "forward", + "weight": 1.0 + }, + { + "source": "file:static/js/main.js", + "target": "function:static/js/main.js:renderLoincResults", + "type": "contains", + "direction": "forward", + "weight": 1.0 + }, + { + "source": "file:static/js/main.js", + "target": "function:static/js/main.js:renderLoincMetaDisplay", + "type": "contains", + "direction": "forward", + "weight": 1.0 + }, + { + "source": "file:static/js/main.js", + "target": "function:static/js/main.js:initPendingNcitConcept", + "type": "contains", + "direction": "forward", + "weight": 1.0 + }, + { + "source": "function:static/js/main.js:initNcitLookup", + "target": "function:static/js/main.js:renderNcitResults", + "type": "calls", + "direction": "forward", + "weight": 0.8 + }, + { + "source": "function:static/js/main.js:renderNcitResults", + "target": "function:static/js/main.js:renderNcitMetaDisplay", + "type": "calls", + "direction": "forward", + "weight": 0.8 + }, + { + "source": "function:static/js/main.js:initDecTable", + "target": "function:static/js/main.js:buildDecRow", + "type": "calls", + "direction": "forward", + "weight": 0.8 + }, + { + "source": "function:static/js/main.js:initKanban", + "target": "function:static/js/main.js:showInlineToast", + "type": "calls", + "direction": "forward", + "weight": 0.8 + }, + { + "source": "function:static/js/main.js:initNcitSearch", + "target": "function:static/js/main.js:renderNcitSearchResults", + "type": "calls", + "direction": "forward", + "weight": 0.8 + }, + { + "source": "function:static/js/main.js:showInlineToast", + "target": "function:static/js/main.js:getOrCreateToastContainer", + "type": "calls", + "direction": "forward", + "weight": 0.8 + }, + { + "source": "function:static/js/main.js:initPendingNcitConcept", + "target": "function:static/js/main.js:renderNcitMetaDisplay", + "type": "calls", + "direction": "forward", + "weight": 0.8 + }, + { + "source": "file:static/css/custom.css", + "target": "file:static/js/main.js", + "type": "related", + "direction": "forward", + "weight": 0.5 + }, + { + "source": "document:.claude/skills/code-review/SKILL.md", + "target": "file:mcp_server/__init__.py", + "type": "related", + "direction": "forward", + "weight": 0.5 + }, + { + "source": "document:files/plan.md", + "target": "file:files/cdisc_bc_ai_platform.html", + "type": "related", + "direction": "forward", + "weight": 0.5 + }, + { + "source": "document:files/plan.md", + "target": "file:files/cdisc_smart_goals_infographic.html", + "type": "related", + "direction": "forward", + "weight": 0.5 + }, + { + "source": "document:files/plan.md", + "target": "file:files/BC Curation Principles and Completion GLs.xlsx", + "type": "related", + "direction": "forward", + "weight": 0.5 + }, + { + "source": "document:files/plan.md", + "target": "file:files/BC Examples.xlsx", + "type": "related", + "direction": "forward", + "weight": 0.5 + }, + { + "source": "document:files/plan.md", + "target": "file:files/BC DEC Templates.xlsx", + "type": "related", + "direction": "forward", + "weight": 0.5 + }, + { + "source": "document:files/plan.md", + "target": "file:files/BC Governance.docx", + "type": "related", + "direction": "forward", + "weight": 0.5 + } + ], + "layers": [ + { + "id": "layer:api", + "name": "API Layer", + "description": "Flask blueprint route handlers (dashboard, ingestion, bc, ncit, loinc, specializations, governance, audit) plus the MCP server package that exposes the same read/write operations as a second tool-based API surface for AI agents.", + "nodeIds": [ + "file:routes/audit.py", + "file:routes/ingestion.py", + "file:routes/bc.py", + "file:routes/governance.py", + "file:routes/ncit.py", + "file:routes/dashboard.py", + "file:routes/loinc.py", + "file:routes/specializations.py", + "file:routes/__init__.py", + "file:mcp_server/__main__.py", + "file:mcp_server/server.py", + "file:mcp_server/__init__.py" + ] + }, + { + "id": "layer:service", + "name": "Service Layer", + "description": "Business logic and external API clients \u2014 BC/governance/audit orchestration, the ingestion parser and field mapper, the XLSX/JSON/ODM-XML exporter, and the CDISC Library, NCIt, and LOINC API clients (with in-memory caching).", + "nodeIds": [ + "file:services/ingestion.py", + "file:services/__init__.py", + "file:services/audit.py", + "file:services/bc_service.py", + "file:services/export.py", + "file:services/governance_service.py", + "file:services/ncit_api.py", + "file:services/api_cache.py", + "file:services/cdisc_api.py", + "file:services/loinc_api.py" + ] + }, + { + "id": "layer:data", + "name": "Data Layer", + "description": "SQLAlchemy ORM models (BiomedicalConcept, DataElementConcept, IngestionRecord, AuditLog, GovernanceRecord, Specialization) together with the Alembic migration environment and baseline schema revision that bring the SQLite database to head.", + "nodeIds": [ + "file:models/audit.py", + "file:models/bc.py", + "file:models/ingestion.py", + "file:models/governance.py", + "file:models/__init__.py", + "file:models/specialization.py", + "document:migrations/README", + "config:migrations/alembic.ini", + "file:migrations/env.py", + "file:migrations/script.py.mako", + "file:migrations/versions/51d4a009d291_baseline_initial_schema.py" + ] + }, + { + "id": "layer:ui", + "name": "UI Layer", + "description": "Jinja2 + Bootstrap 5 templates for every page (dashboard, ingestion, BC detail/list, governance Kanban, NCIt mapping, specializations, audit log) plus the shared CSS and JavaScript assets that render them in the browser.", + "nodeIds": [ + "file:templates/base.html", + "file:templates/audit.html", + "file:templates/bc_detail.html", + "file:templates/bc_list.html", + "file:templates/dashboard.html", + "file:templates/governance.html", + "file:templates/ingestion.html", + "file:templates/library_bc_detail.html", + "file:templates/library_spec_detail.html", + "file:templates/ncit_mapping.html", + "file:templates/specializations.html", + "file:static/css/custom.css", + "file:static/js/main.js" + ] + }, + { + "id": "layer:config", + "name": "Configuration & Application Setup", + "description": "The application factory, extension singletons, and DB-bootstrap entry points (app.py, extensions.py, db_bootstrap.py) alongside project-level configuration \u2014 config.py, dependency pins, lint/format tool config, and the MCP server registration.", + "nodeIds": [ + "file:app.py", + "file:db_bootstrap.py", + "file:extensions.py", + "file:config.py", + "config:.pre-commit-config.yaml", + "config:.mcp.json", + "config:pyproject.toml", + "document:requirements-dev.txt", + "document:requirements.txt", + "config:.flake8", + "config:.hintrc", + "config:.ua/.understandignore", + "config:.ua/config.json" + ] + }, + { + "id": "layer:test", + "name": "Test Layer", + "description": "The pytest suite covering every blueprint, service, and model, plus shared fixtures in conftest.py, run against the in-memory SQLite TestConfig database used by both CI and local development.", + "nodeIds": [ + "file:tests/conftest.py", + "file:tests/test_audit_routes.py", + "file:tests/test_bc_routes.py", + "file:tests/test_governance_routes.py", + "file:tests/test_ingestion_routes.py", + "file:tests/test_ingestion_service.py", + "file:tests/test_models.py", + "file:tests/test_export_service.py", + "file:tests/test_ncit.py", + "file:tests/test_cdisc_api_cache.py", + "file:tests/test_loinc.py", + "file:tests/test_dashboard.py", + "file:tests/test_db_bootstrap.py", + "file:tests/test_mcp_server.py", + "file:tests/test_specializations_routes.py", + "file:tests/__init__.py" + ] + }, + { + "id": "layer:documentation", + "name": "Documentation & Reference Materials", + "description": "Project-level documentation (CLAUDE.md, README, progress log) and the domain reference materials in files/ \u2014 BC curation principles, DEC templates, worked examples, governance guidance, and platform planning docs \u2014 that ground the curation workflow the app implements.", + "nodeIds": [ + "document:CLAUDE.md", + "document:README-PROGRESS.md", + "document:README.md", + "document:files/plan.md", + "document:files/implementation.md", + "document:files/result.md", + "file:files/BC Curation Principles and Completion GLs.xlsx", + "file:files/BC DEC Templates.xlsx", + "file:files/BC Examples.xlsx", + "file:files/BC Governance.docx", + "file:files/cdisc_bc_ai_platform.html", + "file:files/cdisc_smart_goals_infographic.html" + ] + }, + { + "id": "layer:dev-tooling", + "name": "Dev Tooling & CI/CD", + "description": "The GitHub Actions CI pipeline plus Claude Code agent configuration and memory (.claude/agents, .claude/agent-memory, .claude/skills) that automate linting, testing, and AI-assisted development for this repository.", + "nodeIds": [ + "pipeline:.github/workflows/ci.yml", + "document:.claude/agent-memory/cdisc-frontend-dev/MEMORY.md", + "document:.claude/agent-memory/cdisc-frontend-dev/frontend_architecture.md", + "document:.claude/agent-memory/cdisc-frontend-dev/project_foundation.md", + "document:.claude/agents/cdisc-concept-explorer.md", + "document:.claude/agents/cdisc-frontend-dev.md", + "document:.claude/agents/mike.md", + "document:.claude/agent-memory/cdisc-concept-explorer/MEMORY.md", + "document:.claude/agent-memory/cdisc-concept-explorer/reference_api_endpoints.md", + "document:.claude/skills/code-review/SKILL.md", + "document:.claude/skills/run-concept-curation/SKILL.md", + "file:.claude/skills/run-concept-curation/smoke.sh" + ] + } + ], + "tour": [ + { + "order": 1, + "title": "Project Overview", + "description": "The README explains the platform's purpose: replacing spreadsheet-and-email workflows for curating CDISC Biomedical Concepts (BCs) with a structured pipeline that goes from file ingestion through AI-assisted field mapping, SME review, governance approval, and publication to the CDISC Library. It documents prerequisites, installation, and the external CDISC/NCIt/LOINC APIs the app integrates with, giving you the map before you look at any code.", + "nodeIds": [ + "document:README.md" + ] + }, + { + "order": 2, + "title": "Application Entry Point", + "description": "app.py is the Flask application factory: it configures logging, initializes the shared database and migration extensions, registers all eight feature blueprints, and doubles as the dev-server entry point run via `python app.py`. Every other module you will see in this tour is wired together from this one file, making it the natural starting point for reading the codebase.", + "nodeIds": [ + "file:app.py" + ], + "languageLesson": "Flask's application-factory pattern (a `create_app()` function that builds and configures the app object) keeps extension setup and blueprint registration in one controlled place, which makes the app testable without import-time side effects." + }, + { + "order": 3, + "title": "Shared Extensions and Config", + "description": "extensions.py defines the shared SQLAlchemy `db` and Flask-Migrate `migrate` instances that every model and route imports from, avoiding the circular-import problems that arise when models import an app-bound `db` directly. config.py centralizes every environment-driven setting (secret key, database URI, CDISC/NCIt API bases, upload limits) that app.py loads at startup. Together they are the most depended-upon files in the project \u2014 extensions.py alone is imported by 30 other nodes.", + "nodeIds": [ + "file:extensions.py", + "file:config.py" + ], + "languageLesson": "Splitting shared singletons (db, migrate) into their own module \u2014 rather than defining them alongside the app or the models \u2014 is a common Flask idiom for breaking circular-import cycles between app.py and models/." + }, + { + "order": 4, + "title": "Database Bootstrapping and Migrations", + "description": "db_bootstrap.py brings the SQLite database to the current Alembic migration head on every startup, distinguishing fresh databases from legacy create_all() databases and pre-baseline databases that need stamping. The baseline migration and Alembic's env.py show how that head state is defined and applied \u2014 this is the schema that the models in the next step map onto.", + "nodeIds": [ + "file:db_bootstrap.py", + "config:migrations/alembic.ini", + "file:migrations/env.py", + "file:migrations/versions/51d4a009d291_baseline_initial_schema.py", + "document:migrations/README" + ], + "languageLesson": "Alembic migrations should be incremental and reversible: each revision file pairs an `upgrade()` with a `downgrade()`, letting the schema evolve forward or roll back without hand-editing the database." + }, + { + "order": 5, + "title": "Core Data Models", + "description": "These five SQLAlchemy models are the domain vocabulary of the entire application. BiomedicalConcept and DataElementConcept (models/bc.py) are the central entities being curated; IngestionRecord stages parsed upload rows before approval; GovernanceRecord tracks approval-workflow stage transitions; AuditLog records before/after JSON state for every change; and Specialization stores SDTM/CDASH variable groups. models/bc.py has the highest fan-in of any model (19), reflecting how central it is to routes and services alike.", + "nodeIds": [ + "file:models/bc.py", + "file:models/governance.py", + "file:models/ingestion.py", + "file:models/audit.py", + "file:models/specialization.py" + ] + }, + { + "order": 6, + "title": "API Layer: Flask Blueprints", + "description": "Building on the models from Step 5, these route modules are the HTTP surface of the app. routes/bc.py handles BC CRUD, NCIt/LOINC lookup, and export \u2014 it has the highest fan-out of any route module because it touches nearly every service. routes/ingestion.py drives the upload-to-queue workflow, routes/governance.py implements the Kanban approval board, and routes/dashboard.py aggregates KPI stats from both the local database and the live CDISC Library API.", + "nodeIds": [ + "file:routes/bc.py", + "file:routes/ingestion.py", + "file:routes/governance.py", + "file:routes/dashboard.py" + ], + "languageLesson": "Flask blueprints let each feature area register its own routes, templates, and static assets under a URL prefix, then get wired into the app in one place (app.py) \u2014 a lightweight alternative to a monolithic routes file." + }, + { + "order": 7, + "title": "Service Layer: Core Business Logic", + "description": "Routes stay thin by delegating to the service layer. services/bc_service.py implements creating, updating, and merging Biomedical Concepts, including NCIt remapping and review submission. services/governance_service.py implements the advance/reject transitions that move a BC through Provisional \u2192 SME Review \u2192 CDISC Approval \u2192 Published, and services/audit.py provides the single log_change() helper both of the others call to write immutable AuditLog rows for every mutation.", + "nodeIds": [ + "file:services/bc_service.py", + "file:services/governance_service.py", + "file:services/audit.py" + ] + }, + { + "order": 8, + "title": "The Ingestion Pipeline", + "description": "services/ingestion.py is where the AI-assisted field mapping mentioned in the README actually happens: it parses uploaded XLSX/CSV/JSON files, groups rows by BC ID to handle multi-row DECs, and uses fuzzy string matching to assign canonical field names with confidence scores before rows are staged as IngestionRecord entries (Step 5) for SME approval via routes/ingestion.py (Step 6). This module is the core differentiator that replaces the manual spreadsheet workflow the project was built to eliminate.", + "nodeIds": [ + "file:services/ingestion.py" + ], + "languageLesson": "Python's difflib.SequenceMatcher computes a similarity ratio between strings without any ML model \u2014 a lightweight way to fuzzy-match inconsistent spreadsheet column names against a canonical field vocabulary." + }, + { + "order": 9, + "title": "External API Clients", + "description": "These four modules wrap the platform's three external data sources. services/cdisc_api.py talks to the CDISC Library COSMOS API for BC/specialization lookups and publishing; services/ncit_api.py queries the NCI Thesaurus EVS for concept search; and services/loinc_api.py queries NLM's LOINC search API. All three share services/api_cache.py, a stale-tolerant in-memory cache that serves fresh data for 5 minutes and falls back to stale data for up to an hour if a refresh fails, keeping the UI responsive even when an upstream API is slow.", + "nodeIds": [ + "file:services/cdisc_api.py", + "file:services/ncit_api.py", + "file:services/loinc_api.py", + "file:services/api_cache.py" + ] + }, + { + "order": 10, + "title": "UI Layer: Templates and Styling", + "description": "Every page the routes from Step 6 render extends templates/base.html, the master Jinja2 layout providing sidebar navigation, the top bar, flash-message rendering, and named template blocks that individual pages fill in. static/css/custom.css defines the design tokens (colors, spacing) and component styles \u2014 cards, badges, nav items \u2014 that give those pages a consistent look across the whole app.", + "nodeIds": [ + "file:templates/base.html", + "file:static/css/custom.css" + ], + "languageLesson": "Jinja2 template inheritance (`{% extends %}` plus named `{% block %}` regions) lets every page share one layout while overriding only the content it needs, avoiding duplicated HTML across the eleven page templates." + }, + { + "order": 11, + "title": "The MCP Server: A Second API Surface", + "description": "Alongside the Flask HTTP routes, mcp_server/server.py exposes the same underlying data and write operations as 14 stdio-based MCP tools (8 read, 6 write) for AI agents, dispatching each call into a Flask app context so it shares the exact same SQLite database and service-layer code path as the web app \u2014 writes go through the same services/bc_service.py and services/governance_service.py from Step 7. mcp_server/__main__.py is the process entry point, invoked via `python -m mcp_server`, mirroring app.py's role for the web app in Step 2.", + "nodeIds": [ + "file:mcp_server/server.py", + "file:mcp_server/__main__.py" + ] + }, + { + "order": 12, + "title": "Quality Gates: CI and Pre-commit", + "description": "The GitHub Actions workflow runs on every push and pull request: it installs the project and dev dependencies, lints with isort/black/flake8, and runs the full pytest suite on Python 3.12 \u2014 the same commands documented for local development. .pre-commit-config.yaml enforces the identical checks locally before a commit is even created, so the same rules from Step 2 through Step 11's code all get verified automatically before they ever reach CI.", + "nodeIds": [ + "pipeline:.github/workflows/ci.yml", + "config:.pre-commit-config.yaml" + ], + "languageLesson": "GitHub Actions workflows are declared with `on` triggers, `jobs` (parallel by default), and `steps` (sequential within a job) \u2014 this project's single job runs lint and test steps sequentially on one Python version rather than a matrix build." + } + ] +} \ No newline at end of file diff --git a/.ua/meta.json b/.ua/meta.json new file mode 100644 index 0000000..b9c2732 --- /dev/null +++ b/.ua/meta.json @@ -0,0 +1,6 @@ +{ + "lastAnalyzedAt": "2026-08-04T08:52:00Z", + "gitCommitHash": "8df239f9988f09a9476e634ceb7cac80c29f72fe", + "version": "1.0.0", + "analyzedFiles": 97 +} \ No newline at end of file