Sfoglia il codice sorgente

app: Implémentation d’une recherche de test

master
Mattéo Delabre 4 anni fa
parent
commit
8b77ed5435
Firmato da: matteo ID Chiave GPG: AE3FBD02DC583ABB
  1. 3
      .babelrc
  2. 2
      app/index.html
  3. 4
      app/src/App.js
  4. 1
      app/src/Graph.js
  5. 13
      app/src/ResultsGraph.js
  6. 37
      app/src/SearchResults.js
  7. 13
      app/src/TermInput.js
  8. 108
      app/src/fetch.js
  9. 0
      app/src/index.js
  10. 2
      app/style/style.css
  11. 44
      package-lock.json
  12. 2
      package.json

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"]
}

2
app/index.html

@ -9,6 +9,6 @@
</head>
<body>
<div id="root"></div>
<script src="index.js"></script>
<script src="src/index.js"></script>
</body>
</html>

4
app/App.js → app/src/App.js

@ -1,6 +1,6 @@
import React, {useState} from 'react';
import TermInput from './TermInput.js';
import ResultsGraph from './ResultsGraph.js';
import SearchResults from './SearchResults.js';
const App = () =>
{
@ -9,7 +9,7 @@ const App = () =>
return (
<div className="App">
<TermInput terms={terms} setTerms={setTerms} />
<ResultsGraph />
<SearchResults terms={terms} />
</div>
);
};

1
app/Graph.js → app/src/Graph.js

@ -191,7 +191,6 @@ const Graph = ({nodes, edges, render}) =>
return () =>
{
layout.stop();
graphParent.current.removeEventListener('mousedown', mouseDown);
document.body.removeEventListener('mousemove', mouseMove);
document.body.removeEventListener('mouseup', mouseUp);

13
app/src/ResultsGraph.js

@ -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;

37
app/src/SearchResults.js

@ -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;

13
app/TermInput.js → app/src/TermInput.js

@ -1,6 +1,7 @@
import React, {useState} from 'react';
const enterKey = 13;
const backspaceKey = 8;
const TermInput = ({terms, setTerms}) =>
{
@ -8,7 +9,7 @@ const TermInput = ({terms, setTerms}) =>
const handleKeyDown = ev =>
{
if (ev.keyCode === enterKey)
if (ev.keyCode === enterKey && value)
{
ev.preventDefault();
@ -18,6 +19,14 @@ const TermInput = ({terms, setTerms}) =>
}
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>
)}
<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…"
value={value}
onChange={handleChange} onKeyDown={handleKeyDown} />

108
app/ResultsGraph.js → app/src/fetch.js

@ -1,21 +1,4 @@
import React from 'react';
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 = {
const mockNodes = {
Q2840: {
id: 'Q2840',
name: 'Grippe',
@ -127,7 +110,7 @@ const nodes = {
}
};
const edges = [
const mockEdges = [
['Q2840', 'Q38933'],
['Q2840', 'Q1115038'],
['Q2840', 'Q474959'],
@ -165,12 +148,83 @@ const edges = [
['Q133780', 'Q160796']
];
const ResultsGraph = () => (
<Graph
nodes={Object.keys(nodes)}
edges={edges}
render={id => nodes[id].name}
/>
);
/**
* Vérifie si une liste darêtes contient une arête donnée, dans un sens ou
* dans lautre.
*
* @param list Liste darêtes.
* @param from Premier nœud de larête.
* @param to Second nœud de larête.
* @return Vrai si et seulement si larê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)
);
// 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]);
}
}
}
export default ResultsGraph;
res({nodes, edges});
}, 500);
});

0
app/index.js → app/src/index.js

2
app/style/style.css

@ -104,7 +104,7 @@ input
color: var(--color-light);
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));
cursor: pointer;

44
package-lock.json

@ -730,6 +730,40 @@
"@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": {
"version": "7.2.0",
"resolved": "https://registry.npmjs.org/@babel/plugin-transform-shorthand-properties/-/plugin-transform-shorthand-properties-7.2.0.tgz",
@ -860,10 +894,9 @@
}
},
"@babel/runtime": {
"version": "7.7.2",
"resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.7.2.tgz",
"integrity": "sha512-JONRbXbTXc9WQE2mAZd1p0Z3DZ/6vaQIkgYMSTP3KjRCyd7rCZCcfhCyX+YjwcKxcZ82UrxbRD358bpExNgrjw==",
"dev": true,
"version": "7.7.4",
"resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.7.4.tgz",
"integrity": "sha512-r24eVUUr0QqNZa+qrImUk8fn5SPhHq+IfYvIoIMg0do3GdK9sMdiLKP3GYVVaxpPKORgm8KRKaNTEhAjgIpLMw==",
"requires": {
"regenerator-runtime": "^0.13.2"
}
@ -5954,8 +5987,7 @@
"regenerator-runtime": {
"version": "0.13.3",
"resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.13.3.tgz",
"integrity": "sha512-naKIZz2GQ8JWh///G7L3X6LaQUAMp2lvb1rvwwsURe/VXwD6VMfr+/1NuNw3ag8v2kY1aQ/go5SNn79O9JU7yw==",
"dev": true
"integrity": "sha512-naKIZz2GQ8JWh///G7L3X6LaQUAMp2lvb1rvwwsURe/VXwD6VMfr+/1NuNw3ag8v2kY1aQ/go5SNn79O9JU7yw=="
},
"regenerator-transform": {
"version": "0.14.1",

2
package.json

@ -5,11 +5,13 @@
"main": "index.js",
"devDependencies": {
"@babel/core": "^7.7.2",
"@babel/plugin-transform-runtime": "^7.7.4",
"@babel/preset-env": "^7.7.1",
"@babel/preset-react": "^7.7.0",
"parcel-bundler": "^1.12.4"
},
"dependencies": {
"@babel/runtime": "^7.7.4",
"react": "^16.12.0",
"react-dom": "^16.12.0",
"springy": "^2.8.0"

Caricamento…
Annulla
Salva