Skip to content

Commit 8397e82

Browse files
authored
Merge pull request #5041 from broadinstitute/delete-search-functionality-cleanup
Delete search functionality cleanup
2 parents 745f20f + 33e6fbd commit 8397e82

File tree

5 files changed

+44
-72
lines changed

5 files changed

+44
-72
lines changed
Lines changed: 2 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,7 @@
11
from django.core.management.base import BaseCommand, CommandError
22

3-
from clickhouse_search.search import delete_clickhouse_project
43
from seqr.models import Project, Sample
5-
from seqr.utils.search.utils import backend_specific_call
4+
from seqr.utils.search.utils import es_only
65

76
import logging
87
logger = logging.getLogger(__name__)
@@ -12,6 +11,7 @@ class Command(BaseCommand):
1211
def add_arguments(self, parser):
1312
parser.add_argument('project')
1413

14+
@es_only
1515
def handle(self, *args, **options):
1616
project = Project.objects.get(guid=options['project'])
1717

@@ -21,15 +21,3 @@ def handle(self, *args, **options):
2121
updated = Sample.bulk_update(user=None, update_json={'is_active': False}, individual__family__project=project)
2222

2323
logger.info(f'Deactivated {len(updated)} samples')
24-
25-
if updated:
26-
dataset_types = Sample.objects.filter(guid__in=updated).values_list('dataset_type', 'sample_type').order_by('dataset_type').distinct()
27-
for dataset_type, sample_type in dataset_types:
28-
backend_specific_call(
29-
lambda *args, **kwargs: True, self._delete_clickhouse_project,
30-
)(project, dataset_type, sample_type)
31-
32-
@staticmethod
33-
def _delete_clickhouse_project(project, dataset_type, sample_type):
34-
info = delete_clickhouse_project(project, dataset_type, sample_type)
35-
logger.info(info)

seqr/management/tests/deactivate_project_search_tests.py

Lines changed: 8 additions & 42 deletions
Original file line numberDiff line numberDiff line change
@@ -15,9 +15,8 @@
1515
VARIANT_ID = '21-3343353-GAGA-G'
1616

1717

18-
class DeactivateProjectSearchTest(object):
19-
20-
DELETE_SUCCESS_LOGS = []
18+
class ElasticsearchDeactivateProjectSearchTest(AuthenticationTestCase):
19+
fixtures = ['users', '1kg_project']
2120

2221
@responses.activate
2322
@mock.patch('seqr.management.commands.deactivate_project_search.input')
@@ -47,54 +46,21 @@ def test_command(self, mock_input):
4746
'updateType': 'bulk_update'},
4847
}),
4948
('Deactivated 11 samples', None),
50-
] + self.DELETE_SUCCESS_LOGS)
49+
])
5150

5251
active_samples = Sample.objects.filter(individual__family__project__guid=PROJECT_GUID, is_active=True)
5352
self.assertEqual(active_samples.count(), 0)
54-
self._assert_expected_delete()
5553

5654
# Re-running has no effect
5755
self.reset_logs()
5856
call_command('deactivate_project_search', PROJECT_GUID)
5957
self.assert_json_logs(user=None, expected=[('Deactivated 0 samples', None)])
6058

61-
def _assert_expected_delete(self):
62-
pass
63-
64-
class ElasticsearchDeactivateProjectSearchTest(AuthenticationTestCase, DeactivateProjectSearchTest):
65-
fixtures = ['users', '1kg_project']
66-
6759

68-
class ClickhouseDeactivateProjectSearchTest(AnvilAuthenticationTestCase, DeactivateProjectSearchTest):
60+
class ClickhouseDeactivateProjectSearchTest(AnvilAuthenticationTestCase):
6961
fixtures = ['users', '1kg_project', 'reference_data', 'clickhouse_search']
7062

