@hpcc-js/marshaller 2.28.7 → 2.28.9

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.
Files changed (55) hide show
  1. package/LICENSE +43 -43
  2. package/dist/index.es6.js +25 -13
  3. package/dist/index.es6.js.map +1 -1
  4. package/dist/index.js +25 -13
  5. package/dist/index.js.map +1 -1
  6. package/dist/index.min.js +2 -2
  7. package/dist/index.min.js.map +1 -1
  8. package/package.json +14 -14
  9. package/src/__package__.ts +3 -3
  10. package/src/dashy.css +239 -239
  11. package/src/dashy.ts +521 -521
  12. package/src/ddl1/DDLApi.ts +229 -229
  13. package/src/ddl1/FlyoutButton.ts +120 -120
  14. package/src/ddl1/Graph.ts +93 -93
  15. package/src/ddl1/HTML.ts +77 -77
  16. package/src/ddl1/HipieDDL.ts +2437 -2437
  17. package/src/ddl1/HipieDDLMixin.ts +380 -380
  18. package/src/ddl1/Tabbed.ts +91 -91
  19. package/src/ddl1/TargetMarshaller.ts +57 -57
  20. package/src/ddl2/PopupManager.ts +89 -89
  21. package/src/ddl2/activities/activity.ts +431 -431
  22. package/src/ddl2/activities/databomb.ts +237 -237
  23. package/src/ddl2/activities/datasource.ts +52 -52
  24. package/src/ddl2/activities/dspicker.ts +106 -106
  25. package/src/ddl2/activities/filter.ts +542 -542
  26. package/src/ddl2/activities/form.ts +153 -153
  27. package/src/ddl2/activities/groupby.ts +439 -439
  28. package/src/ddl2/activities/hipiepipeline.ts +114 -114
  29. package/src/ddl2/activities/limit.ts +49 -49
  30. package/src/ddl2/activities/logicalfile.ts +62 -62
  31. package/src/ddl2/activities/nullview.ts +12 -12
  32. package/src/ddl2/activities/project.ts +764 -764
  33. package/src/ddl2/activities/rest.ts +568 -568
  34. package/src/ddl2/activities/roxie.ts +490 -490
  35. package/src/ddl2/activities/sampledata.json +16264 -16264
  36. package/src/ddl2/activities/sort.ts +176 -176
  37. package/src/ddl2/activities/wuresult.ts +395 -395
  38. package/src/ddl2/dashboard.css +13 -13
  39. package/src/ddl2/dashboard.ts +330 -330
  40. package/src/ddl2/dashboardDockPanel.ts +123 -123
  41. package/src/ddl2/dashboardGrid.ts +202 -202
  42. package/src/ddl2/ddl.ts +410 -410
  43. package/src/ddl2/ddleditor.ts +60 -60
  44. package/src/ddl2/dsTable.ts +238 -238
  45. package/src/ddl2/dvTable.ts +31 -31
  46. package/src/ddl2/graphadapter.ts +297 -297
  47. package/src/ddl2/javascriptadapter.ts +354 -354
  48. package/src/ddl2/model/element.ts +398 -398
  49. package/src/ddl2/model/visualization.ts +351 -351
  50. package/src/ddl2/model/vizChartPanel.ts +149 -149
  51. package/src/ddl2/pipelinePanel.css +4 -4
  52. package/src/ddl2/pipelinePanel.ts +465 -465
  53. package/src/index.ts +26 -26
  54. package/types/__package__.d.ts +2 -2
  55. package/types-3.4/__package__.d.ts +2 -2
