spanner-graph-notebook 1.0.0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- assets/authentication.png +0 -0
- assets/full_viz.png +0 -0
- assets/hero.png +0 -0
- assets/hero_with_properties.png +0 -0
- assets/jupyter-spin-up.png +0 -0
- assets/load_ext.png +0 -0
- assets/mock_data_result.png +0 -0
- assets/new_notebook.png +0 -0
- assets/notebook_package_load.png +0 -0
- assets/query_graph.png +0 -0
- assets/sample_jupyter.png +0 -0
- spanner_graph_notebook-1.0.0.dist-info/LICENSE +202 -0
- spanner_graph_notebook-1.0.0.dist-info/METADATA +169 -0
- spanner_graph_notebook-1.0.0.dist-info/RECORD +40 -0
- spanner_graph_notebook-1.0.0.dist-info/WHEEL +5 -0
- spanner_graph_notebook-1.0.0.dist-info/top_level.txt +2 -0
- spanner_graphs/__init__.py +19 -0
- spanner_graphs/conversion.py +299 -0
- spanner_graphs/database.py +199 -0
- spanner_graphs/graph_entities.py +405 -0
- spanner_graphs/graph_mock_data.csv +51 -0
- spanner_graphs/graph_mock_schema.json +681 -0
- spanner_graphs/magics.py +211 -0
- spanner_graphs/schema_manager.py +60 -0
- templates/assets/images/graph-bg.svg +4 -0
- templates/spanner-graph/models/edge.js +77 -0
- templates/spanner-graph/models/graph-object.js +64 -0
- templates/spanner-graph/models/node.js +77 -0
- templates/spanner-graph/models/schema.js +327 -0
- templates/spanner-graph/spanner-config.js +304 -0
- templates/spanner-graph/spanner-store.js +382 -0
- templates/spanner-graph/visualization/spanner-forcegraph.js +1380 -0
- templates/spanner-graph/visualization/spanner-sidebar.js +904 -0
- templates/template-spannergraph.html +210 -0
- tests/__init__.py +13 -0
- tests/conversion_test.py +163 -0
- tests/database_test.py +62 -0
- tests/graph_entities_test.py +124 -0
- tests/schema_manager_test.py +115 -0
- tests/test_notebook.json +23 -0
|
@@ -0,0 +1,327 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Represents the schema of the data.
|
|
3
|
+
* @class
|
|
4
|
+
*/
|
|
5
|
+
class Schema {
|
|
6
|
+
/**
|
|
7
|
+
* @typedef {Object} PropertyDefinition
|
|
8
|
+
* @property {string} propertyDeclarationName
|
|
9
|
+
* @property {string} valueExpressionSql
|
|
10
|
+
* @property {string} nodeTableName
|
|
11
|
+
*/
|
|
12
|
+
|
|
13
|
+
/**
|
|
14
|
+
* @typedef {Object} EdgeDestinationNode
|
|
15
|
+
* @property {Array<string>} edgeTableColumns
|
|
16
|
+
* @property {Array<string>} nodeTableColumns
|
|
17
|
+
* @property {string} nodeTableName
|
|
18
|
+
*/
|
|
19
|
+
|
|
20
|
+
/**
|
|
21
|
+
* @typedef {Object} EdgeTable
|
|
22
|
+
* @property {string} baseCatalogName
|
|
23
|
+
* @property {string} baseSchemaName
|
|
24
|
+
* @property {string} baseTableName
|
|
25
|
+
* @property {EdgeDestinationNode} destinationNodeTable
|
|
26
|
+
* @property {Array<string>} keyColumns
|
|
27
|
+
* @property {string} kind
|
|
28
|
+
* @property {Array<string>} labelNames
|
|
29
|
+
* @property {string} name
|
|
30
|
+
* @property {Array<PropertyDefinition>} propertyDefinitions
|
|
31
|
+
* @property {EdgeDestinationNode} sourceNodeTable
|
|
32
|
+
*/
|
|
33
|
+
|
|
34
|
+
/**
|
|
35
|
+
* @typedef {Object} NodeTable
|
|
36
|
+
* @property {string} baseCatalogName
|
|
37
|
+
* @property {string} baseSchemaName
|
|
38
|
+
* @property {string} baseTableName
|
|
39
|
+
* @property {Array<string>} keyColumns
|
|
40
|
+
* @property {string} kind
|
|
41
|
+
* @property {Array<string>} labelNames
|
|
42
|
+
* @property {string} name
|
|
43
|
+
* @property {Array<PropertyDefinition>} propertyDefinitions
|
|
44
|
+
*/
|
|
45
|
+
|
|
46
|
+
/**
|
|
47
|
+
* @typedef PropertyDeclarationType
|
|
48
|
+
* @param {'INT64'|'STRING'|'FLOAT64'|'TIMESTAMP'|'BOOL'}
|
|
49
|
+
*/
|
|
50
|
+
|
|
51
|
+
/**
|
|
52
|
+
* @type {PropertyDeclarationType}
|
|
53
|
+
*/
|
|
54
|
+
propertyDeclarationTypes = [
|
|
55
|
+
'INT64', 'STRING', 'FLOAT64', 'TIMESTAMP', 'BOOL'
|
|
56
|
+
];
|
|
57
|
+
|
|
58
|
+
/**
|
|
59
|
+
* @typedef PropertyDeclaration
|
|
60
|
+
* @param {string} name
|
|
61
|
+
* @param {PropertyDeclarationType} type
|
|
62
|
+
*/
|
|
63
|
+
|
|
64
|
+
/**
|
|
65
|
+
* @typedef {Object} RawSchema The raw schema object returned from Cloud Spanner
|
|
66
|
+
* @property {string} catalog
|
|
67
|
+
* @property {Array<EdgeTable>} edgeTables
|
|
68
|
+
* @property {number} labels
|
|
69
|
+
* @property {string} name
|
|
70
|
+
* @property {Array<NodeTable>} nodeTables
|
|
71
|
+
* @property {Array<PropertyDeclaration>} propertyDeclarations
|
|
72
|
+
* @property {string} schema
|
|
73
|
+
*/
|
|
74
|
+
|
|
75
|
+
/**
|
|
76
|
+
* @type {RawSchema}
|
|
77
|
+
*/
|
|
78
|
+
rawSchema;
|
|
79
|
+
|
|
80
|
+
/**
|
|
81
|
+
* @param {RawSchema} rawSchemaObject
|
|
82
|
+
*/
|
|
83
|
+
constructor(rawSchemaObject) {
|
|
84
|
+
this.rawSchema = rawSchemaObject;
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
/**
|
|
88
|
+
* @param {Array<EdgeTable|NodeTable>} tables
|
|
89
|
+
* @returns {Array<string>}
|
|
90
|
+
*/
|
|
91
|
+
getNamesOfTables(tables) {
|
|
92
|
+
const names = {};
|
|
93
|
+
|
|
94
|
+
if (!this.rawSchema) {
|
|
95
|
+
console.error('No schema found');
|
|
96
|
+
return [];
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
for (let i = 0; i < tables.length; i++) {
|
|
100
|
+
const table = tables[i];
|
|
101
|
+
|
|
102
|
+
if (!table.name) {
|
|
103
|
+
console.error('name of nodeTable is not declared');
|
|
104
|
+
continue;
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
if (typeof table.name != 'string') {
|
|
108
|
+
console.error('name of nodeTable is not a string');
|
|
109
|
+
continue;
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
names[table.name] = '';
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
return Object.keys(names);
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
/**
|
|
119
|
+
* We are only returning the first label as a stopgap
|
|
120
|
+
* until the Spanner Backend settles on a solution.
|
|
121
|
+
* @param {Array<EdgeTable|NodeTable>} tables
|
|
122
|
+
* @returns {Array<string>}
|
|
123
|
+
*/
|
|
124
|
+
getUniqueLabels(tables) {
|
|
125
|
+
/**
|
|
126
|
+
* @type {Array<string>}
|
|
127
|
+
*/
|
|
128
|
+
const labels = [];
|
|
129
|
+
|
|
130
|
+
for (let i = 0; i < tables.length; i++) {
|
|
131
|
+
const table = tables[i];
|
|
132
|
+
if (!(table instanceof Object) ||
|
|
133
|
+
!(table.labelNames instanceof Array) ||
|
|
134
|
+
table.labelNames.length === 0) {
|
|
135
|
+
continue;
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
labels.push(tables[i].labelNames[0]);
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
return labels;
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
/**
|
|
145
|
+
* @returns {Array<string>}
|
|
146
|
+
*/
|
|
147
|
+
getNodeNames() {
|
|
148
|
+
return this.getUniqueLabels(this.rawSchema.nodeTables);
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
/**
|
|
152
|
+
* @returns {Array<string>}
|
|
153
|
+
*/
|
|
154
|
+
getEdgeNames() {
|
|
155
|
+
return this.getUniqueLabels(this.rawSchema.edgeTables);
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
/**
|
|
159
|
+
* @returns {{nodes: Array<string>, edges: Array<string>}}
|
|
160
|
+
*/
|
|
161
|
+
getTableNames() {
|
|
162
|
+
return {
|
|
163
|
+
edges: this.getEdgeNames(),
|
|
164
|
+
nodes: this.getNodeNames()
|
|
165
|
+
};
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
/**
|
|
169
|
+
* @param {EdgeTable|NodeTable} table
|
|
170
|
+
* @returns {{name: string, type: PropertyDeclarationType}} The keys are the property names, and the values are the value types (int, float, etc.)
|
|
171
|
+
*/
|
|
172
|
+
getPropertiesOfTable(table){
|
|
173
|
+
const properties = {}
|
|
174
|
+
|
|
175
|
+
const getPropertyType = (name) => {
|
|
176
|
+
for (let j = 0; j < this.rawSchema.propertyDeclarations.length; j++) {
|
|
177
|
+
const declaration = this.rawSchema.propertyDeclarations[j];
|
|
178
|
+
if (declaration.name === name) {
|
|
179
|
+
return declaration.type;
|
|
180
|
+
}
|
|
181
|
+
}
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
for (let i = 0; i < table.propertyDefinitions.length; i++) {
|
|
185
|
+
const propertyDefinition = table.propertyDefinitions[i];
|
|
186
|
+
|
|
187
|
+
const propertyType = getPropertyType(propertyDefinition.propertyDeclarationName);
|
|
188
|
+
|
|
189
|
+
if (!propertyType) {
|
|
190
|
+
console.error(`Property Declaration does not contain Property Definition: ${propertyDefinition.propertyDeclarationName}`);
|
|
191
|
+
continue;
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
properties[propertyDefinition.propertyDeclarationName] = propertyType;
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
return properties;
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
/**
|
|
201
|
+
* @param {NodeTable} nodeTable
|
|
202
|
+
* @returns {Array<EdgeTable>} Edges
|
|
203
|
+
*/
|
|
204
|
+
getEdgesOfNode(nodeTable){
|
|
205
|
+
return this.rawSchema.edgeTables.filter(edgeTable =>
|
|
206
|
+
edgeTable.sourceNodeTable.nodeTableName === nodeTable.name ||
|
|
207
|
+
edgeTable.destinationNodeTable.nodeTableName === nodeTable.name);
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
/**
|
|
211
|
+
* @param edgeTable
|
|
212
|
+
* @returns {{
|
|
213
|
+
* to: NodeTable
|
|
214
|
+
* from: NodeTable
|
|
215
|
+
* }}
|
|
216
|
+
*/
|
|
217
|
+
getNodesOfEdges(edgeTable) {
|
|
218
|
+
/**
|
|
219
|
+
* @type {{to: {nodeTable}, from: {nodeTable}}}
|
|
220
|
+
*/
|
|
221
|
+
const nodes = {};
|
|
222
|
+
for (let i = 0; i < this.rawSchema.nodeTables.length; i++) {
|
|
223
|
+
const nodeTable = this.rawSchema.nodeTables[i];
|
|
224
|
+
if (edgeTable.sourceNodeTable.nodeTableName === nodeTable.name) {
|
|
225
|
+
nodes.from = nodeTable;
|
|
226
|
+
}
|
|
227
|
+
|
|
228
|
+
if (edgeTable.destinationNodeTable.nodeTableName === nodeTable.name) {
|
|
229
|
+
nodes.to = nodeTable;
|
|
230
|
+
}
|
|
231
|
+
|
|
232
|
+
if (nodes.from && nodes.to) {
|
|
233
|
+
break;
|
|
234
|
+
}
|
|
235
|
+
}
|
|
236
|
+
|
|
237
|
+
if (!nodes.to || !nodes.from) {
|
|
238
|
+
console.error('EdgeTable does not have a source or destination node', edgeTable);
|
|
239
|
+
}
|
|
240
|
+
|
|
241
|
+
return nodes;
|
|
242
|
+
}
|
|
243
|
+
|
|
244
|
+
/**
|
|
245
|
+
* @param {String} name
|
|
246
|
+
* @returns {EdgeTable}
|
|
247
|
+
*/
|
|
248
|
+
getEdgeFromName(name) {
|
|
249
|
+
const edges = this.rawSchema.edgeTables.filter(edgeTable =>
|
|
250
|
+
edgeTable.name === name);
|
|
251
|
+
|
|
252
|
+
if (edges.length > 0) {
|
|
253
|
+
return edges[0];
|
|
254
|
+
}
|
|
255
|
+
|
|
256
|
+
console.error(`No edgeTable associated with name ${name}`);
|
|
257
|
+
}
|
|
258
|
+
|
|
259
|
+
/**
|
|
260
|
+
* @param {String} name
|
|
261
|
+
* @returns {NodeTable}
|
|
262
|
+
*/
|
|
263
|
+
getNodeFromName(name) {
|
|
264
|
+
const nodes = this.rawSchema.nodeTables.filter(nodeTable =>
|
|
265
|
+
nodeTable.name === name);
|
|
266
|
+
|
|
267
|
+
if (nodes.length > 0) {
|
|
268
|
+
return nodes[0];
|
|
269
|
+
}
|
|
270
|
+
|
|
271
|
+
console.error(`No nodeTable associated with name ${name}`);
|
|
272
|
+
}
|
|
273
|
+
|
|
274
|
+
/**
|
|
275
|
+
* @param {EdgeTable} edgeTable
|
|
276
|
+
* @returns {number}
|
|
277
|
+
*/
|
|
278
|
+
getEdgeTableId(edgeTable) {
|
|
279
|
+
return this.rawSchema.edgeTables.indexOf(edgeTable);
|
|
280
|
+
}
|
|
281
|
+
|
|
282
|
+
/**
|
|
283
|
+
* @param {NodeTable} nodeTable
|
|
284
|
+
* @returns {number}
|
|
285
|
+
*/
|
|
286
|
+
getNodeTableId(nodeTable) {
|
|
287
|
+
return this.rawSchema.nodeTables.indexOf(nodeTable);
|
|
288
|
+
}
|
|
289
|
+
|
|
290
|
+
/**
|
|
291
|
+
* @param {String} nodeName
|
|
292
|
+
* @param {String} edgeName
|
|
293
|
+
* @returns {{isConnected: Boolean, isSource: Boolean}}
|
|
294
|
+
*/
|
|
295
|
+
nodeIsConnectedToEdge(nodeName, edgeName) {
|
|
296
|
+
const connection = {
|
|
297
|
+
isConnected: false,
|
|
298
|
+
isSource: false
|
|
299
|
+
};
|
|
300
|
+
|
|
301
|
+
const node = this.getNodeFromName(nodeName);
|
|
302
|
+
if (!node) {
|
|
303
|
+
console.error(`No node found from name ${nodeName}`);
|
|
304
|
+
return connection;
|
|
305
|
+
}
|
|
306
|
+
|
|
307
|
+
const edge = this.getEdgeFromName(edgeName);
|
|
308
|
+
if (!edge) {
|
|
309
|
+
console.error(`No edge found from name ${edgeName}`);
|
|
310
|
+
return connection;
|
|
311
|
+
}
|
|
312
|
+
|
|
313
|
+
connection.isConnected = true;
|
|
314
|
+
connection.isSource = edge.sourceNodeTable.nodeTableName === node.name
|
|
315
|
+
return connection;
|
|
316
|
+
}
|
|
317
|
+
|
|
318
|
+
/**
|
|
319
|
+
* @param {NodeTable|EdgeTable} table
|
|
320
|
+
* @return {string}
|
|
321
|
+
*/
|
|
322
|
+
getDisplayName(table) {
|
|
323
|
+
return table.labelNames[0];
|
|
324
|
+
}
|
|
325
|
+
}
|
|
326
|
+
|
|
327
|
+
window[namespace].Schema = Schema;
|
|
@@ -0,0 +1,304 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Copyright 2024 Google LLC
|
|
3
|
+
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
4
|
+
* you may not use this file except in compliance with the License.
|
|
5
|
+
* You may obtain a copy of the License at
|
|
6
|
+
*
|
|
7
|
+
* http://www.apache.org/licenses/LICENSE-2.0
|
|
8
|
+
*
|
|
9
|
+
* Unless required by applicable law or agreed to in writing, software
|
|
10
|
+
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
11
|
+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
12
|
+
* See the License for the specific language governing permissions and
|
|
13
|
+
* limitations under the License.
|
|
14
|
+
*/
|
|
15
|
+
|
|
16
|
+
class GraphConfig {
|
|
17
|
+
|
|
18
|
+
/**
|
|
19
|
+
* The array of node objects to be rendered. 123123123
|
|
20
|
+
* @type {Schema}
|
|
21
|
+
*/
|
|
22
|
+
schema = null;
|
|
23
|
+
|
|
24
|
+
/**
|
|
25
|
+
* The array of node objects to be rendered.
|
|
26
|
+
* @type {Array<Node>}
|
|
27
|
+
*/
|
|
28
|
+
schemaNodes = [];
|
|
29
|
+
|
|
30
|
+
/**
|
|
31
|
+
* The array of edge objects to be rendered.
|
|
32
|
+
* @type {Array<Edge>}
|
|
33
|
+
*/
|
|
34
|
+
schemaEdges = [];
|
|
35
|
+
|
|
36
|
+
/**
|
|
37
|
+
* The array of node objects to be rendered.
|
|
38
|
+
* @type {Array<Node>}
|
|
39
|
+
*/
|
|
40
|
+
nodes = [];
|
|
41
|
+
|
|
42
|
+
/**
|
|
43
|
+
* The array of edge objects to be rendered.
|
|
44
|
+
* @type {Array<Edge>}
|
|
45
|
+
*/
|
|
46
|
+
edges = [];
|
|
47
|
+
|
|
48
|
+
/**
|
|
49
|
+
* Raw data of rows from Spanner Graph
|
|
50
|
+
* @type {Array<any>}
|
|
51
|
+
*/
|
|
52
|
+
rowsData = [];
|
|
53
|
+
|
|
54
|
+
/**
|
|
55
|
+
* The currently focused GraphObject. This is usually the
|
|
56
|
+
* node or edge that the user is hovering their mouse over.
|
|
57
|
+
* @type {GraphObject}
|
|
58
|
+
* @default null
|
|
59
|
+
*/
|
|
60
|
+
focusedGraphObject = null;
|
|
61
|
+
|
|
62
|
+
/**
|
|
63
|
+
* The currently selected GraphObject. This is usually
|
|
64
|
+
* the node or edge that the user has clicked on.
|
|
65
|
+
* @type {GraphObject}
|
|
66
|
+
* @default null
|
|
67
|
+
*/
|
|
68
|
+
selectedGraphObject = null;
|
|
69
|
+
|
|
70
|
+
/**
|
|
71
|
+
* The color scheme to use for nodes.
|
|
72
|
+
* @type {GraphConfig.ColorScheme}
|
|
73
|
+
* @default GraphConfig.ColorScheme.NEIGHBORHOOD
|
|
74
|
+
*/
|
|
75
|
+
colorScheme = GraphConfig.ColorScheme.NEIGHBORHOOD;
|
|
76
|
+
|
|
77
|
+
colorPalette = [
|
|
78
|
+
'#1A73E8', '#E52592', '#12A4AF', '#F4511E',
|
|
79
|
+
'#9334E6', '#689F38', '#3949AB', '#546E7A',
|
|
80
|
+
'#EF6C00', '#D93025', '#1E8E3E', '#039BE5'
|
|
81
|
+
];
|
|
82
|
+
|
|
83
|
+
// [label: string]: colorString
|
|
84
|
+
nodeColors = {};
|
|
85
|
+
// [label: string]: colorString
|
|
86
|
+
schemaNodeColors = {};
|
|
87
|
+
|
|
88
|
+
edgeDesign = {
|
|
89
|
+
default: {
|
|
90
|
+
color: '#DADCE0',
|
|
91
|
+
width: 2,
|
|
92
|
+
shadowWidth: 0,
|
|
93
|
+
shadowColor: '#000000'
|
|
94
|
+
},
|
|
95
|
+
focused: {
|
|
96
|
+
color: '#80868B',
|
|
97
|
+
width: 4,
|
|
98
|
+
shadowWidth: 6,
|
|
99
|
+
shadowColor: '#E8EAED'
|
|
100
|
+
},
|
|
101
|
+
selected: {
|
|
102
|
+
color: '#1A73E8',
|
|
103
|
+
width: 4,
|
|
104
|
+
shadowWidth: 8,
|
|
105
|
+
shadowColor: 'rgba(26, 115, 232, 0.25)'
|
|
106
|
+
}
|
|
107
|
+
};
|
|
108
|
+
|
|
109
|
+
static ColorScheme = Object.freeze({
|
|
110
|
+
NEIGHBORHOOD: Symbol('neighborhood'),
|
|
111
|
+
LABEL: Symbol('label')
|
|
112
|
+
});
|
|
113
|
+
|
|
114
|
+
/**
|
|
115
|
+
* Constructs a new GraphConfig instance.
|
|
116
|
+
* @constructor
|
|
117
|
+
* @param {Object} config - The configuration object.
|
|
118
|
+
* @param {Array} config.nodesData - An array of data objects for nodes.
|
|
119
|
+
* @param {Array} config.edgesData - An array of data objects for edges.
|
|
120
|
+
* @param {Array} [config.colorPalette] - An optional array of colors to use as the color palette.
|
|
121
|
+
* @param {GraphConfig.ColorScheme} [config.colorScheme] - Color scheme can be optionally declared.
|
|
122
|
+
* @param {Array} [config.rowsData] - Raw row data from Spanner
|
|
123
|
+
* @param {RawSchema} config.schemaData - Raw schema data from Spanner
|
|
124
|
+
*/
|
|
125
|
+
constructor({ nodesData, edgesData, colorPalette, colorScheme, rowsData, schemaData}) {
|
|
126
|
+
this.nodes = this.parseNodes(nodesData);
|
|
127
|
+
this.edges = this.parseEdges(edgesData);
|
|
128
|
+
this.nodeColors = this.assignColors(this.nodes);
|
|
129
|
+
this.parseSchema(schemaData);
|
|
130
|
+
|
|
131
|
+
if (colorPalette && Array.isArray(colorPalette)) {
|
|
132
|
+
this.colorPalette = colorPalette;
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
if (colorScheme) {
|
|
136
|
+
this.colorScheme = colorScheme;
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
this.rowsData = rowsData;
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
/**
|
|
143
|
+
* @param nodes
|
|
144
|
+
* @returns {{}} Color map by the node's label
|
|
145
|
+
*/
|
|
146
|
+
assignColors(nodes) {
|
|
147
|
+
const colors = {};
|
|
148
|
+
const colorPalette = this.colorPalette.map(color => color);
|
|
149
|
+
|
|
150
|
+
if (!nodes || !nodes instanceof Array) {
|
|
151
|
+
console.error('Nodes must be array', nodes);
|
|
152
|
+
throw Error('Nodes must be an array');
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
nodes.forEach(node => {
|
|
156
|
+
if (colorPalette.length === 0) {
|
|
157
|
+
console.error('Node labels exceed the color palette. Assigning default color.');
|
|
158
|
+
return;
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
if (!node || !node instanceof Node) {
|
|
162
|
+
console.error('Object is not an instance of Node', node);
|
|
163
|
+
return;
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
const label = node.label;
|
|
167
|
+
if (!label || !label instanceof String) {
|
|
168
|
+
console.error('Node does not have a label', node);
|
|
169
|
+
return;
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
if (!colors[label]) {
|
|
173
|
+
colors[label] = colorPalette.shift();
|
|
174
|
+
}
|
|
175
|
+
});
|
|
176
|
+
|
|
177
|
+
return colors;
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
/**
|
|
181
|
+
* Parses schema data into nodes and edges
|
|
182
|
+
* @param {RawSchema} schemaData - The raw data representing a schema
|
|
183
|
+
* @throws {Error} Throws an error if the schema data can not be parsed
|
|
184
|
+
*/
|
|
185
|
+
parseSchema(schemaData) {
|
|
186
|
+
this.schema = new Schema(schemaData);
|
|
187
|
+
|
|
188
|
+
const nodesData = this.schema.rawSchema.nodeTables.map(
|
|
189
|
+
/**
|
|
190
|
+
* @param {NodeTable} nodeTable
|
|
191
|
+
* @returns {NodeData}
|
|
192
|
+
*/
|
|
193
|
+
(nodeTable, i) => {
|
|
194
|
+
const name = this.schema.getDisplayName(nodeTable)
|
|
195
|
+
|
|
196
|
+
/**
|
|
197
|
+
* @type {NodeData}
|
|
198
|
+
*/
|
|
199
|
+
return {
|
|
200
|
+
label: name,
|
|
201
|
+
properties: this.schema.getPropertiesOfTable(nodeTable),
|
|
202
|
+
color: 'rgb(0, 0, 100)', // this isn't used
|
|
203
|
+
key_property_names: ['id'],
|
|
204
|
+
id: this.schema.getNodeTableId(nodeTable)
|
|
205
|
+
};
|
|
206
|
+
}
|
|
207
|
+
);
|
|
208
|
+
this.schemaNodes = this.parseNodes(nodesData);
|
|
209
|
+
|
|
210
|
+
const edgesData = this.schema.rawSchema.edgeTables.map(
|
|
211
|
+
/**
|
|
212
|
+
* @param {EdgeTable} edgeTable
|
|
213
|
+
* @returns {EdgeData}
|
|
214
|
+
*/
|
|
215
|
+
(edgeTable, i) => {
|
|
216
|
+
const connectedNodes = this.schema.getNodesOfEdges(edgeTable);
|
|
217
|
+
const name = this.schema.getDisplayName(edgeTable)
|
|
218
|
+
|
|
219
|
+
/**
|
|
220
|
+
* @type {EdgeData}
|
|
221
|
+
*/
|
|
222
|
+
return {
|
|
223
|
+
label: name,
|
|
224
|
+
properties: this.schema.getPropertiesOfTable(edgeTable),
|
|
225
|
+
color: 'rgb(0, 0, 100)', // this isn't used
|
|
226
|
+
to: this.schema.getNodeTableId(connectedNodes.to),
|
|
227
|
+
from: this.schema.getNodeTableId(connectedNodes.from),
|
|
228
|
+
key_property_names: ['id'],
|
|
229
|
+
id: this.schema.getEdgeTableId(edgeTable)
|
|
230
|
+
};
|
|
231
|
+
});
|
|
232
|
+
this.schemaEdges = this.parseEdges(edgesData);
|
|
233
|
+
this.schemaNodeColors = this.assignColors(this.schemaNodes);
|
|
234
|
+
}
|
|
235
|
+
|
|
236
|
+
/**
|
|
237
|
+
* Parses an array of node data, instantiates nodes, and adds them to the graph.
|
|
238
|
+
* @param {Array<NodeData>} nodesData - An array of objects representing the data for each node.
|
|
239
|
+
* @throws {Error} Throws an error if `nodesData` is not an array.
|
|
240
|
+
*/
|
|
241
|
+
parseNodes(nodesData) {
|
|
242
|
+
if (!Array.isArray(nodesData)) {
|
|
243
|
+
console.error('Nodes must be an array', nodesData)
|
|
244
|
+
throw Error('Nodes must be an array');
|
|
245
|
+
}
|
|
246
|
+
|
|
247
|
+
/** @type {Node[]} */
|
|
248
|
+
const nodes = []
|
|
249
|
+
nodesData.forEach(nodeData => {
|
|
250
|
+
if (!(nodeData instanceof Object)) {
|
|
251
|
+
console.error('Node data is not an object', nodeData);
|
|
252
|
+
return;
|
|
253
|
+
}
|
|
254
|
+
|
|
255
|
+
// Try to create a Node
|
|
256
|
+
const node = new Node(nodeData);
|
|
257
|
+
if (!node || !node.instantiated) {
|
|
258
|
+
console.error('Unable to instantiate node', node.instantiationErrorReason);
|
|
259
|
+
return;
|
|
260
|
+
}
|
|
261
|
+
if (node instanceof Node) {
|
|
262
|
+
nodes.push(node);
|
|
263
|
+
} else {
|
|
264
|
+
node.instantiationErrorReason = 'Could not construct an instance of Node';
|
|
265
|
+
console.error(node.instantiationErrorReason, { nodeData, node });
|
|
266
|
+
}
|
|
267
|
+
});
|
|
268
|
+
|
|
269
|
+
return nodes;
|
|
270
|
+
}
|
|
271
|
+
|
|
272
|
+
parseEdges(edgesData) {
|
|
273
|
+
if (!Array.isArray(edgesData)) {
|
|
274
|
+
console.error('Edges must be an array', edgesData)
|
|
275
|
+
throw Error('Edges must be an array');
|
|
276
|
+
}
|
|
277
|
+
|
|
278
|
+
/** @type {Edge[]} */
|
|
279
|
+
const edges = []
|
|
280
|
+
edgesData.forEach(edgeData => {
|
|
281
|
+
if (!(edgeData instanceof Object)) {
|
|
282
|
+
console.error('Edge data is not an object', edgeData);
|
|
283
|
+
return;
|
|
284
|
+
}
|
|
285
|
+
|
|
286
|
+
// Try to create an Edge
|
|
287
|
+
const edge = new Edge(edgeData);
|
|
288
|
+
if (!edge || !edge.instantiated) {
|
|
289
|
+
console.error('Unable to instantiate edge', edge.instantiationErrorReason);
|
|
290
|
+
return;
|
|
291
|
+
}
|
|
292
|
+
if (edge instanceof Edge) {
|
|
293
|
+
edges.push(edge);
|
|
294
|
+
} else {
|
|
295
|
+
edge.instantiationErrorReason = 'Could not construct an instance of Edge';
|
|
296
|
+
console.error(edge.instantiationErrorReason, { edgeData, edge });
|
|
297
|
+
}
|
|
298
|
+
});
|
|
299
|
+
|
|
300
|
+
return edges;
|
|
301
|
+
}
|
|
302
|
+
}
|
|
303
|
+
|
|
304
|
+
window[namespace].GraphConfig = GraphConfig;
|