71-
DELETE_SUCCESS_LOGS = [
72-
('Deleted all MITO search data for project 1kg project nåme with uniçøde', None),
73-
('Deleted all SNV_INDEL search data for project 1kg project nåme with uniçøde', None),
74-
('Deleted all GCNV search data for project 1kg project nåme with uniçøde', None),
75-
]
76-
77-
def _assert_expected_delete(self):
78-
self.assertEqual(EntriesSnvIndel.objects.filter(project_guid=PROJECT_GUID).count(), 0)
79-
self.assertEqual(ProjectGtStatsSnvIndel.objects.filter(project_guid=PROJECT_GUID).count(), 0)
80-
self.assertEqual(EntriesMito.objects.filter(project_guid=PROJECT_GUID).count(), 0)
81-
self.assertEqual(ProjectGtStatsMito.objects.filter(project_guid=PROJECT_GUID).count(), 0)
82-
self.assertEqual(EntriesGcnv.objects.filter(project_guid=PROJECT_GUID).count(), 0)
83-
84-
updated_seqr_pops_by_key = dict(AnnotationsSnvIndel.objects.all().join_seqr_pop().values_list('key', 'seqrPop'))
85-
self.assertDictEqual(updated_seqr_pops_by_key, {
86-
1: (2, 2, 1, 1),
87-
2: (1, 1, 0, 0),
88-
3: (0, 0, 0, 0),
89-
4: (0, 0, 0, 0),
90-
5: (1, 1, 0, 0),
91-
6: (0, 0, 0, 0),
92-
22: (0, 3, 0, 1),
93-
})
94-
95-
updated_mito_pops_by_key = dict(AnnotationsMito.objects.all().join_seqr_pop().values_list('key', 'seqrPop'))
96-
self.assertDictEqual(updated_mito_pops_by_key, {
97-
6: (0, 0, 0, 0),
98-
7: (0, 0, 0, 0),
99-
8: (0, 0, 0, 0),
100-
})
63+
def test_command(self):
64+
with self.assertRaises(ValueError) as e:
65+
call_command('deactivate_project_search', 'foo')
66+
self.assertEqual(str(e.exception), 'handle is disabled without the elasticsearch backend')

seqr/views/apis/data_manager_api.py

Lines changed: 11 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -425,7 +425,8 @@ def trigger_delete_project(request):
425425
request_json = json.loads(request.body)
426426
project_guid = request_json.pop('project')
427427
project = Project.objects.get(guid=project_guid)
428-
return _trigger_data_update(delete_clickhouse_project, request_json, project)
428+
samples = Sample.objects.filter(individual__family__project=project)
429+
return _trigger_data_update(delete_clickhouse_project, request.user, samples, request_json, project)
429430

430431

431432
@data_manager_required
@@ -434,15 +435,18 @@ def trigger_delete_family(request):
434435
request_json = json.loads(request.body)
435436
family_guid = request_json.pop('family')
436437
project = Project.objects.get(family__guid=family_guid)
437-
return _trigger_data_update(delete_clickhouse_family, request_json, project, family_guid)
438+
samples = Sample.objects.filter(individual__family__guid=family_guid)
439+
return _trigger_data_update(delete_clickhouse_family, request.user, samples, request_json, project, family_guid)
438440

439441

440-
def _trigger_data_update(clickhouse_func, request_json, project, *args):
442+
def _trigger_data_update(clickhouse_func, user, samples, request_json, project, *args):
441443
dataset_type = request_json.get('datasetType')
442-
sample_types = Sample.objects.filter(
443-
individual__family__project=project, dataset_type=dataset_type, is_active=True,
444-
).values_list('sample_type', flat=True).distinct() if dataset_type == Sample.DATASET_TYPE_SV_CALLS else [None]
445-
info = []
444+
samples = samples.filter(dataset_type=dataset_type, is_active=True)
445+
sample_types = list(
446+
samples.values_list('sample_type', flat=True).distinct()
447+
) if dataset_type == Sample.DATASET_TYPE_SV_CALLS else [None]
448+
updated = Sample.bulk_update(user=user, update_json={'is_active': False}, queryset=samples)
449+
info = [f'Deactivated search for {len(updated)} individuals']
446450
for sample_type in sample_types:
447451
info.append(clickhouse_func(project, *args, dataset_type=dataset_type, sample_type=sample_type))
448452
return create_json_response({'info': info})

seqr/views/apis/data_manager_api_tests.py

Lines changed: 17 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@
1414
from seqr.views.utils.orm_to_json_utils import _get_json_for_models
1515
from seqr.views.utils.test_utils import AuthenticationTestCase, AnvilAuthenticationTestCase, AirtableTest
1616
from seqr.utils.search.elasticsearch.es_utils_tests import urllib3_responses
17-
from seqr.models import Individual, RnaSeqOutlier, RnaSeqTpm, RnaSeqSpliceOutlier, RnaSample, Project, PhenotypePrioritization
17+
from seqr.models import Individual, Sample, RnaSeqOutlier, RnaSeqTpm, RnaSeqSpliceOutlier, RnaSample, Project, PhenotypePrioritization
1818
from settings import SEQR_SLACK_LOADING_NOTIFICATION_CHANNEL
1919

