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

✨Family relationship resource #194

Merged
merged 3 commits into from
Mar 22, 2018
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
2 changes: 2 additions & 0 deletions dataservice/api/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,8 @@
from dataservice.api.investigator import InvestigatorListAPI
from dataservice.api.participant import ParticipantAPI
from dataservice.api.participant import ParticipantListAPI
from dataservice.api.family_relationship import FamilyRelationshipAPI
from dataservice.api.family_relationship import FamilyRelationshipListAPI
from dataservice.api.demographic import DemographicAPI
from dataservice.api.demographic import DemographicListAPI
from dataservice.api.diagnosis import DiagnosisAPI
Expand Down
6 changes: 6 additions & 0 deletions dataservice/api/family_relationship/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
from dataservice.api.family_relationship.resources import (
FamilyRelationshipAPI
)
from dataservice.api.family_relationship.resources import (
FamilyRelationshipListAPI
)
154 changes: 154 additions & 0 deletions dataservice/api/family_relationship/resources.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,154 @@
from flask import abort, request
from marshmallow import ValidationError

from dataservice.extensions import db
from dataservice.api.common.pagination import paginated, Pagination
from dataservice.api.family_relationship.models import FamilyRelationship
from dataservice.api.family_relationship.schemas import (
FamilyRelationshipSchema
)
from dataservice.api.common.views import CRUDView


class FamilyRelationshipListAPI(CRUDView):
"""
FamilyRelationship REST API
"""
endpoint = 'family_relationships_list'
rule = '/family-relationships'
schemas = {'FamilyRelationship': FamilyRelationshipSchema}

@paginated
def get(self, after, limit):
"""
Get all family_relationships
---
description: Get all family_relationships
template:
path:
get_list.yml
properties:
resource:
FamilyRelationship
"""
q = FamilyRelationship.query

return (FamilyRelationshipSchema(many=True)
.jsonify(Pagination(q, after, limit)))

def post(self):
"""
Create a new family_relationship
---
template:
path:
new_resource.yml
properties:
resource:
FamilyRelationship
"""

body = request.json

# Deserialize
try:
fr = FamilyRelationshipSchema(strict=True).load(body).data
# Request body not valid
except ValidationError as e:
abort(400, 'could not create family_relationship: {}'
.format(e.messages))

# Add to and save in database
db.session.add(fr)
db.session.commit()

return FamilyRelationshipSchema(201, 'family_relationship {} created'
.format(fr.kf_id)).jsonify(fr), 201


class FamilyRelationshipAPI(CRUDView):
"""
FamilyRelationship REST API
"""
endpoint = 'family_relationships'
rule = '/family-relationships/<string:kf_id>'
schemas = {'FamilyRelationship': FamilyRelationshipSchema}

def get(self, kf_id):
"""
Get a family_relationship by id
---
template:
path:
get_by_id.yml
properties:
resource:
FamilyRelationship
"""
# Get one
fr = FamilyRelationship.query.get(kf_id)
if fr is None:
abort(404, 'could not find {} `{}`'
.format('family_relationship', kf_id))
return FamilyRelationshipSchema().jsonify(fr)

def patch(self, kf_id):
"""
Update an existing family_relationship.

Allows partial update of resource
---
template:
path:
update_by_id.yml
properties:
resource:
FamilyRelationship
"""
fr = FamilyRelationship.query.get(kf_id)
if fr is None:
abort(404, 'could not find {} `{}`'
.format('family_relationship', kf_id))

# Partial update - validate but allow missing required fields
body = request.json or {}
try:
fr = FamilyRelationshipSchema(strict=True).load(body, instance=fr,
partial=True).data
except ValidationError as err:
abort(400, 'could not update family_relationship: {}'
.format(err.messages))

db.session.add(fr)
db.session.commit()

return FamilyRelationshipSchema(
200, 'family_relationship {} updated'.format(fr.kf_id)
).jsonify(fr), 200

def delete(self, kf_id):
"""
Delete family_relationship by id

Deletes a family_relationship given a Kids First id
---
template:
path:
delete_by_id.yml
properties:
resource:
FamilyRelationship
"""

# Check if family_relationship exists
fr = FamilyRelationship.query.get(kf_id)
if fr is None:
abort(404, 'could not find {} `{}`'
.format('family_relationship', kf_id))

# Save in database
db.session.delete(fr)
db.session.commit()

