@hpcc-js/eclwatch 2.77.6 → 2.77.7
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.
- package/LICENSE +43 -43
- package/dist/index.es6.js.map +1 -1
- package/dist/index.js.map +1 -1
- package/dist/index.min.js.map +1 -1
- package/package.json +12 -12
- package/src/ECLArchiveViewer.ts +160 -160
- package/src/WUGraph.css +40 -40
- package/src/WUGraph.ts +265 -265
- package/src/WUGraphLegend.css +7 -7
- package/src/WUGraphLegend.ts +126 -126
- package/src/WUResult.ts +111 -111
- package/src/WUResultStore.ts +202 -202
- package/src/WUScopeController.ts +548 -548
- package/src/WUStatus.ts +206 -206
- package/src/WUTimeline.ts +122 -122
- package/src/__package__.ts +3 -3
- package/src/index.ts +7 -7
package/src/WUScopeController.ts
CHANGED
|
@@ -1,548 +1,548 @@
|
|
|
1
|
-
import { Icon } from "@hpcc-js/common";
|
|
2
|
-
import { BaseScope, ScopeEdge, ScopeGraph, ScopeSubgraph, ScopeVertex } from "@hpcc-js/comms";
|
|
3
|
-
import { Edge, IGraphData, Lineage, Subgraph, Vertex } from "@hpcc-js/graph";
|
|
4
|
-
import { Edge as UtilEdge, Subgraph as UtilSubgraph, Vertex as UtilVertex } from "@hpcc-js/util";
|
|
5
|
-
import { WUGraphLegendData } from "./WUGraphLegend";
|
|
6
|
-
|
|
7
|
-
export type VertexType = Vertex | Icon;
|
|
8
|
-
|
|
9
|
-
export interface MyGraphData {
|
|
10
|
-
subgraphs: Subgraph[];
|
|
11
|
-
vertices: VertexType[];
|
|
12
|
-
edges: Edge[];
|
|
13
|
-
hierarchy: Lineage[];
|
|
14
|
-
}
|
|
15
|
-
|
|
16
|
-
function faCharFactory(kind): string {
|
|
17
|
-
switch (kind) {
|
|
18
|
-
case "2": return "\uf0c7"; // Disk Write
|
|
19
|
-
case "3": return "\uf15d"; // sort
|
|
20
|
-
case "5": return "\uf0b0"; // Filter
|
|
21
|
-
case "6": return "\uf1e0"; // Split
|
|
22
|
-
case "12": return "\uf039"; // First N
|
|
23
|
-
case "15": return "\uf126"; // Lightweight Join
|
|
24
|
-
case "17": return "\uf126"; // Lookup Join
|
|
25
|
-
case "22": return "\uf1e6"; // Pipe Output
|
|
26
|
-
case "23": return "\uf078"; // Funnel
|
|
27
|
-
case "25": return "\uf0ce"; // Inline Dataset
|
|
28
|
-
case "26": return "\uf074"; // distribute
|
|
29
|
-
case "29": return "\uf005"; // Store Internal Result
|
|
30
|
-
case "36": return "\uf128"; // If
|
|
31
|
-
case "44": return "\uf0c7"; // write csv
|
|
32
|
-
case "47": return "\uf0c7"; // write
|
|
33
|
-
case "54": return "\uf013"; // Workunit Read
|
|
34
|
-
case "56": return "\uf0c7"; // Spill
|
|
35
|
-
case "59": return "\uf126"; // Merge
|
|
36
|
-
case "61": return "\uf0c7"; // write xml
|
|
37
|
-
case "82": return "\uf1c0"; // Projected Disk Read Spill
|
|
38
|
-
case "88": return "\uf1c0"; // Projected Disk Read Spill
|
|
39
|
-
case "92": return "\uf129"; // Limted Index Read
|
|
40
|
-
case "93": return "\uf129"; // Limted Index Read
|
|
41
|
-
case "99": return "\uf1c0"; // CSV Read
|
|
42
|
-
case "105": return "\uf1c0"; // CSV Read
|
|
43
|
-
|
|
44
|
-
case "7": return "\uf090"; // Project
|
|
45
|
-
case "9": return "\uf0e2"; // Local Iterate
|
|
46
|
-
case "16": return "\uf005"; // Output Internal
|
|
47
|
-
case "19": return "\uf074"; // Hash Distribute
|
|
48
|
-
case "21": return "\uf275"; // Normalize
|
|
49
|
-
case "35": return "\uf0c7"; // CSV Write
|
|
50
|
-
case "37": return "\uf0c7"; // Index Write
|
|
51
|
-
case "71": return "\uf1c0"; // Disk Read Spill
|
|
52
|
-
case "133": return "\uf0ce"; // Inline Dataset
|
|
53
|
-
case "148": return "\uf0ce"; // Inline Dataset
|
|
54
|
-
case "168": return "\uf275"; // Local Denormalize
|
|
55
|
-
}
|
|
56
|
-
return "\uf063";
|
|
57
|
-
}
|
|
58
|
-
|
|
59
|
-
export class WUScopeController {
|
|
60
|
-
private graphDB: ScopeGraph;
|
|
61
|
-
private subgraphsMap: { [id: string]: Subgraph } = {};
|
|
62
|
-
private rSubgraphsMap: { [id: string]: ScopeSubgraph } = {};
|
|
63
|
-
private verticesMap: { [id: string]: VertexType } = {};
|
|
64
|
-
private rVerticesMap: { [id: string]: ScopeVertex } = {};
|
|
65
|
-
private edgesMap: { [id: string]: Edge } = {};
|
|
66
|
-
private rEdgesMap: { [id: string]: ScopeEdge } = {};
|
|
67
|
-
private kindMap: { [id: string]: ScopeVertex[] } = {};
|
|
68
|
-
|
|
69
|
-
protected _disabled: { [kind: number]: boolean } = {};
|
|
70
|
-
|
|
71
|
-
constructor() {
|
|
72
|
-
}
|
|
73
|
-
|
|
74
|
-
clear() {
|
|
75
|
-
this.subgraphsMap = {};
|
|
76
|
-
this.rSubgraphsMap = {};
|
|
77
|
-
this.verticesMap = {};
|
|
78
|
-
this.rVerticesMap = {};
|
|
79
|
-
this.edgesMap = {};
|
|
80
|
-
this.rEdgesMap = {};
|
|
81
|
-
}
|
|
82
|
-
|
|
83
|
-
set(masterGraph: ScopeGraph) {
|
|
84
|
-
this.graphDB = masterGraph;
|
|
85
|
-
this.graphGui(this.graphDB);
|
|
86
|
-
|
|
87
|
-
this.kindMap = {};
|
|
88
|
-
this.graphDB.walk(item => {
|
|
89
|
-
if (item instanceof UtilSubgraph) {
|
|
90
|
-
} else if (item instanceof UtilVertex) {
|
|
91
|
-
const kind = item._.attr("Kind").RawValue;
|
|
92
|
-
if (!this.kindMap[kind]) {
|
|
93
|
-
this.kindMap[kind] = [];
|
|
94
|
-
}
|
|
95
|
-
this.kindMap[kind].push(item);
|
|
96
|
-
} else if (item instanceof UtilEdge) {
|
|
97
|
-
}
|
|
98
|
-
});
|
|
99
|
-
}
|
|
100
|
-
|
|
101
|
-
disabled(): number[];
|
|
102
|
-
disabled(_: number[]): this;
|
|
103
|
-
disabled(_?: number[]): number[] | this {
|
|
104
|
-
if (!arguments.length) {
|
|
105
|
-
const retVal = [];
|
|
106
|
-
for (const key in this._disabled) {
|
|
107
|
-
if (this._disabled[key]) {
|
|
108
|
-
retVal.push(key);
|
|
109
|
-
}
|
|
110
|
-
}
|
|
111
|
-
return retVal;
|
|
112
|
-
}
|
|
113
|
-
this._disabled = {};
|
|
114
|
-
_.forEach(kind => this._disabled[kind] = true);
|
|
115
|
-
return this;
|
|
116
|
-
}
|
|
117
|
-
|
|
118
|
-
graphGui(graphDB: ScopeGraph): IGraphData {
|
|
119
|
-
const graphGui: MyGraphData = {
|
|
120
|
-
subgraphs: [],
|
|
121
|
-
vertices: [],
|
|
122
|
-
edges: [],
|
|
123
|
-
hierarchy: []
|
|
124
|
-
};
|
|
125
|
-
|
|
126
|
-
graphDB.walk((item) => {
|
|
127
|
-
if (item instanceof UtilSubgraph) {
|
|
128
|
-
const subgraph = this.appendSubgraph(item, graphGui.hierarchy, graphGui.subgraphs);
|
|
129
|
-
subgraph.showMinMax(item.vertices.length > 3 || subgraph.minState() !== "normal");
|
|
130
|
-
} else if (item instanceof UtilVertex) {
|
|
131
|
-
this.appendVertex(item, graphGui.hierarchy, graphGui.vertices);
|
|
132
|
-
} else if (item instanceof UtilEdge) {
|
|
133
|
-
this.appendEdge(item, graphGui.edges);
|
|
134
|
-
}
|
|
135
|
-
});
|
|
136
|
-
|
|
137
|
-
return graphGui;
|
|
138
|
-
}
|
|
139
|
-
|
|
140
|
-
format(labelTpl, obj) {
|
|
141
|
-
let retVal = "";
|
|
142
|
-
let lpos = labelTpl.indexOf("%");
|
|
143
|
-
let rpos = -1;
|
|
144
|
-
while (lpos >= 0) {
|
|
145
|
-
retVal += labelTpl.substring(rpos + 1, lpos);
|
|
146
|
-
rpos = labelTpl.indexOf("%", lpos + 1);
|
|
147
|
-
if (rpos < 0) {
|
|
148
|
-
console.warn("Invalid Label Template");
|
|
149
|
-
break;
|
|
150
|
-
}
|
|
151
|
-
const key = labelTpl.substring(lpos + 1, rpos);
|
|
152
|
-
retVal += !key ? "%" : (obj[labelTpl.substring(lpos + 1, rpos)] || "");
|
|
153
|
-
lpos = labelTpl.indexOf("%", rpos + 1);
|
|
154
|
-
}
|
|
155
|
-
retVal += labelTpl.substring(rpos + 1, labelTpl.length);
|
|
156
|
-
return retVal.split("\\n").join("\n");
|
|
157
|
-
}
|
|
158
|
-
|
|
159
|
-
createSubgraph(subgraph: ScopeSubgraph): Subgraph {
|
|
160
|
-
let sg = this.subgraphsMap[subgraph._.Id];
|
|
161
|
-
if (!sg) {
|
|
162
|
-
sg = new Subgraph()
|
|
163
|
-
.title(subgraph._.Id)
|
|
164
|
-
.on("minClick", () => {
|
|
165
|
-
this.minClick(sg);
|
|
166
|
-
})
|
|
167
|
-
;
|
|
168
|
-
this.subgraphsMap[subgraph._.Id] = sg;
|
|
169
|
-
this.rSubgraphsMap[sg.id()] = subgraph;
|
|
170
|
-
}
|
|
171
|
-
return sg;
|
|
172
|
-
}
|
|
173
|
-
|
|
174
|
-
createVertex(vertex: ScopeVertex): VertexType {
|
|
175
|
-
let v = this.verticesMap[vertex._.Id];
|
|
176
|
-
if (!v) {
|
|
177
|
-
const attrs = vertex._.rawAttrs();
|
|
178
|
-
if (vertex._.ScopeType === "dummy") {
|
|
179
|
-
const parent = this.subgraphsMap[vertex.parent._.Id];
|
|
180
|
-
v = new Icon()
|
|
181
|
-
.shape_colorFill("darkred")
|
|
182
|
-
.shape_colorStroke("darkred")
|
|
183
|
-
.image_colorFill("white")
|
|
184
|
-
.faChar("\uf067")
|
|
185
|
-
.on("click", () => {
|
|
186
|
-
parent.minState("normal");
|
|
187
|
-
this.minClick(parent);
|
|
188
|
-
})
|
|
189
|
-
;
|
|
190
|
-
} else {
|
|
191
|
-
v = new Vertex()
|
|
192
|
-
.icon_shape_colorFill("#1f77b4")
|
|
193
|
-
.icon_image_colorFill("white")
|
|
194
|
-
.faChar(faCharFactory(attrs["Kind"]))
|
|
195
|
-
.text(attrs["Label"])
|
|
196
|
-
;
|
|
197
|
-
const annotations = [];
|
|
198
|
-
if (vertex._.hasAttr("Definition")) {
|
|
199
|
-
annotations.push({
|
|
200
|
-
faChar: "\uf036",
|
|
201
|
-
tooltip: "Definition",
|
|
202
|
-
shape_colorFill: "lightgray",
|
|
203
|
-
shape_colorStroke: "lightgray",
|
|
204
|
-
image_colorFill: "black"
|
|
205
|
-
});
|
|
206
|
-
}
|
|
207
|
-
if (vertex._.hasAttr("IsInternal")) {
|
|
208
|
-
annotations.push({
|
|
209
|
-
faChar: "\uf085",
|
|
210
|
-
tooltip: "IsInternal",
|
|
211
|
-
shape_colorFill: "red",
|
|
212
|
-
shape_colorStroke: "red",
|
|
213
|
-
image_colorFill: "white"
|
|
214
|
-
});
|
|
215
|
-
}
|
|
216
|
-
v.annotationIcons(annotations);
|
|
217
|
-
}
|
|
218
|
-
this.verticesMap[vertex._.Id] = v;
|
|
219
|
-
this.rVerticesMap[v.id()] = vertex;
|
|
220
|
-
}
|
|
221
|
-
return v;
|
|
222
|
-
}
|
|
223
|
-
|
|
224
|
-
isSpill(edge: ScopeEdge): boolean {
|
|
225
|
-
const sourceKind = edge.source._.attr("Kind").RawValue;
|
|
226
|
-
const targetKind = edge.target._.attr("Kind").RawValue;
|
|
227
|
-
return sourceKind === "2" || targetKind === "71";
|
|
228
|
-
}
|
|
229
|
-
|
|
230
|
-
spansSubgraph(edge: ScopeEdge): boolean {
|
|
231
|
-
return edge.source.parent._.Id !== edge.target.parent._.Id;
|
|
232
|
-
}
|
|
233
|
-
|
|
234
|
-
createEdge(edge: ScopeEdge): Edge | undefined {
|
|
235
|
-
let e = this.edgesMap[edge._.Id];
|
|
236
|
-
if (!e) {
|
|
237
|
-
const attrs = edge._.rawAttrs();
|
|
238
|
-
const sourceV = this.verticesMap[edge.source._.Id];
|
|
239
|
-
const targetV = this.verticesMap[edge.target._.Id];
|
|
240
|
-
if (sourceV && targetV) {
|
|
241
|
-
const isSpill = this.isSpill(edge);
|
|
242
|
-
const spansSubgraph = this.spansSubgraph(edge);
|
|
243
|
-
|
|
244
|
-
const label = this.format("%Label%\n%NumRowsProcessed%", attrs);
|
|
245
|
-
/* TODO: Add extra annotations once WUDetails is fixed...
|
|
246
|
-
const numSlaves = parseInt(attrs["NumSlaves"]);
|
|
247
|
-
const numStarts = parseInt(attrs["NumStarts"]);
|
|
248
|
-
const numStops = parseInt(attrs["NumStops"]);
|
|
249
|
-
const started = numStarts > 0;
|
|
250
|
-
const finished = numStops === numSlaves;
|
|
251
|
-
const active = started && !finished;
|
|
252
|
-
*/
|
|
253
|
-
|
|
254
|
-
let strokeDasharray = null;
|
|
255
|
-
let weight = 100;
|
|
256
|
-
if (attrs["IsDependency"]) {
|
|
257
|
-
weight = 10;
|
|
258
|
-
strokeDasharray = "1,5";
|
|
259
|
-
} else if (attrs["_childGraph"]) {
|
|
260
|
-
strokeDasharray = "5,5";
|
|
261
|
-
} else if (isSpill) {
|
|
262
|
-
weight = 25;
|
|
263
|
-
strokeDasharray = "5,5,10,5";
|
|
264
|
-
} else if (spansSubgraph) {
|
|
265
|
-
weight = 5;
|
|
266
|
-
strokeDasharray = "5,5";
|
|
267
|
-
}
|
|
268
|
-
e = new Edge()
|
|
269
|
-
.sourceVertex(sourceV)
|
|
270
|
-
.targetVertex(targetV)
|
|
271
|
-
.sourceMarker("circle")
|
|
272
|
-
.targetMarker("arrow")
|
|
273
|
-
.weight(weight)
|
|
274
|
-
.strokeDasharray(strokeDasharray)
|
|
275
|
-
.text(label)
|
|
276
|
-
;
|
|
277
|
-
this.edgesMap[edge._.Id] = e;
|
|
278
|
-
this.rEdgesMap[e.id()] = edge;
|
|
279
|
-
}
|
|
280
|
-
}
|
|
281
|
-
return e;
|
|
282
|
-
}
|
|
283
|
-
|
|
284
|
-
appendSubgraph(subgraph: ScopeSubgraph, hierarchy: Lineage[], subgraphs: Subgraph[]): Subgraph {
|
|
285
|
-
const sg = this.createSubgraph(subgraph);
|
|
286
|
-
subgraphs.push(sg);
|
|
287
|
-
const parent = this.subgraphsMap[subgraph.parent._.Id];
|
|
288
|
-
if (parent) {
|
|
289
|
-
hierarchy.push({ parent, child: sg });
|
|
290
|
-
}
|
|
291
|
-
return sg;
|
|
292
|
-
}
|
|
293
|
-
|
|
294
|
-
appendVertex(vertex: ScopeVertex, hierarchy: Lineage[], vertices: VertexType[]): VertexType {
|
|
295
|
-
const v = this.createVertex(vertex);
|
|
296
|
-
vertices.push(v);
|
|
297
|
-
const parent = this.subgraphsMap[vertex.parent._.Id];
|
|
298
|
-
if (parent) {
|
|
299
|
-
hierarchy.push({ parent, child: v });
|
|
300
|
-
}
|
|
301
|
-
return v;
|
|
302
|
-
}
|
|
303
|
-
|
|
304
|
-
appendEdge(edge: ScopeEdge, edges: Edge[]): Edge {
|
|
305
|
-
const e = this.createEdge(edge);
|
|
306
|
-
if (e) {
|
|
307
|
-
edges.push(e);
|
|
308
|
-
}
|
|
309
|
-
return e;
|
|
310
|
-
}
|
|
311
|
-
|
|
312
|
-
filterLegend(graphDB: ScopeGraph) {
|
|
313
|
-
for (let i = graphDB.vertices.length - 1; i >= 0; --i) {
|
|
314
|
-
const vertex = graphDB.vertices[i];
|
|
315
|
-
const kind = vertex._.attr("Kind").RawValue;
|
|
316
|
-
if (this._disabled[kind]) {
|
|
317
|
-
vertex.remove(false, (source: BaseScope, target: BaseScope) => {
|
|
318
|
-
return new BaseScope({
|
|
319
|
-
ScopeName: vertex._.ScopeName + ":in",
|
|
320
|
-
Id: source.Id + "->" + target.Id,
|
|
321
|
-
ScopeType: "dummy-edge",
|
|
322
|
-
Properties: {
|
|
323
|
-
Property: [vertex._.attr("Label")]
|
|
324
|
-
},
|
|
325
|
-
Notes: {
|
|
326
|
-
Note: []
|
|
327
|
-
},
|
|
328
|
-
SinkActivity: ""
|
|
329
|
-
});
|
|
330
|
-
});
|
|
331
|
-
}
|
|
332
|
-
}
|
|
333
|
-
}
|
|
334
|
-
|
|
335
|
-
filterPartial(graphDB: ScopeGraph) {
|
|
336
|
-
for (const subgraph of graphDB.subgraphs) {
|
|
337
|
-
const sg = this.subgraphsMap[subgraph._.Id];
|
|
338
|
-
switch (sg.minState()) {
|
|
339
|
-
case "partial":
|
|
340
|
-
const childVertices: ReadonlyArray<ScopeVertex> = subgraph.vertices;
|
|
341
|
-
const vShow: ScopeVertex[] = [];
|
|
342
|
-
const vHide: ScopeVertex[] = [];
|
|
343
|
-
|
|
344
|
-
for (const vertex of childVertices) {
|
|
345
|
-
if (vertex.inEdges.length === 0 || vertex.inEdges.some(edge => edge.source.parent !== edge.target.parent) ||
|
|
346
|
-
vertex.outEdges.length === 0 || vertex.outEdges.some(edge => edge.source.parent !== edge.target.parent)) {
|
|
347
|
-
vShow.push(vertex);
|
|
348
|
-
} else {
|
|
349
|
-
vHide.push(vertex);
|
|
350
|
-
}
|
|
351
|
-
}
|
|
352
|
-
|
|
353
|
-
if (vHide.length > 1) {
|
|
354
|
-
const dummyDetails = {
|
|
355
|
-
ScopeName: subgraph._.ScopeName,
|
|
356
|
-
Id: subgraph._.Id + ":dummy",
|
|
357
|
-
ScopeType: "dummy",
|
|
358
|
-
Properties: {
|
|
359
|
-
Property: [{
|
|
360
|
-
Name: "Activities",
|
|
361
|
-
RawValue: "" + vHide.length,
|
|
362
|
-
Formatted: "" + vHide.length,
|
|
363
|
-
Measure: "count",
|
|
364
|
-
Creator: "",
|
|
365
|
-
CreatorType: ""
|
|
366
|
-
}]
|
|
367
|
-
},
|
|
368
|
-
Notes: {
|
|
369
|
-
Note: []
|
|
370
|
-
},
|
|
371
|
-
SinkActivity: ""
|
|
372
|
-
};
|
|
373
|
-
const dummyScope = new BaseScope(dummyDetails);
|
|
374
|
-
const dummyVertex = subgraph.createVertex(dummyScope);
|
|
375
|
-
|
|
376
|
-
for (const vertex of vHide) {
|
|
377
|
-
for (const edge of vertex.inEdges) {
|
|
378
|
-
if (vShow.indexOf(edge.source) >= 0) {
|
|
379
|
-
const dummyEdgeScope = new BaseScope({
|
|
380
|
-
ScopeName: edge.source._.ScopeName,
|
|
381
|
-
Id: edge.source._.Id + "->" + dummyVertex._.Id,
|
|
382
|
-
ScopeType: "dummy-in",
|
|
383
|
-
Properties: {
|
|
384
|
-
Property: []
|
|
385
|
-
},
|
|
386
|
-
Notes: {
|
|
387
|
-
Note: []
|
|
388
|
-
},
|
|
389
|
-
SinkActivity: ""
|
|
390
|
-
});
|
|
391
|
-
subgraph.createEdge(edge.source, dummyVertex, dummyEdgeScope);
|
|
392
|
-
}
|
|
393
|
-
}
|
|
394
|
-
for (const edge of vertex.outEdges) {
|
|
395
|
-
if (vShow.indexOf(edge.target) >= 0) {
|
|
396
|
-
const dummyEdgeScope = new BaseScope({
|
|
397
|
-
ScopeName: edge.target._.ScopeName,
|
|
398
|
-
Id: dummyVertex._.Id + "->" + edge.target._.Id,
|
|
399
|
-
ScopeType: "dummy-out",
|
|
400
|
-
Properties: {
|
|
401
|
-
Property: []
|
|
402
|
-
},
|
|
403
|
-
Notes: {
|
|
404
|
-
Note: []
|
|
405
|
-
},
|
|
406
|
-
SinkActivity: ""
|
|
407
|
-
});
|
|
408
|
-
subgraph.createEdge(dummyVertex, edge.target, dummyEdgeScope);
|
|
409
|
-
}
|
|
410
|
-
}
|
|
411
|
-
}
|
|
412
|
-
vHide.forEach(vertex => vertex.remove(true));
|
|
413
|
-
}
|
|
414
|
-
break;
|
|
415
|
-
}
|
|
416
|
-
}
|
|
417
|
-
}
|
|
418
|
-
|
|
419
|
-
filterEmptySubgraphs(graphDB: ScopeGraph) {
|
|
420
|
-
while (true) {
|
|
421
|
-
const emptySubgraphs = graphDB.subgraphs.filter(subgraph => subgraph.subgraphs.length === 0 && subgraph.vertices.length === 0);
|
|
422
|
-
if (emptySubgraphs.length === 0) break;
|
|
423
|
-
emptySubgraphs.forEach(subgraph => subgraph.remove(true));
|
|
424
|
-
}
|
|
425
|
-
}
|
|
426
|
-
|
|
427
|
-
removeObsoleteSubgraphs(graphDB: ScopeGraph) {
|
|
428
|
-
for (const subgraph of [...graphDB.subgraphs]) {
|
|
429
|
-
if (subgraph.vertices.length === 0) {
|
|
430
|
-
subgraph.remove(false);
|
|
431
|
-
}
|
|
432
|
-
}
|
|
433
|
-
}
|
|
434
|
-
|
|
435
|
-
graphData(): IGraphData {
|
|
436
|
-
const graphDB = this.graphDB.clone();
|
|
437
|
-
this.filterLegend(graphDB);
|
|
438
|
-
this.filterPartial(graphDB);
|
|
439
|
-
this.filterEmptySubgraphs(graphDB);
|
|
440
|
-
this.removeObsoleteSubgraphs(graphDB);
|
|
441
|
-
return this.graphGui(graphDB);
|
|
442
|
-
}
|
|
443
|
-
|
|
444
|
-
calcLegend(): WUGraphLegendData[] {
|
|
445
|
-
const retVal: WUGraphLegendData[] = [];
|
|
446
|
-
for (const kind in this.kindMap) {
|
|
447
|
-
retVal.push({
|
|
448
|
-
kind: parseInt(kind),
|
|
449
|
-
faChar: faCharFactory(kind),
|
|
450
|
-
label: this.kindMap[kind][0]._.attr("Label").RawValue.split("\n")[0],
|
|
451
|
-
count: this.kindMap[kind].length
|
|
452
|
-
});
|
|
453
|
-
}
|
|
454
|
-
return retVal;
|
|
455
|
-
}
|
|
456
|
-
|
|
457
|
-
vertices(kind: number): VertexType[] {
|
|
458
|
-
const retVal: VertexType[] = [];
|
|
459
|
-
for (const v of this.kindMap[kind]) {
|
|
460
|
-
retVal.push(this.verticesMap[v._.Id]);
|
|
461
|
-
}
|
|
462
|
-
return retVal;
|
|
463
|
-
}
|
|
464
|
-
|
|
465
|
-
formatRow(item: ScopeEdge | ScopeSubgraph | ScopeVertex, columns, row) {
|
|
466
|
-
const attrs = item._.formattedAttrs();
|
|
467
|
-
for (const key in attrs) {
|
|
468
|
-
const idx = columns.indexOf(key);
|
|
469
|
-
if (idx === -1) {
|
|
470
|
-
columns.push(key);
|
|
471
|
-
row.push(attrs[key]);
|
|
472
|
-
} else {
|
|
473
|
-
row[idx] = attrs[key];
|
|
474
|
-
}
|
|
475
|
-
}
|
|
476
|
-
for (let i = 0; i < 100; ++i) {
|
|
477
|
-
if (row[i] === undefined) {
|
|
478
|
-
row[i] = "";
|
|
479
|
-
}
|
|
480
|
-
}
|
|
481
|
-
return row;
|
|
482
|
-
}
|
|
483
|
-
|
|
484
|
-
activityData(): { columns: string[], data: any[][] } {
|
|
485
|
-
const columns = ["Id", "Kind", "Label"];
|
|
486
|
-
const data = this.graphDB.vertices.map(v => {
|
|
487
|
-
const row = [parseInt(v._.Id.split("a")[1])];
|
|
488
|
-
return this.formatRow(v, columns, row);
|
|
489
|
-
});
|
|
490
|
-
return { columns, data };
|
|
491
|
-
}
|
|
492
|
-
|
|
493
|
-
edgeData(): { columns: string[], data: any[][] } {
|
|
494
|
-
const columns = ["Id", "Label"];
|
|
495
|
-
const data = this.graphDB.edges.map(e => {
|
|
496
|
-
const row = [e._.Id];
|
|
497
|
-
return this.formatRow(e, columns, row);
|
|
498
|
-
});
|
|
499
|
-
return { columns, data };
|
|
500
|
-
}
|
|
501
|
-
|
|
502
|
-
subgraphData(): { columns: string[], data: any[][] } {
|
|
503
|
-
const columns = ["Id", "Label"];
|
|
504
|
-
const data = this.graphDB.subgraphs.map(sg => {
|
|
505
|
-
const row = [sg._.Id];
|
|
506
|
-
return this.formatRow(sg, columns, row);
|
|
507
|
-
});
|
|
508
|
-
return { columns, data };
|
|
509
|
-
}
|
|
510
|
-
|
|
511
|
-
calcGraphTooltip(item: VertexType | Edge) {
|
|
512
|
-
let scope;
|
|
513
|
-
let parentScope;
|
|
514
|
-
if (item instanceof Subgraph) {
|
|
515
|
-
const subgraph = this.rSubgraphsMap[item.id()];
|
|
516
|
-
scope = subgraph._;
|
|
517
|
-
parentScope = subgraph.parent._;
|
|
518
|
-
} else if (item instanceof Vertex || item instanceof Icon) {
|
|
519
|
-
const vertex = this.rVerticesMap[item.id()];
|
|
520
|
-
scope = vertex._;
|
|
521
|
-
parentScope = vertex.parent._;
|
|
522
|
-
} else if (item instanceof Edge) {
|
|
523
|
-
const edge = this.rEdgesMap[item.id()];
|
|
524
|
-
scope = edge._;
|
|
525
|
-
parentScope = edge.parent._;
|
|
526
|
-
}
|
|
527
|
-
if (scope) {
|
|
528
|
-
return scope.calcTooltip(parentScope);
|
|
529
|
-
}
|
|
530
|
-
return "";
|
|
531
|
-
}
|
|
532
|
-
|
|
533
|
-
subgraph(id: string): Subgraph | undefined {
|
|
534
|
-
return this.subgraphsMap[id];
|
|
535
|
-
}
|
|
536
|
-
|
|
537
|
-
vertex(id: string): VertexType | undefined {
|
|
538
|
-
return this.verticesMap[id];
|
|
539
|
-
}
|
|
540
|
-
|
|
541
|
-
edge(id: string): Edge {
|
|
542
|
-
return this.edgesMap[id];
|
|
543
|
-
}
|
|
544
|
-
|
|
545
|
-
// Events ---
|
|
546
|
-
minClick(sg: Subgraph) {
|
|
547
|
-
}
|
|
548
|
-
}
|
|
1
|
+
import { Icon } from "@hpcc-js/common";
|
|
2
|
+
import { BaseScope, ScopeEdge, ScopeGraph, ScopeSubgraph, ScopeVertex } from "@hpcc-js/comms";
|
|
3
|
+
import { Edge, IGraphData, Lineage, Subgraph, Vertex } from "@hpcc-js/graph";
|
|
4
|
+
import { Edge as UtilEdge, Subgraph as UtilSubgraph, Vertex as UtilVertex } from "@hpcc-js/util";
|
|
5
|
+
import { WUGraphLegendData } from "./WUGraphLegend";
|
|
6
|
+
|
|
7
|
+
export type VertexType = Vertex | Icon;
|
|
8
|
+
|
|
9
|
+
export interface MyGraphData {
|
|
10
|
+
subgraphs: Subgraph[];
|
|
11
|
+
vertices: VertexType[];
|
|
12
|
+
edges: Edge[];
|
|
13
|
+
hierarchy: Lineage[];
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
function faCharFactory(kind): string {
|
|
17
|
+
switch (kind) {
|
|
18
|
+
case "2": return "\uf0c7"; // Disk Write
|
|
19
|
+
case "3": return "\uf15d"; // sort
|
|
20
|
+
case "5": return "\uf0b0"; // Filter
|
|
21
|
+
case "6": return "\uf1e0"; // Split
|
|
22
|
+
case "12": return "\uf039"; // First N
|
|
23
|
+
case "15": return "\uf126"; // Lightweight Join
|
|
24
|
+
case "17": return "\uf126"; // Lookup Join
|
|
25
|
+
case "22": return "\uf1e6"; // Pipe Output
|
|
26
|
+
case "23": return "\uf078"; // Funnel
|
|
27
|
+
case "25": return "\uf0ce"; // Inline Dataset
|
|
28
|
+
case "26": return "\uf074"; // distribute
|
|
29
|
+
case "29": return "\uf005"; // Store Internal Result
|
|
30
|
+
case "36": return "\uf128"; // If
|
|
31
|
+
case "44": return "\uf0c7"; // write csv
|
|
32
|
+
case "47": return "\uf0c7"; // write
|
|
33
|
+
case "54": return "\uf013"; // Workunit Read
|
|
34
|
+
case "56": return "\uf0c7"; // Spill
|
|
35
|
+
case "59": return "\uf126"; // Merge
|
|
36
|
+
case "61": return "\uf0c7"; // write xml
|
|
37
|
+
case "82": return "\uf1c0"; // Projected Disk Read Spill
|
|
38
|
+
case "88": return "\uf1c0"; // Projected Disk Read Spill
|
|
39
|
+
case "92": return "\uf129"; // Limted Index Read
|
|
40
|
+
case "93": return "\uf129"; // Limted Index Read
|
|
41
|
+
case "99": return "\uf1c0"; // CSV Read
|
|
42
|
+
case "105": return "\uf1c0"; // CSV Read
|
|
43
|
+
|
|
44
|
+
case "7": return "\uf090"; // Project
|
|
45
|
+
case "9": return "\uf0e2"; // Local Iterate
|
|
46
|
+
case "16": return "\uf005"; // Output Internal
|
|
47
|
+
case "19": return "\uf074"; // Hash Distribute
|
|
48
|
+
case "21": return "\uf275"; // Normalize
|
|
49
|
+
case "35": return "\uf0c7"; // CSV Write
|
|
50
|
+
case "37": return "\uf0c7"; // Index Write
|
|
51
|
+
case "71": return "\uf1c0"; // Disk Read Spill
|
|
52
|
+
case "133": return "\uf0ce"; // Inline Dataset
|
|
53
|
+
case "148": return "\uf0ce"; // Inline Dataset
|
|
54
|
+
case "168": return "\uf275"; // Local Denormalize
|
|
55
|
+
}
|
|
56
|
+
return "\uf063";
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
export class WUScopeController {
|
|
60
|
+
private graphDB: ScopeGraph;
|
|
61
|
+
private subgraphsMap: { [id: string]: Subgraph } = {};
|
|
62
|
+
private rSubgraphsMap: { [id: string]: ScopeSubgraph } = {};
|
|
63
|
+
private verticesMap: { [id: string]: VertexType } = {};
|
|
64
|
+
private rVerticesMap: { [id: string]: ScopeVertex } = {};
|
|
65
|
+
private edgesMap: { [id: string]: Edge } = {};
|
|
66
|
+
private rEdgesMap: { [id: string]: ScopeEdge } = {};
|
|
67
|
+
private kindMap: { [id: string]: ScopeVertex[] } = {};
|
|
68
|
+
|
|
69
|
+
protected _disabled: { [kind: number]: boolean } = {};
|
|
70
|
+
|
|
71
|
+
constructor() {
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
clear() {
|
|
75
|
+
this.subgraphsMap = {};
|
|
76
|
+
this.rSubgraphsMap = {};
|
|
77
|
+
this.verticesMap = {};
|
|
78
|
+
this.rVerticesMap = {};
|
|
79
|
+
this.edgesMap = {};
|
|
80
|
+
this.rEdgesMap = {};
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
set(masterGraph: ScopeGraph) {
|
|
84
|
+
this.graphDB = masterGraph;
|
|
85
|
+
this.graphGui(this.graphDB);
|
|
86
|
+
|
|
87
|
+
this.kindMap = {};
|
|
88
|
+
this.graphDB.walk(item => {
|
|
89
|
+
if (item instanceof UtilSubgraph) {
|
|
90
|
+
} else if (item instanceof UtilVertex) {
|
|
91
|
+
const kind = item._.attr("Kind").RawValue;
|
|
92
|
+
if (!this.kindMap[kind]) {
|
|
93
|
+
this.kindMap[kind] = [];
|
|
94
|
+
}
|
|
95
|
+
this.kindMap[kind].push(item);
|
|
96
|
+
} else if (item instanceof UtilEdge) {
|
|
97
|
+
}
|
|
98
|
+
});
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
disabled(): number[];
|
|
102
|
+
disabled(_: number[]): this;
|
|
103
|
+
disabled(_?: number[]): number[] | this {
|
|
104
|
+
if (!arguments.length) {
|
|
105
|
+
const retVal = [];
|
|
106
|
+
for (const key in this._disabled) {
|
|
107
|
+
if (this._disabled[key]) {
|
|
108
|
+
retVal.push(key);
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
return retVal;
|
|
112
|
+
}
|
|
113
|
+
this._disabled = {};
|
|
114
|
+
_.forEach(kind => this._disabled[kind] = true);
|
|
115
|
+
return this;
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
graphGui(graphDB: ScopeGraph): IGraphData {
|
|
119
|
+
const graphGui: MyGraphData = {
|
|
120
|
+
subgraphs: [],
|
|
121
|
+
vertices: [],
|
|
122
|
+
edges: [],
|
|
123
|
+
hierarchy: []
|
|
124
|
+
};
|
|
125
|
+
|
|
126
|
+
graphDB.walk((item) => {
|
|
127
|
+
if (item instanceof UtilSubgraph) {
|
|
128
|
+
const subgraph = this.appendSubgraph(item, graphGui.hierarchy, graphGui.subgraphs);
|
|
129
|
+
subgraph.showMinMax(item.vertices.length > 3 || subgraph.minState() !== "normal");
|
|
130
|
+
} else if (item instanceof UtilVertex) {
|
|
131
|
+
this.appendVertex(item, graphGui.hierarchy, graphGui.vertices);
|
|
132
|
+
} else if (item instanceof UtilEdge) {
|
|
133
|
+
this.appendEdge(item, graphGui.edges);
|
|
134
|
+
}
|
|
135
|
+
});
|
|
136
|
+
|
|
137
|
+
return graphGui;
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
format(labelTpl, obj) {
|
|
141
|
+
let retVal = "";
|
|
142
|
+
let lpos = labelTpl.indexOf("%");
|
|
143
|
+
let rpos = -1;
|
|
144
|
+
while (lpos >= 0) {
|
|
145
|
+
retVal += labelTpl.substring(rpos + 1, lpos);
|
|
146
|
+
rpos = labelTpl.indexOf("%", lpos + 1);
|
|
147
|
+
if (rpos < 0) {
|
|
148
|
+
console.warn("Invalid Label Template");
|
|
149
|
+
break;
|
|
150
|
+
}
|
|
151
|
+
const key = labelTpl.substring(lpos + 1, rpos);
|
|
152
|
+
retVal += !key ? "%" : (obj[labelTpl.substring(lpos + 1, rpos)] || "");
|
|
153
|
+
lpos = labelTpl.indexOf("%", rpos + 1);
|
|
154
|
+
}
|
|
155
|
+
retVal += labelTpl.substring(rpos + 1, labelTpl.length);
|
|
156
|
+
return retVal.split("\\n").join("\n");
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
createSubgraph(subgraph: ScopeSubgraph): Subgraph {
|
|
160
|
+
let sg = this.subgraphsMap[subgraph._.Id];
|
|
161
|
+
if (!sg) {
|
|
162
|
+
sg = new Subgraph()
|
|
163
|
+
.title(subgraph._.Id)
|
|
164
|
+
.on("minClick", () => {
|
|
165
|
+
this.minClick(sg);
|
|
166
|
+
})
|
|
167
|
+
;
|
|
168
|
+
this.subgraphsMap[subgraph._.Id] = sg;
|
|
169
|
+
this.rSubgraphsMap[sg.id()] = subgraph;
|
|
170
|
+
}
|
|
171
|
+
return sg;
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
createVertex(vertex: ScopeVertex): VertexType {
|
|
175
|
+
let v = this.verticesMap[vertex._.Id];
|
|
176
|
+
if (!v) {
|
|
177
|
+
const attrs = vertex._.rawAttrs();
|
|
178
|
+
if (vertex._.ScopeType === "dummy") {
|
|
179
|
+
const parent = this.subgraphsMap[vertex.parent._.Id];
|
|
180
|
+
v = new Icon()
|
|
181
|
+
.shape_colorFill("darkred")
|
|
182
|
+
.shape_colorStroke("darkred")
|
|
183
|
+
.image_colorFill("white")
|
|
184
|
+
.faChar("\uf067")
|
|
185
|
+
.on("click", () => {
|
|
186
|
+
parent.minState("normal");
|
|
187
|
+
this.minClick(parent);
|
|
188
|
+
})
|
|
189
|
+
;
|
|
190
|
+
} else {
|
|
191
|
+
v = new Vertex()
|
|
192
|
+
.icon_shape_colorFill("#1f77b4")
|
|
193
|
+
.icon_image_colorFill("white")
|
|
194
|
+
.faChar(faCharFactory(attrs["Kind"]))
|
|
195
|
+
.text(attrs["Label"])
|
|
196
|
+
;
|
|
197
|
+
const annotations = [];
|
|
198
|
+
if (vertex._.hasAttr("Definition")) {
|
|
199
|
+
annotations.push({
|
|
200
|
+
faChar: "\uf036",
|
|
201
|
+
tooltip: "Definition",
|
|
202
|
+
shape_colorFill: "lightgray",
|
|
203
|
+
shape_colorStroke: "lightgray",
|
|
204
|
+
image_colorFill: "black"
|
|
205
|
+
});
|
|
206
|
+
}
|
|
207
|
+
if (vertex._.hasAttr("IsInternal")) {
|
|
208
|
+
annotations.push({
|
|
209
|
+
faChar: "\uf085",
|
|
210
|
+
tooltip: "IsInternal",
|
|
211
|
+
shape_colorFill: "red",
|
|
212
|
+
shape_colorStroke: "red",
|
|
213
|
+
image_colorFill: "white"
|
|
214
|
+
});
|
|
215
|
+
}
|
|
216
|
+
v.annotationIcons(annotations);
|
|
217
|
+
}
|
|
218
|
+
this.verticesMap[vertex._.Id] = v;
|
|
219
|
+
this.rVerticesMap[v.id()] = vertex;
|
|
220
|
+
}
|
|
221
|
+
return v;
|
|
222
|
+
}
|
|
223
|
+
|
|
224
|
+
isSpill(edge: ScopeEdge): boolean {
|
|
225
|
+
const sourceKind = edge.source._.attr("Kind").RawValue;
|
|
226
|
+
const targetKind = edge.target._.attr("Kind").RawValue;
|
|
227
|
+
return sourceKind === "2" || targetKind === "71";
|
|
228
|
+
}
|
|
229
|
+
|
|
230
|
+
spansSubgraph(edge: ScopeEdge): boolean {
|
|
231
|
+
return edge.source.parent._.Id !== edge.target.parent._.Id;
|
|
232
|
+
}
|
|
233
|
+
|
|
234
|
+
createEdge(edge: ScopeEdge): Edge | undefined {
|
|
235
|
+
let e = this.edgesMap[edge._.Id];
|
|
236
|
+
if (!e) {
|
|
237
|
+
const attrs = edge._.rawAttrs();
|
|
238
|
+
const sourceV = this.verticesMap[edge.source._.Id];
|
|
239
|
+
const targetV = this.verticesMap[edge.target._.Id];
|
|
240
|
+
if (sourceV && targetV) {
|
|
241
|
+
const isSpill = this.isSpill(edge);
|
|
242
|
+
const spansSubgraph = this.spansSubgraph(edge);
|
|
243
|
+
|
|
244
|
+
const label = this.format("%Label%\n%NumRowsProcessed%", attrs);
|
|
245
|
+
/* TODO: Add extra annotations once WUDetails is fixed...
|
|
246
|
+
const numSlaves = parseInt(attrs["NumSlaves"]);
|
|
247
|
+
const numStarts = parseInt(attrs["NumStarts"]);
|
|
248
|
+
const numStops = parseInt(attrs["NumStops"]);
|
|
249
|
+
const started = numStarts > 0;
|
|
250
|
+
const finished = numStops === numSlaves;
|
|
251
|
+
const active = started && !finished;
|
|
252
|
+
*/
|
|
253
|
+
|
|
254
|
+
let strokeDasharray = null;
|
|
255
|
+
let weight = 100;
|
|
256
|
+
if (attrs["IsDependency"]) {
|
|
257
|
+
weight = 10;
|
|
258
|
+
strokeDasharray = "1,5";
|
|
259
|
+
} else if (attrs["_childGraph"]) {
|
|
260
|
+
strokeDasharray = "5,5";
|
|
261
|
+
} else if (isSpill) {
|
|
262
|
+
weight = 25;
|
|
263
|
+
strokeDasharray = "5,5,10,5";
|
|
264
|
+
} else if (spansSubgraph) {
|
|
265
|
+
weight = 5;
|
|
266
|
+
strokeDasharray = "5,5";
|
|
267
|
+
}
|
|
268
|
+
e = new Edge()
|
|
269
|
+
.sourceVertex(sourceV)
|
|
270
|
+
.targetVertex(targetV)
|
|
271
|
+
.sourceMarker("circle")
|
|
272
|
+
.targetMarker("arrow")
|
|
273
|
+
.weight(weight)
|
|
274
|
+
.strokeDasharray(strokeDasharray)
|
|
275
|
+
.text(label)
|
|
276
|
+
;
|
|
277
|
+
this.edgesMap[edge._.Id] = e;
|
|
278
|
+
this.rEdgesMap[e.id()] = edge;
|
|
279
|
+
}
|
|
280
|
+
}
|
|
281
|
+
return e;
|
|
282
|
+
}
|
|
283
|
+
|
|
284
|
+
appendSubgraph(subgraph: ScopeSubgraph, hierarchy: Lineage[], subgraphs: Subgraph[]): Subgraph {
|
|
285
|
+
const sg = this.createSubgraph(subgraph);
|
|
286
|
+
subgraphs.push(sg);
|
|
287
|
+
const parent = this.subgraphsMap[subgraph.parent._.Id];
|
|
288
|
+
if (parent) {
|
|
289
|
+
hierarchy.push({ parent, child: sg });
|
|
290
|
+
}
|
|
291
|
+
return sg;
|
|
292
|
+
}
|
|
293
|
+
|
|
294
|
+
appendVertex(vertex: ScopeVertex, hierarchy: Lineage[], vertices: VertexType[]): VertexType {
|
|
295
|
+
const v = this.createVertex(vertex);
|
|
296
|
+
vertices.push(v);
|
|
297
|
+
const parent = this.subgraphsMap[vertex.parent._.Id];
|
|
298
|
+
if (parent) {
|
|
299
|
+
hierarchy.push({ parent, child: v });
|
|
300
|
+
}
|
|
301
|
+
return v;
|
|
302
|
+
}
|
|
303
|
+
|
|
304
|
+
appendEdge(edge: ScopeEdge, edges: Edge[]): Edge {
|
|
305
|
+
const e = this.createEdge(edge);
|
|
306
|
+
if (e) {
|
|
307
|
+
edges.push(e);
|
|
308
|
+
}
|
|
309
|
+
return e;
|
|
310
|
+
}
|
|
311
|
+
|
|
312
|
+
filterLegend(graphDB: ScopeGraph) {
|
|
313
|
+
for (let i = graphDB.vertices.length - 1; i >= 0; --i) {
|
|
314
|
+
const vertex = graphDB.vertices[i];
|
|
315
|
+
const kind = vertex._.attr("Kind").RawValue;
|
|
316
|
+
if (this._disabled[kind]) {
|
|
317
|
+
vertex.remove(false, (source: BaseScope, target: BaseScope) => {
|
|
318
|
+
return new BaseScope({
|
|
319
|
+
ScopeName: vertex._.ScopeName + ":in",
|
|
320
|
+
Id: source.Id + "->" + target.Id,
|
|
321
|
+
ScopeType: "dummy-edge",
|
|
322
|
+
Properties: {
|
|
323
|
+
Property: [vertex._.attr("Label")]
|
|
324
|
+
},
|
|
325
|
+
Notes: {
|
|
326
|
+
Note: []
|
|
327
|
+
},
|
|
328
|
+
SinkActivity: ""
|
|
329
|
+
});
|
|
330
|
+
});
|
|
331
|
+
}
|
|
332
|
+
}
|
|
333
|
+
}
|
|
334
|
+
|
|
335
|
+
filterPartial(graphDB: ScopeGraph) {
|
|
336
|
+
for (const subgraph of graphDB.subgraphs) {
|
|
337
|
+
const sg = this.subgraphsMap[subgraph._.Id];
|
|
338
|
+
switch (sg.minState()) {
|
|
339
|
+
case "partial":
|
|
340
|
+
const childVertices: ReadonlyArray<ScopeVertex> = subgraph.vertices;
|
|
341
|
+
const vShow: ScopeVertex[] = [];
|
|
342
|
+
const vHide: ScopeVertex[] = [];
|
|
343
|
+
|
|
344
|
+
for (const vertex of childVertices) {
|
|
345
|
+
if (vertex.inEdges.length === 0 || vertex.inEdges.some(edge => edge.source.parent !== edge.target.parent) ||
|
|
346
|
+
vertex.outEdges.length === 0 || vertex.outEdges.some(edge => edge.source.parent !== edge.target.parent)) {
|
|
347
|
+
vShow.push(vertex);
|
|
348
|
+
} else {
|
|
349
|
+
vHide.push(vertex);
|
|
350
|
+
}
|
|
351
|
+
}
|
|
352
|
+
|
|
353
|
+
if (vHide.length > 1) {
|
|
354
|
+
const dummyDetails = {
|
|
355
|
+
ScopeName: subgraph._.ScopeName,
|
|
356
|
+
Id: subgraph._.Id + ":dummy",
|
|
357
|
+
ScopeType: "dummy",
|
|
358
|
+
Properties: {
|
|
359
|
+
Property: [{
|
|
360
|
+
Name: "Activities",
|
|
361
|
+
RawValue: "" + vHide.length,
|
|
362
|
+
Formatted: "" + vHide.length,
|
|
363
|
+
Measure: "count",
|
|
364
|
+
Creator: "",
|
|
365
|
+
CreatorType: ""
|
|
366
|
+
}]
|
|
367
|
+
},
|
|
368
|
+
Notes: {
|
|
369
|
+
Note: []
|
|
370
|
+
},
|
|
371
|
+
SinkActivity: ""
|
|
372
|
+
};
|
|
373
|
+
const dummyScope = new BaseScope(dummyDetails);
|
|
374
|
+
const dummyVertex = subgraph.createVertex(dummyScope);
|
|
375
|
+
|
|
376
|
+
for (const vertex of vHide) {
|
|
377
|
+
for (const edge of vertex.inEdges) {
|
|
378
|
+
if (vShow.indexOf(edge.source) >= 0) {
|
|
379
|
+
const dummyEdgeScope = new BaseScope({
|
|
380
|
+
ScopeName: edge.source._.ScopeName,
|
|
381
|
+
Id: edge.source._.Id + "->" + dummyVertex._.Id,
|
|
382
|
+
ScopeType: "dummy-in",
|
|
383
|
+
Properties: {
|
|
384
|
+
Property: []
|
|
385
|
+
},
|
|
386
|
+
Notes: {
|
|
387
|
+
Note: []
|
|
388
|
+
},
|
|
389
|
+
SinkActivity: ""
|
|
390
|
+
});
|
|
391
|
+
subgraph.createEdge(edge.source, dummyVertex, dummyEdgeScope);
|
|
392
|
+
}
|
|
393
|
+
}
|
|
394
|
+
for (const edge of vertex.outEdges) {
|
|
395
|
+
if (vShow.indexOf(edge.target) >= 0) {
|
|
396
|
+
const dummyEdgeScope = new BaseScope({
|
|
397
|
+
ScopeName: edge.target._.ScopeName,
|
|
398
|
+
Id: dummyVertex._.Id + "->" + edge.target._.Id,
|
|
399
|
+
ScopeType: "dummy-out",
|
|
400
|
+
Properties: {
|
|
401
|
+
Property: []
|
|
402
|
+
},
|
|
403
|
+
Notes: {
|
|
404
|
+
Note: []
|
|
405
|
+
},
|
|
406
|
+
SinkActivity: ""
|
|
407
|
+
});
|
|
408
|
+
subgraph.createEdge(dummyVertex, edge.target, dummyEdgeScope);
|
|
409
|
+
}
|
|
410
|
+
}
|
|
411
|
+
}
|
|
412
|
+
vHide.forEach(vertex => vertex.remove(true));
|
|
413
|
+
}
|
|
414
|
+
break;
|
|
415
|
+
}
|
|
416
|
+
}
|
|
417
|
+
}
|
|
418
|
+
|
|
419
|
+
filterEmptySubgraphs(graphDB: ScopeGraph) {
|
|
420
|
+
while (true) {
|
|
421
|
+
const emptySubgraphs = graphDB.subgraphs.filter(subgraph => subgraph.subgraphs.length === 0 && subgraph.vertices.length === 0);
|
|
422
|
+
if (emptySubgraphs.length === 0) break;
|
|
423
|
+
emptySubgraphs.forEach(subgraph => subgraph.remove(true));
|
|
424
|
+
}
|
|
425
|
+
}
|
|
426
|
+
|
|
427
|
+
removeObsoleteSubgraphs(graphDB: ScopeGraph) {
|
|
428
|
+
for (const subgraph of [...graphDB.subgraphs]) {
|
|
429
|
+
if (subgraph.vertices.length === 0) {
|
|
430
|
+
subgraph.remove(false);
|
|
431
|
+
}
|
|
432
|
+
}
|
|
433
|
+
}
|
|
434
|
+
|
|
435
|
+
graphData(): IGraphData {
|
|
436
|
+
const graphDB = this.graphDB.clone();
|
|
437
|
+
this.filterLegend(graphDB);
|
|
438
|
+
this.filterPartial(graphDB);
|
|
439
|
+
this.filterEmptySubgraphs(graphDB);
|
|
440
|
+
this.removeObsoleteSubgraphs(graphDB);
|
|
441
|
+
return this.graphGui(graphDB);
|
|
442
|
+
}
|
|
443
|
+
|
|
444
|
+
calcLegend(): WUGraphLegendData[] {
|
|
445
|
+
const retVal: WUGraphLegendData[] = [];
|
|
446
|
+
for (const kind in this.kindMap) {
|
|
447
|
+
retVal.push({
|
|
448
|
+
kind: parseInt(kind),
|
|
449
|
+
faChar: faCharFactory(kind),
|
|
450
|
+
label: this.kindMap[kind][0]._.attr("Label").RawValue.split("\n")[0],
|
|
451
|
+
count: this.kindMap[kind].length
|
|
452
|
+
});
|
|
453
|
+
}
|
|
454
|
+
return retVal;
|
|
455
|
+
}
|
|
456
|
+
|
|
457
|
+
vertices(kind: number): VertexType[] {
|
|
458
|
+
const retVal: VertexType[] = [];
|
|
459
|
+
for (const v of this.kindMap[kind]) {
|
|
460
|
+
retVal.push(this.verticesMap[v._.Id]);
|
|
461
|
+
}
|
|
462
|
+
return retVal;
|
|
463
|
+
}
|
|
464
|
+
|
|
465
|
+
formatRow(item: ScopeEdge | ScopeSubgraph | ScopeVertex, columns, row) {
|
|
466
|
+
const attrs = item._.formattedAttrs();
|
|
467
|
+
for (const key in attrs) {
|
|
468
|
+
const idx = columns.indexOf(key);
|
|
469
|
+
if (idx === -1) {
|
|
470
|
+
columns.push(key);
|
|
471
|
+
row.push(attrs[key]);
|
|
472
|
+
} else {
|
|
473
|
+
row[idx] = attrs[key];
|
|
474
|
+
}
|
|
475
|
+
}
|
|
476
|
+
for (let i = 0; i < 100; ++i) {
|
|
477
|
+
if (row[i] === undefined) {
|
|
478
|
+
row[i] = "";
|
|
479
|
+
}
|
|
480
|
+
}
|
|
481
|
+
return row;
|
|
482
|
+
}
|
|
483
|
+
|
|
484
|
+
activityData(): { columns: string[], data: any[][] } {
|
|
485
|
+
const columns = ["Id", "Kind", "Label"];
|
|
486
|
+
const data = this.graphDB.vertices.map(v => {
|
|
487
|
+
const row = [parseInt(v._.Id.split("a")[1])];
|
|
488
|
+
return this.formatRow(v, columns, row);
|
|
489
|
+
});
|
|
490
|
+
return { columns, data };
|
|
491
|
+
}
|
|
492
|
+
|
|
493
|
+
edgeData(): { columns: string[], data: any[][] } {
|
|
494
|
+
const columns = ["Id", "Label"];
|
|
495
|
+
const data = this.graphDB.edges.map(e => {
|
|
496
|
+
const row = [e._.Id];
|
|
497
|
+
return this.formatRow(e, columns, row);
|
|
498
|
+
});
|
|
499
|
+
return { columns, data };
|
|
500
|
+
}
|
|
501
|
+
|
|
502
|
+
subgraphData(): { columns: string[], data: any[][] } {
|
|
503
|
+
const columns = ["Id", "Label"];
|
|
504
|
+
const data = this.graphDB.subgraphs.map(sg => {
|
|
505
|
+
const row = [sg._.Id];
|
|
506
|
+
return this.formatRow(sg, columns, row);
|
|
507
|
+
});
|
|
508
|
+
return { columns, data };
|
|
509
|
+
}
|
|
510
|
+
|
|
511
|
+
calcGraphTooltip(item: VertexType | Edge) {
|
|
512
|
+
let scope;
|
|
513
|
+
let parentScope;
|
|
514
|
+
if (item instanceof Subgraph) {
|
|
515
|
+
const subgraph = this.rSubgraphsMap[item.id()];
|
|
516
|
+
scope = subgraph._;
|
|
517
|
+
parentScope = subgraph.parent._;
|
|
518
|
+
} else if (item instanceof Vertex || item instanceof Icon) {
|
|
519
|
+
const vertex = this.rVerticesMap[item.id()];
|
|
520
|
+
scope = vertex._;
|
|
521
|
+
parentScope = vertex.parent._;
|
|
522
|
+
} else if (item instanceof Edge) {
|
|
523
|
+
const edge = this.rEdgesMap[item.id()];
|
|
524
|
+
scope = edge._;
|
|
525
|
+
parentScope = edge.parent._;
|
|
526
|
+
}
|
|
527
|
+
if (scope) {
|
|
528
|
+
return scope.calcTooltip(parentScope);
|
|
529
|
+
}
|
|
530
|
+
return "";
|
|
531
|
+
}
|
|
532
|
+
|
|
533
|
+
subgraph(id: string): Subgraph | undefined {
|
|
534
|
+
return this.subgraphsMap[id];
|
|
535
|
+
}
|
|
536
|
+
|
|
537
|
+
vertex(id: string): VertexType | undefined {
|
|
538
|
+
return this.verticesMap[id];
|
|
539
|
+
}
|
|
540
|
+
|
|
541
|
+
edge(id: string): Edge {
|
|
542
|
+
return this.edgesMap[id];
|
|
543
|
+
}
|
|
544
|
+
|
|
545
|
+
// Events ---
|
|
546
|
+
minClick(sg: Subgraph) {
|
|
547
|
+
}
|
|
548
|
+
}
|