@platforma-sdk/workflow-tengo 5.4.4 → 5.5.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.
- package/dist/index.cjs +1 -0
- package/dist/index.d.ts +1 -1
- package/dist/index.js +1 -0
- package/dist/tengo/lib/annotations.lib.tengo +92 -0
- package/dist/tengo/lib/annotations.util.lib.tengo +117 -0
- package/dist/tengo/lib/pframes.bundle.lib.tengo +83 -17
- package/dist/tengo/lib/pframes.lib.tengo +1 -0
- package/dist/tengo/lib/pframes.spec.lib.tengo +40 -24
- package/dist/tengo/lib/pframes.util.lib.tengo +1 -1
- package/dist/tengo/lib/pt.expression-traverse.lib.tengo +198 -0
- package/dist/tengo/lib/pt.lib.tengo +35 -24
- package/dist/tengo/lib/pt.util.lib.tengo +2 -4
- package/dist/tengo/lib/slices.lib.tengo +17 -7
- package/dist/tengo/tpl/annotations.compute.plj.gz +0 -0
- package/dist/tengo/tpl/anonymize.deanonymize.plj.gz +0 -0
- package/dist/tengo/tpl/anonymize.impl.plj.gz +0 -0
- package/dist/tengo/tpl/check_network.create_workdir_from_storage.plj.gz +0 -0
- package/dist/tengo/tpl/check_network.download_blob.plj.gz +0 -0
- package/dist/tengo/tpl/check_network.run_hello_world.plj.gz +0 -0
- package/dist/tengo/tpl/check_network.run_hello_world_py.plj.gz +0 -0
- package/dist/tengo/tpl/check_network.upload_blob.plj.gz +0 -0
- package/dist/tengo/tpl/check_network.upload_template.plj.gz +0 -0
- package/dist/tengo/tpl/check_network.workdir_type_name_extractor.plj.gz +0 -0
- package/dist/tengo/tpl/exec.R.create-renv.plj.gz +0 -0
- package/dist/tengo/tpl/exec.conda.create-env.plj.gz +0 -0
- package/dist/tengo/tpl/exec.delayed-compute-allocation.impl.plj.gz +0 -0
- package/dist/tengo/tpl/exec.exec-impl.plj.gz +0 -0
- package/dist/tengo/tpl/exec.exec.plj.gz +0 -0
- package/dist/tengo/tpl/exec.load-software.impl.plj.gz +0 -0
- package/dist/tengo/tpl/exec.monetization_info.plj.gz +0 -0
- package/dist/tengo/tpl/exec.monetization_to_json.plj.gz +0 -0
- package/dist/tengo/tpl/exec.python.create-venv.plj.gz +0 -0
- package/dist/tengo/tpl/exec.python.install-deps.plj.gz +0 -0
- package/dist/tengo/tpl/exec.python.venv-bin-path.plj.gz +0 -0
- package/dist/tengo/tpl/ll.get-field.plj.gz +0 -0
- package/dist/tengo/tpl/pframes.export-multiple-pcolumns.plj.gz +0 -0
- package/dist/tengo/tpl/pframes.export-parquet-pcolumn-chunk.plj.gz +0 -0
- package/dist/tengo/tpl/pframes.export-pframe-for-ui.plj.gz +0 -0
- package/dist/tengo/tpl/pframes.export-pframe.plj.gz +0 -0
- package/dist/tengo/tpl/pframes.export-single-pcolumn.plj.gz +0 -0
- package/dist/tengo/tpl/pframes.import-dir.plj.gz +0 -0
- package/dist/tengo/tpl/pframes.parse-to-json.plj.gz +0 -0
- package/dist/tengo/tpl/pframes.process-pcolumn-data.plj.gz +0 -0
- package/dist/tengo/tpl/pframes.remove-nulls.plj.gz +0 -0
- package/dist/tengo/tpl/pframes.slice-data.plj.gz +0 -0
- package/dist/tengo/tpl/pframes.xsv-export-pframe.plj.gz +0 -0
- package/dist/tengo/tpl/pframes.xsv-import-if-not-null.plj.gz +0 -0
- package/dist/tengo/tpl/pframes.xsv-import-map.plj.gz +0 -0
- package/dist/tengo/tpl/pt.import-dir.plj.gz +0 -0
- package/dist/tengo/tpl/pt.workflow-run.plj.gz +0 -0
- package/dist/tengo/tpl/waiter.waiter.plj.gz +0 -0
- package/dist/tengo/tpl/workdir.save.plj.gz +0 -0
- package/dist/tengo/tpl/workflow.build-ctx.plj.gz +0 -0
- package/dist/tengo/tpl/workflow.query-anchored.plj.gz +0 -0
- package/package.json +4 -4
package/dist/index.cjs
CHANGED
|
@@ -31,6 +31,7 @@ module.exports = { Templates: {
|
|
|
31
31
|
'exec.load-software.impl': { type: 'from-file', path: require.resolve('./tengo/tpl/exec.load-software.impl.plj.gz') },
|
|
32
32
|
'exec.exec': { type: 'from-file', path: require.resolve('./tengo/tpl/exec.exec.plj.gz') },
|
|
33
33
|
'pt.workflow-run': { type: 'from-file', path: require.resolve('./tengo/tpl/pt.workflow-run.plj.gz') },
|
|
34
|
+
'annotations.compute': { type: 'from-file', path: require.resolve('./tengo/tpl/annotations.compute.plj.gz') },
|
|
34
35
|
'check_network.run_hello_world': { type: 'from-file', path: require.resolve('./tengo/tpl/check_network.run_hello_world.plj.gz') },
|
|
35
36
|
'check_network.run_hello_world_py': { type: 'from-file', path: require.resolve('./tengo/tpl/check_network.run_hello_world_py.plj.gz') },
|
|
36
37
|
'pframes.xsv-export-pframe': { type: 'from-file', path: require.resolve('./tengo/tpl/pframes.xsv-export-pframe.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-parquet-pcolumn-chunk" | "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.conda.create-env" | "exec.exec-impl" | "exec.monetization_info" | "exec.python.create-venv" | "pframes.parse-to-json" | "exec.load-software.impl" | "exec.exec" | "pt.workflow-run" | "check_network.run_hello_world" | "check_network.run_hello_world_py" | "pframes.xsv-export-pframe" | "pframes.xsv-import-if-not-null" | "pframes.xsv-import-map" | "pframes.process-pcolumn-data" | "pframes.export-pframe" | "pframes.export-pframe-for-ui";
|
|
2
|
+
declare type TplName = "ll.get-field" | "pframes.export-parquet-pcolumn-chunk" | "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.conda.create-env" | "exec.exec-impl" | "exec.monetization_info" | "exec.python.create-venv" | "pframes.parse-to-json" | "exec.load-software.impl" | "exec.exec" | "pt.workflow-run" | "annotations.compute" | "check_network.run_hello_world" | "check_network.run_hello_world_py" | "pframes.xsv-export-pframe" | "pframes.xsv-import-if-not-null" | "pframes.xsv-import-map" | "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
|
@@ -32,6 +32,7 @@ export const Templates = {
|
|
|
32
32
|
'exec.load-software.impl': { type: 'from-file', path: resolve(import.meta.dirname, './tengo/tpl/exec.load-software.impl.plj.gz') },
|
|
33
33
|
'exec.exec': { type: 'from-file', path: resolve(import.meta.dirname, './tengo/tpl/exec.exec.plj.gz') },
|
|
34
34
|
'pt.workflow-run': { type: 'from-file', path: resolve(import.meta.dirname, './tengo/tpl/pt.workflow-run.plj.gz') },
|
|
35
|
+
'annotations.compute': { type: 'from-file', path: resolve(import.meta.dirname, './tengo/tpl/annotations.compute.plj.gz') },
|
|
35
36
|
'check_network.run_hello_world': { type: 'from-file', path: resolve(import.meta.dirname, './tengo/tpl/check_network.run_hello_world.plj.gz') },
|
|
36
37
|
'check_network.run_hello_world_py': { type: 'from-file', path: resolve(import.meta.dirname, './tengo/tpl/check_network.run_hello_world_py.plj.gz') },
|
|
37
38
|
'pframes.xsv-export-pframe': { type: 'from-file', path: resolve(import.meta.dirname, './tengo/tpl/pframes.xsv-export-pframe.plj.gz') },
|
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
ll := import("@platforma-sdk/workflow-tengo:ll")
|
|
2
|
+
wf := import("@platforma-sdk/workflow-tengo:workflow")
|
|
3
|
+
util := import("@platforma-sdk/workflow-tengo:annotations.util")
|
|
4
|
+
render := import("@platforma-sdk/workflow-tengo:render")
|
|
5
|
+
assets := import("@platforma-sdk/workflow-tengo:assets")
|
|
6
|
+
slices := import("@platforma-sdk/workflow-tengo:slices")
|
|
7
|
+
validation := import("@platforma-sdk/workflow-tengo:validation")
|
|
8
|
+
|
|
9
|
+
compute := assets.importTemplate("@platforma-sdk/workflow-tengo:annotations.compute")
|
|
10
|
+
|
|
11
|
+
_STEP_SCHEMA := {
|
|
12
|
+
type: "string",
|
|
13
|
+
name: "string",
|
|
14
|
+
value: { "any": "any" }
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
_STEP_SCHEMAS := [_STEP_SCHEMA]
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
|
|
31
|
+
|
|
32
|
+
|
|
33
|
+
|
|
34
|
+
|
|
35
|
+
computeAnnotations := func(inputs) {
|
|
36
|
+
if len(inputs.steps) == 0 {
|
|
37
|
+
return {}
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
validation.assertType(inputs.steps, _STEP_SCHEMAS)
|
|
41
|
+
|
|
42
|
+
steps := inputs.steps
|
|
43
|
+
title := inputs.title
|
|
44
|
+
inputAnchor := inputs.inputAnchor
|
|
45
|
+
resultAxesSpec := inputs.resultAxesSpec
|
|
46
|
+
resultColumnSpec := util.prepareResultColumnSpec(inputs)
|
|
47
|
+
shouldComputeFilters := is_bool(inputs.shouldComputeFilters) ? inputs.shouldComputeFilters : false
|
|
48
|
+
|
|
49
|
+
columnsFromExpressions := util.extractColumnsFromExpressions(slices.map(steps, func(step) { return step.value }))
|
|
50
|
+
additionalColumns := is_array(inputs.additionalColumns) ? inputs.additionalColumns : []
|
|
51
|
+
columns := columnsFromExpressions + additionalColumns
|
|
52
|
+
|
|
53
|
+
bundleBuilder := wf.createPBundleBuilder()
|
|
54
|
+
bundleBuilder.addAnchor("main", inputAnchor)
|
|
55
|
+
for column in columns { bundleBuilder.addById(column) }
|
|
56
|
+
columnBundle := bundleBuilder.build()
|
|
57
|
+
|
|
58
|
+
result := render.create(compute, {
|
|
59
|
+
columnBundle: columnBundle,
|
|
60
|
+
params: {
|
|
61
|
+
steps: steps,
|
|
62
|
+
columns: columns,
|
|
63
|
+
resultColumnSpec: resultColumnSpec,
|
|
64
|
+
shouldComputeFilters: shouldComputeFilters
|
|
65
|
+
}
|
|
66
|
+
})
|
|
67
|
+
|
|
68
|
+
annotationsData := result.output("annotationsData")
|
|
69
|
+
annotationsColumn := { column: util.ANNOTATION_HEADER, data: annotationsData, spec: resultColumnSpec }
|
|
70
|
+
|
|
71
|
+
idx := 0
|
|
72
|
+
annotationsFiltersColumns := shouldComputeFilters ? slices.map(steps, func(v) {
|
|
73
|
+
idx += 1
|
|
74
|
+
column := util.encodeColumnName(v.name, idx)
|
|
75
|
+
filterData := result.resolveOutput(["filtersData", column])
|
|
76
|
+
filterSpec := util.createFilterColumnSpec(resultColumnSpec, column, v.name)
|
|
77
|
+
return {
|
|
78
|
+
column: v.name,
|
|
79
|
+
data: filterData,
|
|
80
|
+
spec: filterSpec
|
|
81
|
+
}
|
|
82
|
+
}) : []
|
|
83
|
+
|
|
84
|
+
return {
|
|
85
|
+
annotationsColumn: annotationsColumn,
|
|
86
|
+
annotationsFiltersColumns: annotationsFiltersColumns
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
export ll.toStrict({
|
|
91
|
+
computeAnnotations: computeAnnotations
|
|
92
|
+
})
|
|
@@ -0,0 +1,117 @@
|
|
|
1
|
+
hex := import("hex")
|
|
2
|
+
rand := import("rand")
|
|
3
|
+
text := import("text")
|
|
4
|
+
|
|
5
|
+
ll := import("@platforma-sdk/workflow-tengo:ll")
|
|
6
|
+
wf := import("@platforma-sdk/workflow-tengo:workflow")
|
|
7
|
+
sets := import("@platforma-sdk/workflow-tengo:sets")
|
|
8
|
+
maps := import("@platforma-sdk/workflow-tengo:maps")
|
|
9
|
+
slices := import("@platforma-sdk/workflow-tengo:slices")
|
|
10
|
+
|
|
11
|
+
pSpec := import("@platforma-sdk/workflow-tengo:pframes.spec")
|
|
12
|
+
|
|
13
|
+
expr := import("@platforma-sdk/workflow-tengo:pt.expression-traverse")
|
|
14
|
+
|
|
15
|
+
ANNOTATION_HEADER := "annotation"
|
|
16
|
+
|
|
17
|
+
createFilterFrameName := func(colName) {
|
|
18
|
+
return colName + "_filter_frame"
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
createFilterColumnSpec := func(columnSpec, colName, label) {
|
|
22
|
+
return maps.deepMerge(columnSpec, {
|
|
23
|
+
kind: "PColumn",
|
|
24
|
+
valueType: "Int",
|
|
25
|
+
name: columnSpec.name + "/filter/" + colName,
|
|
26
|
+
annotations: {
|
|
27
|
+
"pl7.app/label": columnSpec.annotations["pl7.app/label"] + "/" + label,
|
|
28
|
+
"pl7.app/isSubset": "true"
|
|
29
|
+
}
|
|
30
|
+
});
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
ENCODE_SEPARATOR := "_encoded_"
|
|
34
|
+
|
|
35
|
+
encodeColumnName := func(name, idx) {
|
|
36
|
+
return hex.encode(name) + ENCODE_SEPARATOR + string(idx)
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
decodeColumnName := func(encoded) {
|
|
40
|
+
parts := text.split(encoded, ENCODE_SEPARATOR)
|
|
41
|
+
if len(parts) != 2 {
|
|
42
|
+
ll.panic("Invalid encoded column name: " + encoded)
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
return string(hex.decode(parts[0]))
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
addColumnsFromExpression := func(expression, columnsSet) {
|
|
49
|
+
expr.expressionTraverse(expression, func(node) {
|
|
50
|
+
if node.type == "col" {
|
|
51
|
+
|
|
52
|
+
if is_string(node.name) {
|
|
53
|
+
sets.add(columnsSet, node.name)
|
|
54
|
+
}
|
|
55
|
+
return
|
|
56
|
+
}
|
|
57
|
+
})
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
mapExpressionColumnNames := func(expression, transformFn) {
|
|
61
|
+
copy := maps.clone(expression)
|
|
62
|
+
|
|
63
|
+
expr.expressionTraverse(copy, func(node) {
|
|
64
|
+
if node.type == "col" {
|
|
65
|
+
|
|
66
|
+
if is_string(node.name) {
|
|
67
|
+
node.name = transformFn(node.name)
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
})
|
|
71
|
+
|
|
72
|
+
return copy
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
extractColumnsFromExpressions := func(expressions) {
|
|
76
|
+
columnsSet := {}
|
|
77
|
+
|
|
78
|
+
for expression in expressions {
|
|
79
|
+
addColumnsFromExpression(expression, columnsSet)
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
return sets.toSlice(columnsSet)
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
prepareResultColumnSpec := func(args) {
|
|
86
|
+
blockId := wf.getBlockId()
|
|
87
|
+
|
|
88
|
+
steps := slices.filter([args.trace, {
|
|
89
|
+
id: blockId,
|
|
90
|
+
type: "milaboratories.annotation",
|
|
91
|
+
label: args.title,
|
|
92
|
+
importance: 20
|
|
93
|
+
}], func(v) { return !is_undefined(v) })
|
|
94
|
+
|
|
95
|
+
trace := pSpec.makeTrace(steps...)
|
|
96
|
+
|
|
97
|
+
return trace.inject({
|
|
98
|
+
kind: "PColumn",
|
|
99
|
+
valueType: "String",
|
|
100
|
+
name: "pl7.app/annotation/result",
|
|
101
|
+
domain: { "pl7.app/annotationRunId": blockId },
|
|
102
|
+
axesSpec: args.resultAxesSpec,
|
|
103
|
+
annotations: { "pl7.app/label": args.title }
|
|
104
|
+
})
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
export ll.toStrict({
|
|
108
|
+
ANNOTATION_HEADER: ANNOTATION_HEADER,
|
|
109
|
+
|
|
110
|
+
encodeColumnName: encodeColumnName,
|
|
111
|
+
decodeColumnName: decodeColumnName,
|
|
112
|
+
createFilterFrameName: createFilterFrameName,
|
|
113
|
+
prepareResultColumnSpec: prepareResultColumnSpec,
|
|
114
|
+
createFilterColumnSpec: createFilterColumnSpec,
|
|
115
|
+
mapExpressionColumnNames: mapExpressionColumnNames,
|
|
116
|
+
extractColumnsFromExpressions: extractColumnsFromExpressions
|
|
117
|
+
})
|
|
@@ -394,11 +394,6 @@ pColumnBundleUnmarshaller := func() {
|
|
|
394
394
|
}
|
|
395
395
|
}
|
|
396
396
|
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
axisHeaderOverrides := []
|
|
401
|
-
|
|
402
397
|
get := func(resultKey) {
|
|
403
398
|
if !is_string(resultKey) {
|
|
404
399
|
resultKey = canonical.encode(resultKey)
|
|
@@ -435,7 +430,11 @@ pColumnBundleUnmarshaller := func() {
|
|
|
435
430
|
|
|
436
431
|
|
|
437
432
|
|
|
438
|
-
|
|
433
|
+
|
|
434
|
+
|
|
435
|
+
|
|
436
|
+
getColumn: func(resultKey, ...options) {
|
|
437
|
+
options = is_array(options) && is_map(options[0]) ? options[0] : {}
|
|
439
438
|
processedId := processColumnId(resultKey)
|
|
440
439
|
columnKeyForPool := processedId.columnKey // Key to fetch from the pool (source for filtered)
|
|
441
440
|
columnAxisFilters := processedId.axisFilters // Filters from FilteredPColumnId
|
|
@@ -450,7 +449,7 @@ pColumnBundleUnmarshaller := func() {
|
|
|
450
449
|
ll.panic("Cannot get a single column from a multi-column result: %v. Use getColumns() instead.", columnKeyForPool)
|
|
451
450
|
}
|
|
452
451
|
|
|
453
|
-
currentSpec := bundle.getSpec(resultKey) // getSpec already handles filtering for the spec
|
|
452
|
+
currentSpec := bundle.getSpec(resultKey, options) // getSpec already handles filtering for the spec
|
|
454
453
|
currentData := result.data
|
|
455
454
|
|
|
456
455
|
|
|
@@ -458,7 +457,9 @@ pColumnBundleUnmarshaller := func() {
|
|
|
458
457
|
sliceDataTemplate := assets.importTemplate("@platforma-sdk/workflow-tengo:pframes.slice-data")
|
|
459
458
|
currentData = render.createEphemeral(sliceDataTemplate, {
|
|
460
459
|
data: currentData,
|
|
461
|
-
slicingParams: smart.createJsonResource(columnAxisFilters)
|
|
460
|
+
slicingParams: smart.createJsonResource(columnAxisFilters),
|
|
461
|
+
keepColumns: options.keepColumns,
|
|
462
|
+
ignoreNonPartitioned: options.ignoreNonPartitioned
|
|
462
463
|
}).output("result")
|
|
463
464
|
}
|
|
464
465
|
|
|
@@ -475,11 +476,16 @@ pColumnBundleUnmarshaller := func() {
|
|
|
475
476
|
|
|
476
477
|
|
|
477
478
|
|
|
478
|
-
|
|
479
|
+
|
|
480
|
+
|
|
481
|
+
getSpec: func(id, ...options) {
|
|
479
482
|
processedId := processColumnId(id)
|
|
480
483
|
columnKey := processedId.columnKey
|
|
481
484
|
columnAxisFilters := processedId.axisFilters // Filters from FilteredPColumnId
|
|
482
485
|
|
|
486
|
+
options = is_array(options) && is_map(options[0]) ? options[0] : {}
|
|
487
|
+
keepColumns := is_bool(options.keepColumns) ? options.keepColumns : false
|
|
488
|
+
|
|
483
489
|
if is_undefined(pool[columnKey]) {
|
|
484
490
|
ll.panic("Column not found in bundle: " + columnKey)
|
|
485
491
|
}
|
|
@@ -492,9 +498,11 @@ pColumnBundleUnmarshaller := func() {
|
|
|
492
498
|
baseSpec := result.spec
|
|
493
499
|
|
|
494
500
|
|
|
495
|
-
if !is_undefined(columnAxisFilters) && len(columnAxisFilters) > 0 {
|
|
496
|
-
prepared := pSpec.
|
|
497
|
-
|
|
501
|
+
if !keepColumns && !is_undefined(columnAxisFilters) && len(columnAxisFilters) > 0 {
|
|
502
|
+
prepared := pSpec.prepareAxisFiltersAndAxesSpec(columnAxisFilters, baseSpec.axesSpec)
|
|
503
|
+
clonedSpec := maps.clone(baseSpec)
|
|
504
|
+
clonedSpec.axesSpec = prepared.axesSpec
|
|
505
|
+
return clonedSpec
|
|
498
506
|
} else {
|
|
499
507
|
|
|
500
508
|
return baseSpec
|
|
@@ -505,15 +513,73 @@ pColumnBundleUnmarshaller := func() {
|
|
|
505
513
|
|
|
506
514
|
|
|
507
515
|
|
|
516
|
+
getColumnKeys: func() {
|
|
517
|
+
return maps.getKeys(pool)
|
|
518
|
+
},
|
|
519
|
+
|
|
520
|
+
|
|
521
|
+
|
|
522
|
+
|
|
523
|
+
|
|
524
|
+
|
|
525
|
+
|
|
526
|
+
|
|
508
527
|
|
|
509
528
|
|
|
510
|
-
|
|
511
|
-
|
|
512
|
-
|
|
529
|
+
getAxesSpec: func(...columnIds) {
|
|
530
|
+
|
|
531
|
+
uniqueAxesMap := {}
|
|
532
|
+
allAxes := []
|
|
533
|
+
|
|
534
|
+
|
|
535
|
+
processAxesSpec := func(spec) {
|
|
536
|
+
if !is_undefined(spec) && !is_undefined(spec.axesSpec) {
|
|
537
|
+
for axis in spec.axesSpec {
|
|
538
|
+
axisKey := pSpec.getAxisId(axis)
|
|
539
|
+
|
|
540
|
+
|
|
541
|
+
if is_undefined(uniqueAxesMap[axisKey]) {
|
|
542
|
+
uniqueAxesMap[axisKey] = true
|
|
543
|
+
allAxes = append(allAxes, axis)
|
|
544
|
+
}
|
|
545
|
+
}
|
|
546
|
+
}
|
|
547
|
+
}
|
|
548
|
+
|
|
549
|
+
|
|
550
|
+
processResult := func(result) {
|
|
551
|
+
if is_array(result) {
|
|
552
|
+
|
|
553
|
+
for _, col in result {
|
|
554
|
+
processAxesSpec(col.spec)
|
|
555
|
+
}
|
|
556
|
+
} else {
|
|
557
|
+
|
|
558
|
+
processAxesSpec(result.spec)
|
|
559
|
+
}
|
|
560
|
+
}
|
|
561
|
+
|
|
562
|
+
|
|
563
|
+
if len(columnIds) > 0 {
|
|
564
|
+
for id in columnIds {
|
|
565
|
+
processedId := processColumnId(id)
|
|
566
|
+
columnKey := processedId.columnKey
|
|
567
|
+
|
|
568
|
+
result := pool[columnKey]
|
|
569
|
+
if is_undefined(result) {
|
|
570
|
+
ll.panic("Column not found: %v. Pool: %v", columnKey, maps.getKeys(pool))
|
|
571
|
+
}
|
|
572
|
+
|
|
573
|
+
processResult(result)
|
|
574
|
+
}
|
|
575
|
+
} else {
|
|
576
|
+
|
|
577
|
+
for result in pool {
|
|
578
|
+
processResult(result)
|
|
579
|
+
}
|
|
513
580
|
}
|
|
514
581
|
|
|
515
|
-
|
|
516
|
-
return bundle
|
|
582
|
+
return allAxes
|
|
517
583
|
}
|
|
518
584
|
})
|
|
519
585
|
|
|
@@ -165,6 +165,7 @@ transformTsvSettings := func(settings) {
|
|
|
165
165
|
|
|
166
166
|
|
|
167
167
|
exportFrame := func(pf) {
|
|
168
|
+
ll.assert(!is_undefined(pf), "pframe reference is required")
|
|
168
169
|
return render.createEphemeral(assets.importTemplate("@platforma-sdk/workflow-tengo:pframes.export-pframe"), { pf: pf }).output("result")
|
|
169
170
|
}
|
|
170
171
|
|
|
@@ -10,6 +10,7 @@ sets := import("@platforma-sdk/workflow-tengo:sets")
|
|
|
10
10
|
slices := import("@platforma-sdk/workflow-tengo:slices")
|
|
11
11
|
validation := import("@platforma-sdk/workflow-tengo:validation")
|
|
12
12
|
json := import("json")
|
|
13
|
+
canonical := import("@platforma-sdk/workflow-tengo:canonical")
|
|
13
14
|
|
|
14
15
|
|
|
15
16
|
|
|
@@ -234,27 +235,33 @@ _TRACE_SCHEMA := [_TRACE_STEP_SCHEMA]
|
|
|
234
235
|
|
|
235
236
|
|
|
236
237
|
|
|
237
|
-
makeTrace := func(
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
currentTraceContent := undefined
|
|
242
|
-
if is_string(input) {
|
|
243
|
-
currentTraceContent = input
|
|
244
|
-
} else if is_string(input[A_TRACE]) {
|
|
245
|
-
currentTraceContent = input[A_TRACE]
|
|
246
|
-
} else if is_string(input["annotations"][A_TRACE]) {
|
|
247
|
-
currentTraceContent = input["annotations"][A_TRACE]
|
|
248
|
-
}
|
|
238
|
+
makeTrace := func(...steps) {
|
|
239
|
+
ll.assert(len(steps) > 0, "makeTrace: expects at least one argument")
|
|
240
|
+
|
|
249
241
|
|
|
242
|
+
input := steps[0]
|
|
243
|
+
steps = steps[1:]
|
|
250
244
|
currentTrace := []
|
|
251
|
-
|
|
252
|
-
|
|
245
|
+
|
|
246
|
+
if (!validation.isValid(input, _TRACE_STEP_SCHEMA)) {
|
|
247
|
+
inputTraceContent := undefined
|
|
248
|
+
|
|
249
|
+
if is_string(input) {
|
|
250
|
+
inputTraceContent = input
|
|
251
|
+
} else if is_map(input) && is_string(input[A_TRACE]) {
|
|
252
|
+
inputTraceContent = input[A_TRACE]
|
|
253
|
+
} else if is_map(input["annotations"]) && is_map(input["annotations"]) && is_string(input["annotations"][A_TRACE]) {
|
|
254
|
+
inputTraceContent = input["annotations"][A_TRACE]
|
|
255
|
+
}
|
|
253
256
|
|
|
254
|
-
|
|
257
|
+
if !is_undefined(inputTraceContent) {
|
|
258
|
+
currentTrace = json.decode(inputTraceContent)
|
|
259
|
+
validation.assertType(currentTrace, _TRACE_SCHEMA)
|
|
260
|
+
}
|
|
255
261
|
}
|
|
256
262
|
|
|
257
263
|
if len(steps) > 0 {
|
|
264
|
+
validation.assertType(steps, _TRACE_SCHEMA)
|
|
258
265
|
currentTrace = append(currentTrace, steps...)
|
|
259
266
|
}
|
|
260
267
|
currentTraceStr := string(json.encode(currentTrace))
|
|
@@ -465,7 +472,7 @@ createSpecDistiller := func(specs) {
|
|
|
465
472
|
|
|
466
473
|
|
|
467
474
|
|
|
468
|
-
|
|
475
|
+
prepareAxisFiltersAndAxesSpec := func(rules, axesSpec) {
|
|
469
476
|
axisFilters := []
|
|
470
477
|
filteredIndices := {}
|
|
471
478
|
|
|
@@ -480,7 +487,7 @@ prepareAxisFilters := func(rules, spec) {
|
|
|
480
487
|
axisIdx := axisIdentifier
|
|
481
488
|
if is_string(axisIdentifier) {
|
|
482
489
|
axisIdx = -1
|
|
483
|
-
for idx, axis in
|
|
490
|
+
for idx, axis in axesSpec {
|
|
484
491
|
if axis.name == axisIdentifier {
|
|
485
492
|
axisIdx = idx
|
|
486
493
|
break
|
|
@@ -488,7 +495,7 @@ prepareAxisFilters := func(rules, spec) {
|
|
|
488
495
|
}
|
|
489
496
|
|
|
490
497
|
if axisIdx == -1 {
|
|
491
|
-
ll.panic("Cannot find axis with name '%v' in column spec %v", axisIdentifier,
|
|
498
|
+
ll.panic("Cannot find axis with name '%v' in column spec %v", axisIdentifier, axesSpec)
|
|
492
499
|
}
|
|
493
500
|
}
|
|
494
501
|
|
|
@@ -501,18 +508,16 @@ prepareAxisFilters := func(rules, spec) {
|
|
|
501
508
|
filteredIndices[axisIdx] = true
|
|
502
509
|
}
|
|
503
510
|
|
|
504
|
-
transformedSpec := maps.clone(spec)
|
|
505
511
|
newAxesSpec := []
|
|
506
|
-
for idx, axis in
|
|
512
|
+
for idx, axis in axesSpec {
|
|
507
513
|
if !filteredIndices[idx] {
|
|
508
514
|
newAxesSpec = append(newAxesSpec, maps.clone(axis))
|
|
509
515
|
}
|
|
510
516
|
}
|
|
511
|
-
transformedSpec.axesSpec = newAxesSpec
|
|
512
517
|
|
|
513
518
|
return {
|
|
514
519
|
axisFilters: axisFilters,
|
|
515
|
-
|
|
520
|
+
axesSpec: newAxesSpec
|
|
516
521
|
}
|
|
517
522
|
}
|
|
518
523
|
|
|
@@ -535,6 +540,15 @@ axisSpecToMatcher := func(axisSpec) {
|
|
|
535
540
|
return matcher
|
|
536
541
|
}
|
|
537
542
|
|
|
543
|
+
getAxisId := func(axisSpec) {
|
|
544
|
+
validation.assertType(axisSpec, P_AXIS_SPEC_SCHEMA)
|
|
545
|
+
return canonical.encode({
|
|
546
|
+
name: axisSpec.name,
|
|
547
|
+
type: axisSpec.type,
|
|
548
|
+
domain: axisSpec.domain
|
|
549
|
+
})
|
|
550
|
+
}
|
|
551
|
+
|
|
538
552
|
export ll.toStrict({
|
|
539
553
|
P_UNTYPED_SPEC_SCHEMA: P_UNTYPED_SPEC_SCHEMA,
|
|
540
554
|
P_AXIS_SPEC_SCHEMA: P_AXIS_SPEC_SCHEMA,
|
|
@@ -561,7 +575,9 @@ export ll.toStrict({
|
|
|
561
575
|
|
|
562
576
|
createSpecDistiller: createSpecDistiller,
|
|
563
577
|
|
|
564
|
-
|
|
565
|
-
axisSpecToMatcher: axisSpecToMatcher
|
|
578
|
+
prepareAxisFiltersAndAxesSpec: prepareAxisFiltersAndAxesSpec,
|
|
579
|
+
axisSpecToMatcher: axisSpecToMatcher,
|
|
580
|
+
|
|
581
|
+
getAxisId: getAxisId
|
|
566
582
|
})
|
|
567
583
|
|
|
@@ -436,7 +436,7 @@ pFrameToColumnsMap := func(pf) {
|
|
|
436
436
|
ll.assert(pf.checkResourceType(constants.RTYPE_P_FRAME), "Unexpected resource type: %v", pf.info().Type)
|
|
437
437
|
ll.assert(pf.info().InputsLocked, "PFrame resource does not have inputs locked")
|
|
438
438
|
for key, field in pf.inputs() {
|
|
439
|
-
addColumnSegment(key, field
|
|
439
|
+
addColumnSegment(key, field)
|
|
440
440
|
}
|
|
441
441
|
} else if ll.isMap(pf) {
|
|
442
442
|
for key, value in pf {
|
|
@@ -0,0 +1,198 @@
|
|
|
1
|
+
ll := import("@platforma-sdk/workflow-tengo:ll")
|
|
2
|
+
|
|
3
|
+
isExpression := func(v) {
|
|
4
|
+
return !is_undefined(v) && is_map(v) && is_string(v.type)
|
|
5
|
+
}
|
|
6
|
+
|
|
7
|
+
expressionTraverse := func(expression, visitFn) {
|
|
8
|
+
if (!isExpression(expression)) {
|
|
9
|
+
return
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
visitFn(expression)
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
t := expression.type
|
|
16
|
+
|
|
17
|
+
if t == "col" {
|
|
18
|
+
return
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
if t == "const" {
|
|
22
|
+
return
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
if t == "eq" || t == "neq" || t == "gt" || t == "lt" || t == "ge" || t == "le" {
|
|
27
|
+
expressionTraverse(expression.lhs, visitFn)
|
|
28
|
+
expressionTraverse(expression.rhs, visitFn)
|
|
29
|
+
return
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
|
|
33
|
+
if t == "and" || t == "or" {
|
|
34
|
+
if is_array(expression.operands) {
|
|
35
|
+
for _, op in expression.operands { expressionTraverse(op, visitFn) }
|
|
36
|
+
}
|
|
37
|
+
return
|
|
38
|
+
}
|
|
39
|
+
if t == "not" {
|
|
40
|
+
expressionTraverse(expression.value, visitFn)
|
|
41
|
+
return
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
|
|
45
|
+
if t == "plus" || t == "minus" || t == "multiply" || t == "truediv" || t == "floordiv" {
|
|
46
|
+
expressionTraverse(expression.lhs, visitFn)
|
|
47
|
+
expressionTraverse(expression.rhs, visitFn)
|
|
48
|
+
return
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
|
|
52
|
+
if t == "log10" || t == "log" || t == "log2" || t == "abs" || t == "sqrt" || t == "negate" || t == "floor" || t == "round" || t == "ceil" {
|
|
53
|
+
expressionTraverse(expression.value, visitFn)
|
|
54
|
+
return
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
if t == "cast" {
|
|
58
|
+
expressionTraverse(expression.value, visitFn)
|
|
59
|
+
return
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
|
|
63
|
+
if t == "is_na" || t == "is_not_na" {
|
|
64
|
+
expressionTraverse(expression.value, visitFn)
|
|
65
|
+
return
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
|
|
69
|
+
if t == "str_join" {
|
|
70
|
+
if is_array(expression.operands) {
|
|
71
|
+
for _, op in expression.operands { expressionTraverse(op, visitFn) }
|
|
72
|
+
}
|
|
73
|
+
return
|
|
74
|
+
}
|
|
75
|
+
if t == "hash" {
|
|
76
|
+
expressionTraverse(expression.value, visitFn)
|
|
77
|
+
return
|
|
78
|
+
}
|
|
79
|
+
if t == "to_upper" || t == "to_lower" || t == "str_len" {
|
|
80
|
+
expressionTraverse(expression.value, visitFn)
|
|
81
|
+
return
|
|
82
|
+
}
|
|
83
|
+
if t == "string_distance" {
|
|
84
|
+
expressionTraverse(expression.string1, visitFn)
|
|
85
|
+
expressionTraverse(expression.string2, visitFn)
|
|
86
|
+
return
|
|
87
|
+
}
|
|
88
|
+
if t == "fuzzy_string_filter" {
|
|
89
|
+
expressionTraverse(expression.value, visitFn)
|
|
90
|
+
if isExpression(expression.pattern) { expressionTraverse(expression.pattern, visitFn) }
|
|
91
|
+
return
|
|
92
|
+
}
|
|
93
|
+
if t == "when_then_otherwise" {
|
|
94
|
+
if is_array(expression.conditions) {
|
|
95
|
+
for _, c in expression.conditions {
|
|
96
|
+
|
|
97
|
+
if is_map(c) {
|
|
98
|
+
expressionTraverse(c.when, visitFn)
|
|
99
|
+
expressionTraverse(c.then, visitFn)
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
expressionTraverse(expression.otherwise, visitFn)
|
|
104
|
+
return
|
|
105
|
+
}
|
|
106
|
+
if t == "substring" {
|
|
107
|
+
expressionTraverse(expression.value, visitFn)
|
|
108
|
+
expressionTraverse(expression.start, visitFn)
|
|
109
|
+
if !is_undefined(expression.length) { expressionTraverse(expression.length, visitFn) }
|
|
110
|
+
if !is_undefined(expression.end) { expressionTraverse(expression.end, visitFn) }
|
|
111
|
+
return
|
|
112
|
+
}
|
|
113
|
+
if t == "str_replace" {
|
|
114
|
+
expressionTraverse(expression.value, visitFn)
|
|
115
|
+
if isExpression(expression.pattern) { expressionTraverse(expression.pattern, visitFn) }
|
|
116
|
+
if isExpression(expression.replacement) { expressionTraverse(expression.replacement, visitFn) }
|
|
117
|
+
return
|
|
118
|
+
}
|
|
119
|
+
if t == "str_contains" {
|
|
120
|
+
expressionTraverse(expression.value, visitFn)
|
|
121
|
+
if isExpression(expression.pattern) { expressionTraverse(expression.pattern, visitFn) }
|
|
122
|
+
return
|
|
123
|
+
}
|
|
124
|
+
if t == "str_starts_with" {
|
|
125
|
+
expressionTraverse(expression.value, visitFn)
|
|
126
|
+
if isExpression(expression.prefix) { expressionTraverse(expression.prefix, visitFn) }
|
|
127
|
+
return
|
|
128
|
+
}
|
|
129
|
+
if t == "str_ends_with" {
|
|
130
|
+
expressionTraverse(expression.value, visitFn)
|
|
131
|
+
if isExpression(expression.suffix) { expressionTraverse(expression.suffix, visitFn) }
|
|
132
|
+
return
|
|
133
|
+
}
|
|
134
|
+
if t == "str_contains_any" {
|
|
135
|
+
expressionTraverse(expression.value, visitFn)
|
|
136
|
+
return
|
|
137
|
+
}
|
|
138
|
+
if t == "str_count_matches" {
|
|
139
|
+
expressionTraverse(expression.value, visitFn)
|
|
140
|
+
if isExpression(expression.pattern) { expressionTraverse(expression.pattern, visitFn) }
|
|
141
|
+
return
|
|
142
|
+
}
|
|
143
|
+
if t == "str_extract" {
|
|
144
|
+
expressionTraverse(expression.value, visitFn)
|
|
145
|
+
if isExpression(expression.pattern) { expressionTraverse(expression.pattern, visitFn) }
|
|
146
|
+
return
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
|
|
150
|
+
if t == "min" || t == "max" {
|
|
151
|
+
if is_array(expression.operands) {
|
|
152
|
+
for _, op in expression.operands { expressionTraverse(op, visitFn) }
|
|
153
|
+
}
|
|
154
|
+
return
|
|
155
|
+
}
|
|
156
|
+
if t == "fill_null" || t == "fill_nan" {
|
|
157
|
+
expressionTraverse(expression.input, visitFn)
|
|
158
|
+
expressionTraverse(expression.fillValue, visitFn)
|
|
159
|
+
return
|
|
160
|
+
}
|
|
161
|
+
if t == "aggregate" {
|
|
162
|
+
expressionTraverse(expression.value, visitFn)
|
|
163
|
+
if is_array(expression.partitionBy) {
|
|
164
|
+
for _, p in expression.partitionBy { expressionTraverse(p, visitFn) }
|
|
165
|
+
}
|
|
166
|
+
return
|
|
167
|
+
}
|
|
168
|
+
if t == "rank" {
|
|
169
|
+
if is_array(expression.partitionBy) {
|
|
170
|
+
for _, p in expression.partitionBy { expressionTraverse(p, visitFn) }
|
|
171
|
+
}
|
|
172
|
+
if is_array(expression.orderBy) {
|
|
173
|
+
for _, o in expression.orderBy { expressionTraverse(o, visitFn) }
|
|
174
|
+
}
|
|
175
|
+
return
|
|
176
|
+
}
|
|
177
|
+
if t == "cumsum" {
|
|
178
|
+
expressionTraverse(expression.value, visitFn)
|
|
179
|
+
if is_array(expression.additionalOrderBy) {
|
|
180
|
+
for _, o in expression.additionalOrderBy { expressionTraverse(o, visitFn) }
|
|
181
|
+
}
|
|
182
|
+
if is_array(expression.partitionBy) {
|
|
183
|
+
for _, p in expression.partitionBy { expressionTraverse(p, visitFn) }
|
|
184
|
+
}
|
|
185
|
+
return
|
|
186
|
+
}
|
|
187
|
+
if t == "struct_field" {
|
|
188
|
+
expressionTraverse(expression.struct, visitFn)
|
|
189
|
+
return
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
ll.panic("Unknown expression type: " + t)
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
export ll.toStrict({
|
|
196
|
+
expressionTraverse: expressionTraverse,
|
|
197
|
+
isExpression: isExpression
|
|
198
|
+
})
|
|
@@ -58,7 +58,6 @@ _newDataFrameGroupBy := undefined
|
|
|
58
58
|
|
|
59
59
|
workflowCounter := 0
|
|
60
60
|
|
|
61
|
-
|
|
62
61
|
_validateColumn := func(name, columnStruct) {
|
|
63
62
|
ll.assert(is_string(name), "column name must be a string")
|
|
64
63
|
ll.assert(is_map(columnStruct), "columnStruct must be a map")
|
|
@@ -646,25 +645,35 @@ workflow := func() {
|
|
|
646
645
|
|
|
647
646
|
|
|
648
647
|
|
|
649
|
-
frameFromColumnBundle: func(bundle, options) {
|
|
648
|
+
frameFromColumnBundle: func(bundle, ...options) {
|
|
649
|
+
options = len(options) > 0 && is_map(options[0]) ? options[0] : {}
|
|
650
650
|
util.validateParamsForFrameFromColumnBundle(options)
|
|
651
651
|
|
|
652
|
+
columns := is_array(options.columns) ? options.columns : bundle.getColumnKeys()
|
|
653
|
+
|
|
654
|
+
axes := bundle.getAxesSpec(columns...)
|
|
655
|
+
|
|
652
656
|
entries := []
|
|
653
657
|
aliases := []
|
|
654
|
-
|
|
655
|
-
|
|
658
|
+
|
|
659
|
+
for axis in axes {
|
|
660
|
+
aliases = append(aliases, exp.sc.axis(axis).alias(pframesSpec.getAxisId(axis)))
|
|
656
661
|
}
|
|
657
|
-
|
|
662
|
+
|
|
663
|
+
for column in columns {
|
|
658
664
|
decoded := json.decode(column)
|
|
665
|
+
aliases = append(aliases, exp.col(column))
|
|
666
|
+
|
|
659
667
|
if (is_undefined(decoded.axisFilters)) {
|
|
660
|
-
|
|
661
|
-
entry := p.column(decoded.name, bundle.getColumn(decoded))
|
|
668
|
+
entry := p.column(column, bundle.getColumn(decoded))
|
|
662
669
|
entries = append(entries, entry)
|
|
663
670
|
} else {
|
|
664
|
-
aliases = append(aliases, exp.col(decoded.source.name))
|
|
665
671
|
entry := p.slicedColumn(
|
|
666
|
-
|
|
667
|
-
bundle.getColumn(decoded
|
|
672
|
+
column,
|
|
673
|
+
bundle.getColumn(decoded, {
|
|
674
|
+
keepColumns: true,
|
|
675
|
+
ignoreNonPartitioned: true
|
|
676
|
+
}),
|
|
668
677
|
slices.map(decoded.axisFilters, func(axisFilter) {
|
|
669
678
|
return {
|
|
670
679
|
axisIndex: axisFilter[0],
|
|
@@ -675,6 +684,7 @@ workflow := func() {
|
|
|
675
684
|
entries = append(entries, entry)
|
|
676
685
|
}
|
|
677
686
|
}
|
|
687
|
+
|
|
678
688
|
return self.frame(p.full(entries...)).select(aliases...)
|
|
679
689
|
},
|
|
680
690
|
|
|
@@ -978,30 +988,31 @@ _newDataFrameGroupBy = func(parentWorkflow, dfName, groupByExpressions) {
|
|
|
978
988
|
}
|
|
979
989
|
|
|
980
990
|
_newDataFrame = func(parentWorkflow, dfName) {
|
|
981
|
-
_mapExprsToStepCols := func(
|
|
982
|
-
ll.assert(len(
|
|
991
|
+
_mapExprsToStepCols := func(items, methodName, ctx) {
|
|
992
|
+
ll.assert(len(items) > 0, methodName + " requires at least one expression argument.")
|
|
983
993
|
cols := []
|
|
984
|
-
for
|
|
985
|
-
if
|
|
986
|
-
|
|
994
|
+
for item in items {
|
|
995
|
+
if exp._isExpression(item) {
|
|
996
|
+
cols = append(cols, item._getExpression(ctx))
|
|
997
|
+
} else {
|
|
998
|
+
ll.panic("Invalid argument to " + methodName + ": Expected an expression/column object, got %v", item)
|
|
987
999
|
}
|
|
988
|
-
cols = append(cols, expr._getExpression(ctx))
|
|
989
1000
|
}
|
|
990
1001
|
return cols
|
|
991
1002
|
}
|
|
992
1003
|
|
|
993
|
-
_mapExprsToStepColsAllowingStrings := func(
|
|
994
|
-
ll.assert(len(
|
|
1004
|
+
_mapExprsToStepColsAllowingStrings := func(items, methodName, ctx) {
|
|
1005
|
+
ll.assert(len(items) > 0, methodName + " requires at least one expression argument.")
|
|
995
1006
|
cols := []
|
|
996
|
-
for
|
|
997
|
-
if is_string(
|
|
1007
|
+
for item in items {
|
|
1008
|
+
if is_string(item) {
|
|
998
1009
|
|
|
999
|
-
colExpr := exp.col(
|
|
1010
|
+
colExpr := exp.col(item)
|
|
1000
1011
|
cols = append(cols, colExpr._getExpression(ctx))
|
|
1001
|
-
} else if exp._isExpression(
|
|
1002
|
-
cols = append(cols,
|
|
1012
|
+
} else if exp._isExpression(item) {
|
|
1013
|
+
cols = append(cols, item._getExpression(ctx))
|
|
1003
1014
|
} else {
|
|
1004
|
-
ll.panic("Invalid argument to " + methodName + ": Expected an expression object or string column name, got %v",
|
|
1015
|
+
ll.panic("Invalid argument to " + methodName + ": Expected an expression/column object or string column name, got %v", item)
|
|
1005
1016
|
}
|
|
1006
1017
|
}
|
|
1007
1018
|
return cols
|
|
@@ -84,15 +84,13 @@ validateParamsForSaveFrameDirect := func(params) {
|
|
|
84
84
|
|
|
85
85
|
_FRAME_FROM_COLUMN_BUNDLE_PARAMS_SCHEMA := {
|
|
86
86
|
|
|
87
|
-
`axes
|
|
87
|
+
`axes,?`: [spec.P_AXIS_SPEC_SCHEMA],
|
|
88
88
|
|
|
89
|
-
`columns
|
|
89
|
+
`columns,?`: [`string`]
|
|
90
90
|
}
|
|
91
91
|
|
|
92
92
|
validateParamsForFrameFromColumnBundle := func(params) {
|
|
93
93
|
validation.assertType(params, _FRAME_FROM_COLUMN_BUNDLE_PARAMS_SCHEMA, "Invalid params structure")
|
|
94
|
-
ll.assert(len(params.axes) > 0, "At least one axis is required")
|
|
95
|
-
ll.assert(len(params.columns) > 0, "At least one column is required")
|
|
96
94
|
}
|
|
97
95
|
|
|
98
96
|
makeFrameSpecs := func(params) {
|
|
@@ -140,15 +140,10 @@ map := func(slice, fn) {
|
|
|
140
140
|
|
|
141
141
|
|
|
142
142
|
reduce := func(slice, fn, ...init) {
|
|
143
|
-
result := undefined
|
|
143
|
+
result := len(init) > 0 ? init[0] : undefined
|
|
144
144
|
for i, elem in slice {
|
|
145
|
-
|
|
146
|
-
result = len(init) > 0 ? init : elem
|
|
147
|
-
} else {
|
|
148
|
-
result = fn(result, elem)
|
|
149
|
-
}
|
|
145
|
+
result = fn(result, elem)
|
|
150
146
|
}
|
|
151
|
-
|
|
152
147
|
return result
|
|
153
148
|
}
|
|
154
149
|
|
|
@@ -158,6 +153,20 @@ reduce := func(slice, fn, ...init) {
|
|
|
158
153
|
|
|
159
154
|
|
|
160
155
|
|
|
156
|
+
reverse := func(slice) {
|
|
157
|
+
rev := []
|
|
158
|
+
for i := len(slice)-1; i >= 0; i-- {
|
|
159
|
+
rev = append(rev, slice[i])
|
|
160
|
+
}
|
|
161
|
+
return rev
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
|
|
165
|
+
|
|
166
|
+
|
|
167
|
+
|
|
168
|
+
|
|
169
|
+
|
|
161
170
|
|
|
162
171
|
|
|
163
172
|
|
|
@@ -376,6 +385,7 @@ export ll.toStrict({
|
|
|
376
385
|
filter: filter,
|
|
377
386
|
map: map,
|
|
378
387
|
reduce: reduce,
|
|
388
|
+
reverse: reverse,
|
|
379
389
|
flatten: flatten,
|
|
380
390
|
every: every,
|
|
381
391
|
quickSortInPlace: quickSortInPlace,
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
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.
|
|
3
|
+
"version": "5.5.0",
|
|
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.9",
|
|
20
20
|
"@platforma-open/milaboratories.software-small-binaries": "^1.15.23",
|
|
21
|
-
"@platforma-open/milaboratories.software-ptabler": "1.
|
|
22
|
-
"@platforma-open/milaboratories.software-ptexter": "1.
|
|
21
|
+
"@platforma-open/milaboratories.software-ptabler": "1.13.0",
|
|
22
|
+
"@platforma-open/milaboratories.software-ptexter": "1.2.0"
|
|
23
23
|
},
|
|
24
24
|
"devDependencies": {
|
|
25
|
-
"@platforma-sdk/tengo-builder": "2.3.
|
|
25
|
+
"@platforma-sdk/tengo-builder": "2.3.3"
|
|
26
26
|
},
|
|
27
27
|
"scripts": {
|
|
28
28
|
"type-check": "pl-tengo check",
|