From cbd77da6b564e6646c193630135958cd0cc05145 Mon Sep 17 00:00:00 2001 From: larissacsh Date: Tue, 15 Sep 2020 00:15:06 +0200 Subject: [PATCH 1/2] shiner tab --- config/gcore.env.example | 1 + docker-compose.yml | 5 +- visual-analytics-engine/app/app.py | 64 +++++++++ .../app/tools/gcore_manager.py | 97 +++++++++++++ visual-explorer/app/package-lock.json | 96 +++++++++---- visual-explorer/app/package.json | 6 +- .../src/components/Dashboard/Dashboard.tsx | 56 ++++---- .../Dashboard/ShinerTab/ShinerTab.scss | 8 ++ .../Dashboard/ShinerTab/ShinerTab.tsx | 84 +++++++++++ .../ShinerTab/gcore/ConstraintTable.jsx | 59 ++++++++ .../Dashboard/ShinerTab/gcore/Table.jsx | 61 ++++++++ .../ShinerTab/gcore/construct-panel.tsx | 136 ++++++++++++++++++ .../ShinerTab/gcore/getneighbors-panel.tsx | 112 +++++++++++++++ .../ShinerTab/gcore/graphneighbor-panel.tsx | 112 +++++++++++++++ .../ShinerTab/gcore/select-panel.tsx | 121 ++++++++++++++++ .../Dashboard/ShinerTab/graph/visNetwork.jsx | 119 +++++++++++++++ .../components/Dashboard/ShinerTab/index.tsx | 1 + .../ShinerTab/panels/getggds-panel.tsx | 130 +++++++++++++++++ .../panels/graphdb-panel-wrapper.tsx | 134 +++++++++++++++++ .../Dashboard/ShinerTab/panels/panels.scss | 24 ++++ .../ShinerTab/panels/runer-panel.tsx | 80 +++++++++++ .../ShinerTab/panels/setggds-panel.tsx | 95 ++++++++++++ 22 files changed, 1543 insertions(+), 58 deletions(-) create mode 100644 config/gcore.env.example create mode 100644 visual-analytics-engine/app/tools/gcore_manager.py create mode 100644 visual-explorer/app/src/components/Dashboard/ShinerTab/ShinerTab.scss create mode 100644 visual-explorer/app/src/components/Dashboard/ShinerTab/ShinerTab.tsx create mode 100644 visual-explorer/app/src/components/Dashboard/ShinerTab/gcore/ConstraintTable.jsx create mode 100644 visual-explorer/app/src/components/Dashboard/ShinerTab/gcore/Table.jsx create mode 100644 visual-explorer/app/src/components/Dashboard/ShinerTab/gcore/construct-panel.tsx create mode 100644 visual-explorer/app/src/components/Dashboard/ShinerTab/gcore/getneighbors-panel.tsx create mode 100644 visual-explorer/app/src/components/Dashboard/ShinerTab/gcore/graphneighbor-panel.tsx create mode 100644 visual-explorer/app/src/components/Dashboard/ShinerTab/gcore/select-panel.tsx create mode 100644 visual-explorer/app/src/components/Dashboard/ShinerTab/graph/visNetwork.jsx create mode 100644 visual-explorer/app/src/components/Dashboard/ShinerTab/index.tsx create mode 100644 visual-explorer/app/src/components/Dashboard/ShinerTab/panels/getggds-panel.tsx create mode 100644 visual-explorer/app/src/components/Dashboard/ShinerTab/panels/graphdb-panel-wrapper.tsx create mode 100644 visual-explorer/app/src/components/Dashboard/ShinerTab/panels/panels.scss create mode 100644 visual-explorer/app/src/components/Dashboard/ShinerTab/panels/runer-panel.tsx create mode 100644 visual-explorer/app/src/components/Dashboard/ShinerTab/panels/setggds-panel.tsx diff --git a/config/gcore.env.example b/config/gcore.env.example new file mode 100644 index 0000000..29c561a --- /dev/null +++ b/config/gcore.env.example @@ -0,0 +1 @@ +GCORE_ENDPOINT=http://diascld32.iccluster.epfl.ch:6080/ \ No newline at end of file diff --git a/docker-compose.yml b/docker-compose.yml index 3001324..b9a697e 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -42,6 +42,7 @@ services: - ./config/postgres.env - ./config/proteus.env - ./config/qal.env + - ./config/gcore.env working_dir: /usr/src/app command: ["python", "app.py", "--port", "8080", "--dev"] depends_on: @@ -96,7 +97,7 @@ services: command: ["sh", "-c", "npm install && npx bower --allow-root install && npx grunt serve"] networks: - sdl_net - + #################### # Postgres Test DB # #################### @@ -118,4 +119,4 @@ volumes: vae-data: external: false redis-data: - external: false + external: false \ No newline at end of file diff --git a/visual-analytics-engine/app/app.py b/visual-analytics-engine/app/app.py index 556e17a..f4911ac 100644 --- a/visual-analytics-engine/app/app.py +++ b/visual-analytics-engine/app/app.py @@ -17,6 +17,7 @@ from tools.data_transformer import transform from tools.simsearch_manager import SimSearchManager from tools.timeseries_manager import TimeSeriesManager +from tools.gcore_manager import GCoreManager warnings.filterwarnings("ignore", category=UserWarning) @@ -36,6 +37,7 @@ def create_paths(): PROTEUS_PASSWORD = os.environ["PROTEUS_PASSWORD"] QAL_ENDPOINT = os.environ["QAL_ENDPOINT"] +GCORE_ENDPOINT = os.environ["GCORE_ENDPOINT"] def make_flask_app() -> Flask: @@ -66,6 +68,7 @@ def make_cache_key(*args, **kwargs): proteus_manager = ProteusManager(PROTEUS_URL, PROTEUS_USER, PROTEUS_PASSWORD) qal_manager = QALManager(QAL_ENDPOINT) timeseries_manager = TimeSeriesManager() + gcore_manager = GCoreManager(GCORE_ENDPOINT) with app.app_context(): graph = nx.read_gpickle("/data/input/graph/graph.gpickle") @@ -88,6 +91,67 @@ def index(): def graph_route(): return clustered_graph["hierarchical"] + ############graph g-core entity resolution - routes############### + + #graph schema + @app.route("/gcore/schema/") # ok + def get_schema(graph_name): + return gcore_manager.graph_schema(graph_name) + + @app.route("/gcore/availableGraphs") + def get_graphdb(): + return gcore_manager.get_graphs() + + @app.route("/gcore/er/setggds", methods=['POST']) + def set_ggds(): + args = request.json + return gcore_manager.set_ggds(args) + + @app.route("/gcore/er/getggds") + def get_ggds(): + return gcore_manager.get_ggds() + + @app.route("/gcore/er/run") + def run_er(): + return gcore_manager.run_ER() + + @app.route("/gcore/query/select", methods=['POST']) + def select_query(): + args = request.json + query = args["query"] + limit = args["limit"] + print("here select panel" + query) + return gcore_manager.selectQuery(query, limit) + + @app.route("/gcore/query/construct", methods=['POST']) + def construct_query(): + args = request.json + query = args["query"] + limit = args["limit"] + print("here construct panel" + query) + return gcore_manager.constructQuery(query, limit) + + #args for both select and graph neighbor + # json format for "passing node information" + # { + # "nodeLabel": "ProductAmazon", + # "id": "1", + # "edgeLabel": "", + # "graphName": "Amazon", + # "limit": -1 + # } + @app.route("/gcore/query/select-neighbor", methods=['POST']) + def select_neighbor(): + args = request.json + return gcore_manager.getNeighbors(args) + + @app.route("/gcore/query/graph-neighbor", methods=['POST']) + def graph_neighbor(): + args = request.json + return gcore_manager.getNeighborsGraph(args) + + ############################## + # Renamed from /tables to /schema to avoid confusion @app.route('/schema', methods=['POST']) @cache.cached(timeout=432000, key_prefix=make_cache_key) diff --git a/visual-analytics-engine/app/tools/gcore_manager.py b/visual-analytics-engine/app/tools/gcore_manager.py new file mode 100644 index 0000000..4b0f961 --- /dev/null +++ b/visual-analytics-engine/app/tools/gcore_manager.py @@ -0,0 +1,97 @@ +import requests +import json +import pandas as pd +import networkx as nx + + +class GCoreManager: + _service_url = None + + def __init__(self, gcore_endpoint): + self._service_url = gcore_endpoint + r = requests.get(self._service_url + 'graphDB') + print(r.content) + + def get_graphs(self): + print("GCore service url::" + self._service_url) + r = requests.get(self._service_url + 'graphDB') + print(r.content) + return r.content + + def graph_schema(self, name=None): + urlGCore = self._service_url + 'graphDB/' + name + r = requests.get(url=urlGCore) + return r.content + + def set_ggds(self, ggds): + r = requests.post(url=self._service_url + 'ggds/setGGDs', json=ggds) + print("Here answer to content:" + r.content) + return r.content + + def get_ggds(self): + r = requests.get(url=self._service_url+'ggds/getGGDs') + return r.content + + def run_ER(self): + r = requests.get(url=self._service_url+'ggds/runER') + print(r.content) + return r.content + + def selectQuery(self, query, limit): + #json = {"key": "value"} + r = requests.post(url=self._service_url + 'gcore/select', json={"query": query, "limit": limit}) + #r = requests.post(url=service_url + 'gcore/select', data=query) + #tableData = pd.DataFrame.from_dict(json.loads(r['content'])) + tableData = r.content + return tableData + + def constructQuery(self, query, limit): + print(query) + r = requests.post(url=self._service_url + 'gcore/construct', json={"query": query, "limit": limit}) + #r = requests.post(url=service_url + "gcore/construct", data=query) + jsonobj = json.loads(r.content) + print(r.content) + graph = r.content#nx.readwrite.json_graph.node_link_graph(r.content) + return graph + + def selectQuery_table(self, tableName, graphName=None): + if graphName is None: + query = "SELECT * MATCH (a:" + tableName + ")" + else: + query = "SELECT * MATCH (a:" + tableName + ") ON " + graphName + r = requests.post(url=self._service_url+'gcore/select', data=query) + tableData = pd.DataFrame.from_dict(json.loads(r['content'])) + return tableData + + def constructQuery_graph(self, resPattern, matchPattern, graphName=None): + if graphName is None: + query = "CONSTRUCT " + resPattern + " MATCH " + matchPattern + else: + query = "CONSTRUCT " + resPattern + " MATCH " + matchPattern + " ON " + graphName + r = requests.post(url=self._service_url+"gcore/construct", data=query) + graph = nx.readwrite.json_graph.node_link_graph(r.content) + return graph + +#example json file +# with open("data/graph.txt") as json_file: +# jsonParam = json.load(json_file) +# print(jsonParam) +# r = requests.post(url='http://localhost:8080/gcore/construct-neighbor', json=jsonParam) + +#json format for "passing node information" +#{ +# "nodeLabel": "ProductAmazon", +# "id": "1", +# "edgeLabel": "", +# "graphName": "Amazon", +# "limit": -1 +# } + def getNeighbors(self, nodeParam): + r = requests.post(url=self._service_url+"gcore/select-neighbor",json=nodeParam) + neighborData = pd.DataFrame.from_dict(json.loads(r['content'])) + return neighborData + + def getNeighborsGraph(self, nodeParam): + r = requests.post(url=self._service_url+"gcore/construct-neighbor",json=nodeParam) + graphNeighbor = nx.readwrite.json_graph.node_link_graph(r.content) + return graphNeighbor diff --git a/visual-explorer/app/package-lock.json b/visual-explorer/app/package-lock.json index 7951df7..b15f7ec 100644 --- a/visual-explorer/app/package-lock.json +++ b/visual-explorer/app/package-lock.json @@ -1038,7 +1038,7 @@ "@emotion/is-prop-valid": { "version": "0.8.8", "resolved": "https://registry.npmjs.org/@emotion/is-prop-valid/-/is-prop-valid-0.8.8.tgz", - "integrity": "sha512-u5WtneEAr5IDG2Wv65yhunPSMLIpuKsbuOktRojfrEiEvRyC85LgPMZI63cr7NUqT8ZIGdSVg8ZKGxIug4lXcA==", + "integrity": "sha1-2yixxDaKJZtgqXMR1qlS1P0BrBo=", "requires": { "@emotion/memoize": "0.7.4" } @@ -1046,17 +1046,17 @@ "@emotion/memoize": { "version": "0.7.4", "resolved": "https://registry.npmjs.org/@emotion/memoize/-/memoize-0.7.4.tgz", - "integrity": "sha512-Ja/Vfqe3HpuzRsG1oBtWTHk2PGZ7GR+2Vz5iYGelAw8dx32K0y7PjVuxK6z1nMpZOqAFsRUPCkK1YjJ56qJlgw==" + "integrity": "sha1-Gb8PWvGRSREcQNmLsM+CEZ9dnus=" }, "@emotion/stylis": { "version": "0.8.5", "resolved": "https://registry.npmjs.org/@emotion/stylis/-/stylis-0.8.5.tgz", - "integrity": "sha512-h6KtPihKFn3T9fuIrwvXXUOwlx3rfUvfZIcP5a6rh8Y7zjE3O06hT5Ss4S/YI1AYhuZ1kjaE/5EaOOI2NqSylQ==" + "integrity": "sha1-3qyzib1u530ef8rMzp4WxcfnjgQ=" }, "@emotion/unitless": { "version": "0.7.5", "resolved": "https://registry.npmjs.org/@emotion/unitless/-/unitless-0.7.5.tgz", - "integrity": "sha512-OWORNpfjMsSSUBVrRBVGECkhWcULOAJz9ZW8uK9qgxD+87M7jHRcvh/A96XXNhXTLmKcoYSQtBEX7lHMO7YRwg==" + "integrity": "sha1-dyESkcGQCnALinjPr9oxYNdpSe0=" }, "@hapi/address": { "version": "2.0.0", @@ -1481,17 +1481,17 @@ "@popperjs/core": { "version": "2.4.4", "resolved": "https://registry.npmjs.org/@popperjs/core/-/core-2.4.4.tgz", - "integrity": "sha512-1oO6+dN5kdIA3sKPZhRGJTfGVP4SWV6KqlMOwry4J3HfyD68sl/3KmG7DeYUzvN+RbhXDnv/D8vNNB8168tAMg==" + "integrity": "sha1-EdXbGb0XiTbsic2EUZxN5DlXQ5g=" }, "@restart/context": { "version": "2.1.4", "resolved": "https://registry.npmjs.org/@restart/context/-/context-2.1.4.tgz", - "integrity": "sha512-INJYZQJP7g+IoDUh/475NlGiTeMfwTXUEr3tmRneckHIxNolGOW9CTq83S8cxq0CgJwwcMzMJFchxvlwe7Rk8Q==" + "integrity": "sha1-qZ2HwpmjTCi9hbtInLB7/SMUnAI=" }, "@restart/hooks": { "version": "0.3.25", "resolved": "https://registry.npmjs.org/@restart/hooks/-/hooks-0.3.25.tgz", - "integrity": "sha512-m2v3N5pxTsIiSH74/sb1yW8D9RxkJidGW+5Mfwn/lHb2QzhZNlaU1su7abSyT9EGf0xS/0waLjrf7/XxQHUk7w==", + "integrity": "sha1-EQBBOa0ccNL1llqJOdy1rrlqplI=", "requires": { "lodash": "^4.17.15", "lodash-es": "^4.17.15" @@ -2012,7 +2012,7 @@ "@types/lodash": { "version": "4.14.161", "resolved": "https://registry.npmjs.org/@types/lodash/-/lodash-4.14.161.tgz", - "integrity": "sha512-EP6O3Jkr7bXvZZSZYlsgt5DIjiGr0dXP1/jVEwVLTFgg0d+3lWVQkRavYVQszV7dYUwvg0B8R0MBDpcmXg7XIA==", + "integrity": "sha1-ohygd32rxuT0Tz0H83t2X1QYixg=", "dev": true }, "@types/matter-js": { @@ -2112,7 +2112,7 @@ "@types/react-transition-group": { "version": "4.4.0", "resolved": "https://registry.npmjs.org/@types/react-transition-group/-/react-transition-group-4.4.0.tgz", - "integrity": "sha512-/QfLHGpu+2fQOqQaXh8MG9q03bFENooTb/it4jr5kKaZlDQfWvjqWZg48AwzPVMBHlRuTRAY7hRHCEOXz5kV6w==", + "integrity": "sha1-iCg520Zd8TIOR1Pm6fcMp+m01G0=", "dev": true, "requires": { "@types/react": "*" @@ -3059,7 +3059,7 @@ "babel-plugin-styled-components": { "version": "1.11.1", "resolved": "https://registry.npmjs.org/babel-plugin-styled-components/-/babel-plugin-styled-components-1.11.1.tgz", - "integrity": "sha512-YwrInHyKUk1PU3avIRdiLyCpM++18Rs1NgyMXEAQC33rIXs/vro0A+stf4sT0Gf22Got+xRWB8Cm0tw+qkRzBA==", + "integrity": "sha1-Upap5VfXNsMYa+B5//J8ZmXWPXY=", "requires": { "@babel/helper-annotate-as-pure": "^7.0.0", "@babel/helper-module-imports": "^7.0.0", @@ -5037,7 +5037,7 @@ "css-to-react-native": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/css-to-react-native/-/css-to-react-native-3.0.0.tgz", - "integrity": "sha512-Ro1yETZA813eoyUp2GDBhG2j+YggidUmzO1/v9eYBKR2EHVEniE2MI/NqpTQ954BMpTPZFsGNPm46qFB9dpaPQ==", + "integrity": "sha1-YtvmeAcqgkpom8/uAR/JbgKn11Y=", "requires": { "camelize": "^1.0.0", "css-color-keywords": "^1.0.0", @@ -5047,7 +5047,7 @@ "postcss-value-parser": { "version": "4.1.0", "resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-4.1.0.tgz", - "integrity": "sha512-97DXOFbQJhk71ne5/Mt6cOu6yxsSfM0QGQyl0L25Gca4yGWEGJaig7l7gbCX623VqTBNGLRLaVUCnNkcedlRSQ==" + "integrity": "sha1-RD9qIM7WSBor2k+oUypuVdeJoss=" } } }, @@ -5873,7 +5873,7 @@ "dom-helpers": { "version": "5.2.0", "resolved": "https://registry.npmjs.org/dom-helpers/-/dom-helpers-5.2.0.tgz", - "integrity": "sha512-Ru5o9+V8CpunKnz5LGgWXkmrH/20cGKwcHwS4m73zIvs54CN9epEmT/HLqFJW3kXpakAFkEdzgy1hzlJe3E4OQ==", + "integrity": "sha1-V/0FTF+PNMUqPu/9t+fpPNNX2Vs=", "requires": { "@babel/runtime": "^7.8.7", "csstype": "^3.0.2" @@ -5882,7 +5882,7 @@ "@babel/runtime": { "version": "7.11.2", "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.11.2.tgz", - "integrity": "sha512-TeWkU52so0mPtDcaCTxNBI/IHiz0pZgr8VEFqXFtZWpYD08ZB6FaSwVAS8MKRQAP3bYKiVjwysOJgMFY28o6Tw==", + "integrity": "sha1-9UnBPHVMxAuHZEufqfCaapX+BzY=", "requires": { "regenerator-runtime": "^0.13.4" } @@ -5895,7 +5895,7 @@ "regenerator-runtime": { "version": "0.13.7", "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.13.7.tgz", - "integrity": "sha512-a54FxoJDIr27pgf7IgeQGxmqUNYrcV338lf/6gH456HZ/PhX+5BcwHXG9ajESmwe6WRO0tAzRUrRmNONWgkrew==" + "integrity": "sha1-ysLazIoepnX+qrrriugziYrkb1U=" } } }, @@ -11163,12 +11163,12 @@ "lodash": { "version": "4.17.20", "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.20.tgz", - "integrity": "sha512-PlhdFcillOINfeV7Ni6oF1TAEayyZBoZ8bcshTHqOYJYlrqzRK5hagpagky5o4HfCzzd1TRkXPMFq6cKk9rGmA==" + "integrity": "sha1-tEqbYpe8tpjxxRo1RaKzs2jVnFI=" }, "lodash-es": { "version": "4.17.15", "resolved": "https://registry.npmjs.org/lodash-es/-/lodash-es-4.17.15.tgz", - "integrity": "sha512-rlrc3yU3+JNOpZ9zj5pQtxnx2THmvRykwL4Xlxoa8I9lHBlVbbyPhgyPMioxVZ4NqyxaVVtaJnzsyOidQIhyyQ==" + "integrity": "sha1-Ib2Wg5NUQS8j16EDQOXqxu5FXXg=" }, "lodash._getnative": { "version": "3.9.1", @@ -13836,7 +13836,7 @@ "prop-types-extra": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/prop-types-extra/-/prop-types-extra-1.1.1.tgz", - "integrity": "sha512-59+AHNnHYCdiC+vMwY52WmvP5dM3QLeoumYuEyceQDi9aEhtwN9zIQ2ZNo25sMyXnbh32h+P1ezDsUpUH3JAew==", + "integrity": "sha1-WMO3TL+7ldMEYll1qi8ISDKaAQs=", "requires": { "react-is": "^16.3.2", "warning": "^4.0.0" @@ -14413,7 +14413,7 @@ "react-icons": { "version": "3.11.0", "resolved": "https://registry.npmjs.org/react-icons/-/react-icons-3.11.0.tgz", - "integrity": "sha512-JRgiI/vdF6uyBgyZhVyYJUZAop95Sy4XDe/jmT3R/bKliFWpO/uZBwvSjWEdxwzec7SYbEPNPck0Kff2tUGM2Q==", + "integrity": "sha1-LKKQPfq4Joyhjr2Mwuh5kh7DslQ=", "requires": { "camelcase": "^5.0.0" }, @@ -14421,7 +14421,7 @@ "camelcase": { "version": "5.3.1", "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-5.3.1.tgz", - "integrity": "sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg==" + "integrity": "sha1-48mzFWnhBoEd8kL3FXJaH0xJQyA=" } } }, @@ -14578,7 +14578,7 @@ "react-transition-group": { "version": "4.4.1", "resolved": "https://registry.npmjs.org/react-transition-group/-/react-transition-group-4.4.1.tgz", - "integrity": "sha512-Djqr7OQ2aPUiYurhPalTrVy9ddmFCCzwhqQmtN+J3+3DzLO209Fdr70QrN8Z3DsglWql6iY1lDWAfpFiBtuKGw==", + "integrity": "sha1-Y4aPkyWjjqXulTXYKDJ/hXczRck=", "requires": { "@babel/runtime": "^7.5.5", "dom-helpers": "^5.0.1", @@ -14898,6 +14898,14 @@ "tough-cookie": "~2.4.3", "tunnel-agent": "^0.6.0", "uuid": "^3.3.2" + }, + "dependencies": { + "uuid": { + "version": "3.4.0", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-3.4.0.tgz", + "integrity": "sha1-sj5DWK+oogL+ehAK8fX4g/AgB+4=", + "dev": true + } } }, "request-promise-core": { @@ -15782,6 +15790,12 @@ "requires": { "websocket-driver": ">=0.5.1" } + }, + "uuid": { + "version": "3.4.0", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-3.4.0.tgz", + "integrity": "sha1-sj5DWK+oogL+ehAK8fX4g/AgB+4=", + "dev": true } } }, @@ -16841,7 +16855,7 @@ "tunnel": { "version": "0.0.6", "resolved": "https://registry.npmjs.org/tunnel/-/tunnel-0.0.6.tgz", - "integrity": "sha512-1h/Lnq9yajKY2PEbBadPXj3VxsDDu844OnaAo52UVmIzIvwwtBPIuNvkjuzBlTWpfJyUbG3ez0KSBibQkj4ojg==" + "integrity": "sha1-cvExSzSlsZLbASMk3yzFh8pH+Sw=" }, "tunnel-agent": { "version": "0.6.0", @@ -16892,7 +16906,7 @@ "typed-rest-client": { "version": "1.7.3", "resolved": "https://registry.npmjs.org/typed-rest-client/-/typed-rest-client-1.7.3.tgz", - "integrity": "sha512-CwTpx/TkRHGZoHkJhBcp4X8K3/WtlzSHVQR0OIFnt10j4tgy4ypgq/SrrgVpA1s6tAL49Q6J3R5C0Cgfh2ddqA==", + "integrity": "sha1-G+smO4axTTRZb2Enxhct1f1lLns=", "requires": { "qs": "^6.9.1", "tunnel": "0.0.6", @@ -16902,7 +16916,7 @@ "qs": { "version": "6.9.4", "resolved": "https://registry.npmjs.org/qs/-/qs-6.9.4.tgz", - "integrity": "sha512-A1kFqHekCTM7cz0udomYUoYNWjBebHm/5wzU/XqrBRBNWectVH0QIiN+NEcZ0Dte5hvzHwbr8+XQmguPhJ6WdQ==" + "integrity": "sha1-kJCykNH5FyjTwi5UhDykSupatoc=" } } }, @@ -16950,7 +16964,7 @@ "uncontrollable": { "version": "7.1.1", "resolved": "https://registry.npmjs.org/uncontrollable/-/uncontrollable-7.1.1.tgz", - "integrity": "sha512-EcPYhot3uWTS3w00R32R2+vS8Vr53tttrvMj/yA1uYRhf8hbTG2GyugGqWDY0qIskxn0uTTojVd6wPYW9ZEf8Q==", + "integrity": "sha1-9n/tPvk2NxJlcYCXRjI6nbgV1VY=", "requires": { "@babel/runtime": "^7.6.3", "@types/react": "^16.9.11", @@ -17250,9 +17264,9 @@ "dev": true }, "uuid": { - "version": "3.3.3", - "resolved": "https://registry.npmjs.org/uuid/-/uuid-3.3.3.tgz", - "integrity": "sha512-pW0No1RGHgzlpHJO1nsVrHKpOEIxkGg1xB+v0ZmdNH5OAeAwzAVrCnI2/6Mtx+Uys6iaylxa+D3g4j63IKKjSQ==", + "version": "7.0.3", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-7.0.3.tgz", + "integrity": "sha1-xcnyyM8l3Ao3LE3xRBxB9b0MaAs=", "dev": true }, "v8-compile-cache": { @@ -17294,6 +17308,24 @@ "extsprintf": "^1.2.0" } }, + "vis-data": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/vis-data/-/vis-data-7.0.0.tgz", + "integrity": "sha1-cf+OwG5NC5nxyM3s/flzCetFYs8=", + "dev": true + }, + "vis-network": { + "version": "8.3.2", + "resolved": "https://registry.npmjs.org/vis-network/-/vis-network-8.3.2.tgz", + "integrity": "sha1-Ip4laekfDp85H4wyypNeMgqE04g=", + "dev": true + }, + "vis-util": { + "version": "4.3.4", + "resolved": "https://registry.npmjs.org/vis-util/-/vis-util-4.3.4.tgz", + "integrity": "sha1-AjGfvZCfgngrlqNtEiTxvupn+LI=", + "dev": true + }, "vm-browserify": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/vm-browserify/-/vm-browserify-1.1.0.tgz", @@ -17611,6 +17643,14 @@ "requires": { "ansi-colors": "^3.0.0", "uuid": "^3.3.2" + }, + "dependencies": { + "uuid": { + "version": "3.4.0", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-3.4.0.tgz", + "integrity": "sha1-sj5DWK+oogL+ehAK8fX4g/AgB+4=", + "dev": true + } } }, "webpack-manifest-plugin": { diff --git a/visual-explorer/app/package.json b/visual-explorer/app/package.json index c5fd74d..a27b5af 100644 --- a/visual-explorer/app/package.json +++ b/visual-explorer/app/package.json @@ -27,7 +27,11 @@ "tslint-config-prettier": "^1.18.0", "tslint-eslint-rules": "^5.4.0", "tslint-react": "^4.2.0", - "typescript": "3.5.3" + "typescript": "3.5.3", + "uuid": "^7.0.3", + "vis-data": "^7.0.0", + "vis-network": "^8.2.0", + "vis-util": "^4.3.4" }, "dependencies": { "bootstrap": "^4.3.1", diff --git a/visual-explorer/app/src/components/Dashboard/Dashboard.tsx b/visual-explorer/app/src/components/Dashboard/Dashboard.tsx index 9223a28..f7abbb4 100644 --- a/visual-explorer/app/src/components/Dashboard/Dashboard.tsx +++ b/visual-explorer/app/src/components/Dashboard/Dashboard.tsx @@ -8,6 +8,7 @@ import SimSearchProjectionTab from 'components/Dashboard/SimSearchProjectionTab/ import ProfilingTab from 'components/Dashboard/ProfilingTab'; import VPlotsTab from 'components/Dashboard/vPlotsTab'; import TimeSeriesTab from 'components/Dashboard/TimeSeriesTab'; +import ShinerTab from "components/Dashboard/ShinerTab"; import 'bootstrap/dist/css/bootstrap.min.css'; interface IDashboardProps { @@ -15,34 +16,35 @@ interface IDashboardProps { } const Dashboard = (props: IDashboardProps) => { - return ( -
- + + + + + - - - - - - - - - - - - - - - - -
- ); + + + + + + + + + + + + + + + + ; }; export default Dashboard; diff --git a/visual-explorer/app/src/components/Dashboard/ShinerTab/ShinerTab.scss b/visual-explorer/app/src/components/Dashboard/ShinerTab/ShinerTab.scss new file mode 100644 index 0000000..e3dc4df --- /dev/null +++ b/visual-explorer/app/src/components/Dashboard/ShinerTab/ShinerTab.scss @@ -0,0 +1,8 @@ +.er-graph-tab { + display: flex; + flex-direction: column; + align-items: stretch; + align-content: stretch; + flex-grow: 1; + height: inherit; +} diff --git a/visual-explorer/app/src/components/Dashboard/ShinerTab/ShinerTab.tsx b/visual-explorer/app/src/components/Dashboard/ShinerTab/ShinerTab.tsx new file mode 100644 index 0000000..3306bec --- /dev/null +++ b/visual-explorer/app/src/components/Dashboard/ShinerTab/ShinerTab.tsx @@ -0,0 +1,84 @@ +import React from 'react'; +import { Store } from 'redux'; +import { IApplicationState } from 'redux-types'; +import './ShinerTab.scss'; +import GraphdbPanelWrapper from "./panels/graphdb-panel-wrapper"; +import SetGGDsPanel from "./panels/setggds-panel"; +import GetGGDsPanel from "./panels/getggds-panel"; +import RunERPanel from "./panels/runer-panel"; +import SelectQueryPanelWrapper from "./gcore/select-panel"; +import ConstructQueryPanelWrapper from "./gcore/construct-panel"; +import GetNeighborPanelWrapper from "./gcore/getneighbors-panel"; +import GetNeighborGraphPanelWrapper from "./gcore/graphneighbor-panel"; +import DataPofilingPanel from "../HierarchicalGraphTab/panels/data-pofiling-panel"; +import {Card, Col, Container, Row} from "react-bootstrap"; +import VisNetwork from "./graph/visNetwork"; +import {Network} from "vis-network"; + +interface Props { + store: Store; +} + +const ShinerTab = (props: Props) => { + console.log('sHINERTab.render()'); + + return ( +
+
+ + + + + + Available graphs + + + + + + + + + + GGDs: Graph Generating Dependencies + + + + + + + + + Entity Resolution + + + + + + + + + + + G-Core Tabular Queries + + + + + + + + + G-Core Graph Queries + + + + + + +
+
+ ); +}; + +export default ShinerTab; diff --git a/visual-explorer/app/src/components/Dashboard/ShinerTab/gcore/ConstraintTable.jsx b/visual-explorer/app/src/components/Dashboard/ShinerTab/gcore/ConstraintTable.jsx new file mode 100644 index 0000000..b4f359f --- /dev/null +++ b/visual-explorer/app/src/components/Dashboard/ShinerTab/gcore/ConstraintTable.jsx @@ -0,0 +1,59 @@ +import React from 'react'; +import {Table} from "react-bootstrap"; +/* +code for quick testing tables from +https://plnkr.co/edit/ysN4Qb3lUp9mbqFhumWJ?p=preview&preview +https://medium.com/@subalerts/create-dynamic-table-from-json-in-react-js-1a4a7b1146ef + */ + +export default class ConstraintTable extends React.Component { + + constructor(props) { + super(props); + /*this.state = { + items: this.props.data + }*/ + this.constraints = this.props.data + this.keys = ["distance", "var1", "var2", "operator", "threshold"] + } + + componentDidUpdate(prevProps, prevState, snapshot) { + if(this.props.data !== prevProps.data ) // Check if it's a new user, you can also use some unique property, like the ID (this.props.user.id !== prevProps.user.id) + { + //alert("Change component!!!" + this.props.data) + this.render(); + } + } + + + render() { + return ( +
+ + + + + {this.keys.map((_, index) => ( + + ))} + + + + {this.constraints.map((_, index) => ( + + + + + + + + + ))} + +
Constraints{_}
{index}{_['distance']}{_['var1'].toString()+ "." + _['attr1'].toString()}{_['var2'].toString() + "." + _['attr2'].toString()}{_['operator']}{_['threshold']}
+
+ ); + } + + +} \ No newline at end of file diff --git a/visual-explorer/app/src/components/Dashboard/ShinerTab/gcore/Table.jsx b/visual-explorer/app/src/components/Dashboard/ShinerTab/gcore/Table.jsx new file mode 100644 index 0000000..13f4368 --- /dev/null +++ b/visual-explorer/app/src/components/Dashboard/ShinerTab/gcore/Table.jsx @@ -0,0 +1,61 @@ +import React from 'react'; +import {Table} from "react-bootstrap"; +/* +code for quick testing tables from +https://plnkr.co/edit/ysN4Qb3lUp9mbqFhumWJ?p=preview&preview +https://medium.com/@subalerts/create-dynamic-table-from-json-in-react-js-1a4a7b1146ef + */ + +export default class TableDyn extends React.Component { + + constructor(props){ + super(props); + /*this.state = { + items: this.props.data + }*/ + this.getHeader = this.getHeader.bind(this); + this.getRowsData = this.getRowsData.bind(this); + this.getKeys = this.getKeys.bind(this); + } + + getKeys = function(){ + return Object.keys(this.props.data[0]); + } + + getHeader = function(){ + const keys = this.getKeys(); + return keys.map((key, index)=>{ + return {key.toUpperCase()} + }) + } + + getRowsData = function(){ + //this.items = this.props.data; + const items = Array.from(this.props.data)//JSON.parse(this.props.data);//Array.from(this.props.data); + const keys = this.getKeys(); + return items.map((row, index)=>{ + return + }) + } + + render() { + return ( +
+ + + {this.getHeader()} + + + {this.getRowsData()} + +
+
+ ); + } +} + +const RenderRow = (props) =>{ + return props.keys.map((key, index)=>{ + return {props.data[key]} + }) +} \ No newline at end of file diff --git a/visual-explorer/app/src/components/Dashboard/ShinerTab/gcore/construct-panel.tsx b/visual-explorer/app/src/components/Dashboard/ShinerTab/gcore/construct-panel.tsx new file mode 100644 index 0000000..51cea84 --- /dev/null +++ b/visual-explorer/app/src/components/Dashboard/ShinerTab/gcore/construct-panel.tsx @@ -0,0 +1,136 @@ +import React, {Component} from 'react'; +import {Store} from "redux"; +import * as d3 from 'd3'; +import {IApplicationState} from "../../../../redux-types"; +import {Button, Form, FormGroup, Row, ToggleButton, ToggleButtonGroup} from "react-bootstrap"; +import VisNetwork from "../graph/visNetwork"; + +interface ConstructQueryPanelWrapperProps { + store: Store; +} + +interface ConstructQueryPanelWrapperState { + //graph: string, + //match: string + //construct: string + query: string, + //answer: any + nodes: any, + links: any +} + + +/** + * App + * + * Simple react js fetch example + */ +class ConstructQueryPanelWrapper extends React.Component { + + /** + * constructor + * + * @object @props parent props + * @object @state component state + */ + constructor(props: ConstructQueryPanelWrapperProps) { + + super(props); + + this.state = { + //graph : "Graph name, AmazonGoogle", + //match: "Match clause, (p:ProductGoogle)", + //construct: "Construct clause, (p)", + query: "CONSTRUCT (p) MATCH (p:Person) ON people_graph", + //answer: undefined + nodes: undefined, + links: undefined + } + + } + + /* handleChangeGraph = (event: any) => { + const target = event.target + const value = target.value + this.setState({graph: value}); + } + + handleChangeMatch = (event: any) => { + const target = event.target + const value = target.value + this.setState({match: value}); + } + + handleChangeConstruct = (event: any) => { + const target = event.target + const value = target.value + this.setState({construct: value}); + }*/ + + handleChangeQuery = (event: any) => { + const target = event.target + const value = target.value + this.setState({query: value}); + } + + handleSubmit = (event: any) => { + event.preventDefault(); + fetch('http://127.0.0.1:3001/gcore/query/construct', { + method: 'POST', + // We convert the React state to JSON and send it as the POST body + //body: JSON.stringify(this.state) + headers: { + 'Access-Control-Allow-Origin': '*', + 'Access-Control-Allow-Methods': 'GET, POST, PUT, DELETE, OPTIONS', + "Content-Type": "application/json" + }, + //body: JSON.stringify({graph: this.state.graph, match: this.state.match, construct: this.state.construct}) + //body: JSON.stringify({query: this.state.query}) + body: JSON.stringify({query: this.state.query, limit:50}) + //JSON.stringify(this.state) + }).then(res => res.text()) + .then(json => { + this.setState({ + //answer: JSON.parse(json) + nodes: JSON.parse(json)['nodes'], + links: JSON.parse(json)['links'] + }) + console.log(JSON.parse(json)) + }).catch((err) => { + console.log(err); + }); + } + + render() { + return ( +
+ {/*
+ + +
*/} +
+ + Construct Query + {/* + + */} + + + +
+ Answer: + {this.state.nodes && + this.state.links && + } +
+ ); + } + +} + +export default ConstructQueryPanelWrapper; diff --git a/visual-explorer/app/src/components/Dashboard/ShinerTab/gcore/getneighbors-panel.tsx b/visual-explorer/app/src/components/Dashboard/ShinerTab/gcore/getneighbors-panel.tsx new file mode 100644 index 0000000..001c9f2 --- /dev/null +++ b/visual-explorer/app/src/components/Dashboard/ShinerTab/gcore/getneighbors-panel.tsx @@ -0,0 +1,112 @@ +import React, {Component} from 'react'; +import {Store} from "redux"; +import * as d3 from 'd3'; +import {IApplicationState} from "../../../../redux-types"; +import {Row, ToggleButton, ToggleButtonGroup} from "react-bootstrap"; + + /* # json format for "passing node information" + # { + # "nodeLabel": "ProductAmazon", + # "id": "1", + # "edgeLabel": "", + # "graphName": "Amazon", + # "limit": -1 + # } + #args for both select and graph neighbor*/ + + +interface GetNeighborPanelWrapperProps { + store: Store; +} + +interface GetNeighborPanelWrapperState { + graphName: string, + nodeLabel: string, + id: string, + edgeLabel: string, + limit: number + answer: string +} + + +/** + * App + * + * Simple react js fetch example + */ +class GetNeighborPanelWrapper extends React.Component { + + /** + * constructor + * + * @object @props parent props + * @object @state component state + */ + constructor(props: GetNeighborPanelWrapperProps) { + + super(props); + + this.state = { + graphName: "AmazonGoogle", + nodeLabel: "ProductGoogle", + id: "1", + edgeLabel: "", + limit: -1, + answer: "query answer" + } + } + + handleChangeGraph = (event: any) => { + const target = event.target + const value = target.value + this.setState({graphName: value}); + } + + handleChangeNode = (event: any) => { + const target = event.target + const value = target.value + this.setState({nodeLabel: value}); + } + + handleSubmit = (event: any) => { + event.preventDefault(); + fetch('http://127.0.0.1:3001/gcore/query/select-neighbor', { + method: 'POST', + // We convert the React state to JSON and send it as the POST body + // body: JSON.stringify(this.state) + headers: { + "Content-Type": "application/json" + }, + body: JSON.stringify({graphName: this.state.graphName, nodeLabel: this.state.nodeLabel, + edgeLabel: this.state.edgeLabel, id: this.state.id, limit: this.state.limit}) + // JSON.stringify(this.state) + }).then(res => res.text()) + .then(json => { + this.setState({ + answer: JSON.stringify(json) + }) + console.log(JSON.stringify(json)) + }).catch((err) => { + console.log(err); + }); + } + + render() { + return ( +
+
+ + +
+ Answer:{this.state.answer} +
+ ); + } + +} + +export default GetNeighborPanelWrapper; diff --git a/visual-explorer/app/src/components/Dashboard/ShinerTab/gcore/graphneighbor-panel.tsx b/visual-explorer/app/src/components/Dashboard/ShinerTab/gcore/graphneighbor-panel.tsx new file mode 100644 index 0000000..2cb16ca --- /dev/null +++ b/visual-explorer/app/src/components/Dashboard/ShinerTab/gcore/graphneighbor-panel.tsx @@ -0,0 +1,112 @@ +import React, {Component} from 'react'; +import {Store} from "redux"; +import * as d3 from 'd3'; +import {IApplicationState} from "../../../../redux-types"; +import {Row, ToggleButton, ToggleButtonGroup} from "react-bootstrap"; + + /* # json format for "passing node information" + # { + # "nodeLabel": "ProductAmazon", + # "id": "1", + # "edgeLabel": "", + # "graphName": "Amazon", + # "limit": -1 + # } + #args for both select and graph neighbor*/ + + +interface GetNeighborGraphPanelWrapperProps { + store: Store; +} + +interface GetNeighborGraphPanelWrapperState { + graphName: string, + nodeLabel: string, + id: string, + edgeLabel: string, + limit: number + answer: string +} + + +/** + * App + * + * Simple react js fetch example + */ +class GetNeighborGraphPanelWrapper extends React.Component { + + /** + * constructor + * + * @object @props parent props + * @object @state component state + */ + constructor(props: GetNeighborGraphPanelWrapperProps) { + + super(props); + + this.state = { + graphName: "AmazonGoogle", + nodeLabel: "ProductGoogle", + id: "1", + edgeLabel: "", + limit: -1, + answer: "query answer" + } + } + + handleChangeGraph = (event: any) => { + const target = event.target + const value = target.value + this.setState({graphName: value}); + } + + handleChangeNode = (event: any) => { + const target = event.target + const value = target.value + this.setState({nodeLabel: value}); + } + + handleSubmit = (event: any) => { + event.preventDefault(); + fetch('http://127.0.0.1:3001/gcore/query/select-neighbor', { + method: 'POST', + // We convert the React state to JSON and send it as the POST body + // body: JSON.stringify(this.state) + headers: { + "Content-Type": "application/json" + }, + body: JSON.stringify({graphName: this.state.graphName, nodeLabel: this.state.nodeLabel, + edgeLabel: this.state.edgeLabel, id: this.state.id, limit: this.state.limit}) + // JSON.stringify(this.state) + }).then(res => res.text()) + .then(json => { + this.setState({ + answer: JSON.stringify(json) + }) + console.log(JSON.stringify(json)) + }).catch((err) => { + console.log(err); + }); + } + + render() { + return ( +
+
+ + +
+ Answer:{this.state.answer} +
+ ); + } + +} + +export default GetNeighborGraphPanelWrapper; diff --git a/visual-explorer/app/src/components/Dashboard/ShinerTab/gcore/select-panel.tsx b/visual-explorer/app/src/components/Dashboard/ShinerTab/gcore/select-panel.tsx new file mode 100644 index 0000000..9e84a62 --- /dev/null +++ b/visual-explorer/app/src/components/Dashboard/ShinerTab/gcore/select-panel.tsx @@ -0,0 +1,121 @@ +import React, {Component} from 'react'; +import {Store} from "redux"; +import * as d3 from 'd3'; +import {IApplicationState} from "../../../../redux-types"; +import {Alert, Button, Form, FormGroup, Row, ToggleButton, ToggleButtonGroup} from "react-bootstrap"; +import TableDyn from "./Table"; +import {JsonFormatter} from "tslint/lib/formatters"; +// import JsonTable from 'react-json-table' + +interface SelectQueryPanelWrapperProps { + store: Store; +} + +interface SelectQueryPanelWrapperState { + // graph: string, + // table: string + query: string, + answer: any// string +} + + +/** + * App + * + * Simple react js fetch example + */ +class SelectQueryPanelWrapper extends React.Component { + + /** + * constructor + * + * @object @props parent props + * @object @state component state + */ + constructor(props: SelectQueryPanelWrapperProps) { + + super(props); + + this.state = { + // graph : "Graph name", + // table: "Label", + query: "SELECT * MATCH (p:Person) ON people_graph", + answer: [{'label': 'Label', 'id': 0}] + } + + } + + /*handleChangeGraph = (event: any) => { + const target = event.target + const value = target.value + this.setState({graph: value}); + } + + handleChangeTable = (event: any) => { + const target = event.target + const value = target.value + this.setState({table: value}); + }*/ + + handleChangeQuery = (event: any) => { + const target = event.target + const value = target.value + this.setState({query: value}); + } + + handleSubmit = (event: any) => { + event.preventDefault(); + fetch('http://127.0.0.1:3001/gcore/query/select', { + // mode: 'no-cors', + method: 'POST', + // We convert the React state to JSON and send it as the POST body + // body: JSON.stringify(this.state) + headers: { + 'Access-Control-Allow-Origin': '*', + 'Access-Control-Allow-Methods': 'GET, POST, PUT, DELETE, OPTIONS', + "Content-Type": "application/json" + }, + // body: JSON.stringify({graph: this.state.graph, table: this.state.table}) + body: JSON.stringify({query: this.state.query, limit:50}) + // JSON.stringify(this.state) + }).then(res => res.text()) + .then(json => { + this.setState({ + answer: JSON.parse(json) + }) + console.log(JSON.parse(json)) + }).catch((err) => { + console.log(err); + }); + } + + render() { + return ( +
+ {/*
+ + +
*/} +
+ + Select Query + {/* + */} + + + +
+ {/*Answer:{this.state.answer}*/} + ANSWER (Limit 50) + +
+ ); + } + +} + +export default SelectQueryPanelWrapper; diff --git a/visual-explorer/app/src/components/Dashboard/ShinerTab/graph/visNetwork.jsx b/visual-explorer/app/src/components/Dashboard/ShinerTab/graph/visNetwork.jsx new file mode 100644 index 0000000..8443604 --- /dev/null +++ b/visual-explorer/app/src/components/Dashboard/ShinerTab/graph/visNetwork.jsx @@ -0,0 +1,119 @@ +import React, { Component, createRef } from "react"; +import { DataSet, Network } from 'vis-network/standalone/umd/vis-network.min'; +import {Alert} from "react-bootstrap"; +import TableDyn from "../gcore/Table"; + +// Create an array with nodes +const nodes = new DataSet([ + {id: 1, label: 'Node 1'}, + {id: 2, label: 'Node 2'}, + {id: 3, label: 'Node 3'}, + {id: 4, label: 'Node 4'}, + {id: 5, label: 'Node 5'} +]); + +// Create an array with edges +const edges = new DataSet([ + {from: 1, to: 3}, + {from: 1, to: 2}, + {from: 2, to: 4}, + {from: 2, to: 5} +]); + +// Provide the data in the vis format +const data = { + nodes: nodes, + edges: edges +}; + +const myHeight = Math.round(parseInt(window.innerHeight)*0.5) + 'px'; + +const options = { + autoResize: true, + height: myHeight, + edges: { + arrows: { + to: { + enabled: true + } + } + } +}; + +// Initialize your network! +export default class VisNetwork extends React.Component { + + constructor(props) { + super(props); + console.log("vis network component!!!" + this.props.name) + //this.nodes = this.props.nodes; + //console.log("props::" + this.props.nodes) + //this.edges = this.props.edges; + this.name = this.props.name; + this.network = {}; + this.appRef = createRef(); + this.state = { info: [{'label': 'Label', 'id': 0}]} + this.data = { + nodes: JSON.parse(this.props.nodes), + edges: JSON.parse(this.props.edges) + } + } + + componentDidMount() { + this.name = this.props.name + this.data.nodes = JSON.parse(this.props.nodes); + this.data.edges = JSON.parse(this.props.edges); + //console.log("data here network!!"+ this.data.nodes) + if (this.data.nodes.length > 0) { + // console.log("data here network dentro!!"+ this.data.nodes.length) + this.network = new Network(this.appRef.current, this.data, options); + this.network.on("click",this.handleChange) + // console.log(this.network) + } + } + + handleChange = (networkEvents) => { + console.log(networkEvents) + //var nodesEvent = networkEvents.nodes + //var edgesEvent = networkEvents.edges + if(networkEvents.nodes.length > 0){ + let id = networkEvents.nodes[0] + let nodeInformation = JSON.parse(this.props.nodes).find(element => element['id'] === id) + console.log(JSON.parse(this.props.nodes).find(element => element['id'] === id)) + this.setState({ + info: Array.of(JSON.parse(this.props.nodes).find(element => element['id'] === id)) + //info: JSON.parse(this.props.nodes).find(element => element['id'] === id) + }) + }else if(networkEvents.edges.length > 0){ + let id = networkEvents.edges[0] + let edgeInformation = JSON.parse(this.props.edges).find(element => element['id'] === id) + let array = [] + this.setState({ + info: Array.of(JSON.parse(this.props.edges).find(element => element['id'] === id)) + //info: JSON.parse(this.props.edges).find(element => element['id'] === id) + }) + } + } + + componentDidUpdate(prevProps, prevState, snapshot) { + if(this.props.nodes !== prevProps.nodes && this.props.nodes.length > 0) // Check if it's a new user, you can also use some unique property, like the ID (this.props.user.id !== prevProps.user.id) + { + // alert("Change component!!!" + this.props.nodes) + this.componentDidMount(); + } + } + + render(){ + return ( +
+ + {console.log("This is information data" + this.state.info)} + + +
+
+
+
+ ); + } +} \ No newline at end of file diff --git a/visual-explorer/app/src/components/Dashboard/ShinerTab/index.tsx b/visual-explorer/app/src/components/Dashboard/ShinerTab/index.tsx new file mode 100644 index 0000000..2106167 --- /dev/null +++ b/visual-explorer/app/src/components/Dashboard/ShinerTab/index.tsx @@ -0,0 +1 @@ +export { default } from './ShinerTab'; diff --git a/visual-explorer/app/src/components/Dashboard/ShinerTab/panels/getggds-panel.tsx b/visual-explorer/app/src/components/Dashboard/ShinerTab/panels/getggds-panel.tsx new file mode 100644 index 0000000..ae48cad --- /dev/null +++ b/visual-explorer/app/src/components/Dashboard/ShinerTab/panels/getggds-panel.tsx @@ -0,0 +1,130 @@ +import 'bootstrap/scss/bootstrap.scss'; +import React from 'react'; +import {Alert, Button, Card, Col, Container, ListGroup, Row, ToggleButton, ToggleButtonGroup} from 'react-bootstrap'; +import './panels.scss'; +import {Store} from "redux"; +import {IApplicationState} from "../../../../redux-types"; +import VisNetwork from "../graph/visNetwork"; +import TableDyn from "../gcore/Table"; +import ConstraintTable from "../gcore/ConstraintTable"; + +interface GetGGDsPanelProps { + store: Store; +} + +interface GetGGDsPanelState { + ggds: string + json: any +} + +function getNodes(gp: any) { + const allVertices = gp.flatMap(function (gp: any) { + return gp.vertices + }) + const verticesString = JSON.stringify(allVertices).replace(/variable/g, "id") + console.log(verticesString) + return verticesString + return JSON.parse(verticesString) +} + +function getEdges(gp: any) { + const allEdges = gp.flatMap(function (gp: any) { + return gp.edges + }) + const edgesString = JSON.stringify(allEdges).replace(/variable/g, "id") + .replace(/fromVariable/g, "from") + .replace(/toVariable/g, "to") + console.log(edgesString) + return edgesString + return JSON.parse(edgesString) +} + +class GetGGDsPanel extends React.Component { + + constructor(props: GetGGDsPanelProps) { + super(props); + this.state = { + ggds: "", + json: [] + } + } + + render() { + + const onClickGGDs = (e: any) => { + console.log(e.target.value); // graph name + // if (e.target.value) this.props.setType(e.target.value); + fetch('http://127.0.0.1:3001/gcore/er/getggds') + .then(res => res.text()) + .then(json => { + if (JSON.parse(json).status === 'No ggds were uploaded in server!') { + alert('No ggds were uploaded in server!'); + } else { + this.setState({ + ggds: json, + json: JSON.parse(json) + }) + } + console.log(json) + // console.log(json) + }).catch((err) => { + console.log(err); + }); + };// make component to render graph schemas + + + return ( +
+ + {this.state.json.map(function (ggd: any) { + return ( +
+
{"GGD"}
+ + + + + + + + + + + + + {/* + */} +
+ ) + // let visNetwork = <>; + // return visNetwork + // + // ) + })} +
+ ); + } +} + +export default GetGGDsPanel; + +{/* + {this.state.json.map(function(ggd: any){ + return ( + {ggd.sourceGP[0].vertices[0].label} + ) + } + + )} + */ +} \ No newline at end of file diff --git a/visual-explorer/app/src/components/Dashboard/ShinerTab/panels/graphdb-panel-wrapper.tsx b/visual-explorer/app/src/components/Dashboard/ShinerTab/panels/graphdb-panel-wrapper.tsx new file mode 100644 index 0000000..2cbe605 --- /dev/null +++ b/visual-explorer/app/src/components/Dashboard/ShinerTab/panels/graphdb-panel-wrapper.tsx @@ -0,0 +1,134 @@ +import React, {Component} from 'react'; +import {Store} from "redux"; +import * as d3 from 'd3'; +import {IApplicationState} from "../../../../redux-types"; +import {Alert, Row, ToggleButton, ToggleButtonGroup} from "react-bootstrap"; +import VisNetwork from "../graph/visNetwork"; + +interface GraphdbPanelWrapperProps { + store: Store; +} + +interface GraphdbPanelWrapperState { + graphs: [], + graph: string, + isLoaded: boolean, + nodes?: { + label: string + property: [] + }[] + links?: { + label: string + property: [] + source: string + target: string + }[] +} + + +/** + * App + * + * Simple react js fetch example + */ +class GraphdbPanelWrapper extends React.Component { + + /** + * constructor + * + * @object @props parent props + * @object @state component state + */ + constructor(props: GraphdbPanelWrapperProps) { + + super(props); + + this.state = { + graphs: [], + isLoaded: false, + graph: "schema-graph", + // schema : undefined + nodes: undefined, + links: undefined + } + + } + + /** + * componentDidMount + * + * Fetch json array of objects from given url and update state. + */ + componentDidMount() { + + fetch('http://127.0.0.1:3001/gcore/availableGraphs') + .then(res => res.json()) + .then(json => { + this.setState({ + graphs: json, + isLoaded: true, + }) + }).catch((err) => { + console.log(err); + }); + + } + + /** + * render + * + * Render UI + */ + render() { + + const onGraphChangeFn = (e: any) => { + console.log(e.target.value); // graph name + // if (e.target.value) this.props.setType(e.target.value); + const graphValue = e.target.value; + fetch('http://127.0.0.1:3001/gcore/schema/' + e.target.value) + .then(res => res.json()) + .then(json => { + this.setState({ + nodes: json.nodes, + links: json.links + }) + console.log(json) + }).catch((err) => { + console.log(err); + }); + this.setState({ + graph: graphValue + }) + };// make component to render graph schemas + + const {isLoaded, graphs} = this.state; + + if (!isLoaded) + return
Loading...(check for connection or load database error) +
; + + return ( +
+ + {graphs.map(graph => ( + + {graph} + + ))} + + {this.state.nodes && + this.state.links && + } +
+ ); + + } + +} + +export default GraphdbPanelWrapper; diff --git a/visual-explorer/app/src/components/Dashboard/ShinerTab/panels/panels.scss b/visual-explorer/app/src/components/Dashboard/ShinerTab/panels/panels.scss new file mode 100644 index 0000000..039dafb --- /dev/null +++ b/visual-explorer/app/src/components/Dashboard/ShinerTab/panels/panels.scss @@ -0,0 +1,24 @@ +.panel-card { + width: 100%; + background-color: rgba(255, 255, 255, 0.95); + position: relative; + z-index: 2; +} + +.panel { + display: block; + position: absolute; + min-width: 300px; + width: 30%; + max-width: 400px; + + &.left { + top: 20px; + left: 20px; + } + + &.right { + top: 20px; + right: 20px; + } +} diff --git a/visual-explorer/app/src/components/Dashboard/ShinerTab/panels/runer-panel.tsx b/visual-explorer/app/src/components/Dashboard/ShinerTab/panels/runer-panel.tsx new file mode 100644 index 0000000..a1c3427 --- /dev/null +++ b/visual-explorer/app/src/components/Dashboard/ShinerTab/panels/runer-panel.tsx @@ -0,0 +1,80 @@ +import 'bootstrap/scss/bootstrap.scss'; +import React from 'react'; +import {Alert, Button, Card, ToggleButton, ToggleButtonGroup} from 'react-bootstrap'; +import './panels.scss'; +import {Store} from "redux"; +import {IApplicationState} from "../../../../redux-types"; +import TableDyn from "../gcore/Table"; + +interface RunerPanelProps { + store: Store; +} + +interface RunerPanelState { + status: string + info: any +} + +class RunERPanel extends React.Component { + + constructor(props: RunerPanelProps) { + super(props); + this.state = { + status: 'Click to run', + info: [{'name': 'Label', 'number': 0}] + } + } + + render() { + + const onClickRun = (e: any) => { + this.setState({ + status: 'Running....' + }) + // if (e.target.value) this.props.setType(e.target.value); + fetch('http://127.0.0.1:3001/gcore/er/run',{ + // mode: 'cors' + /* method: 'POST', + // We convert the React state to JSON and send it as the POST body + //body: JSON.stringify(this.state)*/ + headers: { + 'Access-Control-Allow-Origin': '*', + 'Access-Control-Allow-Methods': 'GET, POST, PUT, DELETE, OPTIONS', + "Content-Type": "application/json" + } + }) + .then(res => res.text()) + .then(json => { + this.setState({ + status: "Resulting graph name:" + JSON.parse(json)['graphName'], + info: JSON.parse(json)['resultInfo'] + }) + console.log(json) + }).catch((err) => { + console.log(err); + }); + };//make component to render graph schemas + + + return ( +
+ + + {this.state.status} + + +
+ ); + } +} + +export default RunERPanel; \ No newline at end of file diff --git a/visual-explorer/app/src/components/Dashboard/ShinerTab/panels/setggds-panel.tsx b/visual-explorer/app/src/components/Dashboard/ShinerTab/panels/setggds-panel.tsx new file mode 100644 index 0000000..ea7fc0f --- /dev/null +++ b/visual-explorer/app/src/components/Dashboard/ShinerTab/panels/setggds-panel.tsx @@ -0,0 +1,95 @@ +import 'bootstrap/scss/bootstrap.scss'; +import React from 'react'; +import {Button, Card, Form, FormGroup} from 'react-bootstrap'; +import './panels.scss'; +import {Store} from "redux"; +import {IApplicationState} from "../../../../redux-types"; +import {getBindingElementVariableDeclaration} from "tslint"; + +interface SetGGDsPanelProps { + store: Store; +} + +interface SetGGDsPanelState { + ggds: string + file: any +} + +class SetGGDsPanel extends React.Component { + + constructor(props: SetGGDsPanelProps) { + super(props); + this.state = { + ggds: 'Please write your GGDs here', + file: null + } + this.file = this.file.bind(this) + } + + handleChange = (event: any) => { + const target = event.target + const value = target.value + this.setState({ggds: value}); + } + +file = (evt: any) => { + // Retrieve the first (and only!) File from the FileList object + let f = evt.target.files[0]; + this.setState({file:evt.target.files[0]}) + if (f) { + let r = new FileReader(); + r.onload = (e:any) =>{ + // tslint:disable-next-line:prefer-const + var contents = e.target.result; + this.setState({ ggds: contents }); + // alert(contents); + } + r.readAsText(f); + } else{ + alert("Failed to load file"); + } + } + + handleSubmit = (event: any) => { + alert("GGDs submitted!"); + event.preventDefault(); + fetch('http://127.0.0.1:3001/gcore/er/setggds', { + method: 'POST', + // We convert the React state to JSON and send it as the POST body + // body: JSON.stringify(this.state) + headers: { + "Content-Type": "application/json" + }, + body: this.state.ggds// JSON.stringify(this.state.ggds) + }).then(res => res.text()) + .then(json => { + console.log(JSON.stringify(json)) + alert(JSON.stringify(json)) + // alert("Graph Generating Dependencies submitted!") + }).catch((err) => { + console.log(err); + }); + } + + render() { + return ( +
+ + Upload you GGDs JSON file here: + + {/**/} + + +
+ /*
+