@datagrok/sequence-translator 1.4.5 → 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.
@@ -39,6 +39,6 @@
39
39
  },
40
40
  {
41
41
  "type": "differentFragments",
42
- "code": "$4"
42
+ "code": "$3"
43
43
  }
44
44
  ]
@@ -0,0 +1,6 @@
1
+ HELM
2
+ "PEPTIDE1{[meI].[hHis].[Aca].N.T.[dE].[Aca].[D-Tyr_Et].[Tyr_ab-dehydroMe].[dV].E.N.[D-Orn].[D-aThr].[Phe_4Me]}$PEPTIDE1,PEPTIDE1,15:R2-1:R1$$$V2.0"
3
+ "PEPTIDE1{R.F.C.T.G.H.F.Y.P.C.[meI]}$PEPTIDE1,PEPTIDE1,3:R3-10:R3$$$V2.0",
4
+ PEPTIDE1{meI.hHis.Aca.Cys_SEt.T.dK.Thr_PO3H2.Aca.Tyr_PO3H2.D-Chg.dV.Phe_ab-dehydro.N.D-Orn.D-aThr.Phe_4Me}$$$$
5
+ "PEPTIDE1{C.A.C.A.C.A.C.A}|PEPTIDE2{C.A.C.A.C.A.C.A}|PEPTIDE3{C.A.C.A.C.A.C.A}|PEPTIDE4{C.A.C.A.C.A.C.A}$PEPTIDE1,PEPTIDE2,3:R3-8:R2|PEPTIDE3,PEPTIDE4,3:R3-8:R2|PEPTIDE1,PEPTIDE3,5:R3-5:R3$$$V2.0"
6
+ "PEPTIDE1{C.A.C.A.C.A.C.A.C.A.C.A}|PEPTIDE2{C.A.C.A.C.A.C.A.C.A.C.A}$PEPTIDE2,PEPTIDE1,1:R1-3:R3|PEPTIDE2,PEPTIDE2,1:R3-12:R2$$$V2.0"
@@ -4,5 +4,6 @@ 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(4)-T-G-H-F-Y-P-aG(4)-meI
8
- 7,R-F-aG(4)-T-G-H-F-Y-P-azG(4)-meI
7
+ 6,D(2)-T-G-H-F-Y-P-NH2(2)
8
+ 7,R-F-azG(4)-T-G-H-F-Y-P-aG(4)-meI
9
+ 8,R-F-aG(4)-T-G-H-F-Y-P-azG(4)-meI
@@ -0,0 +1,5 @@
1
+ n,seqs
2
+ 1,R-F-C(1)-Thz-G-H-F-Y-G-P-C(1)-meI-R
3
+ 2,R-F-C(1)-T-Thz-H-F-Y-G-P-C(1)-meI-R
4
+ 3,R-F-C(1)-T-G-Thz-F-Y-G-P-C(1)-meI-R
5
+ 4,R-F-C(1)-T-G-H-Thz-Y-G-P-C(1)-meI-R
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@datagrok/sequence-translator",
3
3
  "friendlyName": "Sequence Translator",
4
- "version": "1.4.5",
4
+ "version": "1.4.6",
5
5
  "author": {
6
6
  "name": "Alexey Chopovsky",
7
7
  "email": "achopovsky@datagrok.ai"
@@ -22,7 +22,7 @@
22
22
  }
23
23
  ],