@@ -1,439 +1,439 @@
1
- import { PropertyExt, publish } from "@hpcc-js/common";
2
- import { DDL2 } from "@hpcc-js/ddl-shim";
3
- import { hashSum } from "@hpcc-js/util";
4
- import { deviation as d3Deviation, max as d3Max, mean as d3Mean, median as d3Median, min as d3Min, sum as d3Sum, variance as d3Variance } from "d3-array";
5
- import { nest as d3Nest } from "d3-collection";
6
- import { Activity, IActivityError, ReferencedFields } from "./activity";
7
-
8
- export class GroupByColumn extends PropertyExt {
9
- private _owner: GroupBy;
10
-
11
- @publish(undefined, "set", "Field", function (this: GroupByColumn) { return this.columns(); }, {
12
- optional: true,
13
- validate: (w: GroupByColumn): boolean => w.columns().indexOf(w.label()) >= 0
14
- })
15
- label: publish<this, string>;
16
- label_valid: () => boolean;
17
-
18
- validate(prefix: string): IActivityError[] {
19
- const retVal: IActivityError[] = [];
20
- if (!this.label_valid()) {
21
- retVal.push({
22
- source: `${prefix}.label`,
23
- msg: `Invalid label: "${this.label()}"`,
24
- hint: `expected ${JSON.stringify(this.columns())}`
25
- });
26
- }
27
- return retVal;
28
- }
29
-
30
- constructor() {
31
- super();
32
- }
33
-
34
- owner(): GroupBy;
35
- owner(_: GroupBy): this;
36
- owner(_?: GroupBy): GroupBy | this {
37
- if (!arguments.length) return this._owner;
38
- this._owner = _;
39
- return this;
40
- }
41
-
42
- valid(): boolean {
43
- return !!this.label();
44
- }
45
-
46
- toDDL(): string {
47
- return this.label();
48
- }
49
-
50
- fromDDL(label: string): this {
51
- return this
52
- .label(label)
53
- ;
54
- }
55
-
56
- static fromDDL(label: string): GroupByColumn {
57
- return new GroupByColumn().fromDDL(label);
58
- }
59
-
60
- hash(): string {
61
- return hashSum(this.label());
62
- }
63
-
64
- columns() {
65
- return this._owner.inFieldIDs();
66
- }
67
- }
68
- GroupByColumn.prototype._class += " GroupByColumn";
69
-
70
- // ===========================================================================
71
- export type AggrFuncCallback = (item: any) => number;
72
- export type AggrFunc = (leaves: any[], callback: AggrFuncCallback) => number;
73
- function localCount(leaves: any[], callback: AggrFuncCallback): number {
74
- return leaves.length;
75
- }
76
-
77
- const d3Aggr: { [key: string]: AggrFunc } = {
78
- count: localCount,
79
- min: d3Min,
80
- max: d3Max,
81
- mean: d3Mean,
82
- median: d3Median,
83
- variance: d3Variance,
84
- deviation: d3Deviation,
85
- sum: d3Sum
86
- };
87
-
88
- export type AggregateType = "count" | "min" | "max" | "sum" | "mean" | "median" | "variance" | "deviation";
89
- export class AggregateField extends PropertyExt {
90
- private _owner: GroupBy;
91
-
92
- @publish(null, "string", "new Field ID", null, { optional: true, disable: (w: AggregateField) => !w.hasColumn() })
93
- fieldID: publish<this, string>;
94
- @publish("count", "set", "Aggregation Type", ["count", "min", "max", "sum", "mean", "median", "variance", "deviation"], { optional: true, disable: (w: AggregateField) => !w.fieldID() })
95
- aggrType: publish<this, AggregateType>;
96
- @publish(null, "set", "Aggregation Field", function (this: AggregateField) { return this.columns(); }, {
97
- optional: true,
98
- disable: (w: AggregateField) => w.disableAggrColumn(),
99
- validate: (w: AggregateField): boolean => w.columns().indexOf(w.aggrColumn()) >= 0
100
- })
101
- aggrColumn: publish<this, string>;
102
- aggrColumn_valid: () => boolean;
103
- @publish(null, "set", "Base Count Field", function (this: AggregateField) { return this.columns(); }, {
104
- optional: true,
105
- disable: (w: AggregateField) => w.disableBaseCountColumn(),
106
- validate: (w: AggregateField): boolean => w.columns().indexOf(w.baseCountColumn()) >= 0
107
- })
108
- baseCountColumn: publish<this, string>;
109
- baseCountColumn_valid: () => boolean;
110
-
111
- disableAggrColumn(): boolean {
112
- return !this.fieldID() || !this.aggrType() || this.aggrType() === "count";
113
- }
114
-
115
- disableBaseCountColumn(): boolean {
116
- return !this.fieldID() || !this.aggrType() || this.aggrType() !== "mean";
117
- }
118
-
119
- validate(prefix: string): IActivityError[] {
120
- const retVal: IActivityError[] = [];
121
- if (!this.aggrColumn_valid()) {
122
- retVal.push({
123
- source: `${prefix}.${this.fieldID()}.aggrColumn`,
124
- msg: `Invalid aggrColumn: "${this.aggrColumn()}"`,
125
- hint: `expected ${JSON.stringify(this.columns())}`
126
- });
127
- }
128
- if (!this.baseCountColumn_valid()) {
129
- retVal.push({
130
- source: `${prefix}.${this.fieldID()}.aggrColumn`,
131
- msg: `Invalid baseCountColumn: "${this.baseCountColumn()}"`,
132
- hint: `expected ${JSON.stringify(this.columns())}`
133
- });
134
- }
135
- return retVal;
136
- }
137
-
138
- constructor() {
139
- super();
140
- }
141
-
142
- owner(): GroupBy;
143
- owner(_: GroupBy): this;
144
- owner(_?: GroupBy): GroupBy | this {
145
- if (!arguments.length) return this._owner;
146
- this._owner = _;
147
- return this;
148
- }
149
-
150
- valid(): boolean {
151
- return !!this.fieldID();
152
- }
153
-
154
- toDDL(): DDL2.IAggregate | DDL2.ICount {
155
- if (this.aggrType() === "count") {
156
- return {
157
- fieldID: this.fieldID(),
158
- type: "count"
159
- };
160
- }
161
- return {
162
- fieldID: this.fieldID(),
163
- type: this.aggrType() as DDL2.IAggregateType,
164
- inFieldID: this.aggrColumn(),
165
- baseCountFieldID: this.baseCountColumn()
166
- };
167
- }
168
-
169
- fromDDL(ddl: DDL2.IAggregate | DDL2.ICount): this {
170
- const retVal = this
171
- .fieldID(ddl.fieldID)
172
- .aggrType(ddl.type)
173
- ;
174
- if (ddl.type !== "count") {
175
- retVal.aggrColumn(ddl.inFieldID);
176
- retVal.baseCountColumn(ddl.baseCountFieldID);
177
- }
178
- return retVal;
179
- }
180
-
181
- static fromDDL(ddl: DDL2.IAggregate | DDL2.ICount): AggregateField {
182
- return new AggregateField().fromDDL(ddl);
183
- }
184
-
185
- hash(): string {
186
- return hashSum({
187
- label: this.fieldID(),
188
- aggrType: this.aggrType(),
189
- aggrColumn: this.aggrColumn()
190
- });
191
- }
192
-
193
- columns() {
194
- return this._owner.inFieldIDs();
195
- }
196
-
197
- hasColumn() {
198
- return this.columns().length;
199
- }
200
-
201
- aggregate(values: Array<{ [key: string]: any }>) {
202
- return d3Aggr[this.aggrType() as string](values, leaf => +leaf[this.aggrColumn()]);
203
- }
204
-
205
- aggrFunc(): (leaves: any[]) => number {
206
- const aggrFunc = d3Aggr[this.aggrType() as string];
207
- const aggrColumn = this.aggrColumn();
208
- const baseCountColumn = this.baseCountColumn();
209
- if (baseCountColumn) {
210
- return (values: any[]) => {
211
- return aggrFunc(values, leaf => +leaf[aggrColumn] / +leaf[baseCountColumn]);
212
- };
213
- } else {
214
- return (values: any[]) => {
215
- return aggrFunc(values, leaf => +leaf[aggrColumn]);
216
- };
217
- }
218
- }
219
- }
220
- AggregateField.prototype._class += " AggregateField";
221
-
222
- // ===========================================================================
223
- export class GroupBy extends Activity {
224
-
225
- @publish([], "propertyArray", "Source Columns", null, { autoExpand: GroupByColumn })
226
- column: publish<this, GroupByColumn[]>;
227
- @publish([], "propertyArray", "Computed Fields", null, { autoExpand: AggregateField })
228
- computedFields: publish<this, AggregateField[]>;
229
- @publish(false, "boolean", "Show details")
230
- details: publish<this, boolean>;
231
- @publish(false, "boolean", "Show groupBy fileds in details")
232
- fullDetails: publish<this, boolean>;
233
-
234
- validate(): IActivityError[] {
235
- let retVal: IActivityError[] = [];
236
- for (const gbColumn of this.validGroupBy()) {
237
- retVal = retVal.concat(gbColumn.validate("GroupBy.column"));
238
- }
239
- for (const cfs of this.validComputedFields()) {
240
- retVal = retVal.concat(cfs.validate("GroupBy.computedFields"));
241
- }
242
- return retVal;
243
- }
244
-
245
- constructor() {
246
- super();
247
- }
248
-
249
- toDDL(): DDL2.IGroupBy {
250
- return {
251
- type: "groupby",
252
- groupByIDs: this.fieldIDs(),
253
- aggregates: this.aggregates()
254
- };
255
- }
256
-
257
- fromDDL(ddl: DDL2.IGroupBy): this {
258
- return this
259
- .fieldIDs(ddl.groupByIDs)
260
- .aggregates(ddl.aggregates)
261
- ;
262
- }
263
-
264
- static fromDDL(ddl: DDL2.IGroupBy): GroupBy {
265
- return new GroupBy().fromDDL(ddl);
266
- }
267
-
268
- fieldIDs(): string[];
269
- fieldIDs(_: string[]): this;
270
- fieldIDs(_?: string[]): string[] | this {
271
- if (!arguments.length) return this.validGroupBy().map(gb => gb.toDDL());
272
- this.column(_.map(fieldID => GroupByColumn.fromDDL(fieldID)));
273
- return this;
274
- }
275
-
276
- aggregates(): DDL2.AggregateType[];
277
- aggregates(_: DDL2.AggregateType[]): this;
278
- aggregates(_?: DDL2.AggregateType[]): DDL2.AggregateType[] | this {
279
- if (!arguments.length) return this.validComputedFields().map(cf => cf.toDDL());
280
- this.computedFields(_.map(aggrType => AggregateField.fromDDL(aggrType)));
281
- return this;
282
- }
283
-
284
- // Activity
285
- hash(): string {
286
- return hashSum({
287
- groupBy: this.column().map(gb => gb.hash()),
288
- computedFields: this.computedFields().map(cf => cf.hash())
289
- });
290
- }
291
-
292
- appendGroupBys(columns: [{ field: string }]): this {
293
- for (const column of columns) {
294
- this.column().push(new GroupByColumn()
295
- .owner(this)
296
- .label(column.field)
297
- );
298
- }
299
- return this;
300
- }
301
-
302
- validGroupBy(): GroupByColumn[] {
303
- return this.column().filter(groupBy => groupBy.valid());
304
- }
305
-
306
- exists(): boolean {
307
- return this.validGroupBy().length > 0;
308
- }
309
-
310
- inFieldIDs(): string[] {
311
- return this.inFields().map(field => field.id);
312
- }
313
-
314
- field(fieldID: string): DDL2.IField | null {
315
- for (const field of this.inFields()) {
316
- if (field.id === fieldID) {
317
- return field;
318
- }
319
- }
320
- return null;
321
- }
322
-
323
- appendComputedFields(aggregateFields: [{ label: string, type: AggregateType, column?: string }]): this {
324
- for (const aggregateField of aggregateFields) {
325
- const aggrField = new AggregateField()
326
- .owner(this)
327
- .fieldID(aggregateField.label)
328
- .aggrType(aggregateField.type)
329
- ;
330
- if (aggregateField.column !== void 0) {
331
- aggrField.aggrColumn(aggregateField.column);
332
- }
333
- this.computedFields().push(aggrField);
334
- }
335
- return this;
336
- }
337
-
338
- validComputedFields() {
339
- return this.computedFields().filter(computedField => computedField.valid());
340
- }
341
-
342
- hasComputedFields(): boolean {
343
- return this.validComputedFields().length > 0;
344
- }
345
-
346
- computeFields(inFields: ReadonlyArray<DDL2.IField>): () => ReadonlyArray<DDL2.IField> {
347
- if (!this.exists()) return super.computeFields(inFields);
348
- const retVal: DDL2.IField[] = [];
349
- const groups: GroupByColumn[] = this.validGroupBy();
350
- for (const groupBy of groups) {
351
- const groupByField = this.field(groupBy.label());
352
- const field = {
353
- type: groupByField ? groupByField.type : "string",
354
- id: groupBy.label()
355
- } as DDL2.IField;
356
- retVal.push(field);
357
- }
358
- for (const cf of this.computedFields()) {
359
- if (cf.fieldID()) {
360
- const computedField: DDL2.IField = {
361
- id: cf.fieldID(),
362
- type: "number"
363
- };
364
- retVal.push(computedField);
365
- }
366
- }
367
- if (this.details()) {
368
- let detailsTarget: DDL2.IField[] = retVal;
369
- if (this.exists()) {
370
- const rows: DDL2.IField = {
371
- id: "values",
372
- type: "dataset",
373
- children: []
374
- };
375
- retVal.push(rows);
376
- detailsTarget = rows.children;
377
- }
378
- const columns = groups.map(groupBy => groupBy.label());
379
- detailsTarget.push(...this.inFields().filter(field => {
380
- return this.fullDetails() || columns.indexOf(field.id) < 0;
381
- }));
382
- }
383
- return () => retVal;
384
- }
385
-
386
- referencedFields(refs: ReferencedFields): void {
387
- super.referencedFields(refs);
388
- const fieldIDs: string[] = [];
389
- for (const gb of this.validGroupBy()) {
390
- fieldIDs.push(gb.label());
391
- }
392
- for (const cf of this.validComputedFields()) {
393
- if (cf.aggrColumn()) {
394
- fieldIDs.push(cf.aggrColumn());
395
- }
396
- }
397
- super.resolveInFields(refs, fieldIDs);
398
- }
399
-
400
- computeData(): ReadonlyArray<object> {
401
- const data = super.computeData();
402
- if (data.length === 0 || !this.exists()) return data;
403
- const columnLabels: string[] = this.validGroupBy().map(gb => gb.label());
404
- const computedFields = this.validComputedFields().map(cf => {
405
- return { label: cf.fieldID(), aggrFunc: cf.aggrFunc() };
406
- });
407
- const retVal = d3Nest()
408
- .key((row: { [key: string]: any }) => {
409
- let key = "";
410
- for (const groupByLabel of columnLabels) {
411
- key += ":" + row[groupByLabel];
412
- }
413
- return key;
414
- })
415
- .entries(data as object[]).map(_row => {
416
- const row: {
417
- [key: string]: any
418
- } = _row;
419
- delete row.key;
420
- for (const groupByLabel of columnLabels) {
421
- row[groupByLabel] = row.values[0][groupByLabel];
422
- }
423
- for (const cf of computedFields) {
424
- row[cf.label] = cf.aggrFunc(row.values);
425
- }
426
- return row;
427
- })
428
- ;
429
- const outFields = this.outFields();
430
- return retVal.map(row => {
431
- const retVal = {};
432
- for (const field of outFields) {
433
- retVal[field.id] = row[field.id];
434
- }
435
- return retVal;
436
- });
437
- }
438
- }
439
- GroupBy.prototype._class += " GroupBy";
1
+ import { PropertyExt, publish } from "@hpcc-js/common";
2
+ import { DDL2 } from "@hpcc-js/ddl-shim";
3
+ import { hashSum } from "@hpcc-js/util";
4
+ import { deviation as d3Deviation, max as d3Max, mean as d3Mean, median as d3Median, min as d3Min, sum as d3Sum, variance as d3Variance } from "d3-array";
5
+ import { nest as d3Nest } from "d3-collection";
6
+ import { Activity, IActivityError, ReferencedFields } from "./activity";
7
+
8
+ export class GroupByColumn extends PropertyExt {
9
+ private _owner: GroupBy;
10
+
11
+ @publish(undefined, "set", "Field", function (this: GroupByColumn) { return this.columns(); }, {
12
+ optional: true,
13
+ validate: (w: GroupByColumn): boolean => w.columns().indexOf(w.label()) >= 0
14
+ })
15
+ label: publish<this, string>;
16
+ label_valid: () => boolean;
17
+
18
+ validate(prefix: string): IActivityError[] {
19
+ const retVal: IActivityError[] = [];
20
+ if (!this.label_valid()) {
21
+ retVal.push({
22
+ source: `${prefix}.label`,
23
+ msg: `Invalid label: "${this.label()}"`,
24
+ hint: `expected ${JSON.stringify(this.columns())}`
25
+ });
26
+ }
27
+ return retVal;
28
+ }
29
+
30
+ constructor() {
31
+ super();
32
+ }
33
+
34
+ owner(): GroupBy;
35
+ owner(_: GroupBy): this;
36
+ owner(_?: GroupBy): GroupBy | this {
37
+ if (!arguments.length) return this._owner;
38
+ this._owner = _;
39
+ return this;
40
+ }
41
+
42
+ valid(): boolean {
43
+ return !!this.label();
44
+ }
45
+
46
+ toDDL(): string {
47
+ return this.label();
48
+ }
49
+
50
+ fromDDL(label: string): this {
51
+ return this
52
+ .label(label)
53
+ ;
54
+ }
55
+
56
+ static fromDDL(label: string): GroupByColumn {
57
+ return new GroupByColumn().fromDDL(label);
58
+ }
59
+
60
+ hash(): string {
61
+ return hashSum(this.label());
62
+ }
63
+
64
+ columns() {
65
+ return this._owner.inFieldIDs();
66
+ }
67
+ }
68
+ GroupByColumn.prototype._class += " GroupByColumn";
69
+
70
+ // ===========================================================================
71
+ export type AggrFuncCallback = (item: any) => number;
72
+ export type AggrFunc = (leaves: any[], callback: AggrFuncCallback) => number;
73
+ function localCount(leaves: any[], callback: AggrFuncCallback): number {
74
+ return leaves.length;
75
+ }
76
+
77
+ const d3Aggr: { [key: string]: AggrFunc } = {
78
+ count: localCount,
79
+ min: d3Min,
80
+ max: d3Max,
81
+ mean: d3Mean,
82
+ median: d3Median,
83
+ variance: d3Variance,
84
+ deviation: d3Deviation,
85
+ sum: d3Sum
86
+ };
87
+
88
+ export type AggregateType = "count" | "min" | "max" | "sum" | "mean" | "median" | "variance" | "deviation";
89
+ export class AggregateField extends PropertyExt {
90
+ private _owner: GroupBy;
91
+
92
+ @publish(null, "string", "new Field ID", null, { optional: true, disable: (w: AggregateField) => !w.hasColumn() })
93
+ fieldID: publish<this, string>;
94
+ @publish("count", "set", "Aggregation Type", ["count", "min", "max", "sum", "mean", "median", "variance", "deviation"], { optional: true, disable: (w: AggregateField) => !w.fieldID() })
95
+ aggrType: publish<this, AggregateType>;
96
+ @publish(null, "set", "Aggregation Field", function (this: AggregateField) { return this.columns(); }, {
97
+ optional: true,
98
+ disable: (w: AggregateField) => w.disableAggrColumn(),
99
+ validate: (w: AggregateField): boolean => w.columns().indexOf(w.aggrColumn()) >= 0
100
+ })
101
+ aggrColumn: publish<this, string>;
102
+ aggrColumn_valid: () => boolean;
103
+ @publish(null, "set", "Base Count Field", function (this: AggregateField) { return this.columns(); }, {
104
+ optional: true,
105
+ disable: (w: AggregateField) => w.disableBaseCountColumn(),
106
+ validate: (w: AggregateField): boolean => w.columns().indexOf(w.baseCountColumn()) >= 0
107
+ })
108
+ baseCountColumn: publish<this, string>;
109
+ baseCountColumn_valid: () => boolean;
110
+
111
+ disableAggrColumn(): boolean {
112
+ return !this.fieldID() || !this.aggrType() || this.aggrType() === "count";
113
+ }
114
+
115
+ disableBaseCountColumn(): boolean {
116
+ return !this.fieldID() || !this.aggrType() || this.aggrType() !== "mean";
117
+ }
118
+
119
+ validate(prefix: string): IActivityError[] {
120
+ const retVal: IActivityError[] = [];
121
+ if (!this.aggrColumn_valid()) {
122
+ retVal.push({
123
+ source: `${prefix}.${this.fieldID()}.aggrColumn`,
124
+ msg: `Invalid aggrColumn: "${this.aggrColumn()}"`,
125
+ hint: `expected ${JSON.stringify(this.columns())}`
126
+ });
127
+ }
128
+ if (!this.baseCountColumn_valid()) {
129
+ retVal.push({
130
+ source: `${prefix}.${this.fieldID()}.aggrColumn`,
131
+ msg: `Invalid baseCountColumn: "${this.baseCountColumn()}"`,
132
+ hint: `expected ${JSON.stringify(this.columns())}`
133
+ });
134
+ }
135
+ return retVal;
136
+ }
137
+
138
+ constructor() {
139
+ super();
140
+ }
141
+
142
+ owner(): GroupBy;
143
+ owner(_: GroupBy): this;
144
+ owner(_?: GroupBy): GroupBy | this {
145
+ if (!arguments.length) return this._owner;
146
+ this._owner = _;
147
+ return this;
148
+ }
149
+
150
+ valid(): boolean {
151
+ return !!this.fieldID();
152
+ }
153
+
154
+ toDDL(): DDL2.IAggregate | DDL2.ICount {
155
+ if (this.aggrType() === "count") {
156
+ return {
157
+ fieldID: this.fieldID(),
158
+ type: "count"
159
+ };
160
+ }
161
+ return {
162
+ fieldID: this.fieldID(),
163
+ type: this.aggrType() as DDL2.IAggregateType,
164
+ inFieldID: this.aggrColumn(),
165
+ baseCountFieldID: this.baseCountColumn()
166
+ };
167
+ }
168
+
169
+ fromDDL(ddl: DDL2.IAggregate | DDL2.ICount): this {
170
+ const retVal = this
171
+ .fieldID(ddl.fieldID)
172
+ .aggrType(ddl.type)
173
+ ;
174
+ if (ddl.type !== "count") {
175
+ retVal.aggrColumn(ddl.inFieldID);
176
+ retVal.baseCountColumn(ddl.baseCountFieldID);
177
+ }
178
+ return retVal;
179
+ }
180
+
181
+ static fromDDL(ddl: DDL2.IAggregate | DDL2.ICount): AggregateField {
182
+ return new AggregateField().fromDDL(ddl);
183
+ }
184
+
185
+ hash(): string {
186
+ return hashSum({
187
+ label: this.fieldID(),
188
+ aggrType: this.aggrType(),
189
+ aggrColumn: this.aggrColumn()
190
+ });
191
+ }
192
+
193
+ columns() {
194
+ return this._owner.inFieldIDs();
195
+ }
196
+
197
+ hasColumn() {
198
+ return this.columns().length;
199
+ }
200
+
201
+ aggregate(values: Array<{ [key: string]: any }>) {
202
+ return d3Aggr[this.aggrType() as string](values, leaf => +leaf[this.aggrColumn()]);
203
+ }
204
+
205
+ aggrFunc(): (leaves: any[]) => number {
206
+ const aggrFunc = d3Aggr[this.aggrType() as string];
207
+ const aggrColumn = this.aggrColumn();
208
+ const baseCountColumn = this.baseCountColumn();
209
+ if (baseCountColumn) {
210
+ return (values: any[]) => {
211
+ return aggrFunc(values, leaf => +leaf[aggrColumn] / +leaf[baseCountColumn]);
212
+ };
213
+ } else {
214
+ return (values: any[]) => {
215
+ return aggrFunc(values, leaf => +leaf[aggrColumn]);
216
+ };
217
+ }
218
+ }
219
+ }
220
+ AggregateField.prototype._class += " AggregateField";
221
+
222
+ // ===========================================================================
223
+ export class GroupBy extends Activity {
224
+
225
+ @publish([], "propertyArray", "Source Columns", null, { autoExpand: GroupByColumn })
226
+ column: publish<this, GroupByColumn[]>;
227
+ @publish([], "propertyArray", "Computed Fields", null, { autoExpand: AggregateField })
228
+ computedFields: publish<this, AggregateField[]>;
229
+ @publish(false, "boolean", "Show details")
230
+ details: publish<this, boolean>;
231
+ @publish(false, "boolean", "Show groupBy fileds in details")
232
+ fullDetails: publish<this, boolean>;
233
+
234
+ validate(): IActivityError[] {
235
+ let retVal: IActivityError[] = [];
236
+ for (const gbColumn of this.validGroupBy()) {
237
+ retVal = retVal.concat(gbColumn.validate("GroupBy.column"));
238
+ }
239
+ for (const cfs of this.validComputedFields()) {
240
+ retVal = retVal.concat(cfs.validate("GroupBy.computedFields"));
241
+ }
242
+ return retVal;
243
+ }
244
+
245
+ constructor() {
246
+ super();
247
+ }
248
+
249
+ toDDL(): DDL2.IGroupBy {
250
+ return {
251
+ type: "groupby",
252
+ groupByIDs: this.fieldIDs(),
253
+ aggregates: this.aggregates()
254
+ };
255
+ }
256
+
257
+ fromDDL(ddl: DDL2.IGroupBy): this {
258
+ return this
259
+ .fieldIDs(ddl.groupByIDs)
260
+ .aggregates(ddl.aggregates)
261
+ ;
262
+ }
263
+
264
+ static fromDDL(ddl: DDL2.IGroupBy): GroupBy {
265
+ return new GroupBy().fromDDL(ddl);
266
+ }
267
+
268
+ fieldIDs(): string[];
269
+ fieldIDs(_: string[]): this;
270
+ fieldIDs(_?: string[]): string[] | this {
271
+ if (!arguments.length) return this.validGroupBy().map(gb => gb.toDDL());
272
+ this.column(_.map(fieldID => GroupByColumn.fromDDL(fieldID)));
273
+ return this;
274
+ }
275
+
276
+ aggregates(): DDL2.AggregateType[];
277
+ aggregates(_: DDL2.AggregateType[]): this;
278
+ aggregates(_?: DDL2.AggregateType[]): DDL2.AggregateType[] | this {
279
+ if (!arguments.length) return this.validComputedFields().map(cf => cf.toDDL());
280
+ this.computedFields(_.map(aggrType => AggregateField.fromDDL(aggrType)));
281
+ return this;
282
+ }
283
+
284
+ // Activity
285
+ hash(): string {
286
+ return hashSum({
287
+ groupBy: this.column().map(gb => gb.hash()),
288
+ computedFields: this.computedFields().map(cf => cf.hash())
289
+ });
290
+ }
291
+
292
+ appendGroupBys(columns: [{ field: string }]): this {
293
+ for (const column of columns) {
294
+ this.column().push(new GroupByColumn()
295
+ .owner(this)
296
+ .label(column.field)
297
+ );
298
+ }
299
+ return this;
300
+ }
301
+
302
+ validGroupBy(): GroupByColumn[] {
303
+ return this.column().filter(groupBy => groupBy.valid());
304
+ }
305
+
306
+ exists(): boolean {
307
+ return this.validGroupBy().length > 0;
308
+ }
309
+
310
+ inFieldIDs(): string[] {
311
+ return this.inFields().map(field => field.id);
312
+ }
313
+
314
+ field(fieldID: string): DDL2.IField | null {
315
+ for (const field of this.inFields()) {
316
+ if (field.id === fieldID) {
317
+ return field;
318
+ }
319
+ }
320
+ return null;
321
+ }
322
+
323
+ appendComputedFields(aggregateFields: [{ label: string, type: AggregateType, column?: string }]): this {
324
+ for (const aggregateField of aggregateFields) {
325
+ const aggrField = new AggregateField()
326
+ .owner(this)
327
+ .fieldID(aggregateField.label)
328
+ .aggrType(aggregateField.type)
329
+ ;
330
+ if (aggregateField.column !== void 0) {
331
+ aggrField.aggrColumn(aggregateField.column);
332
+ }
333
+ this.computedFields().push(aggrField);
334
+ }
335
+ return this;
336
+ }
337
+
338
+ validComputedFields() {
339
+ return this.computedFields().filter(computedField => computedField.valid());
340
+ }
341
+
342
+ hasComputedFields(): boolean {
343
+ return this.validComputedFields().length > 0;
344
+ }
345
+
346
+ computeFields(inFields: ReadonlyArray<DDL2.IField>): () => ReadonlyArray<DDL2.IField> {
347
+ if (!this.exists()) return super.computeFields(inFields);
348
+ const retVal: DDL2.IField[] = [];
349
+ const groups: GroupByColumn[] = this.validGroupBy();
350
+ for (const groupBy of groups) {
351
+ const groupByField = this.field(groupBy.label());
352
+ const field = {
353
+ type: groupByField ? groupByField.type : "string",
354
+ id: groupBy.label()
355
+ } as DDL2.IField;
356
+ retVal.push(field);
357
+ }
358
+ for (const cf of this.computedFields()) {
359
+ if (cf.fieldID()) {
360
+ const computedField: DDL2.IField = {
361
+ id: cf.fieldID(),
362
+ type: "number"
363
+ };
364
+ retVal.push(computedField);
365
+ }
366
+ }
367
+ if (this.details()) {
368
+ let detailsTarget: DDL2.IField[] = retVal;
369
+ if (this.exists()) {
370
+ const rows: DDL2.IField = {
371
+ id: "values",
372
+ type: "dataset",
373
+ children: []
374
+ };
375
+ retVal.push(rows);
376
+ detailsTarget = rows.children;
377
+ }
378
+ const columns = groups.map(groupBy => groupBy.label());
379
+ detailsTarget.push(...this.inFields().filter(field => {
380
+ return this.fullDetails() || columns.indexOf(field.id) < 0;
381
+ }));
382
+ }
383
+ return () => retVal;
384
+ }
385
+
386
+ referencedFields(refs: ReferencedFields): void {
387
+ super.referencedFields(refs);
388
+ const fieldIDs: string[] = [];
389
+ for (const gb of this.validGroupBy()) {
390
+ fieldIDs.push(gb.label());
391
+ }
392
+ for (const cf of this.validComputedFields()) {
393
+ if (cf.aggrColumn()) {
394
+ fieldIDs.push(cf.aggrColumn());
395
+ }
396
+ }
397
+ super.resolveInFields(refs, fieldIDs);
398
+ }
399
+
400
+ computeData(): ReadonlyArray<object> {
401
+ const data = super.computeData();
402
+ if (data.length === 0 || !this.exists()) return data;
403
+ const columnLabels: string[] = this.validGroupBy().map(gb => gb.label());
404
+ const computedFields = this.validComputedFields().map(cf => {
405
+ return { label: cf.fieldID(), aggrFunc: cf.aggrFunc() };
406
+ });
407
+ const retVal = d3Nest()
408
+ .key((row: { [key: string]: any }) => {
409
+ let key = "";
410
+ for (const groupByLabel of columnLabels) {
411
+ key += ":" + row[groupByLabel];
412
+ }
413
+ return key;
414
+ })
415
+ .entries(data as object[]).map(_row => {
416
+ const row: {
417
+ [key: string]: any
418
+ } = _row;
419
+ delete row.key;
420
+ for (const groupByLabel of columnLabels) {
421
+ row[groupByLabel] = row.values[0][groupByLabel];
422
+ }
423
+ for (const cf of computedFields) {
424
+ row[cf.label] = cf.aggrFunc(row.values);
425
+ }
426
+ return row;
427
+ })
428
+ ;
429
+ const outFields = this.outFields();
430
+ return retVal.map(row => {
431
+ const retVal = {};
432
+ for (const field of outFields) {
433
+ retVal[field.id] = row[field.id];
434
+ }
435
+ return retVal;
436
+ });
437
+ }
438
+ }
439
+ GroupBy.prototype._class += " GroupBy";