@hpcc-js/ddl-shim 3.3.2 → 3.3.4

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/src/upgrade.ts CHANGED
@@ -1,734 +1,734 @@
1
- import { PKG_NAME, PKG_VERSION } from "./__package__.ts";
2
- import * as DDL1 from "./ddl/v1.ts";
3
- import * as DDL2 from "./ddl/v2.ts";
4
- import { upgrade as dermatologyUpgrade } from "./dermatology.ts";
5
-
6
- interface IDatasourceOutput {
7
- datasource: DDL1.IAnyDatasource;
8
- output: DDL1.IOutput;
9
- }
10
-
11
- interface IDatasourceOutputFilter extends IDatasourceOutput {
12
- filter: DDL1.IFilter;
13
- }
14
-
15
- const UPGRADE_HEX_CHAR: boolean = false;
16
- function faCharFix(faChar: any): string | undefined {
17
- if (UPGRADE_HEX_CHAR && typeof faChar === "string") {
18
- return String.fromCharCode(parseInt(faChar));
19
- }
20
- return faChar;
21
- }
22
-
23
- class DDLUpgrade {
24
- _ddl: DDL1.IDDL;
25
- _baseUrl: string;
26
- _wuid?: string;
27
- _toLowerCase: boolean;
28
-
29
- _datasources: { [id: string]: DDL1.IAnyDatasource } = {};
30
- _datasourceUpdates: { [id: string]: { id: string, output?: string } } = {};
31
- _visualizations: { [id: string]: DDL1.IAnyVisualization } = {};
32
-
33
- _ddl2Datasources: { [id: string]: DDL2.DatasourceType } = {};
34
- _ddl2DatasourceFields: { [dsid: string]: { [outputID: string]: { [fieldID: string]: DDL2.IField } } } = {};
35
-
36
- _ddl2Dataviews: { [id: string]: DDL2.IView } = {};
37
- _ddl2DataviewActivities: {
38
- [viewID: string]: {
39
- project: DDL2.IProject,
40
- filters: DDL2.IFilter,
41
- sort: DDL2.ISort,
42
- groupBy: DDL2.IGroupBy,
43
- limit: DDL2.ILimit,
44
- mappings: DDL2.IMappings
45
- }
46
- } = {};
47
-
48
- constructor(ddl: DDL1.IDDL, baseUrl: string = "http://localhost:8010", wuid: string = "WUID", toLowerCase = true) {
49
- this._ddl = ddl;
50
- this._baseUrl = baseUrl;
51
- this._wuid = wuid;
52
- this._toLowerCase = toLowerCase;
53
-
54
- this.indexDDL();
55
- this.readDDL();
56
- }
57
-
58
- toLowerCase(s: string): string {
59
- return this._toLowerCase ? s.toLowerCase() : s;
60
- }
61
-
62
- isVizDatasourceRoxie(viz: DDL1.IAnyVisualization): boolean {
63
- if ((viz as any).source) {
64
- const ds = this._datasources[(viz as any).source.id];
65
- if (DDL1.isHipieDatasource(ds)) {
66
- return true;
67
- }
68
- }
69
- return false;
70
- }
71
-
72
- getDatasourceOutputs(dsID: string, vizID: string): IDatasourceOutput[] {
73
- const retVal: IDatasourceOutput[] = [];
74
- const datasource = this._datasources[dsID];
75
- for (const output of datasource.outputs) {
76
- if (output.notify) {
77
- for (const notify of output.notify) {
78
- if (notify === vizID) {
79
- retVal.push({
80
- datasource,
81
- output
82
- });
83
- }
84
- }
85
- }
86
- }
87
- return retVal;
88
- }
89
-
90
- getDatasourceFilters(dsID: string, vizID: string): { [id: string]: IDatasourceOutputFilter } {
91
- const retVal: { [id: string]: IDatasourceOutputFilter } = {};
92
- for (const dsOut of this.getDatasourceOutputs(dsID, vizID)) {
93
- if (dsOut.output.filter) {
94
- for (const filter of dsOut.output.filter) {
95
- retVal[filter.fieldid] = {
96
- datasource: dsOut.datasource,
97
- output: dsOut.output,
98
- filter
99
- };
100
- }
101
- }
102
- }
103
- return retVal;
104
- }
105
-
106
- indexDDL() {
107
- for (const dash of this._ddl.dashboards) {
108
- for (const viz of dash.visualizations) {
109
- this._visualizations[viz.id] = viz;
110
- }
111
- }
112
-
113
- for (const ds of this._ddl.datasources) {
114
- this._datasources[ds.id] = ds;
115
- for (const output of ds.outputs) {
116
- if (output.notify) {
117
- for (const notify of output.notify) {
118
- this._datasourceUpdates[notify] = {
119
- id: ds.id,
120
- output: output.from || output.id
121
- };
122
- }
123
- }
124
- }
125
- }
126
- }
127
-
128
- readDDL() {
129
- for (const ds of this._ddl.datasources) {
130
- if (DDL1.isWorkunitDatasource(ds)) {
131
- const ddl2DS: DDL2.IWUResult = {
132
- type: "wuresult",
133
- id: ds.id,
134
- url: this._baseUrl,
135
- wuid: this._wuid!,
136
- outputs: {}
137
- };
138
- for (const output of ds.outputs) {
139
- this.output2output(output, ddl2DS.outputs);
140
- }
141
- this._ddl2Datasources[ds.id] = ddl2DS;
142
- } else if (DDL1.isDatabombDatasource(ds)) {
143
- } else {
144
- const urlParts = ds.URL!.split("/WsEcl/submit/query/");
145
- const hostParts = urlParts[0];
146
- const roxieParts = urlParts[1].split("/");
147
- const ddl2DS: DDL2.IHipieService = {
148
- type: "hipie",
149
- id: ds.id,
150
- url: hostParts,
151
- querySet: roxieParts[0],
152
- queryID: roxieParts[1],
153
- inputs: [],
154
- outputs: {}
155
- };
156
- for (const output of ds.outputs) {
157
- this.output2output(output, ddl2DS.outputs);
158
- }
159
- this._ddl2Datasources[ds.id] = ddl2DS;
160
- }
161
- }
162
- for (const dash of this._ddl.dashboards) {
163
- for (const viz of dash.visualizations) {
164
- if (viz.type === "FORM") {
165
- this._ddl2Datasources[viz.id] = {
166
- type: "form",
167
- id: viz.id,
168
- fields: this.formFields2field(viz.fields)
169
- };
170
- this._datasourceUpdates[viz.id] = { id: viz.id };
171
- } else if (viz.type === "SLIDER") {
172
- this._ddl2Datasources[viz.id] = {
173
- type: "form",
174
- id: viz.id,
175
- fields: this.formFields2field(viz.fields, true)
176
- };
177
- this._datasourceUpdates[viz.id] = { id: viz.id };
178
- }
179
-
180
- this._ddl2Dataviews[viz.id] = this.anyViz2view(viz);
181
- }
182
- }
183
-
184
- this.readGroupBy();
185
- this.readFilters();
186
- this.readSort();
187
- this.readMappings();
188
- }
189
-
190
- readGroupBy() {
191
- for (const dash of this._ddl.dashboards) {
192
- for (const viz of dash.visualizations) {
193
- if (viz.fields) {
194
- const projectTransformations: DDL2.ProjectTransformationType[] = [];
195
- const groupByColumns: string[] = [];
196
- const aggrFields: DDL2.IAggregate[] = [];
197
- for (const field of viz.fields) {
198
- if (field.properties && field.properties.function) {
199
- switch (field.properties.function) {
200
- case "SUM":
201
- case "MIN":
202
- case "MAX":
203
- aggrFields.push({
204
- type: this.func2aggr(field.properties.function),
205
- inFieldID: this.toLowerCase(field.properties.params!.param1),
206
- fieldID: this.toLowerCase(field.id)
207
- } as DDL2.IAggregate);
208
- break;
209
- case "AVE":
210
- aggrFields.push({
211
- type: this.func2aggr(field.properties.function),
212
- inFieldID: this.toLowerCase(field.properties.params!.param1),
213
- baseCountFieldID: field.properties.params!.param2 ? this.toLowerCase(field.properties.params!.param2) : undefined,
214
- fieldID: this.toLowerCase(field.id)
215
- } as DDL2.IAggregate);
216
- break;
217
- case "SCALE":
218
- if (typeof field.properties.params!.param1 === "object") {
219
- const props: any = field.properties.params!.param1;
220
- switch (props.function) {
221
- case "SUM":
222
- case "MIN":
223
- case "MAX":
224
- aggrFields.push({
225
- type: this.func2aggr(props.function),
226
- inFieldID: this.toLowerCase(props.params.param1),
227
- fieldID: this.toLowerCase(field.id)
228
- });
229
- break;
230
- }
231
- }
232
- projectTransformations.push({
233
- type: "scale",
234
- sourceFieldID: this.toLowerCase(field.id),
235
- fieldID: this.toLowerCase(field.id),
236
- factor: +field.properties.params!.param2
237
- });
238
- break;
239
- default:
240
- groupByColumns.push(this.toLowerCase(field.id));
241
- throw new Error(`Unhandled field function: ${field.properties.function}`);
242
- }
243
- } else {
244
- groupByColumns.push(this.toLowerCase(field.id));
245
- }
246
- }
247
- if (projectTransformations.length) {
248
- this._ddl2DataviewActivities[viz.id].project.transformations = projectTransformations;
249
- }
250
- if (aggrFields.length) {
251
- this._ddl2DataviewActivities[viz.id].groupBy.groupByIDs = [...groupByColumns];
252
- this._ddl2DataviewActivities[viz.id].groupBy.aggregates = aggrFields;
253
- }
254
- }
255
- }
256
- }
257
- }
258
-
259
- func2aggr(func: DDL1.VisualizationFieldFuncitonType): DDL2.IAggregateType {
260
- switch (func) {
261
- case "SUM":
262
- return "sum";
263
- case "AVE":
264
- return "mean";
265
- case "MIN":
266
- return "min";
267
- case "MAX":
268
- return "max";
269
- }
270
- throw new Error(`Unknown DDL1 Function Type: ${func}`);
271
- }
272
-
273
- readMappings() {
274
- for (const dash of this._ddl.dashboards) {
275
- for (const viz of dash.visualizations) {
276
- if (DDL1.isFormVisualization(viz)) {
277
- } else if (DDL1.isPieVisualization(viz)) {
278
- this.readPieMappings(viz);
279
- } else if (DDL1.isChoroVisualization(viz)) {
280
- this.readChoroMappings(viz);
281
- } else if (DDL1.isLineVisualization(viz)) {
282
- this.readLineMappings(viz);
283
- } else if (DDL1.isTableVisualization(viz)) {
284
- this.readTableMappings(viz);
285
- } else if (DDL1.isGraphVisualization(viz)) {
286
- this.readGraphMappings(viz);
287
- } else if (DDL1.isSliderVisualization(viz)) {
288
- this.readSliderMappings(viz);
289
- } else {
290
- throw new Error(`Unkown DDL1 mapping type: ${viz.type}`);
291
- }
292
- }
293
- }
294
- }
295
-
296
- readPieMappings(viz: DDL1.IPieVisualization) {
297
- const mappings = this._ddl2DataviewActivities[viz.id].mappings;
298
- mappings.transformations.push({
299
- fieldID: "label",
300
- type: "=",
301
- sourceFieldID: this.toLowerCase(viz.source.mappings.label)
302
- });
303
- mappings.transformations.push({
304
- fieldID: "weight",
305
- type: "=",
306
- sourceFieldID: this.toLowerCase(viz.source.mappings.weight[0])
307
- });
308
- }
309
-
310
- readChoroMappings(viz: DDL1.IChoroVisualization) {
311
- const mappings = this._ddl2DataviewActivities[viz.id].mappings;
312
- mappings.transformations.push({
313
- fieldID: "label",
314
- type: "=",
315
- sourceFieldID: this.toLowerCase(this.anyChoroMapping2label(viz.source.mappings))
316
- });
317
- mappings.transformations.push({
318
- fieldID: "weight",
319
- type: "=",
320
- sourceFieldID: this.toLowerCase(viz.source.mappings.weight[0])
321
- });
322
- }
323
-
324
- anyChoroMapping2label(mapping: any) {
325
- return mapping.state || mapping.county || mapping.geohash;
326
- }
327
-
328
- readLineMappings(viz: DDL1.ILineVisualization) {
329
- const mappings = this._ddl2DataviewActivities[viz.id].mappings;
330
- mappings.transformations.push({
331
- fieldID: viz.source.mappings.x[0],
332
- type: "=",
333
- sourceFieldID: this.toLowerCase(viz.source.mappings.x[0])
334
- });
335
- for (let i = 0; i < viz.source.mappings.y.length; ++i) {
336
- mappings.transformations.push({
337
- fieldID: viz.source.mappings.y[i],
338
- type: "=",
339
- sourceFieldID: this.toLowerCase(viz.source.mappings.y[i])
340
- });
341
- }
342
- }
343
-
344
- readTableMappings(viz: DDL1.ITableVisualization) {
345
- const mappings = this._ddl2DataviewActivities[viz.id].mappings;
346
- for (let i = 0; i < viz.label.length; ++i) {
347
- mappings.transformations.push({
348
- fieldID: viz.label[i],
349
- type: "=",
350
- sourceFieldID: this.toLowerCase(viz.source.mappings.value[i])
351
- });
352
- }
353
- }
354
-
355
- readGraphEnums(valueMappings?: DDL1.IValueMappings, annotation: boolean = false): DDL2.IMapMapping[] {
356
- const retVal: DDL2.IMapMapping[] = [];
357
- if (valueMappings) {
358
- for (const value in valueMappings) {
359
- const newValue: { [key: string]: string | number | boolean | undefined } = {};
360
- for (const key in valueMappings[value]) {
361
- if (key === "faChar") {
362
- newValue[key] = faCharFix(valueMappings[value][key]);
363
- } else if (annotation && key.indexOf("icon_") === 0) {
364
- console.warn("Deprecated flag property: " + key);
365
- newValue[key.split("icon_")[1]] = valueMappings[value][key];
366
- } else {
367
- newValue[key] = valueMappings[value][key];
368
- }
369
- }
370
- // remove v1.x "0" annotations as they equated to "nothing" ---
371
- if (!annotation || value !== "0") {
372
- retVal.push({
373
- value,
374
- newValue
375
- });
376
- }
377
- }
378
- }
379
- return retVal;
380
- }
381
-
382
- readGraphMappings(viz: DDL1.IGraphVisualization) {
383
- const mappings = this._ddl2DataviewActivities[viz.id].mappings;
384
- mappings.transformations.push({
385
- fieldID: "uid",
386
- type: "=",
387
- sourceFieldID: this.toLowerCase(viz.source.mappings.uid)
388
- });
389
- mappings.transformations.push({
390
- fieldID: "label",
391
- type: "=",
392
- sourceFieldID: this.toLowerCase(viz.source.mappings.label)
393
- });
394
- if (viz.icon.fieldid) {
395
- mappings.transformations.push({
396
- fieldID: "icon",
397
- type: "map",
398
- sourceFieldID: this.toLowerCase(viz.icon.fieldid),
399
- default: { fachar: faCharFix(viz.icon.faChar) },
400
- mappings: this.readGraphEnums(viz.icon.valuemappings)
401
- });
402
- }
403
- let idx = 0;
404
- if (viz.flag) {
405
- for (const flag of viz.flag) {
406
- if (flag.fieldid) {
407
- mappings.transformations.push({
408
- fieldID: `annotation_${idx++}`,
409
- type: "map",
410
- sourceFieldID: this.toLowerCase(flag.fieldid),
411
- default: {},
412
- mappings: this.readGraphEnums(flag.valuemappings, true)
413
- });
414
- }
415
- }
416
- }
417
- mappings.transformations.push({
418
- fieldID: "links",
419
- type: "=",
420
- sourceFieldID: this.toLowerCase(viz.source.link.childfile),
421
- transformations: [{
422
- fieldID: "uid",
423
- type: "=",
424
- sourceFieldID: this.toLowerCase(viz.source.link.mappings.uid)
425
- }]
426
- });
427
- }
428
-
429
- readSliderMappings(viz: DDL1.ISliderVisualization) {
430
- const mappings = this._ddl2DataviewActivities[viz.id].mappings;
431
- mappings.transformations.push({
432
- fieldID: "label",
433
- type: "=",
434
- sourceFieldID: this.toLowerCase(viz.source.mappings.label)
435
- });
436
- }
437
-
438
- readFilters() {
439
- for (const dash of this._ddl.dashboards) {
440
- for (const viz of dash.visualizations) {
441
- if (viz.events) {
442
- for (const eventID in viz.events) {
443
- const event = viz.events[eventID];
444
- for (const update of event.updates) {
445
- const otherViz = this._ddl2Dataviews[update.visualization];
446
- const dsFilters = this.getDatasourceFilters(update.datasource, otherViz.id);
447
- if (update.mappings) {
448
- if (DDL2.isRoxieServiceRef(otherViz.datasource)) {
449
- for (const key in update.mappings) {
450
- otherViz.datasource.request.push({
451
- source: viz.id,
452
- remoteFieldID: this.toLowerCase(key),
453
- localFieldID: this.toLowerCase(update.mappings[key])
454
- } as DDL2.IRequestField);
455
- }
456
- } else {
457
- const condition: DDL2.IFilterCondition = {
458
- viewID: viz.id,
459
- mappings: []
460
- };
461
- for (const key in update.mappings) {
462
- const mapping = update.mappings[key];
463
- const dsFilter = (mapping && dsFilters[mapping]) ? dsFilters[mapping].filter : undefined;
464
- if (!dsFilter) {
465
- console.warn(`Select Mapping "${mapping}" in viz "${viz.id}" not found in filters for "${otherViz.id}"`);
466
- } else {
467
- condition.mappings.push({
468
- remoteFieldID: this.toLowerCase(key),
469
- localFieldID: this.toLowerCase(update.mappings[key]),
470
- condition: this.rule2condition(dsFilter.rule),
471
- nullable: dsFilter.nullable
472
- });
473
- }
474
- }
475
- this._ddl2DataviewActivities[otherViz.id].filters.conditions.push(condition);
476
- }
477
- }
478
- }
479
- }
480
- }
481
- }
482
- }
483
- }
484
-
485
- rule2condition(_: DDL1.IFilterRule): DDL2.IMappingConditionType {
486
- switch (_) {
487
- case "set":
488
- return "in";
489
- case "notequals":
490
- return "!=";
491
- }
492
- return _;
493
- }
494
-
495
- readSort() {
496
- for (const dash of this._ddl.dashboards) {
497
- for (const viz of dash.visualizations) {
498
- if ((viz as any).source) {
499
- if ((viz as any).source.sort) {
500
- const vizSort = this._ddl2DataviewActivities[viz.id].sort;
501
- vizSort.conditions = ((viz as any).source.sort as string[]).map(s => {
502
- if (s.indexOf("-") === 0) {
503
- return {
504
- fieldID: this.toLowerCase(s.substr(1)),
505
- descending: true
506
- } as DDL2.ISortCondition;
507
- }
508
- return {
509
- fieldID: this.toLowerCase(s),
510
- descending: false
511
- } as DDL2.ISortCondition;
512
- });
513
- }
514
- if ((viz as any).source.first) {
515
- const vizLimit = this._ddl2DataviewActivities[viz.id].limit;
516
- vizLimit.limit = +(viz as any).source.first;
517
- }
518
- }
519
- }
520
- }
521
- }
522
-
523
- anyViz2view(viz: DDL1.IAnyVisualization): DDL2.IView {
524
- const project: DDL2.IProject = {
525
- type: "project",
526
- transformations: []
527
- };
528
- const filters: DDL2.IFilter = {
529
- type: "filter",
530
- conditions: []
531
- };
532
- const groupBy: DDL2.IGroupBy = {
533
- type: "groupby",
534
- groupByIDs: [],
535
- aggregates: []
536
- };
537
- const sort: DDL2.ISort = {
538
- type: "sort",
539
- conditions: []
540
- };
541
- const limit: DDL2.ILimit = {
542
- type: "limit",
543
- limit: 0
544
- };
545
- const mappings: DDL2.IMappings = {
546
- type: "mappings",
547
- transformations: []
548
- };
549
- this._ddl2DataviewActivities[viz.id] = {
550
- project,
551
- filters,
552
- sort,
553
- groupBy,
554
- limit,
555
- mappings
556
- };
557
- const datasourceRef: DDL2.IDatasourceBaseRef | DDL2.IRoxieServiceRef = this.isVizDatasourceRoxie(viz) ? {
558
- id: this._datasourceUpdates[viz.id].id,
559
- request: [],
560
- output: this._datasourceUpdates[viz.id].output
561
- } : {
562
- id: this._datasourceUpdates[viz.id].id,
563
- output: this._datasourceUpdates[viz.id].output
564
- };
565
- return {
566
- id: viz.id,
567
- datasource: datasourceRef,
568
- activities: [
569
- project,
570
- filters,
571
- sort,
572
- groupBy,
573
- limit
574
- ],
575
- visualization: {
576
- id: viz.id,
577
- title: viz.title || "",
578
- description: "",
579
- visibility: viz.properties && viz.properties.flyout === true ? "flyout" : "normal",
580
- ...this.type2chartType(viz.type),
581
- mappings,
582
- properties: (viz.properties || {}) as DDL2.IWidgetProperties
583
- }
584
- };
585
- }
586
-
587
- type2chartType(chartType: DDL1.VisualizationType): { chartType: string, __class: string } {
588
- switch (chartType) {
589
- case "LINE":
590
- return { chartType: "Line", __class: "chart_Line" };
591
- case "BUBBLE":
592
- return { chartType: "Bubble", __class: "chart_Bubble" };
593
- case "PIE":
594
- return { chartType: "Pie", __class: "chart_Pie" };
595
- case "BAR":
596
- return { chartType: "Column", __class: "chart_Column" };
597
- case "FORM":
598
- return { chartType: "FieldForm", __class: "form_FieldForm" };
599
- case "WORD_CLOUD":
600
- return { chartType: "WordCloud", __class: "chart_WordCloud" };
601
- case "CHORO":
602
- return { chartType: "ChoroplethStates", __class: "map_ChoroplethStates" };
603
- case "SUMMARY":
604
- return { chartType: "Summary", __class: "chart_Summary" };
605
- case "SLIDER":
606
- return { chartType: "FieldForm", __class: "form_FieldForm" };
607
- case "HEAT_MAP":
608
- return { chartType: "HeatMap", __class: "other_HeatMap" };
609
- case "2DCHART":
610
- return { chartType: "Column", __class: "chart_Column" };
611
- case "GRAPH":
612
- return { chartType: "AdjacencyGraph", __class: "graph_AdjacencyGraph" };
613
- case "TABLE":
614
- default:
615
- return { chartType: "Table", __class: "dgrid_Table" };
616
- }
617
- }
618
-
619
- formFields2field(fields?: DDL1.IVisualizationField[], slider: boolean = false): DDL2.IField[] {
620
- if (!fields) return [];
621
- return fields.map(field => {
622
- switch (field.properties.type) {
623
- case "range":
624
- return {
625
- type: "range",
626
- id: field.id,
627
- default: (field.properties.default ? field.properties.default as DDL2.Range : undefined)
628
- };
629
- case "dataset":
630
- return {
631
- type: "dataset",
632
- id: field.id,
633
- default: [],
634
- children: []
635
- };
636
- default:
637
- return {
638
- type: this.formFieldType2fieldType(field.properties.datatype, slider),
639
- id: field.id,
640
- default: field.properties.default ? field.properties.default[0] : undefined
641
- };
642
- }
643
- });
644
- }
645
-
646
- formFieldType2fieldType(fieldType: DDL1.VisualizationFieldType, slider: boolean): "string" | "number" | "boolean" {
647
- switch (fieldType) {
648
- case "bool":
649
- case "boolean":
650
- return "boolean";
651
- case "integer":
652
- case "unsigned":
653
- case "float":
654
- case "double":
655
- case "real":
656
- return "number";
657
- case "string":
658
- return "string";
659
- default:
660
- return slider ? "number" : "string";
661
- }
662
- }
663
-
664
- output2output(output: DDL1.IOutput, target: DDL2.OutputDict) {
665
- target[output.from || output.id] = {
666
- fields: this.filters2fields(output.filter)
667
- };
668
- }
669
-
670
- filters2fields(filters?: DDL1.IFilter[]): DDL2.IField[] {
671
- if (!filters) return [];
672
- return filters.filter(filter => {
673
- const idParts = filter.fieldid.split("-");
674
- return idParts.length === 1 || idParts[1] === "range";
675
- }).map(filter => {
676
- const idParts = filter.fieldid.split("-");
677
- const retVal: DDL2.IFieldString = {
678
- type: "string",
679
- id: idParts[0]
680
- };
681
- return retVal;
682
- });
683
- }
684
-
685
- getVizField(vizID: string, fieldID: string): DDL2.IField {
686
- return {
687
- type: "string",
688
- id: "",
689
- default: ""
690
- };
691
- }
692
-
693
- writeDatasources(): DDL2.DatasourceType[] {
694
- const retVal: DDL2.DatasourceType[] = [];
695
- for (const id in this._ddl2Datasources) {
696
- retVal.push(this._ddl2Datasources[id]);
697
- }
698
- return retVal;
699
- }
700
-
701
- writeDataviews(): DDL2.IView[] {
702
- const retVal: DDL2.IView[] = [];
703
- for (const id in this._ddl2Dataviews) {
704
- retVal.push(this._ddl2Dataviews[id]);
705
- }
706
- return retVal;
707
- }
708
-
709
- writeProperties(): DDL2.IProperties | undefined {
710
- return {
711
- // TODO
712
- };
713
- }
714
-
715
- write(): DDL2.Schema {
716
- return {
717
- version: "2.2.1",
718
- createdBy: {
719
- name: PKG_NAME,
720
- version: PKG_VERSION
721
- },
722
- datasources: this.writeDatasources(),
723
- dataviews: this.writeDataviews(),
724
- properties: this.writeProperties()
725
- };
726
- }
727
- }
728
-
729
- export function upgrade(ddl: DDL1.IDDL, baseUrl?: string, wuid?: string, toLowerCase: boolean = true, dermatologyJson = {}): DDL2.Schema {
730
- const ddlUp = new DDLUpgrade(ddl, baseUrl, wuid, toLowerCase);
731
- const retVal = ddlUp.write();
732
- retVal.properties = dermatologyUpgrade(retVal, dermatologyJson);
733
- return retVal;
734
- }
1
+ import { PKG_NAME, PKG_VERSION } from "./__package__.ts";
2
+ import * as DDL1 from "./ddl/v1.ts";
3
+ import * as DDL2 from "./ddl/v2.ts";
4
+ import { upgrade as dermatologyUpgrade } from "./dermatology.ts";
5
+
6
+ interface IDatasourceOutput {
7
+ datasource: DDL1.IAnyDatasource;
8
+ output: DDL1.IOutput;
9
+ }
10
+
11
+ interface IDatasourceOutputFilter extends IDatasourceOutput {
12
+ filter: DDL1.IFilter;
13
+ }
14
+
15
+ const UPGRADE_HEX_CHAR: boolean = false;
16
+ function faCharFix(faChar: any): string | undefined {
17
+ if (UPGRADE_HEX_CHAR && typeof faChar === "string") {
18
+ return String.fromCharCode(parseInt(faChar));
19
+ }
20
+ return faChar;
21
+ }
22
+
23
+ class DDLUpgrade {
24
+ _ddl: DDL1.IDDL;
25
+ _baseUrl: string;
26
+ _wuid?: string;
27
+ _toLowerCase: boolean;
28
+
29
+ _datasources: { [id: string]: DDL1.IAnyDatasource } = {};
30
+ _datasourceUpdates: { [id: string]: { id: string, output?: string } } = {};
31
+ _visualizations: { [id: string]: DDL1.IAnyVisualization } = {};
32
+
33
+ _ddl2Datasources: { [id: string]: DDL2.DatasourceType } = {};
34
+ _ddl2DatasourceFields: { [dsid: string]: { [outputID: string]: { [fieldID: string]: DDL2.IField } } } = {};
35
+
36
+ _ddl2Dataviews: { [id: string]: DDL2.IView } = {};
37
+ _ddl2DataviewActivities: {
38
+ [viewID: string]: {
39
+ project: DDL2.IProject,
40
+ filters: DDL2.IFilter,
41
+ sort: DDL2.ISort,
42
+ groupBy: DDL2.IGroupBy,
43
+ limit: DDL2.ILimit,
44
+ mappings: DDL2.IMappings
45
+ }
46
+ } = {};
47
+
48
+ constructor(ddl: DDL1.IDDL, baseUrl: string = "http://localhost:8010", wuid: string = "WUID", toLowerCase = true) {
49
+ this._ddl = ddl;
50
+ this._baseUrl = baseUrl;
51
+ this._wuid = wuid;
52
+ this._toLowerCase = toLowerCase;
53
+
54
+ this.indexDDL();
55
+ this.readDDL();
56
+ }
57
+
58
+ toLowerCase(s: string): string {
59
+ return this._toLowerCase ? s.toLowerCase() : s;
60
+ }
61
+
62
+ isVizDatasourceRoxie(viz: DDL1.IAnyVisualization): boolean {
63
+ if ((viz as any).source) {
64
+ const ds = this._datasources[(viz as any).source.id];
65
+ if (DDL1.isHipieDatasource(ds)) {
66
+ return true;
67
+ }
68
+ }
69
+ return false;
70
+ }
71
+
72
+ getDatasourceOutputs(dsID: string, vizID: string): IDatasourceOutput[] {
73
+ const retVal: IDatasourceOutput[] = [];
74
+ const datasource = this._datasources[dsID];
75
+ for (const output of datasource.outputs) {
76
+ if (output.notify) {
77
+ for (const notify of output.notify) {
78
+ if (notify === vizID) {
79
+ retVal.push({
80
+ datasource,
81
+ output
82
+ });
83
+ }
84
+ }
85
+ }
86
+ }
87
+ return retVal;
88
+ }
89
+
90
+ getDatasourceFilters(dsID: string, vizID: string): { [id: string]: IDatasourceOutputFilter } {
91
+ const retVal: { [id: string]: IDatasourceOutputFilter } = {};
92
+ for (const dsOut of this.getDatasourceOutputs(dsID, vizID)) {
93
+ if (dsOut.output.filter) {
94
+ for (const filter of dsOut.output.filter) {
95
+ retVal[filter.fieldid] = {
96
+ datasource: dsOut.datasource,
97
+ output: dsOut.output,
98
+ filter
99
+ };
100
+ }
101
+ }
102
+ }
103
+ return retVal;
104
+ }
105
+
106
+ indexDDL() {
107
+ for (const dash of this._ddl.dashboards) {
108
+ for (const viz of dash.visualizations) {
109
+ this._visualizations[viz.id] = viz;
110
+ }
111
+ }
112
+
113
+ for (const ds of this._ddl.datasources) {
114
+ this._datasources[ds.id] = ds;
115
+ for (const output of ds.outputs) {
116
+ if (output.notify) {
117
+ for (const notify of output.notify) {
118
+ this._datasourceUpdates[notify] = {
119
+ id: ds.id,
120
+ output: output.from || output.id
121
+ };
122
+ }
123
+ }
124
+ }
125
+ }
126
+ }
127
+
128
+ readDDL() {
129
+ for (const ds of this._ddl.datasources) {
130
+ if (DDL1.isWorkunitDatasource(ds)) {
131
+ const ddl2DS: DDL2.IWUResult = {
132
+ type: "wuresult",
133
+ id: ds.id,
134
+ url: this._baseUrl,
135
+ wuid: this._wuid!,
136
+ outputs: {}
137
+ };
138
+ for (const output of ds.outputs) {
139
+ this.output2output(output, ddl2DS.outputs);
140
+ }
141
+ this._ddl2Datasources[ds.id] = ddl2DS;
142
+ } else if (DDL1.isDatabombDatasource(ds)) {
143
+ } else {
144
+ const urlParts = ds.URL!.split("/WsEcl/submit/query/");
145
+ const hostParts = urlParts[0];
146
+ const roxieParts = urlParts[1].split("/");
147
+ const ddl2DS: DDL2.IHipieService = {
148
+ type: "hipie",
149
+ id: ds.id,
150
+ url: hostParts,
151
+ querySet: roxieParts[0],
152
+ queryID: roxieParts[1],
153
+ inputs: [],
154
+ outputs: {}
155
+ };
156
+ for (const output of ds.outputs) {
157
+ this.output2output(output, ddl2DS.outputs);
158
+ }
159
+ this._ddl2Datasources[ds.id] = ddl2DS;
160
+ }
161
+ }
162
+ for (const dash of this._ddl.dashboards) {
163
+ for (const viz of dash.visualizations) {
164
+ if (viz.type === "FORM") {
165
+ this._ddl2Datasources[viz.id] = {
166
+ type: "form",
167
+ id: viz.id,
168
+ fields: this.formFields2field(viz.fields)
169
+ };
170
+ this._datasourceUpdates[viz.id] = { id: viz.id };
171
+ } else if (viz.type === "SLIDER") {
172
+ this._ddl2Datasources[viz.id] = {
173
+ type: "form",
174
+ id: viz.id,
175
+ fields: this.formFields2field(viz.fields, true)
176
+ };
177
+ this._datasourceUpdates[viz.id] = { id: viz.id };
178
+ }
179
+
180
+ this._ddl2Dataviews[viz.id] = this.anyViz2view(viz);
181
+ }
182
+ }
183
+
184
+ this.readGroupBy();
185
+ this.readFilters();
186
+ this.readSort();
187
+ this.readMappings();
188
+ }
189
+
190
+ readGroupBy() {
191
+ for (const dash of this._ddl.dashboards) {
192
+ for (const viz of dash.visualizations) {
193
+ if (viz.fields) {
194
+ const projectTransformations: DDL2.ProjectTransformationType[] = [];
195
+ const groupByColumns: string[] = [];
196
+ const aggrFields: DDL2.IAggregate[] = [];
197
+ for (const field of viz.fields) {
198
+ if (field.properties && field.properties.function) {
199
+ switch (field.properties.function) {
200
+ case "SUM":
201
+ case "MIN":
202
+ case "MAX":
203
+ aggrFields.push({
204
+ type: this.func2aggr(field.properties.function),
205
+ inFieldID: this.toLowerCase(field.properties.params!.param1),
206
+ fieldID: this.toLowerCase(field.id)
207
+ } as DDL2.IAggregate);
208
+ break;
209
+ case "AVE":
210
+ aggrFields.push({
211
+ type: this.func2aggr(field.properties.function),
212
+ inFieldID: this.toLowerCase(field.properties.params!.param1),
213
+ baseCountFieldID: field.properties.params!.param2 ? this.toLowerCase(field.properties.params!.param2) : undefined,
214
+ fieldID: this.toLowerCase(field.id)
215
+ } as DDL2.IAggregate);
216
+ break;
217
+ case "SCALE":
218
+ if (typeof field.properties.params!.param1 === "object") {
219
+ const props: any = field.properties.params!.param1;
220
+ switch (props.function) {
221
+ case "SUM":
222
+ case "MIN":
223
+ case "MAX":
224
+ aggrFields.push({
225
+ type: this.func2aggr(props.function),
226
+ inFieldID: this.toLowerCase(props.params.param1),
227
+ fieldID: this.toLowerCase(field.id)
228
+ });
229
+ break;
230
+ }
231
+ }
232
+ projectTransformations.push({
233
+ type: "scale",
234
+ sourceFieldID: this.toLowerCase(field.id),
235
+ fieldID: this.toLowerCase(field.id),
236
+ factor: +field.properties.params!.param2
237
+ });
238
+ break;
239
+ default:
240
+ groupByColumns.push(this.toLowerCase(field.id));
241
+ throw new Error(`Unhandled field function: ${field.properties.function}`);
242
+ }
243
+ } else {
244
+ groupByColumns.push(this.toLowerCase(field.id));
245
+ }
246
+ }
247
+ if (projectTransformations.length) {
248
+ this._ddl2DataviewActivities[viz.id].project.transformations = projectTransformations;
249
+ }
250
+ if (aggrFields.length) {
251
+ this._ddl2DataviewActivities[viz.id].groupBy.groupByIDs = [...groupByColumns];
252
+ this._ddl2DataviewActivities[viz.id].groupBy.aggregates = aggrFields;
253
+ }
254
+ }
255
+ }
256
+ }
257
+ }
258
+
259
+ func2aggr(func: DDL1.VisualizationFieldFuncitonType): DDL2.IAggregateType {
260
+ switch (func) {
261
+ case "SUM":
262
+ return "sum";
263
+ case "AVE":
264
+ return "mean";
265
+ case "MIN":
266
+ return "min";
267
+ case "MAX":
268
+ return "max";
269
+ }
270
+ throw new Error(`Unknown DDL1 Function Type: ${func}`);
271
+ }
272
+
273
+ readMappings() {
274
+ for (const dash of this._ddl.dashboards) {
275
+ for (const viz of dash.visualizations) {
276
+ if (DDL1.isFormVisualization(viz)) {
277
+ } else if (DDL1.isPieVisualization(viz)) {
278
+ this.readPieMappings(viz);
279
+ } else if (DDL1.isChoroVisualization(viz)) {
280
+ this.readChoroMappings(viz);
281
+ } else if (DDL1.isLineVisualization(viz)) {
282
+ this.readLineMappings(viz);
283
+ } else if (DDL1.isTableVisualization(viz)) {
284
+ this.readTableMappings(viz);
285
+ } else if (DDL1.isGraphVisualization(viz)) {
286
+ this.readGraphMappings(viz);
287
+ } else if (DDL1.isSliderVisualization(viz)) {
288
+ this.readSliderMappings(viz);
289
+ } else {
290
+ throw new Error(`Unkown DDL1 mapping type: ${viz.type}`);
291
+ }
292
+ }
293
+ }
294
+ }
295
+
296
+ readPieMappings(viz: DDL1.IPieVisualization) {
297
+ const mappings = this._ddl2DataviewActivities[viz.id].mappings;
298
+ mappings.transformations.push({
299
+ fieldID: "label",
300
+ type: "=",
301
+ sourceFieldID: this.toLowerCase(viz.source.mappings.label)
302
+ });
303
+ mappings.transformations.push({
304
+ fieldID: "weight",
305
+ type: "=",
306
+ sourceFieldID: this.toLowerCase(viz.source.mappings.weight[0])
307
+ });
308
+ }
309
+
310
+ readChoroMappings(viz: DDL1.IChoroVisualization) {
311
+ const mappings = this._ddl2DataviewActivities[viz.id].mappings;
312
+ mappings.transformations.push({
313
+ fieldID: "label",
314
+ type: "=",
315
+ sourceFieldID: this.toLowerCase(this.anyChoroMapping2label(viz.source.mappings))
316
+ });
317
+ mappings.transformations.push({
318
+ fieldID: "weight",
319
+ type: "=",
320
+ sourceFieldID: this.toLowerCase(viz.source.mappings.weight[0])
321
+ });
322
+ }
323
+
324
+ anyChoroMapping2label(mapping: any) {
325
+ return mapping.state || mapping.county || mapping.geohash;
326
+ }
327
+
328
+ readLineMappings(viz: DDL1.ILineVisualization) {
329
+ const mappings = this._ddl2DataviewActivities[viz.id].mappings;
330
+ mappings.transformations.push({
331
+ fieldID: viz.source.mappings.x[0],
332
+ type: "=",
333
+ sourceFieldID: this.toLowerCase(viz.source.mappings.x[0])
334
+ });
335
+ for (let i = 0; i < viz.source.mappings.y.length; ++i) {
336
+ mappings.transformations.push({
337
+ fieldID: viz.source.mappings.y[i],
338
+ type: "=",
339
+ sourceFieldID: this.toLowerCase(viz.source.mappings.y[i])
340
+ });
341
+ }
342
+ }
343
+
344
+ readTableMappings(viz: DDL1.ITableVisualization) {
345
+ const mappings = this._ddl2DataviewActivities[viz.id].mappings;
346
+ for (let i = 0; i < viz.label.length; ++i) {
347
+ mappings.transformations.push({
348
+ fieldID: viz.label[i],
349
+ type: "=",
350
+ sourceFieldID: this.toLowerCase(viz.source.mappings.value[i])
351
+ });
352
+ }
353
+ }
354
+
355
+ readGraphEnums(valueMappings?: DDL1.IValueMappings, annotation: boolean = false): DDL2.IMapMapping[] {
356
+ const retVal: DDL2.IMapMapping[] = [];
357
+ if (valueMappings) {
358
+ for (const value in valueMappings) {
359
+ const newValue: { [key: string]: string | number | boolean | undefined } = {};
360
+ for (const key in valueMappings[value]) {
361
+ if (key === "faChar") {
362
+ newValue[key] = faCharFix(valueMappings[value][key]);
363
+ } else if (annotation && key.indexOf("icon_") === 0) {
364
+ console.warn("Deprecated flag property: " + key);
365
+ newValue[key.split("icon_")[1]] = valueMappings[value][key];
366
+ } else {
367
+ newValue[key] = valueMappings[value][key];
368
+ }
369
+ }
370
+ // remove v1.x "0" annotations as they equated to "nothing" ---
371
+ if (!annotation || value !== "0") {
372
+ retVal.push({
373
+ value,
374
+ newValue
375
+ });
376
+ }
377
+ }
378
+ }
379
+ return retVal;
380
+ }
381
+
382
+ readGraphMappings(viz: DDL1.IGraphVisualization) {
383
+ const mappings = this._ddl2DataviewActivities[viz.id].mappings;
384
+ mappings.transformations.push({
385
+ fieldID: "uid",
386
+ type: "=",
387
+ sourceFieldID: this.toLowerCase(viz.source.mappings.uid)
388
+ });
389
+ mappings.transformations.push({
390
+ fieldID: "label",
391
+ type: "=",
392
+ sourceFieldID: this.toLowerCase(viz.source.mappings.label)
393
+ });
394
+ if (viz.icon.fieldid) {
395
+ mappings.transformations.push({
396
+ fieldID: "icon",
397
+ type: "map",
398
+ sourceFieldID: this.toLowerCase(viz.icon.fieldid),
399
+ default: { fachar: faCharFix(viz.icon.faChar) },
400
+ mappings: this.readGraphEnums(viz.icon.valuemappings)
401
+ });
402
+ }
403
+ let idx = 0;
404
+ if (viz.flag) {
405
+ for (const flag of viz.flag) {
406
+ if (flag.fieldid) {
407
+ mappings.transformations.push({
408
+ fieldID: `annotation_${idx++}`,
409
+ type: "map",
410
+ sourceFieldID: this.toLowerCase(flag.fieldid),
411
+ default: {},
412
+ mappings: this.readGraphEnums(flag.valuemappings, true)
413
+ });
414
+ }
415
+ }
416
+ }
417
+ mappings.transformations.push({
418
+ fieldID: "links",
419
+ type: "=",
420
+ sourceFieldID: this.toLowerCase(viz.source.link.childfile),
421
+ transformations: [{
422
+ fieldID: "uid",
423
+ type: "=",
424
+ sourceFieldID: this.toLowerCase(viz.source.link.mappings.uid)
425
+ }]
426
+ });
427
+ }
428
+
429
+ readSliderMappings(viz: DDL1.ISliderVisualization) {
430
+ const mappings = this._ddl2DataviewActivities[viz.id].mappings;
431
+ mappings.transformations.push({
432
+ fieldID: "label",
433
+ type: "=",
434
+ sourceFieldID: this.toLowerCase(viz.source.mappings.label)
435
+ });
436
+ }
437
+
438
+ readFilters() {
439
+ for (const dash of this._ddl.dashboards) {
440
+ for (const viz of dash.visualizations) {
441
+ if (viz.events) {
442
+ for (const eventID in viz.events) {
443
+ const event = viz.events[eventID];
444
+ for (const update of event.updates) {
445
+ const otherViz = this._ddl2Dataviews[update.visualization];
446
+ const dsFilters = this.getDatasourceFilters(update.datasource, otherViz.id);
447
+ if (update.mappings) {
448
+ if (DDL2.isRoxieServiceRef(otherViz.datasource)) {
449
+ for (const key in update.mappings) {
450
+ otherViz.datasource.request.push({
451
+ source: viz.id,
452
+ remoteFieldID: this.toLowerCase(key),
453
+ localFieldID: this.toLowerCase(update.mappings[key])
454
+ } as DDL2.IRequestField);
455
+ }
456
+ } else {
457
+ const condition: DDL2.IFilterCondition = {
458
+ viewID: viz.id,
459
+ mappings: []
460
+ };
461
+ for (const key in update.mappings) {
462
+ const mapping = update.mappings[key];
463
+ const dsFilter = (mapping && dsFilters[mapping]) ? dsFilters[mapping].filter : undefined;
464
+ if (!dsFilter) {
465
+ console.warn(`Select Mapping "${mapping}" in viz "${viz.id}" not found in filters for "${otherViz.id}"`);
466
+ } else {
467
+ condition.mappings.push({
468
+ remoteFieldID: this.toLowerCase(key),
469
+ localFieldID: this.toLowerCase(update.mappings[key]),
470
+ condition: this.rule2condition(dsFilter.rule),
471
+ nullable: dsFilter.nullable
472
+ });
473
+ }
474
+ }
475
+ this._ddl2DataviewActivities[otherViz.id].filters.conditions.push(condition);
476
+ }
477
+ }
478
+ }
479
+ }
480
+ }
481
+ }
482
+ }
483
+ }
484
+
485
+ rule2condition(_: DDL1.IFilterRule): DDL2.IMappingConditionType {
486
+ switch (_) {
487
+ case "set":
488
+ return "in";
489
+ case "notequals":
490
+ return "!=";
491
+ }
492
+ return _;
493
+ }
494
+
495
+ readSort() {
496
+ for (const dash of this._ddl.dashboards) {
497
+ for (const viz of dash.visualizations) {
498
+ if ((viz as any).source) {
499
+ if ((viz as any).source.sort) {
500
+ const vizSort = this._ddl2DataviewActivities[viz.id].sort;
501
+ vizSort.conditions = ((viz as any).source.sort as string[]).map(s => {
502
+ if (s.indexOf("-") === 0) {
503
+ return {
504
+ fieldID: this.toLowerCase(s.substr(1)),
505
+ descending: true
506
+ } as DDL2.ISortCondition;
507
+ }
508
+ return {
509
+ fieldID: this.toLowerCase(s),
510
+ descending: false
511
+ } as DDL2.ISortCondition;
512
+ });
513
+ }
514
+ if ((viz as any).source.first) {
515
+ const vizLimit = this._ddl2DataviewActivities[viz.id].limit;
516
+ vizLimit.limit = +(viz as any).source.first;
517
+ }
518
+ }
519
+ }
520
+ }
521
+ }
522
+
523
+ anyViz2view(viz: DDL1.IAnyVisualization): DDL2.IView {
524
+ const project: DDL2.IProject = {
525
+ type: "project",
526
+ transformations: []
527
+ };
528
+ const filters: DDL2.IFilter = {
529
+ type: "filter",
530
+ conditions: []
531
+ };
532
+ const groupBy: DDL2.IGroupBy = {
533
+ type: "groupby",
534
+ groupByIDs: [],
535
+ aggregates: []
536
+ };
537
+ const sort: DDL2.ISort = {
538
+ type: "sort",
539
+ conditions: []
540
+ };
541
+ const limit: DDL2.ILimit = {
542
+ type: "limit",
543
+ limit: 0
544
+ };
545
+ const mappings: DDL2.IMappings = {
546
+ type: "mappings",
547
+ transformations: []
548
+ };
549
+ this._ddl2DataviewActivities[viz.id] = {
550
+ project,
551
+ filters,
552
+ sort,
553
+ groupBy,
554
+ limit,
555
+ mappings
556
+ };
557
+ const datasourceRef: DDL2.IDatasourceBaseRef | DDL2.IRoxieServiceRef = this.isVizDatasourceRoxie(viz) ? {
558
+ id: this._datasourceUpdates[viz.id].id,
559
+ request: [],
560
+ output: this._datasourceUpdates[viz.id].output
561
+ } : {
562
+ id: this._datasourceUpdates[viz.id].id,
563
+ output: this._datasourceUpdates[viz.id].output
564
+ };
565
+ return {
566
+ id: viz.id,
567
+ datasource: datasourceRef,
568
+ activities: [
569
+ project,
570
+ filters,
571
+ sort,
572
+ groupBy,
573
+ limit
574
+ ],
575
+ visualization: {
576
+ id: viz.id,
577
+ title: viz.title || "",
578
+ description: "",
579
+ visibility: viz.properties && viz.properties.flyout === true ? "flyout" : "normal",
580
+ ...this.type2chartType(viz.type),
581
+ mappings,
582
+ properties: (viz.properties || {}) as DDL2.IWidgetProperties
583
+ }
584
+ };
585
+ }
586
+
587
+ type2chartType(chartType: DDL1.VisualizationType): { chartType: string, __class: string } {
588
+ switch (chartType) {
589
+ case "LINE":
590
+ return { chartType: "Line", __class: "chart_Line" };
591
+ case "BUBBLE":
592
+ return { chartType: "Bubble", __class: "chart_Bubble" };
593
+ case "PIE":
594
+ return { chartType: "Pie", __class: "chart_Pie" };
595
+ case "BAR":
596
+ return { chartType: "Column", __class: "chart_Column" };
597
+ case "FORM":
598
+ return { chartType: "FieldForm", __class: "form_FieldForm" };
599
+ case "WORD_CLOUD":
600
+ return { chartType: "WordCloud", __class: "chart_WordCloud" };
601
+ case "CHORO":
602
+ return { chartType: "ChoroplethStates", __class: "map_ChoroplethStates" };
603
+ case "SUMMARY":
604
+ return { chartType: "Summary", __class: "chart_Summary" };
605
+ case "SLIDER":
606
+ return { chartType: "FieldForm", __class: "form_FieldForm" };
607
+ case "HEAT_MAP":
608
+ return { chartType: "HeatMap", __class: "other_HeatMap" };
609
+ case "2DCHART":
610
+ return { chartType: "Column", __class: "chart_Column" };
611
+ case "GRAPH":
612
+ return { chartType: "AdjacencyGraph", __class: "graph_AdjacencyGraph" };
613
+ case "TABLE":
614
+ default:
615
+ return { chartType: "Table", __class: "dgrid_Table" };
616
+ }
617
+ }
618
+
619
+ formFields2field(fields?: DDL1.IVisualizationField[], slider: boolean = false): DDL2.IField[] {
620
+ if (!fields) return [];
621
+ return fields.map(field => {
622
+ switch (field.properties.type) {
623
+ case "range":
624
+ return {
625
+ type: "range",
626
+ id: field.id,
627
+ default: (field.properties.default ? field.properties.default as DDL2.Range : undefined)
628
+ };
629
+ case "dataset":
630
+ return {
631
+ type: "dataset",
632
+ id: field.id,
633
+ default: [],
634
+ children: []
635
+ };
636
+ default:
637
+ return {
638
+ type: this.formFieldType2fieldType(field.properties.datatype, slider),
639
+ id: field.id,
640
+ default: field.properties.default ? field.properties.default[0] : undefined
641
+ };
642
+ }
643
+ });
644
+ }
645
+
646
+ formFieldType2fieldType(fieldType: DDL1.VisualizationFieldType, slider: boolean): "string" | "number" | "boolean" {
647
+ switch (fieldType) {
648
+ case "bool":
649
+ case "boolean":
650
+ return "boolean";
651
+ case "integer":
652
+ case "unsigned":
653
+ case "float":
654
+ case "double":
655
+ case "real":
656
+ return "number";
657
+ case "string":
658
+ return "string";
659
+ default:
660
+ return slider ? "number" : "string";
661
+ }
662
+ }
663
+
664
+ output2output(output: DDL1.IOutput, target: DDL2.OutputDict) {
665
+ target[output.from || output.id] = {
666
+ fields: this.filters2fields(output.filter)
667
+ };
668
+ }
669
+
670
+ filters2fields(filters?: DDL1.IFilter[]): DDL2.IField[] {
671
+ if (!filters) return [];
672
+ return filters.filter(filter => {
673
+ const idParts = filter.fieldid.split("-");
674
+ return idParts.length === 1 || idParts[1] === "range";
675
+ }).map(filter => {
676
+ const idParts = filter.fieldid.split("-");
677
+ const retVal: DDL2.IFieldString = {
678
+ type: "string",
679
+ id: idParts[0]
680
+ };
681
+ return retVal;
682
+ });
683
+ }
684
+
685
+ getVizField(vizID: string, fieldID: string): DDL2.IField {
686
+ return {
687
+ type: "string",
688
+ id: "",
689
+ default: ""
690
+ };
691
+ }
692
+
693
+ writeDatasources(): DDL2.DatasourceType[] {
694
+ const retVal: DDL2.DatasourceType[] = [];
695
+ for (const id in this._ddl2Datasources) {
696
+ retVal.push(this._ddl2Datasources[id]);
697
+ }
698
+ return retVal;
699
+ }
700
+
701
+ writeDataviews(): DDL2.IView[] {
702
+ const retVal: DDL2.IView[] = [];
703
+ for (const id in this._ddl2Dataviews) {
704
+ retVal.push(this._ddl2Dataviews[id]);
705
+ }
706
+ return retVal;
707
+ }
708
+
709
+ writeProperties(): DDL2.IProperties | undefined {
710
+ return {
711
+ // TODO
712
+ };
713
+ }
714
+
715
+ write(): DDL2.Schema {
716
+ return {
717
+ version: "2.2.1",
718
+ createdBy: {
719
+ name: PKG_NAME,
720
+ version: PKG_VERSION
721
+ },
722
+ datasources: this.writeDatasources(),
723
+ dataviews: this.writeDataviews(),
724
+ properties: this.writeProperties()
725
+ };
726
+ }
727
+ }
728
+
729
+ export function upgrade(ddl: DDL1.IDDL, baseUrl?: string, wuid?: string, toLowerCase: boolean = true, dermatologyJson = {}): DDL2.Schema {
730
+ const ddlUp = new DDLUpgrade(ddl, baseUrl, wuid, toLowerCase);
731
+ const retVal = ddlUp.write();
732
+ retVal.properties = dermatologyUpgrade(retVal, dermatologyJson);
733
+ return retVal;
734
+ }