Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

added uploading dataset and drag & drop support #317

Open
wants to merge 39 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from 11 commits
Commits
Show all changes
39 commits
Select commit Hold shift + click to select a range
d126f34
added uploading dataset and drag & drop support
AjayThorve Oct 18, 2021
0a0616e
fix client-server bug
AjayThorve Oct 18, 2021
19c189a
added server side pagination API
AjayThorve Oct 19, 2021
e0a39c0
create dir if not exists
AjayThorve Oct 19, 2021
69d5ce9
Merge branch 'main' into demo/viz-app/improvements
AjayThorve Oct 21, 2021
f0fa54d
added redbird node based reverse-proxying
AjayThorve Oct 22, 2021
90e5eb3
apply suggestions
AjayThorve Oct 22, 2021
6a919aa
update readme instructions
AjayThorve Oct 22, 2021
ce3cdaa
typo
AjayThorve Oct 22, 2021
5dc3eda
change front-end app port
AjayThorve Oct 22, 2021
59d279a
add missing license text
AjayThorve Oct 22, 2021
8058756
update rest api routes as per suggestions
AjayThorve Oct 27, 2021
df6f994
fix route name
AjayThorve Oct 27, 2021
0625236
removed unnecessary condition
AjayThorve Oct 27, 2021
f7f0d74
bug fixes and improvements
AjayThorve Oct 28, 2021
8ba7977
sending arrow tables to the client directly
AjayThorve Oct 28, 2021
0bac16f
update route
AjayThorve Oct 28, 2021
e28eb5a
typo fix route
AjayThorve Oct 28, 2021
f589db7
update apache-arrow version
AjayThorve Oct 28, 2021
6420370
revert apache arrow version update
AjayThorve Oct 28, 2021
cba5b22
add apache-arrow based streaming
AjayThorve Oct 29, 2021
5881444
add updated viz to html front-end as well
AjayThorve Oct 29, 2021
5c504ea
handle cases where numRows is less than pageIndex*pageSize
AjayThorve Oct 29, 2021
8174c67
Update modules/demo/viz-app/pages/demo/graph.jsx
AjayThorve Nov 11, 2021
765e61e
Merge branch 'main' into demo/viz-app/improvements
AjayThorve Jan 19, 2022
eb95097
Merge branch 'main' of github.com:rapidsai/node into demo/viz-app/imp…
trxcllnt Jan 20, 2022
bd535a5
update math.gl version
trxcllnt Jan 20, 2022
b7fde93
Merge branch 'main' of github.com:rapidsai/node into demo/viz-app/imp…
trxcllnt Jan 24, 2022
b4dc58a
Merge branch 'main' of github.com:rapidsai/node into demo/viz-app/imp…
trxcllnt Jan 27, 2022
5718e16
add missing parameters
trxcllnt Jan 27, 2022
47de288
add missing const and script type
trxcllnt Jan 27, 2022
48de9ca
fix graph viz column dtypes
trxcllnt Jan 27, 2022
b59926c
Update yarn.lock
trxcllnt Jan 27, 2022
b6a27f3
separate makeDeck from the render class, and generalize the render mo…
AjayThorve Feb 4, 2022
e3ec1a0
Merge branch 'main' into demo/viz-app/improvements
AjayThorve Feb 4, 2022
27bb83f
Merge branch 'main' into demo/viz-app/improvements
AjayThorve Feb 28, 2022
e23304f
fix graph api
AjayThorve Feb 28, 2022
9ca9a98
update forceatlas2 calculations
AjayThorve Feb 28, 2022
d3be34c
fix positions
AjayThorve Feb 28, 2022
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions modules/demo/client-server/components/server/cache.js
Original file line number Diff line number Diff line change
Expand Up @@ -93,11 +93,11 @@ readMortgageData() {
sources: [path.resolve('./public', 'data/mortgage.csv')],
dataTypes: {
index: new Int16,
zip: new Int32,
zip: new Uint32,
dti: new Float32,
current_actual_upb: new Float32,
borrower_credit_score: new Int16,
load_id: new Int32,
load_id: new Uint32,
delinquency_12_prediction: new Float32,
seller_name: new Int16
}
Expand Down
6 changes: 5 additions & 1 deletion modules/demo/ssr/graph/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,10 @@ const fastify = require('fastify')();
fastify //
.register(require('./plugins/webrtc'), require('./plugins/graph')(fastify))
.register(require('fastify-static'), {root: require('path').join(__dirname, 'public')})
.get('/', (req, reply) => reply.sendFile('video.html'));
.register(require('fastify-multipart'))
.register(require('fastify-cors'), {})
.register((require('fastify-arrow')))
.register(require('./plugins/api'))
.get('/', (req, reply) => reply.sendFile('video.html'))

fastify.listen(8080).then(() => console.log('server ready'));
6 changes: 5 additions & 1 deletion modules/demo/ssr/graph/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -21,11 +21,15 @@
"fastify-socket.io": "2.0.0",
"fastify-static": "4.2.3",
"fastify": "3.20.2",
"fastify-multipart": "5.0.2",
"fastify-cors": "6.0.2",
"fastify-arrow": "0.1.0",
"nanoid": "3.1.25",
"rxjs": "6.6.7",
"shm-typed-array": "0.0.13",
"simple-peer": "9.11.0",
"socket.io": "4.1.3"
"socket.io": "4.1.3",
"glob": "7.2.0"
},
"files": [
"render",
Expand Down
249 changes: 249 additions & 0 deletions modules/demo/ssr/graph/plugins/api/index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,249 @@
// Copyright (c) 2021, NVIDIA CORPORATION.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

const {graphs, clients} = require('../graph');
const fs = require('fs')
const util = require('util')
const {pipeline} = require('stream')
const pump = util.promisify(pipeline)
var glob = require('glob');
const {Float32Buffer} = require('@rapidsai/cuda');
const {GraphCOO} = require('@rapidsai/cugraph');
const {DataFrame, Series, Int32, Uint64, Float64} = require('@rapidsai/cudf');
const {loadEdges, loadNodes} = require('../graph/loader');
const {RecordBatchStreamWriter} = require('apache-arrow');

function readDataFrame(path) {
if (path.indexOf('.csv', path.length - 4) !== -1) {
// csv file
return DataFrame.readCSV({sources: [path], header: 0, sourceType: 'files'});

} else if (path.indexOf('.parquet', path.length - 8) !== -1) {
// csv file
return DataFrame.readParquet({sources: [path]});
}
// if (df.names.includes('Unnamed: 0')) { df = df.cast({'Unnamed: 0': new Uint32}); }
return new DataFrame({});
}

async function getNodesForGraph(asDeviceMemory, nodes, numNodes) {
let nodesRes = {};
const pos = new Float32Buffer(Array.from(
{length: numNodes * 2},
() => Math.random() * 1000 * (Math.random() < 0.5 ? -1 : 1),
));

if (nodes.x in nodes.dataframe.names) {
nodesRes.nodeXPositions = asDeviceMemory(nodes.dataframe.get(node.x).data);
} else {
nodesRes.nodeXPositions = pos.subarray(0, pos.length / 2);
}
if (nodes.y in nodes.dataframe.names) {
nodesRes.nodeYPositions = asDeviceMemory(nodes.dataframe.get(node.y).data);
} else {
nodesRes.nodeYPositions = pos.subarray(pos.length / 2);
}
if (nodes.dataframe.names.includes(nodes.size)) {
nodesRes.nodeRadius = asDeviceMemory(nodes.dataframe.get(nodes.size).cast(new Float64).data);
}
if (nodes.dataframe.names.includes(nodes.color)) {
nodesRes.nodeFillColors =
asDeviceMemory(nodes.dataframe.get(nodes.color).cast(new Float64).data);
}
if (nodes.dataframe.names.includes(nodes.id)) {
nodesRes.nodeElementIndices =
asDeviceMemory(nodes.dataframe.get(nodes.id).cast(new Uint64).data);
}
return nodesRes;
}

async function getEdgesForGraph(asDeviceMemory, edges) {
let edgesRes = {};

if (edges.dataframe.names.includes(edges.color)) {
edgesRes.edgeColors = asDeviceMemory(edges.dataframe.get(edges.color).data);
} else {
edgesRes.edgeColors = asDeviceMemory(
Series
.sequence(
{type: new Uint64, size: edges.dataframe.numRows, init: 18443486512814075489n, step: 0})
.data);
}
if (edges.dataframe.names.includes(edges.id)) {
edgesRes.edgeList = asDeviceMemory(edges.dataframe.get(edges.id).cast(new Uint64).data);
}
if (edges.dataframe.names.includes(edges.bundle)) {
edgesRes.edgeBundles = asDeviceMemory(edges.dataframe.get(edges.bundle).data);
}
return edgesRes;
}

async function getPaginatedRows(df, pageIndex = 1, pageSize = 400, selected = []) {
console.log(selected);
AjayThorve marked this conversation as resolved.
Show resolved Hide resolved
if (selected.length == 0) {
return [df.head(pageIndex * pageSize).tail(pageSize).toArrow(), df.numRows];
AjayThorve marked this conversation as resolved.
Show resolved Hide resolved
}
const selectedSeries = Series.new({type: new Int32, data: selected});
let dfUpdated = df.gather(selectedSeries);
return [dfUpdated.head(pageIndex * pageSize).tail(pageSize).toArrow(), dfUpdated.numRows];
AjayThorve marked this conversation as resolved.
Show resolved Hide resolved
}

module.exports = function(fastify, opts, done) {
// fastify.addHook('preValidation', (request, reply, done) => {
// console.log('this is executed', request);
// done()
// });
AjayThorve marked this conversation as resolved.
Show resolved Hide resolved

async function loadGraph(id, data) {
if (!(id in fastify[graphs])) {
const asDeviceMemory = (buf) => new (buf[Symbol.species])(buf);
const src = data.edges.dataframe.get(data.edges.src);
const dst = data.edges.dataframe.get(data.edges.dst);
const graph = new GraphCOO(src._col, dst._col, {directedEdges: true});
fastify[graphs][id] = {
refCount: 0,
nodes: await getNodesForGraph(asDeviceMemory, data.nodes, graph.numNodes()),
edges: await getEdgesForGraph(asDeviceMemory, data.edges),
graph: graph,
};
}

++fastify[graphs][id].refCount;

return {
gravity: 0.0,
linLogMode: false,
scalingRatio: 5.0,
barnesHutTheta: 0.0,
jitterTolerance: 0.05,
strongGravityMode: false,
outboundAttraction: false,
graph: fastify[graphs][id].graph,
nodes: {
...fastify[graphs][id].nodes,
length: fastify[graphs][id].graph.numNodes(),
},
edges: {
...fastify[graphs][id].edges,
length: fastify[graphs][id].graph.numEdges(),
},
};
}

fastify.get('/getIDValue', async (request, reply) => {
console.log(fastify[clients][request.query.id + ':video']);
reply.send(fastify[clients][request.query.id + ':video'].graph.dataframes[0].numRows);
});
AjayThorve marked this conversation as resolved.
Show resolved Hide resolved

fastify.post('/uploadFile', async function(req, reply) {
const data = await req.file();
const basePath = `${__dirname}/../../data/`;
if (!fs.existsSync(basePath)) { fs.mkdirSync(basePath); }
AjayThorve marked this conversation as resolved.
Show resolved Hide resolved
const filepath = `${basePath}${data.filename}`;
AjayThorve marked this conversation as resolved.
Show resolved Hide resolved
const target = fs.createWriteStream(filepath);
try {
await pump(data.file, target);
console.log('success');
} catch (err) { console.log(err); }
reply.send()
});

fastify.get('/getFileNames', async (request, reply) => {
if (`${request.query.id}:video` in fastify[clients]) {
glob(`*.{csv,parquet}`,
{cwd: `${__dirname}/../../data/`},
(er, files) => { reply.send(JSON.stringify(files.concat(['defaultExample']))); });
} else {
reply.code(500).send('client handshake not established');
}
});
AjayThorve marked this conversation as resolved.
Show resolved Hide resolved

fastify.get('/loadOnGPU', async (request, reply) => {
const id = `${request.query.id}:video`;
const filePath = `${__dirname}/../../data/`
if (id in fastify[clients]) {
if (fs.existsSync(`${filePath}${request.query.nodes}`) &&
fs.existsSync(`${filePath}${request.query.edges}`)) {
fastify[clients][id].data.nodes.dataframe =
await readDataFrame(`${filePath}${request.query.nodes}`);

fastify[clients][id].data.edges.dataframe =
await readDataFrame(`${filePath}${request.query.edges}`);
} else {
fastify[clients][id].data.nodes.dataframe = await loadNodes();
fastify[clients][id].data.edges.dataframe = await loadEdges();
}
if (fastify[clients][id].data.nodes.dataframe.numRows == 0) {
reply.code(500).send('no dataframe loaded');
}
reply.send(JSON.stringify({
'nodes': fastify[clients][id].data.nodes.dataframe.numRows,
'edges': fastify[clients][id].data.edges.dataframe.numRows
}));
}
else {
reply.code(500).send('client handshake not established');
}
})
AjayThorve marked this conversation as resolved.
Show resolved Hide resolved

fastify.get('/fetchDFParameters', async (request, reply) => {
const id = `${request.query.id}:video`;
if (id in fastify[clients]) {
reply.send(JSON.stringify({
nodesParams: fastify[clients][id].data.nodes.dataframe.names.concat([null]),
edgesParams: fastify[clients][id].data.edges.dataframe.names.concat([null])
}));
} else {
reply.code(500).send('client handshake not established');
}
});

fastify.post('/updateRenderColumns', async (request, reply) => {
const id = `${request.body.id}:video`;
if (id in fastify[clients]) {
Object.assign(fastify[clients][id].data.nodes, request.body.nodes);
Object.assign(fastify[clients][id].data.edges, request.body.edges);
fastify[clients][id].graph = await loadGraph('default', fastify[clients][id].data);
reply.code(200).send();
} else {
reply.code(500).send('client handshake not established');
}
});

fastify.post('/fetchPaginatedData', async (request, reply) => {
const id = `${request.body.id}:video`;
if (id in fastify[clients]) {
const pageIndex = request.body.pageIndex;
const pageSize = request.body.pageSize;
const dataframe = request.body.dataframe; //{'nodes', 'edges'}
const [res, numRows] =
await getPaginatedRows(fastify[clients][id].data[dataframe].dataframe,
pageIndex,
pageSize,
fastify[clients][id].state.selectedInfo[dataframe]);

reply.send(JSON.stringify({page: res.toArray(), numRows: numRows}));
// try {
// // RecordBatchStreamWriter.writeAll(res).pipe(reply.stream());
// } catch (err) {
// request.log.error({err}, '/run_query error');
// reply.code(500).send(err);
// }
AjayThorve marked this conversation as resolved.
Show resolved Hide resolved
} else {
reply.code(500).send('client handshake not established');
}
});

done();
}
Loading