@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.
@@ -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"
@@ -4,3 +4,5 @@ n,seqs
4
4
  3,R-F-C(1)-T-G-H-F-Y-P-C(1)
5
5
  4,C(1)-T-G-H-F-H-P-C(1)
6
6
  5,R-F-D(2)-T-G-H-F-Y-P-NH2(2)
7
+ 6,R-F-azG(3)-T-G-H-F-Y-P-aG(3)-meI
8
+ 7,R-F-aG(3)-T-G-H-F-Y-P-azG(3)-meI
@@ -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.2",
4
+ "version": "1.4.4",
5
5
  "author": {
6
- "name": "Alexey Choposky",
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.0",
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.0",
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.3",
45
- "@datagrok/helm": "^2.5.0",
46
- "@datagrok/chem": "^1.12.0",
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",
@@ -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 } from '@datagrok-libraries/utils/src/test';
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
@@ -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 {NOTATION} from '@datagrok-libraries/bio/src/utils/macromolecule';
5
- import {ALIGNMENT, ALPHABET} from '@datagrok-libraries/bio/src/utils/macromolecule';
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, getRules} from './pt-rules';
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
- col.semType = DG.SEMTYPE.MACROMOLECULE;
14
- col.setTag('aligned', ALIGNMENT.SEQ);
15
- col.setTag('alphabet', ALPHABET.PT);
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: {fChain: number, sChain: number, fMonomer:number, sMonomer:number, fR:number, sR:number}[] = [];
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: {fChain: number, sChain: number, fMonomer:number, sMonomer:number, fR:number, sR:number}[] = [];
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
- this.getAllCycles(rules.linkRules, rawMonomers, linkedPositions);
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
- monomersReady[j] = `[${monomersCycled[j]}]`;
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] = monomersReady;
162
+ monomers[i] = monomersCycled;
126
163
  }
127
164
 
128
- return new Chain(monomers, linkages);
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
- for (let i = 0; i < this.monomers.length; i++) {
138
- loop2:
139
- for (let j = 0; j < this.monomers[i].length; j++) {
140
- if (counter == changeNumber) {
141
- idx1 = i;
142
- idx2 = j;
143
- break loop1;
144
- }
145
- counter++;
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
- helm += this.monomers[i][j];
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
- protected static getLinkedPositions(monomers: string[], rules: RuleLink[]): [number, number][] {
189
- const result: [number, number][] = new Array<[number, number]>(rules.length);
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
- [string [], number [], number [], number [], number []] {
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
- if (sequences[i] === undefined) { helms[i] = ''; } else {
273
- const chain = Chain.fromNotation(sequences[i], rules);
274
- helms[i] = chain.getHelm();
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
+ }