Skip to content

Commit

Permalink
Merge pull request #498 from tone-row/dev
Browse files Browse the repository at this point in the history
v1.32.0
  • Loading branch information
rob-gordon authored Apr 13, 2023
2 parents 509304f + 2e6cdf9 commit a5a25f8
Show file tree
Hide file tree
Showing 50 changed files with 5,442 additions and 1,549 deletions.
564 changes: 564 additions & 0 deletions api/data/_mapDataToGraph.test.ts

Large diffs are not rendered by default.

170 changes: 170 additions & 0 deletions api/data/_mapDataToGraph.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,170 @@
import { FeatureData, Node, Edge, Graph } from "graph-selector";
import { ImportDataFormType } from "shared";

/**
* Takes Data and a mapping object and converts it into a graph which can be
* stringified and returned to the client.
*/
export function mapDataToGraph(
data: any[],
mapping: ImportDataFormType
): Graph {
// Create an empty list of nodes and edges
const nodes: Node[] = [];
const edges: Edge[] = [];

// create a function to know whether to create a node or not
// this only changes if edges are declared
// in separate rows because then the record may not contain a node
let isRecordEdgeOnly: (record: any) => boolean;
if (mapping.edgesDeclared === "separateRows") {
isRecordEdgeOnly = (record: any) => {
if (mapping.rowRepresentsEdgeWhenIs === "notEmpty") {
return record[mapping.rowRepresentsEdgeWhenColumn] !== "";
} else if (mapping.rowRepresentsEdgeWhenIs === "empty") {
return record[mapping.rowRepresentsEdgeWhenColumn] === "";
} else if (mapping.rowRepresentsEdgeWhenIs === "equals") {
return (
record[mapping.rowRepresentsEdgeWhenColumn] ===
mapping.rowRepresentsEdgeWhenValue
);
}
return false;
};
} else {
isRecordEdgeOnly = () => false;
}

// Iterate over the data records
for (const record of data) {
// Extract the ID, node label, and any additional data for the node
const id = record[mapping.idColumn];

const edgeOnly = isRecordEdgeOnly(record);

// only do node stuff if this doesn't represent an edge when edges are in their own row
if (!edgeOnly) {
const nodeLabel = mapping.nodeLabelColumn
? record[mapping.nodeLabelColumn]
: "";
const nodeData: FeatureData = {
id,
classes: "",
label: nodeLabel,
// ...record,
};

// Create a node object and add it to the list of nodes
const node: Node = { data: nodeData };
nodes.push(node);
}

// Create edge if there is an edge in this record
if (mapping.edgesDeclared === "sourceNode") {
const { targetColumn, targetDelimiter, edgeLabelColumn } = mapping;
// make sure target column isn't an empty string
if (!targetColumn) continue;

// Get array of target id's
let targetIds = targetDelimiter
? record[targetColumn].split(targetDelimiter)
: [record[targetColumn]];

// Get array of edge labels
let edgeLabels: string[] = [];
if (edgeLabelColumn) {
edgeLabels = targetDelimiter
? record[edgeLabelColumn].split(targetDelimiter)
: [record[edgeLabelColumn]];
}

// Remove empty strings from the list of target IDs and edge labels
targetIds = targetIds.filter(Boolean);
edgeLabels = edgeLabels.filter(Boolean);

// Create edges
for (let i = 0; i < targetIds.length; i++) {
const targetId = targetIds[i];
const edgeLabel = edgeLabels[i] ?? "";
const edgeData: FeatureData = {
// Edge Id's can be empty, they'll be added when stringified
id: "",
classes: "",
label: edgeLabel,
// ...record,
};
const edge: Edge = {
source: id,
target: targetId,
data: edgeData,
};
edges.push(edge);
}
} else if (mapping.edgesDeclared === "targetNode") {
const { sourceColumn, sourceDelimiter, edgeLabelColumn } = mapping;
// make sure source column isn't an empty string
if (!sourceColumn) continue;

// Get array of source id's
let sourceIds = sourceDelimiter
? record[sourceColumn].split(sourceDelimiter)
: [record[sourceColumn]];

// Get array of edge labels
let edgeLabels: string[] = [];
if (edgeLabelColumn) {
edgeLabels = sourceDelimiter
? record[edgeLabelColumn].split(sourceDelimiter)
: [record[edgeLabelColumn]];
}

// Remove empty strings from the list of source IDs and edge labels
sourceIds = sourceIds.filter(Boolean);
edgeLabels = edgeLabels.filter(Boolean);

// Create edges
for (let i = 0; i < sourceIds.length; i++) {
const sourceId = sourceIds[i];
const edgeLabel = edgeLabels[i] ?? "";
const edgeData: FeatureData = {
// Edge Id's can be empty, they'll be added when stringified
id: "",
classes: "",
label: edgeLabel,
// ...record,
};
const edge: Edge = {
source: sourceId,
target: id,
data: edgeData,
};
edges.push(edge);
}
} else if (mapping.edgesDeclared === "separateRows") {
if (edgeOnly) {
const { sourceColumn, targetColumn } = mapping;
const sourceId = record[sourceColumn];
const targetId = record[targetColumn];
const edgeLabel = mapping.nodeLabelColumn
? record[mapping.nodeLabelColumn]
: "";
const edgeData: FeatureData = {
// Edge Id's can be empty, they'll be added when stringified
id: "",
classes: "",
label: edgeLabel,
// ...record,
};
const edge: Edge = {
source: sourceId,
target: targetId,
data: edgeData,
};
edges.push(edge);
}
}
}

// Return the resulting Graph object
return { nodes, edges };
}
10 changes: 10 additions & 0 deletions api/data/fixtures/example-visio-process.csv
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
"Process Step ID","Process Step Description","Next Step ID","Connector Label","Shape Type"
"n1","Start","n2","","Start"
"n2","Step 1","n3","","Process"
"n3","Step 2","n4,n6,n9","label,,","Decision"
"n4","Step 3","n10","","Process"
"n6","Step 4","n7","","Process"
"n7","Step 6","n8","","Process"
"n8","Finish","","","End"
"n9","Step 5","n10","","Process"
"n10","Step 7","n8","","Process"
152 changes: 152 additions & 0 deletions api/data/fixtures/simpleLucidChart.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,152 @@
[
{
"Id": 1,
"Name": "Document",
"Shape Library": "",
"Page ID": null,
"Contained By": "",
"Group": "",
"Line Source": null,
"Line Destination": null,
"Source Arrow": "",
"Destination Arrow": "",
"Status": "Draft",
"Text Area 1": "Blank diagram",
"Comments": ""
},
{
"Id": 2,
"Name": "Page",
"Shape Library": "",
"Page ID": null,
"Contained By": "",
"Group": "",
"Line Source": null,
"Line Destination": null,
"Source Arrow": "",
"Destination Arrow": "",
"Status": "",
"Text Area 1": "Page 1",
"Comments": ""
},
{
"Id": 3,
"Name": "MinimalTextBlock",
"Shape Library": "Standard",
"Page ID": 2,
"Contained By": "",
"Group": "",
"Line Source": null,
"Line Destination": null,
"Source Arrow": "",
"Destination Arrow": "",
"Status": "",
"Text Area 1": "gsd",
"Comments": ""
},
{
"Id": 4,
"Name": "Data (I/O)",
"Shape Library": "Flowchart Shapes",
"Page ID": 2,
"Contained By": "",
"Group": "",
"Line Source": null,
"Line Destination": null,
"Source Arrow": "",
"Destination Arrow": "",
"Status": "",
"Text Area 1": "sdfsdfsdfsdfsdfsdfsdfsdfsdf",
"Comments": ""
},
{
"Id": 5,
"Name": "Data (I/O)",
"Shape Library": "Flowchart Shapes",
"Page ID": 2,
"Contained By": "",
"Group": "",
"Line Source": null,
"Line Destination": null,
"Source Arrow": "",
"Destination Arrow": "",
"Status": "",
"Text Area 1": "b",
"Comments": ""
},
{
"Id": 6,
"Name": "Data (I/O)",
"Shape Library": "Flowchart Shapes",
"Page ID": 2,
"Contained By": "",
"Group": "",
"Line Source": null,
"Line Destination": null,
"Source Arrow": "",
"Destination Arrow": "",
"Status": "",
"Text Area 1": "c",
"Comments": ""
},
{
"Id": 7,
"Name": "Or",
"Shape Library": "Flowchart Shapes",
"Page ID": 2,
"Contained By": "",
"Group": "",
"Line Source": null,
"Line Destination": null,
"Source Arrow": "",
"Destination Arrow": "",
"Status": "",
"Text Area 1": "",
"Comments": ""
},
{
"Id": 8,
"Name": "Line",
"Shape Library": "",
"Page ID": 2,
"Contained By": "",
"Group": "",
"Line Source": 4,
"Line Destination": 5,
"Source Arrow": "None",
"Destination Arrow": "Arrow",
"Status": "",
"Text Area 1": "",
"Comments": ""
},
{
"Id": 9,
"Name": "Line",
"Shape Library": "",
"Page ID": 2,
"Contained By": "",
"Group": "",
"Line Source": 4,
"Line Destination": 6,
"Source Arrow": "None",
"Destination Arrow": "Arrow",
"Status": "",
"Text Area 1": "",
"Comments": ""
},
{
"Id": 10,
"Name": "Line",
"Shape Library": "",
"Page ID": 2,
"Contained By": "",
"Group": "",
"Line Source": 7,
"Line Destination": 6,
"Source Arrow": "None",
"Destination Arrow": "Arrow",
"Status": "",
"Text Area 1": "",
"Comments": ""
}
]
Loading

1 comment on commit a5a25f8

@vercel
Copy link

@vercel vercel bot commented on a5a25f8 Apr 13, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please sign in to comment.