@datagrok/bio 2.11.30 → 2.11.33

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 (53) hide show
  1. package/CHANGELOG.md +13 -0
  2. package/dist/36.js +1 -1
  3. package/dist/36.js.map +1 -1
  4. package/dist/42.js +1 -1
  5. package/dist/42.js.map +1 -1
  6. package/dist/590.js +2 -0
  7. package/dist/590.js.map +1 -0
  8. package/dist/709.js +1 -2
  9. package/dist/709.js.map +1 -1
  10. package/dist/79.js.map +1 -1
  11. package/dist/895.js +3 -0
  12. package/dist/895.js.map +1 -0
  13. package/dist/package-test.js +8 -1
  14. package/dist/package-test.js.LICENSE.txt +1 -0
  15. package/dist/package-test.js.map +1 -1
  16. package/dist/package.js +8 -1
  17. package/dist/package.js.LICENSE.txt +1 -0
  18. package/dist/package.js.map +1 -1
  19. package/files/{data → monomer-libraries}/HELMCoreLibrary.json +594 -594
  20. package/files/tests/libraries/HELMmonomerSchema.json +96 -0
  21. package/package.json +12 -10
  22. package/scripts/sequence_generator.md +48 -0
  23. package/scripts/sequence_generator.py +515 -256
  24. package/src/package-test.ts +4 -0
  25. package/src/package.ts +26 -24
  26. package/src/tests/WebLogo-layout-tests.ts +37 -0
  27. package/src/tests/WebLogo-positions-test.ts +5 -0
  28. package/src/tests/WebLogo-project-tests.ts +63 -0
  29. package/src/tests/activity-cliffs-tests.ts +3 -2
  30. package/src/tests/monomer-libraries-tests.ts +7 -4
  31. package/src/tests/scoring.ts +3 -2
  32. package/src/tests/substructure-filters-tests.ts +3 -2
  33. package/src/tests/to-atomic-level-tests.ts +3 -2
  34. package/src/utils/helm-to-molfile.ts +3 -3
  35. package/src/utils/monomer-lib/lib-manager.ts +116 -0
  36. package/src/utils/monomer-lib/library-file-manager/consts.ts +1 -0
  37. package/src/utils/monomer-lib/library-file-manager/custom-monomer-lib-handlers.ts +80 -0
  38. package/src/utils/monomer-lib/library-file-manager/event-manager.ts +58 -0
  39. package/src/utils/monomer-lib/library-file-manager/file-manager.ts +187 -0
  40. package/src/utils/monomer-lib/library-file-manager/file-validator.ts +56 -0
  41. package/src/utils/monomer-lib/library-file-manager/style.css +8 -0
  42. package/src/utils/monomer-lib/library-file-manager/ui.ts +224 -0
  43. package/src/utils/monomer-lib/monomer-lib.ts +114 -0
  44. package/src/utils/poly-tool/const.ts +28 -0
  45. package/src/utils/poly-tool/monomer-lib-handler.ts +115 -0
  46. package/src/utils/poly-tool/types.ts +6 -0
  47. package/src/utils/poly-tool/ui.ts +2 -2
  48. package/src/viewers/vd-regions-viewer.ts +5 -4
  49. package/src/viewers/web-logo-viewer.ts +6 -5
  50. package/src/widgets/bio-substructure-filter.ts +4 -1
  51. package/files/libraries/HELMCoreLibrary.json +0 -18218
  52. package/src/utils/monomer-lib.ts +0 -305
  53. /package/dist/{709.js.LICENSE.txt → 895.js.LICENSE.txt} +0 -0
@@ -1,3 +1,5 @@
1
+ import * as grok from 'datagrok-api/grok';
2
+ import * as ui from 'datagrok-api/ui';
1
3
  import * as DG from 'datagrok-api/dg';
2
4
 
3
5
  import {runTests, TestContext, tests} from '@datagrok-libraries/utils/src/test';
@@ -17,6 +19,8 @@ import './tests/fasta-handler-test';
17
19
  import './tests/fasta-export-tests';
18
20
  import './tests/bio-tests';
19
21
  import './tests/WebLogo-positions-test';
22
+ import './tests/WebLogo-project-tests';
23
+ import './tests/WebLogo-layout-tests';
20
24
  import './tests/checkInputColumn-tests';
21
25
  import './tests/similarity-diversity-tests';
22
26
  import './tests/substructure-filters-tests';
package/src/package.ts CHANGED
@@ -41,10 +41,8 @@ import {getMacromoleculeColumnPropertyPanel} from './widgets/representations';
41
41
  import {saveAsFastaUI} from './utils/save-as-fasta';
42
42
  import {BioSubstructureFilter} from './widgets/bio-substructure-filter';
43
43
  import {WebLogoViewer} from './viewers/web-logo-viewer';
