Skip to content
This repository has been archived by the owner on Apr 4, 2024. It is now read-only.

Commit

Permalink
Merge pull request #249 from codaco/feature/export-entity-type
Browse files Browse the repository at this point in the history
export node and edge types
  • Loading branch information
wwqrd committed Jun 17, 2019
2 parents cb9d49d + 2d234f0 commit 0fb591f
Show file tree
Hide file tree
Showing 7 changed files with 62 additions and 20 deletions.
18 changes: 9 additions & 9 deletions src/main/utils/formatters/__tests__/attribute-list-test.js
Expand Up @@ -18,51 +18,51 @@ describe('toCSVStream', () => {
});

it('writes a simple CSV', async () => {
toCSVStream([{ _uid: 1, _egoID: 2, attributes: { name: 'Jane' } }], writable);
toCSVStream([{ _uid: 1, _egoID: 2, type: 'type', attributes: { name: 'Jane' } }], writable);
const csv = await writable.asString();
expect(csv).toEqual('networkCanvasAlterID,name\r\n1,Jane\r\n');
expect(csv).toEqual('networkCanvasAlterID,networkCanvasNodeType,name\r\n1,type,Jane\r\n');
});

it('escapes quotes', async () => {
toCSVStream([{ _uid: 1, attributes: { nickname: '"Nicky"' } }], writable);
const csv = await writable.asString();
expect(csv).toEqual('networkCanvasAlterID,nickname\r\n1,"""Nicky"""\r\n');
expect(csv).toEqual('networkCanvasAlterID,networkCanvasNodeType,nickname\r\n1,,"""Nicky"""\r\n');
});

it('escapes quotes in attr names', async () => {
toCSVStream([{ _uid: 1, attributes: { '"quoted"': 1 } }], writable);
const csv = await writable.asString();
expect(csv).toEqual('networkCanvasAlterID,"""quoted"""\r\n1,1\r\n');
expect(csv).toEqual('networkCanvasAlterID,networkCanvasNodeType,"""quoted"""\r\n1,,1\r\n');
});

it('stringifies and quotes objects', async () => {
toCSVStream([{ _uid: 1, attributes: { location: { x: 1, y: 1 } } }], writable);
const csv = await writable.asString();
expect(csv).toEqual('networkCanvasAlterID,location\r\n1,"{""x"":1,""y"":1}"\r\n');
expect(csv).toEqual('networkCanvasAlterID,networkCanvasNodeType,location\r\n1,,"{""x"":1,""y"":1}"\r\n');
});

it('exports undefined values as blank', async () => {
toCSVStream([{ _uid: 1, attributes: { prop: undefined } }], writable);
const csv = await writable.asString();
expect(csv).toEqual('networkCanvasAlterID,prop\r\n1,\r\n');
expect(csv).toEqual('networkCanvasAlterID,networkCanvasNodeType,prop\r\n1,,\r\n');
});

it('exports null values as blank', async () => {
toCSVStream([{ _uid: 1, attributes: { prop: null } }], writable);
const csv = await writable.asString();
expect(csv).toEqual('networkCanvasAlterID,prop\r\n1,\r\n');
expect(csv).toEqual('networkCanvasAlterID,networkCanvasNodeType,prop\r\n1,,\r\n');
});

it('exports `false` values as "false"', async () => {
toCSVStream([{ _uid: 1, attributes: { prop: false } }], writable);
const csv = await writable.asString();
expect(csv).toEqual('networkCanvasAlterID,prop\r\n1,false\r\n');
expect(csv).toEqual('networkCanvasAlterID,networkCanvasNodeType,prop\r\n1,,false\r\n');
});

it('exports egoID', async () => {
toCSVStream([{ _uid: 1, _egoID: 2, attributes: { prop: false } }], writable, true);
const csv = await writable.asString();
expect(csv).toEqual('networkCanvasEgoID,networkCanvasAlterID,prop\r\n2,1,false\r\n');
expect(csv).toEqual('networkCanvasEgoID,networkCanvasAlterID,networkCanvasNodeType,prop\r\n2,1,,false\r\n');
});
});

