@datagrok/bio 2.12.17 → 2.12.19
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +21 -2
- package/dist/79.js.map +1 -1
- package/dist/package-test.js +3 -3
- package/dist/package-test.js.map +1 -1
- package/dist/package.js +3 -3
- package/dist/package.js.map +1 -1
- package/package.json +4 -4
- package/src/package.ts +56 -12
- package/src/tests/monomer-libraries-tests.ts +1 -4
- package/src/tests/renderers-monomer-placer-tests.ts +59 -12
- package/src/tests/renderers-test.ts +3 -5
- package/src/tests/scoring.ts +2 -2
- package/src/tests/substructure-filters-tests.ts +2 -0
- package/src/tests/to-atomic-level-tests.ts +1 -1
- package/src/tests/utils/sequences-generators.ts +0 -20
- package/src/tests/utils.ts +15 -0
- package/src/utils/cell-renderer.ts +39 -46
- package/src/utils/helm-to-molfile/converter/converter.ts +10 -5
- package/src/utils/helm-to-molfile/converter/monomer-wrapper.ts +9 -9
- package/src/utils/helm-to-molfile/converter/polymer.ts +10 -3
- package/src/utils/macromolecule-column-widget.ts +2 -0
- package/src/utils/monomer-cell-renderer.ts +18 -8
- package/src/utils/monomer-lib/lib-manager.ts +56 -18
- package/src/utils/monomer-lib/library-file-manager/event-manager.ts +15 -9
- package/src/utils/monomer-lib/library-file-manager/file-manager.ts +78 -59
- package/src/utils/monomer-lib/library-file-manager/file-validator.ts +3 -1
- package/src/utils/monomer-lib/library-file-manager/ui.ts +52 -47
- package/src/utils/monomer-lib/monomer-lib.ts +78 -9
- package/src/utils/multiple-sequence-alignment-ui.ts +31 -18
- package/src/utils/sequence-to-mol.ts +7 -7
- package/src/viewers/web-logo-viewer.ts +14 -4
- package/src/widgets/bio-substructure-filter-helm.ts +9 -3
- package/src/widgets/bio-substructure-filter.ts +2 -2
- package/webpack.config.js +1 -0
|
@@ -4,16 +4,15 @@ import * as ui from 'datagrok-api/ui';
|
|
|
4
4
|
import * as DG from 'datagrok-api/dg';
|
|
5
5
|
|
|
6
6
|
import $ from 'cash-dom';
|
|
7
|
-
import
|
|
7
|
+
import {Subject} from 'rxjs';
|
|
8
8
|
import './style.css';
|
|
9
9
|
|
|
10
10
|
import {
|
|
11
11
|
getUserLibSettings, setUserLibSettings
|
|
12
12
|
} from '@datagrok-libraries/bio/src/monomer-works/lib-settings';
|
|
13
13
|
import {UserLibSettings} from '@datagrok-libraries/bio/src/monomer-works/types';
|
|
14
|
-
import {
|
|
14
|
+
import {getMonomerLibHelper, IMonomerLibFileManager} from '@datagrok-libraries/bio/src/monomer-works/monomer-utils';
|
|
15
15
|
|
|
16
|
-
import {MonomerLibFileManager} from './file-manager';
|
|
17
16
|
import {MonomerLibFileEventManager} from './event-manager';
|
|
18
17
|
|
|
19
18
|
export async function showManageLibrariesDialog(): Promise<void> {
|
|
@@ -30,37 +29,39 @@ export async function getMonomerLibraryManagerLink(): Promise<DG.Widget> {
|
|
|
30
29
|
}
|
|
31
30
|
|
|
32
31
|
class MonomerLibraryManagerWidget {
|
|
33
|
-
private
|
|
34
|
-
private eventManager: MonomerLibFileEventManager
|
|
35
|
-
) { }
|
|
32
|
+
private _fileManager: IMonomerLibFileManager;
|
|
36
33
|
|
|
37
|
-
private
|
|
34
|
+
private _widget: DG.Widget;
|
|
35
|
+
public get widget(): DG.Widget { return this._widget; }
|
|
38
36
|
|
|
39
|
-
|
|
40
|
-
if (!MonomerLibraryManagerWidget._instance)
|
|
41
|
-
MonomerLibraryManagerWidget._instance = new MonomerLibraryManagerWidget(eventManager);
|
|
37
|
+
private constructor() {}
|
|
42
38
|
|
|
43
|
-
|
|
44
|
-
MonomerLibraryManagerWidget._instance.widget = await MonomerLibraryManagerWidget._instance.createWidget();
|
|
39
|
+
private static instancePromise?: Promise<MonomerLibraryManagerWidget>;
|
|
45
40
|
|
|
46
|
-
|
|
41
|
+
static async getInstance(): Promise<MonomerLibraryManagerWidget> {
|
|
42
|
+
if (MonomerLibraryManagerWidget.instancePromise === undefined) {
|
|
43
|
+
MonomerLibraryManagerWidget.instancePromise = (async () => {
|
|
44
|
+
const instance = new MonomerLibraryManagerWidget();
|
|
45
|
+
const libHelper = await getMonomerLibHelper();
|
|
46
|
+
instance._fileManager = await libHelper.getFileManager();
|
|
47
|
+
instance._widget = await instance.createWidget();
|
|
48
|
+
return instance;
|
|
49
|
+
})();
|
|
50
|
+
}
|
|
51
|
+
return MonomerLibraryManagerWidget.instancePromise;
|
|
47
52
|
}
|
|
48
53
|
|
|
49
|
-
private monomerLibFileManager: MonomerLibFileManager;
|
|
50
|
-
private widget: DG.Widget | undefined;
|
|
51
|
-
|
|
52
54
|
private async createWidget() {
|
|
53
|
-
this.monomerLibFileManager = await MonomerLibFileManager.getInstance(this.eventManager);
|
|
54
55
|
const content = await this.getWidgetContent();
|
|
55
|
-
|
|
56
|
+
const monomerLibHelper = await getMonomerLibHelper();
|
|
57
|
+
monomerLibHelper.eventManager.addLibraryFileRequested$.subscribe(
|
|
56
58
|
async () => await this.promptToAddLibraryFiles()
|
|
57
59
|
);
|
|
58
60
|
return new DG.Widget(content);
|
|
59
61
|
}
|
|
60
62
|
|
|
61
63
|
private async getWidgetContent(): Promise<HTMLElement> {
|
|
62
|
-
|
|
63
|
-
const libControlsForm = await LibraryControlsManager.createControlsForm(this.eventManager);
|
|
64
|
+
const libControlsForm = await LibraryControlsManager.createControlsForm();
|
|
64
65
|
$(libControlsForm).addClass('monomer-lib-controls-form');
|
|
65
66
|
const widgetContent = ui.divV([libControlsForm]);
|
|
66
67
|
return widgetContent;
|
|
@@ -74,7 +75,7 @@ class MonomerLibraryManagerWidget {
|
|
|
74
75
|
const name = selectedFile.name;
|
|
75
76
|
const progressIndicator = DG.TaskBarProgressIndicator.create(`Adding ${name} as a monomer library`);
|
|
76
77
|
try {
|
|
77
|
-
await this.
|
|
78
|
+
await this._fileManager.addLibraryFile(content, name);
|
|
78
79
|
// this.eventManager.updateLibrarySelectionStatus(name, true);
|
|
79
80
|
} catch (e) {
|
|
80
81
|
grok.shell.error(`File ${name} is not a valid monomer library, verify it is aligned to HELM JSON schema.`);
|
|
@@ -87,27 +88,30 @@ class MonomerLibraryManagerWidget {
|
|
|
87
88
|
}
|
|
88
89
|
|
|
89
90
|
class LibraryControlsManager {
|
|
90
|
-
private constructor(
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
)
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
)
|
|
91
|
+
private constructor(
|
|
92
|
+
private fileManager: IMonomerLibFileManager
|
|
93
|
+
) {
|
|
94
|
+
this.fileManager.eventManager.updateUIControlsRequested$.subscribe(() => {
|
|
95
|
+
this.updateControlsForm();
|
|
96
|
+
});
|
|
97
|
+
this.fileManager.eventManager.librarySelectionRequested$.subscribe(async ([fileName, isSelected]) => {
|
|
98
|
+
await this.updateLibrarySelectionStatus(isSelected, fileName);
|
|
99
|
+
});
|
|
97
100
|
}
|
|
98
|
-
|
|
101
|
+
|
|
99
102
|
private userLibSettings: UserLibSettings;
|
|
100
103
|
|
|
101
|
-
static async createControlsForm(
|
|
102
|
-
const
|
|
104
|
+
static async createControlsForm(): Promise<HTMLElement> {
|
|
105
|
+
const libHelper = await getMonomerLibHelper();
|
|
106
|
+
const fileManager = await libHelper.getFileManager();
|
|
107
|
+
const manager = new LibraryControlsManager(fileManager);
|
|
103
108
|
await manager.initialize();
|
|
104
109
|
|
|
105
|
-
return
|
|
110
|
+
return manager._createControlsForm();
|
|
106
111
|
}
|
|
107
112
|
|
|
108
|
-
private
|
|
109
|
-
|
|
110
|
-
const libraryControls = await this.createLibraryControls();
|
|
113
|
+
private _createControlsForm(): HTMLElement {
|
|
114
|
+
const libraryControls = this.createLibraryControls();
|
|
111
115
|
const inputsForm = ui.form(libraryControls);
|
|
112
116
|
$(inputsForm).addClass('monomer-lib-controls-form');
|
|
113
117
|
|
|
@@ -118,14 +122,13 @@ class LibraryControlsManager {
|
|
|
118
122
|
this.userLibSettings = await getUserLibSettings();
|
|
119
123
|
};
|
|
120
124
|
|
|
121
|
-
private
|
|
122
|
-
const updatedForm =
|
|
125
|
+
private updateControlsForm(): void {
|
|
126
|
+
const updatedForm = this._createControlsForm();
|
|
123
127
|
$('.monomer-lib-controls-form').replaceWith(updatedForm);
|
|
124
128
|
}
|
|
125
129
|
|
|
126
|
-
private
|
|
127
|
-
const
|
|
128
|
-
const libFileNameList: string[] = fileManager.getValidLibraryPaths();
|
|
130
|
+
private createLibraryControls(): DG.InputBase<boolean | null>[] {
|
|
131
|
+
const libFileNameList: string[] = this.fileManager.getValidLibraryPaths();
|
|
129
132
|
return libFileNameList.map((libFileName) => this.createLibInput(libFileName));
|
|
130
133
|
}
|
|
131
134
|
|
|
@@ -134,8 +137,9 @@ class LibraryControlsManager {
|
|
|
134
137
|
const libInput = ui.boolInput(
|
|
135
138
|
libFileName,
|
|
136
139
|
isMonomerLibrarySelected,
|
|
137
|
-
(isSelected: boolean) =>
|
|
138
|
-
|
|
140
|
+
(isSelected: boolean) => {
|
|
141
|
+
this.fileManager.eventManager.updateLibrarySelectionStatus(libFileName, isSelected);
|
|
142
|
+
});
|
|
139
143
|
ui.tooltip.bind(libInput.root, `Include monomers from ${libFileName}`);
|
|
140
144
|
const deleteIcon = ui.iconFA('trash-alt', () => this.promptForLibraryDeletion(libFileName));
|
|
141
145
|
ui.tooltip.bind(deleteIcon, `Delete ${libFileName}`);
|
|
@@ -149,7 +153,8 @@ class LibraryControlsManager {
|
|
|
149
153
|
): Promise<void> {
|
|
150
154
|
this.updateLibrarySettings(isMonomerLibrarySelected, libFileName);
|
|
151
155
|
await setUserLibSettings(this.userLibSettings);
|
|
152
|
-
await
|
|
156
|
+
const monomerLibHelper = await getMonomerLibHelper();
|
|
157
|
+
await monomerLibHelper.loadLibraries(true);
|
|
153
158
|
grok.shell.info('Monomer library user settings saved');
|
|
154
159
|
}
|
|
155
160
|
|
|
@@ -172,8 +177,8 @@ class LibraryControlsManager {
|
|
|
172
177
|
.onOK(async () => {
|
|
173
178
|
try {
|
|
174
179
|
const progressIndicator = DG.TaskBarProgressIndicator.create(`Deleting ${fileName} library`);
|
|
175
|
-
this.updateLibrarySelectionStatus(false, fileName);
|
|
176
|
-
await this.
|
|
180
|
+
await this.updateLibrarySelectionStatus(false, fileName);
|
|
181
|
+
await this.fileManager.deleteLibraryFile(fileName);
|
|
177
182
|
progressIndicator.close();
|
|
178
183
|
} catch (e) {
|
|
179
184
|
console.error(e);
|
|
@@ -189,7 +194,7 @@ class DialogWrapper {
|
|
|
189
194
|
|
|
190
195
|
private static _instance: DialogWrapper;
|
|
191
196
|
private dialog?: DG.Dialog;
|
|
192
|
-
private closeDialogSubject$ = new
|
|
197
|
+
private closeDialogSubject$ = new Subject<void>();
|
|
193
198
|
|
|
194
199
|
static async showDialog(): Promise<void> {
|
|
195
200
|
if (!DialogWrapper._instance) {
|
|
@@ -207,7 +212,7 @@ class DialogWrapper {
|
|
|
207
212
|
|
|
208
213
|
private async getDialog(): Promise<DG.Dialog> {
|
|
209
214
|
const eventManager = MonomerLibFileEventManager.getInstance();
|
|
210
|
-
const widget = await MonomerLibraryManagerWidget.
|
|
215
|
+
const widget = (await MonomerLibraryManagerWidget.getInstance()).widget;
|
|
211
216
|
const dialog = ui.dialog(
|
|
212
217
|
{
|
|
213
218
|
title: 'Manage monomer libraries',
|
|
@@ -1,10 +1,17 @@
|
|
|
1
1
|
/* Do not change these import lines to match external modules in webpack configuration */
|
|
2
2
|
import * as grok from 'datagrok-api/grok';
|
|
3
3
|
import * as ui from 'datagrok-api/ui';
|
|
4
|
+
import * as DG from 'datagrok-api/dg';
|
|
4
5
|
|
|
6
|
+
import wu from 'wu';
|
|
5
7
|
import {Observable, Subject} from 'rxjs';
|
|
6
8
|
|
|
7
|
-
import {
|
|
9
|
+
import {
|
|
10
|
+
IMonomerLib, Monomer, MonomerLibSummaryType, MonomerType, PolymerType, RGroup
|
|
11
|
+
} from '@datagrok-libraries/bio/src/types';
|
|
12
|
+
import {
|
|
13
|
+
HELM_REQUIRED_FIELD as REQ, HELM_RGROUP_FIELDS as RGP
|
|
14
|
+
} from '@datagrok-libraries/bio/src/utils/const';
|
|
8
15
|
import {MolfileHandler} from '@datagrok-libraries/chem-meta/src/parsing-utils/molfile-handler';
|
|
9
16
|
|
|
10
17
|
import '../../../css/cell-renderer.css';
|
|
@@ -27,18 +34,57 @@ export class MonomerLib implements IMonomerLib {
|
|
|
27
34
|
}
|
|
28
35
|
}
|
|
29
36
|
|
|
30
|
-
|
|
37
|
+
/** Creates missing {@link Monomer} */
|
|
38
|
+
addMissingMonomer(polymerType: PolymerType, monomerSymbol: string): Monomer {
|
|
39
|
+
let mSet = this._monomers[polymerType];
|
|
40
|
+
if (!mSet)
|
|
41
|
+
mSet = this._monomers[polymerType] = {};
|
|
42
|
+
const m = mSet[monomerSymbol] = {
|
|
43
|
+
[REQ.SYMBOL]: monomerSymbol,
|
|
44
|
+
[REQ.NAME]: monomerSymbol,
|
|
45
|
+
[REQ.MOLFILE]: '',
|
|
46
|
+
[REQ.AUTHOR]: 'MISSING',
|
|
47
|
+
[REQ.ID]: -1,
|
|
48
|
+
[REQ.RGROUPS]:
|
|
49
|
+
wu.count(1).take(9).map((i) => {
|
|
50
|
+
return {
|
|
51
|
+
/* eslint-disable no-multi-spaces */
|
|
52
|
+
// Samples // PEPTIDE RNA
|
|
53
|
+
[RGP.CAP_GROUP_SMILES]: '', // '[*:1][H]' '[*:1][H]'
|
|
54
|
+
[RGP.ALTERNATE_ID]: '', // 'R1-H' 'R1-H'
|
|
55
|
+
[RGP.CAP_GROUP_NAME]: '', // 'H' 'H'
|
|
56
|
+
[RGP.LABEL]: `R${i.toString()}`, // 'R1' 'R1'
|
|
57
|
+
/* eslint-enable no-multi-spaces */
|
|
58
|
+
} as RGroup;
|
|
59
|
+
}).toArray(),
|
|
60
|
+
[REQ.SMILES]: '',
|
|
61
|
+
[REQ.POLYMER_TYPE]: polymerType,
|
|
62
|
+
[REQ.MONOMER_TYPE]: undefined as unknown as MonomerType, // TODO: Can we get monomerType from atom of POM
|
|
63
|
+
[REQ.CREATE_DATE]: null,
|
|
64
|
+
} as Monomer;
|
|
65
|
+
return m;
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
getMonomer(polymerType: PolymerType, argMonomerSymbol: string): Monomer | null {
|
|
69
|
+
// Adjust RNA's 'R' for ribose to 'r' and 'P' for phosphate to 'p' for case-sensitive monomer names.
|
|
70
|
+
// There are uppercase 'R' and 'P' at RNA samples in test data 'helm2.csv' but lowercase in HELMCoreLibrary.json
|
|
71
|
+
let monomerSymbol = argMonomerSymbol;
|
|
72
|
+
if (polymerType == 'RNA' && monomerSymbol == 'R')
|
|
73
|
+
monomerSymbol = 'r';
|
|
74
|
+
if (polymerType == 'RNA' && monomerSymbol == 'P')
|
|
75
|
+
monomerSymbol = 'p';
|
|
76
|
+
|
|
31
77
|
if (polymerType in this._monomers! && monomerSymbol in this._monomers![polymerType])
|
|
32
78
|
return this._monomers![polymerType][monomerSymbol];
|
|
33
79
|
else
|
|
34
80
|
return null;
|
|
35
81
|
}
|
|
36
82
|
|
|
37
|
-
getPolymerTypes():
|
|
38
|
-
return Object.keys(this._monomers);
|
|
83
|
+
getPolymerTypes(): PolymerType[] {
|
|
84
|
+
return Object.keys(this._monomers) as PolymerType[];
|
|
39
85
|
}
|
|
40
86
|
|
|
41
|
-
getMonomerMolsByPolymerType(polymerType:
|
|
87
|
+
getMonomerMolsByPolymerType(polymerType: PolymerType): { [monomerSymbol: string]: string } {
|
|
42
88
|
const res: { [monomerSymbol: string]: string } = {};
|
|
43
89
|
|
|
44
90
|
Object.keys(this._monomers[polymerType] ?? {}).forEach((monomerSymbol) => {
|
|
@@ -48,14 +94,14 @@ export class MonomerLib implements IMonomerLib {
|
|
|
48
94
|
return res;
|
|
49
95
|
}
|
|
50
96
|
|
|
51
|
-
getMonomerSymbolsByType(polymerType:
|
|
97
|
+
getMonomerSymbolsByType(polymerType: PolymerType): string[] {
|
|
52
98
|
return Object.keys(this._monomers[polymerType]);
|
|
53
99
|
}
|
|
54
100
|
|
|
55
101
|
/** Get a list of monomers with specified element attached to specified
|
|
56
102
|
* R-group
|
|
57
103
|
* WARNING: RGroup numbering starts from 1, not 0*/
|
|
58
|
-
getMonomerSymbolsByRGroup(rGroupNumber: number, polymerType:
|
|
104
|
+
getMonomerSymbolsByRGroup(rGroupNumber: number, polymerType: PolymerType, element?: string): string[] {
|
|
59
105
|
const monomerSymbols = this.getMonomerSymbolsByType(polymerType);
|
|
60
106
|
let monomers = monomerSymbols.map((sym) => this.getMonomer(polymerType, sym));
|
|
61
107
|
monomers = monomers.filter((el) => el !== null);
|
|
@@ -116,15 +162,38 @@ export class MonomerLib implements IMonomerLib {
|
|
|
116
162
|
this._onChanged.next();
|
|
117
163
|
}
|
|
118
164
|
|
|
165
|
+
getSummaryObj(): MonomerLibSummaryType {
|
|
166
|
+
const res: MonomerLibSummaryType = {};
|
|
167
|
+
const ptList: PolymerType[] = this.getPolymerTypes();
|
|
168
|
+
for (const pt of ptList)
|
|
169
|
+
res[pt] = this.getMonomerSymbolsByType(pt).length;
|
|
170
|
+
return res;
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
getSummaryDf(): DG.DataFrame {
|
|
174
|
+
const ptList = this.getPolymerTypes();
|
|
175
|
+
|
|
176
|
+
const countList: number[] = new Array<number>(ptList.length);
|
|
177
|
+
for (const [pt, i] of wu.enumerate(ptList))
|
|
178
|
+
countList[i] = this.getMonomerSymbolsByType(pt).length;
|
|
179
|
+
|
|
180
|
+
const resDf: DG.DataFrame = DG.DataFrame.fromColumns([
|
|
181
|
+
DG.Column.fromStrings('polymerType', ptList),
|
|
182
|
+
DG.Column.fromList(DG.COLUMN_TYPE.INT, 'count', countList),
|
|
183
|
+
]);
|
|
184
|
+
return resDf;
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
/** @deprecated Keep for backward compatibility */
|
|
119
188
|
getSummary(): string {
|
|
120
|
-
const monTypeList:
|
|
189
|
+
const monTypeList: PolymerType[] = this.getPolymerTypes();
|
|
121
190
|
const resStr: string = monTypeList.length == 0 ? 'empty' : monTypeList.map((monType) => {
|
|
122
191
|
return `${monType} ${this.getMonomerSymbolsByType(monType).length}`;
|
|
123
192
|
}).join('\n');
|
|
124
193
|
return resStr;
|
|
125
194
|
}
|
|
126
195
|
|
|
127
|
-
getTooltip(polymerType:
|
|
196
|
+
getTooltip(polymerType: PolymerType, monomerSymbol: string): HTMLElement {
|
|
128
197
|
// getTooltip(monomer: Monomer): HTMLElement;
|
|
129
198
|
// getTooltip(monomerOrPolymerType: string | Monomer, symbol?: string): HTMLElement {
|
|
130
199
|
// let polymerType: string;
|
|
@@ -2,9 +2,10 @@ import * as grok from 'datagrok-api/grok';
|
|
|
2
2
|
import * as DG from 'datagrok-api/dg';
|
|
3
3
|
import * as ui from 'datagrok-api/ui';
|
|
4
4
|
|
|
5
|
+
import {ColumnInputOptions} from '@datagrok-libraries/utils/src/type-declarations';
|
|
6
|
+
import {delay} from '@datagrok-libraries/utils/src/test';
|
|
5
7
|
import {ALPHABET, NOTATION} from '@datagrok-libraries/bio/src/utils/macromolecule';
|
|
6
8
|
import {SeqHandler} from '@datagrok-libraries/bio/src/utils/seq-handler';
|
|
7
|
-
import {ColumnInputOptions} from '@datagrok-libraries/utils/src/type-declarations';
|
|
8
9
|
|
|
9
10
|
import {runKalign} from './multiple-sequence-alignment';
|
|
10
11
|
import {pepseaMethods, runPepsea} from './pepsea';
|
|
@@ -36,9 +37,10 @@ export async function multipleSequenceAlignmentUI(
|
|
|
36
37
|
const table = options.col?.dataFrame ?? grok.shell.t;
|
|
37
38
|
const seqCol = options.col ?? table.columns.bySemType(DG.SEMTYPE.MACROMOLECULE);
|
|
38
39
|
if (seqCol == null) {
|
|
39
|
-
const errMsg = `
|
|
40
|
+
const errMsg: string = `Multiple sequence analysis requires a dataset with a macromolecule column.`;
|
|
40
41
|
grok.shell.warning(errMsg);
|
|
41
42
|
reject(new MsaWarning(errMsg));
|
|
43
|
+
return; // Prevents creating the MSA dialog
|
|
42
44
|
}
|
|
43
45
|
|
|
44
46
|
// UI for PepSea alignment
|
|
@@ -72,39 +74,50 @@ export async function multipleSequenceAlignmentUI(
|
|
|
72
74
|
|
|
73
75
|
let performAlignment: (() => Promise<DG.Column<string> | null>) | undefined;
|
|
74
76
|
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
77
|
+
let prevSeqCol = seqCol;
|
|
78
|
+
const colInput = ui.columnInput(
|
|
79
|
+
'Sequence', table, seqCol,
|
|
80
|
+
async (valueCol: DG.Column) => {
|
|
81
|
+
if (!valueCol || valueCol.semType !== DG.SEMTYPE.MACROMOLECULE) {
|
|
82
|
+
okBtn.disabled = true;
|
|
83
|
+
await delay(0); // to
|
|
84
|
+
colInput.value = prevSeqCol as DG.Column<string>;
|
|
85
|
+
return;
|
|
86
|
+
}
|
|
87
|
+
prevSeqCol = valueCol;
|
|
88
|
+
okBtn.disabled = false;
|
|
78
89
|
performAlignment = await onColInputChange(
|
|
79
90
|
colInput.value, table, pepseaInputRootStyles, kalignInputRootStyles,
|
|
80
91
|
methodInput, clustersColInput, gapOpenInput, gapExtendInput, terminalGapInput,
|
|
81
92
|
);
|
|
82
|
-
//@ts-ignore
|
|
83
93
|
}, {filter: (col: DG.Column) => col.semType === DG.SEMTYPE.MACROMOLECULE} as ColumnInputOptions
|
|
84
94
|
) as DG.InputBase<DG.Column<string>>;
|
|
85
95
|
colInput.setTooltip('Sequences column to use for alignment');
|
|
86
96
|
const clustersColInput = ui.columnInput('Clusters', table, options.clustersCol);
|
|
87
97
|
clustersColInput.nullable = true;
|
|
88
|
-
|
|
98
|
+
|
|
99
|
+
const dlg = ui.dialog('MSA')
|
|
100
|
+
.add(colInput)
|
|
101
|
+
.add(clustersColInput)
|
|
102
|
+
.add(methodInput)
|
|
103
|
+
.add(msaParamsDiv)
|
|
104
|
+
.add(msaParamsButton)
|
|
105
|
+
.add(kalignVersionDiv)
|
|
106
|
+
.onOK(async () => { await onDialogOk(colInput, table, performAlignment, resolve, reject); });
|
|
107
|
+
const okBtn = dlg.getButton('OK');
|
|
108
|
+
|
|
109
|
+
colInput.fireChanged(); // changes okBtn
|
|
89
110
|
//if column is specified (from tests), run alignment and resolve with the result
|
|
90
111
|
if (options.col) {
|
|
91
112
|
performAlignment = await onColInputChange(
|
|
92
113
|
options.col, table, pepseaInputRootStyles, kalignInputRootStyles,
|
|
93
114
|
methodInput, clustersColInput, gapOpenInput, gapExtendInput, terminalGapInput,
|
|
94
115
|
);
|
|
95
|
-
|
|
96
116
|
await onDialogOk(colInput, table, performAlignment, resolve, reject);
|
|
97
|
-
return;
|
|
117
|
+
return; // Prevents show the dialog
|
|
98
118
|
}
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
.add(clustersColInput)
|
|
102
|
-
.add(methodInput)
|
|
103
|
-
.add(msaParamsDiv)
|
|
104
|
-
.add(msaParamsButton)
|
|
105
|
-
.add(kalignVersionDiv)
|
|
106
|
-
.onOK(async () => { await onDialogOk(colInput, table, performAlignment, resolve, reject); })
|
|
107
|
-
.show();
|
|
119
|
+
|
|
120
|
+
dlg.show();
|
|
108
121
|
});
|
|
109
122
|
}
|
|
110
123
|
|
|
@@ -4,14 +4,16 @@ import * as ui from 'datagrok-api/ui';
|
|
|
4
4
|
import * as DG from 'datagrok-api/dg';
|
|
5
5
|
|
|
6
6
|
import {_toAtomicLevel} from '@datagrok-libraries/bio/src/monomer-works/to-atomic-level';
|
|
7
|
-
import {helm2mol} from './helm-to-molfile/utils';
|
|
8
7
|
import {IMonomerLib} from '@datagrok-libraries/bio/src/types';
|
|
9
|
-
import {checkInputColumnUI} from './check-input-column';
|
|
10
|
-
import {getMonomerLibHelper} from '../package';
|
|
11
8
|
import {SeqHandler} from '@datagrok-libraries/bio/src/utils/seq-handler';
|
|
12
9
|
import {NOTATION} from '@datagrok-libraries/bio/src/utils/macromolecule';
|
|
13
10
|
|
|
14
|
-
|
|
11
|
+
import {helm2mol} from './helm-to-molfile/utils';
|
|
12
|
+
import {checkInputColumnUI} from './check-input-column';
|
|
13
|
+
|
|
14
|
+
export async function sequenceToMolfile(
|
|
15
|
+
df: DG.DataFrame, macroMolecule: DG.Column, nonlinear: boolean, monomerLib: IMonomerLib
|
|
16
|
+
): Promise<void> {
|
|
15
17
|
if (DG.Func.find({package: 'Chem', name: 'getRdKitModule'}).length === 0) {
|
|
16
18
|
grok.shell.warning('Transformation to atomic level requires package "Chem" installed.');
|
|
17
19
|
return;
|
|
@@ -20,12 +22,10 @@ export async function sequenceToMolfile(df: DG.DataFrame, macroMolecule: DG.Colu
|
|
|
20
22
|
const seqSh = SeqHandler.forColumn(macroMolecule);
|
|
21
23
|
if (!seqSh.isHelm())
|
|
22
24
|
macroMolecule = seqSh.convert(NOTATION.HELM);
|
|
23
|
-
helm2mol(df, macroMolecule);
|
|
24
|
-
return;
|
|
25
|
+
return helm2mol(df, macroMolecule);
|
|
25
26
|
}
|
|
26
27
|
if (!checkInputColumnUI(macroMolecule, 'To Atomic Level'))
|
|
27
28
|
return;
|
|
28
|
-
const monomerLib: IMonomerLib = getMonomerLibHelper().getBioLib();
|
|
29
29
|
const atomicLevelRes = await _toAtomicLevel(df, macroMolecule, monomerLib);
|
|
30
30
|
if (atomicLevelRes.col !== null) {
|
|
31
31
|
df.columns.add(atomicLevelRes.col, true);
|
|
@@ -953,16 +953,26 @@ export class WebLogoViewer extends DG.JsViewer implements IWebLogoViewer {
|
|
|
953
953
|
|
|
954
954
|
// region updatePositions
|
|
955
955
|
|
|
956
|
+
/** positionNames and positionLabel can be set up through the column's tags only */
|
|
957
|
+
const positionNamesTxt = this.seqCol.getTag(bioTAGS.positionNames);
|
|
958
|
+
const positionLabelsTxt = this.seqCol.getTag(bioTAGS.positionLabels);
|
|
959
|
+
|
|
960
|
+
// WebLogo in the column tooltip / widget is limited, speed up for long sequences
|
|
961
|
+
let splitLimit: number | undefined = undefined;
|
|
962
|
+
if (!positionNamesTxt && this.endPositionName && /\d+/.test(this.endPositionName))
|
|
963
|
+
splitLimit = Number(this.endPositionName);
|
|
964
|
+
else if (positionNamesTxt && this.endPositionName) {
|
|
965
|
+
splitLimit = positionNamesTxt.split(positionSeparator).indexOf(this.endPositionName);
|
|
966
|
+
splitLimit = splitLimit !== -1 ? splitLimit : undefined;
|
|
967
|
+
}
|
|
968
|
+
|
|
956
969
|
const dfFilter = this.getFilter();
|
|
957
970
|
const maxLength: number = dfFilter.trueCount === 0 ? this.seqHandler!.maxLength :
|
|
958
971
|
wu.count(0).take(this.seqHandler!.length).map((rowIdx) => {
|
|
959
|
-
const mList = this.seqHandler!.getSplitted(rowIdx);
|
|
972
|
+
const mList = this.seqHandler!.getSplitted(rowIdx, splitLimit);
|
|
960
973
|
return dfFilter.get(rowIdx) && !!mList ? mList.length : 0;
|
|
961
974
|
}).reduce((max, l) => Math.max(max, l), 0);
|
|
962
975
|
|
|
963
|
-
/** positionNames and positionLabel can be set up through the column's tags only */
|
|
964
|
-
const positionNamesTxt = this.seqCol.getTag(bioTAGS.positionNames);
|
|
965
|
-
const positionLabelsTxt = this.seqCol.getTag(bioTAGS.positionLabels);
|
|
966
976
|
this.positionNames = !!positionNamesTxt ? positionNamesTxt.split(positionSeparator).map((v) => v.trim()) :
|
|
967
977
|
[...Array(maxLength).keys()].map((jPos) => `${jPos + 1}`)/* fallback if tag is not provided */;
|
|
968
978
|
this.positionLabels = !!positionLabelsTxt ? positionLabelsTxt.split(positionSeparator).map((v) => v.trim()) :
|
|
@@ -135,9 +135,15 @@ export class HelmBioFilter extends BioFilterBase<BioFilterProps> /* implements I
|
|
|
135
135
|
}
|
|
136
136
|
|
|
137
137
|
async substructureSearch(column: DG.Column): Promise<DG.BitSet | null> {
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
138
|
+
const logPrefix = `${this.viewerToLog()}.substructureSearch( column = <${column.name}> )`;
|
|
139
|
+
_package.logger.debug(`${logPrefix}, start`);
|
|
140
|
+
try {
|
|
141
|
+
await delay(10);
|
|
142
|
+
const res = await helmSubstructureSearch(this.props.substructure, column);
|
|
143
|
+
return res;
|
|
144
|
+
} finally {
|
|
145
|
+
_package.logger.debug(`${logPrefix}, end`);
|
|
146
|
+
}
|
|
141
147
|
}
|
|
142
148
|
|
|
143
149
|
// // -- IRenderer --
|
|
@@ -273,12 +273,12 @@ export class BioSubstructureFilter extends DG.Filter implements IRenderer {
|
|
|
273
273
|
async awaitRendered(timeout: number = 10000): Promise<void> {
|
|
274
274
|
const callLog = `awaitRendered( ${timeout} )`;
|
|
275
275
|
const logPrefix = `${this.filterToLog()}.${callLog}`;
|
|
276
|
-
await delay(
|
|
276
|
+
await delay(10);
|
|
277
277
|
await testEvent(this.onRendered, () => {
|
|
278
278
|
this.logger.debug(`${logPrefix}, ` + '_onRendered event caught');
|
|
279
279
|
}, () => {
|
|
280
280
|
this.invalidate(callLog);
|
|
281
|
-
}, timeout, `${logPrefix}
|
|
281
|
+
}, timeout, `${logPrefix} timeout`);
|
|
282
282
|
|
|
283
283
|
// Rethrow stored syncer error (for test purposes)
|
|
284
284
|
const viewErrors = this.filterSyncer.resetErrors();
|