@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/package.json CHANGED
@@ -5,7 +5,7 @@
5
5
  "name": "Leonid Stolbov",
6
6
  "email": "lstolbov@datagrok.ai"
7
7
  },
8
- "version": "2.10.24",
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.12",
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 { sequenceToMolfile } from './utils/sequence-to-mol';
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 ? 100000 : 15000;
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
- }, 200);
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
- }, 200);
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
- }, 200);
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
- }, 200);
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
- import {errorToConsole} from '@datagrok-libraries/utils';
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
- errorToConsole(err);
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.detachPromise = this.detachPromise.then(async () => { // detach
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 instanceof Error ? err.message : err.toString();
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, stack);
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.detachPromise = this.detachPromise.then(async () => { // detach
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} recalcLevel - indicates that need to recalculate data for rendering
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 = Math.floor(this.slider.min); jPos <= Math.floor(this.slider.max); ++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
- if (renderLevel >= WlRenderLevel.Layout) calculateLayoutInt(window.devicePixelRatio, positionLabelsHeight);
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
- const length: number = this.Length;
1008
- g.resetTransform();
1009
- g.fillStyle = intToHtmlA(this.backgroundColor);
1010
- g.fillRect(0, 0, this.canvas.width, this.canvas.height);
1011
- g.textBaseline = this.textBaseline;
1012
-
1013
- //#region Plot positionNames
1014
- const positionFontSize = 10 * dpr;
1015
- g.resetTransform();
1016
- g.fillStyle = 'black';
1017
- g.textAlign = 'center';
1018
- g.font = `${positionFontSize.toFixed(1)}px Roboto, Roboto Local, sans-serif`;
1019
- const posNameMaxWidth = Math.max(...this.positions.map((pos) => g.measureText(pos.name).width));
1020
- const hScale = posNameMaxWidth < (this._positionWidth * dpr - 2) ? 1 :
1021
- (this._positionWidth * dpr - 2) / posNameMaxWidth;
1022
-
1023
- if (positionLabelsHeight > 0) {
1024
- renderPositionLabels(g, dpr, hScale, this._positionWidthWithMargin, this._positionWidth,
1025
- this.positions, this.slider.min, this.slider.max);
1026
- }
1027
- //#endregion Plot positionNames
1028
- const fontStyle = '16px Roboto, Roboto Local, sans-serif';
1029
- // Hacks to scale uppercase characters to target rectangle
1030
- const uppercaseLetterAscent = 0.25;
1031
- const uppercaseLetterHeight = 12.2;
1032
- for (let jPos = Math.floor(this.slider.min); jPos <= Math.floor(this.slider.max); jPos++) {
1033
- this.positions[jPos].render(g, (m) => { return this.unitsHandler!.isGap(m); },
1034
- fontStyle, uppercaseLetterAscent, uppercaseLetterHeight,
1035
- /* this._positionWidthWithMargin, firstVisiblePosIdx,*/ this.cp);
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`);