@platforma-sdk/model 1.45.35 → 1.45.45
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/annotations/converter.cjs +30 -14
- package/dist/annotations/converter.cjs.map +1 -1
- package/dist/annotations/converter.d.ts.map +1 -1
- package/dist/annotations/converter.js +30 -14
- package/dist/annotations/converter.js.map +1 -1
- package/dist/components/PFrameForGraphs.cjs +12 -27
- package/dist/components/PFrameForGraphs.cjs.map +1 -1
- package/dist/components/PFrameForGraphs.d.ts +4 -3
- package/dist/components/PFrameForGraphs.d.ts.map +1 -1
- package/dist/components/PFrameForGraphs.js +12 -28
- package/dist/components/PFrameForGraphs.js.map +1 -1
- package/dist/filters/converter.cjs +4 -2
- package/dist/filters/converter.cjs.map +1 -1
- package/dist/filters/converter.d.ts.map +1 -1
- package/dist/filters/converter.js +4 -2
- package/dist/filters/converter.js.map +1 -1
- package/dist/filters/types.d.ts +1 -1
- package/dist/filters/types.d.ts.map +1 -1
- package/dist/index.cjs +10 -0
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.ts +1 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +2 -1
- package/dist/index.js.map +1 -1
- package/dist/internal.cjs.map +1 -1
- package/dist/internal.js.map +1 -1
- package/dist/package.json.cjs +1 -1
- package/dist/package.json.js +1 -1
- package/dist/pframe.cjs +11 -5
- package/dist/pframe.cjs.map +1 -1
- package/dist/pframe.d.ts +2 -1
- package/dist/pframe.d.ts.map +1 -1
- package/dist/pframe.js +11 -5
- package/dist/pframe.js.map +1 -1
- package/dist/pframe_utils/index.cjs +294 -0
- package/dist/pframe_utils/index.cjs.map +1 -0
- package/dist/pframe_utils/index.d.ts +48 -0
- package/dist/pframe_utils/index.d.ts.map +1 -0
- package/dist/pframe_utils/index.js +285 -0
- package/dist/pframe_utils/index.js.map +1 -0
- package/dist/render/api.cjs.map +1 -1
- package/dist/render/api.d.ts +2 -2
- package/dist/render/api.d.ts.map +1 -1
- package/dist/render/api.js.map +1 -1
- package/dist/render/util/column_collection.cjs.map +1 -1
- package/dist/render/util/column_collection.d.ts +11 -6
- package/dist/render/util/column_collection.d.ts.map +1 -1
- package/dist/render/util/column_collection.js.map +1 -1
- package/dist/render/util/pcolumn_data.cjs +10 -6
- package/dist/render/util/pcolumn_data.cjs.map +1 -1
- package/dist/render/util/pcolumn_data.d.ts +3 -3
- package/dist/render/util/pcolumn_data.d.ts.map +1 -1
- package/dist/render/util/pcolumn_data.js +10 -6
- package/dist/render/util/pcolumn_data.js.map +1 -1
- package/package.json +11 -10
- package/src/annotations/converter.ts +35 -15
- package/src/components/PFrameForGraphs.ts +19 -37
- package/src/filters/converter.ts +4 -1
- package/src/filters/types.ts +1 -1
- package/src/global.d.ts +2 -2
- package/src/index.ts +1 -0
- package/src/internal.ts +2 -2
- package/src/pframe.ts +13 -6
- package/src/pframe_utils/index.ts +442 -0
- package/src/render/api.ts +6 -4
- package/src/render/util/column_collection.ts +13 -5
- package/src/render/util/pcolumn_data.ts +15 -10
|
@@ -23,8 +23,8 @@ import {
|
|
|
23
23
|
readAnnotationJson,
|
|
24
24
|
stringifyJson,
|
|
25
25
|
} from '@milaboratories/pl-model-common';
|
|
26
|
-
import type { PColumnDataUniversal, RenderCtx } from '../render';
|
|
27
|
-
import {
|
|
26
|
+
import type { PColumnDataUniversal, PColumnEntryUniversal, PColumnEntryWithLabel, RenderCtx } from '../render';
|
|
27
|
+
import { PColumnCollection } from '../render';
|
|
28
28
|
|
|
29
29
|
/** Create id for column copy with added keys in axes domains */
|
|
30
30
|
const colId = (id: PObjectId, domains: (Record<string, string> | undefined)[]) => {
|
|
@@ -60,10 +60,14 @@ export function isHiddenFromGraphColumn(column: PColumnSpec): boolean {
|
|
|
60
60
|
return !!readAnnotationJson(column, Annotation.HideDataFromGraphs);
|
|
61
61
|
}
|
|
62
62
|
|
|
63
|
+
export function isHiddenFromUIColumn(column: PColumnSpec): boolean {
|
|
64
|
+
return !!readAnnotationJson(column, Annotation.HideDataFromUi);
|
|
65
|
+
}
|
|
66
|
+
|
|
63
67
|
type AxesVault = Map<CanonicalizedJson<AxisId>, AxisSpecNormalized>;
|
|
64
68
|
|
|
65
69
|
export function getAvailableWithLinkersAxes(
|
|
66
|
-
linkerColumns:
|
|
70
|
+
linkerColumns: (PColumnEntryWithLabel | PColumnEntryUniversal)[],
|
|
67
71
|
blockAxes: AxesVault,
|
|
68
72
|
): AxesVault {
|
|
69
73
|
const linkerMap = LinkerMap.fromColumns(linkerColumns.map(getColumnIdAndSpec));
|
|
@@ -90,18 +94,14 @@ export function getAvailableWithLinkersAxes(
|
|
|
90
94
|
}));
|
|
91
95
|
}
|
|
92
96
|
/** Add columns with fully compatible axes created from partial compatible ones */
|
|
93
|
-
export function enrichCompatible(blockAxes: AxesVault, columns:
|
|
94
|
-
|
|
95
|
-
columns.forEach((column) => {
|
|
96
|
-
result.push(...getAdditionalColumnsForColumn(blockAxes, column));
|
|
97
|
-
});
|
|
98
|
-
return result;
|
|
97
|
+
export function enrichCompatible<T extends Omit<PColumn<PColumnDataUniversal>, 'data'>>(blockAxes: AxesVault, columns: T[]): T[] {
|
|
98
|
+
return columns.flatMap((column) => getAdditionalColumnsForColumn(blockAxes, column));
|
|
99
99
|
}
|
|
100
100
|
|
|
101
|
-
function getAdditionalColumnsForColumn(
|
|
101
|
+
function getAdditionalColumnsForColumn<T extends Omit<PColumn<PColumnDataUniversal>, 'data'>>(
|
|
102
102
|
blockAxes: AxesVault,
|
|
103
|
-
column:
|
|
104
|
-
):
|
|
103
|
+
column: T,
|
|
104
|
+
): T[] {
|
|
105
105
|
const columnAxesIds = column.spec.axesSpec.map(getAxisId);
|
|
106
106
|
|
|
107
107
|
if (columnAxesIds.every((id) => blockAxes.has(canonicalizeJson(id)))) {
|
|
@@ -168,6 +168,7 @@ function getAdditionalColumnsForColumn(
|
|
|
168
168
|
}
|
|
169
169
|
|
|
170
170
|
return {
|
|
171
|
+
...column,
|
|
171
172
|
id: id as PObjectId,
|
|
172
173
|
spec: {
|
|
173
174
|
...column.spec,
|
|
@@ -177,7 +178,6 @@ function getAdditionalColumnsForColumn(
|
|
|
177
178
|
})),
|
|
178
179
|
annotations,
|
|
179
180
|
},
|
|
180
|
-
data: column.data,
|
|
181
181
|
};
|
|
182
182
|
});
|
|
183
183
|
|
|
@@ -200,16 +200,12 @@ export function createPFrameForGraphs<A, U>(
|
|
|
200
200
|
ctx: RenderCtx<A, U>,
|
|
201
201
|
blockColumns?: PColumn<PColumnDataUniversal>[],
|
|
202
202
|
): PFrameHandle | undefined {
|
|
203
|
+
const suitableSpec = (spec: PColumnSpec) => !isHiddenFromUIColumn(spec) && !isHiddenFromGraphColumn(spec);
|
|
203
204
|
// if current block doesn't produce own columns then use all columns from result pool
|
|
204
205
|
if (!blockColumns) {
|
|
205
206
|
const columns = new PColumnCollection();
|
|
206
207
|
columns.addColumnProvider(ctx.resultPool);
|
|
207
|
-
|
|
208
|
-
const allColumns = columns.getColumns((spec) => !isHiddenFromGraphColumn(spec), { dontWaitAllData: true, overrideLabelAnnotation: false }) ?? [];
|
|
209
|
-
// if at least one column is not yet ready, we can't show the graph
|
|
210
|
-
if (!allPColumnsReady(allColumns)) {
|
|
211
|
-
return undefined;
|
|
212
|
-
}
|
|
208
|
+
const allColumns = columns.getUniversalEntries(suitableSpec, { dontWaitAllData: true, overrideLabelAnnotation: false }) ?? [];
|
|
213
209
|
|
|
214
210
|
const allAxes: AxesVault = new Map(allColumns
|
|
215
211
|
.flatMap((column) => getNormalizedAxesList(column.spec.axesSpec))
|
|
@@ -224,10 +220,6 @@ export function createPFrameForGraphs<A, U>(
|
|
|
224
220
|
return ctx.createPFrame(extendedColumns);
|
|
225
221
|
};
|
|
226
222
|
|
|
227
|
-
if (!allPColumnsReady(blockColumns)) {
|
|
228
|
-
return undefined;
|
|
229
|
-
}
|
|
230
|
-
|
|
231
223
|
// if current block has its own columns then take from result pool only compatible with them
|
|
232
224
|
const columns = new PColumnCollection();
|
|
233
225
|
columns.addColumnProvider(ctx.resultPool);
|
|
@@ -246,7 +238,7 @@ export function createPFrameForGraphs<A, U>(
|
|
|
246
238
|
}
|
|
247
239
|
|
|
248
240
|
// all linker columns always go to pFrame - even it's impossible to use some of them they all are hidden
|
|
249
|
-
const linkerColumns = columns.
|
|
241
|
+
const linkerColumns = columns.getUniversalEntries((spec) => suitableSpec(spec) && isLinkerColumn(spec)) ?? [];
|
|
250
242
|
const availableWithLinkersAxes = getAvailableWithLinkersAxes(linkerColumns, blockAxes);
|
|
251
243
|
|
|
252
244
|
// all possible axes from connected linkers
|
|
@@ -257,16 +249,11 @@ export function createPFrameForGraphs<A, U>(
|
|
|
257
249
|
|
|
258
250
|
const blockAxesArr = Array.from(blockAxes.values());
|
|
259
251
|
// all compatible with block columns but without label columns
|
|
260
|
-
let compatibleWithoutLabels = (columns.
|
|
252
|
+
let compatibleWithoutLabels = (columns.getUniversalEntries((spec) => suitableSpec(spec) && spec.axesSpec.some((axisSpec) => {
|
|
261
253
|
const axisId = getAxisId(axisSpec);
|
|
262
254
|
return blockAxesArr.some((selectorAxisSpec) => matchAxisId(getAxisId(selectorAxisSpec), axisId));
|
|
263
255
|
}), { dontWaitAllData: true, overrideLabelAnnotation: false }) ?? []).filter((column) => !isLabelColumn(column.spec));
|
|
264
256
|
|
|
265
|
-
// if at least one column is not yet ready, we can't show the graph
|
|
266
|
-
if (!allPColumnsReady(compatibleWithoutLabels)) {
|
|
267
|
-
return undefined;
|
|
268
|
-
}
|
|
269
|
-
|
|
270
257
|
// extend axes set for label columns request
|
|
271
258
|
for (const c of compatibleWithoutLabels) {
|
|
272
259
|
for (const spec of getNormalizedAxesList(c.spec.axesSpec)) {
|
|
@@ -277,22 +264,17 @@ export function createPFrameForGraphs<A, U>(
|
|
|
277
264
|
|
|
278
265
|
const allAxesArr = Array.from(allAxes.values());
|
|
279
266
|
// extend allowed columns - add columns thad doesn't have axes from block, but have all axes in 'allAxes' list (that means all axes from linkers or from 'hanging' of other selected columns)
|
|
280
|
-
compatibleWithoutLabels = (columns.
|
|
267
|
+
compatibleWithoutLabels = (columns.getUniversalEntries((spec) => suitableSpec(spec) && spec.axesSpec.every((axisSpec) => {
|
|
281
268
|
const axisId = getAxisId(axisSpec);
|
|
282
269
|
return allAxesArr.some((selectorAxisSpec) => matchAxisId(getAxisId(selectorAxisSpec), axisId));
|
|
283
270
|
}), { dontWaitAllData: true, overrideLabelAnnotation: false }) ?? []).filter((column) => !isLabelColumn(column.spec));
|
|
284
271
|
|
|
285
272
|
// label columns must be compatible with full set of axes - block axes and axes from compatible columns from result pool
|
|
286
|
-
const compatibleLabels = (columns.
|
|
273
|
+
const compatibleLabels = (columns.getUniversalEntries((spec) => suitableSpec(spec) && spec.axesSpec.some((axisSpec) => {
|
|
287
274
|
const axisId = getAxisId(axisSpec);
|
|
288
275
|
return allAxesArr.some((selectorAxisSpec) => matchAxisId(getAxisId(selectorAxisSpec), axisId));
|
|
289
276
|
}), { dontWaitAllData: true, overrideLabelAnnotation: false }) ?? []).filter((column) => isLabelColumn(column.spec));
|
|
290
277
|
|
|
291
|
-
// if at least one column is not yet ready, we can't show the graph
|
|
292
|
-
if (!allPColumnsReady(compatibleLabels)) {
|
|
293
|
-
return undefined;
|
|
294
|
-
}
|
|
295
|
-
|
|
296
278
|
const compatible = [...compatibleWithoutLabels, ...compatibleLabels];
|
|
297
279
|
|
|
298
280
|
// additional columns are duplicates with extra fields in domains for compatibility if there are ones with partial match
|
package/src/filters/converter.ts
CHANGED
|
@@ -51,6 +51,10 @@ export function convertFilterUiToExpressionImpl(value: FilterSpec): ExpressionIm
|
|
|
51
51
|
return col(value.column).eq(lit(value.x));
|
|
52
52
|
}
|
|
53
53
|
|
|
54
|
+
if (value.type === 'notEqual') {
|
|
55
|
+
return col(value.column).neq(lit(value.x));
|
|
56
|
+
}
|
|
57
|
+
|
|
54
58
|
if (value.type === 'lessThan') {
|
|
55
59
|
return col(value.column).lt(lit(value.x));
|
|
56
60
|
}
|
|
@@ -112,7 +116,6 @@ export function convertFilterUiToExpressionImpl(value: FilterSpec): ExpressionIm
|
|
|
112
116
|
|| value.type === 'patternFuzzyContainSubsequence'
|
|
113
117
|
|| value.type === 'inSet'
|
|
114
118
|
|| value.type === 'notInSet'
|
|
115
|
-
|| value.type === 'notEqual'
|
|
116
119
|
) {
|
|
117
120
|
throw new Error('Not implemented filter type: ' + value.type);
|
|
118
121
|
}
|
package/src/filters/types.ts
CHANGED
|
@@ -10,7 +10,7 @@ export type SimplifiedUniversalPColumnEntry = {
|
|
|
10
10
|
|
|
11
11
|
export type FilterSpecNode<Leaf, CommonNode = {}, CommonLeaf = {}> =
|
|
12
12
|
| CommonLeaf & Leaf
|
|
13
|
-
| CommonNode & { type: 'not'; filter:
|
|
13
|
+
| CommonNode & { type: 'not'; filter: FilterSpecNode<Leaf, CommonNode, CommonLeaf> }
|
|
14
14
|
| CommonNode & { type: 'or'; filters: FilterSpecNode<Leaf, CommonNode, CommonLeaf>[] }
|
|
15
15
|
| CommonNode & { type: 'and'; filters: FilterSpecNode<Leaf, CommonNode, CommonLeaf>[] };
|
|
16
16
|
|
package/src/global.d.ts
CHANGED
|
@@ -1,9 +1,9 @@
|
|
|
1
|
-
import type { PlatformaApiVersion } from './platforma';
|
|
1
|
+
import type { Platforma, PlatformaApiVersion } from './platforma';
|
|
2
2
|
|
|
3
3
|
declare global {
|
|
4
4
|
/** Global factory method returning platforma instance */
|
|
5
5
|
var getPlatforma: PlatformaSDKVersion; // TODO: invalid type
|
|
6
|
-
var platforma: Platforma;
|
|
6
|
+
var platforma: undefined | Platforma;
|
|
7
7
|
var platformaApiVersion: PlatformaApiVersion;
|
|
8
8
|
|
|
9
9
|
function getEnvironmentValue(name: string): string | undefined;
|
package/src/index.ts
CHANGED
|
@@ -14,6 +14,7 @@ export * from './block_api_v1';
|
|
|
14
14
|
export * from './block_api_v2';
|
|
15
15
|
export * from './filters';
|
|
16
16
|
export * from './annotations';
|
|
17
|
+
export * from './pframe_utils';
|
|
17
18
|
|
|
18
19
|
// reexporting everything from SDK model
|
|
19
20
|
export * from '@milaboratories/pl-model-common';
|
package/src/internal.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import type { ValueOrErrors } from '@milaboratories/pl-model-common';
|
|
2
|
-
import {} from './global';
|
|
2
|
+
import { } from './global';
|
|
3
3
|
import type { Platforma, PlatformaApiVersion } from './platforma';
|
|
4
4
|
import type { FutureHandle, GlobalCfgRenderCtx } from './render/internal';
|
|
5
5
|
|
|
@@ -19,7 +19,7 @@ export function getPlatformaInstance<
|
|
|
19
19
|
>(config?: { sdkVersion: string; apiVersion: PlatformaApiVersion }): Platforma<Args, Outputs, UiState, Href> {
|
|
20
20
|
if (config && typeof globalThis.getPlatforma === 'function')
|
|
21
21
|
return globalThis.getPlatforma(config);
|
|
22
|
-
else if (typeof globalThis.platforma !== 'undefined') return globalThis.platforma
|
|
22
|
+
else if (typeof globalThis.platforma !== 'undefined') return globalThis.platforma as Platforma<Args, Outputs, UiState, Href>;
|
|
23
23
|
else throw new Error('Can\'t get platforma instance.');
|
|
24
24
|
}
|
|
25
25
|
|
package/src/pframe.ts
CHANGED
|
@@ -18,15 +18,15 @@ export class PFrameImpl implements PFrame {
|
|
|
18
18
|
constructor(private readonly handle: PFrameHandle) {}
|
|
19
19
|
|
|
20
20
|
public async findColumns(request: FindColumnsRequest): Promise<FindColumnsResponse> {
|
|
21
|
-
return await
|
|
21
|
+
return await this.getPlatforma().pFrameDriver.findColumns(this.handle, request);
|
|
22
22
|
}
|
|
23
23
|
|
|
24
|
-
public async getColumnSpec(columnId: PObjectId): Promise<PColumnSpec> {
|
|
25
|
-
return await
|
|
24
|
+
public async getColumnSpec(columnId: PObjectId): Promise<PColumnSpec | null> {
|
|
25
|
+
return await this.getPlatforma().pFrameDriver.getColumnSpec(this.handle, columnId);
|
|
26
26
|
}
|
|
27
27
|
|
|
28
28
|
public async listColumns(): Promise<PColumnIdAndSpec[]> {
|
|
29
|
-
return await
|
|
29
|
+
return await this.getPlatforma().pFrameDriver.listColumns(this.handle);
|
|
30
30
|
}
|
|
31
31
|
|
|
32
32
|
public async calculateTableData(
|
|
@@ -39,10 +39,17 @@ export class PFrameImpl implements PFrame {
|
|
|
39
39
|
filters: patchInSetFilters(request.filters),
|
|
40
40
|
};
|
|
41
41
|
}
|
|
42
|
-
return await
|
|
42
|
+
return await this.getPlatforma().pFrameDriver.calculateTableData(this.handle, request, range);
|
|
43
43
|
}
|
|
44
44
|
|
|
45
45
|
public async getUniqueValues(request: UniqueValuesRequest): Promise<UniqueValuesResponse> {
|
|
46
|
-
return await
|
|
46
|
+
return await this.getPlatforma().pFrameDriver.getUniqueValues(this.handle, request);
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
private getPlatforma() {
|
|
50
|
+
if (platforma === undefined) {
|
|
51
|
+
throw new Error('Platforma instance is not available in the current context.');
|
|
52
|
+
}
|
|
53
|
+
return platforma;
|
|
47
54
|
}
|
|
48
55
|
}
|
|
@@ -0,0 +1,442 @@
|
|
|
1
|
+
import { flatten, uniq } from 'es-toolkit';
|
|
2
|
+
|
|
3
|
+
import type {
|
|
4
|
+
AxisSpec,
|
|
5
|
+
CalculateTableDataRequest,
|
|
6
|
+
PColumnSpec,
|
|
7
|
+
PFrameHandle,
|
|
8
|
+
PTableVector,
|
|
9
|
+
UniqueValuesRequest,
|
|
10
|
+
AxisId,
|
|
11
|
+
CanonicalizedJson,
|
|
12
|
+
FindColumnsRequest,
|
|
13
|
+
FindColumnsResponse,
|
|
14
|
+
FullPTableColumnData,
|
|
15
|
+
PColumnIdAndSpec,
|
|
16
|
+
PObjectId,
|
|
17
|
+
PTableRecordSingleValueFilterV2,
|
|
18
|
+
ValueType,
|
|
19
|
+
} from '@milaboratories/pl-model-common';
|
|
20
|
+
import { pTableValue, Annotation, canonicalizeAxisId, getAxisId, readAnnotation } from '@milaboratories/pl-model-common';
|
|
21
|
+
|
|
22
|
+
// Types
|
|
23
|
+
type PValue = string | number | null;
|
|
24
|
+
|
|
25
|
+
type SuggestionResponse = {
|
|
26
|
+
values: {
|
|
27
|
+
value: string;
|
|
28
|
+
label: string;
|
|
29
|
+
}[];
|
|
30
|
+
overflow: boolean;
|
|
31
|
+
};
|
|
32
|
+
|
|
33
|
+
type SingleColumnData = {
|
|
34
|
+
axesData: Record<string, PValue[]>;
|
|
35
|
+
data: PValue[];
|
|
36
|
+
};
|
|
37
|
+
|
|
38
|
+
type UniqueValuesResponse = {
|
|
39
|
+
values: string[];
|
|
40
|
+
overflow: boolean;
|
|
41
|
+
};
|
|
42
|
+
|
|
43
|
+
type GetUniqueSourceValuesParams = {
|
|
44
|
+
columnId: PObjectId;
|
|
45
|
+
axisIdx?: number;
|
|
46
|
+
limit?: number;
|
|
47
|
+
searchQuery?: string;
|
|
48
|
+
searchQueryValue?: string;
|
|
49
|
+
};
|
|
50
|
+
|
|
51
|
+
type GetAxisUniqueValuesParams = {
|
|
52
|
+
axisId: AxisId;
|
|
53
|
+
parentColumnIds: PObjectId[];
|
|
54
|
+
limit?: number;
|
|
55
|
+
filters?: PTableRecordSingleValueFilterV2[];
|
|
56
|
+
};
|
|
57
|
+
|
|
58
|
+
type GetColumnsFullParams = {
|
|
59
|
+
selectedSources: PObjectId[];
|
|
60
|
+
strictlyCompatible: boolean;
|
|
61
|
+
types?: ValueType[];
|
|
62
|
+
names?: string[];
|
|
63
|
+
annotations?: FindColumnsRequest['columnFilter']['annotationValue'];
|
|
64
|
+
annotationsNotEmpty?: string[];
|
|
65
|
+
};
|
|
66
|
+
|
|
67
|
+
// Constants
|
|
68
|
+
const UNIQUE_VALUES_LIMIT = 1000000;
|
|
69
|
+
|
|
70
|
+
// Helper functions
|
|
71
|
+
const sortValuesPredicate = (a: { label: string }, b: { label: string }) =>
|
|
72
|
+
a.label.localeCompare(b.label, 'en', { numeric: true });
|
|
73
|
+
|
|
74
|
+
function convertColumnData(type: ValueType, response: PTableVector, absentValue: number | null = null): PValue[] {
|
|
75
|
+
if (type === 'String') {
|
|
76
|
+
return response.data as PValue[];
|
|
77
|
+
}
|
|
78
|
+
const res: PValue[] = new Array(response.data.length);
|
|
79
|
+
for (let i = 0; i < response.data.length; i++) {
|
|
80
|
+
res[i] = pTableValue(response, i, { absent: absentValue, na: null }) as PValue;
|
|
81
|
+
}
|
|
82
|
+
return res;
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
function createSearchFilter(
|
|
86
|
+
columnId: PObjectId,
|
|
87
|
+
substring: string,
|
|
88
|
+
): PTableRecordSingleValueFilterV2 {
|
|
89
|
+
return {
|
|
90
|
+
type: 'bySingleColumnV2',
|
|
91
|
+
column: {
|
|
92
|
+
type: 'column',
|
|
93
|
+
id: columnId,
|
|
94
|
+
},
|
|
95
|
+
predicate: {
|
|
96
|
+
operator: 'StringIContains',
|
|
97
|
+
substring,
|
|
98
|
+
},
|
|
99
|
+
};
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
function createAxisSearchFilter(
|
|
103
|
+
axisSpec: AxisSpec,
|
|
104
|
+
substring: string,
|
|
105
|
+
): PTableRecordSingleValueFilterV2 {
|
|
106
|
+
return {
|
|
107
|
+
type: 'bySingleColumnV2',
|
|
108
|
+
column: {
|
|
109
|
+
type: 'axis',
|
|
110
|
+
id: {
|
|
111
|
+
type: axisSpec.type,
|
|
112
|
+
name: axisSpec.name,
|
|
113
|
+
},
|
|
114
|
+
},
|
|
115
|
+
predicate: {
|
|
116
|
+
operator: 'StringIContains',
|
|
117
|
+
substring,
|
|
118
|
+
},
|
|
119
|
+
};
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
function mapValuesToSuggestions(values: string[]): { value: string; label: string }[] {
|
|
123
|
+
return values.map((v) => ({ value: String(v), label: String(v) })).sort(sortValuesPredicate);
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
function getPFrameDriver() {
|
|
127
|
+
if (typeof platforma === 'undefined') {
|
|
128
|
+
throw new Error('Platforma instance is not available');
|
|
129
|
+
}
|
|
130
|
+
if (typeof platforma.pFrameDriver === 'undefined') {
|
|
131
|
+
throw new Error('PFrame driver is not available in the current Platforma instance');
|
|
132
|
+
}
|
|
133
|
+
return platforma.pFrameDriver;
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
// Core functions
|
|
137
|
+
export async function getColumnSpecById(handle: PFrameHandle, id: PObjectId): Promise<PColumnSpec | null> {
|
|
138
|
+
try {
|
|
139
|
+
const response = await getPFrameDriver().getColumnSpec(handle, id);
|
|
140
|
+
return response ?? null;
|
|
141
|
+
} catch (err) {
|
|
142
|
+
console.error('PFrame: get single column error', err);
|
|
143
|
+
return null;
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
export async function getSingleColumnData(
|
|
148
|
+
handle: PFrameHandle,
|
|
149
|
+
id: PObjectId,
|
|
150
|
+
filters: PTableRecordSingleValueFilterV2[] = [],
|
|
151
|
+
): Promise<SingleColumnData> {
|
|
152
|
+
try {
|
|
153
|
+
const response: FullPTableColumnData[] = await getPFrameDriver().calculateTableData(handle, {
|
|
154
|
+
src: {
|
|
155
|
+
type: 'column',
|
|
156
|
+
column: id,
|
|
157
|
+
},
|
|
158
|
+
filters,
|
|
159
|
+
sorting: [],
|
|
160
|
+
} as CalculateTableDataRequest<PObjectId>);
|
|
161
|
+
|
|
162
|
+
const axes = response.filter((item) => item.spec.type === 'axis');
|
|
163
|
+
const columns = response.filter((item) => item.spec.type === 'column');
|
|
164
|
+
|
|
165
|
+
return {
|
|
166
|
+
axesData: axes.reduce((res: Record<string, PValue[]>, item) => {
|
|
167
|
+
const id = getAxisId(item.spec.spec as AxisSpec);
|
|
168
|
+
res[canonicalizeAxisId(id)] = convertColumnData(id.type, item.data);
|
|
169
|
+
return res;
|
|
170
|
+
}, {}),
|
|
171
|
+
data: columns.length ? convertColumnData(columns[0].data.type, columns[0].data) : [],
|
|
172
|
+
};
|
|
173
|
+
} catch (err) {
|
|
174
|
+
console.error('PFrame: calculateTableData error');
|
|
175
|
+
throw err;
|
|
176
|
+
}
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
export async function getColumnUniqueValues(
|
|
180
|
+
handle: PFrameHandle,
|
|
181
|
+
id: PObjectId,
|
|
182
|
+
limit = UNIQUE_VALUES_LIMIT,
|
|
183
|
+
filters: PTableRecordSingleValueFilterV2[] = [],
|
|
184
|
+
): Promise<UniqueValuesResponse> {
|
|
185
|
+
const request: UniqueValuesRequest = {
|
|
186
|
+
columnId: id,
|
|
187
|
+
filters,
|
|
188
|
+
limit,
|
|
189
|
+
};
|
|
190
|
+
|
|
191
|
+
try {
|
|
192
|
+
const response = await getPFrameDriver().getUniqueValues(handle, request);
|
|
193
|
+
if (response.overflow) {
|
|
194
|
+
console.warn(`More than ${limit} values for ${id} column`);
|
|
195
|
+
}
|
|
196
|
+
return {
|
|
197
|
+
values: Array.from(response.values.data as ArrayLike<unknown>).map(String),
|
|
198
|
+
overflow: response.overflow,
|
|
199
|
+
};
|
|
200
|
+
} catch (err) {
|
|
201
|
+
console.error('PFrame: getUniqueValues for column error');
|
|
202
|
+
throw err;
|
|
203
|
+
}
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
export async function getAxisUniqueValues(
|
|
207
|
+
handle: PFrameHandle,
|
|
208
|
+
params: GetAxisUniqueValuesParams,
|
|
209
|
+
): Promise<UniqueValuesResponse> {
|
|
210
|
+
const { axisId, parentColumnIds, limit = UNIQUE_VALUES_LIMIT, filters = [] } = params;
|
|
211
|
+
const strAxisId = canonicalizeAxisId(axisId);
|
|
212
|
+
|
|
213
|
+
const parentsSpecs = (await Promise.all(parentColumnIds.map((p) => getColumnSpecById(handle, p))))
|
|
214
|
+
.flatMap((spec, i): [PObjectId, PColumnSpec][] =>
|
|
215
|
+
spec != null && spec.kind === 'PColumn' ? [[parentColumnIds[i], spec]] : [],
|
|
216
|
+
)
|
|
217
|
+
.filter(([_, spec]) =>
|
|
218
|
+
spec.axesSpec.some((axisSpec) => canonicalizeAxisId(getAxisId(axisSpec)) === strAxisId),
|
|
219
|
+
);
|
|
220
|
+
|
|
221
|
+
if (parentsSpecs.length === 0) {
|
|
222
|
+
console.warn('Axis unique values requested without parent columns');
|
|
223
|
+
return { values: [], overflow: false };
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
try {
|
|
227
|
+
const responses = await Promise.all(
|
|
228
|
+
parentsSpecs.map(([id]) =>
|
|
229
|
+
getPFrameDriver().getUniqueValues(handle, {
|
|
230
|
+
columnId: id,
|
|
231
|
+
axis: axisId,
|
|
232
|
+
filters,
|
|
233
|
+
limit,
|
|
234
|
+
}),
|
|
235
|
+
),
|
|
236
|
+
);
|
|
237
|
+
|
|
238
|
+
const overflow = responses.some((r) => r.overflow);
|
|
239
|
+
return {
|
|
240
|
+
values: uniq(
|
|
241
|
+
flatten(responses.map((r) =>
|
|
242
|
+
Array.from(r.values.data as ArrayLike<unknown>).map(String),
|
|
243
|
+
)),
|
|
244
|
+
),
|
|
245
|
+
overflow,
|
|
246
|
+
};
|
|
247
|
+
} catch (err) {
|
|
248
|
+
console.error('PFrame: getUniqueValues for axis error', err);
|
|
249
|
+
return { values: [], overflow: false };
|
|
250
|
+
}
|
|
251
|
+
}
|
|
252
|
+
|
|
253
|
+
export async function getRequestColumnsFromSelectedSources(
|
|
254
|
+
handle: PFrameHandle,
|
|
255
|
+
sources: PObjectId[],
|
|
256
|
+
): Promise<AxisId[]> {
|
|
257
|
+
const result: AxisId[] = [];
|
|
258
|
+
for (const item of sources) {
|
|
259
|
+
const spec = await getColumnSpecById(handle, item);
|
|
260
|
+
if (spec?.kind === 'PColumn') {
|
|
261
|
+
result.push(...spec.axesSpec.map((spec) => getAxisId(spec)));
|
|
262
|
+
}
|
|
263
|
+
}
|
|
264
|
+
return result;
|
|
265
|
+
}
|
|
266
|
+
|
|
267
|
+
export async function getColumnsFull(
|
|
268
|
+
handle: PFrameHandle,
|
|
269
|
+
params: GetColumnsFullParams,
|
|
270
|
+
): Promise<PColumnIdAndSpec[]> {
|
|
271
|
+
const { selectedSources, strictlyCompatible, types, names, annotations, annotationsNotEmpty } = params;
|
|
272
|
+
|
|
273
|
+
try {
|
|
274
|
+
const request: FindColumnsRequest = {
|
|
275
|
+
columnFilter: {
|
|
276
|
+
type: types,
|
|
277
|
+
name: names,
|
|
278
|
+
annotationValue: annotations,
|
|
279
|
+
annotationPattern: annotationsNotEmpty?.reduce((res, v) => {
|
|
280
|
+
res[v] = '.+';
|
|
281
|
+
return res;
|
|
282
|
+
}, {} as Record<string, string>),
|
|
283
|
+
},
|
|
284
|
+
compatibleWith: await getRequestColumnsFromSelectedSources(handle, selectedSources),
|
|
285
|
+
strictlyCompatible,
|
|
286
|
+
};
|
|
287
|
+
|
|
288
|
+
const response: FindColumnsResponse = await getPFrameDriver().findColumns(handle, request);
|
|
289
|
+
return response.hits;
|
|
290
|
+
} catch (err) {
|
|
291
|
+
console.error('PFrame: findColumns error');
|
|
292
|
+
throw err;
|
|
293
|
+
}
|
|
294
|
+
}
|
|
295
|
+
|
|
296
|
+
export async function getColumnOrAxisValueLabelsId(
|
|
297
|
+
handle: PFrameHandle,
|
|
298
|
+
strAxisId: CanonicalizedJson<AxisId>,
|
|
299
|
+
): Promise<PObjectId | undefined> {
|
|
300
|
+
const labelColumns = await getColumnsFull(handle, {
|
|
301
|
+
selectedSources: [],
|
|
302
|
+
strictlyCompatible: false,
|
|
303
|
+
names: [Annotation.Label],
|
|
304
|
+
});
|
|
305
|
+
|
|
306
|
+
const labelColumn = labelColumns.find(({ spec }) => {
|
|
307
|
+
return spec && spec.axesSpec.length === 1 && canonicalizeAxisId(spec.axesSpec[0]) === strAxisId;
|
|
308
|
+
});
|
|
309
|
+
|
|
310
|
+
return labelColumn?.columnId;
|
|
311
|
+
}
|
|
312
|
+
|
|
313
|
+
function getDiscreteValuesFromAnnotation(columnSpec: PColumnSpec): undefined | SuggestionResponse {
|
|
314
|
+
const discreteValuesStr = readAnnotation(columnSpec, Annotation.DiscreteValues);
|
|
315
|
+
if (!discreteValuesStr) {
|
|
316
|
+
return undefined;
|
|
317
|
+
}
|
|
318
|
+
|
|
319
|
+
try {
|
|
320
|
+
const discreteValues: string[] = (JSON.parse(discreteValuesStr) as (string | number)[]).map((v) => String(v));
|
|
321
|
+
const values = discreteValues.map((v) => ({ value: v, label: v })).sort(sortValuesPredicate);
|
|
322
|
+
return { values, overflow: false };
|
|
323
|
+
} catch {
|
|
324
|
+
console.error(`Parsing error: discrete values annotation ${discreteValuesStr}`);
|
|
325
|
+
return undefined;
|
|
326
|
+
}
|
|
327
|
+
}
|
|
328
|
+
|
|
329
|
+
async function getAxisValuesWithLabels(
|
|
330
|
+
handle: PFrameHandle,
|
|
331
|
+
params: {
|
|
332
|
+
columnId: PObjectId;
|
|
333
|
+
axisSpec: AxisSpec;
|
|
334
|
+
labelsColumnId: PObjectId | undefined;
|
|
335
|
+
limit?: number;
|
|
336
|
+
searchQuery?: string;
|
|
337
|
+
searchQueryValue?: string;
|
|
338
|
+
},
|
|
339
|
+
): Promise<SuggestionResponse> {
|
|
340
|
+
const { columnId, axisSpec, labelsColumnId, limit, searchQuery, searchQueryValue } = params;
|
|
341
|
+
const strAxisId = canonicalizeAxisId(getAxisId(axisSpec));
|
|
342
|
+
|
|
343
|
+
let filters: PTableRecordSingleValueFilterV2[] = [];
|
|
344
|
+
|
|
345
|
+
if (labelsColumnId) {
|
|
346
|
+
if (searchQuery) {
|
|
347
|
+
filters = [createSearchFilter(labelsColumnId, searchQuery)];
|
|
348
|
+
}
|
|
349
|
+
if (searchQueryValue) {
|
|
350
|
+
filters = [createAxisSearchFilter(axisSpec, searchQueryValue)];
|
|
351
|
+
}
|
|
352
|
+
|
|
353
|
+
const { data: dataValues, axesData } = await getSingleColumnData(handle, labelsColumnId, filters);
|
|
354
|
+
const axisKeys = axesData[strAxisId];
|
|
355
|
+
const values: { value: string; label: string }[] = [];
|
|
356
|
+
|
|
357
|
+
for (let i = 0; i < Math.min(axisKeys.length, limit ?? axisKeys.length); i++) {
|
|
358
|
+
values.push({ value: String(axisKeys[i]), label: String(dataValues[i]) });
|
|
359
|
+
}
|
|
360
|
+
|
|
361
|
+
values.sort(sortValuesPredicate);
|
|
362
|
+
return { values, overflow: !(limit === undefined || axisKeys.length < limit) };
|
|
363
|
+
} else {
|
|
364
|
+
const searchInLabelsOrValue = searchQuery ?? searchQueryValue;
|
|
365
|
+
if (searchInLabelsOrValue) {
|
|
366
|
+
filters = [createAxisSearchFilter(axisSpec, searchInLabelsOrValue)];
|
|
367
|
+
}
|
|
368
|
+
|
|
369
|
+
const response = await getAxisUniqueValues(handle, {
|
|
370
|
+
axisId: getAxisId(axisSpec),
|
|
371
|
+
parentColumnIds: [columnId],
|
|
372
|
+
limit,
|
|
373
|
+
filters,
|
|
374
|
+
});
|
|
375
|
+
|
|
376
|
+
const values = mapValuesToSuggestions(response.values);
|
|
377
|
+
return { values, overflow: response.overflow };
|
|
378
|
+
}
|
|
379
|
+
}
|
|
380
|
+
|
|
381
|
+
async function getColumnValuesWithLabels(
|
|
382
|
+
handle: PFrameHandle,
|
|
383
|
+
params: {
|
|
384
|
+
columnId: PObjectId;
|
|
385
|
+
limit?: number;
|
|
386
|
+
searchQuery?: string;
|
|
387
|
+
searchQueryValue?: string;
|
|
388
|
+
},
|
|
389
|
+
): Promise<SuggestionResponse> {
|
|
390
|
+
const { columnId, limit, searchQuery, searchQueryValue } = params;
|
|
391
|
+
const searchInLabelsOrValue = searchQuery ?? searchQueryValue;
|
|
392
|
+
|
|
393
|
+
const filters: PTableRecordSingleValueFilterV2[] = searchInLabelsOrValue
|
|
394
|
+
? [createSearchFilter(columnId, searchInLabelsOrValue)]
|
|
395
|
+
: [];
|
|
396
|
+
|
|
397
|
+
const response = await getColumnUniqueValues(handle, columnId, limit, filters);
|
|
398
|
+
const values = mapValuesToSuggestions(response.values);
|
|
399
|
+
return { values, overflow: response.overflow };
|
|
400
|
+
}
|
|
401
|
+
|
|
402
|
+
export async function getUniqueSourceValuesWithLabels(
|
|
403
|
+
handle: PFrameHandle,
|
|
404
|
+
params: GetUniqueSourceValuesParams,
|
|
405
|
+
): Promise<SuggestionResponse> {
|
|
406
|
+
const { columnId, axisIdx, limit, searchQuery, searchQueryValue } = params;
|
|
407
|
+
|
|
408
|
+
const selectedSourceSpec = await getColumnSpecById(handle, columnId);
|
|
409
|
+
if (selectedSourceSpec == null || selectedSourceSpec.kind !== 'PColumn') {
|
|
410
|
+
return { values: [], overflow: false };
|
|
411
|
+
}
|
|
412
|
+
|
|
413
|
+
// Try to get discrete values from annotation
|
|
414
|
+
const discreteValues = getDiscreteValuesFromAnnotation(selectedSourceSpec);
|
|
415
|
+
if (discreteValues != null) {
|
|
416
|
+
return discreteValues;
|
|
417
|
+
}
|
|
418
|
+
|
|
419
|
+
// Handle axis values
|
|
420
|
+
if (axisIdx != null) {
|
|
421
|
+
const axisSpec = selectedSourceSpec.axesSpec[axisIdx];
|
|
422
|
+
const strAxisId = canonicalizeAxisId(getAxisId(axisSpec));
|
|
423
|
+
const labelsColumnId = await getColumnOrAxisValueLabelsId(handle, strAxisId);
|
|
424
|
+
|
|
425
|
+
return getAxisValuesWithLabels(handle, {
|
|
426
|
+
columnId,
|
|
427
|
+
axisSpec,
|
|
428
|
+
labelsColumnId,
|
|
429
|
+
limit,
|
|
430
|
+
searchQuery,
|
|
431
|
+
searchQueryValue,
|
|
432
|
+
});
|
|
433
|
+
}
|
|
434
|
+
|
|
435
|
+
// Handle column values
|
|
436
|
+
return getColumnValuesWithLabels(handle, {
|
|
437
|
+
columnId,
|
|
438
|
+
limit,
|
|
439
|
+
searchQuery,
|
|
440
|
+
searchQueryValue,
|
|
441
|
+
});
|
|
442
|
+
}
|