@datagrok/bio 2.12.17 → 2.12.19

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.
Files changed (34) hide show
  1. package/CHANGELOG.md +21 -2
  2. package/dist/79.js.map +1 -1
  3. package/dist/package-test.js +3 -3
  4. package/dist/package-test.js.map +1 -1
  5. package/dist/package.js +3 -3
  6. package/dist/package.js.map +1 -1
  7. package/package.json +4 -4
  8. package/src/package.ts +56 -12
  9. package/src/tests/monomer-libraries-tests.ts +1 -4
  10. package/src/tests/renderers-monomer-placer-tests.ts +59 -12
  11. package/src/tests/renderers-test.ts +3 -5
  12. package/src/tests/scoring.ts +2 -2
  13. package/src/tests/substructure-filters-tests.ts +2 -0
  14. package/src/tests/to-atomic-level-tests.ts +1 -1
  15. package/src/tests/utils/sequences-generators.ts +0 -20
  16. package/src/tests/utils.ts +15 -0
  17. package/src/utils/cell-renderer.ts +39 -46
  18. package/src/utils/helm-to-molfile/converter/converter.ts +10 -5
  19. package/src/utils/helm-to-molfile/converter/monomer-wrapper.ts +9 -9
  20. package/src/utils/helm-to-molfile/converter/polymer.ts +10 -3
  21. package/src/utils/macromolecule-column-widget.ts +2 -0
  22. package/src/utils/monomer-cell-renderer.ts +18 -8
  23. package/src/utils/monomer-lib/lib-manager.ts +56 -18
  24. package/src/utils/monomer-lib/library-file-manager/event-manager.ts +15 -9
  25. package/src/utils/monomer-lib/library-file-manager/file-manager.ts +78 -59
  26. package/src/utils/monomer-lib/library-file-manager/file-validator.ts +3 -1
  27. package/src/utils/monomer-lib/library-file-manager/ui.ts +52 -47
  28. package/src/utils/monomer-lib/monomer-lib.ts +78 -9
  29. package/src/utils/multiple-sequence-alignment-ui.ts +31 -18
  30. package/src/utils/sequence-to-mol.ts +7 -7
  31. package/src/viewers/web-logo-viewer.ts +14 -4
  32. package/src/widgets/bio-substructure-filter-helm.ts +9 -3
  33. package/src/widgets/bio-substructure-filter.ts +2 -2
  34. package/webpack.config.js +1 -0
package/package.json CHANGED
@@ -5,7 +5,7 @@
5
5
  "name": "Aleksandr Tanas",
6
6
  "email": "atanas@datagrok.ai"
7
7
  },
8
- "version": "2.12.17",
8
+ "version": "2.12.19",
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,12 +34,12 @@
34
34
  ],
