@datagrok/bio 2.18.4 → 2.20.0

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 (52) hide show
  1. package/CHANGELOG.md +5 -0
  2. package/README.md +2 -2
  3. package/dist/242.js +1 -1
  4. package/dist/242.js.map +1 -1
  5. package/dist/284.js +1 -1
  6. package/dist/284.js.map +1 -1
  7. package/dist/589.js +1 -1
  8. package/dist/589.js.map +1 -1
  9. package/dist/731.js +1 -1
  10. package/dist/731.js.map +1 -1
  11. package/dist/810.js +2 -0
  12. package/dist/810.js.map +1 -0
  13. package/dist/980.js +1 -1
  14. package/dist/980.js.map +1 -1
  15. package/dist/package-test.js +2 -2
  16. package/dist/package-test.js.map +1 -1
  17. package/dist/package.js +3 -3
  18. package/dist/package.js.map +1 -1
  19. package/dockerfiles/container.json +1 -1
  20. package/files/monomer-libraries/HELMCoreLibrary.json +18926 -18215
  21. package/files/samples/HELM_BI_CYCLIC.csv +7 -0
  22. package/files/samples/peptides-non-natural.csv +1001 -0
  23. package/files/tests/to-atomic-level-dna-fasta-output.csv +15077 -15077
  24. package/files/tests/to-atomic-level-msa-fasta-output.csv +1903 -1903
  25. package/files/tests/to-atomic-level-msa-separator-output.csv +3236 -3236
  26. package/files/tests/to-atomic-level-peptides-fasta-output.csv +32262 -32262
  27. package/files/tests/to-atomic-level-pt-fasta-2.mol +29 -29
  28. package/package.json +7 -7
  29. package/projects/seq_space_demo.zip +0 -0
  30. package/src/analysis/sequence-diversity-viewer.ts +22 -14
  31. package/src/analysis/sequence-search-base-viewer.ts +6 -72
  32. package/src/analysis/sequence-similarity-viewer.ts +42 -23
  33. package/src/demo/bio01-similarity-diversity.ts +21 -2
  34. package/src/demo/bio01a-hierarchical-clustering-and-sequence-space.ts +7 -0
  35. package/src/demo/bio01b-hierarchical-clustering-and-activity-cliffs.ts +1 -1
  36. package/src/demo/bio03-atomic-level.ts +15 -0
  37. package/src/package.ts +29 -59
  38. package/src/tests/activity-cliffs-utils.ts +7 -6
  39. package/src/tests/scoring.ts +2 -2
  40. package/src/tests/similarity-diversity-tests.ts +2 -2
  41. package/src/utils/cell-renderer.ts +11 -1
  42. package/src/utils/helm-to-molfile/converter/converter.ts +21 -9
  43. package/src/utils/helm-to-molfile/converter/monomer-wrapper.ts +8 -4
  44. package/src/utils/helm-to-molfile/converter/simple-polymer.ts +18 -9
  45. package/src/utils/monomer-lib/monomer-lib-base.ts +4 -4
  46. package/src/utils/pepsea.ts +22 -39
  47. package/src/utils/seq-helper/seq-handler.ts +2 -2
  48. package/src/utils/seq-helper/seq-helper.ts +50 -34
  49. package/src/utils/sequence-to-mol.ts +1 -1
  50. package/src/utils/ui-utils.ts +23 -0
  51. package/test-console-output-1.log +0 -7387
  52. package/test-record-1.mp4 +0 -0
package/src/package.ts CHANGED
@@ -47,11 +47,10 @@ import {BioSubstructureFilter} from './widgets/bio-substructure-filter';
47
47
  import {WebLogoViewer} from './viewers/web-logo-viewer';
48
48
  import {MonomerLibManager} from './utils/monomer-lib/lib-manager';
49
49
  import {getMonomerLibraryManagerLink, showManageLibrariesDialog, showManageLibrariesView} from './utils/monomer-lib/library-file-manager/ui';
50
- import {demoBio01UI} from './demo/bio01-similarity-diversity';
51
- import {demoBio01aUI} from './demo/bio01a-hierarchical-clustering-and-sequence-space';
50
+ import {demoBioSimDiv} from './demo/bio01-similarity-diversity';
51
+ import {demoSeqSpace} from './demo/bio01a-hierarchical-clustering-and-sequence-space';
52
52
  import {demoBio01bUI} from './demo/bio01b-hierarchical-clustering-and-activity-cliffs';
53
- import {demoBio03UI} from './demo/bio03-atomic-level';
54
- import {demoBio05UI} from './demo/bio05-helm-msa-sequence-space';
53
+ import {demoToAtomicLevel} from './demo/bio03-atomic-level';
55
54
  import {checkInputColumnUI} from './utils/check-input-column';
56
55
  import {MsaWarning} from './utils/multiple-sequence-alignment';
57
56
  import {multipleSequenceAlignmentUI} from './utils/multiple-sequence-alignment-ui';