44
- import {
45
- MonomerLibHelper,
46
- getLibraryPanelUI
47
- } from './utils/monomer-lib';
44
+ import {MonomerLibManager} from './utils/monomer-lib/lib-manager';
45
+ import {getMonomerLibraryManagerLink, showManageLibrariesDialog} from './utils/monomer-lib/library-file-manager/ui';
48
46
  import {demoBio01UI} from './demo/bio01-similarity-diversity';
49
47
  import {demoBio01aUI} from './demo/bio01a-hierarchical-clustering-and-sequence-space';
50
48
  import {demoBio01bUI} from './demo/bio01b-hierarchical-clustering-and-activity-cliffs';
@@ -88,7 +86,7 @@ export const _package = new BioPackage();
88
86
  //description:
89
87
  //output: object result
90
88
  export function getMonomerLibHelper(): IMonomerLibHelper {
91
- return MonomerLibHelper.instance;
89
+ return MonomerLibManager.instance;
92
90
  }
93
91
 
94
92
  export let hydrophobPalette: SeqPaletteCustom | null = null;
@@ -110,7 +108,7 @@ export async function initBio() {
110
108
  _package.logger.debug('Bio: initBio(), started');
111
109
  const module = await grok.functions.call('Chem:getRdKitModule');
112
110
  await Promise.all([
113
- (async () => { await MonomerLibHelper.instance.loadLibraries(); })(),
111
+ (async () => { await MonomerLibManager.instance.loadLibraries(); })(),
114
112
  (async () => {
115
113
  const pkgProps = await _package.getProperties();
116
114
  const bioPkgProps = new BioPackageProperties(pkgProps);
@@ -120,7 +118,7 @@ export async function initBio() {
120
118
  _package.completeInit();
121
119
  });
122
120
 
123
- const monomerLib = MonomerLibHelper.instance.getBioLib();
121
+ const monomerLib = MonomerLibManager.instance.getBioLib();
124
122
  const monomers: string[] = [];
125
123
  const logPs: number[] = [];
126
124
 
@@ -163,12 +161,19 @@ export function sequenceTooltip(col: DG.Column): DG.Widget<any> {
163
161
  //name: getBioLib
164
162
  //output: object monomerLib
165
163
  export function getBioLib(): IMonomerLib {
166
- return MonomerLibHelper.instance.getBioLib();
164
+ return MonomerLibManager.instance.getBioLib();
165
+ }
166
+
167
+ //name: getUnitsHandler
168
+ //input: column sequence { semType: Macromolecule }
169
+ //output: object result
170
+ export function getUnitsHandler(sequence: DG.Column<string>): UnitsHandler {
171
+ return UnitsHandler.getOrCreate(sequence);
167
172
  }
168
173
 
169
174
  // -- Panels --
170
175
 
171
- //name: Get Region
176
+ //name: Bioinformatics | Get Region
172
177
  //description: Creates a new column with sequences of the region between start and end
173
178
  //tags: panel
174
179
  //input: column seqCol {semType: Macromolecule}
@@ -183,13 +188,14 @@ export function getRegionPanel(seqCol: DG.Column<string>): DG.Widget {
183
188
  return funcEditor.widget();
184
189
  }
185
190
 
186
- //name: Manage Libraries
191
+ //name: Bioinformatics | Manage Monomer Libraries
187
192
  //description:
188
193
  //tags: panel, exclude-actions-panel
189
194
  //input: column seqColumn {semType: Macromolecule}
190
195
  //output: widget result
191
196
  export async function libraryPanel(_seqColumn: DG.Column): Promise<DG.Widget> {
192
- return getLibraryPanelUI();
197
+ // return getLibraryPanelUI();
198
+ return getMonomerLibraryManagerLink();
193
199
  }
194
200
 
195
201
  // -- Func Editors --
@@ -293,7 +299,7 @@ export function fastaSequenceCellRenderer(): MacromoleculeSequenceCellRenderer {
293
299
 
294
300
  // -- Property panels --
295
301
 
296
- //name: Sequence Renderer
302
+ //name: Bioinformatics | Sequence Renderer
297
303
  //input: column molColumn {semType: Macromolecule}
298
304
  //tags: panel
299
305
  //output: widget result
@@ -650,18 +656,6 @@ export async function compositionAnalysis(): Promise<void> {
650
656
  await handler(col);
651
657
  }
652
658
 
653
- // 2023-05-17 Representations does not work at BioIT
654
- // //name: Representations
655
- // //tags: panel, widgets
656
- // //input: cell macroMolecule {semType: Macromolecule}
657
- // //output: widget result
658
- // export async function peptideMolecule(macroMolecule: DG.Cell): Promise<DG.Widget> {
659
- // const monomersLibFile = await _package.files.readAsText(HELM_CORE_LIB_FILENAME);
660
- // const monomersLibObject: any[] = JSON.parse(monomersLibFile);
661
- //
662
- // return representationsWidget(macroMolecule, monomersLibObject);
663
- // }
664
-
665
659
  //name: importFasta
666
660
  //description: Opens FASTA file
667
661
  //tags: file-handler
@@ -871,6 +865,14 @@ export async function sequenceSimilarityScoring(
871
865
  return scores;
872
866
  }
873
867
 
868
+
869
+ //top-menu: Bio | Manage | Monomer Libraries
870
+ //name: Manage Monomer Libraries
871
+ //description: Manage HELM monomer libraries
872
+ export async function manageMonomerLibraries(): Promise<void> {
873
+ showManageLibrariesDialog();
874
+ }
875
+
874
876
  //name: saveAsFasta
875
877
  //description: As FASTA...
876
878
  //tags: fileExporter
@@ -0,0 +1,37 @@
1
+ import * as DG from 'datagrok-api/dg';
2
+ import * as grok from 'datagrok-api/grok';
3
+ import * as ui from 'datagrok-api/ui';
4
+
5
+ import wu from 'wu';
6
+
7
+ import {category, expect, test, testViewer} from '@datagrok-libraries/utils/src/test';
8
+
9
+ import {awaitGrid} from './utils';
10
+ import {WebLogoViewer} from '../viewers/web-logo-viewer';
11
+
12
+ import {_package} from '../package-test';
13
+
14
+ category('WebLogo-layout', () => {
15
+ test('fasta', async () => {
16
+ const df = await _package.files.readCsv('tests/filter_FASTA.csv');
17
+ const col = df.getCol('fasta');
18
+ await grok.data.detectSemanticTypes(df);
19
+ const view = grok.shell.addTableView(df);
20
+ const wlViewer = await df.plot.fromType('WebLogo',
21
+ {sequenceColumnName: col.name}) as unknown as WebLogoViewer;
22
+ view.dockManager.dock(wlViewer);
23
+ await wlViewer.awaitRendered();
24
+ await awaitGrid(view.grid);
25
+
26
+ const viewLayout = view.saveLayout();
27
+ const viewLayoutJsonStr = viewLayout.toJson();
28
+ view.loadLayout(viewLayout);
29
+ await wlViewer.awaitRendered();
30
+ await awaitGrid(view.grid);
31
+
32
+ const viewersA = wu(view.viewers).toArray();
33
+ expect(viewersA.length, 2 /* Grid and WebLogo */);
34
+ expect(viewersA.filter((f) => f.type === 'Grid').length, 1);
35
+ expect(viewersA.filter((f) => f.type === 'WebLogo').length, 1);
36
+ });
37
+ });
@@ -57,6 +57,7 @@ ATC-G-TTGC--
57
57
  for (const m of positions[i].getMonomers())
58
58
  expect(positions[i].getFreq(m).rowCount, resAllDf1[i].getFreq(m).rowCount);
59
59
  }
60
+ await wlViewer.awaitRendered();
60
61
  });
61
62
 
62
63
  test('positions with shrinkEmptyTail option true (filtered)', async () => {
@@ -106,6 +107,7 @@ ATC-G-TTGC--
106
107
  for (const m of positions[i].getMonomers())
107
108
  expect(positions[i].getFreq(m).rowCount, resAllDf1[i].getFreq(m).rowCount);
108
109
  }
110
+ await wlViewer.awaitRendered();
109
111
  });
110
112
 
111
113
  test('positions with skipEmptyPositions option', async () => {
@@ -143,6 +145,7 @@ ATC-G-TTGC--
143
145
  const tgtPos = tgtPosList[posI];
144
146
  expectPositionInfo(resPos, tgtPos);
145
147
  }
148
+ await wlViewer.awaitRendered();
146
149
  });
147
150
 
148
151
  test('count sequences for monomer at position', async () => {
@@ -178,6 +181,7 @@ ATC-G-TTGC--
178
181
  const uh = UnitsHandler.getOrCreate(seqCol);
179
182
  const countAt1 = countForMonomerAtPosition(df, uh, df.filter, 'G', atPI1);
180
183
  expect(countAt1, 5);
184
+ await wlViewer.awaitRendered();
181
185
  });
182
186
 
183
187
  test('empty', async () => {
@@ -196,6 +200,7 @@ ATC-G-TTGC--
196
200
  tv.dockManager.dock(wlViewer.root, DG.DOCK_TYPE.DOWN);
197
201
  }, 500);
198
202
  const resPosList: PI[] = wlViewer['positions'];
203
+ await wlViewer.awaitRendered();
199
204
  });
