@datagrok/sequence-translator 1.4.1 → 1.4.3
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/.eslintrc.json +1 -1
- package/CHANGELOG.md +20 -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/files/polytool-rules/rules_example.json +10 -0
- package/files/samples/cyclized.csv +2 -1
- package/files/tests/polytool-reaction-lib.json +40 -0
- package/package.json +7 -7
- package/src/package-test.ts +9 -1
- package/src/package.ts +8 -0
- package/src/polytool/const.ts +1 -0
- package/src/polytool/pt-conversion.ts +147 -43
- package/src/polytool/pt-dialog.ts +86 -24
- package/src/polytool/pt-enumeration-helm-dialog.ts +35 -20
- package/src/polytool/pt-enumeration-helm.ts +46 -26
- package/src/polytool/pt-placeholders-breadth-input.ts +111 -0
- package/src/polytool/pt-placeholders-input.ts +13 -20
- package/src/polytool/pt-rules.ts +24 -4
- package/src/polytool/pt-unrule-dialog.ts +106 -0
- package/src/polytool/pt-unrule.ts +37 -0
- package/src/polytool/types.ts +5 -2
- package/src/tests/polytool-convert-tests.ts +8 -9
- package/src/tests/polytool-enumerate-breadth-tests.ts +116 -0
- package/src/tests/polytool-enumerate-tests.ts +40 -41
- package/src/tests/polytool-unrule-tests.ts +10 -0
- package/src/tests/toAtomicLevel-tests.ts +72 -0
|
@@ -10,7 +10,7 @@ import {NOTATION} from '@datagrok-libraries/bio/src/utils/macromolecule';
|
|
|
10
10
|
import {HelmAtom, HelmMol} from '@datagrok-libraries/helm-web-editor/src/types/org-helm';
|
|
11
11
|
import {getHelmHelper, HelmInputBase} from '@datagrok-libraries/bio/src/helm/helm-helper';
|
|
12
12
|
import {getMonomerLibHelper} from '@datagrok-libraries/bio/src/monomer-works/monomer-utils';
|
|
13
|
-
import {HelmType,
|
|
13
|
+
import {HelmType, PolymerType} from '@datagrok-libraries/bio/src/helm/types';
|
|
14
14
|
import {helmTypeToPolymerType} from '@datagrok-libraries/bio/src/monomer-works/monomer-works';
|
|
15
15
|
import {getSeqHelper, ISeqHelper} from '@datagrok-libraries/bio/src/utils/seq-helper';
|
|
16
16
|
import '@datagrok-libraries/bio/src/types/input';
|
|
@@ -25,6 +25,7 @@ import {getLibrariesList} from './utils';
|
|
|
25
25
|
import {doPolyToolEnumerateHelm, PT_HELM_EXAMPLE} from './pt-enumeration-helm';
|
|
26
26
|
import {PolyToolPlaceholdersInput} from './pt-placeholders-input';
|
|
27
27
|
import {defaultErrorHandler} from '../utils/err-info';
|
|
28
|
+
import {PolyToolPlaceholdersBreadthInput} from './pt-placeholders-breadth-input';
|
|
28
29
|
import {PT_UI_DIALOG_ENUMERATION} from './const';
|
|
29
30
|
|
|
30
31
|
import {_package} from '../package';
|
|
@@ -32,15 +33,17 @@ import {_package} from '../package';
|
|
|
32
33
|
type PolyToolEnumerateInputs = {
|
|
33
34
|
macromolecule: HelmInputBase;
|
|
34
35
|
placeholders: PolyToolPlaceholdersInput;
|
|
36
|
+
placeholdersBreadth: PolyToolPlaceholdersBreadthInput;
|
|
35
37
|
enumeratorType: DG.ChoiceInput<PolyToolEnumeratorType>
|
|
36
38
|
trivialNameCol: InputColumnBase,
|
|
37
39
|
toAtomicLevel: DG.InputBase<boolean>;
|
|
38
40
|
keepOriginal: DG.InputBase<boolean>;
|
|
39
41
|
};
|
|
40
42
|
|
|
41
|
-
type
|
|
43
|
+
type PolyToolEnumerateHelmSerialized = {
|
|
42
44
|
macromolecule: string;
|
|
43
45
|
placeholders: string;
|
|
46
|
+
placeholdersBreadth: string;
|
|
44
47
|
enumeratorType: PolyToolEnumeratorType;
|
|
45
48
|
trivialNameCol: string;
|
|
46
49
|
toAtomicLevel: boolean;
|
|
@@ -136,20 +139,27 @@ async function getPolyToolEnumerateDialog(
|
|
|
136
139
|
const trivialNameSampleDiv = ui.divText('', {style: {marginLeft: '8px', marginTop: '2px'}});
|
|
137
140
|
const warningsTextDiv = ui.divText('', {style: {color: 'red'}});
|
|
138
141
|
inputs = {
|
|
142
|
+
macromolecule: helmHelper.createHelmInput(
|
|
143
|
+
'Macromolecule', {editable: false}),
|
|
144
|
+
placeholders: await PolyToolPlaceholdersInput.create(
|
|
145
|
+
'Placeholders', {
|
|
146
|
+
showAddNewRowIcon: true,
|
|
147
|
+
showRemoveRowIcon: true,
|
|
148
|
+
showRowHeader: false,
|
|
149
|
+
showCellTooltip: false,
|
|
150
|
+
}),
|
|
139
151
|
enumeratorType: ui.input.choice<PolyToolEnumeratorType>(
|
|
140
152
|
'Enumerator type', {
|
|
141
153
|
value: PolyToolEnumeratorTypes.Single,
|
|
142
154
|
items: Object.values(PolyToolEnumeratorTypes)
|
|
143
155
|
}) as DG.ChoiceInput<PolyToolEnumeratorType>,
|
|
144
|
-
|
|
145
|
-
'
|
|
146
|
-
placeholders: await PolyToolPlaceholdersInput.create(
|
|
147
|
-
'Placeholders', {
|
|
156
|
+
placeholdersBreadth: await PolyToolPlaceholdersBreadthInput.create(
|
|
157
|
+
'Breadth', {
|
|
148
158
|
showAddNewRowIcon: true,
|
|
149
159
|
showRemoveRowIcon: true,
|
|
150
160
|
showRowHeader: false,
|
|
151
161
|
showCellTooltip: false,
|
|
152
|
-
}
|
|
162
|
+
}),
|
|
153
163
|
toAtomicLevel: ui.input.bool(
|
|
154
164
|
'To atomic level', {value: false}),
|
|
155
165
|
keepOriginal: ui.input.bool(
|
|
@@ -180,9 +190,9 @@ async function getPolyToolEnumerateDialog(
|
|
|
180
190
|
inputs.placeholders.addValidator((value: string): string | null => {
|
|
181
191
|
const errors: string[] = [];
|
|
182
192
|
try {
|
|
183
|
-
const missedMonomerList:
|
|
184
|
-
for (const
|
|
185
|
-
const pos =
|
|
193
|
+
const missedMonomerList: { polymerType: PolymerType, symbol: string }[] = [];
|
|
194
|
+
for (const ph of inputs.placeholders.placeholdersValue) {
|
|
195
|
+
const pos = ph.position;
|
|
186
196
|
if (pos >= inputs.macromolecule.molValue.atoms.length) {
|
|
187
197
|
errors.push(`There is no monomer at position ${pos + 1}.`);
|
|
188
198
|
continue;
|
|
@@ -190,7 +200,7 @@ async function getPolyToolEnumerateDialog(
|
|
|
190
200
|
const a = inputs.macromolecule.molValue.atoms[pos];
|
|
191
201
|
const helmType: HelmType = a.biotype()!;
|
|
192
202
|
const polymerType = helmTypeToPolymerType(helmType);
|
|
193
|
-
for (const symbol of
|
|
203
|
+
for (const symbol of ph.monomers) {
|
|
194
204
|
const substituteMonomer = monomerLib.getMonomer(polymerType, symbol)!;
|
|
195
205
|
// TODO: Check substitution monomer is presented in the library
|
|
196
206
|
if (!substituteMonomer || !substituteMonomer.lib)
|
|
@@ -228,8 +238,8 @@ async function getPolyToolEnumerateDialog(
|
|
|
228
238
|
const hoveredAtom = helmHelper.getHoveredAtom(argsX, argsY, mol, inputs.macromolecule.root.clientHeight);
|
|
229
239
|
if (hoveredAtom) {
|
|
230
240
|
const hoveredAtomContIdx = hoveredAtom._parent.atoms.indexOf(hoveredAtom);
|
|
231
|
-
const
|
|
232
|
-
|
|
241
|
+
const substitutingMonomers = inputs.placeholders.placeholdersValue
|
|
242
|
+
.find((ph) => ph.position === hoveredAtomContIdx)?.monomers;
|
|
233
243
|
|
|
234
244
|
if (substitutingMonomers) {
|
|
235
245
|
const cnt = ui.divText(substitutingMonomers.join(', '));
|
|
@@ -377,14 +387,17 @@ async function getPolyToolEnumerateDialog(
|
|
|
377
387
|
} else /* if (helmSelections === undefined || helmSelections.length < 1) {
|
|
378
388
|
grok.shell.warning('PolyTool: no selection was provided');
|
|
379
389
|
} else /**/ {
|
|
380
|
-
if (Object.keys(inputs.placeholders.placeholdersValue).length === 0
|
|
390
|
+
if (Object.keys(inputs.placeholders.placeholdersValue).length === 0 &&
|
|
391
|
+
Object.keys(inputs.placeholdersBreadth.placeholdersBreadthValue).length === 0
|
|
392
|
+
) {
|
|
381
393
|
grok.shell.warning(`${PT_UI_DIALOG_ENUMERATION}: placeholders are empty`);
|
|
382
394
|
return;
|
|
383
395
|
}
|
|
384
396
|
await getHelmHelper(); // initializes JSDraw and org
|
|
385
397
|
const params: PolyToolEnumeratorParams = {
|
|
386
|
-
type: inputs.enumeratorType.value!,
|
|
387
398
|
placeholders: inputs.placeholders.placeholdersValue,
|
|
399
|
+
type: inputs.enumeratorType.value!,
|
|
400
|
+
placeholdersBreadth: inputs.placeholdersBreadth.placeholdersBreadthValue,
|
|
388
401
|
keepOriginal: inputs.keepOriginal.value,
|
|
389
402
|
};
|
|
390
403
|
const enumeratorResDf = await polyToolEnumerateHelm(srcHelm, srcId, params, inputs.toAtomicLevel.value, seqHelper);
|
|
@@ -399,6 +412,7 @@ async function getPolyToolEnumerateDialog(
|
|
|
399
412
|
.add(inputs.macromolecule)
|
|
400
413
|
.add(inputs.placeholders)
|
|
401
414
|
.add(inputs.enumeratorType)
|
|
415
|
+
.add(inputs.placeholdersBreadth)
|
|
402
416
|
.add(inputs.trivialNameCol)
|
|
403
417
|
.add(inputs.toAtomicLevel)
|
|
404
418
|
.add(inputs.keepOriginal)
|
|
@@ -412,20 +426,22 @@ async function getPolyToolEnumerateDialog(
|
|
|
412
426
|
destroy();
|
|
413
427
|
}));
|
|
414
428
|
dialog.history(
|
|
415
|
-
/* getInput */ ():
|
|
429
|
+
/* getInput */ (): PolyToolEnumerateHelmSerialized => {
|
|
416
430
|
return {
|
|
417
431
|
macromolecule: inputs.macromolecule.stringValue,
|
|
418
432
|
placeholders: inputs.placeholders.stringValue,
|
|
419
433
|
enumeratorType: inputs.enumeratorType.value,
|
|
434
|
+
placeholdersBreadth: inputs.placeholdersBreadth.stringValue,
|
|
420
435
|
trivialNameCol: inputs.trivialNameCol.stringValue,
|
|
421
436
|
toAtomicLevel: inputs.toAtomicLevel.value,
|
|
422
437
|
keepOriginal: inputs.keepOriginal.value,
|
|
423
438
|
};
|
|
424
439
|
},
|
|
425
|
-
/* applyInput */ (x:
|
|
440
|
+
/* applyInput */ (x: PolyToolEnumerateHelmSerialized): void => {
|
|
426
441
|
inputs.macromolecule.stringValue = x.macromolecule;
|
|
427
442
|
inputs.placeholders.stringValue = x.placeholders;
|
|
428
443
|
inputs.enumeratorType.value = x.enumeratorType;
|
|
444
|
+
inputs.placeholdersBreadth.stringValue = x.placeholdersBreadth;
|
|
429
445
|
inputs.trivialNameCol.stringValue = x.trivialNameCol;
|
|
430
446
|
inputs.toAtomicLevel.value = x.toAtomicLevel;
|
|
431
447
|
inputs.keepOriginal.value = x.keepOriginal;
|
|
@@ -453,9 +469,8 @@ async function polyToolEnumerateHelm(
|
|
|
453
469
|
if (toAtomicLevel) {
|
|
454
470
|
const seqHelper: ISeqHelper = await getSeqHelper();
|
|
455
471
|
const toAtomicLevelRes = await seqHelper.helmToAtomicLevel(enumHelmCol, true, true);
|
|
456
|
-
toAtomicLevelRes.molCol
|
|
457
|
-
enumeratorResDf.columns.add(toAtomicLevelRes.molCol
|
|
458
|
-
enumeratorResDf.columns.add(toAtomicLevelRes.molHighlightCol, false);
|
|
472
|
+
toAtomicLevelRes.molCol!.semType = DG.SEMTYPE.MOLECULE;
|
|
473
|
+
enumeratorResDf.columns.add(toAtomicLevelRes.molCol!, false);
|
|
459
474
|
}
|
|
460
475
|
|
|
461
476
|
if (srcId) {
|
|
@@ -10,7 +10,7 @@ import {
|
|
|
10
10
|
|
|
11
11
|
import {Chain} from './pt-conversion';
|
|
12
12
|
import {getAvailableMonomers} from './utils';
|
|
13
|
-
import {PolyToolEnumeratorParams, PolyToolEnumeratorTypes, PolyToolPlaceholders} from './types';
|
|
13
|
+
import {PolyToolEnumeratorParams, PolyToolEnumeratorTypes, PolyToolPlaceholders, PolyToolPlaceholdersBreadth} from './types';
|
|
14
14
|
|
|
15
15
|
// For example keep monomers presented in HELMCoreLibrary.json only (not [NH2])
|
|
16
16
|
export const PT_HELM_EXAMPLE = 'PEPTIDE1{R.[Aca].T.G.H.F.G.A.A.Y.P.E.[meI]}$$$$';
|
|
@@ -19,17 +19,21 @@ export const PT_HELM_EXAMPLE = 'PEPTIDE1{R.[Aca].T.G.H.F.G.A.A.Y.P.E.[meI]}$$$$'
|
|
|
19
19
|
declare const JSDraw2: JSDraw2ModuleType;
|
|
20
20
|
declare const org: OrgType;
|
|
21
21
|
|
|
22
|
-
function polyToolEnumeratorCore(m: HelmMol,
|
|
23
|
-
const resMolList: HelmMol[] = new Array<HelmMol>(monomerList.length);
|
|
24
|
-
for (let
|
|
25
|
-
const
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
22
|
+
function polyToolEnumeratorCore(m: HelmMol, start: number, end: number, monomerList: string[]): HelmMol[] {
|
|
23
|
+
const resMolList: HelmMol[] = new Array<HelmMol>(monomerList.length * (end - start + 1));
|
|
24
|
+
for (let monI: number = 0; monI < monomerList.length; ++monI) {
|
|
25
|
+
const posCount = end - start + 1;
|
|
26
|
+
for (let posI: number = 0; posI < posCount; ++posI) {
|
|
27
|
+
const pos = start + posI;
|
|
28
|
+
const newSymbol = monomerList[monI];
|
|
29
|
+
const resM = resMolList[monI * posCount + posI] = m.clone() as HelmMol;
|
|
30
|
+
const oldSymbol = resM.atoms[pos].elem;
|
|
31
|
+
resM.atoms[pos].elem = newSymbol;
|
|
29
32
|
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
+
const idOldSymbol = oldSymbol?.length > 1 ? `[${oldSymbol}]` : oldSymbol;
|
|
34
|
+
const idNewSymbol = newSymbol?.length > 1 ? `[${newSymbol}]` : newSymbol;
|
|
35
|
+
resM.name = `${m.name}-${idOldSymbol}${pos + 1}${idNewSymbol}`;
|
|
36
|
+
}
|
|
33
37
|
}
|
|
34
38
|
return resMolList;
|
|
35
39
|
}
|
|
@@ -40,18 +44,26 @@ function polyToolEnumeratorCore(m: HelmMol, position: number, monomerList: strin
|
|
|
40
44
|
* @returns {string[]} List of enumerated molecules in Helm format
|
|
41
45
|
*/
|
|
42
46
|
function getPtEnumeratorSingle(m: HelmMol, placeholders: PolyToolPlaceholders): HelmMol[] {
|
|
43
|
-
const coreResList: HelmMol[][] =
|
|
44
|
-
.map((
|
|
47
|
+
const coreResList: HelmMol[][] = placeholders
|
|
48
|
+
.map((ph) => polyToolEnumeratorCore(m, ph.position, ph.position, ph.monomers));
|
|
45
49
|
const resMolList = coreResList.reduce((acc, posList) => acc.concat(posList), []);
|
|
46
50
|
return resMolList;
|
|
47
51
|
}
|
|
48
52
|
|
|
49
53
|
function getPtEnumeratorMatrix(m: HelmMol, placeholders: PolyToolPlaceholders): HelmMol[] {
|
|
50
54
|
let resMolList = [m];
|
|
51
|
-
for (const
|
|
52
|
-
const
|
|
53
|
-
|
|
54
|
-
|
|
55
|
+
for (const ph of placeholders) {
|
|
56
|
+
const phResMolList: HelmMol[][] = resMolList.map((m: HelmMol) => polyToolEnumeratorCore(m, ph.position, ph.position, ph.monomers));
|
|
57
|
+
resMolList = phResMolList.reduce((acc, l) => acc.concat(l), []);
|
|
58
|
+
}
|
|
59
|
+
return resMolList;
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
function getPtEnumeratorBreadth(m: HelmMol, placeholdersBreadth: PolyToolPlaceholdersBreadth): HelmMol[] {
|
|
63
|
+
let resMolList = [m];
|
|
64
|
+
for (const phb of placeholdersBreadth) {
|
|
65
|
+
const phResMolList: HelmMol[][] = resMolList.map((m: HelmMol) => polyToolEnumeratorCore(m, phb.start, phb.end, phb.monomers));
|
|
66
|
+
resMolList = phResMolList.reduce((acc, l) => acc.concat(l), []);
|
|
55
67
|
}
|
|
56
68
|
return resMolList;
|
|
57
69
|
}
|
|
@@ -66,17 +78,25 @@ export function doPolyToolEnumerateHelm(
|
|
|
66
78
|
const m = molHandler.m;
|
|
67
79
|
m.name = id;
|
|
68
80
|
|
|
69
|
-
let resMolList: HelmMol[];
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
81
|
+
let resMolList: HelmMol[] = [];
|
|
82
|
+
if (params.placeholders) {
|
|
83
|
+
switch (params.type) {
|
|
84
|
+
case PolyToolEnumeratorTypes.Single: {
|
|
85
|
+
resMolList = getPtEnumeratorSingle(molHandler.m, params.placeholders);
|
|
86
|
+
break;
|
|
87
|
+
}
|
|
88
|
+
case PolyToolEnumeratorTypes.Matrix: {
|
|
89
|
+
resMolList = getPtEnumeratorMatrix(molHandler.m, params.placeholders);
|
|
90
|
+
break;
|
|
91
|
+
}
|
|
92
|
+
}
|
|
78
93
|
}
|
|
94
|
+
|
|
95
|
+
let resBreadthMolList: HelmMol[] = [];
|
|
96
|
+
if (params.placeholdersBreadth) {
|
|
97
|
+
resBreadthMolList = getPtEnumeratorBreadth(molHandler.m, params.placeholdersBreadth);
|
|
79
98
|
}
|
|
99
|
+
resMolList = resMolList.concat(resBreadthMolList);
|
|
80
100
|
|
|
81
101
|
if (params.keepOriginal)
|
|
82
102
|
resMolList = [m, ...resMolList];
|
|
@@ -0,0 +1,111 @@
|
|
|
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 {Unsubscribable} from 'rxjs';
|
|
6
|
+
import {PolyToolPlaceholders, PolyToolPlaceholdersBreadth} from './types';
|
|
7
|
+
import {parseMonomerSymbolList} from './pt-placeholders-input';
|
|
8
|
+
|
|
9
|
+
export class PolyToolPlaceholdersBreadthInput extends DG.JsInputBase<DG.DataFrame> {
|
|
10
|
+
get inputType(): string { return 'Breadth'; }
|
|
11
|
+
|
|
12
|
+
get dataType(): string { return DG.TYPE.DATA_FRAME; }
|
|
13
|
+
|
|
14
|
+
getInput(): HTMLElement { return this.gridHost; }
|
|
15
|
+
|
|
16
|
+
getValue(): DG.DataFrame { return this.grid.dataFrame; }
|
|
17
|
+
|
|
18
|
+
setValue(value: DG.DataFrame): void { this.grid.dataFrame = value; }
|
|
19
|
+
|
|
20
|
+
getStringValue(): string { return this.grid.dataFrame.toCsv(); }
|
|
21
|
+
|
|
22
|
+
setStringValue(str: string): void { this.grid.dataFrame = DG.DataFrame.fromCsv(str); }
|
|
23
|
+
|
|
24
|
+
get placeholdersBreadthValue(): PolyToolPlaceholdersBreadth {
|
|
25
|
+
return dfToPlaceholdersBreadth(this.grid.dataFrame);
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
private readonly gridHost: HTMLDivElement;
|
|
29
|
+
public readonly grid: DG.Grid;
|
|
30
|
+
|
|
31
|
+
private subs: Unsubscribable[] = [];
|
|
32
|
+
|
|
33
|
+
protected constructor(name: string | undefined, grid: DG.Grid, heightRowCount?: number) {
|
|
34
|
+
super();
|
|
35
|
+
|
|
36
|
+
if (name) this.captionLabel.innerText = name;
|
|
37
|
+
|
|
38
|
+
this.gridHost = ui.div([], {
|
|
39
|
+
classes: 'ui-input-editor',
|
|
40
|
+
style: {width: '100%', height: '100%', marginTop: '-8px', marginBottom: '8px', paddingBottom: '4px'},
|
|
41
|
+
});
|
|
42
|
+
|
|
43
|
+
this.grid = grid;
|
|
44
|
+
this.gridHost.append(this.grid.root);
|
|
45
|
+
|
|
46
|
+
if (heightRowCount != null) {
|
|
47
|
+
this.updateGridHeight(heightRowCount + 0.7);
|
|
48
|
+
} else {
|
|
49
|
+
this.updateGridHeight(this.grid.dataFrame.rowCount + 0.6);
|
|
50
|
+
this.subs.push(this.grid.dataFrame.onRowsAdded
|
|
51
|
+
.subscribe(() => { this.updateGridHeight(this.grid.dataFrame.rowCount + 0.6); }));
|
|
52
|
+
}
|
|
53
|
+
this.grid.root.style.width = `100%`;
|
|
54
|
+
|
|
55
|
+
this.subs.push(this.grid.dataFrame.onDataChanged.subscribe(() => {
|
|
56
|
+
this.fireChanged();
|
|
57
|
+
}));
|
|
58
|
+
|
|
59
|
+
this.subs.push(ui.onSizeChanged(this.grid.root).subscribe(() => {
|
|
60
|
+
this.grid.columns.byIndex(3)!.width = this.grid.root.clientWidth - this.grid.horzScroll.root.offsetWidth -
|
|
61
|
+
this.grid.columns.byIndex(0)!.width - this.grid.columns.byIndex(1)!.width - this.grid.columns.byIndex(2)!.width - 10;
|
|
62
|
+
}));
|
|
63
|
+
|
|
64
|
+
this.root.classList.add('ui-input-polytool-pos-grid');
|
|
65
|
+
this.root.append(this.gridHost);
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
detach(): void {
|
|
69
|
+
for (const sub of this.subs) sub.unsubscribe();
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
public static async create(
|
|
73
|
+
name?: string, options?: {}, heightRowCount?: number
|
|
74
|
+
): Promise<PolyToolPlaceholdersBreadthInput> {
|
|
75
|
+
const df: DG.DataFrame = DG.DataFrame.fromColumns([
|
|
76
|
+
DG.Column.fromType(DG.COLUMN_TYPE.INT, 'Start', 0),
|
|
77
|
+
DG.Column.fromType(DG.COLUMN_TYPE.INT, 'End', 0),
|
|
78
|
+
DG.Column.fromType(DG.COLUMN_TYPE.STRING, 'Monomers', 0),
|
|
79
|
+
])!;
|
|
80
|
+
const grid = (await df.plot.fromType(DG.VIEWER.GRID, options)) as DG.Grid;
|
|
81
|
+
grid.sort(['Start', 'End']);
|
|
82
|
+
return new PolyToolPlaceholdersBreadthInput(name, grid, heightRowCount);
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
// -- Update view --
|
|
86
|
+
|
|
87
|
+
private updateGridHeight(visibleRowCount: number): void {
|
|
88
|
+
const gridHeight = this.grid.colHeaderHeight + visibleRowCount * this.grid.props.rowHeight + 6 + 2;
|
|
89
|
+
this.grid.root.style.height = `${gridHeight}px`;
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
// -- Handle events --
|
|
93
|
+
|
|
94
|
+
private gridRootOnSizeChanged(): void {
|
|
95
|
+
this.grid.columns.byIndex(3)!.width = this.grid.root.clientWidth - this.grid.horzScroll.root.offsetWidth -
|
|
96
|
+
this.grid.columns.byIndex(0)!.width - this.grid.columns.byIndex(1)!.width - this.grid.columns.byIndex(2)!.width - 10;
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
export function dfToPlaceholdersBreadth(df: DG.DataFrame): PolyToolPlaceholdersBreadth {
|
|
101
|
+
const res: PolyToolPlaceholdersBreadth = [];
|
|
102
|
+
for (let rowI = 0; rowI < df.rowCount; rowI++) {
|
|
103
|
+
const startPos = parseInt(df.get('Start', rowI)) - 1;
|
|
104
|
+
const endPos = parseInt(df.get('End', rowI)) - 1;
|
|
105
|
+
if (!isNaN(startPos) && !isNaN(endPos)) {
|
|
106
|
+
const monomerSymbolList = parseMonomerSymbolList(df.get('Monomers', rowI));
|
|
107
|
+
res.push({start: startPos, end: endPos, monomers: monomerSymbolList});
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
return res;
|
|
111
|
+
}
|
|
@@ -13,23 +13,15 @@ export class PolyToolPlaceholdersInput extends DG.JsInputBase<DG.DataFrame> {
|
|
|
13
13
|
|
|
14
14
|
getInput(): HTMLElement { return this.gridHost; }
|
|
15
15
|
|
|
16
|
-
getValue(): DG.DataFrame {
|
|
17
|
-
return this.grid.dataFrame;
|
|
18
|
-
}
|
|
16
|
+
getValue(): DG.DataFrame { return this.grid.dataFrame; }
|
|
19
17
|
|
|
20
|
-
setValue(value: DG.DataFrame): void {
|
|
21
|
-
this.grid.dataFrame = value;
|
|
22
|
-
}
|
|
18
|
+
setValue(value: DG.DataFrame): void { this.grid.dataFrame = value; }
|
|
23
19
|
|
|
24
|
-
getStringValue(): string {
|
|
25
|
-
return this.grid.dataFrame.toCsv();
|
|
26
|
-
}
|
|
20
|
+
getStringValue(): string { return this.grid.dataFrame.toCsv(); }
|
|
27
21
|
|
|
28
|
-
setStringValue(str: string): void {
|
|
29
|
-
this.grid.dataFrame = DG.DataFrame.fromCsv(str);
|
|
30
|
-
}
|
|
22
|
+
setStringValue(str: string): void { this.grid.dataFrame = DG.DataFrame.fromCsv(str); }
|
|
31
23
|
|
|
32
|
-
get placeholdersValue() {
|
|
24
|
+
get placeholdersValue(): PolyToolPlaceholders {
|
|
33
25
|
return dfToPlaceholders(this.grid.dataFrame);
|
|
34
26
|
}
|
|
35
27
|
|
|
@@ -80,7 +72,9 @@ export class PolyToolPlaceholdersInput extends DG.JsInputBase<DG.DataFrame> {
|
|
|
80
72
|
public static async create(
|
|
81
73
|
name?: string, options?: {}, heightRowCount?: number
|
|
82
74
|
): Promise<PolyToolPlaceholdersInput> {
|
|
83
|
-
const df: DG.DataFrame = DG.DataFrame.
|
|
75
|
+
const df: DG.DataFrame = DG.DataFrame.fromColumns([
|
|
76
|
+
DG.Column.fromType(DG.COLUMN_TYPE.INT, 'Position', 0),
|
|
77
|
+
DG.Column.fromType(DG.COLUMN_TYPE.STRING, 'Monomers', 0),])!;
|
|
84
78
|
const grid = (await df.plot.fromType(DG.VIEWER.GRID, options)) as DG.Grid;
|
|
85
79
|
grid.sort(['Position']);
|
|
86
80
|
return new PolyToolPlaceholdersInput(name, grid, heightRowCount);
|
|
@@ -102,26 +96,25 @@ export class PolyToolPlaceholdersInput extends DG.JsInputBase<DG.DataFrame> {
|
|
|
102
96
|
}
|
|
103
97
|
|
|
104
98
|
export function getPlaceholdersFromText(src: string): PolyToolPlaceholders {
|
|
105
|
-
const res: PolyToolPlaceholders =
|
|
99
|
+
const res: PolyToolPlaceholders = [];
|
|
106
100
|
for (const line of src.split('\n')) {
|
|
107
101
|
const lineM = /^\s*(?<pos>\d+)\s*:\s*(?<monomers>.+)$/.exec(line);
|
|
108
102
|
if (lineM) {
|
|
109
103
|
const pos: number = parseInt(lineM.groups!['pos']) - 1;
|
|
110
104
|
const monomerList: string[] = lineM.groups!['monomers'].split(',').map((m) => m.trim());
|
|
111
|
-
|
|
112
|
-
res[pos].push(...monomerList);
|
|
105
|
+
res.push({position: pos, monomers: monomerList});
|
|
113
106
|
}
|
|
114
107
|
}
|
|
115
108
|
return res;
|
|
116
109
|
}
|
|
117
110
|
|
|
118
111
|
export function dfToPlaceholders(df: DG.DataFrame): PolyToolPlaceholders {
|
|
119
|
-
const res: PolyToolPlaceholders =
|
|
112
|
+
const res: PolyToolPlaceholders = [];
|
|
120
113
|
for (let rowI = 0; rowI < df.rowCount; rowI++) {
|
|
121
|
-
const pos = parseInt(df.get('Position', rowI));
|
|
114
|
+
const pos = parseInt(df.get('Position', rowI)) - 1;
|
|
122
115
|
if (!isNaN(pos)) {
|
|
123
116
|
const monomerSymbolList = parseMonomerSymbolList(df.get('Monomers', rowI));
|
|
124
|
-
res
|
|
117
|
+
res.push({position: pos, monomers: monomerSymbolList});
|
|
125
118
|
}
|
|
126
119
|
}
|
|
127
120
|
return res;
|
package/src/polytool/pt-rules.ts
CHANGED
|
@@ -5,19 +5,24 @@ import {ActiveFiles} from '@datagrok-libraries/utils/src/settings/active-files-b
|
|
|
5
5
|
export const RULES_PATH = 'System:AppData/SequenceTranslator/polytool-rules/';
|
|
6
6
|
export const RULES_STORAGE_NAME = 'Polytool';
|
|
7
7
|
export const RULES_TYPE_LINK = 'link';
|
|
8
|
+
export const RULES_TYPE_REACTION = 'reaction';
|
|
8
9
|
export const RULES_TYPE_HOMODIMER = 'fragmentDuplication';
|
|
9
10
|
export const RULES_TYPE_HETERODIMER = 'differentFragments';
|
|
10
11
|
|
|
11
12
|
export class RuleInputs extends ActiveFiles {
|
|
12
|
-
constructor(
|
|
13
|
-
|
|
13
|
+
constructor(
|
|
14
|
+
path: string, userStorageName: string, ext: string,
|
|
15
|
+
options?: { onValueChanged: (value: string[]) => void }
|
|
16
|
+
) {
|
|
17
|
+
super(path, userStorageName, ext, options);
|
|
14
18
|
}
|
|
15
19
|
}
|
|
16
20
|
|
|
17
21
|
export type Rules = {
|
|
18
22
|
homodimerCode: string | null,
|
|
19
23
|
heterodimerCode: string | null,
|
|
20
|
-
linkRules: RuleLink[]
|
|
24
|
+
linkRules: RuleLink[],
|
|
25
|
+
reactionRules: RuleReaction[]
|
|
21
26
|
}
|
|
22
27
|
|
|
23
28
|
export type RuleLink = {
|
|
@@ -30,10 +35,19 @@ export type RuleLink = {
|
|
|
30
35
|
secondLinkingGroup: number
|
|
31
36
|
}
|
|
32
37
|
|
|
38
|
+
export type RuleReaction = {
|
|
39
|
+
code: number,
|
|
40
|
+
firstMonomer: string,
|
|
41
|
+
secondMonomer: string,
|
|
42
|
+
reaction: string,
|
|
43
|
+
name: string
|
|
44
|
+
}
|
|
45
|
+
|
|
33
46
|
export async function getRules(ruleFiles: string[]): Promise<Rules> {
|
|
34
47
|
const fileSource = new DG.FileSource(RULES_PATH);
|
|
35
48
|
const linkRules: RuleLink[] = [];
|
|
36
|
-
const
|
|
49
|
+
const reactionRules: RuleReaction[] = [];
|
|
50
|
+
const rules: Rules = {homodimerCode: null, heterodimerCode: null, linkRules: linkRules, reactionRules: reactionRules};
|
|
37
51
|
|
|
38
52
|
for (let i = 0; i < ruleFiles.length; i++) {
|
|
39
53
|
const rulesRaw = await fileSource.readAsText(ruleFiles[i].replace(RULES_PATH, ''));
|
|
@@ -47,6 +61,12 @@ export async function getRules(ruleFiles: string[]): Promise<Rules> {
|
|
|
47
61
|
linkRules.push(rule);
|
|
48
62
|
break;
|
|
49
63
|
}
|
|
64
|
+
case RULES_TYPE_REACTION: {
|
|
65
|
+
const rule = ruleSingle[j].monomericSubstitution;
|
|
66
|
+
rule['code'] = ruleSingle[j].code;
|
|
67
|
+
reactionRules.push(rule);
|
|
68
|
+
break;
|
|
69
|
+
}
|
|
50
70
|
case RULES_TYPE_HOMODIMER: {
|
|
51
71
|
if (rules.homodimerCode)
|
|
52
72
|
grok.shell.warning(`PolyTool: homodimer code is duplicated in rules.`);
|
|
@@ -0,0 +1,106 @@
|
|
|
1
|
+
import * as grok from 'datagrok-api/grok';
|
|
2
|
+
import * as ui from 'datagrok-api/ui';
|
|
3
|
+
import * as DG from 'datagrok-api/dg';
|
|
4
|
+
|
|
5
|
+
import {Unsubscribable} from 'rxjs';
|
|
6
|
+
|
|
7
|
+
import {SeqHandler} from '@datagrok-libraries/bio/src/utils/seq-handler';
|
|
8
|
+
import {NOTATION} from '@datagrok-libraries/bio/src/utils/macromolecule';
|
|
9
|
+
import {getUnusedColName} from '@datagrok-libraries/bio/src/monomer-works/utils';
|
|
10
|
+
|
|
11
|
+
import {defaultErrorHandler} from '../utils/err-info';
|
|
12
|
+
import {doPolyToolUnrule} from './pt-unrule';
|
|
13
|
+
import {getRules, RuleInputs, RULES_PATH, RULES_STORAGE_NAME} from './pt-rules';
|
|
14
|
+
import {PT_ERROR_DATAFRAME, PT_UI_DIALOG_UNRULE, PT_UI_RULES_USED} from './const';
|
|
15
|
+
|
|
16
|
+
type PolyToolUnruleSerialized = {
|
|
17
|
+
rules: string[];
|
|
18
|
+
};
|
|
19
|
+
|
|
20
|
+
export async function getPolyToolUnruleDialog(srcCol?: DG.Column<string>): Promise<DG.Dialog> {
|
|
21
|
+
const subs: Unsubscribable[] = [];
|
|
22
|
+
const destroy = () => {
|
|
23
|
+
for (const sub of subs) sub.unsubscribe();
|
|
24
|
+
};
|
|
25
|
+
try {
|
|
26
|
+
let srcColVal: DG.Column<string> | undefined = srcCol;
|
|
27
|
+
if (!srcColVal) {
|
|
28
|
+
const srcColList = grok.shell.t.columns.bySemTypeAll(DG.SEMTYPE.MACROMOLECULE);
|
|
29
|
+
if (!srcColList)
|
|
30
|
+
throw new Error(PT_ERROR_DATAFRAME);
|
|
31
|
+
srcColVal = srcColList[0];
|
|
32
|
+
}
|
|
33
|
+
const srcColInput = ui.input.column('Column', {
|
|
34
|
+
table: srcColVal.dataFrame, value: srcColVal,
|
|
35
|
+
filter: (col: DG.Column) => {
|
|
36
|
+
if (col.semType !== DG.SEMTYPE.MACROMOLECULE) return false;
|
|
37
|
+
const sh = SeqHandler.forColumn(col);
|
|
38
|
+
return sh.notation === NOTATION.HELM;
|
|
39
|
+
}
|
|
40
|
+
});
|
|
41
|
+
let ruleFileList: string[];
|
|
42
|
+
const ruleInputs = new RuleInputs(RULES_PATH, RULES_STORAGE_NAME, '.json', {
|
|
43
|
+
onValueChanged: (value: string[]) => { ruleFileList = value;}
|
|
44
|
+
});
|
|
45
|
+
const rulesHeader = ui.inlineText([PT_UI_RULES_USED]);
|
|
46
|
+
const rulesForm = await ruleInputs.getForm();
|
|
47
|
+
|
|
48
|
+
const div = ui.divV([
|
|
49
|
+
srcColInput,
|
|
50
|
+
rulesHeader,
|
|
51
|
+
rulesForm
|
|
52
|
+
]);
|
|
53
|
+
|
|
54
|
+
const exec = async (): Promise<void> => {
|
|
55
|
+
try {
|
|
56
|
+
const ruleFileList = await ruleInputs.getActive();
|
|
57
|
+
await polyToolUnrule(srcColInput.value!, ruleFileList);
|
|
58
|
+
} catch (err: any) {
|
|
59
|
+
defaultErrorHandler(err);
|
|
60
|
+
}
|
|
61
|
+
};
|
|
62
|
+
|
|
63
|
+
const dialog = ui.dialog(PT_UI_DIALOG_UNRULE)
|
|
64
|
+
.add(div)
|
|
65
|
+
.onOK(() => { exec(); });
|
|
66
|
+
subs.push(dialog.onClose.subscribe(() => {
|
|
67
|
+
destroy();
|
|
68
|
+
}));
|
|
69
|
+
dialog.history(
|
|
70
|
+
/* getInput */ (): PolyToolUnruleSerialized => {
|
|
71
|
+
return {
|
|
72
|
+
rules: ruleFileList,
|
|
73
|
+
};
|
|
74
|
+
},
|
|
75
|
+
/* applyInput */ (x: PolyToolUnruleSerialized): void => {
|
|
76
|
+
ruleInputs.setActive(ruleFileList);
|
|
77
|
+
});
|
|
78
|
+
return dialog;
|
|
79
|
+
} catch (err: any) {
|
|
80
|
+
destroy(); // on failing to build a dialog
|
|
81
|
+
throw err;
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
export async function polyToolUnrule(
|
|
86
|
+
srcCol: DG.Column<string>, ruleFiles: string[]
|
|
87
|
+
): Promise<DG.Column> {
|
|
88
|
+
const pi = DG.TaskBarProgressIndicator.create('PolyTool unrule...');
|
|
89
|
+
try {
|
|
90
|
+
const table = srcCol.dataFrame;
|
|
91
|
+
const rules = await getRules(ruleFiles);
|
|
92
|
+
const resHelmList = doPolyToolUnrule(srcCol.toList(), rules);
|
|
93
|
+
const resHelmColName = `harmonized(srcCol.name)`;
|
|
94
|
+
const resHelmCol = DG.Column.fromList(DG.COLUMN_TYPE.STRING, resHelmColName, resHelmList,);
|
|
95
|
+
resHelmCol.semType = DG.SEMTYPE.MACROMOLECULE;
|
|
96
|
+
resHelmCol.meta.units = NOTATION.CUSTOM;
|
|
97
|
+
if (table) {
|
|
98
|
+
resHelmCol.name = getUnusedColName(table, resHelmColName);
|
|
99
|
+
table.columns.add(resHelmCol, true);
|
|
100
|
+
await grok.data.detectSemanticTypes(table);
|
|
101
|
+
}
|
|
102
|
+
return resHelmCol;
|
|
103
|
+
} finally {
|
|
104
|
+
pi.close();
|
|
105
|
+
}
|
|
106
|
+
}
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
import * as grok from 'datagrok-api/grok';
|
|
2
|
+
import * as ui from 'datagrok-api/ui';
|
|
3
|
+
import * as DG from 'datagrok-api/dg';
|
|
4
|
+
|
|
5
|
+
import {errInfo} from '@datagrok-libraries/bio/src/utils/err-info';
|
|
6
|
+
|
|
7
|
+
import {Chain} from './pt-conversion';
|
|
8
|
+
import {getPolyToolUnruleDialog} from './pt-unrule-dialog';
|
|
9
|
+
import {Rules} from './pt-rules';
|
|
10
|
+
|
|
11
|
+
import {_package} from '../package';
|
|
12
|
+
|
|
13
|
+
export async function polyToolUnruleUI(): Promise<void> {
|
|
14
|
+
let dialog: DG.Dialog;
|
|
15
|
+
try {
|
|
16
|
+
dialog = await getPolyToolUnruleDialog();
|
|
17
|
+
dialog.show();
|
|
18
|
+
} catch (err: any) {
|
|
19
|
+
const [errMsg, errStack] = errInfo(err);
|
|
20
|
+
grok.shell.warning('To run PolyTool Unrule, open a dataframe with Helm');
|
|
21
|
+
_package.logger.error(errMsg, undefined, errStack);
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
/** Returns list of harmonized sequences. Covered with tests. */
|
|
26
|
+
export function doPolyToolUnrule(helms: string[], rules: Rules): string[] {
|
|
27
|
+
const resHrzSeqList = new Array<string>(helms.length);
|
|
28
|
+
for (let i = 0; i < helms.length; ++i) {
|
|
29
|
+
if (!helms[i])
|
|
30
|
+
resHrzSeqList[i] = '';
|
|
31
|
+
else {
|
|
32
|
+
const chain = Chain.fromHelm(helms[i]);
|
|
33
|
+
resHrzSeqList[i] = chain.getNotation(rules);
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
return resHrzSeqList;
|
|
37
|
+
}
|