Skip to content

Commit

Permalink
Merge pull request #380 from MoTrPAC/366_JZ_Search_Protein_Names
Browse files Browse the repository at this point in the history
Connects to #366. DEA search by protein names.
  • Loading branch information
jimmyzhen authored Dec 12, 2024
2 parents 2cd73d3 + ee8715d commit ee75bbc
Show file tree
Hide file tree
Showing 6 changed files with 308 additions and 533 deletions.
20 changes: 20 additions & 0 deletions src/Search/searchActions.js
Original file line number Diff line number Diff line change
Expand Up @@ -108,6 +108,26 @@ function handleSearch(params, inputValue, scope) {
p_value: { min: '', max: '' },
};
}
// insert 'is_meta" field to fields array if ktype is 'metab'
// and delete 'protein_name' field if it exists
if (params.ktype === 'metab') {
if (!params.fields.includes('is_meta')) {
params.fields = ['is_meta', ...params.fields];
}
if (params.fields.includes('protein_name')) {
const index = params.fields.indexOf('protein_name');
params.fields.splice(index, 1);
}
}
// delete 'is_meta' flag from fields array (if it exists)
// if ktype is 'protein' or 'gene'
if (params.ktype === 'protein' || params.ktype === 'gene') {
if (params.fields.includes('is_meta')) {
const index = params.fields.indexOf('is_meta');
params.fields.splice(index, 1);
}
}

