@datagrok/bio 2.10.24 → 2.10.26
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 +4 -1
- package/dist/package-test.js +1 -1
- package/dist/package-test.js.map +1 -1
- package/dist/package.js +1 -1
- package/dist/package.js.map +1 -1
- package/package.json +2 -2
- package/src/package.ts +2 -2
- package/src/tests/WebLogo-positions-test.ts +22 -4
- package/src/utils/helm-to-molfile.ts +8 -4
- package/src/viewers/vd-regions-viewer.ts +9 -10
- package/src/viewers/web-logo-viewer.ts +48 -40
package/package.json
CHANGED
|
@@ -5,7 +5,7 @@
|
|
|
5
5
|
"name": "Leonid Stolbov",
|
|
6
6
|
"email": "lstolbov@datagrok.ai"
|
|
7
7
|
},
|
|
8
|
-
"version": "2.10.
|
|
8
|
+
"version": "2.10.26",
|
|
9
9
|
"description": "Bioinformatics support (import/export of sequences, conversion, visualization, analysis). [See more](https://github.com/datagrok-ai/public/blob/master/packages/Bio/README.md) for details.",
|
|
10
10
|
"repository": {
|
|
11
11
|
"type": "git",
|
|
@@ -34,7 +34,7 @@
|
|
|
34
34
|
],
|
|
35
35
|
"dependencies": {
|
|
36
36
|
"@biowasm/aioli": "^3.1.0",
|
|
37
|
-
"@datagrok-libraries/bio": "^5.38.
|
|
37
|
+
"@datagrok-libraries/bio": "^5.38.13",
|
|
38
38
|
"@datagrok-libraries/chem-meta": "^1.0.1",
|
|
39
39
|
"@datagrok-libraries/ml": "^6.3.49",
|
|
40
40
|
"@datagrok-libraries/tutorials": "^1.3.6",
|
package/src/package.ts
CHANGED
|
@@ -73,7 +73,7 @@ import {HelmToMolfileConverter} from './utils/helm-to-molfile';
|
|
|
73
73
|
import {DIMENSIONALITY_REDUCER_TERMINATE_EVENT}
|
|
74
74
|
from '@datagrok-libraries/ml/src/workers/dimensionality-reducing-worker-creator';
|
|
75
75
|
import {Options} from '@datagrok-libraries/utils/src/type-declarations';
|
|
76
|
-
import {
|
|
76
|
+
import {sequenceToMolfile} from './utils/sequence-to-mol';
|
|
77
77
|
|
|
78
78
|
export const _package = new BioPackage();
|
|
79
79
|
|
|
@@ -515,7 +515,7 @@ export async function sequenceSpaceTopMenu(
|
|
|
515
515
|
usingSparseMatrix: table.rowCount > 20000},
|
|
516
516
|
};
|
|
517
517
|
|
|
518
|
-
const allowedRowCount = methodName === DimReductionMethods.UMAP ?
|
|
518
|
+
const allowedRowCount = methodName === DimReductionMethods.UMAP ? 500000 : 15000;
|
|
519
519
|
// number of rows which will be processed relatively fast
|
|
520
520
|
const fastRowCount = methodName === DimReductionMethods.UMAP ? 5000 : 2000;
|
|
521
521
|
if (table.rowCount > allowedRowCount) {
|
|
@@ -32,7 +32,7 @@ ATC-G-TTGC--
|
|
|
32
32
|
const wlViewer: WebLogoViewer = (await df.plot.fromType('WebLogo')) as WebLogoViewer;
|
|
33
33
|
await testEvent(wlViewer.onLayoutCalculated, () => {}, () => {
|
|
34
34
|
tv.dockManager.dock(wlViewer.root, DG.DOCK_TYPE.DOWN);
|
|
35
|
-
},
|
|
35
|
+
}, 300);
|
|
36
36
|
const positions: PI[] = wlViewer['positions'];
|
|
37
37
|
|
|
38
38
|
const resAllDf1: PI[] = [
|
|
@@ -84,7 +84,7 @@ ATC-G-TTGC--
|
|
|
84
84
|
{'shrinkEmptyTail': true})) as WebLogoViewer;
|
|
85
85
|
await testEvent(wlViewer.onLayoutCalculated, () => {}, () => {
|
|
86
86
|
tv.dockManager.dock(wlViewer.root, DG.DOCK_TYPE.DOWN);
|
|
87
|
-
},
|
|
87
|
+
}, 500);
|
|
88
88
|
const positions: PI[] = wlViewer['positions'];
|
|
89
89
|
|
|
90
90
|
const resAllDf1: PI[] = [
|
|
@@ -122,7 +122,7 @@ ATC-G-TTGC--
|
|
|
122
122
|
{'skipEmptyPositions': true})) as WebLogoViewer;
|
|
123
123
|
await testEvent(wlViewer.onLayoutCalculated, () => {}, () => {
|
|
124
124
|
tv.dockManager.dock(wlViewer.root, DG.DOCK_TYPE.DOWN);
|
|
125
|
-
},
|
|
125
|
+
}, 300);
|
|
126
126
|
const resPosList: PI[] = wlViewer['positions'];
|
|
127
127
|
|
|
128
128
|
const tgtPosList: PI[] = [
|
|
@@ -158,7 +158,7 @@ ATC-G-TTGC--
|
|
|
158
158
|
})) as WebLogoViewer;
|
|
159
159
|
await testEvent(wlViewer.onLayoutCalculated, () => {}, () => {
|
|
160
160
|
tv.dockManager.dock(wlViewer.root, DG.DOCK_TYPE.DOWN);
|
|
161
|
-
},
|
|
161
|
+
}, 300);
|
|
162
162
|
const resPosList: PI[] = wlViewer['positions'];
|
|
163
163
|
const tgtPosList: PI[] = [
|
|
164
164
|
new PI(2, '3', {'C': new PMI(5)}),
|
|
@@ -179,6 +179,24 @@ ATC-G-TTGC--
|
|
|
179
179
|
const countAt1 = countForMonomerAtPosition(df, uh, df.filter, 'G', atPI1);
|
|
180
180
|
expect(countAt1, 5);
|
|
181
181
|
});
|
|
182
|
+
|
|
183
|
+
test('empty', async () => {
|
|
184
|
+
const df: DG.DataFrame = DG.DataFrame.fromColumns([(() => {
|
|
185
|
+
const col = DG.Column.fromStrings('seq', []);
|
|
186
|
+
col.setTag(DG.TAGS.SEMTYPE, DG.SEMTYPE.MACROMOLECULE);
|
|
187
|
+
col.setTag(DG.TAGS.UNITS, NOTATION.FASTA);
|
|
188
|
+
col.setTag(bioTAGS.alphabet, ALPHABET.DNA);
|
|
189
|
+
return col;
|
|
190
|
+
})()]);
|
|
191
|
+
|
|
192
|
+
const tv: DG.TableView = grok.shell.addTableView(df);
|
|
193
|
+
|
|
194
|
+
const wlViewer: WebLogoViewer = (await df.plot.fromType('WebLogo')) as WebLogoViewer;
|
|
195
|
+
await testEvent(wlViewer.onLayoutCalculated, () => {}, () => {
|
|
196
|
+
tv.dockManager.dock(wlViewer.root, DG.DOCK_TYPE.DOWN);
|
|
197
|
+
}, 300);
|
|
198
|
+
const resPosList: PI[] = wlViewer['positions'];
|
|
199
|
+
});
|
|
182
200
|
});
|
|
183
201
|
|
|
184
202
|
function expectPositionInfo(actualPos: PI, expectedPos: PI): void {
|
|
@@ -2,13 +2,16 @@
|
|
|
2
2
|
import * as grok from 'datagrok-api/grok';
|
|
3
3
|
import * as ui from 'datagrok-api/ui';
|
|
4
4
|
import * as DG from 'datagrok-api/dg';
|
|
5
|
-
import {MonomerLibHelper} from './monomer-lib';
|
|
6
5
|
|
|
7
6
|
import {MolfileHandler} from '@datagrok-libraries/chem-meta/src/parsing-utils/molfile-handler';
|
|
8
7
|
import {MolfileHandlerBase} from '@datagrok-libraries/chem-meta/src/parsing-utils/molfile-handler-base';
|
|
9
8
|
import {RDMol, RDModule} from '@datagrok-libraries/chem-meta/src/rdkit-api';
|
|
10
9
|
import {HELM_POLYMER_TYPE, HELM_RGROUP_FIELDS} from '@datagrok-libraries/bio/src/utils/const';
|
|
11
|
-
|
|
10
|
+
|
|
11
|
+
import {MonomerLibHelper} from './monomer-lib';
|
|
12
|
+
import {errInfo} from './err-info';
|
|
13
|
+
|
|
14
|
+
import {_package} from '../package';
|
|
12
15
|
|
|
13
16
|
const enum V2K_CONST {
|
|
14
17
|
MAX_ATOM_COUNT = 999,
|
|
@@ -92,8 +95,9 @@ export class HelmToMolfileConverter {
|
|
|
92
95
|
let result = '';
|
|
93
96
|
try {
|
|
94
97
|
result = this.getPolymerMolfile(helm, pseudoMolfile);
|
|
95
|
-
} catch (err) {
|
|
96
|
-
|
|
98
|
+
} catch (err: any) {
|
|
99
|
+
const [errMsg, errStack] = errInfo(err);
|
|
100
|
+
_package.logger.error(errMsg, undefined, errStack);
|
|
97
101
|
} finally {
|
|
98
102
|
return result;
|
|
99
103
|
}
|
|
@@ -12,6 +12,7 @@ import {
|
|
|
12
12
|
import {FilterSources, IWebLogoViewer, PositionHeight} from '@datagrok-libraries/bio/src/viewers/web-logo';
|
|
13
13
|
|
|
14
14
|
import {WebLogoViewer, PROPS as wlPROPS} from '../viewers/web-logo-viewer';
|
|
15
|
+
import {errInfo} from '../utils/err-info';
|
|
15
16
|
|
|
16
17
|
import {_package} from '../package';
|
|
17
18
|
|
|
@@ -153,15 +154,18 @@ export class VdRegionsViewer extends DG.JsViewer implements IVdRegionsViewer {
|
|
|
153
154
|
|
|
154
155
|
override detach() {
|
|
155
156
|
const superDetach = super.detach.bind(this);
|
|
156
|
-
this.
|
|
157
|
-
await this.viewPromise;
|
|
157
|
+
this.viewPromise = this.viewPromise.then(async () => { // detach
|
|
158
158
|
if (this.setDataInProgress) return; // check setDataInProgress synced
|
|
159
159
|
if (this.viewed) {
|
|
160
160
|
await this.destroyView('detach');
|
|
161
161
|
this.viewed = false;
|
|
162
162
|
}
|
|
163
163
|
superDetach();
|
|
164
|
-
})
|
|
164
|
+
})
|
|
165
|
+
.catch((err: any) => {
|
|
166
|
+
const [errMsg, errStack] = errInfo(err);
|
|
167
|
+
_package.logger.error(errMsg, undefined, errStack);
|
|
168
|
+
});
|
|
165
169
|
}
|
|
166
170
|
|
|
167
171
|
override onTableAttached() {
|
|
@@ -247,9 +251,6 @@ export class VdRegionsViewer extends DG.JsViewer implements IVdRegionsViewer {
|
|
|
247
251
|
this.viewed = false;
|
|
248
252
|
}
|
|
249
253
|
|
|
250
|
-
await this.detachPromise;
|
|
251
|
-
// Wait whether this.dataFrame assigning has called detach() before continue set data and build view
|
|
252
|
-
|
|
253
254
|
// -- Data --
|
|
254
255
|
this.regions = regions;
|
|
255
256
|
|
|
@@ -261,10 +262,9 @@ export class VdRegionsViewer extends DG.JsViewer implements IVdRegionsViewer {
|
|
|
261
262
|
this.viewed = true;
|
|
262
263
|
}
|
|
263
264
|
} catch (err: any) {
|
|
264
|
-
const errMsg = err
|
|
265
|
-
const stack = err instanceof Error ? err.stack : undefined;
|
|
265
|
+
const [errMsg, errStack] = errInfo(err);
|
|
266
266
|
grok.shell.error(errMsg);
|
|
267
|
-
_package.logger.error(errMsg, undefined,
|
|
267
|
+
_package.logger.error(errMsg, undefined, errStack);
|
|
268
268
|
} finally {
|
|
269
269
|
// _package.logger.debug('Bio: VdRegionsViewer.setData(), finally, ' +
|
|
270
270
|
// `viewerId = ${this.viewerId}, setDataInId = ${setDataInId}, ` +
|
|
@@ -277,7 +277,6 @@ export class VdRegionsViewer extends DG.JsViewer implements IVdRegionsViewer {
|
|
|
277
277
|
// -- View --
|
|
278
278
|
|
|
279
279
|
private viewPromise: Promise<void> = Promise.resolve();
|
|
280
|
-
private detachPromise: Promise<void> = Promise.resolve();
|
|
281
280
|
private setDataInProgress: boolean = false;
|
|
282
281
|
|
|
283
282
|
private host: HTMLElement | null = null;
|
|
@@ -415,7 +415,6 @@ export class WebLogoViewer extends DG.JsViewer implements IWebLogoViewer {
|
|
|
415
415
|
this.viewed = false;
|
|
416
416
|
}
|
|
417
417
|
|
|
418
|
-
await this.detachPromise;
|
|
419
418
|
this.updateSeqCol();
|
|
420
419
|
|
|
421
420
|
if (!this.viewed) {
|
|
@@ -435,7 +434,6 @@ export class WebLogoViewer extends DG.JsViewer implements IWebLogoViewer {
|
|
|
435
434
|
// -- View --
|
|
436
435
|
|
|
437
436
|
private viewPromise: Promise<void> = Promise.resolve();
|
|
438
|
-
private detachPromise: Promise<void> = Promise.resolve();
|
|
439
437
|
private setDataInProgress: boolean = false;
|
|
440
438
|
private viewSubs: Unsubscribable[] = [];
|
|
441
439
|
|
|
@@ -834,15 +832,18 @@ export class WebLogoViewer extends DG.JsViewer implements IWebLogoViewer {
|
|
|
834
832
|
_package.logger.debug(`Bio: WebLogoViewer<${this.viewerId}>.detach(), `);
|
|
835
833
|
|
|
836
834
|
const superDetach = super.detach.bind(this);
|
|
837
|
-
this.
|
|
838
|
-
await this.viewPromise;
|
|
835
|
+
this.viewPromise = this.viewPromise.then(async () => { // detach
|
|
839
836
|
if (this.setDataInProgress) return; // check setDataInProgress synced
|
|
840
837
|
if (this.viewed) {
|
|
841
838
|
await this.destroyView();
|
|
842
839
|
this.viewed = false;
|
|
843
840
|
}
|
|
844
841
|
superDetach();
|
|
845
|
-
})
|
|
842
|
+
})
|
|
843
|
+
.catch((err: any) => {
|
|
844
|
+
const [errMsg, errStack] = errInfo(err);
|
|
845
|
+
_package.logger.error(errMsg, undefined, errStack);
|
|
846
|
+
});
|
|
846
847
|
}
|
|
847
848
|
|
|
848
849
|
private _onSizeChanged: Subject<void> = new Subject<void>();
|
|
@@ -910,7 +911,7 @@ export class WebLogoViewer extends DG.JsViewer implements IWebLogoViewer {
|
|
|
910
911
|
}
|
|
911
912
|
|
|
912
913
|
/** Render WebLogo sensitive to changes in params of rendering
|
|
913
|
-
*@param {WlRenderLevel}
|
|
914
|
+
*@param {WlRenderLevel} renderLevel - indicates that need to recalculate data for rendering
|
|
914
915
|
*/
|
|
915
916
|
protected async renderInt(renderLevel: WlRenderLevel): Promise<void> {
|
|
916
917
|
_package.logger.debug(`Bio: WebLogoViewer<${this.viewerId}>.render.renderInt( renderLevel=${renderLevel} ), ` +
|
|
@@ -958,7 +959,7 @@ export class WebLogoViewer extends DG.JsViewer implements IWebLogoViewer {
|
|
|
958
959
|
};
|
|
959
960
|
|
|
960
961
|
/** Calculate layout of monomers on screen (canvas) based on freqs, required to handle mouse events */
|
|
961
|
-
const calculateLayoutInt = (dpr: number, positionLabelsHeight: number): void => {
|
|
962
|
+
const calculateLayoutInt = (firstPos: number, lastPos: number, dpr: number, positionLabelsHeight: number): void => {
|
|
962
963
|
_package.logger.debug(`Bio: WebLogoViewer<${this.viewerId}>.render.calculateLayoutInt(), start `);
|
|
963
964
|
|
|
964
965
|
const absoluteMaxHeight = this.canvas.height - positionLabelsHeight * dpr;
|
|
@@ -967,7 +968,7 @@ export class WebLogoViewer extends DG.JsViewer implements IWebLogoViewer {
|
|
|
967
968
|
grok.shell.error('WebLogo: alphabet is undefined.');
|
|
968
969
|
const alphabetSizeLog = Math.log2(alphabetSize);
|
|
969
970
|
|
|
970
|
-
for (let jPos =
|
|
971
|
+
for (let jPos = firstPos; jPos <= lastPos; ++jPos) {
|
|
971
972
|
if (!(jPos in this.positions)) {
|
|
972
973
|
console.warn(`Bio: WebLogoViewer<${this.viewerId}>.render.calculateLayoutInt() ` +
|
|
973
974
|
`this.positions.length = ${this.positions.length}, jPos = ${jPos}`);
|
|
@@ -998,41 +999,48 @@ export class WebLogoViewer extends DG.JsViewer implements IWebLogoViewer {
|
|
|
998
999
|
const positionLabelsHeight = this.showPositionLabels ? POSITION_LABELS_HEIGHT : 0;
|
|
999
1000
|
if (renderLevel >= WlRenderLevel.Freqs) calculateFreqsInt();
|
|
1000
1001
|
this.calcLayout(dpr); // after _skipEmptyPositions
|
|
1001
|
-
if (this.positions.length === 0 || this.startPosition === -1 || this.endPosition === -1) return;
|
|
1002
|
-
|
|
1002
|
+
if ( /* this.positions.length === 0 || */ this.startPosition === -1 /* || this.endPosition === -1*/) return;
|
|
1003
|
+
const firstPos: number = Math.max(Math.floor(this.slider.min), 0);
|
|
1004
|
+
const lastPos: number = Math.min(this.positions.length - 1, Math.floor(this.slider.max));
|
|
1005
|
+
if (renderLevel >= WlRenderLevel.Layout)
|
|
1006
|
+
calculateLayoutInt(firstPos, lastPos, window.devicePixelRatio, positionLabelsHeight);
|
|
1003
1007
|
|
|
1004
1008
|
const g = this.canvas.getContext('2d');
|
|
1005
1009
|
if (!g) return;
|
|
1006
|
-
|
|
1007
|
-
|
|
1008
|
-
|
|
1009
|
-
|
|
1010
|
-
|
|
1011
|
-
|
|
1012
|
-
|
|
1013
|
-
|
|
1014
|
-
|
|
1015
|
-
|
|
1016
|
-
|
|
1017
|
-
|
|
1018
|
-
|
|
1019
|
-
|
|
1020
|
-
|
|
1021
|
-
(this._positionWidth * dpr - 2)
|
|
1022
|
-
|
|
1023
|
-
|
|
1024
|
-
|
|
1025
|
-
|
|
1026
|
-
|
|
1027
|
-
|
|
1028
|
-
|
|
1029
|
-
|
|
1030
|
-
|
|
1031
|
-
|
|
1032
|
-
|
|
1033
|
-
|
|
1034
|
-
|
|
1035
|
-
|
|
1010
|
+
g.save();
|
|
1011
|
+
try {
|
|
1012
|
+
const length: number = this.Length;
|
|
1013
|
+
g.resetTransform();
|
|
1014
|
+
g.fillStyle = intToHtmlA(this.backgroundColor);
|
|
1015
|
+
g.fillRect(0, 0, this.canvas.width, this.canvas.height);
|
|
1016
|
+
g.textBaseline = this.textBaseline;
|
|
1017
|
+
|
|
1018
|
+
//#region Plot positionNames
|
|
1019
|
+
const positionFontSize = 10 * dpr;
|
|
1020
|
+
g.resetTransform();
|
|
1021
|
+
g.fillStyle = 'black';
|
|
1022
|
+
g.textAlign = 'center';
|
|
1023
|
+
g.font = `${positionFontSize.toFixed(1)}px Roboto, Roboto Local, sans-serif`;
|
|
1024
|
+
const posNameMaxWidth = Math.max(...this.positions.map((pos) => g.measureText(pos.name).width));
|
|
1025
|
+
const hScale = posNameMaxWidth < (this._positionWidth * dpr - 2) ? 1 :
|
|
1026
|
+
(this._positionWidth * dpr - 2) / posNameMaxWidth;
|
|
1027
|
+
|
|
1028
|
+
if (positionLabelsHeight > 0 && this.positions.length > 0) {
|
|
1029
|
+
renderPositionLabels(g, dpr, hScale, this._positionWidthWithMargin, this._positionWidth,
|
|
1030
|
+
this.positions, this.slider.min, this.slider.max);
|
|
1031
|
+
}
|
|
1032
|
+
//#endregion Plot positionNames
|
|
1033
|
+
const fontStyle = '16px Roboto, Roboto Local, sans-serif';
|
|
1034
|
+
// Hacks to scale uppercase characters to target rectangle
|
|
1035
|
+
const uppercaseLetterAscent = 0.25;
|
|
1036
|
+
const uppercaseLetterHeight = 12.2;
|
|
1037
|
+
for (let jPos = firstPos; jPos <= lastPos; jPos++) {
|
|
1038
|
+
this.positions[jPos].render(g, (m) => { return this.unitsHandler!.isGap(m); },
|
|
1039
|
+
fontStyle, uppercaseLetterAscent, uppercaseLetterHeight,
|
|
1040
|
+
/* this._positionWidthWithMargin, firstVisiblePosIdx,*/ this.cp);
|
|
1041
|
+
}
|
|
1042
|
+
} finally {
|
|
1043
|
+
g.restore();
|
|
1036
1044
|
}
|
|
1037
1045
|
|
|
1038
1046
|
_package.logger.debug(`Bio: WebLogoViewer<${this.viewerId}>.render.renderInt( recalcLevel=${renderLevel} ), end`);
|