@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.
- package/CHANGELOG.md +13 -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 +1 -1
- package/files/samples/HELM.csv +6 -0
- package/files/samples/cyclized.csv +3 -2
- package/files/samples/cyclized_MSA.csv +5 -0
- package/package.json +5 -5
- package/src/apps/common/model/oligo-toolkit-package.ts +11 -4
- package/src/consts.ts +12 -0
- package/src/global.d.ts +13 -0
- package/src/package-test.ts +1 -0
- package/src/package.ts +5 -4
- package/src/polytool/pt-conversion.ts +222 -70
- package/src/polytool/pt-dialog.ts +17 -11
- package/src/polytool/pt-enumeration-helm-dialog.ts +63 -25
- package/src/polytool/pt-enumeration-helm.ts +9 -6
- 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 +5 -1
- package/src/polytool/pt-unrule.ts +3 -2
- 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 +52 -11
- package/src/tests/polytool-convert-tests.ts +102 -25
- package/src/tests/polytool-detectors-custom-notation-test.ts +1 -1
- package/src/tests/polytool-enumerate-breadth-tests.ts +4 -4
- package/src/utils/context-menu.ts +2 -2
- package/src/utils/cyclized.ts +6 -8
|
@@ -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,
|
|
8
|
-
7,R-F-
|
|
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
|
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.
|
|
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.
|
|
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.
|
|
45
|
-
"@datagrok/helm": "^2.5.
|
|
46
|
-
"@datagrok/chem": "^1.12.
|
|
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
|
-
|
|
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.
|
|
64
|
-
this.
|
|
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.
|
|
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
|
+
}
|
package/src/global.d.ts
ADDED
|
@@ -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
|
+
}
|
package/src/package-test.ts
CHANGED
|
@@ -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[
|
|
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 {
|
|
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:
|
|
38
|
+
linkages: Linkage[];
|
|
28
39
|
monomers: string[][];
|
|
29
|
-
mol: HelmMol
|
|
40
|
+
mol: HelmMol;
|
|
30
41
|
|
|
31
42
|
constructor(
|
|
32
43
|
monomers: string[][],
|
|
33
|
-
linkages:
|
|
34
|
-
mol: HelmMol
|
|
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
|
-
|
|
41
|
-
|
|
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
|
-
|
|
48
|
-
|
|
49
|
-
|
|
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
|
|
68
|
-
const
|
|
69
|
-
const
|
|
70
|
-
const
|
|
71
|
-
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(':');
|
|
72
104
|
|
|
73
105
|
linkages.push({
|
|
74
|
-
fChain:
|
|
75
|
-
sChain:
|
|
76
|
-
fMonomer:
|
|
77
|
-
sMonomer:
|
|
78
|
-
fR:
|
|
79
|
-
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', '')),
|
|
80
112
|
});
|
|
81
113
|
}
|
|
82
114
|
}
|
|
83
115
|
|
|
84
|
-
|
|
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
|
-
|
|
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
|
|
93
|
-
|
|
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
|
-
|
|
182
|
-
|
|
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
|
|
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
|
-
|
|
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
|
|
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
|
-
|
|
378
|
-
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}`;
|
|
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
|
|
387
|
-
const bonds = this.mol
|
|
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
|
+
}
|