FHIR to CDISC Joint Mapping Implementation Guide
1.0.0 - STU 1 International flag

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

Laboratory

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:

  • DiagnosticReport which covers the overall report of a collection of lab reports
  • Observation which handles the individual data measurements within an overall lab report

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.

LB Mappings

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)

LAB Mappings

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