@datagrok/sequence-translator 1.3.11 → 1.3.12
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 +10 -0
- 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/package.json +8 -7
- package/src/apps/common/model/oligo-toolkit-package.ts +5 -3
- package/src/package-test.ts +1 -0
- package/src/package.ts +7 -7
- package/src/polytool/const.ts +10 -0
- package/src/polytool/pt-conversion.ts +1 -0
- package/src/polytool/pt-dialog.ts +69 -74
- package/src/polytool/pt-enumeration-chem.ts +1 -16
- package/src/polytool/pt-enumeration-helm-dialog.ts +218 -0
- package/src/polytool/pt-enumeration-helm.ts +63 -11
- package/src/polytool/pt-placeholders-input.ts +127 -0
- package/src/polytool/types.ts +18 -0
- package/src/tests/polytool-enumerate-tests.ts +78 -0
- package/src/utils/err-info.ts +2 -2
- package/webpack.config.js +3 -10
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@datagrok/sequence-translator",
|
|
3
3
|
"friendlyName": "Sequence Translator",
|
|
4
|
-
"version": "1.3.
|
|
4
|
+
"version": "1.3.12",
|
|
5
5
|
"author": {
|
|
6
6
|
"name": "Alexey Choposky",
|
|
7
7
|
"email": "achopovsky@datagrok.ai"
|
|
@@ -22,9 +22,9 @@
|
|
|
22
22
|
}
|
|
23
23
|
],
|
|
24
24
|
"dependencies": {
|
|
25
|
-
"@datagrok-libraries/bio": "5.42.
|
|
25
|
+
"@datagrok-libraries/bio": "5.42.9",
|
|
26
26
|
"@datagrok-libraries/chem-meta": "^1.2.5",
|
|
27
|
-
"@datagrok-libraries/tutorials": "^1.3.
|
|
27
|
+
"@datagrok-libraries/tutorials": "^1.3.13",
|
|
28
28
|
"@datagrok-libraries/utils": "^4.2.13",
|
|
29
29
|
"@types/react": "^18.0.15",
|
|
30
30
|
"cash-dom": "^8.1.0",
|
|
@@ -39,10 +39,11 @@
|
|
|
39
39
|
"wu": "latest"
|
|
40
40
|
},
|
|
41
41
|
"devDependencies": {
|
|
42
|
-
"@datagrok-libraries/helm-web-editor": "^1.1.
|
|
43
|
-
"@datagrok-libraries/js-draw-lite": "^0.0.
|
|
44
|
-
"@datagrok/bio": "^2.
|
|
45
|
-
"@datagrok/
|
|
42
|
+
"@datagrok-libraries/helm-web-editor": "^1.1.8",
|
|
43
|
+
"@datagrok-libraries/js-draw-lite": "^0.0.6",
|
|
44
|
+
"@datagrok/bio": "^2.14.2",
|
|
45
|
+
"@datagrok/helm": "^2.4.1",
|
|
46
|
+
"@datagrok/chem": "^1.11.3",
|
|
46
47
|
"@types/jquery": "^3.5.14",
|
|
47
48
|
"@types/js-yaml": "^4.0.5",
|
|
48
49
|
"@types/lodash": "^4.14.202",
|
|
@@ -4,6 +4,7 @@ import * as DG from 'datagrok-api/dg';
|
|
|
4
4
|
|
|
5
5
|
import {getMonomerLibHelper, IMonomerLibHelper} from '@datagrok-libraries/bio/src/monomer-works/monomer-utils';
|
|
6
6
|
import {IMonomerLib, Monomer} from '@datagrok-libraries/bio/src/types';
|
|
7
|
+
import {LoggerWrapper} from '@datagrok-libraries/bio/src/utils/logger';
|
|
7
8
|
|
|
8
9
|
import {APP_NAME} from '../view/const';
|
|
9
10
|
import {DEFAULT_LIB_FILENAME, FALLBACK_LIB_PATH} from './data-loader/const';
|
|
@@ -12,7 +13,6 @@ import {ITranslationHelper} from '../../../types';
|
|
|
12
13
|
import {SequenceValidator} from './parsing-validation/sequence-validator';
|
|
13
14
|
import {JsonData, loadJsonData} from './data-loader/json-loader';
|
|
14
15
|
import {MonomerLibWrapper} from './monomer-lib/lib-wrapper';
|
|
15
|
-
import {_package} from '../../../package';
|
|
16
16
|
import {FormatConverter} from '../../translator/model/format-converter';
|
|
17
17
|
import {FormatDetector} from './parsing-validation/format-detector';
|
|
18
18
|
import {highlightInvalidSubsequence} from '../view/components/colored-input/input-painters';
|
|
@@ -39,8 +39,10 @@ export class OligoToolkitPackage extends DG.Package implements ITranslationHelpe
|
|
|
39
39
|
return this._monomerLibWrapper;
|
|
40
40
|
}
|
|
41
41
|
|
|
42
|
-
constructor() {
|
|
42
|
+
constructor(opts: { debug: boolean } = {debug: false}) {
|
|
43
43
|
super();
|
|
44
|
+
// @ts-ignore
|
|
45
|
+
super._logger = new LoggerWrapper(super.logger, opts.debug);
|
|
44
46
|
}
|
|
45
47
|
|
|
46
48
|
private initPromise?: Promise<void>;
|
|
@@ -51,7 +53,7 @@ export class OligoToolkitPackage extends DG.Package implements ITranslationHelpe
|
|
|
51
53
|
const packageSettings = await this.getSettings();
|
|
52
54
|
let monomersPath: string = packageSettings['MonomersPath'];
|
|
53
55
|
if (!monomersPath || !(await grok.dapi.files.exists(monomersPath))) {
|
|
54
|
-
|
|
56
|
+
this.logger.warning(`Monomers path '${monomersPath}' not found. ` +
|
|
55
57
|
`Fallback to monomers sample path '${FALLBACK_LIB_PATH}'.`);
|
|
56
58
|
monomersPath = FALLBACK_LIB_PATH;
|
|
57
59
|
}
|
package/src/package-test.ts
CHANGED
package/src/package.ts
CHANGED
|
@@ -21,9 +21,9 @@ import {_setPeptideColumn} from './polytool/utils';
|
|
|
21
21
|
import {PolyToolCsvLibHandler} from './polytool/csv-to-json-monomer-lib-converter';
|
|
22
22
|
import {ITranslationHelper} from './types';
|
|
23
23
|
import {addContextMenuUI} from './utils/context-menu';
|
|
24
|
-
import {
|
|
24
|
+
import {NOTATION} from '@datagrok-libraries/bio/src/utils/macromolecule/consts';
|
|
25
25
|
|
|
26
|
-
export const _package: OligoToolkitPackage = new OligoToolkitPackage();
|
|
26
|
+
export const _package: OligoToolkitPackage = new OligoToolkitPackage(/*{debug: true}/**/);
|
|
27
27
|
|
|
28
28
|
//name: Oligo Toolkit
|
|
29
29
|
//meta.icon: img/icons/toolkit.png
|
|
@@ -169,7 +169,7 @@ export async function polyToolConvert(): Promise<void> {
|
|
|
169
169
|
//name: polyToolEnumerateHelm
|
|
170
170
|
//description: Perform cyclization of polymers
|
|
171
171
|
export async function polyToolEnumerateHelm(): Promise<void> {
|
|
172
|
-
polyToolEnumerateHelmUI();
|
|
172
|
+
await polyToolEnumerateHelmUI();
|
|
173
173
|
}
|
|
174
174
|
|
|
175
175
|
//top-menu: Bio | Convert | PolyTool-Enumerate Chem
|
|
@@ -213,7 +213,7 @@ export function addContextMenu(event: DG.EventData): void {
|
|
|
213
213
|
// export async function ptConverterApp(): Promise<void> {
|
|
214
214
|
// const view = grok.shell.v as DG.TableView;
|
|
215
215
|
// const table = view.dataFrame;
|
|
216
|
-
// const colNames = table.columns.names();
|
|
216
|
+
// const colNames = table.columns.names();
|
|
217
217
|
// let covertableName = '';
|
|
218
218
|
|
|
219
219
|
// for (let i = 0; i < colNames.length; i++) {
|
|
@@ -229,7 +229,7 @@ export function addContextMenu(event: DG.EventData): void {
|
|
|
229
229
|
// else {
|
|
230
230
|
// const dialog = await getPolyToolConversionDialog();
|
|
231
231
|
// dialog.show();
|
|
232
|
-
// }
|
|
232
|
+
// }
|
|
233
233
|
// }
|
|
234
234
|
|
|
235
235
|
//name: PolyTool Enumerator Helm
|
|
@@ -237,7 +237,7 @@ export function addContextMenu(event: DG.EventData): void {
|
|
|
237
237
|
//meta.browsePath: PolyTool
|
|
238
238
|
//tags: app
|
|
239
239
|
export async function ptEnumeratorHelmApp(): Promise<void> {
|
|
240
|
-
polyToolEnumerateHelmUI();
|
|
240
|
+
await polyToolEnumerateHelmUI();
|
|
241
241
|
}
|
|
242
242
|
|
|
243
243
|
//name: PolyTool Enumerator Chem
|
|
@@ -246,4 +246,4 @@ export async function ptEnumeratorHelmApp(): Promise<void> {
|
|
|
246
246
|
//tags: app
|
|
247
247
|
export async function ptEnumeratorChemApp(): Promise<void> {
|
|
248
248
|
polyToolEnumerateChemUI();
|
|
249
|
-
}
|
|
249
|
+
}
|
package/src/polytool/const.ts
CHANGED
|
@@ -26,3 +26,13 @@ export const R_GROUP_BLOCK_DUMMY = [
|
|
|
26
26
|
'label': 'R3'
|
|
27
27
|
}
|
|
28
28
|
];
|
|
29
|
+
|
|
30
|
+
export const PT_ERROR_DATAFRAME = 'No dataframe with macromolecule columns open';
|
|
31
|
+
export const PT_WARNING_COLUMN = 'No marcomolecule column chosen!';
|
|
32
|
+
|
|
33
|
+
export const PT_UI_GET_HELM = 'Get HELM';
|
|
34
|
+
export const PT_UI_ADD_HELM = 'Add HELM column';
|
|
35
|
+
export const PT_UI_USE_CHIRALITY = 'Chirality engine';
|
|
36
|
+
export const PT_UI_DIALOG_CONVERSION = 'Poly Tool Conversion';
|
|
37
|
+
export const PT_UI_DIALOG_ENUMERATION = 'Poly Tool Enumeration';
|
|
38
|
+
export const PT_UI_RULES_USED = 'Rules used';
|
|
@@ -3,11 +3,14 @@ import * as grok from 'datagrok-api/grok';
|
|
|
3
3
|
import * as ui from 'datagrok-api/ui';
|
|
4
4
|
import * as DG from 'datagrok-api/dg';
|
|
5
5
|
|
|
6
|
+
import $ from 'cash-dom';
|
|
6
7
|
import wu from 'wu';
|
|
8
|
+
import {Unsubscribable} from 'rxjs';
|
|
7
9
|
|
|
8
10
|
import {NOTATION} from '@datagrok-libraries/bio/src/utils/macromolecule/consts';
|
|
9
11
|
import {getHelmHelper} from '@datagrok-libraries/bio/src/helm/helm-helper';
|
|
10
|
-
import {HelmAtom} from '@datagrok-libraries/bio/src/helm/types';
|
|
12
|
+
import {HelmAtom, HelmMol} from '@datagrok-libraries/bio/src/helm/types';
|
|
13
|
+
// import {FormsViewer} from '@datagrok-libraries/utils/src/viewers/forms-viewer';
|
|
11
14
|
|
|
12
15
|
import {RuleInputs, RULES_PATH, RULES_STORAGE_NAME} from './pt-rules';
|
|
13
16
|
import {addTransformedColumn} from './pt-conversion';
|
|
@@ -15,27 +18,74 @@ import {addTransformedColumn} from './pt-conversion';
|
|
|
15
18
|
import {handleError} from './utils';
|
|
16
19
|
import {defaultErrorHandler} from '../utils/err-info';
|
|
17
20
|
import {getLibrariesList} from './utils';
|
|
18
|
-
import {
|
|
21
|
+
import {getPtEnumeratorHelm, PT_HELM_EXAMPLE} from './pt-enumeration-helm';
|
|
19
22
|
import {getEnumerationChem, PT_CHEM_EXAMPLE} from './pt-enumeration-chem';
|
|
23
|
+
import {
|
|
24
|
+
PolyToolEnumeratorParams, PolyToolEnumeratorType, PolyToolEnumeratorTypes, PolyToolPlaceholders
|
|
25
|
+
} from './types';
|
|
26
|
+
|
|
27
|
+
import {_package} from '../package';
|
|
28
|
+
import {PolyToolPlaceholdersInput} from './pt-placeholders-input';
|
|
29
|
+
import {InputBase} from 'datagrok-api/dg';
|
|
30
|
+
import {PT_ERROR_DATAFRAME, PT_UI_ADD_HELM, PT_UI_DIALOG_CONVERSION, PT_UI_DIALOG_ENUMERATION, PT_UI_GET_HELM, PT_UI_RULES_USED, PT_UI_USE_CHIRALITY, PT_WARNING_COLUMN} from './const';
|
|
31
|
+
import {PolyToolEnumerateDialog} from './pt-enumeration-helm-dialog';
|
|
32
|
+
|
|
33
|
+
export async function polyToolEnumerateHelmUI(cell?: DG.Cell): Promise<void> {
|
|
34
|
+
const maxWidth = window.innerWidth;
|
|
35
|
+
const maxHeight = window.innerHeight;
|
|
36
|
+
|
|
37
|
+
try {
|
|
38
|
+
const resizeInputs = () => {
|
|
39
|
+
const contentHeight = $(dialog.root).find('div.d4-dialog-contents').get(0)!.clientHeight;
|
|
40
|
+
|
|
41
|
+
const fitInputs: { [idx: number]: number } = {1: 1 /*, 3: 0.5*/};
|
|
42
|
+
const fitInputsSumHeight = Object.values(fitInputs).reduce((sum, h) => sum + h, 0);
|
|
43
|
+
|
|
44
|
+
const otherInputsHeight: number = dialog.inputs.filter((input, idx) => !(idx in fitInputs))
|
|
45
|
+
.map((input) => input.root.offsetHeight).reduce((sum, h) => sum + h, 0);
|
|
46
|
+
const remainFitHeight = contentHeight - otherInputsHeight - 38;
|
|
47
|
+
dialog.inputs.forEach((input, idx) => {
|
|
48
|
+
if (idx in fitInputs) {
|
|
49
|
+
const inputFitHeight = remainFitHeight * fitInputs[idx] / fitInputsSumHeight;
|
|
50
|
+
input.root.style.height = `${inputFitHeight}px`;
|
|
51
|
+
}
|
|
52
|
+
});
|
|
53
|
+
};
|
|
54
|
+
const [dialog, inputs] = await PolyToolEnumerateDialog.create2(cell, resizeInputs);
|
|
55
|
+
|
|
56
|
+
let isFirstShow = true;
|
|
57
|
+
ui.onSizeChanged(dialog.root).subscribe(() => {
|
|
58
|
+
if (isFirstShow) {
|
|
59
|
+
const dialogInputList = dialog.inputs;
|
|
60
|
+
const dialogRootCash = $(dialog.root);
|
|
61
|
+
const contentMaxHeight = maxHeight
|
|
62
|
+
- dialogRootCash.find('div.d4-dialog-header').get(0)!.offsetHeight
|
|
63
|
+
- dialogRootCash.find('div.d4-dialog-footer').get(0)!.offsetHeight;
|
|
64
|
+
|
|
65
|
+
// dialog.inputs2.macromolecule.root.style.backgroundColor = '#CCFFCC';
|
|
66
|
+
|
|
67
|
+
const dialogWidth = maxWidth * 0.7;
|
|
68
|
+
const dialogHeight = maxHeight * 0.7;
|
|
69
|
+
|
|
70
|
+
// Centered, but resizable dialog
|
|
71
|
+
dialog.root.style.width = `${Math.min(maxWidth, dialogWidth)}px`;
|
|
72
|
+
dialog.root.style.height = `${Math.min(maxHeight, dialogHeight)}px`;
|
|
73
|
+
dialog.root.style.left = `${Math.floor((maxWidth - dialog.root.offsetWidth) / 2)}px`;
|
|
74
|
+
dialog.root.style.top = `${Math.floor((maxHeight - dialog.root.offsetHeight) / 2)}px`;
|
|
75
|
+
|
|
76
|
+
isFirstShow = false;
|
|
77
|
+
}
|
|
20
78
|
|
|
21
|
-
|
|
22
|
-
const PT_WARNING_COLUMN = 'No marcomolecule column chosen!';
|
|
23
|
-
|
|
24
|
-
const PT_UI_GET_HELM = 'Get HELM';
|
|
25
|
-
const PT_UI_ADD_HELM = 'Add HELM column';
|
|
26
|
-
const PT_UI_USE_CHIRALITY = 'Chirality engine';
|
|
27
|
-
const PT_UI_DIALOG_CONVERSION = 'Poly Tool Conversion';
|
|
28
|
-
const PT_UI_DIALOG_ENUMERATION = 'Poly Tool Enumeration';
|
|
29
|
-
const PT_UI_RULES_USED = 'Rules used';
|
|
30
|
-
|
|
31
|
-
export function polyToolEnumerateHelmUI(cell?: DG.Cell): void {
|
|
32
|
-
getPolyToolEnumerationHelmDialog(cell)
|
|
33
|
-
.then((dialog) => {
|
|
34
|
-
dialog.show({resizable: true});
|
|
35
|
-
})
|
|
36
|
-
.catch((_err: any) => {
|
|
37
|
-
grok.shell.warning('To run PolyTool Enumeration, sketch the macromolecule and select monomers to vary');
|
|
79
|
+
resizeInputs();
|
|
38
80
|
});
|
|
81
|
+
|
|
82
|
+
_package.logger.debug('PolyToolEnumerateHelmUI: dialog before show');
|
|
83
|
+
const res = dialog.show({width: Math.max(350, maxWidth * 0.7), /* center: true,*/ resizable: true});
|
|
84
|
+
_package.logger.debug('PolyToolEnumerateHelmUI: dialog after show');
|
|
85
|
+
const k = 42;
|
|
86
|
+
} catch (_err: any) {
|
|
87
|
+
grok.shell.warning('To run PolyTool Enumeration, sketch the macromolecule and select monomers to vary');
|
|
88
|
+
}
|
|
39
89
|
}
|
|
40
90
|
|
|
41
91
|
export function polyToolEnumerateChemUI(cell?: DG.Cell): void {
|
|
@@ -104,61 +154,6 @@ export async function getPolyToolConversionDialog(targetCol?: DG.Column): Promis
|
|
|
104
154
|
return dialog;
|
|
105
155
|
}
|
|
106
156
|
|
|
107
|
-
async function getPolyToolEnumerationHelmDialog(cell?: DG.Cell): Promise<DG.Dialog> {
|
|
108
|
-
const [libList, helmHelper] = await Promise.all([
|
|
109
|
-
getLibrariesList(), getHelmHelper()]);
|
|
110
|
-
|
|
111
|
-
const helmValue = cell ? cell.value : PT_HELM_EXAMPLE;
|
|
112
|
-
|
|
113
|
-
const helmInput = helmHelper.createHelmInput('Macromolecule', {value: helmValue});
|
|
114
|
-
const screenLibrary = ui.input.choice('Library to use', {value: null, items: libList});
|
|
115
|
-
|
|
116
|
-
helmInput.input.setAttribute('style', `min-width:250px!important;`);
|
|
117
|
-
screenLibrary.input.setAttribute('style', `min-width:250px!important;`);
|
|
118
|
-
|
|
119
|
-
const div = ui.div([
|
|
120
|
-
helmInput.root,
|
|
121
|
-
screenLibrary.root
|
|
122
|
-
]);
|
|
123
|
-
|
|
124
|
-
// Displays the molecule from a current cell (monitors changes)
|
|
125
|
-
const cccSubs = grok.events.onCurrentCellChanged.subscribe(() => {
|
|
126
|
-
const cell = grok.shell.tv.dataFrame.currentCell;
|
|
127
|
-
|
|
128
|
-
if (cell.column.semType === DG.SEMTYPE.MACROMOLECULE && cell.column.meta.units === NOTATION.HELM)
|
|
129
|
-
helmInput.stringValue = cell.value;
|
|
130
|
-
});
|
|
131
|
-
|
|
132
|
-
const dialog = ui.dialog(PT_UI_DIALOG_ENUMERATION)
|
|
133
|
-
.add(div)
|
|
134
|
-
.onOK(async () => {
|
|
135
|
-
try {
|
|
136
|
-
const helmString = helmInput.stringValue;
|
|
137
|
-
const helmSelections: number[] = wu.enumerate<HelmAtom>(helmInput.value.atoms)
|
|
138
|
-
.filter(([a, aI]) => a.highlighted)
|
|
139
|
-
.map(([a, aI]) => aI).toArray();
|
|
140
|
-
if (helmString === undefined || helmString === '') {
|
|
141
|
-
grok.shell.warning('PolyTool: no molecule was provided');
|
|
142
|
-
} else if (helmSelections === undefined || helmSelections.length < 1) {
|
|
143
|
-
grok.shell.warning('PolyTool: no selection was provided');
|
|
144
|
-
} else {
|
|
145
|
-
const molecules = await getEnumerationHelm(helmString, helmSelections, screenLibrary.value!);
|
|
146
|
-
const molCol = DG.Column.fromStrings('Enumerated', molecules);
|
|
147
|
-
const df = DG.DataFrame.fromColumns([molCol]);
|
|
148
|
-
grok.shell.addTableView(df);
|
|
149
|
-
}
|
|
150
|
-
} catch (err: any) {
|
|
151
|
-
defaultErrorHandler(err);
|
|
152
|
-
} finally {
|
|
153
|
-
cccSubs.unsubscribe();
|
|
154
|
-
}
|
|
155
|
-
}).onCancel(() => {
|
|
156
|
-
cccSubs.unsubscribe();
|
|
157
|
-
});
|
|
158
|
-
|
|
159
|
-
return dialog;
|
|
160
|
-
}
|
|
161
|
-
|
|
162
157
|
async function getPolyToolEnumerationChemDialog(cell?: DG.Cell): Promise<DG.Dialog> {
|
|
163
158
|
const [libList, helmHelper] = await Promise.all([
|
|
164
159
|
getLibrariesList(), getHelmHelper()]);
|
|
@@ -87,23 +87,8 @@ export async function getEnumerationChem(molString: string, screenLibrary: strin
|
|
|
87
87
|
//TODO: use RDKit linking function when exposed
|
|
88
88
|
const smiResRaw = `${smiScaffold}.${smilesSubsts[i]}`.replaceAll('[1*]C', 'C([1*])').replaceAll('[1*]c', 'c([1*])').replaceAll('[1*]O', 'O([1*])').replaceAll('[1*]N', 'N([1*])');
|
|
89
89
|
const smiRes = `${smiResRaw}`.replaceAll('([1*])', '9').replaceAll('[1*]', '9');
|
|
90
|
-
molRes = rdkitModule.get_mol(smiRes)
|
|
90
|
+
molRes = rdkitModule.get_mol(smiRes, JSON.stringify({mappedDummiesAreRGroups: true}))
|
|
91
91
|
let molV3 = molRes.get_v3Kmolblock();
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
//TODO: use RDKit when MASS problem is solved
|
|
95
|
-
while(molV3.includes('MASS')) {
|
|
96
|
-
|
|
97
|
-
//MASS=2 VAL=1
|
|
98
|
-
//RGROUPS=(1 2)
|
|
99
|
-
const idx = molV3.indexOf('MASS');
|
|
100
|
-
const rNum = parseInt(molV3.slice(idx + 5, idx + 6));
|
|
101
|
-
const replace = `RGROUPS=(1 ${rNum})`;
|
|
102
|
-
molV3 = molV3.substring(0, idx) + replace + molV3.substring(idx + 12);
|
|
103
|
-
}
|
|
104
|
-
|
|
105
|
-
molV3 = molV3.replaceAll('R ', 'R# ')
|
|
106
|
-
|
|
107
92
|
enumerations[i] = molV3;
|
|
108
93
|
}
|
|
109
94
|
catch(err:any) {
|
|
@@ -0,0 +1,218 @@
|
|
|
1
|
+
import * as ui from 'datagrok-api/ui';
|
|
2
|
+
import * as grok from 'datagrok-api/grok';
|
|
3
|
+
import * as DG from 'datagrok-api/dg';
|
|
4
|
+
|
|
5
|
+
import wu from 'wu';
|
|
6
|
+
import {Unsubscribable} from 'rxjs';
|
|
7
|
+
|
|
8
|
+
import {NOTATION} from '@datagrok-libraries/bio/src/utils/macromolecule';
|
|
9
|
+
import {HelmAtom, HelmMol} from '@datagrok-libraries/helm-web-editor/src/types/org-helm';
|
|
10
|
+
import {getHelmHelper, HelmInputBase} from '@datagrok-libraries/bio/src/helm/helm-helper';
|
|
11
|
+
|
|
12
|
+
import {
|
|
13
|
+
PolyToolEnumeratorParams, PolyToolEnumeratorType, PolyToolEnumeratorTypes, PolyToolPlaceholders
|
|
14
|
+
} from './types';
|
|
15
|
+
|
|
16
|
+
import {PT_UI_DIALOG_ENUMERATION} from './const';
|
|
17
|
+
import {getLibrariesList} from './utils';
|
|
18
|
+
import {getPtEnumeratorHelm, PT_HELM_EXAMPLE} from './pt-enumeration-helm';
|
|
19
|
+
import {PolyToolPlaceholdersInput} from './pt-placeholders-input';
|
|
20
|
+
import {Dialog} from 'datagrok-api/dg';
|
|
21
|
+
import {defaultErrorHandler} from '../utils/err-info';
|
|
22
|
+
|
|
23
|
+
import {_package} from '../package';
|
|
24
|
+
|
|
25
|
+
type PolyToolEnumerateInputs = {
|
|
26
|
+
enumeratorType: DG.ChoiceInput<PolyToolEnumeratorType>
|
|
27
|
+
macromolecule: HelmInputBase;
|
|
28
|
+
placeholders: PolyToolPlaceholdersInput;
|
|
29
|
+
toAtomicLevel: DG.InputBase<boolean>;
|
|
30
|
+
};
|
|
31
|
+
|
|
32
|
+
|
|
33
|
+
export class PolyToolEnumerateDialog extends DG.Dialog {
|
|
34
|
+
protected constructor(
|
|
35
|
+
// public readonly inputs2: PolyToolEnumerateInputs
|
|
36
|
+
) {
|
|
37
|
+
const dlg = ui.dialog({title: PT_UI_DIALOG_ENUMERATION});
|
|
38
|
+
super(dlg.dart);
|
|
39
|
+
|
|
40
|
+
// for (const [key, value] of Object.entries(this.inputs2)) { this.add(value); }
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
public override show(options?: { modal?: boolean; resizable?: boolean; fullScreen?: boolean; center?: boolean; centerAt?: Element; x?: number; y?: number; width?: number; height?: number; backgroundColor?: string; showNextTo?: HTMLElement }): Dialog {
|
|
44
|
+
return super.show(options);
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
public static async create2(cell?: DG.Cell, resizeInputs?: () => void): Promise<[PolyToolEnumerateDialog, PolyToolEnumerateInputs]> {
|
|
48
|
+
const logPrefix = `ST: PT: HelmDialog()`;
|
|
49
|
+
|
|
50
|
+
const [libList, helmHelper] = await Promise.all([getLibrariesList(), getHelmHelper()]);
|
|
51
|
+
|
|
52
|
+
const helmValue = cell ? cell.value : PT_HELM_EXAMPLE;
|
|
53
|
+
const posDf = DG.DataFrame.fromObjects([{Position: '', Monomers: ''}, {Position: '', Monomers: ''}]);
|
|
54
|
+
|
|
55
|
+
const macromoleculeInput = helmHelper.createHelmInput(
|
|
56
|
+
'Macromolecule', {value: helmValue, editable: false});
|
|
57
|
+
|
|
58
|
+
const inputs: PolyToolEnumerateInputs = {
|
|
59
|
+
enumeratorType: ui.input.choice<PolyToolEnumeratorType>(
|
|
60
|
+
'Enumerator type', {
|
|
61
|
+
value: PolyToolEnumeratorTypes.Single,
|
|
62
|
+
items: Object.values(PolyToolEnumeratorTypes)
|
|
63
|
+
}) as DG.ChoiceInput<PolyToolEnumeratorType>,
|
|
64
|
+
macromolecule: macromoleculeInput,
|
|
65
|
+
|
|
66
|
+
placeholders: await PolyToolPlaceholdersInput.create(
|
|
67
|
+
'Placeholders', posDf, {
|
|
68
|
+
showAddNewRowIcon: true,
|
|
69
|
+
showRemoveRowIcon: true,
|
|
70
|
+
showRowHeader: false,
|
|
71
|
+
showCellTooltip: false,
|
|
72
|
+
}/*, 2/**/),
|
|
73
|
+
|
|
74
|
+
toAtomicLevel: ui.input.bool(
|
|
75
|
+
'To atomic level', {value: true}),
|
|
76
|
+
};
|
|
77
|
+
|
|
78
|
+
const subs: Unsubscribable[] = [];
|
|
79
|
+
const destroy = () => { for (const sub of subs) sub.unsubscribe(); };
|
|
80
|
+
|
|
81
|
+
subs.push(inputs.macromolecule.onMouseMove.subscribe((e: MouseEvent) => {
|
|
82
|
+
try {
|
|
83
|
+
_package.logger.debug(`${logPrefix}, placeholdersInput.onMouseMove()`);
|
|
84
|
+
|
|
85
|
+
const argsX = e.offsetX;
|
|
86
|
+
const argsY = e.offsetY;
|
|
87
|
+
const mol = inputs.macromolecule.molValue;
|
|
88
|
+
const hoveredAtom = helmHelper.getHoveredAtom(argsX, argsY, mol, inputs.macromolecule.root.clientHeight);
|
|
89
|
+
if (hoveredAtom) {
|
|
90
|
+
const hoveredAtomContIdx = hoveredAtom._parent.atoms.indexOf(hoveredAtom);
|
|
91
|
+
const hoveredAtomContIdxStr = (hoveredAtomContIdx + 1).toString();
|
|
92
|
+
const substitutingMonomers = inputs.placeholders.placeholdersValue[hoveredAtomContIdx];
|
|
93
|
+
|
|
94
|
+
if (substitutingMonomers) {
|
|
95
|
+
const cnt = ui.divText(substitutingMonomers.join(', '));
|
|
96
|
+
inputs.macromolecule.showTooltip(cnt, hoveredAtom);
|
|
97
|
+
e.preventDefault();
|
|
98
|
+
e.stopPropagation();
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
} catch (err: any) {
|
|
102
|
+
defaultErrorHandler(err, false);
|
|
103
|
+
}
|
|
104
|
+
}));
|
|
105
|
+
subs.push(inputs.macromolecule.onClick.subscribe((e: MouseEvent) => {
|
|
106
|
+
try {
|
|
107
|
+
_package.logger.debug(`${logPrefix}, placeholdersInput.onClick()`);
|
|
108
|
+
|
|
109
|
+
const argsX = e.offsetX;
|
|
110
|
+
const argsY = e.offsetY;
|
|
111
|
+
const mol = inputs.macromolecule.molValue;
|
|
112
|
+
const clickedAtom = helmHelper.getHoveredAtom(argsX, argsY, mol, inputs.macromolecule.root.clientHeight);
|
|
113
|
+
if (clickedAtom) {
|
|
114
|
+
const clickedAtomContIdx = clickedAtom._parent.atoms.indexOf(clickedAtom);
|
|
115
|
+
const clickedAtomContIdxStr = (clickedAtomContIdx + 1).toString();
|
|
116
|
+
|
|
117
|
+
const phDf = inputs.placeholders.grid.dataFrame;
|
|
118
|
+
const posList = phDf.columns.byName('Position').toList();
|
|
119
|
+
let rowIdx = posList.indexOf(clickedAtomContIdxStr);
|
|
120
|
+
if (rowIdx === -1) {
|
|
121
|
+
rowIdx = posList.findIndex((v) => isNaN(v));
|
|
122
|
+
if (rowIdx === -1)
|
|
123
|
+
rowIdx = phDf.rows.addNew([clickedAtomContIdxStr, '']).idx;
|
|
124
|
+
phDf.set('Position', rowIdx, clickedAtomContIdxStr);
|
|
125
|
+
// const tgtCell = inputs.placeholders.grid.cell('Monomers', rowIdx);
|
|
126
|
+
}
|
|
127
|
+
phDf.currentCell = phDf.cell(rowIdx, 'Monomers');
|
|
128
|
+
//const gridRowIdx = inputs.placeholders.grid.tableRowToGrid(rowIdx);
|
|
129
|
+
//const monomersGCell = inputs.placeholders.grid.cell('Monomers', gridRowIdx);
|
|
130
|
+
const k = 42;
|
|
131
|
+
}
|
|
132
|
+
} catch (err: any) {
|
|
133
|
+
defaultErrorHandler(err);
|
|
134
|
+
}
|
|
135
|
+
}));
|
|
136
|
+
subs.push(inputs.placeholders.grid.dataFrame.onDataChanged.subscribe(() => {
|
|
137
|
+
updateMolView();
|
|
138
|
+
}));
|
|
139
|
+
|
|
140
|
+
|
|
141
|
+
// TODO: suspect
|
|
142
|
+
subs.push(ui.onSizeChanged(inputs.placeholders.root).subscribe(() => {
|
|
143
|
+
if (resizeInputs) resizeInputs();
|
|
144
|
+
}));
|
|
145
|
+
|
|
146
|
+
// Displays the molecule from a current cell (monitors changes)
|
|
147
|
+
subs.push(grok.events.onCurrentCellChanged.subscribe(() => {
|
|
148
|
+
const cell = grok.shell.tv.dataFrame.currentCell;
|
|
149
|
+
|
|
150
|
+
if (cell.column.semType === DG.SEMTYPE.MACROMOLECULE && cell.column.meta.units === NOTATION.HELM)
|
|
151
|
+
inputs.macromolecule.stringValue = cell.value;
|
|
152
|
+
}));
|
|
153
|
+
|
|
154
|
+
inputs.macromolecule.root.style.setProperty('min-width', '250px', 'important');
|
|
155
|
+
// inputs.macromolecule.root.style.setProperty('max-height', '300px', 'important');
|
|
156
|
+
|
|
157
|
+
const updateMolView = () => {
|
|
158
|
+
const mol = inputs.macromolecule.molValue;
|
|
159
|
+
for (let aI = 0; aI < mol.atoms.length; aI++) {
|
|
160
|
+
const a = mol.atoms[aI];
|
|
161
|
+
a.highlighted = aI in inputs.placeholders.placeholdersValue;
|
|
162
|
+
inputs.macromolecule.redraw();
|
|
163
|
+
}
|
|
164
|
+
};
|
|
165
|
+
|
|
166
|
+
const dialog = new PolyToolEnumerateDialog()
|
|
167
|
+
.add(inputs.enumeratorType)
|
|
168
|
+
.add(inputs.macromolecule)
|
|
169
|
+
.add(inputs.placeholders)
|
|
170
|
+
.add(inputs.toAtomicLevel)
|
|
171
|
+
.onOK(async () => {
|
|
172
|
+
try {
|
|
173
|
+
const helmString = inputs.macromolecule.stringValue;
|
|
174
|
+
const helmSelections: number[] = wu.enumerate<HelmAtom>(inputs.macromolecule.molValue.atoms)
|
|
175
|
+
.filter(([a, aI]) => a.highlighted)
|
|
176
|
+
.map(([a, aI]) => aI).toArray();
|
|
177
|
+
if (helmString === undefined || helmString === '') {
|
|
178
|
+
grok.shell.warning('PolyTool: no molecule was provided');
|
|
179
|
+
} else /* if (helmSelections === undefined || helmSelections.length < 1) {
|
|
180
|
+
grok.shell.warning('PolyTool: no selection was provided');
|
|
181
|
+
} else /**/ {
|
|
182
|
+
await getHelmHelper(); // initializes JSDraw and org
|
|
183
|
+
|
|
184
|
+
const params: PolyToolEnumeratorParams = {
|
|
185
|
+
type: inputs.enumeratorType.value!,
|
|
186
|
+
placeholders: inputs.placeholders.placeholdersValue,
|
|
187
|
+
};
|
|
188
|
+
if (Object.keys(params.placeholders).length === 0) {
|
|
189
|
+
grok.shell.warning(`${PT_UI_DIALOG_ENUMERATION}: placeholders are empty`);
|
|
190
|
+
return;
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
const enumHelmList = getPtEnumeratorHelm(helmString, params);
|
|
194
|
+
const enumHelmCol = DG.Column.fromStrings('Enumerated', enumHelmList);
|
|
195
|
+
const enumeratorResDf = DG.DataFrame.fromColumns([enumHelmCol]);
|
|
196
|
+
|
|
197
|
+
if (inputs.toAtomicLevel.value) {
|
|
198
|
+
const enumMolCol = await grok.functions.call('Bio:getMolFromHelm', {
|
|
199
|
+
'df': enumeratorResDf,
|
|
200
|
+
'helmCol': enumHelmCol,
|
|
201
|
+
'chiralityEngine': true,
|
|
202
|
+
});
|
|
203
|
+
enumMolCol.name = enumeratorResDf.columns.getUnusedName(`molfile(${enumHelmCol.name})`);
|
|
204
|
+
enumMolCol.semType = DG.SEMTYPE.MOLECULE;
|
|
205
|
+
enumeratorResDf.columns.add(enumMolCol, true);
|
|
206
|
+
}
|
|
207
|
+
grok.shell.addTableView(enumeratorResDf);
|
|
208
|
+
}
|
|
209
|
+
} catch (err: any) {
|
|
210
|
+
defaultErrorHandler(err);
|
|
211
|
+
} finally {
|
|
212
|
+
destroy();
|
|
213
|
+
}
|
|
214
|
+
})
|
|
215
|
+
.onCancel(() => { destroy(); }) as PolyToolEnumerateDialog;
|
|
216
|
+
return [dialog, inputs];
|
|
217
|
+
}
|
|
218
|
+
}
|
|
@@ -1,22 +1,74 @@
|
|
|
1
1
|
import * as ui from 'datagrok-api/ui';
|
|
2
2
|
import * as grok from 'datagrok-api/grok';
|
|
3
3
|
import * as DG from 'datagrok-api/dg';
|
|
4
|
+
|
|
5
|
+
import {HelmType, JSDraw2ModuleType, OrgType, HelmAtom, HelmMol} from '@datagrok-libraries/bio/src/helm/types';
|
|
6
|
+
|
|
4
7
|
import {Chain} from './pt-conversion';
|
|
5
|
-
import {getAvailableMonomers} from './utils'
|
|
8
|
+
import {getAvailableMonomers} from './utils';
|
|
9
|
+
import {PolyToolEnumeratorParams, PolyToolEnumeratorTypes, PolyToolPlaceholders} from './types';
|
|
6
10
|
|
|
7
11
|
export const PT_HELM_EXAMPLE = 'PEPTIDE1{[R].[F].[T].[G].[H].[F].[G].[A].[A].[Y].[P].[E].[NH2]}$$$$';
|
|
8
12
|
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
const chain: Chain = Chain.fromHelm(helmString);
|
|
13
|
-
const size = helmSelections.length * variableMonomers.length;
|
|
14
|
-
const enumerations = new Array<string>(size);
|
|
13
|
+
/** Initialized by getHelmHelper via init Helm package */
|
|
14
|
+
declare const JSDraw2: JSDraw2ModuleType;
|
|
15
|
+
declare const org: OrgType;
|
|
15
16
|
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
17
|
+
function polyToolEnumeratorCore(m: HelmMol, position: number, monomerList: string[]): HelmMol[] {
|
|
18
|
+
const resMolList: HelmMol[] = new Array<HelmMol>(monomerList.length);
|
|
19
|
+
for (let i = 0; i < monomerList.length; i++) {
|
|
20
|
+
const symbolName = monomerList[i];
|
|
21
|
+
const resM = resMolList[i] = m.clone();
|
|
22
|
+
resM.atoms[position].elem = symbolName;
|
|
19
23
|
}
|
|
24
|
+
return resMolList;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
/**
|
|
28
|
+
* @param {string} helm Molecule string Helm format
|
|
29
|
+
* @param placeholders Placeholders by zero-based position key
|
|
30
|
+
* @returns {string[]} List of enumerated molecules in Helm format
|
|
31
|
+
*/
|
|
32
|
+
function getPtEnumeratorSingle(helm: string, placeholders: PolyToolPlaceholders): string[] {
|
|
33
|
+
const molHandler = new JSDraw2.MolHandler<HelmType>();
|
|
34
|
+
const plugin = new org.helm.webeditor.Plugin(molHandler);
|
|
35
|
+
const io = org.helm.webeditor.IO;
|
|
36
|
+
|
|
37
|
+
const origin = new JSDraw2.Point(0, 0);
|
|
38
|
+
io.parseHelm(plugin, helm, origin, undefined);
|
|
39
|
+
|
|
40
|
+
const coreResList: HelmMol[][] = Object.entries(placeholders)
|
|
41
|
+
.map(([p, monomerList]: [string, string[]]) => polyToolEnumeratorCore(molHandler.m, parseInt(p), monomerList));
|
|
42
|
+
const resMolList = coreResList.reduce((acc, posList) => acc.concat(posList), []);
|
|
43
|
+
|
|
44
|
+
const resHelmList = resMolList.map((m: HelmMol) => org.helm.webeditor.IO.getHelm(m)!);
|
|
45
|
+
return resHelmList;
|
|
46
|
+
}
|
|
20
47
|
|
|
21
|
-
|
|
48
|
+
function getPtEnumeratorMatrix(helm: string, placeholders: PolyToolPlaceholders): string[] {
|
|
49
|
+
const molHandler = new JSDraw2.MolHandler<HelmType>();
|
|
50
|
+
const plugin = new org.helm.webeditor.Plugin(molHandler);
|
|
51
|
+
const io = org.helm.webeditor.IO;
|
|
52
|
+
|
|
53
|
+
const origin = new JSDraw2.Point(0, 0);
|
|
54
|
+
io.parseHelm(plugin, helm, origin, undefined);
|
|
55
|
+
|
|
56
|
+
let resMolList = [molHandler.m];
|
|
57
|
+
for (const [p, monomerList] of Object.entries(placeholders)) {
|
|
58
|
+
const pos: number = parseInt(p);
|
|
59
|
+
const posResMolList: HelmMol[][] = resMolList.map((m: HelmMol) => polyToolEnumeratorCore(m, pos, monomerList));
|
|
60
|
+
resMolList = posResMolList.reduce((acc, l) => acc.concat(l), []);
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
const resHelmList = resMolList.map((m: HelmMol) => org.helm.webeditor.IO.getHelm(m)!);
|
|
64
|
+
return resHelmList;
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
export function getPtEnumeratorHelm(helm: string, params: PolyToolEnumeratorParams): string[] {
|
|
68
|
+
switch (params.type) {
|
|
69
|
+
case PolyToolEnumeratorTypes.Single:
|
|
70
|
+
return getPtEnumeratorSingle(helm, params.placeholders);
|
|
71
|
+
case PolyToolEnumeratorTypes.Matrix:
|
|
72
|
+
return getPtEnumeratorMatrix(helm, params.placeholders);
|
|
73
|
+
}
|
|
22
74
|
}
|