@platforma-sdk/workflow-tengo 4.14.1 → 4.16.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/tengo/lib/pframes.data.lib.tengo +187 -3
- package/dist/tengo/lib/pt.expression.lib.tengo +35 -0
- package/dist/tengo/lib/pt.lib.tengo +243 -43
- package/dist/tengo/lib/slices.lib.tengo +37 -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.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-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/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 +3 -3
|
@@ -98,9 +98,12 @@ parseData := func(inputParam, ...optsRaw) {
|
|
|
98
98
|
state.keyLength = resourceData.keyLength
|
|
99
99
|
state.relevantTopLayerKeyLength = state.keyLength
|
|
100
100
|
} else if state.resourceType == pConstants.RTYPE_P_COLUMN_DATA_JSON_PARTITIONED ||
|
|
101
|
-
state.resourceType == pConstants.RTYPE_P_COLUMN_DATA_BINARY_PARTITIONED
|
|
102
|
-
|
|
101
|
+
state.resourceType == pConstants.RTYPE_P_COLUMN_DATA_BINARY_PARTITIONED {
|
|
102
|
+
state.partitionKeyLength = resourceData.partitionKeyLength
|
|
103
|
+
state.relevantTopLayerKeyLength = state.partitionKeyLength
|
|
104
|
+
} else if state.resourceType == pConstants.RTYPE_P_COLUMN_DATA_RESOURCE_MAP_PARTITIONED {
|
|
103
105
|
state.partitionKeyLength = resourceData.partitionKeyLength
|
|
106
|
+
state.keyLength = resourceData.keyLength
|
|
104
107
|
state.relevantTopLayerKeyLength = state.partitionKeyLength
|
|
105
108
|
} else if state.resourceType == pConstants.RTYPE_P_COLUMN_DATA_JSON_SUPER_PARTITIONED ||
|
|
106
109
|
state.resourceType == pConstants.RTYPE_P_COLUMN_DATA_BINARY_SUPER_PARTITIONED {
|
|
@@ -188,26 +191,86 @@ _createPColumnDataInstance = func(state) {
|
|
|
188
191
|
self := undefined
|
|
189
192
|
|
|
190
193
|
self = ll.toStrict({
|
|
194
|
+
|
|
195
|
+
|
|
196
|
+
|
|
197
|
+
|
|
198
|
+
|
|
191
199
|
resource: state.resource,
|
|
192
200
|
|
|
201
|
+
|
|
202
|
+
|
|
203
|
+
|
|
204
|
+
|
|
193
205
|
parsed: state.parsed,
|
|
194
206
|
|
|
207
|
+
|
|
208
|
+
|
|
209
|
+
|
|
210
|
+
|
|
211
|
+
|
|
212
|
+
|
|
213
|
+
|
|
195
214
|
getSpec: func() {
|
|
196
215
|
return state.spec
|
|
197
216
|
},
|
|
198
217
|
|
|
218
|
+
|
|
219
|
+
|
|
220
|
+
|
|
221
|
+
|
|
222
|
+
|
|
199
223
|
getResourceType: func() {
|
|
200
224
|
return state.resourceType
|
|
201
225
|
},
|
|
202
226
|
|
|
227
|
+
|
|
228
|
+
|
|
229
|
+
|
|
230
|
+
|
|
203
231
|
keyLength: state.keyLength,
|
|
232
|
+
|
|
233
|
+
|
|
234
|
+
|
|
235
|
+
|
|
236
|
+
|
|
237
|
+
|
|
204
238
|
partitionKeyLength: state.partitionKeyLength,
|
|
239
|
+
|
|
240
|
+
|
|
241
|
+
|
|
242
|
+
|
|
243
|
+
|
|
205
244
|
superPartitionKeyLength: state.superPartitionKeyLength,
|
|
206
245
|
|
|
246
|
+
|
|
247
|
+
|
|
248
|
+
|
|
249
|
+
|
|
250
|
+
|
|
251
|
+
|
|
252
|
+
|
|
253
|
+
|
|
207
254
|
data: state.data,
|
|
208
255
|
|
|
256
|
+
|
|
257
|
+
|
|
258
|
+
|
|
259
|
+
|
|
260
|
+
|
|
261
|
+
|
|
262
|
+
|
|
209
263
|
parseKey: _createDecodeKeyString,
|
|
210
264
|
|
|
265
|
+
|
|
266
|
+
|
|
267
|
+
|
|
268
|
+
|
|
269
|
+
|
|
270
|
+
|
|
271
|
+
|
|
272
|
+
|
|
273
|
+
|
|
211
274
|
uniqueKeyValues: func(axisIdx) {
|
|
212
275
|
if !state.parsed {
|
|
213
276
|
ll.panic("uniqueKeyValues: resource is not parsed.")
|
|
@@ -224,6 +287,17 @@ _createPColumnDataInstance = func(state) {
|
|
|
224
287
|
return maps.values(uniqueValsMap)
|
|
225
288
|
},
|
|
226
289
|
|
|
290
|
+
|
|
291
|
+
|
|
292
|
+
|
|
293
|
+
|
|
294
|
+
|
|
295
|
+
|
|
296
|
+
|
|
297
|
+
|
|
298
|
+
|
|
299
|
+
|
|
300
|
+
|
|
227
301
|
createDataResource: func() {
|
|
228
302
|
if !is_undefined(state.resource) {
|
|
229
303
|
|
|
@@ -271,6 +345,27 @@ _createPColumnDataInstance = func(state) {
|
|
|
271
345
|
return builder.lockAndBuild()
|
|
272
346
|
},
|
|
273
347
|
|
|
348
|
+
|
|
349
|
+
|
|
350
|
+
|
|
351
|
+
|
|
352
|
+
|
|
353
|
+
|
|
354
|
+
|
|
355
|
+
|
|
356
|
+
|
|
357
|
+
|
|
358
|
+
|
|
359
|
+
|
|
360
|
+
|
|
361
|
+
|
|
362
|
+
|
|
363
|
+
|
|
364
|
+
|
|
365
|
+
|
|
366
|
+
|
|
367
|
+
|
|
368
|
+
|
|
274
369
|
filter: func(slicingParams) {
|
|
275
370
|
if len(slicingParams) == 0 {
|
|
276
371
|
return self
|
|
@@ -465,6 +560,26 @@ _createPColumnDataInstance = func(state) {
|
|
|
465
560
|
ll.panic("Filtering not supported for type %s", state.resourceType)
|
|
466
561
|
}
|
|
467
562
|
},
|
|
563
|
+
|
|
564
|
+
|
|
565
|
+
|
|
566
|
+
|
|
567
|
+
|
|
568
|
+
|
|
569
|
+
|
|
570
|
+
|
|
571
|
+
|
|
572
|
+
|
|
573
|
+
|
|
574
|
+
|
|
575
|
+
|
|
576
|
+
|
|
577
|
+
|
|
578
|
+
|
|
579
|
+
|
|
580
|
+
|
|
581
|
+
|
|
582
|
+
|
|
468
583
|
|
|
469
584
|
partition: func(...axesToPartitionBy) {
|
|
470
585
|
if !state.parsed {
|
|
@@ -511,6 +626,20 @@ _createPColumnDataInstance = func(state) {
|
|
|
511
626
|
return resultPartitions
|
|
512
627
|
},
|
|
513
628
|
|
|
629
|
+
|
|
630
|
+
|
|
631
|
+
|
|
632
|
+
|
|
633
|
+
|
|
634
|
+
|
|
635
|
+
|
|
636
|
+
|
|
637
|
+
|
|
638
|
+
|
|
639
|
+
|
|
640
|
+
|
|
641
|
+
|
|
642
|
+
|
|
514
643
|
forEach: func(userCallback) {
|
|
515
644
|
if !state.parsed {
|
|
516
645
|
ll.panic("forEach: resource is not parsed.")
|
|
@@ -525,6 +654,61 @@ _createPColumnDataInstance = func(state) {
|
|
|
525
654
|
})
|
|
526
655
|
},
|
|
527
656
|
|
|
657
|
+
|
|
658
|
+
|
|
659
|
+
|
|
660
|
+
|
|
661
|
+
|
|
662
|
+
|
|
663
|
+
|
|
664
|
+
|
|
665
|
+
|
|
666
|
+
|
|
667
|
+
|
|
668
|
+
|
|
669
|
+
|
|
670
|
+
|
|
671
|
+
|
|
672
|
+
|
|
673
|
+
|
|
674
|
+
|
|
675
|
+
forEachPartition: func(axesToPartitionBy, userCallback) {
|
|
676
|
+
if !state.parsed {
|
|
677
|
+
ll.panic("forEachPartition: resource is not parsed.")
|
|
678
|
+
}
|
|
679
|
+
if !is_array(axesToPartitionBy) {
|
|
680
|
+
ll.panic("forEachPartition: first argument must be an array of axis indices.")
|
|
681
|
+
}
|
|
682
|
+
if !is_callable(userCallback) {
|
|
683
|
+
ll.panic("forEachPartition: second argument must be a function.")
|
|
684
|
+
}
|
|
685
|
+
|
|
686
|
+
partitions := self.partition(axesToPartitionBy...)
|
|
687
|
+
|
|
688
|
+
maps.forEach(partitions, func(partitionKeyStr, partitionPColumnData) {
|
|
689
|
+
decodedPartitionKey := json.decode(partitionKeyStr)
|
|
690
|
+
userCallback(decodedPartitionKey, partitionPColumnData)
|
|
691
|
+
})
|
|
692
|
+
},
|
|
693
|
+
|
|
694
|
+
|
|
695
|
+
|
|
696
|
+
|
|
697
|
+
|
|
698
|
+
|
|
699
|
+
|
|
700
|
+
|
|
701
|
+
|
|
702
|
+
|
|
703
|
+
|
|
704
|
+
|
|
705
|
+
|
|
706
|
+
|
|
707
|
+
|
|
708
|
+
|
|
709
|
+
|
|
710
|
+
|
|
711
|
+
|
|
528
712
|
flatten: func() {
|
|
529
713
|
if !state.parsed {
|
|
530
714
|
ll.panic("flatten: resource is not parsed.")
|
|
@@ -532,7 +716,7 @@ _createPColumnDataInstance = func(state) {
|
|
|
532
716
|
|
|
533
717
|
isJsonSuper := state.resourceType == pConstants.RTYPE_P_COLUMN_DATA_JSON_SUPER_PARTITIONED
|
|
534
718
|
isBinarySuper := state.resourceType == pConstants.RTYPE_P_COLUMN_DATA_BINARY_SUPER_PARTITIONED
|
|
535
|
-
isResourceMapSuper := state.resourceType == pConstants.
|
|
719
|
+
isResourceMapSuper := state.resourceType == pConstants.RTYPE_P_COLUMN_DATA_RESOURCE_MAP_PARTITIONED
|
|
536
720
|
|
|
537
721
|
if !(isJsonSuper || isBinarySuper || isResourceMapSuper) {
|
|
538
722
|
return self // Not a type that can be flattened
|
|
@@ -323,6 +323,41 @@ _newExpression = func(expression, currentAlias) {
|
|
|
323
323
|
isNotNull: func() { return unaryOp("is_not_na") }, // Polars: is_not_null(), TS: is_not_na
|
|
324
324
|
|
|
325
325
|
|
|
326
|
+
|
|
327
|
+
|
|
328
|
+
|
|
329
|
+
|
|
330
|
+
|
|
331
|
+
|
|
332
|
+
|
|
333
|
+
|
|
334
|
+
|
|
335
|
+
|
|
336
|
+
|
|
337
|
+
|
|
338
|
+
|
|
339
|
+
|
|
340
|
+
|
|
341
|
+
structField: func(...fieldNames) {
|
|
342
|
+
ll.assert(len(fieldNames) > 0, "structField expects at least one field name")
|
|
343
|
+
for fieldName in fieldNames {
|
|
344
|
+
ll.assert(is_string(fieldName), "All field names must be strings, got: %T", fieldName)
|
|
345
|
+
}
|
|
346
|
+
|
|
347
|
+
|
|
348
|
+
currentExpr := expression
|
|
349
|
+
for fieldName in fieldNames {
|
|
350
|
+
currentExpr = {
|
|
351
|
+
type: "struct_field",
|
|
352
|
+
struct: currentExpr,
|
|
353
|
+
fields: fieldName
|
|
354
|
+
}
|
|
355
|
+
}
|
|
356
|
+
|
|
357
|
+
return _newExpression(currentExpr, undefined)
|
|
358
|
+
},
|
|
359
|
+
|
|
360
|
+
|
|
326
361
|
|
|
327
362
|
strToUpper: func() { return unaryStrOp("to_upper") },
|
|
328
363
|
|
|
@@ -52,6 +52,19 @@ workflowCounter := 0
|
|
|
52
52
|
|
|
53
53
|
|
|
54
54
|
|
|
55
|
+
_detectFormatFromFileName := func(fileName) {
|
|
56
|
+
if text.has_suffix(fileName, ".csv") {
|
|
57
|
+
return "csv"
|
|
58
|
+
} else if text.has_suffix(fileName, ".tsv") {
|
|
59
|
+
return "tsv"
|
|
60
|
+
} else if text.has_suffix(fileName, ".ndjson") || text.has_suffix(fileName, ".jsonl") {
|
|
61
|
+
return "ndjson"
|
|
62
|
+
}
|
|
63
|
+
return undefined // No format detected
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
|
|
67
|
+
|
|
55
68
|
|
|
56
69
|
|
|
57
70
|
|
|
@@ -118,6 +131,16 @@ workflow := func() {
|
|
|
118
131
|
|
|
119
132
|
|
|
120
133
|
|
|
134
|
+
|
|
135
|
+
|
|
136
|
+
|
|
137
|
+
|
|
138
|
+
|
|
139
|
+
|
|
140
|
+
|
|
141
|
+
|
|
142
|
+
|
|
143
|
+
|
|
121
144
|
|
|
122
145
|
|
|
123
146
|
|
|
@@ -145,9 +168,11 @@ workflow := func() {
|
|
|
145
168
|
inputXsvType := undefined
|
|
146
169
|
finalDataFrameId := undefined
|
|
147
170
|
finalFileName := undefined
|
|
148
|
-
|
|
171
|
+
finalFormat := undefined
|
|
149
172
|
inferSchemaOpt := undefined
|
|
150
173
|
optionsSchema := undefined
|
|
174
|
+
nRowsOpt := undefined
|
|
175
|
+
ignoreErrorsOpt := undefined
|
|
151
176
|
|
|
152
177
|
isStructuralInput := is_map(frameInput) && !is_undefined(frameInput.file)
|
|
153
178
|
|
|
@@ -162,7 +187,7 @@ workflow := func() {
|
|
|
162
187
|
inputSchema = frameInput.schema
|
|
163
188
|
}
|
|
164
189
|
|
|
165
|
-
|
|
190
|
+
finalFormat = inputXsvType
|
|
166
191
|
} else {
|
|
167
192
|
if smart.isReference(frameInput) {
|
|
168
193
|
fileRef = frameInput
|
|
@@ -172,15 +197,25 @@ workflow := func() {
|
|
|
172
197
|
}
|
|
173
198
|
}
|
|
174
199
|
|
|
175
|
-
ll.assert(is_undefined(finalXsvType) || is_undefined(opts.xsvType), "xsvType option cannot be used with structural input as xsvType is already defined in the input structure")
|
|
176
200
|
|
|
177
|
-
if !is_undefined(opts.
|
|
178
|
-
|
|
201
|
+
if !is_undefined(opts.format) {
|
|
202
|
+
if opts.format != "csv" && opts.format != "tsv" && opts.format != "ndjson" {
|
|
203
|
+
ll.panic("format must be 'csv', 'tsv', or 'ndjson'. Got: %v", opts.format)
|
|
204
|
+
}
|
|
205
|
+
if !is_undefined(finalFormat) && finalFormat != opts.format {
|
|
206
|
+
ll.panic("format and xsvType from structural input cannot be used together. Got: %v and %v", opts.format, finalFormat)
|
|
207
|
+
}
|
|
208
|
+
finalFormat = opts.format
|
|
209
|
+
} else if !is_undefined(opts.xsvType) {
|
|
210
|
+
if opts.xsvType != "csv" && opts.xsvType != "tsv" {
|
|
211
|
+
ll.panic("xsvType must be 'csv' or 'tsv'. Got: %v", opts.xsvType)
|
|
212
|
+
}
|
|
213
|
+
if !is_undefined(finalFormat) && finalFormat != opts.xsvType {
|
|
214
|
+
ll.panic("opts.xsvType and xsvType from structural input cannot be used together. Got: %v and %v", opts.xsvType, finalFormat)
|
|
215
|
+
}
|
|
216
|
+
finalFormat = opts.xsvType
|
|
179
217
|
}
|
|
180
218
|
|
|
181
|
-
ll.assert(!is_undefined(finalXsvType), "xsvType option is required for direct file input")
|
|
182
|
-
ll.assert(finalXsvType == "csv" || finalXsvType == "tsv", "xsvType must be 'csv' or 'tsv'")
|
|
183
|
-
|
|
184
219
|
if !is_undefined(opts.id) {
|
|
185
220
|
finalDataFrameId = opts.id
|
|
186
221
|
} else {
|
|
@@ -189,11 +224,21 @@ workflow := func() {
|
|
|
189
224
|
|
|
190
225
|
if !is_undefined(opts.fileName) {
|
|
191
226
|
finalFileName = opts.fileName
|
|
227
|
+
if is_undefined(finalFormat) {
|
|
228
|
+
detectedFormat := _detectFormatFromFileName(finalFileName)
|
|
229
|
+
if !is_undefined(detectedFormat) {
|
|
230
|
+
finalFormat = detectedFormat
|
|
231
|
+
}
|
|
232
|
+
}
|
|
192
233
|
} else {
|
|
193
|
-
finalFileName = self._newAnonymousFileId(
|
|
234
|
+
finalFileName = self._newAnonymousFileId(finalFormat)
|
|
194
235
|
}
|
|
195
236
|
|
|
196
237
|
|
|
238
|
+
if is_undefined(finalFormat) {
|
|
239
|
+
ll.panic("Unable to determine file format. Please specify 'format' or 'xsvType' option, or use a fileName with a recognizable extension (.csv, .tsv, .ndjson, .jsonl)")
|
|
240
|
+
}
|
|
241
|
+
|
|
197
242
|
if !is_undefined(opts.inferSchema) {
|
|
198
243
|
if !is_bool(opts.inferSchema) {
|
|
199
244
|
ll.panic("'inferSchema' option must be a boolean. Got: %T", opts.inferSchema)
|
|
@@ -201,7 +246,6 @@ workflow := func() {
|
|
|
201
246
|
inferSchemaOpt = opts.inferSchema
|
|
202
247
|
}
|
|
203
248
|
|
|
204
|
-
|
|
205
249
|
if !is_undefined(opts.schema) {
|
|
206
250
|
if !is_array(opts.schema) {
|
|
207
251
|
ll.panic("'schema' option (from options map) must be an array. Got: %T", opts.schema)
|
|
@@ -209,6 +253,21 @@ workflow := func() {
|
|
|
209
253
|
optionsSchema = opts.schema
|
|
210
254
|
}
|
|
211
255
|
|
|
256
|
+
if !is_undefined(opts.nRows) {
|
|
257
|
+
if !is_int(opts.nRows) || opts.nRows < 0 {
|
|
258
|
+
ll.panic("'nRows' option must be a non-negative integer. Got: %v", opts.nRows)
|
|
259
|
+
}
|
|
260
|
+
nRowsOpt = opts.nRows
|
|
261
|
+
}
|
|
262
|
+
|
|
263
|
+
if !is_undefined(opts.ignoreErrors) {
|
|
264
|
+
if !is_bool(opts.ignoreErrors) {
|
|
265
|
+
ll.panic("'ignoreErrors' option must be a boolean. Got: %T", opts.ignoreErrors)
|
|
266
|
+
}
|
|
267
|
+
ignoreErrorsOpt = opts.ignoreErrors
|
|
268
|
+
}
|
|
269
|
+
|
|
270
|
+
|
|
212
271
|
if !is_undefined(fileRef) && !smart.isReference(fileRef) {
|
|
213
272
|
ll.panic("frameInput.file (for structural input) or frameInput (for direct reference) must be a valid resource reference. Got: %v", fileRef)
|
|
214
273
|
} else if !is_undefined(fileContent) && !is_string(fileContent) {
|
|
@@ -221,16 +280,26 @@ workflow := func() {
|
|
|
221
280
|
inFiles = append(inFiles, { content: fileContent, name: finalFileName })
|
|
222
281
|
}
|
|
223
282
|
|
|
224
|
-
readCsvStep := {
|
|
225
|
-
type: "read_csv",
|
|
226
|
-
file: finalFileName,
|
|
227
|
-
name: finalDataFrameId
|
|
228
|
-
}
|
|
229
283
|
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
284
|
+
step := undefined
|
|
285
|
+
if finalFormat == "ndjson" {
|
|
286
|
+
step = {
|
|
287
|
+
type: "read_ndjson",
|
|
288
|
+
file: finalFileName,
|
|
289
|
+
name: finalDataFrameId
|
|
290
|
+
}
|
|
291
|
+
} else { // csv or tsv
|
|
292
|
+
step = {
|
|
293
|
+
type: "read_csv",
|
|
294
|
+
file: finalFileName,
|
|
295
|
+
name: finalDataFrameId
|
|
296
|
+
}
|
|
297
|
+
|
|
298
|
+
if finalFormat == "csv" {
|
|
299
|
+
step.delimiter = ","
|
|
300
|
+
} else if finalFormat == "tsv" {
|
|
301
|
+
step.delimiter = "\t"
|
|
302
|
+
}
|
|
234
303
|
}
|
|
235
304
|
|
|
236
305
|
|
|
@@ -241,17 +310,24 @@ workflow := func() {
|
|
|
241
310
|
finalSchemaToUse = inputSchema
|
|
242
311
|
}
|
|
243
312
|
|
|
313
|
+
|
|
244
314
|
if !is_undefined(finalSchemaToUse) {
|
|
245
|
-
|
|
315
|
+
step.schema = finalSchemaToUse
|
|
246
316
|
}
|
|
247
317
|
|
|
318
|
+
if !is_undefined(inferSchemaOpt) && inferSchemaOpt == false {
|
|
319
|
+
step.inferSchema = false
|
|
320
|
+
}
|
|
248
321
|
|
|
322
|
+
if !is_undefined(ignoreErrorsOpt) && ignoreErrorsOpt == true {
|
|
323
|
+
step.ignoreErrors = true
|
|
324
|
+
}
|
|
249
325
|
|
|
250
|
-
if !is_undefined(
|
|
251
|
-
|
|
326
|
+
if !is_undefined(nRowsOpt) {
|
|
327
|
+
step.nRows = nRowsOpt
|
|
252
328
|
}
|
|
253
329
|
|
|
254
|
-
self.addRawStep(
|
|
330
|
+
self.addRawStep(step)
|
|
255
331
|
return _newDataFrame(self, finalDataFrameId)
|
|
256
332
|
},
|
|
257
333
|
|
|
@@ -465,32 +541,52 @@ _newDataFrame = func(parentWorkflow, dfName) {
|
|
|
465
541
|
opts = options[0]
|
|
466
542
|
}
|
|
467
543
|
|
|
468
|
-
|
|
469
|
-
if text.has_suffix(outputFile, ".csv") {
|
|
470
|
-
delimiter = ","
|
|
471
|
-
} else if text.has_suffix(outputFile, ".tsv") {
|
|
472
|
-
delimiter = "\t"
|
|
473
|
-
}
|
|
544
|
+
finalFormat := undefined
|
|
474
545
|
|
|
475
|
-
|
|
476
|
-
|
|
477
|
-
|
|
478
|
-
|
|
479
|
-
|
|
480
|
-
|
|
481
|
-
|
|
546
|
+
|
|
547
|
+
if !is_undefined(opts.format) {
|
|
548
|
+
if opts.format != "csv" && opts.format != "tsv" && opts.format != "ndjson" {
|
|
549
|
+
ll.panic("format must be 'csv', 'tsv', or 'ndjson'. Got: %v", opts.format)
|
|
550
|
+
}
|
|
551
|
+
finalFormat = opts.format
|
|
552
|
+
} else if !is_undefined(opts.xsvType) {
|
|
553
|
+
if opts.xsvType != "csv" && opts.xsvType != "tsv" {
|
|
554
|
+
ll.panic("xsvType must be 'csv' or 'tsv'. Got: %v", opts.xsvType)
|
|
555
|
+
}
|
|
556
|
+
finalFormat = opts.xsvType
|
|
557
|
+
} else {
|
|
558
|
+
|
|
559
|
+
detectedFormat := _detectFormatFromFileName(outputFile)
|
|
560
|
+
if !is_undefined(detectedFormat) {
|
|
561
|
+
finalFormat = detectedFormat
|
|
482
562
|
}
|
|
483
563
|
}
|
|
484
564
|
|
|
485
|
-
if is_undefined(
|
|
486
|
-
ll.panic("
|
|
565
|
+
if is_undefined(finalFormat) {
|
|
566
|
+
ll.panic("Unable to determine output format from file extension. Please specify 'format' or 'xsvType' option, or use a file name with a recognizable extension (.csv, .tsv, .ndjson, .jsonl)")
|
|
487
567
|
}
|
|
488
568
|
|
|
489
|
-
step :=
|
|
490
|
-
|
|
491
|
-
|
|
492
|
-
|
|
493
|
-
|
|
569
|
+
step := undefined
|
|
570
|
+
if finalFormat == "ndjson" {
|
|
571
|
+
step = {
|
|
572
|
+
type: "write_ndjson",
|
|
573
|
+
table: dfName,
|
|
574
|
+
file: outputFile
|
|
575
|
+
}
|
|
576
|
+
} else { // csv or tsv
|
|
577
|
+
delimiter := undefined
|
|
578
|
+
if finalFormat == "csv" {
|
|
579
|
+
delimiter = ","
|
|
580
|
+
} else if finalFormat == "tsv" {
|
|
581
|
+
delimiter = "\t"
|
|
582
|
+
}
|
|
583
|
+
|
|
584
|
+
step = {
|
|
585
|
+
type: "write_csv",
|
|
586
|
+
table: dfName,
|
|
587
|
+
file: outputFile,
|
|
588
|
+
delimiter: delimiter
|
|
589
|
+
}
|
|
494
590
|
}
|
|
495
591
|
|
|
496
592
|
if !is_undefined(opts.columns) {
|
|
@@ -589,6 +685,19 @@ _newDataFrame = func(parentWorkflow, dfName) {
|
|
|
589
685
|
|
|
590
686
|
|
|
591
687
|
|
|
688
|
+
|
|
689
|
+
|
|
690
|
+
|
|
691
|
+
|
|
692
|
+
|
|
693
|
+
|
|
694
|
+
|
|
695
|
+
|
|
696
|
+
|
|
697
|
+
|
|
698
|
+
|
|
699
|
+
|
|
700
|
+
|
|
592
701
|
|
|
593
702
|
|
|
594
703
|
|
|
@@ -616,6 +725,14 @@ _newDataFrame = func(parentWorkflow, dfName) {
|
|
|
616
725
|
|
|
617
726
|
|
|
618
727
|
|
|
728
|
+
|
|
729
|
+
|
|
730
|
+
|
|
731
|
+
|
|
732
|
+
|
|
733
|
+
|
|
734
|
+
|
|
735
|
+
|
|
619
736
|
|
|
620
737
|
|
|
621
738
|
|
|
@@ -1078,9 +1195,92 @@ concat := func(dataframes, ...optionsRaw) {
|
|
|
1078
1195
|
return _newDataFrame(parentWorkflow, outputDfName)
|
|
1079
1196
|
}
|
|
1080
1197
|
|
|
1198
|
+
|
|
1199
|
+
|
|
1200
|
+
|
|
1201
|
+
|
|
1202
|
+
|
|
1203
|
+
|
|
1204
|
+
|
|
1205
|
+
|
|
1206
|
+
|
|
1207
|
+
|
|
1208
|
+
|
|
1209
|
+
|
|
1210
|
+
|
|
1211
|
+
|
|
1212
|
+
|
|
1213
|
+
|
|
1214
|
+
|
|
1215
|
+
|
|
1216
|
+
|
|
1217
|
+
|
|
1218
|
+
|
|
1219
|
+
|
|
1220
|
+
|
|
1221
|
+
|
|
1222
|
+
|
|
1223
|
+
|
|
1224
|
+
|
|
1225
|
+
|
|
1226
|
+
|
|
1227
|
+
treeJoin := func(dataframes, opts) {
|
|
1228
|
+
ll.assert(is_array(dataframes), "First argument to treeJoin must be an array of DataFrame objects.")
|
|
1229
|
+
ll.assert(len(dataframes) > 0, "DataFrame array for treeJoin cannot be empty.")
|
|
1230
|
+
|
|
1231
|
+
if !is_map(opts) || is_undefined(opts.on) {
|
|
1232
|
+
ll.panic("treeJoin requires options with 'on' field")
|
|
1233
|
+
}
|
|
1234
|
+
if !is_string(opts.on) {
|
|
1235
|
+
ll.panic("'on' option must be a string")
|
|
1236
|
+
}
|
|
1237
|
+
|
|
1238
|
+
|
|
1239
|
+
if len(dataframes) == 1 {
|
|
1240
|
+
return dataframes[0]
|
|
1241
|
+
}
|
|
1242
|
+
|
|
1243
|
+
|
|
1244
|
+
parentWorkflow := undefined
|
|
1245
|
+
for i, df in dataframes {
|
|
1246
|
+
if is_undefined(df) || is_undefined(df._getDfName) || is_undefined(df._getWorkflow) {
|
|
1247
|
+
ll.panic("Argument at index %d in dataframes array is not a valid DataFrame object.", i)
|
|
1248
|
+
}
|
|
1249
|
+
currentWorkflow := df._getWorkflow()
|
|
1250
|
+
if is_undefined(parentWorkflow) {
|
|
1251
|
+
parentWorkflow = currentWorkflow
|
|
1252
|
+
} else if currentWorkflow._wfId != parentWorkflow._wfId {
|
|
1253
|
+
ll.panic("All DataFrames in a treeJoin operation must belong to the same workflow.")
|
|
1254
|
+
}
|
|
1255
|
+
}
|
|
1256
|
+
|
|
1257
|
+
pfs := dataframes
|
|
1258
|
+
|
|
1259
|
+
|
|
1260
|
+
for len(pfs) > 1 {
|
|
1261
|
+
nextPfs := []
|
|
1262
|
+
|
|
1263
|
+
for i := 1; i < len(pfs); i += 2 {
|
|
1264
|
+
pf1 := pfs[i - 1]
|
|
1265
|
+
pf2 := pfs[i]
|
|
1266
|
+
joined := pf1.join(pf2, { on: opts.on, how: "full", coalesce: true })
|
|
1267
|
+
nextPfs = append(nextPfs, joined)
|
|
1268
|
+
}
|
|
1269
|
+
|
|
1270
|
+
if len(pfs) % 2 == 1 {
|
|
1271
|
+
nextPfs = append(nextPfs, pfs[len(pfs) - 1])
|
|
1272
|
+
}
|
|
1273
|
+
pfs = nextPfs
|
|
1274
|
+
}
|
|
1275
|
+
|
|
1276
|
+
ll.assert(len(pfs) == 1, "Expected exactly one data frame, got " + string(len(pfs)))
|
|
1277
|
+
return pfs[0]
|
|
1278
|
+
}
|
|
1279
|
+
|
|
1081
1280
|
export ll.toStrict({
|
|
1082
1281
|
workflow: workflow,
|
|
1083
1282
|
concat: concat,
|
|
1283
|
+
treeJoin: treeJoin,
|
|
1084
1284
|
|
|
1085
1285
|
col: exp.col,
|
|
1086
1286
|
lit: exp.lit,
|
|
@@ -153,6 +153,42 @@ reduce := func(slice, fn, ...init) {
|
|
|
153
153
|
}
|
|
154
154
|
|
|
155
155
|
|
|
156
|
+
|
|
157
|
+
|
|
158
|
+
|
|
159
|
+
|
|
160
|
+
|
|
161
|
+
|
|
162
|
+
|
|
163
|
+
|
|
164
|
+
flatten := func(slice, ...opts) {
|
|
165
|
+
if is_undefined(slice) {
|
|
166
|
+
return []
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
deep := false
|
|
170
|
+
if len(opts) > 0 && !is_undefined(opts[0]) && !is_undefined(opts[0].deep) {
|
|
171
|
+
deep = opts[0].deep
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
result := []
|
|
175
|
+
for _, elem in slice {
|
|
176
|
+
if is_array(elem) {
|
|
177
|
+
if deep {
|
|
178
|
+
flattened := flatten(elem, {deep: true})
|
|
179
|
+
result = append(result, flattened...)
|
|
180
|
+
} else {
|
|
181
|
+
result = append(result, elem...)
|
|
182
|
+
}
|
|
183
|
+
} else {
|
|
184
|
+
ll.panic("flatten: expected array element but got %s", type_name(elem))
|
|
185
|
+
}
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
return result
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
|
|
156
192
|
every := func(slice, fn) {
|
|
157
193
|
for _, elem in slice {
|
|
158
194
|
if !fn(elem) {
|
|
@@ -322,6 +358,7 @@ export ll.toStrict({
|
|
|
322
358
|
filter: filter,
|
|
323
359
|
map: map,
|
|
324
360
|
reduce: reduce,
|
|
361
|
+
flatten: flatten,
|
|
325
362
|
every: every,
|
|
326
363
|
quickSortInPlace: quickSortInPlace,
|
|
327
364
|
quickSort: quickSort,
|
|
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": "4.
|
|
3
|
+
"version": "4.16.0",
|
|
4
4
|
"description": "Tengo SDK library",
|
|
5
5
|
"types": "./dist/index.d.ts",
|
|
6
6
|
"main": "./dist/index.js",
|
|
@@ -18,10 +18,10 @@
|
|
|
18
18
|
"dependencies": {
|
|
19
19
|
"@milaboratories/software-pframes-conv": "2.2.2",
|
|
20
20
|
"@platforma-open/milaboratories.software-small-binaries": "^1.15.21",
|
|
21
|
-
"@platforma-open/milaboratories.software-ptabler": "1.
|
|
21
|
+
"@platforma-open/milaboratories.software-ptabler": "1.11.0"
|
|
22
22
|
},
|
|
23
23
|
"devDependencies": {
|
|
24
|
-
"@platforma-sdk/tengo-builder": "^2.1.
|
|
24
|
+
"@platforma-sdk/tengo-builder": "^2.1.14"
|
|
25
25
|
},
|
|
26
26
|
"scripts": {
|
|
27
27
|
"type-check": "pl-tengo check",
|