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

Update endpoint lgd-publication to support extra data #58

Merged
merged 23 commits into from
Dec 5, 2024
Merged
Show file tree
Hide file tree
Changes from 10 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
Original file line number Diff line number Diff line change
Expand Up @@ -573,11 +573,43 @@ def update_mechanism(self, lgd_instance, validated_data):
is_deleted = 0
)

# Save old molecular mechanism to delete it later
old_mechanism_obj = lgd_instance.molecular_mechanism

# Update LGD record
# The mechanism has to be updated in the locus_genotype_disease before the evidence is added
# Because the evidence is going to be linked to the new lgd.molecular_mechanism
lgd_instance.molecular_mechanism = mechanism_obj
lgd_instance.date_review = datetime.now()
lgd_instance.save()

# Get evidence - the mechanism evidence was validated in the view 'LGDUpdateMechanism'
# Example: {'pmid': '25099252', 'description': 'text', 'evidence_types':
# [{'primary_type': 'Rescue', 'secondary_type': ['Human', 'Patient Cells']}]}
for evidence in mechanism_evidence:
pmid = evidence.get("pmid")
self.update_mechanism_evidence(lgd_instance, mechanism_evidence)

# The old molecular mechanism can be deleted - the deletion is stored in the history table
# As this method only allows to update 'undetermined' mechanisms, there is no evidence to be deleted
old_mechanism_obj.delete()

return lgd_instance

def update_mechanism_evidence(self, lgd_obj, validated_data):
"""
Method to only update the evidence of the LGD molecular mechanism.

'validated_data' example:
"mechanism_evidence": [{
"pmid": "1234",
"description": "This is new evidence for the existing mechanism evidence.",
"evidence_types": [ { "primary_type": "Function",
"secondary_type": [ "Biochemical" ]}
]}]
"""
mechanism_obj = lgd_obj.molecular_mechanism

for evidence in validated_data:
pmid = evidence["pmid"]

# Check if the PMID exists in G2P
# When updating the mechanism the supporting pmid used as evidence
Expand All @@ -588,20 +620,20 @@ def update_mechanism(self, lgd_instance, validated_data):
# TODO: improve in future to insert new pmids + link them to the record
raise serializers.ValidationError({"message": f"pmid '{pmid}' not found in G2P"})

if evidence.get("description") != "":
description = evidence.get("description")
if evidence["description"] != "":
description = evidence["description"]
else:
description = None

