@platforma-sdk/workflow-tengo 5.2.0 → 5.2.2

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 (49) hide show
  1. package/README.md +4 -0
  2. package/dist/index.cjs +1 -0
  3. package/dist/index.d.ts +1 -1
  4. package/dist/index.js +1 -0
  5. package/dist/tengo/lib/pframes.constants.lib.tengo +7 -1
  6. package/dist/tengo/lib/pframes.export-pcolumn.lib.tengo +37 -2
  7. package/dist/tengo/lib/pframes.export-pframe.lib.tengo +2 -0
  8. package/dist/tengo/lib/pframes.spec.lib.tengo +13 -11
  9. package/dist/tengo/lib/pt.lib.tengo +95 -3
  10. package/dist/tengo/lib/pt.util.lib.tengo +178 -0
  11. package/dist/tengo/lib/validation.lib.tengo +14 -0
  12. package/dist/tengo/tpl/anonymize.deanonymize.plj.gz +0 -0
  13. package/dist/tengo/tpl/anonymize.impl.plj.gz +0 -0
  14. package/dist/tengo/tpl/check_network.create_workdir_from_storage.plj.gz +0 -0
  15. package/dist/tengo/tpl/check_network.download_blob.plj.gz +0 -0
  16. package/dist/tengo/tpl/check_network.run_hello_world.plj.gz +0 -0
  17. package/dist/tengo/tpl/check_network.run_hello_world_py.plj.gz +0 -0
  18. package/dist/tengo/tpl/check_network.upload_blob.plj.gz +0 -0
  19. package/dist/tengo/tpl/check_network.upload_template.plj.gz +0 -0
  20. package/dist/tengo/tpl/check_network.workdir_type_name_extractor.plj.gz +0 -0
  21. package/dist/tengo/tpl/exec.R.create-renv.plj.gz +0 -0
  22. package/dist/tengo/tpl/exec.delayed-compute-allocation.impl.plj.gz +0 -0
  23. package/dist/tengo/tpl/exec.exec-impl.plj.gz +0 -0
  24. package/dist/tengo/tpl/exec.exec.plj.gz +0 -0
  25. package/dist/tengo/tpl/exec.load-software.impl.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/exec.python.venv-bin-path.plj.gz +0 -0
  31. package/dist/tengo/tpl/ll.get-field.plj.gz +0 -0
  32. package/dist/tengo/tpl/pframes.export-multiple-pcolumns.plj.gz +0 -0
  33. package/dist/tengo/tpl/pframes.export-pframe-for-ui.plj.gz +0 -0
  34. package/dist/tengo/tpl/pframes.export-pframe.plj.gz +0 -0
  35. package/dist/tengo/tpl/pframes.export-single-pcolumn.plj.gz +0 -0
  36. package/dist/tengo/tpl/pframes.import-dir.plj.gz +0 -0
  37. package/dist/tengo/tpl/pframes.parse-to-json.plj.gz +0 -0
  38. package/dist/tengo/tpl/pframes.process-pcolumn-data.plj.gz +0 -0
  39. package/dist/tengo/tpl/pframes.remove-nulls.plj.gz +0 -0
  40. package/dist/tengo/tpl/pframes.slice-data.plj.gz +0 -0
  41. package/dist/tengo/tpl/pframes.xsv-export-pframe.plj.gz +0 -0
  42. package/dist/tengo/tpl/pframes.xsv-import-if-not-null.plj.gz +0 -0
  43. package/dist/tengo/tpl/pframes.xsv-import-map.plj.gz +0 -0
  44. package/dist/tengo/tpl/pt.import-dir.plj.gz +0 -0
  45. package/dist/tengo/tpl/waiter.waiter.plj.gz +0 -0
  46. package/dist/tengo/tpl/workdir.save.plj.gz +0 -0
  47. package/dist/tengo/tpl/workflow.build-ctx.plj.gz +0 -0
  48. package/dist/tengo/tpl/workflow.query-anchored.plj.gz +0 -0
  49. package/package.json +4 -4
