@platforma-sdk/workflow-tengo 5.3.2 → 5.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/dist/index.cjs +2 -2
- package/dist/index.d.ts +1 -1
- package/dist/index.js +2 -2
- package/dist/tengo/lib/exec.lib.tengo +4 -0
- package/dist/tengo/lib/exec.monetization_internal.lib.tengo +8 -0
- package/dist/tengo/lib/pframes.spec.lib.tengo +6 -0
- package/dist/tengo/lib/pframes.util.lib.tengo +404 -1
- package/dist/tengo/lib/pframes.xsv.lib.tengo +117 -65
- package/dist/tengo/lib/pt.expression.lib.tengo +828 -268
- package/dist/tengo/lib/pt.lib.tengo +567 -216
- package/dist/tengo/lib/pt.util.lib.tengo +231 -15
- package/dist/tengo/lib/slices.lib.tengo +20 -1
- package/dist/tengo/lib/smart.lib.tengo +0 -1
- 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.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-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-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/tengo/tpl/pframes.xsv-export-pframe.plj.gz +0 -0
|
@@ -7,14 +7,16 @@ exec := import("@platforma-sdk/workflow-tengo:exec")
|
|
|
7
7
|
json := import("json")
|
|
8
8
|
text := import("text")
|
|
9
9
|
slices := import("@platforma-sdk/workflow-tengo:slices")
|
|
10
|
-
validation := import("@platforma-sdk/workflow-tengo:validation")
|
|
11
10
|
execConstants := import("@platforma-sdk/workflow-tengo:exec.constants")
|
|
12
|
-
|
|
13
|
-
|
|
11
|
+
pframesBuilder := import("@platforma-sdk/workflow-tengo:pframes.builder")
|
|
12
|
+
pframesConstants := import("@platforma-sdk/workflow-tengo:pframes.constants")
|
|
13
|
+
pframesUtil := import("@platforma-sdk/workflow-tengo:pframes.util")
|
|
14
|
+
pframesSpec := import("@platforma-sdk/workflow-tengo:pframes.spec")
|
|
15
|
+
render := import("@platforma-sdk/workflow-tengo:render")
|
|
16
|
+
maps := import("@platforma-sdk/workflow-tengo:maps")
|
|
17
|
+
canonical := import("@platforma-sdk/workflow-tengo:canonical")
|
|
14
18
|
|
|
15
19
|
|
|
16
|
-
importDirTpl := assets.importTemplate("@platforma-sdk/workflow-tengo:pt.import-dir")
|
|
17
|
-
|
|
18
20
|
|
|
19
21
|
|
|
20
22
|
|
|
@@ -57,6 +59,186 @@ _newDataFrameGroupBy := undefined
|
|
|
57
59
|
workflowCounter := 0
|
|
58
60
|
|
|
59
61
|
|
|
62
|
+
_validateColumn := func(name, columnStruct) {
|
|
63
|
+
ll.assert(is_string(name), "column name must be a string")
|
|
64
|
+
ll.assert(is_map(columnStruct), "columnStruct must be a map")
|
|
65
|
+
ll.assert(!is_undefined(columnStruct.spec), "columnStruct must have 'spec' field defined")
|
|
66
|
+
ll.assert(!is_undefined(columnStruct.data), "columnStruct must have 'data' field defined")
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
_newPEntry := func(columnsByName, joinTree) {
|
|
70
|
+
return ll.toStrict({
|
|
71
|
+
|
|
72
|
+
_getColumnsMap: func() {
|
|
73
|
+
return columnsByName
|
|
74
|
+
},
|
|
75
|
+
|
|
76
|
+
_getJoinTree: func() {
|
|
77
|
+
return joinTree
|
|
78
|
+
}
|
|
79
|
+
})
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
_isPEntry := func(entry) {
|
|
83
|
+
return ll.isMap(entry) && ll.methodExists(entry, "_getColumnsMap") && ll.methodExists(entry, "_getJoinTree");
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
slicedColumnCounter := 0
|
|
87
|
+
|
|
88
|
+
p := ll.toStrict({
|
|
89
|
+
|
|
90
|
+
|
|
91
|
+
|
|
92
|
+
|
|
93
|
+
|
|
94
|
+
|
|
95
|
+
column: func(name, columnStruct) {
|
|
96
|
+
_validateColumn(name, columnStruct)
|
|
97
|
+
columnsByName := {}
|
|
98
|
+
columnsByName[name] = columnStruct
|
|
99
|
+
return _newPEntry(columnsByName, {
|
|
100
|
+
type: "column",
|
|
101
|
+
columnId: name
|
|
102
|
+
})
|
|
103
|
+
},
|
|
104
|
+
|
|
105
|
+
|
|
106
|
+
|
|
107
|
+
|
|
108
|
+
|
|
109
|
+
|
|
110
|
+
|
|
111
|
+
slicedColumn: func(name, columnMap, axisFilters) {
|
|
112
|
+
_validateColumn(name, columnMap)
|
|
113
|
+
util.validateAxisFilters(axisFilters)
|
|
114
|
+
|
|
115
|
+
slicedColumnCounter += 1
|
|
116
|
+
oldName := "p_sliced_" + slicedColumnCounter
|
|
117
|
+
|
|
118
|
+
columnsByName := {}
|
|
119
|
+
columnsByName[oldName] = columnMap
|
|
120
|
+
return _newPEntry(columnsByName, {
|
|
121
|
+
type: "slicedColumn",
|
|
122
|
+
columnId: oldName,
|
|
123
|
+
newId: name,
|
|
124
|
+
axisFilters: axisFilters
|
|
125
|
+
})
|
|
126
|
+
},
|
|
127
|
+
|
|
128
|
+
|
|
129
|
+
|
|
130
|
+
|
|
131
|
+
|
|
132
|
+
inner: func(...entries) {
|
|
133
|
+
ll.assert(len(entries) > 0, "inner join requires at least one entry")
|
|
134
|
+
|
|
135
|
+
mergedColMap := {}
|
|
136
|
+
joinTreeEntries := []
|
|
137
|
+
|
|
138
|
+
for entry in entries {
|
|
139
|
+
ll.assert(_isPEntry(entry), "all entries must be PEntry objects")
|
|
140
|
+
|
|
141
|
+
entryColMap := entry._getColumnsMap()
|
|
142
|
+
entryJoinTree := entry._getJoinTree()
|
|
143
|
+
|
|
144
|
+
for colName, colInfo in entryColMap {
|
|
145
|
+
if !is_undefined(mergedColMap[colName]) {
|
|
146
|
+
ll.panic("column name %s is already used, please pick a different name", colName)
|
|
147
|
+
}
|
|
148
|
+
mergedColMap[colName] = colInfo
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
joinTreeEntries = append(joinTreeEntries, entryJoinTree)
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
joinTree := {
|
|
155
|
+
type: "inner",
|
|
156
|
+
entries: joinTreeEntries
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
return _newPEntry(mergedColMap, joinTree)
|
|
160
|
+
},
|
|
161
|
+
|
|
162
|
+
|
|
163
|
+
|
|
164
|
+
|
|
165
|
+
|
|
166
|
+
full: func(...entries) {
|
|
167
|
+
ll.assert(len(entries) > 0, "full join requires at least one entry")
|
|
168
|
+
|
|
169
|
+
mergedColMap := {}
|
|
170
|
+
joinTreeEntries := []
|
|
171
|
+
|
|
172
|
+
for entry in entries {
|
|
173
|
+
ll.assert(_isPEntry(entry), "all entries must be PEntry objects")
|
|
174
|
+
|
|
175
|
+
entryColMap := entry._getColumnsMap()
|
|
176
|
+
entryJoinTree := entry._getJoinTree()
|
|
177
|
+
|
|
178
|
+
for colName, colInfo in entryColMap {
|
|
179
|
+
if !is_undefined(mergedColMap[colName]) {
|
|
180
|
+
ll.panic("column name %s is already used, please pick a different name", colName)
|
|
181
|
+
}
|
|
182
|
+
mergedColMap[colName] = colInfo
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
joinTreeEntries = append(joinTreeEntries, entryJoinTree)
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
joinTree := {
|
|
189
|
+
type: "full",
|
|
190
|
+
entries: joinTreeEntries
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
return _newPEntry(mergedColMap, joinTree)
|
|
194
|
+
},
|
|
195
|
+
|
|
196
|
+
|
|
197
|
+
|
|
198
|
+
|
|
199
|
+
|
|
200
|
+
|
|
201
|
+
outer: func(primary, ...secondary) {
|
|
202
|
+
ll.assert(_isPEntry(primary), "primary entry must be a PEntry object")
|
|
203
|
+
ll.assert(len(secondary) > 0, "outer join requires at least one secondary entry")
|
|
204
|
+
|
|
205
|
+
mergedColMap := {}
|
|
206
|
+
|
|
207
|
+
primaryColMap := primary._getColumnsMap()
|
|
208
|
+
primaryJoinTree := primary._getJoinTree()
|
|
209
|
+
|
|
210
|
+
for colName, colInfo in primaryColMap {
|
|
211
|
+
mergedColMap[colName] = colInfo
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
secondaryJoinTrees := []
|
|
215
|
+
|
|
216
|
+
for entry in secondary {
|
|
217
|
+
ll.assert(_isPEntry(entry), "all secondary entries must be PEntry objects")
|
|
218
|
+
|
|
219
|
+
entryColMap := entry._getColumnsMap()
|
|
220
|
+
entryJoinTree := entry._getJoinTree()
|
|
221
|
+
|
|
222
|
+
for colName, colInfo in entryColMap {
|
|
223
|
+
if !is_undefined(mergedColMap[colName]) {
|
|
224
|
+
ll.panic("column name %s is already used, please pick a different name", colName)
|
|
225
|
+
}
|
|
226
|
+
mergedColMap[colName] = colInfo
|
|
227
|
+
}
|
|
228
|
+
|
|
229
|
+
secondaryJoinTrees = append(secondaryJoinTrees, entryJoinTree)
|
|
230
|
+
}
|
|
231
|
+
|
|
232
|
+
joinTree := {
|
|
233
|
+
type: "outer",
|
|
234
|
+
primary: primaryJoinTree,
|
|
235
|
+
secondary: secondaryJoinTrees
|
|
236
|
+
}
|
|
237
|
+
|
|
238
|
+
return _newPEntry(mergedColMap, joinTree)
|
|
239
|
+
}
|
|
240
|
+
})
|
|
241
|
+
|
|
60
242
|
|
|
61
243
|
_detectFormatFromFileName := func(fileName) {
|
|
62
244
|
if text.has_suffix(fileName, ".csv") {
|
|
@@ -83,17 +265,25 @@ workflow := func() {
|
|
|
83
265
|
|
|
84
266
|
inFiles := []
|
|
85
267
|
|
|
268
|
+
inFrames := {}
|
|
269
|
+
|
|
86
270
|
outFiles := []
|
|
87
271
|
|
|
88
272
|
outContentFiles := []
|
|
89
273
|
|
|
90
274
|
outFramesSpecs := {}
|
|
91
275
|
|
|
276
|
+
outFramesRename := {}
|
|
277
|
+
|
|
92
278
|
anonymousFilesCounter := 0
|
|
93
279
|
anonymousDFCounter := 0
|
|
280
|
+
pcolumnCounter := 0
|
|
281
|
+
pframeCounter := 0
|
|
282
|
+
specDistillerSingleton := undefined
|
|
94
283
|
|
|
95
284
|
cpu := undefined
|
|
96
285
|
mem := undefined
|
|
286
|
+
inputCache := undefined
|
|
97
287
|
queue := execConstants.MEDIUM_QUEUE
|
|
98
288
|
|
|
99
289
|
self := undefined
|
|
@@ -111,10 +301,20 @@ workflow := func() {
|
|
|
111
301
|
|
|
112
302
|
|
|
113
303
|
addRawStep: func(step) {
|
|
114
|
-
|
|
304
|
+
ll.assert(is_map(step), "step must be a map")
|
|
305
|
+
ll.assert(is_string(step.type), "step.type must be a string discriminator")
|
|
306
|
+
self._addStep(func(ctx) {
|
|
307
|
+
return step
|
|
308
|
+
})
|
|
115
309
|
return self
|
|
116
310
|
},
|
|
117
311
|
|
|
312
|
+
_addStep: func(step) {
|
|
313
|
+
ll.assert(is_callable(step), "step must be a function")
|
|
314
|
+
steps = append(steps, step)
|
|
315
|
+
},
|
|
316
|
+
|
|
317
|
+
|
|
118
318
|
|
|
119
319
|
|
|
120
320
|
|
|
@@ -163,6 +363,8 @@ workflow := func() {
|
|
|
163
363
|
|
|
164
364
|
|
|
165
365
|
frame: func(frameInput, ...optionsRaw) {
|
|
366
|
+
ll.assert(!is_undefined(frameInput), "frameInput must be provided")
|
|
367
|
+
|
|
166
368
|
opts := {}
|
|
167
369
|
if len(optionsRaw) > 0 {
|
|
168
370
|
if len(optionsRaw) == 1 && is_map(optionsRaw[0]) {
|
|
@@ -172,6 +374,7 @@ workflow := func() {
|
|
|
172
374
|
}
|
|
173
375
|
}
|
|
174
376
|
|
|
377
|
+
frameResource := undefined
|
|
175
378
|
fileRef := undefined
|
|
176
379
|
fileContent := undefined
|
|
177
380
|
inputSchema := undefined
|
|
@@ -184,9 +387,7 @@ workflow := func() {
|
|
|
184
387
|
nRowsOpt := undefined
|
|
185
388
|
ignoreErrorsOpt := undefined
|
|
186
389
|
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
if isStructuralInput {
|
|
390
|
+
if is_map(frameInput) && !is_undefined(frameInput.file) {
|
|
190
391
|
fileRef = frameInput.file
|
|
191
392
|
inputXsvType = frameInput.xsvType
|
|
192
393
|
|
|
@@ -198,13 +399,33 @@ workflow := func() {
|
|
|
198
399
|
}
|
|
199
400
|
|
|
200
401
|
finalFormat = inputXsvType
|
|
201
|
-
} else {
|
|
202
|
-
|
|
203
|
-
|
|
402
|
+
} else if smart.isReference(frameInput) {
|
|
403
|
+
frameInputResource := frameInput
|
|
404
|
+
if smart.isField(frameInputResource) {
|
|
405
|
+
frameInputResource = frameInputResource.getValue()
|
|
406
|
+
}
|
|
407
|
+
if frameInputResource.checkResourceType(pframesConstants.RTYPE_P_FRAME) {
|
|
408
|
+
frameResource = frameInputResource
|
|
409
|
+
finalFormat = "pframe"
|
|
204
410
|
} else {
|
|
205
|
-
|
|
206
|
-
|
|
411
|
+
fileRef = frameInput
|
|
412
|
+
}
|
|
413
|
+
} else if is_string(frameInput) || is_bytes(frameInput) {
|
|
414
|
+
fileContent = frameInput
|
|
415
|
+
} else if pframesUtil.isFlatPfMap(frameInput) {
|
|
416
|
+
frameResource = frameInput
|
|
417
|
+
finalFormat = "pframe"
|
|
418
|
+
} else if pframesUtil.isStructuredPfMap(frameInput) {
|
|
419
|
+
pfBuilder := pframesBuilder.pFrameBuilder()
|
|
420
|
+
for key, value in frameInput {
|
|
421
|
+
pfBuilder.add(key, value.spec, value.data)
|
|
207
422
|
}
|
|
423
|
+
frameResource = pfBuilder.build()
|
|
424
|
+
finalFormat = "pframe"
|
|
425
|
+
} else if _isPEntry(frameInput) {
|
|
426
|
+
finalFormat = "pentry"
|
|
427
|
+
} else {
|
|
428
|
+
ll.panic("unknown frame input type: %v", frameInput)
|
|
208
429
|
}
|
|
209
430
|
|
|
210
431
|
|
|
@@ -277,22 +498,67 @@ workflow := func() {
|
|
|
277
498
|
ignoreErrorsOpt = opts.ignoreErrors
|
|
278
499
|
}
|
|
279
500
|
|
|
280
|
-
|
|
281
|
-
if !is_undefined(fileRef) && !smart.isReference(fileRef) {
|
|
282
|
-
ll.panic("frameInput.file (for structural input) or frameInput (for direct reference) must be a valid resource reference. Got: %v", fileRef)
|
|
283
|
-
} else if !is_undefined(fileContent) && !is_string(fileContent) {
|
|
284
|
-
ll.panic("frameInput (for direct content) must be a string. Got: %v", fileContent)
|
|
285
|
-
}
|
|
286
|
-
|
|
287
501
|
if !is_undefined(fileRef) {
|
|
502
|
+
ll.assert(
|
|
503
|
+
smart.isReference(fileRef),
|
|
504
|
+
"frameInput.file (for structural input) or frameInput (for direct reference) must be a valid resource reference, got: %v",
|
|
505
|
+
fileRef)
|
|
288
506
|
inFiles = append(inFiles, { file: fileRef, name: finalFileName })
|
|
289
|
-
} else {
|
|
507
|
+
} else if !is_undefined(fileContent) {
|
|
508
|
+
ll.assert(is_string(fileContent), "frameInput (for direct content) must be a string, got: %v", fileContent)
|
|
290
509
|
inFiles = append(inFiles, { content: fileContent, name: finalFileName })
|
|
291
510
|
}
|
|
292
511
|
|
|
293
512
|
|
|
294
|
-
|
|
295
|
-
|
|
513
|
+
step := undefined
|
|
514
|
+
if finalFormat == "pentry" || finalFormat == "pframe" {
|
|
515
|
+
if finalFormat == "pframe" {
|
|
516
|
+
columnsMap := pframesUtil.pFrameToColumnsMap(frameResource)
|
|
517
|
+
columnEntries := []
|
|
518
|
+
for frameUniqueColumnId, columnInfo in columnsMap {
|
|
519
|
+
columnEntry := p.column(frameUniqueColumnId, columnInfo)
|
|
520
|
+
columnEntries = append(columnEntries, columnEntry)
|
|
521
|
+
}
|
|
522
|
+
frameInput = p.full(columnEntries...)
|
|
523
|
+
}
|
|
524
|
+
|
|
525
|
+
columnsMap := frameInput._getColumnsMap()
|
|
526
|
+
src := frameInput._getJoinTree()
|
|
527
|
+
|
|
528
|
+
|
|
529
|
+
translation := {}
|
|
530
|
+
rename := {}
|
|
531
|
+
renamedColumnsMap := {}
|
|
532
|
+
|
|
533
|
+
for frameUniqueColumnId, columnInfo in columnsMap {
|
|
534
|
+
workflowUniqueColumnId := self._newUniqueColumnId()
|
|
535
|
+
|
|
536
|
+
translation[workflowUniqueColumnId] = frameUniqueColumnId
|
|
537
|
+
rename[frameUniqueColumnId] = workflowUniqueColumnId
|
|
538
|
+
renamedColumnsMap[workflowUniqueColumnId] = columnInfo
|
|
539
|
+
}
|
|
540
|
+
|
|
541
|
+
|
|
542
|
+
request := {
|
|
543
|
+
src: src,
|
|
544
|
+
filters: []
|
|
545
|
+
}
|
|
546
|
+
renamedRequest := util.renameJoinColumns(request, rename)
|
|
547
|
+
|
|
548
|
+
|
|
549
|
+
ll.assert(is_undefined(inFrames[finalDataFrameId]),
|
|
550
|
+
"frame with name '%s' already added", finalDataFrameId)
|
|
551
|
+
inFrames[finalDataFrameId] = {
|
|
552
|
+
columns: renamedColumnsMap
|
|
553
|
+
}
|
|
554
|
+
|
|
555
|
+
step = {
|
|
556
|
+
type: "read_frame",
|
|
557
|
+
name: finalDataFrameId,
|
|
558
|
+
request: renamedRequest,
|
|
559
|
+
translation: translation
|
|
560
|
+
}
|
|
561
|
+
} else if finalFormat == "parquet" {
|
|
296
562
|
step = {
|
|
297
563
|
type: "read_parquet",
|
|
298
564
|
file: finalFileName,
|
|
@@ -360,66 +626,33 @@ workflow := func() {
|
|
|
360
626
|
frameFromColumnBundle: func(bundle, options) {
|
|
361
627
|
util.validateParamsForFrameFromColumnBundle(options)
|
|
362
628
|
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
} else if is_map(columnId) {
|
|
368
|
-
decoded = columnId
|
|
369
|
-
} else {
|
|
370
|
-
ll.panic("Invalid column ID type. Expected string or object.")
|
|
371
|
-
}
|
|
372
|
-
return decoded
|
|
373
|
-
}
|
|
374
|
-
isFilteredColumn := func(columnId) {
|
|
375
|
-
decoded := decodeColumn(columnId)
|
|
376
|
-
return is_map(decoded) && !is_undefined(decoded.source) && !is_undefined(decoded.axisFilters)
|
|
629
|
+
entries := []
|
|
630
|
+
aliases := []
|
|
631
|
+
for axis in options.axes {
|
|
632
|
+
aliases = append(aliases, exp.axis(axis.spec).alias(axis.column))
|
|
377
633
|
}
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
for idx, axis in axes {
|
|
385
|
-
tsvBuilder.setAxisHeader(axis.spec, axis.column)
|
|
386
|
-
axesHeaders = append(axesHeaders, axis.column)
|
|
387
|
-
}
|
|
388
|
-
|
|
389
|
-
filteredColumns := []
|
|
390
|
-
for column in columns {
|
|
391
|
-
if (isFilteredColumn(column)) {
|
|
392
|
-
filteredColumns = append(filteredColumns, column)
|
|
634
|
+
for column in options.columns {
|
|
635
|
+
decoded := json.decode(column)
|
|
636
|
+
if (is_undefined(decoded.axisFilters)) {
|
|
637
|
+
aliases = append(aliases, exp.col(decoded.name))
|
|
638
|
+
entry := p.column(decoded.name, bundle.getColumn(decoded))
|
|
639
|
+
entries = append(entries, entry)
|
|
393
640
|
} else {
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
for axis in axes {
|
|
407
|
-
tsv.setAxisHeader(axis.spec, axis.column)
|
|
408
|
-
}
|
|
409
|
-
tsv.add(bundle.getColumn(source), { header: column })
|
|
410
|
-
df := self.frame(tsv.buildForPT())
|
|
411
|
-
joinColumns := slices.map(axes, func(axis) { return axis.column })
|
|
412
|
-
for axisFilter in axisFilters {
|
|
413
|
-
id := axesHeaders[axisFilter[0]]
|
|
414
|
-
value := axisFilter[1]
|
|
415
|
-
df = df.filter(exp.col(id).eq(exp.lit(value))).withoutColumns(id)
|
|
416
|
-
joinColumns = slices.filter(joinColumns, func(c) { return c != id } )
|
|
641
|
+
aliases = append(aliases, exp.col(decoded.source.name))
|
|
642
|
+
entry := p.slicedColumn(
|
|
643
|
+
decoded.source.name,
|
|
644
|
+
bundle.getColumn(decoded.source),
|
|
645
|
+
slices.map(decoded.axisFilters, func(axisFilter) {
|
|
646
|
+
return {
|
|
647
|
+
axisIndex: axisFilter[0],
|
|
648
|
+
constant: axisFilter[1]
|
|
649
|
+
}
|
|
650
|
+
})
|
|
651
|
+
)
|
|
652
|
+
entries = append(entries, entry)
|
|
417
653
|
}
|
|
418
|
-
|
|
419
|
-
mainDF = mainDF.join(df, { on: joinColumns })
|
|
420
654
|
}
|
|
421
|
-
|
|
422
|
-
return mainDF
|
|
655
|
+
return self.frame(p.full(entries...)).select(aliases...)
|
|
423
656
|
},
|
|
424
657
|
|
|
425
658
|
_newAnonymousDataFrameId: func() {
|
|
@@ -430,19 +663,63 @@ workflow := func() {
|
|
|
430
663
|
anonymousFilesCounter += 1
|
|
431
664
|
return "anonymous_" + string(anonymousFilesCounter) + "." + extension
|
|
432
665
|
},
|
|
666
|
+
_newUniqueColumnId: func() {
|
|
667
|
+
pcolumnCounter += 1
|
|
668
|
+
return "pcolumn_" + string(pcolumnCounter)
|
|
669
|
+
},
|
|
670
|
+
_newUniquePFrameName: func() {
|
|
671
|
+
pframeCounter += 1
|
|
672
|
+
return "pframe_" + string(pframeCounter)
|
|
673
|
+
},
|
|
433
674
|
_saveFile: func(name) {
|
|
434
675
|
outFiles = append(outFiles, name)
|
|
435
676
|
},
|
|
436
677
|
_saveFileContent: func(name) {
|
|
437
678
|
outContentFiles = append(outContentFiles, name)
|
|
438
679
|
},
|
|
439
|
-
_saveFrameSpecs: func(name, specs) {
|
|
680
|
+
_saveFrameSpecs: func(name, rename, specs) {
|
|
440
681
|
ll.assert(
|
|
441
682
|
outFramesSpecs[name] == undefined,
|
|
442
683
|
"frame with name '%s' already saved, please pick a different name",
|
|
443
684
|
name
|
|
444
685
|
)
|
|
445
686
|
outFramesSpecs[name] = specs
|
|
687
|
+
outFramesRename[name] = rename
|
|
688
|
+
},
|
|
689
|
+
_getSpecDistiller: func() {
|
|
690
|
+
if is_undefined(specDistillerSingleton) {
|
|
691
|
+
rawColumnSpecs := []
|
|
692
|
+
for _, columns in inFrames {
|
|
693
|
+
for columnId, columnInfo in columns {
|
|
694
|
+
rawColumnSpecs = append(rawColumnSpecs, columnInfo.spec)
|
|
695
|
+
}
|
|
696
|
+
}
|
|
697
|
+
specDistillerSingleton = pframesSpec.createSpecDistiller(rawColumnSpecs)
|
|
698
|
+
}
|
|
699
|
+
return specDistillerSingleton
|
|
700
|
+
},
|
|
701
|
+
_getDistilledAxesSpecs: func() {
|
|
702
|
+
specDistiller := self._getSpecDistiller()
|
|
703
|
+
canonicalMap := {}
|
|
704
|
+
distilledAxesSpecs := {}
|
|
705
|
+
for _, frame in inFrames {
|
|
706
|
+
for _, columnInfo in frame.columns {
|
|
707
|
+
for axis in columnInfo.spec.axesSpec {
|
|
708
|
+
if is_undefined(canonicalMap[axis.name]) {
|
|
709
|
+
canonicalMap[axis.name] = {}
|
|
710
|
+
}
|
|
711
|
+
canonicalizedAxisSpec := canonical.encode(axis)
|
|
712
|
+
if is_undefined(canonicalMap[axis.name][canonicalizedAxisSpec]) {
|
|
713
|
+
canonicalMap[axis.name][canonicalizedAxisSpec] = true
|
|
714
|
+
if is_undefined(distilledAxesSpecs[axis.name]) {
|
|
715
|
+
distilledAxesSpecs[axis.name] = []
|
|
716
|
+
}
|
|
717
|
+
distilledAxesSpecs[axis.name] = append(distilledAxesSpecs[axis.name], axis)
|
|
718
|
+
}
|
|
719
|
+
}
|
|
720
|
+
}
|
|
721
|
+
}
|
|
722
|
+
return distilledAxesSpecs
|
|
446
723
|
},
|
|
447
724
|
|
|
448
725
|
|
|
@@ -509,60 +786,112 @@ workflow := func() {
|
|
|
509
786
|
|
|
510
787
|
|
|
511
788
|
|
|
789
|
+
|
|
790
|
+
|
|
791
|
+
|
|
792
|
+
cacheInputs: func(time) {
|
|
793
|
+
ll.assert(
|
|
794
|
+
is_int(time),
|
|
795
|
+
"input cache time must be an integer. " +
|
|
796
|
+
"Did you forget to import a standard tengo library 'times'?")
|
|
797
|
+
inputCache = time
|
|
798
|
+
return self
|
|
799
|
+
},
|
|
800
|
+
|
|
801
|
+
|
|
802
|
+
|
|
803
|
+
|
|
804
|
+
|
|
805
|
+
|
|
806
|
+
|
|
512
807
|
run: func() {
|
|
513
|
-
|
|
514
|
-
ptablerCmdBuilder := exec.builder().
|
|
515
|
-
setQueue(queue).
|
|
516
|
-
printErrStreamToStdout().
|
|
517
|
-
dontSaveStdoutOrStderr().
|
|
518
|
-
software(ptablerSw).
|
|
519
|
-
envExpr("POLARS_MAX_THREADS", "{system.cpu}").
|
|
520
|
-
arg("workflow_sc.json").
|
|
521
|
-
writeFile("workflow_sc.json", json.encode({
|
|
522
|
-
workflow: steps
|
|
523
|
-
}));
|
|
808
|
+
workflowRunTpl := assets.importTemplate("@platforma-sdk/workflow-tengo:pt.workflow-run")
|
|
524
809
|
|
|
525
|
-
|
|
526
|
-
|
|
810
|
+
specDistiller := self._getSpecDistiller()
|
|
811
|
+
distilledAxesSpecs := self._getDistilledAxesSpecs()
|
|
812
|
+
ctx := {
|
|
813
|
+
specDistiller: specDistiller,
|
|
814
|
+
distilledAxesSpecs: distilledAxesSpecs
|
|
527
815
|
}
|
|
528
|
-
|
|
529
|
-
|
|
530
|
-
|
|
816
|
+
workflowJsonStruct := {
|
|
817
|
+
workflow: slices.map(steps, func(step) {
|
|
818
|
+
return step(ctx)
|
|
819
|
+
})
|
|
531
820
|
}
|
|
532
821
|
|
|
822
|
+
inFilesMap := {}
|
|
823
|
+
inFilesContentMap := {}
|
|
533
824
|
for inFile in inFiles {
|
|
534
825
|
if !is_undefined(inFile.file) {
|
|
535
|
-
|
|
826
|
+
inFilesMap[inFile.name] = inFile.file
|
|
827
|
+
} else if !is_undefined(inFile.content) {
|
|
828
|
+
inFilesContentMap[inFile.name] = inFile.content
|
|
536
829
|
} else {
|
|
537
|
-
|
|
830
|
+
ll.panic("inFile must have either file or content, got: %v", inFile)
|
|
538
831
|
}
|
|
539
832
|
}
|
|
540
|
-
|
|
541
|
-
|
|
542
|
-
ptablerCmdBuilder.saveFile(outFile)
|
|
543
|
-
}
|
|
544
|
-
|
|
545
|
-
for outContentFile in outContentFiles {
|
|
546
|
-
ptablerCmdBuilder.saveFileContent(outContentFile)
|
|
547
|
-
}
|
|
548
|
-
|
|
549
|
-
tplArgs := {}
|
|
833
|
+
|
|
834
|
+
outFrames := {}
|
|
550
835
|
for frameName, specsById in outFramesSpecs {
|
|
551
836
|
columnIds := []
|
|
552
837
|
for columnId, _ in specsById {
|
|
553
838
|
columnIds = append(columnIds, columnId)
|
|
554
839
|
}
|
|
555
|
-
|
|
556
|
-
}
|
|
557
|
-
workdirProcessorName := "import_dir";
|
|
558
|
-
if len(tplArgs) > 0 {
|
|
559
|
-
ptablerCmdBuilder.processWorkdir(workdirProcessorName, importDirTpl, tplArgs)
|
|
840
|
+
outFrames[outFramesRename[frameName]] = columnIds;
|
|
560
841
|
}
|
|
561
842
|
|
|
562
|
-
|
|
843
|
+
inFramesDistilled := maps.mapValues(inFrames, func(frame) {
|
|
844
|
+
return {
|
|
845
|
+
columns: maps.mapValues(frame.columns, func(columnInfo) {
|
|
846
|
+
return {
|
|
847
|
+
spec: specDistiller.distill(columnInfo.spec),
|
|
848
|
+
data: columnInfo.data
|
|
849
|
+
}
|
|
850
|
+
})
|
|
851
|
+
}
|
|
852
|
+
})
|
|
853
|
+
|
|
854
|
+
runInputs := {
|
|
855
|
+
workflowJsonStruct: workflowJsonStruct,
|
|
856
|
+
inFiles: inFilesMap,
|
|
857
|
+
inFilesContent: inFilesContentMap,
|
|
858
|
+
inFrames: inFramesDistilled,
|
|
859
|
+
outFiles: outFiles,
|
|
860
|
+
outFilesContent: outContentFiles,
|
|
861
|
+
outFrames: outFrames
|
|
862
|
+
}
|
|
863
|
+
runMetaInputs := {
|
|
864
|
+
queue: queue,
|
|
865
|
+
cpu: cpu,
|
|
866
|
+
mem: mem,
|
|
867
|
+
inputCache: inputCache
|
|
868
|
+
}
|
|
869
|
+
ptResult := render.create(workflowRunTpl, runInputs, {
|
|
870
|
+
metaInputs: runMetaInputs
|
|
871
|
+
});
|
|
563
872
|
|
|
564
873
|
self := undefined
|
|
565
|
-
self = ll.toStrict(
|
|
874
|
+
self = ll.toStrict({
|
|
875
|
+
|
|
876
|
+
|
|
877
|
+
|
|
878
|
+
|
|
879
|
+
|
|
880
|
+
|
|
881
|
+
getFile: func(fileName) {
|
|
882
|
+
return ptResult.resolveOutput(["outFiles", fileName])
|
|
883
|
+
},
|
|
884
|
+
|
|
885
|
+
|
|
886
|
+
|
|
887
|
+
|
|
888
|
+
|
|
889
|
+
|
|
890
|
+
|
|
891
|
+
getFileContent: func(fileName) {
|
|
892
|
+
return ptResult.resolveOutput(["outFilesContent", fileName])
|
|
893
|
+
},
|
|
894
|
+
|
|
566
895
|
|
|
567
896
|
|
|
568
897
|
|
|
@@ -572,17 +901,15 @@ workflow := func() {
|
|
|
572
901
|
getFrameDirect: func(frameName) {
|
|
573
902
|
specs := outFramesSpecs[frameName]
|
|
574
903
|
ll.assert(!is_undefined(specs), "saveFrameDirect was not called for frame '%s'", frameName)
|
|
575
|
-
savedFrames := execResult.getProcessorResult(workdirProcessorName)
|
|
576
904
|
|
|
577
|
-
pf :=
|
|
578
|
-
for
|
|
579
|
-
|
|
580
|
-
|
|
581
|
-
pf.add(pColumnId, pColumnSpec, pColumnData)
|
|
905
|
+
pf := pframesBuilder.pFrameBuilder()
|
|
906
|
+
for columnId, columnSpec in specs {
|
|
907
|
+
columnData := ptResult.resolveOutput(["outFrames", outFramesRename[frameName], columnId])
|
|
908
|
+
pf.add(columnId, columnSpec, columnData)
|
|
582
909
|
}
|
|
583
910
|
return pf.build()
|
|
584
911
|
}
|
|
585
|
-
})
|
|
912
|
+
})
|
|
586
913
|
return self
|
|
587
914
|
}
|
|
588
915
|
}
|
|
@@ -612,20 +939,22 @@ _newDataFrameGroupBy = func(parentWorkflow, dfName, groupByExpressions) {
|
|
|
612
939
|
agg: func(...aggExpressions) {
|
|
613
940
|
ll.assert(len(aggExpressions) > 0, "agg method requires at least one aggregation expression.")
|
|
614
941
|
|
|
615
|
-
aggregations := slices.map(aggExpressions, func(aggExpr) {
|
|
616
|
-
if !exp._isAggregation(aggExpr) {
|
|
617
|
-
ll.panic("Invalid argument to agg: Expected an aggregation expression object, got %v", aggExpr)
|
|
618
|
-
}
|
|
619
|
-
return aggExpr.getAggregation()
|
|
620
|
-
})
|
|
621
|
-
|
|
622
942
|
outputDfName := parentWorkflow._newAnonymousDataFrameId()
|
|
623
|
-
parentWorkflow.
|
|
624
|
-
|
|
625
|
-
|
|
626
|
-
|
|
627
|
-
|
|
628
|
-
|
|
943
|
+
parentWorkflow._addStep(func(ctx) {
|
|
944
|
+
aggregations := slices.map(aggExpressions, func(aggExpr) {
|
|
945
|
+
if !exp._isAggregation(aggExpr) {
|
|
946
|
+
ll.panic("Invalid argument to agg: Expected an aggregation expression object, got %v", aggExpr)
|
|
947
|
+
}
|
|
948
|
+
return aggExpr._getAggregation(ctx)
|
|
949
|
+
})
|
|
950
|
+
|
|
951
|
+
return {
|
|
952
|
+
type: "aggregate",
|
|
953
|
+
inputTable: dfName,
|
|
954
|
+
outputTable: outputDfName,
|
|
955
|
+
groupBy: exp._mapToExpressionStructList(groupByExpressions, "col", ctx),
|
|
956
|
+
aggregations: aggregations
|
|
957
|
+
}
|
|
629
958
|
})
|
|
630
959
|
return _newDataFrame(parentWorkflow, outputDfName)
|
|
631
960
|
}
|
|
@@ -634,37 +963,28 @@ _newDataFrameGroupBy = func(parentWorkflow, dfName, groupByExpressions) {
|
|
|
634
963
|
}
|
|
635
964
|
|
|
636
965
|
_newDataFrame = func(parentWorkflow, dfName) {
|
|
637
|
-
_mapExprsToStepCols := func(exprs, methodName) {
|
|
966
|
+
_mapExprsToStepCols := func(exprs, methodName, ctx) {
|
|
638
967
|
ll.assert(len(exprs) > 0, methodName + " requires at least one expression argument.")
|
|
639
968
|
cols := []
|
|
640
969
|
for expr in exprs {
|
|
641
970
|
if !exp._isExpression(expr) {
|
|
642
971
|
ll.panic("Invalid argument to " + methodName + ": Expected an expression object, got %v", expr)
|
|
643
972
|
}
|
|
644
|
-
cols = append(cols,
|
|
645
|
-
name: expr.getAlias(),
|
|
646
|
-
expression: expr.getExpression()
|
|
647
|
-
})
|
|
973
|
+
cols = append(cols, expr._getExpression(ctx))
|
|
648
974
|
}
|
|
649
975
|
return cols
|
|
650
976
|
}
|
|
651
977
|
|
|
652
|
-
_mapExprsToStepColsAllowingStrings := func(exprs, methodName) {
|
|
978
|
+
_mapExprsToStepColsAllowingStrings := func(exprs, methodName, ctx) {
|
|
653
979
|
ll.assert(len(exprs) > 0, methodName + " requires at least one expression argument.")
|
|
654
980
|
cols := []
|
|
655
981
|
for expr in exprs {
|
|
656
982
|
if is_string(expr) {
|
|
657
983
|
|
|
658
984
|
colExpr := exp.col(expr)
|
|
659
|
-
cols = append(cols,
|
|
660
|
-
name: colExpr.getAlias(),
|
|
661
|
-
expression: colExpr.getExpression()
|
|
662
|
-
})
|
|
985
|
+
cols = append(cols, colExpr._getExpression(ctx))
|
|
663
986
|
} else if exp._isExpression(expr) {
|
|
664
|
-
cols = append(cols,
|
|
665
|
-
name: expr.getAlias(),
|
|
666
|
-
expression: expr.getExpression()
|
|
667
|
-
})
|
|
987
|
+
cols = append(cols, expr._getExpression(ctx))
|
|
668
988
|
} else {
|
|
669
989
|
ll.panic("Invalid argument to " + methodName + ": Expected an expression object or string column name, got %v", expr)
|
|
670
990
|
}
|
|
@@ -763,13 +1083,15 @@ _newDataFrame = func(parentWorkflow, dfName) {
|
|
|
763
1083
|
|
|
764
1084
|
|
|
765
1085
|
withColumns: func(...expressions) {
|
|
766
|
-
stepCols := _mapExprsToStepCols(expressions, "withColumns")
|
|
767
1086
|
outputDfName := parentWorkflow._newAnonymousDataFrameId()
|
|
768
|
-
parentWorkflow.
|
|
769
|
-
|
|
770
|
-
|
|
771
|
-
|
|
772
|
-
|
|
1087
|
+
parentWorkflow._addStep(func(ctx) {
|
|
1088
|
+
stepCols := _mapExprsToStepCols(expressions, "withColumns", ctx)
|
|
1089
|
+
return {
|
|
1090
|
+
type: "with_columns",
|
|
1091
|
+
inputTable: dfName,
|
|
1092
|
+
outputTable: outputDfName,
|
|
1093
|
+
columns: stepCols
|
|
1094
|
+
}
|
|
773
1095
|
})
|
|
774
1096
|
return _newDataFrame(parentWorkflow, outputDfName)
|
|
775
1097
|
},
|
|
@@ -789,13 +1111,15 @@ _newDataFrame = func(parentWorkflow, dfName) {
|
|
|
789
1111
|
|
|
790
1112
|
|
|
791
1113
|
select: func(...expressions) {
|
|
792
|
-
stepCols := _mapExprsToStepColsAllowingStrings(expressions, "select")
|
|
793
1114
|
outputDfName := parentWorkflow._newAnonymousDataFrameId()
|
|
794
|
-
parentWorkflow.
|
|
795
|
-
|
|
796
|
-
|
|
797
|
-
|
|
798
|
-
|
|
1115
|
+
parentWorkflow._addStep(func(ctx) {
|
|
1116
|
+
stepCols := _mapExprsToStepColsAllowingStrings(expressions, "select", ctx)
|
|
1117
|
+
return {
|
|
1118
|
+
type: "select",
|
|
1119
|
+
inputTable: dfName,
|
|
1120
|
+
outputTable: outputDfName,
|
|
1121
|
+
columns: stepCols
|
|
1122
|
+
}
|
|
799
1123
|
})
|
|
800
1124
|
return _newDataFrame(parentWorkflow, outputDfName)
|
|
801
1125
|
},
|
|
@@ -814,11 +1138,13 @@ _newDataFrame = func(parentWorkflow, dfName) {
|
|
|
814
1138
|
|
|
815
1139
|
|
|
816
1140
|
addColumns: func(...expressions) {
|
|
817
|
-
|
|
818
|
-
|
|
819
|
-
|
|
820
|
-
|
|
821
|
-
|
|
1141
|
+
parentWorkflow._addStep(func(ctx) {
|
|
1142
|
+
stepCols := _mapExprsToStepCols(expressions, "addColumns", ctx)
|
|
1143
|
+
return {
|
|
1144
|
+
type: "add_columns",
|
|
1145
|
+
table: dfName,
|
|
1146
|
+
columns: stepCols
|
|
1147
|
+
}
|
|
822
1148
|
})
|
|
823
1149
|
return self
|
|
824
1150
|
},
|
|
@@ -907,9 +1233,10 @@ _newDataFrame = func(parentWorkflow, dfName) {
|
|
|
907
1233
|
util.validateParamsForSaveFrameDirect(params)
|
|
908
1234
|
|
|
909
1235
|
specs := util.makeFrameSpecs(params)
|
|
910
|
-
parentWorkflow.
|
|
1236
|
+
sequentialFrameName := parentWorkflow._newUniquePFrameName()
|
|
1237
|
+
parentWorkflow._saveFrameSpecs(frameName, sequentialFrameName, specs)
|
|
911
1238
|
|
|
912
|
-
step := util.makeWriteFrameStep(
|
|
1239
|
+
step := util.makeWriteFrameStep(sequentialFrameName, dfName, params)
|
|
913
1240
|
parentWorkflow.addRawStep(step)
|
|
914
1241
|
},
|
|
915
1242
|
|
|
@@ -965,12 +1292,35 @@ _newDataFrame = func(parentWorkflow, dfName) {
|
|
|
965
1292
|
condition = exp.and(allConditions...)
|
|
966
1293
|
}
|
|
967
1294
|
|
|
1295
|
+
outputDfName := parentWorkflow._newAnonymousDataFrameId()
|
|
1296
|
+
parentWorkflow._addStep(func(ctx) {
|
|
1297
|
+
return {
|
|
1298
|
+
type: "filter",
|
|
1299
|
+
inputTable: dfName,
|
|
1300
|
+
outputTable: outputDfName,
|
|
1301
|
+
condition: condition._getExpression(ctx)
|
|
1302
|
+
}
|
|
1303
|
+
})
|
|
1304
|
+
return _newDataFrame(parentWorkflow, outputDfName)
|
|
1305
|
+
},
|
|
1306
|
+
|
|
1307
|
+
|
|
1308
|
+
|
|
1309
|
+
|
|
1310
|
+
|
|
1311
|
+
|
|
1312
|
+
|
|
1313
|
+
|
|
1314
|
+
|
|
1315
|
+
limit: func(nRows) {
|
|
1316
|
+
ll.assert(is_int(nRows) && nRows > 0, "limit argument must be a positive number.")
|
|
1317
|
+
|
|
968
1318
|
outputDfName := parentWorkflow._newAnonymousDataFrameId()
|
|
969
1319
|
parentWorkflow.addRawStep({
|
|
970
|
-
type: "
|
|
1320
|
+
type: "limit",
|
|
971
1321
|
inputTable: dfName,
|
|
972
1322
|
outputTable: outputDfName,
|
|
973
|
-
|
|
1323
|
+
n: nRows
|
|
974
1324
|
})
|
|
975
1325
|
return _newDataFrame(parentWorkflow, outputDfName)
|
|
976
1326
|
},
|
|
@@ -1029,60 +1379,58 @@ _newDataFrame = func(parentWorkflow, dfName) {
|
|
|
1029
1379
|
ll.panic("sort 'nulls_last' array length (%d) must match the number of sort keys (%d).", len(nullsLastOpt), len(by))
|
|
1030
1380
|
}
|
|
1031
1381
|
|
|
1382
|
+
outputDfName := parentWorkflow._newAnonymousDataFrameId()
|
|
1032
1383
|
|
|
1384
|
+
parentWorkflow._addStep(func(ctx) {
|
|
1385
|
+
sortDirectives := []
|
|
1386
|
+
for i, item in by {
|
|
1387
|
+
directive := { value: undefined }
|
|
1033
1388
|
|
|
1034
|
-
|
|
1035
|
-
|
|
1036
|
-
|
|
1389
|
+
if is_string(item) {
|
|
1390
|
+
directive.value = exp.col(item)._getExpression(ctx)
|
|
1391
|
+
} else if exp._isExpression(item) {
|
|
1392
|
+
directive.value = item._getExpression(ctx)
|
|
1393
|
+
} else {
|
|
1394
|
+
ll.panic("Invalid sort key at index %d in array: Expected a column name (string) or an expression object, got %T", i, item)
|
|
1395
|
+
}
|
|
1037
1396
|
|
|
1038
|
-
if is_string(item) {
|
|
1039
|
-
directive.value = exp.col(item).getExpression()
|
|
1040
|
-
} else if exp._isExpression(item) {
|
|
1041
|
-
directive.value = item.getExpression()
|
|
1042
|
-
} else {
|
|
1043
|
-
ll.panic("Invalid sort key at index %d in array: Expected a column name (string) or an expression object, got %T", i, item)
|
|
1044
|
-
}
|
|
1045
1397
|
|
|
1398
|
+
itemDesc := false // Polars default
|
|
1399
|
+
if is_array(descendingOpt) {
|
|
1400
|
+
val := descendingOpt[i]
|
|
1401
|
+
ll.assert(is_bool(val), "Elements of 'descending' array (at index %d) must be booleans. Got: %T", i, val)
|
|
1402
|
+
itemDesc = val
|
|
1403
|
+
} else if is_bool(descendingOpt) {
|
|
1404
|
+
itemDesc = descendingOpt
|
|
1405
|
+
}
|
|
1406
|
+
if itemDesc { // Only include if true, as PTabler 'descending' defaults to false
|
|
1407
|
+
directive.descending = true
|
|
1408
|
+
}
|
|
1046
1409
|
|
|
1047
|
-
itemDesc := false // Polars default
|
|
1048
|
-
if is_array(descendingOpt) {
|
|
1049
|
-
val := descendingOpt[i]
|
|
1050
|
-
ll.assert(is_bool(val), "Elements of 'descending' array (at index %d) must be booleans. Got: %T", i, val)
|
|
1051
|
-
itemDesc = val
|
|
1052
|
-
} else if is_bool(descendingOpt) {
|
|
1053
|
-
itemDesc = descendingOpt
|
|
1054
|
-
}
|
|
1055
|
-
if itemDesc { // Only include if true, as PTabler 'descending' defaults to false
|
|
1056
|
-
directive.descending = true
|
|
1057
|
-
}
|
|
1058
1410
|
|
|
1411
|
+
itemNullsLast := undefined // Let PTabler use its default if not specified
|
|
1412
|
+
if is_array(nullsLastOpt) {
|
|
1413
|
+
val := nullsLastOpt[i]
|
|
1414
|
+
ll.assert(is_bool(val), "Elements of 'nulls_last' array (at index %d) must be booleans. Got: %T", i, val)
|
|
1415
|
+
itemNullsLast = val
|
|
1416
|
+
} else if is_bool(nullsLastOpt) {
|
|
1417
|
+
itemNullsLast = nullsLastOpt
|
|
1418
|
+
}
|
|
1059
1419
|
|
|
1060
|
-
|
|
1061
|
-
|
|
1062
|
-
|
|
1063
|
-
ll.assert(is_bool(val), "Elements of 'nulls_last' array (at index %d) must be booleans. Got: %T", i, val)
|
|
1064
|
-
itemNullsLast = val
|
|
1065
|
-
} else if is_bool(nullsLastOpt) {
|
|
1066
|
-
itemNullsLast = nullsLastOpt
|
|
1067
|
-
}
|
|
1420
|
+
if !is_undefined(itemNullsLast) { // Only include if explicitly set
|
|
1421
|
+
directive.nullsLast = itemNullsLast
|
|
1422
|
+
}
|
|
1068
1423
|
|
|
1069
|
-
|
|
1070
|
-
directive.nullsLast = itemNullsLast
|
|
1424
|
+
sortDirectives = append(sortDirectives, directive)
|
|
1071
1425
|
}
|
|
1072
1426
|
|
|
1073
|
-
|
|
1074
|
-
|
|
1075
|
-
|
|
1076
|
-
|
|
1077
|
-
|
|
1078
|
-
|
|
1079
|
-
|
|
1080
|
-
inputTable: dfName,
|
|
1081
|
-
outputTable: outputDfName,
|
|
1082
|
-
by: sortDirectives
|
|
1083
|
-
}
|
|
1084
|
-
|
|
1085
|
-
parentWorkflow.addRawStep(sortStep)
|
|
1427
|
+
return {
|
|
1428
|
+
type: "sort",
|
|
1429
|
+
inputTable: dfName,
|
|
1430
|
+
outputTable: outputDfName,
|
|
1431
|
+
by: sortDirectives
|
|
1432
|
+
}
|
|
1433
|
+
})
|
|
1086
1434
|
return _newDataFrame(parentWorkflow, outputDfName)
|
|
1087
1435
|
},
|
|
1088
1436
|
|
|
@@ -1438,10 +1786,12 @@ treeJoin := func(dataframes, opts) {
|
|
|
1438
1786
|
}
|
|
1439
1787
|
|
|
1440
1788
|
export ll.toStrict({
|
|
1789
|
+
p: p,
|
|
1441
1790
|
workflow: workflow,
|
|
1442
1791
|
concat: concat,
|
|
1443
1792
|
treeJoin: treeJoin,
|
|
1444
1793
|
|
|
1794
|
+
axis: exp.axis,
|
|
1445
1795
|
col: exp.col,
|
|
1446
1796
|
lit: exp.lit,
|
|
1447
1797
|
concatStr: exp.concatStr,
|
|
@@ -1453,5 +1803,6 @@ export ll.toStrict({
|
|
|
1453
1803
|
or: exp.or,
|
|
1454
1804
|
rank: exp.rank,
|
|
1455
1805
|
when: exp.when,
|
|
1456
|
-
rawExp: exp.rawExp
|
|
1806
|
+
rawExp: exp.rawExp,
|
|
1807
|
+
sc: exp.sc
|
|
1457
1808
|
})
|