@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.
@@ -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
+ }