Skip to content

Commit

Permalink
MOTR-122 MOTR-123 Implementation of search/summary/meta-analysis feat…
Browse files Browse the repository at this point in the history
…ures (#38)

* ✨ Adding quick search box to header

* 🎉 Initial commit for quick search box component

Still need to add handling for form submission

* 🎉 Initial commit for quick search box

* ✨ Adding quick search and advanced search modules

* ✨ Adding search module

* 🎉 Initial commit for advanced search form

* ✨ Adding route for search page

* ✨ Introducing Google Material Icons

* 🔋 Added handling for quick search form submission

* 💅 Styling for search page's advanced search form

* 🎉 Initial commit for advanced search page's single search term/value pair row

Each row comes with conditional operator, row removal button and row addition button.

* 🎉 Initial commit for advanced search page's search term list

* 🎉 Initial commit for advanced search page's search form

* 🎉 Initial commit for advanced search page

* ✨ Added new 'Summry' link under 'Data'

Replaced icons with Google Material Icons except the rat icon

* ✨ Allow Material Icons to be used in Storybook

* ✨ Add pacakge dependency for REST async calls

* ✨ Add search reducer to root

* 💅 Styling tweaks for advanced search form

* ✨ Update code to use Redux for state management

* 🎉 Initial commit for components to render search result UIs

They are just placeholders at the moment.

* 🎉 Initial commit for search actions

Form submission handling is NOT yet fully implemented at this moment. Need more work.

* 🎉 Initial commit for search reducer

* 🎉 Initial commit for Storybook implementations of quick and advanced search components

* 🔋 Implement action creator to append search query to URL when user submits the search form

Also implement a simple action creator for the 'Advanced' link to show the advanced search page.

* 🔋 Implement handling for the 'Advanced' link to show the advanced search page

* 🐛 Fix page crashing issue when submitting the advanced search form

* Passing the params to action creator for constructing the search query

* ✨ Implement Redux for quick search box

* ✨ Implementation of Redux for quick search box

* 🔧 Rename arguments for distinguish quick search from advanced search

* Combining quick and advanced search states for rendering search results

* 🚀 Redux implementation for quick search box

* 🎉 Initial commit for the Redux implementation of quick search box

* 🔧 Move quick search Redux states and actions to its parent component

* 🔧 Move quick search Redux states and actions to the parent navbar component

And pass the Redux states and actions to quick search component as props

* 🔧 Quick search Redux implementation in Storybook

* ✨ Added summary page route

* 💅 Changed goBack button icon to use Material Icons

* Added 'dataSummary' module

* Added different school colors for badges representing different sites

* Added styling for goBack button on search results page

* ✨ Added rendering for tissue analysis sample table if user clicks on any given 'received sample' links in the tissue analysis status table on the data summary page

Also added goBack button in the page header

* ✨ Added implementation of rendering tissue sample results table

* 🎉 Initial commits for styling used on tissue analysis status table and tissue sample results table

* 🎉 Initial commit for data summary page component

It only contains the tissue analysis status table component for now. More will be added later.

* 🎉 Initial commit for list of tissues

* 🎉 Initial commit for tissue analysis status table component

* 🎉 Initial commit for tissue sample results table component

* 🔧 Added new goBack button for going back to advanced search page form from search results

* 🎉 Initial commit for search param icons

* 🔧 Changed search param option

* ✨ Implemented the rendering of visual cues for selected search params

* 💅 Added styling for search params visual cues

* 🎉 Initial commit for mocked data

* 🎉 Initial commit for mocked data

* ✨ UI Implementation of human meta-analysis of a gene for acute muscle with mocked data

* 🎉 Initial commit for the UI Implementation of human meta-analysis of a gene for acute muscle with mocked data

* 🎉 Initial commit for icons used on data summary table

* 🎉 Initial commit for the UI Implementation of human meta-analysis of a gene for acute muscle with mocked data

* 🎉 Initial commit for stylings used for the UI Implementation of human meta-analysis of a gene for acute muscle with mocked data

* 🎉 Initial commit for styling used for showing tooltips on the progress bar UI on the data summary page

* 💅 Added analysis module

* 💅 Updated styling for data summary table

* 💅 Added tooltip module

* 💅 Hiding phenotype UI option on human data analysis page

* 🔧 Updated setting to enable the UI option for meta-analysis

* 🚀 Various UI enhancements including progress bars and icons for study types

* 🚀 Various UI enhancements including the rendering of links for vial ids and biospecimen ids

Also enable the rendering of all samples from a given site on a given assay without the presence of tissue param.

* ⚠️ Initial commit for mocked meta analysis data

* ⚠️ Initial commit for mocked meta-analysis data

* ⚠️ Initial commit for mocked meta-analysis data

* ⚠️ Initial commit for mocked meta-analysis data

* ⚠️ Initial commit for mocked meta-analysis data

* ⚠️ Initial commit for mocked meta-analysis data

* ⚠️ Initial commit for mocked meta-analysis plot images

* 💅 Added a comment note below the meta analysis gene data table

* 💅 Added icon to visual cue for the 'all' selection option on advanced search page

* 🔧 Customize header titles for selected analysis and sub-analysis types

* ⚠️ Added 'lunr' package dependency for mocked searches in client

* Added search results module

* 🎉 Initial commit for search results styling

* ⚠️ Temporary implementation for mocked searches in client using lunr

* 💅 Temp UI implementation for search result filters

* ⚠️ Temp UI implementation for search results using mocked client-side search indexing
  • Loading branch information
jimmyzhen authored and kilodalton committed Jul 14, 2019
1 parent 434610b commit 9ac940a
Show file tree
Hide file tree
Showing 73 changed files with 3,340 additions and 28 deletions.
1 change: 1 addition & 0 deletions .storybook/preview-head.html
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
<link href="https://fonts.googleapis.com/icon?family=Material+Icons" rel="stylesheet">
2 changes: 2 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -4,13 +4,15 @@
"private": true,
"dependencies": {
"auth0-js": "9.10.0",
"axios": "^0.19.0",
"bootstrap": "^4.3.1",
"chart.js": "^2.7.3",
"d3": "^5.9.2",
"dayjs": "1.8.5",
"history": "^4.7.2",
"jquery": "^3.3.1",
"lodash": "^4.17.11",
"lunr": "^2.3.6",
"popper.js": "1.14.7",
"prop-types": "15.7.1",
"react": "16.8.1",
Expand Down
2 changes: 2 additions & 0 deletions public/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,8 @@

<!-- Warm up connection for better performance in loading Google Fonts -->
<link rel="preconnect" href="https://fonts.gstatic.com/" crossorigin>
<!-- Google Material Icons -->
<link href="https://fonts.googleapis.com/icon?family=Material+Icons" rel="stylesheet">

<title>MoTrPAC Data Hub</title>
</head>
Expand Down
43 changes: 39 additions & 4 deletions src/AnalysisPage/analysisHomePage.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,9 @@ export function AnalysisHomePage({
isAuthenticated,
depth,
currentAnalysis,
currentAnalysisTitle,
currentSubAnalysis,
currentSubAnalysisTitle,
onPickAnalysis,
onPickSubAnalysis,
goBack,
Expand All @@ -28,7 +30,15 @@ export function AnalysisHomePage({
function AnalysisTypeButton({ analysisType }) {
if (analysisType.active) {
return (
<div id={analysisType.shortName} onClick={onPickAnalysis} onKeyPress={onPickAnalysis} tabIndex={0} role="button" className={`col-5 col-sm-3 m-3 analysisType analysisTypeActive ${subjectType}`}>
<div
id={analysisType.shortName}
onClick={onPickAnalysis}
onKeyPress={onPickAnalysis}
tabIndex={0}
role="button"
className={`col-5 col-sm-3 m-3 analysisType analysisTypeActive ${subjectType}`}
title={analysisType.title}
>
<p className="centered">{analysisType.title}</p>
<img src={analysisType.icon} className="align-self-end" alt={`${analysisType.title} Icon`} />
</div>
Expand Down Expand Up @@ -59,7 +69,15 @@ export function AnalysisHomePage({
function SubAnalysisButton({ subAnalysis }) {
if (subAnalysis.active) {
return (
<div className="row subAnalysisRow justify-content-center m-1 m-sm-4" onClick={onPickSubAnalysis} onKeyPress={onPickSubAnalysis} tabIndex={0} role="button" id={subAnalysis.shortName}>
<div
className="row subAnalysisRow justify-content-center m-1 m-sm-4"
onClick={onPickSubAnalysis}
onKeyPress={onPickSubAnalysis}
tabIndex={0}
role="button"
id={subAnalysis.shortName}
title={subAnalysis.title}
>
<div className="col-11 col-md-5 m-1 my-2 align-self-center imgCont">
<img src={subAnalysis.icon} className="align-self-end" alt={`${subAnalysis.title} Icon`} />
</div>
Expand Down Expand Up @@ -110,7 +128,11 @@ export function AnalysisHomePage({
};
// Button to return 1 depth level
function BackButton() {
return <button className="backButton" onClick={goBack} type="button"><span className="oi oi-arrow-thick-left" /></button>;
return (
<button className="backButton" onClick={goBack} type="button">
<span className="material-icons">arrow_back</span>
</button>
);
}

const analyses = analysisTypes
Expand Down Expand Up @@ -150,12 +172,19 @@ export function AnalysisHomePage({
);
}

// Render header title
const renderHeaderTitle = () => {
if (depth === 0) return `${subjectType} Data Analysis`;
if (depth === 1) return `${currentAnalysisTitle} (${subjectType})`;
if (depth === 2) return `${currentSubAnalysisTitle} (${subjectType})`;
};

return (
<div className="analysisPage col-md-9 ml-sm-auto col-lg-10 px-4">
<div className="page-title pt-3 pb-2 border-bottom">
<h3>
{(depth > 0) ? <BackButton /> : ''}
{`${subjectType} Data Analysis`}
{renderHeaderTitle()}
</h3>
</div>
{(depth === 2) ? selectedDataAnalysis : ''}
Expand All @@ -181,7 +210,9 @@ AnalysisHomePage.propTypes = {
depth: PropTypes.number.isRequired,
isAuthenticated: PropTypes.bool,
currentAnalysis: PropTypes.string.isRequired,
currentAnalysisTitle: PropTypes.string.isRequired,
currentSubAnalysis: PropTypes.string.isRequired,
currentSubAnalysisTitle: PropTypes.string.isRequired,
goBack: PropTypes.func.isRequired,
onPickAnalysis: PropTypes.func.isRequired,
onPickSubAnalysis: PropTypes.func.isRequired,
Expand All @@ -199,21 +230,25 @@ AnalysisHomePage.defaultProps = {
const mapStateToProps = state => ({
depth: state.analysis.depth,
currentAnalysis: state.analysis.currentAnalysis,
currentAnalysisTitle: state.analysis.currentAnalysisTitle,
currentSubAnalysis: state.analysis.currentSubAnalysis,
currentSubAnalysisTitle: state.analysis.currentSubAnalysisTitle,
isAuthenticated: state.auth.isAuthenticated,
});

const mapDispatchToProps = dispatch => ({
onPickAnalysis: e => dispatch({
type: 'ANALYSIS_SELECT',
analysis: e.currentTarget.id,
analysisTitle: e.currentTarget.title,
}),
goBack: () => dispatch({
type: 'GO_BACK',
}),
onPickSubAnalysis: e => dispatch({
type: 'SUBANALYSIS_SELECT',
subAnalysis: e.currentTarget.id,
subAnalysisTitle: e.currentTarget.title,
}),
});

Expand Down
6 changes: 6 additions & 0 deletions src/AnalysisPage/analysisReducer.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,9 @@ export const defaultAnalysisState = {
},
},
currentAnalysis: '',
currentAnalysisTitle: '',
currentSubAnalysis: '',
currentSubAnalysisTitle: '',
depth: 0,
analysisSelected: false,
subAnalysisSelected: false,
Expand All @@ -17,13 +19,15 @@ export default function AnalysisReducer(state = { ...defaultAnalysisState }, act
return {
...state,
currentAnalysis: action.analysis,
currentAnalysisTitle: action.analysisTitle,
analysisSelected: true,
depth: 1,
};
case 'SUBANALYSIS_SELECT':
return {
...state,
currentSubAnalysis: action.subAnalysis,
currentSubAnalysisTitle: action.subAnalysisTitle,
subAnalysisSelected: true,
depth: 2,
};
Expand All @@ -36,7 +40,9 @@ export default function AnalysisReducer(state = { ...defaultAnalysisState }, act
return {
...state,
currentAnalysis: '',
currentAnalysisTitle: '',
currentSubAnalysis: '',
currentSubAnalysisTitle: '',
depth: 0,
analysisSelected: false,
subAnalysisSelected: false,
Expand Down
14 changes: 9 additions & 5 deletions src/AnalysisPage/humanDataAnalysis.jsx
Original file line number Diff line number Diff line change
@@ -1,15 +1,19 @@
import React from 'react';
import PropTypes from 'prop-types';
import HumanGeneMetaAnalysis from './humanGeneMetaAnalysis';

export default function HumanDataAnalysis({
analysis,
subAnalysis,
}) {
return (
<div className="data-analysis-container">
Coming Soon
</div>
);
if (analysis === 'PDMA') {
switch (subAnalysis) {
case 'MA_G':
return <HumanGeneMetaAnalysis />;
default:
return null;
}
}
}

HumanDataAnalysis.propTypes = {
Expand Down
195 changes: 195 additions & 0 deletions src/AnalysisPage/humanGeneMetaAnalysis.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,195 @@
import React, { useState } from 'react';
import HumanMetaAnalysisAcuteMuscleGenes from '../data/humanMetaAnalysisAcuteMuscleGenes';

import PlotFOXO1 from '../assets/plots/analysis/meta-analysis/human/acute-muscle/FOXO1.png';
import PlotID1 from '../assets/plots/analysis/meta-analysis/human/acute-muscle/ID1.png';
import PlotPPARGC1A from '../assets/plots/analysis/meta-analysis/human/acute-muscle/PPARGC1A.png';
import PlotSMAD3 from '../assets/plots/analysis/meta-analysis/human/acute-muscle/SMAD3.png';
import PlotVEGFA from '../assets/plots/analysis/meta-analysis/human/acute-muscle/VEGFA.png';

const plotMapping = {
FOXO1: PlotFOXO1,
ID1: PlotID1,
PPARGC1A: PlotPPARGC1A,
SMAD3: PlotSMAD3,
VEGFA: PlotVEGFA,
};

const humanMetaAnalysisAcuteMuscleGeneStats = require('../data/human_meta_analysis_acute_muscle_gene_stats');

/**
* Functional component to render human meta-analysis acute muscle data visualization
* It uses internal states not shared by other components
*
* @return {Object} JSX representation of the meta analysis data visualization
*/
function HumanGeneMetaAnalysis() {
// Local states
const [inputValue, setInputValue] = useState('');
const [gene, setGene] = useState('');

/**
* Simple Math.round method
* alternative #1 - Math.round(num * 10) / 10; //*** returns 1 decimal
* alternative #2 - Math.round((num + 0.00001) * 100) / 100; //*** returns 2 decimals
*/
const classificationMathRound = (number, decimals) => {
return Number(Math.round(number + ('e' + decimals)) + ('e-' + decimals));
};

const geneObj = humanMetaAnalysisAcuteMuscleGeneStats
.find(item => item.Symbol === gene.toUpperCase());

// Renders found gene stats info in the gene search panel
const renderGeneInfo = () => {
if (geneObj && Object.keys(geneObj).length) {
const geneObjClone = Object.assign({}, geneObj);
delete geneObjClone.Summary;

return (
<div className="gene-search-result">
<table className="table table-borderless table-striped">
<tbody>
{Object.entries(geneObjClone).map(([key, value]) => {
return (
<tr>
<th scope="row">{`${key}:`}</th>
<td>{parseFloat(value) ? classificationMathRound(Number(value), 2) : value}</td>
</tr>
);
})}
<tr>
<td colSpan="2">
<div className="font-weight-bold">Summary:</div>
{geneObj.Summary}
</td>
</tr>
</tbody>
</table>
</div>
);
}

return <div className="gene-search-result">No matching gene found.</div>;
};

const geneAanlysisDataArray = HumanMetaAnalysisAcuteMuscleGenes[gene.toUpperCase()]
? HumanMetaAnalysisAcuteMuscleGenes[gene.toUpperCase()] : null;

// Renders table head of gene meta-analysis
const renderMetaAnalysisTableHead = () => {
return (
<tr className="table-head">
<th scope="col" className="gene-meta-analysis-label text-nowrap">Cohort ID</th>
<th scope="col" className="gene-meta-analysis-label text-nowrap">Geo ID</th>
<th scope="col" className="gene-meta-analysis-label text-nowrap">Training</th>
<th scope="col" className="gene-meta-analysis-label text-nowrap">Avg Age</th>
<th scope="col" className="gene-meta-analysis-label text-nowrap">Age SD</th>
<th scope="col" className="gene-meta-analysis-label text-nowrap">SDD</th>
</tr>
);
};

// Renders individual rows of gene meta-analysis data
const renderMetaAnalysisTableRows = (data) => {
const rows = data.map(item => (
<tr key={item.sdd} className={`${item.avg_age} ${item.age_sd} ${item.sdd}`}>
<td className="gene-meta-analysis-value text-nowrap">{item.V1}</td>
<td className="gene-meta-analysis-value text-nowrap">{item.gse}</td>
<td className="gene-meta-analysis-value text-nowrap">{item.training}</td>
<td className="gene-meta-analysis-value text-nowrap">{classificationMathRound(Number(item.avg_age), 2)}</td>
<td className="gene-meta-analysis-value text-nowrap">{classificationMathRound(Number(item.age_sd), 2)}</td>
<td className="gene-meta-analysis-value text-nowrap">{classificationMathRound(Number(item.sdd), 2)}</td>
</tr>
));

return rows;
};

// Renders meta-analysis of a gene for acute muscle
const renderMetaAnalysisData = () => {
if (geneAanlysisDataArray && geneAanlysisDataArray.length) {
return (
<div className="meta-analysis-data-content d-flex align-items-start">
<div className="col meta-analysis-data-gene-acute-muscle">
<div className="table-responsive meta-analysis-data-table-wrapper">
<table className="table table-sm table-striped metaAnalysisAcuteMuscleGeneTable">
<thead className="thead-dark">
{renderMetaAnalysisTableHead()}
</thead>
<tbody>
{renderMetaAnalysisTableRows(geneAanlysisDataArray)}
</tbody>
</table>
</div>
<div className="note-comment d-flex align-items-center text-secondary">
<span className="material-icons">info</span>
<span>A cohort can have more than a single data point in a time window.</span>
</div>
</div>
<div className="col meta-analysis-forest-plot">
<img src={plotMapping[gene.toUpperCase()]} alt={gene.toUpperCase()} />
</div>
</div>
);
}

return null;
};

return (
<div className="human-gene-meta-analysis">
<div className="alert alert-warning alert-dismissible fade show warning-note d-flex align-items-center" role="alert">
<span className="material-icons">info</span>
<span className="warning-note-text">
The following analyses use existing published data sets. They
do not represent the complete meta-analysis data set.
</span>
<button type="button" className="close" data-dismiss="alert" aria-label="Close">
<span aria-hidden="true">&times;</span>
</button>
</div>
<div className="d-flex justify-content-center meta-analysis-content-container">
{/* gene search */}
<div className="card gene-search-container col-12 col-md-3">
<div className="card-body">
<h6 className="card-title">Select a gene:</h6>
<div className="gene-search-content">
<form id="metaAnalysisGeneSearchForm" name="metaAnalysisGeneSearchForm">
<div className="input-group mb-3">
<input
type="text"
className="form-control"
value={inputValue}
placeholder="Gene symbol"
aria-label="Gene symbol"
aria-describedby="gene-search-btn"
onChange={(e) => { e.preventDefault(); setInputValue(e.target.value); }}
/>
<div className="input-group-append">
<button
type="button"
id="gene-search-btn"
className="btn btn-primary"
onClick={(e) => { e.preventDefault(); setGene(inputValue); }}
>
<i className="material-icons">search</i>
</button>
</div>
</div>
</form>
</div>
{renderGeneInfo()}
</div>
</div>
{/* meta-analysis acute muscle data */}
<div className="col meta-analysis-data-container">
<h5>{`Meta-Analysis${geneObj && geneObj.Symbol ? ` of ${geneObj.Symbol.toUpperCase()}` : ''} for Acute Muscle`}</h5>
{renderMetaAnalysisData()}
</div>
</div>
</div>
);
}

export default HumanGeneMetaAnalysis;
Loading

0 comments on commit 9ac940a

Please sign in to comment.