@datagrok/bio 2.26.2 → 2.26.5
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/CHANGELOG.md +16 -0
- package/dist/284.js.map +1 -1
- package/dist/455.js.map +1 -1
- package/dist/705.js +1 -1
- package/dist/705.js.map +1 -1
- package/dist/980.js.map +1 -1
- package/dist/package-test.js +2 -2
- package/dist/package-test.js.map +1 -1
- package/dist/package.js +2 -2
- package/dist/package.js.map +1 -1
- package/package.json +5 -5
- package/src/analysis/sequence-activity-cliffs.ts +12 -0
- package/src/package-api.ts +13 -1
- package/src/package-test.ts +1 -0
- package/src/package.g.ts +35 -0
- package/src/package.ts +163 -56
- package/src/tests/projects-tests.ts +202 -0
- package/src/utils/cell-renderer.ts +4 -2
- package/src/utils/convert.ts +9 -3
- package/src/utils/monomer-cell-renderer.ts +40 -3
- package/src/utils/seq-helper/seq-handler.ts +18 -8
- package/test-console-output-1.log +748 -583
- package/test-record-1.mp4 +0 -0
package/src/utils/convert.ts
CHANGED
|
@@ -162,10 +162,16 @@ export async function convertDo(srcCol: DG.Column, seqHelper: ISeqHelper, target
|
|
|
162
162
|
return '';
|
|
163
163
|
}
|
|
164
164
|
});
|
|
165
|
+
newCol.semType = DG.SEMTYPE.MACROMOLECULE;
|
|
166
|
+
newCol.meta.units = NOTATION.CUSTOM;
|
|
167
|
+
newCol.setTag('separator', '-');
|
|
168
|
+
newCol.setTag('aligned', 'SEQ');
|
|
169
|
+
newCol.setTag('alphabet', 'UN');
|
|
170
|
+
newCol.setTag('.alphabetIsMultichar', 'true');
|
|
165
171
|
srcCol.dataFrame.columns.add(newCol);
|
|
166
|
-
const semType = await grok.functions.call('Bio:detectMacromolecule', {col: newCol});
|
|
167
|
-
if (semType)
|
|
168
|
-
|
|
172
|
+
// const semType = await grok.functions.call('Bio:detectMacromolecule', {col: newCol});
|
|
173
|
+
// if (semType)
|
|
174
|
+
// newCol.semType = semType;
|
|
169
175
|
await grok.data.detectSemanticTypes(srcCol.dataFrame);
|
|
170
176
|
return newCol;
|
|
171
177
|
} else {
|
|
@@ -3,7 +3,8 @@ import * as DG from 'datagrok-api/dg';
|
|
|
3
3
|
import {GridCell} from 'datagrok-api/dg';
|
|
4
4
|
import * as ui from 'datagrok-api/ui';
|
|
5
5
|
import {ALPHABET, monomerToShort} from '@datagrok-libraries/bio/src/utils/macromolecule';
|
|
6
|
-
import {BioTags, GAP_SYMBOL, MONOMER_MOTIF_SPLITTER, TAGS as bioTAGS,} from '@datagrok-libraries/bio/src/utils/macromolecule/consts';
|
|
6
|
+
import {BioTags, GAP_SYMBOL, MONOMER_CANONICALIZER_FUNC_TAG, MONOMER_CANONICALIZER_TEMP, MONOMER_MOTIF_SPLITTER, TAGS as bioTAGS,} from '@datagrok-libraries/bio/src/utils/macromolecule/consts';
|
|
7
|
+
import {IMonomerCanonicalizer} from '@datagrok-libraries/bio/src/utils/macromolecule/types';
|
|
7
8
|
import {MONOMER_RENDERER_TAGS} from '@datagrok-libraries/bio/src/utils/cell-renderer';
|
|
8
9
|
import {getGridCellColTemp} from '@datagrok-libraries/bio/src/utils/cell-renderer-back-base';
|
|
9
10
|
|
|
@@ -24,6 +25,34 @@ export class MonomerCellRendererBack extends CellRendererWithMonomerLibBackBase
|
|
|
24
25
|
super(gridCol, tableCol);
|
|
25
26
|
}
|
|
26
27
|
|
|
28
|
+
private static readonly _canonLoadingKey = MONOMER_CANONICALIZER_TEMP + '.loading';
|
|
29
|
+
|
|
30
|
+
/** Lazily resolves and caches the IMonomerCanonicalizer from the column tag/temp.
|
|
31
|
+
* Uses async apply() so the plugin package doesn't need to be loaded yet.
|
|
32
|
+
* Returns null while loading; the resolved instance is cached for subsequent calls. */
|
|
33
|
+
private getCanonicalizer(col: DG.Column): IMonomerCanonicalizer | null {
|
|
34
|
+
const cached: IMonomerCanonicalizer | null = col.temp[MONOMER_CANONICALIZER_TEMP] ?? null;
|
|
35
|
+
if (cached) return cached;
|
|
36
|
+
// Already loading — don't fire another request
|
|
37
|
+
if (col.temp[MonomerCellRendererBack._canonLoadingKey]) return null;
|
|
38
|
+
const funcName = col.getTag(MONOMER_CANONICALIZER_FUNC_TAG);
|
|
39
|
+
if (!funcName) return null;
|
|
40
|
+
const parts = funcName.includes(':') ? funcName.split(':') : [undefined, funcName];
|
|
41
|
+
const funcs = DG.Func.find({name: parts[1], package: parts[0]});
|
|
42
|
+
if (funcs.length === 0) return null;
|
|
43
|
+
col.temp[MonomerCellRendererBack._canonLoadingKey] = true;
|
|
44
|
+
funcs[0].apply({}).then((canon: IMonomerCanonicalizer) => {
|
|
45
|
+
col.temp[MONOMER_CANONICALIZER_TEMP] = canon;
|
|
46
|
+
col.temp[MonomerCellRendererBack._canonLoadingKey] = false;
|
|
47
|
+
// Invalidate the grid so cells re-render with the canonicalizer
|
|
48
|
+
if (this.gridCol?.grid?.invalidate)
|
|
49
|
+
this.gridCol.grid.invalidate();
|
|
50
|
+
}).catch(() => {
|
|
51
|
+
col.temp[MonomerCellRendererBack._canonLoadingKey] = false;
|
|
52
|
+
});
|
|
53
|
+
return null;
|
|
54
|
+
}
|
|
55
|
+
|
|
27
56
|
render(g: CanvasRenderingContext2D,
|
|
28
57
|
x: number, y: number, w: number, h: number, gridCell: DG.GridCell, cellStyle: DG.GridCellStyle
|
|
29
58
|
): void {
|
|
@@ -41,6 +70,10 @@ export class MonomerCellRendererBack extends CellRendererWithMonomerLibBackBase
|
|
|
41
70
|
g.textAlign = 'left';
|
|
42
71
|
|
|
43
72
|
let value: string = gridCell.cell.value;
|
|
73
|
+
// render original value
|
|
74
|
+
// const canonicalizer = this.getCanonicalizer(gridCell.cell.column);
|
|
75
|
+
// if (canonicalizer && value)
|
|
76
|
+
// value = canonicalizer.canonicalize(value);
|
|
44
77
|
if (!value || value === GAP_SYMBOL)
|
|
45
78
|
value = DASH_GAP_SYMBOL;
|
|
46
79
|
const symbols = value.split(MONOMER_MOTIF_SPLITTER).map((s) => !s || s === GAP_SYMBOL ? DASH_GAP_SYMBOL : s.trim());
|
|
@@ -88,12 +121,16 @@ export class MonomerCellRendererBack extends CellRendererWithMonomerLibBackBase
|
|
|
88
121
|
) return false;
|
|
89
122
|
|
|
90
123
|
const alphabet = tableCol.getTag(bioTAGS.alphabet) as ALPHABET;
|
|
91
|
-
const
|
|
124
|
+
const rawMonomerName: string = gridCell.cell.value;
|
|
125
|
+
const canonicalizer = this.getCanonicalizer(tableCol);
|
|
126
|
+
const monomerName = canonicalizer && rawMonomerName ? canonicalizer.canonicalize(rawMonomerName) : rawMonomerName;
|
|
92
127
|
const canvasClientRect = gridCell.grid.canvas.getBoundingClientRect();
|
|
93
128
|
const x1 = gridCell.bounds.right + canvasClientRect.left - 4;
|
|
94
129
|
const y1 = gridCell.bounds.bottom + canvasClientRect.top - 4;
|
|
95
130
|
|
|
96
|
-
|
|
131
|
+
const isGap = !rawMonomerName || rawMonomerName == GAP_SYMBOL || rawMonomerName == DASH_GAP_SYMBOL ||
|
|
132
|
+
(canonicalizer != null && canonicalizer.isGap(rawMonomerName));
|
|
133
|
+
if (isGap) {
|
|
97
134
|
ui.tooltip.show(ui.divText('gap'), x1, y1);
|
|
98
135
|
return true;
|
|
99
136
|
}
|
|
@@ -44,6 +44,15 @@ export class SeqHandler implements ISeqHandler {
|
|
|
44
44
|
private _refinerPromise: Promise<void> = Promise.resolve();
|
|
45
45
|
public get refinerPromise(): Promise<void> { return this._refinerPromise; }
|
|
46
46
|
|
|
47
|
+
private runInnerDetector() {
|
|
48
|
+
if (!this._column.temp['seqHandlerDetectorRun']) {
|
|
49
|
+
this._column.temp['seqHandlerDetectorRun'] = true;
|
|
50
|
+
const detectorFunc = DG.Func.find({name: 'detectMacromolecule', meta: {role: 'semTypeDetector'}})[0];
|
|
51
|
+
if (detectorFunc)
|
|
52
|
+
detectorFunc.applySync({col: this._column});
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
|
|
47
56
|
protected constructor(col: DG.Column<string>,
|
|
48
57
|
private readonly seqHelper: SeqHelper,
|
|
49
58
|
) {
|
|
@@ -53,19 +62,20 @@ export class SeqHandler implements ISeqHandler {
|
|
|
53
62
|
let units: string | null = this._column.meta.units;
|
|
54
63
|
if (!units) {
|
|
55
64
|
// it may be from layout that the macromolecule semtype is set but every other tag is missing, so we manually run detectors
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
const detectorFunc = DG.Func.find({name: 'detectMacromolecule', meta: {role: 'semTypeDetector'}})[0];
|
|
59
|
-
if (detectorFunc)
|
|
60
|
-
detectorFunc.applySync({col: this._column});
|
|
61
|
-
units = this._column.meta.units;
|
|
62
|
-
}
|
|
65
|
+
this.runInnerDetector();
|
|
66
|
+
units = this._column.meta.units;
|
|
63
67
|
if (!units)
|
|
64
68
|
throw new Error('Units are not specified in column');
|
|
65
69
|
}
|
|
66
70
|
this._units = units!;
|
|
67
71
|
|
|
68
72
|
this._notation = this.getNotation();
|
|
73
|
+
if ([NOTATION.BILN, NOTATION.CUSTOM, NOTATION.SEPARATOR].includes(this._notation) && !this.column.getTag(TAGS.separator)) {
|
|
74
|
+
this.runInnerDetector();
|
|
75
|
+
if (!this.column.getTag(TAGS.separator))
|
|
76
|
+
throw new Error(`Separator tag '${TAGS.separator}' is not set for notation '${this._notation}'.`);
|
|
77
|
+
}
|
|
78
|
+
|
|
69
79
|
if (this.isCustom() || this.isBiln()) {
|
|
70
80
|
// this.column.temp[SeqTemps.notationProvider] must be set at detector stage
|
|
71
81
|
this.notationProvider = this.column.temp[SeqTemps.notationProvider] ?? null;
|
|
@@ -185,7 +195,7 @@ export class SeqHandler implements ISeqHandler {
|
|
|
185
195
|
public static setTags(uh: SeqHandler): void {
|
|
186
196
|
const units = uh.column.meta.units as NOTATION;
|
|
187
197
|
|
|
188
|
-
if ([NOTATION.FASTA, NOTATION.SEPARATOR].includes(units)) {
|
|
198
|
+
if ([NOTATION.FASTA, NOTATION.SEPARATOR, NOTATION.BILN].includes(units)) {
|
|
189
199
|
// Empty monomer alphabet is allowed, only if alphabet tag is annotated
|
|
190
200
|
if (!uh.column.getTag(TAGS.alphabet) && Object.keys(uh.stats.freq).length === 0)
|
|
191
201
|
throw new Error('Alphabet is empty and not annotated.');
|