@datagrok/bio 2.15.2 → 2.15.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.
Files changed (54) hide show
  1. package/CHANGELOG.md +32 -0
  2. package/detectors.js +3 -1
  3. package/dist/284.js +1 -1
  4. package/dist/284.js.map +1 -1
  5. package/dist/455.js +2 -0
  6. package/dist/455.js.map +1 -0
  7. package/dist/980.js +1 -1
  8. package/dist/980.js.map +1 -1
  9. package/dist/package-test.js +3 -3
  10. package/dist/package-test.js.map +1 -1
  11. package/dist/package.js +2 -2
  12. package/dist/package.js.map +1 -1
  13. package/files/tests/to-atomic-level-msa-fasta-output.csv +683 -683
  14. package/files/tests/to-atomic-level-msa-separator-output.csv +104 -104
  15. package/package.json +9 -9
  16. package/src/analysis/sequence-activity-cliffs.ts +3 -1
  17. package/src/calculations/monomerLevelMols.ts +2 -1
  18. package/src/demo/bio03-atomic-level.ts +1 -1
  19. package/src/package-test.ts +6 -1
  20. package/src/package.ts +151 -38
  21. package/src/tests/WebLogo-positions-test.ts +1 -1
  22. package/src/tests/bio-tests.ts +1 -1
  23. package/src/tests/detectors-tests.ts +10 -10
  24. package/src/tests/monomer-libraries-tests.ts +1 -1
  25. package/src/tests/seq-handler-splitted-tests.ts +6 -2
  26. package/src/tests/splitters-test.ts +6 -6
  27. package/src/tests/to-atomic-level-tests.ts +21 -14
  28. package/src/tests/to-atomic-level-ui-tests.ts +75 -35
  29. package/src/tests/utils.ts +2 -2
  30. package/src/utils/cell-renderer-custom.ts +62 -0
  31. package/src/utils/cell-renderer.ts +58 -126
  32. package/src/utils/cyclized.ts +28 -14
  33. package/src/utils/dimerized.ts +0 -2
  34. package/src/utils/helm-to-molfile/converter/converter.ts +75 -54
  35. package/src/utils/helm-to-molfile/converter/monomer-wrapper.ts +2 -2
  36. package/src/utils/helm-to-molfile/converter/polymer.ts +23 -16
  37. package/src/utils/helm-to-molfile/converter/types.ts +0 -10
  38. package/src/utils/helm-to-molfile/utils.ts +10 -7
  39. package/src/utils/monomer-cell-renderer.ts +8 -4
  40. package/src/utils/monomer-lib/lib-manager.ts +2 -2
  41. package/src/utils/monomer-lib/monomer-colors.ts +68 -0
  42. package/src/utils/monomer-lib/monomer-lib-base.ts +165 -0
  43. package/src/utils/monomer-lib/monomer-lib.ts +19 -68
  44. package/src/utils/monomer-lib/web-editor-monomer-dummy.ts +121 -0
  45. package/src/utils/monomer-lib/web-editor-monomer-of-library.ts +102 -0
  46. package/src/utils/save-as-fasta.ts +1 -1
  47. package/src/utils/seq-helper/seq-helper.ts +20 -49
  48. package/src/utils/sequence-to-mol.ts +24 -28
  49. package/src/viewers/web-logo-viewer.ts +2 -1
  50. package/src/widgets/composition-analysis-widget.ts +4 -3
  51. package/src/widgets/representations.ts +8 -10
  52. package/dist/248.js +0 -2
  53. package/dist/248.js.map +0 -1
  54. package/src/utils/cell-renderer-consts.ts +0 -31
@@ -7,7 +7,7 @@ import {asRenderer, IRenderer, isRenderer} from '@datagrok-libraries/bio/src/typ
7
7
  import {startDockerContainer} from '../utils/docker';
8
8
 
9
9
  import {_package} from '../package-test';
10
- import {CellRendererBackBase, getGridCellRendererBack} from '@datagrok-libraries/bio/src/utils/cell-renderer-back-base';
10
+ import {CellRendererBackBase, getGridCellColTemp} from '@datagrok-libraries/bio/src/utils/cell-renderer-back-base';
11
11
 
