FHIR to CDISC Joint Mapping Implementation Guide, published by HL7 International - BR&R Work Group. This guide is not an authorized publication; it is the continuous build for version 1.0.0 built by the FHIR (HL7® FHIR® Standard) CI Build. This version is based on the current content of https://github.com/HL7/fhir-cdisc-mapping/ and changes regularly. See the Directory of published versions
Contents:
This domain contains two mapping tables. The first is similar to the other domains and covers the SDTM and CDASH specification. The second covers the CDISC LAB specification. It is handled as a separate table because it has a significantly larger number of data elements than the other two specifications and the element names have less correlation. Readability of the mappings was enhanced by moving the content to a separate specification.
Lab data in FHIR is handled by two primary resources:
The CDISC specifications focus almost exclusively on the latter. As a result, mappings in both tables are expressed from the perspective of an Observation-rooted transformation. (I.e. All paths are rooted in Observation or are driven by a search based on Observation.) For studies interested in the retrieval of legacy lab information, it may in some cases be necessary to retrieve the DiagnosticReport and manually extract information from a PDF or other report representation. Obviously no standardized mapping can be provided here to assist with that.
In FHIR, the Observation resource is used for a wide range of data collection purposes. In addition to lab data, it is also used to capture vital signs, patient symptoms, psychological assessments, device data, and others. Ideally, lab data can be distinguished from types of Observations using the Observation.category element which should, ideally, have a code of laboratory
drawn from the http://terminology.hl7.org/CodeSystem/observation-category. However, the core FHIR specification does not mandate the use of this code or system. (The U.S. Realm implementation guide and several other national implementation guides do mandate the use of this category.)
The 'laboratory' category encompasses both simple chemical measurements as well as complex assessments including the description of genetic variants, microbiology tests, etc. This implementation guide focuses only on simple measurements and does not attempt to map more complex structures - which in some cases correspond to distinct CDISC domains. In part, this is because FHIR has not yet tried to enforce standardized representation of more complex areas, though initial work has been completed on the capture of genetic findings. Future versions of this implementation guide will likely tackle more complex lab structures.
Guidance on interpreting the tables can be found here.
CDISC | FHIR map (or gap) | Comment | |||
---|---|---|---|---|---|
Label | CDASH | SDTM | Element | FHIRPath | |
Study Identifier |
DM.STUDYID
Core: HR Type: Char |
DM.STUDYID
Core: Req Type: Char |
ResearchStudy.identifier
0..* Identifier |
ResearchSubject.where(subject=Observation.subject).study.resolve().partOf.resolve().identifier Observation.extension(workflow-researchstudy).valueReference.resolve().partOf.resolve().identifier |
Mapping is based on presumption that research subject will be tied to site-specific ResearchStudy, which will then be part of overall ResearchStudy. The path using the extension will only exist if the system maintaining the Observation is aware of its relevance to the Study. ResearchStudy links to Patient. Observation links to Patient. If the patients are the same, that establishes the linkage. There's also an extension that allows direct linkage to a 'study' from any resource - if the source system has actually established such a linkage. |
Study Site Identifier |
DM.SITEID
Core: HR Type: Char |
DM.SITEID
Core: Req Type: Char |
ResearchStudy.identifier
0..* Identifier |
ResearchSubject.where(subject=Observation.subject).study.resolve().identifier Observation.extension(workflow-researchstudy).valueReference.resolve().identifier |
Mapping is based on presumption that research subject will be tied to site-specific ResearchStudy, which will then be part of overall ResearchStudy. The path using the extension will only exist if the system maintaining the Observation is aware of its relevance to the Study. |
Investigator Identifier |
DM.INVID
Core: Perm Type: Char |
Practitioner.identifier
0..* Identifier |
ResearchSubject.where(subject=Observation.subject).study.resolve().principleInvestigator.resolve().identifier Observation.extension(workflow-researchstudy).valueReference.resolve().principleInvestigator.resolve().identifier |
Will need to decide which id to expose. If you want the PI for the overall study rather than just for the site associated with the Observation, you'll need to traverse the partOf.resolve() from the study-specific ResearchStudy. The path using the extension will only exist if the system maintaining the Observation is aware of its relevance to the Study. |
|
Subject Identifier for the Study |
DM.SUBJID
Core: HR Type: Char |
DM.SUBJID
Core: Req Type: Char |
ResearchSubject.identifier
0..* Identifier |
ResearchSubject.where(individual=Observation.subject).identifier |
Study subject is found by finding the StudySubject that corresponds to the same Patient and ResearchStudy as the Observation. No standard way to decide which subject identifier to use |
Visit Number | VISITNUM |
VISITNUM
Core: Exp Type: Num |
Encounter.identifier
0..* Identifier |
Observation.encounter.resolve().identifier |
This won't necessarily correlate with the protocol-defined visit number. Correspondance to a protocol-defined visit will typically require rules-based analysis based on time, encounter type and other factors. |
Visit Name |
VISIT
Core: R/C Type: Char |
VISIT
Core: Perm Type: Char |
ActivityDefinition.name
0..1 string inv-0 |
Observation.encounter.resolve().extension(workflow-instantiates).valueCanonical.resolve().title |
In most cases, real world data won't have a direct association to a study protocol activity and the link would need to be inferred. However, if it exists, it would map as follows: We could go through Encounter.basedOn-> ServiceRequest.instantiatesCanonical, you arrive at ActivityDefinition. Encounter should have a standard instantiatesCanonical extension that would allow pointing to the ActivityDefinition. This would be the ActivityDefinition.name. Submit a change request to add the extension |
Lab Specimen ID |
LBREFID
Core: R/C Type: Char |
LBREFID
Core: Perm Type: Char |
Specimen.accessionIdentifier
0..1 Identifier |
Observation.specimen.resolve().accessionIdentifier |
As reported from the Site |
Date/Time of Specimen Collection |
LBDTC
Core: Exp Type: Char values: ISO 8601 |
Specimen.collection.collectedDateTime
0..1 dateTime |
Observation.specimen.resolve().collection.collectedDateTime |
NOTE: no mapping included for CDASH LBDAT and LBTIM To map directly, the data must be formatted in ISO 8601 date/time format (e.g., 2015-02-07T13:28:17-05:00) | |
Specimen.collection.collectedPeriod
0..1 Period |
Observation.specimen.resolve().collection.collectedPeriod.start |
||||
End Date/Time of Specimen Collection |
LBENDTC
Core: Perm Type: Char values: ISO 8601 |
Specimen.collection.collectedPeriod
0..1 Period |
Observation.specimen.resolve().collection.collectedPeriod.end |
NOTE: No mapping included for CDASH LBENDAT and LBENTIM) To map directly, the data must be formatted in ISO 8601 date/time format (e.g., 2015-02-07T13:28:17-05:00) |
|
Specimen Collection Duration (for Clinical Research with extended time of collection) | LBCDUR | LBDUR* |
Specimen.collection.collectedPeriod
0..1 Period |
Observation.specimen.resolve().collection.collectedPeriod |
Will need to convert value and unit to ISO syntax. E.g. 90 min would convert to PT1H30M |
Specimen Planned Time Point Name (Planned Collection Time Point - time within Encounter) | LBCTPT |
LBTPT
Core: Perm Type: Char |
Specimen.extension
0..* Extension |
Observation.specimen.resolve().extension(cqf-relativeDateTime).extension('reference').valueReference.reference.resolve().title |
Typically named timepoints won't exist in real-world data and the timing point name will need to be assigned by the sponsor. However, this shows where the data would be expressed in FHIR if it was present there. |
Specimen Planned Elapsed Time from Time Point Reference | LBCELTM |
LBELTM
Core: Perm Type: Char values: ISO 8601 |
Specimen.extension
0..* Extension |
Observation.specimen.resolve().extension(cqf-relativeDateTime).extension('offset').valueDuration |
Typically named timepoints won't exist in real-world data and the timing point offset will need to be calculated by the sponsor. However, this shows where the data would be expressed in FHIR if it was present there. If present, would need to translate to ISO-8601 |
Specimen Condition | LBCSPCCN |
LBSPCCND
Core: Perm Type: Char |
Specimen.condition
0..* CodeableConcept Binding: SpecimenCondition extensible |
Observation.specimen.resolve().condition.coding.code |
Recommend submitting change proposals to extend the FHIR vocabulary to include CDISC values. Sponsor will need to decide how to map when FHIR terminology contains codes not present in CDISC. NOTE: requires an extension to the FHIR vocabulary to include CDISC values (e.g., Refrigerated). An "as collected" version of the SDTM variable is recommended to allow the sponsor to make adjustments to ensure terminology compliance while mapping to SDTM. |
Specimen Type | LBCSPEC |
LBSPEC
Core: Perm Type: Char |
Specimen.type
0..1 CodeableConcept Binding: SpecimenType example |
Observation.specimen.resolve().type.coding.display |
NOTE: In some cases, this may be inferred from the LOINC. FHIR terminology is not standardized. Sponsor will need to map to CDISC terminology if raw data does not use CDISC codes. An "as collected" version of the SDTM variable is recommended to allow the sponsor to make adjustments to ensure terminology compliance while mapping to SDTM. |
Fasting Status | LBCFAST |
LBFAST
Core: Perm Type: Char |
Specimen.collection.fastingStatusCodeableConcept
0..1 CodeableConcept Binding: FastingStatus extensible |
Observation.specimen.resolve().collection.fastingStatusCodeableConcept.coding.code |
FHIR to CDISC mapping as follows:
F -> Y; NF -> N; FNA -> N/A; NG -> Not Applicable. If fastingStatusDuration is present and non-zero, that would also map to 'Y'. In some cases, this may be inferred from the LOINC term (Challenge). For example, Challenge=post CFst to signify a calorie fast. |
Lab Test Condition Met |
LBCOND
Core: R/C Type: Char |
This is an 'ask on entry' question. May be able to determine some conditions, such as fasting, by using other specimen.collection attributes (e.g., fasting status). Others might be captured as components or extensions. Alternatively, obtain this information from the case report form entries. | There are explicit elements for Specimen.collection.fastingStatus and collection.fastingDuration. May need extensions for other pre-conditions | ||
Specimen Collection Position | LBCPOS | LBPOS* |
Specimen.collection
0..1 BackboneElement |
Observation.specimen.resolve().collection.extension(observation-bodyPosition).valueCodeableConcept |
As reported from the lab. Will need to map to CDISC controlled terminology |
Specimen Usability for the Test | LBCSPCUF | LBSPCUFL* |
Specimen.status
0..1 code Binding: SpecimenStatus required |
Observation.specimen.resolve()status.where($this='Unsatisfactory') |
Need to translate to blank or N if not received as blank or N May not be applicable for safety labs. |
Category | LBCCAT |
LBCAT
Core: Exp Type: Char |
DiagnosticReport.category
0..* CodeableConcept Binding: DiagnosticServiceSection example |
DiagnosticReport.category.coding.where(system='LBSCAT').code |
NOTE: In some cases, this may be inferred from the LOINC. It might also be found by looking for the 'panel' Obsesrvation this Observation is a part of. In most cases, translation will be required to sponsor-defined codes. It may work best for the sponsor to categorize based on TESTCD values and ignore lab-reported and EHR-determined categories entirely. |
DiagnosticReport.code
1..1 CodeableConcept Binding: DiagnosticReportCodes preferred |
DiagnosticReport.code |
||||
Test Status | LBCSTAT |
LBSTAT
Core: Perm Type: Char |
ServiceRequest.doNotPerform
0..1 boolean |
Observation.basedOn.resolve().doNotPerform |
Sponsor may choose to map a situation in which the entire panel is cancelled as one summary record compliant with SDTM IG examples.
Some sponsor may decline to accept cancelled records under specific circumstances (e.g., unscheduled)
Sponsor may also choose to populate LBREASND as CANCELLED Needs to be translated from FHIR vocabulary to SDTM controlled terminology: FHIR -> CDISC: Not performed = NOT DONE Cancelled = NOT DONE Completed = <blank> |
ServiceRequest.status
1..1 code Binding: ServiceRequestStatus required |
Observation.basedOn.resolve().status |
||||
Reason Test Not Done |
LBREASND
Core: Perm Type: Char |
Observation.dataAbsentReason
0..1 CodeableConcept obs-6 Binding: ObservationValueAbsentReason extensible |
Observation.dataAbsentReason.text |
see notes on LBSTAT | |
Lab Test or Examination Short Name |
LBTESTCD
Core: Req Type: Char |
Observation.code
1..1 CodeableConcept Binding: ObservationCode example |
Observation.code.coding.where(selected=true).code |
Translate to compliant terminology (if needed) | |
Lab Test or Examination Name |
LBTEST
Core: Req Type: Char |
Observation.code
1..1 CodeableConcept Binding: ObservationCode example |
Observation.code.coding.where(selected=true).display |
Translate to compliant terminology (if needed) | |
Test LOINC Code | LBLOINC |
LBLOINC
Core: Perm Type: Char |
Observation.code
1..1 CodeableConcept Binding: ObservationCode example |
Observation.code.coding.where(system='http://loinc.org').code |
|
Vendor Name |
LBNAM
Core: R/C Type: Char |
LBNAM
Core: Perm Type: Char |
Organization.name
0..1 string org-1 |
Observation.performer.where($this is Organization).resolve().name |
|
Reference Range Indicator | LBCNRIND |
LBNRIND
Core: Exp Type: Char |
Observation.interpretation
0..* CodeableConcept Binding: ObservationInterpretation extensible |
Observation.interpretation.where(system ='NRIND') |
Must map to CDISC controlled terminology |
Standard Toxicity Grade |
LBTOXGR
Core: Perm Type: Char |
Observation.interpretation
0..* CodeableConcept Binding: ObservationInterpretation extensible |
Observation.interpretation.where(system=[Toxicity Grade]).coding.code |
Must map to sponsor-determind terminology | |
Toxicity Grade Code List Version | LBCTOXV |
LBTOX
Core: Perm Type: Char |
Observation.interpretation
0..* CodeableConcept Binding: ObservationInterpretation extensible |
Observation.interpretation.where(system=[Toxicity Grade]).coding.version |
Typically FHIR systems will only populate Coding.version where the terminology is ill-behaved and code meanings change over time. The code system version doesn't convey what value set the user was choosing from (which could be a subset of the codes from the code system or could draw from multiple code systems). |
Toxicity Grade Code List | LBCTOX |
LBTOX
Core: Perm Type: Char |
Observation.interpretation
0..* CodeableConcept Binding: ObservationInterpretation extensible |
Observation.interpretation.where(system=[Toxicity Grade]).system |
Note that the system doesn't define the value set nor does it specify value set version or code system version(s) |
Lab Ref Range Lower Limit in Orig Unit |
LBORNRLO
Core: R/C Type: Char |
LBORNRLO
Core: Exp Type: Char |
Observation.referenceRange
0..* BackboneElement |
Observation.referenceRange.low |
As reported from the lab. |
Lab Ref Range Upper Limit in Orig Unit |
LBORNRHI
Core: R/C Type: Char |
LBORNRHI
Core: Exp Type: Char |
Observation.referenceRange
0..* BackboneElement |
Observation.referenceRange.high |
As reported from the lab. |
Reference Range (for string value) | LBCSTNRC |
LBSTNRC
Core: Perm Type: Char |
Observation.referenceRange
0..* BackboneElement |
Observation.referenceRange.text |
Sponsor would normalize this. |
Lab Result or Finding in Original Units |
LBORRES
Core: HR Type: Char |
LBORRES
Core: Exp Type: Char |
Observation.valueQuantity
0..1 Quantity obs-7 |
Observation.valueQuantity.value Observation.valueCodeableConcept.text |
Might also include valueQuantity.comparator CDISC (and sponsors) may have different definitions about what constitutes an "original result". Sponsors will need to make a determination of whether the value found in the Observation.value meets those criteria and is appropriate to be mapped. (In some cases, what's in Observation.value could have been transformed from what was captured at the point of measurement.) |
Lab Original Units |
LBORRESU
Core: O Type: Char |
LBORRESU
Core: Exp Type: Char |
Observation.valueQuantity
0..1 Quantity obs-7 |
Observation.valueQuantity.unit |
Needs to be translated to match the controlled terminology, if not already compliant |
Lab Clinical Significance |
LBCLSIG
Core: HR Type: Char |
SUPPLB.QVAL |
Observation.interpretation
0..* CodeableConcept Binding: ObservationInterpretation extensible |
Observation.interpretation |
As determined by the lab, not the sponsor |
Method | LBCMETH |
LBMETHOD
Core: Perm Type: Char |
Observation.method
0..1 CodeableConcept Binding: ObservationMethod example |
Observation.method |
Method can cover collection and many other features. In some cases, collection method may be inferred from the Observation.code. Would need to map to CDISC controlled terminology |
Anatomical Region | LBLOC* |
Specimen.collection.bodySite
0..1 CodeableConcept Binding: BodySite example |
Observation.specimen.resolve().collection.bodySite |
In some cases, the anatomical location can be inferred from the Observation.code. Would need to map to CDISC controlled terminology. | |
Run ID | LBRUNID | LBRUNID* | Would need to introduce an extension | May not be applicable for safety labs. | |
Unique Subject Identifier |
USUBJID
Core: Req Type: Char |
Patient.identifier
0..* Identifier |
Observation.subject.resolve().identifier |
To find an identifier that was consistent for the subject across studies would typically mean looking to the Patient identifier. However, this may not be appropriate to be exposed directly, so some degree of mapping, obfuscation or anonymization may be required. In theory, a sponsor specific study-independent identifier could be maintained on Patient alongside other patient identifiers, but this would be unusual in most clinical systems. |
|
Result Categorization | LBRESCAT | LBRESCAT* |
Observation.interpretation
0..* CodeableConcept Binding: ObservationInterpretation extensible |
Observation.interpretation |
This wouldn't come from the lab. It *could* be added after the fact. If so, this would be the mapping |
Anatomical Region | LBANTREG | LBANTREG* |
Specimen.collection.bodySite
0..1 CodeableConcept Binding: BodySite example |
Observation.specimen.resolve().collection.bodySite |
Not differentiated from LBLOC. Not for basic safety labs |
Unscheduled Flag | LBUSCHFL | LBUSCHFL* | This can sort of be inferred from the absence of a link to a PlanDefinition or ActivityDefinition, however there are lots of other reasons these links could be missing, so that's not totally reliable. Could look at using an extension or a tag Not generally used for human clinical trials |
||
Planned Time Point Number |
LBTPTNUM
Core: Perm Type: Num |
Pull this information from the Clinical Plan (PlanDefinition) | Link to the clinical plan would be through the Observation.basedOn link to CarePlan or the instantiatesCanonical link to PlanDefinition or ActivityDefinition | ||
Analytical Method | LBANMETH* | This does not apply to safety labs. | The corresponding values need to be evaluated and aligned accordingly to the CDISC context of --METHOD. Potentially could appear in Observation.method. An extension is required to distinguish. | ||
Baseline Flag |
LBBLFL
Core: Exp Type: Char |
Variable may be deprecated. This information would come from the Sponsor. | Could appear in Observation.reasonCode. In practice the notion of 'baseline' is more of a relationship. Any arbitrary Observation might be selected as the 'baseline' for a particular step in the study. As such, the real linkage is the tie to the CarePlan activity that's tied to the ActivityDefinition with a reason of 'baseline'. | ||
Derived Flag |
LBDRVFL
Core: Perm Type: Char |
This information would come from the Sponsor. | FHIR doesn't use a flag. If an Observation is derived, it should point to the Observations it's derived from with the derivedFrom association. (If that relationship is present, then the Derived Flag is true) |
FHIR map (or gap) | CDISC | Comment | |||
---|---|---|---|---|---|
Label | Element | FHIRPath | CDASH | SDTM | |
Study Identifier |
ResearchStudy.identifier
0..* Identifier |
ResearchSubject.where(subject=Observation.subject).study.resolve().partOf.resolve().identifier Observation.extension(workflow-researchstudy).valueReference.resolve().partOf.resolve().identifier |
DM.STUDYID
Core: HR Type: Char |
DM.STUDYID
Core: Req Type: Char |
Mapping is based on presumption that research subject will be tied to site-specific ResearchStudy, which will then be part of overall ResearchStudy. The path using the extension will only exist if the system maintaining the Observation is aware of its relevance to the Study. ResearchStudy links to Patient. Observation links to Patient. If the patients are the same, that establishes the linkage. There's also an extension that allows direct linkage to a 'study' from any resource - if the source system has actually established such a linkage. |
Study Site Identifier |
ResearchStudy.identifier
0..* Identifier |
ResearchSubject.where(subject=Observation.subject).study.resolve().identifier Observation.extension(workflow-researchstudy).valueReference.resolve().identifier |
DM.SITEID
Core: HR Type: Char |
DM.SITEID
Core: Req Type: Char |
Mapping is based on presumption that research subject will be tied to site-specific ResearchStudy, which will then be part of overall ResearchStudy. The path using the extension will only exist if the system maintaining the Observation is aware of its relevance to the Study. |
Investigator Identifier |
Practitioner.identifier
0..* Identifier |
ResearchSubject.where(subject=Observation.subject).study.resolve().principleInvestigator.resolve().identifier Observation.extension(workflow-researchstudy).valueReference.resolve().principleInvestigator.resolve().identifier |
DM.INVID
Core: Perm Type: Char |
Will need to decide which id to expose. If you want the PI for the overall study rather than just for the site associated with the Observation, you'll need to traverse the partOf.resolve() from the study-specific ResearchStudy. The path using the extension will only exist if the system maintaining the Observation is aware of its relevance to the Study. |
|
Subject Identifier for the Study |
ResearchSubject.identifier
0..* Identifier |
ResearchSubject.where(individual=Observation.subject).identifier |
DM.SUBJID
Core: HR Type: Char |
DM.SUBJID
Core: Req Type: Char |
Study subject is found by finding the StudySubject that corresponds to the same Patient and ResearchStudy as the Observation. No standard way to decide which subject identifier to use |
Visit Number |
Encounter.identifier
0..* Identifier |
Observation.encounter.resolve().identifier |
VISITNUM |
VISITNUM
Core: Exp Type: Num |
This won't necessarily correlate with the protocol-defined visit number. Correspondance to a protocol-defined visit will typically require rules-based analysis based on time, encounter type and other factors. |
Visit Name |
ActivityDefinition.name
0..1 string inv-0 |
Observation.encounter.resolve().extension(workflow-instantiates).valueCanonical.resolve().title |
VISIT
Core: R/C Type: Char |
VISIT
Core: Perm Type: Char |
In most cases, real world data won't have a direct association to a study protocol activity and the link would need to be inferred. However, if it exists, it would map as follows: We could go through Encounter.basedOn-> ServiceRequest.instantiatesCanonical, you arrive at ActivityDefinition. Encounter should have a standard instantiatesCanonical extension that would allow pointing to the ActivityDefinition. This would be the ActivityDefinition.name. Submit a change request to add the extension |
Lab Specimen ID |
Specimen.accessionIdentifier
0..1 Identifier |
Observation.specimen.resolve().accessionIdentifier |
LBREFID
Core: R/C Type: Char |
LBREFID
Core: Perm Type: Char |
As reported from the Site |
Date/Time of Specimen Collection |
Specimen.collection.collectedDateTime
0..1 dateTime |
Observation.specimen.resolve().collection.collectedDateTime |
LBDTC
Core: Exp Type: Char values: ISO 8601 |
NOTE: no mapping included for CDASH LBDAT and LBTIM To map directly, the data must be formatted in ISO 8601 date/time format (e.g., 2015-02-07T13:28:17-05:00) | |
Specimen.collection.collectedPeriod
0..1 Period |
Observation.specimen.resolve().collection.collectedPeriod.start |
||||
End Date/Time of Specimen Collection |
Specimen.collection.collectedPeriod
0..1 Period |
Observation.specimen.resolve().collection.collectedPeriod.end |
LBENDTC
Core: Perm Type: Char values: ISO 8601 |
NOTE: No mapping included for CDASH LBENDAT and LBENTIM) To map directly, the data must be formatted in ISO 8601 date/time format (e.g., 2015-02-07T13:28:17-05:00) |
|
Specimen Collection Duration (for Clinical Research with extended time of collection) |
Specimen.collection.collectedPeriod
0..1 Period |
Observation.specimen.resolve().collection.collectedPeriod |
LBCDUR | LBDUR* | Will need to convert value and unit to ISO syntax. E.g. 90 min would convert to PT1H30M |
Specimen Planned Time Point Name (Planned Collection Time Point - time within Encounter) |
Specimen.extension
0..* Extension |
Observation.specimen.resolve().extension(cqf-relativeDateTime).extension('reference').valueReference.reference.resolve().title |
LBCTPT |
LBTPT
Core: Perm Type: Char |
Typically named timepoints won't exist in real-world data and the timing point name will need to be assigned by the sponsor. However, this shows where the data would be expressed in FHIR if it was present there. |
Specimen Planned Elapsed Time from Time Point Reference |
Specimen.extension
0..* Extension |
Observation.specimen.resolve().extension(cqf-relativeDateTime).extension('offset').valueDuration |
LBCELTM |
LBELTM
Core: Perm Type: Char values: ISO 8601 |
Typically named timepoints won't exist in real-world data and the timing point offset will need to be calculated by the sponsor. However, this shows where the data would be expressed in FHIR if it was present there. If present, would need to translate to ISO-8601 |
Specimen Condition |
Specimen.condition
0..* CodeableConcept Binding: SpecimenCondition extensible |
Observation.specimen.resolve().condition.coding.code |
LBCSPCCN |
LBSPCCND
Core: Perm Type: Char |
Recommend submitting change proposals to extend the FHIR vocabulary to include CDISC values. Sponsor will need to decide how to map when FHIR terminology contains codes not present in CDISC. NOTE: requires an extension to the FHIR vocabulary to include CDISC values (e.g., Refrigerated). An "as collected" version of the SDTM variable is recommended to allow the sponsor to make adjustments to ensure terminology compliance while mapping to SDTM. |
Specimen Type |
Specimen.type
0..1 CodeableConcept Binding: SpecimenType example |
Observation.specimen.resolve().type.coding.display |
LBCSPEC |
LBSPEC
Core: Perm Type: Char |
NOTE: In some cases, this may be inferred from the LOINC. FHIR terminology is not standardized. Sponsor will need to map to CDISC terminology if raw data does not use CDISC codes. An "as collected" version of the SDTM variable is recommended to allow the sponsor to make adjustments to ensure terminology compliance while mapping to SDTM. |
Fasting Status |
Specimen.collection.fastingStatusCodeableConcept
0..1 CodeableConcept Binding: FastingStatus extensible |
Observation.specimen.resolve().collection.fastingStatusCodeableConcept.coding.code |
LBCFAST |
LBFAST
Core: Perm Type: Char |
FHIR to CDISC mapping as follows:
F -> Y; NF -> N; FNA -> N/A; NG -> Not Applicable. If fastingStatusDuration is present and non-zero, that would also map to 'Y'. In some cases, this may be inferred from the LOINC term (Challenge). For example, Challenge=post CFst to signify a calorie fast. |
Lab Test Condition Met | This is an 'ask on entry' question. May be able to determine some conditions, such as fasting, by using other specimen.collection attributes (e.g., fasting status). Others might be captured as components or extensions. Alternatively, obtain this information from the case report form entries. |
LBCOND
Core: R/C Type: Char |
There are explicit elements for Specimen.collection.fastingStatus and collection.fastingDuration. May need extensions for other pre-conditions | ||
Specimen Collection Position |
Specimen.collection
0..1 BackboneElement |
Observation.specimen.resolve().collection.extension(observation-bodyPosition).valueCodeableConcept |
LBCPOS | LBPOS* | As reported from the lab. Will need to map to CDISC controlled terminology |
Specimen Usability for the Test |
Specimen.status
0..1 code Binding: SpecimenStatus required |
Observation.specimen.resolve()status.where($this='Unsatisfactory') |
LBCSPCUF | LBSPCUFL* | Need to translate to blank or N if not received as blank or N May not be applicable for safety labs. |
Category |
DiagnosticReport.category
0..* CodeableConcept Binding: DiagnosticServiceSection example |
DiagnosticReport.category.coding.where(system='LBSCAT').code |
LBCCAT |
LBCAT
Core: Exp Type: Char |
NOTE: In some cases, this may be inferred from the LOINC. It might also be found by looking for the 'panel' Obsesrvation this Observation is a part of. In most cases, translation will be required to sponsor-defined codes. It may work best for the sponsor to categorize based on TESTCD values and ignore lab-reported and EHR-determined categories entirely. |
DiagnosticReport.code
1..1 CodeableConcept Binding: DiagnosticReportCodes preferred |
DiagnosticReport.code |
||||
Test Status |
ServiceRequest.doNotPerform
0..1 boolean |
Observation.basedOn.resolve().doNotPerform |
LBCSTAT |
LBSTAT
Core: Perm Type: Char |
Sponsor may choose to map a situation in which the entire panel is cancelled as one summary record compliant with SDTM IG examples.
Some sponsor may decline to accept cancelled records under specific circumstances (e.g., unscheduled)
Sponsor may also choose to populate LBREASND as CANCELLED Needs to be translated from FHIR vocabulary to SDTM controlled terminology: FHIR -> CDISC: Not performed = NOT DONE Cancelled = NOT DONE Completed = <blank> |
ServiceRequest.status
1..1 code Binding: ServiceRequestStatus required |
Observation.basedOn.resolve().status |
||||
Reason Test Not Done |
Observation.dataAbsentReason
0..1 CodeableConcept obs-6 Binding: ObservationValueAbsentReason extensible |
Observation.dataAbsentReason.text |
LBREASND
Core: Perm Type: Char |
see notes on LBSTAT | |
Lab Test or Examination Short Name |
Observation.code
1..1 CodeableConcept Binding: ObservationCode example |
Observation.code.coding.where(selected=true).code |
LBTESTCD
Core: Req Type: Char |
Translate to compliant terminology (if needed) | |
Lab Test or Examination Name |
Observation.code
1..1 CodeableConcept Binding: ObservationCode example |
Observation.code.coding.where(selected=true).display |
LBTEST
Core: Req Type: Char |
Translate to compliant terminology (if needed) | |
Test LOINC Code |
Observation.code
1..1 CodeableConcept Binding: ObservationCode example |
Observation.code.coding.where(system='http://loinc.org').code |
LBLOINC |
LBLOINC
Core: Perm Type: Char |
|
Vendor Name |
Organization.name
0..1 string org-1 |
Observation.performer.where($this is Organization).resolve().name |
LBNAM
Core: R/C Type: Char |
LBNAM
Core: Perm Type: Char |
|
Reference Range Indicator |
Observation.interpretation
0..* CodeableConcept Binding: ObservationInterpretation extensible |
Observation.interpretation.where(system ='NRIND') |
LBCNRIND |
LBNRIND
Core: Exp Type: Char |
Must map to CDISC controlled terminology |
Standard Toxicity Grade |
Observation.interpretation
0..* CodeableConcept Binding: ObservationInterpretation extensible |
Observation.interpretation.where(system=[Toxicity Grade]).coding.code |
LBTOXGR
Core: Perm Type: Char |
Must map to sponsor-determind terminology | |
Toxicity Grade Code List Version |
Observation.interpretation
0..* CodeableConcept Binding: ObservationInterpretation extensible |
Observation.interpretation.where(system=[Toxicity Grade]).coding.version |
LBCTOXV |
LBTOX
Core: Perm Type: Char |
Typically FHIR systems will only populate Coding.version where the terminology is ill-behaved and code meanings change over time. The code system version doesn't convey what value set the user was choosing from (which could be a subset of the codes from the code system or could draw from multiple code systems). |
Toxicity Grade Code List |
Observation.interpretation
0..* CodeableConcept Binding: ObservationInterpretation extensible |
Observation.interpretation.where(system=[Toxicity Grade]).system |
LBCTOX |
LBTOX
Core: Perm Type: Char |
Note that the system doesn't define the value set nor does it specify value set version or code system version(s) |
Lab Ref Range Lower Limit in Orig Unit |
Observation.referenceRange
0..* BackboneElement |
Observation.referenceRange.low |
LBORNRLO
Core: R/C Type: Char |
LBORNRLO
Core: Exp Type: Char |
As reported from the lab. |
Lab Ref Range Upper Limit in Orig Unit |
Observation.referenceRange
0..* BackboneElement |
Observation.referenceRange.high |
LBORNRHI
Core: R/C Type: Char |
LBORNRHI
Core: Exp Type: Char |
As reported from the lab. |
Reference Range (for string value) |
Observation.referenceRange
0..* BackboneElement |
Observation.referenceRange.text |
LBCSTNRC |
LBSTNRC
Core: Perm Type: Char |
Sponsor would normalize this. |
Lab Result or Finding in Original Units |
Observation.valueQuantity
0..1 Quantity obs-7 |
Observation.valueQuantity.value Observation.valueCodeableConcept.text |
LBORRES
Core: HR Type: Char |
LBORRES
Core: Exp Type: Char |
Might also include valueQuantity.comparator CDISC (and sponsors) may have different definitions about what constitutes an "original result". Sponsors will need to make a determination of whether the value found in the Observation.value meets those criteria and is appropriate to be mapped. (In some cases, what's in Observation.value could have been transformed from what was captured at the point of measurement.) |
Lab Original Units |
Observation.valueQuantity
0..1 Quantity obs-7 |
Observation.valueQuantity.unit |
LBORRESU
Core: O Type: Char |
LBORRESU
Core: Exp Type: Char |
Needs to be translated to match the controlled terminology, if not already compliant |
Lab Clinical Significance |
Observation.interpretation
0..* CodeableConcept Binding: ObservationInterpretation extensible |
Observation.interpretation |
LBCLSIG
Core: HR Type: Char |
SUPPLB.QVAL | As determined by the lab, not the sponsor |
Method |
Observation.method
0..1 CodeableConcept Binding: ObservationMethod example |
Observation.method |
LBCMETH |
LBMETHOD
Core: Perm Type: Char |
Method can cover collection and many other features. In some cases, collection method may be inferred from the Observation.code. Would need to map to CDISC controlled terminology |
Anatomical Region |
Specimen.collection.bodySite
0..1 CodeableConcept Binding: BodySite example |
Observation.specimen.resolve().collection.bodySite |
LBLOC* | In some cases, the anatomical location can be inferred from the Observation.code. Would need to map to CDISC controlled terminology. | |
Run ID | Would need to introduce an extension | LBRUNID | LBRUNID* | May not be applicable for safety labs. | |
Unique Subject Identifier |
Patient.identifier
0..* Identifier |
Observation.subject.resolve().identifier |
USUBJID
Core: Req Type: Char |
To find an identifier that was consistent for the subject across studies would typically mean looking to the Patient identifier. However, this may not be appropriate to be exposed directly, so some degree of mapping, obfuscation or anonymization may be required. In theory, a sponsor specific study-independent identifier could be maintained on Patient alongside other patient identifiers, but this would be unusual in most clinical systems. |
|
Result Categorization |
Observation.interpretation
0..* CodeableConcept Binding: ObservationInterpretation extensible |
Observation.interpretation |
LBRESCAT | LBRESCAT* | This wouldn't come from the lab. It *could* be added after the fact. If so, this would be the mapping |
Anatomical Region |
Specimen.collection.bodySite
0..1 CodeableConcept Binding: BodySite example |
Observation.specimen.resolve().collection.bodySite |
LBANTREG | LBANTREG* | Not differentiated from LBLOC. Not for basic safety labs |
Unscheduled Flag | This can sort of be inferred from the absence of a link to a PlanDefinition or ActivityDefinition, however there are lots of other reasons these links could be missing, so that's not totally reliable. Could look at using an extension or a tag Not generally used for human clinical trials |
LBUSCHFL | LBUSCHFL* | ||
Planned Time Point Number | Pull this information from the Clinical Plan (PlanDefinition) |
LBTPTNUM
Core: Perm Type: Num |
Link to the clinical plan would be through the Observation.basedOn link to CarePlan or the instantiatesCanonical link to PlanDefinition or ActivityDefinition | ||
Analytical Method | This does not apply to safety labs. | LBANMETH* | The corresponding values need to be evaluated and aligned accordingly to the CDISC context of --METHOD. Potentially could appear in Observation.method. An extension is required to distinguish. | ||
Baseline Flag | Variable may be deprecated. This information would come from the Sponsor. |
LBBLFL
Core: Exp Type: Char |
Could appear in Observation.reasonCode. In practice the notion of 'baseline' is more of a relationship. Any arbitrary Observation might be selected as the 'baseline' for a particular step in the study. As such, the real linkage is the tie to the CarePlan activity that's tied to the ActivityDefinition with a reason of 'baseline'. | ||
Derived Flag | This information would come from the Sponsor. |
LBDRVFL
Core: Perm Type: Char |
FHIR doesn't use a flag. If an Observation is derived, it should point to the Observations it's derived from with the derivedFrom association. (If that relationship is present, then the Derived Flag is true) |
Guidance on interpreting the tables can be found here.
CDISC | FHIR map (or gap) | Comment | ||
---|---|---|---|---|
Label | LAB | Element | FHIRPath | |
Study ID or Number | /GTP/Study/@ID |
ResearchStudy.identifier
0..* Identifier |
ResearchSubject.where(subject=Observation.subject).study.resolve().partOf.resolve().identifier Observation.extension(workflow-researchstudy).valueReference.resolve().partOf.resolve().identifier |
Mapping is based on presumption that research subject will be tied to site-specific ResearchStudy, which will then be part of overall ResearchStudy. The path using the extension will only exist if the system maintaining the Observation is aware of its relevance to the Study. |
Study Name | /GTP/Study/@Name |
ResearchStudy.title
0..1 string |
ResearchSubject.where(subject=Observation.subject).study.resolve()partOf.resolve().title Observation.extension(workflow-researchstudy).valueReference.resolve().partOf.resolve().title |
Mapping is based on presumption that research subject will be tied to site-specific ResearchStudy, which will then be part of overall ResearchStudy. The path using the extension will only exist if the system maintaining the Observation is aware of its relevance to the Study. |
Transmission Type | /GTP/Study/@TransmissionType | Determine from type of call to service (requesting all data vs incremental data - e.g. filtering by _lastUpdated). NOTE: If data are not persisted between FHIR and LAB, cannot do incremental data processing (due to lack of delta detection). | If the data is being 'pushed' in a message, this could also be conveyed in the MessageHeader.event | |
Study Transaction Type | /GTP/Study/TransactionType | Determining whether transactions are insert or update is a task for the data consumer. (NOTE: This requires persistent storage to compare current data with data in the incoming file) | This would be conveyed using a transaction Bundle - specifically the Bundle.entry.request.method | |
Site ID or Number | /GTP/Study/Site/@ID |
ResearchStudy.site
0..* Reference |
ResearchSubject.where(subject=Observation.subject).study.resolve().identifier Observation.extension(workflow-researchstudy).valueReference.resolve().identifier |
Mapping is based on presumption that research subject will be tied to site-specific ResearchStudy, which will then be part of overall ResearchStudy. The path using the extension will only exist if the system maintaining the Observation is aware of its relevance to the Study. |
Site Transaction Type | /GTP/Study/Site/TransactionType | Determining whether transactions are insert or update is a task for the data consumer. (NOTE: This requires persistent storage to compare current data with data in the incoming file) | As per study transaction type (not clear why it happens at both levels - what would it mean to 'delete' at the study level and 'update at the site level?) | |
Investigator ID or Number | /GTP/Study/Site/Investigator/@ID |
Practitioner.identifier
0..* Identifier |
ResearchSubject.where(subject=Observation.subject).study.resolve().principleInvestigator.resolve().identifier Observation.extension(workflow-researchstudy).valueReference.resolve().principleInvestigator.resolve().identifier |
Will need to decide which id to expose or may need to map to one recognized by the sponsor. If you want the PI for the overall study rather than just for the site associated with the Observation, you'll need to traverse the partOf.resolve() from the study-specific ResearchStudy. The path using the extension will only exist if the system maintaining the Observation is aware of its relevance to the Study. |
Investigator Name | /GTP/Study/Site/Investigator/@Name |
Practitioner.name
0..* HumanName |
ResearchSubject.where(subject=Observation.subject).study.resolve().principleInvestigator.resolve().name.text Observation.extension(workflow-researchstudy).valueReference.resolve().principleInvestigator.resolve().name.text |
Will need to decide which name to expose if there are multiples. Can also extract the prefix, given and family names if text isn't specified. The path using the extension will only exist if the system maintaining the Observation is aware of its relevance to the Study. |
Investigator Transaction Type | /GTP/Study/Site/Investigator/TransactionType | Determining whether transactions are insert or update is a task for the data consumer. (NOTE: This requires persistent storage to compare current data with data in the incoming file) | As per study transaction type (not clear why it happens at both levels - what would it mean to 'delete' at the study level and 'update at the site level?) | |
Screen ID or Number | /GTP/Study/Site/Investigator/Subject/ScreenID |
ResearchSubject.identifier
0..* Identifier |
ResearchSubject.where(subject=Observation.subject).identifier |
Study subject is found by finding the StudySubject that corresponds to the same Patient and ResearchStudy as the Observation. Use the identifier.type to differentiate subject identifiers. No standard way to decide which subject identifier to use. Also no standard way to differentiate subject id from screening id from spare subject id. |
Subject ID or Number | /GTP/Study/Site/Investigator/Subject/SubjectID |
ResearchSubject.identifier
0..* Identifier |
ResearchSubject.where(subject=Observation.subject).identifier |
Study subject is found by finding the StudySubject that corresponds to the same Patient and ResearchStudy as the Observation. Use the identifier.type to differentiate subject identifiers. No standard way to decide which subject identifier to use. Also no standard way to differentiate subject id from screening id from spare subject id. |
Spare subject level ID or Number | /GTP/Study/Site/Investigator/Subject/SpareSubjectID |
ResearchSubject.identifier
0..* Identifier |
ResearchSubject.where(subject=Observation.subject).identifier |
Study subject is found by finding the StudySubject that corresponds to the same Patient and ResearchStudy as the Observation. Use the identifier.type to differentiate subject identifiers. No standard way to decide which subject identifier to use. Also no standard way to differentiate subject id from screening id from spare subject id. |
Subject Sex | /GTP/Study/Site/Investigator/Subject/Sex/@Value |
Patient.gender
0..1 code Binding: AdministrativeGender required |
Observation.subject.resolve().gender |
Consider whether patient personal information is required for sponsor to perform patient reconciliation. If not, do not send this data element. The US-core birth-sex extension (if it is present) may be more consistently populated than Patient.gender. If patient gender is sent, the values must be mapped from the FHIR vocabulary to the LAB standard: male -> M female -> F other -> U unknown -> U |
Subject Sex Code List ID | /GTP/Study/Site/Investigator/Subject/Sex/@CodeListID | For FHIR, the administrative gender code list is fixed, so no need to send it in the instance. If there's a need to convey alternate gender codes, then those would appear either as Observation values or as extension values on Patient (the former for clinical information, the latter for administrative purposes). In either event, the code would be in CodeableConcept.coding which allows identifying both the code system and (if relevant), the version | ||
Subject Race | /GTP/Study/Site/Investigator/Subject/Race/@Value | There's an US-core extension (us-core-race) for capturing this in the U.S. and the possibility that other countries will define their own extensions. Note that this is for administrative, not clinical purposes. Genetic heritage would typically be captured as an Observation | Consider whether patient personal information is required for the sponsor to perform patient reconciliation. If not, do not send this data element. If patient race is sent, use the us-core-race extension in FHIR. |
|
Subject Race Code List ID | /GTP/Study/Site/Investigator/Subject/Race/@CodeListID | Race, whether captured as an extension or observation value is generally a CodeableConcept allowing capturing both code system and (if relevant) version. Race is highly variable from country to country (both whether it's allowed and how it's coded), so there's no standard element for this in the core spec. | ||
Subject Initials | /GTP/Study/Site/Investigator/Subject/Confidential/@Initials |
Patient.name
0..* HumanName |
ResearchSubject.where(subject=Observation.subject).individual.resolve().name.text |
Note that name.text will typically contain the full name, but *can* contain initials only. If the full name is present, initials can be extracted by looking at the given and family name components and converting to initials Consider whether patient personal information is required for the sponsor to perform patient reconciliation. If not, do not send this data element. If required, be sure to send only the initials, derived from the name. |
Subject Date Of Birth | /GTP/Study/Site/Investigator/Subject/Confidential/@Birthdate |
Patient.birthDate
0..1 date |
ResearchSubject.where(subject=Observation.subject).individual.resolve().birthDate |
Note that precision can vary (YYYY, YYYY-MM or YYYY-MM-DD) Consider whether patient personal information is required for the sponsor to perform patient reconciliation. If not, do not send this data element. |
Visit ID or Number | /GTP/Study/Site/Investigator/Subject/Visit/@ID |
Encounter.identifier
0..* Identifier |
Observation.encounter.resolve().identifier |
No standard way to decide which identifier to use if multiples are present |
Visit Name | /GTP/Study/Site/Investigator/Subject/Visit/@Name | In practice, visit name would be the ActivityDefinition.title for the activity in the protocol associated with the encounter. There is no standard extension for this link (though one will be defined). Most clinical systems won't actually capture this, so the determination will need to be made at time of data extraction based on the protocol | See LB mapping comment | |
Visit Type | /GTP/Study/Site/Investigator/Subject/Visit/@Type | This is essentially whether the visit is tied to a particular activity within the PlanDefinition (study protocol) or not. Given that in non-study-specific systems, there won't typically be a linkage even when the encounter *is* driven by the study, this will generally need to be populated algorithmically on extension | This could theoretically be distinguished by whether there was a link to a CarePlan activity or ActivityDefinition. Alternatively, you could use an extension or tag. (What's 'planned' for one study might be unplanned for another) This would be Encounter.reasonCode |
|
Visit Type Modifier | /GTP/Study/Site/Investigator/Subject/Visit/@TypeModifier |
Encounter.reasonCode
0..* CodeableConcept Binding: EncounterReason preferred |
Observation.encounter.resolve().reasonCode |
This would be Encounter.reasonCode |
Subject Transaction Type | /GTP/Study/Site/Investigator/Subject/TransactionType | Determining whether transactions are insert or update is a task for the data consumer. (NOTE: This requires persistent storage to compare current data with data in the incoming file) | As per study transaction type (not clear why it happens at both levels - what would it mean to 'delete' at the study level and 'update at the site level?) | |
Accession ID or Number | /GTP/Study/Site/Investigator/Subject/Visit/Accession/@ID |
Specimen.accessionIdentifier
0..1 Identifier |
Observation.resolve().accessionIdentifier |
|
Last Active Date and Time | /GTP/Study/Site/Investigator/Subject/Visit/Accession/@LastActiveDateTime | FHIR allows capture meta.lastUpdated which reflects when the data last changed on the server in question, but would need to look at Provenance to see when data last changed on a particular system. This typically won't be available. | ||
Visit Transaction Type | /GTP/Study/Site/Investigator/Subject/Visit/Accession/TransactionType | Determining whether transactions are insert or update is a task for the data consumer. (NOTE: This requires persistent storage to compare current data with data in the incoming file) | As per study transaction type (not clear why it happens at both levels - what would it mean to 'delete' at the study level and 'update at the site level?) | |
Central Laboratory ID | /GTP/Study/Site/Investigator/Subject/Visit/Accession/CentralLab/@ID |
Organization.identifier
0..* Identifier org-1 |
Observation.performer.resolve().identifier |
Will need to look at identifier.type or identifier.system to know which identifier to use. In some cases, performer might be PractitionerRole, in which case, will need to map through PractitionerRole.organization. If there are multiple performers that link to multiple organizations, converter will need to look at Organization.type or have other rules to decide amongst the candidates. |
Central Laboratory Name | /GTP/Study/Site/Investigator/Subject/Visit/Accession/CentralLab/@Name |
Organization.name
0..1 string org-1 |
Observation.performer.resolve().name |
If there are multiple performers that link to multiple organizations, converter will need to look at Organization.type or have other rules to decide amongst the candidates. |
Specimen ID or Number | /GTP/Study/Site/Investigator/Subject/Visit/Accession/BaseSpecimen/@ID |
Specimen.identifier
0..* Identifier |
Observation.specimen.resolve().identifier |
If there are multiple identifiers, need to look at identifier.system or identifier.type to determine |
Actual Collection Date and Time | /GTP/Study/Site/Investigator/Subject/Visit/Accession/BaseSpecimen/SpecimenCollection/@ActualCollectionDateTime |
Specimen.collection.collectedDateTime
0..1 dateTime |
Observation.specimen.resolve().collection.collectedDateTime |
Most of the time there'll only be a single dateTime. If collected over a period of time (e.g. urine), use the end. |
Specimen.collection.collectedPeriod
0..1 Period |
Observation.specimen.resolve().collection.collectedPeriod.end |
|||
Planned Collection Time Elapsed | /GTP/Study/Site/Investigator/Subject/Visit/Accession/BaseSpecimen/SpecimenCollection/@PlannedCollectionTimeElapsed |
Specimen.extension
0..* Extension |
Observation.specimen.resolve().extension(cqf-relativeDateTime).valueDuration |
Will need to convert value + units into an ISO duration |
Planned Collection Time Elapsed Description | /GTP/Study/Site/Investigator/Subject/Visit/Accession/BaseSpecimen/SpecimenCollection/@PlannedCollectionTimeElapsedDescription |
Specimen.extension
0..* Extension |
Observation.specimen.resolve().extension(cqf-relativeDateTime).valueReference.reference |
The event would be in extension('target').valueReference.display and the time would be in extension('offset').valueDuration value and unit. |
Collection End Date and Time | /GTP/Study/Site/Investigator/Subject/Visit/Accession/BaseSpecimen/SpecimenCollection/@CollectionEndDateTime |
Specimen.collection.collectedPeriod
0..1 Period |
Observation.specimen.resolve().collection.collectedPeriod.end |
|
Received Date and Time | /GTP/Study/Site/Investigator/Subject/Visit/Accession/BaseSpecimen/SpecimenTransport/@ReceivedDateTime |
Specimen.receivedTime
0..1 dateTime |
Observation.specimen.resolve().receivedTime |
|
Specimen Condition | /GTP/Study/Site/Investigator/Subject/Visit/Accession/BaseSpecimen/SpecimenTransport/@SpecimenCondition |
Specimen.condition
0..* CodeableConcept Binding: SpecimenCondition extensible |
Observation.specimen.resolve().condition.coding.code |
Will need to choose which coding to extract the code for |
Investigator - Specimen Comment Source | /GTP/Study/Site/Investigator/Subject/Visit/Accession/BaseSpecimen/SpecimenComment/@Source |
Practitioner.identifier
0..* Identifier |
Observation.specimen.resolve().note.author.resolve().where($thisisPractitioner).identifier |
Note that practitioners can have multiple identifiers - use type or system to decide which identifier to expose |
Investigator - Specimen Comment Text | /GTP/Study/Site/Investigator/Subject/Visit/Accession/BaseSpecimen/SpecimenComment/@Text |
Specimen.note
0..* Annotation |
Observation.specimen.resolve().where($author.resolve()isPractitioner).note.text |
|
Lab - Specimen Comment Source | /GTP/Study/Site/Investigator/Subject/Visit/Accession/BaseSpecimen/SpecimenComment/@Source |
Observation.identifier
0..* Identifier |
Observation.specimen.resolve().note.author.resolve().where($thisisOrganization).identifier |
Note that organizations can have multiple identifiers - use type or system to decide which identifier to expose |
Lab - Specimen Comment Text | /GTP/Study/Site/Investigator/Subject/Visit/Accession/BaseSpecimen/SpecimenComment/@Text |
Specimen.note
0..* Annotation |
Observation.specimen.resolve().where($author.resolve() isOrganization).note.text |
|
Specimen Material ID | /GTP/Study/Site/Investigator/Subject/Visit/Accession/BaseSpecimen/SpecimenMaterial/@ID |
Specimen.type
0..1 CodeableConcept Binding: SpecimenType example |
Observation.specimen.resolve().where($author.resolve() isOrganization).type.coding.code |
If multiple codes are present, filter based on system |
Specimen Material Name | /GTP/Study/Site/Investigator/Subject/Visit/Accession/BaseSpecimen/SpecimenMaterial/@Name |
Specimen.type
0..1 CodeableConcept Binding: SpecimenType example |
Observation.specimen.resolve().where($author.resolve() isOrganization).type.coding.display |
If multiple codes are present, filter based on system. Translation may be required |
Specimen Material Code List ID | /GTP/Study/Site/Investigator/Subject/Visit/Accession/BaseSpecimen/SpecimenMaterial/@CodeListID |
Specimen.type
0..1 CodeableConcept Binding: SpecimenType example |
Observation.specimen.resolve().where($author.resolve() isOrganization).type.coding.system |
If multiple codings are present, will need to decide which to use |
Subject Age at Collection | /GTP/Study/Site/Investigator/Subject/Visit/Accession/BaseSpecimen/SubjectAtCollection/@AgeAtCollection | If captured as an Observation, this would be in the ValueQuantity.code derived from the patient.birthDate(Observation.subject (ref:Patient.birthDate). If the full birthdate cannot be shared, due to country restrictions, use the birth year to derive age information. Due to lost accuracy in having only the year of birth, 'years' would be the default unit. As an alternative use the cqf-relativeTime extension on Patient.birthDate and convey as a relative time to study enrollment |
||
Subject Age Units | /GTP/Study/Site/Investigator/Subject/Visit/Accession/BaseSpecimen/SubjectAtCollection/@AgeUnits | If captured as an Observation, this would be in the ValueQuantity.code derived from the patient.birthDate(Observation.subject (ref:Patient.birthDate). If the full birthdate cannot be shared, due to country restrictions, use the birth year to derive age information. Due to lost accuracy in having only the year of birth, 'years' would be the default unit. |
||
Fasting Status | /GTP/Study/Site/Investigator/Subject/Visit/Accession/BaseSpecimen/SubjectAtCollection/@FastingStatus |
Specimen.collection.fastingStatusCodeableConcept
0..1 CodeableConcept Binding: FastingStatus extensible |
Observation.specimen.resolve().collection.fastingStatusCodeableConcept.coding.code |
FHIR to CDISC mapping as follows:
F -> Y; NF -> N; FNA -> N/A; NG -> Not Applicable (blank) If fastingStatusDuration is present and non-zero, that would also map to 'Y'. There are times where this can be inferred from the Observation.code. |
Battery ID | /GTP/Study/Site/Investigator/Subject/Visit/Accession/BaseSpecimen/BaseBattery/@ID |
DiagnosticReport.identifier
0..* Identifier |
DiagnosticReport.identifier |
|
Battery Name | /GTP/Study/Site/Investigator/Subject/Visit/Accession/BaseSpecimen/BaseBattery/@Name |
DiagnosticReport.code
1..1 CodeableConcept Binding: DiagnosticReportCodes preferred |
DiagnosticReport.code.text |
|
Battery Transaction Type | /GTP/Study/Site/Investigator/Subject/Visit/Accession/BaseSpecimen/BaseBattery/TransactionType | Determining whether transactions are insert or update is a task for the data consumer. (NOTE: This requires persistent storage to compare current data with data in the incoming file) | As per study transaction type (not clear why it happens at both levels - what would it mean to 'delete' at the study level and 'update at the site level?) | |
Test Status | /GTP/Study/Site/Investigator/Subject/Visit/Accession/BaseSpecimen/BaseBattery/BaseTest/@Status |
ServiceRequest.doNotPerform
0..1 boolean |
Observation.basedOn.resolve().doNotPerform |
Multiple fields are required to populate this value per the standard. Use ServiceRequest.status and ServiceRequest.doNotPerform (doNotPerform will only be true if the test is not to be performed) to represent the test status. From these two fields, sponsors will need to translate to the CDISC test statuses: D - Done N - Not Performed X - Cancelled. |
ServiceRequest.status
1..1 code Binding: ServiceRequestStatus required |
Observation.basedon.resolve().status |
|||
Testing Date and Time | /GTP/Study/Site/Investigator/Subject/Visit/Accession/BaseSpecimen/BaseBattery/BaseTest/@TestingDateTime |
Observation.effectiveDateTime
0..1 dateTime |
Observation.effectiveDateTime |
|
Test Type | /GTP/Study/Site/Investigator/Subject/Visit/Accession/BaseSpecimen/BaseBattery/BaseTest/@TestType | The normal transmission of data, via FHIR, is for study tests. If non-study test results are required (e.g., AdverseEvent follow-up), these would be obtained via a special request from the data provider. Alternatively, if the system requires this field to be populated, derive whether the test was for the study or not, use the ServiceRequest resource. | Differentiation would be whether the Observation was basedOn a particular activity in the CarePlan (scheduled) or tied to a particular ActivityDefinition (Study test) | |
Performing Laboratory ID | /GTP/Study/Site/Investigator/Subject/Visit/Accession/BaseSpecimen/BaseBattery/BaseTest/PerformingLab/@ID |
Organization.identifier
0..* Identifier org-1 |
Observation.performer.resolve().identifier |
Use "Organization/type" to pick the type of Organization that represents "Performing Lab" |
Performing Laboratory Name | /GTP/Study/Site/Investigator/Subject/Visit/Accession/BaseSpecimen/BaseBattery/BaseTest/PerformingLab/@Name |
Organization.name
0..1 string org-1 |
Observation.performer.resolve().name |
|
Lab Test ID | /GTP/Study/Site/Investigator/Subject/Visit/Accession/BaseSpecimen/BaseBattery/BaseTest/LabTest/@ID |
Observation.code
1..1 CodeableConcept Binding: ObservationCode example |
Observation.code.coding.where(system = 'Lab Test') |
Since this is a codeable concept, there may be multiple codes for a test. The sponsor will have to determine which is the "lab's" code vs. "receiver's" code vs. others. |
Lab Test Name | /GTP/Study/Site/Investigator/Subject/Visit/Accession/BaseSpecimen/BaseBattery/BaseTest/LabTest/@Name |
Observation.code
1..1 CodeableConcept Binding: ObservationCode example |
Observation.code.coding.display |
|
Additional Test Description | /GTP/Study/Site/Investigator/Subject/Visit/Accession/BaseSpecimen/BaseBattery/BaseTest/LabTest/@AdditionalDescription |
Observation.note
0..* Annotation |
Observation.note.text |
Not all notes will necessarily be appropriate to map here. Would need to decide whether to also bring across authors and dates and would need to combined multiple repetitions into a single string. |
Receiver Test ID | /GTP/Study/Site/Investigator/Subject/Visit/Accession/BaseSpecimen/BaseBattery/BaseTest/ReceiverTest/@ID |
Observation.code
1..1 CodeableConcept Binding: ObservationCode example |
Observation.code.coding.where (system = [Recipient Test]) |
Observation/code/coding/system/@value ("system" element used to designate the type of code that is being represented (in this case the "ReceiverTest") by the "code" element) |
Receiver Test Name | /GTP/Study/Site/Investigator/Subject/Visit/Accession/BaseSpecimen/BaseBattery/BaseTest/ReceiverTest/@Name |
Observation.code
1..1 CodeableConcept Binding: ObservationCode example |
Observation.code.coding.display where (coding system = 'Recipient Test') |
|
LOINC Code | /GTP/Study/Site/Investigator/Subject/Visit/Accession/BaseSpecimen/BaseBattery/BaseTest/LOINCTestCode/@Value |
Observation.code
1..1 CodeableConcept Binding: ObservationCode example |
Observation.code.coding.where (coding system = 'http://loinc.org') |
|
LOINC Code List ID | /GTP/Study/Site/Investigator/Subject/Visit/Accession/BaseSpecimen/BaseBattery/BaseTest/LOINCTestCode/@CodeListID |
Observation.code
1..1 CodeableConcept Binding: ObservationCode example |
Observation.code.coding.where(system = 'http://loinc.org') |
This will always be 'LOINC' |
Test Level Comments | /GTP/Study/Site/Investigator/Subject/Visit/Accession/BaseSpecimen/BaseBattery/BaseTest/TestLevelComment |
Observation.extension
0..* Extension |
Observation.note.text |
Will need to determine whether to also incude date and author. |
Reported Result Status | /GTP/Study/Site/Investigator/Subject/Visit/Accession/BaseSpecimen/BaseBattery/BaseTest/BaseResult/@ReportedResultStatus |
Observation.status
1..1 code Binding: ObservationStatus required |
Observation.status |
This has two possible values: P for Preliminary and F for Final. |
Alert Flag | /GTP/Study/Site/Investigator/Subject/Visit/Accession/BaseSpecimen/BaseBattery/BaseTest/BaseResult/@AlertFlag |
Observation.interpretation
0..* CodeableConcept Binding: ObservationInterpretation extensible |
Observation.interpretation.coding.where(system='Alert Flag') |
The alert flag generated by the reference ranges applied and tied to the reported result. There are 9 alert flag values: LP - Low Panic LT - Low Telephone LN - Low Normal N - Normal HN - High Normal HT - High Telephone HP - High Panic AB - Abnormal and blank when not used. It may be necessary to translate from other interpretation codings. |
Delta Flag | /GTP/Study/Site/Investigator/Subject/Visit/Accession/BaseSpecimen/BaseBattery/BaseTest/BaseResult/@DeltaFlag |
Observation.interpretation
0..* CodeableConcept Binding: ObservationInterpretation extensible |
Observation.interpretation.coding.where (system = 'Delta Flag') |
The delta flag generated by the reference ranges applied and tied to the reported result. There are three delta flags: D+ for an increase in value D- for a decrease in value and blank for no flag. It may be necessary to translate from other interpretation codings. |
Exclusion Flag | /GTP/Study/Site/Investigator/Subject/Visit/Accession/BaseSpecimen/BaseBattery/BaseTest/BaseResult/@ExclusionFlag |
Observation.interpretation
0..* CodeableConcept Binding: ObservationInterpretation extensible |
Observation.interpretation.coding.where (system = 'Exclusion Flag') |
The exclusion flag generated by the reference ranges applied and tied to the reported result. There are four exclusion flag values: LX - Low Exclusion HX - High Exclusion EX - Excluded (for exclusions not falling under high or low) and blank for no flag. It may be necessary to translate from other |
Blinding Flag | /GTP/Study/Site/Investigator/Subject/Visit/Accession/BaseSpecimen/BaseBattery/BaseTest/BaseResult/@BlindingFlag |
Observation.meta
0..1 Meta |
Observation.meta.security.code |
A new code would likely be needed to specifically designate 'blinded'. |
Reported Date and Time | /GTP/Study/Site/Investigator/Subject/Visit/Accession/BaseSpecimen/BaseBattery/BaseTest/BaseResult/@ReportedDateTime |
Observation.issued
0..1 instant |
Observation.issued |
|
Test Transaction Type | /GTP/Study/Site/Investigator/Subject/Visit/Accession/BaseSpecimen/BaseBattery/BaseTest/TransactionType | Determining whether transactions are insert or update is a task for the data consumer. (NOTE: This requires persistent storage to compare current data with data in the incoming file) | As per study transaction type (not clear why it happens at both levels - what would it mean to 'delete' at the study level and 'update at the site level?) | |
Toxicity Grade | /GTP/Study/Site/Investigator/Subject/Visit/Accession/BaseSpecimen/BaseBattery/BaseTest/BaseResult/ToxicityGrade/@Value |
Observation.interpretation
0..* CodeableConcept Binding: ObservationInterpretation extensible |
Observation.interpretation.coding.where (system = 'Toxicity Grade').code |
If there are multiple possible systems, the sponsor will have to determine which interpretation corresponds to toxicity grade |
Toxicity Grade Code List ID | /GTP/Study/Site/Investigator/Subject/Visit/Accession/BaseSpecimen/BaseBattery/BaseTest/BaseResult/ToxicityGrade/@CodeListID |
Observation.interpretation
0..* CodeableConcept Binding: ObservationInterpretation extensible |
Observation.interpretation.coding.where (system = 'Toxicity Grade') |
May need to translate the URI to a CDISC Code List id |
Reported Result Type (C=coded; N=numeric; T=text; R=range; G = GT; L = LT) | /GTP/Study/Site/Investigator/Subject/Visit/Accession/BaseSpecimen/BaseBattery/BaseTest/BaseResult/SingleResult/@ResultType | Determine from the field in which the result resides. valueCodeableConcept vs. valueQuantity vs. valueString Greater than and less than can be determined by valueQuantity.qualifier | ||
Text Result | /GTP/Study/Site/Investigator/Subject/Visit/Accession/BaseSpecimen/BaseBattery/BaseTest/BaseResult/SingleResult/TextResult/@Value |
Observation.valueCodeableConcept
0..1 CodeableConcept obs-7 |
Observation.valueCodeableConcept.text |
|
Text Result Code List ID | /GTP/Study/Site/Investigator/Subject/Visit/Accession/BaseSpecimen/BaseBattery/BaseTest/BaseResult/SingleResult/TextResult/@CodeListID |
Observation.valueCodeableConcept
0..1 CodeableConcept obs-7 |
Observation.valueCodeableConcept.coding.system |
May need to convert from URI to CDISC-recognized 'ID' |
Conventional Numeric Result | /GTP/Study/Site/Investigator/Subject/Visit/Accession/BaseSpecimen/BaseBattery/BaseTest/BaseResult/SingleResult/NumericResult/@Value |
Observation.valueQuantity
0..1 Quantity obs-7 |
Observation.valueQuantity.value |
If the reported result isn't in conventional units, can use the PQ-translation extension to convey a translation. |
Reported Numeric Result | /GTP/Study/Site/Investigator/Subject/Visit/Accession/BaseSpecimen/BaseBattery/BaseTest/BaseResult/SingleResult/NumericResult/@Value |
Observation.valueQuantity
0..1 Quantity obs-7 |
Observation.valueQuantity.value |
|
SI Numeric Result | /GTP/Study/Site/Investigator/Subject/Visit/Accession/BaseSpecimen/BaseBattery/BaseTest/BaseResult/SingleResult/NumericResult/@Value |
Observation.valueQuantity
0..1 Quantity obs-7 |
Observation.valueQuantity.value |
If the reported result isn't in SI units, can use the PQ-translation extension to convey a translation. |
Conventional Numeric Result Precision | /GTP/Study/Site/Investigator/Subject/Visit/Accession/BaseSpecimen/BaseBattery/BaseTest/BaseResult/SingleResult/NumericResult/@Precision |
Observation.valueQuantity
0..1 Quantity obs-7 |
Observation.valueQuantity.value |
Determine the precision from the value. Precision is implicitly determined from the attribute Observation/valueQuantity/value/@value If the reported result isn't in conventional units, can use the PQ-translation extension to convey a translation. |
Reported Numeric Result Precision | /GTP/Study/Site/Investigator/Subject/Visit/Accession/BaseSpecimen/BaseBattery/BaseTest/BaseResult/SingleResult/NumericResult/@Precision |
Observation.valueQuantity
0..1 Quantity obs-7 |
Observation.valueQuantity.value |
Determine the precision from the value. Precision is implicitly determined from the attribute Observation/valueQuantity/value/@value |
SI Numeric Result Precision | /GTP/Study/Site/Investigator/Subject/Visit/Accession/BaseSpecimen/BaseBattery/BaseTest/BaseResult/SingleResult/NumericResult/@Precision |
Observation.valueQuantity
0..1 Quantity obs-7 |
Observation.valueQuantity.value |
Determine the precision from the value. Precision is implicitly determined from the attribute Observation/valueQuantity/value/@value If the reported result isn't in SI units, can use the PQ-translation extension to convey a translation. |
Conventional Reference Range Low | /GTP/Study/Site/Investigator/Subject/Visit/Accession/BaseSpecimen/BaseBattery/BaseTest/BaseResult/SingleResult/ResultReferenceRange/@ReferenceRangeLow |
Observation.referenceRange
0..* BackboneElement |
Observation.referenceRange.low.value Observation.referenceRange.low.unit |
Will need to combine value and unit into a string. If the reported units aren't 'conventional, can use the PQ-translation extension on Quantity to convey a translation. |
Reported Reference Range Low | /GTP/Study/Site/Investigator/Subject/Visit/Accession/BaseSpecimen/BaseBattery/BaseTest/BaseResult/SingleResult/ResultReferenceRange/@ReferenceRangeLow |
Observation.referenceRange
0..* BackboneElement |
Observation.referenceRange.low.value Observation.referenceRange.low.unit |
Will need to combine value and unit into a string. |
SI Reference Range Low | /GTP/Study/Site/Investigator/Subject/Visit/Accession/BaseSpecimen/BaseBattery/BaseTest/BaseResult/SingleResult/ResultReferenceRange/@ReferenceRangeLow |
Observation.referenceRange
0..* BackboneElement |
Observation.referenceRange.low.value Observation.referenceRange.low.unit |
Will need to combine value and unit into a string. If the reported units aren't SI, can use the PQ-translation extension on Quantity to convey a translation. |
Conventional Reference Range High | /GTP/Study/Site/Investigator/Subject/Visit/Accession/BaseSpecimen/BaseBattery/BaseTest/BaseResult/SingleResult/ResultReferenceRange/@ReferenceRangeHigh |
Observation.referenceRange
0..* BackboneElement |
Observation.referenceRange.high.value Observation.referenceRange.high.unit |
Will need to combine value and unit into a string. If the reported units aren't 'conventional, can use the PQ-translation extension on Quantity to convey a translation. |
Reported Reference Range High | /GTP/Study/Site/Investigator/Subject/Visit/Accession/BaseSpecimen/BaseBattery/BaseTest/BaseResult/SingleResult/ResultReferenceRange/@ReferenceRangeHigh |
Observation.referenceRange
0..* BackboneElement |
Observation.referenceRange.high.value Observation.referenceRange.high.unit |
Will need to combine value and unit into a string. |
SI Reference Range High | /GTP/Study/Site/Investigator/Subject/Visit/Accession/BaseSpecimen/BaseBattery/BaseTest/BaseResult/SingleResult/ResultReferenceRange/@ReferenceRangeHigh |
Observation.referenceRange
0..* BackboneElement |
Observation.referenceRange.high.value Observation.referenceRange.high.unit |
Will need to combine value and unit into a string. If the reported units aren't SI, can use the PQ-translation extension on Quantity to convey a translation. |
Conventional Units | /GTP/Study/Site/Investigator/Subject/Visit/Accession/BaseSpecimen/BaseBattery/BaseTest/BaseResult/SingleResult/ResultUnits/@Value |
Observation.valueQuantity
0..1 Quantity obs-7 |
Observation.valueQuantity.code Observation.valueQuantity.extension('PQ-translation').code |
If the reported units are not the same as conventional, can convey with the translation extension. Translation to CDISC terminology may be required. |
Reported Units | /GTP/Study/Site/Investigator/Subject/Visit/Accession/BaseSpecimen/BaseBattery/BaseTest/BaseResult/SingleResult/ResultUnits/@Value |
Observation.valueQuantity
0..1 Quantity obs-7 |
Observation.valueQuantity.code |
This may require translation to CDISC terminology |
SI Units | /GTP/Study/Site/Investigator/Subject/Visit/Accession/BaseSpecimen/BaseBattery/BaseTest/BaseResult/SingleResult/ResultUnits/@Value |
Observation.valueQuantity
0..1 Quantity obs-7 |
Observation.valueQuantity.code Observation.valueQuantity.extensoin('PQ-translation').code |
If the reported units are not SI units, can convey with the translation extension. Translation to CDISC terminology may be required. |
Conventional Units Code List ID | /GTP/Study/Site/Investigator/Subject/Visit/Accession/BaseSpecimen/BaseBattery/BaseTest/BaseResult/SingleResult/ResultUnits/@CodeListID |
Observation.valueQuantity
0..1 Quantity obs-7 |
Observation.valueQuantity.system |
|
Reported Units Code List ID | /GTP/Study/Site/Investigator/Subject/Visit/Accession/BaseSpecimen/BaseBattery/BaseTest/BaseResult/SingleResult/ResultUnits/@CodeListID |
Observation.valueQuantity
0..1 Quantity obs-7 |
Observation.valueQuantity.system |
|
SI Units Code List ID | /GTP/Study/Site/Investigator/Subject/Visit/Accession/BaseSpecimen/BaseBattery/BaseTest/BaseResult/SingleResult/ResultUnits/@CodeListID |
Observation.valueQuantity
0..1 Quantity obs-7 |
Observation.valueQuantity.system |
|
Record Extension Type | Populate with a default value, depending on the type of data being transmitted. E.g., "BASE" |
|||
Result Class | /GTP/Study/Site/Investigator/Subject/Visit/Accession/BaseSpecimen/BaseBattery/BaseTest/BaseResult/SingleResult/ResultClass/@Value | In FHIR, the value will always be 'reported', with the ability to convey translations in an extension. Nothing will differentiate whether a translation is considered 'conventional' or not, so if there are multiple translations present and the receiver can't figure it out by looking, an additional extension may be necessary to differentiate. This can sometimes also be inferred from the Observation.code. | ||
Model Version | /GTP/@ModelVersion | Populate with a default value. | The version of FHIR in use is conveyed using Resource.meta.profile | |
File Creation Date and Time | /GTP/@CreationDateTime | Pull from the message wrapper. (file creation date) | Bundle.timestamp | |
Transaction Type | /GTP/TransactionType | No mitigation. If required, fill with a default value. | As per study.transaction | |
Transmission Source ID | /GTP/TransmissionSource/@ID | Pull from the message wrapper. | MessageHeader.source.endpoint if using messaging, otherwise determined out of band based on sender authentication process | |
Transmission Source Name | /GTP/TransmissionSource/@Name | Pull from the message wrapper. | MessageHeader.source.name if using messaging, otherwise determined out of band based on sender authentication process |
FHIR map (or gap) | CDISC | Comment | ||
---|---|---|---|---|
Label | Element | FHIRPath | LAB | |
Study ID or Number |
ResearchStudy.identifier
0..* Identifier |
ResearchSubject.where(subject=Observation.subject).study.resolve().partOf.resolve().identifier Observation.extension(workflow-researchstudy).valueReference.resolve().partOf.resolve().identifier |
/GTP/Study/@ID | Mapping is based on presumption that research subject will be tied to site-specific ResearchStudy, which will then be part of overall ResearchStudy. The path using the extension will only exist if the system maintaining the Observation is aware of its relevance to the Study. |
Study Name |
ResearchStudy.title
0..1 string |
ResearchSubject.where(subject=Observation.subject).study.resolve()partOf.resolve().title Observation.extension(workflow-researchstudy).valueReference.resolve().partOf.resolve().title |
/GTP/Study/@Name | Mapping is based on presumption that research subject will be tied to site-specific ResearchStudy, which will then be part of overall ResearchStudy. The path using the extension will only exist if the system maintaining the Observation is aware of its relevance to the Study. |
Transmission Type | Determine from type of call to service (requesting all data vs incremental data - e.g. filtering by _lastUpdated). NOTE: If data are not persisted between FHIR and LAB, cannot do incremental data processing (due to lack of delta detection). | /GTP/Study/@TransmissionType | If the data is being 'pushed' in a message, this could also be conveyed in the MessageHeader.event | |
Study Transaction Type | Determining whether transactions are insert or update is a task for the data consumer. (NOTE: This requires persistent storage to compare current data with data in the incoming file) | /GTP/Study/TransactionType | This would be conveyed using a transaction Bundle - specifically the Bundle.entry.request.method | |
Site ID or Number |
ResearchStudy.site
0..* Reference |
ResearchSubject.where(subject=Observation.subject).study.resolve().identifier Observation.extension(workflow-researchstudy).valueReference.resolve().identifier |
/GTP/Study/Site/@ID | Mapping is based on presumption that research subject will be tied to site-specific ResearchStudy, which will then be part of overall ResearchStudy. The path using the extension will only exist if the system maintaining the Observation is aware of its relevance to the Study. |
Site Transaction Type | Determining whether transactions are insert or update is a task for the data consumer. (NOTE: This requires persistent storage to compare current data with data in the incoming file) | /GTP/Study/Site/TransactionType | As per study transaction type (not clear why it happens at both levels - what would it mean to 'delete' at the study level and 'update at the site level?) | |
Investigator ID or Number |
Practitioner.identifier
0..* Identifier |
ResearchSubject.where(subject=Observation.subject).study.resolve().principleInvestigator.resolve().identifier Observation.extension(workflow-researchstudy).valueReference.resolve().principleInvestigator.resolve().identifier |
/GTP/Study/Site/Investigator/@ID | Will need to decide which id to expose or may need to map to one recognized by the sponsor. If you want the PI for the overall study rather than just for the site associated with the Observation, you'll need to traverse the partOf.resolve() from the study-specific ResearchStudy. The path using the extension will only exist if the system maintaining the Observation is aware of its relevance to the Study. |
Investigator Name |
Practitioner.name
0..* HumanName |
ResearchSubject.where(subject=Observation.subject).study.resolve().principleInvestigator.resolve().name.text Observation.extension(workflow-researchstudy).valueReference.resolve().principleInvestigator.resolve().name.text |
/GTP/Study/Site/Investigator/@Name | Will need to decide which name to expose if there are multiples. Can also extract the prefix, given and family names if text isn't specified. The path using the extension will only exist if the system maintaining the Observation is aware of its relevance to the Study. |
Investigator Transaction Type | Determining whether transactions are insert or update is a task for the data consumer. (NOTE: This requires persistent storage to compare current data with data in the incoming file) | /GTP/Study/Site/Investigator/TransactionType | As per study transaction type (not clear why it happens at both levels - what would it mean to 'delete' at the study level and 'update at the site level?) | |
Screen ID or Number |
ResearchSubject.identifier
0..* Identifier |
ResearchSubject.where(subject=Observation.subject).identifier |
/GTP/Study/Site/Investigator/Subject/ScreenID | Study subject is found by finding the StudySubject that corresponds to the same Patient and ResearchStudy as the Observation. Use the identifier.type to differentiate subject identifiers. No standard way to decide which subject identifier to use. Also no standard way to differentiate subject id from screening id from spare subject id. |
Subject ID or Number |
ResearchSubject.identifier
0..* Identifier |
ResearchSubject.where(subject=Observation.subject).identifier |
/GTP/Study/Site/Investigator/Subject/SubjectID | Study subject is found by finding the StudySubject that corresponds to the same Patient and ResearchStudy as the Observation. Use the identifier.type to differentiate subject identifiers. No standard way to decide which subject identifier to use. Also no standard way to differentiate subject id from screening id from spare subject id. |
Spare subject level ID or Number |
ResearchSubject.identifier
0..* Identifier |
ResearchSubject.where(subject=Observation.subject).identifier |
/GTP/Study/Site/Investigator/Subject/SpareSubjectID | Study subject is found by finding the StudySubject that corresponds to the same Patient and ResearchStudy as the Observation. Use the identifier.type to differentiate subject identifiers. No standard way to decide which subject identifier to use. Also no standard way to differentiate subject id from screening id from spare subject id. |
Subject Sex |
Patient.gender
0..1 code Binding: AdministrativeGender required |
Observation.subject.resolve().gender |
/GTP/Study/Site/Investigator/Subject/Sex/@Value | Consider whether patient personal information is required for sponsor to perform patient reconciliation. If not, do not send this data element. The US-core birth-sex extension (if it is present) may be more consistently populated than Patient.gender. If patient gender is sent, the values must be mapped from the FHIR vocabulary to the LAB standard: male -> M female -> F other -> U unknown -> U |
Subject Sex Code List ID | For FHIR, the administrative gender code list is fixed, so no need to send it in the instance. If there's a need to convey alternate gender codes, then those would appear either as Observation values or as extension values on Patient (the former for clinical information, the latter for administrative purposes). In either event, the code would be in CodeableConcept.coding which allows identifying both the code system and (if relevant), the version | /GTP/Study/Site/Investigator/Subject/Sex/@CodeListID | ||
Subject Race | There's an US-core extension (us-core-race) for capturing this in the U.S. and the possibility that other countries will define their own extensions. Note that this is for administrative, not clinical purposes. Genetic heritage would typically be captured as an Observation | /GTP/Study/Site/Investigator/Subject/Race/@Value | Consider whether patient personal information is required for the sponsor to perform patient reconciliation. If not, do not send this data element. If patient race is sent, use the us-core-race extension in FHIR. |
|
Subject Race Code List ID | Race, whether captured as an extension or observation value is generally a CodeableConcept allowing capturing both code system and (if relevant) version. Race is highly variable from country to country (both whether it's allowed and how it's coded), so there's no standard element for this in the core spec. | /GTP/Study/Site/Investigator/Subject/Race/@CodeListID | ||
Subject Initials |
Patient.name
0..* HumanName |
ResearchSubject.where(subject=Observation.subject).individual.resolve().name.text |
/GTP/Study/Site/Investigator/Subject/Confidential/@Initials | Note that name.text will typically contain the full name, but *can* contain initials only. If the full name is present, initials can be extracted by looking at the given and family name components and converting to initials Consider whether patient personal information is required for the sponsor to perform patient reconciliation. If not, do not send this data element. If required, be sure to send only the initials, derived from the name. |
Subject Date Of Birth |
Patient.birthDate
0..1 date |
ResearchSubject.where(subject=Observation.subject).individual.resolve().birthDate |
/GTP/Study/Site/Investigator/Subject/Confidential/@Birthdate | Note that precision can vary (YYYY, YYYY-MM or YYYY-MM-DD) Consider whether patient personal information is required for the sponsor to perform patient reconciliation. If not, do not send this data element. |
Visit ID or Number |
Encounter.identifier
0..* Identifier |
Observation.encounter.resolve().identifier |
/GTP/Study/Site/Investigator/Subject/Visit/@ID | No standard way to decide which identifier to use if multiples are present |
Visit Name | In practice, visit name would be the ActivityDefinition.title for the activity in the protocol associated with the encounter. There is no standard extension for this link (though one will be defined). Most clinical systems won't actually capture this, so the determination will need to be made at time of data extraction based on the protocol | /GTP/Study/Site/Investigator/Subject/Visit/@Name | See LB mapping comment | |
Visit Type | This is essentially whether the visit is tied to a particular activity within the PlanDefinition (study protocol) or not. Given that in non-study-specific systems, there won't typically be a linkage even when the encounter *is* driven by the study, this will generally need to be populated algorithmically on extension | /GTP/Study/Site/Investigator/Subject/Visit/@Type | This could theoretically be distinguished by whether there was a link to a CarePlan activity or ActivityDefinition. Alternatively, you could use an extension or tag. (What's 'planned' for one study might be unplanned for another) This would be Encounter.reasonCode |
|
Visit Type Modifier |
Encounter.reasonCode
0..* CodeableConcept Binding: EncounterReason preferred |
Observation.encounter.resolve().reasonCode |
/GTP/Study/Site/Investigator/Subject/Visit/@TypeModifier | This would be Encounter.reasonCode |
Subject Transaction Type | Determining whether transactions are insert or update is a task for the data consumer. (NOTE: This requires persistent storage to compare current data with data in the incoming file) | /GTP/Study/Site/Investigator/Subject/TransactionType | As per study transaction type (not clear why it happens at both levels - what would it mean to 'delete' at the study level and 'update at the site level?) | |
Accession ID or Number |
Specimen.accessionIdentifier
0..1 Identifier |
Observation.resolve().accessionIdentifier |
/GTP/Study/Site/Investigator/Subject/Visit/Accession/@ID | |
Last Active Date and Time | FHIR allows capture meta.lastUpdated which reflects when the data last changed on the server in question, but would need to look at Provenance to see when data last changed on a particular system. This typically won't be available. | /GTP/Study/Site/Investigator/Subject/Visit/Accession/@LastActiveDateTime | ||
Visit Transaction Type | Determining whether transactions are insert or update is a task for the data consumer. (NOTE: This requires persistent storage to compare current data with data in the incoming file) | /GTP/Study/Site/Investigator/Subject/Visit/Accession/TransactionType | As per study transaction type (not clear why it happens at both levels - what would it mean to 'delete' at the study level and 'update at the site level?) | |
Central Laboratory ID |
Organization.identifier
0..* Identifier org-1 |
Observation.performer.resolve().identifier |
/GTP/Study/Site/Investigator/Subject/Visit/Accession/CentralLab/@ID | Will need to look at identifier.type or identifier.system to know which identifier to use. In some cases, performer might be PractitionerRole, in which case, will need to map through PractitionerRole.organization. If there are multiple performers that link to multiple organizations, converter will need to look at Organization.type or have other rules to decide amongst the candidates. |
Central Laboratory Name |
Organization.name
0..1 string org-1 |
Observation.performer.resolve().name |
/GTP/Study/Site/Investigator/Subject/Visit/Accession/CentralLab/@Name | If there are multiple performers that link to multiple organizations, converter will need to look at Organization.type or have other rules to decide amongst the candidates. |
Specimen ID or Number |
Specimen.identifier
0..* Identifier |
Observation.specimen.resolve().identifier |
/GTP/Study/Site/Investigator/Subject/Visit/Accession/BaseSpecimen/@ID | If there are multiple identifiers, need to look at identifier.system or identifier.type to determine |
Actual Collection Date and Time |
Specimen.collection.collectedDateTime
0..1 dateTime |
Observation.specimen.resolve().collection.collectedDateTime |
/GTP/Study/Site/Investigator/Subject/Visit/Accession/BaseSpecimen/SpecimenCollection/@ActualCollectionDateTime | Most of the time there'll only be a single dateTime. If collected over a period of time (e.g. urine), use the end. |
Specimen.collection.collectedPeriod
0..1 Period |
Observation.specimen.resolve().collection.collectedPeriod.end |
|||
Planned Collection Time Elapsed |
Specimen.extension
0..* Extension |
Observation.specimen.resolve().extension(cqf-relativeDateTime).valueDuration |
/GTP/Study/Site/Investigator/Subject/Visit/Accession/BaseSpecimen/SpecimenCollection/@PlannedCollectionTimeElapsed | Will need to convert value + units into an ISO duration |
Planned Collection Time Elapsed Description |
Specimen.extension
0..* Extension |
Observation.specimen.resolve().extension(cqf-relativeDateTime).valueReference.reference |
/GTP/Study/Site/Investigator/Subject/Visit/Accession/BaseSpecimen/SpecimenCollection/@PlannedCollectionTimeElapsedDescription | The event would be in extension('target').valueReference.display and the time would be in extension('offset').valueDuration value and unit. |
Collection End Date and Time |
Specimen.collection.collectedPeriod
0..1 Period |
Observation.specimen.resolve().collection.collectedPeriod.end |
/GTP/Study/Site/Investigator/Subject/Visit/Accession/BaseSpecimen/SpecimenCollection/@CollectionEndDateTime | |
Received Date and Time |
Specimen.receivedTime
0..1 dateTime |
Observation.specimen.resolve().receivedTime |
/GTP/Study/Site/Investigator/Subject/Visit/Accession/BaseSpecimen/SpecimenTransport/@ReceivedDateTime | |
Specimen Condition |
Specimen.condition
0..* CodeableConcept Binding: SpecimenCondition extensible |
Observation.specimen.resolve().condition.coding.code |
/GTP/Study/Site/Investigator/Subject/Visit/Accession/BaseSpecimen/SpecimenTransport/@SpecimenCondition | Will need to choose which coding to extract the code for |
Investigator - Specimen Comment Source |
Practitioner.identifier
0..* Identifier |
Observation.specimen.resolve().note.author.resolve().where($thisisPractitioner).identifier |
/GTP/Study/Site/Investigator/Subject/Visit/Accession/BaseSpecimen/SpecimenComment/@Source | Note that practitioners can have multiple identifiers - use type or system to decide which identifier to expose |
Investigator - Specimen Comment Text |
Specimen.note
0..* Annotation |
Observation.specimen.resolve().where($author.resolve()isPractitioner).note.text |
/GTP/Study/Site/Investigator/Subject/Visit/Accession/BaseSpecimen/SpecimenComment/@Text | |
Lab - Specimen Comment Source |
Observation.identifier
0..* Identifier |
Observation.specimen.resolve().note.author.resolve().where($thisisOrganization).identifier |
/GTP/Study/Site/Investigator/Subject/Visit/Accession/BaseSpecimen/SpecimenComment/@Source | Note that organizations can have multiple identifiers - use type or system to decide which identifier to expose |
Lab - Specimen Comment Text |
Specimen.note
0..* Annotation |
Observation.specimen.resolve().where($author.resolve() isOrganization).note.text |
/GTP/Study/Site/Investigator/Subject/Visit/Accession/BaseSpecimen/SpecimenComment/@Text | |
Specimen Material ID |
Specimen.type
0..1 CodeableConcept Binding: SpecimenType example |
Observation.specimen.resolve().where($author.resolve() isOrganization).type.coding.code |
/GTP/Study/Site/Investigator/Subject/Visit/Accession/BaseSpecimen/SpecimenMaterial/@ID | If multiple codes are present, filter based on system |
Specimen Material Name |
Specimen.type
0..1 CodeableConcept Binding: SpecimenType example |
Observation.specimen.resolve().where($author.resolve() isOrganization).type.coding.display |
/GTP/Study/Site/Investigator/Subject/Visit/Accession/BaseSpecimen/SpecimenMaterial/@Name | If multiple codes are present, filter based on system. Translation may be required |
Specimen Material Code List ID |
Specimen.type
0..1 CodeableConcept Binding: SpecimenType example |
Observation.specimen.resolve().where($author.resolve() isOrganization).type.coding.system |
/GTP/Study/Site/Investigator/Subject/Visit/Accession/BaseSpecimen/SpecimenMaterial/@CodeListID | If multiple codings are present, will need to decide which to use |
Subject Age at Collection | If captured as an Observation, this would be in the ValueQuantity.code derived from the patient.birthDate(Observation.subject (ref:Patient.birthDate). If the full birthdate cannot be shared, due to country restrictions, use the birth year to derive age information. Due to lost accuracy in having only the year of birth, 'years' would be the default unit. As an alternative use the cqf-relativeTime extension on Patient.birthDate and convey as a relative time to study enrollment |
/GTP/Study/Site/Investigator/Subject/Visit/Accession/BaseSpecimen/SubjectAtCollection/@AgeAtCollection | ||
Subject Age Units | If captured as an Observation, this would be in the ValueQuantity.code derived from the patient.birthDate(Observation.subject (ref:Patient.birthDate). If the full birthdate cannot be shared, due to country restrictions, use the birth year to derive age information. Due to lost accuracy in having only the year of birth, 'years' would be the default unit. |
/GTP/Study/Site/Investigator/Subject/Visit/Accession/BaseSpecimen/SubjectAtCollection/@AgeUnits | ||
Fasting Status |
Specimen.collection.fastingStatusCodeableConcept
0..1 CodeableConcept Binding: FastingStatus extensible |
Observation.specimen.resolve().collection.fastingStatusCodeableConcept.coding.code |
/GTP/Study/Site/Investigator/Subject/Visit/Accession/BaseSpecimen/SubjectAtCollection/@FastingStatus | FHIR to CDISC mapping as follows:
F -> Y; NF -> N; FNA -> N/A; NG -> Not Applicable (blank) If fastingStatusDuration is present and non-zero, that would also map to 'Y'. There are times where this can be inferred from the Observation.code. |
Battery ID |
DiagnosticReport.identifier
0..* Identifier |
DiagnosticReport.identifier |
/GTP/Study/Site/Investigator/Subject/Visit/Accession/BaseSpecimen/BaseBattery/@ID | |
Battery Name |
DiagnosticReport.code
1..1 CodeableConcept Binding: DiagnosticReportCodes preferred |
DiagnosticReport.code.text |
/GTP/Study/Site/Investigator/Subject/Visit/Accession/BaseSpecimen/BaseBattery/@Name | |
Battery Transaction Type | Determining whether transactions are insert or update is a task for the data consumer. (NOTE: This requires persistent storage to compare current data with data in the incoming file) | /GTP/Study/Site/Investigator/Subject/Visit/Accession/BaseSpecimen/BaseBattery/TransactionType | As per study transaction type (not clear why it happens at both levels - what would it mean to 'delete' at the study level and 'update at the site level?) | |
Test Status |
ServiceRequest.doNotPerform
0..1 boolean |
Observation.basedOn.resolve().doNotPerform |
/GTP/Study/Site/Investigator/Subject/Visit/Accession/BaseSpecimen/BaseBattery/BaseTest/@Status | Multiple fields are required to populate this value per the standard. Use ServiceRequest.status and ServiceRequest.doNotPerform (doNotPerform will only be true if the test is not to be performed) to represent the test status. From these two fields, sponsors will need to translate to the CDISC test statuses: D - Done N - Not Performed X - Cancelled. |
ServiceRequest.status
1..1 code Binding: ServiceRequestStatus required |
Observation.basedon.resolve().status |
|||
Testing Date and Time |
Observation.effectiveDateTime
0..1 dateTime |
Observation.effectiveDateTime |
/GTP/Study/Site/Investigator/Subject/Visit/Accession/BaseSpecimen/BaseBattery/BaseTest/@TestingDateTime | |
Test Type | The normal transmission of data, via FHIR, is for study tests. If non-study test results are required (e.g., AdverseEvent follow-up), these would be obtained via a special request from the data provider. Alternatively, if the system requires this field to be populated, derive whether the test was for the study or not, use the ServiceRequest resource. | /GTP/Study/Site/Investigator/Subject/Visit/Accession/BaseSpecimen/BaseBattery/BaseTest/@TestType | Differentiation would be whether the Observation was basedOn a particular activity in the CarePlan (scheduled) or tied to a particular ActivityDefinition (Study test) | |
Performing Laboratory ID |
Organization.identifier
0..* Identifier org-1 |
Observation.performer.resolve().identifier |
/GTP/Study/Site/Investigator/Subject/Visit/Accession/BaseSpecimen/BaseBattery/BaseTest/PerformingLab/@ID | Use "Organization/type" to pick the type of Organization that represents "Performing Lab" |
Performing Laboratory Name |
Organization.name
0..1 string org-1 |
Observation.performer.resolve().name |
/GTP/Study/Site/Investigator/Subject/Visit/Accession/BaseSpecimen/BaseBattery/BaseTest/PerformingLab/@Name | |
Lab Test ID |
Observation.code
1..1 CodeableConcept Binding: ObservationCode example |
Observation.code.coding.where(system = 'Lab Test') |
/GTP/Study/Site/Investigator/Subject/Visit/Accession/BaseSpecimen/BaseBattery/BaseTest/LabTest/@ID | Since this is a codeable concept, there may be multiple codes for a test. The sponsor will have to determine which is the "lab's" code vs. "receiver's" code vs. others. |
Lab Test Name |
Observation.code
1..1 CodeableConcept Binding: ObservationCode example |
Observation.code.coding.display |
/GTP/Study/Site/Investigator/Subject/Visit/Accession/BaseSpecimen/BaseBattery/BaseTest/LabTest/@Name | |
Additional Test Description |
Observation.note
0..* Annotation |
Observation.note.text |
/GTP/Study/Site/Investigator/Subject/Visit/Accession/BaseSpecimen/BaseBattery/BaseTest/LabTest/@AdditionalDescription | Not all notes will necessarily be appropriate to map here. Would need to decide whether to also bring across authors and dates and would need to combined multiple repetitions into a single string. |
Receiver Test ID |
Observation.code
1..1 CodeableConcept Binding: ObservationCode example |
Observation.code.coding.where (system = [Recipient Test]) |
/GTP/Study/Site/Investigator/Subject/Visit/Accession/BaseSpecimen/BaseBattery/BaseTest/ReceiverTest/@ID | Observation/code/coding/system/@value ("system" element used to designate the type of code that is being represented (in this case the "ReceiverTest") by the "code" element) |
Receiver Test Name |
Observation.code
1..1 CodeableConcept Binding: ObservationCode example |
Observation.code.coding.display where (coding system = 'Recipient Test') |
/GTP/Study/Site/Investigator/Subject/Visit/Accession/BaseSpecimen/BaseBattery/BaseTest/ReceiverTest/@Name | |
LOINC Code |
Observation.code
1..1 CodeableConcept Binding: ObservationCode example |
Observation.code.coding.where (coding system = 'http://loinc.org') |
/GTP/Study/Site/Investigator/Subject/Visit/Accession/BaseSpecimen/BaseBattery/BaseTest/LOINCTestCode/@Value | |
LOINC Code List ID |
Observation.code
1..1 CodeableConcept Binding: ObservationCode example |
Observation.code.coding.where(system = 'http://loinc.org') |
/GTP/Study/Site/Investigator/Subject/Visit/Accession/BaseSpecimen/BaseBattery/BaseTest/LOINCTestCode/@CodeListID | This will always be 'LOINC' |
Test Level Comments |
Observation.extension
0..* Extension |
Observation.note.text |
/GTP/Study/Site/Investigator/Subject/Visit/Accession/BaseSpecimen/BaseBattery/BaseTest/TestLevelComment | Will need to determine whether to also incude date and author. |
Reported Result Status |
Observation.status
1..1 code Binding: ObservationStatus required |
Observation.status |
/GTP/Study/Site/Investigator/Subject/Visit/Accession/BaseSpecimen/BaseBattery/BaseTest/BaseResult/@ReportedResultStatus | This has two possible values: P for Preliminary and F for Final. |
Alert Flag |
Observation.interpretation
0..* CodeableConcept Binding: ObservationInterpretation extensible |
Observation.interpretation.coding.where(system='Alert Flag') |
/GTP/Study/Site/Investigator/Subject/Visit/Accession/BaseSpecimen/BaseBattery/BaseTest/BaseResult/@AlertFlag | The alert flag generated by the reference ranges applied and tied to the reported result. There are 9 alert flag values: LP - Low Panic LT - Low Telephone LN - Low Normal N - Normal HN - High Normal HT - High Telephone HP - High Panic AB - Abnormal and blank when not used. It may be necessary to translate from other interpretation codings. |
Delta Flag |
Observation.interpretation
0..* CodeableConcept Binding: ObservationInterpretation extensible |
Observation.interpretation.coding.where (system = 'Delta Flag') |
/GTP/Study/Site/Investigator/Subject/Visit/Accession/BaseSpecimen/BaseBattery/BaseTest/BaseResult/@DeltaFlag | The delta flag generated by the reference ranges applied and tied to the reported result. There are three delta flags: D+ for an increase in value D- for a decrease in value and blank for no flag. It may be necessary to translate from other interpretation codings. |
Exclusion Flag |
Observation.interpretation
0..* CodeableConcept Binding: ObservationInterpretation extensible |
Observation.interpretation.coding.where (system = 'Exclusion Flag') |
/GTP/Study/Site/Investigator/Subject/Visit/Accession/BaseSpecimen/BaseBattery/BaseTest/BaseResult/@ExclusionFlag | The exclusion flag generated by the reference ranges applied and tied to the reported result. There are four exclusion flag values: LX - Low Exclusion HX - High Exclusion EX - Excluded (for exclusions not falling under high or low) and blank for no flag. It may be necessary to translate from other |
Blinding Flag |
Observation.meta
0..1 Meta |
Observation.meta.security.code |
/GTP/Study/Site/Investigator/Subject/Visit/Accession/BaseSpecimen/BaseBattery/BaseTest/BaseResult/@BlindingFlag | A new code would likely be needed to specifically designate 'blinded'. |
Reported Date and Time |
Observation.issued
0..1 instant |
Observation.issued |
/GTP/Study/Site/Investigator/Subject/Visit/Accession/BaseSpecimen/BaseBattery/BaseTest/BaseResult/@ReportedDateTime | |
Test Transaction Type | Determining whether transactions are insert or update is a task for the data consumer. (NOTE: This requires persistent storage to compare current data with data in the incoming file) | /GTP/Study/Site/Investigator/Subject/Visit/Accession/BaseSpecimen/BaseBattery/BaseTest/TransactionType | As per study transaction type (not clear why it happens at both levels - what would it mean to 'delete' at the study level and 'update at the site level?) | |
Toxicity Grade |
Observation.interpretation
0..* CodeableConcept Binding: ObservationInterpretation extensible |
Observation.interpretation.coding.where (system = 'Toxicity Grade').code |
/GTP/Study/Site/Investigator/Subject/Visit/Accession/BaseSpecimen/BaseBattery/BaseTest/BaseResult/ToxicityGrade/@Value | If there are multiple possible systems, the sponsor will have to determine which interpretation corresponds to toxicity grade |
Toxicity Grade Code List ID |
Observation.interpretation
0..* CodeableConcept Binding: ObservationInterpretation extensible |
Observation.interpretation.coding.where (system = 'Toxicity Grade') |
/GTP/Study/Site/Investigator/Subject/Visit/Accession/BaseSpecimen/BaseBattery/BaseTest/BaseResult/ToxicityGrade/@CodeListID | May need to translate the URI to a CDISC Code List id |
Reported Result Type (C=coded; N=numeric; T=text; R=range; G = GT; L = LT) | Determine from the field in which the result resides. valueCodeableConcept vs. valueQuantity vs. valueString Greater than and less than can be determined by valueQuantity.qualifier | /GTP/Study/Site/Investigator/Subject/Visit/Accession/BaseSpecimen/BaseBattery/BaseTest/BaseResult/SingleResult/@ResultType | ||
Text Result |
Observation.valueCodeableConcept
0..1 CodeableConcept obs-7 |
Observation.valueCodeableConcept.text |
/GTP/Study/Site/Investigator/Subject/Visit/Accession/BaseSpecimen/BaseBattery/BaseTest/BaseResult/SingleResult/TextResult/@Value | |
Text Result Code List ID |
Observation.valueCodeableConcept
0..1 CodeableConcept obs-7 |
Observation.valueCodeableConcept.coding.system |
/GTP/Study/Site/Investigator/Subject/Visit/Accession/BaseSpecimen/BaseBattery/BaseTest/BaseResult/SingleResult/TextResult/@CodeListID | May need to convert from URI to CDISC-recognized 'ID' |
Conventional Numeric Result |
Observation.valueQuantity
0..1 Quantity obs-7 |
Observation.valueQuantity.value |
/GTP/Study/Site/Investigator/Subject/Visit/Accession/BaseSpecimen/BaseBattery/BaseTest/BaseResult/SingleResult/NumericResult/@Value | If the reported result isn't in conventional units, can use the PQ-translation extension to convey a translation. |
Reported Numeric Result |
Observation.valueQuantity
0..1 Quantity obs-7 |
Observation.valueQuantity.value |
/GTP/Study/Site/Investigator/Subject/Visit/Accession/BaseSpecimen/BaseBattery/BaseTest/BaseResult/SingleResult/NumericResult/@Value | |
SI Numeric Result |
Observation.valueQuantity
0..1 Quantity obs-7 |
Observation.valueQuantity.value |
/GTP/Study/Site/Investigator/Subject/Visit/Accession/BaseSpecimen/BaseBattery/BaseTest/BaseResult/SingleResult/NumericResult/@Value | If the reported result isn't in SI units, can use the PQ-translation extension to convey a translation. |
Conventional Numeric Result Precision |
Observation.valueQuantity
0..1 Quantity obs-7 |
Observation.valueQuantity.value |
/GTP/Study/Site/Investigator/Subject/Visit/Accession/BaseSpecimen/BaseBattery/BaseTest/BaseResult/SingleResult/NumericResult/@Precision | Determine the precision from the value. Precision is implicitly determined from the attribute Observation/valueQuantity/value/@value If the reported result isn't in conventional units, can use the PQ-translation extension to convey a translation. |
Reported Numeric Result Precision |
Observation.valueQuantity
0..1 Quantity obs-7 |
Observation.valueQuantity.value |
/GTP/Study/Site/Investigator/Subject/Visit/Accession/BaseSpecimen/BaseBattery/BaseTest/BaseResult/SingleResult/NumericResult/@Precision | Determine the precision from the value. Precision is implicitly determined from the attribute Observation/valueQuantity/value/@value |
SI Numeric Result Precision |
Observation.valueQuantity
0..1 Quantity obs-7 |
Observation.valueQuantity.value |
/GTP/Study/Site/Investigator/Subject/Visit/Accession/BaseSpecimen/BaseBattery/BaseTest/BaseResult/SingleResult/NumericResult/@Precision | Determine the precision from the value. Precision is implicitly determined from the attribute Observation/valueQuantity/value/@value If the reported result isn't in SI units, can use the PQ-translation extension to convey a translation. |
Conventional Reference Range Low |
Observation.referenceRange
0..* BackboneElement |
Observation.referenceRange.low.value Observation.referenceRange.low.unit |
/GTP/Study/Site/Investigator/Subject/Visit/Accession/BaseSpecimen/BaseBattery/BaseTest/BaseResult/SingleResult/ResultReferenceRange/@ReferenceRangeLow | Will need to combine value and unit into a string. If the reported units aren't 'conventional, can use the PQ-translation extension on Quantity to convey a translation. |
Reported Reference Range Low |
Observation.referenceRange
0..* BackboneElement |
Observation.referenceRange.low.value Observation.referenceRange.low.unit |
/GTP/Study/Site/Investigator/Subject/Visit/Accession/BaseSpecimen/BaseBattery/BaseTest/BaseResult/SingleResult/ResultReferenceRange/@ReferenceRangeLow | Will need to combine value and unit into a string. |
SI Reference Range Low |
Observation.referenceRange
0..* BackboneElement |
Observation.referenceRange.low.value Observation.referenceRange.low.unit |
/GTP/Study/Site/Investigator/Subject/Visit/Accession/BaseSpecimen/BaseBattery/BaseTest/BaseResult/SingleResult/ResultReferenceRange/@ReferenceRangeLow | Will need to combine value and unit into a string. If the reported units aren't SI, can use the PQ-translation extension on Quantity to convey a translation. |
Conventional Reference Range High |
Observation.referenceRange
0..* BackboneElement |
Observation.referenceRange.high.value Observation.referenceRange.high.unit |
/GTP/Study/Site/Investigator/Subject/Visit/Accession/BaseSpecimen/BaseBattery/BaseTest/BaseResult/SingleResult/ResultReferenceRange/@ReferenceRangeHigh | Will need to combine value and unit into a string. If the reported units aren't 'conventional, can use the PQ-translation extension on Quantity to convey a translation. |
Reported Reference Range High |
Observation.referenceRange
0..* BackboneElement |
Observation.referenceRange.high.value Observation.referenceRange.high.unit |
/GTP/Study/Site/Investigator/Subject/Visit/Accession/BaseSpecimen/BaseBattery/BaseTest/BaseResult/SingleResult/ResultReferenceRange/@ReferenceRangeHigh | Will need to combine value and unit into a string. |
SI Reference Range High |
Observation.referenceRange
0..* BackboneElement |
Observation.referenceRange.high.value Observation.referenceRange.high.unit |
/GTP/Study/Site/Investigator/Subject/Visit/Accession/BaseSpecimen/BaseBattery/BaseTest/BaseResult/SingleResult/ResultReferenceRange/@ReferenceRangeHigh | Will need to combine value and unit into a string. If the reported units aren't SI, can use the PQ-translation extension on Quantity to convey a translation. |
Conventional Units |
Observation.valueQuantity
0..1 Quantity obs-7 |
Observation.valueQuantity.code Observation.valueQuantity.extension('PQ-translation').code |
/GTP/Study/Site/Investigator/Subject/Visit/Accession/BaseSpecimen/BaseBattery/BaseTest/BaseResult/SingleResult/ResultUnits/@Value | If the reported units are not the same as conventional, can convey with the translation extension. Translation to CDISC terminology may be required. |
Reported Units |
Observation.valueQuantity
0..1 Quantity obs-7 |
Observation.valueQuantity.code |
/GTP/Study/Site/Investigator/Subject/Visit/Accession/BaseSpecimen/BaseBattery/BaseTest/BaseResult/SingleResult/ResultUnits/@Value | This may require translation to CDISC terminology |
SI Units |
Observation.valueQuantity
0..1 Quantity obs-7 |
Observation.valueQuantity.code Observation.valueQuantity.extensoin('PQ-translation').code |
/GTP/Study/Site/Investigator/Subject/Visit/Accession/BaseSpecimen/BaseBattery/BaseTest/BaseResult/SingleResult/ResultUnits/@Value | If the reported units are not SI units, can convey with the translation extension. Translation to CDISC terminology may be required. |
Conventional Units Code List ID |
Observation.valueQuantity
0..1 Quantity obs-7 |
Observation.valueQuantity.system |
/GTP/Study/Site/Investigator/Subject/Visit/Accession/BaseSpecimen/BaseBattery/BaseTest/BaseResult/SingleResult/ResultUnits/@CodeListID | |
Reported Units Code List ID |
Observation.valueQuantity
0..1 Quantity obs-7 |
Observation.valueQuantity.system |
/GTP/Study/Site/Investigator/Subject/Visit/Accession/BaseSpecimen/BaseBattery/BaseTest/BaseResult/SingleResult/ResultUnits/@CodeListID | |
SI Units Code List ID |
Observation.valueQuantity
0..1 Quantity obs-7 |
Observation.valueQuantity.system |
/GTP/Study/Site/Investigator/Subject/Visit/Accession/BaseSpecimen/BaseBattery/BaseTest/BaseResult/SingleResult/ResultUnits/@CodeListID | |
Record Extension Type | Populate with a default value, depending on the type of data being transmitted. E.g., "BASE" |
|||
Result Class | In FHIR, the value will always be 'reported', with the ability to convey translations in an extension. Nothing will differentiate whether a translation is considered 'conventional' or not, so if there are multiple translations present and the receiver can't figure it out by looking, an additional extension may be necessary to differentiate. This can sometimes also be inferred from the Observation.code. | /GTP/Study/Site/Investigator/Subject/Visit/Accession/BaseSpecimen/BaseBattery/BaseTest/BaseResult/SingleResult/ResultClass/@Value | ||
Model Version | Populate with a default value. | /GTP/@ModelVersion | The version of FHIR in use is conveyed using Resource.meta.profile | |
File Creation Date and Time | Pull from the message wrapper. (file creation date) | /GTP/@CreationDateTime | Bundle.timestamp | |
Transaction Type | No mitigation. If required, fill with a default value. | /GTP/TransactionType | As per study.transaction | |
Transmission Source ID | Pull from the message wrapper. | /GTP/TransmissionSource/@ID | MessageHeader.source.endpoint if using messaging, otherwise determined out of band based on sender authentication process | |
Transmission Source Name | Pull from the message wrapper. | /GTP/TransmissionSource/@Name | MessageHeader.source.name if using messaging, otherwise determined out of band based on sender authentication process |