35
35
  "dependencies": {
36
36
  "@biowasm/aioli": "^3.1.0",
37
- "@datagrok-libraries/bio": "^5.41.0",
37
+ "@datagrok-libraries/bio": "^5.41.5",
38
38
  "@datagrok-libraries/chem-meta": "^1.2.5",
39
39
  "@datagrok-libraries/math": "^1.1.5",
40
40
  "@datagrok-libraries/ml": "^6.6.5",
41
41
  "@datagrok-libraries/tutorials": "^1.3.12",
42
- "@datagrok-libraries/utils": "^4.2.2",
42
+ "@datagrok-libraries/utils": "^4.2.5",
43
43
  "@webgpu/types": "^0.1.40",
44
44
  "ajv": "^8.12.0",
45
45
  "ajv-errors": "^3.0.0",
@@ -57,7 +57,7 @@
57
57
  "devDependencies": {
58
58
  "@datagrok/chem": "^1.9.2",
59
59
  "@datagrok/dendrogram": "^1.2.29",
60
- "@datagrok/helm": "^2.1.34",
60
+ "@datagrok/helm": "^2.2.1",
61
61
  "@types/node": "^17.0.24",
62
62
  "@types/wu": "latest",
63
63
  "@typescript-eslint/eslint-plugin": "latest",
package/src/package.ts CHANGED
@@ -11,9 +11,7 @@ import {DimReductionBaseEditor, PreprocessFunctionReturnType}
11
11
  import {getActivityCliffs} from '@datagrok-libraries/ml/src/viewers/activity-cliffs';
12
12
  import {MmDistanceFunctionsNames} from '@datagrok-libraries/ml/src/macromolecule-distance-functions';
13
13
  import {BitArrayMetrics, KnownMetrics} from '@datagrok-libraries/ml/src/typed-metrics';
14
- import {
15
- TAGS as bioTAGS,
16
- } from '@datagrok-libraries/bio/src/utils/macromolecule';
14
+ import {NOTATION, TAGS as bioTAGS} from '@datagrok-libraries/bio/src/utils/macromolecule';
17
15
  import {SeqHandler, SeqTemps} from '@datagrok-libraries/bio/src/utils/seq-handler';
18
16
  import {IMonomerLib} from '@datagrok-libraries/bio/src/types';
19
17
  import {SeqPalette} from '@datagrok-libraries/bio/src/seq-palettes';
@@ -51,7 +49,7 @@ import {demoBio01bUI} from './demo/bio01b-hierarchical-clustering-and-activity-c
51
49
  import {demoBio03UI} from './demo/bio03-atomic-level';
52
50
  import {demoBio05UI} from './demo/bio05-helm-msa-sequence-space';
53
51
  import {checkInputColumnUI} from './utils/check-input-column';
54
- import {multipleSequenceAlignmentUI} from './utils/multiple-sequence-alignment-ui';
52
+ import {MsaWarning, multipleSequenceAlignmentUI} from './utils/multiple-sequence-alignment-ui';
55
53
  import {WebLogoApp} from './apps/web-logo-app';
56
54
  import {SplitToMonomersFunctionEditor} from './function-edtiors/split-to-monomers-editor';
57
55
  import {splitToMonomersUI} from './utils/split-to-monomers';
@@ -75,6 +73,8 @@ import {DimReductionMethods} from '@datagrok-libraries/ml/src/multi-column-dimen
75
73
  import {
76
74
  ITSNEOptions, IUMAPOptions
77
75
  } from '@datagrok-libraries/ml/src/multi-column-dimensionality-reduction/multi-column-dim-reducer';
76
+ import {generateLongSequence, generateLongSequence2} from '@datagrok-libraries/bio/src/utils/generator';
77
+
78
78
  import {CyclizedNotationProvider} from './utils/cyclized';
79
79
  import {getMolColumnFromHelm} from './utils/helm-to-molfile/utils';
80
80
 
@@ -86,8 +86,8 @@ export const _package = new BioPackage();
86
86
  //name: getMonomerLibHelper
87
87
  //description:
88
88
  //output: object result
89
- export function getMonomerLibHelper(): IMonomerLibHelper {
90
- return MonomerLibManager.instance;
89
+ export async function getMonomerLibHelper(): Promise<IMonomerLibHelper> {
90
+ return await MonomerLibManager.getInstance();
91
91
  }
92
92
 
93
93
  export let hydrophobPalette: SeqPaletteCustom | null = null;
@@ -104,12 +104,20 @@ export class SeqPaletteCustom implements SeqPalette {
104
104
  }
105
105
  }
106
106
 
107
+ let monomerLib: IMonomerLib | null = null;
108
+
107
109
  //tags: init
108
110
  export async function initBio() {
109
- _package.logger.debug('Bio: initBio(), started');
111
+ const logPrefix = 'Bio: initBio()';
112
+ _package.logger.debug(`${logPrefix}, start`);
110
113
  const module = await grok.functions.call('Chem:getRdKitModule');
114
+ const t1: number = window.performance.now();
111
115
  await Promise.all([
112
- (async () => { await MonomerLibManager.instance.loadLibraries(); })(),
116
+ (async () => {
117
+ const monomerLibManager = await MonomerLibManager.getInstance();
118
+ await monomerLibManager.loadLibraries();
119
+ monomerLib = monomerLibManager.getBioLib();
120
+ })(),
113
121
  (async () => {
114
122
  const pkgProps = await _package.getProperties();
115
123
  const bioPkgProps = new BioPackageProperties(pkgProps);
@@ -117,9 +125,10 @@ export async function initBio() {
117
125
  })(),
118
126
  ]).finally(() => {
119
127
  _package.completeInit();
128
+ const t2: number = window.performance.now();
129
+ _package.logger.debug(`${logPrefix}, loading ET: ${t2 - t1} ms`);
120
130
  });
121
131
 
122
- const monomerLib = MonomerLibManager.instance.getBioLib();
123
132
  const monomers: string[] = [];
124
133
  const logPs: number[] = [];
125
134
 
@@ -159,10 +168,16 @@ export function sequenceTooltip(col: DG.Column): DG.Widget<any> {
159
168
  return resWidget;
160
169
  }
161
170
 
171
+ // Keep for backward compatibility
162
172
  //name: getBioLib
163
173
  //output: object monomerLib
164
174
  export function getBioLib(): IMonomerLib {
165
- return MonomerLibManager.instance.getBioLib();
175
+ return monomerLib!;
176
+ }
177
+
178
+ // For sync internal use, on initialized package
179
+ export function getMonomerLib(): IMonomerLib | null {
180
+ return monomerLib!;
166
181
  }
167
182
 
168
183
  //name: getSeqHandler
@@ -571,7 +586,8 @@ export async function sequenceSpaceTopMenu(table: DG.DataFrame, molecules: DG.Co
571
586
  export async function toAtomicLevel(table: DG.DataFrame, seqCol: DG.Column, nonlinear: boolean): Promise<void> {
572
587
  const pi = DG.TaskBarProgressIndicator.create('Converting to atomic level ...');
573
588
  try {
574
- await sequenceToMolfile(table, seqCol, nonlinear);
589
+ const monomerLib = (await getMonomerLibHelper()).getBioLib();
590
+ await sequenceToMolfile(table, seqCol, nonlinear, monomerLib);
575
591
  } finally {
576
592
  pi.close();
577
593
  }
@@ -582,7 +598,15 @@ export async function toAtomicLevel(table: DG.DataFrame, seqCol: DG.Column, nonl
582
598
  //description: Performs multiple sequence alignment
583
599
  //tags: bio, panel
584
600
  export function multipleSequenceAlignmentDialog(): void {
585
- multipleSequenceAlignmentUI();
601
+ multipleSequenceAlignmentUI()
602
+ .catch((err: any) => {
603
+ const [errMsg, _errStack] = errInfo(err);
604
+ if (err instanceof MsaWarning) {
605
+ _package.logger.warning(errMsg);
606
+ return;
607
+ }
608
+ throw err;
609
+ });
586
610
  }
587
611
 
588
612
  //name: Multiple Sequence Alignment
@@ -930,6 +954,26 @@ export async function getRegionHelmApp(): Promise<void> {
930
954
  }
931
955
  }
932
956
 
957
+ // -- Tests long seq --
958
+
959
+ //name: longSeqTableSeparator
960
+ export function longSeqTableSeparator(): void {
961
+ const df = DG.DataFrame.fromColumns(generateLongSequence());
962
+ grok.shell.addTableView(df);
963
+ }
964
+
965
+ //name: longSeqTableFasta
966
+ export function longSeqTableFasta(): void {
967
+ const df = DG.DataFrame.fromColumns([generateLongSequence2(NOTATION.FASTA)]);
968
+ grok.shell.addTableView(df);
969
+ }
970
+
971
+ //name: longSeqTableHelm
972
+ export function longSeqTableHelm(): void {
973
+ const df = DG.DataFrame.fromColumns([generateLongSequence2(NOTATION.HELM)]);
974
+ grok.shell.addTableView(df);
975
+ }
976
+
933
977
  // -- Handle context menu --
934
978
 
935
979
  ///name: addCopyMenu
@@ -8,8 +8,6 @@ import {getMonomerLibHelper, IMonomerLibHelper} from '@datagrok-libraries/bio/sr
8
8
  import {
9
9
  getUserLibSettings, setUserLibSettings, setUserLibSettingsForTests
10
10
  } from '@datagrok-libraries/bio/src/monomer-works/lib-settings';
11
- import {MonomerLibFileManager} from '../utils/monomer-lib/library-file-manager/file-manager';
12
- import {MonomerLibFileEventManager} from '../utils/monomer-lib/library-file-manager/event-manager';
13
11
 
14
12
 
15
13
  category('monomerLibraries', () => {
@@ -51,8 +49,7 @@ category('monomerLibraries', () => {
51
49
  test('empty', async () => {
52
50
  // exclude all monomer libraries for empty set
53
51
  const libSettings = await getUserLibSettings();
54
- const libFileEventManager = MonomerLibFileEventManager.getInstance();
55
- const libFileManager = await MonomerLibFileManager.getInstance(libFileEventManager);
52
+ const libFileManager = await monomerLibHelper.getFileManager();
56
53
 
57
54
  let libFnList = libFileManager.getValidLibraryPaths();
58
55
  if (libFnList.length === 0)
@@ -4,16 +4,36 @@ import * as ui from 'datagrok-api/ui';
4
4
 
5
5
  import wu from 'wu';
6
6
 
7
- import {category, test} from '@datagrok-libraries/utils/src/test';
8
- import {MonomerPlacer} from '@datagrok-libraries/bio/src/utils/cell-renderer-monomer-placer';
7
+ import {after, before, category, expect, test} from '@datagrok-libraries/utils/src/test';
8
+ import {MonomerPlacer, hitBounds} from '@datagrok-libraries/bio/src/utils/cell-renderer-monomer-placer';
9
9
  import {monomerToShort} from '@datagrok-libraries/bio/src/utils/macromolecule';
10
10
  import {SeqHandler} from '@datagrok-libraries/bio/src/utils/seq-handler';
11
-
12
- import {MonomerLibManager} from '../utils/monomer-lib/lib-manager';
11
+ import {getMonomerLibHelper, IMonomerLibHelper} from '@datagrok-libraries/bio/src/monomer-works/monomer-utils';
12
+ import {
13
+ getUserLibSettings, setUserLibSettings, setUserLibSettingsForTests
14
+ } from '@datagrok-libraries/bio/src/monomer-works/lib-settings';
15
+ import {UserLibSettings} from '@datagrok-libraries/bio/src/monomer-works/types';
13
16
 
14
17
  import {_package} from '../package-test';
15
18
 
16
19
  category('renderers: monomerPlacer', () => {
20
+ let libHelper: IMonomerLibHelper;
21
+ let libSettings: UserLibSettings;
22
+
23
+ before(async () => {
24
+ libHelper = await getMonomerLibHelper();
25
+ libSettings = await getUserLibSettings();
26
+
27
+ await setUserLibSettingsForTests();
28
+ await libHelper.awaitLoaded();
29
+ await libHelper.loadLibraries(true);
30
+ });
31
+
32
+ after(async () => {
33
+ await setUserLibSettings(libSettings);
34
+ await libHelper.loadLibraries(true);
35
+ });
36
+
17
37
  const tests = {
18
38
  splitter: {
19
39
  /**
@@ -45,7 +65,7 @@ category('renderers: monomerPlacer', () => {
45
65
  'id1,m1-M-m3-mon4-mon5-N-T-MON8-N9\n' +
46
66
  'id2,m1-mon2-m3-mon4-mon5-Num--MON8-N9\n' +
47
67
  'id3,\n' + // empty
48
- 'id4,mon1-M-mon3-mon4-mon5---MON8-N9\n',
68
+ 'id4,mon1-M-mon3-mon4-mon5---MON8-N9\n', // [ 5, 38, 71, 104, 137, 170, 203, 236, 269, 295 ]
49
69
  testList: [
50
70
  {src: {row: 0, x: -1}, tgt: {pos: null}},
51
71
  {src: {row: 1, x: 0}, tgt: {pos: null}},
@@ -54,12 +74,12 @@ category('renderers: monomerPlacer', () => {
54
74
  {src: {row: 1, x: 5}, tgt: {pos: 0}},
55
75
  {src: {row: 1, x: 37}, tgt: {pos: 0}},
56
76
  {src: {row: 1, x: 38}, tgt: {pos: 1}},
57
- {src: {row: 1, x: 170}, tgt: {pos: 4}},
77
+ {src: {row: 1, x: 170}, tgt: {pos: 5}},
58
78
  {src: {row: 1, x: 200}, tgt: {pos: 5}},
59
79
  {src: {row: 2, x: 20}, tgt: {pos: null}}, // empty value
60
- {src: {row: 3, x: 170}, tgt: {pos: 4}},
80
+ {src: {row: 3, x: 170}, tgt: {pos: 5}},
61
81
  {src: {row: 3, x: 200}, tgt: {pos: 5}},
62
- {src: {row: 3, x: 282}, tgt: {pos: null}},
82
+ {src: {row: 3, x: 297}, tgt: {pos: null}},
63
83
  ]
64
84
  },
65
85
  fastaMsa: {
@@ -88,6 +108,8 @@ id3,QHIRE--LT
88
108
 
89
109
  for (const [testName, testData] of Object.entries(tests)) {
90
110
  test(`getPosition-${testName}`, async () => {
111
+ const libHelper = await getMonomerLibHelper();
112
+ const monomerLib = libHelper.getBioLib();
91
113
  const df: DG.DataFrame = DG.DataFrame.fromCsv(testData.csv);
92
114
  await grok.data.detectSemanticTypes(df);
93
115
  const seqCol: DG.Column = df.getCol('seq');
@@ -103,25 +125,50 @@ id3,QHIRE--LT
103
125
  separatorWidth: sepWidth,
104
126
  monomerToShort: monomerToShort,
105
127
  monomerLengthLimit: monLength,
106
- monomerLib: MonomerLibManager.instance.getBioLib(),
107
128
  };
108
129
  });
109
130
 
131
+ const width: number = 10000;
110
132
  const testList = testData.testList;
111
133
  // simulate rendering
112
134
  for (let rowI: number = 0; rowI < seqCol.length; ++rowI)
113
- colTemp.getCellMonomerLengths(rowI);
135
+ colTemp.getCellMonomerLengths(rowI, 10000);
114
136
 
115
137
  const errorList: string[] = [];
116
138
  for (const [test, _testI] of wu.enumerate(testList)) {
117
- const res = {pos: colTemp.getPosition(test.src.row, test.src.x)};
139
+ const res = {pos: colTemp.getPosition(test.src.row, test.src.x, width)};
118
140
  if (test.tgt.pos != res.pos) {
119
141
  errorList.push(`Test src ${JSON.stringify(test.src)} expected tgt ${JSON.stringify(test.tgt)},` +
120
142
  ` but get ${JSON.stringify({res})}`);
121
143
  }
122
144
  }
123
145
  if (errorList.length > 0)
124
- throw new Error('Test failed error(s):\n' + errorList.join(', n'));
146
+ throw new Error('Test failed error(s):\n' + errorList.join(', \n'));
147
+ });
148
+ }
149
+ });
150
+
151
+ category('renderers: monomerPlacer', () => {
152
+ const boundsTestData = {
153
+ bounds: [10, 20, 30, 40, 50, 60],
154
+ tests: {
155
+ left: {x: 3, tgt: null},
156
+ c0left: {x: 10, tgt: 0},
157
+ c0mid: {x: 12, tgt: 0},
158
+ c0right: {x: 19, tgt: 0},
159
+ c1left: {x: 20, tgt: 1},
160
+ c2right: {x: 39, tgt: 2},
161
+ c4left: {x: 50, tgt: 4},
162
+ c4right: {x: 59, tgt: 4},
163
+ max: {x: 60, tgt: null},
164
+ right: {x: 65, tgt: null},
165
+ }
166
+ };
167
+
168
+ for (const [testName, testData] of Object.entries(boundsTestData.tests)) {
169
+ test('hitBounds-' + testName, async () => {
170
+ const res = hitBounds(boundsTestData.bounds, testData.x);
171
+ expect(res, testData.tgt);
125
172
  });
126
173
  }
127
174
  });
@@ -8,10 +8,11 @@ import {fromEvent} from 'rxjs';
8
8
  import {category, expect, test, delay, testEvent} from '@datagrok-libraries/utils/src/test';
9
9
  import {ALIGNMENT, ALPHABET, NOTATION, TAGS as bioTAGS} from '@datagrok-libraries/bio/src/utils/macromolecule';
10
10
  import {SeqHandler} from '@datagrok-libraries/bio/src/utils/seq-handler';
11
+ import {generateLongSequence, generateManySequences} from '@datagrok-libraries/bio/src/utils/generator';
11
12
 
12
13
  import {importFasta} from '../package';
13
14
  import {convertDo} from '../utils/convert';
14
- import {generateLongSequence, generateManySequences, performanceTest} from './utils/sequences-generators';
15
+ import {performanceTest} from './utils/sequences-generators';
15
16
  import {multipleSequenceAlignmentUI} from '../utils/multiple-sequence-alignment-ui';
16
17
  import {awaitGrid} from './utils';
17
18
  import * as C from '../utils/constants';
@@ -23,9 +24,6 @@ category('renderers', () => {
23
24
  await performanceTest(generateLongSequence, 'Long sequences');
24
25
  });
25
26
 
26
- test('many sequence performance', async () => {
27
- await performanceTest(generateManySequences, 'Many sequences');
28
- });
29
27
  test('many sequence performance', async () => {
30
28
  await performanceTest(generateManySequences, 'Many sequences');
31
29
  });
@@ -56,7 +54,7 @@ category('renderers', () => {
56
54
 
57
55
  test('scatterPlotTooltip', async () => {
58
56
  await _testScatterPlotTooltip();
59
- });
57
+ }, {skipReason: 'GROK-15679'});
60
58
 
61
59
  async function _rendererMacromoleculeFasta() {
62
60
  const csv: string = await grok.dapi.files.readAsText('System:AppData/Bio/samples/FASTA.csv');
@@ -4,9 +4,9 @@ import * as DG from 'datagrok-api/dg';
4
4
 
5
5
  import {category, test, expectFloat, before, after} from '@datagrok-libraries/utils/src/test';
6
6
  import {NOTATION} from '@datagrok-libraries/bio/src/utils/macromolecule';
7
- import {getMonomerLibHelper, IMonomerLibHelper} from '@datagrok-libraries/bio/src/monomer-works/monomer-utils';
7
+ import {IMonomerLibHelper} from '@datagrok-libraries/bio/src/monomer-works/monomer-utils';
8
8
 
9
- import {sequenceIdentityScoring, sequenceSimilarityScoring} from '../package';
9
+ import {getMonomerLibHelper, sequenceIdentityScoring, sequenceSimilarityScoring} from '../package';
10
10
  import {
11
11
  getUserLibSettings, setUserLibSettings, setUserLibSettingsForTests
12
12
  } from '@datagrok-libraries/bio/src/monomer-works/lib-settings';
@@ -185,6 +185,7 @@ category('bio-substructure-filters', async () => {
185
185
  dlg.close();
186
186
  }
187
187
  await filter.awaitRendered();
188
+ await delay(3000); //TODO: await for grid.onLookChanged
188
189
  });
189
190
 
190
191
  // Generates unhandled exception accessing isFiltering before bioFilter created
@@ -308,6 +309,7 @@ category('bio-substructure-filters', async () => {
308
309
  }
309
310
  await Promise.all([f1.awaitRendered(), f2.awaitRendered()]);
310
311
  await awaitGrid(view.grid);
312
+ await delay(3000); //TODO: await for grid.onLookChanged
311
313
  });
312
314
 
313
315
  // two seq columns
@@ -7,7 +7,7 @@ import wu from 'wu';
7
7
 
8
8
  import {before, after, category, test, expectArray, expect} from '@datagrok-libraries/utils/src/test';
9
9
  import {_toAtomicLevel} from '@datagrok-libraries/bio/src/monomer-works/to-atomic-level';
10
- import {IMonomerLib} from '@datagrok-libraries/bio/src/types/index';
10
+ import {IMonomerLib} from '@datagrok-libraries/bio/src/types';
11
11
  import {ALPHABET, NOTATION, TAGS as bioTAGS} from '@datagrok-libraries/bio/src/utils/macromolecule';
12
12
  import {getMonomerLibHelper, IMonomerLibHelper} from '@datagrok-libraries/bio/src/monomer-works/monomer-utils';
13
13
  import {
@@ -7,26 +7,6 @@ import {expect} from '@datagrok-libraries/utils/src/test';
7
7
 
8
8
  import {awaitGrid} from '../utils';
9
9
 
10
-
11
- export function generateManySequences(): DG.Column[] {
12
- const columns: DG.Column[] = [];
13
- columns.push(DG.Column.fromList('string', 'MSA',
14
- new Array(10 ** 6).fill(
15
- 'meI/hHis/Aca/N/T/dE/Thr_PO3H2/Aca/D-Tyr_Et/Tyr_ab-dehydroMe/dV/E/N/D-Orn/D-aThr//Phe_4Me')),
16
- );
17
- columns.push(DG.Column.fromList('string', 'Activity', new Array(10 ** 6).fill('5.30751')));
18
- return columns;
19
- }
20
-
21
- export function generateLongSequence(): DG.Column[] {
22
- const columns: DG.Column[] = [];
23
- const longSequence =
24
- `meI/hHis/Aca/N/T/dE/Thr_PO3H2/Aca/D-Tyr_Et/Tyr_ab-dehydroMe/dV/E/N/D-Orn/D-aThr`.repeat(10 ** 5);
25
- columns.push(DG.Column.fromList('string', 'MSA', new Array(10 ** 2).fill(longSequence)));
26
- columns.push(DG.Column.fromList('string', 'Activity', new Array(10 ** 2).fill('7.30751')));
27
- return columns;
28
- }
29
-
30
10
  export async function performanceTest(generateFunc: () => DG.Column[], testName: string) {
31
11
  const columns = generateFunc();
32
12
  const df: DG.DataFrame = DG.DataFrame.fromColumns(columns);
@@ -2,10 +2,12 @@ import * as DG from 'datagrok-api/dg';
2
2
  import * as grok from 'datagrok-api/grok';
3
3
 
4
4
  import {delay, expect, testEvent} from '@datagrok-libraries/utils/src/test';
5
+ import {asRenderer, IRenderer, isRenderer} from '@datagrok-libraries/bio/src/types/renderer';
5
6
 
6
7
  import {startDockerContainer} from '../utils/docker';
7
8
 
8
9
  import {_package} from '../package-test';
10
+ import {CellRendererBackBase, getGridCellRendererBack} from '@datagrok-libraries/bio/src/utils/cell-renderer-back-base';
9
11
 
10
12
  export async function loadFileAsText(name: string): Promise<string> {
11
13
  return await _package.files.readAsText(name);
@@ -38,4 +40,17 @@ export async function awaitGrid(grid: DG.Grid, timeout: number = 5000): Promise<
38
40
  await delay(0);
39
41
  await testEvent(grid.onAfterDrawContent, () => {},
40
42
  () => { grid.invalidate(); }, timeout);
43
+
44
+ const colCount = grid.columns.length;
45
+ for (let colI = 0; colI < colCount; ++colI) {
46
+ const gridCol = grid.columns.byIndex(colI);
47
+ if (gridCol) {
48
+ const gridCell = grid.cell(gridCol.name, 0);
49
+ const [_gridCol, _tableCol, temp] =
50
+ getGridCellRendererBack<void, CellRendererBackBase<void>>(gridCell);
51
+
52
+ const renderer = asRenderer(temp.rendererBack);
53
+ if (renderer) await renderer.awaitRendered();
54
+ }
55
+ }
41
56
  }
@@ -3,7 +3,6 @@ import * as DG from 'datagrok-api/dg';
3
3
  import * as ui from 'datagrok-api/ui';
4
4
 
5
5
  import wu from 'wu';
6
- import {Unsubscribable} from 'rxjs';
7
6
 
8
7
  import {printLeftOrCentered, DrawStyle, TAGS as mmcrTAGS} from '@datagrok-libraries/bio/src/utils/cell-renderer';
9
8
  import {MonomerPlacer} from '@datagrok-libraries/bio/src/utils/cell-renderer-monomer-placer';
@@ -22,7 +21,7 @@ import {GapOriginals, SeqHandler} from '@datagrok-libraries/bio/src/utils/seq-ha
22
21
  import {ISeqSplitted, SeqSplittedBase} from '@datagrok-libraries/bio/src/utils/macromolecule/types';
23
22
  import {getSplitter} from '@datagrok-libraries/bio/src/utils/macromolecule/utils';
24
23
  import {errInfo} from '@datagrok-libraries/bio/src/utils/err-info';
25
- import {alphabetPolymerTypes, IMonomerLib} from '@datagrok-libraries/bio/src/types/index';
24
+ import {alphabetPolymerTypes, IMonomerLib} from '@datagrok-libraries/bio/src/types';
26
25
  import {getGridCellRendererBack} from '@datagrok-libraries/bio/src/utils/cell-renderer-back-base';
27
26
 
28
27
  import {
@@ -31,14 +30,16 @@ import {
31
30
  } from '../utils/cell-renderer-consts';
32
31
  import * as C from './constants';
33
32
 
34
- import {_package, getBioLib} from '../package';
33
+ import {_package, getMonomerLib} from '../package';
35
34
 
36
35
  type TempType = { [tagName: string]: any };
37
36
 
38
37
  const undefinedColor = 'rgb(100,100,100)';
39
38
  const monomerToShortFunction: MonomerToShortFunc = monomerToShort;
40
39
 
41
- function getUpdatedWidth(grid: DG.Grid | null, g: CanvasRenderingContext2D, x: number, w: number, dpr: number): number {
40
+ function getUpdatedWidth(
41
+ grid: DG.Grid | null | undefined, g: CanvasRenderingContext2D, x: number, w: number, dpr: number
42
+ ): number {
42
43
  return !!grid ? Math.max(Math.min(grid.canvas.width / dpr - x, w)) : Math.max(g.canvas.width / dpr - x, 0);
43
44
  }
44
45
 
@@ -99,7 +100,7 @@ export class MacromoleculeSequenceCellRenderer extends DG.GridCellRenderer {
99
100
  // maxLengthWordsSum[posI] = maxLengthWordsSum[posI - 1] + maxLengthWords[posI];
100
101
  // const maxIndex = maxLengthWords.length;
101
102
  const argsX = e.offsetX - gridCell.gridColumn.left + (gridCell.gridColumn.left - gridCellBounds.x);
102
- const left: number | null = seqColTemp.getPosition(gridCell.tableRowIndex!, argsX);
103
+ const left: number | null = seqColTemp.getPosition(gridCell.tableRowIndex!, argsX, gridCellBounds.width);
103
104
 
104
105
  const seqCList: SeqSplittedBase = SeqHandler.forColumn(tableCol)
105
106
  .getSplitted(gridCell.tableRowIndex!).canonicals;
@@ -113,8 +114,8 @@ export class MacromoleculeSequenceCellRenderer extends DG.GridCellRenderer {
113
114
  const alphabet = sh.alphabet ?? ALPHABET.UN;
114
115
  const polymerType = alphabetPolymerTypes[alphabet as ALPHABET];
115
116
 
116
- const lib: IMonomerLib = seqColTemp.props.monomerLib!;
117
- return lib.getTooltip(polymerType, monomerSymbol);
117
+ const lib: IMonomerLib | null = getMonomerLib();
118
+ return lib ? lib.getTooltip(polymerType, monomerSymbol) : ui.divText('Monomer library is not available');
118
119
  })();
119
120
  }
120
121
  tooltipElements.push(monomerDiv);
@@ -142,23 +143,17 @@ export class MacromoleculeSequenceCellRenderer extends DG.GridCellRenderer {
142
143
  _cellStyle: DG.GridCellStyle
143
144
  ): void {
144
145
  const logPrefix: string = 'MacromoleculeSequenceCellRenderer.render()';
146
+
147
+ const dpr = window.devicePixelRatio;
148
+ const [gridCol, tableCol, _temp] =
149
+ getGridCellRendererBack<string, MonomerPlacer>(gridCell);
150
+ if (!tableCol) return;
151
+ const tableColTemp: TempType = tableCol.temp;
152
+
145
153
  let gapLength = 0;
146
154
  const msaGapLength = 8;
147
155
  let maxLengthOfMonomer = 50; // in case of long monomer representation, do not limit max length
148
156
 
149
- // TODO: Store temp data to GridColumn
150
- // Now the renderer requires data frame table Column underlying GridColumn
151
- let grid: DG.Grid | undefined = undefined;
152
- try { grid = gridCell.grid; } catch (err: any) {
153
- grid = undefined;
154
- const [errMsg, errStack] = errInfo(err);
155
- _package.logger.error(errMsg, undefined, errStack);
156
- }
157
- const tableCol: DG.Column = gridCell.cell.column;
158
- if (!grid || !tableCol) return;
159
-
160
- const tableColTemp: TempType = tableCol.temp;
161
-
162
157
  // Cell renderer settings
163
158
  const tempMonomerWidth: string | null = tableColTemp[tempTAGS.monomerWidth];
164
159
  const monomerWidth: string = (tempMonomerWidth != null) ? tempMonomerWidth : 'short';
@@ -170,46 +165,41 @@ export class MacromoleculeSequenceCellRenderer extends DG.GridCellRenderer {
170
165
  (!isNaN(tagMaxMonomerLength) ? tagMaxMonomerLength : _package.properties?.MaxMonomerLength) ?? 4;
171
166
  }
172
167
 
173
- const [gridCol, _tc, temp] =
168
+ const [_gc, _tc, temp] =
174
169
  getGridCellRendererBack<string, MonomerPlacer>(gridCell);
175
- let seqColTemp: MonomerPlacer = temp['rendererBack'];
170
+ let seqColTemp: MonomerPlacer = temp.rendererBack;
176
171
  if (!seqColTemp) {
177
- seqColTemp = new MonomerPlacer(gridCol, tableCol, _package.logger,
172
+ seqColTemp = temp.rendererBack = new MonomerPlacer(gridCol, tableCol, _package.logger,
178
173
  () => {
179
174
  const sh = SeqHandler.forColumn(tableCol);
180
175
  return {
181
176
  seqHandler: sh,
182
177
  monomerCharWidth: 7, separatorWidth: !sh.isMsa() ? gapLength : msaGapLength,
183
178
  monomerToShort: monomerToShortFunction, monomerLengthLimit: maxLengthOfMonomer,
184
- monomerLib: getBioLib()
185
179
  };
186
180
  });
187
181
  }
188
182
 
189
- if (tableCol.tags[mmcrTags.RendererSettingsChanged] === rendererSettingsChangedState.true) {
190
- gapLength = tableColTemp[mmcrTemps.gapLength] as number ?? gapLength;
191
- // this event means that the mm renderer settings have changed, particularly monomer representation and max width.
192
- seqColTemp.setMonomerLengthLimit(maxLengthOfMonomer);
193
- seqColTemp.setSeparatorWidth(seqColTemp.isMsa() ? msaGapLength : gapLength);
194
- tableCol.setTag(mmcrTags.RendererSettingsChanged, rendererSettingsChangedState.false);
195
- }
196
-
197
- const [maxLengthWords, maxLengthWordsSum]: [number[], number[]] =
198
- seqColTemp.getCellMonomerLengths(gridCell.tableRowIndex!);
199
- const _maxIndex = maxLengthWords.length;
200
-
201
- // Store updated seqColTemp to the col temp
202
- if (seqColTemp.updated) temp['rendererBack'] = seqColTemp;
203
-
204
183
  g.save();
205
184
  try {
206
- const dpr = window.devicePixelRatio;
207
- const grid = gridCell.gridRow !== -1 ? gridCell.grid : null;
185
+ if (tableCol.tags[mmcrTags.RendererSettingsChanged] === rendererSettingsChangedState.true) {
186
+ gapLength = tableColTemp[mmcrTemps.gapLength] as number ?? gapLength;
187
+ // this event means that the mm renderer settings have changed,
188
+ // particularly monomer representation and max width.
189
+ seqColTemp.setMonomerLengthLimit(maxLengthOfMonomer);
190
+ seqColTemp.setSeparatorWidth(seqColTemp.isMsa() ? msaGapLength : gapLength);
191
+ tableCol.setTag(mmcrTags.RendererSettingsChanged, rendererSettingsChangedState.false);
192
+ }
193
+
194
+ const [maxLengthWords, maxLengthWordsSum]: [number[], number[]] =
195
+ seqColTemp.getCellMonomerLengths(gridCell.tableRowIndex!, w);
196
+ const _maxIndex = maxLengthWords.length;
197
+
208
198
  const value: any = gridCell.cell.value;
209
199
  const rowIdx = gridCell.cell.rowIndex;
210
200
  const paletteType = tableCol.getTag(bioTAGS.alphabet);
211
201
  const minDistanceRenderer = 50;
212
- w = getUpdatedWidth(grid, g, x, w, dpr);
202
+ w = getUpdatedWidth(gridCol?.grid, g, x, w, dpr);
213
203
  g.beginPath();
214
204
  g.rect(x + this.padding, y + this.padding, w - this.padding - 1, h - this.padding * 2);
215
205
  g.clip();
@@ -223,7 +213,8 @@ export class MacromoleculeSequenceCellRenderer extends DG.GridCellRenderer {
223
213
  const palette = getPaletteByType(paletteType);
224
214
 
225
215
  const separator = tableCol.getTag(bioTAGS.separator) ?? '';
226
- const splitLimit = w / 5;
216
+ const minMonWidth = seqColTemp.props.separatorWidth + 1 * seqColTemp.props.monomerCharWidth;
217
+ const splitLimit = Math.ceil(w / minMonWidth);
227
218
  const sh = SeqHandler.forColumn(tableCol);
228
219
 
229
220
  const tempReferenceSequence: string | null = tableColTemp[tempTAGS.referenceSequence];
@@ -247,7 +238,8 @@ export class MacromoleculeSequenceCellRenderer extends DG.GridCellRenderer {
247
238
  if (aligned && aligned.includes('MSA') && units == NOTATION.SEPARATOR)
248
239
  drawStyle = DrawStyle.MSA;
249
240
 
250
- for (let posIdx: number = 0; posIdx < subParts.length; ++posIdx) {
241
+ const visibleSeqLength = Math.min(subParts.length, splitLimit);
242
+ for (let posIdx: number = 0; posIdx < visibleSeqLength; ++posIdx) {
251
243
  const amino: string = subParts.getOriginal(posIdx);
252
244
  color = palette.get(amino);
253
245
  g.fillStyle = undefinedColor;
@@ -259,8 +251,9 @@ export class MacromoleculeSequenceCellRenderer extends DG.GridCellRenderer {
259
251
  if (minDistanceRenderer > w) break;
260
252
  }
261
253
  } catch (err: any) {
262
- const errMsg: string = err instanceof Error ? err.message : !!err ? err.toString() : 'Error \'undefined\'';
263
- _package.logger.error(`Bio: MacromoleculeSequenceCellRenderer.render() error: ${errMsg}`);
254
+ const [errMsg, errStack] = errInfo(err);
255
+ seqColTemp.logger.error(errMsg, undefined, errStack);
256
+ seqColTemp.errors.push(err);
264
257
  //throw err; // Do not throw to prevent disabling renderer
265
258
  } finally {
266
259
  g.restore();