@platforma-sdk/workflow-tengo 3.0.2 → 3.1.0

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 (46) hide show
  1. package/dist/index.d.ts +1 -1
  2. package/dist/index.js +2 -1
  3. package/dist/index.mjs +2 -1
  4. package/dist/tengo/lib/canonical.lib.tengo +73 -0
  5. package/dist/tengo/lib/exec.monetization.lib.tengo +3 -1
  6. package/dist/tengo/lib/exec.monetization_internal.lib.tengo +2 -1
  7. package/dist/tengo/lib/maps.lib.tengo +1 -1
  8. package/dist/tengo/lib/pframes.builder.lib.tengo +5 -1
  9. package/dist/tengo/lib/pframes.bundle.lib.tengo +544 -0
  10. package/dist/tengo/lib/pframes.constants.lib.tengo +5 -1
  11. package/dist/tengo/lib/pframes.lib.tengo +3 -0
  12. package/dist/tengo/lib/pframes.spec.lib.tengo +224 -1
  13. package/dist/tengo/lib/pframes.xsv-import-file.lib.tengo +3 -3
  14. package/dist/tengo/lib/pframes.xsv.lib.tengo +79 -4
  15. package/dist/tengo/lib/sets.lib.tengo +2 -0
  16. package/dist/tengo/lib/slices.lib.tengo +4 -1
  17. package/dist/tengo/lib/smart.lib.tengo +24 -6
  18. package/dist/tengo/lib/tpl.base.lib.tengo +582 -0
  19. package/dist/tengo/lib/tpl.lib.tengo +6 -567
  20. package/dist/tengo/lib/tpl.light.lib.tengo +9 -0
  21. package/dist/tengo/lib/workdir.proc.lib.tengo +1 -1
  22. package/dist/tengo/lib/workflow.lib.tengo +41 -2
  23. package/dist/tengo/lib/workflow.model.lib.tengo +3 -2
  24. package/dist/tengo/tpl/exec.R.create-renv.plj.gz +0 -0
  25. package/dist/tengo/tpl/exec.exec.plj.gz +0 -0
  26. package/dist/tengo/tpl/exec.monetization_info.plj.gz +0 -0
  27. package/dist/tengo/tpl/exec.monetization_to_json.plj.gz +0 -0
  28. package/dist/tengo/tpl/exec.python.create-venv.plj.gz +0 -0
  29. package/dist/tengo/tpl/exec.python.install-deps.plj.gz +0 -0
  30. package/dist/tengo/tpl/ll.get-field.plj.gz +0 -0
  31. package/dist/tengo/tpl/pframes.export-multiple-pcolumns.plj.gz +0 -0
  32. package/dist/tengo/tpl/pframes.export-pframe-for-ui.plj.gz +0 -0
  33. package/dist/tengo/tpl/pframes.export-pframe.plj.gz +0 -0
  34. package/dist/tengo/tpl/pframes.export-single-pcolumn.plj.gz +0 -0
  35. package/dist/tengo/tpl/pframes.import-dir.plj.gz +0 -0
  36. package/dist/tengo/tpl/pframes.process-pcolumn-data.plj.gz +0 -0
  37. package/dist/tengo/tpl/pframes.remove-nulls.plj.gz +0 -0
  38. package/dist/tengo/tpl/pframes.slice-data.plj.gz +0 -0
  39. package/dist/tengo/tpl/pframes.xsv-export-pframe.plj.gz +0 -0
  40. package/dist/tengo/tpl/pframes.xsv-import-if-not-null.plj.gz +0 -0
  41. package/dist/tengo/tpl/pframes.xsv-import-map.plj.gz +0 -0
  42. package/dist/tengo/tpl/waiter.waiter.plj.gz +0 -0
  43. package/dist/tengo/tpl/workdir.save.plj.gz +0 -0
  44. package/dist/tengo/tpl/workflow.build-ctx.plj.gz +0 -0
  45. package/dist/tengo/tpl/workflow.query-anchored.plj.gz +0 -0
  46. package/package.json +3 -3