200
205
  });
201
206
 
@@ -0,0 +1,63 @@
1
+ import * as DG from 'datagrok-api/dg';
2
+ import * as grok from 'datagrok-api/grok';
3
+ import * as ui from 'datagrok-api/ui';
4
+
5
+ import wu from 'wu';
6
+
7
+ import {category, delay, expect, test, testViewer} from '@datagrok-libraries/utils/src/test';
8
+
9
+ import {awaitGrid} from './utils';
10
+ import {WebLogoViewer} from '../viewers/web-logo-viewer';
11
+
12
+ import {_package} from '../package-test';
13
+
14
+ const PROJECT_PREFIX: string = 'Tests.Bio.WebLogo-project';
15
+
16
+ category('WebLogo-project', () => {
17
+ test('fasta', async () => {
18
+ const prjName = `${PROJECT_PREFIX}.fasta`;
19
+ const df = await _package.files.readCsv('tests/filter_FASTA.csv');
20
+ const tableName = df.name;
21
+ const col = df.getCol('fasta');
22
+ await grok.data.detectSemanticTypes(df);
23
+ const view = grok.shell.addTableView(df);
24
+ const wlViewer = await df.plot.fromType('WebLogo',
25
+ {sequenceColumnName: col.name}) as unknown as WebLogoViewer;
26
+ view.dockManager.dock(wlViewer);
27
+ await wlViewer.awaitRendered();
28
+ await awaitGrid(view.grid);
29
+
30
+ await uploadProject(prjName, df.getTableInfo(), view, df);
31
+ grok.shell.closeAll();
32
+ await delay(500);
33
+
34
+ const prj2 = await grok.dapi.projects.open(prjName);
35
+ const view2 = grok.shell.getTableView(tableName);
36
+
37
+ const viewersA = wu(view2.viewers).toArray();
38
+ expect(viewersA.length, 2);
39
+ expect(viewersA.filter((f) => f.type === 'Grid').length, 1);
40
+ const wlViewer2 = viewersA.find((f) => f.type === 'WebLogo') as WebLogoViewer;
41
+ expect(!!wlViewer2, true);
42
+
43
+ await awaitGrid(view.grid);
44
+ await wlViewer2.awaitRendered();
45
+ // TODO: Check WebLogo viewer content
46
+ }, {skipReason: 'depends on 1.18'});
47
+ });
48
+
49
+ export async function uploadProject(projectName: string, tableInfo: DG.TableInfo,
50
+ view: DG.TableView, df: DG.DataFrame): Promise<void> {
51
+ const project = DG.Project.create();
52
+ const viewLayout = view.saveLayout();
53
+
54
+ await grok.dapi.layouts.save(view.saveLayout());
55
+ await grok.dapi.tables.uploadDataFrame(df);
56
+ await grok.dapi.tables.save(tableInfo);
57
+
58
+ project.name = projectName;
59
+ project.addChild(tableInfo);
60
+ project.addChild(viewLayout); // cause error
61
+
62
+ await grok.dapi.projects.save(project);
63
+ }
@@ -10,8 +10,9 @@ import {MmDistanceFunctionsNames} from '@datagrok-libraries/ml/src/macromolecule
10
10
  import {BitArrayMetricsNames} from '@datagrok-libraries/ml/src/typed-metrics';
