Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

🚧 Map Concepts between KF and FHIR #78

Closed
wants to merge 31 commits into from
Closed
Show file tree
Hide file tree
Changes from 10 commits
Commits
Show all changes
31 commits
Select commit Hold shift + click to select a range
cac3148
:sparkles: ignore vscode cache
liberaliscomputing Feb 13, 2020
9eaa241
::construction: map concepts
liberaliscomputing Feb 13, 2020
81638f9
:construction: update profiles against updated ig
liberaliscomputing Feb 17, 2020
16c882c
:construction: fix typo
liberaliscomputing Feb 17, 2020
bab1841
Fix syntax
fiendish Feb 17, 2020
2d18c42
Merge pull request #79 from kids-first/fixes
liberaliscomputing Feb 17, 2020
86f6cc6
:construction: import constants and concepts
liberaliscomputing Feb 17, 2020
0cae8fb
:construction: deprecate dict update
liberaliscomputing Feb 17, 2020
06d21ff
:construction: convert individual template to prototype
fiendish Feb 17, 2020
ddbcbb3
:construction: wip
fiendish Feb 18, 2020
89dbdc2
:construction: wip
fiendish Feb 19, 2020
554f71c
:construction: individuals load now
fiendish Feb 19, 2020
dee356d
:construction: samples load now
fiendish Feb 19, 2020
f4b1a0b
:construction: abort without waiting
fiendish Feb 20, 2020
90c8f41
:construction: updates + phenotype wip
fiendish Feb 21, 2020
efe586e
🚧 update readme
liberaliscomputing Feb 21, 2020
707652e
🚧 fix indentation
liberaliscomputing Feb 21, 2020
890cc8b
🚧 update readme
liberaliscomputing Feb 21, 2020
3d6f29c
🚧 fix indentation
liberaliscomputing Feb 21, 2020
a3f204c
Merge pull request #82 from kids-first/functified
liberaliscomputing Feb 25, 2020
c01eda9
:construction: change style
liberaliscomputing Feb 28, 2020
e55247c
:construction: change style and add kfid assignment
liberaliscomputing Feb 28, 2020
29b8490
:construction: functify disease
liberaliscomputing Feb 28, 2020
2252e76
:construction: add .DS_Store
liberaliscomputing Mar 4, 2020
8327509
:construction: add mappers
liberaliscomputing Mar 4, 2020
90c936e
:construction: add id max char restriction
liberaliscomputing Mar 4, 2020
38b3a1c
:construction: add resource caches
liberaliscomputing Mar 4, 2020
02e6574
:construction: add HPO ontology and code system
liberaliscomputing Mar 4, 2020
3ad4fd1
:construction: add disease terminology
liberaliscomputing Mar 6, 2020
6bba1f3
:construction: update packer
liberaliscomputing Mar 6, 2020
0098372
:construction: add loading disease and phenotypic feature
liberaliscomputing Mar 6, 2020
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -112,3 +112,6 @@ license/
vonk-trial-license.json
appsettings.env
logsettings.env

# Miscellaneous
.vscode/
28 changes: 28 additions & 0 deletions docs/no_peeking/mappings/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
# Concept-Mappers between Kids First and Phenopackets FHIR

