@datagrok/sequence-translator 1.6.4 → 1.8.0
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 +2 -1
- package/CHANGELOG.md +17 -0
- package/dist/455.js +1 -1
- package/dist/455.js.map +1 -1
- 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/package.json +8 -8
- package/src/polytool/conversion/pt-atomic.ts +70 -0
- package/src/polytool/conversion/pt-chain.ts +22 -2
- package/src/polytool/conversion/pt-conversion.ts +5 -2
- package/src/polytool/conversion/pt-rule-cards.ts +160 -0
- package/src/polytool/conversion/pt-rules.ts +24 -0
- package/src/polytool/conversion/pt-tools-parse.ts +11 -21
- package/src/polytool/conversion/rule-manager.ts +52 -6
- package/src/polytool/conversion/style.css +32 -0
- package/src/polytool/pt-dialog.ts +48 -74
- package/src/polytool/pt-enumerate-seq-dialog.ts +19 -5
- package/src/tests/polytool-convert-tests.ts +1 -1
- package/test-console-output-1.log +1685 -0
- package/test-record-1.mp4 +0 -0
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
|
+
"version": "1.8.0",
|
|
5
5
|
"author": {
|
|
6
6
|
"name": "Leonid Stolbov",
|
|
7
7
|
"email": "lstolbov@datagrok.ai"
|
|
@@ -22,13 +22,13 @@
|
|
|
22
22
|
}
|
|
23
23
|
],
|
|
24
24
|
"dependencies": {
|
|
25
|
-
"@datagrok-libraries/bio": "^5.
|
|
26
|
-
"@datagrok-libraries/chem-meta": "^1.2.
|
|
25
|
+
"@datagrok-libraries/bio": "^5.50.0",
|
|
26
|
+
"@datagrok-libraries/chem-meta": "^1.2.8",
|
|
27
27
|
"@datagrok-libraries/tutorials": "^1.4.3",
|
|
28
28
|
"@datagrok-libraries/utils": "^4.3.7",
|
|
29
29
|
"@types/react": "^18.0.15",
|
|
30
30
|
"cash-dom": "^8.1.0",
|
|
31
|
-
"datagrok-api": "^1.
|
|
31
|
+
"datagrok-api": "^1.24.0",
|
|
32
32
|
"lodash": "^4.17.21",
|
|
33
33
|
"object-hash": "^3.0.0",
|
|
34
34
|
"openchemlib": "6.0.1",
|
|
@@ -41,10 +41,10 @@
|
|
|
41
41
|
"devDependencies": {
|
|
42
42
|
"@datagrok-libraries/helm-web-editor": "^1.1.13",
|
|
43
43
|
"@datagrok-libraries/js-draw-lite": "^0.0.10",
|
|
44
|
-
"@datagrok/bio": "^2.
|
|
45
|
-
"@datagrok/
|
|
46
|
-
"@datagrok/
|
|
47
|
-
"@types/jquery": "^3.5.
|
|
44
|
+
"@datagrok/bio": "^2.18.0",
|
|
45
|
+
"@datagrok/chem": "^1.13.0",
|
|
46
|
+
"@datagrok/helm": "^2.7.0",
|
|
47
|
+
"@types/jquery": "^3.5.32",
|
|
48
48
|
"@types/js-yaml": "^4.0.5",
|
|
49
49
|
"@types/lodash": "^4.14.202",
|
|
50
50
|
"@types/node-fetch": "^2.6.2",
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
import * as DG from 'datagrok-api/dg';
|
|
2
|
+
import * as grok from 'datagrok-api/grok';
|
|
3
|
+
|
|
4
|
+
import {getMonomerLibHelper} from '@datagrok-libraries/bio/src/monomer-works/monomer-utils';
|
|
5
|
+
import {getSeqHelper, ISeqHelper} from '@datagrok-libraries/bio/src/utils/seq-helper';
|
|
6
|
+
import {_toAtomicLevel} from '@datagrok-libraries/bio/src/monomer-works/to-atomic-level';
|
|
7
|
+
import {ALPHABET, NOTATION} from '@datagrok-libraries/bio/src/utils/macromolecule';
|
|
8
|
+
import {IMonomerLibBase} from '@datagrok-libraries/bio/src/types';
|
|
9
|
+
import {RDModule} from '@datagrok-libraries/chem-meta/src/rdkit-api';
|
|
10
|
+
|
|
11
|
+
export function dealGroups(col: DG.Column<string>): void {
|
|
12
|
+
for (let i = 0; i < col.length; i++) {
|
|
13
|
+
col.set(i, col.get(i)!.replaceAll('undefined', 'H'));
|
|
14
|
+
col.set(i, col.get(i)!.replaceAll('Oh', 'O'));
|
|
15
|
+
col.set(i, col.get(i)!.replaceAll('0.000000 3', '0.000000 0'));
|
|
16
|
+
col.set(i, col.get(i)!.replaceAll('?', 'O'));
|
|
17
|
+
col.set(i, col.get(i)!.replaceAll('0 3\n', '0 0\n'));
|
|
18
|
+
col.set(i, col.get(i)!.replaceAll('RGROUPS=(1 1)', ''));
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
export async function helmToMol(resHelmCol: DG.Column, resList: string[],
|
|
23
|
+
isLinear: boolean[], chiralityEngine: boolean, highlight: boolean, linearize: boolean,
|
|
24
|
+
lib: IMonomerLibBase, rdkit: RDModule, seqHelper: ISeqHelper) {
|
|
25
|
+
const getUnusedName = (df: DG.DataFrame | undefined, colName: string): string => {
|
|
26
|
+
if (!df) return colName;
|
|
27
|
+
return df.columns.getUnusedName(colName);
|
|
28
|
+
};
|
|
29
|
+
|
|
30
|
+
const toAtomicLevelRes =
|
|
31
|
+
await seqHelper.helmToAtomicLevel(resHelmCol, chiralityEngine, highlight, lib);
|
|
32
|
+
|
|
33
|
+
const resMolCol = toAtomicLevelRes.molCol!;
|
|
34
|
+
|
|
35
|
+
const allLinear = isLinear.filter((l) => l).length;
|
|
36
|
+
if (linearize && allLinear > 0) {
|
|
37
|
+
const lin = new Array<string>(allLinear);
|
|
38
|
+
let counter = 0;
|
|
39
|
+
for (let i = 0; i < isLinear.length; i++) {
|
|
40
|
+
if (isLinear[i]) {
|
|
41
|
+
lin[counter] = resList[i];
|
|
42
|
+
counter++;
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
const linCol = DG.Column.fromStrings('helm', lin);
|
|
47
|
+
linCol.semType = DG.SEMTYPE.MACROMOLECULE;
|
|
48
|
+
linCol.meta.units = NOTATION.HELM;
|
|
49
|
+
linCol.setTag(DG.TAGS.CELL_RENDERER, 'helm');
|
|
50
|
+
|
|
51
|
+
const monomerLibHelper = await getMonomerLibHelper();
|
|
52
|
+
const systemMonomerLib = monomerLibHelper.getMonomerLib();
|
|
53
|
+
try {
|
|
54
|
+
const linear = await _toAtomicLevel(DG.DataFrame.create(0), linCol, systemMonomerLib, seqHelper, rdkit);
|
|
55
|
+
counter = 0;
|
|
56
|
+
for (let i = 0; i < isLinear.length; i++) {
|
|
57
|
+
if (isLinear[i]) {
|
|
58
|
+
resMolCol.set(i, linear!.molCol!.get(counter));
|
|
59
|
+
counter++;
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
} catch (e: any) {
|
|
63
|
+
grok.shell.warning('PolyTool was not able to linearize sequences');
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
dealGroups(resMolCol);
|
|
68
|
+
|
|
69
|
+
return resMolCol;
|
|
70
|
+
}
|
|
@@ -16,6 +16,7 @@ export class Chain {
|
|
|
16
16
|
linkagesUnderRules: Linkage[];
|
|
17
17
|
monomersUnderRules: string[][];
|
|
18
18
|
molUnderRules: HelmMol;
|
|
19
|
+
posToPosUnderRules: number[][] = [];
|
|
19
20
|
|
|
20
21
|
constructor(monomers: string[][], linkages: Linkage[], protected helmHelper: IHelmHelper) {
|
|
21
22
|
this.linkages = linkages;
|
|
@@ -52,9 +53,28 @@ export class Chain {
|
|
|
52
53
|
applyRules(rules: Rules): void {
|
|
53
54
|
const sequence = this.getNotation();
|
|
54
55
|
|
|
55
|
-
const [linkages, mainFragments] = handleDuplicated(sequence, rules);
|
|
56
|
+
const [linkages, mainFragments, isDuplicated] = handleDuplicated(sequence, rules);
|
|
56
57
|
const monomers = new Array<Array<string>>(mainFragments.length);
|
|
57
|
-
|
|
58
|
+
|
|
59
|
+
let counter = 0;
|
|
60
|
+
for (let i = 0; i < mainFragments.length; i++) {
|
|
61
|
+
monomers[i] = mainFragments[i].split('-');
|
|
62
|
+
if (!isDuplicated[i]) {
|
|
63
|
+
for (let j = 0; j < monomers[i].length; j++) {
|
|
64
|
+
this.posToPosUnderRules.push([counter]);
|
|
65
|
+
counter++;
|
|
66
|
+
}
|
|
67
|
+
} else {
|
|
68
|
+
const start = this.posToPosUnderRules.length - monomers[i].length;
|
|
69
|
+
for (let j = 0; j < monomers[i].length; j++) {
|
|
70
|
+
this.posToPosUnderRules[start + j].push(counter);
|
|
71
|
+
counter++;
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
//his.posToPosUnderRules
|
|
77
|
+
handleLinkRules(monomers, linkages, rules);
|
|
58
78
|
handleReactionRules(monomers, linkages, rules);
|
|
59
79
|
|
|
60
80
|
this.underRules = true;
|
|
@@ -6,9 +6,11 @@ import {Chain} from './pt-chain';
|
|
|
6
6
|
import {_package} from '../../package';
|
|
7
7
|
|
|
8
8
|
/** The main PolyTool convert engine. Returns list of Helms. Covered with tests. */
|
|
9
|
-
export function doPolyToolConvert(sequences: string[], rules: Rules, helmHelper: IHelmHelper):
|
|
9
|
+
export function doPolyToolConvert(sequences: string[], rules: Rules, helmHelper: IHelmHelper):
|
|
10
|
+
[string[], boolean[], number[][][]] {
|
|
10
11
|
const helms = new Array<string>(sequences.length);
|
|
11
12
|
const isLinear = new Array<boolean>(sequences.length);
|
|
13
|
+
const positionMaps = new Array<number[][]>(sequences.length);
|
|
12
14
|
for (let i = 0; i < sequences.length; i++) {
|
|
13
15
|
try {
|
|
14
16
|
if (sequences[i] == null) { helms[i] = ''; } else {
|
|
@@ -16,6 +18,7 @@ export function doPolyToolConvert(sequences: string[], rules: Rules, helmHelper:
|
|
|
16
18
|
chain.applyRules(rules);
|
|
17
19
|
isLinear[i] = chain.monomersUnderRules.length > 1 || chain.linkagesUnderRules.length > 0 ? false : true;
|
|
18
20
|
helms[i] = chain.getHelm();
|
|
21
|
+
positionMaps[i] = chain.posToPosUnderRules;
|
|
19
22
|
}
|
|
20
23
|
} catch (err: any) {
|
|
21
24
|
const [errMsg, errStack] = errInfo(err);
|
|
@@ -23,5 +26,5 @@ export function doPolyToolConvert(sequences: string[], rules: Rules, helmHelper:
|
|
|
23
26
|
helms[i] = '';
|
|
24
27
|
}
|
|
25
28
|
}
|
|
26
|
-
return [helms, isLinear];
|
|
29
|
+
return [helms, isLinear, positionMaps];
|
|
27
30
|
}
|
|
@@ -0,0 +1,160 @@
|
|
|
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
|
+
import './style.css';
|
|
5
|
+
|
|
6
|
+
import {IMonomerLib, Monomer} from '@datagrok-libraries/bio/src/types';
|
|
7
|
+
import {Rules} from './pt-rules';
|
|
8
|
+
import {getHelmHelper} from '@datagrok-libraries/bio/src/helm/helm-helper';
|
|
9
|
+
import {doPolyToolConvert} from './pt-conversion';
|
|
10
|
+
import {NOTATION} from '@datagrok-libraries/bio/src/utils/macromolecule';
|
|
11
|
+
import {getRdKitModule} from '@datagrok-libraries/bio/src/chem/rdkit-module';
|
|
12
|
+
import {getSeqHelper, ISeqHelper} from '@datagrok-libraries/bio/src/utils/seq-helper';
|
|
13
|
+
import {RDModule} from '@datagrok-libraries/chem-meta/src/rdkit-api';
|
|
14
|
+
import {getOverriddenLibrary} from './pt-synthetic';
|
|
15
|
+
import {helmToMol} from './pt-atomic';
|
|
16
|
+
|
|
17
|
+
class MonomerCard {
|
|
18
|
+
root: HTMLElement = ui.divV([], {classes: 'monomer-card-rule-root'});
|
|
19
|
+
|
|
20
|
+
private _selected: boolean = false;
|
|
21
|
+
get selected(): boolean { return this._selected; }
|
|
22
|
+
set selected(value: boolean) {
|
|
23
|
+
this._selected = value;
|
|
24
|
+
this.root.style.border = value ? '2px solid var(--green-2)' : '2px solid var(--grey-2)';
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
constructor(public monomer: Monomer | null) {}
|
|
28
|
+
|
|
29
|
+
render() {
|
|
30
|
+
if (this.monomer) {
|
|
31
|
+
ui.empty(this.root);
|
|
32
|
+
const monomerMolSvg = this.monomer.smiles && grok.chem.checkSmiles(this.monomer.smiles) ?
|
|
33
|
+
grok.chem.drawMolecule(this.monomer.smiles, 150, 120) :
|
|
34
|
+
grok.chem.drawMolecule(this.monomer.molfile ?? '', 150, 120);
|
|
35
|
+
this.root.appendChild(monomerMolSvg);
|
|
36
|
+
const monomerName =
|
|
37
|
+
ui.divH([ui.divText('Monomer Name: '), ui.divText(this.monomer.name)], {classes: 'monomer-card-info-rules'});
|
|
38
|
+
|
|
39
|
+
this.root.appendChild(monomerName);
|
|
40
|
+
ui.tooltip.bind(monomerName, this.monomer.name);
|
|
41
|
+
if (this.monomer.lib?.source) {
|
|
42
|
+
const monomerSource =
|
|
43
|
+
ui.divH([ui.divText('Source: '), ui.divText(this.monomer.lib.source)], {classes: 'monomer-card-info-rules'});
|
|
44
|
+
this.root.appendChild(monomerSource);
|
|
45
|
+
ui.tooltip.bind(monomerSource, this.monomer.lib.source);
|
|
46
|
+
}
|
|
47
|
+
const monomerType = ui.divH([
|
|
48
|
+
ui.divText('Polymer Type: '),
|
|
49
|
+
ui.divText(this.monomer.polymerType)
|
|
50
|
+
], {classes: 'monomer-card-info-rules'});
|
|
51
|
+
this.root.appendChild(monomerType);
|
|
52
|
+
ui.tooltip.bind(monomerType, this.monomer.polymerType);
|
|
53
|
+
|
|
54
|
+
ui.tooltip.bind(this.root, 'Select Monomer');
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
export class RuleCards {
|
|
60
|
+
root: HTMLElement = ui.divH([],
|
|
61
|
+
{style: {
|
|
62
|
+
alignItems: 'center',
|
|
63
|
+
width: '100%',
|
|
64
|
+
overflow: 'hidden',
|
|
65
|
+
visibility: 'visible',
|
|
66
|
+
}, classes: 'monomer-cards'}
|
|
67
|
+
);
|
|
68
|
+
cardsFirst: MonomerCard[];
|
|
69
|
+
cardsSecond: MonomerCard[];
|
|
70
|
+
firstCard: MonomerCard;
|
|
71
|
+
secondCard: MonomerCard;
|
|
72
|
+
resulting: HTMLElement;
|
|
73
|
+
actionable: boolean;
|
|
74
|
+
|
|
75
|
+
constructor(public firstMonomers: string[], public secondMonomers: string[],
|
|
76
|
+
lib: IMonomerLib, public code: number, public rules: Rules) {
|
|
77
|
+
if (firstMonomers.length > 0) {
|
|
78
|
+
this.actionable = true;
|
|
79
|
+
const monomerGalleryFirst = ui.divH([], {style: {overflowX: 'auto', width: '100%'}});
|
|
80
|
+
const monomerGallerySecond = ui.divH([], {style: {overflowX: 'auto', width: '100%'}});
|
|
81
|
+
this.resulting = ui.divH([], {style: {overflowX: 'auto', width: '100%', minHeight: '150px'}});
|
|
82
|
+
const galleries = ui.divV([monomerGalleryFirst, monomerGallerySecond, this.resulting]);
|
|
83
|
+
this.cardsFirst = firstMonomers.map((symbol) => new MonomerCard(lib.getMonomer('PEPTIDE', symbol)));
|
|
84
|
+
this.cardsSecond = secondMonomers.map((symbol) => new MonomerCard(lib.getMonomer('PEPTIDE', symbol)));
|
|
85
|
+
|
|
86
|
+
this.cardsFirst.forEach((card) => {
|
|
87
|
+
card.root.onclick = () => {
|
|
88
|
+
this.cardsFirst.forEach((c) => c.selected = false);
|
|
89
|
+
card.selected = true;
|
|
90
|
+
this.firstCard = card;
|
|
91
|
+
this.reset();
|
|
92
|
+
//onMonomerSelected(card.monomer);
|
|
93
|
+
};
|
|
94
|
+
monomerGalleryFirst.appendChild(card.root);
|
|
95
|
+
});
|
|
96
|
+
|
|
97
|
+
this.cardsSecond.forEach((card) => {
|
|
98
|
+
card.root.onclick = () => {
|
|
99
|
+
this.cardsSecond.forEach((c) => c.selected = false);
|
|
100
|
+
card.selected = true;
|
|
101
|
+
this.secondCard = card;
|
|
102
|
+
this.reset();
|
|
103
|
+
//onMonomerSelected(card.monomer);
|
|
104
|
+
};
|
|
105
|
+
monomerGallerySecond.appendChild(card.root);
|
|
106
|
+
});
|
|
107
|
+
|
|
108
|
+
this.cardsFirst[0].selected = true;
|
|
109
|
+
this.cardsSecond[0].selected = true;
|
|
110
|
+
this.firstCard = this.cardsFirst[0];
|
|
111
|
+
this.secondCard = this.cardsSecond[0];
|
|
112
|
+
this.root.appendChild(galleries);
|
|
113
|
+
} else {
|
|
114
|
+
const es = ui.divV(['Rule is active for all monomers, no examples to show']);
|
|
115
|
+
this.root.appendChild(es);
|
|
116
|
+
this.actionable = false;
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
async reset() {
|
|
121
|
+
if (this.actionable) {
|
|
122
|
+
const seqs: string[] = [
|
|
123
|
+
`${this.firstCard.monomer?.symbol}(${this.code})-A-A-A-A-${this.secondCard.monomer?.symbol}(${this.code})`
|
|
124
|
+
];
|
|
125
|
+
|
|
126
|
+
const helmHelper = await getHelmHelper();
|
|
127
|
+
const [helms, isLinear, positionMaps] = doPolyToolConvert(seqs, this.rules, helmHelper);
|
|
128
|
+
|
|
129
|
+
const resHelmCol = DG.Column.fromType(DG.COLUMN_TYPE.STRING, 'helm', helms.length)
|
|
130
|
+
.init((rowIdx: number) => { return helms[rowIdx]; });
|
|
131
|
+
resHelmCol.semType = DG.SEMTYPE.MACROMOLECULE;
|
|
132
|
+
resHelmCol.meta.units = NOTATION.HELM;
|
|
133
|
+
resHelmCol.setTag(DG.TAGS.CELL_RENDERER, 'helm');
|
|
134
|
+
|
|
135
|
+
const rdKitModule: RDModule = await getRdKitModule();
|
|
136
|
+
const seqHelper: ISeqHelper = await getSeqHelper();
|
|
137
|
+
|
|
138
|
+
const lib = await getOverriddenLibrary(this.rules);
|
|
139
|
+
const resHelmColTemp = resHelmCol.temp;
|
|
140
|
+
resHelmCol.temp = resHelmColTemp;
|
|
141
|
+
const resMolCol = await helmToMol(resHelmCol, helms,
|
|
142
|
+
isLinear, true, false, false, lib, rdKitModule, seqHelper);
|
|
143
|
+
|
|
144
|
+
const mol = resMolCol.get(0);
|
|
145
|
+
const monomerMolSvg = mol && grok.chem.checkSmiles(mol) ?
|
|
146
|
+
grok.chem.drawMolecule(mol, 150, 120) :
|
|
147
|
+
grok.chem.drawMolecule(mol ?? '', 150, 120);
|
|
148
|
+
|
|
149
|
+
ui.empty(this.resulting);
|
|
150
|
+
this.resulting.append(ui.divH([monomerMolSvg], {style: {overflowX: 'auto', width: '100%', minHeight: '150px'}}));
|
|
151
|
+
}
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
render() {
|
|
155
|
+
if (this.actionable) {
|
|
156
|
+
this.cardsFirst.forEach((card) => card.render());
|
|
157
|
+
this.cardsSecond.forEach((card) => card.render());
|
|
158
|
+
}
|
|
159
|
+
}
|
|
160
|
+
}
|
|
@@ -3,6 +3,9 @@ import * as grok from 'datagrok-api/grok';
|
|
|
3
3
|
import * as ui from 'datagrok-api/ui';
|
|
4
4
|
import {ActiveFiles} from '@datagrok-libraries/utils/src/settings/active-files-base';
|
|
5
5
|
import {RulesManager} from './rule-manager';
|
|
6
|
+
import {RuleCards} from './pt-rule-cards';
|
|
7
|
+
import {getMonomerLibHelper} from '@datagrok-libraries/bio/src/monomer-works/monomer-utils';
|
|
8
|
+
import {applyNotationProviderForCyclized} from '../../package';
|
|
6
9
|
|
|
7
10
|
export const RULES_PATH = 'System:AppData/SequenceTranslator/polytool-rules/';
|
|
8
11
|
export const RULES_STORAGE_NAME = 'Polytool';
|
|
@@ -95,10 +98,17 @@ export class Rules {
|
|
|
95
98
|
const length = this.linkRules.length;
|
|
96
99
|
const codeCol = DG.Column.int(NAME_CODE, length);
|
|
97
100
|
codeCol.setTag('friendlyName', 'Code');
|
|
101
|
+
//ui.tooltip.bind(codeCol.root, 'Click to zoom');
|
|
98
102
|
const firstMonomerCol = DG.Column.string(NAME_FIRST_MONOMERS, length);
|
|
99
103
|
firstMonomerCol.setTag('friendlyName', 'First monomers');
|
|
104
|
+
firstMonomerCol.semType = DG.SEMTYPE.MACROMOLECULE;
|
|
105
|
+
applyNotationProviderForCyclized(firstMonomerCol, ',');
|
|
106
|
+
|
|
100
107
|
const secondMonomerCol = DG.Column.string(NAME_SECOND_MONOMERS, length);
|
|
101
108
|
secondMonomerCol.setTag('friendlyName', 'Second monomers');
|
|
109
|
+
secondMonomerCol.semType = DG.SEMTYPE.MACROMOLECULE;
|
|
110
|
+
applyNotationProviderForCyclized(secondMonomerCol, ',');
|
|
111
|
+
|
|
102
112
|
const firstLinkingGroup = DG.Column.int(NAME_FIRST_LINK, length);
|
|
103
113
|
firstLinkingGroup.setTag('friendlyName', 'First group');
|
|
104
114
|
const secondLinkingGroup = DG.Column.int(NAME_SECOND_LINK, length);
|
|
@@ -119,6 +129,20 @@ export class Rules {
|
|
|
119
129
|
return res;
|
|
120
130
|
}
|
|
121
131
|
|
|
132
|
+
async getLinkCards(): Promise<RuleCards[]> {
|
|
133
|
+
const length = this.linkRules.length;
|
|
134
|
+
const cards: RuleCards[] = new Array<RuleCards>(length);
|
|
135
|
+
const monomerLibHelper = await getMonomerLibHelper();
|
|
136
|
+
const systemMonomerLib = monomerLibHelper.getMonomerLib();
|
|
137
|
+
|
|
138
|
+
for (let i = 0; i < length; i++) {
|
|
139
|
+
cards[i] = new RuleCards(this.linkRules[i].firstMonomers, this.linkRules[i].secondMonomers,
|
|
140
|
+
systemMonomerLib, this.linkRules[i].code, this);
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
return cards;
|
|
144
|
+
}
|
|
145
|
+
|
|
122
146
|
getSynthesisRulesDf(): DG.DataFrame {
|
|
123
147
|
const length = this.reactionRules.length;
|
|
124
148
|
const codeCol = DG.Column.int(NAME_CODE, length);
|
|
@@ -121,9 +121,10 @@ export function fromObjectsToHelm(linkages: Linkage[], monomers: string[][]): st
|
|
|
121
121
|
return helm;
|
|
122
122
|
}
|
|
123
123
|
|
|
124
|
-
//homo and hetero dimers
|
|
125
|
-
export function handleDuplicated(sequence: string, rules: Rules): [Linkage[], string[]] {
|
|
124
|
+
//homo and hetero dimers/ also fills map from initial positions to positions arrays
|
|
125
|
+
export function handleDuplicated(sequence: string, rules: Rules): [Linkage[], string[], boolean[]] {
|
|
126
126
|
const mainFragments: string[] = [];
|
|
127
|
+
const isDuplicated: boolean[] = [];
|
|
127
128
|
const linkages: Linkage[] = [];
|
|
128
129
|
const heterodimerCode = rules.heterodimerCode;
|
|
129
130
|
const homodimerCode = rules.homodimerCode;
|
|
@@ -134,8 +135,11 @@ export function handleDuplicated(sequence: string, rules: Rules): [Linkage[], st
|
|
|
134
135
|
linkages.push({fChain: 0, sChain: 1, fMonomer: 1, sMonomer: 1, fR: 1, sR: 1});
|
|
135
136
|
mainFragments.push(heterodimeric[1].replaceAll('{', '').replaceAll('}', ''));
|
|
136
137
|
mainFragments.push(heterodimeric[2].replaceAll('{', '').replaceAll('}', ''));
|
|
138
|
+
isDuplicated.push(false);
|
|
139
|
+
isDuplicated.push(false);
|
|
137
140
|
} else {
|
|
138
141
|
mainFragments.push(sequence);
|
|
142
|
+
isDuplicated.push(false);
|
|
139
143
|
}
|
|
140
144
|
|
|
141
145
|
//NOTICE: this works only with simple single dimers
|
|
@@ -151,30 +155,16 @@ export function handleDuplicated(sequence: string, rules: Rules): [Linkage[], st
|
|
|
151
155
|
|
|
152
156
|
mainFragments[i] = linker + body;
|
|
153
157
|
mainFragments.push(body);
|
|
158
|
+
isDuplicated.push(true);
|
|
154
159
|
}
|
|
155
160
|
}
|
|
156
161
|
|
|
157
|
-
|
|
158
|
-
if (homodimerCode !== null && mainFragments[i].includes(`(${homodimerCode!})`)) {
|
|
159
|
-
const idxSequence = mainFragments.length;
|
|
160
|
-
|
|
161
|
-
linkages.push({fChain: i, sChain: idxSequence, fMonomer: 1, sMonomer: 1, fR: 1, sR: 1});
|
|
162
|
-
const rawDimer = mainFragments[i].replace(`(${homodimerCode!})`, '');
|
|
163
|
-
const idx = rawDimer.indexOf('{');
|
|
164
|
-
const linker = rawDimer.slice(0, idx);
|
|
165
|
-
const body = rawDimer.replace(linker, '').replaceAll('{', '').replaceAll('}', '');
|
|
166
|
-
|
|
167
|
-
mainFragments[i] = linker + body;
|
|
168
|
-
mainFragments.push(body);
|
|
169
|
-
}
|
|
170
|
-
}
|
|
171
|
-
|
|
172
|
-
return [linkages, mainFragments];
|
|
162
|
+
return [linkages, mainFragments, isDuplicated];
|
|
173
163
|
}
|
|
174
164
|
|
|
175
|
-
export function handleLinkRules(
|
|
176
|
-
for (let i = 0; i <
|
|
177
|
-
const rawMonomers =
|
|
165
|
+
export function handleLinkRules(monomers: string[][], linkages: Linkage[], rules: Rules): void {
|
|
166
|
+
for (let i = 0; i < monomers.length; i++) {
|
|
167
|
+
const rawMonomers = monomers[i];
|
|
178
168
|
const linkedPositions = getLinkedPositions(rawMonomers, rules.linkRules);
|
|
179
169
|
const [allPos1, allPos2, allAttaches1, allAttaches2] =
|
|
180
170
|
getAllCycles(rules.linkRules, rawMonomers, linkedPositions);
|
|
@@ -6,6 +6,7 @@ import {_package, applyNotationProviderForCyclized} from '../../package';
|
|
|
6
6
|
import {getHelmHelper} from '@datagrok-libraries/bio/src/helm/helm-helper';
|
|
7
7
|
import {doPolyToolConvert} from './pt-conversion';
|
|
8
8
|
import {NOTATION} from '@datagrok-libraries/bio/src/utils/macromolecule/consts';
|
|
9
|
+
import {RuleCards} from './pt-rule-cards';
|
|
9
10
|
|
|
10
11
|
|
|
11
12
|
const TAB_LINKS = 'Links';
|
|
@@ -22,12 +23,15 @@ export class RulesManager {
|
|
|
22
23
|
homoDimerInput: DG.InputBase;
|
|
23
24
|
heteroDimerInput: DG.InputBase;
|
|
24
25
|
|
|
26
|
+
linkCards: RuleCards[];
|
|
27
|
+
|
|
25
28
|
// every rule set will have its editor instance
|
|
26
29
|
private static instances: Record<string, RulesManager> = {};
|
|
27
30
|
|
|
28
31
|
protected constructor(rules: Rules, fileName: string) {
|
|
29
32
|
this.rules = rules;
|
|
30
33
|
this.linkRuleDataFrame = this.rules.getLinkRulesDf();
|
|
34
|
+
|
|
31
35
|
this.synthRuleDataFrame = this.rules.getSynthesisRulesDf();
|
|
32
36
|
this.fileName = fileName;
|
|
33
37
|
|
|
@@ -96,8 +100,9 @@ export class RulesManager {
|
|
|
96
100
|
grok.shell.info(`Polytool rules at ${this.fileName} was updated`);
|
|
97
101
|
}
|
|
98
102
|
|
|
99
|
-
private createGridDiv(name: string, grid: DG.Grid) {
|
|
103
|
+
private createGridDiv(name: string, grid: DG.Grid, tooltipMsg?: string) {
|
|
100
104
|
const header = ui.h1(name, 'polytool-grid-header');
|
|
105
|
+
ui.tooltip.bind(header, tooltipMsg);
|
|
101
106
|
header.style.marginTop = '10px';
|
|
102
107
|
header.style.marginRight = '10px';
|
|
103
108
|
grid.root.style.height = '100%';
|
|
@@ -128,7 +133,7 @@ export class RulesManager {
|
|
|
128
133
|
}
|
|
129
134
|
const helmHelper = await getHelmHelper();
|
|
130
135
|
|
|
131
|
-
const [helms, isLinear] = doPolyToolConvert(seqs, this.rules, helmHelper);
|
|
136
|
+
const [helms, isLinear, positionMaps] = doPolyToolConvert(seqs, this.rules, helmHelper);
|
|
132
137
|
|
|
133
138
|
const initCol = DG.Column.fromStrings('Monomers', seqs);
|
|
134
139
|
const helmCol = DG.Column.fromStrings('Helm', helms);
|
|
@@ -157,7 +162,7 @@ export class RulesManager {
|
|
|
157
162
|
}
|
|
158
163
|
const helmHelper = await getHelmHelper();
|
|
159
164
|
|
|
160
|
-
const [helms, isLinear] = doPolyToolConvert(seqs, this.rules, helmHelper);
|
|
165
|
+
const [helms, isLinear, positionMaps] = doPolyToolConvert(seqs, this.rules, helmHelper);
|
|
161
166
|
|
|
162
167
|
const initCol = DG.Column.fromStrings('Monomers', seqs);
|
|
163
168
|
const helmCol = DG.Column.fromStrings('Helm', helms);
|
|
@@ -177,11 +182,45 @@ export class RulesManager {
|
|
|
177
182
|
inputsTabControl: DG.TabControl;
|
|
178
183
|
|
|
179
184
|
const linksGridDiv = this.createGridDiv('Rules',
|
|
180
|
-
this.linkRuleDataFrame.plot.grid({showAddNewRowIcon: true})
|
|
181
|
-
|
|
185
|
+
this.linkRuleDataFrame.plot.grid({showAddNewRowIcon: true}),
|
|
186
|
+
'specification for monomers to link and linking positions');
|
|
187
|
+
const linkExamples = this.createGridDiv('Examples', await this.getLinkExamplesGrid(),
|
|
188
|
+
'specification for monomers to link and linking positions');
|
|
182
189
|
linksGridDiv.style.width = '50%';
|
|
183
190
|
linkExamples.style.width = '50%';
|
|
184
|
-
const
|
|
191
|
+
const header = ui.h1('Monomers', 'polytool-grid-header');
|
|
192
|
+
ui.tooltip.bind(header, 'Click different cobination to see how monomers will link');
|
|
193
|
+
this.linkCards = await this.rules.getLinkCards();
|
|
194
|
+
const gridDiv: HTMLElement = ui.splitV([
|
|
195
|
+
ui.box(
|
|
196
|
+
header,
|
|
197
|
+
{style: {maxHeight: '30px'}},
|
|
198
|
+
),
|
|
199
|
+
this.linkCards[0].root,
|
|
200
|
+
]);
|
|
201
|
+
|
|
202
|
+
this.linkCards[0].render();
|
|
203
|
+
await this.linkCards[0].reset();
|
|
204
|
+
|
|
205
|
+
this.linkRuleDataFrame.currentRowIdx = 0;
|
|
206
|
+
this.linkRuleDataFrame.onCurrentRowChanged.subscribe(async () => {
|
|
207
|
+
const idx = this.linkRuleDataFrame.currentRowIdx;
|
|
208
|
+
if (idx !== -1) {
|
|
209
|
+
ui.empty(gridDiv);
|
|
210
|
+
gridDiv.append(ui.splitV([
|
|
211
|
+
ui.box(
|
|
212
|
+
header,
|
|
213
|
+
{style: {maxHeight: '30px'}},
|
|
214
|
+
),
|
|
215
|
+
this.linkCards[idx].root,
|
|
216
|
+
]));
|
|
217
|
+
}
|
|
218
|
+
|
|
219
|
+
this.linkCards[idx].render();
|
|
220
|
+
await this.linkCards[idx].reset();
|
|
221
|
+
});
|
|
222
|
+
|
|
223
|
+
const links = ui.splitH([linksGridDiv, gridDiv], null, true);
|
|
185
224
|
|
|
186
225
|
const reactionsGridDiv = this.createGridDiv('Rules',
|
|
187
226
|
this.synthRuleDataFrame.plot.grid({showAddNewRowIcon: true}));
|
|
@@ -201,6 +240,13 @@ export class RulesManager {
|
|
|
201
240
|
'Dimers': dimerInputsDiv,
|
|
202
241
|
}, false);
|
|
203
242
|
|
|
243
|
+
ui.tooltip.bind(inputsTabControl.getPane(TAB_LINKS).header,
|
|
244
|
+
'Specify rules to link monomers based on HELM notation');
|
|
245
|
+
ui.tooltip.bind(inputsTabControl.getPane(TAB_REACTIONS).header,
|
|
246
|
+
'Specify rules to perform reactions within monomers');
|
|
247
|
+
ui.tooltip.bind(inputsTabControl.getPane(TAB_DIMERS).header,
|
|
248
|
+
'Specify symbols for homodimeric and heterodimeric codes');
|
|
249
|
+
|
|
204
250
|
inputsTabControl.root.style.height = '100%';
|
|
205
251
|
inputsTabControl.root.style.width = '100%';
|
|
206
252
|
inputsTabControl.root.classList.add('rules-manager-form-tab-control');
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
.monomer-card-rule-root {
|
|
2
|
+
border: 2px solid var(--grey-2);
|
|
3
|
+
border-radius: 5px;
|
|
4
|
+
padding: 5px;
|
|
5
|
+
margin: 5px;
|
|
6
|
+
overflow: hidden;
|
|
7
|
+
align-items: center;
|
|
8
|
+
cursor: pointer;
|
|
9
|
+
width: 200px;
|
|
10
|
+
height: 210px;
|
|
11
|
+
min-width: 200px;
|
|
12
|
+
min-height: 210px;
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
.monomer-card-info-rules {
|
|
16
|
+
width: 100%;
|
|
17
|
+
overflow: hidden;
|
|
18
|
+
text-overflow: ellipsis;
|
|
19
|
+
margin-bottom: 10px;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
.monomer-card-info-rules > div:nth-child(2) {
|
|
23
|
+
white-space: nowrap;
|
|
24
|
+
overflow: hidden;
|
|
25
|
+
text-overflow: ellipsis;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
.monomer-card-info-rules > div:nth-child(1) {
|
|
29
|
+
font-weight: bold;
|
|
30
|
+
margin-right: 5px;
|
|
31
|
+
text-wrap: nowrap;
|
|
32
|
+
}
|