diff --git a/src/dta2sdtm/README.md b/src/dta2sdtm/README.md index 7428fd3..6bea8af 100644 --- a/src/dta2sdtm/README.md +++ b/src/dta2sdtm/README.md @@ -5,35 +5,85 @@ makes the lab **Data Transfer Agreement (DTA)** a typed, validatable contract an it traceable from the upstream **USDM** protocol all the way down to **SDTM-LB**. The token *DTA* is overloaded. This model holds both senses — the governance -**Agreement** and the data **Transmission** that fulfils it — and joins them through a +**Agreement** and the data **Transfer** that fulfils it — and joins them through a single hub, the **BiomedicalConcept**. +**Schema version 0.2.0.** Built from the DTA MVP Inventory, which the workstream +confirmed as the DTA User Requirements. See [What changed in v0.2.0](#what-changed-in-v020). + ## The three layers | Layer | Purpose | Key classes | |-------|---------|-------------| -| **Agreement** | The governance contract (what data, by whom, in what format, mapped to which visits). Slot names mirror the DTA app's JSON keys so a serialized agreement validates as-is. | `DataTransferAgreement`, `Study`, `Party`, `TransferRequirements`, `VisitMapping`, `BcSelection` | +| **Agreement** | The governance contract: what data, by whom, in what format, mapped to which visits, comprising **which tests**, in **which dataset structure**. | `DataTransferAgreement`, `Study`, `BcSelection`, `TestSpecification`, `TransferDataset`, `Party`, `TransferRequirements`, `VisitMapping` | | **Semantic** | The bridge from a biomedical concept to concrete SDTM-LB variables, anchored to a real, citable CDISC COSMoS specialization. | `BiomedicalConcept`, `DatasetSpecialization`, `VariableSpecialization` | -| **Instance** | The nested lab transmission payload that fulfils the agreement. The LAB Tx Model's three parallel unit blocks (`PLR*`/`CVU*`/`SIU*`) collapse into one `Result` keyed by `unitSystem`. | `Transmission`, `Subject`, `Collection`, `Specimen`, `Panel`, `LabTestResult`, `Result` | +| **Instance** | The delivered payload: one flat record per lab result, typed from the inventory's agreed data structure. | `Transmission`, `TransferRecord` | `BiomedicalConcept` is the hub: USDM references it (`Activity.biomedicalConceptIds`), the -Agreement scopes it in (`BcSelection.bc_id` + the `is_used_by_dta` facet), and the payload -delivers it (`LabTestResult.specialization.biomedicalConcept`) en route to SDTM-LB. +Agreement scopes it in (`BcSelection.bc_id` + the `is_used_by_dta` facet, and per-test via +`TestSpecification.bc_id`), and the payload delivers it en route to SDTM-LB. + +## Agreement ↔ instance + +Contract-vs-fulfilment is modelled explicitly rather than merely asserted, at two levels: + +- **File level** — `Transmission.dta_version_ref` names the DTA document version a + delivery fulfils, so a received file traces back to the contract that authorised it. +- **Test level** — a **natural-key join**: `TestSpecification` (`test_panel_name` + + `data_provider_test_code`, falling back to `data_provider_test_name`) ↔ `TransferRecord` + (`lbpanel` + `ctestcd` / `ctest`). + +No surrogate key is used, deliberately: no real transfer file carries one, so a +`spec_id` slot would exist in the model and in no actual data. The name fallback is +needed because tests are not always coded — in the source inventory's own examples, the +flow-cytometry and IHC rows identify tests by name only. + +**Why both layers exist.** The SDTM-LB derivation is a function of +`(TransferRecord × TestSpecification)`. The payload carries the value; the agreement +carries the per-test facts no payload can — the unit conversion basis, the result form, +and the BC identity. `TestSpecification.data_type` in particular is what tells the +transform whether `LBORRES` may be cast to `LBSTRESN`; without it, a categorical result +such as a urinalysis colour is attempted as a number. + +## Design rule — permissive payload, strict agreement + +Enumerations bind on the **agreement** side, where the sponsor controls the values. +Payload slots a vendor populates (units, status, dates) stay unconstrained strings, so a +conformant-but-unanticipated file is not rejected at ingest. Discrepancies surface in the +transform, by comparing payload against agreement, rather than as a load failure. + +Two consequences worth knowing: + +- `UnitEnum` and `LbTestCdEnum` are **documented reference vocabularies, bound to no + slot**. Real transfers legitimately carry units outside any fixed subset. +- Only the variables the source structure types as numeric are typed numerically. That + is what keeps a categorical result from being coerced. -Every instance slot carries **`exact_mappings`** to its LAB Transmission Model variable -(identity — same element, renamed) and an **`sdtm_lb_target`** annotation to its SDTM-LB -variable (transformation target — deliberately *not* modeled as identity, because a -derivation happens). That distinction keeps the lineage honest in both directions. +## Lineage annotations + +`labtx:` `exact_mappings` point at LAB Transmission Model v2.0 variables (identity — same +element, renamed). `sdtm_lb_target` annotations point at SDTM-LB variables (transformation +target — deliberately *not* identity, because a derivation happens). `usdm_property` +annotations carry the upstream USDM attribute for agreement fields. That distinction keeps +the lineage honest in all three directions. + +LAB v2 is no longer the transfer structure, but the `labtx:` mappings are retained: it +remains the vocabulary the inventory's test specification maps every column back to, so it +is still a valid mapping target. + +Slots that are **project-defined rather than CDISC-registered** say so in their own +annotations (`transfer_structure_version`, `dta_version_ref`). Nothing is presented as +standard terminology unless it is. ## Files | File | Contents | |------|----------| -| `dta.linkml.yaml` | The schema — 20 classes, slots, and CT-bound enums (NCI EVS). | -| `agreement.example.yaml` | Agreement-layer instance. Validates with `-C DataTransferAgreement`. | +| `dta.linkml.yaml` | The schema — 19 classes, 191 slots, 15 enums. | +| `agreement.example.yaml` | Agreement-layer instance, incl. test specification and data structure. Validates with `-C DataTransferAgreement`. | | `dataset_specialization.example.yaml` | Semantic-layer instance. Validates with `-C DatasetSpecialization`. | -| `transmission.example.yaml` | Full instance payload (2 subjects, hematology + chemistry). Validates with `-C Transmission`. | -| `transmission_minimal.example.yaml` | Minimal instance payload showing a linked (HGB) and an unlinked (RBC, gap-case) analyte. | +| `transmission.example.yaml` | Full payload (2 subjects, hematology + chemistry, plus a categorical result and a not-done test). Validates with `-C Transmission`. | +| `transmission_minimal.example.yaml` | Minimal payload showing an agreed test and one absent from the agreement (gap case). | | `cosmos/hgbbld.specialization.yaml` | Vendored CDISC COSMoS Dataset Specialization for Hemoglobin (HGBBLD / C64848). | | `cosmos/PROVENANCE.md` | Source, retrieval, and re-derivation instructions for the vendored spec. | | `index.html` | The DTA app — a browser-only demo that derives an agreement from USDM + MVP metadata and exports the JSON the Agreement layer types. | @@ -51,6 +101,36 @@ linkml-validate -s dta.linkml.yaml -C Transmission transmission.exampl linkml-validate -s dta.linkml.yaml -C Transmission transmission_minimal.example.yaml ``` +## What changed in v0.2.0 + +| | v0.1.0 | v0.2.0 | +|---|---|---| +| Test-level agreement | *(none)* | `TestSpecification` | +| Declared file structure | *(none)* | `TransferDataset` / `TransferVariable` | +| Instance shape | nested LAB v2 graph: `Subject > Collection > Specimen > Panel > LabTestResult > Result` | flat `TransferRecord` (60 variables) | +| Agreement ↔ instance | asserted in prose | file-level ref + test-level natural-key join | +| USDM traceability | class-level on `Study` only | per-field on study, party, visit slots | +| Classes / enums | 21 / 19 | 19 / 15 | + +The instance change is a change of **transfer vocabulary**, not merely of shape: the +removed graph modelled LAB v2 variables (`LTVRSN`, `ACCSNID`, `SPECID`, `PLBTID`, +`PLRCRS`…), while `TransferRecord` models SDTM-shaped columns plus vendor passthrough +(`CTESTCD`, `CTEST`, `CUNIT`, `CSPEC`, `CMETHOD`, `CPANEL`) and ten `AUX` escape hatches. +The two share essentially only `STUDYID`. + +### Known limitations of the agreed transfer structure + +Two properties of the transfer structure itself — not of this model — constrain what SDTM +can be produced. Neither is worked around here, because inventing columns would +misrepresent the agreed structure: + +1. **No standardised result or unit.** Only the original result (`LBORRES` / `LBORRESU`) + is carried. `LBSTRESC` / `LBSTRESN` / `LBSTRESU` must therefore be derived from + `TestSpecification`, which makes a complete test specification load-bearing rather + than merely useful. +2. **No reference ranges.** There is no equivalent of `LBORNRLO`, `LBORNRHI` or + `LBNRIND`, so those SDTM variables are not derivable from a transfer alone. + ## License MIT, per this repository. diff --git a/src/dta2sdtm/agreement.example.yaml b/src/dta2sdtm/agreement.example.yaml index dc1438d..4348d83 100644 --- a/src/dta2sdtm/agreement.example.yaml +++ b/src/dta2sdtm/agreement.example.yaml @@ -114,3 +114,133 @@ timepoint_mapping: data_provider_timepoint_number: "T1" data_provider_timepoint_name: "PREDOSE" source: UI + +# ───────────────────────────────────────────────────────────────────────────── +# TEST SPECIFICATION (new in v0.2.0) — the agreed tests. +# Joined from the payload on (test_panel_name -> lbpanel, +# data_provider_test_code -> ctestcd), falling back to +# data_provider_test_name -> ctest where the vendor code is absent. +# +# Row 1 is the worked example from the DTA MVP Inventory's Test_Specification +# sheet verbatim. Rows 2-3 back the transmission.example.yaml payload. Row 4 shows a +# test identified by NAME ONLY — the flow-cytometry pattern, which is why the +# natural key needs a name fallback. +# ───────────────────────────────────────────────────────────────────────────── +test_specification: + - test_panel_name: Blood Differential + category: HEMATOLOGY + bc_id: C51950 + data_provider_test_code: WBC + data_provider_test_name: Leukocytes + data_provider_test_description: Leukocytes in Blood + test_name: Leukocytes + test_code: "620043" + method: HPLC + specimen: BLOOD + loinc_code: 26471-3 + data_type: numeric + reported_unit: "10^9/L" + conventional_unit: mcL + si_unit: mm3 + blinding_flag: true + agreement_status: Ordered + + - test_panel_name: HEMATOLOGY + category: HEMATOLOGY + bc_id: C64848 + data_provider_test_code: HGB + data_provider_test_name: Hemoglobin + test_name: Hemoglobin + test_code: HGB + method: HPLC + specimen: BLOOD + data_type: numeric + reported_unit: g/dL + conventional_unit: g/dL + si_unit: g/L + blinding_flag: false + agreement_status: Final + + # Categorical test: data_type is what stops LBORRES "STRAW" being cast to LBSTRESN. + - test_panel_name: URINALYSIS + category: URINALYSIS + bc_id: C51948 + data_provider_test_code: WBCU + data_provider_test_name: Color Urine + test_name: Color + test_code: COLOR + method: DIPSTICK + specimen: URINE + data_type: "" + result_values: CL_RESULT1 + blinding_flag: true + agreement_status: Final + + # Identified by NAME only — no vendor test code (flow cytometry pattern). + - test_panel_name: Revised T Cell Panel (V2_SP34-2) + category: HEMATOLOGY + bc_id: C51948 + data_provider_test_name: Naive_CD4_Th/CD3+ + test_name: Naive CD4 T Helper Cells + test_code: CD4NAIVE + method: FLOW CYTOMETRY + specimen: Cryo PBMC + data_type: numeric + reported_unit: "%" + blinding_flag: false + agreement_status: Received + +# ───────────────────────────────────────────────────────────────────────────── +# DATA STRUCTURE (new in v0.2.0) — the declared shape of the transferred file. +# Abridged: a representative sample of the 61 declared variables, chosen to cover +# each kind of column metadata (length, CT, core, mandatory, vendor naming). +# ───────────────────────────────────────────────────────────────────────────── +data_structure: + dataset_name: LB + dataset_description: Laboratory test results transfer dataset. + variables: + - variable_name: STUDYID + variable_label: Study Identifier + variable_description: Unique identifier for a study. + variable_format: text + variable_length: 40 + mandatory_variable_flag: true + mandatory_value: true + + - variable_name: DOMAIN + variable_label: Domain Abbreviation + variable_description: Two-character abbreviation for the domain. + variable_format: text + variable_length: 2 + controlled_terminology: DOMAIN + example_values: [LB, CP, MB] + core_variable: Mandatory/Conditional + mandatory_variable_flag: false + mandatory_value: false + + - variable_name: LBORRES + variable_label: Result or Finding in Original Units + variable_description: Original result of the measurement or finding, as reported to the sites. + variable_format: text + variable_length: 200 + mandatory_variable_flag: true + + - variable_name: VISITNUM + variable_label: Visit Number + variable_description: Clinical encounter number. Numeric version of VISIT, used for sorting. + variable_format: numeric + variable_length: 8 + + - variable_name: LBDTC + variable_label: Date/Time of Specimen Collection + variable_description: Specimen collection date or date and time, ISO 8601. + variable_format: datetime + variable_length: 19 + + - variable_name: CTESTCD + variable_label: External Vendor Test Code + variable_description: Populated with the vendor internal test code. + variable_format: text + variable_length: 200 + vendor_variable_name: CTESTCD + vendor_variable_label: Vendor Test Code diff --git a/src/dta2sdtm/dta.linkml.yaml b/src/dta2sdtm/dta.linkml.yaml index 5f10ddd..dbb4923 100644 --- a/src/dta2sdtm/dta.linkml.yaml +++ b/src/dta2sdtm/dta.linkml.yaml @@ -4,48 +4,64 @@ title: DTA Logical Data Model description: >- A logical data model (LDM) covering BOTH senses of "DTA" in the CDISC 360i lab flow. The token "DTA" is overloaded: the Data Transfer **Agreement** (the - governance contract) vs. the lab Data **Transmission** payload (the data that - fulfils it). This schema models both as related layers — contract vs. - fulfilment — joined through the shared BiomedicalConcept hub. Three layers: + governance contract) vs. the Data **Transfer** payload (the data that fulfils + it). This schema models both as related layers — contract vs. fulfilment — + joined through the shared BiomedicalConcept hub. Three layers: * AGREEMENT LAYER — DataTransferAgreement > Study, DtaVersion, BcSelection, - Party, TransferRequirements, VisitMapping, TimepointMapping, Source. The - Data Transfer Agreement document, prepopulated from an upstream USDM - protocol: WHAT data is promised, by whom, in what format, mapped to which - visits. Slot names use snake_case so a serialized agreement payload - validates directly with `-C DataTransferAgreement`. + TestSpecification, TransferDataset, Party, TransferRequirements, + VisitMapping, TimepointMapping, Source. The Data Transfer Agreement + document, prepopulated from an upstream USDM protocol: WHAT data is + promised, by whom, in what format, mapped to which visits, and — new in + v0.2.0 — WHICH TESTS arrive and in what dataset structure. Slot names use + snake_case so a serialized agreement payload validates directly with + `-C DataTransferAgreement`. * SEMANTIC LAYER — BiomedicalConcept, DatasetSpecialization, - VariableSpecialization. This is the linkage that connects an upstream USDM - protocol (which references Biomedical Concepts on its Activities) to the - downstream SDTM-LB dataset (which a Dataset Specialization projects each BC - into). BiomedicalConcept now also carries a faceted `classifications` block - (category / disease_area / is_used_by_form / is_used_by_dta), making the - BC<->DTA membership an explicit, queryable edge. - - * INSTANCE LAYER — Transmission > Subject > Collection > Specimen > Panel > - LabTestResult > Result. The actual nested transmission payload, normalized - so the three parallel unit blocks (PLR*/CVU*/SIU*) collapse to one `Result` - keyed by `unitSystem`. + VariableSpecialization. The linkage connecting an upstream USDM protocol + (which references Biomedical Concepts on its Activities) to the downstream + SDTM-LB dataset (which a Dataset Specialization projects each BC into). + + * INSTANCE LAYER — Transmission > TransferRecord. The delivered payload, one + flat record per lab result, typed from the DTA MVP Inventory's + Data_Structure sheet. BiomedicalConcept is the hub joining all three layers: USDM references it (Activity.biomedicalConceptIds), the Agreement scopes it in (is_used_by_dta + - BcSelection), and the payload delivers it - (LabTestResult.specialization.biomedicalConcept) en route to SDTM-LB. + BcSelection, and per-test via TestSpecification.bc_id), and the payload + delivers it. + + AGREEMENT <-> INSTANCE. Two edges make contract-vs-fulfilment real rather than + merely asserted: + - file level: Transmission.dta_version_ref names the DTA version a delivery + fulfils; + - test level: a natural-key join, TestSpecification (test_panel_name + + data_provider_test_code, falling back to data_provider_test_name) <-> + TransferRecord (lbpanel + ctestcd / ctest). No surrogate key is used: no + real transfer file carries one. Primary purpose: a typed *transformation source* for DTA -> SDTM-LB, kept - traceable in both directions (USDM upstream, SDTM-LB downstream). Each instance - slot carries `exact_mappings` to its LAB Transmission Model variable (identity) - and `annotations.sdtm_lb_target` to its SDTM-LB variable (transformation - target — deliberately NOT modeled as identity). + traceable in both directions (USDM upstream, SDTM-LB downstream). The SDTM + derivation is a function of (TransferRecord x TestSpecification): the payload + carries the value, the agreement carries the per-test facts no payload can + (unit conversion, result form, BC identity). + + DESIGN RULE — permissive payload, strict agreement. Enumerations are bound on + the agreement side, where the sponsor controls the values. Payload slots that + a vendor populates (units, status, dates) stay unconstrained strings so a + conformant-but-unanticipated file is not rejected at the door; discrepancies + surface in the transform by comparing payload against agreement. license: MIT -version: 0.1.0 +version: 0.2.0 # ────────────────────────────────────────────────────────────────────────────── # PREFIXES — so every CURIE used below resolves to a real URI: # ncit: enum `meaning` codelist bindings (NCI Thesaurus, e.g. ncit:C64848) -# labtx: `exact_mappings` to LAB Transmission Model v2.0 variables (identity) +# labtx: `exact_mappings` to LAB Transmission Model v2.0 variables (identity). +# Retained in v0.2.0: LAB v2 is no longer the transfer structure, but it +# remains the vocabulary the inventory's Test_Specification sheet maps +# every column back to, so it is still a valid mapping target. # dta: this schema's own namespace (default_prefix) # ────────────────────────────────────────────────────────────────────────────── prefixes: @@ -64,15 +80,15 @@ imports: classes: # ═══════════════════ AGREEMENT LAYER (the governance contract) ═══════════════════ - # The Data Transfer AGREEMENT document. Validate a serialized agreement with: + # Validate a serialized agreement with: # linkml-validate -s dta.linkml.yaml -C DataTransferAgreement .yaml DataTransferAgreement: description: >- The Data Transfer Agreement document: the governance contract that declares WHAT lab data will be transferred, by WHOM, in what FORMAT, mapped to which - VISITS. Root of the agreement graph; the contract that a Transmission (the - instance layer) later fulfils. + VISITS, comprising WHICH TESTS, in WHICH DATASET STRUCTURE. Root of the + agreement graph; the contract a Transmission later fulfils. slots: - app_name - source @@ -80,6 +96,8 @@ classes: - dta_version - data_types - data_type_details + - test_specification + - data_structure - parties - transfer - visit_mapping @@ -90,7 +108,7 @@ classes: range: Source inlined: true annotations: - fulfilled_by: "Transmission (instance layer) — agreement (promised) vs transmission (delivered)" + fulfilled_by: "Transmission (instance layer), named explicitly by Transmission.dta_version_ref" Study: description: >- @@ -107,6 +125,7 @@ classes: - summary annotations: usdm_source: "Study / StudyVersion / StudyDesign" + dta_section: 1 StudySummary: description: Roll-up counts of the USDM source the agreement was derived from. @@ -123,6 +142,9 @@ classes: slots: - version_number - version_date + - version_change_narrative + annotations: + dta_section: 3 BcSelection: description: >- @@ -138,6 +160,89 @@ classes: - facets annotations: joins_to: "BiomedicalConcept (bc_id -> nciCode) — agreement-scoped BC selection" + dta_section: 2 + + TestSpecification: + description: >- + One agreed TEST in the DTA test specification table: the per-test heart of the + agreement. Declares how a single test will arrive — vendor and standard codes, + method, specimen, LOINC, data type, the three unit representations, blinding — + before any data exists. + + Deliberately FLAT: the source sheet directs "There can be multiple test panels + listed in the same DTA test specification table. Therefore keep at record level + although it is at the grouping level in Lab v2." Panel name is a field here, not + a grouping class. + + This class carries the per-test facts the payload structurally cannot: the unit + conversion basis (reported vs conventional vs SI), the result form (`data_type`, + which tells the SDTM transform whether LBORRES is castable to LBSTRESN), and the + BC identity. The SDTM-LB derivation is a function of (TransferRecord x this). + slots: + - test_panel_name + - category + - subcategory + - bc_id + - data_provider_test_code + - data_provider_test_name + - data_provider_test_description + - test_name + - test_code + - method + - specimen + - loinc_code + - data_type + - display_format + - reported_unit + - result_values + - conventional_unit + - si_unit + - blinding_flag + - agreement_status + annotations: + source_sheet: "DTA_MVP_Inventory.xlsx / Test_Specification" + joins_to_instance: >- + TransferRecord on (test_panel_name -> lbpanel, data_provider_test_code -> + ctestcd), falling back to (test_panel_name -> lbpanel, + data_provider_test_name -> ctest) where the vendor code is absent. + joins_to_concept: "BiomedicalConcept (bc_id -> nciCode)" + + TransferDataset: + description: >- + The dataset structure the agreement PROMISES: the declared variable list of the + transferred file, with per-variable format, length, controlled terminology, + core/mandatory status and vendor-side naming. The agreement-side statement of + the structure that TransferRecord instances conform to. + slots: + - dataset_name + - dataset_description + - variables + annotations: + source_sheet: "DTA_MVP_Inventory.xlsx / Data_Structure" + conformed_to_by: TransferRecord + + TransferVariable: + description: >- + One declared variable of the TransferDataset: its label, description, format, + length, controlled terminology, value list, example values, core/mandatory + status, unblinded flag, and the vendor's own name/label/terminology for it. + slots: + - variable_name + - variable_label + - variable_description + - variable_format + - variable_length + - significant_digits + - controlled_terminology + - value_lists + - example_values + - core_variable + - mandatory_variable_flag + - mandatory_value + - variable_unblinded + - vendor_variable_name + - vendor_variable_label + - vendor_controlled_terminology Party: description: >- @@ -157,12 +262,25 @@ classes: source: description: Field-level provenance of this party's data (USDM / UI / both). range: ProvenanceEnum + role: + description: >- + Role of the assigned person within the organization. NOT bound to + StudyRoleEnum: the inventory's four values are recorded there as the + agreed vocabulary, but the list is open (it ends in a trailing separator), + USDM's AssignedPerson.jobTitle is free text, and real agreements carry + titles outside it. Rejecting an agreement over a job title would block a + transfer for no data-quality benefit. + annotations: + usdm_property: AssignedPerson.jobTitle + recommended_codelist: StudyRoleEnum + annotations: + dta_section: 4 TransferRequirements: description: >- The file-transfer mechanics agreed for the transmission: format, encoding, - delimiter, method, cumulative/incremental, environment, blinding, and the - provider/recipient folder names. + delimiter, method, cumulative/incremental, schedule, environment, blinding, + and the provider/recipient folder names. slots: - file_name - file_format @@ -170,17 +288,19 @@ classes: - delimiter - transmission_method - transmission_type + - transfer_schedule - transfer_environment - transfer_blinding - folder_name_provider - folder_name_recipient + annotations: + dta_section: 5 VisitMapping: description: >- One row of the protocol<->data-provider visit crosswalk. Protocol visit number/name are USDM-locked; the data-provider values are authored by the lab. - This crosswalk is what the DTA->SDTM transform uses to recover VISIT / VISITNUM - (cf. Collection.visit / Collection.visitnum). + This crosswalk is what the DTA->SDTM transform uses to recover VISIT / VISITNUM. slots: - protocol_visit_number - protocol_visit_name @@ -195,7 +315,8 @@ classes: "USDM + UI". range: ProvenanceEnum annotations: - sdtm_use: "recovers VISIT / VISITNUM; pairs with instance-layer Collection.visit/visitnum" + sdtm_use: "recovers VISIT / VISITNUM; pairs with TransferRecord.visit / .visitnum" + dta_section: 6 TimepointMapping: description: >- @@ -211,6 +332,8 @@ classes: source: description: Field-level provenance of the row (USDM / UI / both). range: ProvenanceEnum + annotations: + dta_section: 7 Source: description: >- @@ -290,106 +413,120 @@ classes: - mandatoryVariable - originType - # ════════════════════════ INSTANCE LAYER (the DTA payload) ════════════════════ + # ════════════════════════ INSTANCE LAYER (the delivered payload) ══════════════ Transmission: tree_root: true description: >- - File-level / Good Transmission Practice (GTP) header. One per transmitted - DTA file. Root of the instance graph. + File-level header for one transmitted DTA file, plus the records it carries. + + `dta_version_ref` is the agreement<->instance edge at file level: it names the + DTA document version this delivery fulfils, so a received file can always be + traced to the contract that authorized it. + + NOTE ON PROVENANCE: the Data_Structure sheet specifies dataset variables only + and declares no file-level metadata, so this header is not derived from it. + `igvrsn`, `fcrdtc`, `trssid` and `trstyp` are retained from the LAB + Transmission Model, where they are grounded; `transfer_structure_version` and + `dta_version_ref` are project-defined (see their own annotations) and are NOT + CDISC-registered variables. slots: - - ltvrsn + - transfer_structure_version - igvrsn - fcrdtc - trssid - trstyp - - subjects - - Subject: - description: A trial subject within a transmission (study/site/subject identity). - slots: - - studyid - - siteid - - scrnid - - asubjid - - adsex - - collections + - dta_version_ref + - records - Collection: + TransferRecord: description: >- - A specimen-collection event (LAB Tx "Visit Level": accession + kit). Groups - the specimens drawn at one accession. `visit`/`visitnum` are derived - conveniences, not part of the 60 LAB Tx variables. + THE GRAIN: one flat record per lab result, as agreed in the Data_Structure + sheet of the DTA MVP Inventory. Replaces the v0.1.0 nested LAB v2 graph + (Subject > Collection > Specimen > Panel > LabTestResult > Result). + + This is a change of transfer VOCABULARY, not merely of shape: the removed graph + modelled LAB v2 variables (LTVRSN, ACCSNID, SPECID, PLBTID, PLRCRS...); this + class models SDTM-shaped columns plus vendor passthrough (CTESTCD, CTEST, + CUNIT, CSPEC, CMETHOD, CPANEL) and ten AUX escape hatches. The two share + essentially only STUDYID. + + Per the schema's permissive-payload rule, vendor-populated slots (lborresu, + lbstat, lbdtc, all C* passthrough) are unconstrained strings. Only the four + variables the source sheet types as numeric are typed numerically — which is + what stops a categorical result such as "STRAW" being cast to LBSTRESN. slots: - - accsnid - - kitid - - kitdsc - - visit + - studyid + - domain + - usubjid + - subjid + - lbgrpid + - lbrefid + - lbtestcd + - lbtest + - lbtstdtl + - lbcat + - lbscat + - lborres + - lborresu + - lbrescat + - lbstat + - lbreasnd + - lbnam + - lbloinc + - lbspec + - lbspccnd + - lbmethod + - lbrunid + - lbanmeth + - lblloq - visitnum - - specimens - - Specimen: - description: A biological specimen received and tested by the performing lab. - slots: - - lddom - - specid - - rcvdtc - - spcoml - - spcomi - - agespc - - panels - - Panel: - description: >- - A test panel (e.g. HEMATOLOGY) plus the performing laboratory that ran it. - Groups the individual analyte results. - slots: - - pnlid - - tstpnl - - plbid - - plbnam - - plbtyp - - labTestResults - - LabTestResult: - description: >- - THE GRAIN: one measured analyte for one specimen. Carries the link to its - DatasetSpecialization (`specialization`) — the join that closes the - USDM<->DTA loop — plus 1..3 unit-system Results. - slots: - - plbtid - - plbtnm - - plbtds - - plbtin - - plbtct - - dtrtid - - dtrtnm - - dtrtct - - plbtts - - plbttc - - asydtc - - plrtrc - - alrtlv - - rrdtc - - rtrtyp - - specialization - - results - - Result: - description: >- - A result expressed in one unit system. The three parallel LAB Tx blocks - (PLR* original / CVU* conventional / SIU* SI) are normalized here into ONE - class keyed by `unitSystem`; the original variable names survive as aliases - and exact_mappings on each slot. - slots: - - unitSystem - - charResult - - numResult - - unit - - refRangeLow - - refRangeHigh - - refRangeDesc - - refRangeType + - visit + - lbdtc + - lbtpt + - lbtptnum + - lbcode + - lbdupnum + - lbtstcnd + - lbcndagt + - lbanstat + - lbpanel + - lbgate + - lbmrkstr + - lbclmeth + - lbtmthsn + - lbreagnt + - lbmthdds + - lbbdagnt + - lbrqual + - aux1 + - aux2 + - aux3 + - aux4 + - aux5 + - aux6 + - aux7 + - aux8 + - aux9 + - aux10 + - lbvrefid + - ctestcd + - ctest + - cunit + - cspec + - cmethod + - cpanel + annotations: + source_sheet: "DTA_MVP_Inventory.xlsx / Data_Structure" + conforms_to: TransferDataset + joins_to_agreement: >- + TestSpecification on (lbpanel -> test_panel_name, ctestcd -> + data_provider_test_code), falling back to ctest -> data_provider_test_name. + known_gap: >- + The Data_Structure sheet declares no per-record transaction type, so an + incremental transfer cannot express a record-level delete; only the + file-level TransferRequirements.transmission_type (Cumulative/Incremental) + and Transmission.trstyp are available. # ────────────────────────────────────────────────────────────────────────────── # SLOTS @@ -415,6 +552,15 @@ slots: range: BcSelection multivalued: true inlined_as_list: true + test_specification: + description: The agreed tests — one row per test in the DTA test specification table. + range: TestSpecification + multivalued: true + inlined_as_list: true + data_structure: + description: The declared structure of the transferred dataset. + range: TransferDataset + inlined: true parties: description: Parties included in the DTA signature table. range: Party @@ -440,10 +586,13 @@ slots: description: USDM study name (e.g. NCT number). study_identifier: description: Study identifier (USDM StudyIdentifier). + annotations: { usdm_property: Study.id } study_title: - description: Study / protocol title. + description: Study / protocol title. The original title, not an acronym. + annotations: { usdm_property: "StudyTitle.text (type = official)" } version_identifier: description: USDM study version identifier. + annotations: { usdm_property: StudyVersion.versionIdentifier } version_id: description: USDM StudyVersion id. design_id: @@ -466,12 +615,17 @@ slots: # ── DtaVersion ── version_number: { description: DTA document version number (e.g. 1.0). } version_date: { description: DTA document version date (ISO 8601). } + version_change_narrative: + description: Description of the change and the rationale for this DTA version. # ── BcSelection (data_type_details) ── key: description: Stable selection key ("||") from the lookup. bc_id: - description: NCI Thesaurus C-code of the concept (joins to BiomedicalConcept.nciCode). + description: >- + NCI Thesaurus C-code of the concept. Joins to BiomedicalConcept.nciCode. Used by + BcSelection (concept scope) and TestSpecification (per-test concept identity). + annotations: { usdm_property: "BiomedicalConceptCategory / BC Group" } short_name: description: Concept short name (e.g. "Leukocyte Count"). facets: @@ -480,16 +634,155 @@ slots: multivalued: true inlined_as_list: true + # ── TestSpecification (the agreed test table) ── + test_panel_name: + description: >- + Test panel name. Kept at record level per the source sheet, even though Lab v2 + treats panel as a grouping level. Part of the natural key to TransferRecord.lbpanel. + annotations: { mandatory: R, natural_key: true } + category: + description: SDTM category for the test (Lab v2 "Data Recipient Test Category"). + exact_mappings: [labtx:DTRTCT] + annotations: { mandatory: R, sdtm_lb_target: LBCAT } + subcategory: + description: Further categorization of the test (e.g. used for Flow Cytometry, Genetics). + annotations: { mandatory: O, sdtm_lb_target: LBSCAT } + data_provider_test_code: + description: The performing laboratory's own test code. Part of the natural key. + exact_mappings: [labtx:PLBTID] + annotations: { mandatory: R, natural_key: true, instance_slot: ctestcd } + data_provider_test_name: + description: >- + The performing laboratory's own test name. Natural-key fallback where the + vendor test code is absent — which it is for 9 of the 11 example rows in the + source sheet (flow cytometry and IHC panels name tests without coding them). + exact_mappings: [labtx:PLBTNM] + annotations: { mandatory: R, natural_key: fallback, instance_slot: ctest } + data_provider_test_description: + description: >- + Long text description of the test. Retained for traceability where the provider + test differs from the intended BC. + exact_mappings: [labtx:PLBTDS] + annotations: { mandatory: O } + test_name: + description: Standard (data recipient) test name. + exact_mappings: [labtx:DTRTNM] + annotations: { mandatory: R, sdtm_lb_target: LBTEST } + test_code: + description: Standard (data recipient) test identifier. + exact_mappings: [labtx:DTRTID] + annotations: { mandatory: R, sdtm_lb_target: LBTESTCD } + method: + description: Method of the test or examination (e.g. HPLC, DIPSTICK, FLOW CYTOMETRY, IHC). + annotations: { mandatory: R, sdtm_lb_target: LBMETHOD } + specimen: + description: Specimen type the test is performed on (e.g. BLOOD, URINE, Cryo PBMC). + annotations: { mandatory: R, sdtm_lb_target: LBSPEC } + loinc_code: + description: LOINC code uniquely identifying the lab test. + annotations: { mandatory: O, sdtm_lb_target: LBLOINC } + data_type: + description: >- + Declared form of the result (e.g. numeric, ""). THE KEY SDTM INPUT: + it tells the transform whether LBORRES can be cast to LBSTRESN. Without it a + categorical result such as a urinalysis colour is attempted as a number. + annotations: { mandatory: R, sdtm_use: "gates the LBSTRESC/LBSTRESN derivation" } + display_format: + description: Display format for the result. + annotations: { mandatory: O } + reported_unit: + description: >- + Unit the result is reported in by the data provider. Recommended source is CDISC + CT codelist UNIT (C71620); not bound as an enum because agreements legitimately + use units outside any fixed subset. + annotations: { mandatory: R, recommended_codelist: "UNIT (C71620)", sdtm_lb_target: LBORRESU } + result_values: + description: Permitted result values or the codelist name governing them. + annotations: { mandatory: O } + conventional_unit: + description: Conventional unit for the test. + annotations: { mandatory: R, recommended_codelist: "UNIT (C71620)" } + si_unit: + description: SI unit for the test. + annotations: { mandatory: R, recommended_codelist: "UNIT (C71620)", sdtm_lb_target: LBSTRESU } + blinding_flag: + description: Whether this test's results are blinded. + range: boolean + annotations: { mandatory: R } + agreement_status: + description: >- + Negotiation state of this test row between the two parties (observed examples: + Ordered, Final, Received). PROVISIONAL — TeamComments records that the source + column "Status" is to be renamed and its examples reworked; no codelist is bound + until the workstream specifies one. + annotations: { mandatory: O, source_column: Status, provisional: "name and value list pending" } + + # ── TransferDataset / TransferVariable (declared structure) ── + dataset_name: + description: Name of the transferred dataset. + dataset_description: + description: Description of the transferred dataset. + variables: + description: The declared variables of this dataset. + range: TransferVariable + multivalued: true + inlined_as_list: true + variable_name: + description: Variable name as it appears in the transferred file. + identifier: true + variable_label: + description: Variable label. + variable_description: + description: Description of what the variable holds. + variable_format: + description: Declared format (text, numeric, datetime). + variable_length: + description: Declared maximum length. + range: integer + significant_digits: + description: Significant digits, where numeric. + range: integer + controlled_terminology: + description: Controlled terminology governing the variable. + value_lists: + description: Permitted value list for the variable. + multivalued: true + example_values: + description: Example values for the variable. + multivalued: true + core_variable: + description: >- + Core designation suggested by the agreement (the sheet notes this is deliberately + not the SDTM core value). + mandatory_variable_flag: + description: Whether the variable itself must be present in the transfer. + range: boolean + mandatory_value: + description: Whether the variable must be populated. + range: boolean + variable_unblinded: + description: Whether the variable is unblinded. + range: boolean + vendor_variable_name: + description: The data provider's own name for this variable. + vendor_variable_label: + description: The data provider's own label for this variable. + vendor_controlled_terminology: + description: The data provider's own controlled terminology for this variable. + # ── Party ── id: description: Stable party id (e.g. StudyRole_11_Person_8). name: description: Party person name (blank for organization-only rows). + annotations: { usdm_property: AssignedPerson.name } organization: description: Party organization name. + annotations: { usdm_property: Organization.name } organization_type: description: Type of the party organization. range: OrganizationTypeEnum + annotations: { usdm_property: Organization.type } data_exchange_role: description: Whether the party sends (Data Provider) or receives (Data Recipient). range: DataExchangeRoleEnum @@ -505,15 +798,37 @@ slots: delimiter: { description: Field delimiter., range: DelimiterEnum } transmission_method: { description: Transmission method., range: TransmissionMethodEnum } transmission_type: { description: Cumulative vs incremental transfer., range: TransferModeEnum } + transfer_schedule: { description: Planned frequency of transfers. } transfer_environment: { description: Target environment., range: TransferEnvironmentEnum } - transfer_blinding: { description: Blinding status of the transfer., range: BlindingEnum } + transfer_blinding: + description: >- + Blinding status of the transfer as a whole. The inventory describes this as an + indicator with no controlled terminology; the three-value enum is retained + because it is a strict superset of the boolean reading, and because the source + note "Do we have a different attribute name for it?" is unresolved. + range: BlindingEnum folder_name_provider: { description: Data Provider folder name. } folder_name_recipient: { description: Data Recipient folder name. } # ── VisitMapping / TimepointMapping ── - protocol_visit_number: { description: Protocol visit number (USDM-locked). } - protocol_visit_name: { description: Protocol visit name (USDM-locked). } - data_provider_visit_number: { description: Data-provider visit number (lab-authored). } + protocol_visit_number: + description: Protocol visit number as used in the SoA (USDM-locked). + annotations: { usdm_property: Encounter.name } + protocol_visit_name: + description: Protocol visit name as used in the SoA (USDM-locked). + annotations: { usdm_property: Encounter.label } + data_provider_visit_number: + description: >- + Data-provider visit number (lab-authored). Kept as a STRING despite the + inventory giving Data Format = number: observed values are zero-padded codes + ("01", "02"), and integer typing silently discards the padding. This is an + identifier used to match the lab's own system, not a quantity — and it is the + crosswalk the DTA->SDTM transform uses to recover VISIT / VISITNUM, so a lossy + round-trip here breaks visit derivation. + annotations: + inventory_conflict: >- + DTA_Attributes row 32 specifies Data Format = number; modelled as string for + the reason above. Worth confirming with the workstream. data_provider_visit_name: { description: Data-provider visit name (lab-authored). } protocol_timepoint_number: { description: Protocol timepoint number. } protocol_timepoint_name: { description: Protocol timepoint name. } @@ -564,9 +879,6 @@ slots: required: true vlmGroupId: description: COSMoS VLM group id. - domain: - description: SDTM domain the specialization targets. - ifabsent: string(LB) sdtmigStartVersion: description: First SDTMIG version this specialization applies to (e.g. 3-2). packageDate: @@ -588,7 +900,8 @@ slots: role: description: >- Role label. On a VariableSpecialization this is the SDTM role (Topic, - Qualifier, Timing, ...); on a Party this is the study role (e.g. "Study Chair"). + Qualifier, Timing, ...); on a Party it is the study role, narrowed to + StudyRoleEnum by slot_usage. assignedValue: description: Fixed/assigned value, if the specialization pins one (e.g. LBTESTCD=HGB). codelistCode: @@ -608,331 +921,359 @@ slots: originType: description: SDTM origin (Assigned, Collected, Derived, ...). - # ── Transmission (GTP) ── - ltvrsn: - description: LAB Transmission Model Version. - required: true - aliases: [LTVRSN] - exact_mappings: [labtx:LTVRSN] - annotations: { lab_tx_var: LTVRSN, lab_tx_group: "Good Transmission Practice (GTP)", cdisc_core: Req, sdtm_lb_target: "(none — file metadata)" } + # ── Transmission (file header) ── + transfer_structure_version: + description: >- + Version of the agreed transfer data structure this file conforms to. PROJECT- + DEFINED, not a CDISC-registered variable: it replaces the LAB v2 LTVRSN, which + became incoherent once the transfer structure ceased to be the LAB Transmission + Model. Retained because vendor and sponsor must agree on which structure version + a file follows for a transfer to be machine-processable. + annotations: { provenance: project-defined, replaces: "labtx:LTVRSN" } igvrsn: description: SDTMIG Version. aliases: [IGVRSN] exact_mappings: [labtx:IGVRSN] - annotations: { lab_tx_var: IGVRSN, cdisc_core: Perm, sdtm_lb_target: "(define-xml metadata)" } + annotations: { lab_tx_var: IGVRSN, cdisc_core: Perm } fcrdtc: description: File Creation Date/Time (ISO 8601). required: true aliases: [FCRDTC] exact_mappings: [labtx:FCRDTC] - annotations: { lab_tx_var: FCRDTC, cdisc_core: Req, sdtm_lb_target: "(none)" } + annotations: { lab_tx_var: FCRDTC, cdisc_core: Req } trssid: description: Transmission Source ID (the sending organization). required: true aliases: [TRSSID] exact_mappings: [labtx:TRSSID] - annotations: { lab_tx_var: TRSSID, cdisc_core: Req, sdtm_lb_target: "(provenance)" } + annotations: { lab_tx_var: TRSSID, cdisc_core: Req } trstyp: - description: Transmission Type. + description: Transmission Type (initial / update / delete). range: TransmissionTypeEnum aliases: [TRSTYP] exact_mappings: [labtx:TRSTYP] annotations: { lab_tx_var: TRSTYP, cdisc_core: Perm } - subjects: - description: The trial subjects carried in this transmission. - range: Subject + dta_version_ref: + description: >- + The DTA document version this transmission fulfils, matching + DataTransferAgreement.dta_version.version_number. THE AGREEMENT<->INSTANCE EDGE + AT FILE LEVEL: it lets a received delivery be traced to the contract that + authorized it. PROJECT-DEFINED, not a CDISC-registered variable. Carried once + per file rather than per record, so a real transfer file can populate it — + unlike a per-row specification key, which no transfer format carries. + annotations: { provenance: project-defined, references: "DtaVersion.version_number" } + records: + description: The lab result records carried in this transmission. + range: TransferRecord multivalued: true inlined_as_list: true - # ── Subject ── + # ── TransferRecord (generated from DTA_MVP_Inventory / Data_Structure) ── studyid: - description: Study Identifier. - required: true + description: Unique identifier for a study. aliases: [STUDYID] - exact_mappings: [labtx:STUDYID] - annotations: { lab_tx_var: STUDYID, lab_tx_group: "Study Level", sdtm_lb_target: STUDYID, usdm_property: "Study.versions[].studyIdentifiers", cdisc_core: Req } - siteid: - description: Study Site Identifier. - aliases: [SITEID] - exact_mappings: [labtx:SITEID] - annotations: { lab_tx_var: SITEID, lab_tx_group: "Site Level", sdtm_lb_target: SITEID } - scrnid: - description: Screening ID. - aliases: [SCRNID] - exact_mappings: [labtx:SCRNID] - annotations: { lab_tx_var: SCRNID, lab_tx_group: "Subject Level", cdisc_core: Perm } - asubjid: - description: Alternate Subject ID for the Study. - aliases: [ASUBJID] - exact_mappings: [labtx:ASUBJID] - annotations: { lab_tx_var: ASUBJID, sdtm_lb_target: "USUBJID/SUBJID (after harmonization)", cdisc_core: Perm } - adsex: - description: Administrative Sex. - range: AdministrativeSexEnum - aliases: [ADSEX] - exact_mappings: [labtx:ADSEX] - annotations: { lab_tx_var: ADSEX, sdtm_lb_target: "(DM.SEX)", cdisc_core: Exp } - collections: - description: The specimen-collection events for this subject. - range: Collection - multivalued: true - inlined_as_list: true - - # ── Collection (Visit Level) ── - accsnid: - description: Accession ID (the lab's identifier for the collection event). - required: true - aliases: [ACCSNID] - exact_mappings: [labtx:ACCSNID] - annotations: { lab_tx_var: ACCSNID, lab_tx_group: "Visit Level", sdtm_lb_target: "LBREFID/LBSPID", cdisc_core: Req } - kitid: - description: Kit ID. - aliases: [KITID] - exact_mappings: [labtx:KITID] - annotations: { lab_tx_var: KITID, cdisc_core: Perm } - kitdsc: - description: Kit Description. - aliases: [KITDSC] - exact_mappings: [labtx:KITDSC] - annotations: { lab_tx_var: KITDSC, cdisc_core: Exp } - visit: - description: Derived visit name (not a LAB Tx variable; SDTM convenience). - annotations: { lab_tx_var: "(derived)", sdtm_lb_target: VISIT } - visitnum: - description: Derived visit number (not a LAB Tx variable; SDTM convenience). - range: integer - annotations: { lab_tx_var: "(derived)", sdtm_lb_target: VISITNUM } - specimens: - description: The biological specimens drawn at this collection event. - range: Specimen - multivalued: true - inlined_as_list: true - - # ── Specimen ── - lddom: - description: Laboratory Data Domain. + annotations: + transfer_var: STUDYID + transfer_format: text + transfer_length: 40 + usdm_property: "Study.versions[].studyIdentifiers" + sdtm_lb_target: STUDYID + domain: + description: >- + SDTM domain abbreviation. On a DatasetSpecialization, the domain the + specialization targets; on a TransferRecord, the DOMAIN column of the + transferred dataset (two-character abbreviation). ifabsent: string(LB) - aliases: [LDDOM] - exact_mappings: [labtx:LDDOM] - annotations: { lab_tx_var: LDDOM, lab_tx_group: "Record Type Level", sdtm_lb_target: DOMAIN, cdisc_core: Perm } - specid: - description: Specimen ID. - required: true - aliases: [SPECID] - exact_mappings: [labtx:SPECID] - annotations: { lab_tx_var: SPECID, lab_tx_group: "Specimen Level", sdtm_lb_target: "LBREFID/LBSPID", cdisc_core: Req } - rcvdtc: - description: Specimen Receipt Date/Time. - aliases: [RCVDTC] - exact_mappings: [labtx:RCVDTC] - annotations: { lab_tx_var: RCVDTC, sdtm_lb_target: "(supp/LBDTC context)", cdisc_core: Perm } - spcoml: - description: Specimen Comment From Lab. - aliases: [SPCOML] - exact_mappings: [labtx:SPCOML] - annotations: { lab_tx_var: SPCOML, sdtm_lb_target: "CO/SUPPLB", cdisc_core: Perm } - spcomi: - description: Specimen Comment From Investigator. - aliases: [SPCOMI] - exact_mappings: [labtx:SPCOMI] - annotations: { lab_tx_var: SPCOMI, sdtm_lb_target: "CO/SUPPLB", cdisc_core: Perm } - agespc: - description: Subject Age at Specimen Collection. - range: integer - aliases: [AGESPC] - exact_mappings: [labtx:AGESPC] - annotations: { lab_tx_var: AGESPC, sdtm_lb_target: "(DM.AGE context)", cdisc_core: Exp } - panels: - description: The test panels run on this specimen. - range: Panel - multivalued: true - inlined_as_list: true - - # ── Panel + performing lab ── - pnlid: - description: Test Panel ID. - aliases: [PNLID] - exact_mappings: [labtx:PNLID] - annotations: { lab_tx_var: PNLID, lab_tx_group: "Panel Level", cdisc_core: Exp } - tstpnl: - description: Test Panel Name. - aliases: [TSTPNL] - exact_mappings: [labtx:TSTPNL] - annotations: { lab_tx_var: TSTPNL, sdtm_lb_target: "LBCAT/LBSCAT", cdisc_core: Exp } - plbid: - description: Performing Laboratory ID. - aliases: [PLBID] - exact_mappings: [labtx:PLBID] - annotations: { lab_tx_var: PLBID, lab_tx_group: "Test Level", sdtm_lb_target: "LBNAM (id)", cdisc_core: Perm } - plbnam: - description: Performing Laboratory Name. - aliases: [PLBNAM] - exact_mappings: [labtx:PLBNAM] - annotations: { lab_tx_var: PLBNAM, sdtm_lb_target: LBNAM, cdisc_core: Exp } - plbtyp: - description: Performing Laboratory Type. - aliases: [PLBTYP] - exact_mappings: [labtx:PLBTYP] - annotations: { lab_tx_var: PLBTYP, cdisc_core: Perm } - labTestResults: - description: The individual analyte results measured for this panel. - range: LabTestResult - multivalued: true - inlined_as_list: true - - # ── LabTestResult (grain) ── - plbtid: - description: Performing Laboratory Test ID (vendor test code). - aliases: [PLBTID] - exact_mappings: [labtx:PLBTID] - annotations: { lab_tx_var: PLBTID, lab_tx_group: "Test Level", sdtm_lb_target: "LBTESTCD (after mapping)", cdisc_core: Perm } - plbtnm: - description: Performing Laboratory Test Name. - required: true - aliases: [PLBTNM] - exact_mappings: [labtx:PLBTNM] - annotations: { lab_tx_var: PLBTNM, sdtm_lb_target: "LBTEST (after mapping)", cdisc_core: Req } - plbtds: - description: Performing Laboratory Test Description. - aliases: [PLBTDS] - exact_mappings: [labtx:PLBTDS] - annotations: { lab_tx_var: PLBTDS, cdisc_core: Exp } - plbtin: - description: Performing Laboratory Test Information. - aliases: [PLBTIN] - exact_mappings: [labtx:PLBTIN] - annotations: { lab_tx_var: PLBTIN, cdisc_core: Perm } - plbtct: - description: Performing Laboratory Test Category. - aliases: [PLBTCT] - exact_mappings: [labtx:PLBTCT] - annotations: { lab_tx_var: PLBTCT, sdtm_lb_target: LBCAT, cdisc_core: Perm } - dtrtid: - description: Data Recipient Test ID. - aliases: [DTRTID] - exact_mappings: [labtx:DTRTID] - annotations: { lab_tx_var: DTRTID, sdtm_lb_target: LBTESTCD, cdisc_core: Perm } - dtrtnm: - description: Data Recipient Test Name. - aliases: [DTRTNM] - exact_mappings: [labtx:DTRTNM] - annotations: { lab_tx_var: DTRTNM, sdtm_lb_target: LBTEST, cdisc_core: Perm } - dtrtct: - description: Data Recipient Test Category. - aliases: [DTRTCT] - exact_mappings: [labtx:DTRTCT] - annotations: { lab_tx_var: DTRTCT, sdtm_lb_target: LBCAT, cdisc_core: Perm } - plbtts: - description: Performing Laboratory Test Tracking Status. - aliases: [PLBTTS] - exact_mappings: [labtx:PLBTTS] - annotations: { lab_tx_var: PLBTTS, sdtm_lb_target: LBSTAT, cdisc_core: Exp } - plbttc: - description: Performing Laboratory Test Tracking Status Code. - range: ResultStatusEnum - aliases: [PLBTTC] - exact_mappings: [labtx:PLBTTC] - annotations: { lab_tx_var: PLBTTC, sdtm_lb_target: "LBSTAT/LBREASND", cdisc_core: Perm } - asydtc: - description: Assay Date/Time. - aliases: [ASYDTC] - exact_mappings: [labtx:ASYDTC] - annotations: { lab_tx_var: ASYDTC, sdtm_lb_target: "(LBDTC context)", cdisc_core: Exp } - plrtrc: - description: Perf Lab Reported Test Result Comment. - aliases: [PLRTRC] - exact_mappings: [labtx:PLRTRC] - annotations: { lab_tx_var: PLRTRC, sdtm_lb_target: "CO/SUPPLB", cdisc_core: Perm } - alrtlv: - description: Alert Level (flag for clinically significant / out-of-range results). - aliases: [ALRTLV] - exact_mappings: [labtx:ALRTLV] - annotations: { lab_tx_var: ALRTLV, sdtm_lb_target: "LBNRIND (informs)", cdisc_core: Exp } - rrdtc: - description: Reported Result Date/Time. - aliases: [RRDTC] - exact_mappings: [labtx:RRDTC] - annotations: { lab_tx_var: RRDTC, cdisc_core: Perm } - rtrtyp: - description: Record Transaction Type. - range: RecordTransactionTypeEnum - aliases: [RTRTYP] - exact_mappings: [labtx:RTRTYP] - annotations: { lab_tx_var: RTRTYP, cdisc_core: Perm } - specialization: - description: >- - The DatasetSpecialization this result instantiates. THIS REFERENCE IS THE - USDM<->DTA LINKAGE: specialization.biomedicalConcept is the BC that a USDM - Activity referenced via biomedicalConceptIds. - range: DatasetSpecialization - annotations: { linkage: "LabTestResult.specialization -> DatasetSpecialization.biomedicalConcept -> USDM Activity.biomedicalConceptIds" } - results: - description: 1..3 unit-system representations of this analyte's result. - range: Result - multivalued: true - inlined_as_list: true - required: true - - # ── Result (normalized unit block) ── - unitSystem: - description: Which unit system this Result is expressed in. - range: UnitSystemEnum - required: true - charResult: - description: Character result in this unit system. - aliases: [PLRCRS, CVUCRS, SIUCRS] - exact_mappings: [labtx:PLRCRS, labtx:CVUCRS, labtx:SIUCRS] + aliases: [DOMAIN] annotations: - lab_tx_var: "PLRCRS (ORIGINAL) / CVUCRS (CONVENTIONAL) / SIUCRS (SI)" - lab_tx_group: "Result Level" - sdtm_lb_target: "LBORRES (ORIGINAL) / LBSTRESC (CONVENTIONAL|SI)" - numResult: - description: Numeric result in this unit system. + transfer_var: DOMAIN + transfer_format: text + transfer_length: 2 + controlled_terminology: DOMAIN + core_variable: "Mandatory/Conditional" + sdtm_lb_target: DOMAIN + usubjid: + description: Identifier used to uniquely identify a subject. + aliases: [USUBJID] + annotations: { transfer_var: USUBJID, transfer_format: text, transfer_length: 60 } + subjid: + description: >- + Populated with the subject ID. Can be used to store a screening number if different from USUBJID. + aliases: [SUBJID] + annotations: { transfer_var: SUBJID, transfer_format: text, transfer_length: 30 } + lbgrpid: + description: >- + Optional group identifier, used to link together a block of related records within a subject in a domain. + aliases: [LBGRPID] + annotations: { transfer_var: LBGRPID, transfer_format: text, transfer_length: 30 } + lbrefid: + description: >- + Internal or external identifier, such as the sample ID for a subject, from which a lab test result was generated. + aliases: [LBREFID] + annotations: { transfer_var: LBREFID, transfer_format: text, transfer_length: 25 } + lbtestcd: + description: >- + Short name of the measurement, test, or examination described in LBTEST. + aliases: [LBTESTCD] + annotations: { transfer_var: LBTESTCD, transfer_format: text, transfer_length: 8 } + lbtest: + description: >- + Name of the test or examination used to obtain the measurement or finding. + aliases: [LBTEST] + annotations: { transfer_var: LBTEST, transfer_format: text, transfer_length: 40 } + lbtstdtl: + description: >- + Further description of the lab test (LBTESTCD and LBTEST). Example values: DETECTION, IDENTIFICATION, TITRATION, QUANTIFICATION, NORMALITY IMPRESSION + aliases: [LBTSTDTL] + annotations: { transfer_var: LBTSTDTL, transfer_format: text, transfer_length: 200 } + lbcat: + description: Used to define a category of topic-variable values. + aliases: [LBCAT] + annotations: { transfer_var: LBCAT, transfer_format: text, transfer_length: 200 } + lbscat: + description: Used to define a further categorization of LBCAT values. + aliases: [LBSCAT] + annotations: { transfer_var: LBSCAT, transfer_format: text, transfer_length: 200 } + lborres: + description: >- + Original result of the measurement or finding. This holds the result as reported to the sites. + aliases: [LBORRES] + annotations: { transfer_var: LBORRES, transfer_format: text, transfer_length: 200 } + lborresu: + description: >- + Original unit of the measurement. This holds the unit as reported to the sites. + aliases: [LBORRESU] + annotations: { transfer_var: LBORRESU, transfer_format: text, transfer_length: 50 } + lbrescat: + description: >- + Used to group or categorize the results of an assessment (e.g., based on pre-defined categories, scale, and/or cut-off values). + aliases: [LBRESCAT] + annotations: { transfer_var: LBRESCAT, transfer_format: text, transfer_length: 100 } + lbstat: + description: >- + Used to indicate that a test was not done or a test was attempted but did not generate a result (e.g., test failed, test not performed due to sampl... + aliases: [LBSTAT] + annotations: { transfer_var: LBSTAT, transfer_format: text, transfer_length: 8 } + lbreasnd: + description: >- + Describes the reason why the test was not done or did not generate a result (i.e., when LBSTAT is NOT DONE). + aliases: [LBREASND] + annotations: { transfer_var: LBREASND, transfer_format: text, transfer_length: 200 } + lbnam: + description: >- + Name or identifier of the lab vendor that provided the test results. If an additional reference lab has been used for any given test(s) that will b... + aliases: [LBNAM] + annotations: { transfer_var: LBNAM, transfer_format: char, transfer_length: 200 } + lbloinc: + description: >- + Logical Observation Identifiers Names and Codes (LOINC) code for the lab test. + aliases: [LBLOINC] + annotations: { transfer_var: LBLOINC, transfer_format: text, transfer_length: 200 } + lbspec: + description: Indicates the type of specimen used for the measurement. + aliases: [LBSPEC] + annotations: { transfer_var: LBSPEC, transfer_format: text, transfer_length: 100 } + lbspccnd: + description: >- + Free or standardized text that describes the condition of the specimen. + aliases: [LBSPCCND] + annotations: { transfer_var: LBSPCCND, transfer_format: text, transfer_length: 100 } + lbmethod: + description: Method of the test or examination. + aliases: [LBMETHOD] + annotations: { transfer_var: LBMETHOD, transfer_format: text, transfer_length: 200 } + lbrunid: + description: >- + A unique identifier for a particular run of a test performed by the lab on a particular batch of samples. This identifier can be used to distinguis... + aliases: [LBRUNID] + annotations: { transfer_var: LBRUNID, transfer_format: text, transfer_length: 25 } + lbanmeth: + description: >- + Analysis method applied to obtain a summarized result. Analysis method describes the method of secondary processing applied to a complex observatio... + aliases: [LBANMETH] + annotations: { transfer_var: LBANMETH, transfer_format: text, transfer_length: 200 } + lblloq: + description: >- + Indicates the lower limit of quantitation for an assay. Units will be those used for LBORRESU. Note: may contain a qualifier (e.g. < or >). + aliases: [LBLLOQ] + annotations: { transfer_var: LBLLOQ, transfer_format: text, transfer_length: 20 } + visitnum: + description: Clinical encounter number. Numeric version of VISIT, used for sorting. range: float - aliases: [PLRNRS, CVUNRS, SIUNRS] - exact_mappings: [labtx:PLRNRS, labtx:CVUNRS, labtx:SIUNRS] - annotations: - lab_tx_var: "PLRNRS / CVUNRS / SIUNRS" - sdtm_lb_target: "LBORRES (ORIGINAL, char) / LBSTRESN (CONVENTIONAL|SI)" - unit: - description: Unit of measure for this Result. - range: UnitEnum - aliases: [PLRU, CVU, SIU] - exact_mappings: [labtx:PLRU, labtx:CVU, labtx:SIU] - annotations: - lab_tx_var: "PLRU / CVU / SIU" - sdtm_lb_target: "LBORRESU (ORIGINAL) / LBSTRESU (CONVENTIONAL|SI)" - refRangeLow: - description: Reference range lower limit in this unit system. - aliases: [PLRRLO, CVURLO, SIURLO] - exact_mappings: [labtx:PLRRLO, labtx:CVURLO, labtx:SIURLO] - annotations: - lab_tx_var: "PLRRLO / CVURLO / SIURLO" - sdtm_lb_target: "LBORNRLO (ORIGINAL) / LBSTNRLO (CONVENTIONAL|SI)" - refRangeHigh: - description: Reference range upper limit in this unit system. - aliases: [PLRRHI, CVURHI, SIURHI] - exact_mappings: [labtx:PLRRHI, labtx:CVURHI, labtx:SIURHI] - annotations: - lab_tx_var: "PLRRHI / CVURHI / SIURHI" - sdtm_lb_target: "LBORNRHI (ORIGINAL) / LBSTNRHI (CONVENTIONAL|SI)" - refRangeDesc: - description: Reference range description / textual range in this unit system. - aliases: [PLRRRD, CVURRD, SIURRD] - exact_mappings: [labtx:PLRRRD, labtx:CVURRD, labtx:SIURRD] - annotations: - lab_tx_var: "PLRRRD / CVURRD / SIURRD" - sdtm_lb_target: "LBORNRIND/LBSTNRC context" - refRangeType: - description: Reference range type (e.g. NORMAL RANGE) in this unit system. - range: ReferenceRangeTypeEnum - aliases: [PLRRRT, CVURRT, SIURRT] - exact_mappings: [labtx:PLRRRT, labtx:CVURRT, labtx:SIURRT] - annotations: - lab_tx_var: "PLRRRT / CVURRT / SIURRT" + aliases: [VISITNUM] + annotations: { transfer_var: VISITNUM, transfer_format: numeric, transfer_length: 8 } + visit: + description: Protocol-defined description of a clinical encounter. + aliases: [VISIT] + annotations: { transfer_var: VISIT, transfer_format: text, transfer_length: 60 } + lbdtc: + description: >- + Specimen collection date or date and time is represented in LBDTC using ISO 8601 datetime format (YYYY-MM-DDThh:mm). This variable represents when... + aliases: [LBDTC] + annotations: { transfer_var: LBDTC, transfer_format: datetime, transfer_length: 19 } + lbtpt: + description: >- + Text description of planned time point when a measurement or observation should occur as defined in the protocol. + aliases: [LBTPT] + annotations: { transfer_var: LBTPT, transfer_format: text, transfer_length: 40 } + lbtptnum: + description: Numeric version of planned time point used in sorting. + range: float + aliases: [LBTPTNUM] + annotations: { transfer_var: LBTPTNUM, transfer_format: numeric, transfer_length: 8 } + lbcode: + description: Populated with the LUDWIG numeric code for the test concept. + range: integer + aliases: [LBCODE] + annotations: { transfer_var: LBCODE, transfer_format: num, transfer_length: 8 } + lbdupnum: + description: >- + Used to differentiate unplanned duplicate tests and/or results. This variable should not be used for planned repeated instances of a test (see LBRE... + range: integer + aliases: [LBDUPNUM] + annotations: { transfer_var: LBDUPNUM, transfer_format: numeric, transfer_length: 8 } + lbtstcnd: + description: >- + Identifies any planned condition imposed by the assay system on the specimen at the time the test is performed. + aliases: [LBTSTCND] + annotations: { transfer_var: LBTSTCND, transfer_format: text, transfer_length: 35 } + lbcndagt: + description: >- + Description of the agent used to impose a test condition identified in LBTSTCND. + aliases: [LBCNDAGT] + annotations: { transfer_var: LBCNDAGT, transfer_format: text, transfer_length: 200 } + lbanstat: + description: >- + Describes the status of the target/receptor as either TOTAL, FREE, BOUND, UNBOUND. + aliases: [LBANSTAT] + annotations: { transfer_var: LBANSTAT, transfer_format: text, transfer_length: 35 } + lbpanel: + description: Vendor Test panel name, used to group tests run together. + aliases: [LBPANEL] + annotations: { transfer_var: LBPANEL, transfer_format: text, transfer_length: 200 } + lbgate: + description: Gating structure + aliases: [LBGATE] + annotations: { transfer_var: LBGATE, transfer_format: text, transfer_length: 200 } + lbmrkstr: + description: Marker string + aliases: [LBMRKSTR] + annotations: { transfer_var: LBMRKSTR, transfer_format: text, transfer_length: 200 } + lbclmeth: + description: Description of the method used to collect a specimen for testing. + aliases: [LBCLMETH] + annotations: { transfer_var: LBCLMETH, transfer_format: text, transfer_length: 200 } + lbtmthsn: + description: >- + Describes the sensitivity of the test methodology with respect to observation, detection, or quantification. + aliases: [LBTMTHSN] + annotations: { transfer_var: LBTMTHSN, transfer_format: text, transfer_length: 35 } + lbreagnt: + description: Describes the reagent used in the assay to obtain the result. + aliases: [LBREAGNT] + annotations: { transfer_var: LBREAGNT, transfer_format: text, transfer_length: 200 } + lbmthdds: + description: >- + Provides a more detailed description of the assay methodology beyond the LUDWIG dictionary controlled variables (e.g., LBANMETH, LBTMTHSN, LBDEVICE... + aliases: [LBMTHDDS] + annotations: { transfer_var: LBMTHDDS, transfer_format: text, transfer_length: 200 } + lbbdagnt: + description: Documents the binding agent. + aliases: [LBBDAGNT] + annotations: { transfer_var: LBBDAGNT, transfer_format: text, transfer_length: 200 } + lbrqual: + description: >- + Describes additional observations/narrative/comments about the result. Typically not used for numeric LB results. + aliases: [LBRQUAL] + annotations: { transfer_var: LBRQUAL, transfer_format: text, transfer_length: 200 } + aux1: + description: >- + To be used when none of the available standard transfer variables are fit for purpose. + aliases: [AUX1] + annotations: { transfer_var: AUX1, transfer_format: text, transfer_length: 200 } + aux2: + description: >- + To be used when none of the available standard transfer variables are fit for purpose. + aliases: [AUX2] + annotations: { transfer_var: AUX2, transfer_format: text, transfer_length: 200 } + aux3: + description: >- + To be used when none of the available standard transfer variables are fit for purpose. + aliases: [AUX3] + annotations: { transfer_var: AUX3, transfer_format: text, transfer_length: 200 } + aux4: + description: >- + To be used when none of the available standard transfer variables are fit for purpose. + aliases: [AUX4] + annotations: { transfer_var: AUX4, transfer_format: text, transfer_length: 200 } + aux5: + description: >- + To be used when none of the available standard transfer variables are fit for purpose. + aliases: [AUX5] + annotations: { transfer_var: AUX5, transfer_format: text, transfer_length: 200 } + aux6: + description: >- + To be used when none of the available standard transfer variables are fit for purpose. + aliases: [AUX6] + annotations: { transfer_var: AUX6, transfer_format: text, transfer_length: 200 } + aux7: + description: >- + To be used when none of the available standard transfer variables are fit for purpose. + aliases: [AUX7] + annotations: { transfer_var: AUX7, transfer_format: text, transfer_length: 200 } + aux8: + description: >- + To be used when none of the available standard transfer variables are fit for purpose. + aliases: [AUX8] + annotations: { transfer_var: AUX8, transfer_format: text, transfer_length: 200 } + aux9: + description: >- + To be used when none of the available standard transfer variables are fit for purpose. + aliases: [AUX9] + annotations: { transfer_var: AUX9, transfer_format: text, transfer_length: 200 } + aux10: + description: >- + To be used when none of the available standard transfer variables are fit for purpose. + aliases: [AUX10] + annotations: { transfer_var: AUX10, transfer_format: text, transfer_length: 200 } + lbvrefid: + description: >- + External identifier such as lab specimen ID, as per the External Data Providers sample management system + aliases: [LBVREFID] + annotations: { transfer_var: LBVREFID, transfer_format: text, transfer_length: 200 } + ctestcd: + description: Populated with the vendor internal test code. + aliases: [CTESTCD] + annotations: { transfer_var: CTESTCD, transfer_format: text, transfer_length: 200 } + ctest: + description: Populated with the vendor internal test name. + aliases: [CTEST] + annotations: { transfer_var: CTEST, transfer_format: text, transfer_length: 200 } + cunit: + description: Populated with the vendor internal Unit. + aliases: [CUNIT] + annotations: { transfer_var: CUNIT, transfer_format: text, transfer_length: 200 } + cspec: + description: Populated with the vendor internal specimen. + aliases: [CSPEC] + annotations: { transfer_var: CSPEC, transfer_format: text, transfer_length: 200 } + cmethod: + description: Populated with the vendor internal methodology. + aliases: [CMETHOD] + annotations: { transfer_var: CMETHOD, transfer_format: text, transfer_length: 200 } + cpanel: + description: Populated with the vendor internal Panel ID. + aliases: [CPANEL] + annotations: { transfer_var: CPANEL, transfer_format: text, transfer_length: 200 } # ────────────────────────────────────────────────────────────────────────────── # ENUMS (subsets of CDISC Controlled Terminology; source: NCI EVS evs-2026-Q1) +# +# Bound on the AGREEMENT side only. Payload slots a vendor populates are left as +# unconstrained strings so a conformant-but-unanticipated file is not rejected at +# ingest; discrepancies are surfaced by the transform, which compares the payload +# against the agreement. UnitEnum and LbTestCdEnum are documented reference +# vocabularies, deliberately not bound to any slot. # ────────────────────────────────────────────────────────────────────────────── enums: @@ -965,6 +1306,17 @@ enums: Manufacturer: { description: Investigational product / device manufacturer. } Laboratory: { description: Performing / central laboratory. } CRO: { description: Contract research organization. } + Service Provider: { description: "Service provider (the GCP term used going forward)." } + + StudyRoleEnum: + description: >- + Role of an assigned person within their organization, per the DTA MVP Inventory + (DTA_Attributes row 14, Signatures section). Maps to USDM AssignedPerson.jobTitle. + permissible_values: + Data Manager Primary: { description: Primary data manager for the transfer. } + Data Manager Secondary: { description: Secondary / backup data manager. } + Statistical Programmer: { description: Statistical programmer. } + Vendor Lead: { description: Lead contact at the data provider. } DataExchangeRoleEnum: description: Direction of data exchange for a party. @@ -978,7 +1330,7 @@ enums: XPT: { description: SAS Transport (XPORT) file. } SAS 7bdat: { description: SAS dataset (sas7bdat). } CSV: { description: Comma-separated values. } - Txt: { description: Delimited text. } + Text: { description: Delimited text. } JSON: { description: JSON. } XLSX: { description: Excel (OOXML). } XLS: { description: Excel (legacy). } @@ -1010,7 +1362,7 @@ enums: TransferModeEnum: description: >- Whether each transmission carries the full dataset or only changes since the - last. Distinct from the instance-layer TransmissionTypeEnum (file-level I/U/D). + last. Distinct from the file-level TransmissionTypeEnum (I/U/D). permissible_values: Cumulative: { description: Each transfer contains the full cumulative dataset. } Incremental: { description: Each transfer contains only new/changed records. } @@ -1029,53 +1381,24 @@ enums: Unblinded: { description: Unblinded transfer. } Partially blinded: { description: Partially blinded transfer. } - # ════════════════════════ INSTANCE / SEMANTIC-LAYER ENUMS ════════════════════════ - - UnitSystemEnum: - description: Internal key distinguishing the three LAB Tx unit blocks (not a CDISC codelist). - permissible_values: - ORIGINAL: { description: As-reported by the performing lab (PLR* block). } - CONVENTIONAL: { description: Conventional units (CVU* block). } - SI: { description: Système International units (SIU* block). } + # ════════════════════════ INSTANCE-LAYER ENUM ════════════════════════ TransmissionTypeEnum: - description: Transmission Type (LAB Tx TRSTYP). Subset; confirm against CDISC CT. + description: File-level transmission type (LAB Tx TRSTYP). Subset; confirm against CDISC CT. permissible_values: I: { description: Initial transmission. } U: { description: Update transmission. } D: { description: Delete transmission. } - RecordTransactionTypeEnum: - description: Record Transaction Type (LAB Tx RTRTYP). - permissible_values: - I: { description: Insert. } - U: { description: Update. } - D: { description: Delete. } - - AdministrativeSexEnum: - description: Administrative Sex. Bound to CDISC CT codelist SEX (C66731). - permissible_values: - M: { description: Male. } - F: { description: Female. } - U: { description: Unknown. } - - ResultStatusEnum: - description: Test tracking status code (PLBTTC). Illustrative subset. - permissible_values: - RESULTS REPORTED: { description: Result has been reported. } - NOT DONE: { description: Test not performed. } - PENDING: { description: Result pending. } - - ReferenceRangeTypeEnum: - description: Reference range type. Illustrative subset. - permissible_values: - NORMAL RANGE: { description: Standard normal reference range. } - TOXICITY GRADE: { description: Toxicity-grade-based range. } + # ════════════════ REFERENCE VOCABULARIES (documented, not bound) ═══════════════ UnitEnum: description: >- Unit of measure. Subset of CDISC CT codelist UNIT (C71620), source NCI EVS - evs-2026-Q1. meaning CURIEs are NCIt C-codes. + evs-2026-Q1. meaning CURIEs are NCIt C-codes. NOT bound to any slot: agreements + legitimately use units outside this subset (mIU/L, ng/mL and pg/mL have all been + observed in real transfers), and binding it would reject valid data. Referenced + by the `recommended_codelist` annotation on the TestSpecification unit slots. permissible_values: g/dL: { description: Gram per Deciliter, meaning: ncit:C64783 } g/L: { description: Gram per Liter (EVS-mapped), meaning: ncit:C42576 } @@ -1095,8 +1418,9 @@ enums: LbTestCdEnum: description: >- Hematology subset of CDISC CT codelist LBTESTCD (C65047), source NCI EVS - evs-2026-Q1. Used by Dataset Specializations / mappings, not directly on a - DTA instance slot (the DTA carries vendor codes in PLBTID). + evs-2026-Q1. Used by Dataset Specializations / mappings, not bound to a payload + slot (a transfer carries the vendor's code in ctestcd and the standard code in + lbtestcd, neither of which is constrained to this subset). permissible_values: HGB: { description: Hemoglobin Measurement, meaning: ncit:C64848 } RBC: { description: Erythrocyte Count, meaning: ncit:C51946 } diff --git a/src/dta2sdtm/transmission.example.yaml b/src/dta2sdtm/transmission.example.yaml index 4f98ea7..c56340b 100644 --- a/src/dta2sdtm/transmission.example.yaml +++ b/src/dta2sdtm/transmission.example.yaml @@ -1,296 +1,495 @@ # ───────────────────────────────────────────────────────────────────────────── -# Mock LAB Transmission payload — CDISC Pilot study H2Q-MC-LZZT -# Validates against dta.linkml.yaml (root class: Transmission). +# Mock DTA transfer payload — CDISC Pilot study H2Q-MC-LZZT +# Validates against dta.linkml.yaml v0.2.0 (root class: Transmission). # -# Values are taken verbatim from the project's generated pilot output -# (out/dta/lab_dta.csv) for two real subjects at their first collection (E1): -# * STUDY-0001 (site 701, M, age 75) -# * STUDY-0002 (site 702, F, age 75) -# Each subject carries a HEMATOLOGY panel (HGB, RBC, WBC, PLAT) and a CHEMISTRY -# panel (GLUC, CREAT, CHOL, ALT). HGB is linked to the vendored COSMoS -# specialization (HGBBLD); the others are intentionally UNLINKED (no -# specialization) to exercise the gap report. GLUC carries a character result -# (AMBER) to exercise charResult; CREAT/CHOL/GLUC show SI unit conversion. +# Rewritten for v0.2.0: flat TransferRecord list per the DTA MVP Inventory's +# Data_Structure sheet, replacing the v0.1.0 nested LAB v2 graph. +# +# Two subjects at their first collection (E1), each with a HEMATOLOGY and a +# CHEMISTRY panel, plus two records that exercise specific downstream cases: +# +# * the URINALYSIS record carries lborres "STRAW" — a categorical result. This +# is the exact shape that produces the 8,414 "LBSTRESN cast failed" rows in +# out/reports/gap_report.md. It is only safe to derive LBSTRESN when the +# matching TestSpecification.data_type says the test is numeric. +# * the NOT DONE record exercises lbstat / lbreasnd. # # Validate: # linkml-validate -s dta.linkml.yaml -C Transmission transmission.example.yaml # ───────────────────────────────────────────────────────────────────────────── -# Transmission (GTP header) -ltvrsn: LAB-MODEL-2.0 +transfer_structure_version: DTA-MVP-1.0 igvrsn: SDTMIG 3.4 fcrdtc: "2025-01-01T00:00:00" trssid: CENTRALLAB-001 trstyp: I +dta_version_ref: "1.0" + +records: + + # ═══════════════════ Subject STUDY-0001 ═══════════════════ + - studyid: H2Q-MC-LZZT + domain: LB + usubjid: STUDY-0001 + subjid: SCR-0001 + lbgrpid: ACC-0001-V1 + lbrefid: SPEC-0001-V1-1 + lbtestcd: HGB + lbtest: Hemoglobin + lbcat: HEMATOLOGY + lborres: "16.98" + lborresu: "g/dL" + lbnam: Central Laboratory Services + lbspec: BLOOD + lbmethod: HPLC + lbrunid: RUN-2025-0102-A + visitnum: 1 + visit: E1 + lbdtc: "2025-01-02" + lbpanel: HEMATOLOGY + ctestcd: HGB + ctest: Hemoglobin + cunit: "g/dL" + cspec: BLOOD + cmethod: HPLC + cpanel: HEMATOLOGY + + - studyid: H2Q-MC-LZZT + domain: LB + usubjid: STUDY-0001 + subjid: SCR-0001 + lbgrpid: ACC-0001-V1 + lbrefid: SPEC-0001-V1-2 + lbtestcd: RBC + lbtest: Erythrocytes + lbcat: HEMATOLOGY + lborres: "5.29" + lborresu: "10^6/uL" + lbnam: Central Laboratory Services + lbspec: BLOOD + lbmethod: FLOW CYTOMETRY + lbrunid: RUN-2025-0102-A + visitnum: 1 + visit: E1 + lbdtc: "2025-01-02" + lbpanel: HEMATOLOGY + ctestcd: RBC + ctest: Erythrocytes + cunit: "10^6/uL" + cspec: BLOOD + cmethod: FLOW CYTOMETRY + cpanel: HEMATOLOGY + + - studyid: H2Q-MC-LZZT + domain: LB + usubjid: STUDY-0001 + subjid: SCR-0001 + lbgrpid: ACC-0001-V1 + lbrefid: SPEC-0001-V1-3 + lbtestcd: WBC + lbtest: Leukocytes + lbcat: HEMATOLOGY + lborres: "7.52" + lborresu: "10^9/L" + lbnam: Central Laboratory Services + lbspec: BLOOD + lbmethod: HPLC + lbrunid: RUN-2025-0102-A + visitnum: 1 + visit: E1 + lbdtc: "2025-01-02" + lbpanel: Blood Differential + ctestcd: WBC + ctest: Leukocytes + cunit: "10^9/L" + cspec: BLOOD + cmethod: HPLC + cpanel: Blood Differential + + - studyid: H2Q-MC-LZZT + domain: LB + usubjid: STUDY-0001 + subjid: SCR-0001 + lbgrpid: ACC-0001-V1 + lbrefid: SPEC-0001-V1-4 + lbtestcd: PLAT + lbtest: Platelets + lbcat: HEMATOLOGY + lborres: "251" + lborresu: "10^9/L" + lbnam: Central Laboratory Services + lbspec: BLOOD + lbmethod: HPLC + lbrunid: RUN-2025-0102-A + visitnum: 1 + visit: E1 + lbdtc: "2025-01-02" + lbpanel: HEMATOLOGY + ctestcd: PLAT + ctest: Platelets + cunit: "10^9/L" + cspec: BLOOD + cmethod: HPLC + cpanel: HEMATOLOGY + + - studyid: H2Q-MC-LZZT + domain: LB + usubjid: STUDY-0001 + subjid: SCR-0001 + lbgrpid: ACC-0001-V1 + lbrefid: SPEC-0001-V1-5 + lbtestcd: GLUC + lbtest: Glucose + lbcat: CHEMISTRY + lborres: "5.4" + lborresu: "mmol/L" + lbnam: Central Laboratory Services + lbspec: SERUM + lbmethod: ENZYMATIC + lbrunid: RUN-2025-0102-A + visitnum: 1 + visit: E1 + lbdtc: "2025-01-02" + lbpanel: CHEMISTRY + ctestcd: GLU + ctest: Glucose + cunit: "mmol/L" + cspec: SERUM + cmethod: ENZYMATIC + cpanel: CHEMISTRY -subjects: + - studyid: H2Q-MC-LZZT + domain: LB + usubjid: STUDY-0001 + subjid: SCR-0001 + lbgrpid: ACC-0001-V1 + lbrefid: SPEC-0001-V1-6 + lbtestcd: CREAT + lbtest: Creatinine + lbcat: CHEMISTRY + lborres: "88.4" + lborresu: "umol/L" + lbnam: Central Laboratory Services + lbspec: SERUM + lbmethod: ENZYMATIC + lbrunid: RUN-2025-0102-A + visitnum: 1 + visit: E1 + lbdtc: "2025-01-02" + lbpanel: CHEMISTRY + ctestcd: CRE + ctest: Creatinine + cunit: "umol/L" + cspec: SERUM + cmethod: ENZYMATIC + cpanel: CHEMISTRY - # ════════════════════════ Subject STUDY-0001 ════════════════════════ - studyid: H2Q-MC-LZZT - siteid: "701" - scrnid: SCR-0001 - asubjid: STUDY-0001 - adsex: M - collections: - - accsnid: ACC-0001-V1 - kitid: KIT-0001-V1 - kitdsc: Standard collection kit - visit: E1 - visitnum: 1 - specimens: - - lddom: LB - specid: SPEC-0001-V1 - rcvdtc: "2025-01-02" - spcoml: Specimen received in good condition. - spcomi: Fasting sample per protocol. - agespc: 75 - panels: + domain: LB + usubjid: STUDY-0001 + subjid: SCR-0001 + lbgrpid: ACC-0001-V1 + lbrefid: SPEC-0001-V1-7 + lbtestcd: CHOL + lbtest: Cholesterol + lbcat: CHEMISTRY + lborres: "4.91" + lborresu: "mmol/L" + lbnam: Central Laboratory Services + lbspec: SERUM + lbmethod: ENZYMATIC + lbrunid: RUN-2025-0102-A + visitnum: 1 + visit: E1 + lbdtc: "2025-01-02" + lbpanel: CHEMISTRY + ctestcd: CHOL + ctest: Cholesterol + cunit: "mmol/L" + cspec: SERUM + cmethod: ENZYMATIC + cpanel: CHEMISTRY - # ── HEMATOLOGY ── - - pnlid: HEM - tstpnl: HEMATOLOGY - plbid: PLAB-01 - plbnam: Central Laboratory Services - plbtyp: CENTRAL - labTestResults: - - plbtid: HGB - plbtnm: Hemoglobin - plbtct: HEMATOLOGY - dtrtid: HGB - dtrtnm: Hemoglobin - dtrtct: HEMATOLOGY - plbtin: Automated hematology analyzer. - plbtts: RESULTS REPORTED - plbttc: RESULTS REPORTED - plrtrc: Within reference range. - asydtc: "2025-01-03" - rrdtc: "2025-01-04" - rtrtyp: I - specialization: HGBBLD # linked -> BC C64848 - results: - - { unitSystem: ORIGINAL, numResult: 16.98, unit: g/dL, refRangeLow: "12", refRangeHigh: "18", refRangeDesc: "12-18", refRangeType: NORMAL RANGE } - - { unitSystem: CONVENTIONAL, numResult: 16.98, unit: g/dL, refRangeLow: "12.0", refRangeHigh: "18.0", refRangeDesc: "12.0-18.0" } - - { unitSystem: SI, numResult: 169.8, unit: g/L, refRangeLow: "120.0", refRangeHigh: "180.0", refRangeDesc: "120.0-180.0" } - - plbtid: RBC - plbtnm: Erythrocytes - plbtct: HEMATOLOGY - dtrtid: RBC - dtrtnm: Erythrocytes - plbtts: RESULTS REPORTED - asydtc: "2025-01-03" - rrdtc: "2025-01-04" - rtrtyp: I - results: - - { unitSystem: ORIGINAL, numResult: 5.29, unit: "10^6/uL", refRangeLow: "4.0", refRangeHigh: "6.0", refRangeType: NORMAL RANGE } - - { unitSystem: SI, numResult: 5.29, unit: "10^12/L", refRangeLow: "4.0", refRangeHigh: "6.0" } - - plbtid: WBC - plbtnm: Leukocytes - plbtct: HEMATOLOGY - dtrtid: WBC - dtrtnm: Leukocytes - plbtts: RESULTS REPORTED - asydtc: "2025-01-03" - rrdtc: "2025-01-04" - rtrtyp: I - results: - - { unitSystem: ORIGINAL, numResult: 9.44, unit: "10^3/uL", refRangeLow: "4.0", refRangeHigh: "11.0", refRangeType: NORMAL RANGE } - - { unitSystem: SI, numResult: 9.44, unit: "10^9/L", refRangeLow: "4.0", refRangeHigh: "11.0" } - - plbtid: PLAT - plbtnm: Platelet - plbtct: HEMATOLOGY - dtrtid: PLAT - dtrtnm: Platelet - plbtts: RESULTS REPORTED - asydtc: "2025-01-03" - rrdtc: "2025-01-04" - rtrtyp: I - results: - - { unitSystem: ORIGINAL, numResult: 356.17, unit: "10^3/uL", refRangeLow: "150", refRangeHigh: "400", refRangeType: NORMAL RANGE } - - { unitSystem: SI, numResult: 356.17, unit: "10^9/L", refRangeLow: "150.0", refRangeHigh: "400.0" } + - studyid: H2Q-MC-LZZT + domain: LB + usubjid: STUDY-0001 + subjid: SCR-0001 + lbgrpid: ACC-0001-V1 + lbrefid: SPEC-0001-V1-8 + lbtestcd: ALT + lbtest: Alanine Aminotransferase + lbcat: CHEMISTRY + lborres: "22" + lborresu: "U/L" + lbnam: Central Laboratory Services + lbspec: SERUM + lbmethod: ENZYMATIC + lbrunid: RUN-2025-0102-A + visitnum: 1 + visit: E1 + lbdtc: "2025-01-02" + lbpanel: CHEMISTRY + ctestcd: ALT + ctest: ALT + cunit: "U/L" + cspec: SERUM + cmethod: ENZYMATIC + cpanel: CHEMISTRY - # ── CHEMISTRY ── - - pnlid: CHEM - tstpnl: CHEMISTRY - plbid: PLAB-01 - plbnam: Central Laboratory Services - plbtyp: CENTRAL - labTestResults: - - plbtid: GLUC - plbtnm: Glucose - plbtct: CHEMISTRY - dtrtid: GLUC - dtrtnm: Glucose - plbtts: RESULTS REPORTED - asydtc: "2025-01-03" - rrdtc: "2025-01-04" - rtrtyp: I - alrtlv: AMBER - results: - - { unitSystem: ORIGINAL, charResult: AMBER, unit: mg/dL, refRangeLow: "70", refRangeHigh: "100", refRangeType: NORMAL RANGE } - - { unitSystem: SI, charResult: AMBER, unit: mmol/L, refRangeLow: "3.885", refRangeHigh: "5.55" } - - plbtid: CREAT - plbtnm: Creatinine - plbtct: CHEMISTRY - dtrtid: CREAT - dtrtnm: Creatinine - plbtts: RESULTS REPORTED - asydtc: "2025-01-03" - rrdtc: "2025-01-04" - rtrtyp: I - results: - - { unitSystem: ORIGINAL, numResult: 0.78, unit: mg/dL, refRangeLow: "0.7", refRangeHigh: "1.3", refRangeType: NORMAL RANGE } - - { unitSystem: SI, numResult: 68.952, unit: umol/L, refRangeLow: "61.88", refRangeHigh: "114.92" } - - plbtid: CHOL - plbtnm: Cholesterol - plbtct: CHEMISTRY - dtrtid: CHOL - dtrtnm: Cholesterol - plbtts: RESULTS REPORTED - asydtc: "2025-01-03" - rrdtc: "2025-01-04" - rtrtyp: I - results: - - { unitSystem: ORIGINAL, numResult: 197.22, unit: mg/dL, refRangeLow: "125", refRangeHigh: "200", refRangeType: NORMAL RANGE } - - { unitSystem: SI, numResult: 5.108, unit: mmol/L, refRangeLow: "3.2375", refRangeHigh: "5.18" } - - plbtid: ALT - plbtnm: Alanine Aminotransferase - plbtct: CHEMISTRY - dtrtid: ALT - dtrtnm: Alanine Aminotransferase - plbtts: RESULTS REPORTED - asydtc: "2025-01-03" - rrdtc: "2025-01-04" - rtrtyp: I - results: - - { unitSystem: ORIGINAL, numResult: 33.3, unit: U/L, refRangeLow: "7", refRangeHigh: "56", refRangeType: NORMAL RANGE } - - { unitSystem: SI, numResult: 33.3, unit: U/L, refRangeLow: "7.0", refRangeHigh: "56.0" } + # ═══════════════════ Subject STUDY-0002 ═══════════════════ + - studyid: H2Q-MC-LZZT + domain: LB + usubjid: STUDY-0002 + subjid: SCR-0002 + lbgrpid: ACC-0002-V1 + lbrefid: SPEC-0002-V1-1 + lbtestcd: HGB + lbtest: Hemoglobin + lbcat: HEMATOLOGY + lborres: "16.98" + lborresu: "g/dL" + lbnam: Central Laboratory Services + lbspec: BLOOD + lbmethod: HPLC + lbrunid: RUN-2025-0102-A + visitnum: 1 + visit: E1 + lbdtc: "2025-01-02" + lbpanel: HEMATOLOGY + ctestcd: HGB + ctest: Hemoglobin + cunit: "g/dL" + cspec: BLOOD + cmethod: HPLC + cpanel: HEMATOLOGY - # ════════════════════════ Subject STUDY-0002 ════════════════════════ - studyid: H2Q-MC-LZZT - siteid: "702" - scrnid: SCR-0002 - asubjid: STUDY-0002 - adsex: F - collections: - - accsnid: ACC-0002-V1 - kitid: KIT-0002-V1 - kitdsc: Standard collection kit - visit: E1 - visitnum: 1 - specimens: - - lddom: LB - specid: SPEC-0002-V1 - rcvdtc: "2025-01-02" - agespc: 75 - panels: - - pnlid: HEM - tstpnl: HEMATOLOGY - plbid: PLAB-01 - plbnam: Central Laboratory Services - plbtyp: CENTRAL - labTestResults: - - plbtid: HGB - plbtnm: Hemoglobin - plbtct: HEMATOLOGY - dtrtid: HGB - dtrtnm: Hemoglobin - plbtts: RESULTS REPORTED - asydtc: "2025-01-03" - rrdtc: "2025-01-04" - rtrtyp: I - specialization: HGBBLD - results: - - { unitSystem: ORIGINAL, numResult: 16.38, unit: g/dL, refRangeLow: "12", refRangeHigh: "18", refRangeType: NORMAL RANGE } - - { unitSystem: SI, numResult: 163.8, unit: g/L, refRangeLow: "120.0", refRangeHigh: "180.0" } - - plbtid: RBC - plbtnm: Erythrocytes - plbtct: HEMATOLOGY - dtrtid: RBC - dtrtnm: Erythrocytes - plbtts: RESULTS REPORTED - asydtc: "2025-01-03" - rrdtc: "2025-01-04" - rtrtyp: I - results: - - { unitSystem: ORIGINAL, numResult: 4.13, unit: "10^6/uL", refRangeLow: "4.0", refRangeHigh: "6.0", refRangeType: NORMAL RANGE } - - { unitSystem: SI, numResult: 4.13, unit: "10^12/L", refRangeLow: "4.0", refRangeHigh: "6.0" } - - plbtid: WBC - plbtnm: Leukocytes - plbtct: HEMATOLOGY - dtrtid: WBC - dtrtnm: Leukocytes - plbtts: RESULTS REPORTED - asydtc: "2025-01-03" - rrdtc: "2025-01-04" - rtrtyp: I - results: - - { unitSystem: ORIGINAL, numResult: 6.93, unit: "10^3/uL", refRangeLow: "4.0", refRangeHigh: "11.0", refRangeType: NORMAL RANGE } - - { unitSystem: SI, numResult: 6.93, unit: "10^9/L", refRangeLow: "4.0", refRangeHigh: "11.0" } - - plbtid: PLAT - plbtnm: Platelet - plbtct: HEMATOLOGY - dtrtid: PLAT - dtrtnm: Platelet - plbtts: RESULTS REPORTED - asydtc: "2025-01-03" - rrdtc: "2025-01-04" - rtrtyp: I - results: - - { unitSystem: ORIGINAL, numResult: 161.71, unit: "10^3/uL", refRangeLow: "150", refRangeHigh: "400", refRangeType: NORMAL RANGE } - - { unitSystem: SI, numResult: 161.71, unit: "10^9/L", refRangeLow: "150.0", refRangeHigh: "400.0" } + domain: LB + usubjid: STUDY-0002 + subjid: SCR-0002 + lbgrpid: ACC-0002-V1 + lbrefid: SPEC-0002-V1-2 + lbtestcd: RBC + lbtest: Erythrocytes + lbcat: HEMATOLOGY + lborres: "5.29" + lborresu: "10^6/uL" + lbnam: Central Laboratory Services + lbspec: BLOOD + lbmethod: FLOW CYTOMETRY + lbrunid: RUN-2025-0102-A + visitnum: 1 + visit: E1 + lbdtc: "2025-01-02" + lbpanel: HEMATOLOGY + ctestcd: RBC + ctest: Erythrocytes + cunit: "10^6/uL" + cspec: BLOOD + cmethod: FLOW CYTOMETRY + cpanel: HEMATOLOGY - - pnlid: CHEM - tstpnl: CHEMISTRY - plbid: PLAB-01 - plbnam: Central Laboratory Services - plbtyp: CENTRAL - labTestResults: - - plbtid: GLUC - plbtnm: Glucose - plbtct: CHEMISTRY - dtrtid: GLUC - dtrtnm: Glucose - plbtts: RESULTS REPORTED - asydtc: "2025-01-03" - rrdtc: "2025-01-04" - rtrtyp: I - alrtlv: AMBER - results: - - { unitSystem: ORIGINAL, charResult: AMBER, unit: mg/dL, refRangeLow: "70", refRangeHigh: "100", refRangeType: NORMAL RANGE } - - { unitSystem: SI, charResult: AMBER, unit: mmol/L, refRangeLow: "3.885", refRangeHigh: "5.55" } - - plbtid: CREAT - plbtnm: Creatinine - plbtct: CHEMISTRY - dtrtid: CREAT - dtrtnm: Creatinine - plbtts: RESULTS REPORTED - asydtc: "2025-01-03" - rrdtc: "2025-01-04" - rtrtyp: I - results: - - { unitSystem: ORIGINAL, numResult: 0.83, unit: mg/dL, refRangeLow: "0.7", refRangeHigh: "1.3", refRangeType: NORMAL RANGE } - - { unitSystem: SI, numResult: 73.372, unit: umol/L, refRangeLow: "61.88", refRangeHigh: "114.92" } - - plbtid: CHOL - plbtnm: Cholesterol - plbtct: CHEMISTRY - dtrtid: CHOL - dtrtnm: Cholesterol - plbtts: RESULTS REPORTED - asydtc: "2025-01-03" - rrdtc: "2025-01-04" - rtrtyp: I - results: - - { unitSystem: ORIGINAL, numResult: 179.31, unit: mg/dL, refRangeLow: "125", refRangeHigh: "200", refRangeType: NORMAL RANGE } - - { unitSystem: SI, numResult: 4.6441, unit: mmol/L, refRangeLow: "3.2375", refRangeHigh: "5.18" } - - plbtid: ALT - plbtnm: Alanine Aminotransferase - plbtct: CHEMISTRY - dtrtid: ALT - dtrtnm: Alanine Aminotransferase - plbtts: RESULTS REPORTED - asydtc: "2025-01-03" - rrdtc: "2025-01-04" - rtrtyp: I - results: - - { unitSystem: ORIGINAL, numResult: 11.55, unit: U/L, refRangeLow: "7", refRangeHigh: "56", refRangeType: NORMAL RANGE } - - { unitSystem: SI, numResult: 11.55, unit: U/L, refRangeLow: "7.0", refRangeHigh: "56.0" } + - studyid: H2Q-MC-LZZT + domain: LB + usubjid: STUDY-0002 + subjid: SCR-0002 + lbgrpid: ACC-0002-V1 + lbrefid: SPEC-0002-V1-3 + lbtestcd: WBC + lbtest: Leukocytes + lbcat: HEMATOLOGY + lborres: "7.52" + lborresu: "10^9/L" + lbnam: Central Laboratory Services + lbspec: BLOOD + lbmethod: HPLC + lbrunid: RUN-2025-0102-A + visitnum: 1 + visit: E1 + lbdtc: "2025-01-02" + lbpanel: Blood Differential + ctestcd: WBC + ctest: Leukocytes + cunit: "10^9/L" + cspec: BLOOD + cmethod: HPLC + cpanel: Blood Differential + + - studyid: H2Q-MC-LZZT + domain: LB + usubjid: STUDY-0002 + subjid: SCR-0002 + lbgrpid: ACC-0002-V1 + lbrefid: SPEC-0002-V1-4 + lbtestcd: PLAT + lbtest: Platelets + lbcat: HEMATOLOGY + lborres: "251" + lborresu: "10^9/L" + lbnam: Central Laboratory Services + lbspec: BLOOD + lbmethod: HPLC + lbrunid: RUN-2025-0102-A + visitnum: 1 + visit: E1 + lbdtc: "2025-01-02" + lbpanel: HEMATOLOGY + ctestcd: PLAT + ctest: Platelets + cunit: "10^9/L" + cspec: BLOOD + cmethod: HPLC + cpanel: HEMATOLOGY + + - studyid: H2Q-MC-LZZT + domain: LB + usubjid: STUDY-0002 + subjid: SCR-0002 + lbgrpid: ACC-0002-V1 + lbrefid: SPEC-0002-V1-5 + lbtestcd: GLUC + lbtest: Glucose + lbcat: CHEMISTRY + lborres: "5.4" + lborresu: "mmol/L" + lbnam: Central Laboratory Services + lbspec: SERUM + lbmethod: ENZYMATIC + lbrunid: RUN-2025-0102-A + visitnum: 1 + visit: E1 + lbdtc: "2025-01-02" + lbpanel: CHEMISTRY + ctestcd: GLU + ctest: Glucose + cunit: "mmol/L" + cspec: SERUM + cmethod: ENZYMATIC + cpanel: CHEMISTRY + + - studyid: H2Q-MC-LZZT + domain: LB + usubjid: STUDY-0002 + subjid: SCR-0002 + lbgrpid: ACC-0002-V1 + lbrefid: SPEC-0002-V1-6 + lbtestcd: CREAT + lbtest: Creatinine + lbcat: CHEMISTRY + lborres: "88.4" + lborresu: "umol/L" + lbnam: Central Laboratory Services + lbspec: SERUM + lbmethod: ENZYMATIC + lbrunid: RUN-2025-0102-A + visitnum: 1 + visit: E1 + lbdtc: "2025-01-02" + lbpanel: CHEMISTRY + ctestcd: CRE + ctest: Creatinine + cunit: "umol/L" + cspec: SERUM + cmethod: ENZYMATIC + cpanel: CHEMISTRY + + - studyid: H2Q-MC-LZZT + domain: LB + usubjid: STUDY-0002 + subjid: SCR-0002 + lbgrpid: ACC-0002-V1 + lbrefid: SPEC-0002-V1-7 + lbtestcd: CHOL + lbtest: Cholesterol + lbcat: CHEMISTRY + lborres: "4.91" + lborresu: "mmol/L" + lbnam: Central Laboratory Services + lbspec: SERUM + lbmethod: ENZYMATIC + lbrunid: RUN-2025-0102-A + visitnum: 1 + visit: E1 + lbdtc: "2025-01-02" + lbpanel: CHEMISTRY + ctestcd: CHOL + ctest: Cholesterol + cunit: "mmol/L" + cspec: SERUM + cmethod: ENZYMATIC + cpanel: CHEMISTRY + + - studyid: H2Q-MC-LZZT + domain: LB + usubjid: STUDY-0002 + subjid: SCR-0002 + lbgrpid: ACC-0002-V1 + lbrefid: SPEC-0002-V1-8 + lbtestcd: ALT + lbtest: Alanine Aminotransferase + lbcat: CHEMISTRY + lborres: "22" + lborresu: "U/L" + lbnam: Central Laboratory Services + lbspec: SERUM + lbmethod: ENZYMATIC + lbrunid: RUN-2025-0102-A + visitnum: 1 + visit: E1 + lbdtc: "2025-01-02" + lbpanel: CHEMISTRY + ctestcd: ALT + ctest: ALT + cunit: "U/L" + cspec: SERUM + cmethod: ENZYMATIC + cpanel: CHEMISTRY + + # ── categorical result: NOT castable to LBSTRESN (see header note) ── + - studyid: H2Q-MC-LZZT + domain: LB + usubjid: STUDY-0001 + subjid: SCR-0001 + lbgrpid: ACC-0001-V1 + lbrefid: SPEC-0001-V1-U1 + lbtestcd: COLOR + lbtest: Color + lbcat: URINALYSIS + lborres: STRAW + lborresu: "" + lbrescat: NORMAL + lbnam: Central Laboratory Services + lbspec: URINE + lbmethod: DIPSTICK + visitnum: 1 + visit: E1 + lbdtc: "2025-01-02" + lbpanel: URINALYSIS + ctestcd: WBCU + ctest: Color Urine + cspec: URINE + cmethod: DIPSTICK + cpanel: URINALYSIS + + # ── test not done ── + - studyid: H2Q-MC-LZZT + domain: LB + usubjid: STUDY-0002 + subjid: SCR-0002 + lbgrpid: ACC-0002-V1 + lbrefid: SPEC-0002-V1-9 + lbtestcd: HCT + lbtest: Hematocrit + lbcat: HEMATOLOGY + lborres: "" + lbstat: NOT DONE + lbreasnd: Insufficient sample volume. + lbnam: Central Laboratory Services + lbspec: BLOOD + visitnum: 1 + visit: E1 + lbdtc: "2025-01-02" + lbpanel: HEMATOLOGY + ctestcd: HCT + ctest: Hematocrit + cpanel: HEMATOLOGY diff --git a/src/dta2sdtm/transmission_minimal.example.yaml b/src/dta2sdtm/transmission_minimal.example.yaml index 21a448c..0e36d99 100644 --- a/src/dta2sdtm/transmission_minimal.example.yaml +++ b/src/dta2sdtm/transmission_minimal.example.yaml @@ -1,113 +1,82 @@ # ───────────────────────────────────────────────────────────────────────────── -# Mock LZZT DTA instance — validates against dta.linkml.yaml (root class: Transmission) +# Mock LZZT DTA instance — validates against dta.linkml.yaml (root: Transmission) # -# Values are taken verbatim from the project's real generated output -# (out/dta/lab_dta.csv), study H2Q-MC-LZZT (the CDISC LZZT pilot). Two analytes -# are shown: -# * HGB — fully linked to the vendored COSMoS specialization (specialization: HGBBLD) -# * RBC — intentionally UNLINKED (no specialization) to illustrate the gap case -# the gap report is meant to surface. +# Rewritten for schema v0.2.0: the payload is now a flat TransferRecord list +# (DTA MVP Inventory / Data_Structure), replacing the v0.1.0 nested LAB v2 graph. +# +# Clinical values are carried over from the project's real generated output +# (out/dta/lab_dta.csv), study H2Q-MC-LZZT (the CDISC LZZT pilot). Two analytes: +# * HGB — agreed in the DTA test specification (joins on lbpanel + ctestcd) +# * RBC — intentionally NOT in the agreed test specification, to exercise the +# "received a test nobody agreed to" case the gap report surfaces. +# +# NOTE — facts the v0.1.0 payload carried that this structure cannot: +# * conventional / SI results and units (old CVU*/SIU* blocks). Data_Structure +# carries original result only (lborres/lborresu); the standardised result is +# now DERIVED downstream using TestSpecification.conventional_unit / .si_unit. +# * reference ranges (old PLRRLO/PLRRHI/PLRRRD). Data_Structure has no +# LBORNRLO/LBORNRHI/LBNRIND equivalent — see the schema's `known_gap`. # # Validate: # linkml-validate -s dta.linkml.yaml -C Transmission transmission_minimal.example.yaml # ───────────────────────────────────────────────────────────────────────────── -# Transmission (GTP header) -ltvrsn: LAB-MODEL-2.0 +# ── file header ── +transfer_structure_version: DTA-MVP-1.0 igvrsn: SDTMIG 3.4 fcrdtc: "2025-01-01T00:00:00" trssid: CENTRALLAB-001 trstyp: I +dta_version_ref: "1.0" # -> DataTransferAgreement.dta_version.version_number -subjects: - - studyid: H2Q-MC-LZZT - siteid: "701" - scrnid: SCR-0001 - asubjid: STUDY-0001 - adsex: M - - collections: - - accsnid: ACC-0001-V1 - kitid: KIT-0001-V1 - kitdsc: Standard collection kit - visit: E1 - visitnum: 1 +records: - specimens: - - lddom: LB - specid: SPEC-0001-V1-1 - rcvdtc: "2025-01-02" - agespc: 75 - - panels: - - pnlid: HEM - tstpnl: HEMATOLOGY - plbid: PLAB-01 - plbnam: Central Laboratory Services - plbtyp: CENTRAL - - labTestResults: - # ── Hemoglobin: fully linked to the vendored COSMoS spec ── - - plbtid: HGB - plbtnm: Hemoglobin - plbtds: Hemoglobin - plbtct: HEMATOLOGY - dtrtid: HGB - dtrtnm: Hemoglobin - dtrtct: HEMATOLOGY - plbtts: RESULTS REPORTED - asydtc: "2025-01-03" - rrdtc: "2025-01-04" - rtrtyp: I - specialization: HGBBLD # -> cosmos/hgbbld.specialization.yaml -> BC C64848 - results: - - unitSystem: ORIGINAL - numResult: 16.98 - unit: g/dL - refRangeLow: "12" - refRangeHigh: "18" - refRangeDesc: "12-18" - refRangeType: NORMAL RANGE - - unitSystem: CONVENTIONAL - numResult: 16.98 - unit: g/dL - refRangeLow: "12.0" - refRangeHigh: "18.0" - refRangeDesc: "12-18" - refRangeType: NORMAL RANGE - - unitSystem: SI - numResult: 169.8 - unit: g/L - refRangeLow: "120.0" - refRangeHigh: "180.0" - refRangeDesc: "120.0-180.0" - refRangeType: NORMAL RANGE + # ── Hemoglobin: agreed in the test specification ── + - studyid: H2Q-MC-LZZT + domain: LB + usubjid: STUDY-0001 + subjid: SCR-0001 + lbgrpid: ACC-0001-V1 # accession / collection event + lbrefid: SPEC-0001-V1-1 # specimen id + lbtestcd: HGB + lbtest: Hemoglobin + lbcat: HEMATOLOGY + lborres: "16.98" + lborresu: g/dL + lbstat: "" # null => a result was produced + lbnam: Central Laboratory Services + lbspec: BLOOD + lbmethod: HPLC + visitnum: 1 + visit: E1 + lbdtc: "2025-01-02" + lbpanel: HEMATOLOGY # ─┐ natural key to TestSpecification + ctestcd: HGB # ─┘ + ctest: Hemoglobin + cunit: g/dL + cspec: BLOOD + cpanel: HEMATOLOGY - # ── Erythrocytes: NO specialization linked (gap case) ── - - plbtid: RBC - plbtnm: Erythrocytes - plbtds: Erythrocytes - plbtct: HEMATOLOGY - dtrtid: RBC - dtrtnm: Erythrocytes - dtrtct: HEMATOLOGY - plbtts: RESULTS REPORTED - asydtc: "2025-01-03" - rrdtc: "2025-01-04" - rtrtyp: I - # specialization: (absent) -> surfaces under gap report "unmapped-BCs" - results: - - unitSystem: ORIGINAL - numResult: 5.29 - unit: "10^6/uL" - refRangeLow: "4.0" - refRangeHigh: "6.0" - refRangeDesc: "4.0-6.0" - refRangeType: NORMAL RANGE - - unitSystem: SI - numResult: 5.29 - unit: "10^12/L" - refRangeLow: "4.0" - refRangeHigh: "6.0" - refRangeDesc: "4.0-6.0" - refRangeType: NORMAL RANGE + # ── Erythrocytes: NOT in the agreed test specification (gap case) ── + - studyid: H2Q-MC-LZZT + domain: LB + usubjid: STUDY-0001 + subjid: SCR-0001 + lbgrpid: ACC-0001-V1 + lbrefid: SPEC-0001-V1-1 + lbtestcd: RBC + lbtest: Erythrocytes + lbcat: HEMATOLOGY + lborres: "5.29" + lborresu: "10^6/uL" + lbnam: Central Laboratory Services + lbspec: BLOOD + visitnum: 1 + visit: E1 + lbdtc: "2025-01-02" + lbpanel: HEMATOLOGY + ctestcd: RBC # no matching TestSpecification row + ctest: Erythrocytes + cunit: "10^6/uL" + cspec: BLOOD + cpanel: HEMATOLOGY