Merge branch 'master' of gitlab.com:matteodelabre/wikimedica-disease-search
This commit is contained in:
commit
69c96ac537
3
.babelrc
3
.babelrc
|
@ -1,3 +1,4 @@
|
||||||
{
|
{
|
||||||
"presets": ["@babel/preset-env", "@babel/preset-react"]
|
"presets": ["@babel/preset-env", "@babel/preset-react"],
|
||||||
|
"plugins": ["@babel/plugin-transform-runtime"]
|
||||||
}
|
}
|
||||||
|
|
|
@ -9,6 +9,6 @@
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
<div id="root"></div>
|
<div id="root"></div>
|
||||||
<script src="index.js"></script>
|
<script src="src/index.js"></script>
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
import React, {useState} from 'react';
|
import React, {useState} from 'react';
|
||||||
import TermInput from './TermInput.js';
|
import TermInput from './TermInput.js';
|
||||||
import ResultsGraph from './ResultsGraph.js';
|
import SearchResults from './SearchResults.js';
|
||||||
|
|
||||||
const App = () =>
|
const App = () =>
|
||||||
{
|
{
|
||||||
|
@ -9,7 +9,7 @@ const App = () =>
|
||||||
return (
|
return (
|
||||||
<div className="App">
|
<div className="App">
|
||||||
<TermInput terms={terms} setTerms={setTerms} />
|
<TermInput terms={terms} setTerms={setTerms} />
|
||||||
<ResultsGraph />
|
<SearchResults terms={terms} />
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
};
|
};
|
|
@ -191,7 +191,6 @@ const Graph = ({nodes, edges, render}) =>
|
||||||
|
|
||||||
return () =>
|
return () =>
|
||||||
{
|
{
|
||||||
layout.stop();
|
|
||||||
graphParent.current.removeEventListener('mousedown', mouseDown);
|
graphParent.current.removeEventListener('mousedown', mouseDown);
|
||||||
document.body.removeEventListener('mousemove', mouseMove);
|
document.body.removeEventListener('mousemove', mouseMove);
|
||||||
document.body.removeEventListener('mouseup', mouseUp);
|
document.body.removeEventListener('mouseup', mouseUp);
|
|
@ -0,0 +1,13 @@
|
||||||
|
import React from 'react';
|
||||||
|
import Graph from './Graph.js';
|
||||||
|
|
||||||
|
|
||||||
|
const ResultsGraph = () => (
|
||||||
|
<Graph
|
||||||
|
nodes={Object.keys(nodes)}
|
||||||
|
edges={edges}
|
||||||
|
render={id => nodes[id].name}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
|
||||||
|
export default ResultsGraph;
|
|
@ -0,0 +1,37 @@
|
||||||
|
import React, {useState, useEffect} from 'react';
|
||||||
|
import Graph from './Graph.js';
|
||||||
|
import {searchTerms} from './fetch.js';
|
||||||
|
|
||||||
|
const useResults = terms =>
|
||||||
|
{
|
||||||
|
const [results, setResults] = useState({
|
||||||
|
nodes: {},
|
||||||
|
edges: []
|
||||||
|
});
|
||||||
|
|
||||||
|
useEffect(() =>
|
||||||
|
{
|
||||||
|
const fetch = async () =>
|
||||||
|
{
|
||||||
|
setResults(await searchTerms(terms));
|
||||||
|
};
|
||||||
|
|
||||||
|
fetch();
|
||||||
|
}, [terms]);
|
||||||
|
|
||||||
|
return results;
|
||||||
|
};
|
||||||
|
|
||||||
|
const SearchResults = ({terms}) =>
|
||||||
|
{
|
||||||
|
const {nodes, edges} = useResults(terms);
|
||||||
|
return (
|
||||||
|
<Graph
|
||||||
|
nodes={Object.keys(nodes)}
|
||||||
|
edges={edges}
|
||||||
|
render={id => nodes[id].name}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default SearchResults;
|
|
@ -1,6 +1,7 @@
|
||||||
import React, {useState} from 'react';
|
import React, {useState} from 'react';
|
||||||
|
|
||||||
const enterKey = 13;
|
const enterKey = 13;
|
||||||
|
const backspaceKey = 8;
|
||||||
|
|
||||||
const TermInput = ({terms, setTerms}) =>
|
const TermInput = ({terms, setTerms}) =>
|
||||||
{
|
{
|
||||||
|
@ -8,7 +9,7 @@ const TermInput = ({terms, setTerms}) =>
|
||||||
|
|
||||||
const handleKeyDown = ev =>
|
const handleKeyDown = ev =>
|
||||||
{
|
{
|
||||||
if (ev.keyCode === enterKey)
|
if (ev.keyCode === enterKey && value)
|
||||||
{
|
{
|
||||||
ev.preventDefault();
|
ev.preventDefault();
|
||||||
|
|
||||||
|
@ -18,6 +19,14 @@ const TermInput = ({terms, setTerms}) =>
|
||||||
}
|
}
|
||||||
|
|
||||||
setValue('');
|
setValue('');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (ev.keyCode === backspaceKey && !value)
|
||||||
|
{
|
||||||
|
ev.preventDefault();
|
||||||
|
setTerms(terms.slice(0, -1));
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -42,7 +51,7 @@ const TermInput = ({terms, setTerms}) =>
|
||||||
>{term}</span>
|
>{term}</span>
|
||||||
)}
|
)}
|
||||||
<input
|
<input
|
||||||
autofocus="true" type="text" className="TermInput_input"
|
autoFocus={true} type="text" className="TermInput_input"
|
||||||
placeholder="Rechercher un symptôme, un signe ou une maladie…"
|
placeholder="Rechercher un symptôme, un signe ou une maladie…"
|
||||||
value={value}
|
value={value}
|
||||||
onChange={handleChange} onKeyDown={handleKeyDown} />
|
onChange={handleChange} onKeyDown={handleKeyDown} />
|
|
@ -1,21 +1,4 @@
|
||||||
import React from 'react';
|
const mockNodes = {
|
||||||
import Graph from './Graph.js';
|
|
||||||
|
|
||||||
// const config = {
|
|
||||||
// automaticRearrangeAfterDropNode: true,
|
|
||||||
// node: {
|
|
||||||
// color: 'lightgreen',
|
|
||||||
// size: 240,
|
|
||||||
// fontSize: 14,
|
|
||||||
// highlightStrokeColor: 'blue',
|
|
||||||
// labelProperty: 'name'
|
|
||||||
// },
|
|
||||||
// link: {
|
|
||||||
// highlightColor: 'lightblue'
|
|
||||||
// }
|
|
||||||
// };
|
|
||||||
|
|
||||||
const nodes = {
|
|
||||||
Q2840: {
|
Q2840: {
|
||||||
id: 'Q2840',
|
id: 'Q2840',
|
||||||
name: 'Grippe',
|
name: 'Grippe',
|
||||||
|
@ -127,7 +110,7 @@ const nodes = {
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
const edges = [
|
const mockEdges = [
|
||||||
['Q2840', 'Q38933'],
|
['Q2840', 'Q38933'],
|
||||||
['Q2840', 'Q1115038'],
|
['Q2840', 'Q1115038'],
|
||||||
['Q2840', 'Q474959'],
|
['Q2840', 'Q474959'],
|
||||||
|
@ -165,12 +148,83 @@ const edges = [
|
||||||
['Q133780', 'Q160796']
|
['Q133780', 'Q160796']
|
||||||
];
|
];
|
||||||
|
|
||||||
const ResultsGraph = () => (
|
/**
|
||||||
<Graph
|
* Vérifie si une liste d’arêtes contient une arête donnée, dans un sens ou
|
||||||
nodes={Object.keys(nodes)}
|
* dans l’autre.
|
||||||
edges={edges}
|
*
|
||||||
render={id => nodes[id].name}
|
* @param list Liste d’arêtes.
|
||||||
/>
|
* @param from Premier nœud de l’arête.
|
||||||
|
* @param to Second nœud de l’arête.
|
||||||
|
* @return Vrai si et seulement si l’arête existe.
|
||||||
|
*/
|
||||||
|
const includesEdge = (list, from, to) =>
|
||||||
|
list.some(([source, target]) => (
|
||||||
|
(source === from && target === to)
|
||||||
|
|| (source === to && target === from)
|
||||||
|
));
|
||||||
|
|
||||||
|
export const searchTerms = terms => new Promise((res, rej) =>
|
||||||
|
{
|
||||||
|
// Fait attendre artificiellement pour simuler une requête
|
||||||
|
setTimeout(() =>
|
||||||
|
{
|
||||||
|
const nodes = {};
|
||||||
|
const edges = [];
|
||||||
|
|
||||||
|
// Récupération des identifiants correspondant aux termes de la requête
|
||||||
|
const termIds = terms.map(term =>
|
||||||
|
Object.keys(mockNodes)
|
||||||
|
.find(id => mockNodes[id].name === term)
|
||||||
);
|
);
|
||||||
|
|
||||||
export default ResultsGraph;
|
// Si l’un des termes est inconnu, aucun résultat
|
||||||
|
if (!termIds.includes(undefined))
|
||||||
|
{
|
||||||
|
// Sélection des termes de la requête
|
||||||
|
for (let termId of termIds)
|
||||||
|
{
|
||||||
|
nodes[termId] = mockNodes[termId];
|
||||||
|
}
|
||||||
|
|
||||||
|
// Sélection des nœuds liés à tous les éléments de la requête
|
||||||
|
const resultIds = [];
|
||||||
|
|
||||||
|
for (let [id, data] of Object.entries(mockNodes))
|
||||||
|
{
|
||||||
|
if (termIds.every(
|
||||||
|
termId => includesEdge(mockEdges, id, termId)
|
||||||
|
))
|
||||||
|
{
|
||||||
|
nodes[id] = data;
|
||||||
|
resultIds.push(id);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Sélection des voisins des résultats de la requête
|
||||||
|
for (let [id, data] of Object.entries(mockNodes))
|
||||||
|
{
|
||||||
|
for (let resultId of resultIds)
|
||||||
|
{
|
||||||
|
if (includesEdge(mockEdges, resultId, id))
|
||||||
|
{
|
||||||
|
nodes[id] = data;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Sélection des arêtes liant les nœuds sélectionnés
|
||||||
|
for (let [from, to] of mockEdges)
|
||||||
|
{
|
||||||
|
if (
|
||||||
|
(from in nodes && to in nodes)
|
||||||
|
&& !includesEdge(edges, from, to)
|
||||||
|
)
|
||||||
|
{
|
||||||
|
edges.push([from, to]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
res({nodes, edges});
|
||||||
|
}, 500);
|
||||||
|
});
|
|
@ -104,7 +104,7 @@ input
|
||||||
color: var(--color-light);
|
color: var(--color-light);
|
||||||
|
|
||||||
border-radius: 2px;
|
border-radius: 2px;
|
||||||
padding: calc(.2 * var(--base-size));
|
padding: calc(.25 * var(--base-size)) calc(.5 * var(--base-size));
|
||||||
margin-left: calc(.3 * var(--base-size));
|
margin-left: calc(.3 * var(--base-size));
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
|
|
||||||
|
|
|
@ -730,6 +730,40 @@
|
||||||
"@babel/helper-plugin-utils": "^7.0.0"
|
"@babel/helper-plugin-utils": "^7.0.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"@babel/plugin-transform-runtime": {
|
||||||
|
"version": "7.7.4",
|
||||||
|
"resolved": "https://registry.npmjs.org/@babel/plugin-transform-runtime/-/plugin-transform-runtime-7.7.4.tgz",
|
||||||
|
"integrity": "sha512-O8kSkS5fP74Ad/8pfsCMGa8sBRdLxYoSReaARRNSz3FbFQj3z/QUvoUmJ28gn9BO93YfnXc3j+Xyaqe8cKDNBQ==",
|
||||||
|
"dev": true,
|
||||||
|
"requires": {
|
||||||
|
"@babel/helper-module-imports": "^7.7.4",
|
||||||
|
"@babel/helper-plugin-utils": "^7.0.0",
|
||||||
|
"resolve": "^1.8.1",
|
||||||
|
"semver": "^5.5.1"
|
||||||
|
},
|
||||||
|
"dependencies": {
|
||||||
|
"@babel/helper-module-imports": {
|
||||||
|
"version": "7.7.4",
|
||||||
|
"resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.7.4.tgz",
|
||||||
|
"integrity": "sha512-dGcrX6K9l8258WFjyDLJwuVKxR4XZfU0/vTUgOQYWEnRD8mgr+p4d6fCUMq/ys0h4CCt/S5JhbvtyErjWouAUQ==",
|
||||||
|
"dev": true,
|
||||||
|
"requires": {
|
||||||
|
"@babel/types": "^7.7.4"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"@babel/types": {
|
||||||
|
"version": "7.7.4",
|
||||||
|
"resolved": "https://registry.npmjs.org/@babel/types/-/types-7.7.4.tgz",
|
||||||
|
"integrity": "sha512-cz5Ji23KCi4T+YIE/BolWosrJuSmoZeN1EFnRtBwF+KKLi8GG/Z2c2hOJJeCXPk4mwk4QFvTmwIodJowXgttRA==",
|
||||||
|
"dev": true,
|
||||||
|
"requires": {
|
||||||
|
"esutils": "^2.0.2",
|
||||||
|
"lodash": "^4.17.13",
|
||||||
|
"to-fast-properties": "^2.0.0"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
"@babel/plugin-transform-shorthand-properties": {
|
"@babel/plugin-transform-shorthand-properties": {
|
||||||
"version": "7.2.0",
|
"version": "7.2.0",
|
||||||
"resolved": "https://registry.npmjs.org/@babel/plugin-transform-shorthand-properties/-/plugin-transform-shorthand-properties-7.2.0.tgz",
|
"resolved": "https://registry.npmjs.org/@babel/plugin-transform-shorthand-properties/-/plugin-transform-shorthand-properties-7.2.0.tgz",
|
||||||
|
@ -860,10 +894,9 @@
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"@babel/runtime": {
|
"@babel/runtime": {
|
||||||
"version": "7.7.2",
|
"version": "7.7.4",
|
||||||
"resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.7.2.tgz",
|
"resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.7.4.tgz",
|
||||||
"integrity": "sha512-JONRbXbTXc9WQE2mAZd1p0Z3DZ/6vaQIkgYMSTP3KjRCyd7rCZCcfhCyX+YjwcKxcZ82UrxbRD358bpExNgrjw==",
|
"integrity": "sha512-r24eVUUr0QqNZa+qrImUk8fn5SPhHq+IfYvIoIMg0do3GdK9sMdiLKP3GYVVaxpPKORgm8KRKaNTEhAjgIpLMw==",
|
||||||
"dev": true,
|
|
||||||
"requires": {
|
"requires": {
|
||||||
"regenerator-runtime": "^0.13.2"
|
"regenerator-runtime": "^0.13.2"
|
||||||
}
|
}
|
||||||
|
@ -5954,8 +5987,7 @@
|
||||||
"regenerator-runtime": {
|
"regenerator-runtime": {
|
||||||
"version": "0.13.3",
|
"version": "0.13.3",
|
||||||
"resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.13.3.tgz",
|
"resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.13.3.tgz",
|
||||||
"integrity": "sha512-naKIZz2GQ8JWh///G7L3X6LaQUAMp2lvb1rvwwsURe/VXwD6VMfr+/1NuNw3ag8v2kY1aQ/go5SNn79O9JU7yw==",
|
"integrity": "sha512-naKIZz2GQ8JWh///G7L3X6LaQUAMp2lvb1rvwwsURe/VXwD6VMfr+/1NuNw3ag8v2kY1aQ/go5SNn79O9JU7yw=="
|
||||||
"dev": true
|
|
||||||
},
|
},
|
||||||
"regenerator-transform": {
|
"regenerator-transform": {
|
||||||
"version": "0.14.1",
|
"version": "0.14.1",
|
||||||
|
|
|
@ -5,11 +5,13 @@
|
||||||
"main": "index.js",
|
"main": "index.js",
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@babel/core": "^7.7.2",
|
"@babel/core": "^7.7.2",
|
||||||
|
"@babel/plugin-transform-runtime": "^7.7.4",
|
||||||
"@babel/preset-env": "^7.7.1",
|
"@babel/preset-env": "^7.7.1",
|
||||||
"@babel/preset-react": "^7.7.0",
|
"@babel/preset-react": "^7.7.0",
|
||||||
"parcel-bundler": "^1.12.4"
|
"parcel-bundler": "^1.12.4"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
|
"@babel/runtime": "^7.7.4",
|
||||||
"react": "^16.12.0",
|
"react": "^16.12.0",
|
||||||
"react-dom": "^16.12.0",
|
"react-dom": "^16.12.0",
|
||||||
"springy": "^2.8.0"
|
"springy": "^2.8.0"
|
||||||
|
|
Loading…
Reference in New Issue