@datagrok/bio 2.17.4 → 2.17.6
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +9 -0
- package/detectors.js +4 -1
- package/dist/package-test.js +1 -1
- package/dist/package-test.js.map +1 -1
- package/dist/package.js +1 -1
- package/dist/package.js.map +1 -1
- package/files/icons/monomers.png +0 -0
- package/files/tests/fasta_negative_words.csv +6 -0
- package/package.json +1 -1
- package/src/package.ts +53 -33
- package/src/tests/detectors-tests.ts +8 -0
- package/src/utils/monomer-lib/lib-manager.ts +6 -2
- package/src/utils/monomer-lib/library-file-manager/file-manager.ts +13 -1
- package/src/utils/monomer-lib/library-file-manager/ui.ts +31 -12
- package/src/utils/monomer-lib/monomer-manager/monomer-manager.ts +41 -15
|
Binary file
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
model,mpg,cyl,disp,hp,drat,wt,qsec,vs,am,gear,carb
|
|
2
|
+
MAINTAINED,21.0,6,160,110,3.90,2.620,16.46,0,1,4,4
|
|
3
|
+
WORSENED,21.0,6,160,110,3.90,2.875,17.02,0,1,4,4
|
|
4
|
+
MAINTAINED,22.8,4,108,93,3.85,2.320,18.61,1,1,4,1
|
|
5
|
+
IMPROVED,21.4,6,258,110,3.08,3.215,19.44,1,0,3,1
|
|
6
|
+
WORSENED,18.7,8,360,175,3.15,3.440,17.02,0,0,3,2
|
package/package.json
CHANGED
|
@@ -5,7 +5,7 @@
|
|
|
5
5
|
"name": "Leonid Stolbov",
|
|
6
6
|
"email": "lstolbov@datagrok.ai"
|
|
7
7
|
},
|
|
8
|
-
"version": "2.17.
|
|
8
|
+
"version": "2.17.6",
|
|
9
9
|
"description": "Bioinformatics support (import/export of sequences, conversion, visualization, analysis). [See more](https://github.com/datagrok-ai/public/blob/master/packages/Bio/README.md) for details.",
|
|
10
10
|
"repository": {
|
|
11
11
|
"type": "git",
|
package/src/package.ts
CHANGED
|
@@ -85,7 +85,7 @@ export async function getMonomerLibHelper(): Promise<IMonomerLibHelper> {
|
|
|
85
85
|
return await MonomerLibManager.getInstance();
|
|
86
86
|
}
|
|
87
87
|
|
|
88
|
-
export let hydrophobPalette: SeqPaletteCustom | null = null;
|
|
88
|
+
//export let hydrophobPalette: SeqPaletteCustom | null = null;
|
|
89
89
|
|
|
90
90
|
export class SeqPaletteCustom implements SeqPalette {
|
|
91
91
|
private readonly _palette: { [m: string]: string };
|
|
@@ -128,39 +128,42 @@ async function initBioInt() {
|
|
|
128
128
|
libSettings.explicit = [];
|
|
129
129
|
await setUserLibSettings(libSettings);
|
|
130
130
|
}
|
|
131
|
-
libHelper.awaitLoaded(Infinity)
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
131
|
+
await libHelper.awaitLoaded(Infinity);
|
|
132
|
+
if (!libHelper.initialLoadCompleted)
|
|
133
|
+
await libHelper.loadMonomerLib();
|
|
134
|
+
// Do not wait for monomers and sets loaded
|
|
135
|
+
libHelper.loadMonomerSets();
|
|
135
136
|
const monomerLib = libHelper.getMonomerLib();
|
|
136
137
|
const monomerSets = libHelper.getMonomerSets();
|
|
137
138
|
// finally log
|
|
138
139
|
const t2: number = window.performance.now();
|
|
139
140
|
_package.logger.debug(`${logPrefix}, loading ET: ${t2 - t1} ms`);
|
|
140
141
|
|
|
141
|
-
const monomers: string[] = [];
|
|
142
|
-
const logPs: number[] = [];
|
|
142
|
+
// const monomers: string[] = [];
|
|
143
|
+
// const logPs: number[] = [];
|
|
143
144
|
|
|
144
145
|
const seqHelper = new SeqHelper(libHelper, rdKitModule);
|
|
145
146
|
_package.completeInit(seqHelper, monomerLib, monomerSets, rdKitModule);
|
|
146
|
-
const series = monomerLib!.getMonomerMolsByPolymerType('PEPTIDE')!;
|
|
147
|
-
Object.keys(series).forEach((symbol) => {
|
|
148
|
-
monomers.push(symbol);
|
|
149
|
-
const block = series[symbol].replaceAll('#R', 'O ');
|
|
150
|
-
const mol = rdKitModule.get_mol(block);
|
|
151
|
-
const logP = JSON.parse(mol.get_descriptors()).CrippenClogP;
|
|
152
|
-
logPs.push(logP);
|
|
153
|
-
mol?.delete();
|
|
154
|
-
});
|
|
155
147
|
|
|
156
|
-
|
|
157
|
-
const
|
|
148
|
+
// NB! do not delete the code below. not used now but in future we might use hydrophobicity palette
|
|
149
|
+
// const series = monomerLib!.getMonomerMolsByPolymerType('PEPTIDE')!;
|
|
150
|
+
// Object.keys(series).forEach((symbol) => {
|
|
151
|
+
// monomers.push(symbol);
|
|
152
|
+
// const block = series[symbol].replaceAll('#R', 'O ');
|
|
153
|
+
// const mol = rdKitModule.get_mol(block);
|
|
154
|
+
// const logP = JSON.parse(mol.get_descriptors()).CrippenClogP;
|
|
155
|
+
// logPs.push(logP);
|
|
156
|
+
// mol?.delete();
|
|
157
|
+
// });
|
|
158
|
+
|
|
159
|
+
// const sum = logPs.reduce((a, b) => a + b, 0);
|
|
160
|
+
// const avg = (sum / logPs.length) || 0;
|
|
158
161
|
|
|
159
|
-
const palette: { [monomer: string]: string } = {};
|
|
160
|
-
for (let i = 0; i < monomers.length; i++)
|
|
161
|
-
|
|
162
|
+
// const palette: { [monomer: string]: string } = {};
|
|
163
|
+
// for (let i = 0; i < monomers.length; i++)
|
|
164
|
+
// palette[monomers[i]] = logPs[i] < avg ? '#4682B4' : '#DC143C';
|
|
162
165
|
|
|
163
|
-
hydrophobPalette = new SeqPaletteCustom(palette);
|
|
166
|
+
// hydrophobPalette = new SeqPaletteCustom(palette);
|
|
164
167
|
|
|
165
168
|
_package.logger.debug(`${logPrefix}, end`);
|
|
166
169
|
}
|
|
@@ -961,19 +964,36 @@ export async function manageMonomersView() {
|
|
|
961
964
|
await monomerManager.getViewRoot();
|
|
962
965
|
}
|
|
963
966
|
|
|
964
|
-
//name:
|
|
967
|
+
//name: Monomers
|
|
965
968
|
//tags: app
|
|
966
|
-
//meta.browsePath: Peptides
|
|
967
|
-
|
|
968
|
-
|
|
969
|
-
|
|
970
|
-
|
|
969
|
+
//meta.browsePath: Peptides
|
|
970
|
+
//meta.icon: files/icons/monomers.png
|
|
971
|
+
//output: view v
|
|
972
|
+
export async function manageLibrariesApp(): Promise<DG.View> {
|
|
973
|
+
return await showManageLibrariesView(false);
|
|
974
|
+
}
|
|
975
|
+
|
|
976
|
+
//name: Monomer Manager Tree Browser
|
|
977
|
+
//input: dynamic treeNode
|
|
978
|
+
//input: view browseView
|
|
979
|
+
export async function manageLibrariesAppTreeBrowser(treeNode: DG.TreeViewGroup, browseView: DG.BrowseView) {
|
|
980
|
+
const libraries = (await (await MonomerLibManager.getInstance()).getFileManager()).getValidLibraryPaths();
|
|
981
|
+
libraries.forEach((libName) => {
|
|
982
|
+
const libNode = treeNode.item(libName);
|
|
983
|
+
// eslint-disable-next-line rxjs/no-ignored-subscription, rxjs/no-async-subscribe
|
|
984
|
+
libNode.onSelected.subscribe(async () => {
|
|
985
|
+
const monomerManager = await MonomerManager.getNewInstance();
|
|
986
|
+
browseView.preview = await monomerManager.getViewRoot(libName, false);
|
|
987
|
+
});
|
|
971
988
|
|
|
972
|
-
|
|
973
|
-
|
|
974
|
-
|
|
975
|
-
|
|
976
|
-
|
|
989
|
+
libNode.root.addEventListener('dblclick', async (e) => {
|
|
990
|
+
e.preventDefault();
|
|
991
|
+
e.stopImmediatePropagation();
|
|
992
|
+
const monomerManager = await MonomerManager.getInstance();
|
|
993
|
+
await monomerManager.getViewRoot(libName, true);
|
|
994
|
+
monomerManager.resetCurrentRowFollowing();
|
|
995
|
+
});
|
|
996
|
+
});
|
|
977
997
|
}
|
|
978
998
|
|
|
979
999
|
//name: saveAsFasta
|
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
/* eslint-disable max-lines */
|
|
2
|
+
/* eslint-disable max-lines-per-function */
|
|
1
3
|
import * as grok from 'datagrok-api/grok';
|
|
2
4
|
import * as ui from 'datagrok-api/ui';
|
|
3
5
|
import * as DG from 'datagrok-api/dg';
|
|
@@ -271,6 +273,7 @@ MWRSWY-CKHPMWRSWY-CKHP`;
|
|
|
271
273
|
testSpgi = 'testSpgi',
|
|
272
274
|
testSpgi100 = 'testSpgi100',
|
|
273
275
|
testUrl = 'testUrl',
|
|
276
|
+
fastaNegativeWords = 'fasta_negative_words'
|
|
274
277
|
}
|
|
275
278
|
|
|
276
279
|
const samples: { [key: string]: string } = {
|
|
@@ -295,6 +298,7 @@ MWRSWY-CKHPMWRSWY-CKHP`;
|
|
|
295
298
|
[Samples.testSpgi100]: 'System:AppData/Bio/tests/testSpgi100.csv',
|
|
296
299
|
[Samples.testSpgi]: 'System:AppData/Bio/tests/SPGI-derived.csv',
|
|
297
300
|
[Samples.testUrl]: 'System:AppData/Bio/tests/testUrl.csv',
|
|
301
|
+
[Samples.fastaNegativeWords]: 'System:AppData/Bio/tests/fasta_negative_words.csv',
|
|
298
302
|
};
|
|
299
303
|
|
|
300
304
|
const _samplesDfs: { [key: string]: Promise<DG.DataFrame> } = {};
|
|
@@ -505,6 +509,10 @@ MWRSWY-CKHPMWRSWY-CKHP`;
|
|
|
505
509
|
test('samplesTestUrl', async () => {
|
|
506
510
|
await _testDf(readSamples(Samples.testUrl), {} /* no positive */, seqHelper);
|
|
507
511
|
});
|
|
512
|
+
|
|
513
|
+
test('samplesFastaNegativeWords', async () => {
|
|
514
|
+
await _testDf(readSamples(Samples.fastaNegativeWords), {} /* no positive */, seqHelper);
|
|
515
|
+
});
|
|
508
516
|
});
|
|
509
517
|
|
|
510
518
|
export async function _testNegList(list: string[]): Promise<void> {
|
|
@@ -30,7 +30,8 @@ declare const window: MonomerLibWindowType;
|
|
|
30
30
|
export class MonomerLibManager implements IMonomerLibHelper {
|
|
31
31
|
private readonly _monomerLib = new MonomerLib({}, 'MAIN');
|
|
32
32
|
private readonly _monomerSets = new MonomerSet('MAIN', []);
|
|
33
|
-
|
|
33
|
+
private _initialLoadCompleted: boolean = false;
|
|
34
|
+
public get initialLoadCompleted(): boolean { return this._initialLoadCompleted; }
|
|
34
35
|
private _eventManager: MonomerLibFileEventManager;
|
|
35
36
|
|
|
36
37
|
public get eventManager(): IMonomerLibFileEventManager { return this._eventManager; }
|
|
@@ -85,7 +86,8 @@ export class MonomerLibManager implements IMonomerLibHelper {
|
|
|
85
86
|
return this._monomerSets;
|
|
86
87
|
}
|
|
87
88
|
|
|
88
|
-
/** Object containing symbols for each type of polymer where duplicate monomers
|
|
89
|
+
/** Object containing symbols for each type of polymer where duplicate monomers
|
|
90
|
+
* are found in different libs (based on symbol as key) */
|
|
89
91
|
get duplicateMonomers() {
|
|
90
92
|
return this._monomerLib.duplicateMonomers;
|
|
91
93
|
}
|
|
@@ -107,6 +109,7 @@ export class MonomerLibManager implements IMonomerLibHelper {
|
|
|
107
109
|
this._fileManagerPromise = (async () => {
|
|
108
110
|
const fileManager: MonomerLibFileManager =
|
|
109
111
|
await MonomerLibFileManager.create(this, this._eventManager, this.logger);
|
|
112
|
+
await fileManager.initializedPromise;
|
|
110
113
|
return fileManager;
|
|
111
114
|
})();
|
|
112
115
|
}
|
|
@@ -158,6 +161,7 @@ export class MonomerLibManager implements IMonomerLibHelper {
|
|
|
158
161
|
});
|
|
159
162
|
})),]);
|
|
160
163
|
this._monomerLib.updateLibs(libs, reload);
|
|
164
|
+
this._initialLoadCompleted = true;
|
|
161
165
|
} catch (err: any) {
|
|
162
166
|
// WARNING: This function is not allowed to throw any exception,
|
|
163
167
|
// because it will prevent further handling monomer library settings
|
|
@@ -28,6 +28,7 @@ import {_package} from '../../../package';
|
|
|
28
28
|
* All files **must** be aligned to the HELM standard before adding. */
|
|
29
29
|
export class MonomerLibFileManager implements IMonomerLibFileManager {
|
|
30
30
|
public filesPromise: Promise<void> = Promise.resolve();
|
|
31
|
+
public initializedPromise: Promise<void> = Promise.resolve();
|
|
31
32
|
|
|
32
33
|
private constructor(
|
|
33
34
|
private readonly fileValidator: MonomerLibFileValidator,
|
|
@@ -35,8 +36,19 @@ export class MonomerLibFileManager implements IMonomerLibFileManager {
|
|
|
35
36
|
public readonly eventManager: MonomerLibFileEventManager,
|
|
36
37
|
private readonly logger: ILogger,
|
|
37
38
|
) {
|
|
39
|
+
// these both are behavioral subjects, i.e. they emit their value when subscribed.
|
|
40
|
+
// initial creation/request from bio package on awaiting files promise makes no sense,
|
|
41
|
+
// until the subscription fires first time
|
|
42
|
+
let resolveFilesPromise: () => void;
|
|
43
|
+
let initialized = false;
|
|
44
|
+
this.initializedPromise =
|
|
45
|
+
Promise.race([DG.delay(1000), new Promise<void>((resolve) => resolveFilesPromise = resolve)]);
|
|
38
46
|
const _libSub = this.eventManager.updateValidLibraryFileListRequested$.subscribe(() => {
|
|
39
47
|
this.updateValidLibList().then(() => {});
|
|
48
|
+
if (!initialized) {
|
|
49
|
+
initialized = true;
|
|
50
|
+
resolveFilesPromise();
|
|
51
|
+
}
|
|
40
52
|
});
|
|
41
53
|
const _setSub = this.eventManager.updateValidSetFileListRequested$.subscribe(() => {
|
|
42
54
|
this.updateValidSetList().then(() => {});
|
|
@@ -181,7 +193,7 @@ export class MonomerLibFileManager implements IMonomerLibFileManager {
|
|
|
181
193
|
|
|
182
194
|
if (this.libListHasChanged(validLibPathList)) {
|
|
183
195
|
this.eventManager.changeValidLibPathList(validLibPathList);
|
|
184
|
-
this.libHelper.loadMonomerLib(true);
|
|
196
|
+
await this.libHelper.loadMonomerLib(true);
|
|
185
197
|
}
|
|
186
198
|
// console.log(`files after validation:`, this.libraryEventManager.getValidFilesPathList());
|
|
187
199
|
|
|
@@ -24,8 +24,8 @@ export async function showManageLibrariesDialog(): Promise<void> {
|
|
|
24
24
|
await DialogWrapper.showDialog();
|
|
25
25
|
}
|
|
26
26
|
|
|
27
|
-
export async function showManageLibrariesView() {
|
|
28
|
-
await LibManagerView.showView();
|
|
27
|
+
export async function showManageLibrariesView(addView = true) {
|
|
28
|
+
return await LibManagerView.showView(addView);
|
|
29
29
|
}
|
|
30
30
|
|
|
31
31
|
export async function getMonomerLibraryManagerLink(): Promise<DG.Widget> {
|
|
@@ -68,8 +68,9 @@ class MonomerLibraryManagerWidget {
|
|
|
68
68
|
private async createWidget() {
|
|
69
69
|
const content = await this.getWidgetContent();
|
|
70
70
|
const monomerLibHelper = await getMonomerLibHelper();
|
|
71
|
+
// eslint-disable-next-line rxjs/no-ignored-subscription
|
|
71
72
|
monomerLibHelper.eventManager.addLibraryFileRequested$.subscribe(
|
|
72
|
-
|
|
73
|
+
() => this.promptToAddLibraryFiles()
|
|
73
74
|
);
|
|
74
75
|
return new DG.Widget(content);
|
|
75
76
|
}
|
|
@@ -109,11 +110,13 @@ class LibraryControlsManager {
|
|
|
109
110
|
private fileManager: IMonomerLibFileManager,
|
|
110
111
|
private readonly userLibSettings: UserLibSettings,
|
|
111
112
|
) {
|
|
113
|
+
// eslint-disable-next-line rxjs/no-ignored-subscription
|
|
112
114
|
this.fileManager.eventManager.updateUIControlsRequested$.subscribe(() => {
|
|
113
115
|
this.updateControlsForm();
|
|
114
116
|
});
|
|
115
|
-
|
|
116
|
-
|
|
117
|
+
// eslint-disable-next-line rxjs/no-ignored-subscription
|
|
118
|
+
this.fileManager.eventManager.librarySelectionRequested$.subscribe(([fileName, isSelected]) => {
|
|
119
|
+
this.updateLibrarySelectionStatus(isSelected, fileName);
|
|
117
120
|
});
|
|
118
121
|
}
|
|
119
122
|
|
|
@@ -222,6 +225,7 @@ class DialogWrapper {
|
|
|
222
225
|
static async showDialog(): Promise<void> {
|
|
223
226
|
if (!DialogWrapper._instance) {
|
|
224
227
|
DialogWrapper._instance = new DialogWrapper();
|
|
228
|
+
// eslint-disable-next-line rxjs/no-ignored-subscription
|
|
225
229
|
DialogWrapper._instance.closeDialogSubject$.subscribe(
|
|
226
230
|
() => { DialogWrapper._instance.dialog = undefined; }
|
|
227
231
|
);
|
|
@@ -251,7 +255,10 @@ class DialogWrapper {
|
|
|
251
255
|
'Upload new HELM monomer library'
|
|
252
256
|
);
|
|
253
257
|
dialog.add(widget);
|
|
254
|
-
dialog.onClose.subscribe(() =>
|
|
258
|
+
const sub = dialog.onClose.subscribe(() => {
|
|
259
|
+
this.closeDialogSubject$.next();
|
|
260
|
+
sub.unsubscribe();
|
|
261
|
+
});
|
|
255
262
|
return dialog;
|
|
256
263
|
}
|
|
257
264
|
}
|
|
@@ -259,10 +266,11 @@ class DialogWrapper {
|
|
|
259
266
|
class LibManagerView {
|
|
260
267
|
private constructor() {};
|
|
261
268
|
private static _instance: LibManagerView;
|
|
269
|
+
static viewName = 'Manage Monomer Libraries';
|
|
262
270
|
private _view: DG.View;
|
|
263
271
|
private _duplicateManager: DuplicateMonomerManager;
|
|
264
272
|
private libManager: MonomerLibManager;
|
|
265
|
-
private async getView() {
|
|
273
|
+
private async getView(addView = true) {
|
|
266
274
|
const eventManager = MonomerLibFileEventManager.getInstance();
|
|
267
275
|
const widget = (await MonomerLibraryManagerWidget.getInstance()).widget;
|
|
268
276
|
const addButton = ui.bigButton('Add',
|
|
@@ -275,7 +283,10 @@ class LibManagerView {
|
|
|
275
283
|
this._duplicateManager.root],
|
|
276
284
|
{style: {width: '100%', height: '100%'}},
|
|
277
285
|
true);
|
|
278
|
-
this._view =
|
|
286
|
+
this._view = DG.View.fromRoot(v);
|
|
287
|
+
this._view.name = LibManagerView.viewName;
|
|
288
|
+
if (addView)
|
|
289
|
+
grok.shell.addView(this._view);
|
|
279
290
|
|
|
280
291
|
ui.tools.waitForElementInDom(v).then(() => {
|
|
281
292
|
setTimeout(() => {
|
|
@@ -300,24 +311,32 @@ class LibManagerView {
|
|
|
300
311
|
}
|
|
301
312
|
}));
|
|
302
313
|
});
|
|
314
|
+
return this._view;
|
|
303
315
|
//grok.shell.dockManager.dock(this._duplicateManager.root, DG.DOCK_TYPE.RIGHT, null, '', 0.4);
|
|
304
316
|
}
|
|
305
317
|
|
|
306
|
-
static async showView() {
|
|
318
|
+
static async showView(addView = true) {
|
|
307
319
|
if (!LibManagerView._instance)
|
|
308
320
|
LibManagerView._instance = new LibManagerView();
|
|
309
321
|
if (!LibManagerView._instance._duplicateManager)
|
|
310
322
|
LibManagerView._instance._duplicateManager = await DuplicateMonomerManager.getInstance();
|
|
311
323
|
if (!LibManagerView._instance.libManager)
|
|
312
324
|
LibManagerView._instance.libManager = await MonomerLibManager.getInstance();
|
|
313
|
-
if (LibManagerView._instance._view &&
|
|
325
|
+
if (addView && LibManagerView._instance._view &&
|
|
314
326
|
Array.from(grok.shell.views).find((v) => v.id && v.id === LibManagerView._instance._view.id)) {
|
|
315
327
|
grok.shell.v = LibManagerView._instance._view;
|
|
316
328
|
await LibManagerView._instance._duplicateManager.refresh();
|
|
317
|
-
return;
|
|
329
|
+
return LibManagerView._instance._view;
|
|
318
330
|
}
|
|
319
|
-
|
|
331
|
+
// something can conflict with browse view, so need to make sure that we close all existing views
|
|
332
|
+
LibManagerView.closeExistingViews();
|
|
333
|
+
return LibManagerView._instance.getView(addView);
|
|
320
334
|
}
|
|
335
|
+
|
|
336
|
+
private static closeExistingViews() {
|
|
337
|
+
Array.from(grok.shell.views).filter((v) => v.name === LibManagerView.viewName).forEach((v) => v.close());
|
|
338
|
+
}
|
|
339
|
+
|
|
321
340
|
async mergeSelectedLibs() {
|
|
322
341
|
const libraryExistsError = 'Library with this name already exists';
|
|
323
342
|
const libManager = await MonomerLibManager.getInstance();
|
|
@@ -51,9 +51,14 @@ export const MONOMER_DF_COLUMNS = {
|
|
|
51
51
|
|
|
52
52
|
|
|
53
53
|
export class MonomerManager implements IMonomerManager {
|
|
54
|
-
private
|
|
54
|
+
private adjustTable() {
|
|
55
|
+
if (this.tv?.dataFrame) {
|
|
56
|
+
grok.data.detectSemanticTypes(this.tv.dataFrame);
|
|
57
|
+
this.tv.dataFrame.meta.detectSemanticTypes();
|
|
58
|
+
}
|
|
55
59
|
setTimeout(() => {
|
|
56
60
|
if (this.tv?.grid) {
|
|
61
|
+
this.tv!.grid.props.allowEdit = false;
|
|
57
62
|
this.tv!.grid.col(MONOMER_DF_COLUMN_NAMES.NAME)!.width = 100;
|
|
58
63
|
this.tv!.grid.col(MONOMER_DF_COLUMN_NAMES.SYMBOL)!.width = 70;
|
|
59
64
|
}
|
|
@@ -75,7 +80,7 @@ export class MonomerManager implements IMonomerManager {
|
|
|
75
80
|
const df = await this.getMonomersDf(this.libInput.value!);
|
|
76
81
|
if (this.tv?.dataFrame) {
|
|
77
82
|
this.tv.dataFrame = df;
|
|
78
|
-
this.
|
|
83
|
+
this.adjustTable();
|
|
79
84
|
if (scrollToRowSymbol != undefined) {
|
|
80
85
|
setTimeout(() => {
|
|
81
86
|
const col = df.col(MONOMER_DF_COLUMN_NAMES.SYMBOL)!;
|
|
@@ -99,6 +104,13 @@ export class MonomerManager implements IMonomerManager {
|
|
|
99
104
|
return this.instance;
|
|
100
105
|
}
|
|
101
106
|
|
|
107
|
+
public static async getNewInstance(): Promise<MonomerManager> {
|
|
108
|
+
const monManager = await MonomerLibManager.getInstance();
|
|
109
|
+
await monManager.awaitLoaded();
|
|
110
|
+
await monManager.loadLibrariesPromise;
|
|
111
|
+
return new MonomerManager(monManager);
|
|
112
|
+
}
|
|
113
|
+
|
|
102
114
|
async createNewMonomerLib(libName: string, _monomers: Monomer[]): Promise<void> {
|
|
103
115
|
this.tv?.grid && ui.setUpdateIndicator(this.tv.grid.root, true);
|
|
104
116
|
try {
|
|
@@ -168,11 +180,11 @@ export class MonomerManager implements IMonomerManager {
|
|
|
168
180
|
return this._newMonomerForm;
|
|
169
181
|
}
|
|
170
182
|
|
|
171
|
-
private async getMonomersTableView(fileName?: string): Promise<DG.TableView> {
|
|
183
|
+
private async getMonomersTableView(fileName?: string, addView = true): Promise<DG.TableView> {
|
|
172
184
|
const df = await this.getMonomersDf(fileName);
|
|
173
|
-
this.tv = DG.TableView.create(df,
|
|
174
|
-
|
|
175
|
-
this.
|
|
185
|
+
this.tv = DG.TableView.create(df, addView);
|
|
186
|
+
|
|
187
|
+
this.adjustTable();
|
|
176
188
|
this.tv.subs.push(
|
|
177
189
|
grok.events.onContextMenu.subscribe(({args}) => {
|
|
178
190
|
if (!args || !args.menu || !args.context || args.context.type !== DG.VIEWER.GRID || !args.context.tableView ||
|
|
@@ -205,21 +217,28 @@ export class MonomerManager implements IMonomerManager {
|
|
|
205
217
|
return this.tv;
|
|
206
218
|
}
|
|
207
219
|
|
|
220
|
+
private static closeAllMonomerManagers() {
|
|
221
|
+
Array.from(grok.shell.tableViews ?? []).filter((v) => v.name === MonomerManager.VIEW_NAME).forEach((v) => v.close());
|
|
222
|
+
}
|
|
223
|
+
|
|
208
224
|
private findActiveManagerView() {
|
|
209
225
|
if (!this.tv)
|
|
210
226
|
return null;
|
|
211
227
|
const tv = Array.from(grok.shell.tableViews ?? []).find((tv) => tv.id === this.tv!.id);
|
|
212
228
|
if (tv)
|
|
213
229
|
grok.shell.v = tv;
|
|
230
|
+
else
|
|
231
|
+
MonomerManager.closeAllMonomerManagers();
|
|
232
|
+
|
|
214
233
|
return tv ?? null;
|
|
215
234
|
}
|
|
216
235
|
|
|
217
236
|
private _skipLibInputOnchange: boolean = false;
|
|
218
237
|
|
|
219
|
-
async getViewRoot(libName?: string) {
|
|
238
|
+
async getViewRoot(libName?: string, addView = true) {
|
|
220
239
|
const availableMonLibs = (await this.monomerLibManamger.getFileManager()).getValidLibraryPaths();
|
|
221
240
|
this._newMonomerForm.molSketcher.resize();
|
|
222
|
-
if ((this.tv = this.findActiveManagerView()) && (libName ?? this.libInput.value)) {
|
|
241
|
+
if (addView && (this.tv = this.findActiveManagerView()) && (libName ?? this.libInput.value)) {
|
|
223
242
|
// get monomer library list
|
|
224
243
|
try {
|
|
225
244
|
this._skipLibInputOnchange = true;
|
|
@@ -233,12 +252,12 @@ export class MonomerManager implements IMonomerManager {
|
|
|
233
252
|
}
|
|
234
253
|
const df = await this.getMonomersDf(libName);
|
|
235
254
|
this.tv.dataFrame = df;
|
|
236
|
-
this.
|
|
255
|
+
this.adjustTable();
|
|
237
256
|
return this.tv;
|
|
238
257
|
}
|
|
239
258
|
|
|
240
259
|
libName ??= availableMonLibs[0];
|
|
241
|
-
this.tv = await this.getMonomersTableView(libName);
|
|
260
|
+
this.tv = await this.getMonomersTableView(libName, addView);
|
|
242
261
|
|
|
243
262
|
// remove project save button and download from ribbons
|
|
244
263
|
let ribbons = this.tv.getRibbonPanels();
|
|
@@ -300,7 +319,7 @@ export class MonomerManager implements IMonomerManager {
|
|
|
300
319
|
if (this._skipLibInputOnchange) return;
|
|
301
320
|
const df = await this.getMonomersDf(this.libInput.value!);
|
|
302
321
|
this.tv!.dataFrame = df;
|
|
303
|
-
this.
|
|
322
|
+
this.adjustTable();
|
|
304
323
|
} catch (e) {
|
|
305
324
|
console.error(e);
|
|
306
325
|
}
|
|
@@ -308,7 +327,9 @@ export class MonomerManager implements IMonomerManager {
|
|
|
308
327
|
this.libInput.addOptions(ui.icons.add(() => { this.createNewLibDialog(); }, 'Create new monomer library...'));
|
|
309
328
|
const monForm = this._newMonomerForm.form;
|
|
310
329
|
monForm.prepend(this.libInput.root);
|
|
311
|
-
this.tv.
|
|
330
|
+
ui.tools.waitForElementInDom(this.tv.root).then(() => {
|
|
331
|
+
this.tv!.dockManager.dock(monForm, DG.DOCK_TYPE.LEFT, null, undefined, 0.4);
|
|
332
|
+
});
|
|
312
333
|
return this.tv;
|
|
313
334
|
}
|
|
314
335
|
|
|
@@ -403,6 +424,10 @@ export class MonomerManager implements IMonomerManager {
|
|
|
403
424
|
this.tv?.grid && ui.setUpdateIndicator(this.tv.grid.root, false);
|
|
404
425
|
}
|
|
405
426
|
}
|
|
427
|
+
|
|
428
|
+
public resetCurrentRowFollowing() {
|
|
429
|
+
this._newMonomerForm.molChanged = false;
|
|
430
|
+
}
|
|
406
431
|
}
|
|
407
432
|
|
|
408
433
|
// some monomers might be in form of cap groups in place of r-groups (with supplied rgroups info), this function will convert them to r-groups
|
|
@@ -474,7 +499,7 @@ function getCaseInvariantValue<T>(obj: { [key: string]: T }, key: string): T | u
|
|
|
474
499
|
|
|
475
500
|
// some r groups for some monomers can lack smiles, or something else :D this function will try to fix that
|
|
476
501
|
function resolveRGroupInfo(rgps: RGroup[]): RGroup[] {
|
|
477
|
-
return rgps.map((rg) => {
|
|
502
|
+
return (rgps.map((rg) => {
|
|
478
503
|
const cp = assignObjectCaseInvariant(RGROUP_FIELDS, rg);
|
|
479
504
|
const smi = getCaseInvariantValue(cp, HELM_RGROUP_FIELDS.CAP_GROUP_SMILES_UPPERCASE);
|
|
480
505
|
const altId = getCaseInvariantValue(cp, HELM_RGROUP_FIELDS.ALTERNATE_ID);
|
|
@@ -498,7 +523,7 @@ function resolveRGroupInfo(rgps: RGroup[]): RGroup[] {
|
|
|
498
523
|
cp[HELM_RGROUP_FIELDS.ALTERNATE_ID] = `${label}-${capName}`;
|
|
499
524
|
}
|
|
500
525
|
return cp;
|
|
501
|
-
}) as RGroup[];
|
|
526
|
+
}) as RGroup[]).sort((a, b) => a.label?.localeCompare(b.label ?? '') ?? 0);
|
|
502
527
|
}
|
|
503
528
|
|
|
504
529
|
|
|
@@ -522,6 +547,7 @@ class MonomerForm implements INewMonomerForm {
|
|
|
522
547
|
rgroupsGridRoot: HTMLElement;
|
|
523
548
|
private _molChanged: boolean = false;
|
|
524
549
|
get molChanged() { return this._molChanged; }
|
|
550
|
+
set molChanged(v: boolean) { this._molChanged = v; }
|
|
525
551
|
private saveValidationResult?: string | null = null;
|
|
526
552
|
private triggerMolChange: boolean = true; // makes sure that change is not triggered by copying the molecule from grid
|
|
527
553
|
inputsTabControl: DG.TabControl;
|
|
@@ -607,7 +633,7 @@ class MonomerForm implements INewMonomerForm {
|
|
|
607
633
|
} as unknown as RGroup;
|
|
608
634
|
});
|
|
609
635
|
// if (this.rgroupsGrid.items.length !== rGroupItems.length)
|
|
610
|
-
this.rgroupsGrid.items = rGroupItems.sort((a, b) => a.label
|
|
636
|
+
this.rgroupsGrid.items = rGroupItems.sort((a, b) => a.label?.localeCompare(b.label ?? '') ?? 0);
|
|
611
637
|
this.rgroupsGrid.render();
|
|
612
638
|
this.rgroupsGridRoot.style.display = 'flex';
|
|
613
639
|
const mostSimilar = await mostSimilarNaturalAnalog(capSmiles(smiles, rGroupItems), this.polymerTypeInput.value ?? '');
|