return FamilyRelationshipSchema(200, 'family_relationship {} deleted'
.format(fr.kf_id)).jsonify(fr), 200
27 changes: 27 additions & 0 deletions dataservice/api/family_relationship/schemas.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
from marshmallow_sqlalchemy import field_for

from dataservice.api.family_relationship.models import FamilyRelationship
from dataservice.api.common.schemas import BaseSchema
from dataservice.extensions import ma


class FamilyRelationshipSchema(BaseSchema):
participant_id = field_for(FamilyRelationship, 'participant_id',
required=True,
load_only=True, example='PT_B048J5')
relative_id = field_for(FamilyRelationship, 'relative_id',
required=True,
load_only=True, example='PT_B048J6')

class Meta(BaseSchema.Meta):
model = FamilyRelationship
resource_url = 'api.family_relationships'
collection_url = 'api.family_relationships_list'
exclude = ('relative', 'participant')

_links = ma.Hyperlinks({
'self': ma.URLFor(Meta.resource_url, kf_id='<kf_id>'),
'collection': ma.URLFor(Meta.collection_url),
'participant': ma.URLFor('api.participants', kf_id='<participant_id>'),
Copy link
Contributor

Choose a reason for hiding this comment

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

I know we've discussed doing this in #104, but do we want to do it to all resources at once to stay consistent? How do we track?

Copy link
Member Author

Choose a reason for hiding this comment

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

Welp, I think I might have added these types of links for the other resources I did...

Copy link
Contributor

Choose a reason for hiding this comment

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

oh well, we'll just need to make sure the others get done.

'relative': ma.URLFor('api.participants', kf_id='<relative_id>')
})
10 changes: 10 additions & 0 deletions dataservice/utils.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,15 @@
import pkg_resources
from itertools import tee


def _get_version():
return pkg_resources.get_distribution("kf-api-dataservice").version


def iterate_pairwise(iterable):
"""
Iterate over an iterable in consecutive pairs
"""
a, b = tee(iterable)
next(b, None)
return zip(a, b)
28 changes: 23 additions & 5 deletions tests/conftest.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
from dataservice.api.investigator.models import Investigator
from dataservice.api.study.models import Study
from dataservice.api.participant.models import Participant
from dataservice.api.family_relationship.models import FamilyRelationship
from dataservice.api.demographic.models import Demographic
from dataservice.api.diagnosis.models import Diagnosis
from dataservice.api.sample.models import Sample
Expand Down Expand Up @@ -107,13 +108,28 @@ def entities(client):
'phenotype': 'test phenotype 1',
'hpo_id': 'HP:0000118',
'age_at_event_days': 120
},
'/family-relationships': {
'participant_to_relative_relation': 'mother'
}
}

# Create and save entities to db
# Study, investigator
investigator = Investigator(**inputs['/investigators'])
study = Study(**inputs['/studies'])
study.investigator = investigator

# Add participants to study
p = Participant(**inputs['/participants'])
p1 = Participant(**inputs['/participants'])
p2 = Participant(**inputs['/participants'])

study.participants.extend([p, p1, p2])
db.session.add(study)
db.session.commit()

# Add entities to participant
outcome = Outcome(**inputs['/outcomes'], participant_id=p.kf_id)
phenotype = Phenotype(**inputs['/phenotypes'], participant_id=p.kf_id)
demo = Demographic(**inputs['/demographics'], participant_id=p.kf_id)
Expand All @@ -131,11 +147,12 @@ def entities(client):
p.outcomes = [outcome]
p.phenotypes = [phenotype]

# Add participants to study
study.investigator = investigator
study.participants.append(p)
db.session.add(study)
db.session.add(p)
# Family relationship
inputs['/family-relationships']['participant_id'] = p1.kf_id
inputs['/family-relationships']['relative_id'] = p2.kf_id
fr = FamilyRelationship(**inputs['/family-relationships'])

db.session.add(fr)
db.session.commit()

# Add foreign keys
Expand Down Expand Up @@ -165,5 +182,6 @@ def entities(client):
inputs['kf_ids'].update({'/samples': sample.kf_id})
inputs['kf_ids'].update({'/aliquots': aliquot.kf_id})
inputs['kf_ids'].update({'/sequencing-experiments': seq_exp.kf_id})
inputs['kf_ids'].update({'/family-relationships': fr.kf_id})

return inputs
Loading