24
24
  "dependencies": {
25
- "@datagrok-libraries/bio": "^5.45.2",
25
+ "@datagrok-libraries/bio": "^5.45.5",
26
26
  "@datagrok-libraries/chem-meta": "^1.2.7",
27
27
  "@datagrok-libraries/tutorials": "^1.4.3",
28
28
  "@datagrok-libraries/utils": "^4.3.6",
@@ -41,9 +41,9 @@
41
41
  "devDependencies": {
42
42
  "@datagrok-libraries/helm-web-editor": "^1.1.12",
43
43
  "@datagrok-libraries/js-draw-lite": "^0.0.9",
44
- "@datagrok/bio": "^2.16.2",
45
- "@datagrok/helm": "^2.5.4",
46
- "@datagrok/chem": "^1.12.3",
44
+ "@datagrok/bio": "^2.16.6",
45
+ "@datagrok/helm": "^2.5.7",
46
+ "@datagrok/chem": "^1.12.4",
47
47
  "@types/jquery": "^3.5.14",
48
48
  "@types/js-yaml": "^4.0.5",
49
49
  "@types/lodash": "^4.14.202",
@@ -47,21 +47,28 @@ export class OligoToolkitPackage extends DG.Package implements ITranslationHelpe
47
47
  return this._monomerLibWrapper;
48
48
  }
49
49
 
50
+ private _initPromise: Promise<void>;
51
+ public get initPromise(): Promise<void> { return this._initPromise; }
52
+
50
53
  constructor(opts: { debug: boolean } = {debug: false}) {
51
54
  super();
52
55
  // @ts-ignore
53
56
  super._logger = new LoggerWrapper(super.logger, opts.debug);
54
57
  }
55
58
 
56
- private initPromise?: Promise<void>;
59
+ startInit(initPromise: Promise<void>): void {
60
+ this._initPromise = initPromise;
61
+ }
57
62
 
58
63
  completeInit(seqHelper: ISeqHelper): void {
59
64
  this._seqHelper = seqHelper;
60
65
  }
61
66
 
67
+ private initLibDataPromise?: Promise<void>;
68
+
62
69
  async initLibData(): Promise<void> {
63
- if (!this.initPromise) {
64
- this.initPromise = (async () => {
70
+ if (!this.initLibDataPromise) {
71
+ this.initLibDataPromise = (async () => {
65
72
  const packageSettings = await this.getSettings();
66
73
  let monomersPath: string = packageSettings['MonomersPath'];
67
74
  if (!monomersPath || !(await grok.dapi.files.exists(monomersPath))) {
@@ -76,7 +83,7 @@ export class OligoToolkitPackage extends DG.Package implements ITranslationHelpe
76
83
  this._monomerLibWrapper = new MonomerLibWrapper(this.monomerLib, this.jsonData);
77
84
  })();
78
85
  }
79
- return this.initPromise;
86
+ return this.initLibDataPromise;
80
87
  }
81
88
 
82
89
  async getTranslationHelper(): Promise<ITranslationHelper> {
package/src/consts.ts ADDED
@@ -0,0 +1,12 @@
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
+ export const enum PolyToolTags {
6
+ dataRole = 'polytool-data-role',
7
+ }
8
+
9
+ export const enum PolyToolDataRole {
10
+ template = 'template',
11
+ macromolecule = 'macromolecule',
12
+ }
@@ -0,0 +1,13 @@
1
+ import * as grokNamespace from 'datagrok-api/grok';
2
+ import * as uiNamespace from 'datagrok-api/ui';
3
+ import * as DGNamespace from 'datagrok-api/dg';
4
+ import * as rxjsNamespace from 'rxjs';
5
+ import $Namespace from 'cash-dom';
6
+
7
+ declare global {
8
+ const grok: typeof grokNamespace;
9
+ const ui: typeof uiNamespace;
10
+ const DG: typeof DGNamespace;
11
+ const rjxs: typeof rxjsNamespace;
12
+ const $: typeof $Namespace;
13
+ }
@@ -14,6 +14,7 @@ import './tests/polytool-unrule-tests';
14
14
  import './tests/polytool-enumerate-tests';
15
15
  import './tests/polytool-enumerate-breadth-tests';
16
16
  import './tests/polytool-chain-parse-notation-tests';
17
+ import './tests/polytool-chain-from-notation-tests';
17
18
  import './tests/toAtomicLevel-tests';
18
19
 
19
20
  import {OligoToolkitTestPackage} from './tests/utils';
package/src/package.ts CHANGED
@@ -24,9 +24,9 @@ import {PolyToolCsvLibHandler} from './polytool/csv-to-json-monomer-lib-converte
24
24
  import {ITranslationHelper} from './types';
25
25
  import {addContextMenuUI} from './utils/context-menu';
26
26
  import {PolyToolConvertFuncEditor} from './polytool/pt-convert-editor';
27
- import {polyToolUnruleUI} from './polytool/pt-unrule';
28
27
  import {CyclizedNotationProvider} from './utils/cyclized';
29
28
  import {getSeqHelper} from '@datagrok-libraries/bio/src/utils/seq-helper';
29
+ import {PolyToolTags} from './consts';
30
30
 
31
31
  export const _package: OligoToolkitPackage = new OligoToolkitPackage({debug: true}/**/);
32
32
 
@@ -34,8 +34,9 @@ let initSequenceTranslatorPromise: Promise<void> | null = null;
34
34
 
35
35
  //tags: init
36
36
  export async function init(): Promise<void> {
37
- if (initSequenceTranslatorPromise === null)
38
- initSequenceTranslatorPromise = initSequenceTranslatorInt();
37
+ if (initSequenceTranslatorPromise === null) {
38
+ _package.startInit(initSequenceTranslatorPromise = initSequenceTranslatorInt());
39
+ }
39
40
  return initSequenceTranslatorPromise;
40
41
  }
41
42
 
@@ -300,6 +301,6 @@ export async function ptEnumeratorChemApp(): Promise<void> {
300
301
  //input: string separator
301
302
  export function applyNotationProviderForCyclized(col: DG.Column<string>, separator: string) {
302
303
  col.meta.units = NOTATION.CUSTOM;
303
- col.tags['pt-role'] = 'template';
304
+ col.tags[PolyToolTags.dataRole] = 'template';
304
305
  col.temp[SeqTemps.notationProvider] = new CyclizedNotationProvider(separator, _package.seqHelper);
305
306
  }
@@ -4,6 +4,7 @@ import * as DG from 'datagrok-api/dg';
4
4
 
5
5
  import wu from 'wu';
6
6
 
7
+ import {cleanupHelmSymbol} from '@datagrok-libraries/bio/src/helm/utils';
7
8
  import {HelmTypes, PolymerTypes} from '@datagrok-libraries/bio/src/helm/consts';
8
9
  import {getMonomerLibHelper} from '@datagrok-libraries/bio/src/monomer-works/monomer-utils';
9
10
  import {IMonomerLib, IMonomerLibBase, Monomer, MonomerLibData, RGroup} from '@datagrok-libraries/bio/src/types';
@@ -11,8 +12,8 @@ import {RDModule, RDMol, RDReaction, MolList, RDReactionResult} from '@datagrok-
11
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';
12
13
  import {getRdKitModule} from '@datagrok-libraries/bio/src/chem/rdkit-module';
13
14
  import {errInfo} from '@datagrok-libraries/bio/src/utils/err-info';
14
- import {HelmMol, HelmType, JSDraw2ModuleType, OrgType} from '@datagrok-libraries/bio/src/helm/types';
15
- import {getHelmHelper} from '@datagrok-libraries/bio/src/helm/helm-helper';
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';
16
17
 
17
18
  import {Rules, RuleLink, RuleReaction} from './pt-rules';
18
19
  import {InvalidReactionError, MonomerNotFoundError} from './types';
@@ -22,81 +23,128 @@ import {_package} from '../package';
22
23
  declare const JSDraw2: JSDraw2ModuleType;
23
24
  declare const org: OrgType;
24
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
+ }
34
+
35
+ type PolyToolBio = HelmBio & { i: number, j: number };
25
36
 
26
37
  export class Chain {
27
- linkages: { fChain: number, sChain: number, fMonomer: number, sMonomer: number, fR: number, sR: number }[];
38
+ linkages: Linkage[];
28
39
  monomers: string[][];
29
- mol: HelmMol | null;
40
+ mol: HelmMol;
30
41
 
31
42
  constructor(
32
43
  monomers: string[][],
33
- linkages: { fChain: number, sChain: number, fMonomer: number, sMonomer: number, fR: number, sR: number }[],
34
- mol: HelmMol | null) {
44
+ linkages: Linkage[],
45
+ mol: HelmMol) {
35
46
  this.linkages = linkages;
36
47
  this.monomers = monomers;
37
48
  this.mol = mol;
38
49
  }
39
50
 
40
- static fromHelm(helm: string) {
41
- const fragmentation = helm.split('$');
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
+
42
57
  const rawFragments = fragmentation[0].split('|');
43
58
  const rawLinkages = fragmentation[1].split('|');
44
59
 
45
60
  const monomers = new Array<Array<string>>(rawFragments.length);
46
- const linkages: {
47
- fChain: number,
48
- sChain: number,
49
- fMonomer: number,
50
- sMonomer: number,
51
- fR: number,
52
- sR: number
53
- }[] = [];
61
+ const linkages: Linkage[] = [];
62
+
63
+ const resHwe = helmHelper.createHelmWebEditor();
64
+ const resMol = resHwe.editor.m;
54
65
 
66
+ let counter = 0;
67
+ const p = new JSDraw2.Point(0, 0);
55
68
  //HELM parsing
56
69
  for (let i = 0; i < rawFragments.length; i++) {
57
70
  const idxStart = rawFragments[i].indexOf('{');
58
71
  const idxEnd = rawFragments[i].indexOf('}');
59
72
 
60
- 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
61
93
  }
62
94
 
63
95
  //HELM parsing
64
96
  for (let i = 0; i < rawLinkages.length; i++) {
65
97
  if (rawLinkages[i] !== '' && rawLinkages[i] !== 'V2.0') {
66
98
  const rawData = rawLinkages[i].split(',');
67
- const seq1 = (rawData[0].replace('PEPTIDE', '') as unknown as number) - 1;
68
- const seq2 = (rawData[1].replace('PEPTIDE', '') as unknown as number) - 1;
69
- const rawDataConnctions = rawData[2].split('-');
70
- const rawDataConnction1 = rawDataConnctions[0].split(':');
71
- const rawDataConnction2 = rawDataConnctions[1].split(':');
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(':');
72
104
 
73
105
  linkages.push({
74
- fChain: seq1,
75
- sChain: seq2,
76
- fMonomer: rawDataConnction1[0] as unknown as number,
77
- sMonomer: rawDataConnction2[0] as unknown as number,
78
- fR: rawDataConnction1[1].replace('R', '') as unknown as number,
79
- sR: rawDataConnction2[1].replace('R', '') as unknown as number,
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', '')),
80
112
  });
81
113
  }
82
114
  }
83
115
 
84
- return new Chain(monomers, linkages, null);
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);
85
126
  }
86
127
 
87
- static fromNotation(sequence: string, rules: Rules): Chain {
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;
139
+ }
140
+
141
+ /** @deprecated Use {@link parseNotation} and {@link applyRules} instead. */
142
+ static fromNotation(sequence: string, rules: Rules, helmHelper: IHelmHelper): Chain {
88
143
  const heterodimerCode = rules.heterodimerCode;
89
144
  const homodimerCode = rules.homodimerCode;
90
- const mainFragments: string[] = [];
91
145
 
92
- const linkages: {
93
- fChain: number,
94
- sChain: number,
95
- fMonomer: number,
96
- sMonomer: number,
97
- fR: number,
98
- sR: number
99
- }[] = [];
146
+ const mainFragments: string[] = [];
147
+ const linkages: Linkage[] = [];
100
148
 
101
149
  //NOTICE: this works only with simple single heterodimers
102
150
  const heterodimeric = heterodimerCode !== null ? sequence.split(`(${rules.heterodimerCode!})`) : '';
@@ -167,6 +215,11 @@ export class Chain {
167
215
 
168
216
  const monomersAll: string[][] = [];
169
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);
170
223
  for (let i = 0; i < monomers.length; i++) {
171
224
  const linkedPositions = this.getLinkedPositions(monomers[i], rules.reactionRules);
172
225
  const [monomersCycled, allPos1, allPos2, ruleN] =
@@ -175,14 +228,41 @@ export class Chain {
175
228
  if (allPos1.length >= 1) {
176
229
  const ch1 = new Array<string>(allPos2[0] - 1);
177
230
  const ch2 = new Array<string>(monomersCycled.length - allPos2[0]);
178
- for (let j = 0; j < allPos2[0] - 1; j++)
179
- ch1[j] = monomersCycled[j];
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);
180
236
 
181
- for (let j = allPos2[0]; j < monomersCycled.length; j++)
182
- ch2[j - allPos2[0]] = monomersCycled[j];
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
+ }
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);
183
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
+ }
184
264
 
185
- 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;
186
266
 
187
267
  for (let j = 0; j < linkages.length; j++) {
188
268
  if (linkages[j].fMonomer > allPos2[0]) {
@@ -222,32 +302,46 @@ export class Chain {
222
302
  monomersAll.push(ch1);
223
303
  monomersAll.push(ch2);
224
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
+ }
225
321
  monomersAll.push(monomers[i]);
226
322
  }
227
323
  }
228
324
 
229
- const chain = new Chain(monomersAll, linkages, null);
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);
230
335
  return chain;
231
336
  }
232
337
 
233
- static async parseNotation(sequence: string): Promise<Chain> {
338
+ /** Parse harmonized sequence notation (template) */
339
+ static parseNotation(sequence: string, helmHelper: IHelmHelper): Chain {
234
340
  const mainFragments: string[][] = [];
235
341
 
236
- const linkages: {
237
- fChain: number,
238
- sChain: number,
239
- fMonomer: number,
240
- sMonomer: number,
241
- fR: number,
242
- sR: number
243
- }[] = [];
244
-
342
+ const linkages: Linkage[] = [];
245
343
 
246
- const hh = await getHelmHelper();
247
- // const sampleHwe = hh.createHelmWebEditor();
248
- // sampleHwe.editor.setHelm('PEPTIDE1{R.P.D.[meI]}$$$$');
249
-
250
- const resHwe = hh.createHelmWebEditor();
344
+ const resHwe = helmHelper.createHelmWebEditor();
251
345
  const resMol = resHwe.editor.m;
252
346
 
253
347
  const rxp = /(\(.\d+\))?\{[^\}]*\}/g;
@@ -268,17 +362,18 @@ export class Chain {
268
362
  for (let i = 0; i < seqs.length; i++) {
269
363
  const splMonomers = seqs[i].split('-');
270
364
  const monomers: string [] = new Array<string>(splMonomers.length);
365
+ let spmCount: number = 0;
271
366
  for (let j = 0; j < splMonomers.length; j++) {
272
367
  const monomer = splMonomers[j].replace('{', '').replace('}', '');
273
368
  if (monomer !== '') {
274
369
  monomers[j] = monomer;
275
370
  counter++;
371
+ spmCount++;
276
372
  } else {
277
373
  linkages.push({fChain: i, sChain: i + 1, fMonomer: counter, sMonomer: counter + 1, fR: 1, sR: 1});
278
374
  }
279
375
  }
280
-
281
- mainFragments.push(monomers);
376
+ mainFragments.push(monomers.slice(0, spmCount));
282
377
  }
283
378
 
284
379
  counter = 0;
@@ -287,7 +382,7 @@ export class Chain {
287
382
  for (let j = 0; j < mainFragments[i].length; j++) {
288
383
  if (!!mainFragments[i][j]) {
289
384
  const elem = mainFragments[i][j];
290
- const bio = {type: HelmTypes.AA, i: i, j: j};
385
+ const bio: PolyToolBio = {type: HelmTypes.AA, i: i, j: j, continuousId: counter};
291
386
  const atom = new JSDraw2.Atom<HelmType>(p, elem, bio);
292
387
  resMol.addAtom(atom);
293
388
 
@@ -347,10 +442,12 @@ export class Chain {
347
442
  return res;
348
443
  }
349
444
 
445
+ /** Gets harmonized sequence (template) pseudo helm */
350
446
  getNotationHelm(): string {
351
447
  return this.getHelm();
352
448
  }
353
449
 
450
+ /** Gets harmonized sequence (template) pseudo helm */
354
451
  getHelm(): string {
355
452
  let helm = '';
356
453
  for (let i = 0; i < this.monomers.length; i++) {
@@ -374,17 +471,18 @@ export class Chain {
374
471
  if (i > 0)
375
472
  helm += '|';
376
473
  helm += `PEPTIDE${this.linkages[i].fChain + 1},PEPTIDE${this.linkages[i].sChain + 1},`;
377
- helm += `${this.linkages[i].fMonomer}:R${this.linkages[i].fR}-`;
378
- helm += `${this.linkages[i].sMonomer}:R${this.linkages[i].sR}`;
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}`;
379
477
  }
380
478
 
381
- helm += '$$$';
479
+ helm += '$$$' + 'V2.0';
382
480
  return helm;
383
481
  }
384
482
 
385
483
  getNotation(): string {
386
- const atoms = this.mol!.atoms;
387
- const bonds = this.mol!.bonds;
484
+ const atoms = this.mol.atoms;
485
+ const bonds = this.mol.bonds;
388
486
  const chains: number[] = [];
389
487
  const specialBonds: number[] = [];
390
488
  for (let i = 0; i < bonds.length!; i++) {
@@ -545,15 +643,48 @@ export class Chain {
545
643
 
546
644
  return [monomers, allPos1, allPos2, rule];
547
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
+ }
548
679
  }
549
680
 
550
681
  /** The main PolyTool convert engine. Returns list of Helms. Covered with tests. */
551
- export function doPolyToolConvert(sequences: string[], rules: Rules): string[] {
682
+ export function doPolyToolConvert(sequences: string[], rules: Rules, helmHelper: IHelmHelper): string[] {
552
683
  const helms = new Array<string>(sequences.length);
553
684
  for (let i = 0; i < sequences.length; i++) {
554
685
  try {
555
686
  if (sequences[i] == null) { helms[i] = ''; } else {
556
- const chain = Chain.fromNotation(sequences[i], rules);
687
+ const chain = Chain.fromNotation(sequences[i], rules, helmHelper);
557
688
  helms[i] = chain.getHelm();
558
689
  }
559
690
  } catch (err: any) {
@@ -710,3 +841,24 @@ export async function getOverriddenLibrary(rules: Rules): Promise<IMonomerLibBas
710
841
  'ST-PT-reactions.' + wu.repeat(1).map(() => Math.floor((Math.random() * 36)).toString(36)).take(4).toArray().join(''));
711
842
  return overriddenMonomerLib;
712
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
+ }