Skip to content

Commit

Permalink
Regular refresh ClinVar individual from Case (#158)
Browse files Browse the repository at this point in the history
Related-Issue: #158
Projected-Results-Impact: none
  • Loading branch information
holtgrewe committed Sep 20, 2022
1 parent 8e2fe3a commit 72b6a97
Show file tree
Hide file tree
Showing 5 changed files with 81 additions and 14 deletions.
1 change: 1 addition & 0 deletions HISTORY.rst
Original file line number Diff line number Diff line change
Expand Up @@ -112,6 +112,7 @@ Full Change List
- Warning in the case of truncated displayed results (#641).
- Improving Clinvar record aggregation (#640).
- Fixing Docker builds (#660).
- Adding regular task to sync ClinVar submission ``Individual`` sex from the one from the ``Case``.

------
v1.2.0
Expand Down
16 changes: 16 additions & 0 deletions clinvar_export/management/commands/refresh_individuals.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
"""Django management command for triggering clinvar_export individual update."""

from django.core.management.base import BaseCommand

from clinvar_export.models import refresh_individual_sex_affected


class Command(BaseCommand):
#: Help message displayed on the command line.
help = "Refresh ClinVar export individual sex/affected attributes."

def handle(self, *args, **options):
"""The actual implementation is in ``_handle()``, splitting to get commit times."""
self.stderr.write(self.style.NOTICE("Refreshing all clinvar_xml individuals"))
refresh_individual_sex_affected()
self.stderr.write(self.style.NOTICE("All done. Have a nice day!"))
42 changes: 30 additions & 12 deletions clinvar_export/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,20 @@
#: Django user model.
AUTH_USER_MODEL = getattr(settings, "AUTH_USER_MODEL", "auth.User")

#: Map pedigree value for "sex" to value for models.
SEX_MAP = {
0: "unknown",
1: "male",
2: "female",
}

#: Map pedigree value for "affected" to value for models.
AFFECTED_MAP = {
0: "unknown",
1: "no",
2: "yes",
}


class FamilyManager(models.Manager):
"""Custom ``Manager`` that allows to easily get or create a ``Family`` from a project with a given name."""
Expand Down Expand Up @@ -123,16 +137,6 @@ class Meta:

def create_families_and_individuals(project: Project) -> None:
"""Create missing families and individuals for the given ``project``."""
sex_map = {
0: "unknown",
1: "male",
2: "female",
}
affected_map = {
0: "unknown",
1: "no",
2: "yes",
}
with transaction.atomic():
for case in Case.objects.filter(project=project):
family = Family.objects.get_or_create_in_project(project, case=case)
Expand All @@ -141,8 +145,8 @@ def create_families_and_individuals(project: Project) -> None:
project,
entry["patient"],
family=family,
sex=sex_map.get(entry.get("sex", 0), "unknown"),
affected=affected_map.get(entry.get("affected", 0), "unknown"),
sex=SEX_MAP.get(entry.get("sex", 0), "unknown"),
affected=AFFECTED_MAP.get(entry.get("affected", 0), "unknown"),
)


Expand Down Expand Up @@ -391,3 +395,17 @@ def get_project(self):

class Meta:
ordering = ("sort_order",)


def refresh_individual_sex_affected():
"""Update the ``Individual.sex`` field from the upstream case.
This is done regularly in a related task.
"""
for family in Family.objects.select_related("case").prefetch_related("individual_set").all():
ped_entries = {entry["patient"]: entry for entry in family.case.pedigree}
for individual in family.individual_set.all():
related_ped_entry = ped_entries[individual.name]
individual.sex = SEX_MAP.get(related_ped_entry["sex"], "unknown")
individual.affected = AFFECTED_MAP.get(related_ped_entry["affected"], "unknown")
individual.save()
15 changes: 15 additions & 0 deletions clinvar_export/tasks.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
from celery.schedules import crontab
from config.celery import app

from clinvar_export import models


@app.task(bind=True)
def refresh_individual_sex_affected(_self):
models.refresh_individual_sex_affected()


@app.on_after_finalize.connect
def setup_periodic_tasks(sender, **_kwargs):
"""Register periodic tasks"""
sender.add_periodic_task(schedule=crontab(minute=11), sig=refresh_individual_sex_affected.s())
21 changes: 19 additions & 2 deletions clinvar_export/tests/test_models.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
from variants.models import Case
from variants.tests.factories import CaseFactory

from ..models import (
from clinvar_export.models import (
AssertionMethod,
Family,
Individual,
Expand All @@ -14,8 +14,9 @@
SubmissionSet,
Submitter,
SubmittingOrg,
refresh_individual_sex_affected,
)
from .factories import (
from clinvar_export.tests.factories import (
AssertionMethodFactory,
FamilyFactory,
IndividualFactory,
Expand Down Expand Up @@ -169,3 +170,19 @@ def testCreate(self):
self.assertEquals(Submission.objects.count(), 0)
SubmissionFactory()
self.assertEquals(Submission.objects.count(), 1)


class TestUpdateIndividualSexAffectedTask(TestCase):
"""Test the task that updates the sex/affected of ``Individual`` records from upstream case."""

def testRun(self):
individual = IndividualFactory(sex="male", affected="yes")
individual.sex = "unknown"
individual.affected = "unknown"
individual.save()
self.assertEquals(individual.sex, "unknown")
self.assertEquals(individual.sex, "unknown")
refresh_individual_sex_affected()
individual.refresh_from_db()
self.assertEquals(individual.sex, "male")
self.assertEquals(individual.affected, "yes")

0 comments on commit 72b6a97

Please sign in to comment.