return (dispatch) => {
dispatch(searchSubmit(params, scope));
return axios
Expand Down
157 changes: 81 additions & 76 deletions src/Search/searchPage.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ import IconSet from '../lib/iconSet';
import { trackEvent } from '../GoogleAnalytics/googleAnalytics';
import { genes } from '../data/genes';
import { metabolites } from '../data/metabolites';
import { proteins } from '../data/proteins';
import searchStructuredData from '../lib/searchStructuredData/search';
import UserSurveyModal from '../UserSurvey/userSurveyModal';

Expand Down Expand Up @@ -110,50 +111,61 @@ export function SearchPage({
return null;
}

// get options based on selected search context
function getOptions() {
switch (searchParams.ktype) {
case 'gene':
return genes;
case 'metab':
return metabolites;
case 'protein':
return proteins;
default:
return [];
}
}

// render placeholder text in primary search input field
function renderPlaceholder() {
if (searchParams.ktype === 'protein') {
return 'Example: NP_001000006.1, NP_001001508.2, NP_001005898.3';
return 'Example: "atpase inhibitor, mitochondrial", "global ischemia-induced protein 11"';
}
if (searchParams.ktype === 'metab') {
return 'Example: 8,9-EpETrE, C18:1 LPC plasmalogen B';
return 'Example: "amino acids and peptides", "c10:2 carnitine"';
}
return 'Example: BRD2, SMAD3, ID1';
return 'Example: brd2, smad3, vegfa';
}

const inputEl = document.querySelector('.rbt-input-main');

// FIXME: transform react-bootstrap-typeahead state from array to string
// Transform input values
// Keep react-bootstrap-typeahead state array as is
// Convert manually entered gene/protein/metabolite string input to array
function formatSearchInput() {
const newArr = [];
// react-bootstrap-typeahead state array has values
if (multiSelections.length) {
multiSelections.forEach((item) => newArr.push(item.id));
return newArr.join(', ');
return newArr;
}
// Handle manually entered gene/metabolite input
// Handle manually entered gene/protein/metabolite string input
// convert formatted string to array
if (inputEl.value && inputEl.value.length) {
const str = inputEl.value;
if (searchParams.ktype === 'gene') {
const arr = str.split(',').map((s) => s.trim());
return arr.join(', ');
}
return str;
const inputStr = inputEl.value;
// Match terms enclosed in double quotes or not containing commas
const terms = inputStr.match(/("[^"]+"|[^, ]+)/g);
// Remove double quotes from terms that are enclosed and trim any extra spaces
return terms.map((term) => term.replace(/"/g, '').trim());
}
return '';
return newArr;
}

// Clear manually entered gene/protein/metabolite input
function clearGeneInput(ktype) {
const inputElProtein = document.querySelector('.search-input-kype');

if (ktype && ktype === 'protein') {
if (inputElProtein && inputElProtein.value && inputElProtein.value.length) {
inputElProtein.value = '';
}
} else if (inputEl && inputEl.value && inputEl.value.length) {
const clearSearchTermInput = () => {
if (inputEl && inputEl.value && inputEl.value.length) {
inputRef.current.clear();
}
}
};

return (
<div className="searchPage px-3 px-md-4 mb-3">
Expand All @@ -167,78 +179,71 @@ export function SearchPage({
<form id="searchForm" name="searchForm">
<PageTitle title="Search differential abundance data" />
<div className="search-content-container">
<div className="search-summary-container row mb-4 pb-2">
<div className="search-summary-container row mb-4">
<div className="lead col-12">
Search by gene symbol, protein ID or metabolite name to examine the
Search by gene symbol, protein name or metabolite name to examine the
timewise endurance training response over 8 weeks of training in
young adult rats.
{' '}
<span className="font-weight-bold">
Multiple search terms MUST be separated by comma and space.
Examples: "NP_001000006.1, NP_001001508.2, NP_001005898.3" or
"8,9-EpETrE, C18:1 LPC plasmalogen B".
</span>
{' '}
young adult rats. To ensure the best search results, please use the
following guidelines:
<ol>
<li>
Use
{' '}
<span className="font-weight-bold">
auto-suggested search terms
</span>
{' '}
by typing the first few
characters of the gene symbol, protein or metabolite names.
</li>
<li>
Separate multiple search terms using a comma followed by a space. For example:
{' '}
<code>brd2, smad3, vegfa</code>
</li>
<li>
Use double quotes to enclose search terms containing commas,
spaces or commas followed by spaces. For example:
{' '}
<code>"tca acids", "8,9-epetre", "coa(3:0, 3-oh)"</code>
</li>
</ol>
<p>
The endurance trained young adult rats dataset is made available
under the
{' '}
<Link to="/license">CC BY 4.0 license</Link>
.
</p>
</div>
</div>
<div className="es-search-ui-container d-flex align-items-center w-100 pb-2">
<RadioButton
changeParam={changeParam}
ktype={searchParams.ktype}
resetSearch={resetSearch}
clearInput={clearSearchTermInput}
setMultiSelections={setMultiSelections}
inputEl={inputEl}
/>
<div className="search-box-input-group d-flex align-items-center flex-grow-1">
{/*
<input
type="text"
id="keys"
name="keys"
className="form-control search-input-kype flex-grow-1"
placeholder={renderPlaceholder()}
value={searchParams.keys}
onChange={(e) => changeParam('keys', e.target.value)}
/>
*/}
<div className="input-group">
<div className="input-group-prepend">
<span className="input-group-text material-icons">
pest_control_rodent
</span>
</div>
{searchParams.ktype === 'gene' ||
searchParams.ktype === 'metab' ? (
<Typeahead
id="dea-search-typeahead-multiple"
labelKey="id"
multiple
onChange={setMultiSelections}
options={
searchParams.ktype === 'gene' ? genes : metabolites
}
placeholder={renderPlaceholder()}
selected={multiSelections}
minLength={2}
ref={inputRef}
/>
) : null}
{searchParams.ktype === 'protein' && (
<input
type="text"
id="keys"
name="keys"
className="form-control search-input-kype flex-grow-1"
placeholder={renderPlaceholder()}
value={searchParams.keys}
onChange={(e) => changeParam('keys', e.target.value)}
/>
)}
<Typeahead
id="dea-search-typeahead-multiple"
labelKey="id"
multiple
onChange={setMultiSelections}
options={getOptions()}
placeholder={renderPlaceholder()}
selected={multiSelections}
minLength={2}
ref={inputRef}
/>
</div>
<PrimaryOmicsFilter
omics={searchParams.omics}
Expand Down Expand Up @@ -280,9 +285,7 @@ export function SearchPage({
type="button"
className="btn btn-secondary search-reset ml-2"
onClick={() => {
clearGeneInput(
searchParams.ktype === 'protein' ? 'protein' : null
);
clearSearchTermInput();
resetSearch('all');
setMultiSelections([]);
}}
Expand Down Expand Up @@ -472,29 +475,31 @@ function RadioButton({
changeParam,
ktype,
resetSearch,
clearInput,
setMultiSelections,
inputEl,
}) {
const radioButtons = [
{
keyType: 'gene',
id: 'inlineRadioGene',
label: 'Gene Symbol',
label: 'Gene symbol',
},
{
keyType: 'protein',
id: 'inlineRadioProtein',
label: 'Protein ID',
label: 'Protein name',
},
{
keyType: 'metab',
id: 'inlineRadioMetab',
label: 'Metabolite',
label: 'Metabolite name',
},
];

const handleRadioChange = (e) => {
resetSearch('all');
clearInput();
setMultiSelections([]);
changeParam('ktype', e.target.value);
if (inputEl && inputEl.value && inputEl.value.length) {
Expand Down
24 changes: 15 additions & 9 deletions src/Search/searchReducer.js
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ export const defaultSearchState = {
searchResults: {},
searchParams: {
ktype: 'gene',
keys: '',
keys: [],
omics: 'all',
analysis: 'all',
filters: {
Expand Down Expand Up @@ -42,9 +42,11 @@ export const defaultSearchState = {
'p_value_female',
],
unique_fields: ['tissue', 'assay', 'sex', 'comparison_group'],
size: 25000,
size: 10000,
start: 0,
save: false,
convert_assay_code: 1,
convert_tissue_code: 1,
},
scope: 'all',
searching: false,
Expand Down Expand Up @@ -121,6 +123,7 @@ export function SearchReducer(state = { ...defaultSearchState }, action) {
fields,
unique_fields,
size,
start,
} = action.params;
return {
...state,
Expand All @@ -134,8 +137,11 @@ export function SearchReducer(state = { ...defaultSearchState }, action) {
fields,
unique_fields,
size,
start,
debug: true,
save: false,
convert_assay_code: 1,
convert_tissue_code: 1,
},
scope: action.scope,
searching: true,
Expand All @@ -157,9 +163,9 @@ export function SearchReducer(state = { ...defaultSearchState }, action) {
searchResults:
action.searchResults.message || action.searchResults.errors
? {
errors:
errors:
action.searchResults.message || action.searchResults.errors,
}
}
: action.searchResults,
searching: false,
hasResultFilters:
Expand Down Expand Up @@ -189,7 +195,7 @@ export function SearchReducer(state = { ...defaultSearchState }, action) {
}

const defaultParams = { ...defaultSearchState.searchParams };
defaultParams.keys = '';
defaultParams.keys = [];
defaultParams.filters = {
tissue: [],
assay: [],
Expand Down Expand Up @@ -229,10 +235,10 @@ export function SearchReducer(state = { ...defaultSearchState }, action) {
downloadResults:
action.downloadResults.message || action.downloadResults.errors
? {
errors:
action.downloadResults.message ||
action.downloadResults.errors,
}
errors:
action.downloadResults.message
|| action.downloadResults.errors,
}
: action.downloadResults,
downloading: false,
};
Expand Down
Loading

0 comments on commit ee75bbc

Please sign in to comment.