2020
PROJECT_GUID = 'R0001_1kg'
@@ -2053,7 +2053,10 @@ def _test_validate_dataset_type(self, url):
20532053
def _assert_expected_delete_project(self, response):
20542054
self.assertEqual(response.status_code, 200)
20552055
self.assertDictEqual(response.json(), {
2056-
'info': ['Deleted all SNV_INDEL search data for project 1kg project n\xe5me with uni\xe7\xf8de'],
2056+
'info': [
2057+
'Deactivated search for 7 individuals',
2058+
'Deleted all SNV_INDEL search data for project 1kg project n\xe5me with uni\xe7\xf8de',
2059+
],
20572060
})
20582061
self.assertEqual(EntriesSnvIndel.objects.filter(project_guid=PROJECT_GUID).count(), 0)
20592062
self.assertEqual(ProjectGtStatsSnvIndel.objects.filter(project_guid=PROJECT_GUID).count(), 0)
@@ -2069,12 +2072,23 @@ def _assert_expected_delete_project(self, response):
20692072
22: (0, 3, 0, 1),
20702073
})
20712074

2075+
project_samples = Sample.objects.filter(individual__family__project__guid=PROJECT_GUID, is_active=True)
2076+
self.assertEqual(project_samples.filter(dataset_type='SNV_INDEL').count(), 0)
2077+
self.assertEqual(project_samples.count(), 4)
2078+
20722079
def _assert_expected_delete_family(self, response):
20732080
self.assertEqual(response.status_code, 200)
20742081
self.assertDictEqual(response.json(), {
2075-
'info': ['Clickhouse does not support deleting individual families from project. Manually delete GCNV data for F000002_2 in project R0001_1kg'],
2082+
'info': [
2083+
'Deactivated search for 3 individuals',
2084+
'Clickhouse does not support deleting individual families from project. Manually delete GCNV data for F000002_2 in project R0001_1kg',
2085+
],
20762086
})
20772087

2088+
family_samples = Sample.objects.filter(individual__family_id=2, is_active=True)
2089+
self.assertEqual(family_samples.filter(dataset_type='SV').count(), 0)
2090+
self.assertEqual(family_samples.count(),4)
2091+
20782092
def _assert_expected_airtable_errors(self, url):
20792093
responses.replace(
20802094
responses.GET, 'https://api.airtable.com/v0/app3Y97xtbbaOopVR/Samples',

ui/pages/DataManagement/components/TriggerSearchDataUpdatePages.jsx

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -46,25 +46,25 @@ const FAMILY_FIELDS = [
4646
DATASET_TYPE_FIELD,
4747
]
4848

49-
const TriggerSearchDataUpdateForm = ({ path, fields }) => (
49+
const TriggerSearchDataUpdateForm = ({ entity, fields }) => (
5050
<SubmitFormPage
51-
header={`Trigger ${snakecaseToTitlecase(path)}`}
52-
url={`/api/data_management/trigger_${path}`}
51+
header={`Trigger Delete ${snakecaseToTitlecase(entity)} Search`}
52+
url={`/api/data_management/trigger_delete_${entity}`}
5353
fields={fields}
5454
/>
5555
)
5656

5757
TriggerSearchDataUpdateForm.propTypes = {
58-
path: PropTypes.string,
58+
entity: PropTypes.string,
5959
fields: PropTypes.arrayOf(PropTypes.object),
6060
}
6161

6262
const TriggerDeleteProjects = () => (
63-
<TriggerSearchDataUpdateForm path="delete_project" fields={PROJECT_FIELDS} />
63+
<TriggerSearchDataUpdateForm entity="project" fields={PROJECT_FIELDS} />
6464
)
6565

6666
const TriggerDeleteFamilies = () => (
67-
<TriggerSearchDataUpdateForm path="delete_family" fields={FAMILY_FIELDS} />
67+
<TriggerSearchDataUpdateForm entity="family" fields={FAMILY_FIELDS} />
6868
)
6969

7070
export default [

0 commit comments

Comments
 (0)