Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: SKFP-894 add posibility to search by external ids in Data Exploration #3897

Merged
merged 1 commit into from
Feb 6, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions src/graphql/biospecimens/queries.ts
Original file line number Diff line number Diff line change
Expand Up @@ -149,6 +149,7 @@ export const CHECK_BIOSPECIMEN_MATCH = gql`
edges {
node {
fhir_id
external_sample_id
sample_id
study {
study_id
Expand All @@ -169,6 +170,7 @@ export const BIOSPECIMEN_SEARCH_BY_ID_QUERY = gql`
edges {
node {
sample_id
external_sample_id
collection_sample_id
}
}
Expand Down
1 change: 1 addition & 0 deletions src/graphql/participants/queries.ts
Original file line number Diff line number Diff line change
Expand Up @@ -307,6 +307,7 @@ export const PARTICIPANT_SEARCH_BY_ID_QUERY = gql`
edges {
node {
participant_id
external_id
}
}
}
Expand Down
32 changes: 32 additions & 0 deletions src/locales/en.ts
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,28 @@ const en = {
title: 'Search by study',
tooltip: 'Search by Study Code, Study Name, dbGaP Accession Number',
},
participant: {
emptyText: 'No participants found',
placeholder: 'e.g. PT_1BCRHQVF, HTP0001',
title: 'Search by Participant ID',
tooltip: 'Search by Participant ID or External Participant ID',
},
biospecimen: {
emptyText: 'No samples found',
placeholder: 'e.g. BS_011DYZ2J, HTP0001B2_Plasma, SSH3953290',
title: 'Search by Sample ID',
tooltip: 'Search by Sample ID or External Sample ID',
collection: {
emptyText: 'No collection ID found',
placeholder: 'e.g. HTP0001B2_Whole blood, BS_1YEZ2XR4_Saliva',
title: 'Search by Collection ID',
},
},
file: {
emptyText: 'No files found',
placeholder: 'e.g. GF_001CSF26',
title: 'Search by File ID',
},
},
filters: {
actions: {
Expand Down Expand Up @@ -364,6 +386,16 @@ const en = {
inputLabel: 'Copy-paste a list of identifiers or upload a file',
match: 'Matched ({count})',
unmatch: 'Unmatched ({count})',
identifiers: {
participant: 'Participant ID, External Participant ID',
biospecimen: 'Sample ID, External Sample ID',
file: 'File ID',
},
placeholders: {
participant: 'e.g. PT_03Y3K025, HTP0001, 10214, HTP0001',
biospecimen: 'e.g. HTP0001B2_Whole blood, BS_011DYZ2J_DNA, 238981007, SSH3953290',
file: 'e.g. GF_2JAYWYDX, GF_TP6PG8Z0',
},
tableMessage:
'{submittedCount} submitted identifiers mapped to {mappedCount} unique system identifiers',
matchTable: {
Expand Down
16 changes: 10 additions & 6 deletions src/views/DataExploration/components/BiospecimenSearch.tsx
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import intl from 'react-intl-universal';
import { ExperimentOutlined } from '@ant-design/icons';
import useQueryBuilderState from '@ferlab/ui/core/components/QueryBuilder/utils/useQueryBuilderState';
import { ISqonGroupFilter } from '@ferlab/ui/core/data/sqon/types';
Expand All @@ -18,9 +19,10 @@ const BiospecimenSearch = ({ queryBuilderId }: ICustomSearchProps) => {
<GlobalSearch<IBiospecimenEntity>
queryBuilderId={queryBuilderId}
field="sample_id" // @todo: search_text, see when implemented
searchFields={['sample_id', 'external_sample_id']}
index={INDEXES.BIOSPECIMEN}
placeholder="e.g. BS_011DYZ2J, HTP0001B2_Plasma"
emptyDescription="No samples found"
placeholder={intl.get('global.search.biospecimen.placeholder')}
emptyDescription={intl.get('global.search.biospecimen.emptyText')}
query={BIOSPECIMEN_SEARCH_BY_ID_QUERY}
sqon={activeQuery as ISqonGroupFilter}
optionsFormatter={(options, matchRegex, search) =>
Expand All @@ -29,12 +31,14 @@ const BiospecimenSearch = ({ queryBuilderId }: ICustomSearchProps) => {
<SelectItem
icon={<ExperimentOutlined />}
title={highlightSearchMatch(option.sample_id, matchRegex, search)}
caption={highlightSearchMatch(option.external_sample_id, matchRegex, search)}
/>
),
value: option.sample_id,
}))
}
title="Search by sample ID"
title={intl.get('global.search.biospecimen.title')}
tooltipText={intl.get('global.search.biospecimen.tooltip')}
/>
);
};
Expand All @@ -47,8 +51,8 @@ const BiospecimenCollectionSearch = ({ queryBuilderId }: ICustomSearchProps) =>
queryBuilderId={queryBuilderId}
field="collection_sample_id" // @todo: search_text, see when implemented
index={INDEXES.BIOSPECIMEN}
placeholder="e.g. HTP0001B2_Whole blood, BS_1YEZ2XR4_Saliva"
emptyDescription="No collection ID found"
placeholder={intl.get('global.search.biospecimen.collection.placeholder')}
emptyDescription={intl.get('global.search.biospecimen.collection.emptyText')}
query={BIOSPECIMEN_SEARCH_BY_ID_QUERY}
sqon={sqon}
optionsFormatter={(options, matchRegex, search) =>
Expand All @@ -62,7 +66,7 @@ const BiospecimenCollectionSearch = ({ queryBuilderId }: ICustomSearchProps) =>
value: option.collection_sample_id,
}))
}
title="Search by collection ID"
title={intl.get('global.search.biospecimen.collection.title')}
/>
);
};
Expand Down
7 changes: 4 additions & 3 deletions src/views/DataExploration/components/FileSearch.tsx
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import intl from 'react-intl-universal';
import { FileTextOutlined } from '@ant-design/icons';
import useQueryBuilderState from '@ferlab/ui/core/components/QueryBuilder/utils/useQueryBuilderState';
import { ISqonGroupFilter } from '@ferlab/ui/core/data/sqon/types';
Expand All @@ -17,8 +18,8 @@ const FileSearch = ({ queryBuilderId }: ICustomSearchProps) => {
queryBuilderId={queryBuilderId}
field="file_id"
index={INDEXES.FILE}
placeholder="e.g. GF_001CSF26"
emptyDescription="No files found"
placeholder={intl.get('global.search.file.placeholder')}
emptyDescription={intl.get('global.search.file.emptyText')}
query={FILE_SEARCH_BY_ID_QUERY}
sqon={activeQuery as ISqonGroupFilter}
optionsFormatter={(options, matchRegex, search) =>
Expand All @@ -32,7 +33,7 @@ const FileSearch = ({ queryBuilderId }: ICustomSearchProps) => {
value: option.file_id,
}))
}
title="Search by file ID"
title={intl.get('global.search.file.title')}
/>
);
};
Expand Down
10 changes: 7 additions & 3 deletions src/views/DataExploration/components/ParticipantSearch.tsx
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import intl from 'react-intl-universal';
import { UserOutlined } from '@ant-design/icons';
import useQueryBuilderState from '@ferlab/ui/core/components/QueryBuilder/utils/useQueryBuilderState';
import { ISqonGroupFilter } from '@ferlab/ui/core/data/sqon/types';
Expand All @@ -16,9 +17,10 @@ const ParticipantSearch = ({ queryBuilderId }: ICustomSearchProps) => {
<GlobalSearch<IParticipantEntity>
queryBuilderId={queryBuilderId}
field="participant_id"
searchFields={['participant_id', 'external_id']}
index={INDEXES.PARTICIPANT}
placeholder="e.g. PT_1BCRHQVF"
emptyDescription="No participants found"
placeholder={intl.get('global.search.participant.placeholder')}
emptyDescription={intl.get('global.search.participant.emptyText')}
query={PARTICIPANT_SEARCH_BY_ID_QUERY}
sqon={activeQuery as ISqonGroupFilter}
optionsFormatter={(options, matchRegex, search) =>
Expand All @@ -27,12 +29,14 @@ const ParticipantSearch = ({ queryBuilderId }: ICustomSearchProps) => {
<SelectItem
icon={<UserOutlined />}
title={highlightSearchMatch(option.participant_id, matchRegex, search)}
caption={highlightSearchMatch(option.external_id, matchRegex, search)}
/>
),
value: option.participant_id,
}))
}
title="Search by participant ID"
title={intl.get('global.search.participant.title')}
tooltipText={intl.get('global.search.participant.tooltip')}
/>
);
};
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,8 +22,8 @@ const BiospecimenUploadIds = ({ queryBuilderId }: OwnProps) => (
<EntityUploadIds
entityId="biospecimen"
entityIdTrans="sample"
entityIdentifiers="Sample ID"
placeHolder="e.g. HTP0001B2_Whole blood, BS_011DYZ2J_DNA, 238981007"
entityIdentifiers={intl.get('components.uploadIds.modal.identifiers.biospecimen')}
placeHolder={intl.get('components.uploadIds.modal.placeholders.biospecimen')}
fetchMatch={async (ids) => {
const response = await ArrangerApi.graphqlRequest({
query: CHECK_BIOSPECIMEN_MATCH.loc?.source.body,
Expand All @@ -32,7 +32,7 @@ const BiospecimenUploadIds = ({ queryBuilderId }: OwnProps) => (
offset: 0,
sqon: generateQuery({
operator: BooleanOperators.or,
newFilters: ['sample_id'].map((field) =>
newFilters: ['sample_id', 'external_sample_id'].map((field) =>
generateValueFilter({
field,
value: ids,
Expand All @@ -49,7 +49,9 @@ const BiospecimenUploadIds = ({ queryBuilderId }: OwnProps) => (

return biospecimens?.flatMap((biospecimen) => {
const matchedIds: string[] = ids.filter(
(id: string) => biospecimen.sample_id.toLocaleLowerCase() === id.toLocaleLowerCase(),
(id: string) =>
biospecimen.sample_id.toLocaleLowerCase() === id.toLocaleLowerCase() ||
biospecimen.external_sample_id.toLocaleLowerCase() === id.toLocaleLowerCase(),
);

return matchedIds.map((id, index) => ({
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,8 +22,8 @@ const FileUploadIds = ({ queryBuilderId }: OwnProps) => (
<EntityUploadIds
entityId="file"
entityIdTrans="file"
entityIdentifiers="File ID"
placeHolder="e.g. GF_2JAYWYDX, GF_TP6PG8Z0"
entityIdentifiers={intl.get('components.uploadIds.modal.identifiers.file')}
placeHolder={intl.get('components.uploadIds.modal.placeholders.file')}
fetchMatch={async (ids: string[]) => {
const response = await ArrangerApi.graphqlRequest({
query: CHECK_FILE_MATCH.loc?.source.body,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,8 +22,8 @@ const ParticipantUploadIds = ({ queryBuilderId }: OwnProps) => (
<EntityUploadIds
entityId="participant"
entityIdTrans="participant"
entityIdentifiers="Participant ID"
placeHolder="e.g. PT_03Y3K025, HTP0001, 10214"
entityIdentifiers={intl.get('components.uploadIds.modal.identifiers.participant')}
placeHolder={intl.get('components.uploadIds.modal.placeholders.participant')}
fetchMatch={async (ids: string[]) => {
const response = await ArrangerApi.graphqlRequest({
query: CHECK_PARTICIPANT_MATCH.loc?.source.body,
Expand All @@ -32,7 +32,7 @@ const ParticipantUploadIds = ({ queryBuilderId }: OwnProps) => (
offset: 0,
sqon: generateQuery({
operator: BooleanOperators.or,
newFilters: ['participant_id'].map((field) =>
newFilters: ['participant_id', 'external_id'].map((field) =>
generateValueFilter({
field,
value: ids,
Expand All @@ -49,7 +49,9 @@ const ParticipantUploadIds = ({ queryBuilderId }: OwnProps) => (

return participants?.flatMap((participant) => {
const matchedIds: string[] = ids.filter(
(id: string) => participant.participant_id.toLocaleLowerCase() === id.toLocaleLowerCase(),
(id: string) =>
participant.participant_id.toLocaleLowerCase() === id.toLocaleLowerCase() ||
participant.external_id.toLocaleLowerCase() === id.toLocaleLowerCase(),
);

return matchedIds.map((id, index) => ({
Expand Down
Loading