evidence_types = evidence.get("evidence_types")
evidence_types = evidence["evidence_types"]
for evidence_type in evidence_types:
# primary_type is the evidence subtype ('rescue')
primary_type = evidence_type.get("primary_type", None)
if not primary_type:
raise serializers.ValidationError({"message": f"Empty evidence subtype"})
primary_type = primary_type.lower()
# secondary_type is the evidence value ('human')
secondary_type = evidence_type.get("secondary_type")
secondary_type = evidence_type["secondary_type"]
for m_type in secondary_type:
try:
cv_evidence_obj = CVMolecularMechanism.objects.get(
Expand All @@ -613,27 +645,28 @@ def update_mechanism(self, lgd_instance, validated_data):
raise serializers.ValidationError({"message": f"Invalid mechanism evidence '{m_type}'"})

# Insert evidence
mechanism_evidence_obj = MolecularMechanismEvidence.objects.create(
try:
mechanism_evidence_obj = MolecularMechanismEvidence.objects.get(
molecular_mechanism = mechanism_obj,
description = description,
publication = publication_obj,
evidence = cv_evidence_obj,
is_deleted = 0
evidence = cv_evidence_obj
)

# Save old molecular mechanism to delete it later
old_mechanism_obj = lgd_instance.molecular_mechanism

# Update LGD record
lgd_instance.molecular_mechanism = mechanism_obj
lgd_instance.date_review = datetime.now()
lgd_instance.save()

# The old molecular mechanism can be deleted - the deletion is stored in the history table
# As this method only allows to update 'undetermined' mechanisms, there is no evidence to be deleted
old_mechanism_obj.delete()

return lgd_instance
except MolecularMechanismEvidence.DoesNotExist:
mechanism_evidence_obj = MolecularMechanismEvidence.objects.create(
molecular_mechanism = mechanism_obj,
description = description,
publication = publication_obj,
evidence = cv_evidence_obj,
is_deleted = 0
)
else:
if(mechanism_evidence_obj.is_deleted == 1):
mechanism_evidence_obj.is_deleted = 0
mechanism_evidence_obj.save()

# Update LGD date_review
lgd_obj.date_review = datetime.now()
lgd_obj.save()

class Meta:
model = LocusGenotypeDisease
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -131,7 +131,7 @@ class Meta:
class LGDPhenotypeListSerializer(serializers.Serializer):
"""
Serializer to accept a list of phenotypes.
Called by: LocusGenotypeDiseaseAddPhenotypes()
Called by: LocusGenotypeDiseaseAddPhenotypes() and view LGDEditPhenotypes()
"""
phenotypes = LGDPhenotypeSerializer(many=True)

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -296,20 +296,20 @@ def validate(self, data):
"""
Overwrite the method to validate the data.
This validate() method is activated by the curation (draft entry) endpoints and
by the LGDPublicationListSerializer as a consequence this method is identical to
the validate method in LGDPublicationListSerializer.
by the LGDPublicationListSerializer.
This method is identical to the validate method in LGDPublicationListSerializer.
"""
if hasattr(self, 'initial_data'):
data = self.initial_data
valid_headers = ["families", "consanguinity", "ancestries", "affected_individuals"]
extra_headers = ["phenotypes", "variant_types", "variant_descriptions"]

publication = self.initial_data.get("publication")
# check if 'families' is defined
# check if 'families' is defined in the initial data
# correct structure is:
# { "families": 200, "consanguinity": "unknown", "ancestries": "african",
# "affected_individuals": 100 }
if "families" in publication and publication.get("families") is not None:
families = publication.get("families")
if "families" in self.initial_data:
families = self.initial_data.get("families") # If 'families' is in initial data then it cannot be null
for header in families.keys():
if header not in valid_headers:
raise serializers.ValidationError(f"Got unknown field in families: {header}")
Expand All @@ -335,24 +335,19 @@ def create(self, validated_data):
comment = None
families = None

publication_data = validated_data.get('publication') # includes 'pmid', 'comment', 'families'
publication_data = self.initial_data.get('publication') # 'publication': {'pmid': 39385417}
comment = self.initial_data.get('comment', None) # 'comment': {'comment': 'this is a comment', 'is_public': 1}
families = self.initial_data.get("families", None) # "families": { "families": 200, "consanguinity": "unknown", "ancestries": "african", "affected_individuals": 2 }

if "comment" in publication_data:
comment = publication_data.get("comment")
if comment:
comment_text = comment["comment"]

# Check if comment text is invalid or missing
if not comment or "comment" not in comment or comment.get("comment") == "":
# Check if comment text is empty string
if not comment_text or comment_text == "":
dglemos marked this conversation as resolved.
Show resolved Hide resolved
comment = None
# If 'is_public' is not defined set it to public (default)
elif "is_public" not in comment:
comment["is_public"] = 1

if "families" in publication_data:
families = publication_data.get('families') # extra data - families reported in publication

# Check if family data is invalid or missing
if not families:
families = None
else:
comment["is_public"] = comment.get("is_public", 1)

# it is necessary to send the user
# the publication comment is linked to the user
Expand All @@ -379,13 +374,22 @@ def create(self, validated_data):
publication = publication_obj,
is_deleted = 0
)
# There is a new publication linked to the LGD record,
# the record has to reflect the date of this change
# update the date_review of the LGD record
lgd.date_review = datetime.now()
lgd.save()

# If LGD-publication already exists then returns the existing object
# New comments and/or family info are added to the existing object
# If existing LGD-publication is deleted then update to not deleted
if lgd_publication_obj.is_deleted != 0:
lgd_publication_obj.is_deleted = 0
lgd_publication_obj.save()
# The lgd-publication is not deleted anymore which is equivallent to
# creating a new link, this means we have to update the record 'date_review'
lgd.date_review = datetime.now()
lgd.save()

return lgd_publication_obj

Expand All @@ -396,7 +400,8 @@ class Meta:
class LGDPublicationListSerializer(serializers.Serializer):
"""
Serializer to accept a list of publications.
Called by: LocusGenotypeDiseaseAddPublication()
This method only validates the publications, it does not update any data.
Called by: LocusGenotypeDiseaseAddPublication() and view LGDEditPublications()
"""
publications = LGDPublicationSerializer(many=True)

Expand All @@ -411,7 +416,8 @@ def validate(self, data):
# by default these fields are not accepted as valid as they are not part of the LGDPublication
if hasattr(self, 'initial_data'):
data = self.initial_data
valid_headers = ["families", "consanguinity", "ancestries", "affected_individuals"]
families_valid_headers = ["families", "consanguinity", "ancestries", "affected_individuals"]
extra_headers = ["phenotypes", "variant_types", "variant_descriptions"]

publications = self.initial_data.get("publications")

Expand All @@ -420,12 +426,11 @@ def validate(self, data):

# check if 'families' is defined
# correct structure is:
# { "families": 200, "consanguinity": "unknown", "ancestries": "african",
# "affected_individuals": 100 }
# "families": { "families": 2, "consanguinity": "unknown", "ancestries": "african", "affected_individuals": 1 }
if "families" in publication:
families = publication.get("families")
for header in families.keys():
if header not in valid_headers:
if header not in families_valid_headers:
raise serializers.ValidationError(f"Got unknown field in families: {header}")

# TODO check if 'comment' is defined
Expand Down
7 changes: 7 additions & 0 deletions gene2phenotype_project/gene2phenotype_app/views/base.py
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,13 @@ def handle_no_update(self, data, stable_id):
else:
raise PermissionDenied(f"Cannot update '{data}' for ID '{stable_id}'")

def handle_update_exception(self, exception, context_message):
if hasattr(exception, 'detail') and 'message' in exception.detail:
error_message = exception.detail['message']
return Response({"error": f"{context_message}: {error_message}"}, status=status.HTTP_400_BAD_REQUEST)
else:
error_message = context_message
return Response({"error": f"{context_message}"}, status=status.HTTP_400_BAD_REQUEST)

@api_view(['GET'])
def ListEndpoints(request):
Expand Down
Loading