11
11
  import {getMonomerLibHelper, IMonomerLibHelper} from '@datagrok-libraries/bio/src/monomer-works/monomer-utils';
12
12
  import {
13
- getUserLibSettings, LibSettings, setUserLibSettings, setUserLibSettingsForTests
13
+ getUserLibSettings, setUserLibSettings, setUserLibSettingsForTests
14
14
  } from '@datagrok-libraries/bio/src/monomer-works/lib-settings';
15
+ import {UserLibSettings} from '@datagrok-libraries/bio/src/monomer-works/types';
15
16
 
16
17
  import {_package} from '../package-test';
17
18
  import {DimReductionMethods} from '@datagrok-libraries/ml/src/multi-column-dimensionality-reduction/types';
@@ -23,7 +24,7 @@ category('activityCliffs', async () => {
23
24
 
24
25
  let monomerLibHelper: IMonomerLibHelper;
25
26
  /** Backup actual user's monomer libraries settings */
26
- let userLibSettings: LibSettings;
27
+ let userLibSettings: UserLibSettings;
27
28
  const seqEncodingFunc = DG.Func.find({name: 'macromoleculePreprocessingFunction', package: 'Bio'})[0];
28
29
  const helmEncodingFunc = DG.Func.find({name: 'helmPreprocessingFunction', package: 'Bio'})[0];
29
30
  before(async () => {
@@ -6,9 +6,10 @@ import {test, after, before, category, expect} from '@datagrok-libraries/utils/s
6
6
 
7
7
  import {getMonomerLibHelper, IMonomerLibHelper} from '@datagrok-libraries/bio/src/monomer-works/monomer-utils';
8
8
  import {
9
- getUserLibSettings, LibSettings, setUserLibSettings, setUserLibSettingsForTests
9
+ getUserLibSettings, setUserLibSettings, setUserLibSettingsForTests
10
10
  } from '@datagrok-libraries/bio/src/monomer-works/lib-settings';
11
- import {getLibFileNameList} from '../utils/monomer-lib';
11
+ import {MonomerLibFileManager} from '../utils/monomer-lib/library-file-manager/file-manager';
12
+ import {MonomerLibFileEventManager} from '../utils/monomer-lib/library-file-manager/event-manager';
12
13
 
13
14
 
14
15
  category('monomerLibraries', () => {
@@ -50,7 +51,9 @@ category('monomerLibraries', () => {
50
51
  test('empty', async () => {
51
52
  // exclude all monomer libraries for empty set
52
53
  const libSettings = await getUserLibSettings();
53
- const libFnList = await getLibFileNameList();
54
+ const libFileEventManager = MonomerLibFileEventManager.getInstance();
55
+ const libFileManager = await MonomerLibFileManager.getInstance(libFileEventManager);
56
+ const libFnList = libFileManager.getValidLibraryPaths();
54
57
  libSettings.exclude = libFnList;
55
58
  libSettings.explicit = [];
56
59
  await setUserLibSettings(libSettings);
@@ -59,5 +62,5 @@ category('monomerLibraries', () => {
59
62
  const currentMonomerLib = monomerLibHelper.getBioLib();
60
63
  expect(currentMonomerLib.getPolymerTypes().length === 0, true);
61
64
  const monomerOfTypesList = currentMonomerLib.getMonomerMolsByPolymerType('PEPTIDE');
62
- });
65
+ }, {skipReason: '#2690'});
63
66
  });
@@ -8,8 +8,9 @@ import {getMonomerLibHelper, IMonomerLibHelper} from '@datagrok-libraries/bio/sr
8
8
 
9
9
  import {sequenceIdentityScoring, sequenceSimilarityScoring} from '../package';
10
10
  import {
11
- LibSettings, getUserLibSettings, setUserLibSettings, setUserLibSettingsForTests
11
+ getUserLibSettings, setUserLibSettings, setUserLibSettingsForTests
12
12
  } from '@datagrok-libraries/bio/src/monomer-works/lib-settings';
13
+ import {UserLibSettings} from '@datagrok-libraries/bio/src/monomer-works/types';
13
14
 
14
15
  category('Scoring', () => {
15
16
  const sequence = 'sequence';
@@ -29,7 +30,7 @@ PEPTIDE1{[1Nal].[1Nal].[1Nal].[1Nal].[1Nal].[1Nal].[1Nal].[1Nal].[1Nal].[1Nal].[
29
30
 
30
31
  let monomerLibHelper: IMonomerLibHelper;
31
32
  /** Backup actual user's monomer libraries settings */
32
- let userLibSettings: LibSettings;
33
+ let userLibSettings: UserLibSettings;
33
34
 
34
35
  before(async () => {
35
36
  monomerLibHelper = await getMonomerLibHelper();
@@ -5,8 +5,9 @@ import * as DG from 'datagrok-api/dg';
5
5
  import {after, before, category, test, expect, delay, testEvent, awaitCheck} from '@datagrok-libraries/utils/src/test';
6
6
  import {getMonomerLibHelper, IMonomerLibHelper} from '@datagrok-libraries/bio/src/monomer-works/monomer-utils';
7
7
  import {
8
- LibSettings, getUserLibSettings, setUserLibSettings, setUserLibSettingsForTests
8
+ getUserLibSettings, setUserLibSettings, setUserLibSettingsForTests
9
9
  } from '@datagrok-libraries/bio/src/monomer-works/lib-settings';
10
+ import {UserLibSettings} from '@datagrok-libraries/bio/src/monomer-works/types';
10
11
 
11
12
  import {awaitGrid, readDataframe} from './utils';
12
13
  import {
@@ -21,7 +22,7 @@ import {_package} from '../package-test';
21
22
  category('substructureFilters', async () => {
22
23
  let monomerLibHelper: IMonomerLibHelper;
23
24
  /** Backup actual user's monomer libraries settings */
24
- let userLibSettings: LibSettings;
25
+ let userLibSettings: UserLibSettings;
25
26
 
26
27
  before(async () => {
27
28
  monomerLibHelper = await getMonomerLibHelper();
@@ -11,8 +11,9 @@ import {IMonomerLib} from '@datagrok-libraries/bio/src/types/index';
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 {
14
- getUserLibSettings, LibSettings, setUserLibSettings, setUserLibSettingsForTests
14
+ getUserLibSettings, setUserLibSettings, setUserLibSettingsForTests
15
15
  } from '@datagrok-libraries/bio/src/monomer-works/lib-settings';
16
+ import {UserLibSettings} from '@datagrok-libraries/bio/src/monomer-works/types';
16
17
  import {UnitsHandler} from '@datagrok-libraries/bio/src/utils/units-handler';
17
18
 
18
19
  import {toAtomicLevel} from '../package';
@@ -56,7 +57,7 @@ category('toAtomicLevel', async () => {
56
57
 
57
58
  let monomerLibHelper: IMonomerLibHelper;
58
59
  /** Backup actual user's monomer libraries settings */
59
- let userLibSettings: LibSettings;
60
+ let userLibSettings: UserLibSettings;
60
61
 
61
62
  before(async () => {
62
63
  monomerLibHelper = await getMonomerLibHelper();
@@ -5,11 +5,11 @@ import * as DG from 'datagrok-api/dg';
5
5
 
6
6
  import {MolfileHandler} from '@datagrok-libraries/chem-meta/src/parsing-utils/molfile-handler';
7
7
  import {MolfileHandlerBase} from '@datagrok-libraries/chem-meta/src/parsing-utils/molfile-handler-base';
8
- import {RDMol, RDModule} from '@datagrok-libraries/chem-meta/src/rdkit-api';
8
+ import {RDModule} from '@datagrok-libraries/chem-meta/src/rdkit-api';
9
9
  import {HELM_POLYMER_TYPE, HELM_RGROUP_FIELDS} from '@datagrok-libraries/bio/src/utils/const';
10
10
  import {errInfo} from '@datagrok-libraries/bio/src/utils/err-info';
11
11
 
12
- import {MonomerLibHelper} from './monomer-lib';
12
+ import {MonomerLibManager} from './monomer-lib/lib-manager';
13
13
 
14
14
  import {_package} from '../package';
15
15
 
@@ -173,7 +173,7 @@ class MonomerWrapper {
173
173
  monomerSymbol: string,
174
174
  polymerType: HELM_POLYMER_TYPE,
175
175
  ) {
176
- const monomerLib = MonomerLibHelper.instance.getBioLib();
176
+ const monomerLib = MonomerLibManager.instance.getBioLib();
177
177
  const monomer = monomerLib.getMonomer(polymerType, monomerSymbol);
178
178
  if (!monomer)
179
179
  throw new Error(`Monomer ${monomerSymbol} is not found in the library`);
@@ -0,0 +1,116 @@
1
+ /* Do not change these import lines to match external modules in webpack configuration */
2
+ import * as grok from 'datagrok-api/grok';
3
+ import * as ui from 'datagrok-api/ui';
4
+ import * as DG from 'datagrok-api/dg';
5
+
6
+ import {IMonomerLib} from '@datagrok-libraries/bio/src/types/index';
7
+ import {
8
+ getUserLibSettings, setUserLibSettings, LIB_PATH
9
+ } from '@datagrok-libraries/bio/src/monomer-works/lib-settings';
10
+ import {UserLibSettings} from '@datagrok-libraries/bio/src/monomer-works/types';
11
+ import {
12
+ IMonomerLibHelper,
13
+ } from '@datagrok-libraries/bio/src/monomer-works/monomer-utils';
14
+ import {MonomerLib} from './monomer-lib';
15
+ import {MonomerLibFileManager} from './library-file-manager/file-manager';
16
+ import {MonomerLibFileEventManager} from './library-file-manager/event-manager';
17
+ import {_package} from '../../package';
18
+
19
+ type MonomerLibWindowType = Window & { $monomerLibHelper: MonomerLibManager };
20
+ declare const window: MonomerLibWindowType;
21
+
22
+ export async function getLibFileNameList(): Promise<string[]> {
23
+ const fileEventManager = MonomerLibFileEventManager.getInstance();
24
+ const fileManager = await MonomerLibFileManager.getInstance(fileEventManager);
25
+ return fileManager.getValidLibraryPaths();
26
+ }
27
+
28
+ /** Singleton wrapper for MonomerLib, provides API for managing libraries on
29
+ * the platform */
30
+ export class MonomerLibManager implements IMonomerLibHelper {
31
+ private readonly _monomerLib = new MonomerLib({});
32
+
33
+ /** Protect constructor to prevent multiple instantiation. */
34
+ protected constructor() {}
35
+
36
+ /** Singleton monomer library
37
+ * @return {MonomerLibManager} MonomerLibHelper instance
38
+ */
39
+ getBioLib(): IMonomerLib {
40
+ return this._monomerLib;
41
+ }
42
+
43
+ /** Allows syncing with managing settings/loading libraries */
44
+ public loadLibrariesPromise: Promise<void> = Promise.resolve();
45
+
46
+ /** Loads libraries based on settings in user storage {@link LIB_STORAGE_NAME}
47
+ * @param {boolean} reload Clean {@link monomerLib} before load libraries [false]
48
+ */
49
+ async loadLibraries(reload: boolean = false): Promise<void> {
50
+ return this.loadLibrariesPromise = this.loadLibrariesPromise.then(async () => {
51
+ // WARNING: This function is not allowed to throw any exception,
52
+ // because it will prevent further handling monomer library settings
53
+ // through blocking this.loadLibrariesPromise
54
+ try {
55
+ const [libFileNameList, settings]: [string[], UserLibSettings] = await Promise.all([
56
+ getLibFileNameList(),
57
+ getUserLibSettings(),
58
+ ]);
59
+ const filteredLibFnList = libFileNameList
60
+ .filter((libFileName) => !settings.exclude.includes(libFileName))
61
+ .filter((libFileName) => settings.explicit.length > 0 ? settings.explicit.includes(libFileName) : true);
62
+ const libs: IMonomerLib[] = await Promise.all(filteredLibFnList
63
+ .map((libFileName) => {
64
+ //TODO handle whether files are in place
65
+ return this.readLibrary(LIB_PATH, libFileName).catch((err: any) => {
66
+ const errMsg: string = `Loading monomers from '${libFileName}' error: ` +
67
+ `${err instanceof Error ? err.message : err.toString()}`;
68
+ return new MonomerLib({}, errMsg);
69
+ });
70
+ }));
71
+ this._monomerLib.updateLibs(libs, reload);
72
+ } catch (err: any) {
73
+ const errMsg: string = 'Loading monomer libraries error: ' +
74
+ `${err instanceof Error ? err.message : err.toString()}`;
75
+ grok.shell.warning(errMsg);
76
+
77
+ const errStack = err instanceof Error ? err.stack : undefined;
78
+ _package.logger.error(errMsg, undefined, errStack);
79
+ }
80
+ });
81
+ }
82
+
83
+ /** Reads library from file shares, handles .json and .sdf
84
+ * @param {string} path Path to library file
85
+ * @param {string} fileName Name of library file
86
+ * @return {Promise<IMonomerLib>} Promise of IMonomerLib
87
+ */
88
+ async readLibrary(path: string, fileName: string): Promise<IMonomerLib> {
89
+ const eventManager = MonomerLibFileEventManager.getInstance();
90
+ const libFileManager = await MonomerLibFileManager.getInstance(eventManager);
91
+ const lib: IMonomerLib = await libFileManager.loadLibraryFromFile(path, fileName);
92
+ return lib;
93
+ }
94
+
95
+ /** Reset user settings to the specified library. WARNING: clears user * settings */
96
+ public async selectSpecifiedLibraries(libFileNameList: string[]): Promise<void> {
97
+ const invalidNames = await this.getInvalidFileNames(libFileNameList);
98
+ if (invalidNames.length > 0)
99
+ throw new Error(`Cannot select libraries ${invalidNames}: no such library in the list`);
100
+ const settings = await getUserLibSettings();
101
+ settings.exclude = (await getLibFileNameList()).filter((fileName) => !libFileNameList.includes(fileName));
102
+ await setUserLibSettings(settings);
103
+ }
104
+
105
+ private async getInvalidFileNames(libFileNameList: string[]): Promise<string[]> {
106
+ const availableFileNames = await getLibFileNameList();
107
+ const invalidNames = libFileNameList.filter((fileName) => !availableFileNames.includes(fileName));
108
+ return invalidNames;
109
+ }
110
+
111
+ // -- Instance singleton --
112
+ public static get instance(): MonomerLibManager {
113
+ if (!window.$monomerLibHelper) window.$monomerLibHelper = new MonomerLibManager();
114
+ return window.$monomerLibHelper;
115
+ }
116
+ }
@@ -0,0 +1 @@
1
+ export const HELM_JSON_SCHEMA_PATH = 'System:AppData/Bio/tests/libraries/HELMmonomerSchema.json';
@@ -0,0 +1,80 @@
1
+ /* Do not change these import lines to match external modules in webpack configuration */
2
+ import * as grok from 'datagrok-api/grok';
3
+ import * as ui from 'datagrok-api/ui';
4
+ import * as DG from 'datagrok-api/dg';
5
+
6
+ import {createJsonMonomerLibFromSdf} from '@datagrok-libraries/bio/src/monomer-works/monomer-utils';
7
+ import {PolyToolMonomerLibHandler} from '../../poly-tool/monomer-lib-handler';
8
+
9
+ interface CustomMonomerLibHandler {
10
+ getHelmLibContent(): Promise<string>;
11
+ }
12
+
13
+ /** Casts specific SDF files as HELM-standard monomer libraries */
14
+ export class SdfMonomerLibHandler implements CustomMonomerLibHandler {
15
+ constructor(private fileName: string, private fileContent: string) {
16
+ this.validateChemInstallation();
17
+ this.validate();
18
+ }
19
+
20
+ async getHelmLibContent(): Promise<string> {
21
+ const bytes = this.transformToBytes();
22
+ const dfSdf = await grok.functions.call('Chem:importSdf', {bytes});
23
+ const jsonContent = createJsonMonomerLibFromSdf(dfSdf[0]);
24
+ return JSON.stringify(jsonContent);
25
+ }
26
+
27
+ private transformToBytes(): Uint8Array {
28
+ return new TextEncoder().encode(this.fileContent);
29
+ }
30
+
31
+ private validateChemInstallation(): void {
32
+ const funcList: DG.Func[] = DG.Func.find({package: 'Chem', name: 'importSdf'});
33
+ if (funcList.length === 1)
34
+ return;
35
+ throw new Error('MonomerLibFileManager: Chem package is not installed, cannot convert SDF to JSON');
36
+ }
37
+
38
+ private validate(): void {
39
+ if (!this.fileName.endsWith('.sdf'))
40
+ throw new Error(`File ${this.fileName} is not an SDF file`);
41
+ }
42
+ }
43
+
44
+
45
+ /** Handler of custom monomer libs for PolyTool */
46
+ export class PolyToolCsvLibHandler implements CustomMonomerLibHandler {
47
+ constructor(private fileName: string, private fileContent: string) {
48
+ this.validateFileType();
49
+ const df = DG.DataFrame.fromCsv(this.fileContent);
50
+ const json = this.toJson(df);
51
+ this.polyToolMonomerLib = new PolyToolMonomerLibHandler(json);
52
+ this.validateContent();
53
+ }
54
+
55
+ private polyToolMonomerLib: PolyToolMonomerLibHandler;
56
+
57
+ async getHelmLibContent(): Promise<string> {
58
+ const rawLibData = this.polyToolMonomerLib.getJsonMonomerLib();
59
+ return JSON.stringify(rawLibData);
60
+ }
61
+
62
+ private toJson(df: DG.DataFrame): any[] {
63
+ return Array.from({length: df.rowCount}, (_, idx) =>
64
+ df.columns.names().reduce((entry: { [key: string]: any }, colName) => {
65
+ entry[colName] = df.get(colName, idx);
66
+ return entry;
67
+ }, {})
68
+ );
69
+ }
70
+
71
+ private validateFileType(): void {
72
+ if (!this.fileName.endsWith('.csv'))
73
+ throw new Error(`File ${this.fileName} is not an CSV file`);
74
+ }
75
+
76
+ private validateContent(): void {
77
+ if (!this.polyToolMonomerLib.isValid())
78
+ throw new Error('Invalid format of CSV monomer lib');
79
+ }
80
+ }
@@ -0,0 +1,58 @@
1
+ import * as rxjs from 'rxjs';
2
+ import {debounceTime, tap} from 'rxjs/operators';
3
+
4
+ export class MonomerLibFileEventManager {
5
+ // WARNING: this must be a singleton because it manages the unique state
6
+ private constructor() {}
7
+
8
+ private static _instance: MonomerLibFileEventManager;
9
+
10
+ static getInstance(): MonomerLibFileEventManager {
11
+ if (!MonomerLibFileEventManager._instance)
12
+ MonomerLibFileEventManager._instance = new MonomerLibFileEventManager();
13
+
14
+ return MonomerLibFileEventManager._instance;
15
+ }
16
+
17
+ private _libraryFilesUpdateSubject$ = new rxjs.BehaviorSubject<string[]>([]);
18
+ private _addLibraryFilesSubject$ = new rxjs.Subject<void>();
19
+ private _librarySelectionSubject$ = new rxjs.Subject<[string, boolean]>();
20
+
21
+ getValidFilesPathList(): string[] {
22
+ return this._libraryFilesUpdateSubject$.getValue();
23
+ }
24
+
25
+ changeValidFilesPathList(newList: string[]): void {
26
+ this._libraryFilesUpdateSubject$.next(newList);
27
+ }
28
+
29
+ get updateUIControlsRequested$(): rxjs.Observable<string[]> {
30
+ return this._libraryFilesUpdateSubject$.pipe(
31
+ // debounceTime(1000)
32
+ );
33
+ }
34
+
35
+ get updateValidLibraryFileListRequested$(): rxjs.Observable<string[]> {
36
+ return this._libraryFilesUpdateSubject$.pipe(
37
+ // debounceTime(3000)
38
+ );
39
+ }
40
+
41
+ get addLibraryFileRequested$(): rxjs.Observable<void> {
42
+ return this._addLibraryFilesSubject$.pipe(
43
+ // debounceTime(1000)
44
+ );
45
+ }
46
+
47
+ addLibraryFile(): void {
48
+ this._addLibraryFilesSubject$.next();
49
+ }
50
+
51
+ get librarySelectionRequested$(): rxjs.Observable<[string, boolean]> {
52
+ return this._librarySelectionSubject$;
53
+ }
54
+
55
+ updateLibrarySelectionStatus(fileName: string, isSelected: boolean): void {
56
+ this._librarySelectionSubject$.next([fileName, isSelected]);
57
+ }
58
+ }