From 71e905dc5b05023c2e1b4a630d532fe18414cc32 Mon Sep 17 00:00:00 2001 From: Shifa Zhang Date: Thu, 20 Jul 2023 11:33:50 -0400 Subject: [PATCH 01/25] Add multiple variant tags filtering feature. --- seqr/views/apis/summary_data_api.py | 9 ++++++--- ui/pages/Project/components/SavedVariants.jsx | 9 ++++++--- ui/pages/SummaryData/components/SavedVariants.jsx | 2 +- ui/shared/components/panel/variants/SavedVariants.jsx | 6 ++++-- ui/shared/components/panel/variants/selectors.js | 6 +++++- 5 files changed, 22 insertions(+), 10 deletions(-) diff --git a/seqr/views/apis/summary_data_api.py b/seqr/views/apis/summary_data_api.py index 847949dc4a..1db0e52f77 100644 --- a/seqr/views/apis/summary_data_api.py +++ b/seqr/views/apis/summary_data_api.py @@ -7,7 +7,7 @@ from matchmaker.matchmaker_utils import get_mme_gene_phenotype_ids_for_submissions, parse_mme_features, \ get_mme_metrics, get_hpo_terms_by_id from matchmaker.models import MatchmakerSubmission -from seqr.models import Family, Individual, VariantTagType, SavedVariant, FamilyAnalysedBy +from seqr.models import Family, Individual, VariantTag, SavedVariant, FamilyAnalysedBy from seqr.views.utils.file_utils import load_uploaded_file from seqr.utils.gene_utils import get_genes from seqr.views.utils.json_utils import create_json_response @@ -87,8 +87,11 @@ def saved_variants_page(request, tag): if tag == 'ALL': saved_variant_models = SavedVariant.objects.exclude(varianttag=None) else: - tag_type = VariantTagType.objects.get(name=tag, project__isnull=True) - saved_variant_models = SavedVariant.objects.filter(varianttag__variant_tag_type=tag_type) + tags = tag.split(';') + saved_variant_models = SavedVariant.objects.filter( + varianttag__variant_tag_type__name__in=tags, + varianttag__variant_tag_type__project__isnull=True, + ) saved_variant_models = saved_variant_models.filter(family__project__guid__in=get_project_guids_user_can_view(request.user)) diff --git a/ui/pages/Project/components/SavedVariants.jsx b/ui/pages/Project/components/SavedVariants.jsx index 98996d7ff6..e53bc1a3f2 100644 --- a/ui/pages/Project/components/SavedVariants.jsx +++ b/ui/pages/Project/components/SavedVariants.jsx @@ -142,12 +142,15 @@ class BaseProjectSavedVariants extends React.PureComponent { project.variantTagTypes.map(type => type.category).filter(category => category), )] - const isCategory = categoryOptions.includes(newTag) - updateTableField('categoryFilter')(isCategory ? newTag : null) + const tagString = newTag.map((tag) => { + const isCategory = categoryOptions.includes(tag) + updateTableField('categoryFilter')(isCategory ? newTag : null) + return !isCategory && tag !== ALL_FILTER && tag + }).filter(tag => tag).join(';') return getSavedVariantsLinkPath({ projectGuid: project.projectGuid, analysisGroupGuid: match.params.analysisGroupGuid, - tag: !isCategory && newTag !== ALL_FILTER && newTag, + tag: tagString, familyGuid: match.params.familyGuid, }) } diff --git a/ui/pages/SummaryData/components/SavedVariants.jsx b/ui/pages/SummaryData/components/SavedVariants.jsx index 43b9d98fa1..213562aa21 100644 --- a/ui/pages/SummaryData/components/SavedVariants.jsx +++ b/ui/pages/SummaryData/components/SavedVariants.jsx @@ -68,7 +68,7 @@ TAG_OPTIONS.push({ const PAGE_URL = '/summary_data/saved_variants' const getUpdateTagUrl = - (selectedTag, match) => `${PAGE_URL}/${selectedTag}${match.params.gene ? `/${match.params.gene}` : ''}` + (selectedTag, match) => `${PAGE_URL}/${selectedTag.join(';')}${match.params.gene ? `/${match.params.gene}` : ''}` const getGeneHref = tag => selectedGene => `${PAGE_URL}/${tag || SHOW_ALL}/${selectedGene.key}` diff --git a/ui/shared/components/panel/variants/SavedVariants.jsx b/ui/shared/components/panel/variants/SavedVariants.jsx index cdc460143d..ca6018ba8a 100644 --- a/ui/shared/components/panel/variants/SavedVariants.jsx +++ b/ui/shared/components/panel/variants/SavedVariants.jsx @@ -81,9 +81,10 @@ class SavedVariants extends React.PureComponent { const { showAllFilters } = this.state const { familyGuid, variantGuid, tag } = match.params - const appliedTagCategoryFilter = tag || (variantGuid ? null : (tableState.categoryFilter || ALL_FILTER)) + const tags = tag ? tag.split(';') : tag + const appliedTagCategoryFilter = tags || (variantGuid ? [] : [(tableState.categoryFilter || ALL_FILTER)]) - let shownFilters = (discoveryFilters && appliedTagCategoryFilter === DISCOVERY_CATEGORY_NAME) ? + let shownFilters = (discoveryFilters && appliedTagCategoryFilter === [DISCOVERY_CATEGORY_NAME]) ? discoveryFilters : filters const hasHiddenFilters = !showAllFilters && shownFilters.length > MAX_FILTERS if (hasHiddenFilters) { @@ -111,6 +112,7 @@ class SavedVariants extends React.PureComponent { {`Showing ${shownSummary} ${filteredVariantsCount} `} mmeSubmissions.length) } else if (tag && tag !== SHOW_ALL) { + const tags = tag.split(';') pairedVariants = matchingVariants( - pairedVariants, ({ tagGuids }) => tagGuids.some(tagGuid => tagsByGuid[tagGuid].name === tag), + pairedVariants, ({ tagGuids }) => { + const tagNames = tagGuids.map(tagGuid => tagsByGuid[tagGuid].name) + return tags.every(tagName => tagNames.includes(tagName)) + }, ) } else if (!(familyGuid || analysisGroupGuid)) { pairedVariants = matchingVariants(pairedVariants, ({ tagGuids }) => tagGuids.length) From 424e66b8c72fe4090507d791d50d498143a05086 Mon Sep 17 00:00:00 2001 From: Shifa Zhang Date: Thu, 20 Jul 2023 14:56:36 -0400 Subject: [PATCH 02/25] Fix codacy and ALL filter. --- seqr/views/apis/summary_data_api.py | 2 +- ui/shared/components/panel/variants/SavedVariants.jsx | 6 +++++- 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/seqr/views/apis/summary_data_api.py b/seqr/views/apis/summary_data_api.py index 1db0e52f77..71aba31d9a 100644 --- a/seqr/views/apis/summary_data_api.py +++ b/seqr/views/apis/summary_data_api.py @@ -7,7 +7,7 @@ from matchmaker.matchmaker_utils import get_mme_gene_phenotype_ids_for_submissions, parse_mme_features, \ get_mme_metrics, get_hpo_terms_by_id from matchmaker.models import MatchmakerSubmission -from seqr.models import Family, Individual, VariantTag, SavedVariant, FamilyAnalysedBy +from seqr.models import Family, Individual, SavedVariant, FamilyAnalysedBy from seqr.views.utils.file_utils import load_uploaded_file from seqr.utils.gene_utils import get_genes from seqr.views.utils.json_utils import create_json_response diff --git a/ui/shared/components/panel/variants/SavedVariants.jsx b/ui/shared/components/panel/variants/SavedVariants.jsx index ca6018ba8a..3a58c82096 100644 --- a/ui/shared/components/panel/variants/SavedVariants.jsx +++ b/ui/shared/components/panel/variants/SavedVariants.jsx @@ -65,7 +65,11 @@ class SavedVariants extends React.PureComponent { navigateToTag = (e, data) => { const { history, getUpdateTagUrl, match } = this.props - history.push(getUpdateTagUrl(data.value, match)) + const { value } = data + if (value.length === 0) { + value.push(ALL_FILTER) + } + history.push(getUpdateTagUrl(value.length > 1 ? value.filter(tag => tag !== ALL_FILTER) : value, match)) } showAllFilters = () => { From f13dc6c68aed2df84452e16fa71ab559ac96b19f Mon Sep 17 00:00:00 2001 From: Shifa Zhang Date: Wed, 26 Jul 2023 12:55:29 -0400 Subject: [PATCH 03/25] Update category and all option controls. --- ui/pages/Project/components/SavedVariants.jsx | 12 ++++++------ ui/pages/SummaryData/components/SavedVariants.jsx | 8 ++++++-- .../components/panel/variants/SavedVariants.jsx | 6 +----- 3 files changed, 13 insertions(+), 13 deletions(-) diff --git a/ui/pages/Project/components/SavedVariants.jsx b/ui/pages/Project/components/SavedVariants.jsx index e53bc1a3f2..fb8034f1f4 100644 --- a/ui/pages/Project/components/SavedVariants.jsx +++ b/ui/pages/Project/components/SavedVariants.jsx @@ -142,15 +142,15 @@ class BaseProjectSavedVariants extends React.PureComponent { project.variantTagTypes.map(type => type.category).filter(category => category), )] - const tagString = newTag.map((tag) => { - const isCategory = categoryOptions.includes(tag) - updateTableField('categoryFilter')(isCategory ? newTag : null) - return !isCategory && tag !== ALL_FILTER && tag - }).filter(tag => tag).join(';') + const lastNewTag = newTag.length > 0 ? newTag[newTag.length - 1] : null + const isCategory = categoryOptions.includes(lastNewTag) + const [firstTag, ...otherTag] = newTag + const updatedTag = firstTag === ALL_FILTER || categoryOptions.includes(firstTag) ? otherTag : newTag + updateTableField('categoryFilter')(isCategory ? lastNewTag : null) return getSavedVariantsLinkPath({ projectGuid: project.projectGuid, analysisGroupGuid: match.params.analysisGroupGuid, - tag: tagString, + tag: !isCategory && lastNewTag !== ALL_FILTER && (updatedTag || []).join(';'), familyGuid: match.params.familyGuid, }) } diff --git a/ui/pages/SummaryData/components/SavedVariants.jsx b/ui/pages/SummaryData/components/SavedVariants.jsx index 213562aa21..88b3bd8d66 100644 --- a/ui/pages/SummaryData/components/SavedVariants.jsx +++ b/ui/pages/SummaryData/components/SavedVariants.jsx @@ -67,8 +67,12 @@ TAG_OPTIONS.push({ const PAGE_URL = '/summary_data/saved_variants' -const getUpdateTagUrl = - (selectedTag, match) => `${PAGE_URL}/${selectedTag.join(';')}${match.params.gene ? `/${match.params.gene}` : ''}` +const getUpdateTagUrl = (selectedTag, match) => { + const lastTag = selectedTag.length > 0 ? selectedTag[selectedTag.length - 1] : null + const [firstTag, ...otherTag] = selectedTag + const updatedTag = firstTag === SHOW_ALL ? otherTag : lastTag !== SHOW_ALL && selectedTag + return `${PAGE_URL}/${(updatedTag || [SHOW_ALL]).join(';')}${match.params.gene ? `/${match.params.gene}` : ''}` +} const getGeneHref = tag => selectedGene => `${PAGE_URL}/${tag || SHOW_ALL}/${selectedGene.key}` diff --git a/ui/shared/components/panel/variants/SavedVariants.jsx b/ui/shared/components/panel/variants/SavedVariants.jsx index 3a58c82096..211ebdcc9d 100644 --- a/ui/shared/components/panel/variants/SavedVariants.jsx +++ b/ui/shared/components/panel/variants/SavedVariants.jsx @@ -65,11 +65,7 @@ class SavedVariants extends React.PureComponent { navigateToTag = (e, data) => { const { history, getUpdateTagUrl, match } = this.props - const { value } = data - if (value.length === 0) { - value.push(ALL_FILTER) - } - history.push(getUpdateTagUrl(value.length > 1 ? value.filter(tag => tag !== ALL_FILTER) : value, match)) + history.push(getUpdateTagUrl(data.value.length > 0 ? data.value : [ALL_FILTER], match)) } showAllFilters = () => { From ab47e9cb8d50b48a93713e14db9df381e3b72e7d Mon Sep 17 00:00:00 2001 From: Shifa Zhang Date: Wed, 26 Jul 2023 16:35:48 -0400 Subject: [PATCH 04/25] Update tests. --- seqr/views/apis/summary_data_api.py | 6 +++--- seqr/views/apis/summary_data_api_tests.py | 6 ++++++ 2 files changed, 9 insertions(+), 3 deletions(-) diff --git a/seqr/views/apis/summary_data_api.py b/seqr/views/apis/summary_data_api.py index 71aba31d9a..a3e113ac07 100644 --- a/seqr/views/apis/summary_data_api.py +++ b/seqr/views/apis/summary_data_api.py @@ -1,5 +1,5 @@ from datetime import datetime -from django.db.models import CharField, F, Value +from django.db.models import CharField, F, Value, Q from django.db.models.functions import Coalesce, Concat, JSONObject, NullIf import json from random import randint @@ -7,7 +7,7 @@ from matchmaker.matchmaker_utils import get_mme_gene_phenotype_ids_for_submissions, parse_mme_features, \ get_mme_metrics, get_hpo_terms_by_id from matchmaker.models import MatchmakerSubmission -from seqr.models import Family, Individual, SavedVariant, FamilyAnalysedBy +from seqr.models import Family, Individual, SavedVariant, FamilyAnalysedBy, VariantTag from seqr.views.utils.file_utils import load_uploaded_file from seqr.utils.gene_utils import get_genes from seqr.views.utils.json_utils import create_json_response @@ -91,7 +91,7 @@ def saved_variants_page(request, tag): saved_variant_models = SavedVariant.objects.filter( varianttag__variant_tag_type__name__in=tags, varianttag__variant_tag_type__project__isnull=True, - ) + ).distinct() saved_variant_models = saved_variant_models.filter(family__project__guid__in=get_project_guids_user_can_view(request.user)) diff --git a/seqr/views/apis/summary_data_api_tests.py b/seqr/views/apis/summary_data_api_tests.py index 2a8dc3b0b3..15aad4a5a0 100644 --- a/seqr/views/apis/summary_data_api_tests.py +++ b/seqr/views/apis/summary_data_api_tests.py @@ -134,6 +134,12 @@ def test_saved_variants_page(self): expected_variant_guids.add('SV0000002_1248367227_r0390_100') self.assertSetEqual(set(response.json()['savedVariantsByGuid'].keys()), expected_variant_guids) + multi_tag_url = reverse(saved_variants_page, args=['Review;Tier 1 - Novel gene and phenotype']) + response = self.client.get('{}?gene=ENSG00000135953'.format(multi_tag_url)) + self.assertEqual(response.status_code, 200) + expected_variant_guids.remove('SV0000002_1248367227_r0390_100') + self.assertSetEqual(set(response.json()['savedVariantsByGuid'].keys()), expected_variant_guids) + def test_hpo_summary_data(self): url = reverse(hpo_summary_data, args=['HP:0002011']) self.check_require_login(url) From f3b94bf4ca1401dea1cde84af04c70ab61c9fb2f Mon Sep 17 00:00:00 2001 From: Shifa Zhang Date: Wed, 26 Jul 2023 16:37:09 -0400 Subject: [PATCH 05/25] Remove unused imports. --- seqr/views/apis/summary_data_api.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/seqr/views/apis/summary_data_api.py b/seqr/views/apis/summary_data_api.py index a3e113ac07..36e0472b8c 100644 --- a/seqr/views/apis/summary_data_api.py +++ b/seqr/views/apis/summary_data_api.py @@ -1,5 +1,5 @@ from datetime import datetime -from django.db.models import CharField, F, Value, Q +from django.db.models import CharField, F, Value from django.db.models.functions import Coalesce, Concat, JSONObject, NullIf import json from random import randint @@ -7,7 +7,7 @@ from matchmaker.matchmaker_utils import get_mme_gene_phenotype_ids_for_submissions, parse_mme_features, \ get_mme_metrics, get_hpo_terms_by_id from matchmaker.models import MatchmakerSubmission -from seqr.models import Family, Individual, SavedVariant, FamilyAnalysedBy, VariantTag +from seqr.models import Family, Individual, SavedVariant, FamilyAnalysedBy from seqr.views.utils.file_utils import load_uploaded_file from seqr.utils.gene_utils import get_genes from seqr.views.utils.json_utils import create_json_response From 48f011155e9aa661d9145186af9b65dd1df8d27b Mon Sep 17 00:00:00 2001 From: Shifa Zhang Date: Thu, 27 Jul 2023 11:34:17 -0400 Subject: [PATCH 06/25] Update reducer to prevent reloading. --- .../SummaryData/components/SavedVariants.jsx | 27 ++----------------- ui/pages/SummaryData/reducers.js | 16 ++++++++--- ui/shared/utils/constants.js | 23 ++++++++++++++++ 3 files changed, 37 insertions(+), 29 deletions(-) diff --git a/ui/pages/SummaryData/components/SavedVariants.jsx b/ui/pages/SummaryData/components/SavedVariants.jsx index 88b3bd8d66..cec39dd269 100644 --- a/ui/pages/SummaryData/components/SavedVariants.jsx +++ b/ui/pages/SummaryData/components/SavedVariants.jsx @@ -7,12 +7,11 @@ import { Form, Button } from 'semantic-ui-react' import { getGenesById } from 'redux/selectors' import { - REVIEW_TAG_NAME, - KNOWN_GENE_FOR_PHENOTYPE_TAG_NAME, VARIANT_SORT_FIELD, VARIANT_PER_PAGE_FIELD, VARIANT_TAGGED_DATE_FIELD, SHOW_ALL, + SUMMARY_PAGE_SAVED_VARIANT_TAGS, } from 'shared/utils/constants' import { StyledForm } from 'shared/components/form/FormHelpers' import AwesomeBar from 'shared/components/page/AwesomeBar' @@ -29,29 +28,7 @@ const FILTER_FIELDS = [ VARIANT_PER_PAGE_FIELD, ] -const TAG_OPTIONS = [ - 'Tier 1 - Novel gene and phenotype', - 'Tier 1 - Novel gene for known phenotype', - 'Tier 1 - Phenotype expansion', - 'Tier 1 - Phenotype not delineated', - 'Tier 1 - Novel mode of inheritance', - 'Tier 1 - Known gene, new phenotype', - 'Tier 2 - Novel gene and phenotype', - 'Tier 2 - Novel gene for known phenotype', - 'Tier 2 - Phenotype expansion', - 'Tier 2 - Phenotype not delineated', - 'Tier 2 - Known gene, new phenotype', - KNOWN_GENE_FOR_PHENOTYPE_TAG_NAME, - REVIEW_TAG_NAME, - 'Send for Sanger validation', - 'Sanger validated', - 'Sanger did not confirm', - 'Confident AR one hit', - 'Analyst high priority', - 'seqr MME (old)', - 'Submit to Clinvar', - 'Share with KOMP', -].map(name => ({ +const TAG_OPTIONS = SUMMARY_PAGE_SAVED_VARIANT_TAGS.map(name => ({ value: name, text: name, key: name, diff --git a/ui/pages/SummaryData/reducers.js b/ui/pages/SummaryData/reducers.js index acd895e5d1..cfdc264859 100644 --- a/ui/pages/SummaryData/reducers.js +++ b/ui/pages/SummaryData/reducers.js @@ -2,7 +2,7 @@ import { combineReducers } from 'redux' import { loadingReducer, createSingleValueReducer, createSingleObjectReducer } from 'redux/utils/reducerFactories' import { RECEIVE_DATA, REQUEST_SAVED_VARIANTS } from 'redux/utils/reducerUtils' -import { SHOW_ALL, SORT_BY_XPOS } from 'shared/utils/constants' +import { SHOW_ALL, SORT_BY_XPOS, SUMMARY_PAGE_SAVED_VARIANT_TAGS } from 'shared/utils/constants' import { HttpRequestHelper } from 'shared/utils/httpRequestHelper' // action creators and reducers in one file as suggested by https://github.com/erikras/ducks-modular-redux @@ -43,8 +43,11 @@ export const loadSuccessStory = successStoryTypes => (dispatch) => { export const loadSavedVariants = ({ tag, gene = '' }) => (dispatch, getState) => { // Do not load if already loaded + let tags = [SHOW_ALL] if (tag) { - if (getState().savedVariantTags[tag]) { + const { savedVariantTags } = getState() + tags = tag.split(';').filter(t => !savedVariantTags[t]) + if (tags.length === 0) { return } } else if (!gene) { @@ -52,13 +55,18 @@ export const loadSavedVariants = ({ tag, gene = '' }) => (dispatch, getState) => } dispatch({ type: REQUEST_SAVED_VARIANTS }) - new HttpRequestHelper(`/api/summary_data/saved_variants/${tag}`, + new HttpRequestHelper(`/api/summary_data/saved_variants/${tags.join(';')}`, (responseJson) => { if (tag && !gene) { + if (tags[0] === SHOW_ALL) { + tags = SUMMARY_PAGE_SAVED_VARIANT_TAGS + } dispatch({ type: RECEIVE_SAVED_VARIANT_TAGS, - updates: { [tag]: true }, + updates: tags.reduce((acc, t) => ({ ...acc, [t]: true }), {}), }) + } else if (gene) { + dispatch({ type: RECEIVE_SAVED_VARIANT_TAGS, updates: {} }) } dispatch({ type: RECEIVE_DATA, updatesById: responseJson }) }, diff --git a/ui/shared/utils/constants.js b/ui/shared/utils/constants.js index a36faa1b3a..64428af64d 100644 --- a/ui/shared/utils/constants.js +++ b/ui/shared/utils/constants.js @@ -1041,6 +1041,29 @@ export const REVIEW_TAG_NAME = 'Review' export const KNOWN_GENE_FOR_PHENOTYPE_TAG_NAME = 'Known gene for phenotype' export const DISCOVERY_CATEGORY_NAME = 'CMG Discovery Tags' export const MME_TAG_NAME = 'MME Submission' +export const SUMMARY_PAGE_SAVED_VARIANT_TAGS = [ + 'Tier 1 - Novel gene and phenotype', + 'Tier 1 - Novel gene for known phenotype', + 'Tier 1 - Phenotype expansion', + 'Tier 1 - Phenotype not delineated', + 'Tier 1 - Novel mode of inheritance', + 'Tier 1 - Known gene, new phenotype', + 'Tier 2 - Novel gene and phenotype', + 'Tier 2 - Novel gene for known phenotype', + 'Tier 2 - Phenotype expansion', + 'Tier 2 - Phenotype not delineated', + 'Tier 2 - Known gene, new phenotype', + KNOWN_GENE_FOR_PHENOTYPE_TAG_NAME, + REVIEW_TAG_NAME, + 'Send for Sanger validation', + 'Sanger validated', + 'Sanger did not confirm', + 'Confident AR one hit', + 'Analyst high priority', + 'seqr MME (old)', + 'Submit to Clinvar', + 'Share with KOMP', +] export const SORT_BY_FAMILY_GUID = 'FAMILY_GUID' export const SORT_BY_XPOS = 'XPOS' From de8cbd45a4006500010168b48280d89116f07baf Mon Sep 17 00:00:00 2001 From: Shifa Zhang Date: Thu, 27 Jul 2023 11:38:38 -0400 Subject: [PATCH 07/25] Correct clear loaded tags. --- ui/pages/SummaryData/reducers.js | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/ui/pages/SummaryData/reducers.js b/ui/pages/SummaryData/reducers.js index cfdc264859..399f632a1d 100644 --- a/ui/pages/SummaryData/reducers.js +++ b/ui/pages/SummaryData/reducers.js @@ -66,7 +66,10 @@ export const loadSavedVariants = ({ tag, gene = '' }) => (dispatch, getState) => updates: tags.reduce((acc, t) => ({ ...acc, [t]: true }), {}), }) } else if (gene) { - dispatch({ type: RECEIVE_SAVED_VARIANT_TAGS, updates: {} }) + dispatch({ + type: RECEIVE_SAVED_VARIANT_TAGS, + updates: SUMMARY_PAGE_SAVED_VARIANT_TAGS.reduce((acc, t) => ({ ...acc, [t]: false }), {}), + }) } dispatch({ type: RECEIVE_DATA, updatesById: responseJson }) }, From 83e3979397d4af6645612378769f59e8b833f06c Mon Sep 17 00:00:00 2001 From: Shifa Zhang Date: Thu, 3 Aug 2023 14:33:42 -0400 Subject: [PATCH 08/25] Use a constant for delimiter and update db filter. --- seqr/views/apis/summary_data_api.py | 9 +++--- seqr/views/apis/summary_data_api_tests.py | 2 +- ui/pages/Project/components/SavedVariants.jsx | 3 +- .../SummaryData/components/SavedVariants.jsx | 5 ++-- ui/pages/SummaryData/constants.js | 28 +++++++++++++++++-- ui/pages/SummaryData/reducers.js | 20 ++++--------- .../panel/variants/SavedVariants.jsx | 3 +- .../components/panel/variants/selectors.js | 3 +- ui/shared/utils/constants.js | 25 ++--------------- 9 files changed, 47 insertions(+), 51 deletions(-) diff --git a/seqr/views/apis/summary_data_api.py b/seqr/views/apis/summary_data_api.py index 36e0472b8c..d742e6bf81 100644 --- a/seqr/views/apis/summary_data_api.py +++ b/seqr/views/apis/summary_data_api.py @@ -88,10 +88,11 @@ def saved_variants_page(request, tag): saved_variant_models = SavedVariant.objects.exclude(varianttag=None) else: tags = tag.split(';') - saved_variant_models = SavedVariant.objects.filter( - varianttag__variant_tag_type__name__in=tags, - varianttag__variant_tag_type__project__isnull=True, - ).distinct() + saved_variant_models = SavedVariant.objects.all() + for t in tags: + saved_variant_models = saved_variant_models.filter( + varianttag__variant_tag_type__name=t, varianttag__variant_tag_type__project__isnull=True, + ).distinct() saved_variant_models = saved_variant_models.filter(family__project__guid__in=get_project_guids_user_can_view(request.user)) diff --git a/seqr/views/apis/summary_data_api_tests.py b/seqr/views/apis/summary_data_api_tests.py index 15aad4a5a0..887b9b0175 100644 --- a/seqr/views/apis/summary_data_api_tests.py +++ b/seqr/views/apis/summary_data_api_tests.py @@ -138,7 +138,7 @@ def test_saved_variants_page(self): response = self.client.get('{}?gene=ENSG00000135953'.format(multi_tag_url)) self.assertEqual(response.status_code, 200) expected_variant_guids.remove('SV0000002_1248367227_r0390_100') - self.assertSetEqual(set(response.json()['savedVariantsByGuid'].keys()), expected_variant_guids) + self.assertSetEqual(set(response.json()['savedVariantsByGuid'].keys()), {'SV0000001_2103343353_r0390_100'}) def test_hpo_summary_data(self): url = reverse(hpo_summary_data, args=['HP:0002011']) diff --git a/ui/pages/Project/components/SavedVariants.jsx b/ui/pages/Project/components/SavedVariants.jsx index fb8034f1f4..e38a555849 100644 --- a/ui/pages/Project/components/SavedVariants.jsx +++ b/ui/pages/Project/components/SavedVariants.jsx @@ -15,6 +15,7 @@ import { VARIANT_PER_PAGE_FIELD, EXCLUDED_TAG_NAME, REVIEW_TAG_NAME, + TAG_URL_DELIMITER, } from 'shared/utils/constants' import UpdateButton from 'shared/components/buttons/UpdateButton' import { LargeMultiselect, Dropdown } from 'shared/components/form/Inputs' @@ -150,7 +151,7 @@ class BaseProjectSavedVariants extends React.PureComponent { return getSavedVariantsLinkPath({ projectGuid: project.projectGuid, analysisGroupGuid: match.params.analysisGroupGuid, - tag: !isCategory && lastNewTag !== ALL_FILTER && (updatedTag || []).join(';'), + tag: !isCategory && lastNewTag !== ALL_FILTER && (updatedTag || []).join(TAG_URL_DELIMITER), familyGuid: match.params.familyGuid, }) } diff --git a/ui/pages/SummaryData/components/SavedVariants.jsx b/ui/pages/SummaryData/components/SavedVariants.jsx index cec39dd269..f0dd8ae2b9 100644 --- a/ui/pages/SummaryData/components/SavedVariants.jsx +++ b/ui/pages/SummaryData/components/SavedVariants.jsx @@ -11,7 +11,7 @@ import { VARIANT_PER_PAGE_FIELD, VARIANT_TAGGED_DATE_FIELD, SHOW_ALL, - SUMMARY_PAGE_SAVED_VARIANT_TAGS, + TAG_URL_DELIMITER, } from 'shared/utils/constants' import { StyledForm } from 'shared/components/form/FormHelpers' import AwesomeBar from 'shared/components/page/AwesomeBar' @@ -19,6 +19,7 @@ import SavedVariants from 'shared/components/panel/variants/SavedVariants' import { HorizontalSpacer } from 'shared/components/Spacers' import { loadSavedVariants, updateAllProjectSavedVariantTable } from '../reducers' +import { SUMMARY_PAGE_SAVED_VARIANT_TAGS } from '../constants' const GENE_SEARCH_CATEGORIES = ['genes'] @@ -48,7 +49,7 @@ const getUpdateTagUrl = (selectedTag, match) => { const lastTag = selectedTag.length > 0 ? selectedTag[selectedTag.length - 1] : null const [firstTag, ...otherTag] = selectedTag const updatedTag = firstTag === SHOW_ALL ? otherTag : lastTag !== SHOW_ALL && selectedTag - return `${PAGE_URL}/${(updatedTag || [SHOW_ALL]).join(';')}${match.params.gene ? `/${match.params.gene}` : ''}` + return `${PAGE_URL}/${(updatedTag || [SHOW_ALL]).join(TAG_URL_DELIMITER)}${match.params.gene ? `/${match.params.gene}` : ''}` } const getGeneHref = tag => selectedGene => `${PAGE_URL}/${tag || SHOW_ALL}/${selectedGene.key}` diff --git a/ui/pages/SummaryData/constants.js b/ui/pages/SummaryData/constants.js index 4fec4609a6..7abc5a3f76 100644 --- a/ui/pages/SummaryData/constants.js +++ b/ui/pages/SummaryData/constants.js @@ -1,9 +1,7 @@ -/* eslint-disable import/prefer-default-export */ - import React from 'react' import { Link } from 'react-router-dom' -import { successStoryTypeDisplay } from 'shared/utils/constants' +import { successStoryTypeDisplay, KNOWN_GENE_FOR_PHENOTYPE_TAG_NAME, REVIEW_TAG_NAME } from 'shared/utils/constants' const formatIDLink = row => {row.family_id} @@ -20,3 +18,27 @@ export const SUCCESS_STORY_COLUMNS = [ { name: 'success_story', content: 'Success Story', style: { minWidth: '564px' } }, { name: 'discovery_tags', content: 'Discovery Tags', format: formatDiscoveryTags, noFormatExport: true, style: { minWidth: '400px' } }, ] + +export const SUMMARY_PAGE_SAVED_VARIANT_TAGS = [ + 'Tier 1 - Novel gene and phenotype', + 'Tier 1 - Novel gene for known phenotype', + 'Tier 1 - Phenotype expansion', + 'Tier 1 - Phenotype not delineated', + 'Tier 1 - Novel mode of inheritance', + 'Tier 1 - Known gene, new phenotype', + 'Tier 2 - Novel gene and phenotype', + 'Tier 2 - Novel gene for known phenotype', + 'Tier 2 - Phenotype expansion', + 'Tier 2 - Phenotype not delineated', + 'Tier 2 - Known gene, new phenotype', + KNOWN_GENE_FOR_PHENOTYPE_TAG_NAME, + REVIEW_TAG_NAME, + 'Send for Sanger validation', + 'Sanger validated', + 'Sanger did not confirm', + 'Confident AR one hit', + 'Analyst high priority', + 'seqr MME (old)', + 'Submit to Clinvar', + 'Share with KOMP', +] diff --git a/ui/pages/SummaryData/reducers.js b/ui/pages/SummaryData/reducers.js index 399f632a1d..bdf18913e6 100644 --- a/ui/pages/SummaryData/reducers.js +++ b/ui/pages/SummaryData/reducers.js @@ -2,7 +2,7 @@ import { combineReducers } from 'redux' import { loadingReducer, createSingleValueReducer, createSingleObjectReducer } from 'redux/utils/reducerFactories' import { RECEIVE_DATA, REQUEST_SAVED_VARIANTS } from 'redux/utils/reducerUtils' -import { SHOW_ALL, SORT_BY_XPOS, SUMMARY_PAGE_SAVED_VARIANT_TAGS } from 'shared/utils/constants' +import { SHOW_ALL, SORT_BY_XPOS, TAG_URL_DELIMITER } from 'shared/utils/constants' import { HttpRequestHelper } from 'shared/utils/httpRequestHelper' // action creators and reducers in one file as suggested by https://github.com/erikras/ducks-modular-redux @@ -43,11 +43,9 @@ export const loadSuccessStory = successStoryTypes => (dispatch) => { export const loadSavedVariants = ({ tag, gene = '' }) => (dispatch, getState) => { // Do not load if already loaded - let tags = [SHOW_ALL] + const stateKey = `${tag ? tag.split(TAG_URL_DELIMITER).sort().join(TAG_URL_DELIMITER) : ''}${gene}` if (tag) { - const { savedVariantTags } = getState() - tags = tag.split(';').filter(t => !savedVariantTags[t]) - if (tags.length === 0) { + if (getState().savedVariantTags[stateKey]) { return } } else if (!gene) { @@ -55,20 +53,12 @@ export const loadSavedVariants = ({ tag, gene = '' }) => (dispatch, getState) => } dispatch({ type: REQUEST_SAVED_VARIANTS }) - new HttpRequestHelper(`/api/summary_data/saved_variants/${tags.join(';')}`, + new HttpRequestHelper(`/api/summary_data/saved_variants/${tag}`, (responseJson) => { if (tag && !gene) { - if (tags[0] === SHOW_ALL) { - tags = SUMMARY_PAGE_SAVED_VARIANT_TAGS - } dispatch({ type: RECEIVE_SAVED_VARIANT_TAGS, - updates: tags.reduce((acc, t) => ({ ...acc, [t]: true }), {}), - }) - } else if (gene) { - dispatch({ - type: RECEIVE_SAVED_VARIANT_TAGS, - updates: SUMMARY_PAGE_SAVED_VARIANT_TAGS.reduce((acc, t) => ({ ...acc, [t]: false }), {}), + updates: { [stateKey]: true }, }) } dispatch({ type: RECEIVE_DATA, updatesById: responseJson }) diff --git a/ui/shared/components/panel/variants/SavedVariants.jsx b/ui/shared/components/panel/variants/SavedVariants.jsx index 211ebdcc9d..afdb8c2ee6 100644 --- a/ui/shared/components/panel/variants/SavedVariants.jsx +++ b/ui/shared/components/panel/variants/SavedVariants.jsx @@ -8,6 +8,7 @@ import { getSavedVariantsIsLoading, getSavedVariantsLoadingError } from 'redux/s import { DISCOVERY_CATEGORY_NAME, VARIANT_PAGINATION_FIELD, + TAG_URL_DELIMITER, } from 'shared/utils/constants' import ExportTableButton from '../../buttons/ExportTableButton' @@ -81,7 +82,7 @@ class SavedVariants extends React.PureComponent { const { showAllFilters } = this.state const { familyGuid, variantGuid, tag } = match.params - const tags = tag ? tag.split(';') : tag + const tags = tag ? tag.split(TAG_URL_DELIMITER) : tag const appliedTagCategoryFilter = tags || (variantGuid ? [] : [(tableState.categoryFilter || ALL_FILTER)]) let shownFilters = (discoveryFilters && appliedTagCategoryFilter === [DISCOVERY_CATEGORY_NAME]) ? diff --git a/ui/shared/components/panel/variants/selectors.js b/ui/shared/components/panel/variants/selectors.js index b36ea2a45a..9a2fef1825 100644 --- a/ui/shared/components/panel/variants/selectors.js +++ b/ui/shared/components/panel/variants/selectors.js @@ -12,6 +12,7 @@ import { VARIANT_SORT_LOOKUP, SHOW_ALL, VARIANT_EXPORT_DATA, + TAG_URL_DELIMITER, } from 'shared/utils/constants' import { getVariantTagsByGuid, getVariantNotesByGuid, getSavedVariantsByGuid, getAnalysisGroupsByGuid, getGenesById, getUser, @@ -135,7 +136,7 @@ export const getPairedSelectedSavedVariants = createSelector( } else if (tag === MME_TAG_NAME) { pairedVariants = matchingVariants(pairedVariants, ({ mmeSubmissions = [] }) => mmeSubmissions.length) } else if (tag && tag !== SHOW_ALL) { - const tags = tag.split(';') + const tags = tag.split(TAG_URL_DELIMITER) pairedVariants = matchingVariants( pairedVariants, ({ tagGuids }) => { const tagNames = tagGuids.map(tagGuid => tagsByGuid[tagGuid].name) diff --git a/ui/shared/utils/constants.js b/ui/shared/utils/constants.js index 64428af64d..6d67fe63ec 100644 --- a/ui/shared/utils/constants.js +++ b/ui/shared/utils/constants.js @@ -1041,29 +1041,8 @@ export const REVIEW_TAG_NAME = 'Review' export const KNOWN_GENE_FOR_PHENOTYPE_TAG_NAME = 'Known gene for phenotype' export const DISCOVERY_CATEGORY_NAME = 'CMG Discovery Tags' export const MME_TAG_NAME = 'MME Submission' -export const SUMMARY_PAGE_SAVED_VARIANT_TAGS = [ - 'Tier 1 - Novel gene and phenotype', - 'Tier 1 - Novel gene for known phenotype', - 'Tier 1 - Phenotype expansion', - 'Tier 1 - Phenotype not delineated', - 'Tier 1 - Novel mode of inheritance', - 'Tier 1 - Known gene, new phenotype', - 'Tier 2 - Novel gene and phenotype', - 'Tier 2 - Novel gene for known phenotype', - 'Tier 2 - Phenotype expansion', - 'Tier 2 - Phenotype not delineated', - 'Tier 2 - Known gene, new phenotype', - KNOWN_GENE_FOR_PHENOTYPE_TAG_NAME, - REVIEW_TAG_NAME, - 'Send for Sanger validation', - 'Sanger validated', - 'Sanger did not confirm', - 'Confident AR one hit', - 'Analyst high priority', - 'seqr MME (old)', - 'Submit to Clinvar', - 'Share with KOMP', -] + +export const TAG_URL_DELIMITER = ';' export const SORT_BY_FAMILY_GUID = 'FAMILY_GUID' export const SORT_BY_XPOS = 'XPOS' From ee466784e07ac6542e87e8618aa7030ae4583ddf Mon Sep 17 00:00:00 2001 From: Shifa Zhang Date: Thu, 3 Aug 2023 17:22:57 -0400 Subject: [PATCH 09/25] Update multiple tags DB queries and tests. --- seqr/views/apis/summary_data_api.py | 9 ++++----- seqr/views/apis/summary_data_api_tests.py | 11 +++++++---- 2 files changed, 11 insertions(+), 9 deletions(-) diff --git a/seqr/views/apis/summary_data_api.py b/seqr/views/apis/summary_data_api.py index d742e6bf81..8dc771a75c 100644 --- a/seqr/views/apis/summary_data_api.py +++ b/seqr/views/apis/summary_data_api.py @@ -7,7 +7,7 @@ from matchmaker.matchmaker_utils import get_mme_gene_phenotype_ids_for_submissions, parse_mme_features, \ get_mme_metrics, get_hpo_terms_by_id from matchmaker.models import MatchmakerSubmission -from seqr.models import Family, Individual, SavedVariant, FamilyAnalysedBy +from seqr.models import Family, Individual, VariantTagType, SavedVariant, FamilyAnalysedBy from seqr.views.utils.file_utils import load_uploaded_file from seqr.utils.gene_utils import get_genes from seqr.views.utils.json_utils import create_json_response @@ -88,11 +88,10 @@ def saved_variants_page(request, tag): saved_variant_models = SavedVariant.objects.exclude(varianttag=None) else: tags = tag.split(';') + tag_type = VariantTagType.objects.filter(name__in=tags, project__isnull=True) saved_variant_models = SavedVariant.objects.all() - for t in tags: - saved_variant_models = saved_variant_models.filter( - varianttag__variant_tag_type__name=t, varianttag__variant_tag_type__project__isnull=True, - ).distinct() + for tt in tag_type: + saved_variant_models = saved_variant_models.filter(varianttag__variant_tag_type=tt).distinct() saved_variant_models = saved_variant_models.filter(family__project__guid__in=get_project_guids_user_can_view(request.user)) diff --git a/seqr/views/apis/summary_data_api_tests.py b/seqr/views/apis/summary_data_api_tests.py index 887b9b0175..343945d661 100644 --- a/seqr/views/apis/summary_data_api_tests.py +++ b/seqr/views/apis/summary_data_api_tests.py @@ -137,7 +137,6 @@ def test_saved_variants_page(self): multi_tag_url = reverse(saved_variants_page, args=['Review;Tier 1 - Novel gene and phenotype']) response = self.client.get('{}?gene=ENSG00000135953'.format(multi_tag_url)) self.assertEqual(response.status_code, 200) - expected_variant_guids.remove('SV0000002_1248367227_r0390_100') self.assertSetEqual(set(response.json()['savedVariantsByGuid'].keys()), {'SV0000001_2103343353_r0390_100'}) def test_hpo_summary_data(self): @@ -237,14 +236,18 @@ class LocalSummaryDataAPITest(AuthenticationTestCase, SummaryDataAPITest): MANAGER_VARIANT_GUID = 'SV0000006_1248367227_r0004_non' -def assert_has_expected_calls(self, users, skip_group_call_idxs=None): +def assert_has_expected_calls(self, users, skip_group_call_idxs=None, has_ws_access_level_call=False): calls = [mock.call(user) for user in users] self.mock_list_workspaces.assert_has_calls(calls) group_calls = [call for i, call in enumerate(calls) if i in skip_group_call_idxs] if skip_group_call_idxs else calls self.mock_get_groups.assert_has_calls(group_calls) self.mock_get_ws_acl.assert_not_called() self.mock_get_group_members.assert_not_called() - self.mock_get_ws_access_level.assert_not_called() + if has_ws_access_level_call: + self.mock_get_ws_access_level.assert_called_with( + self.analyst_user, 'my-seqr-billing', 'anvil-1kg project nåme with uniçøde') + else: + self.mock_get_ws_access_level.assert_not_called() # Test for permissions from AnVIL only class AnvilSummaryDataAPITest(AnvilAuthenticationTestCase, SummaryDataAPITest): @@ -260,4 +263,4 @@ def test_saved_variants_page(self): super(AnvilSummaryDataAPITest, self).test_saved_variants_page() assert_has_expected_calls(self, [ self.no_access_user, self.manager_user, self.manager_user, self.analyst_user, self.analyst_user - ], skip_group_call_idxs=[2]) + ], skip_group_call_idxs=[2], has_ws_access_level_call=True) From 5162ae97d23100748cd7b50d82bf569cab5ecff5 Mon Sep 17 00:00:00 2001 From: Shifa Zhang Date: Thu, 3 Aug 2023 18:00:36 -0400 Subject: [PATCH 10/25] A better state key generation. --- ui/pages/SummaryData/reducers.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ui/pages/SummaryData/reducers.js b/ui/pages/SummaryData/reducers.js index bdf18913e6..28aa7fd8ce 100644 --- a/ui/pages/SummaryData/reducers.js +++ b/ui/pages/SummaryData/reducers.js @@ -43,7 +43,7 @@ export const loadSuccessStory = successStoryTypes => (dispatch) => { export const loadSavedVariants = ({ tag, gene = '' }) => (dispatch, getState) => { // Do not load if already loaded - const stateKey = `${tag ? tag.split(TAG_URL_DELIMITER).sort().join(TAG_URL_DELIMITER) : ''}${gene}` + const stateKey = tag && `${tag.split(TAG_URL_DELIMITER).sort().join(TAG_URL_DELIMITER)}${gene}` if (tag) { if (getState().savedVariantTags[stateKey]) { return From 561822e1145fc3f27eeb084bf1c1f0d0b0a9a581 Mon Sep 17 00:00:00 2001 From: Shifa Zhang Date: Fri, 4 Aug 2023 15:52:40 -0400 Subject: [PATCH 11/25] Update the TAG_OPTIONS constant. --- .../SummaryData/components/SavedVariants.jsx | 27 ++++++++++++++++-- ui/pages/SummaryData/constants.js | 28 ++----------------- ui/pages/SummaryData/reducers.js | 7 ++--- 3 files changed, 31 insertions(+), 31 deletions(-) diff --git a/ui/pages/SummaryData/components/SavedVariants.jsx b/ui/pages/SummaryData/components/SavedVariants.jsx index f0dd8ae2b9..853986c62a 100644 --- a/ui/pages/SummaryData/components/SavedVariants.jsx +++ b/ui/pages/SummaryData/components/SavedVariants.jsx @@ -7,6 +7,8 @@ import { Form, Button } from 'semantic-ui-react' import { getGenesById } from 'redux/selectors' import { + REVIEW_TAG_NAME, + KNOWN_GENE_FOR_PHENOTYPE_TAG_NAME, VARIANT_SORT_FIELD, VARIANT_PER_PAGE_FIELD, VARIANT_TAGGED_DATE_FIELD, @@ -19,7 +21,6 @@ import SavedVariants from 'shared/components/panel/variants/SavedVariants' import { HorizontalSpacer } from 'shared/components/Spacers' import { loadSavedVariants, updateAllProjectSavedVariantTable } from '../reducers' -import { SUMMARY_PAGE_SAVED_VARIANT_TAGS } from '../constants' const GENE_SEARCH_CATEGORIES = ['genes'] @@ -29,7 +30,29 @@ const FILTER_FIELDS = [ VARIANT_PER_PAGE_FIELD, ] -const TAG_OPTIONS = SUMMARY_PAGE_SAVED_VARIANT_TAGS.map(name => ({ +const TAG_OPTIONS = [ + 'Tier 1 - Novel gene and phenotype', + 'Tier 1 - Novel gene for known phenotype', + 'Tier 1 - Phenotype expansion', + 'Tier 1 - Phenotype not delineated', + 'Tier 1 - Novel mode of inheritance', + 'Tier 1 - Known gene, new phenotype', + 'Tier 2 - Novel gene and phenotype', + 'Tier 2 - Novel gene for known phenotype', + 'Tier 2 - Phenotype expansion', + 'Tier 2 - Phenotype not delineated', + 'Tier 2 - Known gene, new phenotype', + KNOWN_GENE_FOR_PHENOTYPE_TAG_NAME, + REVIEW_TAG_NAME, + 'Send for Sanger validation', + 'Sanger validated', + 'Sanger did not confirm', + 'Confident AR one hit', + 'Analyst high priority', + 'seqr MME (old)', + 'Submit to Clinvar', + 'Share with KOMP', +].map(name => ({ value: name, text: name, key: name, diff --git a/ui/pages/SummaryData/constants.js b/ui/pages/SummaryData/constants.js index 7abc5a3f76..4fec4609a6 100644 --- a/ui/pages/SummaryData/constants.js +++ b/ui/pages/SummaryData/constants.js @@ -1,7 +1,9 @@ +/* eslint-disable import/prefer-default-export */ + import React from 'react' import { Link } from 'react-router-dom' -import { successStoryTypeDisplay, KNOWN_GENE_FOR_PHENOTYPE_TAG_NAME, REVIEW_TAG_NAME } from 'shared/utils/constants' +import { successStoryTypeDisplay } from 'shared/utils/constants' const formatIDLink = row => {row.family_id} @@ -18,27 +20,3 @@ export const SUCCESS_STORY_COLUMNS = [ { name: 'success_story', content: 'Success Story', style: { minWidth: '564px' } }, { name: 'discovery_tags', content: 'Discovery Tags', format: formatDiscoveryTags, noFormatExport: true, style: { minWidth: '400px' } }, ] - -export const SUMMARY_PAGE_SAVED_VARIANT_TAGS = [ - 'Tier 1 - Novel gene and phenotype', - 'Tier 1 - Novel gene for known phenotype', - 'Tier 1 - Phenotype expansion', - 'Tier 1 - Phenotype not delineated', - 'Tier 1 - Novel mode of inheritance', - 'Tier 1 - Known gene, new phenotype', - 'Tier 2 - Novel gene and phenotype', - 'Tier 2 - Novel gene for known phenotype', - 'Tier 2 - Phenotype expansion', - 'Tier 2 - Phenotype not delineated', - 'Tier 2 - Known gene, new phenotype', - KNOWN_GENE_FOR_PHENOTYPE_TAG_NAME, - REVIEW_TAG_NAME, - 'Send for Sanger validation', - 'Sanger validated', - 'Sanger did not confirm', - 'Confident AR one hit', - 'Analyst high priority', - 'seqr MME (old)', - 'Submit to Clinvar', - 'Share with KOMP', -] diff --git a/ui/pages/SummaryData/reducers.js b/ui/pages/SummaryData/reducers.js index 28aa7fd8ce..acd895e5d1 100644 --- a/ui/pages/SummaryData/reducers.js +++ b/ui/pages/SummaryData/reducers.js @@ -2,7 +2,7 @@ import { combineReducers } from 'redux' import { loadingReducer, createSingleValueReducer, createSingleObjectReducer } from 'redux/utils/reducerFactories' import { RECEIVE_DATA, REQUEST_SAVED_VARIANTS } from 'redux/utils/reducerUtils' -import { SHOW_ALL, SORT_BY_XPOS, TAG_URL_DELIMITER } from 'shared/utils/constants' +import { SHOW_ALL, SORT_BY_XPOS } from 'shared/utils/constants' import { HttpRequestHelper } from 'shared/utils/httpRequestHelper' // action creators and reducers in one file as suggested by https://github.com/erikras/ducks-modular-redux @@ -43,9 +43,8 @@ export const loadSuccessStory = successStoryTypes => (dispatch) => { export const loadSavedVariants = ({ tag, gene = '' }) => (dispatch, getState) => { // Do not load if already loaded - const stateKey = tag && `${tag.split(TAG_URL_DELIMITER).sort().join(TAG_URL_DELIMITER)}${gene}` if (tag) { - if (getState().savedVariantTags[stateKey]) { + if (getState().savedVariantTags[tag]) { return } } else if (!gene) { @@ -58,7 +57,7 @@ export const loadSavedVariants = ({ tag, gene = '' }) => (dispatch, getState) => if (tag && !gene) { dispatch({ type: RECEIVE_SAVED_VARIANT_TAGS, - updates: { [stateKey]: true }, + updates: { [tag]: true }, }) } dispatch({ type: RECEIVE_DATA, updatesById: responseJson }) From b45b0503346f62c2ca9479687dc8261959139d6a Mon Sep 17 00:00:00 2001 From: Shifa Zhang Date: Mon, 7 Aug 2023 12:13:05 -0400 Subject: [PATCH 12/25] Remove project saved-variant multi-tag filtering. --- ui/pages/Project/components/SavedVariants.jsx | 10 +++------- .../SummaryData/components/SavedVariants.jsx | 1 + .../panel/variants/SavedVariants.jsx | 13 ++++++++----- .../components/panel/variants/selectors.js | 18 ++++++++++++------ 4 files changed, 24 insertions(+), 18 deletions(-) diff --git a/ui/pages/Project/components/SavedVariants.jsx b/ui/pages/Project/components/SavedVariants.jsx index e38a555849..98996d7ff6 100644 --- a/ui/pages/Project/components/SavedVariants.jsx +++ b/ui/pages/Project/components/SavedVariants.jsx @@ -15,7 +15,6 @@ import { VARIANT_PER_PAGE_FIELD, EXCLUDED_TAG_NAME, REVIEW_TAG_NAME, - TAG_URL_DELIMITER, } from 'shared/utils/constants' import UpdateButton from 'shared/components/buttons/UpdateButton' import { LargeMultiselect, Dropdown } from 'shared/components/form/Inputs' @@ -143,15 +142,12 @@ class BaseProjectSavedVariants extends React.PureComponent { project.variantTagTypes.map(type => type.category).filter(category => category), )] - const lastNewTag = newTag.length > 0 ? newTag[newTag.length - 1] : null - const isCategory = categoryOptions.includes(lastNewTag) - const [firstTag, ...otherTag] = newTag - const updatedTag = firstTag === ALL_FILTER || categoryOptions.includes(firstTag) ? otherTag : newTag - updateTableField('categoryFilter')(isCategory ? lastNewTag : null) + const isCategory = categoryOptions.includes(newTag) + updateTableField('categoryFilter')(isCategory ? newTag : null) return getSavedVariantsLinkPath({ projectGuid: project.projectGuid, analysisGroupGuid: match.params.analysisGroupGuid, - tag: !isCategory && lastNewTag !== ALL_FILTER && (updatedTag || []).join(TAG_URL_DELIMITER), + tag: !isCategory && newTag !== ALL_FILTER && newTag, familyGuid: match.params.familyGuid, }) } diff --git a/ui/pages/SummaryData/components/SavedVariants.jsx b/ui/pages/SummaryData/components/SavedVariants.jsx index 853986c62a..9aa26af082 100644 --- a/ui/pages/SummaryData/components/SavedVariants.jsx +++ b/ui/pages/SummaryData/components/SavedVariants.jsx @@ -87,6 +87,7 @@ const BaseSavedVariants = React.memo(({ loadVariants, geneDetail, ...props }) => filters={FILTER_FIELDS} getUpdateTagUrl={getUpdateTagUrl} loadVariants={loadVariants} + multiple additionalFilter={ { const { history, getUpdateTagUrl, match } = this.props - history.push(getUpdateTagUrl(data.value.length > 0 ? data.value : [ALL_FILTER], match)) + history.push(getUpdateTagUrl(data.value, match)) } showAllFilters = () => { @@ -77,15 +78,17 @@ class SavedVariants extends React.PureComponent { const { match, tableState, filters, discoveryFilters, totalPages, variantsToDisplay, totalVariantsCount, firstRecordIndex, tableSummaryComponent, loading, filteredVariantsCount, tagOptions, additionalFilter, updateTableField, - variantExportConfig, loadVariants, error, + variantExportConfig, loadVariants, error, multiple, } = this.props const { showAllFilters } = this.state const { familyGuid, variantGuid, tag } = match.params const tags = tag ? tag.split(TAG_URL_DELIMITER) : tag - const appliedTagCategoryFilter = tags || (variantGuid ? [] : [(tableState.categoryFilter || ALL_FILTER)]) + const tagCategoryFilters = tags || (variantGuid ? [] : [(tableState.categoryFilter || ALL_FILTER)]) + const firstAppliedTagCategoryFilter = tagCategoryFilters.length > 0 ? tagCategoryFilters[0] : null + const appliedTagCategoryFilter = multiple ? tagCategoryFilters : firstAppliedTagCategoryFilter - let shownFilters = (discoveryFilters && appliedTagCategoryFilter === [DISCOVERY_CATEGORY_NAME]) ? + let shownFilters = (discoveryFilters && firstAppliedTagCategoryFilter === DISCOVERY_CATEGORY_NAME) ? discoveryFilters : filters const hasHiddenFilters = !showAllFilters && shownFilters.length > MAX_FILTERS if (hasHiddenFilters) { @@ -113,7 +116,7 @@ class SavedVariants extends React.PureComponent { {`Showing ${shownSummary} ${filteredVariantsCount} `} mmeSubmissions.length) } else if (tag && tag !== SHOW_ALL) { const tags = tag.split(TAG_URL_DELIMITER) - pairedVariants = matchingVariants( - pairedVariants, ({ tagGuids }) => { - const tagNames = tagGuids.map(tagGuid => tagsByGuid[tagGuid].name) - return tags.every(tagName => tagNames.includes(tagName)) - }, - ) + if (tags.length === 1) { + pairedVariants = matchingVariants( + pairedVariants, ({ tagGuids }) => tagGuids.some(tagGuid => tagsByGuid[tagGuid].name === tag), + ) + } else { + pairedVariants = matchingVariants( + pairedVariants, ({ tagGuids }) => { + const tagNames = tagGuids.map(tagGuid => tagsByGuid[tagGuid].name) + return tags.every(tagName => tagNames.includes(tagName)) + }, + ) + } } else if (!(familyGuid || analysisGroupGuid)) { pairedVariants = matchingVariants(pairedVariants, ({ tagGuids }) => tagGuids.length) } From 6b7a039380dbae91ff728d70774952188dad62d8 Mon Sep 17 00:00:00 2001 From: Shifa Zhang Date: Wed, 9 Aug 2023 13:43:41 -0400 Subject: [PATCH 13/25] Update the option value and filters props. --- seqr/views/apis/summary_data_api.py | 4 +-- seqr/views/apis/summary_data_api_tests.py | 13 +++++----- ui/pages/Project/components/SavedVariants.jsx | 10 ++++--- .../SummaryData/components/SavedVariants.jsx | 5 +++- .../panel/variants/SavedVariants.jsx | 25 ++++++------------ .../components/panel/variants/selectors.js | 26 +++++++++---------- 6 files changed, 39 insertions(+), 44 deletions(-) diff --git a/seqr/views/apis/summary_data_api.py b/seqr/views/apis/summary_data_api.py index 8dc771a75c..e5537bbb24 100644 --- a/seqr/views/apis/summary_data_api.py +++ b/seqr/views/apis/summary_data_api.py @@ -88,9 +88,9 @@ def saved_variants_page(request, tag): saved_variant_models = SavedVariant.objects.exclude(varianttag=None) else: tags = tag.split(';') - tag_type = VariantTagType.objects.filter(name__in=tags, project__isnull=True) + tag_types = VariantTagType.objects.filter(name__in=tags, project__isnull=True) saved_variant_models = SavedVariant.objects.all() - for tt in tag_type: + for tt in tag_types: saved_variant_models = saved_variant_models.filter(varianttag__variant_tag_type=tt).distinct() saved_variant_models = saved_variant_models.filter(family__project__guid__in=get_project_guids_user_can_view(request.user)) diff --git a/seqr/views/apis/summary_data_api_tests.py b/seqr/views/apis/summary_data_api_tests.py index 343945d661..871a6c198c 100644 --- a/seqr/views/apis/summary_data_api_tests.py +++ b/seqr/views/apis/summary_data_api_tests.py @@ -236,18 +236,14 @@ class LocalSummaryDataAPITest(AuthenticationTestCase, SummaryDataAPITest): MANAGER_VARIANT_GUID = 'SV0000006_1248367227_r0004_non' -def assert_has_expected_calls(self, users, skip_group_call_idxs=None, has_ws_access_level_call=False): +def assert_has_expected_calls(self, users, skip_group_call_idxs=None): calls = [mock.call(user) for user in users] self.mock_list_workspaces.assert_has_calls(calls) group_calls = [call for i, call in enumerate(calls) if i in skip_group_call_idxs] if skip_group_call_idxs else calls self.mock_get_groups.assert_has_calls(group_calls) self.mock_get_ws_acl.assert_not_called() self.mock_get_group_members.assert_not_called() - if has_ws_access_level_call: - self.mock_get_ws_access_level.assert_called_with( - self.analyst_user, 'my-seqr-billing', 'anvil-1kg project nåme with uniçøde') - else: - self.mock_get_ws_access_level.assert_not_called() + # Test for permissions from AnVIL only class AnvilSummaryDataAPITest(AnvilAuthenticationTestCase, SummaryDataAPITest): @@ -258,9 +254,12 @@ class AnvilSummaryDataAPITest(AnvilAuthenticationTestCase, SummaryDataAPITest): def test_mme_details(self, *args): super(AnvilSummaryDataAPITest, self).test_mme_details(*args) assert_has_expected_calls(self, [self.no_access_user, self.manager_user, self.analyst_user]) + self.mock_get_ws_access_level.assert_not_called() def test_saved_variants_page(self): super(AnvilSummaryDataAPITest, self).test_saved_variants_page() assert_has_expected_calls(self, [ self.no_access_user, self.manager_user, self.manager_user, self.analyst_user, self.analyst_user - ], skip_group_call_idxs=[2], has_ws_access_level_call=True) + ], skip_group_call_idxs=[2]) + self.mock_get_ws_access_level.assert_called_with( + self.analyst_user, 'my-seqr-billing', 'anvil-1kg project nåme with uniçøde') diff --git a/ui/pages/Project/components/SavedVariants.jsx b/ui/pages/Project/components/SavedVariants.jsx index 98996d7ff6..8aef1da539 100644 --- a/ui/pages/Project/components/SavedVariants.jsx +++ b/ui/pages/Project/components/SavedVariants.jsx @@ -15,6 +15,7 @@ import { VARIANT_PER_PAGE_FIELD, EXCLUDED_TAG_NAME, REVIEW_TAG_NAME, + DISCOVERY_CATEGORY_NAME, } from 'shared/utils/constants' import UpdateButton from 'shared/components/buttons/UpdateButton' import { LargeMultiselect, Dropdown } from 'shared/components/form/Inputs' @@ -233,15 +234,18 @@ class BaseProjectSavedVariants extends React.PureComponent { ) } + getSelectedTag = tag => defaultTag => (tag || defaultTag) + render() { const { project, analysisGroup, loadProjectSavedVariants, ...props } = this.props - const { familyGuid } = props.match.params + const { familyGuid, tag } = props.match.params + const filters = (tag === DISCOVERY_CATEGORY_NAME) ? FILTER_FIELDS : NON_DISCOVERY_FILTER_FIELDS return ( : null } diff --git a/ui/pages/SummaryData/components/SavedVariants.jsx b/ui/pages/SummaryData/components/SavedVariants.jsx index 9aa26af082..99456167f8 100644 --- a/ui/pages/SummaryData/components/SavedVariants.jsx +++ b/ui/pages/SummaryData/components/SavedVariants.jsx @@ -72,11 +72,13 @@ const getUpdateTagUrl = (selectedTag, match) => { const lastTag = selectedTag.length > 0 ? selectedTag[selectedTag.length - 1] : null const [firstTag, ...otherTag] = selectedTag const updatedTag = firstTag === SHOW_ALL ? otherTag : lastTag !== SHOW_ALL && selectedTag - return `${PAGE_URL}/${(updatedTag || [SHOW_ALL]).join(TAG_URL_DELIMITER)}${match.params.gene ? `/${match.params.gene}` : ''}` + return `${PAGE_URL}/${updatedTag.join(TAG_URL_DELIMITER) || SHOW_ALL}${match.params.gene ? `/${match.params.gene}` : ''}` } const getGeneHref = tag => selectedGene => `${PAGE_URL}/${tag || SHOW_ALL}/${selectedGene.key}` +const getSelectedTag = tag => defaultTag => (tag ? tag.split(TAG_URL_DELIMITER) : [defaultTag]) + const BaseSavedVariants = React.memo(({ loadVariants, geneDetail, ...props }) => { const { params } = props.match const { tag, gene } = params @@ -88,6 +90,7 @@ const BaseSavedVariants = React.memo(({ loadVariants, geneDetail, ...props }) => getUpdateTagUrl={getUpdateTagUrl} loadVariants={loadVariants} multiple + getSelectedTag={getSelectedTag(tag)} additionalFilter={ 0 ? tagCategoryFilters[0] : null - const appliedTagCategoryFilter = multiple ? tagCategoryFilters : firstAppliedTagCategoryFilter + const { familyGuid, variantGuid } = match.params + const optionValue = getSelectedTag(variantGuid ? null : (tableState.categoryFilter || ALL_FILTER)) - let shownFilters = (discoveryFilters && firstAppliedTagCategoryFilter === DISCOVERY_CATEGORY_NAME) ? - discoveryFilters : filters + let shownFilters = filters const hasHiddenFilters = !showAllFilters && shownFilters.length > MAX_FILTERS if (hasHiddenFilters) { shownFilters = shownFilters.slice(0, MAX_FILTERS) @@ -118,7 +109,7 @@ class SavedVariants extends React.PureComponent { inline multiple={multiple} options={tagOptions} - value={appliedTagCategoryFilter} + value={optionValue} onChange={this.navigateToTag} /> {` variants ${allShown ? '' : `(${totalVariantsCount} total)`}`} diff --git a/ui/shared/components/panel/variants/selectors.js b/ui/shared/components/panel/variants/selectors.js index a96e587e89..fda529a8ff 100644 --- a/ui/shared/components/panel/variants/selectors.js +++ b/ui/shared/components/panel/variants/selectors.js @@ -131,24 +131,22 @@ export const getPairedSelectedSavedVariants = createSelector( return acc }, []) - if (tag === NOTE_TAG_NAME) { + const tags = (tag || '').split(TAG_URL_DELIMITER) + if (tags.length > 1) { + pairedVariants = matchingVariants( + pairedVariants, ({ tagGuids }) => { + const tagNames = tagGuids.map(tagGuid => tagsByGuid[tagGuid].name) + return tags.every(tagName => tagNames.includes(tagName)) + }, + ) + } else if (tag === NOTE_TAG_NAME) { pairedVariants = matchingVariants(pairedVariants, ({ noteGuids }) => noteGuids.length) } else if (tag === MME_TAG_NAME) { pairedVariants = matchingVariants(pairedVariants, ({ mmeSubmissions = [] }) => mmeSubmissions.length) } else if (tag && tag !== SHOW_ALL) { - const tags = tag.split(TAG_URL_DELIMITER) - if (tags.length === 1) { - pairedVariants = matchingVariants( - pairedVariants, ({ tagGuids }) => tagGuids.some(tagGuid => tagsByGuid[tagGuid].name === tag), - ) - } else { - pairedVariants = matchingVariants( - pairedVariants, ({ tagGuids }) => { - const tagNames = tagGuids.map(tagGuid => tagsByGuid[tagGuid].name) - return tags.every(tagName => tagNames.includes(tagName)) - }, - ) - } + pairedVariants = matchingVariants( + pairedVariants, ({ tagGuids }) => tagGuids.some(tagGuid => tagsByGuid[tagGuid].name === tag), + ) } else if (!(familyGuid || analysisGroupGuid)) { pairedVariants = matchingVariants(pairedVariants, ({ tagGuids }) => tagGuids.length) } From 7ea4f23d66c52d76a7d7bcb823c2d4c1133a1141 Mon Sep 17 00:00:00 2001 From: Shifa Zhang Date: Thu, 10 Aug 2023 17:18:56 -0400 Subject: [PATCH 14/25] Save the variants to the state per tag. --- ui/pages/SummaryData/reducers.js | 11 +++-------- .../components/panel/variants/selectors.js | 18 +++++++----------- 2 files changed, 10 insertions(+), 19 deletions(-) diff --git a/ui/pages/SummaryData/reducers.js b/ui/pages/SummaryData/reducers.js index acd895e5d1..dd0204ebba 100644 --- a/ui/pages/SummaryData/reducers.js +++ b/ui/pages/SummaryData/reducers.js @@ -44,7 +44,7 @@ export const loadSuccessStory = successStoryTypes => (dispatch) => { export const loadSavedVariants = ({ tag, gene = '' }) => (dispatch, getState) => { // Do not load if already loaded if (tag) { - if (getState().savedVariantTags[tag]) { + if (getState().savedVariantsByTag[tag]) { return } } else if (!gene) { @@ -54,13 +54,8 @@ export const loadSavedVariants = ({ tag, gene = '' }) => (dispatch, getState) => dispatch({ type: REQUEST_SAVED_VARIANTS }) new HttpRequestHelper(`/api/summary_data/saved_variants/${tag}`, (responseJson) => { - if (tag && !gene) { - dispatch({ - type: RECEIVE_SAVED_VARIANT_TAGS, - updates: { [tag]: true }, - }) - } dispatch({ type: RECEIVE_DATA, updatesById: responseJson }) + dispatch({ type: RECEIVE_SAVED_VARIANT_TAGS, updates: { [tag]: responseJson.savedVariantsByGuid } }) }, (e) => { dispatch({ type: RECEIVE_DATA, error: e.message, updatesById: {} }) @@ -83,7 +78,7 @@ export const reducers = { mmeLoading: loadingReducer(REQUEST_MME, RECEIVE_MME), mmeMetrics: createSingleValueReducer(RECEIVE_MME, {}, 'metrics'), mmeSubmissions: createSingleValueReducer(RECEIVE_MME, [], 'submissions'), - savedVariantTags: createSingleObjectReducer(RECEIVE_SAVED_VARIANT_TAGS), + savedVariantsByTag: createSingleObjectReducer(RECEIVE_SAVED_VARIANT_TAGS), externalAnalysisUploadStats: createSingleValueReducer(RECEIVE_EXTERNAL_ANALYSIS_UPLOAD_STATS, {}), allProjectSavedVariantTableState: createSingleObjectReducer(UPDATE_ALL_PROJECT_SAVED_VARIANT_TABLE_STATE, { categoryFilter: SHOW_ALL, diff --git a/ui/shared/components/panel/variants/selectors.js b/ui/shared/components/panel/variants/selectors.js index fda529a8ff..6a3020d293 100644 --- a/ui/shared/components/panel/variants/selectors.js +++ b/ui/shared/components/panel/variants/selectors.js @@ -12,7 +12,6 @@ import { VARIANT_SORT_LOOKUP, SHOW_ALL, VARIANT_EXPORT_DATA, - TAG_URL_DELIMITER, } from 'shared/utils/constants' import { getVariantTagsByGuid, getVariantNotesByGuid, getSavedVariantsByGuid, getAnalysisGroupsByGuid, getGenesById, getUser, @@ -74,8 +73,13 @@ export const getPairedSelectedSavedVariants = createSelector( (state, props) => (props.project || {}).projectGuid, getVariantTagsByGuid, getVariantNotesByGuid, + state => state.savedVariantsByTag, (savedVariants, { tag, gene, familyGuid, analysisGroupGuid, variantGuid }, familiesByGuid, analysisGroupsByGuid, - projectGuid, tagsByGuid, notesByGuid) => { + projectGuid, tagsByGuid, notesByGuid, savedVariantsByTags) => { + if (!projectGuid) { + return Object.values(savedVariantsByTags[tag] || {}) + } + let variants = Object.values(savedVariants) if (variantGuid) { variants = variants.filter(o => variantGuid.split(',').includes(o.variantGuid)) @@ -131,15 +135,7 @@ export const getPairedSelectedSavedVariants = createSelector( return acc }, []) - const tags = (tag || '').split(TAG_URL_DELIMITER) - if (tags.length > 1) { - pairedVariants = matchingVariants( - pairedVariants, ({ tagGuids }) => { - const tagNames = tagGuids.map(tagGuid => tagsByGuid[tagGuid].name) - return tags.every(tagName => tagNames.includes(tagName)) - }, - ) - } else if (tag === NOTE_TAG_NAME) { + if (tag === NOTE_TAG_NAME) { pairedVariants = matchingVariants(pairedVariants, ({ noteGuids }) => noteGuids.length) } else if (tag === MME_TAG_NAME) { pairedVariants = matchingVariants(pairedVariants, ({ mmeSubmissions = [] }) => mmeSubmissions.length) From e46151cdd7d52b75905f30bfa408f9703f8e270f Mon Sep 17 00:00:00 2001 From: Shifa Zhang Date: Thu, 10 Aug 2023 17:32:17 -0400 Subject: [PATCH 15/25] Update the getSelectedTag function. --- ui/pages/Project/components/SavedVariants.jsx | 15 ++++++++------- ui/pages/SummaryData/components/SavedVariants.jsx | 5 ++--- .../components/panel/variants/SavedVariants.jsx | 3 +-- ui/shared/utils/constants.js | 2 -- 4 files changed, 11 insertions(+), 14 deletions(-) diff --git a/ui/pages/Project/components/SavedVariants.jsx b/ui/pages/Project/components/SavedVariants.jsx index 8aef1da539..1515fa4b9d 100644 --- a/ui/pages/Project/components/SavedVariants.jsx +++ b/ui/pages/Project/components/SavedVariants.jsx @@ -16,6 +16,7 @@ import { EXCLUDED_TAG_NAME, REVIEW_TAG_NAME, DISCOVERY_CATEGORY_NAME, + SHOW_ALL, } from 'shared/utils/constants' import UpdateButton from 'shared/components/buttons/UpdateButton' import { LargeMultiselect, Dropdown } from 'shared/components/form/Inputs' @@ -38,8 +39,6 @@ const LabelLink = styled(Link)` } ` -const ALL_FILTER = 'ALL' - const mapSavedByInputStateToProps = state => ({ options: getProjectVariantSavedByOptions(state), }) @@ -148,7 +147,7 @@ class BaseProjectSavedVariants extends React.PureComponent { return getSavedVariantsLinkPath({ projectGuid: project.projectGuid, analysisGroupGuid: match.params.analysisGroupGuid, - tag: !isCategory && newTag !== ALL_FILTER && newTag, + tag: !isCategory && newTag !== SHOW_ALL && newTag, familyGuid: match.params.familyGuid, }) } @@ -192,7 +191,7 @@ class BaseProjectSavedVariants extends React.PureComponent { }) return acc }, [{ - value: ALL_FILTER, + value: SHOW_ALL, text: 'All Saved', content: ( defaultTag => (tag || defaultTag) + getSelectedTag = (tag, variantGuid) => categoryFilter => ( + tag || (variantGuid ? null : (categoryFilter || SHOW_ALL)) + ) render() { const { project, analysisGroup, loadProjectSavedVariants, ...props } = this.props - const { familyGuid, tag } = props.match.params + const { familyGuid, tag, variantGuid } = props.match.params const filters = (tag === DISCOVERY_CATEGORY_NAME) ? FILTER_FIELDS : NON_DISCOVERY_FILTER_FIELDS return ( : null } diff --git a/ui/pages/SummaryData/components/SavedVariants.jsx b/ui/pages/SummaryData/components/SavedVariants.jsx index 99456167f8..09ed675031 100644 --- a/ui/pages/SummaryData/components/SavedVariants.jsx +++ b/ui/pages/SummaryData/components/SavedVariants.jsx @@ -13,7 +13,6 @@ import { VARIANT_PER_PAGE_FIELD, VARIANT_TAGGED_DATE_FIELD, SHOW_ALL, - TAG_URL_DELIMITER, } from 'shared/utils/constants' import { StyledForm } from 'shared/components/form/FormHelpers' import AwesomeBar from 'shared/components/page/AwesomeBar' @@ -72,12 +71,12 @@ const getUpdateTagUrl = (selectedTag, match) => { const lastTag = selectedTag.length > 0 ? selectedTag[selectedTag.length - 1] : null const [firstTag, ...otherTag] = selectedTag const updatedTag = firstTag === SHOW_ALL ? otherTag : lastTag !== SHOW_ALL && selectedTag - return `${PAGE_URL}/${updatedTag.join(TAG_URL_DELIMITER) || SHOW_ALL}${match.params.gene ? `/${match.params.gene}` : ''}` + return `${PAGE_URL}/${updatedTag.join(';') || SHOW_ALL}${match.params.gene ? `/${match.params.gene}` : ''}` } const getGeneHref = tag => selectedGene => `${PAGE_URL}/${tag || SHOW_ALL}/${selectedGene.key}` -const getSelectedTag = tag => defaultTag => (tag ? tag.split(TAG_URL_DELIMITER) : [defaultTag]) +const getSelectedTag = tag => categoryFilter => (tag ? tag.split(';') : [categoryFilter || SHOW_ALL]) const BaseSavedVariants = React.memo(({ loadVariants, geneDetail, ...props }) => { const { params } = props.match diff --git a/ui/shared/components/panel/variants/SavedVariants.jsx b/ui/shared/components/panel/variants/SavedVariants.jsx index 9a04df2fb5..673d1cf2a4 100644 --- a/ui/shared/components/panel/variants/SavedVariants.jsx +++ b/ui/shared/components/panel/variants/SavedVariants.jsx @@ -19,7 +19,6 @@ import { getVisibleSortedSavedVariants, } from './selectors' -const ALL_FILTER = 'ALL' const MAX_FILTERS = 4 const ControlsRow = styled(Grid.Row)` @@ -78,7 +77,7 @@ class SavedVariants extends React.PureComponent { } = this.props const { showAllFilters } = this.state const { familyGuid, variantGuid } = match.params - const optionValue = getSelectedTag(variantGuid ? null : (tableState.categoryFilter || ALL_FILTER)) + const optionValue = getSelectedTag(tableState.categoryFilter) let shownFilters = filters const hasHiddenFilters = !showAllFilters && shownFilters.length > MAX_FILTERS diff --git a/ui/shared/utils/constants.js b/ui/shared/utils/constants.js index 6d67fe63ec..a36faa1b3a 100644 --- a/ui/shared/utils/constants.js +++ b/ui/shared/utils/constants.js @@ -1042,8 +1042,6 @@ export const KNOWN_GENE_FOR_PHENOTYPE_TAG_NAME = 'Known gene for phenotype' export const DISCOVERY_CATEGORY_NAME = 'CMG Discovery Tags' export const MME_TAG_NAME = 'MME Submission' -export const TAG_URL_DELIMITER = ';' - export const SORT_BY_FAMILY_GUID = 'FAMILY_GUID' export const SORT_BY_XPOS = 'XPOS' const SORT_BY_PATHOGENICITY = 'PATHOGENICITY' From eacc42c7ab426aaff24a2d8c43255017e1346d33 Mon Sep 17 00:00:00 2001 From: Shifa Zhang Date: Fri, 11 Aug 2023 09:43:19 -0400 Subject: [PATCH 16/25] fix tests. --- .../components/panel/variants/selectors.js | 2 +- .../panel/variants/selectors.test.js | 28 +++++++++++++------ 2 files changed, 21 insertions(+), 9 deletions(-) diff --git a/ui/shared/components/panel/variants/selectors.js b/ui/shared/components/panel/variants/selectors.js index 6a3020d293..65feb27361 100644 --- a/ui/shared/components/panel/variants/selectors.js +++ b/ui/shared/components/panel/variants/selectors.js @@ -77,7 +77,7 @@ export const getPairedSelectedSavedVariants = createSelector( (savedVariants, { tag, gene, familyGuid, analysisGroupGuid, variantGuid }, familiesByGuid, analysisGroupsByGuid, projectGuid, tagsByGuid, notesByGuid, savedVariantsByTags) => { if (!projectGuid) { - return Object.values(savedVariantsByTags[tag] || {}) + return Object.values((savedVariantsByTags || {})[tag] || {}) } let variants = Object.values(savedVariants) diff --git a/ui/shared/components/panel/variants/selectors.test.js b/ui/shared/components/panel/variants/selectors.test.js index f3b4628cb9..5bd0b6a032 100644 --- a/ui/shared/components/panel/variants/selectors.test.js +++ b/ui/shared/components/panel/variants/selectors.test.js @@ -10,7 +10,8 @@ import { test('getPairedSelectedSavedVariants', () => { - const savedAllVariants = getPairedSelectedSavedVariants(STATE_WITH_2_FAMILIES, { match: { params: {} } }) + const savedAllVariants = getPairedSelectedSavedVariants(STATE_WITH_2_FAMILIES, + { project: { projectGuid: 'R0237_1000_genomes_demo' }, match: { params: {} } }) expect(savedAllVariants.length).toEqual(3) expect(savedAllVariants[0].variantGuid).toEqual('SV0000004_116042722_r0390_1000') expect(savedAllVariants[1].variantGuid).toEqual('SV0000002_1248367227_r0390_100') @@ -19,21 +20,24 @@ test('getPairedSelectedSavedVariants', () => { expect(savedAllVariants[2][1].variantGuid).toEqual('SV0000005_2246859833_r0390_100') const savedReviewVariants = getPairedSelectedSavedVariants( - STATE_WITH_2_FAMILIES, { match: { params: { tag: 'Review' } } } + STATE_WITH_2_FAMILIES, + { project: { projectGuid: 'R0237_1000_genomes_demo' }, match: { params: { tag: 'Review' } } }, ) expect(savedReviewVariants.length).toEqual(2) expect(savedReviewVariants[0].variantGuid).toEqual('SV0000004_116042722_r0390_1000') expect(savedReviewVariants[1].variantGuid).toEqual('SV0000002_1248367227_r0390_100') const savedNotesVariants = getPairedSelectedSavedVariants( - STATE_WITH_2_FAMILIES, { match: { params: { tag: 'Has Notes' } } } + STATE_WITH_2_FAMILIES, + { project: { projectGuid: 'R0237_1000_genomes_demo' }, match: { params: { tag: 'Has Notes' } } }, ) expect(savedNotesVariants.length).toEqual(1) expect(savedNotesVariants[0].variantGuid).toEqual('SV0000004_116042722_r0390_1000') const savedFamilyVariants = getPairedSelectedSavedVariants( - STATE_WITH_2_FAMILIES, { match: { params: { familyGuid: 'F011652_1' } } } + STATE_WITH_2_FAMILIES, + { project: { projectGuid: 'R0237_1000_genomes_demo' }, match: { params: { familyGuid: 'F011652_1' } } }, ) expect(savedFamilyVariants.length).toEqual(3) expect(savedFamilyVariants[0].variantGuid).toEqual('SV0000004_116042722_r0390_1000') @@ -41,7 +45,11 @@ test('getPairedSelectedSavedVariants', () => { expect(savedFamilyVariants[2].variantGuid).toEqual('SV0000002_SV48367227_r0390_100') const savedAnalysisGroupVariants = getPairedSelectedSavedVariants( - STATE_WITH_2_FAMILIES, { match: { params: { analysisGroupGuid: 'AG0000183_test_group' } } } + STATE_WITH_2_FAMILIES, + { + project: { projectGuid: 'R0237_1000_genomes_demo' }, + match: { params: { analysisGroupGuid: 'AG0000183_test_group' } }, + }, ) expect(savedAnalysisGroupVariants.length).toEqual(3) expect(savedAnalysisGroupVariants[0].variantGuid).toEqual('SV0000004_116042722_r0390_1000') @@ -50,7 +58,11 @@ test('getPairedSelectedSavedVariants', () => { const savedVariants = getPairedSelectedSavedVariants( - STATE_WITH_2_FAMILIES, { match: { params: { variantGuid: 'SV0000004_116042722_r0390_1000' } } } + STATE_WITH_2_FAMILIES, + { + project: { projectGuid: 'R0237_1000_genomes_demo' }, + match: { params: { variantGuid: 'SV0000004_116042722_r0390_1000' } }, + }, ) expect(savedVariants.length).toEqual(1) expect(savedFamilyVariants[0].variantGuid).toEqual('SV0000004_116042722_r0390_1000') @@ -58,7 +70,7 @@ test('getPairedSelectedSavedVariants', () => { test('getPairedFilteredSavedVariants', () => { const pairedSavedVariants = getPairedFilteredSavedVariants( - STATE_WITH_2_FAMILIES, { match: { params: {} } } + STATE_WITH_2_FAMILIES, { project: { projectGuid: 'R0237_1000_genomes_demo' }, match: { params: {} } }, ) expect(pairedSavedVariants.length).toEqual(2) expect(pairedSavedVariants[0].variantGuid).toEqual('SV0000004_116042722_r0390_1000') @@ -67,7 +79,7 @@ test('getPairedFilteredSavedVariants', () => { test('getVisibleSortedSavedVariants', () => { const savedVariants = getVisibleSortedSavedVariants( - STATE_WITH_2_FAMILIES, { match: { params: {} } } + STATE_WITH_2_FAMILIES, { project: { projectGuid: 'R0237_1000_genomes_demo' }, match: { params: {} } }, ) expect(savedVariants.length).toEqual(1) expect(savedVariants[0].variantGuid).toEqual('SV0000002_1248367227_r0390_100') From b2e440e37aa5e64089a36500da0a0e53926319d4 Mon Sep 17 00:00:00 2001 From: Shifa Zhang Date: Fri, 11 Aug 2023 14:45:12 -0400 Subject: [PATCH 17/25] Update the per-tag saved-variant data state. --- ui/pages/SummaryData/reducers.js | 11 ++++++----- ui/shared/components/panel/variants/selectors.js | 2 +- 2 files changed, 7 insertions(+), 6 deletions(-) diff --git a/ui/pages/SummaryData/reducers.js b/ui/pages/SummaryData/reducers.js index dd0204ebba..7ed34c19ba 100644 --- a/ui/pages/SummaryData/reducers.js +++ b/ui/pages/SummaryData/reducers.js @@ -1,6 +1,6 @@ import { combineReducers } from 'redux' -import { loadingReducer, createSingleValueReducer, createSingleObjectReducer } from 'redux/utils/reducerFactories' +import { loadingReducer, createSingleValueReducer, createSingleObjectReducer, createObjectsByIdReducer } from 'redux/utils/reducerFactories' import { RECEIVE_DATA, REQUEST_SAVED_VARIANTS } from 'redux/utils/reducerUtils' import { SHOW_ALL, SORT_BY_XPOS } from 'shared/utils/constants' import { HttpRequestHelper } from 'shared/utils/httpRequestHelper' @@ -10,7 +10,6 @@ const REQUEST_SUCCESS_STORY = 'REQUEST_SUCCESS_STORY' const RECEIVE_SUCCESS_STORY = 'RECEIVE_SUCCESS_STORY' const REQUEST_MME = 'REQUEST_MME' const RECEIVE_MME = 'RECEIVE_MME' -const RECEIVE_SAVED_VARIANT_TAGS = 'RECEIVE_SAVED_VARIANT_TAGS' const UPDATE_ALL_PROJECT_SAVED_VARIANT_TABLE_STATE = 'UPDATE_ALL_PROJECT_VARIANT_STATE' const RECEIVE_EXTERNAL_ANALYSIS_UPLOAD_STATS = 'RECEIVE_EXTERNAL_ANALYSIS_UPLOAD_STATS' @@ -54,8 +53,10 @@ export const loadSavedVariants = ({ tag, gene = '' }) => (dispatch, getState) => dispatch({ type: REQUEST_SAVED_VARIANTS }) new HttpRequestHelper(`/api/summary_data/saved_variants/${tag}`, (responseJson) => { - dispatch({ type: RECEIVE_DATA, updatesById: responseJson }) - dispatch({ type: RECEIVE_SAVED_VARIANT_TAGS, updates: { [tag]: responseJson.savedVariantsByGuid } }) + dispatch({ + type: RECEIVE_DATA, + updatesById: { ...responseJson, multiTagVariants: { [tag]: Object.keys(responseJson.savedVariantsByGuid) } }, + }) }, (e) => { dispatch({ type: RECEIVE_DATA, error: e.message, updatesById: {} }) @@ -78,7 +79,7 @@ export const reducers = { mmeLoading: loadingReducer(REQUEST_MME, RECEIVE_MME), mmeMetrics: createSingleValueReducer(RECEIVE_MME, {}, 'metrics'), mmeSubmissions: createSingleValueReducer(RECEIVE_MME, [], 'submissions'), - savedVariantsByTag: createSingleObjectReducer(RECEIVE_SAVED_VARIANT_TAGS), + savedVariantsByTag: createObjectsByIdReducer(RECEIVE_DATA, 'multiTagVariants'), externalAnalysisUploadStats: createSingleValueReducer(RECEIVE_EXTERNAL_ANALYSIS_UPLOAD_STATS, {}), allProjectSavedVariantTableState: createSingleObjectReducer(UPDATE_ALL_PROJECT_SAVED_VARIANT_TABLE_STATE, { categoryFilter: SHOW_ALL, diff --git a/ui/shared/components/panel/variants/selectors.js b/ui/shared/components/panel/variants/selectors.js index 65feb27361..4cea3524f3 100644 --- a/ui/shared/components/panel/variants/selectors.js +++ b/ui/shared/components/panel/variants/selectors.js @@ -77,7 +77,7 @@ export const getPairedSelectedSavedVariants = createSelector( (savedVariants, { tag, gene, familyGuid, analysisGroupGuid, variantGuid }, familiesByGuid, analysisGroupsByGuid, projectGuid, tagsByGuid, notesByGuid, savedVariantsByTags) => { if (!projectGuid) { - return Object.values((savedVariantsByTags || {})[tag] || {}) + return Object.values((savedVariantsByTags || {})[tag] || {}).map(guid => savedVariants[guid]) } let variants = Object.values(savedVariants) From 72711c2285ffd5de49ca9142b9bdec023df57cd7 Mon Sep 17 00:00:00 2001 From: Shifa Zhang Date: Fri, 11 Aug 2023 15:11:33 -0400 Subject: [PATCH 18/25] Add tests for the selector for the summary data. --- .../panel/variants/selectors.test.js | 22 +++++++++++++++++++ 1 file changed, 22 insertions(+) diff --git a/ui/shared/components/panel/variants/selectors.test.js b/ui/shared/components/panel/variants/selectors.test.js index 5bd0b6a032..c4c244a2bf 100644 --- a/ui/shared/components/panel/variants/selectors.test.js +++ b/ui/shared/components/panel/variants/selectors.test.js @@ -8,6 +8,14 @@ import { getIndividualGeneDataByFamilyGene, } from './selectors' +const STATE_WITH_2_FAMILIES_AND_VARIANT_TAGS = { + ...STATE_WITH_2_FAMILIES, + savedVariantsByTag: { + Review: ['SV0000004_116042722_r0390_1000', 'SV0000002_1248367227_r0390_100'], + 'Review;Tier 1 - Phenotype not delineated': ['SV0000002_1248367227_r0390_100'], + }, +} + test('getPairedSelectedSavedVariants', () => { const savedAllVariants = getPairedSelectedSavedVariants(STATE_WITH_2_FAMILIES, @@ -66,6 +74,20 @@ test('getPairedSelectedSavedVariants', () => { ) expect(savedVariants.length).toEqual(1) expect(savedFamilyVariants[0].variantGuid).toEqual('SV0000004_116042722_r0390_1000') + + const tagSavedVariants = getPairedSelectedSavedVariants( + STATE_WITH_2_FAMILIES_AND_VARIANT_TAGS, + { match: { params: { tag: 'Review' } } }, + ) + expect(tagSavedVariants.length).toEqual(2) + expect(tagSavedVariants[0].variantGuid).toEqual('SV0000004_116042722_r0390_1000') + + const multiTagSavedVariants = getPairedSelectedSavedVariants( + STATE_WITH_2_FAMILIES_AND_VARIANT_TAGS, + { match: { params: { tag: 'Review;Tier 1 - Phenotype not delineated' } } }, + ) + expect(multiTagSavedVariants.length).toEqual(1) + expect(multiTagSavedVariants[0].variantGuid).toEqual('SV0000002_1248367227_r0390_100') }) test('getPairedFilteredSavedVariants', () => { From 03185c6875b24cd69eff540cb416c6575a775bc5 Mon Sep 17 00:00:00 2001 From: Hana Snow Date: Wed, 6 Sep 2023 09:59:20 -0400 Subject: [PATCH 19/25] clean up selected tag logic --- ui/pages/Project/components/SavedVariants.jsx | 16 +++++++--------- ui/pages/Project/selectors.js | 1 + .../SummaryData/components/SavedVariants.jsx | 19 +++---------------- .../panel/variants/SavedVariants.jsx | 7 +++---- 4 files changed, 14 insertions(+), 29 deletions(-) diff --git a/ui/pages/Project/components/SavedVariants.jsx b/ui/pages/Project/components/SavedVariants.jsx index 1515fa4b9d..2f09953bc8 100644 --- a/ui/pages/Project/components/SavedVariants.jsx +++ b/ui/pages/Project/components/SavedVariants.jsx @@ -26,7 +26,7 @@ import { TAG_FORM_FIELD } from '../constants' import { loadSavedVariants, updateSavedVariantTable } from '../reducers' import { getCurrentProject, getProjectTagTypeOptions, getTaggedVariantsByFamily, getProjectVariantSavedByOptions, - getSavedVariantTagTypeCounts, getSavedVariantTagTypeCountsByFamily, + getSavedVariantTagTypeCounts, getSavedVariantTagTypeCountsByFamily, getSavedVariantTableState, } from '../selectors' import VariantTagTypeBar, { getSavedVariantsLinkPath } from './VariantTagTypeBar' import SelectSavedVariantsTable, { TAG_COLUMN, VARIANT_POS_COLUMN, GENES_COLUMN } from './SelectSavedVariantsTable' @@ -134,6 +134,7 @@ class BaseProjectSavedVariants extends React.PureComponent { tagTypeCounts: PropTypes.object, updateTableField: PropTypes.func, loadProjectSavedVariants: PropTypes.func, + categoryFilter: PropTypes.string, } getUpdateTagUrl = (newTag) => { @@ -233,20 +234,16 @@ class BaseProjectSavedVariants extends React.PureComponent { ) } - getSelectedTag = (tag, variantGuid) => categoryFilter => ( - tag || (variantGuid ? null : (categoryFilter || SHOW_ALL)) - ) - render() { - const { project, analysisGroup, loadProjectSavedVariants, ...props } = this.props + const { project, analysisGroup, loadProjectSavedVariants, categoryFilter, ...props } = this.props const { familyGuid, tag, variantGuid } = props.match.params - const filters = (tag === DISCOVERY_CATEGORY_NAME) ? FILTER_FIELDS : NON_DISCOVERY_FILTER_FIELDS + const appliedTagCategoryFilter = tag || (variantGuid ? null : (categoryFilter || SHOW_ALL)) return ( : null } @@ -267,6 +264,7 @@ const mapStateToProps = (state, ownProps) => ({ tagTypeCounts: ownProps.match.params.familyGuid ? getSavedVariantTagTypeCountsByFamily(state)[ownProps.match.params.familyGuid] : getSavedVariantTagTypeCounts(state, ownProps), + categoryFilter: getSavedVariantTableState(state)?.categoryFilter, }) const mapDispatchToProps = dispatch => ({ diff --git a/ui/pages/Project/selectors.js b/ui/pages/Project/selectors.js index 50a103c0fa..1845413bfa 100644 --- a/ui/pages/Project/selectors.js +++ b/ui/pages/Project/selectors.js @@ -63,6 +63,7 @@ export const getMmeSubmissionsLoading = state => state.mmeSubmissionsLoading.isL export const getSamplesLoading = state => state.samplesLoading.isLoading export const getTagTypesLoading = state => state.tagTypesLoading.isLoading export const getFamilyTagTypeCounts = state => state.familyTagTypeCounts +export const getSavedVariantTableState = state => state.savedVariantTableState const getFamiliesTableFiltersByProject = state => state.familyTableFilterState export const getCurrentProject = createSelector( diff --git a/ui/pages/SummaryData/components/SavedVariants.jsx b/ui/pages/SummaryData/components/SavedVariants.jsx index 09ed675031..ddae7b2fe5 100644 --- a/ui/pages/SummaryData/components/SavedVariants.jsx +++ b/ui/pages/SummaryData/components/SavedVariants.jsx @@ -58,26 +58,13 @@ const TAG_OPTIONS = [ label: { empty: true, circular: true, style: { backgroundColor: 'white' } }, })) -TAG_OPTIONS.push({ - value: SHOW_ALL, - text: 'All', - key: 'all', - label: { empty: true, circular: true, style: { backgroundColor: 'white' } }, -}) - const PAGE_URL = '/summary_data/saved_variants' -const getUpdateTagUrl = (selectedTag, match) => { - const lastTag = selectedTag.length > 0 ? selectedTag[selectedTag.length - 1] : null - const [firstTag, ...otherTag] = selectedTag - const updatedTag = firstTag === SHOW_ALL ? otherTag : lastTag !== SHOW_ALL && selectedTag - return `${PAGE_URL}/${updatedTag.join(';') || SHOW_ALL}${match.params.gene ? `/${match.params.gene}` : ''}` -} +const getUpdateTagUrl = + (selectedTag, match) => `${PAGE_URL}/${(selectedTag || []).join(';') || SHOW_ALL}${match.params.gene ? `/${match.params.gene}` : ''}` const getGeneHref = tag => selectedGene => `${PAGE_URL}/${tag || SHOW_ALL}/${selectedGene.key}` -const getSelectedTag = tag => categoryFilter => (tag ? tag.split(';') : [categoryFilter || SHOW_ALL]) - const BaseSavedVariants = React.memo(({ loadVariants, geneDetail, ...props }) => { const { params } = props.match const { tag, gene } = params @@ -89,7 +76,7 @@ const BaseSavedVariants = React.memo(({ loadVariants, geneDetail, ...props }) => getUpdateTagUrl={getUpdateTagUrl} loadVariants={loadVariants} multiple - getSelectedTag={getSelectedTag(tag)} + selectedTag={tag && tag.split(';')} additionalFilter={ MAX_FILTERS @@ -108,7 +107,7 @@ class SavedVariants extends React.PureComponent { inline multiple={multiple} options={tagOptions} - value={optionValue} + value={selectedTag} onChange={this.navigateToTag} /> {` variants ${allShown ? '' : `(${totalVariantsCount} total)`}`} From fae6d42c59cdba0be4ee00df78b6014d56075d37 Mon Sep 17 00:00:00 2001 From: Hana Snow Date: Wed, 6 Sep 2023 11:33:50 -0400 Subject: [PATCH 20/25] update multi tag summary data selector --- ui/pages/Project/reducers.js | 2 +- ui/pages/SummaryData/reducers.js | 22 +++--- .../components/panel/variants/selectors.js | 79 +++++++++++++------ 3 files changed, 69 insertions(+), 34 deletions(-) diff --git a/ui/pages/Project/reducers.js b/ui/pages/Project/reducers.js index 3a7ba7689a..9b47873476 100644 --- a/ui/pages/Project/reducers.js +++ b/ui/pages/Project/reducers.js @@ -86,7 +86,7 @@ export const loadSavedVariants = ({ familyGuids, variantGuid, tag }) => (dispatc // Do not load if already loaded let expectedFamilyGuids if (variantGuid) { - if (state.savedVariantsByGuid[variantGuid]) { + if (variantGuid.split(',').every(g => state.savedVariantsByGuid[g])) { return } url = `${url}/${variantGuid}` diff --git a/ui/pages/SummaryData/reducers.js b/ui/pages/SummaryData/reducers.js index 7ed34c19ba..a04382f026 100644 --- a/ui/pages/SummaryData/reducers.js +++ b/ui/pages/SummaryData/reducers.js @@ -1,8 +1,8 @@ import { combineReducers } from 'redux' -import { loadingReducer, createSingleValueReducer, createSingleObjectReducer, createObjectsByIdReducer } from 'redux/utils/reducerFactories' +import { loadingReducer, createSingleValueReducer, createSingleObjectReducer } from 'redux/utils/reducerFactories' import { RECEIVE_DATA, REQUEST_SAVED_VARIANTS } from 'redux/utils/reducerUtils' -import { SHOW_ALL, SORT_BY_XPOS } from 'shared/utils/constants' +import { SORT_BY_XPOS } from 'shared/utils/constants' import { HttpRequestHelper } from 'shared/utils/httpRequestHelper' // action creators and reducers in one file as suggested by https://github.com/erikras/ducks-modular-redux @@ -10,6 +10,7 @@ const REQUEST_SUCCESS_STORY = 'REQUEST_SUCCESS_STORY' const RECEIVE_SUCCESS_STORY = 'RECEIVE_SUCCESS_STORY' const REQUEST_MME = 'REQUEST_MME' const RECEIVE_MME = 'RECEIVE_MME' +const RECEIVE_SAVED_VARIANT_TAGS = 'RECEIVE_SAVED_VARIANT_TAGS' const UPDATE_ALL_PROJECT_SAVED_VARIANT_TABLE_STATE = 'UPDATE_ALL_PROJECT_VARIANT_STATE' const RECEIVE_EXTERNAL_ANALYSIS_UPLOAD_STATS = 'RECEIVE_EXTERNAL_ANALYSIS_UPLOAD_STATS' @@ -43,7 +44,8 @@ export const loadSuccessStory = successStoryTypes => (dispatch) => { export const loadSavedVariants = ({ tag, gene = '' }) => (dispatch, getState) => { // Do not load if already loaded if (tag) { - if (getState().savedVariantsByTag[tag]) { + const loadedTags = getState().savedVariantTags + if (loadedTags[tag] || tag.split(';').some(t => loadedTags[t])) { return } } else if (!gene) { @@ -53,10 +55,13 @@ export const loadSavedVariants = ({ tag, gene = '' }) => (dispatch, getState) => dispatch({ type: REQUEST_SAVED_VARIANTS }) new HttpRequestHelper(`/api/summary_data/saved_variants/${tag}`, (responseJson) => { - dispatch({ - type: RECEIVE_DATA, - updatesById: { ...responseJson, multiTagVariants: { [tag]: Object.keys(responseJson.savedVariantsByGuid) } }, - }) + if (tag && !gene) { + dispatch({ + type: RECEIVE_SAVED_VARIANT_TAGS, + updates: { [tag]: true }, + }) + } + dispatch({ type: RECEIVE_DATA, updatesById: responseJson }) }, (e) => { dispatch({ type: RECEIVE_DATA, error: e.message, updatesById: {} }) @@ -79,10 +84,9 @@ export const reducers = { mmeLoading: loadingReducer(REQUEST_MME, RECEIVE_MME), mmeMetrics: createSingleValueReducer(RECEIVE_MME, {}, 'metrics'), mmeSubmissions: createSingleValueReducer(RECEIVE_MME, [], 'submissions'), - savedVariantsByTag: createObjectsByIdReducer(RECEIVE_DATA, 'multiTagVariants'), + savedVariantTags: createSingleObjectReducer(RECEIVE_SAVED_VARIANT_TAGS), externalAnalysisUploadStats: createSingleValueReducer(RECEIVE_EXTERNAL_ANALYSIS_UPLOAD_STATS, {}), allProjectSavedVariantTableState: createSingleObjectReducer(UPDATE_ALL_PROJECT_SAVED_VARIANT_TABLE_STATE, { - categoryFilter: SHOW_ALL, sort: SORT_BY_XPOS, page: 1, recordsPerPage: 25, diff --git a/ui/shared/components/panel/variants/selectors.js b/ui/shared/components/panel/variants/selectors.js index 4cea3524f3..8c02ca2c27 100644 --- a/ui/shared/components/panel/variants/selectors.js +++ b/ui/shared/components/panel/variants/selectors.js @@ -65,25 +65,25 @@ const matchingVariants = (variants, matchFunc) => variants.filter(o => (Array.is // sorts manual variants to top of list, as manual variants are missing all populations const sortCompHet = (a, b) => (a.populations ? 1 : 0) - (b.populations ? 1 : 0) -export const getPairedSelectedSavedVariants = createSelector( +const getProjectSavedVariantsSelection = createSelector( getSavedVariantsByGuid, (state, props) => props.match.params, getFamiliesByGuid, getAnalysisGroupsByGuid, - (state, props) => (props.project || {}).projectGuid, + state => state.currentProjectGuid, getVariantTagsByGuid, - getVariantNotesByGuid, - state => state.savedVariantsByTag, - (savedVariants, { tag, gene, familyGuid, analysisGroupGuid, variantGuid }, familiesByGuid, analysisGroupsByGuid, - projectGuid, tagsByGuid, notesByGuid, savedVariantsByTags) => { + (savedVariants, { tag, familyGuid, analysisGroupGuid, variantGuid }, familiesByGuid, analysisGroupsByGuid, + projectGuid, tagsByGuid) => { if (!projectGuid) { - return Object.values((savedVariantsByTags || {})[tag] || {}).map(guid => savedVariants[guid]) + return null } let variants = Object.values(savedVariants) + const pairedFilters = [] + if (variantGuid) { variants = variants.filter(o => variantGuid.split(',').includes(o.variantGuid)) - return variants.length > 1 ? [variants] : variants + return [variants, pairedFilters] } if (analysisGroupGuid && analysisGroupsByGuid[analysisGroupGuid]) { @@ -91,10 +91,53 @@ export const getPairedSelectedSavedVariants = createSelector( variants = variants.filter(o => o.familyGuids.some(fg => analysisGroupFamilyGuids.includes(fg))) } else if (familyGuid) { variants = variants.filter(o => o.familyGuids.includes(familyGuid)) - } else if (projectGuid) { + } else { variants = variants.filter(o => o.familyGuids.some(fg => familiesByGuid[fg].projectGuid === projectGuid)) } + if (tag === NOTE_TAG_NAME) { + pairedFilters.push(({ noteGuids }) => noteGuids.length) + } else if (tag === MME_TAG_NAME) { + pairedFilters.push(({ mmeSubmissions = [] }) => mmeSubmissions.length) + } else if (tag && tag !== SHOW_ALL) { + pairedFilters.push(({ tagGuids }) => tagGuids.some(tagGuid => tagsByGuid[tagGuid].name === tag)) + } else if (!(familyGuid || analysisGroupGuid)) { + pairedFilters.push(({ tagGuids }) => tagGuids.length) + } + + return [variants, pairedFilters] + }, +) + +const getSummaryDataSavedVariantsSelection = createSelector( + getSavedVariantsByGuid, + (state, props) => props.match.params, + state => state.currentProjectGuid, + getVariantTagsByGuid, + (savedVariants, { tag, gene }, projectGuid, tagsByGuid) => { + if (projectGuid) { + return null + } + const pairedFilters = [] + if (gene) { + pairedFilters.push(({ transcripts }) => gene in (transcripts || {})) + } if (tag) { + const tags = tag.split(';') + pairedFilters.push(({ tagGuids }) => tags.every(t => tagGuids.some(tagGuid => tagsByGuid[tagGuid].name === t))) + } + + return [Object.values(savedVariants), pairedFilters] + }, +) + +export const getPairedSelectedSavedVariants = createSelector( + getProjectSavedVariantsSelection, + getSummaryDataSavedVariantsSelection, + getVariantTagsByGuid, + getVariantNotesByGuid, + (projectVariants, summaryDataVariants, tagsByGuid, notesByGuid) => { + const [variants, pairedFilters] = projectVariants || summaryDataVariants + const selectedVariantsByGuid = variants.reduce((acc, variant) => ({ ...acc, [variant.variantGuid]: variant }), {}) const seenPairedGuids = [] let pairedVariants = variants.reduce((acc, variant) => { @@ -135,21 +178,9 @@ export const getPairedSelectedSavedVariants = createSelector( return acc }, []) - if (tag === NOTE_TAG_NAME) { - pairedVariants = matchingVariants(pairedVariants, ({ noteGuids }) => noteGuids.length) - } else if (tag === MME_TAG_NAME) { - pairedVariants = matchingVariants(pairedVariants, ({ mmeSubmissions = [] }) => mmeSubmissions.length) - } else if (tag && tag !== SHOW_ALL) { - pairedVariants = matchingVariants( - pairedVariants, ({ tagGuids }) => tagGuids.some(tagGuid => tagsByGuid[tagGuid].name === tag), - ) - } else if (!(familyGuid || analysisGroupGuid)) { - pairedVariants = matchingVariants(pairedVariants, ({ tagGuids }) => tagGuids.length) - } - - if (gene) { - pairedVariants = matchingVariants(pairedVariants, ({ transcripts }) => gene in (transcripts || {})) - } + pairedFilters.forEach((pairedFilter) => { + pairedVariants = matchingVariants(pairedVariants, pairedFilter) + }) return pairedVariants }, From 664394ffc579ce2d533cfe8deb62d1c451811111 Mon Sep 17 00:00:00 2001 From: Hana Snow Date: Wed, 6 Sep 2023 11:43:11 -0400 Subject: [PATCH 21/25] clean up selectors --- .../components/panel/variants/selectors.js | 38 +++++++++---------- 1 file changed, 19 insertions(+), 19 deletions(-) diff --git a/ui/shared/components/panel/variants/selectors.js b/ui/shared/components/panel/variants/selectors.js index 8c02ca2c27..e42b025bbc 100644 --- a/ui/shared/components/panel/variants/selectors.js +++ b/ui/shared/components/panel/variants/selectors.js @@ -66,35 +66,30 @@ const matchingVariants = (variants, matchFunc) => variants.filter(o => (Array.is const sortCompHet = (a, b) => (a.populations ? 1 : 0) - (b.populations ? 1 : 0) const getProjectSavedVariantsSelection = createSelector( - getSavedVariantsByGuid, (state, props) => props.match.params, getFamiliesByGuid, getAnalysisGroupsByGuid, state => state.currentProjectGuid, getVariantTagsByGuid, - (savedVariants, { tag, familyGuid, analysisGroupGuid, variantGuid }, familiesByGuid, analysisGroupsByGuid, + ({ tag, familyGuid, analysisGroupGuid, variantGuid }, familiesByGuid, analysisGroupsByGuid, projectGuid, tagsByGuid) => { if (!projectGuid) { return null } - let variants = Object.values(savedVariants) - const pairedFilters = [] - + let variantFilter if (variantGuid) { - variants = variants.filter(o => variantGuid.split(',').includes(o.variantGuid)) - return [variants, pairedFilters] - } - - if (analysisGroupGuid && analysisGroupsByGuid[analysisGroupGuid]) { + variantFilter = o => variantGuid.split(',').includes(o.variantGuid) + } else if (analysisGroupGuid && analysisGroupsByGuid[analysisGroupGuid]) { const analysisGroupFamilyGuids = analysisGroupsByGuid[analysisGroupGuid].familyGuids - variants = variants.filter(o => o.familyGuids.some(fg => analysisGroupFamilyGuids.includes(fg))) + variantFilter = o => o.familyGuids.some(fg => analysisGroupFamilyGuids.includes(fg)) } else if (familyGuid) { - variants = variants.filter(o => o.familyGuids.includes(familyGuid)) + variantFilter = o => o.familyGuids.includes(familyGuid) } else { - variants = variants.filter(o => o.familyGuids.some(fg => familiesByGuid[fg].projectGuid === projectGuid)) + variantFilter = o => o.familyGuids.some(fg => familiesByGuid[fg].projectGuid === projectGuid) } + const pairedFilters = [] if (tag === NOTE_TAG_NAME) { pairedFilters.push(({ noteGuids }) => noteGuids.length) } else if (tag === MME_TAG_NAME) { @@ -105,16 +100,15 @@ const getProjectSavedVariantsSelection = createSelector( pairedFilters.push(({ tagGuids }) => tagGuids.length) } - return [variants, pairedFilters] + return [variantFilter, pairedFilters] }, ) const getSummaryDataSavedVariantsSelection = createSelector( - getSavedVariantsByGuid, (state, props) => props.match.params, state => state.currentProjectGuid, getVariantTagsByGuid, - (savedVariants, { tag, gene }, projectGuid, tagsByGuid) => { + ({ tag, gene }, projectGuid, tagsByGuid) => { if (projectGuid) { return null } @@ -126,17 +120,23 @@ const getSummaryDataSavedVariantsSelection = createSelector( pairedFilters.push(({ tagGuids }) => tags.every(t => tagGuids.some(tagGuid => tagsByGuid[tagGuid].name === t))) } - return [Object.values(savedVariants), pairedFilters] + return [null, pairedFilters] }, ) export const getPairedSelectedSavedVariants = createSelector( getProjectSavedVariantsSelection, getSummaryDataSavedVariantsSelection, + getSavedVariantsByGuid, getVariantTagsByGuid, getVariantNotesByGuid, - (projectVariants, summaryDataVariants, tagsByGuid, notesByGuid) => { - const [variants, pairedFilters] = projectVariants || summaryDataVariants + (projectVariants, summaryDataVariants, savedVariants, tagsByGuid, notesByGuid) => { + const [variantFilter, pairedFilters] = projectVariants || summaryDataVariants + + let variants = Object.values(savedVariants) + if (variantFilter) { + variants = variants.filter(variantFilter) + } const selectedVariantsByGuid = variants.reduce((acc, variant) => ({ ...acc, [variant.variantGuid]: variant }), {}) const seenPairedGuids = [] From fb288fe75414229991a1c6ed407cb20fca19a588 Mon Sep 17 00:00:00 2001 From: Hana Snow Date: Wed, 6 Sep 2023 11:55:35 -0400 Subject: [PATCH 22/25] fix tests --- .../panel/variants/selectors.test.js | 50 +++++++------------ 1 file changed, 17 insertions(+), 33 deletions(-) diff --git a/ui/shared/components/panel/variants/selectors.test.js b/ui/shared/components/panel/variants/selectors.test.js index c4c244a2bf..e89f3a1e90 100644 --- a/ui/shared/components/panel/variants/selectors.test.js +++ b/ui/shared/components/panel/variants/selectors.test.js @@ -8,18 +8,11 @@ import { getIndividualGeneDataByFamilyGene, } from './selectors' -const STATE_WITH_2_FAMILIES_AND_VARIANT_TAGS = { - ...STATE_WITH_2_FAMILIES, - savedVariantsByTag: { - Review: ['SV0000004_116042722_r0390_1000', 'SV0000002_1248367227_r0390_100'], - 'Review;Tier 1 - Phenotype not delineated': ['SV0000002_1248367227_r0390_100'], - }, -} +const NON_PROJECT_PAGE_STATE = { ...STATE_WITH_2_FAMILIES, currentProjectGuid: null } test('getPairedSelectedSavedVariants', () => { - const savedAllVariants = getPairedSelectedSavedVariants(STATE_WITH_2_FAMILIES, - { project: { projectGuid: 'R0237_1000_genomes_demo' }, match: { params: {} } }) + const savedAllVariants = getPairedSelectedSavedVariants(STATE_WITH_2_FAMILIES, { match: { params: {} } }) expect(savedAllVariants.length).toEqual(3) expect(savedAllVariants[0].variantGuid).toEqual('SV0000004_116042722_r0390_1000') expect(savedAllVariants[1].variantGuid).toEqual('SV0000002_1248367227_r0390_100') @@ -28,24 +21,20 @@ test('getPairedSelectedSavedVariants', () => { expect(savedAllVariants[2][1].variantGuid).toEqual('SV0000005_2246859833_r0390_100') const savedReviewVariants = getPairedSelectedSavedVariants( - STATE_WITH_2_FAMILIES, - { project: { projectGuid: 'R0237_1000_genomes_demo' }, match: { params: { tag: 'Review' } } }, + STATE_WITH_2_FAMILIES, { match: { params: { tag: 'Review' } } }, ) expect(savedReviewVariants.length).toEqual(2) expect(savedReviewVariants[0].variantGuid).toEqual('SV0000004_116042722_r0390_1000') expect(savedReviewVariants[1].variantGuid).toEqual('SV0000002_1248367227_r0390_100') const savedNotesVariants = getPairedSelectedSavedVariants( - STATE_WITH_2_FAMILIES, - { project: { projectGuid: 'R0237_1000_genomes_demo' }, match: { params: { tag: 'Has Notes' } } }, + STATE_WITH_2_FAMILIES, { match: { params: { tag: 'Has Notes' } } }, ) expect(savedNotesVariants.length).toEqual(1) expect(savedNotesVariants[0].variantGuid).toEqual('SV0000004_116042722_r0390_1000') - const savedFamilyVariants = getPairedSelectedSavedVariants( - STATE_WITH_2_FAMILIES, - { project: { projectGuid: 'R0237_1000_genomes_demo' }, match: { params: { familyGuid: 'F011652_1' } } }, + STATE_WITH_2_FAMILIES, { match: { params: { familyGuid: 'F011652_1' } } }, ) expect(savedFamilyVariants.length).toEqual(3) expect(savedFamilyVariants[0].variantGuid).toEqual('SV0000004_116042722_r0390_1000') @@ -53,38 +42,33 @@ test('getPairedSelectedSavedVariants', () => { expect(savedFamilyVariants[2].variantGuid).toEqual('SV0000002_SV48367227_r0390_100') const savedAnalysisGroupVariants = getPairedSelectedSavedVariants( - STATE_WITH_2_FAMILIES, - { - project: { projectGuid: 'R0237_1000_genomes_demo' }, - match: { params: { analysisGroupGuid: 'AG0000183_test_group' } }, - }, + STATE_WITH_2_FAMILIES, { match: { params: { analysisGroupGuid: 'AG0000183_test_group' } } }, ) expect(savedAnalysisGroupVariants.length).toEqual(3) expect(savedAnalysisGroupVariants[0].variantGuid).toEqual('SV0000004_116042722_r0390_1000') expect(savedAnalysisGroupVariants[1].variantGuid).toEqual('SV0000002_1248367227_r0390_100') expect(savedFamilyVariants[2].variantGuid).toEqual('SV0000002_SV48367227_r0390_100') - const savedVariants = getPairedSelectedSavedVariants( - STATE_WITH_2_FAMILIES, - { - project: { projectGuid: 'R0237_1000_genomes_demo' }, - match: { params: { variantGuid: 'SV0000004_116042722_r0390_1000' } }, - }, + STATE_WITH_2_FAMILIES, { match: { params: { variantGuid: 'SV0000004_116042722_r0390_1000' } } }, ) expect(savedVariants.length).toEqual(1) expect(savedFamilyVariants[0].variantGuid).toEqual('SV0000004_116042722_r0390_1000') const tagSavedVariants = getPairedSelectedSavedVariants( - STATE_WITH_2_FAMILIES_AND_VARIANT_TAGS, - { match: { params: { tag: 'Review' } } }, + STATE_WITH_2_FAMILIES, { match: { params: { tag: 'Review' } } }, ) expect(tagSavedVariants.length).toEqual(2) expect(tagSavedVariants[0].variantGuid).toEqual('SV0000004_116042722_r0390_1000') + const summaryDataTagSavedVariants = getPairedSelectedSavedVariants( + NON_PROJECT_PAGE_STATE, { match: { params: { tag: 'Review' } } }, + ) + expect(summaryDataTagSavedVariants.length).toEqual(2) + expect(summaryDataTagSavedVariants[0].variantGuid).toEqual('SV0000004_116042722_r0390_1000') + const multiTagSavedVariants = getPairedSelectedSavedVariants( - STATE_WITH_2_FAMILIES_AND_VARIANT_TAGS, - { match: { params: { tag: 'Review;Tier 1 - Phenotype not delineated' } } }, + NON_PROJECT_PAGE_STATE, { match: { params: { tag: 'Review;Tier 1 - Phenotype not delineated' } } }, ) expect(multiTagSavedVariants.length).toEqual(1) expect(multiTagSavedVariants[0].variantGuid).toEqual('SV0000002_1248367227_r0390_100') @@ -92,7 +76,7 @@ test('getPairedSelectedSavedVariants', () => { test('getPairedFilteredSavedVariants', () => { const pairedSavedVariants = getPairedFilteredSavedVariants( - STATE_WITH_2_FAMILIES, { project: { projectGuid: 'R0237_1000_genomes_demo' }, match: { params: {} } }, + STATE_WITH_2_FAMILIES, { match: { params: {} } }, ) expect(pairedSavedVariants.length).toEqual(2) expect(pairedSavedVariants[0].variantGuid).toEqual('SV0000004_116042722_r0390_1000') @@ -101,7 +85,7 @@ test('getPairedFilteredSavedVariants', () => { test('getVisibleSortedSavedVariants', () => { const savedVariants = getVisibleSortedSavedVariants( - STATE_WITH_2_FAMILIES, { project: { projectGuid: 'R0237_1000_genomes_demo' }, match: { params: {} } }, + STATE_WITH_2_FAMILIES, { match: { params: {} } }, ) expect(savedVariants.length).toEqual(1) expect(savedVariants[0].variantGuid).toEqual('SV0000002_1248367227_r0390_100') From cc04e2c4f2df33afd4f7763782bae1f5dab71301 Mon Sep 17 00:00:00 2001 From: Hana Snow Date: Wed, 6 Sep 2023 12:05:59 -0400 Subject: [PATCH 23/25] diff cleanup --- .../components/panel/variants/selectors.test.js | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/ui/shared/components/panel/variants/selectors.test.js b/ui/shared/components/panel/variants/selectors.test.js index e89f3a1e90..949eb903d6 100644 --- a/ui/shared/components/panel/variants/selectors.test.js +++ b/ui/shared/components/panel/variants/selectors.test.js @@ -21,20 +21,20 @@ test('getPairedSelectedSavedVariants', () => { expect(savedAllVariants[2][1].variantGuid).toEqual('SV0000005_2246859833_r0390_100') const savedReviewVariants = getPairedSelectedSavedVariants( - STATE_WITH_2_FAMILIES, { match: { params: { tag: 'Review' } } }, + STATE_WITH_2_FAMILIES, { match: { params: { tag: 'Review' } } } ) expect(savedReviewVariants.length).toEqual(2) expect(savedReviewVariants[0].variantGuid).toEqual('SV0000004_116042722_r0390_1000') expect(savedReviewVariants[1].variantGuid).toEqual('SV0000002_1248367227_r0390_100') const savedNotesVariants = getPairedSelectedSavedVariants( - STATE_WITH_2_FAMILIES, { match: { params: { tag: 'Has Notes' } } }, + STATE_WITH_2_FAMILIES, { match: { params: { tag: 'Has Notes' } } } ) expect(savedNotesVariants.length).toEqual(1) expect(savedNotesVariants[0].variantGuid).toEqual('SV0000004_116042722_r0390_1000') const savedFamilyVariants = getPairedSelectedSavedVariants( - STATE_WITH_2_FAMILIES, { match: { params: { familyGuid: 'F011652_1' } } }, + STATE_WITH_2_FAMILIES, { match: { params: { familyGuid: 'F011652_1' } } } ) expect(savedFamilyVariants.length).toEqual(3) expect(savedFamilyVariants[0].variantGuid).toEqual('SV0000004_116042722_r0390_1000') @@ -49,8 +49,9 @@ test('getPairedSelectedSavedVariants', () => { expect(savedAnalysisGroupVariants[1].variantGuid).toEqual('SV0000002_1248367227_r0390_100') expect(savedFamilyVariants[2].variantGuid).toEqual('SV0000002_SV48367227_r0390_100') + const savedVariants = getPairedSelectedSavedVariants( - STATE_WITH_2_FAMILIES, { match: { params: { variantGuid: 'SV0000004_116042722_r0390_1000' } } }, + STATE_WITH_2_FAMILIES, { match: { params: { variantGuid: 'SV0000004_116042722_r0390_1000' } } } ) expect(savedVariants.length).toEqual(1) expect(savedFamilyVariants[0].variantGuid).toEqual('SV0000004_116042722_r0390_1000') @@ -76,7 +77,7 @@ test('getPairedSelectedSavedVariants', () => { test('getPairedFilteredSavedVariants', () => { const pairedSavedVariants = getPairedFilteredSavedVariants( - STATE_WITH_2_FAMILIES, { match: { params: {} } }, + STATE_WITH_2_FAMILIES, { match: { params: {} } } ) expect(pairedSavedVariants.length).toEqual(2) expect(pairedSavedVariants[0].variantGuid).toEqual('SV0000004_116042722_r0390_1000') @@ -85,7 +86,7 @@ test('getPairedFilteredSavedVariants', () => { test('getVisibleSortedSavedVariants', () => { const savedVariants = getVisibleSortedSavedVariants( - STATE_WITH_2_FAMILIES, { match: { params: {} } }, + STATE_WITH_2_FAMILIES, { match: { params: {} } } ) expect(savedVariants.length).toEqual(1) expect(savedVariants[0].variantGuid).toEqual('SV0000002_1248367227_r0390_100') From 00e37d4f82ed89fcfb293c52e602f64a22090678 Mon Sep 17 00:00:00 2001 From: Hana Snow Date: Wed, 6 Sep 2023 12:06:36 -0400 Subject: [PATCH 24/25] diff cleanup --- ui/shared/components/panel/variants/selectors.test.js | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/ui/shared/components/panel/variants/selectors.test.js b/ui/shared/components/panel/variants/selectors.test.js index 949eb903d6..6cbb1fdb06 100644 --- a/ui/shared/components/panel/variants/selectors.test.js +++ b/ui/shared/components/panel/variants/selectors.test.js @@ -33,6 +33,7 @@ test('getPairedSelectedSavedVariants', () => { expect(savedNotesVariants.length).toEqual(1) expect(savedNotesVariants[0].variantGuid).toEqual('SV0000004_116042722_r0390_1000') + const savedFamilyVariants = getPairedSelectedSavedVariants( STATE_WITH_2_FAMILIES, { match: { params: { familyGuid: 'F011652_1' } } } ) @@ -42,7 +43,7 @@ test('getPairedSelectedSavedVariants', () => { expect(savedFamilyVariants[2].variantGuid).toEqual('SV0000002_SV48367227_r0390_100') const savedAnalysisGroupVariants = getPairedSelectedSavedVariants( - STATE_WITH_2_FAMILIES, { match: { params: { analysisGroupGuid: 'AG0000183_test_group' } } }, + STATE_WITH_2_FAMILIES, { match: { params: { analysisGroupGuid: 'AG0000183_test_group' } } } ) expect(savedAnalysisGroupVariants.length).toEqual(3) expect(savedAnalysisGroupVariants[0].variantGuid).toEqual('SV0000004_116042722_r0390_1000') From 91d3959bd8d0d38b6388f845c59c2cabf4ca2622 Mon Sep 17 00:00:00 2001 From: Hana Snow Date: Wed, 6 Sep 2023 12:22:09 -0400 Subject: [PATCH 25/25] better display and behavior --- ui/pages/SummaryData/components/SavedVariants.jsx | 3 ++- ui/pages/SummaryData/reducers.js | 4 ++-- ui/shared/components/panel/variants/SavedVariants.jsx | 7 ++++--- ui/shared/components/panel/variants/selectors.js | 5 +++-- 4 files changed, 11 insertions(+), 8 deletions(-) diff --git a/ui/pages/SummaryData/components/SavedVariants.jsx b/ui/pages/SummaryData/components/SavedVariants.jsx index ddae7b2fe5..941a85432c 100644 --- a/ui/pages/SummaryData/components/SavedVariants.jsx +++ b/ui/pages/SummaryData/components/SavedVariants.jsx @@ -75,8 +75,9 @@ const BaseSavedVariants = React.memo(({ loadVariants, geneDetail, ...props }) => filters={FILTER_FIELDS} getUpdateTagUrl={getUpdateTagUrl} loadVariants={loadVariants} + summaryFullWidth multiple - selectedTag={tag && tag.split(';')} + selectedTag={tag && tag.split(';').filter(t => t !== SHOW_ALL)} additionalFilter={ (dispatch) => { export const loadSavedVariants = ({ tag, gene = '' }) => (dispatch, getState) => { // Do not load if already loaded - if (tag) { + if (tag && tag !== SHOW_ALL) { const loadedTags = getState().savedVariantTags if (loadedTags[tag] || tag.split(';').some(t => loadedTags[t])) { return diff --git a/ui/shared/components/panel/variants/SavedVariants.jsx b/ui/shared/components/panel/variants/SavedVariants.jsx index 95cb2c69be..5e272b8ae1 100644 --- a/ui/shared/components/panel/variants/SavedVariants.jsx +++ b/ui/shared/components/panel/variants/SavedVariants.jsx @@ -56,6 +56,7 @@ class SavedVariants extends React.PureComponent { additionalFilter: PropTypes.node, tableSummaryComponent: PropTypes.elementType, multiple: PropTypes.bool, + summaryFullWidth: PropTypes.bool, } state = { showAllFilters: false } @@ -73,7 +74,7 @@ class SavedVariants extends React.PureComponent { const { match, tableState, filters, totalPages, variantsToDisplay, totalVariantsCount, firstRecordIndex, tableSummaryComponent, loading, filteredVariantsCount, tagOptions, additionalFilter, updateTableField, - variantExportConfig, loadVariants, error, multiple, selectedTag, + variantExportConfig, loadVariants, error, multiple, summaryFullWidth, selectedTag, } = this.props const { showAllFilters } = this.state const { familyGuid, variantGuid } = match.params @@ -101,7 +102,7 @@ class SavedVariants extends React.PureComponent { })} {!loading && ( - + {`Showing ${shownSummary} ${filteredVariantsCount} `} - + {additionalFilter} {!variantGuid && ( gene in (transcripts || {})) - } if (tag) { + } if (tag && tag !== SHOW_ALL) { const tags = tag.split(';') pairedFilters.push(({ tagGuids }) => tags.every(t => tagGuids.some(tagGuid => tagsByGuid[tagGuid].name === t))) } - return [null, pairedFilters] + const variantFilter = tag || gene ? null : () => false + return [variantFilter, pairedFilters] }, )