@datagrok/sequence-translator 1.4.2 → 1.4.4
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 +27 -0
- package/dist/package-test.js +2 -1
- package/dist/package-test.js.LICENSE.txt +8 -0
- 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 -0
- package/files/tests/polytool-reaction-lib.json +40 -0
- package/package.json +7 -7
- package/src/package-test.ts +3 -1
- package/src/package.ts +8 -0
- package/src/polytool/const.ts +1 -0
- package/src/polytool/pt-conversion.ts +314 -43
- package/src/polytool/pt-dialog.ts +37 -22
- package/src/polytool/pt-placeholders-breadth-input.ts +2 -2
- package/src/polytool/pt-placeholders-input.ts +1 -1
- 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 +18 -0
- package/src/tests/polytool-chain-from-notation-tests.ts +45 -0
- package/src/tests/polytool-convert-tests.ts +8 -9
- package/src/tests/polytool-enumerate-breadth-tests.ts +3 -5
- package/src/tests/polytool-enumerate-tests.ts +2 -4
- package/src/tests/polytool-unrule-tests.ts +10 -0
- package/src/tests/toAtomicLevel-tests.ts +97 -0
|
@@ -23,6 +23,16 @@
|
|
|
23
23
|
"secondSubstitution": "D"
|
|
24
24
|
}
|
|
25
25
|
},
|
|
26
|
+
{
|
|
27
|
+
"type": "reaction",
|
|
28
|
+
"code": "3",
|
|
29
|
+
"monomericSubstitution": {
|
|
30
|
+
"firstMonomer": "azG",
|
|
31
|
+
"secondMonomer": "aG",
|
|
32
|
+
"reaction": "[C:1]N=[N+]=[N-].[C:2]C#C>>[C:1]N1-N=NC=C1[C:2]",
|
|
33
|
+
"name": "GGaz"
|
|
34
|
+
}
|
|
35
|
+
},
|
|
26
36
|
{
|
|
27
37
|
"type": "fragmentDuplication",
|
|
28
38
|
"code": "#3"
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
[
|
|
2
|
+
{
|
|
3
|
+
"monomerType": "Backbone",
|
|
4
|
+
"smiles": "[H:1]NC(C([OH:2])=O)c1cn(C(N[H:3])C([OH:4])=O)nn1",
|
|
5
|
+
"name": "GGaz",
|
|
6
|
+
"author": "Datagrok",
|
|
7
|
+
"molfile": "\n MJ201900 \n\n 17 17 0 0 0 0 0 0 0 0999 V2000\n 18.8168 -12.6760 0.0000 N 0 0 0 0 0 0 0 0 0 0 0 0\n 19.5312 -13.0885 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0\n 20.2457 -12.6760 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0\n 20.9602 -13.0885 0.0000 R# 0 0 0 0 0 0 0 0 0 0 0 0\n 20.2457 -11.8510 0.0000 O 0 0 0 0 0 0 0 0 0 0 0 0\n 19.1403 -15.8019 0.0000 N 0 0 0 0 0 0 0 0 0 0 0 0\n 19.8547 -16.2144 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0\n 20.5692 -15.8019 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0\n 21.2837 -16.2144 0.0000 R# 0 0 0 0 0 0 0 0 0 0 0 0\n 20.5692 -14.9769 0.0000 O 0 0 0 0 0 0 0 0 0 0 0 0\n 19.5083 -13.8885 0.0000 N 0 0 0 0 0 0 0 0 0 0 0 0\n 18.8409 -14.3735 0.0000 N 0 0 0 0 0 0 0 0 0 0 0 0\n 19.0958 -15.1581 0.0000 N 0 0 0 0 0 0 0 0 0 0 0 0\n 19.9208 -15.1581 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0\n 20.1757 -14.3735 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0\n 18.4258 -16.2144 0.0000 R# 0 0 0 0 0 0 0 0 0 0 0 0\n 18.1023 -13.0885 0.0000 R# 0 0 0 0 0 0 0 0 0 0 0 0\n 2 3 1 0 0 0 0\n 3 5 2 0 0 0 0\n 7 8 1 0 0 0 0\n 8 10 2 0 0 0 0\n 3 4 1 0 0 0 0\n 8 9 1 0 0 0 0\n 14 15 2 0 0 0 0\n 11 15 1 0 0 0 0\n 11 12 1 0 0 0 0\n 13 14 1 0 0 0 0\n 12 13 2 0 0 0 0\n 2 11 1 0 0 0 0\n 7 14 1 0 0 0 0\n 1 2 1 0 0 0 0\n 1 17 1 0 0 0 0\n 6 7 1 0 0 0 0\n 6 16 1 0 0 0 0\nM RGP 4 4 2 9 4 16 3 17 1\nM END\n",
|
|
8
|
+
"naturalAnalog": "G",
|
|
9
|
+
"rgroups": [
|
|
10
|
+
{
|
|
11
|
+
"capGroupSMILES": "[*:1][H]",
|
|
12
|
+
"alternateId": "R1-H",
|
|
13
|
+
"capGroupName": "H",
|
|
14
|
+
"label": "R1"
|
|
15
|
+
},
|
|
16
|
+
{
|
|
17
|
+
"capGroupSMILES": "O[*:2]",
|
|
18
|
+
"alternateId": "R2-OH",
|
|
19
|
+
"capGroupName": "OH",
|
|
20
|
+
"label": "R2"
|
|
21
|
+
},
|
|
22
|
+
{
|
|
23
|
+
"capGroupSMILES": "[*:3][H]",
|
|
24
|
+
"alternateId": "R3-H",
|
|
25
|
+
"capGroupName": "H",
|
|
26
|
+
"label": "R3"
|
|
27
|
+
},
|
|
28
|
+
{
|
|
29
|
+
"capGroupSMILES": "O[*:4]",
|
|
30
|
+
"alternateId": "R4-OH",
|
|
31
|
+
"capGroupName": "OH",
|
|
32
|
+
"label": "R4"
|
|
33
|
+
}
|
|
34
|
+
],
|
|
35
|
+
"createDate": null,
|
|
36
|
+
"id": 0,
|
|
37
|
+
"polymerType": "PEPTIDE",
|
|
38
|
+
"symbol": "GGaz"
|
|
39
|
+
}
|
|
40
|
+
]
|
package/package.json
CHANGED
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@datagrok/sequence-translator",
|
|
3
3
|
"friendlyName": "Sequence Translator",
|
|
4
|
-
"version": "1.4.
|
|
4
|
+
"version": "1.4.4",
|
|
5
5
|
"author": {
|
|
6
|
-
"name": "Alexey
|
|
6
|
+
"name": "Alexey Chopovsky",
|
|
7
7
|
"email": "achopovsky@datagrok.ai"
|
|
8
8
|
},
|
|
9
9
|
"description": "SequenceTranslator translates [oligonucleotide](https://en.wikipedia.org/wiki/Oligonucleotide) sequences between [different representations](https://github.com/datagrok-ai/public/tree/master/packages/SequenceTranslator#sequence-representations).",
|
|
@@ -22,10 +22,10 @@
|
|
|
22
22
|
}
|
|
23
23
|
],
|
|
24
24
|
"dependencies": {
|
|
25
|
-
"@datagrok-libraries/bio": "^5.44.
|
|
25
|
+
"@datagrok-libraries/bio": "^5.44.2",
|
|
26
26
|
"@datagrok-libraries/chem-meta": "^1.2.7",
|
|
27
27
|
"@datagrok-libraries/tutorials": "^1.4.0",
|
|
28
|
-
"@datagrok-libraries/utils": "^4.3.
|
|
28
|
+
"@datagrok-libraries/utils": "^4.3.5",
|
|
29
29
|
"@types/react": "^18.0.15",
|
|
30
30
|
"cash-dom": "^8.1.0",
|
|
31
31
|
"datagrok-api": "^1.21.1",
|
|
@@ -41,9 +41,9 @@
|
|
|
41
41
|
"devDependencies": {
|
|
42
42
|
"@datagrok-libraries/helm-web-editor": "^1.1.11",
|
|
43
43
|
"@datagrok-libraries/js-draw-lite": "^0.0.8",
|
|
44
|
-
"@datagrok/bio": "^2.15.
|
|
45
|
-
"@datagrok/helm": "^2.5.
|
|
46
|
-
"@datagrok/chem": "^1.12.
|
|
44
|
+
"@datagrok/bio": "^2.15.6",
|
|
45
|
+
"@datagrok/helm": "^2.5.3",
|
|
46
|
+
"@datagrok/chem": "^1.12.1",
|
|
47
47
|
"@types/jquery": "^3.5.14",
|
|
48
48
|
"@types/js-yaml": "^4.0.5",
|
|
49
49
|
"@types/lodash": "^4.14.202",
|
package/src/package-test.ts
CHANGED
|
@@ -2,15 +2,17 @@ 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 {runTests, tests, TestContext, initAutoTests as initTests
|
|
5
|
+
import {runTests, tests, TestContext, initAutoTests as initTests} from '@datagrok-libraries/utils/src/test';
|
|
6
6
|
|
|
7
7
|
import './tests/formats-to-helm';
|
|
8
8
|
import './tests/helm-to-nucleotides';
|
|
9
9
|
import './tests/formats-support';
|
|
10
10
|
import './tests/files-tests';
|
|
11
11
|
import './tests/polytool-convert-tests';
|
|
12
|
+
import './tests/polytool-unrule-tests';
|
|
12
13
|
import './tests/polytool-enumerate-tests';
|
|
13
14
|
import './tests/polytool-enumerate-breadth-tests';
|
|
15
|
+
import './tests/toAtomicLevel-tests';
|
|
14
16
|
|
|
15
17
|
import {OligoToolkitTestPackage} from './tests/utils';
|
|
16
18
|
|
package/src/package.ts
CHANGED
|
@@ -25,6 +25,7 @@ import {PolyToolCsvLibHandler} from './polytool/csv-to-json-monomer-lib-converte
|
|
|
25
25
|
import {ITranslationHelper} from './types';
|
|
26
26
|
import {addContextMenuUI} from './utils/context-menu';
|
|
27
27
|
import {PolyToolConvertFuncEditor} from './polytool/pt-convert-editor';
|
|
28
|
+
import {polyToolUnruleUI} from './polytool/pt-unrule';
|
|
28
29
|
|
|
29
30
|
export const _package: OligoToolkitPackage = new OligoToolkitPackage({debug: true}/**/);
|
|
30
31
|
|
|
@@ -162,6 +163,13 @@ export async function polyToolConvertTopMenu(): Promise<void> {
|
|
|
162
163
|
await polyToolConvertUI();
|
|
163
164
|
}
|
|
164
165
|
|
|
166
|
+
// //top-menu: Bio | PolyTool | Unrule...
|
|
167
|
+
// //name: polyToolUnrule
|
|
168
|
+
// //description: Perform uncyclization of polymers by rules
|
|
169
|
+
// export async function polyToolUnruleTopMenu(): Promise<void> {
|
|
170
|
+
// await polyToolUnruleUI();
|
|
171
|
+
// }
|
|
172
|
+
|
|
165
173
|
//name: getPolyToolConvertEditor
|
|
166
174
|
//tags: editor
|
|
167
175
|
//input: funccall call
|
package/src/polytool/const.ts
CHANGED
|
@@ -34,5 +34,6 @@ export const PT_UI_GET_HELM = 'Get HELM';
|
|
|
34
34
|
export const PT_UI_ADD_HELM = 'Add HELM column';
|
|
35
35
|
export const PT_UI_USE_CHIRALITY = 'Chirality engine';
|
|
36
36
|
export const PT_UI_DIALOG_CONVERSION = 'Poly Tool Conversion';
|
|
37
|
+
export const PT_UI_DIALOG_UNRULE = 'Poly Tool Unrule';
|
|
37
38
|
export const PT_UI_DIALOG_ENUMERATION = 'Poly Tool Enumeration';
|
|
38
39
|
export const PT_UI_RULES_USED = 'Rules used';
|
|
@@ -1,27 +1,35 @@
|
|
|
1
1
|
import * as grok from 'datagrok-api/grok';
|
|
2
|
+
import * as ui from 'datagrok-api/ui';
|
|
2
3
|
import * as DG from 'datagrok-api/dg';
|
|
3
4
|
|
|
4
|
-
import {
|
|
5
|
-
import {
|
|
5
|
+
import {PolymerTypes} from '@datagrok-libraries/bio/src/helm/consts';
|
|
6
|
+
import {getMonomerLibHelper, IMonomerLibHelper} from '@datagrok-libraries/bio/src/monomer-works/monomer-utils';
|
|
7
|
+
import {IMonomerLib, IMonomerLibBase, Monomer, MonomerLibData, RGroup} from '@datagrok-libraries/bio/src/types';
|
|
8
|
+
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';
|
|
10
|
+
import {getRdKitModule} from '@datagrok-libraries/bio/src/chem/rdkit-module';
|
|
6
11
|
|
|
7
|
-
import {Rules, RuleLink,
|
|
12
|
+
import {Rules, RuleLink, RuleReaction} from './pt-rules';
|
|
13
|
+
import {InvalidReactionError, MonomerNotFoundError} from './types';
|
|
14
|
+
import {errInfo} from '@datagrok-libraries/bio/src/utils/err-info';
|
|
15
|
+
import {_package} from '../package';
|
|
8
16
|
|
|
9
17
|
export const RULES_DIMER = '(#2)';
|
|
10
18
|
export const RULES_HETERODIMER = '($2)';
|
|
11
19
|
|
|
12
|
-
function addCommonTags(col: DG.Column): void {
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
}
|
|
20
|
+
// function addCommonTags(col: DG.Column): void {
|
|
21
|
+
// col.semType = DG.SEMTYPE.MACROMOLECULE;
|
|
22
|
+
// col.setTag('aligned', ALIGNMENT.SEQ);
|
|
23
|
+
// col.setTag('alphabet', ALPHABET.PT);
|
|
24
|
+
// }
|
|
17
25
|
|
|
18
26
|
export class Chain {
|
|
19
|
-
linkages: {fChain: number, sChain: number, fMonomer:number, sMonomer:number, fR:number, sR:number}[];
|
|
27
|
+
linkages: { fChain: number, sChain: number, fMonomer: number, sMonomer: number, fR: number, sR: number }[];
|
|
20
28
|
monomers: string[][];
|
|
21
29
|
|
|
22
30
|
constructor(
|
|
23
31
|
monomers: string[][],
|
|
24
|
-
linkages: {fChain: number, sChain: number, fMonomer:number, sMonomer:number, fR:number, sR:number}[]) {
|
|
32
|
+
linkages: { fChain: number, sChain: number, fMonomer: number, sMonomer: number, fR: number, sR: number }[]) {
|
|
25
33
|
this.linkages = linkages;
|
|
26
34
|
this.monomers = monomers;
|
|
27
35
|
}
|
|
@@ -32,7 +40,14 @@ export class Chain {
|
|
|
32
40
|
const rawLinkages = fragmentation[1].split('|');
|
|
33
41
|
|
|
34
42
|
const monomers = new Array<Array<string>>(rawFragments.length);
|
|
35
|
-
const linkages: {
|
|
43
|
+
const linkages: {
|
|
44
|
+
fChain: number,
|
|
45
|
+
sChain: number,
|
|
46
|
+
fMonomer: number,
|
|
47
|
+
sMonomer: number,
|
|
48
|
+
fR: number,
|
|
49
|
+
sR: number
|
|
50
|
+
}[] = [];
|
|
36
51
|
|
|
37
52
|
//HELM parsing
|
|
38
53
|
for (let i = 0; i < rawFragments.length; i++) {
|
|
@@ -66,15 +81,22 @@ export class Chain {
|
|
|
66
81
|
return new Chain(monomers, linkages);
|
|
67
82
|
}
|
|
68
83
|
|
|
69
|
-
static fromNotation(sequence: string, rules: Rules) {
|
|
84
|
+
static fromNotation(sequence: string, rules: Rules): Chain {
|
|
70
85
|
const heterodimerCode = rules.heterodimerCode;
|
|
71
86
|
const homodimerCode = rules.homodimerCode;
|
|
72
87
|
const mainFragments: string[] = [];
|
|
73
88
|
|
|
74
|
-
const linkages: {
|
|
89
|
+
const linkages: {
|
|
90
|
+
fChain: number,
|
|
91
|
+
sChain: number,
|
|
92
|
+
fMonomer: number,
|
|
93
|
+
sMonomer: number,
|
|
94
|
+
fR: number,
|
|
95
|
+
sR: number
|
|
96
|
+
}[] = [];
|
|
75
97
|
|
|
76
98
|
//NOTICE: this works only with simple single heterodimers
|
|
77
|
-
const heterodimeric = heterodimerCode !== null? sequence.split(`(${rules.heterodimerCode!})`) : '';
|
|
99
|
+
const heterodimeric = heterodimerCode !== null ? sequence.split(`(${rules.heterodimerCode!})`) : '';
|
|
78
100
|
if (heterodimerCode !== null && heterodimeric.length > 1) {
|
|
79
101
|
linkages.push({fChain: 0, sChain: 1, fMonomer: 1, sMonomer: 1, fR: 1, sR: 1});
|
|
80
102
|
mainFragments.push(heterodimeric[1].replaceAll('{', '').replaceAll('}', ''));
|
|
@@ -99,17 +121,32 @@ export class Chain {
|
|
|
99
121
|
}
|
|
100
122
|
}
|
|
101
123
|
|
|
124
|
+
for (let i = 0; i < mainFragments.length; i++) {
|
|
125
|
+
if (homodimerCode !== null && mainFragments[i].includes(`(${homodimerCode!})`)) {
|
|
126
|
+
const idxSequence = mainFragments.length;
|
|
127
|
+
|
|
128
|
+
linkages.push({fChain: i, sChain: idxSequence, fMonomer: 1, sMonomer: 1, fR: 1, sR: 1});
|
|
129
|
+
const rawDimer = mainFragments[i].replace(`(${homodimerCode!})`, '');
|
|
130
|
+
const idx = rawDimer.indexOf('{');
|
|
131
|
+
const linker = rawDimer.slice(0, idx);
|
|
132
|
+
const body = rawDimer.replace(linker, '').replaceAll('{', '').replaceAll('}', '');
|
|
133
|
+
|
|
134
|
+
mainFragments[i] = linker + body;
|
|
135
|
+
mainFragments.push(body);
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
|
|
102
139
|
const monomers = new Array<Array<string>>(mainFragments.length);
|
|
103
140
|
|
|
104
141
|
for (let i = 0; i < mainFragments.length; i++) {
|
|
105
142
|
const rawMonomers = mainFragments[i].split('-');
|
|
106
143
|
const linkedPositions = this.getLinkedPositions(rawMonomers, rules.linkRules);
|
|
107
144
|
const [monomersCycled, allPos1, allPos2, allAttaches1, allAttaches2] =
|
|
108
|
-
|
|
145
|
+
this.getAllCycles(rules.linkRules, rawMonomers, linkedPositions);
|
|
109
146
|
|
|
110
147
|
const monomersReady = new Array<string>(monomersCycled.length);
|
|
111
|
-
for (let j = 0; j < monomersCycled.length; j++)
|
|
112
|
-
|
|
148
|
+
// for (let j = 0; j < monomersCycled.length; j++)
|
|
149
|
+
// monomersReady[j] = `[${monomersCycled[j]}]`;
|
|
113
150
|
|
|
114
151
|
for (let j = 0; j < allPos1.length; j++) {
|
|
115
152
|
linkages.push({
|
|
@@ -122,10 +159,72 @@ export class Chain {
|
|
|
122
159
|
});
|
|
123
160
|
}
|
|
124
161
|
|
|
125
|
-
monomers[i] =
|
|
162
|
+
monomers[i] = monomersCycled;
|
|
126
163
|
}
|
|
127
164
|
|
|
128
|
-
|
|
165
|
+
const monomersAll: string[][] = [];
|
|
166
|
+
|
|
167
|
+
for (let i = 0; i < monomers.length; i++) {
|
|
168
|
+
const linkedPositions = this.getLinkedPositions(monomers[i], rules.reactionRules);
|
|
169
|
+
const [monomersCycled, allPos1, allPos2, ruleN] =
|
|
170
|
+
this.getAllReactants(rules.reactionRules, monomers[i], linkedPositions);
|
|
171
|
+
|
|
172
|
+
if (allPos1.length >= 1) {
|
|
173
|
+
const ch1 = new Array<string>(allPos2[0] - 1);
|
|
174
|
+
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
|
+
for (let j = allPos2[0]; j < monomersCycled.length; j++)
|
|
179
|
+
ch2[j - allPos2[0]] = monomersCycled[j];
|
|
180
|
+
|
|
181
|
+
|
|
182
|
+
ch1[allPos1[0] - 1] = rules.reactionRules[ruleN[0]].name;
|
|
183
|
+
|
|
184
|
+
for (let j = 0; j < linkages.length; j++) {
|
|
185
|
+
if (linkages[j].fMonomer > allPos2[0]) {
|
|
186
|
+
linkages[j].fMonomer -= allPos2[0];
|
|
187
|
+
linkages[j].fChain++;
|
|
188
|
+
}
|
|
189
|
+
if (linkages[j].sMonomer > allPos2[0]) {
|
|
190
|
+
linkages[j].sMonomer -= allPos2[0];
|
|
191
|
+
linkages[j].sChain++;
|
|
192
|
+
}
|
|
193
|
+
}
|
|
194
|
+
linkages.push({
|
|
195
|
+
fChain: 0,
|
|
196
|
+
sChain: 0,
|
|
197
|
+
fMonomer: allPos1[0],
|
|
198
|
+
sMonomer: allPos2[0] - 1,
|
|
199
|
+
fR: 3,
|
|
200
|
+
sR: 2,
|
|
201
|
+
});
|
|
202
|
+
|
|
203
|
+
linkages.push({
|
|
204
|
+
fChain: 0,
|
|
205
|
+
sChain: 1,
|
|
206
|
+
fMonomer: allPos1[0],
|
|
207
|
+
sMonomer: 1,
|
|
208
|
+
fR: 4,
|
|
209
|
+
sR: 1,
|
|
210
|
+
});
|
|
211
|
+
|
|
212
|
+
const monomersReady1 = new Array<string>(ch1.length);
|
|
213
|
+
for (let j = 0; j < ch1.length; j++)
|
|
214
|
+
monomersReady1[j] = `[${ch1[j]}]`;
|
|
215
|
+
const monomersReady2 = new Array<string>(ch2.length);
|
|
216
|
+
for (let j = 0; j < ch2.length; j++)
|
|
217
|
+
monomersReady2[j] = `[${ch2[j]}]`;
|
|
218
|
+
|
|
219
|
+
monomersAll.push(ch1);
|
|
220
|
+
monomersAll.push(ch2);
|
|
221
|
+
} else {
|
|
222
|
+
monomersAll.push(monomers[i]);
|
|
223
|
+
}
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
const chain = new Chain(monomersAll, linkages);
|
|
227
|
+
return chain;
|
|
129
228
|
}
|
|
130
229
|
|
|
131
230
|
getHelmChanged(changeNumber: number, monomer: string): string {
|
|
@@ -134,17 +233,17 @@ export class Chain {
|
|
|
134
233
|
let idx1 = 0;
|
|
135
234
|
let idx2 = 0;
|
|
136
235
|
loop1:
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
236
|
+
for (let i = 0; i < this.monomers.length; i++) {
|
|
237
|
+
loop2:
|
|
238
|
+
for (let j = 0; j < this.monomers[i].length; j++) {
|
|
239
|
+
if (counter == changeNumber) {
|
|
240
|
+
idx1 = i;
|
|
241
|
+
idx2 = j;
|
|
242
|
+
break loop1;
|
|
243
|
+
}
|
|
244
|
+
counter++;
|
|
245
|
+
}
|
|
146
246
|
}
|
|
147
|
-
}
|
|
148
247
|
|
|
149
248
|
const previous = this.monomers[idx1][idx2];
|
|
150
249
|
|
|
@@ -166,7 +265,8 @@ export class Chain {
|
|
|
166
265
|
for (let j = 0; j < this.monomers[i].length; j++) {
|
|
167
266
|
if (j > 0)
|
|
168
267
|
helm += '.';
|
|
169
|
-
|
|
268
|
+
const symbol = this.monomers[i][j];
|
|
269
|
+
helm += symbol.length > 1 ? `[${symbol}]` : symbol;
|
|
170
270
|
}
|
|
171
271
|
helm += `}`;
|
|
172
272
|
}
|
|
@@ -185,8 +285,13 @@ export class Chain {
|
|
|
185
285
|
return helm;
|
|
186
286
|
}
|
|
187
287
|
|
|
188
|
-
|
|
189
|
-
|
|
288
|
+
getNotation(rules: Rules): string {
|
|
289
|
+
return 'not implemented';
|
|
290
|
+
}
|
|
291
|
+
|
|
292
|
+
protected static getLinkedPositions(monomers: string[], rules: RuleLink[] | RuleReaction []):
|
|
293
|
+
[number, number, number][] {
|
|
294
|
+
const result: [number, number, number][] = new Array<[number, number, number]>(rules.length);
|
|
190
295
|
|
|
191
296
|
for (let i = 0; i < rules.length; i++) {
|
|
192
297
|
let firstFound = false;
|
|
@@ -226,19 +331,18 @@ export class Chain {
|
|
|
226
331
|
}
|
|
227
332
|
|
|
228
333
|
if (!(firstFound && secondFound))
|
|
229
|
-
result[i] = [-1, -1];
|
|
334
|
+
result[i] = [-1, -1, -1];
|
|
230
335
|
else if (firstIsFirst)
|
|
231
|
-
result[i] = [firstEntryIndex, secondEntryIndex];
|
|
336
|
+
result[i] = [firstEntryIndex, secondEntryIndex, i];
|
|
232
337
|
else
|
|
233
|
-
result[i] = [secondEntryIndex, firstEntryIndex];
|
|
338
|
+
result[i] = [secondEntryIndex, firstEntryIndex, i];
|
|
234
339
|
}
|
|
235
340
|
|
|
236
|
-
|
|
237
341
|
return result;
|
|
238
342
|
}
|
|
239
343
|
|
|
240
|
-
protected static getAllCycles(rules: RuleLink[], monomers: string [], positions: [number, number][])
|
|
241
|
-
|
|
344
|
+
protected static getAllCycles(rules: RuleLink[], monomers: string [], positions: [number, number, number][]):
|
|
345
|
+
[string [], number [], number [], number [], number []] {
|
|
242
346
|
const allPos1: number [] = [];
|
|
243
347
|
const allPos2: number [] = [];
|
|
244
348
|
const allAttaches1: number [] = [];
|
|
@@ -251,7 +355,6 @@ export class Chain {
|
|
|
251
355
|
|
|
252
356
|
const firstMonomer = monomers[positions[i][0]];
|
|
253
357
|
const secondMonomer = monomers[positions[i][1]];
|
|
254
|
-
|
|
255
358
|
monomers[positions[i][0]] = monomers[positions[i][0]].replace(firstMonomer, rules[i].firstSubstitution);
|
|
256
359
|
monomers[positions[i][1]] = monomers[positions[i][1]].replace(secondMonomer, rules[i].secondSubstitution);
|
|
257
360
|
|
|
@@ -263,17 +366,185 @@ export class Chain {
|
|
|
263
366
|
|
|
264
367
|
return [monomers, allPos1, allPos2, allAttaches1, allAttaches2];
|
|
265
368
|
}
|
|
369
|
+
|
|
370
|
+
protected static getAllReactants(rules: RuleReaction[], monomers: string [], positions: [number, number, number][]):
|
|
371
|
+
[string [], number [], number [], number []] {
|
|
372
|
+
const allPos1: number [] = [];
|
|
373
|
+
const allPos2: number [] = [];
|
|
374
|
+
const rule: number [] = [];
|
|
375
|
+
const ruleCount = rules.length;
|
|
376
|
+
|
|
377
|
+
for (let i = 0; i < ruleCount; i++) {
|
|
378
|
+
if (positions[i][0] == -1)
|
|
379
|
+
continue;
|
|
380
|
+
|
|
381
|
+
const firstMonomer = monomers[positions[i][0]];
|
|
382
|
+
const secondMonomer = monomers[positions[i][1]];
|
|
383
|
+
monomers[positions[i][0]] = monomers[positions[i][0]].replace(firstMonomer, rules[i].firstMonomer);
|
|
384
|
+
monomers[positions[i][1]] = monomers[positions[i][1]].replace(secondMonomer, rules[i].secondMonomer);
|
|
385
|
+
|
|
386
|
+
allPos1.push(positions[i][0] + 1);
|
|
387
|
+
allPos2.push(positions[i][1] + 1);
|
|
388
|
+
rule.push(positions[i][2]);
|
|
389
|
+
}
|
|
390
|
+
|
|
391
|
+
return [monomers, allPos1, allPos2, rule];
|
|
392
|
+
}
|
|
266
393
|
}
|
|
267
394
|
|
|
268
395
|
/** The main PolyTool convert engine. Returns list of Helms. Covered with tests. */
|
|
269
396
|
export function doPolyToolConvert(sequences: string[], rules: Rules): string[] {
|
|
270
397
|
const helms = new Array<string>(sequences.length);
|
|
271
398
|
for (let i = 0; i < sequences.length; i++) {
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
399
|
+
try {
|
|
400
|
+
if (sequences[i] == null) { helms[i] = ''; } else {
|
|
401
|
+
const chain = Chain.fromNotation(sequences[i], rules);
|
|
402
|
+
helms[i] = chain.getHelm();
|
|
403
|
+
}
|
|
404
|
+
} catch (err: any) {
|
|
405
|
+
const [errMsg, errStack] = errInfo(err);
|
|
406
|
+
_package.logger.error(errMsg, undefined, errStack);
|
|
407
|
+
helms[i] = '';
|
|
275
408
|
}
|
|
276
409
|
}
|
|
277
|
-
|
|
278
410
|
return helms;
|
|
279
411
|
}
|
|
412
|
+
|
|
413
|
+
function getMonomersMolBlocks(monomer1: Monomer, monomer2: Monomer): [string, string] {
|
|
414
|
+
const mb1 = monomer1.molfile;
|
|
415
|
+
let mb2 = monomer2.molfile;
|
|
416
|
+
const addGroups = monomer1.rgroups.length;
|
|
417
|
+
|
|
418
|
+
//mol v2000 monomer
|
|
419
|
+
const rgpIdx = mb2.indexOf('M RGP');
|
|
420
|
+
if (rgpIdx !== -1) {
|
|
421
|
+
const groupsCountStr = mb2.substring(rgpIdx + 6, rgpIdx + 9);
|
|
422
|
+
const groupsCount = Number(groupsCountStr);
|
|
423
|
+
|
|
424
|
+
for (let i = 0; i < groupsCount; i++) {
|
|
425
|
+
const start = rgpIdx + 9 + 4 + i * 8;
|
|
426
|
+
const end = rgpIdx + 9 + 8 + i * 8;
|
|
427
|
+
const rGroupSpecifier = mb2.substring(start, end);
|
|
428
|
+
const groupPosition = Number(rGroupSpecifier) + addGroups;
|
|
429
|
+
const digits = Math.floor(Math.log10(groupPosition) + 1);
|
|
430
|
+
const newSpecifier = ' '.repeat(4 - digits) + String(groupPosition);
|
|
431
|
+
mb2 = mb2.substring(0, start) + newSpecifier + mb2.substring(end, mb2.length);
|
|
432
|
+
}
|
|
433
|
+
}
|
|
434
|
+
|
|
435
|
+
//TODO: same for v3000 monomer
|
|
436
|
+
|
|
437
|
+
return [mb1, mb2];
|
|
438
|
+
}
|
|
439
|
+
|
|
440
|
+
function getSyntheticMolBlock(rdkit: RDModule, reaction: string,
|
|
441
|
+
mb1: string, mb2: string, monomerName: string): string {
|
|
442
|
+
let rxn: RDReaction | null = null;
|
|
443
|
+
let mols: MolList | null = null;
|
|
444
|
+
let mol1: RDMol | null = null;
|
|
445
|
+
let mol2: RDMol | null = null;
|
|
446
|
+
let rctns: RDReactionResult | null = null;
|
|
447
|
+
let molP: RDMol | null = null;
|
|
448
|
+
let molBlock = '';
|
|
449
|
+
|
|
450
|
+
try {
|
|
451
|
+
rxn = rdkit.get_rxn(reaction);
|
|
452
|
+
if (!rxn) throw new InvalidReactionError(reaction);
|
|
453
|
+
mols = new rdkit.MolList();
|
|
454
|
+
mol1 = rdkit.get_mol(mb1!);
|
|
455
|
+
mol2 = rdkit.get_mol(mb2!);
|
|
456
|
+
mols.append(mol1!);
|
|
457
|
+
mols.append(mol2!);
|
|
458
|
+
|
|
459
|
+
rctns = rxn.run_reactants(mols, 1);
|
|
460
|
+
//const size = rctns.size();
|
|
461
|
+
const element = rctns.get(0);
|
|
462
|
+
|
|
463
|
+
molP = element.next();
|
|
464
|
+
molBlock = molP?.get_molblock();//molP?.get_v3Kmolblock();//
|
|
465
|
+
} catch (err: any) {
|
|
466
|
+
const [errMsg, _errStack] = errInfo(err);
|
|
467
|
+
grok.shell.error(`Can not assemble monomer '${monomerName}': ${errMsg}.`);
|
|
468
|
+
throw err;
|
|
469
|
+
} finally {
|
|
470
|
+
rxn?.delete();
|
|
471
|
+
mols?.delete();
|
|
472
|
+
mol1?.delete();
|
|
473
|
+
mol2?.delete();
|
|
474
|
+
rctns?.delete();
|
|
475
|
+
molP?.delete();
|
|
476
|
+
}
|
|
477
|
+
|
|
478
|
+
return molBlock;
|
|
479
|
+
}
|
|
480
|
+
|
|
481
|
+
function getNewGroups(monomer1: Monomer, monomer2: Monomer): RGroup[] {
|
|
482
|
+
const groups = new Array<RGroup>(monomer1?.rgroups.length! + monomer2?.rgroups.length!);
|
|
483
|
+
const length1 = monomer1?.rgroups.length!;
|
|
484
|
+
const length2 = monomer2?.rgroups.length!;
|
|
485
|
+
|
|
486
|
+
for (let i = 0; i < length1; i++)
|
|
487
|
+
groups[i] = monomer1?.rgroups[i]!;
|
|
488
|
+
|
|
489
|
+
for (let i = 0; i < length2; i++) {
|
|
490
|
+
const rGroupSpecifier = monomer2?.rgroups[i]!.label.replace('R', '');
|
|
491
|
+
const groupPosition = Number(rGroupSpecifier) + length1;
|
|
492
|
+
const group: RGroup = {
|
|
493
|
+
//@ts-ignore
|
|
494
|
+
[HELM_RGROUP_FIELDS.CAP_GROUP_SMILES_UPPERCASE]: monomer2?.rgroups[i].capGroupSMILES.replace(rGroupSpecifier, String(groupPosition)),
|
|
495
|
+
[HELM_RGROUP_FIELDS.ALTERNATE_ID]: monomer2?.rgroups[i].alternateId.replace(rGroupSpecifier, String(groupPosition)),
|
|
496
|
+
[HELM_RGROUP_FIELDS.CAP_GROUP_NAME]: monomer2?.rgroups[i].capGroupName,
|
|
497
|
+
[HELM_RGROUP_FIELDS.LABEL]: monomer2?.rgroups[i].label.replace(rGroupSpecifier, String(groupPosition)),
|
|
498
|
+
};
|
|
499
|
+
|
|
500
|
+
groups[i + length1] = group;
|
|
501
|
+
}
|
|
502
|
+
|
|
503
|
+
return groups;
|
|
504
|
+
}
|
|
505
|
+
|
|
506
|
+
export function getNewMonomer(rdkit: RDModule, mLib: IMonomerLib, rule: RuleReaction): [string, Monomer] {
|
|
507
|
+
const reacSmarts = rule.reaction;
|
|
508
|
+
const monomerName = rule.name;
|
|
509
|
+
|
|
510
|
+
const monomer1 = mLib.getMonomer('PEPTIDE', rule.firstMonomer);
|
|
511
|
+
if (!monomer1) throw new MonomerNotFoundError('PEPTIDE', rule.firstMonomer);
|
|
512
|
+
const monomer2 = mLib.getMonomer('PEPTIDE', rule.secondMonomer);
|
|
513
|
+
if (!monomer2) throw new MonomerNotFoundError('PEPTIDE', rule.secondMonomer);
|
|
514
|
+
|
|
515
|
+
const [mb1, mb2] = getMonomersMolBlocks(monomer1!, monomer2!);
|
|
516
|
+
const molBlock = getSyntheticMolBlock(rdkit, reacSmarts, mb1, mb2, monomerName);
|
|
517
|
+
const groups: RGroup[] = getNewGroups(monomer1!, monomer2!);
|
|
518
|
+
|
|
519
|
+
const resMonomer: Monomer = {
|
|
520
|
+
[HELM_REQUIRED_FIELD.SYMBOL]: monomerName,
|
|
521
|
+
[HELM_REQUIRED_FIELD.NAME]: monomerName,
|
|
522
|
+
[HELM_REQUIRED_FIELD.MOLFILE]: molBlock,
|
|
523
|
+
[HELM_REQUIRED_FIELD.AUTHOR]: '',
|
|
524
|
+
[HELM_REQUIRED_FIELD.ID]: 0,
|
|
525
|
+
[HELM_REQUIRED_FIELD.RGROUPS]: groups,
|
|
526
|
+
[HELM_REQUIRED_FIELD.SMILES]: '',
|
|
527
|
+
[HELM_REQUIRED_FIELD.POLYMER_TYPE]: 'PEPTIDE',
|
|
528
|
+
[HELM_REQUIRED_FIELD.MONOMER_TYPE]: 'Backbone',
|
|
529
|
+
[HELM_REQUIRED_FIELD.CREATE_DATE]: null,
|
|
530
|
+
};
|
|
531
|
+
|
|
532
|
+
return [monomerName, resMonomer];
|
|
533
|
+
}
|
|
534
|
+
|
|
535
|
+
export async function getOverriddenLibrary(rules: Rules): Promise<IMonomerLibBase> {
|
|
536
|
+
const monomerLibHelper = await getMonomerLibHelper();
|
|
537
|
+
const systemMonomerLib = monomerLibHelper.getMonomerLib();
|
|
538
|
+
|
|
539
|
+
const rdkit = await getRdKitModule();
|
|
540
|
+
const argLib: { [symbol: string]: Monomer } = {};
|
|
541
|
+
|
|
542
|
+
for (let i = 0; i < rules.reactionRules.length; i++) {
|
|
543
|
+
const [name, monomer] = getNewMonomer(rdkit, systemMonomerLib, rules.reactionRules[i]);
|
|
544
|
+
argLib[name] = monomer;
|
|
545
|
+
}
|
|
546
|
+
|
|
547
|
+
const overrideMonomerLibData: MonomerLibData = {[PolymerTypes.PEPTIDE]: argLib};
|
|
548
|
+
const overriddenMonomerLib = systemMonomerLib.override(overrideMonomerLibData);
|
|
549
|
+
return overriddenMonomerLib;
|
|
550
|
+
}
|