This directory curates Python modules mapping Kids First concepts to FHIR profiles against the [Phenopackets specifications](https://aehrc.github.io/fhir-phenopackets-ig/index.html).
The following table shows conceptual mappings among KF entities, and Phenopackets and FHIR profiles:

| Kids First | Phenopackets | FHIR |
|-----------------------|---------------------|-----------------------|
| `family` | `Family` | `Group` |
| `participant` | `Individual` | `Patient` |
| `family_relationship` | `PedigreeNode` | `FamilyMemberHistory` |
| `diagnosis` | `Disease` | `Condition` |
| `phenotype` | `PhenotypicFeature` | `Observation` |
| `biospecimen` | `Biosample` | `Specimen` |
| `genomic_file` | `HtsFile` | `DocumentReference` |

As an initial pass, we created modules for the following profiles:

- `Family`;
- `Individual`;
- `PedigreeNode`;
- `Disease`;
- `PhenotypicFeature`; and
- `Biosample`

In creating the modules, the following attributes were considered:

- Most of the Phenopackets atrributes from the differential tables if mapped; and
- All the FHIR attributes required (based on cardinality) as well as `'id'` and `'identifier'`
94 changes: 94 additions & 0 deletions docs/no_peeking/mappings/biosample.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,94 @@
"""
This module maps Kids First biospecimen to Phenopackets Biosample (derived from FHIR Specimen).
Please visit https://aehrc.github.io/fhir-phenopackets-ig/StructureDefinition-Biosample.html
for the detailed structure definition.
"""
from kf_lib_data_ingest.common import constants
from kf_lib_data_ingest.common.concept_schema import CONCEPT


def biosample_status(x):
"""
http://hl7.org/fhir/R4/valueset-specimen-status.html
"""
if x == constants.COMMON.FALSE:
return 'unavailable'
elif x == constants.COMMON.TRUE:
return 'available'
else:
raise Exception('Unknown Biosample status')

def biosample_type(x):
"""
http://terminology.hl7.org/ValueSet/v2-0487
Copy link

@ShahimEssaid ShahimEssaid Feb 20, 2020

Choose a reason for hiding this comment

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

How are these URLs in the comments used? This URL doesn't resolve but it is taken from http://hl7.org/fhir/R4/v2/0487/index.html as the CodeSystem URL.

Also, this is bound as an "example" ValueSet. We probably want to use some other terminology and define our ValueSet.

Terminology choices discussion started on the Slack channel.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

For all the custom functions defined, the URLs in the docstrings are not for direct use, but for reference. And, when I created this PR, it was before the Phenopackets team has made the recent IG updates and the URLs were valid.

@fiendish will create a PR to this branch which resolves terminology issues.

"""
if x == constants.SPECIMEN.COMPOSITION.BLOOD:
return {
'system': 'http://terminology.hl7.org/CodeSystem/v2-0487',
'code': 'BLD',
'display': 'Whole blood'
}
elif x == constants.SPECIMEN.COMPOSITION.SALIVA:
return {
'system': 'http://terminology.hl7.org/CodeSystem/v2-0487',
'code': 'SAL',
'display': 'Saliva'
}
elif x == constants.SPECIMEN.COMPOSITION.TISSUE:
return {
'system': 'http://terminology.hl7.org/CodeSystem/v2-0487',
'code': 'TISS',
'display': 'Tissue'
}
else:
raise Exception('Unknown Biosample type')


biosample = {
'resourceType': 'Specimen',
'id': CONCEPT.BIOSPECIMEN.TARGET_SERVICE_ID,
'meta': {
'profile': ['http://ga4gh.fhir.phenopackets/StructureDefinition/Biosample']

Choose a reason for hiding this comment

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

We could have placeholder KF profiles based on the PK profiles to give us more flexibility down the road. We can also copy their "differential" element definitions to our KF profiles (so we have similar and interoperable constraints) and keep our profiles based on the base FHIR profiles.

},
'identifier': [
{
'system': 'https://kf-api-dataservice.kidsfirstdrc.org/biospecimens',
'value': CONCEPT.BIOSPECIMEN.TARGET_SERVICE_ID
},
{
'value': CONCEPT.BIOSPECIMEN.ID
}
],
'accessionIdentifier': [
{
'value': CONCEPT.BIOSPECIMEN.ID
}
],
'status': biosample_status(CONCEPT.BIOSPECIMEN.VISIBLE),
'type': {
'coding': [
biosample_type(CONCEPT.BIOSPECIMEN.COMPOSITION)
],
'text': CONCEPT.BIOSPECIMEN.COMPOSITION
},
'subject': {
'reference': f'Individual/CONCEPT.{PARTICIPANT.TARGET_SERVICE_ID}',
Copy link
Member

Choose a reason for hiding this comment

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

Should this be:

Suggested change
'reference': f'Individual/CONCEPT.{PARTICIPANT.TARGET_SERVICE_ID}',
'reference': f'Patient/CONCEPT.{PARTICIPANT.TARGET_SERVICE_ID}',

I think the references have to have the FHIR base type in the prefix not the id of the StructureDefinition...I might be wrong. Could someone check the spec and verify?

'type': 'Individual',
Copy link
Member

Choose a reason for hiding this comment

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

Should this be (same reason as above)

Suggested change
'type': 'Individual',
'type': 'Patient',

'identifier': [
{
'system': 'https://kf-api-dataservice.kidsfirstdrc.org/participants',
'value': CONCEPT.PARTICIPANT.TARGET_SERVICE_ID
},
{
'value': CONCEPT.PARTICIPANT.ID
}
],
'display': CONCEPT.PARTICIPANT.ID
},
'collection': {
'quantity': {
'value': CONCEPT.BIOSPECIMEN.VOLUME_UL,
'unit': 'uL'

Choose a reason for hiding this comment

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

We should also add a system for the unit. Not sure why the unit wasn't a coding/CodeableConcept in FHIR.

}
}
}
85 changes: 85 additions & 0 deletions docs/no_peeking/mappings/disease.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,85 @@
"""
This module maps Kids First diagnosis to Phenopackets Disease (derived from FHIR Condition).
Please visit https://aehrc.github.io/fhir-phenopackets-ig/StructureDefinition-Disease.html
for the detailed structure definition.
"""
from kf_lib_data_ingest.common import constants
from kf_lib_data_ingest.common.concept_schema import CONCEPT


def coded_onset(x):
"""
https://aehrc.github.io/fhir-phenopackets-ig/ValueSet-Onset.html
"""
pass

def tumor_stage(x):
"""
https://aehrc.github.io/fhir-phenopackets-ig/ValueSet-TumorStage.html
"""
pass

def disease_code(x):
"""
http://hl7.org/fhir/R4/valueset-condition-code.html
"""
pass

def body_site(x):
"""
http://hl7.org/fhir/R4/valueset-body-site.html
"""
pass


disease = {
'resourceType': 'Condition',
'id': CONCEPT.DIAGNOSIS.TARGET_SERVICE_ID,
'meta': {
'profile': ['http://ga4gh.fhir.phenopackets/StructureDefinition/Disease']
},
'extension': [
{
'text': 'CodedOnset',
'url': 'http://ga4gh.org/fhir/phenopackets/StructureDefinition/CodedOnset',
'valueCodeableConcept': {
'coding': [
{
'system': 'http://purl.obolibrary.org/obo/hp.owl',
'code': 'HP:0410280',
'display': 'Pediatric onset'
}
]
}
}
],
'identifier': [
{
'system': 'https://kf-api-dataservice.kidsfirstdrc.org/diagnoses',
'value': CONCEPT.DIAGNOSIS.TARGET_SERVICE_ID
},
{
'value': CONCEPT.DIAGNOSIS.ID
}
],
'code': {
'coding': None, # condition_code()
'text': CONCEPT.DIAGNOSIS.NAME
},
'bodySite': None, # body_site()
'subject': {
'reference': f'Patient/{CONCEPT.PARTICIPANT.TARGET_SERVICE_ID}',
'type': 'Patient',
'identifier': [
{
'system': 'https://kf-api-dataservice.kidsfirstdrc.org/participants',
'value': CONCEPT.PARTICIPANT.TARGET_SERVICE_ID
},
{
'value': CONCEPT.PARTICIPANT.ID
}
],
'display': CONCEPT.PARTICIPANT.ID
},
'onsetAge': CONCEPT.DIAGNOSIS.EVENT_AGE_DAYS

Choose a reason for hiding this comment

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

What's the format of KF dates? This has to be coded as a FHIR Quantity if it's days.

http://hl7.org/fhir/R4/datatypes.html#quantity

Copy link
Contributor

@fiendish fiendish Feb 20, 2020

Choose a reason for hiding this comment

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

KF doesn't have dates ever. We only have ages specified in days.

}
110 changes: 110 additions & 0 deletions docs/no_peeking/mappings/family.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,110 @@
"""
This module maps Kids First family to Phenopackets Family (derived from FHIR Group).
Please visit https://aehrc.github.io/fhir-phenopackets-ig/StructureDefinition-Family.html
for the detailed structure definition.
"""
from kf_lib_data_ingest.common import constants
from kf_lib_data_ingest.common.concept_schema import CONCEPT


def family_type(x):
"""
http://hl7.org/fhir/R4/valueset-group-type.html
"""
if x in {constants.SPECIES.DOG}:
return 'animal'
elif x == constants.SPECIES.HUMAN:
return 'person'
else:
raise Exception('Unknown Family type')

def family_member_type(x):
"""
https://www.hl7.org/fhir/v3/FamilyMember/vs.html
"""
if x == constants.COMMON.TRUE:
return {
'system': 'http://terminology.hl7.org/CodeSystem/v3-RoleCode',
'code': 'CHILD',
'display': 'child'
}
elif x == constants.RELATIONSHIP.FATHER:
return {
'system': 'http://terminology.hl7.org/CodeSystem/v3-RoleCode',
'code': 'FTH',
'display': 'father'
}
elif x == constants.RELATIONSHIP.MOTHER:
return {
'system': 'http://terminology.hl7.org/CodeSystem/v3-RoleCode',
'code': 'MTH',
'display': 'mother'
}
else:
raise Exception('Unknown Family family-member-type')

def family_member_phenopacket(x): pass


family = {
'resourceType': 'Group',
'id': CONCEPT.FAMILY.TARGET_SERVICE_ID,
'meta': {
'profile': ['http://ga4gh.fhir.phenopackets/StructureDefinition/Family']
},
'identifier': [
{
'system': 'https://kf-api-dataservice.kidsfirstdrc.org/families',
'value': CONCEPT.FAMILY.TARGET_SERVICE_ID
},
{
'value': CONCEPT.FAMILY.ID
}
],
'extension': [
{
'text': 'PedigreeNodeReference',
'url': 'http://ga4gh.org/fhir/phenopackets/StructureDefinition/PedigreeNodeReference',
'valueReference': None
}
],
'type': family_type(CONCEPT.PARTICIPANT.SPECIES or constants.SPECIES.HUMAN),
'actual': constants.COMMON.TRUE, # defaults to True
'member': [

Choose a reason for hiding this comment

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

The "member" is missing the "family-member-phenopacket" extension shown here:

https://aehrc.github.io/fhir-phenopackets-ig/StructureDefinition-Family-definitions.html#Group.member.extension:family-member-phenopacket

Also, the new IG doesn't show the ValueSet/CodeSystme that is used in the "family-member-type" extension. I think it was shown in the older IG format.

{
'id': CONCEPT.PARTICIPANT.TARGET_SERVICE_ID,
'extension': [
{
'text': 'family-member-type',
'id': CONCEPT.FAMILY_RELATIONSHIP.TARGET_SERVICE_ID,
'url': 'family-member-type',
'valueCodeableConcept': {
'coding': [
family_member_type(
CONCEPT.PARTICIPANT.IS_PROBAND or
CONCEPT.FAMILY_RELATIONSHIP.RELATION_FROM_1_TO_2
)
],
'text': constants.RELATIONSHIP.CHILD
if CONCEPT.PARTICIPANT.IS_PROBAND
else CONCEPT.FAMILY_RELATIONSHIP.RELATION_FROM_1_TO_2
}
}
],
'entity': {
'reference': f'Individual/{CONCEPT.PARTICIPANT.TARGET_SERVICE_ID}',
'type': 'Individual',
'identifier': [
{
'system': 'https://kf-api-dataservice.kidsfirstdrc.org/participants',
'value': CONCEPT.PARTICIPANT.TARGET_SERVICE_ID
},
{
'value': CONCEPT.PARTICIPANT.ID
}
],
'display': CONCEPT.PARTICIPANT.ID
}
}
]
}
Loading