@@ -0,0 +1,544 @@
1
+ smart := import("@platforma-sdk/workflow-tengo:smart")
2
+ json := import("json")
3
+ text := import("text")
4
+ pConstants := import("@platforma-sdk/workflow-tengo:pframes.constants")
5
+ bquery := import("@platforma-sdk/workflow-tengo:workflow.bquery")
6
+ canonical := import("@platforma-sdk/workflow-tengo:canonical")
7
+ pSpec := import("@platforma-sdk/workflow-tengo:pframes.spec")
8
+ pBuilder := import("@platforma-sdk/workflow-tengo:pframes.builder")
9
+ maps := import("@platforma-sdk/workflow-tengo:maps")
10
+ ll := import("@platforma-sdk/workflow-tengo:ll")
11
+ xsv := import("@platforma-sdk/workflow-tengo:pframes.xsv")
12
+ assets := import("@platforma-sdk/workflow-tengo:assets")
13
+ render := import("@platforma-sdk/workflow-tengo:render")
14
+
15
+
16
+
17
+
18
+
19
+
20
+
21
+
22
+
23
+
24
+
25
+
26
+
27
+
28
+
29
+
30
+
31
+
32
+
33
+
34
+
35
+
36
+
37
+
38
+
39
+
40
+
41
+
42
+
43
+
44
+
45
+
46
+
47
+
48
+
49
+
50
+
51
+
52
+
53
+
54
+
55
+
56
+
57
+ createBuilder := func(ctx) {
58
+
59
+
60
+
61
+ refs := {}
62
+
63
+ anchors := {}
64
+
65
+ queries := {}
66
+
67
+ self := undefined
68
+ self = {
69
+
70
+
71
+
72
+
73
+
74
+ addRef: func(ref) {
75
+ refKey := canonical.encode(ref)
76
+
77
+
78
+ if !is_string(refs[refKey]) {
79
+ refs[refKey] = ref
80
+ }
81
+ },
82
+
83
+
84
+
85
+
86
+
87
+
88
+
89
+ addAnchor: func(anchorId, ref) {
90
+ anchors[anchorId] = ref
91
+ refKey := canonical.encode(ref)
92
+ if is_undefined(refs[refKey]) || is_map(refs[refKey]) {
93
+ refs[refKey] = anchorId
94
+ } else {
95
+ refs[refKey] = ref
96
+ }
97
+ },
98
+
99
+
100
+
101
+
102
+
103
+
104
+
105
+
106
+ addById: func(id) {
107
+ encodedId := undefined
108
+ decodedId := undefined
109
+
110
+ if is_string(id) {
111
+ decodedId = json.decode(id)
112
+ encodedId = id
113
+ } else if is_map(id) {
114
+ decodedId = id
115
+ } else {
116
+ ll.panic("Invalid column ID type. Expected string or object.")
117
+ }
118
+
119
+ if is_map(decodedId) && !is_undefined(decodedId.source) && !is_undefined(decodedId.axisFilters) {
120
+
121
+ columnKey := canonical.encode(decodedId.source)
122
+ queries[columnKey] = decodedId.source
123
+ } else {
124
+ queries[encodedId] = decodedId
125
+ }
126
+ },
127
+
128
+
129
+
130
+
131
+
132
+
133
+
134
+ addQuery: func(queryId, query) {
135
+ queries[queryId] = query
136
+ },
137
+
138
+
139
+
140
+
141
+
142
+
143
+ build: func() {
144
+ resolveResult := bquery.anchoredQuery(ctx, anchors, queries)
145
+
146
+ bundle := smart.mapBuilder(pConstants.RTYPE_P_COLUMN_BUNDLE)
147
+
148
+ for refKey, ref in refs {
149
+ if is_string(ref) {
150
+ bundle.addRef(refKey, resolveResult.getAnchor(ref))
151
+ } else {
152
+ bundle.addRef(refKey, bquery.resolve(ref, ctx))
153
+ }
154
+ }
155
+
156
+ for queryId, query in queries {
157
+ bundle.addRef(queryId, resolveResult.getResult(queryId))
158
+ }
159
+
160
+ return bundle.build()
161
+ }
162
+ }
163
+
164
+ return self
165
+ }
166
+
167
+ nameCleanPattern := text.re_compile("[-_,.:; \\[\\]}{\"\\\\/:]+")
168
+
169
+ cleanColumnName := func(name) {
170
+ return nameCleanPattern.replace(name, "_")
171
+ }
172
+
173
+
174
+
175
+
176
+
177
+
178
+
179
+
180
+
181
+ pColumnBundleUnmarshaller := func() {
182
+ return {
183
+ canParse: func(resource) {
184
+ return smart.isResource(resource) &&
185
+ resource.info().Type.Name == pConstants.RTYPE_P_COLUMN_BUNDLE.Name
186
+ },
187
+
188
+ parse: func(resource, parser) {
189
+ bResultUnmarshaller := bquery.resultUnmarshaller()
190
+
191
+ pool := {}
192
+
193
+ for resultKey, value in resource.inputs() {
194
+ if !bResultUnmarshaller.canParse(value.getValue()) {
195
+ ll.panic("Failed to unmarshal value for key: " + resultKey)
196
+ }
197
+
198
+ result := bResultUnmarshaller.parse(value.getValue(), parser)
199
+
200
+ pool[resultKey] = {
201
+ spec: result.spec,
202
+ data: result.data
203
+ }
204
+ }
205
+
206
+
207
+
208
+
209
+ axisHeaderOverrides := []
210
+
211
+ bundle := undefined
212
+ bundle = ll.toStrict({
213
+
214
+
215
+
216
+
217
+
218
+ getColumn: func(resultKey) {
219
+ if !is_string(resultKey) {
220
+ resultKey = canonical.encode(resultKey)
221
+ }
222
+
223
+ if is_undefined(pool[resultKey]) {
224
+ ll.panic("Column not found: " + resultKey)
225
+ }
226
+ return pool[resultKey]
227
+ },
228
+
229
+
230
+
231
+
232
+
233
+
234
+
235
+ setAxisHeader: func(matcher, header) {
236
+ if is_string(matcher) {
237
+ matcher = { name: matcher, domain: {} }
238
+ }
239
+
240
+ axisHeaderOverrides = [[matcher, header]] + axisHeaderOverrides
241
+ return bundle
242
+ },
243
+
244
+
245
+
246
+
247
+ xsvTableBuilder: func() {
248
+ localAxisHeaderOverrides := copy(axisHeaderOverrides)
249
+
250
+ requests := []
251
+ addedColumns := {}
252
+
253
+ findAxisHeaderOverride := func(spec) {
254
+ for _, override in localAxisHeaderOverrides {
255
+ matcher := override[0]
256
+
257
+ if matcher.name == spec.name {
258
+ domainsMatch := true
259
+
260
+ for domainKey, domainValue in matcher.domain {
261
+ if is_undefined(spec.domain) ||
262
+ is_undefined(spec.domain[domainKey]) ||
263
+ spec.domain[domainKey] != domainValue {
264
+ domainsMatch = false
265
+ break
266
+ }
267
+ }
268
+
269
+ if domainsMatch {
270
+ return override[1]
271
+ }
272
+ }
273
+ }
274
+ return undefined
275
+ }
276
+
277
+ distiller := undefined
278
+
279
+ getSpecDistiller := func() {
280
+ if is_undefined(distiller) {
281
+ columnsToDistill := []
282
+ for request in requests {
283
+ col := pool[request.key]
284
+ columnsToDistill = append(columnsToDistill, col.spec)
285
+ }
286
+ distiller = pSpec.createSpecDistiller(columnsToDistill)
287
+ }
288
+ return distiller
289
+ }
290
+
291
+ generateLabelForAxis := func(spec) {
292
+ name := spec.name
293
+ label := undefined
294
+
295
+ label = findAxisHeaderOverride(spec)
296
+
297
+ if is_undefined(label) {
298
+ specDistiller := getSpecDistiller()
299
+ discriminativeDomains := specDistiller.getDiscriminativeDomains(name)
300
+
301
+ if len(discriminativeDomains) == 0 {
302
+ label = cleanColumnName(name)
303
+ } else {
304
+ ll.panic("Cannot deduce header for %s in axis. Discriminative domains: %s. Please provide a header override.",
305
+ name, discriminativeDomains)
306
+ }
307
+ }
308
+
309
+ if is_undefined(label) {
310
+ ll.panic("Cannot deduce header for %s in axis. Please provide a header override.",
311
+ name)
312
+ }
313
+
314
+ return label
315
+ }
316
+
317
+
318
+ processColumnId := func(id) {
319
+ columnKey := undefined
320
+ columnAxisFilters := undefined
321
+ serializedId := undefined
322
+
323
+ if is_string(id) {
324
+ serializedId = id
325
+ decodedId := json.decode(id)
326
+
327
+ if is_map(decodedId) && !is_undefined(decodedId.source) && !is_undefined(decodedId.axisFilters) {
328
+ columnKey = canonical.encode(decodedId.source)
329
+ columnAxisFilters = decodedId.axisFilters
330
+ } else {
331
+ columnKey = id
332
+ }
333
+ } else if is_map(id) {
334
+ if !is_undefined(id.source) && !is_undefined(id.axisFilters) {
335
+ columnKey = canonical.encode(id.source)
336
+ columnAxisFilters = id.axisFilters
337
+ serializedId = canonical.encode(id)
338
+ } else {
339
+ columnKey = canonical.encode(id)
340
+ serializedId = columnKey
341
+ }
342
+ } else {
343
+ ll.panic("Invalid column ID type. Expected string or object.")
344
+ }
345
+
346
+ return {
347
+ columnKey: columnKey,
348
+ axisFilters: columnAxisFilters,
349
+ canonicalId: serializedId
350
+ }
351
+ }
352
+
353
+ tableBuilder := undefined
354
+ tableBuilder = {
355
+
356
+
357
+
358
+
359
+
360
+
361
+
362
+ add: func(key, ...ops) {
363
+
364
+ distiller = undefined
365
+
366
+ processedId := processColumnId(key)
367
+ columnKey := processedId.columnKey
368
+ columnAxisFilters := processedId.axisFilters
369
+ serializedId := processedId.canonicalId
370
+
371
+ if is_undefined(pool[columnKey]) {
372
+ ll.panic("Column not found: " + columnKey)
373
+ }
374
+
375
+ request := {
376
+ key: columnKey,
377
+ canonicalId: serializedId
378
+ }
379
+
380
+ if !is_undefined(columnAxisFilters) {
381
+ request.axisFilters = columnAxisFilters
382
+ }
383
+
384
+ if len(ops) > 0 {
385
+ if !is_undefined(ops[0].header) {
386
+ request.headerOverride = ops[0].header
387
+ }
388
+
389
+ if !is_undefined(ops[0].axisFilters) {
390
+ if !is_undefined(request.axisFilters) {
391
+ ll.panic("Cannot provide axis filters in both FilteredPColumnId and options. Use either one, not both.")
392
+ } else {
393
+ request.axisFilters = ops[0].axisFilters
394
+
395
+ request.canonicalId = canonical.encode({
396
+ source: json.decode(columnKey),
397
+ axisFilters: ops[0].axisFilters
398
+ })
399
+ }
400
+ }
401
+ }
402
+
403
+ if !is_undefined(request.axisFilters) && len(request.axisFilters) > 0 {
404
+ colSpec := pool[columnKey].spec
405
+ prepared := pSpec.prepareAxisFilters(request.axisFilters, colSpec)
406
+
407
+ request.axisFilters = prepared.axisFilters
408
+ request.columnSpec = prepared.columnSpec
409
+ } else {
410
+ request.columnSpec = pool[columnKey].spec
411
+ }
412
+
413
+ if !is_undefined(addedColumns[request.canonicalId]) {
414
+ ll.panic("Duplicate column detected: %s. This column has already been added to the table.",
415
+ request.canonicalId)
416
+ }
417
+
418
+ addedColumns[request.canonicalId] = true
419
+
420
+ request.safeId = cleanColumnName(request.canonicalId)
421
+
422
+ if !is_undefined(request.headerOverride) {
423
+ request.header = request.headerOverride
424
+ } else {
425
+
426
+ request.header = request.safeId
427
+ }
428
+
429
+ requests = append(requests, request)
430
+ return tableBuilder
431
+ },
432
+
433
+
434
+
435
+
436
+
437
+
438
+
439
+ setAxisHeader: func(matcher, header) {
440
+ if is_string(matcher) {
441
+ matcher = { name: matcher, domain: {} }
442
+ }
443
+
444
+ localAxisHeaderOverrides = [[matcher, header]] + localAxisHeaderOverrides
445
+ return tableBuilder
446
+ },
447
+
448
+
449
+
450
+
451
+
452
+
453
+
454
+
455
+
456
+
457
+ build: func(format, ...params) {
458
+ if format != "csv" && format != "tsv" {
459
+ ll.panic("Invalid format: '%s'. Expected 'csv' or 'tsv'.", format)
460
+ }
461
+
462
+ exportParams := {
463
+ naStr: "",
464
+ nullStr: ""
465
+ }
466
+ if len(params) > 0 {
467
+ exportParams = maps.deepMerge(exportParams, params[0])
468
+ }
469
+
470
+ sliceDataTemplate := assets.importTemplate("@platforma-sdk/workflow-tengo:pframes.slice-data")
471
+
472
+ columnDistiller := getSpecDistiller()
473
+
474
+ pFrame := pBuilder.pFrameBuilder()
475
+
476
+ for request in requests {
477
+ col := pool[request.key]
478
+ colData := col.data
479
+
480
+ if !is_undefined(request.axisFilters) {
481
+ colData = render.createEphemeral(sliceDataTemplate, {
482
+ data: colData,
483
+ slicingParams: smart.createJsonResource(request.axisFilters)
484
+ }).output("result")
485
+ }
486
+
487
+ preparedSpec := columnDistiller.distill(request.columnSpec)
488
+
489
+ preparedSpec = maps.deepMerge(preparedSpec, {
490
+ annotations: { "pl7.app/label": request.header }
491
+ })
492
+
493
+ for i, axis in preparedSpec.axesSpec {
494
+ preparedSpec.axesSpec[i] = maps.deepMerge(axis, {
495
+ annotations: { "pl7.app/label": generateLabelForAxis(axis) }
496
+ })
497
+ }
498
+
499
+
500
+ pFrame.add(request.safeId, preparedSpec, colData)
501
+ }
502
+
503
+ return xsv.exportFrame(pFrame.build(), format, exportParams)
504
+ },
505
+
506
+
507
+
508
+
509
+
510
+
511
+
512
+
513
+ getHeader: func(item) {
514
+
515
+ if is_map(item) && !is_undefined(item.type) {
516
+ return generateLabelForAxis(item)
517
+ }
518
+
519
+ serializedId := processColumnId(item).canonicalId
520
+
521
+ for _, request in requests {
522
+ if request.canonicalId == serializedId {
523
+ return request.header
524
+ }
525
+ }
526
+
527
+ ll.panic("Column not found in the table: %s. Make sure to call add() before getHeader().",
528
+ serializedId)
529
+ }
530
+ }
531
+
532
+ return tableBuilder
533
+ }
534
+ })
535
+
536
+ return bundle
537
+ }
538
+ }
539
+ }
540
+
541
+ export ll.toStrict({
542
+ createBuilder: createBuilder,
543
+ pColumnBundleUnmarshaller: pColumnBundleUnmarshaller
544
+ })
@@ -43,6 +43,8 @@ P_COLUMN_SUPER_PARTITIONED_DATA_TYPE_PREFIX := P_COLUMN_DATA_TYPE_PREFIX + "Part
43
43
  RTYPE_P_COLUMN_DATA_JSON_SUPER_PARTITIONED := { Name: P_COLUMN_SUPER_PARTITIONED_DATA_TYPE_PREFIX + "JsonPartitioned", Version: "1" }
