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

PXP-2464 fix/homepage chart REST API #476

Merged
merged 12 commits into from
Mar 1, 2019
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
1 change: 1 addition & 0 deletions .eslintrc.js
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,7 @@ module.exports = {
"data/getTexts.js",
"data/gqlSetup.js",
"src/SessionMonitor/index.js",
"src/Index/utils.js",
],
"rules": {
"no-console": "off" // for logging errors
Expand Down
14 changes: 14 additions & 0 deletions data/config/default.json
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,20 @@
"link": "/submission",
"label": "Submit data"
}
],
"homepageChartNodes": [
{
"node": "case",
"name": "Cases"
},
{
"node": "experiment",
"name": "Experiments"
},
{
"node": "aliquot",
"name": "Aliquots"
}
]
},
"navigation": {
Expand Down
19 changes: 18 additions & 1 deletion data/config/ndh.json
Original file line number Diff line number Diff line change
Expand Up @@ -121,7 +121,24 @@
"text": "The website combines government datasets from 3 divisions of NIAID to create clean, easy to navigate visualizations for data-driven discovery within Allergy and Infectious Diseases.",
"contact": "If you have any questions about access or the registration process, please contact ",
"email": "[email protected]"
}
},
"footerLogos": [
{
"src": "/src/img/gen3.png",
"href": "https://ctds.uchicago.edu/gen3",
"alt": "Gen3 Data Commons"
},
{
"src": "/src/img/createdby.png",
"href": "https://ctds.uchicago.edu/",
"alt": "Center for Translational Data Science at the University of Chicago"
},
{
"src": "/src/img/sponsors/niaid.png",
"href": "https://niaid.bionimbus.org",
"alt": "NIAID Data Hub"
}
]
},
"featureFlags": {
"explorer": true
Expand Down
10 changes: 5 additions & 5 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

4 changes: 2 additions & 2 deletions src/Homepage/page.jsx
Original file line number Diff line number Diff line change
@@ -1,12 +1,12 @@
import React from 'react';
import { ReduxProjectDashboard, ReduxTransaction } from './reduxer';
import getProjectsList from '../Index/relayer';
import getProjectNodeCounts from '../Index/utils';
import getTransactionList from './relayer';

class HomePage extends React.Component {
constructor(props) {
super(props);
getProjectsList();
getProjectNodeCounts();
getTransactionList();
}

Expand Down
76 changes: 75 additions & 1 deletion src/Homepage/reducers.js
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
import { components } from '../params';

const homepage = (state = {}, action) => {
switch (action.type) {
case 'RECEIVE_PROJECT_LIST': {
Expand All @@ -15,7 +17,13 @@ const homepage = (state = {}, action) => {
);
const lastestListUpdating = Date.now();
// const { error, ...state } = state;
return { ...state, projectsByName, summaryCounts, lastestListUpdating };
return {
...state,
projectsByName,
summaryCounts,
lastestListUpdating,
countNames: components.charts.indexChartNames,
};
}
case 'RECEIVE_PROJECT_DETAIL': {
const projectsByName = Object.assign({}, state.projectsByName || {});
Expand All @@ -29,6 +37,72 @@ const homepage = (state = {}, action) => {
case 'RECEIVE_RELAY_FAIL': {
return { ...state, error: action.data };
}
case 'RECEIVE_PROJECT_NODE_DATASETS': {
const { projectNodeCounts, homepageChartNodes, fileNodes } = action;
const nodesForIndexChart = homepageChartNodes.map(item => item.node);

// adding counts by node
const summaryCounts = nodesForIndexChart.reduce((acc, curNode, index) => {
Object.keys(projectNodeCounts).forEach((proj) => {
if (projectNodeCounts[proj][curNode]) {
acc[index] += projectNodeCounts[proj][curNode];
}
});
return acc;
}, nodesForIndexChart.map(() => 0));

// keep previous design: if less than 4 nodes, calculate all files number
if (nodesForIndexChart.length < 4) {
// add counts for all file type nodes, as the last count
const fileCount = fileNodes.reduce((acc, fileNode) => {
let newAcc = acc;
Object.keys(projectNodeCounts).forEach((proj) => {
if (projectNodeCounts[proj][fileNode]) {
newAcc += projectNodeCounts[proj][fileNode];
}
});
return newAcc;
}, 0);
summaryCounts.push(fileCount);
}

// constructing projct counts for index bar chart
const projectsByName = {};
Object.keys(projectNodeCounts).forEach((proj) => {
let code = proj;
const projCodeIndex = proj.indexOf('-');
if (projCodeIndex !== -1) {
code = proj.substring(projCodeIndex + 1);
}
let counts = 0;
if (projectNodeCounts[proj]) {
counts = nodesForIndexChart.map(node => projectNodeCounts[proj][node]);
}

if (nodesForIndexChart.length < 4) {
const fileCountsForProj = fileNodes.reduce((acc, fileNode) => {
let newAcc = acc;
if (projectNodeCounts[proj][fileNode]) {
newAcc += projectNodeCounts[proj][fileNode];
}
return newAcc;
}, 0);
counts.push(fileCountsForProj);
}

projectsByName[proj] = {
code,
counts,
name: proj,
};
});

const countNames = homepageChartNodes.map(item => item.name);
if (countNames.length < 4) {
countNames.push('Files');
}
return { ...state, projectsByName, summaryCounts, countNames };
}
default:
return state;
}
Expand Down
17 changes: 13 additions & 4 deletions src/Index/page.jsx
Original file line number Diff line number Diff line change
@@ -1,15 +1,20 @@
import React from 'react';
import PropTypes from 'prop-types';
import Introduction from '../components/Introduction';
import { ReduxIndexButtonBar, ReduxIndexBarChart } from './reduxer';
import dictIcons from '../img/icons';
import { components } from '../params';
import getProjectsList from './relayer';
import getProjectNodeCounts from './utils';
import './page.less';

class IndexPageComponent extends React.Component {
constructor(props) {
super(props);
getProjectsList();
componentDidMount() {
getProjectNodeCounts((res) => {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think that this should go in componentDidMount, not the constructor.
https://reactjs.org/docs/react-component.html#constructor

// If Peregrine returns unauthorized, need to redirect to `/login` page
if (res.needLogin) {
this.props.history.push('/login');
}
});
}

render() {
Expand All @@ -25,6 +30,10 @@ class IndexPageComponent extends React.Component {
}
}

IndexPageComponent.propTypes = {
history: PropTypes.object.isRequired,
};

const IndexPage = IndexPageComponent;

export default IndexPage;
5 changes: 4 additions & 1 deletion src/Index/reduxer.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,10 @@ export const ReduxIndexBarChart = (() => {
const projectList = Object.values(
state.homepage.projectsByName,
).sort(sortCompare);
return { projectList, countNames: components.charts.indexChartNames };
return {
projectList,
countNames: state.homepage.countNames,
};
}
return {};
};
Expand Down
63 changes: 63 additions & 0 deletions src/Index/utils.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
import _ from 'underscore';
import { fetchWithCreds } from '../actions';
import { homepageChartNodes, datasetUrl } from '../localconf';
import getReduxStore from '../reduxStore';
import getProjectsList from './relayer';

const updateRedux = async projectNodeCounts => getReduxStore().then(
(store) => {
store.dispatch({
type: 'RECEIVE_PROJECT_NODE_DATASETS',
projectNodeCounts,
homepageChartNodes,
fileNodes: store.getState().submission.file_nodes,
});
},
(err) => {
console.error('WARNING: failed to load redux store', err);
return 'ERR';
},
);

const getProjectNodeCounts = async (callback) => {
const resultStatus = { needLogin: false };
if (typeof homepageChartNodes === 'undefined') {
getProjectsList();
callback(resultStatus);
return;
}

const store = await getReduxStore();
const fileNodes = store.getState().submission.file_nodes;
const nodesForIndexChart = homepageChartNodes.map(item => item.node);
const nodesToRequest = _.union(fileNodes, nodesForIndexChart);
const url = `${datasetUrl}?nodes=${nodesToRequest.join(',')}`;

fetchWithCreds({
path: url,
}).then((res) => {
switch (res.status) {
case 200:
updateRedux(res.data);
callback(resultStatus);
break;
case 404:
// Shouldn't happen, this means peregrine datasets endpoint not enabled
console.error(`REST endpoint ${datasetUrl} not enabled in Peregrine yet.`);
callback(resultStatus);
break;
case 401:
case 403:
resultStatus.needLogin = true;
callback(resultStatus);
break;
default:
break;
}
})
.catch((err) => {
console.log(err);
});
};

export default getProjectNodeCounts;
14 changes: 12 additions & 2 deletions src/Login/Login.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import React from 'react';
import querystring from 'querystring';
import PropTypes from 'prop-types'; // see https://github.com/facebook/prop-types#prop-types

import { basename } from '../localconf';
import { basename, loginPath } from '../localconf';
import SlidingWindow from '../components/SlidingWindow';
import './Login.less';

Expand All @@ -12,12 +12,22 @@ class Login extends React.Component {
static propTypes = {
providers: PropTypes.arrayOf(
PropTypes.objectOf(PropTypes.any),
).isRequired,
),
location: PropTypes.object.isRequired,
dictIcons: PropTypes.object.isRequired,
data: PropTypes.object.isRequired,
};

static defaultProps = {
providers: [
{
id: 'google',
name: 'Google OAuth',
url: `${loginPath}google/`,
},
],
};

constructor(props) {
super(props);
this.state = getInitialState(window.innerHeight - 221);
Expand Down
Loading