From b7a989c190164a13336109d9331676e60d2a4e19 Mon Sep 17 00:00:00 2001 From: Shubham Singhm Date: Sat, 28 Mar 2020 20:49:18 +0530 Subject: [PATCH 1/7] Click filter added to legends | Network vis and side panel [WIP] --- components/NetworkMap/index.js | 34 +++++++++++++++++++++++---- components/NetworkMapLegend/index.js | 35 ++++++++++++++++++++-------- components/Redux/actionTypes.js | 3 ++- components/Redux/actions.js | 11 ++++++++- components/Redux/reducers.js | 7 +++++- 5 files changed, 73 insertions(+), 17 deletions(-) diff --git a/components/NetworkMap/index.js b/components/NetworkMap/index.js index aa37fe3..b719cc3 100644 --- a/components/NetworkMap/index.js +++ b/components/NetworkMap/index.js @@ -17,6 +17,7 @@ const NetworkMap = ({ selectPatient, height, width, + legendFilter }) => { const graphRef = useRef() @@ -25,6 +26,7 @@ const NetworkMap = ({ searchTerm: state.searchTerm, selected: state.patient })) + const [rawResponseData, setRawResponseData] = useState(null) useEffect(() => { fetch('https://api.rootnet.in/covid19-in/unofficial/covid19india.org', { @@ -34,6 +36,7 @@ const NetworkMap = ({ }) .then(resp => resp.json()) .then(res => { + setRawResponseData(res.data.rawPatientData) updateGraph(rowsToGraph(res.data.rawPatientData)) updatePatients(normalize(res.data.rawPatientData)) updateLastRefreshed(res.data.lastRefreshed) @@ -42,9 +45,10 @@ const NetworkMap = ({ .catch(err => console.log('error', err)) }, [isLoading]) + // This effect handles zoom behaviour when a patient node is clicked useEffect(() => { // TODO: Figure out a way to make this do-able with patient Id search - if (graphRef.current && selected.coords) { // Make sure the ref is ready + if (graphRef.current && selected && selected.coords) { // Make sure the ref is ready const moveParams = { position: selected.coords, scale: 1.5, @@ -58,6 +62,7 @@ const NetworkMap = ({ } }, [selected]) + // This effect handles zoom behaviour when search term is changed useEffect(() => { // TODO: Add search by age, district, etc. if (graphRef.current && searchTerm) { // Make sure the ref is ready @@ -65,6 +70,7 @@ const NetworkMap = ({ const nodeKey = letterToCode(`P${searchTerm}`) const coordsMap = graphRef.current.Network.getPositions([nodeKey]) graphRef.current.Network.selectNodes([nodeKey]) + console.log("SELECT PATIENT IN SEARCH TERM EFFECT") selectPatient({ id: nodeKey, coords: coordsMap[nodeKey] }) } catch (e) { // None found. TODO: Add a UI response @@ -72,6 +78,24 @@ const NetworkMap = ({ } }, [searchTerm]) + + // THis effect handles filtering when a legend item is clicked + useEffect(() => { + console.log('legend filter in neterok map component: ', legendFilter) + if (graph) { + console.log('CURRENT GRAPH: ', graph) + let filteredResult = rawResponseData.filter(item => item.status === legendFilter) + updateGraph(rowsToGraph(filteredResult)) + updatePatients(normalize(filteredResult)) + console.log('FILTERED RESULT: ', filteredResult) + console.log('patientID: ', filteredResult[0].patientId) + // const nodeKey = letterToCode(`P${filteredResult[0].patientId}`) + // const coordsMap = graphRef.current.Network.getPositions([nodeKey]) + // graphRef.current.Network.selectNodes([nodeKey]) + // selectPatient({ id: nodeKey, coords: coordsMap[nodeKey] }) + } + }, [legendFilter]) + const options = { layout: { hierarchical: false, @@ -102,6 +126,7 @@ const NetworkMap = ({ case 'patient': // As per the vis.js API, event.pointer.canvas points to the selected node within the canvas // which in our case is the patient. Inject this into the update logic. + console.log("SELECT PATIENT IN SELECT EVENT") selectPatient({ id: selectedNode.id, coords: event.pointer.canvas }) break case 'city': @@ -115,7 +140,8 @@ const NetworkMap = ({
{isLoading ? null : ( <> - + +
{legendFilter}
@@ -125,8 +151,8 @@ const NetworkMap = ({ } const mapStateToProps = state => { - let { graph, searchTerm, filter } = state - return { graph, searchTerm, filter } + let { graph, searchTerm, filter, legendFilter } = state + return { graph, searchTerm, filter, legendFilter } } export default connect(mapStateToProps, { diff --git a/components/NetworkMapLegend/index.js b/components/NetworkMapLegend/index.js index d8fb572..e2c385e 100644 --- a/components/NetworkMapLegend/index.js +++ b/components/NetworkMapLegend/index.js @@ -9,7 +9,8 @@ import { plane_abroad_node, plane_local_node, } from '../../images/index' - +import { connect, useSelector } from 'react-redux' +import { setLegendFilter } from '../Redux/actions' const LegendContainer = styled.div` position: absolute; @@ -45,7 +46,7 @@ const ImageContainer = styled.div` display: flex; align-items: center; margin-bottom: 0.5rem; - + cursor: pointer; & :last-of-type { margin-bottom: 0; @@ -62,36 +63,46 @@ const Label = styled.span` } ` -const NetworkMapLegend = ({ currentFilter }) => { +const NetworkMapLegend = ({ currentGlobalFilter, setLegendFilter, legendFilter }) => { + + + + const legendClickHandler = (term) => { + console.log('term in legend component: ', term); + if (legendFilter !== term) { + setLegendFilter(term); + } + } + return ( - + legendClickHandler('Recovered')}> - + legendClickHandler('Hospitalized')}> - + legendClickHandler('Deceased')}> - {['State', 'City'].includes(currentFilter) ? + {['State', 'City'].includes(currentGlobalFilter) ? : null } - {currentFilter === 'City' ? + {currentGlobalFilter === 'City' ? : null } - {currentFilter === 'Travel' ? + {currentGlobalFilter === 'Travel' ? <> @@ -108,5 +119,9 @@ const NetworkMapLegend = ({ currentFilter }) => { ) } +const mapStateToProps = state => { + let { legendFilter } = state + return { legendFilter } +} -export default NetworkMapLegend; +export default connect(mapStateToProps, { setLegendFilter })(NetworkMapLegend); diff --git a/components/Redux/actionTypes.js b/components/Redux/actionTypes.js index cd0bbac..45376e5 100644 --- a/components/Redux/actionTypes.js +++ b/components/Redux/actionTypes.js @@ -13,5 +13,6 @@ export default { UPDATE_LAST_REFRESHED: 'UPDATE_LAST_REFRESHED', SELECT_PATIENT: 'SELECT_PATIENT', SELECT_FILTER: 'SELECT_FILTER', - SEARCH: 'SEARCH' + SEARCH: 'SEARCH', + LEGEND_FILTER: 'LEGEND_FILTER' } diff --git a/components/Redux/actions.js b/components/Redux/actions.js index fec439c..79ba35d 100644 --- a/components/Redux/actions.js +++ b/components/Redux/actions.js @@ -64,5 +64,14 @@ const setSearchTerm = term => (dispatch, getState) => { }) } +const setLegendFilter = term => (dispatch, getState) => { + dispatch({ + type: actionTypes.LEGEND_FILTER, + payload: { + term + } + }) +} + // Export the actions. -export { updateGraph, updatePatients, updateLastRefreshed, selectPatient, selectFilter, setSearchTerm } +export { updateGraph, updatePatients, updateLastRefreshed, selectPatient, selectFilter, setSearchTerm, setLegendFilter } diff --git a/components/Redux/reducers.js b/components/Redux/reducers.js index 7c6954f..240bc33 100644 --- a/components/Redux/reducers.js +++ b/components/Redux/reducers.js @@ -13,7 +13,8 @@ const initialState = { selected: null, graph: null, patients: null, - searchTerm: '' + searchTerm: '', + legendFilter: null } // Export the Device Reducer. @@ -49,6 +50,10 @@ export default (state = initialState, action) => { } return existingPatient ? { ...state, patient } : state } + case actionTypes.LEGEND_FILTER: { + const { term } = action.payload + return { ...state, legendFilter: term } + } default: return state } From db9048dbff6e1e228bbd3f95353bcbca99000cdd Mon Sep 17 00:00:00 2001 From: Shubham Singhm Date: Tue, 31 Mar 2020 03:26:17 +0530 Subject: [PATCH 2/7] Added fitlering functionality on the legends | removal/resetting fitler WIP --- components/FilterPanel/index.js | 7 +- components/NetworkMap/index.js | 31 ++----- components/NetworkMapLegend/index.js | 118 ++++++++++++++++----------- components/Redux/actionTypes.js | 3 +- components/Redux/actions.js | 14 +++- components/Redux/reducers.js | 5 ++ components/SidePanel/datagrid.js | 6 +- components/SidePanel/index.js | 2 +- util/filters/index.js | 2 + util/filters/patientFilter.js | 108 ++++++++++++++++++++++++ util/filters/state.js | 2 + 11 files changed, 219 insertions(+), 79 deletions(-) create mode 100644 util/filters/patientFilter.js diff --git a/components/FilterPanel/index.js b/components/FilterPanel/index.js index a1e0896..f6cfc25 100644 --- a/components/FilterPanel/index.js +++ b/components/FilterPanel/index.js @@ -105,10 +105,10 @@ const FilterPanel = ({ const changeGraph = name => { // console.log('Changegraph', graph, patients.byId) - let currentFilter = _.find(filters, function(o) { + let currentFilter = _.find(filters, function (o) { return o.name === filter }) - let choosenFilter = _.find(filters, function(o) { + let choosenFilter = _.find(filters, function (o) { return o.name === name }) @@ -134,8 +134,9 @@ const FilterPanel = ({ Cluster Filter - {filters.map(filterItem => ( + {filters.map((filterItem, index) => ( changeGraph(filterItem.name)} selected={filter === filterItem.name ? true : false} diff --git a/components/NetworkMap/index.js b/components/NetworkMap/index.js index b719cc3..6c366a3 100644 --- a/components/NetworkMap/index.js +++ b/components/NetworkMap/index.js @@ -1,7 +1,7 @@ import React, { useState, useEffect, useRef } from 'react' import Graph from 'react-graph-vis' import { connect, useSelector } from 'react-redux' -import { updateGraph, updatePatients, updateLastRefreshed, selectPatient } from '../Redux/actions' +import { updateGraph, updatePatients, updateLastRefreshed, selectPatient, updateSidePanelPatient, updateLegendFilter } from '../Redux/actions' import { rowsToGraph, letterToCode } from '../../util/parse' import normalize from '../../util/normalize' @@ -9,7 +9,6 @@ import DatePicker from '../DatePicker' import NetworkMapLegend from '../NetworkMapLegend' const NetworkMap = ({ - filter, graph, updateGraph, updatePatients, @@ -17,7 +16,7 @@ const NetworkMap = ({ selectPatient, height, width, - legendFilter + legendFilter, }) => { const graphRef = useRef() @@ -26,7 +25,6 @@ const NetworkMap = ({ searchTerm: state.searchTerm, selected: state.patient })) - const [rawResponseData, setRawResponseData] = useState(null) useEffect(() => { fetch('https://api.rootnet.in/covid19-in/unofficial/covid19india.org', { @@ -36,7 +34,6 @@ const NetworkMap = ({ }) .then(resp => resp.json()) .then(res => { - setRawResponseData(res.data.rawPatientData) updateGraph(rowsToGraph(res.data.rawPatientData)) updatePatients(normalize(res.data.rawPatientData)) updateLastRefreshed(res.data.lastRefreshed) @@ -70,7 +67,6 @@ const NetworkMap = ({ const nodeKey = letterToCode(`P${searchTerm}`) const coordsMap = graphRef.current.Network.getPositions([nodeKey]) graphRef.current.Network.selectNodes([nodeKey]) - console.log("SELECT PATIENT IN SEARCH TERM EFFECT") selectPatient({ id: nodeKey, coords: coordsMap[nodeKey] }) } catch (e) { // None found. TODO: Add a UI response @@ -79,20 +75,10 @@ const NetworkMap = ({ }, [searchTerm]) - // THis effect handles filtering when a legend item is clicked useEffect(() => { - console.log('legend filter in neterok map component: ', legendFilter) - if (graph) { - console.log('CURRENT GRAPH: ', graph) - let filteredResult = rawResponseData.filter(item => item.status === legendFilter) - updateGraph(rowsToGraph(filteredResult)) - updatePatients(normalize(filteredResult)) - console.log('FILTERED RESULT: ', filteredResult) - console.log('patientID: ', filteredResult[0].patientId) - // const nodeKey = letterToCode(`P${filteredResult[0].patientId}`) - // const coordsMap = graphRef.current.Network.getPositions([nodeKey]) - // graphRef.current.Network.selectNodes([nodeKey]) - // selectPatient({ id: nodeKey, coords: coordsMap[nodeKey] }) + if (graphRef.current) { + console.log('redrawing!!!!!!!!!!') + graphRef.current.Network.fit() } }, [legendFilter]) @@ -126,7 +112,6 @@ const NetworkMap = ({ case 'patient': // As per the vis.js API, event.pointer.canvas points to the selected node within the canvas // which in our case is the patient. Inject this into the update logic. - console.log("SELECT PATIENT IN SELECT EVENT") selectPatient({ id: selectedNode.id, coords: event.pointer.canvas }) break case 'city': @@ -140,7 +125,7 @@ const NetworkMap = ({
{isLoading ? null : ( <> - +
{legendFilter}
@@ -151,8 +136,8 @@ const NetworkMap = ({ } const mapStateToProps = state => { - let { graph, searchTerm, filter, legendFilter } = state - return { graph, searchTerm, filter, legendFilter } + let { graph, searchTerm, legendFilter } = state + return { graph, searchTerm, legendFilter } } export default connect(mapStateToProps, { diff --git a/components/NetworkMapLegend/index.js b/components/NetworkMapLegend/index.js index e2c385e..6c96a80 100644 --- a/components/NetworkMapLegend/index.js +++ b/components/NetworkMapLegend/index.js @@ -10,7 +10,8 @@ import { plane_local_node, } from '../../images/index' import { connect, useSelector } from 'react-redux' -import { setLegendFilter } from '../Redux/actions' +import { updateLegendFilter, updateGraph, updateSidePanelPatient } from '../Redux/actions' +import { filterPatients } from '../../util/filters' const LegendContainer = styled.div` position: absolute; @@ -63,65 +64,90 @@ const Label = styled.span` } ` -const NetworkMapLegend = ({ currentGlobalFilter, setLegendFilter, legendFilter }) => { +const patientTypes = [ + { + label: 'Recovered', + image: male_cured, + forGlobalFilters: ['All'] + }, + { + label: 'Hospitalized', + image: male_hosp, + forGlobalFilters: ['All'] + }, + { + label: 'Deceased', + image: male_dead, + forGlobalFilters: ['All'] + }, + { + label: 'State', + image: state_node, + forGlobalFilters: ['State', 'City'] + }, + { + label: 'City', + image: city_node, + forGlobalFilters: ['City'] + }, + { + label: 'Domestic', + image: plane_local_node, + forGlobalFilters: ['Travel'] + }, + { + label: 'International', + image: plane_abroad_node, + forGlobalFilters: ['Travel'] + }, +] + + + + + + + +const NetworkMapLegend = ({ globalFilter, updateLegendFilter, updateGraph, updateSidePanelPatient, legendFilter, graph, patients }) => { const legendClickHandler = (term) => { - console.log('term in legend component: ', term); if (legendFilter !== term) { - setLegendFilter(term); + + let newGraph = filterPatients(graph, patients.byId, term, globalFilter) + updateGraph(newGraph) + console.log('NEW GRAPH: ', newGraph); + + updateLegendFilter(term) + // update side panel patient to show the correct patient(patient should be one from the visible list of nodes) + updateSidePanelPatient(newGraph.nodes[0].id) + + } + } + + const patientLegend = (item) => { + if (item.forGlobalFilters.includes('All') || item.forGlobalFilters.includes(globalFilter)) { + return ( + legendClickHandler(item.label)}> + + + + ) } } + return ( - legendClickHandler('Recovered')}> - - - - legendClickHandler('Hospitalized')}> - - - - legendClickHandler('Deceased')}> - - - - {['State', 'City'].includes(currentGlobalFilter) ? - - - - - : null - } - {currentGlobalFilter === 'City' ? - - - - - : null - } - {currentGlobalFilter === 'Travel' ? - <> - - - - - - - - - - : null - } + {patientTypes.map(item => patientLegend(item))} ) } const mapStateToProps = state => { - let { legendFilter } = state - return { legendFilter } + let { legendFilter, graph, patients, filter } = state + return { legendFilter, graph, patients, globalFilter: filter } } -export default connect(mapStateToProps, { setLegendFilter })(NetworkMapLegend); +export default connect(mapStateToProps, { updateLegendFilter, updateGraph, updateSidePanelPatient })(NetworkMapLegend); diff --git a/components/Redux/actionTypes.js b/components/Redux/actionTypes.js index 45376e5..de69a51 100644 --- a/components/Redux/actionTypes.js +++ b/components/Redux/actionTypes.js @@ -14,5 +14,6 @@ export default { SELECT_PATIENT: 'SELECT_PATIENT', SELECT_FILTER: 'SELECT_FILTER', SEARCH: 'SEARCH', - LEGEND_FILTER: 'LEGEND_FILTER' + LEGEND_FILTER: 'LEGEND_FILTER', + UPDATE_SIDEPANEL_PATIENT: 'UPDATE_SIDEPANEL_PATIENT' } diff --git a/components/Redux/actions.js b/components/Redux/actions.js index 79ba35d..253a424 100644 --- a/components/Redux/actions.js +++ b/components/Redux/actions.js @@ -64,7 +64,7 @@ const setSearchTerm = term => (dispatch, getState) => { }) } -const setLegendFilter = term => (dispatch, getState) => { +const updateLegendFilter = term => (dispatch, getState) => { dispatch({ type: actionTypes.LEGEND_FILTER, payload: { @@ -73,5 +73,15 @@ const setLegendFilter = term => (dispatch, getState) => { }) } +const updateSidePanelPatient = patientId => (dispatch, getState) => { + console.log('PATIENT ID IN SIDEPANEL: ', patientId) + dispatch({ + type: actionTypes.UPDATE_SIDEPANEL_PATIENT, + payload: { + patientId + } + }) +} + // Export the actions. -export { updateGraph, updatePatients, updateLastRefreshed, selectPatient, selectFilter, setSearchTerm, setLegendFilter } +export { updateGraph, updatePatients, updateLastRefreshed, selectPatient, selectFilter, setSearchTerm, updateLegendFilter, updateSidePanelPatient } diff --git a/components/Redux/reducers.js b/components/Redux/reducers.js index 240bc33..48f198f 100644 --- a/components/Redux/reducers.js +++ b/components/Redux/reducers.js @@ -54,6 +54,11 @@ export default (state = initialState, action) => { const { term } = action.payload return { ...state, legendFilter: term } } + case actionTypes.UPDATE_SIDEPANEL_PATIENT: { + const { patients } = state + const { patientId } = action.payload + return { ...state, patient: patients.byId[patientId] } + } default: return state } diff --git a/components/SidePanel/datagrid.js b/components/SidePanel/datagrid.js index 7a14702..ffbbe18 100644 --- a/components/SidePanel/datagrid.js +++ b/components/SidePanel/datagrid.js @@ -74,15 +74,15 @@ export default function DataGrid(patient) { {sources ? sources.map((source, i) => ( -
-
+
+
{i + 1}. 
{_.truncate(source, { length: 40, diff --git a/components/SidePanel/index.js b/components/SidePanel/index.js index 16b9503..41ec6c3 100644 --- a/components/SidePanel/index.js +++ b/components/SidePanel/index.js @@ -16,7 +16,7 @@ const SidePanel = ({ patient, lastRefreshed }) => { return ( {patient ?
: null} - {patient ? : null} + {patient ? : NULL PATIENT} ) } diff --git a/util/filters/index.js b/util/filters/index.js index 7d0686d..8af6520 100644 --- a/util/filters/index.js +++ b/util/filters/index.js @@ -1,6 +1,7 @@ import { addStates, removeStates } from './state' import { addCities, removeCities } from './city' import { addTravel, removeTravel } from './travel' +import { filterPatients } from './patientFilter' export { addStates, @@ -9,4 +10,5 @@ export { removeCities, addTravel, removeTravel, + filterPatients } diff --git a/util/filters/patientFilter.js b/util/filters/patientFilter.js new file mode 100644 index 0000000..d9adc18 --- /dev/null +++ b/util/filters/patientFilter.js @@ -0,0 +1,108 @@ +import hash from 'object-hash' +import dotProp from 'dot-prop-immutable' +import { getIcon, letterToCode, codeToLetter } from '../parse' + + +export const filterPatients = (graph, patients, status, globalFilter) => { + + + console.log('GRAPH IN PATIENT FILTER: ', graph) + + // let filteredPatients = [] + + + let newGraph = { + nodes: [], + edges: [] + } + + for (let patientId in patients) { + // console.log('PATIENT ID: ', patientId) + + if (patients[patientId].status === status) { + let node = { + id: letterToCode(patientId), + label: codeToLetter(patientId), + shape: 'image', + image: getIcon(patients[patientId]), + group: 'patient' + } + + + newGraph = dotProp.set(newGraph, 'nodes', list => [...list, node]) + + if (patients[patientId].contractedFrom) { + let edge = { + from: letterToCode(patients[patientId].contractedFrom), + to: patients[patientId].patientId, + } + + newGraph = dotProp.set(newGraph, 'edges', list => [...list, edge]) + } + + } + + } + + + console.log('gloabl filter in patient filter: ', globalFilter) + + + if (['State', 'City', 'Travel'].includes(globalFilter)) { + graph.nodes.forEach(node => { + if (node.group !== 'patient') { + console.log('non patietnt node found: ', node) + newGraph = dotProp.set(newGraph, 'nodes', list => [...list, node]) + } else { + // let kepMap ={ + // 'State': 'state', + // } + let edge = { + from: hash(patients[node.id].state), + to: patients[node.id].patientId, + length: 250, + dashes: true, + arrows: { + to: { + enabled: false, + }, + }, + color: { opacity: '0.3' }, + } + newGraph = dotProp.set(newGraph, 'edges', list => [...list, edge]) + } + }) + + // graph.nodes.forEach(node => { + + // }) + + } + + + // graph['nodes'].forEach(node => { + // if (node.group == 'patient' && patients[node.id]['status'] == status) { + // newGraph = dotProp.set(newGraph, 'nodes', list => [...list, node]) + + + // if (patients[node.id].contractedFrom) { + // let edge = { + // from: letterToCode(patients[node.id].contractedFrom), + // to: patients[node.id].patientId, + // } + // newGraph = dotProp.set(newGraph, 'edges', list => [...list, edge]) + // } + // } else if (node.group === 'state') { + // newGraph = dotProp.set(newGraph, 'nodes', list => [...list, node]) + // } + + // }) + + + + + console.log('GRAPH IN FITLER FUNCTION: ', newGraph) + + + return newGraph; +} \ No newline at end of file diff --git a/util/filters/state.js b/util/filters/state.js index 0205958..b515f79 100644 --- a/util/filters/state.js +++ b/util/filters/state.js @@ -19,6 +19,7 @@ export const addStates = (graph, patients) => { size: 40, shape: 'image', image: state_node, + group: 'state' } graph = dotProp.set(graph, 'nodes', list => [...list, node]) } @@ -56,6 +57,7 @@ export const removeStates = (graph, patients) => { size: 40, shape: 'image', image: state_node, + group: 'state' } let index = _.findIndex(dotProp.get(graph, 'nodes'), function(o) { return o.id == node.id From 3447a2f657bc095de6dd36475eda689997e57561 Mon Sep 17 00:00:00 2001 From: Shubham Singhm Date: Tue, 31 Mar 2020 03:48:33 +0530 Subject: [PATCH 3/7] Restricted legend filter for domestic and international labels --- components/NetworkMapLegend/index.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/components/NetworkMapLegend/index.js b/components/NetworkMapLegend/index.js index 6c96a80..de4f374 100644 --- a/components/NetworkMapLegend/index.js +++ b/components/NetworkMapLegend/index.js @@ -113,7 +113,7 @@ const NetworkMapLegend = ({ globalFilter, updateLegendFilter, updateGraph, updat const legendClickHandler = (term) => { - if (legendFilter !== term) { + if (legendFilter !== term && !['Domestic', 'International'].includes(term)) { let newGraph = filterPatients(graph, patients.byId, term, globalFilter) updateGraph(newGraph) From ebc3f576f07fe54507d2e2b2055ff68812ba3f08 Mon Sep 17 00:00:00 2001 From: Shubham Singhm Date: Tue, 31 Mar 2020 03:51:35 +0530 Subject: [PATCH 4/7] Restricted legend filter for cities and states --- components/NetworkMapLegend/index.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/components/NetworkMapLegend/index.js b/components/NetworkMapLegend/index.js index de4f374..beb65c1 100644 --- a/components/NetworkMapLegend/index.js +++ b/components/NetworkMapLegend/index.js @@ -113,7 +113,7 @@ const NetworkMapLegend = ({ globalFilter, updateLegendFilter, updateGraph, updat const legendClickHandler = (term) => { - if (legendFilter !== term && !['Domestic', 'International'].includes(term)) { + if (legendFilter !== term && ['Recovered', 'Hospitalized', 'Deceased'].includes(term)) { let newGraph = filterPatients(graph, patients.byId, term, globalFilter) updateGraph(newGraph) From e27a73b86f9741d5bf1f6870286db247da186d8f Mon Sep 17 00:00:00 2001 From: Shubham Singhm Date: Wed, 1 Apr 2020 01:15:43 +0530 Subject: [PATCH 5/7] Added comments to the patientFitler logic --- util/filters/patientFilter.js | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/util/filters/patientFilter.js b/util/filters/patientFilter.js index f630b32..12ed0b8 100644 --- a/util/filters/patientFilter.js +++ b/util/filters/patientFilter.js @@ -16,9 +16,11 @@ export const filterPatients = (graph, patients, status, globalFilter) => { edges: [] } + // looping through each key in the patients object for (let patientId in patients) { // console.log('PATIENT ID: ', patientId) + // find the correct patients for the selected patient type if (patients[patientId].status === status) { let node = { id: letterToCode(patientId), @@ -28,9 +30,10 @@ export const filterPatients = (graph, patients, status, globalFilter) => { group: 'patient' } - + // adding patients as nodes to the new graph newGraph = dotProp.set(newGraph, 'nodes', list => [...list, node]) + // adding edges for contractedFrom of the fitlered patient if (patients[patientId].contractedFrom) { let edge = { from: letterToCode(patients[patientId].contractedFrom), @@ -44,6 +47,7 @@ export const filterPatients = (graph, patients, status, globalFilter) => { } + // adding the other nodes in the array as per the global filter if (['State', 'City', 'Travel'].includes(globalFilter)) { graph.nodes.forEach(node => { if (node.group !== 'patient') { From b5902ed283d76f4babc2e2a95f7ead66edd5a103 Mon Sep 17 00:00:00 2001 From: Shubham Singhm Date: Wed, 1 Apr 2020 04:39:21 +0530 Subject: [PATCH 6/7] Experimented with setData function of vis.js to set graph data, instead of updating the graph variable | Played around with the graph physics --- components/NetworkMap/index.js | 51 +++++++++++++++++++++++----- components/NetworkMapLegend/index.js | 2 +- util/filters/state.js | 5 ++- util/filters/travel.js | 5 +-- 4 files changed, 51 insertions(+), 12 deletions(-) diff --git a/components/NetworkMap/index.js b/components/NetworkMap/index.js index 72f3cb3..1c2ec0e 100644 --- a/components/NetworkMap/index.js +++ b/components/NetworkMap/index.js @@ -2,7 +2,7 @@ import React, { useState, useEffect, useRef, useMemo } from 'react' import Graph from 'react-graph-vis' import { Tooltip, TooltipArrow, TooltipInner } from 'styled-tooltip-component' import { connect, useSelector } from 'react-redux' -import { updateGraph, updatePatients, updateLastRefreshed, selectPatient, updateStates,updateSidePanelPatient, updateLegendFilter } from '../Redux/actions' +import { updateGraph, updatePatients, updateLastRefreshed, selectPatient, updateStates, updateSidePanelPatient, updateLegendFilter } from '../Redux/actions' import { rowsToGraph, letterToCode } from '../../util/parse' import normalize from '../../util/normalize' @@ -22,6 +22,10 @@ const NetworkMap = ({ }) => { const graphRef = useRef() const [isLoading, setIsLoading] = useState(true) + const [graphData, setGraphData] = useState({ + nodes: [], + edges: [] + }) const [toolTipPosition, setToolTipPosition] = useState(null) const [tooltipContent, setToolTipContent] = useState('') const toolTipVisible = useMemo(() => { @@ -32,7 +36,7 @@ const NetworkMap = ({ selected: state.patient, })) useEffect(() => { - if(!states){ + if (!states) { fetch('https://api.covid19india.org/state_district_wise.json', { cors: 'no-cors', method: 'GET', @@ -40,7 +44,7 @@ const NetworkMap = ({ }) .then(resp => resp.json()) .then(res => { - if(res){ + if (res) { let stateNames = Object.keys(res); updateStates(stateNames); } @@ -97,9 +101,18 @@ const NetworkMap = ({ } }, [searchTerm]) + + useEffect(() => { + // setGraphData(graph) + if (graphRef.current) { + graphRef.current.Network.setData(graph) + } + }, [graph]) + const options = { layout: { hierarchical: false, + clusterThreshold: 1300 }, edges: { color: '#000000', @@ -117,10 +130,22 @@ const NetworkMap = ({ navigationButtons: true, hover: true, }, + physics: { + solver: 'forceAtlas2Based', + forceAtlas2Based: { + avoidOverlap: 1 + } + // stabilization: true, + // barnesHut: { + // // gravitationalConstant: -80000, + // springConstant: 0.001, + // springLength: 200 + // } + }, } const events = { - select: function(event) { + select: function (event) { const selectedNodeId = event.nodes[0] const selectedNode = graph.nodes.find(v => v.id === selectedNodeId) if (selectedNode) { @@ -135,7 +160,7 @@ const NetworkMap = ({ } } }, - hoverNode: function(e) { + hoverNode: function (e) { const { node, event } = e const selectedNode = graph.nodes.find(v => v.id === node) setToolTipContent(selectedNode.label) @@ -144,20 +169,30 @@ const NetworkMap = ({ left: event.pageX, }) }, - blurNode: function(event) { + blurNode: function (event) { setToolTipContent('') setToolTipPosition(null) }, + // stabilizationProgress: function (e) { + // document.getElementById('graph-loader').style.display = 'block' + // }, + // stabilizationIterationsDone: function (e) { + // document.getElementById('graph-loader').style.display = 'none' + // } + } return (
{isLoading ? null : ( <> + {/*
+ Loading.... +
*/} @@ -182,7 +217,7 @@ const NetworkMap = ({ const mapStateToProps = state => { let { graph, searchTerm, states } = state - return { graph, searchTerm, states} + return { graph, searchTerm, states } } export default connect(mapStateToProps, { diff --git a/components/NetworkMapLegend/index.js b/components/NetworkMapLegend/index.js index beb65c1..b8d4d99 100644 --- a/components/NetworkMapLegend/index.js +++ b/components/NetworkMapLegend/index.js @@ -129,7 +129,7 @@ const NetworkMapLegend = ({ globalFilter, updateLegendFilter, updateGraph, updat const patientLegend = (item) => { if (item.forGlobalFilters.includes('All') || item.forGlobalFilters.includes(globalFilter)) { return ( - legendClickHandler(item.label)}> + legendClickHandler(item.label)}> diff --git a/util/filters/state.js b/util/filters/state.js index dabf07c..0cac0f6 100644 --- a/util/filters/state.js +++ b/util/filters/state.js @@ -65,7 +65,10 @@ export const removeStates = (graph, patients, states) => { return o.to == edgeTo && o.from === edgeFrom }) - graph = dotProp.delete(graph, `edges.${edgeIndex}`) + if (edgeIndex !== -1) { + graph = dotProp.delete(graph, `edges.${edgeIndex}`) + } + } return graph diff --git a/util/filters/travel.js b/util/filters/travel.js index 5503574..6005a7e 100644 --- a/util/filters/travel.js +++ b/util/filters/travel.js @@ -107,8 +107,9 @@ export const removeTravel = (graph, patients) => { let edgeIndex = _.findIndex(graph.edges, function(o) { return o.to == edge.to && o.from === edge.from }) - - graph = dotProp.delete(graph, `edges.${edgeIndex}`) + if (edgeIndex !== -1) { + graph = dotProp.delete(graph, `edges.${edgeIndex}`) + } }) } } From 8fe93a7c47888a11d8b979b5ea4acab756654ee4 Mon Sep 17 00:00:00 2001 From: Shubham Singhm Date: Wed, 1 Apr 2020 04:47:09 +0530 Subject: [PATCH 7/7] Removed console.logs --- components/NetworkMapLegend/index.js | 1 - components/Redux/actions.js | 1 - components/SidePanel/index.js | 2 +- util/filters/patientFilter.js | 4 ++-- 4 files changed, 3 insertions(+), 5 deletions(-) diff --git a/components/NetworkMapLegend/index.js b/components/NetworkMapLegend/index.js index b8d4d99..b0d0529 100644 --- a/components/NetworkMapLegend/index.js +++ b/components/NetworkMapLegend/index.js @@ -117,7 +117,6 @@ const NetworkMapLegend = ({ globalFilter, updateLegendFilter, updateGraph, updat let newGraph = filterPatients(graph, patients.byId, term, globalFilter) updateGraph(newGraph) - console.log('NEW GRAPH: ', newGraph); updateLegendFilter(term) // update side panel patient to show the correct patient(patient should be one from the visible list of nodes) diff --git a/components/Redux/actions.js b/components/Redux/actions.js index 8f6513a..18b6140 100644 --- a/components/Redux/actions.js +++ b/components/Redux/actions.js @@ -74,7 +74,6 @@ const updateLegendFilter = term => (dispatch, getState) => { } const updateSidePanelPatient = patientId => (dispatch, getState) => { - console.log('PATIENT ID IN SIDEPANEL: ', patientId) dispatch({ type: actionTypes.UPDATE_SIDEPANEL_PATIENT, payload: { diff --git a/components/SidePanel/index.js b/components/SidePanel/index.js index 41ec6c3..16b9503 100644 --- a/components/SidePanel/index.js +++ b/components/SidePanel/index.js @@ -16,7 +16,7 @@ const SidePanel = ({ patient, lastRefreshed }) => { return ( {patient ?
: null} - {patient ? : NULL PATIENT} + {patient ? : null} ) } diff --git a/util/filters/patientFilter.js b/util/filters/patientFilter.js index 12ed0b8..c1fd67f 100644 --- a/util/filters/patientFilter.js +++ b/util/filters/patientFilter.js @@ -6,7 +6,7 @@ import { getIcon, letterToCode, codeToLetter } from '../parse' export const filterPatients = (graph, patients, status, globalFilter) => { - console.log('GRAPH IN PATIENT FILTER: ', graph) + // console.log('GRAPH IN PATIENT FILTER: ', graph) // let filteredPatients = [] @@ -101,7 +101,7 @@ export const filterPatients = (graph, patients, status, globalFilter) => { - console.log('GRAPH IN FITLER FUNCTION: ', newGraph) + // console.log('GRAPH IN FITLER FUNCTION: ', newGraph) return newGraph;