12
12
  export async function loadFileAsText(name: string): Promise<string> {
13
13
  return await _package.files.readAsText(name);
@@ -47,7 +47,7 @@ export async function awaitGrid(grid: DG.Grid, timeout: number = 5000): Promise<
47
47
  if (gridCol) {
48
48
  const gridCell = grid.cell(gridCol.name, 0);
49
49
  const [_gridCol, _tableCol, temp] =
50
- getGridCellRendererBack<void, CellRendererBackBase<void>>(gridCell);
50
+ getGridCellColTemp<void, CellRendererBackBase<void>>(gridCell);
51
51
 
52
52
  const renderer = asRenderer(temp.rendererBack);
53
53
  if (renderer) await renderer.awaitRendered();
@@ -0,0 +1,62 @@
1
+ import * as grok from 'datagrok-api/grok';
2
+ import * as DG from 'datagrok-api/dg';
3
+ import * as ui from 'datagrok-api/ui';
4
+
5
+ import {getGridCellColTemp, CellRendererBackBase} from '@datagrok-libraries/bio/src/utils/cell-renderer-back-base';
6
+ import {SeqHandler} from '@datagrok-libraries/bio/src/utils/seq-handler';
7
+ import {NOTATION} from '@datagrok-libraries/bio/src/utils/macromolecule';
8
+
9
+ export class MacromoleculeCustomCellRenderer extends DG.GridCellRenderer {
10
+ get name(): string { return 'sequence'; }
11
+
12
+ get cellType(): string { return 'sequence'; }
13
+
14
+ get defaultHeight(): number | null { return 30; }
15
+
16
+ get defaultWidth(): number | null { return 230; }
17
+
18
+ getRendererBack(gridCell: DG.GridCell): CellRendererBackBase<string> {
19
+ const [gridCol, tableCol, temp] = getGridCellColTemp<string, any>(gridCell);
20
+ const sh = SeqHandler.forColumn(tableCol);
21
+ if (sh.notation !== NOTATION.CUSTOM)
22
+ throw new Error(`Unexpected notation: '${sh.notation}'.`);
23
+ const back = sh.getRendererBack(gridCol, tableCol);
24
+ return back;
25
+ }
26
+
27
+ override onMouseLeave(gridCell: DG.GridCell, e: MouseEvent) {
28
+ const back = this.getRendererBack(gridCell);
29
+ back.onMouseLeave(gridCell, e);
30
+ }
31
+
32
+ override onMouseMove(gridCell: DG.GridCell, e: MouseEvent) {
33
+ const back = this.getRendererBack(gridCell);
34
+ back.onMouseMove(gridCell, e);
35
+ }
36
+
37
+ override onClick(gridCell: DG.GridCell, e: MouseEvent) {
38
+ const back = this.getRendererBack(gridCell);
39
+ back.onClick(gridCell, e);
40
+ }
41
+
42
+ override onDoubleClick(gridCell: DG.GridCell, e: MouseEvent) {
43
+ const back = this.getRendererBack(gridCell);
44
+ back.onDoubleClick(gridCell, e);
45
+ }
46
+
47
+ override onKeyDown(gridCell: DG.GridCell, e: KeyboardEvent) {
48
+ const back = this.getRendererBack(gridCell);
49
+ back.onKeyDown(gridCell, e);
50
+ }
51
+
52
+ override onKeyPress(gridCell: DG.GridCell, e: KeyboardEvent) {
53
+ const back = this.getRendererBack(gridCell);
54
+ back.onKeyPress(gridCell, e);
55
+ }
56
+
57
+ override render(g: CanvasRenderingContext2D, x: number, y: number, w: number, h: number, gridCell: DG.GridCell, cellStyle: DG.GridCellStyle) {
58
+ const back = this.getRendererBack(gridCell);
59
+ back.render(g, x, y, w, h, gridCell, cellStyle);
60
+ }
61
+ }
62
+
@@ -8,7 +8,7 @@ import * as ui from 'datagrok-api/ui';
8
8
  import wu from 'wu';
9
9
 
10
10
  import {printLeftOrCentered, DrawStyle, TAGS as mmcrTAGS} from '@datagrok-libraries/bio/src/utils/cell-renderer';
11
- import {MonomerPlacer} from '@datagrok-libraries/bio/src/utils/cell-renderer-monomer-placer';
11
+ import {getUpdatedWidth, MonomerPlacer} from '@datagrok-libraries/bio/src/utils/cell-renderer-monomer-placer';
12
12
  import {
13
13
  getPaletteByType,
14
14
  monomerToShort,
@@ -20,31 +20,25 @@ import {
20
20
  } from '@datagrok-libraries/bio/src/utils/macromolecule';
21
21
  import {SeqPalette} from '@datagrok-libraries/bio/src/seq-palettes';
22
22
  import {UnknownSeqPalettes} from '@datagrok-libraries/bio/src/unknown';
23
- import {GapOriginals, SeqHandler} from '@datagrok-libraries/bio/src/utils/seq-handler';
24
- import {ISeqSplitted, SeqSplittedBase} from '@datagrok-libraries/bio/src/utils/macromolecule/types';
23
+ import {SeqHandler} from '@datagrok-libraries/bio/src/utils/seq-handler';
24
+ import {ISeqSplitted} from '@datagrok-libraries/bio/src/utils/macromolecule/types';
25
25
  import {getSplitter} from '@datagrok-libraries/bio/src/utils/macromolecule/utils';
26
26
  import {errInfo} from '@datagrok-libraries/bio/src/utils/err-info';
27
- import {alphabetPolymerTypes, IMonomerLib} from '@datagrok-libraries/bio/src/types';
28
- import {getGridCellRendererBack} from '@datagrok-libraries/bio/src/utils/cell-renderer-back-base';
29
-
30
- import {
31
- Temps as mmcrTemps,
32
- tempTAGS, rendererSettingsChangedState, Temps
33
- } from '../utils/cell-renderer-consts';
27
+ import {IMonomerLib} from '@datagrok-libraries/bio/src/types';
28
+ import {GapOriginals} from '@datagrok-libraries/bio/src/utils/macromolecule/consts';
29
+ import {ISeqMonomer} from '@datagrok-libraries/bio/src/helm/types';
30
+ import {execMonomerHoverLinks} from '@datagrok-libraries/bio/src/monomer-works/monomer-hover';
31
+ import {getGridCellColTemp} from '@datagrok-libraries/bio/src/utils/cell-renderer-back-base';
32
+ import {HelmTypes} from '@datagrok-libraries/bio/src/helm/consts';
33
+ import {MmcrTemps, rendererSettingsChangedState, tempTAGS} from '@datagrok-libraries/bio/src/utils/cell-renderer-consts';
34
34
  import * as C from './constants';
35
35
 
36
36
  import {_package, getMonomerLib} from '../package';
37
+ import {max} from 'rxjs/operators';
37
38
 
38
39
  type TempType = { [tagName: string]: any };
39
40
 
40
- const undefinedColor = 'rgb(100,100,100)';
41
- const monomerToShortFunction: MonomerToShortFunc = monomerToShort;
42
-
43
- function getUpdatedWidth(
44
- grid: DG.Grid | null | undefined, g: CanvasRenderingContext2D, x: number, w: number, dpr: number
45
- ): number {
46
- return !!grid ? Math.max(Math.min(grid.canvas.width / dpr - x, w)) : Math.max(g.canvas.width / dpr - x, 0);
47
- }
41
+ export const monomerToShortFunction: MonomerToShortFunc = monomerToShort;
48
42
 
49
43
  export function processSequence(subParts: string[]): [string[], boolean] {
50
44
  const simplified = !wu.enumerate(subParts).some(([amino, index]) =>
@@ -65,12 +59,10 @@ export function processSequence(subParts: string[]): [string[], boolean] {
65
59
  }
66
60
 
67
61
  type RendererGridCellTemp = {
68
- [mmcrTemps.monomerPlacer]: MonomerPlacer
62
+ [MmcrTemps.monomerPlacer]: MonomerPlacer
69
63
  }
70
64
 
71
65
  export class MacromoleculeSequenceCellRenderer extends DG.GridCellRenderer {
72
- private padding: number = 5;
73
-
74
66
  get name(): string { return 'sequence'; }
75
67
 
76
68
  get cellType(): string { return 'sequence'; }
@@ -85,13 +77,15 @@ export class MacromoleculeSequenceCellRenderer extends DG.GridCellRenderer {
85
77
  gridCell.grid.invalidate();
86
78
  }
87
79
 
88
- onMouseMove(gridCell: DG.GridCell, e: MouseEvent): void {
80
+ override onMouseMove(gridCell: DG.GridCell, e: MouseEvent): void {
81
+ if (gridCell.tableRowIndex == null) return;
82
+
89
83
  // if (gridCell.cell.column.getTag(bioTAGS.aligned) !== ALIGNMENT.SEQ_MSA)
90
84
  // return;
91
85
 
92
86
  const [_gridCol, tableCol, temp] =
93
- getGridCellRendererBack<string, MonomerPlacer>(gridCell);
94
- const seqColTemp: MonomerPlacer = temp['rendererBack'];
87
+ getGridCellColTemp<string, MonomerPlacer>(gridCell);
88
+ const seqColTemp: MonomerPlacer = temp.rendererBack;
95
89
  if (!seqColTemp) return; // Can do nothing without precalculated data
96
90
 
97
91
  const gridCellBounds: DG.Rect = gridCell.bounds;
@@ -105,30 +99,39 @@ export class MacromoleculeSequenceCellRenderer extends DG.GridCellRenderer {
105
99
  const argsX = e.offsetX - gridCell.gridColumn.left + (gridCell.gridColumn.left - gridCellBounds.x);
106
100
  const left: number | null = seqColTemp.getPosition(gridCell.tableRowIndex!, argsX, gridCellBounds.width);
107
101
 
108
- const seqCList: SeqSplittedBase = SeqHandler.forColumn(tableCol)
109
- .getSplitted(gridCell.tableRowIndex!).canonicals;
110
- if (left !== null && left < seqCList.length) {
111
- const monomerSymbol: string = seqCList[left];
102
+ const seqSS = SeqHandler.forColumn(tableCol)
103
+ .getSplitted(gridCell.tableRowIndex!);
104
+ if (left !== null && left < seqSS.length) {
105
+ const sh = SeqHandler.forColumn(tableCol);
106
+ const alphabet = sh.alphabet ?? ALPHABET.UN;
107
+ const seqMonomer = {
108
+ position: left,
109
+ biotype: alphabet === ALPHABET.RNA || alphabet === ALPHABET.DNA ? HelmTypes.NUCLEOTIDE : HelmTypes.AA,
110
+ symbol: seqSS.getCanonical(left),
111
+ } as ISeqMonomer;
112
112
  const tooltipElements: HTMLElement[] = [];
113
- let monomerDiv = seqColTemp._monomerStructureMap[monomerSymbol];
113
+ let monomerDiv = seqColTemp._monomerStructureMap[seqMonomer.symbol];
114
114
  if (!monomerDiv || true) {
115
- monomerDiv = seqColTemp._monomerStructureMap[monomerSymbol] = (() => {
116
- const sh = SeqHandler.forColumn(tableCol);
117
- const alphabet = sh.alphabet ?? ALPHABET.UN;
118
- const polymerType = alphabetPolymerTypes[alphabet as ALPHABET];
119
-
115
+ monomerDiv = seqColTemp._monomerStructureMap[seqMonomer.symbol] = (() => {
120
116
  const lib: IMonomerLib | null = getMonomerLib();
121
- return lib ? lib.getTooltip(polymerType, monomerSymbol) : ui.divText('Monomer library is not available');
117
+ return lib ? lib.getTooltip(seqMonomer.biotype, seqMonomer.symbol) : ui.divText('Monomer library is not available');
122
118
  })();
123
119
  }
124
120
  tooltipElements.push(monomerDiv);
125
121
  ui.tooltip.show(ui.divV(tooltipElements), e.x + 16, e.y + 16);
122
+
123
+ execMonomerHoverLinks(gridCell, seqMonomer);
126
124
  } else {
127
125
  //
128
126
  ui.tooltip.hide();
127
+ execMonomerHoverLinks(gridCell, null);
129
128
  }
130
129
  }
131
130
 
131
+ override onMouseLeave(gridCell: DG.GridCell, e: MouseEvent) {
132
+ execMonomerHoverLinks(gridCell, null);
133
+ }
134
+
132
135
  /**
133
136
  * Cell renderer function.
134
137
  *
@@ -147,9 +150,8 @@ export class MacromoleculeSequenceCellRenderer extends DG.GridCellRenderer {
147
150
  ): void {
148
151
  const logPrefix: string = 'MacromoleculeSequenceCellRenderer.render()';
149
152
 
150
- const dpr = window.devicePixelRatio;
151
153
  const [gridCol, tableCol, _temp] =
152
- getGridCellRendererBack<string, MonomerPlacer>(gridCell);
154
+ getGridCellColTemp<string, MonomerPlacer>(gridCell);
153
155
  if (!tableCol) return;
154
156
  const tableColTemp: TempType = tableCol.temp;
155
157
 
@@ -162,13 +164,13 @@ export class MacromoleculeSequenceCellRenderer extends DG.GridCellRenderer {
162
164
  const v = parseInt(tableCol.getTag(mmcrTAGS.maxMonomerLength));
163
165
  maxLengthOfMonomer = !isNaN(v) && v ? v : 50;
164
166
  }
165
- if (Temps.maxMonomerLength in tableColTemp) {
166
- const v = tableColTemp[Temps.maxMonomerLength];
167
+ if (MmcrTemps.maxMonomerLength in tableColTemp) {
168
+ const v = tableColTemp[MmcrTemps.maxMonomerLength];
167
169
  maxLengthOfMonomer = !isNaN(v) && v ? v : 50;
168
170
  }
169
171
 
170
172
  const [_gc, _tc, temp] =
171
- getGridCellRendererBack<string, MonomerPlacer>(gridCell);
173
+ getGridCellColTemp<string, MonomerPlacer>(gridCell);
172
174
  let seqColTemp: MonomerPlacer = temp.rendererBack;
173
175
  if (!seqColTemp) {
174
176
  seqColTemp = temp.rendererBack = new MonomerPlacer(gridCol, tableCol, _package.logger, maxLengthOfMonomer,
@@ -182,91 +184,19 @@ export class MacromoleculeSequenceCellRenderer extends DG.GridCellRenderer {
182
184
  });
183
185
  }
184
186
 
185
- g.save();
186
- try {
187
- if (
188
- tableCol.temp[Temps.rendererSettingsChanged] === rendererSettingsChangedState.true ||
189
- seqColTemp.monomerLengthLimit != maxLengthOfMonomer
190
- ) {
191
- gapLength = tableColTemp[mmcrTemps.gapLength] as number ?? gapLength;
192
- // this event means that the mm renderer settings have changed,
193
- // particularly monomer representation and max width.
194
- seqColTemp.setMonomerLengthLimit(maxLengthOfMonomer);
195
- seqColTemp.setSeparatorWidth(seqColTemp.isMsa() ? msaGapLength : gapLength);
196
- tableCol.temp[Temps.rendererSettingsChanged] = rendererSettingsChangedState.false;
197
- }
198
-
199
- const [maxLengthWords, maxLengthWordsSum]: [number[], number[]] =
200
- seqColTemp.getCellMonomerLengths(gridCell.tableRowIndex!, w);
201
- const _maxIndex = maxLengthWords.length;
202
-
203
- const value: any = gridCell.cell.value;
204
- const rowIdx = gridCell.cell.rowIndex;
205
- const paletteType = tableCol.getTag(bioTAGS.alphabet);
206
- const minDistanceRenderer = 50;
207
- w = getUpdatedWidth(gridCol?.grid, g, x, w, dpr);
208
- g.beginPath();
209
- g.rect(x + this.padding, y + this.padding, w - this.padding - 1, h - this.padding * 2);
210
- g.clip();
211
- g.font = '12px monospace';
212
- g.textBaseline = 'top';
213
-
214
- //TODO: can this be replaced/merged with splitSequence?
215
- const units = tableCol.meta.units;
216
- const aligned: string = tableCol.getTag(bioTAGS.aligned);
217
-
218
- const palette = getPaletteByType(paletteType);
219
-
220
- const separator = tableCol.getTag(bioTAGS.separator) ?? '';
221
- const minMonWidth = seqColTemp.props.separatorWidth + 1 * seqColTemp.props.monomerCharWidth;
222
- const splitLimit = Math.ceil(w / minMonWidth);
223
- const sh = SeqHandler.forColumn(tableCol);
224
-
225
- const tempReferenceSequence: string | null = tableColTemp[tempTAGS.referenceSequence];
226
- const tempCurrentWord: string | null = tableColTemp[tempTAGS.currentWord];
227
- if (tempCurrentWord && tableCol?.dataFrame?.currentRowIdx === -1)
228
- tableColTemp[tempTAGS.currentWord] = null;
229
-
230
- const referenceSequence: string[] = (() => {
231
- // @ts-ignore
232
- const splitterFunc: SplitterFunc = sh.getSplitter(splitLimit);
233
- return wu(splitterFunc(
234
- ((tempReferenceSequence != null) && (tempReferenceSequence != '')) ?
235
- tempReferenceSequence : tempCurrentWord ?? '').originals).toArray();
236
- })();
237
-
238
- const subParts: ISeqSplitted = sh.getSplitted(rowIdx);
239
- /* let x1 = x; */
240
- let color = undefinedColor;
241
- let drawStyle = DrawStyle.classic;
242
-
243
- if (aligned && aligned.includes('MSA') && units == NOTATION.SEPARATOR)
244
- drawStyle = DrawStyle.MSA;
245
-
246
- const visibleSeqLength = Math.min(subParts.length, splitLimit);
247
- for (let posIdx: number = 0; posIdx < visibleSeqLength; ++posIdx) {
248
- const amino: string = subParts.getOriginal(posIdx);
249
- color = palette.get(amino);
250
- g.fillStyle = undefinedColor;
251
- const last = posIdx === subParts.length - 1;
252
- /*x1 = */
253
- const opts = {
254
- color: color, pivot: 0, left: true, transparencyRate: 1.0, separator: separator, last: last,
255
- drawStyle: drawStyle, maxWord: maxLengthWordsSum, wordIdx: posIdx, gridCell: gridCell,
256
- referenceSequence: referenceSequence, maxLengthOfMonomer: maxLengthOfMonomer,
257
- monomerTextSizeMap: seqColTemp._monomerLengthMap, logger: _package.logger
258
- };
259
- printLeftOrCentered(g, amino, x + this.padding, y, w, h, opts);
260
- if (minDistanceRenderer > w) break;
261
- }
262
- } catch (err: any) {
263
- const [errMsg, errStack] = errInfo(err);
264
- seqColTemp.logger.error(errMsg, undefined, errStack);
265
- seqColTemp.errors.push(err);
266
- //throw err; // Do not throw to prevent disabling renderer
267
- } finally {
268
- g.restore();
187
+ if (
188
+ tableCol.temp[MmcrTemps.rendererSettingsChanged] === rendererSettingsChangedState.true ||
189
+ seqColTemp.monomerLengthLimit != maxLengthOfMonomer
190
+ ) {
191
+ gapLength = tableColTemp[MmcrTemps.gapLength] as number ?? gapLength;
192
+ // this event means that the mm renderer settings have changed,
193
+ // particularly monomer representation and max width.
194
+ seqColTemp.setMonomerLengthLimit(maxLengthOfMonomer);
195
+ seqColTemp.setSeparatorWidth(seqColTemp.isMsa() ? msaGapLength : gapLength);
196
+ tableCol.temp[MmcrTemps.rendererSettingsChanged] = rendererSettingsChangedState.false;
269
197
  }
198
+
199
+ seqColTemp.render(g, x, y, w, h, gridCell, _cellStyle);
270
200
  }
271
201
  }
272
202
 
@@ -306,8 +236,10 @@ export class MacromoleculeDifferenceCellRenderer extends DG.GridCellRenderer {
306
236
  //TODO: can this be replaced/merged with splitSequence?
307
237
  const [s1, s2] = s.split('#');
308
238
  const splitter = getSplitter(units, separator);
309
- const subParts1 = wu(splitter(s1).canonicals).toArray();
310
- const subParts2 = wu(splitter(s2).canonicals).toArray();
239
+ const s1SS = splitter(s1);
240
+ const s2SS = splitter(s2);
241
+ const subParts1 = wu.count(0).take(s1SS.length).map((posIdx) => s1SS.getCanonical(posIdx)).toArray();
242
+ const subParts2 = wu.count(0).take(s2SS.length).map((posIdx) => s2SS.getCanonical(posIdx)).toArray();
311
243
  drawMoleculeDifferenceOnCanvas(g, x, y, w, h, subParts1, subParts2, units);
312
244
  }
313
245
  }
@@ -2,11 +2,20 @@ import * as grok from 'datagrok-api/grok';
2
2
  import * as ui from 'datagrok-api/ui';
3
3
  import * as DG from 'datagrok-api/dg';
4
4
 
5
- import {GAP_SYMBOL, INotationProvider, ISeqSplitted, SeqSplittedBase, SplitterFunc}
5
+ import wu from 'wu';
6
+
7
+ import {INotationProvider, ISeqSplitted, SeqSplittedBase, SplitterFunc}
6
8
  from '@datagrok-libraries/bio/src/utils/macromolecule/types';
7
9
  import {getSplitterWithSeparator, StringListSeqSplitted} from '@datagrok-libraries/bio/src/utils/macromolecule/utils';
8
- import {GapOriginals} from '@datagrok-libraries/bio/src/utils/seq-handler';
9
10
  import {NOTATION} from '@datagrok-libraries/bio/src/utils/macromolecule';
11
+ import {CellRendererBackBase} from '@datagrok-libraries/bio/src/utils/cell-renderer-back-base';
12
+ import {GAP_SYMBOL, GapOriginals} from '@datagrok-libraries/bio/src/utils/macromolecule/consts';
13
+ import {MonomerPlacer} from '@datagrok-libraries/bio/src/utils/cell-renderer-monomer-placer';
14
+ import {SeqHandler} from '@datagrok-libraries/bio/src/utils/seq-handler';
15
+
16
+ import {monomerToShortFunction} from './cell-renderer';
17
+
18
+ import {_package} from '../package';
10
19
 
11
20
  export class CyclizedNotationProvider implements INotationProvider {
12
21
  private readonly separatorSplitter: SplitterFunc;
@@ -21,7 +30,9 @@ export class CyclizedNotationProvider implements INotationProvider {
21
30
 
22
31
  private _splitter(seq: string): ISeqSplitted {
23
32
  const baseSS: ISeqSplitted = this.separatorSplitter(seq);
24
- return new CyclizedSeqSplitted(baseSS.originals, GapOriginals[NOTATION.SEPARATOR]);
33
+ return new CyclizedSeqSplitted(
34
+ wu.count(0).take(baseSS.length).map((p) => baseSS.getOriginal(p)).toArray(),
35
+ GapOriginals[NOTATION.SEPARATOR]);
25
36
  }
26
37
 
27
38
  public async getHelm(seqCol: DG.Column<string>, options?: any): Promise<DG.Column<string>> {
@@ -38,23 +49,26 @@ export class CyclizedNotationProvider implements INotationProvider {
38
49
  const resHelmCol = (await editorFunc.prepare({call: ptConvertCall}).call()).getOutputParamValue() as DG.Column<string>;
39
50
  return resHelmCol;
40
51
  }
52
+
53
+ public createCellRendererBack(gridCol: DG.GridColumn | null, tableCol: DG.Column<string>): CellRendererBackBase<string> {
54
+ let maxLengthOfMonomer: number = (_package.properties ? _package.properties.maxMonomerLength : 4) ?? 50;
55
+ return new MonomerPlacer(gridCol, tableCol, _package.logger, maxLengthOfMonomer,
56
+ () => {
57
+ const sh = SeqHandler.forColumn(tableCol);
58
+ return {
59
+ seqHandler: sh,
60
+ monomerCharWidth: 7,
61
+ separatorWidth: 11,
62
+ monomerToShort: monomerToShortFunction,
63
+ };
64
+ });
65
+ }
41
66
  }
42
67
 
43
68
  /** Gets canonical monomers for original ones with cyclization marks */
44
69
  export class CyclizedSeqSplitted extends StringListSeqSplitted {
45
70
  private readonly seqCList: (string | null)[];
46
71
 
47
- private _canonicals: string[] | null = null;
48
- override get canonicals(): SeqSplittedBase {
49
- if (!this._canonicals) {
50
- const len = this.length;
51
- this._canonicals = new Array<string>(len);
52
- for (let posIdx = 0; posIdx < len; ++posIdx)
53
- this._canonicals[posIdx] = this.getCanonical(posIdx);
54
- }
55
- return this._canonicals;
56
- }
57
-
58
72
  override getCanonical(posIdx: number): string {
59
73
  if (this.isGap(posIdx)) return GAP_SYMBOL;
60
74
 
@@ -2,8 +2,6 @@ import * as grok from 'datagrok-api/grok';
2
2
  import * as ui from 'datagrok-api/ui';
3
3
  import * as DG from 'datagrok-api/dg';
4
4
 
5
- import {GAP_SYMBOL, INotationProvider, ISeqSplitted, SeqSplittedBase, SplitterFunc}
6
- from '@datagrok-libraries/bio/src/utils/macromolecule/types';
7
5
  import {CyclizedNotationProvider} from './cyclized';
8
6
 
9
7
 
@@ -4,37 +4,39 @@ import * as OCL from 'openchemlib/full';
4
4
 
5
5
  import {errInfo} from '@datagrok-libraries/bio/src/utils/err-info';
6
6
  import {RDModule, RDMol} from '@datagrok-libraries/chem-meta/src/rdkit-api';
7
- import {IMonomerLib} from '@datagrok-libraries/bio/src/types/index';
7
+ import {IMonomerLib, IMonomerLibBase} from '@datagrok-libraries/bio/src/types/index';
8
8
  import {IMonomerLibHelper} from '@datagrok-libraries/bio/src/monomer-works/monomer-utils';
9
9
  import {getHelmHelper, IHelmHelper} from '@datagrok-libraries/bio/src/helm/helm-helper';
10
+ import {MolfileWithMap, MonomerMap} from '@datagrok-libraries/bio/src/monomer-works/types';
10
11
 
11
12
  import {Polymer} from './polymer';
12
13
  import {GlobalMonomerPositionHandler} from './position-handler';
13
- import {MolfileWithMap, MonomerMap} from './types';
14
14
 
15
15
  import {_package} from '../../../package';
16
+ import {getUnusedColName} from '@datagrok-libraries/bio/src/monomer-works/utils';
16
17
 
17
18
 
18
19
  export class HelmToMolfileConverter {
19
20
  constructor(
20
- private helmColumn: DG.Column<string>,
21
- private df: DG.DataFrame,
22
- private libHelper: IMonomerLibHelper,
23
- private helmHelper: IHelmHelper,
21
+ private readonly helmHelper: IHelmHelper,
22
+ private readonly rdKitModule: RDModule,
23
+ private readonly monomerLib: IMonomerLibBase
24
24
  ) { }
25
25
 
26
- convertToSmiles(rdKitModule: RDModule): DG.Column<string> {
27
- const smiles = this.getSmilesList(rdKitModule);
28
- const columnName = this.df.columns.getUnusedName(`smiles(${this.helmColumn.name})`);
29
- return DG.Column.fromStrings(columnName, smiles.map((molecule) => {
26
+ convertToSmiles(helmCol: DG.Column<string>): DG.Column<string> {
27
+ const df = helmCol.dataFrame;
28
+ const smiles = this.getSmilesList(helmCol);
29
+ const smilesColName = `smiles(${helmCol.name})`;
30
+ const smilesColNameU = df ? df.columns.getUnusedName(smilesColName) : smilesColName;
31
+ return DG.Column.fromStrings(smilesColNameU, smiles.map((molecule) => {
30
32
  if (molecule === null)
31
33
  return '';
32
34
  return molecule;
33
35
  }));
34
36
  }
35
37
 
36
- private getSmilesList(rdKitModule: RDModule): string[] {
37
- const molfilesV2K = this.convertToMolfileV3KColumn(rdKitModule).toList();
38
+ private getSmilesList(helmCol: DG.Column<string>): string[] {
39
+ const molfilesV2K = this.convertToMolfileV3KColumn(helmCol).toList();
38
40
  const smiles = molfilesV2K.map((mol) => DG.chem.convert(mol, DG.chem.Notation.MolBlock, DG.chem.Notation.Smiles));
39
41
  return smiles;
40
42
  }
@@ -61,8 +63,11 @@ export class HelmToMolfileConverter {
61
63
  }
62
64
 
63
65
  // @deprecated Use SeqHelper.helmToAtomicLevel
64
- convertToRdKitBeautifiedMolfileColumn(chiralityEngine: boolean, rdKitModule: RDModule): DG.Column<string> {
65
- const molfilesV3K = this.convertToMolfileV3KColumn(rdKitModule).toList();
66
+ convertToRdKitBeautifiedMolfileColumn(
67
+ helmCol: DG.Column<string>, chiralityEngine: boolean, rdKitModule: RDModule, monomerLib: IMonomerLibBase
68
+ ): DG.Column<string> {
69
+ const df = helmCol.dataFrame;
70
+ const molfilesV3K = this.convertToMolfileV3KColumn(helmCol).toList();
66
71
  const beautifiedMols = molfilesV3K.map((item) => {
67
72
  if (item === '')
68
73
  return null;
@@ -74,11 +79,12 @@ export class HelmToMolfileConverter {
74
79
  mol.straighten_depiction(true);
75
80
  return mol;
76
81
  });
77
- const columnName = this.df.columns.getUnusedName(`molfile(${this.helmColumn.name})`);
82
+ const molColName = `molfile(${helmCol.name})`;
83
+ const molColNameU = df ? df.columns.getUnusedName(molColName) : molColName;
78
84
 
79
85
  if (chiralityEngine)
80
- return this.getMolV3000ViaOCL(beautifiedMols, columnName);
81
- return DG.Column.fromStrings(columnName, beautifiedMols.map((mol) => {
86
+ return this.getMolV3000ViaOCL(beautifiedMols, molColNameU);
87
+ return DG.Column.fromStrings(molColNameU, beautifiedMols.map((mol) => {
82
88
  if (mol === null)
83
89
  return '';
84
90
  const molBlock = mol.get_v3Kmolblock();
@@ -88,71 +94,86 @@ export class HelmToMolfileConverter {
88
94
  }
89
95
 
90
96
 
91
- public convertToMolfileV3KColumn(rdKitModule: RDModule): DG.Column<string> {
92
- const polymerGraphColumn: DG.Column<string> = this.getPolymerGraphColumn();
93
- const monomerLib: IMonomerLib = this.libHelper.getMonomerLib();
97
+ public convertToMolfileV3KColumn(
98
+ helmCol: DG.Column<string>
99
+ ): DG.Column<string> {
100
+ const df = helmCol.dataFrame;
101
+ const polymerGraphColumn: DG.Column<string> = this.getPolymerGraphColumn(helmCol);
94
102
  const molfileList = polymerGraphColumn.toList().map(
95
103
  (pseudoMolfile: string, idx: number) => {
96
- const helm = this.helmColumn.get(idx);
104
+ const helm = helmCol.get(idx);
97
105
  if (!helm) return '';
98
106
 
99
107
  let resMolfileWithMap: MolfileWithMap;
100
108
  try {
101
- resMolfileWithMap = this.getPolymerMolfile(helm, pseudoMolfile, rdKitModule, monomerLib);
109
+ resMolfileWithMap = this.getPolymerMolfile(helm, pseudoMolfile);
102
110
  } catch (err: any) {
103
111
  const [errMsg, errStack] = errInfo(err);
104
112
  _package.logger.error(errMsg, undefined, errStack);
105
- resMolfileWithMap = MolfileWithMap.empty();
113
+ resMolfileWithMap = MolfileWithMap.createEmpty();
106
114
  }
107
115
  return resMolfileWithMap.molfile;
108
116
  });
109
- const molfileColName = this.df.columns.getUnusedName(`molfileV2K(${this.helmColumn.name})`);
110
- const molfileColumn = DG.Column.fromList('string', molfileColName, molfileList);
117
+ const molColName = getUnusedColName(df, `molfileV2K(${helmCol.name})`);
118
+ const molfileColumn = DG.Column.fromList('string', molColName, molfileList);
111
119
  return molfileColumn;
112
120
  }
113
121
 
114
- public convertToMolfileV3K(rdKitModule: RDModule): MolfileWithMap[] {
115
- const polymerGraphColumn: DG.Column<string> = this.getPolymerGraphColumn();
116
- const monomerLib: IMonomerLib = this.libHelper.getMonomerLib();
117
- const resList: MolfileWithMap[] = polymerGraphColumn.toList().map(
118
- (pseudoMolfile: string, idx: number): MolfileWithMap => {
119
- const helm = this.helmColumn.get(idx);
120
- if (!helm) return {molfile: '', monomers: []};
121
-
122
- let resMolfile: MolfileWithMap;
123
- try {
124
- resMolfile = this.getPolymerMolfile(helm, pseudoMolfile, rdKitModule, monomerLib);
125
- } catch (err: any) {
126
- const [errMsg, errStack] = errInfo(err);
127
- _package.logger.error(errMsg, undefined, errStack);
128
- resMolfile = MolfileWithMap.empty();
129
- }
130
- return resMolfile;
131
- });
122
+ /** Gets list of monomer molfiles */
123
+ public convertToMolfileV3K(helmCol: DG.Column<string>, rdKitModule: RDModule, monomerLib: IMonomerLibBase): MolfileWithMap[] {
124
+ const polymerGraphColumn: DG.Column<string> = this.getPolymerGraphColumn(helmCol);
125
+ const rowCount = helmCol.length;
126
+ const resList: MolfileWithMap[] = new Array<MolfileWithMap>(rowCount);
127
+ for (let rowIdx = 0; rowIdx < rowCount; ++rowIdx) {
128
+ const helm = helmCol.get(rowIdx);
129
+ if (!helm) {
130
+ resList[rowIdx] = MolfileWithMap.createEmpty();
131
+ continue;
132
+ }
133
+
134
+ const pseudoMolfile = polymerGraphColumn.get(rowIdx)!;
135
+ let resMolfile: MolfileWithMap;
136
+ try {
137
+ resMolfile = this.getPolymerMolfile(helm, pseudoMolfile);
138
+ } catch (err: any) {
139
+ const [errMsg, errStack] = errInfo(err);
140
+ _package.logger.error(errMsg, undefined, errStack);
141
+ resMolfile = MolfileWithMap.createEmpty();
142
+ }
143
+ resList[rowIdx] = resMolfile;
144
+ }
132
145
  return resList;
133
146
  }
134
147
 
135
- private getPolymerGraphColumn(): DG.Column<string> {
136
- const helmStrList = this.helmColumn.toList();
148
+ private getPolymerGraphColumn(helmCol: DG.Column<string>): DG.Column<string> {
149
+ const helmStrList = helmCol.toList();
137
150
  const molfileList = this.helmHelper.getMolfiles(helmStrList);
138
151
  const molfileCol = DG.Column.fromStrings('mols', molfileList);
139
152
  return molfileCol;
140
153
  }
141
154
 
142
155
  private getPolymerMolfile(
143
- helm: string,
144
- polymerGraph: string,
145
- rdKitModule: RDModule,
146
- monomerLib: IMonomerLib
156
+ helm: string, polymerGraph: string
147
157
  ): MolfileWithMap {
158
+ const woGapsRes = this.helmHelper.removeGaps(helm);
159
+ const woGapsHelm = woGapsRes.resHelm;
160
+ const woGapsReverseMap = new Map<number, number>();
161
+ for (const [orgPosIdx, woGapsPosIdx] of (woGapsRes.monomerMap?.entries() ?? [])) {
162
+ woGapsReverseMap.set(woGapsPosIdx, orgPosIdx);
163
+ }
148
164
  const globalPositionHandler = new GlobalMonomerPositionHandler(polymerGraph);
149
- const polymer = new Polymer(helm, rdKitModule, monomerLib);
165
+ const woGapsPolymer = new Polymer(woGapsHelm, this.rdKitModule, this.monomerLib);
150
166
  globalPositionHandler.monomerSymbols.forEach((monomerSymbol: string, monomerIdx: number) => {
151
167
  const shift = globalPositionHandler.getMonomerShifts(monomerIdx);
152
- polymer.addMonomer(monomerSymbol, monomerIdx, shift);
168
+ woGapsPolymer.addMonomer(monomerSymbol, monomerIdx, shift);
153
169
  });
154
- const polymerMolfile: MolfileWithMap = polymer.compileToMolfile();
155
- return polymerMolfile;
170
+ const woGapsMolfile: MolfileWithMap = woGapsPolymer.compileToMolfile();
171
+ const orgMonomerMap = new MonomerMap();
172
+ for (const [woGapsPosIdx, m] of woGapsMolfile.monomers.entries()) {
173
+ const orgPosIdx = woGapsReverseMap.get(woGapsPosIdx)!;
174
+ orgMonomerMap.set(orgPosIdx, m);
175
+ }
176
+
177
+ return new MolfileWithMap(woGapsMolfile.molfile, orgMonomerMap);
156
178
  }
157
179
  }
158
-