Expand Down
18 changes: 9 additions & 9 deletions src/main/utils/formatters/__tests__/edge-list-test.js
Expand Up @@ -8,11 +8,11 @@ describe('asEdgeList', () => {
it('takes a network as input', () => {
const network = {
nodes: [],
edges: [{ _uid: 456, from: 'nodeA', to: 'nodeB', attributes: {} }],
edges: [{ _uid: 456, from: 'nodeA', to: 'nodeB', type: 'type', attributes: {} }],
ego: { _uid: 123 },
};
expect(asEdgeList(network)[0]).toEqual(
{ _uid: 456, to: 'nodeB', from: 'nodeA', attributes: {} },
{ _uid: 456, to: 'nodeB', from: 'nodeA', type: 'type', attributes: {} },
);
});

Expand Down Expand Up @@ -48,31 +48,31 @@ describe('toCSVStream', () => {
});

it('Writes a simple csv', async () => {
const list = listFromEdges([{ _uid: 123, from: 1, to: 2 }]);
const list = listFromEdges([{ _uid: 123, type: 'type', from: 1, to: 2 }]);
toCSVStream(list, writable);
const csv = await writable.asString();
expect(csv).toEqual('networkCanvasEdgeID,networkCanvasSource,networkCanvasTarget\r\n291,1,2\r\n291,2,1\r\n');
expect(csv).toEqual('networkCanvasEdgeID,networkCanvasEdgeType,networkCanvasSource,networkCanvasTarget\r\n291,type,1,2\r\n291,type,2,1\r\n');
});

it('Writes multiple edges', async () => {
const list = listFromEdges([{ from: 1, to: 2 }, { from: 1, to: 3 }]);
toCSVStream(list, writable);
const csv = await writable.asString();
expect(csv).toEqual('networkCanvasEdgeID,networkCanvasSource,networkCanvasTarget\r\n,1,2\r\n,2,1\r\n,1,3\r\n,3,1\r\n');
expect(csv).toEqual('networkCanvasEdgeID,networkCanvasEdgeType,networkCanvasSource,networkCanvasTarget\r\n,,1,2\r\n,,2,1\r\n,,1,3\r\n,,3,1\r\n');
});

it('Writes a csv for directed edges', async () => {
const list = listFromEdges([{ from: 1, to: 2 }, { from: 1, to: 3 }], true);
toCSVStream(list, writable);
const csv = await writable.asString();
expect(csv).toEqual('networkCanvasEdgeID,networkCanvasSource,networkCanvasTarget\r\n,1,2\r\n,1,3\r\n');
expect(csv).toEqual('networkCanvasEdgeID,networkCanvasEdgeType,networkCanvasSource,networkCanvasTarget\r\n,,1,2\r\n,,1,3\r\n');
});

it('Writes a csv for directed edges (inverse)', async () => {
const list = listFromEdges([{ from: 1, to: 2 }, { from: 3, to: 1 }], true);
toCSVStream(list, writable);
const csv = await writable.asString();
expect(csv).toEqual('networkCanvasEdgeID,networkCanvasSource,networkCanvasTarget\r\n,1,2\r\n,3,1\r\n');
expect(csv).toEqual('networkCanvasEdgeID,networkCanvasEdgeType,networkCanvasSource,networkCanvasTarget\r\n,,1,2\r\n,,3,1\r\n');
});

it('Writes a csv with egoID', async () => {
Expand All @@ -82,14 +82,14 @@ describe('toCSVStream', () => {
);
toCSVStream(list, writable, true);
const csv = await writable.asString();
expect(csv).toEqual('networkCanvasEgoID,networkCanvasEdgeID,networkCanvasSource,networkCanvasTarget\r\n291,,1,2\r\n1110,,3,1\r\n');
expect(csv).toEqual('networkCanvasEgoID,networkCanvasEdgeID,networkCanvasEdgeType,networkCanvasSource,networkCanvasTarget\r\n291,,,1,2\r\n1110,,,3,1\r\n');
});

it('Writes a csv with attributes', async () => {
const list = listFromEdges([{ from: 1, to: 2, attributes: { a: 1 } }], true);
toCSVStream(list, writable);
const csv = await writable.asString();
expect(csv).toEqual('networkCanvasEdgeID,networkCanvasSource,networkCanvasTarget,a\r\n,1,2,1\r\n');
expect(csv).toEqual('networkCanvasEdgeID,networkCanvasEdgeType,networkCanvasSource,networkCanvasTarget,a\r\n,,1,2,1\r\n');
});
});

Expand Down
7 changes: 6 additions & 1 deletion src/main/utils/formatters/attribute-list.js
@@ -1,6 +1,6 @@
const { Readable } = require('stream');

const { convertUuidToDecimal, nodePrimaryKeyProperty, nodeAttributesProperty, egoProperty, processEntityVariables } = require('./network');
const { convertUuidToDecimal, nodePrimaryKeyProperty, nodeAttributesProperty, entityTypeProperty, egoProperty, processEntityVariables } = require('./network');
const { cellValue, csvEOL } = require('./csv');

const asAttributeList = (network, _, codebook) => {
Expand All @@ -23,6 +23,7 @@ const attributeHeaders = (nodes, withEgo) => {
initialHeaderSet.add(egoProperty);
}
initialHeaderSet.add(nodePrimaryKeyProperty);
initialHeaderSet.add(entityTypeProperty);

const headerSet = nodes.reduce((headers, node) => {
Object.keys(node[nodeAttributesProperty] || []).forEach((key) => {
Expand All @@ -39,6 +40,8 @@ const getPrintableAttribute = (attribute) => {
return 'networkCanvasEgoID';
case nodePrimaryKeyProperty:
return 'networkCanvasAlterID';
case entityTypeProperty:
return 'networkCanvasNodeType';
default:
return attribute;
}
Expand Down Expand Up @@ -67,6 +70,8 @@ const toCSVStream = (nodes, outStream, withEgo = false) => {
let value;
if (attrName === nodePrimaryKeyProperty || attrName === egoProperty) {
value = convertUuidToDecimal(node[attrName]);
} else if (attrName === entityTypeProperty) {
value = node.type;
} else {
value = node[nodeAttributesProperty][attrName];
}
Expand Down
7 changes: 6 additions & 1 deletion src/main/utils/formatters/edge-list.js
@@ -1,7 +1,7 @@
const { Readable } = require('stream');

const { cellValue, csvEOL } = require('./csv');
const { convertUuidToDecimal, nodePrimaryKeyProperty, egoProperty, nodeAttributesProperty, processEntityVariables } = require('./network');
const { convertUuidToDecimal, nodePrimaryKeyProperty, entityTypeProperty, egoProperty, nodeAttributesProperty, processEntityVariables } = require('./network');

/**
* Builds an edge list for a network, based only on its edges (it need
Expand Down Expand Up @@ -51,6 +51,7 @@ const attributeHeaders = (edges, withEgo) => {
initialHeaderSet.add(egoProperty);
}
initialHeaderSet.add(nodePrimaryKeyProperty);
initialHeaderSet.add(entityTypeProperty);
initialHeaderSet.add('from');
initialHeaderSet.add('to');

Expand All @@ -73,6 +74,8 @@ const getPrintableAttribute = (attribute) => {
return 'networkCanvasSource';
case 'to':
return 'networkCanvasTarget';
case entityTypeProperty:
return 'networkCanvasEdgeType';
default:
return attribute;
}
Expand Down Expand Up @@ -112,6 +115,8 @@ const toCSVStream = (edges, outStream, withEgo = false) => {
if (attrName === nodePrimaryKeyProperty || attrName === egoProperty ||
attrName === 'to' || attrName === 'from') {
value = convertUuidToDecimal(edge[attrName]);
} else if (attrName === entityTypeProperty) {
value = edge.type;
} else {
value = edge[nodeAttributesProperty][attrName];
}
Expand Down
10 changes: 10 additions & 0 deletions src/main/utils/formatters/graphml/__tests__/createGraphML-test.js
Expand Up @@ -57,6 +57,16 @@ describe('buildGraphML', () => {
expect(xml.getElementsByTagName('node')).toHaveLength(2);
});

it('adds node type', () => {
const node = xml.getElementsByTagName('node')[0];
expect(node.getElementsByTagName('data')[0].textContent).toEqual('person');
});

it('adds edge type', () => {
const edge = xml.getElementsByTagName('edge')[0];
expect(edge.getElementsByTagName('data')[1].textContent).toEqual('mock-uuid-3');
});

it('adds edges', () => {
expect(xml.getElementsByTagName('edge')).toHaveLength(1);
});
Expand Down
20 changes: 20 additions & 0 deletions src/main/utils/formatters/graphml/createGraphML.js
Expand Up @@ -99,6 +99,23 @@ const generateKeyElements = (
fragment.appendChild(label);
}

// add type
if (type === 'edge') {
const edgeType = document.createElement('key');
edgeType.setAttribute('id', 'networkCanvasEdgeType');
edgeType.setAttribute('attr.name', 'networkCanvasEdgeType');
edgeType.setAttribute('attr.type', 'string');
edgeType.setAttribute('for', type);
fragment.appendChild(edgeType);
} else {
const nodeType = document.createElement('key');
nodeType.setAttribute('id', 'networkCanvasNodeType');
nodeType.setAttribute('attr.name', 'networkCanvasNodeType');
nodeType.setAttribute('attr.type', 'string');
nodeType.setAttribute('for', type);
fragment.appendChild(nodeType);
}

entities.forEach((element) => {
let iterableElement = element;
iterableElement = getEntityAttributes(element);
Expand Down Expand Up @@ -209,6 +226,7 @@ const generateDataElements = (
|| codebook[type][dataElement.type].label);

domElement.appendChild(createDataElement(document, 'label', label));
domElement.appendChild(createDataElement(document, 'networkCanvasEdgeType', dataElement.type));

Object.keys(dataElement).forEach((key) => {
const keyName = getTypeFromCodebook(codebook, type, dataElement, key, 'name') || key;
Expand All @@ -225,6 +243,8 @@ const generateDataElements = (
}
}
});
} else {
domElement.appendChild(createDataElement(document, 'networkCanvasNodeType', dataElement.type));
}

// Add entity attributes
Expand Down
2 changes: 2 additions & 0 deletions src/main/utils/formatters/network.js
Expand Up @@ -8,6 +8,7 @@ const getFilter = require('../network-query/filter').default;
const nodePrimaryKeyProperty = '_uid';
const egoProperty = '_egoID';
const caseProperty = '_caseID';
const entityTypeProperty = '_type'; // NC sends as 'type' at the top level, but this will allow us to also look for a user attribute named type

const nodeAttributesProperty = 'attributes';

Expand Down Expand Up @@ -130,6 +131,7 @@ module.exports = {
getEntityAttributes,
insertEgoInNetworks,
nodeAttributesProperty,
entityTypeProperty,
egoProperty,
caseProperty,
nodePrimaryKeyProperty,
Expand Down

0 comments on commit 0fb591f

Please sign in to comment.