@@ -422,7 +421,7 @@ export function getRegion(
422
421
  start ?? null, end ?? null, name ?? null);
423
422
  }
424
423
 
425
- //top-menu: Bio | Convert | Get Region...
424
+ //top-menu: Bio | Calculate | Get Region...
426
425
  //name: Get Region Top Menu
427
426
  //description: Get sequences for a region specified from a Macromolecule
428
427
  //input: dataframe table [Input data table]
@@ -587,7 +586,7 @@ export async function sequenceSpaceTopMenu(table: DG.DataFrame, molecules: DG.Co
587
586
  plotEmbeddings: boolean, preprocessingFunction?: DG.Func, options?: (IUMAPOptions | ITSNEOptions) & Options,
588
587
  clusterEmbeddings?: boolean, isDemo?: boolean
589
588
  ): Promise<DG.ScatterPlotViewer | undefined> {
590
- const tableView = isDemo ? (grok.shell.view('Browse')! as DG.BrowseView)!.preview! as DG.TableView :
589
+ const tableView =
591
590
  grok.shell.tv.dataFrame == table ? grok.shell.tv : undefined;
592
591
  if (!checkInputColumnUI(molecules, 'Sequence Space'))
593
592
  return;
@@ -607,14 +606,13 @@ export async function sequenceSpaceTopMenu(table: DG.DataFrame, molecules: DG.Co
607
606
  return res;
608
607
  }
609
608
 
610
- //top-menu: Bio | Convert | To Atomic Level...
609
+ //top-menu: Bio | Transform | To Atomic Level...
611
610
  //name: To Atomic Level
612
611
  //description: Converts sequences to molblocks
613
612
  //input: dataframe table [Input data table]
614
613
  //input: column seqCol {caption: Sequence; semType: Macromolecule}
615
614
  //input: bool nonlinear =false {caption: Non-linear; description: Slower mode for cycling/branching HELM structures}
616
615
  //input: bool highlight =false {caption: Highlight monomers; description: Highlight monomers' substructures of the molecule }
617
- //output:
618
616
  export async function toAtomicLevel(
619
617
  table: DG.DataFrame, seqCol: DG.Column, nonlinear: boolean, highlight: boolean = false
620
618
  ): Promise<void> {
@@ -630,6 +628,17 @@ export async function toAtomicLevel(
630
628
  }
631
629
  }
632
630
 
631
+ //name: To Atomic Level...
632
+ //input: column seqCol {semType: Macromolecule}
633
+ //meta.action: to atomic level
634
+ export async function toAtomicLevelAction(seqCol: DG.Column) {
635
+ if (!seqCol?.dataFrame)
636
+ throw new Error('Sequence column is not found or its data frame is not empty');
637
+ const func = DG.Func.find({name: 'toAtomicLevel', package: 'Bio'})[0];
638
+ if (!func) throw new Error('To Atomic level Function not found');
639
+ func.prepare({table: seqCol.dataFrame, seqCol: seqCol}).edit();
640
+ }
641
+
633
642
  //top-menu: Bio | Analyze | MSA...
634
643
  //name: MSA
635
644
  //description: Performs multiple sequence alignment
@@ -743,38 +752,20 @@ export function importBam(fileContent: string): DG.DataFrame [] {
743
752
  return [];
744
753
  }
745
754
 
746
- //top-menu: Bio | Convert | Notation...
755
+ //top-menu: Bio | Transform | Convert Notation...
747
756
  //name: convertDialog
748
757
  export function convertDialog() {
749
758
  const col: DG.Column<string> | undefined = getMacromoleculeColumns()[0];
750
759
  convert(col, _package.seqHelper);
751
760
  }
752
761
 
753
- //top-menu: Bio | Convert | TestConvert
754
- //name: convertSeqNotation
755
- //description: RDKit-based conversion for SMILES, SMARTS, InChi, Molfile V2000 and Molfile V3000
756
- //input: string sequence {semType: Macromolecule}
757
- //input: string targetNotation
758
- //input: string separator
759
- //output: string result
760
- export async function convertSeqNotation(sequence: string, targetNotation: NOTATION, separator?: string): Promise<string | undefined | null> {
761
- try {
762
- const col = DG.Column.fromStrings('sequence', [sequence]);
763
- const _df = DG.DataFrame.fromColumns([col]);
764
- const semType = await grok.functions.call('Bio:detectMacromolecule', {col: col});
765
- if (semType)
766
- col.semType = semType;
767
- const converterSh = _package.seqHelper.getSeqHandler(col);
768
- const newColumn = converterSh.convert(targetNotation, separator);
769
- return newColumn.get(0);
770
- } catch (err: any) {
771
- const [errMsg, errStack] = errInfo(err);
772
- _package.logger.error(errMsg, undefined, errStack);
773
- throw err;
774
- }
762
+ //name: Convert Notation...
763
+ //input: column col {semType: Macromolecule}
764
+ //meta.action: Convert Notation...
765
+ export function convertColumnAction(col: DG.Column) {
766
+ convert(col, _package.seqHelper);
775
767
  }
776
768
 
777
-
778
769
  //name: monomerCellRenderer
779
770
  //tags: cellRenderer
780
771
  //meta.cellType: Monomer
@@ -835,7 +826,7 @@ export async function testDetectMacromolecule(path: string): Promise<DG.DataFram
835
826
  return resDf;
836
827
  }
837
828
 
838
- //top-menu: Bio | Convert | Split to Monomers...
829
+ //top-menu: Bio | Transform | Split to Monomers...
839
830
  //name: Split to Monomers
840
831
  //input: dataframe table
841
832
  //input: column sequence { semType: Macromolecule }
@@ -975,21 +966,14 @@ export async function manageLibrariesApp(): Promise<DG.View> {
975
966
 
976
967
  //name: Monomer Manager Tree Browser
977
968
  //input: dynamic treeNode
978
- //input: view browseView
979
- export async function manageLibrariesAppTreeBrowser(treeNode: DG.TreeViewGroup, browseView: DG.BrowseView) {
969
+ //input: dynamic browsePanel
970
+ export async function manageLibrariesAppTreeBrowser(treeNode: DG.TreeViewGroup, browsePanel: DG.BrowsePanel) {
980
971
  const libraries = (await (await MonomerLibManager.getInstance()).getFileManager()).getValidLibraryPaths();
981
972
  libraries.forEach((libName) => {
982
973
  const nodeName = libName.endsWith('.json') ? libName.substring(0, libName.length - 5) : libName;
983
974
  const libNode = treeNode.item(nodeName);
984
975
  // eslint-disable-next-line rxjs/no-ignored-subscription, rxjs/no-async-subscribe
985
976
  libNode.onSelected.subscribe(async () => {
986
- const monomerManager = await MonomerManager.getNewInstance();
987
- browseView.preview = await monomerManager.getViewRoot(libName, false);
988
- });
989
-
990
- libNode.root.addEventListener('dblclick', async (e) => {
991
- e.preventDefault();
992
- e.stopImmediatePropagation();
993
977
  const monomerManager = await MonomerManager.getInstance();
994
978
  await monomerManager.getViewRoot(libName, true);
995
979
  monomerManager.resetCurrentRowFollowing();
@@ -1111,10 +1095,9 @@ export function addCopyMenu(cell: DG.Cell, menu: DG.Menu): void {
1111
1095
  //meta.demoPath: Bioinformatics | Similarity, Diversity
1112
1096
  //description: Sequence similarity tracking and evaluation dataset diversity
1113
1097
  //meta.path: /apps/Tutorials/Demo/Bioinformatics/Similarity,%20Diversity
1114
- //meta.isDemoScript: True
1115
1098
  //meta.demoSkip: GROK-14320
1116
1099
  export async function demoBioSimilarityDiversity(): Promise<void> {
1117
- await demoBio01UI();
1100
+ await demoBioSimDiv();
1118
1101
  }
1119
1102
 
1120
1103
  // demoBio01a
@@ -1122,10 +1105,9 @@ export async function demoBioSimilarityDiversity(): Promise<void> {
1122
1105
  //meta.demoPath: Bioinformatics | Sequence Space
1123
1106
  //description: Exploring sequence space of Macromolecules, comparison with hierarchical clustering results
1124
1107
  //meta.path: /apps/Tutorials/Demo/Bioinformatics/Sequence%20Space
1125
- //meta.isDemoScript: True
1126
1108
  //meta.demoSkip: GROK-14320
1127
1109
  export async function demoBioSequenceSpace(): Promise<void> {
1128
- await demoBio01aUI();
1110
+ await demoSeqSpace();
1129
1111
  }
1130
1112
 
1131
1113
  // demoBio01b
@@ -1144,21 +1126,9 @@ export async function demoBioActivityCliffs(): Promise<void> {
1144
1126
  //meta.demoPath: Bioinformatics | Atomic Level
1145
1127
  //description: Atomic level structure of Macromolecules
1146
1128
  //meta.path: /apps/Tutorials/Demo/Bioinformatics/Atomic%20Level
1147
- //meta.isDemoScript: True
1148
1129
  //meta.demoSkip: GROK-14320
1149
1130
  export async function demoBioAtomicLevel(): Promise<void> {
1150
- await demoBio03UI();
1151
- }
1152
-
1153
- // demoBio05
1154
- //name: demoBioHelmMsaSequenceSpace
1155
- //meta.demoPath: Bioinformatics | Helm, MSA, Sequence Space
1156
- //description: MSA and composition analysis on Helm data
1157
- //meta.path: /apps/Tutorials/Demo/Bioinformatics/Helm,%20MSA,%20Sequence%20Space
1158
- //meta.isDemoScript: True
1159
- //meta.demoSkip: GROK-14320
1160
- export async function demoBioHelmMsaSequenceSpace(): Promise<void> {
1161
- await demoBio05UI();
1131
+ await demoToAtomicLevel();
1162
1132
  }
1163
1133
 
1164
1134
  //name: SDF to JSON Library
@@ -1,7 +1,7 @@
1
1
  import * as DG from 'datagrok-api/dg';
2
2
  import * as grok from 'datagrok-api/grok';
3
3
 
4
- import {expect} from '@datagrok-libraries/utils/src/test';
4
+ import {awaitCheck, expect} from '@datagrok-libraries/utils/src/test';
5
5
  import {MmDistanceFunctionsNames} from '@datagrok-libraries/ml/src/macromolecule-distance-functions';
6
6
  import {BitArrayMetrics} from '@datagrok-libraries/ml/src/typed-metrics';
7
7
  import {BYPASS_LARGE_DATA_WARNING} from '@datagrok-libraries/ml/src/functionEditors/consts';
@@ -25,9 +25,10 @@ export async function _testActivityCliffsOpen(df: DG.DataFrame, drMethod: DimRed
25
25
  })) as DG.Viewer | undefined;
26
26
  expect(scatterPlot != null, true);
27
27
 
28
- const cliffsLink = Array.from(scatterPlot!.root.children).find((el) => {
29
- const classList: string[] = el.className.split(' ');
30
- return ['ui-btn', 'ui-btn-ok'].every((reqClassName) => classList.includes(reqClassName));
31
- });
32
- expect((cliffsLink as HTMLElement).innerText.toLowerCase(), `${tgtNumberCliffs} cliffs`);
28
+ await awaitCheck(() => {
29
+ const link = Array.from(scatterPlot!.root.getElementsByClassName('scatter_plot_link'));
30
+ if (link.length)
31
+ return (link[0] as HTMLElement).innerText.toLowerCase() === `${tgtNumberCliffs} cliffs`;
32
+ return true;
33
+ }, 'incorrect cliffs link', 3000);
33
34
  }
@@ -21,8 +21,8 @@ category('Scoring', () => {
21
21
  /* eslint-disable max-len */
22
22
  const table = DG.DataFrame.fromCsv(`${sequence},${expectedSimilarity},${expectedIdentity}
23
23
  PEPTIDE1{Aca.Orn.gGlu.Pqa.D-His_1Bn.dH.hHis.4Abz.D-Tic.D-Dap.Y.Iva.meS.F.P.F.D-1Nal}$$$$,1.0,1.0
24
- PEPTIDE1{Iva.Gly_allyl.gGlu.Pqa.D-Dip.dH.hHis.4Abz.D-aHyp.D-Dap.Y.Iva.I.Tyr_26diMe.P.Asu.meC}$$$$,0.68,0.53
25
- PEPTIDE1{[1Nal].[1Nal].[1Nal].[1Nal].[1Nal].[1Nal].[1Nal].[1Nal].[1Nal].[1Nal].[1Nal].[1Nal].[1Nal].[1Nal].[1Nal].[1Nal].[1Nal]}$$$$V2.0,0.34,0.0`
24
+ PEPTIDE1{Iva.Gly_allyl.gGlu.Pqa.D-Dip.dH.hHis.4Abz.D-aHyp.D-Dap.Y.Iva.I.Tyr_26diMe.P.Asu.meC}$$$$,0.691,0.53
25
+ PEPTIDE1{[1Nal].[1Nal].[1Nal].[1Nal].[1Nal].[1Nal].[1Nal].[1Nal].[1Nal].[1Nal].[1Nal].[1Nal].[1Nal].[1Nal].[1Nal].[1Nal].[1Nal]}$$$$V2.0,0.37,0.0`
26
26
  );
27
27
  const seqCol: DG.Column<string> = table.getCol(sequence);
28
28
  seqCol.meta.units = NOTATION.HELM;
@@ -37,7 +37,7 @@ async function _testSimilaritySearchViewer() {
37
37
  await awaitCheck(() => getSearchViewer(moleculesView, 'Sequence Similarity Search') !== undefined,
38
38
  'Sequence Similarity Search viewer has not been created', 100);
39
39
  if (!viewer.initialized) throw new Error('The viewer is not initialized.');
40
- if (!viewer.moleculeColumn) throw new Error('The viewer has not molecule column (onTableAttached).');
40
+ if (!viewer.targetColumn) throw new Error('The viewer has not molecule column (onTableAttached).');
41
41
  if (!viewer.beforeRender()) throw new Error('The viewer is not able to render.');
42
42
  if (!viewer.computeRequested) throw new Error('The viewer has not compute requested even.');
43
43
  if (!computeCompleted) throw new Error('The viewer has not compute completed.');
@@ -87,7 +87,7 @@ async function _testDiversitySearchViewer() {
87
87
  await awaitCheck(() => getSearchViewer(moleculesView, 'Sequence Diversity Search') !== undefined,
88
88
  'Sequence Diversity Search viewer has not been created', 100);
89
89
  if (!viewer.initialized) throw new Error('The viewer is not initialized.');
90
- if (!viewer.moleculeColumn) throw new Error('The viewer has not molecule column (onTableAttached).');
90
+ if (!viewer.targetColumn) throw new Error('The viewer has not molecule column (onTableAttached).');
91
91
  if (!viewer.beforeRender()) throw new Error('The viewer is not able to render.');
92
92
  if (!viewer.computeRequested) throw new Error('The viewer has not compute requested even.');
93
93
  if (!computeCompleted) throw new Error('The viewer has not compute completed.');
@@ -58,7 +58,6 @@ export function processSequence(subParts: string[]): [string[], boolean] {
58
58
  type RendererGridCellTemp = {
59
59
  [MmcrTemps.monomerPlacer]: MonomerPlacer
60
60
  }
61
-
62
61
  export class MacromoleculeSequenceCellRenderer extends DG.GridCellRenderer {
63
62
  private readonly seqHelper: ISeqHelper;
64
63
 
@@ -70,6 +69,8 @@ export class MacromoleculeSequenceCellRenderer extends DG.GridCellRenderer {
70
69
 
71
70
  get defaultWidth(): number | null { return 230; }
72
71
 
72
+ hasMouseOver: boolean = false;
73
+
73
74
  constructor() {
74
75
  super();
75
76
  this.seqHelper = _package.seqHelper;
@@ -101,6 +102,7 @@ export class MacromoleculeSequenceCellRenderer extends DG.GridCellRenderer {
101
102
  override onMouseEnter(gridCell: DG.GridCell, e: MouseEvent) {
102
103
  const back = this.getRendererBack(gridCell);
103
104
  back?.onMouseEnter(gridCell, e);
105
+ this.hasMouseOver = true;
104
106
  }
105
107
 
106
108
  override onMouseMove(gridCell: DG.GridCell, e: MouseEvent): void {
@@ -110,6 +112,14 @@ export class MacromoleculeSequenceCellRenderer extends DG.GridCellRenderer {
110
112
 
111
113
  override onMouseLeave(gridCell: DG.GridCell, _e: MouseEvent) {
112
114
  execMonomerHoverLinks(gridCell, null);
115
+ if (gridCell?.grid) {
116
+ const sub = gridCell.grid.onEvent('d4-grid-show-tooltip').subscribe((e) => {
117
+ sub.unsubscribe();
118
+ if (this.hasMouseOver)
119
+ e.preventDefault();
120
+ });
121
+ }
122
+ this.hasMouseOver = false;
113
123
  }
114
124
 
115
125
  override onDoubleClick(gridCell: DG.GridCell, e: MouseEvent) {
@@ -4,9 +4,8 @@ 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, IMonomerLibBase} from '@datagrok-libraries/bio/src/types/index';
8
- import {IMonomerLibHelper} from '@datagrok-libraries/bio/src/monomer-works/monomer-utils';
9
- import {getHelmHelper, IHelmHelper} from '@datagrok-libraries/bio/src/helm/helm-helper';
7
+ import {IMonomerLibBase} from '@datagrok-libraries/bio/src/types/index';
8
+ import {IHelmHelper} from '@datagrok-libraries/bio/src/helm/helm-helper';
10
9
  import {MolfileWithMap, MonomerMap} from '@datagrok-libraries/bio/src/monomer-works/types';
11
10
 
12
11
  import {Polymer} from './polymer';
@@ -14,9 +13,10 @@ import {GlobalMonomerPositionHandler} from './position-handler';
14
13
 
15
14
  import {_package} from '../../../package';
16
15
  import {getUnusedColName} from '@datagrok-libraries/bio/src/monomer-works/utils';
16
+ import {IHelmToMolfileConverter} from '@datagrok-libraries/bio/src/utils/seq-helper';
17
17
 
18
18
 
19
- export class HelmToMolfileConverter {
19
+ export class HelmToMolfileConverter implements IHelmToMolfileConverter {
20
20
  constructor(
21
21
  private readonly helmHelper: IHelmHelper,
22
22
  private readonly rdKitModule: RDModule,
@@ -41,6 +41,20 @@ export class HelmToMolfileConverter {
41
41
  return smiles;
42
42
  }
43
43
 
44
+ public molV3KtoMolV3KOCL(molV3k: string): string {
45
+ try {
46
+ if (!molV3k)
47
+ return '';
48
+ const oclMolecule = OCL.Molecule.fromMolfile(molV3k);
49
+ const molV3000 = oclMolecule.toMolfileV3();
50
+ return molV3000.replace('STERAC1', 'STEABS');
51
+ } catch (err) {
52
+ const [errMsg, errStack] = errInfo(err);
53
+ _package.logger.error(errMsg, undefined, errStack);
54
+ return '';
55
+ }
56
+ }
57
+
44
58
  public getMolV3000ViaOCL(beautifiedMols: (RDMol | null)[], columnName: string): DG.Column<string> {
45
59
  const beautifiedMolV2000 = beautifiedMols.map((mol) => {
46
60
  if (mol === null)
@@ -52,9 +66,7 @@ export class HelmToMolfileConverter {
52
66
  const molv3000Arr = new Array<string>(beautifiedMolV2000.length);
53
67
  const chiralityPb = DG.TaskBarProgressIndicator.create(`Handling chirality...`);
54
68
  for (let i = 0; i < beautifiedMolV2000.length; i++) {
55
- const oclMolecule = OCL.Molecule.fromMolfile(beautifiedMolV2000[i]);
56
- const molV3000 = oclMolecule.toMolfileV3();
57
- molv3000Arr[i] = molV3000.replace('STERAC1', 'STEABS');
69
+ molv3000Arr[i] = this.molV3KtoMolV3KOCL(beautifiedMolV2000[i]);
58
70
  const progress = i / beautifiedMolV2000.length * 100;
59
71
  chiralityPb.update(progress, `${progress?.toFixed(2)}% of molecules completed`);
60
72
  }
@@ -136,9 +148,9 @@ export class HelmToMolfileConverter {
136
148
  const woGapsRes = this.helmHelper.removeGaps(helm);
137
149
  const woGapsHelm = woGapsRes.resHelm;
138
150
  const woGapsReverseMap = new Map<number, number>();
139
- for (const [orgPosIdx, woGapsPosIdx] of (woGapsRes.monomerMap?.entries() ?? [])) {
151
+ for (const [orgPosIdx, woGapsPosIdx] of (woGapsRes.monomerMap?.entries() ?? []))
140
152
  woGapsReverseMap.set(woGapsPosIdx, orgPosIdx);
141
- }
153
+
142
154
  const pseudoMolfile = this.helmHelper.getMolfiles([woGapsHelm])[0];
143
155
  const globalPositionHandler = new GlobalMonomerPositionHandler(pseudoMolfile);
144
156
  const woGapsPolymer = new Polymer(woGapsHelm, this.rdKitModule, this.monomerLib);
@@ -10,7 +10,7 @@ import {MolfileWrapperFactory} from './mol-wrapper-factory';
10
10
  export class MonomerWrapper {
11
11
  private readonly molfileWrapper: MolfileWrapper;
12
12
  private capGroupElements: string[] = [];
13
-
13
+ private static molfileV2KToV3KCache: Map<string, string> = new Map();
14
14
  constructor(
15
15
  public readonly monomerSymbol: string,
16
16
  public readonly monomerIdx: number,
@@ -39,12 +39,16 @@ export class MonomerWrapper {
39
39
  public get bondCount() { return this.molfileWrapper.bondCount; }
40
40
 
41
41
  private convertMolfileToV3KFormat(molfileV2K: string, monomerSymbol: string, rdKitModule: RDModule): string {
42
+ if (MonomerWrapper.molfileV2KToV3KCache.has(molfileV2K))
43
+ return MonomerWrapper.molfileV2KToV3KCache.get(molfileV2K)!;
42
44
  let mol: RDMol | null = null;
43
45
  try {
44
46
  mol = rdKitModule.get_mol(molfileV2K, JSON.stringify({mergeQueryHs: true}));
45
- if (mol)
46
- return mol.get_v3Kmolblock();
47
- else
47
+ if (mol) {
48
+ const res = mol.get_v3Kmolblock();
49
+ MonomerWrapper.molfileV2KToV3KCache.set(molfileV2K, res);
50
+ return res;
51
+ } else
48
52
  throw new Error(`Cannot convert ${monomerSymbol} to molV3000`);
49
53
  } finally {
50
54
  mol?.delete();
@@ -1,3 +1,4 @@
1
+ /* eslint-disable max-len */
1
2
  import {HELM_MONOMER_TYPE, HELM_POLYMER_TYPE} from '@datagrok-libraries/bio/src/utils/const';
2
3
  import {cleanupHelmSymbol} from '@datagrok-libraries/bio/src/helm/utils';
3
4
 
@@ -5,8 +6,10 @@ import {Bond} from './types';
5
6
 
6
7
  /** Wrapper over simple polymer substring of HELM, like RNA1{d(A)p} */
7
8
  export class SimplePolymer {
9
+ private isNucleotideSequence = false;
8
10
  constructor(private simplePolymer: string) {
9
11
  this.polymerType = this.getPolymerType();
12
+ this.isNucleotideSequence = this.polymerType === HELM_POLYMER_TYPE.RNA;
10
13
  this.idx = this.getIdx();
11
14
  const {monomers, monomerTypes} = this.getMonomerSymbolsAndTypes();
12
15
  this.monomers = monomers;
@@ -49,17 +52,23 @@ export class SimplePolymer {
49
52
  const monomerList: string[] = [];
50
53
  const monomerTypeList: HELM_MONOMER_TYPE[] = [];
51
54
  monomerGroups.forEach((monomerGroup) => {
52
- // const splitted = monomerGroup.split(/\(|\)/).map((el) => el.replace(/[\[\]]/g, ''));
53
- // monomerList.push(...splitted);
54
- // WARNING: only the groups of the form r(A)p, as in RNA, are supported
55
+ if (!this.isNucleotideSequence) {
56
+ // const splitted = monomerGroup.split(/\(|\)/).map((el) => el.replace(/[\[\]]/g, ''));
57
+ // monomerList.push(...splitted);
58
+ // WARNING: only the groups of the form r(A)p, as in RNA, are supported
55
59
 
56
- monomerList.push(cleanupHelmSymbol(monomerGroup));
57
- // const monomerTypes = splitted.map(
58
- // (_, idx) => (idx % 2 === 0) ? HELM_MONOMER_TYPE.BACKBONE : HELM_MONOMER_TYPE.BRANCH
59
- // );
60
+ monomerList.push(cleanupHelmSymbol(monomerGroup));
61
+ // const monomerTypes = splitted.map(
62
+ // (_, idx) => (idx % 2 === 0) ? HELM_MONOMER_TYPE.BACKBONE : HELM_MONOMER_TYPE.BRANCH
63
+ // );
60
64
 
61
- // monomerTypeList.push(...monomerTypes);
62
- monomerTypeList.push(HELM_MONOMER_TYPE.BACKBONE);
65
+ // monomerTypeList.push(...monomerTypes);
66
+ monomerTypeList.push(HELM_MONOMER_TYPE.BACKBONE);
67
+ } else {
68
+ const splitted = monomerGroup.split(/\(|\)/).filter((el) => !!el).map((el) => cleanupHelmSymbol(el));
69
+ monomerList.push(...splitted);
70
+ splitted.forEach((_, i) => monomerTypeList.push(i % 3 === 1 ? HELM_MONOMER_TYPE.BRANCH : HELM_MONOMER_TYPE.BACKBONE));
71
+ }
63
72
  });
64
73
  return {monomers: monomerList, monomerTypes: monomerTypeList};
65
74
  }
@@ -177,13 +177,13 @@ export class MonomerLibBase implements IMonomerLibBase {
177
177
  // Symbol & Name
178
178
  const symbol = monomer[REQ.SYMBOL];
179
179
  const _name = monomer[REQ.NAME];
180
-
181
- const htmlColor = wem.backgroundcolor;
180
+ const [color, backgroundColor, lineColor] =
181
+ wem ? [wem.textcolor, wem.backgroundcolor, wem.linecolor] : ['#202020', '#A0A0A0', '#202020'];
182
182
  res.append(ui.divH([
183
183
  ui.div([symbol], {
184
184
  style: {
185
185
  /* fontWeight: 'bolder', */ textWrap: 'nowrap', marginLeft: '4px', marginRight: '4px',
186
- color: wem.textcolor, backgroundColor: wem.backgroundcolor, borderColor: wem.linecolor,
186
+ color: color, backgroundColor: backgroundColor, borderColor: lineColor,
187
187
  borderWidth: '1px', borderStyle: 'solid', borderRadius: '2px', padding: '3px',
188
188
  minWidth: '24px', textAlign: 'center',
189
189
  }
@@ -192,7 +192,7 @@ export class MonomerLibBase implements IMonomerLibBase {
192
192
  ], {style: {display: 'flex', flexDirection: 'row', justifyContent: 'left'}}));
193
193
 
194
194
  // Structure
195
- const chemOptions = {autoCrop: true, autoCropMargin: 0, suppressChiralText: true};
195
+ //const chemOptions = {autoCrop: true, autoCropMargin: 0, suppressChiralText: true};
196
196
  let structureEl: HTMLElement;
197
197
  if (monomer.molfile)
198
198
  structureEl = drawMoleculeCall(monomer.molfile);
@@ -7,7 +7,7 @@ import {Subject} from 'rxjs';
7
7
 
8
8
  import {testEvent} from '@datagrok-libraries/utils/src/test';
9
9
  import {NOTATION, TAGS as bioTAGS, ALIGNMENT, ALPHABET} from '@datagrok-libraries/bio/src/utils/macromolecule';
10
- import { fetchWrapper } from '@datagrok-libraries/utils/src/fetch-utils';
10
+ import {fetchWrapper} from '@datagrok-libraries/utils/src/fetch-utils';
11
11
  import {ILogger} from '@datagrok-libraries/bio/src/utils/logger';
12
12
 
13
13
  import {checkForSingleSeqClusters} from './multiple-sequence-alignment';
@@ -108,50 +108,33 @@ async function requestAlignedObjects(
108
108
  body: JSON.stringify(body),
109
109
  };
110
110
  const path = `/align?method=${method}&gap_open=${gapOpen}&gap_extend=${gapExtend}`;
111
- let responseObj: any;
112
- if ('fetchProxy' in grok.dapi.docker.dockerContainers) {
113
- // new dockerContainers API
114
- const t1: number = window.performance.now();
115
- // @ts-ignore
116
- const response: Response = await grok.dapi.docker.dockerContainers.fetchProxy(dockerfileId, path, params);
117
- const t2: number = window.performance.now();
118
- _package.logger.debug(`Bio: requestAlignedObjects() dockerContainers.fetchProxy(), ET: ${(t2 - t1)} ms`);
119
- const responseContentType = response.headers.get('content-type');
120
- const isJson: boolean = responseContentType === 'application/json';
121
- if (!response.ok && isJson) {
122
- const responseJson = await response.json();
123
- const pepseaErrorMsg = responseJson['pepsea-error'];
124
- if (!!pepseaErrorMsg)
125
- throw new Error(`PepSeA error: ${pepseaErrorMsg}`);
126
-
127
- const datagrokErrorMsg = responseJson['datagrok-error'];
128
- if (!!datagrokErrorMsg)
129
- throw new Error(`Datagrok error: ${datagrokErrorMsg}`);
130
-
131
- throw new Error(response.statusText);
132
- } else if (!response.ok && !isJson) {
133
- const responseStr = await response.text();
134
- throw new Error(`Error: ${responseStr}`);
135
- } else if (!isJson) {
136
- const responseStr = await response.text();
137
- throw new Error(`Error: PepSeA expected JSON response, got '${responseStr}'.`);
138
- }
139
- responseObj = await response.json();
140
- } else {
141
- // @ts-ignore
142
- const responseStr = await grok.dapi.docker.dockerContainers.request(dockerfileId, path, params)!;
143
- if (!responseStr)
144
- throw new Error('Empty response');
145
- responseObj = JSON.parse(responseStr);
146
-
147
- const pepseaErrorMsg = responseObj['pepsea-error'];
111
+ // new dockerContainers API
112
+ const t1: number = window.performance.now();
113
+ // @ts-ignore
114
+ const response: Response = await grok.dapi.docker.dockerContainers.fetchProxy(dockerfileId, path, params);
115
+ const t2: number = window.performance.now();
116
+ _package.logger.debug(`Bio: requestAlignedObjects() dockerContainers.fetchProxy(), ET: ${(t2 - t1)} ms`);
117
+ const responseContentType = response.headers.get('content-type');
118
+ const isJson: boolean = responseContentType === 'application/json';
119
+ if (!response.ok && isJson) {
120
+ const responseJson = await response.json();
121
+ const pepseaErrorMsg = responseJson['pepsea-error'];
148
122
  if (!!pepseaErrorMsg)
149
123
  throw new Error(`PepSeA error: ${pepseaErrorMsg}`);
150
124
 
151
- const datagrokErrorMsg = responseObj['datagrok-error'];
125
+ const datagrokErrorMsg = responseJson['datagrok-error'];
152
126
  if (!!datagrokErrorMsg)
153
127
  throw new Error(`Datagrok error: ${datagrokErrorMsg}`);
128
+
129
+ throw new Error(response.statusText);
130
+ } else if (!response.ok && !isJson) {
131
+ const responseStr = await response.text();
132
+ throw new Error(`Error: ${responseStr}`);
133
+ } else if (!isJson) {
134
+ const responseStr = await response.text();
135
+ throw new Error(`Error: PepSeA expected JSON response, got '${responseStr}'.`);
154
136
  }
137
+ const responseObj = await response.json();
155
138
  // Check for pepsea stderr output
156
139
  if ('pepsea-stderr' in responseObj) {
157
140
  const pepseaStdErr: string = responseObj['pepsea-stderr'] as string;
@@ -605,12 +605,12 @@ export class SeqHandler implements ISeqHandler {
605
605
 
606
606
  const isNucleotide = srcSeq.startsWith('RNA');
607
607
  // items can be monomers or helms
608
- const helmItemsArray = this.splitter(srcSeq);
608
+ const helmItemsArray = splitterAsHelm(srcSeq);
609
609
  const tgtMonomersArray: string[] = [];
610
610
  for (let posIdx = 0; posIdx < helmItemsArray.length; ++posIdx) {
611
611
  let om: string = helmItemsArray.getOriginal(posIdx);
612
612
  if (isNucleotide)
613
- om = om.replace(HELM_WRAPPERS_REGEXP, '');
613
+ om = om.replace(HELM_WRAPPERS_REGEXP, '$1');
614
614
  if (om === GapOriginals[NOTATION.HELM])
615
615
  tgtMonomersArray.push(tgtGapOriginal);
616
616
  else if (this.toFasta(tgtNotation as NOTATION) && om.length > 1) {