44
44
  RTYPE_P_COLUMN_DATA_BINARY_SUPER_PARTITIONED := { Name: P_COLUMN_SUPER_PARTITIONED_DATA_TYPE_PREFIX + "BinaryPartitioned", Version: "1" }
45
45
 
46
+ RTYPE_P_COLUMN_BUNDLE := { Name: "PColumnBundle", Version: "1" }
47
+
46
48
  KIND_P_COLUMN := "PColumn"
47
49
 
48
50
  export ll.toStrict({
@@ -63,5 +65,7 @@ export ll.toStrict({
63
65
  RTYPE_P_COLUMN_DATA_JSON_PARTITIONED : RTYPE_P_COLUMN_DATA_JSON_PARTITIONED,
64
66
  RTYPE_P_COLUMN_DATA_BINARY_PARTITIONED : RTYPE_P_COLUMN_DATA_BINARY_PARTITIONED,
65
67
  RTYPE_P_COLUMN_DATA_JSON_SUPER_PARTITIONED : RTYPE_P_COLUMN_DATA_JSON_SUPER_PARTITIONED,
66
- RTYPE_P_COLUMN_DATA_BINARY_SUPER_PARTITIONED : RTYPE_P_COLUMN_DATA_BINARY_SUPER_PARTITIONED
68
+ RTYPE_P_COLUMN_DATA_BINARY_SUPER_PARTITIONED : RTYPE_P_COLUMN_DATA_BINARY_SUPER_PARTITIONED,
69
+
70
+ RTYPE_P_COLUMN_BUNDLE : RTYPE_P_COLUMN_BUNDLE
67
71
  })
@@ -273,6 +273,9 @@ processColumn := func(input, bodyTpl, outputs, ...opts) {
273
273
  }
274
274
  outputSpecs[output.name] = spec
275
275
  } else if output.type == "Xsv" {
276
+ if is_undefined(output.settings) {
277
+ ll.panic("settings are required for Xsv output")
278
+ }
276
279
  decomposition := pUtil.decomposePfconvImportCfg(output.settings, {
277
280
  additionalAxesSpec: iterationAxesSpec
278
281
  })