@platforma-sdk/workflow-tengo 4.5.2 → 4.6.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.d.ts +1 -1
- package/dist/index.js +6 -6
- package/dist/index.mjs +6 -6
- package/dist/tengo/lib/pframes.bundle.lib.tengo +37 -300
- package/dist/tengo/lib/pframes.lib.tengo +4 -1
- package/dist/tengo/lib/pframes.spec.lib.tengo +21 -1
- package/dist/tengo/lib/pframes.xsv-builder.lib.tengo +625 -0
- package/dist/tengo/lib/pt.expression.lib.tengo +905 -0
- package/dist/tengo/lib/pt.lib.tengo +980 -0
- package/dist/tengo/lib/tpl.base.lib.tengo +6 -1
- 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/exec.R.create-renv.plj.gz +0 -0
- package/dist/tengo/tpl/exec.exec.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.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 +4 -3
|
@@ -0,0 +1,625 @@
|
|
|
1
|
+
ll := import("@platforma-sdk/workflow-tengo:ll")
|
|
2
|
+
maps := import("@platforma-sdk/workflow-tengo:maps")
|
|
3
|
+
strings := import("@platforma-sdk/workflow-tengo:strings")
|
|
4
|
+
pSpec := import("@platforma-sdk/workflow-tengo:pframes.spec")
|
|
5
|
+
pBuilder := import("@platforma-sdk/workflow-tengo:pframes.builder")
|
|
6
|
+
xsv := import("@platforma-sdk/workflow-tengo:pframes.xsv")
|
|
7
|
+
canonical := import("@platforma-sdk/workflow-tengo:canonical")
|
|
8
|
+
|
|
9
|
+
xsvFileBuilder := func(xsvType) {
|
|
10
|
+
ll.assert(xsvType == "csv" || xsvType == "tsv", "xsvType must be 'csv' or 'tsv'")
|
|
11
|
+
|
|
12
|
+
PTablerNA := "%%NA%%" // Constant for PTabler NA representation
|
|
13
|
+
naValueInternal := "" // Default NA value, can be changed by setNA
|
|
14
|
+
|
|
15
|
+
requests := []
|
|
16
|
+
addedColumnInternalIds := {}
|
|
17
|
+
localAxisHeaderOverrides := []
|
|
18
|
+
distiller := undefined
|
|
19
|
+
nextSequentialId := 1
|
|
20
|
+
nextPFrameKeyIndex := 1
|
|
21
|
+
|
|
22
|
+
getSpecDistiller := func() {
|
|
23
|
+
if is_undefined(distiller) {
|
|
24
|
+
columnsToDistill := []
|
|
25
|
+
for request in requests {
|
|
26
|
+
columnsToDistill = append(columnsToDistill, request.spec)
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
distiller = pSpec.createSpecDistiller(columnsToDistill)
|
|
30
|
+
}
|
|
31
|
+
return distiller
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
|
|
35
|
+
|
|
36
|
+
|
|
37
|
+
parseFlatFrameInput := func(flatFrame) {
|
|
38
|
+
parsedColumns := {}
|
|
39
|
+
processedBaseKeys := {}
|
|
40
|
+
|
|
41
|
+
for key, value in flatFrame {
|
|
42
|
+
if strings.has_suffix(key, ".spec") {
|
|
43
|
+
baseKey := strings.trim_suffix(key, ".spec")
|
|
44
|
+
if is_undefined(parsedColumns[baseKey]) {
|
|
45
|
+
parsedColumns[baseKey] = { key: baseKey }
|
|
46
|
+
}
|
|
47
|
+
parsedColumns[baseKey].spec = value
|
|
48
|
+
processedBaseKeys[baseKey] = true
|
|
49
|
+
} else if strings.has_suffix(key, ".data") {
|
|
50
|
+
baseKey := strings.trim_suffix(key, ".data")
|
|
51
|
+
if is_undefined(parsedColumns[baseKey]) {
|
|
52
|
+
parsedColumns[baseKey] = { key: baseKey }
|
|
53
|
+
}
|
|
54
|
+
parsedColumns[baseKey].data = value
|
|
55
|
+
processedBaseKeys[baseKey] = true
|
|
56
|
+
} else {
|
|
57
|
+
ll.panic("Invalid key format in flat frame input: %s. Expected '.spec' or '.data' suffix.", key)
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
resultArray := []
|
|
62
|
+
for baseKey, colParts in parsedColumns {
|
|
63
|
+
if is_undefined(colParts.spec) {
|
|
64
|
+
ll.panic("Missing .spec for base key '%s' in flat frame input.", baseKey)
|
|
65
|
+
}
|
|
66
|
+
if is_undefined(colParts.data) {
|
|
67
|
+
ll.panic("Missing .data for base key '%s' in flat frame input.", baseKey)
|
|
68
|
+
}
|
|
69
|
+
resultArray = append(resultArray, {
|
|
70
|
+
spec: colParts.spec,
|
|
71
|
+
data: colParts.data,
|
|
72
|
+
key: baseKey
|
|
73
|
+
})
|
|
74
|
+
}
|
|
75
|
+
return resultArray
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
getCanonicalAxisKey := func(axisSpec) {
|
|
79
|
+
|
|
80
|
+
nameStr := axisSpec.name
|
|
81
|
+
typeStr := axisSpec.type
|
|
82
|
+
|
|
83
|
+
domainCanonicalStr := ""
|
|
84
|
+
if is_map(axisSpec.domain) {
|
|
85
|
+
domainCanonicalStr = canonical.encode(axisSpec.domain)
|
|
86
|
+
} else {
|
|
87
|
+
|
|
88
|
+
|
|
89
|
+
domainCanonicalStr = canonical.encode({})
|
|
90
|
+
}
|
|
91
|
+
return nameStr + "|" + typeStr + "|" + domainCanonicalStr
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
findAxisHeaderOverride := func(axisSpec) {
|
|
95
|
+
for _, override in localAxisHeaderOverrides {
|
|
96
|
+
matcher := override[0]
|
|
97
|
+
if matcher.name == axisSpec.name {
|
|
98
|
+
specDistiller := getSpecDistiller()
|
|
99
|
+
discriminativeDomains := specDistiller.getDiscriminativeDomainsSet(axisSpec.name)
|
|
100
|
+
domainsMatch := true
|
|
101
|
+
for domainKey, domainValue in matcher.domain {
|
|
102
|
+
if is_undefined(discriminativeDomains[domainKey]) {
|
|
103
|
+
continue
|
|
104
|
+
}
|
|
105
|
+
if is_undefined(axisSpec.domain) || is_undefined(axisSpec.domain[domainKey]) || axisSpec.domain[domainKey] != domainValue {
|
|
106
|
+
domainsMatch = false
|
|
107
|
+
break
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
if domainsMatch {
|
|
111
|
+
return override[1]
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
return undefined
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
generateLabelForAxis := func(axisSpec) {
|
|
119
|
+
label := findAxisHeaderOverride(axisSpec)
|
|
120
|
+
if !is_undefined(label) {
|
|
121
|
+
return label
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
specDistiller := getSpecDistiller()
|
|
125
|
+
discriminativeDomains := specDistiller.getDiscriminativeDomains(axisSpec.name)
|
|
126
|
+
|
|
127
|
+
if len(discriminativeDomains) == 0 {
|
|
128
|
+
label = strings.substituteSpecialCharacters(axisSpec.name)
|
|
129
|
+
} else {
|
|
130
|
+
|
|
131
|
+
|
|
132
|
+
ll.panic(
|
|
133
|
+
"Cannot deduce header for axis '%s'. Discriminative domains: %s. Please provide a header override using setAxisHeader().",
|
|
134
|
+
axisSpec.name,
|
|
135
|
+
discriminativeDomains
|
|
136
|
+
)
|
|
137
|
+
}
|
|
138
|
+
return label
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
_constructPFrameAndDistillSpecs := func() {
|
|
142
|
+
frameBuilder := pBuilder.pFrameBuilder()
|
|
143
|
+
currentDistiller := getSpecDistiller() // Ensure distiller is created/updated
|
|
144
|
+
processedAxesMap := {}
|
|
145
|
+
|
|
146
|
+
for request in requests {
|
|
147
|
+
distilledSpec := currentDistiller.distill(request.spec)
|
|
148
|
+
|
|
149
|
+
|
|
150
|
+
distilledSpec = maps.deepMerge(distilledSpec, {
|
|
151
|
+
annotations: { "pl7.app/label": request.header }
|
|
152
|
+
})
|
|
153
|
+
|
|
154
|
+
|
|
155
|
+
if !is_undefined(distilledSpec.axesSpec) && is_array(distilledSpec.axesSpec) {
|
|
156
|
+
newAxesSpec := []
|
|
157
|
+
for axis_idx, axis_orig in distilledSpec.axesSpec {
|
|
158
|
+
labelledAxis := maps.deepMerge(axis_orig, {
|
|
159
|
+
annotations: { "pl7.app/label": generateLabelForAxis(axis_orig) }
|
|
160
|
+
})
|
|
161
|
+
newAxesSpec = append(newAxesSpec, labelledAxis)
|
|
162
|
+
|
|
163
|
+
|
|
164
|
+
canonicalKey := getCanonicalAxisKey(labelledAxis) // Pass the whole axis spec
|
|
165
|
+
if is_undefined(processedAxesMap[canonicalKey]) {
|
|
166
|
+
processedAxesMap[canonicalKey] = {
|
|
167
|
+
name: labelledAxis.name,
|
|
168
|
+
type: labelledAxis.type,
|
|
169
|
+
domain: labelledAxis.domain, // Store original domain from the labelled (effectively distilled) axis
|
|
170
|
+
label: labelledAxis.annotations["pl7.app/label"]
|
|
171
|
+
}
|
|
172
|
+
}
|
|
173
|
+
}
|
|
174
|
+
distilledSpec.axesSpec = newAxesSpec
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
|
|
178
|
+
frameBuilder.add(request.pFrameKey, distilledSpec, request.data)
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
finalProcessedAxes := maps.getValues(processedAxesMap)
|
|
182
|
+
return {
|
|
183
|
+
pFrame: frameBuilder.build(),
|
|
184
|
+
uniqueAxes: finalProcessedAxes
|
|
185
|
+
}
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
self := undefined // Forward declaration for builder methods
|
|
189
|
+
|
|
190
|
+
processSingleCol := func(col, ops) {
|
|
191
|
+
if !is_map(col) || is_undefined(col.spec) || is_undefined(col.data) {
|
|
192
|
+
ll.panic("Invalid single column input. Expected {spec: ..., data: ..., key?: ...}, got: %v", col)
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
|
|
196
|
+
|
|
197
|
+
idPrefix := ops.idPrefix ? ops.idPrefix : ""
|
|
198
|
+
idSuffix := ops.idSuffix ? ops.idSuffix : ""
|
|
199
|
+
headerPrefix := ops.headerPrefix ? ops.headerPrefix : ""
|
|
200
|
+
headerSuffix := ops.headerSuffix ? ops.headerSuffix : ""
|
|
201
|
+
|
|
202
|
+
baseId := undefined
|
|
203
|
+
if !is_undefined(ops.id) {
|
|
204
|
+
if !is_string(ops.id) {
|
|
205
|
+
ll.panic("Option 'id' must be a string.")
|
|
206
|
+
}
|
|
207
|
+
baseId = ops.id
|
|
208
|
+
} else if !is_undefined(col.key) {
|
|
209
|
+
if !is_string(col.key) {
|
|
210
|
+
ll.panic("Column 'key' must be a string if provided.")
|
|
211
|
+
}
|
|
212
|
+
baseId = col.key
|
|
213
|
+
} else {
|
|
214
|
+
baseId = "user_col" + nextSequentialId
|
|
215
|
+
nextSequentialId += 1
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
internalId := idPrefix + baseId + idSuffix
|
|
219
|
+
|
|
220
|
+
if !is_undefined(addedColumnInternalIds[internalId]) {
|
|
221
|
+
ll.panic("Duplicate internalId detected: '%s'. Column IDs must be unique.", internalId)
|
|
222
|
+
}
|
|
223
|
+
addedColumnInternalIds[internalId] = true
|
|
224
|
+
|
|
225
|
+
header := undefined
|
|
226
|
+
if !is_undefined(ops.header) {
|
|
227
|
+
if !is_string(ops.header) {
|
|
228
|
+
ll.panic("Option 'header' must be a string.")
|
|
229
|
+
}
|
|
230
|
+
header = ops.header // Explicit header takes full precedence, no prefix/suffix from ops apply here
|
|
231
|
+
} else {
|
|
232
|
+
baseHeaderForOps := undefined
|
|
233
|
+
if !is_undefined(col.spec) &&
|
|
234
|
+
!is_undefined(col.spec.annotations) &&
|
|
235
|
+
!is_undefined(col.spec.annotations["pl7.app/label"]) &&
|
|
236
|
+
is_string(col.spec.annotations["pl7.app/label"]) &&
|
|
237
|
+
col.spec.annotations["pl7.app/label"] != "" {
|
|
238
|
+
baseHeaderForOps = col.spec.annotations["pl7.app/label"]
|
|
239
|
+
} else {
|
|
240
|
+
baseHeaderForOps = strings.substituteSpecialCharacters(internalId) // Fallback to internalId
|
|
241
|
+
}
|
|
242
|
+
header = headerPrefix + baseHeaderForOps + headerSuffix
|
|
243
|
+
}
|
|
244
|
+
|
|
245
|
+
|
|
246
|
+
pFrameKey := "col" + nextPFrameKeyIndex
|
|
247
|
+
nextPFrameKeyIndex += 1
|
|
248
|
+
|
|
249
|
+
requests = append(requests, {
|
|
250
|
+
internalId: internalId,
|
|
251
|
+
header: header,
|
|
252
|
+
spec: col.spec,
|
|
253
|
+
data: col.data,
|
|
254
|
+
originalKey: col.key, // Store original key if present
|
|
255
|
+
pFrameKey: pFrameKey
|
|
256
|
+
})
|
|
257
|
+
distiller = undefined // Invalidate distiller
|
|
258
|
+
}
|
|
259
|
+
|
|
260
|
+
processMultiCol := func(colsArray, ops) {
|
|
261
|
+
if !is_undefined(ops.id) || !is_undefined(ops.header) {
|
|
262
|
+
ll.panic("Single-column options ('id', 'header') cannot be used with multi-column 'add'.")
|
|
263
|
+
}
|
|
264
|
+
idPrefix := ops.idPrefix ? ops.idPrefix : ""
|
|
265
|
+
idSuffix := ops.idSuffix ? ops.idSuffix : ""
|
|
266
|
+
headerPrefix := ops.headerPrefix ? ops.headerPrefix : ""
|
|
267
|
+
headerSuffix := ops.headerSuffix ? ops.headerSuffix : ""
|
|
268
|
+
|
|
269
|
+
for col in colsArray {
|
|
270
|
+
if !is_map(col) || is_undefined(col.spec) || is_undefined(col.data) {
|
|
271
|
+
ll.panic("Invalid column item in array/frame. Expected {spec: ..., data: ..., key?: ...}, got: %v", col)
|
|
272
|
+
}
|
|
273
|
+
|
|
274
|
+
baseId := undefined
|
|
275
|
+
if !is_undefined(col.key) {
|
|
276
|
+
if !is_string(col.key) {
|
|
277
|
+
ll.panic("Column 'key' must be a string if provided in multi-add.")
|
|
278
|
+
}
|
|
279
|
+
baseId = col.key
|
|
280
|
+
} else {
|
|
281
|
+
baseId = "user_col" + nextSequentialId
|
|
282
|
+
nextSequentialId += 1
|
|
283
|
+
}
|
|
284
|
+
|
|
285
|
+
internalId := idPrefix + baseId + idSuffix
|
|
286
|
+
|
|
287
|
+
if !is_undefined(addedColumnInternalIds[internalId]) {
|
|
288
|
+
ll.panic("Duplicate internalId detected: '%s'. Column IDs must be unique.", internalId)
|
|
289
|
+
}
|
|
290
|
+
addedColumnInternalIds[internalId] = true
|
|
291
|
+
|
|
292
|
+
baseHeader := undefined
|
|
293
|
+
if !is_undefined(col.spec) &&
|
|
294
|
+
!is_undefined(col.spec.annotations) &&
|
|
295
|
+
!is_undefined(col.spec.annotations["pl7.app/label"]) &&
|
|
296
|
+
is_string(col.spec.annotations["pl7.app/label"]) &&
|
|
297
|
+
col.spec.annotations["pl7.app/label"] != "" {
|
|
298
|
+
baseHeader = col.spec.annotations["pl7.app/label"]
|
|
299
|
+
} else {
|
|
300
|
+
baseHeader = strings.substituteSpecialCharacters(internalId) // Fallback to internalId
|
|
301
|
+
}
|
|
302
|
+
header := headerPrefix + baseHeader + headerSuffix
|
|
303
|
+
|
|
304
|
+
|
|
305
|
+
pFrameKey := "col" + nextPFrameKeyIndex
|
|
306
|
+
nextPFrameKeyIndex += 1
|
|
307
|
+
|
|
308
|
+
requests = append(requests, {
|
|
309
|
+
internalId: internalId,
|
|
310
|
+
header: header,
|
|
311
|
+
spec: col.spec,
|
|
312
|
+
data: col.data,
|
|
313
|
+
originalKey: col.key,
|
|
314
|
+
pFrameKey: pFrameKey
|
|
315
|
+
})
|
|
316
|
+
}
|
|
317
|
+
distiller = undefined // Invalidate distiller
|
|
318
|
+
}
|
|
319
|
+
|
|
320
|
+
|
|
321
|
+
self = {
|
|
322
|
+
|
|
323
|
+
|
|
324
|
+
|
|
325
|
+
|
|
326
|
+
|
|
327
|
+
|
|
328
|
+
|
|
329
|
+
|
|
330
|
+
|
|
331
|
+
|
|
332
|
+
|
|
333
|
+
|
|
334
|
+
|
|
335
|
+
|
|
336
|
+
|
|
337
|
+
|
|
338
|
+
|
|
339
|
+
|
|
340
|
+
|
|
341
|
+
|
|
342
|
+
|
|
343
|
+
|
|
344
|
+
|
|
345
|
+
|
|
346
|
+
|
|
347
|
+
|
|
348
|
+
|
|
349
|
+
|
|
350
|
+
|
|
351
|
+
|
|
352
|
+
|
|
353
|
+
|
|
354
|
+
|
|
355
|
+
|
|
356
|
+
|
|
357
|
+
|
|
358
|
+
|
|
359
|
+
|
|
360
|
+
|
|
361
|
+
|
|
362
|
+
|
|
363
|
+
|
|
364
|
+
|
|
365
|
+
|
|
366
|
+
|
|
367
|
+
|
|
368
|
+
|
|
369
|
+
|
|
370
|
+
|
|
371
|
+
|
|
372
|
+
|
|
373
|
+
|
|
374
|
+
|
|
375
|
+
|
|
376
|
+
|
|
377
|
+
add: func(item, ...options) {
|
|
378
|
+
ops := {}
|
|
379
|
+
if len(options) > 0 {
|
|
380
|
+
if !is_map(options[0]) {
|
|
381
|
+
ll.panic("Options for 'add' method must be a map.")
|
|
382
|
+
}
|
|
383
|
+
ops = options[0]
|
|
384
|
+
}
|
|
385
|
+
|
|
386
|
+
if is_array(item) { // Array of columns
|
|
387
|
+
processMultiCol(item, ops)
|
|
388
|
+
} else if is_map(item) {
|
|
389
|
+
|
|
390
|
+
if !is_undefined(item.spec) && !is_undefined(item.data) {
|
|
391
|
+
processSingleCol(item, ops)
|
|
392
|
+
} else {
|
|
393
|
+
|
|
394
|
+
|
|
395
|
+
isFlatFrame := false
|
|
396
|
+
for k, _ in item {
|
|
397
|
+
if strings.has_suffix(k, ".spec") || strings.has_suffix(k, ".data") {
|
|
398
|
+
isFlatFrame = true
|
|
399
|
+
break
|
|
400
|
+
}
|
|
401
|
+
}
|
|
402
|
+
|
|
403
|
+
colsToProcess := []
|
|
404
|
+
if isFlatFrame {
|
|
405
|
+
colsToProcess = parseFlatFrameInput(item)
|
|
406
|
+
} else { // Assume map of column objects: { "id1": {spec, data}, ... }
|
|
407
|
+
for k, v_col in item {
|
|
408
|
+
if !is_map(v_col) || is_undefined(v_col.spec) || is_undefined(v_col.data) {
|
|
409
|
+
ll.panic("Invalid column value in frame for key '%s'. Expected {spec: ..., data: ...}.", k)
|
|
410
|
+
}
|
|
411
|
+
colsToProcess = append(colsToProcess, {
|
|
412
|
+
spec: v_col.spec,
|
|
413
|
+
data: v_col.data,
|
|
414
|
+
key: k // Use map key as original key
|
|
415
|
+
})
|
|
416
|
+
}
|
|
417
|
+
}
|
|
418
|
+
processMultiCol(colsToProcess, ops)
|
|
419
|
+
}
|
|
420
|
+
} else {
|
|
421
|
+
ll.panic("'add' method input item must be a single column object, an array of columns, or a frame (map). Got: %v", item)
|
|
422
|
+
}
|
|
423
|
+
return self
|
|
424
|
+
},
|
|
425
|
+
|
|
426
|
+
|
|
427
|
+
|
|
428
|
+
|
|
429
|
+
|
|
430
|
+
|
|
431
|
+
|
|
432
|
+
|
|
433
|
+
|
|
434
|
+
|
|
435
|
+
|
|
436
|
+
|
|
437
|
+
|
|
438
|
+
|
|
439
|
+
|
|
440
|
+
|
|
441
|
+
setAxisHeader: func(matcher, header) {
|
|
442
|
+
processedMatcher := undefined
|
|
443
|
+
if is_string(matcher) {
|
|
444
|
+
processedMatcher = { name: matcher, domain: {} }
|
|
445
|
+
} else if is_map(matcher) {
|
|
446
|
+
|
|
447
|
+
|
|
448
|
+
|
|
449
|
+
processedMatcher = pSpec.axisSpecToMatcher(matcher)
|
|
450
|
+
} else {
|
|
451
|
+
ll.panic("Matcher for setAxisHeader must be a string (axis name) or an AxisSpec-like map (with at least 'name' and 'type' fields).")
|
|
452
|
+
}
|
|
453
|
+
|
|
454
|
+
if !is_string(header) {
|
|
455
|
+
ll.panic("Header for setAxisHeader must be a string.")
|
|
456
|
+
}
|
|
457
|
+
|
|
458
|
+
localAxisHeaderOverrides = [[processedMatcher, header]] + localAxisHeaderOverrides
|
|
459
|
+
|
|
460
|
+
return self
|
|
461
|
+
},
|
|
462
|
+
|
|
463
|
+
|
|
464
|
+
|
|
465
|
+
|
|
466
|
+
|
|
467
|
+
|
|
468
|
+
|
|
469
|
+
|
|
470
|
+
getHeader: func(internalId) {
|
|
471
|
+
if !is_string(internalId) {
|
|
472
|
+
ll.panic("internalId for getHeader must be a string.")
|
|
473
|
+
}
|
|
474
|
+
for request in requests {
|
|
475
|
+
if request.internalId == internalId {
|
|
476
|
+
return request.header
|
|
477
|
+
}
|
|
478
|
+
}
|
|
479
|
+
ll.panic("Header not found for internalId: '%s'. Ensure column was added and ID is correct.", internalId)
|
|
480
|
+
return undefined // Should not reach here
|
|
481
|
+
},
|
|
482
|
+
|
|
483
|
+
|
|
484
|
+
|
|
485
|
+
|
|
486
|
+
|
|
487
|
+
|
|
488
|
+
|
|
489
|
+
|
|
490
|
+
getSpec: func(internalId) {
|
|
491
|
+
if !is_string(internalId) {
|
|
492
|
+
ll.panic("internalId for getSpec must be a string.")
|
|
493
|
+
}
|
|
494
|
+
for request in requests {
|
|
495
|
+
if request.internalId == internalId {
|
|
496
|
+
return request.spec
|
|
497
|
+
}
|
|
498
|
+
}
|
|
499
|
+
ll.panic("Spec not found for internalId: '%s'. Ensure column was added and ID is correct.", internalId)
|
|
500
|
+
return undefined // Should not reach here
|
|
501
|
+
},
|
|
502
|
+
|
|
503
|
+
|
|
504
|
+
|
|
505
|
+
|
|
506
|
+
|
|
507
|
+
|
|
508
|
+
|
|
509
|
+
|
|
510
|
+
|
|
511
|
+
|
|
512
|
+
setNA: func(value) {
|
|
513
|
+
if !is_string(value) {
|
|
514
|
+
ll.panic("NA value for setNA must be a string.")
|
|
515
|
+
}
|
|
516
|
+
naValueInternal = value
|
|
517
|
+
return self
|
|
518
|
+
},
|
|
519
|
+
|
|
520
|
+
|
|
521
|
+
|
|
522
|
+
|
|
523
|
+
|
|
524
|
+
|
|
525
|
+
|
|
526
|
+
|
|
527
|
+
|
|
528
|
+
|
|
529
|
+
|
|
530
|
+
|
|
531
|
+
|
|
532
|
+
|
|
533
|
+
|
|
534
|
+
build: func(...params) {
|
|
535
|
+
if len(requests) == 0 {
|
|
536
|
+
ll.panic("Cannot build XSV: no columns have been added.")
|
|
537
|
+
}
|
|
538
|
+
|
|
539
|
+
|
|
540
|
+
currentExportParams := { naStr: naValueInternal, nullStr: naValueInternal }
|
|
541
|
+
|
|
542
|
+
if len(params) > 0 {
|
|
543
|
+
if !is_map(params[0]) {
|
|
544
|
+
ll.panic("Optional build params must be a map.")
|
|
545
|
+
}
|
|
546
|
+
|
|
547
|
+
currentExportParams = maps.deepMerge(currentExportParams, params[0])
|
|
548
|
+
}
|
|
549
|
+
|
|
550
|
+
pFrame := _constructPFrameAndDistillSpecs().pFrame
|
|
551
|
+
return xsv.exportFrame(pFrame, xsvType, currentExportParams)
|
|
552
|
+
},
|
|
553
|
+
|
|
554
|
+
|
|
555
|
+
|
|
556
|
+
|
|
557
|
+
|
|
558
|
+
|
|
559
|
+
|
|
560
|
+
|
|
561
|
+
|
|
562
|
+
|
|
563
|
+
|
|
564
|
+
|
|
565
|
+
|
|
566
|
+
|
|
567
|
+
|
|
568
|
+
|
|
569
|
+
|
|
570
|
+
|
|
571
|
+
|
|
572
|
+
|
|
573
|
+
|
|
574
|
+
|
|
575
|
+
buildForPT: func() {
|
|
576
|
+
if len(requests) == 0 {
|
|
577
|
+
ll.panic("Cannot build PTabler output: no columns have been added.")
|
|
578
|
+
}
|
|
579
|
+
|
|
580
|
+
finalExportParams := {
|
|
581
|
+
naStr: PTablerNA,
|
|
582
|
+
nullStr: PTablerNA
|
|
583
|
+
}
|
|
584
|
+
|
|
585
|
+
buildOutput := _constructPFrameAndDistillSpecs()
|
|
586
|
+
pFrame := buildOutput.pFrame
|
|
587
|
+
uniqueAxes := buildOutput.uniqueAxes
|
|
588
|
+
|
|
589
|
+
tableResource := xsv.exportFrame(pFrame, xsvType, finalExportParams)
|
|
590
|
+
|
|
591
|
+
schema := []
|
|
592
|
+
|
|
593
|
+
|
|
594
|
+
for axisEntry in uniqueAxes {
|
|
595
|
+
axisSchema := {
|
|
596
|
+
column: axisEntry.label,
|
|
597
|
+
nullValue: PTablerNA
|
|
598
|
+
}
|
|
599
|
+
axisSchema.type = axisEntry.type
|
|
600
|
+
schema = append(schema, axisSchema)
|
|
601
|
+
}
|
|
602
|
+
|
|
603
|
+
|
|
604
|
+
for request in requests {
|
|
605
|
+
colSchema := {
|
|
606
|
+
column: request.header,
|
|
607
|
+
nullValue: PTablerNA
|
|
608
|
+
}
|
|
609
|
+
colSchema.type = request.spec.valueType
|
|
610
|
+
schema = append(schema, colSchema)
|
|
611
|
+
}
|
|
612
|
+
|
|
613
|
+
return {
|
|
614
|
+
xsvType: xsvType,
|
|
615
|
+
file: tableResource,
|
|
616
|
+
schema: schema
|
|
617
|
+
}
|
|
618
|
+
}
|
|
619
|
+
}
|
|
620
|
+
return ll.toStrict(self)
|
|
621
|
+
}
|
|
622
|
+
|
|
623
|
+
export ll.toStrict({
|
|
624
|
+
xsvFileBuilder: xsvFileBuilder
|
|
625
|
+
})
|