@datagrok/sequence-translator 1.4.4 → 1.4.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 +24 -0
- package/detectors.js +24 -0
- package/dist/455.js +2 -0
- package/dist/455.js.map +1 -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 +2 -2
- package/files/samples/HELM.csv +6 -0
- package/files/samples/cyclized.csv +4 -3
- package/files/samples/cyclized_MSA.csv +5 -0
- package/package.json +13 -13
- package/src/apps/common/model/oligo-toolkit-package.ts +23 -4
- package/src/apps/common/view/components/molecule-img.ts +2 -2
- package/src/apps/pattern/model/data-manager.ts +9 -9
- package/src/apps/translator/view/ui.ts +8 -8
- package/src/consts.ts +12 -0
- package/src/global.d.ts +13 -0
- package/src/package-test.ts +3 -0
- package/src/package.ts +32 -5
- package/src/polytool/pt-conversion.ts +395 -81
- package/src/polytool/pt-dialog.ts +29 -13
- package/src/polytool/pt-enumeration-helm-dialog.ts +79 -34
- package/src/polytool/pt-enumeration-helm.ts +12 -8
- package/src/polytool/pt-placeholders-breadth-input.ts +4 -4
- package/src/polytool/pt-placeholders-input.ts +6 -6
- package/src/polytool/pt-unrule-dialog.ts +7 -3
- package/src/polytool/pt-unrule.ts +4 -3
- package/src/polytool/types.ts +4 -4
- package/src/tests/polytool-chain-from-notation-tests.ts +108 -18
- package/src/tests/polytool-chain-parse-notation-tests.ts +100 -0
- package/src/tests/polytool-convert-tests.ts +102 -25
- package/src/tests/polytool-detectors-custom-notation-test.ts +43 -0
- package/src/tests/polytool-enumerate-breadth-tests.ts +4 -4
- package/src/tests/toAtomicLevel-tests.ts +1 -1
- package/src/tests/utils/detect-macromolecule-utils.ts +64 -0
- package/src/tests/{utils.ts → utils/index.ts} +2 -2
- package/src/utils/context-menu.ts +2 -5
- package/src/utils/cyclized.ts +88 -0
- package/src/utils/dimerized.ts +10 -0
|
@@ -2,98 +2,149 @@ import * as grok from 'datagrok-api/grok';
|
|
|
2
2
|
import * as ui from 'datagrok-api/ui';
|
|
3
3
|
import * as DG from 'datagrok-api/dg';
|
|
4
4
|
|
|
5
|
-
import
|
|
6
|
-
|
|
5
|
+
import wu from 'wu';
|
|
6
|
+
|
|
7
|
+
import {cleanupHelmSymbol} from '@datagrok-libraries/bio/src/helm/utils';
|
|
8
|
+
import {HelmTypes, PolymerTypes} from '@datagrok-libraries/bio/src/helm/consts';
|
|
9
|
+
import {getMonomerLibHelper} from '@datagrok-libraries/bio/src/monomer-works/monomer-utils';
|
|
7
10
|
import {IMonomerLib, IMonomerLibBase, Monomer, MonomerLibData, RGroup} from '@datagrok-libraries/bio/src/types';
|
|
8
11
|
import {RDModule, RDMol, RDReaction, MolList, RDReactionResult} from '@datagrok-libraries/chem-meta/src/rdkit-api';
|
|
9
|
-
import {HELM_REQUIRED_FIELD, HELM_RGROUP_FIELDS} from '@datagrok-libraries/bio/src/utils/const';
|
|
12
|
+
import {HELM_REQUIRED_FIELD as REQ, HELM_OPTIONAL_FIELDS as OPT, HELM_RGROUP_FIELDS, HELM_OPTIONAL_FIELDS} from '@datagrok-libraries/bio/src/utils/const';
|
|
10
13
|
import {getRdKitModule} from '@datagrok-libraries/bio/src/chem/rdkit-module';
|
|
14
|
+
import {errInfo} from '@datagrok-libraries/bio/src/utils/err-info';
|
|
15
|
+
import {HelmAtom, HelmBio, HelmMol, HelmType, JSDraw2ModuleType, OrgType} from '@datagrok-libraries/bio/src/helm/types';
|
|
16
|
+
import {IHelmHelper} from '@datagrok-libraries/bio/src/helm/helm-helper';
|
|
11
17
|
|
|
12
18
|
import {Rules, RuleLink, RuleReaction} from './pt-rules';
|
|
13
19
|
import {InvalidReactionError, MonomerNotFoundError} from './types';
|
|
14
|
-
|
|
20
|
+
|
|
15
21
|
import {_package} from '../package';
|
|
16
22
|
|
|
17
|
-
|
|
18
|
-
|
|
23
|
+
declare const JSDraw2: JSDraw2ModuleType;
|
|
24
|
+
declare const org: OrgType;
|
|
25
|
+
|
|
26
|
+
type Linkage = {
|
|
27
|
+
fChain: number,
|
|
28
|
+
sChain: number,
|
|
29
|
+
/** Continuous 1-based numbering */ fMonomer: number,
|
|
30
|
+
/** Continuous 1-based numbering */ sMonomer: number,
|
|
31
|
+
fR: number,
|
|
32
|
+
sR: number
|
|
33
|
+
}
|
|
19
34
|
|
|
20
|
-
|
|
21
|
-
// col.semType = DG.SEMTYPE.MACROMOLECULE;
|
|
22
|
-
// col.setTag('aligned', ALIGNMENT.SEQ);
|
|
23
|
-
// col.setTag('alphabet', ALPHABET.PT);
|
|
24
|
-
// }
|
|
35
|
+
type PolyToolBio = HelmBio & { i: number, j: number };
|
|
25
36
|
|
|
26
37
|
export class Chain {
|
|
27
|
-
linkages:
|
|
38
|
+
linkages: Linkage[];
|
|
28
39
|
monomers: string[][];
|
|
40
|
+
mol: HelmMol;
|
|
29
41
|
|
|
30
42
|
constructor(
|
|
31
43
|
monomers: string[][],
|
|
32
|
-
linkages:
|
|
44
|
+
linkages: Linkage[],
|
|
45
|
+
mol: HelmMol) {
|
|
33
46
|
this.linkages = linkages;
|
|
34
47
|
this.monomers = monomers;
|
|
48
|
+
this.mol = mol;
|
|
35
49
|
}
|
|
36
50
|
|
|
37
|
-
|
|
38
|
-
|
|
51
|
+
/** Parse harmonized sequence (template) from pseudo helm */
|
|
52
|
+
static parseHelm(helm: string, helmHelper: IHelmHelper) {
|
|
53
|
+
const ea = /(\w+\{.*\})\$(.*)\$(.*)\$(.*)\$/g.exec(helm)!;
|
|
54
|
+
// const fragmentation = helm.split('$');
|
|
55
|
+
const fragmentation = [ea[1], ea[2], ea[3], ea[4]];
|
|
56
|
+
|
|
39
57
|
const rawFragments = fragmentation[0].split('|');
|
|
40
58
|
const rawLinkages = fragmentation[1].split('|');
|
|
41
59
|
|
|
42
60
|
const monomers = new Array<Array<string>>(rawFragments.length);
|
|
43
|
-
const linkages:
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
sMonomer: number,
|
|
48
|
-
fR: number,
|
|
49
|
-
sR: number
|
|
50
|
-
}[] = [];
|
|
61
|
+
const linkages: Linkage[] = [];
|
|
62
|
+
|
|
63
|
+
const resHwe = helmHelper.createHelmWebEditor();
|
|
64
|
+
const resMol = resHwe.editor.m;
|
|
51
65
|
|
|
66
|
+
let counter = 0;
|
|
67
|
+
const p = new JSDraw2.Point(0, 0);
|
|
52
68
|
//HELM parsing
|
|
53
69
|
for (let i = 0; i < rawFragments.length; i++) {
|
|
54
70
|
const idxStart = rawFragments[i].indexOf('{');
|
|
55
71
|
const idxEnd = rawFragments[i].indexOf('}');
|
|
56
72
|
|
|
57
|
-
monomers[i] = rawFragments[i].slice(idxStart + 1, idxEnd).split('.');
|
|
73
|
+
monomers[i] = rawFragments[i].slice(idxStart + 1, idxEnd).split('.').map((s) => cleanupHelmSymbol(s));
|
|
74
|
+
for (let j = 0; j < monomers[i].length; j++) {
|
|
75
|
+
const elem = monomers[i][j];
|
|
76
|
+
const bio: PolyToolBio = {type: HelmTypes.AA, i: i, j: j, continuousId: counter};
|
|
77
|
+
const atom = new JSDraw2.Atom<HelmType>(p, elem, bio);
|
|
78
|
+
resMol.addAtom(atom);
|
|
79
|
+
|
|
80
|
+
if (j !== 0) {
|
|
81
|
+
const atom1 = resMol.atoms[counter - 1];
|
|
82
|
+
const atom2 = resMol.atoms[counter];
|
|
83
|
+
const bond = new JSDraw2.Bond<HelmType>(atom1, atom2);
|
|
84
|
+
bond.r1 = 2;
|
|
85
|
+
bond.r2 = 1;
|
|
86
|
+
resMol.addBond(bond);
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
counter++;
|
|
90
|
+
p.x += JSDraw2.Editor.BONDLENGTH; // Inspired by HELMWebEditor
|
|
91
|
+
}
|
|
92
|
+
p.y += 4 * JSDraw2.Editor.BONDLENGTH; // Inspired by HELMWebEditor
|
|
58
93
|
}
|
|
59
94
|
|
|
60
95
|
//HELM parsing
|
|
61
96
|
for (let i = 0; i < rawLinkages.length; i++) {
|
|
62
97
|
if (rawLinkages[i] !== '' && rawLinkages[i] !== 'V2.0') {
|
|
63
98
|
const rawData = rawLinkages[i].split(',');
|
|
64
|
-
const
|
|
65
|
-
const
|
|
66
|
-
const
|
|
67
|
-
const
|
|
68
|
-
const
|
|
99
|
+
const fChainIdx = parseInt(rawData[0].replace('PEPTIDE', '')) - 1;
|
|
100
|
+
const sChainIdx = parseInt(rawData[1].replace('PEPTIDE', '')) - 1;
|
|
101
|
+
const rawDataConnections = rawData[2].split('-');
|
|
102
|
+
const rawDataConnection1 = rawDataConnections[0].split(':');
|
|
103
|
+
const rawDataConnection2 = rawDataConnections[1].split(':');
|
|
69
104
|
|
|
70
105
|
linkages.push({
|
|
71
|
-
fChain:
|
|
72
|
-
sChain:
|
|
73
|
-
fMonomer:
|
|
74
|
-
sMonomer:
|
|
75
|
-
fR:
|
|
76
|
-
sR:
|
|
106
|
+
fChain: fChainIdx,
|
|
107
|
+
sChain: sChainIdx,
|
|
108
|
+
fMonomer: getOuterIdx(parseInt(rawDataConnection1[0]), fChainIdx, monomers),
|
|
109
|
+
sMonomer: getOuterIdx(parseInt(rawDataConnection2[0]), sChainIdx, monomers),
|
|
110
|
+
fR: parseInt(rawDataConnection1[1].replace('R', '')),
|
|
111
|
+
sR: parseInt(rawDataConnection2[1].replace('R', '')),
|
|
77
112
|
});
|
|
78
113
|
}
|
|
79
114
|
}
|
|
80
115
|
|
|
81
|
-
|
|
116
|
+
for (let i = 0; i < linkages.length; i++) {
|
|
117
|
+
const atom1 = resMol.atoms[linkages[i].fMonomer - 1];
|
|
118
|
+
const atom2 = resMol.atoms[linkages[i].sMonomer - 1];
|
|
119
|
+
const bond = new JSDraw2.Bond<HelmType>(atom1, atom2);
|
|
120
|
+
bond.r1 = linkages[i].fR;
|
|
121
|
+
bond.r2 = linkages[i].sR;
|
|
122
|
+
resMol.addBond(bond);
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
return new Chain(monomers, linkages, resMol);
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
/** Get macromolecule from harmonized sequence (template) */
|
|
129
|
+
applyRules(rules: Rules): Chain {
|
|
130
|
+
// Clone this
|
|
131
|
+
const resMonomers: string[][] = this.monomers.map((mL) => [...mL]);
|
|
132
|
+
const resLinkages: Linkage[] = [...this.linkages];
|
|
133
|
+
const resMol: HelmMol = this.mol.clone();
|
|
134
|
+
|
|
135
|
+
throw new Error('not implemented');
|
|
136
|
+
|
|
137
|
+
const chain = new Chain(resMonomers, resLinkages, resMol);
|
|
138
|
+
return chain;
|
|
82
139
|
}
|
|
83
140
|
|
|
84
|
-
|
|
141
|
+
/** @deprecated Use {@link parseNotation} and {@link applyRules} instead. */
|
|
142
|
+
static fromNotation(sequence: string, rules: Rules, helmHelper: IHelmHelper): Chain {
|
|
85
143
|
const heterodimerCode = rules.heterodimerCode;
|
|
86
144
|
const homodimerCode = rules.homodimerCode;
|
|
87
|
-
const mainFragments: string[] = [];
|
|
88
145
|
|
|
89
|
-
const
|
|
90
|
-
|
|
91
|
-
sChain: number,
|
|
92
|
-
fMonomer: number,
|
|
93
|
-
sMonomer: number,
|
|
94
|
-
fR: number,
|
|
95
|
-
sR: number
|
|
96
|
-
}[] = [];
|
|
146
|
+
const mainFragments: string[] = [];
|
|
147
|
+
const linkages: Linkage[] = [];
|
|
97
148
|
|
|
98
149
|
//NOTICE: this works only with simple single heterodimers
|
|
99
150
|
const heterodimeric = heterodimerCode !== null ? sequence.split(`(${rules.heterodimerCode!})`) : '';
|
|
@@ -164,6 +215,11 @@ export class Chain {
|
|
|
164
215
|
|
|
165
216
|
const monomersAll: string[][] = [];
|
|
166
217
|
|
|
218
|
+
const resHwe = helmHelper.createHelmWebEditor();
|
|
219
|
+
const resMol = resHwe.editor.m;
|
|
220
|
+
|
|
221
|
+
let counter = 0;
|
|
222
|
+
const p = new JSDraw2.Point(0, 0);
|
|
167
223
|
for (let i = 0; i < monomers.length; i++) {
|
|
168
224
|
const linkedPositions = this.getLinkedPositions(monomers[i], rules.reactionRules);
|
|
169
225
|
const [monomersCycled, allPos1, allPos2, ruleN] =
|
|
@@ -172,14 +228,41 @@ export class Chain {
|
|
|
172
228
|
if (allPos1.length >= 1) {
|
|
173
229
|
const ch1 = new Array<string>(allPos2[0] - 1);
|
|
174
230
|
const ch2 = new Array<string>(monomersCycled.length - allPos2[0]);
|
|
175
|
-
for (let j = 0; j < allPos2[0] - 1; j++)
|
|
176
|
-
ch1[j] = monomersCycled[j];
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
231
|
+
for (let j = 0; j < allPos2[0] - 1; j++) {
|
|
232
|
+
const elem = ch1[j] = monomersCycled[j];
|
|
233
|
+
const bio: PolyToolBio = {type: HelmTypes.AA, i: i, j: j, continuousId: counter};
|
|
234
|
+
const atom: HelmAtom = new JSDraw2.Atom<HelmType>(p, elem, bio);
|
|
235
|
+
resMol.addAtom(atom);
|
|
236
|
+
|
|
237
|
+
if (j > 0) {
|
|
238
|
+
const atom1 = resMol.atoms[counter - 1];
|
|
239
|
+
const atom2 = resMol.atoms[counter];
|
|
240
|
+
const bond = new JSDraw2.Bond<HelmType>(atom1, atom2);
|
|
241
|
+
bond.r1 = 2;
|
|
242
|
+
bond.r2 = 1;
|
|
243
|
+
resMol.addBond(bond);
|
|
244
|
+
}
|
|
245
|
+
counter++;
|
|
246
|
+
}
|
|
180
247
|
|
|
248
|
+
for (let j = allPos2[0]; j < monomersCycled.length; j++) {
|
|
249
|
+
const elem = ch2[j - allPos2[0]] = monomersCycled[j];
|
|
250
|
+
const bio: PolyToolBio = {type: HelmTypes.AA, i: i, j: j, continuousId: counter};
|
|
251
|
+
const atom: HelmAtom = new JSDraw2.Atom<HelmType>(p, elem, bio);
|
|
252
|
+
resMol.addAtom(atom);
|
|
253
|
+
|
|
254
|
+
if (j > allPos2[0]) {
|
|
255
|
+
const atom1 = resMol.atoms[counter - 1];
|
|
256
|
+
const atom2 = resMol.atoms[counter];
|
|
257
|
+
const bond = new JSDraw2.Bond<HelmType>(atom1, atom2);
|
|
258
|
+
bond.r1 = 2;
|
|
259
|
+
bond.r2 = 1;
|
|
260
|
+
resMol.addBond(bond);
|
|
261
|
+
}
|
|
262
|
+
counter++;
|
|
263
|
+
}
|
|
181
264
|
|
|
182
|
-
ch1[allPos1[0] - 1] = rules.reactionRules[ruleN[0]].name;
|
|
265
|
+
resMol.atoms[allPos1[0] - 1].elem = ch1[allPos1[0] - 1] = rules.reactionRules[ruleN[0]].name;
|
|
183
266
|
|
|
184
267
|
for (let j = 0; j < linkages.length; j++) {
|
|
185
268
|
if (linkages[j].fMonomer > allPos2[0]) {
|
|
@@ -219,11 +302,116 @@ export class Chain {
|
|
|
219
302
|
monomersAll.push(ch1);
|
|
220
303
|
monomersAll.push(ch2);
|
|
221
304
|
} else {
|
|
305
|
+
for (let j = 0; j < monomers[i].length; j++) {
|
|
306
|
+
const elem = monomers[i][j];
|
|
307
|
+
const bio: PolyToolBio = {type: HelmTypes.AA, i: i, j: j, continuousId: counter};
|
|
308
|
+
const atom: HelmAtom = new JSDraw2.Atom<HelmType>(p, elem, bio);
|
|
309
|
+
resMol.addAtom(atom);
|
|
310
|
+
|
|
311
|
+
if (j > 0) {
|
|
312
|
+
const atom1 = resMol.atoms[counter - 1];
|
|
313
|
+
const atom2 = resMol.atoms[counter];
|
|
314
|
+
const bond = new JSDraw2.Bond<HelmType>(atom1, atom2);
|
|
315
|
+
bond.r1 = 2;
|
|
316
|
+
bond.r2 = 1;
|
|
317
|
+
resMol.addBond(bond);
|
|
318
|
+
}
|
|
319
|
+
counter++;
|
|
320
|
+
}
|
|
222
321
|
monomersAll.push(monomers[i]);
|
|
223
322
|
}
|
|
224
323
|
}
|
|
225
324
|
|
|
226
|
-
const
|
|
325
|
+
for (const l of linkages) {
|
|
326
|
+
const atom1 = resMol.atoms[l.fMonomer - 1];
|
|
327
|
+
const atom2 = resMol.atoms[l.sMonomer - 1];
|
|
328
|
+
const bond = new JSDraw2.Bond<HelmType>(atom1, atom2);
|
|
329
|
+
bond.r1 = l.fR;
|
|
330
|
+
bond.r2 = l.sR;
|
|
331
|
+
resMol.addBond(bond);
|
|
332
|
+
}
|
|
333
|
+
|
|
334
|
+
const chain = new Chain(monomersAll, linkages, resMol);
|
|
335
|
+
return chain;
|
|
336
|
+
}
|
|
337
|
+
|
|
338
|
+
/** Parse harmonized sequence notation (template) */
|
|
339
|
+
static parseNotation(sequence: string, helmHelper: IHelmHelper): Chain {
|
|
340
|
+
const mainFragments: string[][] = [];
|
|
341
|
+
|
|
342
|
+
const linkages: Linkage[] = [];
|
|
343
|
+
|
|
344
|
+
const resHwe = helmHelper.createHelmWebEditor();
|
|
345
|
+
const resMol = resHwe.editor.m;
|
|
346
|
+
|
|
347
|
+
const rxp = /(\(.\d+\))?\{[^\}]*\}/g;
|
|
348
|
+
const seqs: string [] = [];
|
|
349
|
+
seqs.push(sequence.replaceAll(rxp, ''));
|
|
350
|
+
|
|
351
|
+
//const l = (rxpRes?.length) ?? -1;
|
|
352
|
+
|
|
353
|
+
const matches = sequence.matchAll(rxp);
|
|
354
|
+
//const rxpRes = rxp.exec(sequence);
|
|
355
|
+
for (const m of matches) {
|
|
356
|
+
const str = m![0];
|
|
357
|
+
if (str)
|
|
358
|
+
seqs.push(str);
|
|
359
|
+
}
|
|
360
|
+
|
|
361
|
+
let counter = 0;
|
|
362
|
+
for (let i = 0; i < seqs.length; i++) {
|
|
363
|
+
const splMonomers = seqs[i].split('-');
|
|
364
|
+
const monomers: string [] = new Array<string>(splMonomers.length);
|
|
365
|
+
let spmCount: number = 0;
|
|
366
|
+
for (let j = 0; j < splMonomers.length; j++) {
|
|
367
|
+
const monomer = splMonomers[j].replace('{', '').replace('}', '');
|
|
368
|
+
if (monomer !== '') {
|
|
369
|
+
monomers[j] = monomer;
|
|
370
|
+
counter++;
|
|
371
|
+
spmCount++;
|
|
372
|
+
} else {
|
|
373
|
+
linkages.push({fChain: i, sChain: i + 1, fMonomer: counter, sMonomer: counter + 1, fR: 1, sR: 1});
|
|
374
|
+
}
|
|
375
|
+
}
|
|
376
|
+
mainFragments.push(monomers.slice(0, spmCount));
|
|
377
|
+
}
|
|
378
|
+
|
|
379
|
+
counter = 0;
|
|
380
|
+
const p = new JSDraw2.Point(0, 0);
|
|
381
|
+
for (let i = 0; i < mainFragments.length; i++) {
|
|
382
|
+
for (let j = 0; j < mainFragments[i].length; j++) {
|
|
383
|
+
if (!!mainFragments[i][j]) {
|
|
384
|
+
const elem = mainFragments[i][j];
|
|
385
|
+
const bio: PolyToolBio = {type: HelmTypes.AA, i: i, j: j, continuousId: counter};
|
|
386
|
+
const atom = new JSDraw2.Atom<HelmType>(p, elem, bio);
|
|
387
|
+
resMol.addAtom(atom);
|
|
388
|
+
|
|
389
|
+
if (j !== 0) {
|
|
390
|
+
const atom1 = resMol.atoms[counter - 1];
|
|
391
|
+
const atom2 = resMol.atoms[counter];
|
|
392
|
+
const bond = new JSDraw2.Bond<HelmType>(atom1, atom2);
|
|
393
|
+
bond.r1 = 2;
|
|
394
|
+
bond.r2 = 1;
|
|
395
|
+
resMol.addBond(bond);
|
|
396
|
+
}
|
|
397
|
+
|
|
398
|
+
counter++;
|
|
399
|
+
p.x += JSDraw2.Editor.BONDLENGTH; // Inspired by HELMWebEditor
|
|
400
|
+
}
|
|
401
|
+
p.y += 4 * JSDraw2.Editor.BONDLENGTH; // Inspired by HELMWebEditor
|
|
402
|
+
}
|
|
403
|
+
}
|
|
404
|
+
|
|
405
|
+
for (let i = 0; i < linkages.length; i++) {
|
|
406
|
+
const atom1 = resMol.atoms[linkages[i].fMonomer - 1];
|
|
407
|
+
const atom2 = resMol.atoms[linkages[i].sMonomer - 1];
|
|
408
|
+
const bond = new JSDraw2.Bond<HelmType>(atom1, atom2);
|
|
409
|
+
bond.r1 = linkages[i].fR;
|
|
410
|
+
bond.r2 = linkages[i].sR;
|
|
411
|
+
resMol.addBond(bond);
|
|
412
|
+
}
|
|
413
|
+
|
|
414
|
+
const chain = new Chain(mainFragments, linkages, resMol);
|
|
227
415
|
return chain;
|
|
228
416
|
}
|
|
229
417
|
|
|
@@ -233,17 +421,17 @@ export class Chain {
|
|
|
233
421
|
let idx1 = 0;
|
|
234
422
|
let idx2 = 0;
|
|
235
423
|
loop1:
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
}
|
|
424
|
+
for (let i = 0; i < this.monomers.length; i++) {
|
|
425
|
+
loop2:
|
|
426
|
+
for (let j = 0; j < this.monomers[i].length; j++) {
|
|
427
|
+
if (counter == changeNumber) {
|
|
428
|
+
idx1 = i;
|
|
429
|
+
idx2 = j;
|
|
430
|
+
break loop1;
|
|
431
|
+
}
|
|
432
|
+
counter++;
|
|
246
433
|
}
|
|
434
|
+
}
|
|
247
435
|
|
|
248
436
|
const previous = this.monomers[idx1][idx2];
|
|
249
437
|
|
|
@@ -254,6 +442,12 @@ export class Chain {
|
|
|
254
442
|
return res;
|
|
255
443
|
}
|
|
256
444
|
|
|
445
|
+
/** Gets harmonized sequence (template) pseudo helm */
|
|
446
|
+
getNotationHelm(): string {
|
|
447
|
+
return this.getHelm();
|
|
448
|
+
}
|
|
449
|
+
|
|
450
|
+
/** Gets harmonized sequence (template) pseudo helm */
|
|
257
451
|
getHelm(): string {
|
|
258
452
|
let helm = '';
|
|
259
453
|
for (let i = 0; i < this.monomers.length; i++) {
|
|
@@ -277,16 +471,75 @@ export class Chain {
|
|
|
277
471
|
if (i > 0)
|
|
278
472
|
helm += '|';
|
|
279
473
|
helm += `PEPTIDE${this.linkages[i].fChain + 1},PEPTIDE${this.linkages[i].sChain + 1},`;
|
|
280
|
-
|
|
281
|
-
helm += `${this.linkages[i].
|
|
474
|
+
|
|
475
|
+
helm += `${getInnerIdx(this.linkages[i].fMonomer - 1, this.monomers)[0] + 1}:R${this.linkages[i].fR}-`;
|
|
476
|
+
helm += `${getInnerIdx(this.linkages[i].sMonomer - 1, this.monomers)[0] + 1}:R${this.linkages[i].sR}`;
|
|
282
477
|
}
|
|
283
478
|
|
|
284
|
-
helm += '$$$';
|
|
479
|
+
helm += '$$$' + 'V2.0';
|
|
285
480
|
return helm;
|
|
286
481
|
}
|
|
287
482
|
|
|
288
|
-
getNotation(
|
|
289
|
-
|
|
483
|
+
getNotation(): string {
|
|
484
|
+
const atoms = this.mol.atoms;
|
|
485
|
+
const bonds = this.mol.bonds;
|
|
486
|
+
const chains: number[] = [];
|
|
487
|
+
const specialBonds: number[] = [];
|
|
488
|
+
for (let i = 0; i < bonds.length!; i++) {
|
|
489
|
+
//@ts-ignore
|
|
490
|
+
if (bonds[i].a1.bio.i !== bonds[i].a2.bio.i)
|
|
491
|
+
specialBonds.push(i);
|
|
492
|
+
}
|
|
493
|
+
|
|
494
|
+
for (let i = 0; i < atoms.length!; i++) {
|
|
495
|
+
//@ts-ignore
|
|
496
|
+
const atomChain = atoms[i].bio?.i;
|
|
497
|
+
if (atomChain + 1 > chains.length)
|
|
498
|
+
chains.push(1);
|
|
499
|
+
else
|
|
500
|
+
chains[atomChain]++;
|
|
501
|
+
}
|
|
502
|
+
|
|
503
|
+
const simpleChains: string[][] = new Array(chains.length);
|
|
504
|
+
let counter = 0;
|
|
505
|
+
for (let i = 0; i < chains.length!; i++) {
|
|
506
|
+
const simpleChain: string[] = new Array(chains[i]);
|
|
507
|
+
for (let j = 0; j < chains[i]; j++) {
|
|
508
|
+
simpleChain[j] = atoms[counter].elem;
|
|
509
|
+
counter++;
|
|
510
|
+
}
|
|
511
|
+
|
|
512
|
+
simpleChains[i] = simpleChain;
|
|
513
|
+
}
|
|
514
|
+
|
|
515
|
+
let res = '';
|
|
516
|
+
for (let i = 0; i < simpleChains.length; i++) {
|
|
517
|
+
let chainAdd = '';
|
|
518
|
+
|
|
519
|
+
for (let j = 0; j < simpleChains[i].length; j++)
|
|
520
|
+
chainAdd += `${j == 0 ? '' : '-'}${simpleChains[i][j]}`;
|
|
521
|
+
|
|
522
|
+
if (i !== 0) {
|
|
523
|
+
const rxp = /(\(.\d+\))/;
|
|
524
|
+
const match = chainAdd.match(rxp);
|
|
525
|
+
chainAdd = chainAdd.replace(match?.[0]!, '');
|
|
526
|
+
const group = match ? match?.[0]! : '';
|
|
527
|
+
chainAdd = `${group}{${chainAdd}}`;
|
|
528
|
+
} else {
|
|
529
|
+
if (simpleChains.length > 1) {
|
|
530
|
+
//@ts-ignore
|
|
531
|
+
const firstAtomLinks = bonds[specialBonds[0]].a1.bio.i == 0 && bonds[specialBonds[0]].a1.bio.j == 0;
|
|
532
|
+
//@ts-ignore
|
|
533
|
+
const secondAtomLinks = bonds[specialBonds[0]].a2.bio.i == 1 && bonds[specialBonds[0]].a1.bio.j == 0;
|
|
534
|
+
if (firstAtomLinks && secondAtomLinks)
|
|
535
|
+
chainAdd += '-';
|
|
536
|
+
}
|
|
537
|
+
}
|
|
538
|
+
|
|
539
|
+
res += chainAdd;
|
|
540
|
+
}
|
|
541
|
+
|
|
542
|
+
return res;
|
|
290
543
|
}
|
|
291
544
|
|
|
292
545
|
protected static getLinkedPositions(monomers: string[], rules: RuleLink[] | RuleReaction []):
|
|
@@ -390,15 +643,48 @@ export class Chain {
|
|
|
390
643
|
|
|
391
644
|
return [monomers, allPos1, allPos2, rule];
|
|
392
645
|
}
|
|
646
|
+
|
|
647
|
+
public check(throwError: boolean = false): string[] {
|
|
648
|
+
const errors: string[] = [];
|
|
649
|
+
|
|
650
|
+
const chainsMonomerCount = this.monomers.map((ch) => ch.length).reduce((acc, curr) => acc + curr, 0);
|
|
651
|
+
if (this.mol.atoms.length !== chainsMonomerCount)
|
|
652
|
+
errors.push(`The mol atoms count ${this.mol.atoms.length} does not match ` +
|
|
653
|
+
`the total number ${chainsMonomerCount} of chains' monomers.`);
|
|
654
|
+
|
|
655
|
+
const internalBondsCount = this.monomers.map((ch) => ch.length - 1).reduce((acc, curr) => acc + curr, 0);
|
|
656
|
+
const chainsBondCount = internalBondsCount + this.linkages.length;
|
|
657
|
+
if (this.mol.bonds.length !== chainsBondCount)
|
|
658
|
+
errors.push(`The mol bonds count ${this.mol.bonds.length} does not match ` +
|
|
659
|
+
`the total number ${chainsBondCount} in- and inter-chain linkages.`);
|
|
660
|
+
|
|
661
|
+
let counter: number = 0;
|
|
662
|
+
for (let spIdx = 0; spIdx < this.monomers.length; ++spIdx) {
|
|
663
|
+
const chain = this.monomers[spIdx];
|
|
664
|
+
for (let mIntIdx = 0; mIntIdx < chain.length; ++mIntIdx) {
|
|
665
|
+
try {
|
|
666
|
+
const m = chain[mIntIdx];
|
|
667
|
+
const a = this.mol.atoms[counter];
|
|
668
|
+
if (a.bio!.continuousId !== counter)
|
|
669
|
+
errors.push(`Atom #${counter} has incorrect .bio.continuousId: ${a.bio!.continuousId}.`);
|
|
670
|
+
if (a.elem !== m)
|
|
671
|
+
errors.push(`Atom #${counter} elem: '${a.elem}' does not match chain monomer: '${m}'.`);
|
|
672
|
+
} finally { counter++; }
|
|
673
|
+
}
|
|
674
|
+
}
|
|
675
|
+
if (throwError && errors.length > 0)
|
|
676
|
+
throw new Error(`Chain errors:\n${errors.map((e) => ` ${e}`).join('\n')}`);
|
|
677
|
+
return errors;
|
|
678
|
+
}
|
|
393
679
|
}
|
|
394
680
|
|
|
395
681
|
/** The main PolyTool convert engine. Returns list of Helms. Covered with tests. */
|
|
396
|
-
export function doPolyToolConvert(sequences: string[], rules: Rules): string[] {
|
|
682
|
+
export function doPolyToolConvert(sequences: string[], rules: Rules, helmHelper: IHelmHelper): string[] {
|
|
397
683
|
const helms = new Array<string>(sequences.length);
|
|
398
684
|
for (let i = 0; i < sequences.length; i++) {
|
|
399
685
|
try {
|
|
400
686
|
if (sequences[i] == null) { helms[i] = ''; } else {
|
|
401
|
-
const chain = Chain.fromNotation(sequences[i], rules);
|
|
687
|
+
const chain = Chain.fromNotation(sequences[i], rules, helmHelper);
|
|
402
688
|
helms[i] = chain.getHelm();
|
|
403
689
|
}
|
|
404
690
|
} catch (err: any) {
|
|
@@ -517,18 +803,24 @@ export function getNewMonomer(rdkit: RDModule, mLib: IMonomerLib, rule: RuleReac
|
|
|
517
803
|
const groups: RGroup[] = getNewGroups(monomer1!, monomer2!);
|
|
518
804
|
|
|
519
805
|
const resMonomer: Monomer = {
|
|
520
|
-
[
|
|
521
|
-
[
|
|
522
|
-
[
|
|
523
|
-
[
|
|
524
|
-
[
|
|
525
|
-
[
|
|
526
|
-
[
|
|
527
|
-
[
|
|
528
|
-
[
|
|
529
|
-
[
|
|
806
|
+
[REQ.SYMBOL]: monomerName,
|
|
807
|
+
[REQ.NAME]: monomerName,
|
|
808
|
+
[REQ.MOLFILE]: molBlock,
|
|
809
|
+
[REQ.AUTHOR]: '',
|
|
810
|
+
[REQ.ID]: 0,
|
|
811
|
+
[REQ.RGROUPS]: groups,
|
|
812
|
+
[REQ.SMILES]: '',
|
|
813
|
+
[REQ.POLYMER_TYPE]: 'PEPTIDE',
|
|
814
|
+
[REQ.MONOMER_TYPE]: 'Backbone',
|
|
815
|
+
[REQ.CREATE_DATE]: null,
|
|
816
|
+
|
|
817
|
+
// // @ts-ignore
|
|
818
|
+
// lib: {source: 'Reaction'},
|
|
530
819
|
};
|
|
531
820
|
|
|
821
|
+
resMonomer[OPT.META] = Object.assign(resMonomer[OPT.META] ?? {},
|
|
822
|
+
{'colors': {'default': {line: '#2083D5', text: '#2083D5', background: '#F2F2F5'}}});
|
|
823
|
+
|
|
532
824
|
return [monomerName, resMonomer];
|
|
533
825
|
}
|
|
534
826
|
|
|
@@ -545,6 +837,28 @@ export async function getOverriddenLibrary(rules: Rules): Promise<IMonomerLibBas
|
|
|
545
837
|
}
|
|
546
838
|
|
|
547
839
|
const overrideMonomerLibData: MonomerLibData = {[PolymerTypes.PEPTIDE]: argLib};
|
|
548
|
-
const overriddenMonomerLib = systemMonomerLib.override(overrideMonomerLibData
|
|
840
|
+
const overriddenMonomerLib = systemMonomerLib.override(overrideMonomerLibData,
|
|
841
|
+
'ST-PT-reactions.' + wu.repeat(1).map(() => Math.floor((Math.random() * 36)).toString(36)).take(4).toArray().join(''));
|
|
549
842
|
return overriddenMonomerLib;
|
|
550
843
|
}
|
|
844
|
+
|
|
845
|
+
/** Gets 0-based in-index (simple polymer) of out-index (continuous) {@link idx} */
|
|
846
|
+
export function getInnerIdx(outIdx: number, monomers: string[][]): [number, number] {
|
|
847
|
+
// let prevSpCount = 0;
|
|
848
|
+
// for (let spI = 0; spI < monomers.length && idx >= (prevSpCount + monomers[spI].length); ++spI)
|
|
849
|
+
// prevSpCount += monomers[spI].length;
|
|
850
|
+
// return idx - prevSpCount;
|
|
851
|
+
let inIdx = outIdx;
|
|
852
|
+
let spIdx: number;
|
|
853
|
+
for (spIdx = 0; spIdx < monomers.length && inIdx >= monomers[spIdx].length; ++spIdx)
|
|
854
|
+
inIdx -= monomers[spIdx].length;
|
|
855
|
+
return [inIdx, spIdx];
|
|
856
|
+
}
|
|
857
|
+
|
|
858
|
+
/** Gets 0-based out-index of 0-based in-index {@link inIdx} monomer of simple polymer {@link spIdx} */
|
|
859
|
+
export function getOuterIdx(inIdx: number, spIdx: number, monomers: string[][]): number {
|
|
860
|
+
let outIdx = 0;
|
|
861
|
+
for (let i = 0; i < spIdx; ++i)
|
|
862
|
+
outIdx += monomers[i].length;
|
|
863
|
+
return outIdx + inIdx;
|
|
864
|
+
}
|