Skip to content

Commit

Permalink
Merge pull request #4245 from broadinstitute/empty-family-error
Browse files Browse the repository at this point in the history
properly populate individual aggregate fields for families with no individuals
  • Loading branch information
hanars authored Jul 22, 2024
2 parents 6651221 + 0b9cd9e commit 67f2aec
Show file tree
Hide file tree
Showing 2 changed files with 55 additions and 23 deletions.
69 changes: 46 additions & 23 deletions seqr/views/apis/project_api.py
Original file line number Diff line number Diff line change
Expand Up @@ -181,6 +181,35 @@ def project_page_data(request, project_guid):
})


FAMILY_INDIVIDUAL_FIELDS = {
'caseReviewStatuses': {'agg': ArrayAgg('case_review_status', distinct=True, filter=~Q(case_review_status=''))},
'caseReviewStatusLastModified': {'agg': Max('case_review_status_last_modified_date'), 'default': None},
'parental_ids': {
'agg': ArrayAgg(JSONObject(**{k: k for k in ['id', 'guid', 'father_id', 'mother_id']})),
'format': lambda parental_ids, id_guid_map: [
{'paternalGuid': id_guid_map.get(p['father_id']), 'maternalGuid': id_guid_map.get(p['mother_id'])}
for p in parental_ids if p['father_id'] or p['mother_id']
],
'response_key': 'parents',
},
'metadata_count': {
'agg': Count('id', filter=Q(
features__0__isnull=False, birth_year__isnull=False,
population__isnull=False, proband_relationship__isnull=False,
)),
'format': lambda metadata_count, *args: bool(metadata_count),
'response_key': 'hasRequiredMetadata',
},
}


def _get_formatted_value(value, config, *args):
value = value or config.get('default', [])
if config.get('format'):
value = config['format'](value, *args)
return value


@login_and_policies_required
def project_families(request, project_guid):
project = get_project_and_check_permissions(project_guid, request.user)
Expand All @@ -197,30 +226,24 @@ def project_families(request, project_guid):
)
families_by_id = {f.pop('id'): f for f in families}

phenotype_priority_families = set(PhenotypePrioritization.objects.filter(
individual__family_id__in=families_by_id).values_list('individual__family_id', flat=True).distinct())
family_individuals = Individual.objects.filter(family_id__in=families_by_id).values('family_id').annotate(
caseReviewStatuses=ArrayAgg('case_review_status', distinct=True, filter=~Q(case_review_status='')),
caseReviewStatusLastModified=Max('case_review_status_last_modified_date'),
parental_ids=ArrayAgg(JSONObject(**{k: k for k in ['id', 'guid', 'father_id', 'mother_id']})),
metadata_count=Count('id', filter=Q(
features__0__isnull=False, birth_year__isnull=False,
population__isnull=False, proband_relationship__isnull=False,
)),
)
for individual_agg in family_individuals:
family_id = individual_agg.pop('family_id')
parental_ids = individual_agg.pop('parental_ids')
id_guid_map = {i['id']: i['guid'] for i in parental_ids}
families_by_id[family_id].update({
has_data_families = {
'hasPhenotypePrioritization': set(PhenotypePrioritization.objects.filter(
individual__family_id__in=families_by_id).values_list('individual__family_id', flat=True).distinct()),
}

family_individual_aggs = {
agg.pop('family_id'): agg for agg in Individual.objects.filter(family_id__in=families_by_id).values('family_id').annotate(
**{k: v['agg'] for k, v in FAMILY_INDIVIDUAL_FIELDS.items()}
)
}
for family_id, family in families_by_id.items():
individual_agg = family_individual_aggs.get(family_id, {})
id_guid_map = {i['id']: i['guid'] for i in individual_agg.get('parental_ids', [])}
family.update({
'individualGuids': sorted(id_guid_map.values()),
'hasPhenotypePrioritization': family_id in phenotype_priority_families,
'hasRequiredMetadata': individual_agg.pop('metadata_count') > 0,
'parents': [
{'paternalGuid': id_guid_map.get(p['father_id']), 'maternalGuid': id_guid_map.get(p['mother_id'])}
for p in parental_ids if p['father_id'] or p['mother_id']
],
**individual_agg,
**{config.get('response_key', key): _get_formatted_value(individual_agg.get(key), config, id_guid_map)
for key, config in FAMILY_INDIVIDUAL_FIELDS.items()},
**{key: family_id in data_families for key, data_families in has_data_families.items()},
})

response = families_discovery_tags(families, project=project)
Expand Down
9 changes: 9 additions & 0 deletions seqr/views/apis/project_api_tests.py
Original file line number Diff line number Diff line change
Expand Up @@ -380,27 +380,36 @@ def test_project_families(self):

family_1 = response_json['familiesByGuid']['F000001_1']
family_3 = response_json['familiesByGuid']['F000003_3']
empty_family = response_json['familiesByGuid']['F000013_13']
family_fields = {
'individualGuids', 'discoveryTags', 'caseReviewStatuses', 'caseReviewStatusLastModified', 'hasRequiredMetadata',
'parents', 'hasPhenotypePrioritization',
}
family_fields.update(SUMMARY_FAMILY_FIELDS)
self.assertSetEqual(set(family_1.keys()), family_fields)
self.assertSetEqual(set(empty_family.keys()), family_fields)

self.assertEqual(len(family_1['individualGuids']), 3)
self.assertEqual(len(family_3['individualGuids']), 1)
self.assertEqual(len(empty_family['individualGuids']), 0)
self.assertListEqual(family_1['caseReviewStatuses'], ['A', 'I', 'U'])
self.assertListEqual(family_3['caseReviewStatuses'], [])
self.assertListEqual(empty_family['caseReviewStatuses'], [])
self.assertEqual(family_1['caseReviewStatusLastModified'], '2017-03-12T22:34:49.964Z')
self.assertIsNone(family_3['caseReviewStatusLastModified'])
self.assertIsNone(empty_family['caseReviewStatusLastModified'])
self.assertTrue(family_1['hasRequiredMetadata'])
self.assertFalse(family_3['hasRequiredMetadata'])
self.assertFalse(empty_family['hasRequiredMetadata'])
self.assertListEqual(family_1['parents'], [{'maternalGuid': 'I000003_na19679', 'paternalGuid': 'I000002_na19678'}])
self.assertListEqual(family_3['parents'], [])
self.assertListEqual(empty_family['parents'], [])
self.assertEqual(family_1['hasPhenotypePrioritization'], True)
self.assertFalse(family_3['hasPhenotypePrioritization'], False)
self.assertFalse(empty_family['hasPhenotypePrioritization'], False)

self.assertListEqual(family_3['discoveryTags'], [])
self.assertListEqual(empty_family['discoveryTags'], [])
self.assertSetEqual({tag['variantGuid'] for tag in family_1['discoveryTags']}, {'SV0000001_2103343353_r0390_100'})
self.assertSetEqual(
{tag['variantGuid'] for tag in response_json['familiesByGuid']['F000002_2']['discoveryTags']},
Expand Down

0 comments on commit 67f2aec

Please sign in to comment.