package/README.md CHANGED
@@ -19,4 +19,8 @@ Perfect for researchers who need flexible data processing that can intelligently
19
19
  - [Tengo syntax](https://github.com/d5/tengo/blob/master/docs/tutorial.md)
20
20
  - [Tengo builtin functions](https://github.com/d5/tengo/blob/master/docs/builtins.md)
21
21
 
22
+ # Running single test
22
23
 
24
+ ```sh
25
+ pnpm build:dev --filter='@platforma-sdk/workflow-tengo-tests...' && (cd tests/workflow-tengo && PL_ADDRESS="http://127.0.0.1:6345" PL_TEST_USER="default" PL_TEST_PASSWORD="__password__" pnpm run test pt -t "pt write frame test" --retry 0)
26
+ ```
package/dist/index.cjs CHANGED
@@ -3,6 +3,7 @@ module.exports = { Templates: {
3
3
  'pframes.export-single-pcolumn': { type: 'from-file', path: require.resolve('./tengo/tpl/pframes.export-single-pcolumn.plj.gz') },
4
4
  'pframes.import-dir': { type: 'from-file', path: require.resolve('./tengo/tpl/pframes.import-dir.plj.gz') },
5
5
  'pframes.remove-nulls': { type: 'from-file', path: require.resolve('./tengo/tpl/pframes.remove-nulls.plj.gz') },
6
+ 'pt.import-dir': { type: 'from-file', path: require.resolve('./tengo/tpl/pt.import-dir.plj.gz') },
6
7
  'waiter.waiter': { type: 'from-file', path: require.resolve('./tengo/tpl/waiter.waiter.plj.gz') },
7
8
  'workdir.save': { type: 'from-file', path: require.resolve('./tengo/tpl/workdir.save.plj.gz') },
8
9
  'workflow.build-ctx': { type: 'from-file', path: require.resolve('./tengo/tpl/workflow.build-ctx.plj.gz') },
package/dist/index.d.ts CHANGED
@@ -1,4 +1,4 @@
1
1
  declare type TemplateFromFile = { readonly type: "from-file"; readonly path: string; };
2
- declare type TplName = "ll.get-field" | "pframes.export-single-pcolumn" | "pframes.import-dir" | "pframes.remove-nulls" | "waiter.waiter" | "workdir.save" | "workflow.build-ctx" | "workflow.query-anchored" | "anonymize.deanonymize" | "anonymize.impl" | "check_network.workdir_type_name_extractor" | "exec.delayed-compute-allocation.impl" | "exec.monetization_to_json" | "exec.python.install-deps" | "exec.python.venv-bin-path" | "pframes.export-multiple-pcolumns" | "pframes.slice-data" | "check_network.create_workdir_from_storage" | "check_network.download_blob" | "check_network.upload_blob" | "check_network.upload_template" | "exec.R.create-renv" | "exec.exec-impl" | "exec.monetization_info" | "exec.python.create-venv" | "pframes.parse-to-json" | "exec.load-software.impl" | "exec.exec" | "pframes.xsv-export-pframe" | "pframes.xsv-import-if-not-null" | "pframes.xsv-import-map" | "check_network.run_hello_world" | "check_network.run_hello_world_py" | "pframes.process-pcolumn-data" | "pframes.export-pframe" | "pframes.export-pframe-for-ui";
2
+ declare type TplName = "ll.get-field" | "pframes.export-single-pcolumn" | "pframes.import-dir" | "pframes.remove-nulls" | "pt.import-dir" | "waiter.waiter" | "workdir.save" | "workflow.build-ctx" | "workflow.query-anchored" | "anonymize.deanonymize" | "anonymize.impl" | "check_network.workdir_type_name_extractor" | "exec.delayed-compute-allocation.impl" | "exec.monetization_to_json" | "exec.python.install-deps" | "exec.python.venv-bin-path" | "pframes.export-multiple-pcolumns" | "pframes.slice-data" | "check_network.create_workdir_from_storage" | "check_network.download_blob" | "check_network.upload_blob" | "check_network.upload_template" | "exec.R.create-renv" | "exec.exec-impl" | "exec.monetization_info" | "exec.python.create-venv" | "pframes.parse-to-json" | "exec.load-software.impl" | "exec.exec" | "pframes.xsv-export-pframe" | "pframes.xsv-import-if-not-null" | "pframes.xsv-import-map" | "check_network.run_hello_world" | "check_network.run_hello_world_py" | "pframes.process-pcolumn-data" | "pframes.export-pframe" | "pframes.export-pframe-for-ui";
3
3
  declare const Templates: Record<TplName, TemplateFromFile>;
4
4
  export { Templates };
package/dist/index.js CHANGED
@@ -4,6 +4,7 @@ export const Templates = {
4
4
  'pframes.export-single-pcolumn': { type: 'from-file', path: resolve(import.meta.dirname, './tengo/tpl/pframes.export-single-pcolumn.plj.gz') },
5
5
  'pframes.import-dir': { type: 'from-file', path: resolve(import.meta.dirname, './tengo/tpl/pframes.import-dir.plj.gz') },
6
6
  'pframes.remove-nulls': { type: 'from-file', path: resolve(import.meta.dirname, './tengo/tpl/pframes.remove-nulls.plj.gz') },
7
+ 'pt.import-dir': { type: 'from-file', path: resolve(import.meta.dirname, './tengo/tpl/pt.import-dir.plj.gz') },
7
8
  'waiter.waiter': { type: 'from-file', path: resolve(import.meta.dirname, './tengo/tpl/waiter.waiter.plj.gz') },
8
9
  'workdir.save': { type: 'from-file', path: resolve(import.meta.dirname, './tengo/tpl/workdir.save.plj.gz') },
9
10
  'workflow.build-ctx': { type: 'from-file', path: resolve(import.meta.dirname, './tengo/tpl/workflow.build-ctx.plj.gz') },
@@ -44,12 +44,15 @@ RTYPE_P_COLUMN_DATA_RESOURCE_MAP := { Name: P_COLUMN_DATA_TYPE_PREFIX + "Resourc
44
44
  RTYPE_P_COLUMN_DATA_RESOURCE_MAP_PARTITIONED := { Name: P_COLUMN_DATA_TYPE_PREFIX + "Partitioned/ResourceMap", Version: "1" }
45
45
  RTYPE_P_COLUMN_DATA_JSON_PARTITIONED := { Name: P_COLUMN_DATA_TYPE_PREFIX + "JsonPartitioned", Version: "1" }
46
46
  RTYPE_P_COLUMN_DATA_BINARY_PARTITIONED := { Name: P_COLUMN_DATA_TYPE_PREFIX + "BinaryPartitioned", Version: "1" }
47
+ RTYPE_P_COLUMN_DATA_PARQUET_PARTITIONED := { Name: P_COLUMN_DATA_TYPE_PREFIX + "ParquetPartitioned", Version: "1" }
47
48
 
48
49
  P_COLUMN_SUPER_PARTITIONED_DATA_TYPE_PREFIX := P_COLUMN_DATA_TYPE_PREFIX + "Partitioned/"
49
50
  RTYPE_P_COLUMN_DATA_JSON_SUPER_PARTITIONED := { Name: P_COLUMN_SUPER_PARTITIONED_DATA_TYPE_PREFIX + "JsonPartitioned", Version: "1" }
50
51
  RTYPE_P_COLUMN_DATA_BINARY_SUPER_PARTITIONED := { Name: P_COLUMN_SUPER_PARTITIONED_DATA_TYPE_PREFIX + "BinaryPartitioned", Version: "1" }
52
+ RTYPE_P_COLUMN_DATA_PARQUET_SUPER_PARTITIONED := { Name: P_COLUMN_SUPER_PARTITIONED_DATA_TYPE_PREFIX + "ParquetPartitioned", Version: "1" }
51
53
 
52
54
  RTYPE_P_COLUMN_BUNDLE := { Name: "PColumnBundle", Version: "1" }
55
+ RTYPE_PARQUET_CHUNK := { Name: "ParquetChunk", Version: "1" }
53
56
 
54
57
  KIND_P_COLUMN := "PColumn"
55
58
 
@@ -71,8 +74,11 @@ export ll.toStrict({
71
74
  RTYPE_P_COLUMN_DATA_RESOURCE_MAP_PARTITIONED : RTYPE_P_COLUMN_DATA_RESOURCE_MAP_PARTITIONED,
72
75
  RTYPE_P_COLUMN_DATA_JSON_PARTITIONED : RTYPE_P_COLUMN_DATA_JSON_PARTITIONED,
73
76
  RTYPE_P_COLUMN_DATA_BINARY_PARTITIONED : RTYPE_P_COLUMN_DATA_BINARY_PARTITIONED,
77
+ RTYPE_P_COLUMN_DATA_PARQUET_PARTITIONED : RTYPE_P_COLUMN_DATA_PARQUET_PARTITIONED,
74
78
  RTYPE_P_COLUMN_DATA_JSON_SUPER_PARTITIONED : RTYPE_P_COLUMN_DATA_JSON_SUPER_PARTITIONED,
75
79
  RTYPE_P_COLUMN_DATA_BINARY_SUPER_PARTITIONED : RTYPE_P_COLUMN_DATA_BINARY_SUPER_PARTITIONED,
80
+ RTYPE_P_COLUMN_DATA_PARQUET_SUPER_PARTITIONED : RTYPE_P_COLUMN_DATA_PARQUET_SUPER_PARTITIONED,
76
81
 
77
- RTYPE_P_COLUMN_BUNDLE : RTYPE_P_COLUMN_BUNDLE
82
+ RTYPE_P_COLUMN_BUNDLE : RTYPE_P_COLUMN_BUNDLE,
83
+ RTYPE_PARQUET_CHUNK : RTYPE_PARQUET_CHUNK
78
84
  })
@@ -4,15 +4,19 @@ render := import("@platforma-sdk/workflow-tengo:render")
4
4
  file := import("@platforma-sdk/workflow-tengo:file")
5
5
  ll := import("@platforma-sdk/workflow-tengo:ll")
6
6
  assets := import("@platforma-sdk/workflow-tengo:assets")
7
+ json := import("json")
7
8
 
8
9
 
9
10
  nestedTpl := assets.importTemplate("@platforma-sdk/workflow-tengo:pframes.export-single-pcolumn")
10
11
 
11
12
 
12
13
  exportPColumnData := func(data) {
14
+ ll.assert(data.info().InputsLocked, "column data resource not locked for export: %v", data.info().Type)
15
+
13
16
  if data.checkResourceType(pConstants.RTYPE_P_COLUMN_DATA_RESOURCE_MAP_PARTITIONED) ||
14
17
  data.checkResourceType(pConstants.RTYPE_P_COLUMN_DATA_JSON_SUPER_PARTITIONED) ||
15
- data.checkResourceType(pConstants.RTYPE_P_COLUMN_DATA_BINARY_SUPER_PARTITIONED) {
18
+ data.checkResourceType(pConstants.RTYPE_P_COLUMN_DATA_BINARY_SUPER_PARTITIONED) ||
19
+ data.checkResourceType(pConstants.RTYPE_P_COLUMN_DATA_PARQUET_SUPER_PARTITIONED) {
16
20
 
17
21
 
18
22
  newData := smart.structBuilder(data.info().Type, data.info().Data)
@@ -32,13 +36,44 @@ exportPColumnData := func(data) {
32
36
  }
33
37
  return newData.lockAndBuild()
34
38
 
39
+ } else if data.checkResourceType(pConstants.RTYPE_P_COLUMN_DATA_PARQUET_PARTITIONED) {
40
+
41
+
42
+ newData := smart.structBuilder(data.info().Type, data.info().Data)
43
+ for key, partData in data.inputs() {
44
+ partDataResource := partData.getValue()
45
+ ll.assert(
46
+ partDataResource.checkResourceType(pConstants.RTYPE_PARQUET_CHUNK),
47
+ "expected resource %v for axis key %v, got %v",
48
+ pConstants.RTYPE_PARQUET_CHUNK,
49
+ key,
50
+ partDataResource.info().Type
51
+ )
52
+ ll.assert(partDataResource.info().InputsLocked, "resource %v inputs not locked", partDataResource.info().Type)
53
+
54
+
55
+
56
+
57
+ chunkMetadata := partDataResource.getDataAsJson()
58
+ chunkMetadata.isDownloadable = true
59
+
60
+ parquetChunkResource := smart.structBuilder(partDataResource.info().Type, json.encode(chunkMetadata))
61
+
62
+ partDataFields := partDataResource.metaInputs()
63
+ parquetChunkResource.createMetaField("blob").set(file.exportFile(partDataFields["blob"]))
64
+ parquetChunkResource.createMetaField("mapping").set(partDataFields["mapping"])
65
+
66
+ newData.createInputField(key).set(parquetChunkResource.lockAndBuild())
67
+ }
68
+ return newData.lockAndBuild()
69
+
35
70
  } else if data.checkResourceType(pConstants.RTYPE_P_COLUMN_DATA_JSON) {
36
71
 
37
72
 
38
73
  return data
39
74
 
40
75
  } else {
41
- ll.panic("unknonw resource type: %v", data.info().Type)
76
+ ll.panic("unknown resource type: %v", data.info().Type)
42
77
  }
43
78
  }
44
79
 
@@ -23,6 +23,8 @@ exportPFrame := func(pfResource, skipHideDataFromUI) {
23
23
 
24
24
  fields := pfResource.inputs()
25
25
 
26
+ ll.assert(pfResource.info().InputsLocked, "pframe resource not locked for export: %v", pfResource.info().Type)
27
+
26
28
 
27
29
  if pfResource.checkResourceType(constants.RTYPE_CONTEXT) {
28
30
  newFields := {}
@@ -15,32 +15,32 @@ json := import("json")
15
15
 
16
16
 
17
17
 
18
- P_AXIS_SPEC_SCHEMA := {
19
-
20
- `type`: `string,regex=Int|Long|Float|Double|String`,
18
+ P_UNTYPED_SPEC_SCHEMA := {
21
19
  `name`: `string`,
22
20
  `domain,?`: { any: `string` },
23
21
  `annotations,?`: { any: `string` },
24
- `parentAxes,?`: `any` // [ `number` ]
22
+ `parentAxes,?`: [ `number` ]
25
23
  }
26
24
 
27
- P_COLUMN_SPEC_SCHEMA := {
25
+ P_AXIS_SPEC_SCHEMA := ["and", P_UNTYPED_SPEC_SCHEMA, {
26
+ `type`: `string,regex=Int|Long|String`
27
+ }]
28
28
 
29
+ P_COLUMN_OWN_SPEC_SCHEMA := ["and", P_UNTYPED_SPEC_SCHEMA, {
30
+ `valueType`: `string,regex=Int|Long|Float|Double|String`
31
+ }]
32
+
33
+ P_COLUMN_SPEC_SCHEMA := ["and", P_UNTYPED_SPEC_SCHEMA, {
29
34
  `kind`: `string,regex=PColumn`,
30
35
  `valueType`: `string`,
31
- `name`: `string`,
32
- `domain,?`: { any: `string` },
33
- `annotations,?`: { any: `string` },
34
- `parentAxes,?`: `any`, // [ `number` ]
35
36
  `axesSpec,?`: [ P_AXIS_SPEC_SCHEMA ]
36
- }
37
+ }]
37
38
 
38
39
  P_OBJECT_SPEC_SCHEMA := ["or", P_COLUMN_SPEC_SCHEMA, {
39
40
  `kind`: `string`,
40
41
  `annotations,?`: { any: `string` }
41
42
  }]
42
43
 
43
-
44
44
  KIND_P_COLUMN := "PColumn"
45
45
 
46
46
 
@@ -531,7 +531,9 @@ axisSpecToMatcher := func(axisSpec) {
531
531
  }
532
532
 
533
533
  export ll.toStrict({
534
+ P_UNTYPED_SPEC_SCHEMA: P_UNTYPED_SPEC_SCHEMA,
534
535
  P_AXIS_SPEC_SCHEMA: P_AXIS_SPEC_SCHEMA,
536
+ P_COLUMN_OWN_SPEC_SCHEMA: P_COLUMN_OWN_SPEC_SCHEMA,
535
537
  P_COLUMN_SPEC_SCHEMA: P_COLUMN_SPEC_SCHEMA,
536
538
  P_OBJECT_SPEC_SCHEMA: P_OBJECT_SPEC_SCHEMA,
537
539
 
@@ -1,5 +1,6 @@
1
1
  ll := import("@platforma-sdk/workflow-tengo:ll")
2
2
  exp := import("@platforma-sdk/workflow-tengo:pt.expression")
3
+ util := import("@platforma-sdk/workflow-tengo:pt.util")
3
4
  assets := import("@platforma-sdk/workflow-tengo:assets")
4
5
  smart := import("@platforma-sdk/workflow-tengo:smart")
5
6
  exec := import("@platforma-sdk/workflow-tengo:exec")
@@ -8,6 +9,11 @@ text := import("text")
8
9
  slices := import("@platforma-sdk/workflow-tengo:slices")
9
10
  validation := import("@platforma-sdk/workflow-tengo:validation")
10
11
  execConstants := import("@platforma-sdk/workflow-tengo:exec.constants")
12
+ oop := import("@platforma-sdk/workflow-tengo:oop")
13
+ pframes := import("@platforma-sdk/workflow-tengo:pframes")
14
+
15
+
16
+ importDirTpl := assets.importTemplate("@platforma-sdk/workflow-tengo:pt.import-dir")
11
17
 
12
18
 
13
19
 
@@ -59,6 +65,8 @@ _detectFormatFromFileName := func(fileName) {
59
65
  return "tsv"
60
66
  } else if text.has_suffix(fileName, ".ndjson") || text.has_suffix(fileName, ".jsonl") {
61
67
  return "ndjson"
68
+ } else if text.has_suffix(fileName, ".parquet") {
69
+ return "parquet"
62
70
  }
63
71
  return undefined // No format detected
64
72
  }
@@ -79,6 +87,8 @@ workflow := func() {
79
87
 
80
88
  outContentFiles := []
81
89
 
90
+ outFramesSpecs := {}
91
+
82
92
  anonymousFilesCounter := 0
83
93
  anonymousDFCounter := 0
84
94
 
@@ -282,7 +292,13 @@ workflow := func() {
282
292
 
283
293
 
284
294
  step := undefined
285
- if finalFormat == "ndjson" {
295
+ if finalFormat == "parquet" {
296
+ step = {
297
+ type: "read_parquet",
298
+ file: finalFileName,
299
+ name: finalDataFrameId
300
+ }
301
+ } else if finalFormat == "ndjson" {
286
302
  step = {
287
303
  type: "read_ndjson",
288
304
  file: finalFileName,
@@ -345,6 +361,14 @@ workflow := func() {
345
361
  _saveFileContent: func(name) {
346
362
  outContentFiles = append(outContentFiles, name)
347
363
  },
364
+ _saveFrameSpecs: func(name, specs) {
365
+ ll.assert(
366
+ outFramesSpecs[name] == undefined,
367
+ "frame with name '%s' already saved, please pick a different name",
368
+ name
369
+ )
370
+ outFramesSpecs[name] = specs
371
+ },
348
372
 
349
373
 
350
374
 
@@ -447,7 +471,44 @@ workflow := func() {
447
471
  ptablerCmdBuilder.saveFileContent(outContentFile)
448
472
  }
449
473
 
450
- return ptablerCmdBuilder.run()
474
+ tplArgs := {}
475
+ for frameName, specsById in outFramesSpecs {
476
+ columnIds := []
477
+ for columnId, _ in specsById {
478
+ columnIds = append(columnIds, columnId)
479
+ }
480
+ tplArgs[frameName] = columnIds;
481
+ }
482
+ workdirProcessorName := "import_dir";
483
+ if len(tplArgs) > 0 {
484
+ ptablerCmdBuilder.processWorkdir(workdirProcessorName, importDirTpl, tplArgs)
485
+ }
486
+
487
+ execResult := ptablerCmdBuilder.run()
488
+
489
+ self := undefined
490
+ self = ll.toStrict(oop.inherit(execResult, {
491
+
492
+
493
+
494
+
495
+
496
+
497
+ getFrameDirect: func(frameName) {
498
+ specs := outFramesSpecs[frameName]
499
+ ll.assert(!is_undefined(specs), "saveFrameDirect was not called for frame '%s'", frameName)
500
+ savedFrames := execResult.getProcessorResult(workdirProcessorName)
501
+
502
+ pf := pframes.pFrameBuilder()
503
+ for pColumnId, pColumnSpec in specs {
504
+ pColumnRef := util.makeFrameColumnRef(frameName, pColumnId)
505
+ pColumnData := savedFrames.getFutureInputField(pColumnRef)
506
+ pf.add(pColumnId, pColumnSpec, pColumnData)
507
+ }
508
+ return pf.build()
509
+ }
510
+ }))
511
+ return self
451
512
  }
452
513
  }
453
514
 
@@ -568,7 +629,13 @@ _newDataFrame = func(parentWorkflow, dfName) {
568
629
  }
569
630
 
570
631
  step := undefined
571
- if finalFormat == "ndjson" {
632
+ if finalFormat == "parquet" {
633
+ step = {
634
+ type: "write_parquet",
635
+ table: dfName,
636
+ file: outputFile
637
+ }
638
+ } else if finalFormat == "ndjson" {
572
639
  step = {
573
640
  type: "write_ndjson",
574
641
  table: dfName,
@@ -756,6 +823,31 @@ _newDataFrame = func(parentWorkflow, dfName) {
756
823
 
757
824
 
758
825
 
826
+
827
+
828
+
829
+
830
+ saveFrameDirect: func(frameName, params) {
831
+ ll.assert(len(frameName) > 0, "frameName must be a non-empty string")
832
+ util.validateParams(params)
833
+
834
+ specs := util.makeFrameSpecs(params)
835
+ parentWorkflow._saveFrameSpecs(frameName, specs)
836
+
837
+ step := util.makeWriteFrameStep(frameName, dfName, params)
838
+ parentWorkflow.addRawStep(step)
839
+ },
840
+
841
+
842
+
843
+
844
+
845
+
846
+
847
+
848
+
849
+
850
+
759
851
  groupBy: func(...expressions) {
760
852
  ll.assert(len(expressions) > 0, "groupBy requires at least one expression argument.")
761
853
 
@@ -0,0 +1,178 @@
1
+ ll := import("@platforma-sdk/workflow-tengo:ll")
2
+ constants := import("@platforma-sdk/workflow-tengo:pframes.constants")
3
+ spec := import("@platforma-sdk/workflow-tengo:pframes.spec")
4
+ validation := import("@platforma-sdk/workflow-tengo:validation")
5
+ slices := import("@platforma-sdk/workflow-tengo:slices")
6
+ canonical := import("@platforma-sdk/workflow-tengo:canonical")
7
+
8
+ _AXIS_SCHEMA := {
9
+
10
+ `column`: `string`,
11
+
12
+ `spec`: spec.P_AXIS_SPEC_SCHEMA
13
+ }
14
+
15
+ _COLUMN_SCHEMA := {
16
+
17
+ `column`: `string`,
18
+
19
+ `spec`: spec.P_COLUMN_OWN_SPEC_SCHEMA
20
+ }
21
+
22
+ _PARAMS_SCHEMA := {
23
+
24
+
25
+ `axes`: [_AXIS_SCHEMA],
26
+
27
+ `columns`: [_COLUMN_SCHEMA],
28
+
29
+
30
+ `partitionKeyLength,?` : `number`,
31
+
32
+
33
+ `strict,?` : `bool`
34
+ }
35
+
36
+ validateParams := func(params) {
37
+ validation.assertType(params, _PARAMS_SCHEMA, "Invalid params structure")
38
+
39
+ columns := {}
40
+ ll.assert(len(params.axes) > 0, "At least one axis is required")
41
+ for entry in params.axes {
42
+ if !is_undefined(columns[entry.column]) {
43
+ ll.assert(false, "Column (%s) used more then once which is prohibited", entry.column)
44
+ }
45
+ columns[entry.column] = 1
46
+ if !is_undefined(entry.spec.parentAxes) && len(entry.spec.parentAxes) > 0 {
47
+ for index in entry.spec.parentAxes {
48
+ ll.assert(
49
+ index < len(params.axes),
50
+ "Axis parent (%d) must be strictly less than the number of axes (%d)",
51
+ index,
52
+ len(params.axes)
53
+ )
54
+ }
55
+ }
56
+ }
57
+ ll.assert(len(params.columns) > 0, "At least one column is required")
58
+ for entry in params.columns {
59
+ if !is_undefined(columns[entry.column]) {
60
+ ll.assert(false, "Column (%s) used more then once which is prohibited", entry.column)
61
+ }
62
+ columns[entry.column] = 1
63
+ if !is_undefined(entry.spec.parentAxes) && len(entry.spec.parentAxes) > 0 {
64
+ for index in entry.spec.parentAxes {
65
+ ll.assert(
66
+ index < len(params.axes),
67
+ "Column parent (%d) must be strictly less than the number of axes (%d)",
68
+ index,
69
+ len(params.axes)
70
+ )
71
+ }
72
+ }
73
+ }
74
+
75
+
76
+ ll.assert(
77
+ params.partitionKeyLength == undefined ||
78
+ (params.partitionKeyLength >= 0 && params.partitionKeyLength < len(params.axes)),
79
+ "partitionKeyLength (%d) must be strictly less than the number of axes (%d)",
80
+ params.partitionKeyLength,
81
+ len(params.axes)
82
+ )
83
+ }
84
+
85
+ unique := func(list) {
86
+ uniqueMap := {}
87
+ for _, item in list {
88
+ uniqueMap[item] = item
89
+ }
90
+ uniqueList := []
91
+ for _, item in uniqueMap {
92
+ uniqueList = append(uniqueList, item)
93
+ }
94
+ return uniqueList
95
+ }
96
+
97
+ makeFrameSpecs := func(params) {
98
+ axes := []
99
+ for entry in params.axes {
100
+ spec := {
101
+ type: entry.spec.type,
102
+ name: entry.spec.name
103
+ }
104
+ if !is_undefined(entry.spec.domain) && len(entry.spec.domain) > 0 {
105
+ spec.domain = entry.spec.domain
106
+ }
107
+ if !is_undefined(entry.spec.annotations) && len(entry.spec.annotations) > 0 {
108
+ spec.annotations = entry.spec.annotations
109
+ }
110
+ if !is_undefined(entry.spec.parentAxes) && len(entry.spec.parentAxes) > 0 {
111
+ spec.parentAxes = unique(entry.spec.parentAxes)
112
+ }
113
+ axes = append(axes, spec)
114
+ }
115
+ columns := {}
116
+ for entry in params.columns {
117
+ spec := {
118
+ kind: constants.KIND_P_COLUMN,
119
+ axesSpec: axes,
120
+ valueType: entry.spec.valueType,
121
+ name: entry.spec.name
122
+ }
123
+ if !is_undefined(entry.spec.domain) && len(entry.spec.domain) > 0 {
124
+ spec.domain = entry.spec.domain
125
+ }
126
+ if !is_undefined(entry.spec.annotations) && len(entry.spec.annotations) > 0 {
127
+ spec.annotations = entry.spec.annotations
128
+ }
129
+ if !is_undefined(entry.spec.parentAxes) && len(entry.spec.parentAxes) > 0 {
130
+ spec.parentAxes = unique(entry.spec.parentAxes)
131
+ }
132
+ columns[entry.column] = spec
133
+ }
134
+ return columns
135
+ }
136
+
137
+ makeWriteFrameStep := func(frameName, dfName, params) {
138
+ step := {
139
+ type: "write_frame",
140
+ inputTable: dfName,
141
+ frameName: frameName,
142
+ axes: slices.map(params.axes, func(entry) {
143
+ return {
144
+ column: entry.column,
145
+ type: entry.spec.type
146
+ }
147
+ }),
148
+ columns: slices.map(params.columns, func(entry) {
149
+ return {
150
+ column: entry.column,
151
+ type: entry.spec.valueType
152
+ }
153
+ }),
154
+ partitionKeyLength: 0,
155
+ strict: false
156
+ }
157
+ if !is_undefined(params.partitionKeyLength) {
158
+ step.partitionKeyLength = params.partitionKeyLength
159
+ }
160
+ if !is_undefined(params.strict) {
161
+ step.strict = params.strict
162
+ }
163
+ return step
164
+ }
165
+
166
+ makeFrameColumnRef := func(frameName, columnId) {
167
+ return canonical.encode({
168
+ frame: frameName,
169
+ columnId: columnId
170
+ })
171
+ }
172
+
173
+ export ll.toStrict({
174
+ validateParams: validateParams,
175
+ makeFrameSpecs: makeFrameSpecs,
176
+ makeWriteFrameStep: makeWriteFrameStep,
177
+ makeFrameColumnRef: makeFrameColumnRef
178
+ })
@@ -206,6 +206,20 @@ _validateJson := func(js, schema, keyPath, ...stack) {
206
206
 
207
207
 
208
208
 
209
+ if is_array(schema) && len(schema) > 0 && schema[0] == "and" {
210
+ for i, elem in schema[1:] {
211
+ r := _validateJson(js, elem, keyPath, stack...)
212
+ if isFail(r) {
213
+ return r
214
+ }
215
+ }
216
+
217
+ return success
218
+ }
219
+
220
+
221
+
222
+
209
223
  if is_array(schema) {
210
224
  if !is_array(js) {
211
225
  return fail(keyPath, "js %q must be array", js)
Binary file
Binary file
Binary file
Binary file
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@platforma-sdk/workflow-tengo",
3
- "version": "5.2.0",
3
+ "version": "5.2.2",
4
4
  "description": "Tengo SDK library",
5
5
  "type": "module",
6
6
  "types": "./dist/index.d.ts",
@@ -18,11 +18,11 @@
18
18
  "dependencies": {
19
19
  "@milaboratories/software-pframes-conv": "2.2.7",
20
20
  "@platforma-open/milaboratories.software-small-binaries": "^1.15.21",
21
- "@platforma-open/milaboratories.software-ptabler": "1.12.2",
22
- "@platforma-open/milaboratories.software-ptexter": "1.1.1"
21
+ "@platforma-open/milaboratories.software-ptexter": "1.1.1",
22
+ "@platforma-open/milaboratories.software-ptabler": "1.12.4"
23
23
  },
24
24
  "devDependencies": {
25
- "@platforma-sdk/tengo-builder": "2.1.19"
25
+ "@platforma-sdk/tengo-builder": "2.1.21"
26
26
  },
27
27
  "scripts": {
28
28
  "type-check": "pl-tengo check",