@datagrok/sequence-translator 1.2.7 → 1.3.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 +5 -5
- package/CHANGELOG.md +14 -0
- package/dist/package-test.js +2 -1
- package/dist/package-test.js.LICENSE.txt +8 -0
- package/dist/package-test.js.map +1 -1
- package/dist/package.js +2 -1
- package/dist/package.js.LICENSE.txt +8 -0
- package/dist/package.js.map +1 -1
- package/files/pattern-app-data.json +80 -0
- package/package.json +22 -14
- package/src/{model → apps/common/model}/const.ts +1 -1
- package/src/{model/data-loading-utils → apps/common/model/data-loader}/const.ts +7 -2
- package/src/apps/common/model/data-loader/json-loader.ts +48 -0
- package/src/{model/data-loading-utils → apps/common/model/data-loader}/types.ts +13 -6
- package/src/{model → apps/common/model}/monomer-lib/lib-wrapper.ts +9 -12
- package/src/apps/common/model/oligo-toolkit-package.ts +30 -0
- package/src/{model → apps/common/model}/parsing-validation/format-detector.ts +5 -5
- package/src/{model → apps/common/model}/parsing-validation/format-handler.ts +18 -19
- package/src/{model → apps/common/model}/parsing-validation/sequence-validator.ts +1 -1
- package/src/apps/common/view/app-ui-base.ts +28 -0
- package/src/apps/common/view/combined-app-ui.ts +66 -0
- package/src/{view/utils → apps/common/view/components}/colored-input/colored-text-input.ts +1 -1
- package/src/{view/utils → apps/common/view/components}/draw-molecule.ts +1 -1
- package/src/{view/utils → apps/common/view/components}/molecule-img.ts +3 -3
- package/src/{view/const/ui.ts → apps/common/view/const.ts} +4 -4
- package/src/apps/common/view/isolated-app-ui.ts +43 -0
- package/src/{view/monomer-lib-viewer/viewer.ts → apps/common/view/monomer-lib-viewer.ts} +2 -2
- package/src/apps/common/view/utils.ts +29 -0
- package/src/apps/pattern/model/const.ts +121 -0
- package/src/apps/pattern/model/data-manager.ts +297 -0
- package/src/apps/pattern/model/event-bus.ts +487 -0
- package/src/apps/pattern/model/router.ts +46 -0
- package/src/apps/pattern/model/subscription-manager.ts +21 -0
- package/src/apps/pattern/model/translator.ts +94 -0
- package/src/apps/pattern/model/types.ts +52 -0
- package/src/apps/pattern/model/utils.ts +110 -0
- package/src/apps/pattern/view/components/bulk-convert/column-input.ts +79 -0
- package/src/apps/pattern/view/components/bulk-convert/table-controls.ts +38 -0
- package/src/apps/pattern/view/components/bulk-convert/table-input.ts +95 -0
- package/src/apps/pattern/view/components/edit-block-controls.ts +196 -0
- package/src/apps/pattern/view/components/left-section.ts +44 -0
- package/src/apps/pattern/view/components/load-block-controls.ts +200 -0
- package/src/apps/pattern/view/components/numeric-label-visibility-controls.ts +69 -0
- package/src/apps/pattern/view/components/right-section.ts +148 -0
- package/src/apps/pattern/view/components/strand-editor/dialog.ts +79 -0
- package/src/apps/pattern/view/components/strand-editor/header-controls.ts +105 -0
- package/src/apps/pattern/view/components/strand-editor/strand-controls.ts +159 -0
- package/src/apps/pattern/view/components/terminal-modification-editor.ts +127 -0
- package/src/apps/pattern/view/components/translation-examples-block.ts +139 -0
- package/src/{view/style/pattern-app.css → apps/pattern/view/style.css} +4 -0
- package/src/apps/pattern/view/svg-utils/const.ts +77 -0
- package/src/apps/pattern/view/svg-utils/legend-block.ts +92 -0
- package/src/apps/pattern/view/svg-utils/strands-block.ts +335 -0
- package/src/apps/pattern/view/svg-utils/svg-block-base.ts +37 -0
- package/src/apps/pattern/view/svg-utils/svg-display-manager.ts +44 -0
- package/src/apps/pattern/view/svg-utils/svg-element-factory.ts +94 -0
- package/src/apps/pattern/view/svg-utils/svg-renderer.ts +51 -0
- package/src/apps/pattern/view/svg-utils/text-dimensions-calculator.ts +29 -0
- package/src/apps/pattern/view/svg-utils/title-block.ts +53 -0
- package/src/apps/pattern/view/svg-utils/utils.ts +37 -0
- package/src/apps/pattern/view/types.ts +14 -0
- package/src/apps/pattern/view/ui.ts +61 -0
- package/src/{model/structure-app → apps/structure/model}/mol-transformations.ts +3 -3
- package/src/{model/structure-app → apps/structure/model}/monomer-code-parser.ts +9 -10
- package/src/{model/structure-app → apps/structure/model}/oligo-structure.ts +4 -4
- package/src/{model/structure-app → apps/structure/model}/sequence-to-molfile.ts +2 -2
- package/src/{view/apps/oligo-structure.ts → apps/structure/view/ui.ts} +31 -17
- package/src/{model/translator-app → apps/translator/model}/conversion-utils.ts +25 -7
- package/src/{model/translator-app → apps/translator/model}/format-converter.ts +7 -12
- package/src/{view/const/oligo-translator.ts → apps/translator/view/const.ts} +1 -1
- package/src/{view/apps/oligo-translator.ts → apps/translator/view/ui.ts} +88 -42
- package/src/demo/demo-st-ui.ts +12 -32
- package/src/package.ts +91 -55
- package/src/plugins/mermade.ts +9 -9
- package/src/polytool/const.ts +28 -0
- package/src/polytool/csv-to-json-monomer-lib-converter.ts +40 -0
- package/src/polytool/cyclized.ts +56 -0
- package/src/polytool/monomer-lib-handler.ts +115 -0
- package/src/polytool/pt-conversion.ts +307 -0
- package/src/polytool/pt-dialog.ts +115 -0
- package/src/polytool/pt-enumeration.ts +127 -0
- package/src/polytool/pt-rules.ts +73 -0
- package/src/polytool/utils.ts +27 -0
- package/src/tests/const.ts +5 -5
- package/src/tests/formats-support.ts +6 -6
- package/src/tests/formats-to-helm.ts +5 -5
- package/src/tests/helm-to-nucleotides.ts +5 -10
- package/tsconfig.json +3 -9
- package/webpack.config.js +3 -0
- package/files/axolabs-style.json +0 -97
- package/src/model/data-loading-utils/json-loader.ts +0 -38
- package/src/model/pattern-app/const.ts +0 -33
- package/src/model/pattern-app/draw-svg.ts +0 -193
- package/src/model/pattern-app/helpers.ts +0 -96
- package/src/model/pattern-app/oligo-pattern.ts +0 -111
- package/src/view/app-ui.ts +0 -193
- package/src/view/apps/oligo-pattern.ts +0 -759
- /package/src/{model → apps/common/model}/helpers.ts +0 -0
- /package/src/{model → apps/common/model}/monomer-lib/const.ts +0 -0
- /package/src/{view/utils → apps/common/view/components}/app-info-dialog.ts +0 -0
- /package/src/{view/utils → apps/common/view/components}/colored-input/input-painters.ts +0 -0
- /package/src/{view/style/colored-text-input.css → apps/common/view/components/colored-input/style.css} +0 -0
- /package/src/{view/utils → apps/common/view/components}/router.ts +0 -0
- /package/src/{model/structure-app → apps/structure/model}/const.ts +0 -0
- /package/src/{view/style/structure-app.css → apps/structure/view/style.css} +0 -0
- /package/src/{model/translator-app → apps/translator/model}/const.ts +0 -0
- /package/src/{view/style/translator-app.css → apps/translator/view/style.css} +0 -0
|
@@ -0,0 +1,307 @@
|
|
|
1
|
+
import * as grok from 'datagrok-api/grok';
|
|
2
|
+
import * as DG from 'datagrok-api/dg';
|
|
3
|
+
|
|
4
|
+
import {NOTATION} from '@datagrok-libraries/bio/src/utils/macromolecule';
|
|
5
|
+
import {ALIGNMENT, ALPHABET} from '@datagrok-libraries/bio/src/utils/macromolecule';
|
|
6
|
+
|
|
7
|
+
import {Rules, RuleLink, getRules} from './pt-rules';
|
|
8
|
+
|
|
9
|
+
export const RULES_DIMER = '(#2)';
|
|
10
|
+
export const RULES_HETERODIMER = '($2)';
|
|
11
|
+
|
|
12
|
+
function addCommonTags(col: DG.Column): void {
|
|
13
|
+
col.setTag('quality', DG.SEMTYPE.MACROMOLECULE);
|
|
14
|
+
col.setTag('aligned', ALIGNMENT.SEQ);
|
|
15
|
+
col.setTag('alphabet', ALPHABET.PT);
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
export class Chain {
|
|
19
|
+
linkages: {fChain: number, sChain: number, fMonomer:number, sMonomer:number, fR:number, sR:number}[];
|
|
20
|
+
monomers: string[][];
|
|
21
|
+
|
|
22
|
+
constructor(
|
|
23
|
+
monomers: string[][],
|
|
24
|
+
linkages: {fChain: number, sChain: number, fMonomer:number, sMonomer:number, fR:number, sR:number}[]) {
|
|
25
|
+
this.linkages = linkages;
|
|
26
|
+
this.monomers = monomers;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
static fromHelm(helm: string) {
|
|
30
|
+
const fragmentation = helm.split('$');
|
|
31
|
+
const rawFragments = fragmentation[0].split('|');
|
|
32
|
+
const rawLinkages = fragmentation[1].split('|');
|
|
33
|
+
|
|
34
|
+
const monomers = new Array<Array<string>>(rawFragments.length);
|
|
35
|
+
const linkages: {fChain: number, sChain: number, fMonomer:number, sMonomer:number, fR:number, sR:number}[] = [];
|
|
36
|
+
|
|
37
|
+
//HELM parsing
|
|
38
|
+
for (let i = 0; i < rawFragments.length; i++) {
|
|
39
|
+
const idxStart = rawFragments[i].indexOf('{');
|
|
40
|
+
const idxEnd = rawFragments[i].indexOf('}');
|
|
41
|
+
|
|
42
|
+
monomers[i] = rawFragments[i].slice(idxStart + 1, idxEnd).split('.');
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
//HELM parsing
|
|
46
|
+
for (let i = 0; i < rawLinkages.length; i++) {
|
|
47
|
+
if (rawLinkages[i] !== '' && rawLinkages[i] !== 'V2.0') {
|
|
48
|
+
const rawData = rawLinkages[i].split(',');
|
|
49
|
+
const seq1 = (rawData[0].replace('PEPTIDE', '') as unknown as number) - 1;
|
|
50
|
+
const seq2 = (rawData[1].replace('PEPTIDE', '') as unknown as number) - 1;
|
|
51
|
+
const rawDataConnctions = rawData[2].split('-');
|
|
52
|
+
const rawDataConnction1 = rawDataConnctions[0].split(':');
|
|
53
|
+
const rawDataConnction2 = rawDataConnctions[1].split(':');
|
|
54
|
+
|
|
55
|
+
linkages.push({
|
|
56
|
+
fChain: seq1,
|
|
57
|
+
sChain: seq2,
|
|
58
|
+
fMonomer: rawDataConnction1[0] as unknown as number,
|
|
59
|
+
sMonomer: rawDataConnction2[0] as unknown as number,
|
|
60
|
+
fR: rawDataConnction1[1].replace('R', '') as unknown as number,
|
|
61
|
+
sR: rawDataConnction2[1].replace('R', '') as unknown as number,
|
|
62
|
+
});
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
return new Chain(monomers, linkages);
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
static fromNotation(sequence: string, rules: Rules) {
|
|
70
|
+
const heterodimerCode = rules.heterodimerCode;
|
|
71
|
+
const homodimerCode = rules.homodimerCode;
|
|
72
|
+
const mainFragments: string[] = [];
|
|
73
|
+
|
|
74
|
+
const linkages: {fChain: number, sChain: number, fMonomer:number, sMonomer:number, fR:number, sR:number}[] = [];
|
|
75
|
+
|
|
76
|
+
//NOTICE: this works only with simple single heterodimers
|
|
77
|
+
const heterodimeric = heterodimerCode !== null? sequence.split(`(${rules.heterodimerCode!})`) : '';
|
|
78
|
+
if (heterodimerCode !== null && heterodimeric.length > 1) {
|
|
79
|
+
linkages.push({fChain: 0, sChain: 1, fMonomer: 1, sMonomer: 1, fR: 1, sR: 1});
|
|
80
|
+
mainFragments.push(heterodimeric[1].replaceAll('{', '').replaceAll('}', ''));
|
|
81
|
+
mainFragments.push(heterodimeric[2].replaceAll('{', '').replaceAll('}', ''));
|
|
82
|
+
} else {
|
|
83
|
+
mainFragments.push(sequence);
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
//NOTICE: this works only with simple single dimers
|
|
87
|
+
for (let i = 0; i < mainFragments.length; i++) {
|
|
88
|
+
if (homodimerCode !== null && mainFragments[i].includes(`(${homodimerCode!})`)) {
|
|
89
|
+
const idxSequence = mainFragments.length;
|
|
90
|
+
|
|
91
|
+
linkages.push({fChain: i, sChain: idxSequence, fMonomer: 1, sMonomer: 1, fR: 1, sR: 1});
|
|
92
|
+
const rawDimer = mainFragments[i].replace(`(${homodimerCode!})`, '');
|
|
93
|
+
const idx = rawDimer.indexOf('{');
|
|
94
|
+
const linker = rawDimer.slice(0, idx);
|
|
95
|
+
const body = rawDimer.replace(linker, '').replaceAll('{', '').replaceAll('}', '');
|
|
96
|
+
|
|
97
|
+
mainFragments[i] = linker + body;
|
|
98
|
+
mainFragments.push(body);
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
const monomers = new Array<Array<string>>(mainFragments.length);
|
|
103
|
+
|
|
104
|
+
for (let i = 0; i < mainFragments.length; i++) {
|
|
105
|
+
const rawMonomers = mainFragments[i].split('-');
|
|
106
|
+
const linkedPositions = this.getLinkedPositions(rawMonomers, rules.linkRules);
|
|
107
|
+
const [monomersCycled, allPos1, allPos2, allAttaches1, allAttaches2] =
|
|
108
|
+
this.getAllCycles(rules.linkRules, rawMonomers, linkedPositions);
|
|
109
|
+
|
|
110
|
+
const monomersReady = new Array<string>(monomersCycled.length);
|
|
111
|
+
for (let j = 0; j < monomersCycled.length; j++)
|
|
112
|
+
monomersReady[j] = `[${monomersCycled[j]}]`;
|
|
113
|
+
|
|
114
|
+
for (let j = 0; j < allPos1.length; j++) {
|
|
115
|
+
linkages.push({
|
|
116
|
+
fChain: i,
|
|
117
|
+
sChain: i,
|
|
118
|
+
fMonomer: allPos1[j],
|
|
119
|
+
sMonomer: allPos2[j],
|
|
120
|
+
fR: allAttaches1[j],
|
|
121
|
+
sR: allAttaches2[j],
|
|
122
|
+
});
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
monomers[i] = monomersReady;
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
return new Chain(monomers, linkages);
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
getHelmChanged(changeNumber: number, monomer: string): string {
|
|
132
|
+
//TODO: make more efficient
|
|
133
|
+
let counter = 0;
|
|
134
|
+
let idx1 = 0;
|
|
135
|
+
let idx2 = 0;
|
|
136
|
+
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++;
|
|
146
|
+
}
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
const previous = this.monomers[idx1][idx2];
|
|
150
|
+
|
|
151
|
+
this.monomers[idx1][idx2] = `[${monomer}]`;
|
|
152
|
+
const res = this.getHelm();
|
|
153
|
+
this.monomers[idx1][idx2] = previous;
|
|
154
|
+
|
|
155
|
+
return res;
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
getHelm(): string {
|
|
159
|
+
let helm = '';
|
|
160
|
+
for (let i = 0; i < this.monomers.length; i++) {
|
|
161
|
+
if (i > 0)
|
|
162
|
+
helm += '|';
|
|
163
|
+
|
|
164
|
+
helm += `PEPTIDE${i + 1}{`;
|
|
165
|
+
|
|
166
|
+
for (let j = 0; j < this.monomers[i].length; j++) {
|
|
167
|
+
if (j > 0)
|
|
168
|
+
helm += '.';
|
|
169
|
+
helm += this.monomers[i][j];
|
|
170
|
+
}
|
|
171
|
+
helm += `}`;
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
helm += '$';
|
|
175
|
+
|
|
176
|
+
for (let i = 0; i < this.linkages.length; i++) {
|
|
177
|
+
if (i > 0)
|
|
178
|
+
helm += '|';
|
|
179
|
+
helm += `PEPTIDE${this.linkages[i].fChain + 1},PEPTIDE${this.linkages[i].sChain + 1},`;
|
|
180
|
+
helm += `${this.linkages[i].fMonomer}:R${this.linkages[i].fR}-`;
|
|
181
|
+
helm += `${this.linkages[i].sMonomer}:R${this.linkages[i].sR}`;
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
helm += '$$$';
|
|
185
|
+
return helm;
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
protected static getLinkedPositions(monomers: string[], rules: RuleLink[]): [number, number][] {
|
|
189
|
+
const result: [number, number][] = new Array<[number, number]>(rules.length);
|
|
190
|
+
|
|
191
|
+
for (let i = 0; i < rules.length; i++) {
|
|
192
|
+
let firstFound = false;
|
|
193
|
+
let secondFound = false;
|
|
194
|
+
let firstIsFirst = false;
|
|
195
|
+
let firstEntryIndex = -1;
|
|
196
|
+
let secondEntryIndex = -1;
|
|
197
|
+
const add = `(${rules[i].code})`;
|
|
198
|
+
for (let j = 0; j < monomers.length; j++) {
|
|
199
|
+
if (monomers[j].includes(add)) {
|
|
200
|
+
if (firstFound) {
|
|
201
|
+
if (firstIsFirst && monomers[j] == rules[i].secondMonomer + add) {
|
|
202
|
+
secondFound = true;
|
|
203
|
+
secondEntryIndex = j;
|
|
204
|
+
break;
|
|
205
|
+
} else if (!firstIsFirst && monomers[j] == rules[i].firstMonomer + add) {
|
|
206
|
+
secondFound = true;
|
|
207
|
+
secondEntryIndex = j;
|
|
208
|
+
break;
|
|
209
|
+
} else {
|
|
210
|
+
continue;
|
|
211
|
+
}
|
|
212
|
+
} else {
|
|
213
|
+
if (monomers[j] == rules[i].firstMonomer + add) {
|
|
214
|
+
firstFound = true;
|
|
215
|
+
firstIsFirst = true;
|
|
216
|
+
firstEntryIndex = j;
|
|
217
|
+
} else if (monomers[j] == rules[i].secondMonomer + add) {
|
|
218
|
+
firstFound = true;
|
|
219
|
+
firstIsFirst = false;
|
|
220
|
+
firstEntryIndex = j;
|
|
221
|
+
} else {
|
|
222
|
+
continue;
|
|
223
|
+
}
|
|
224
|
+
}
|
|
225
|
+
}
|
|
226
|
+
}
|
|
227
|
+
|
|
228
|
+
if (!(firstFound && secondFound))
|
|
229
|
+
result[i] = [-1, -1];
|
|
230
|
+
else if (firstIsFirst)
|
|
231
|
+
result[i] = [firstEntryIndex, secondEntryIndex];
|
|
232
|
+
else
|
|
233
|
+
result[i] = [secondEntryIndex, firstEntryIndex];
|
|
234
|
+
}
|
|
235
|
+
|
|
236
|
+
|
|
237
|
+
return result;
|
|
238
|
+
}
|
|
239
|
+
|
|
240
|
+
protected static getAllCycles(rules: RuleLink[], monomers: string [], positions: [number, number][]) :
|
|
241
|
+
[string [], number [], number [], number [], number []] {
|
|
242
|
+
const allPos1: number [] = [];
|
|
243
|
+
const allPos2: number [] = [];
|
|
244
|
+
const allAttaches1: number [] = [];
|
|
245
|
+
const allAttaches2: number [] = [];
|
|
246
|
+
const ruleCount = rules.length;
|
|
247
|
+
|
|
248
|
+
for (let i = 0; i < ruleCount; i++) {
|
|
249
|
+
if (positions[i][0] == -1)
|
|
250
|
+
continue;
|
|
251
|
+
|
|
252
|
+
const firstMonomer = monomers[positions[i][0]];
|
|
253
|
+
const secondMonomer = monomers[positions[i][1]];
|
|
254
|
+
|
|
255
|
+
monomers[positions[i][0]] = monomers[positions[i][0]].replace(firstMonomer, rules[i].firstSubstitution);
|
|
256
|
+
monomers[positions[i][1]] = monomers[positions[i][1]].replace(secondMonomer, rules[i].secondSubstitution);
|
|
257
|
+
|
|
258
|
+
allPos1.push(positions[i][0] + 1);
|
|
259
|
+
allPos2.push(positions[i][1] + 1);
|
|
260
|
+
allAttaches1.push(rules[i].firstLinkingGroup);
|
|
261
|
+
allAttaches2.push(rules[i].secondLinkingGroup);
|
|
262
|
+
}
|
|
263
|
+
|
|
264
|
+
return [monomers, allPos1, allPos2, allAttaches1, allAttaches2];
|
|
265
|
+
}
|
|
266
|
+
}
|
|
267
|
+
|
|
268
|
+
function getHelms(sequences: string[], rules: Rules): string[] {
|
|
269
|
+
const helms = new Array<string>(sequences.length);
|
|
270
|
+
for (let i = 0; i < sequences.length; i++) {
|
|
271
|
+
const chain = Chain.fromNotation(sequences[i], rules);
|
|
272
|
+
helms[i] = chain.getHelm();
|
|
273
|
+
}
|
|
274
|
+
|
|
275
|
+
return helms;
|
|
276
|
+
}
|
|
277
|
+
|
|
278
|
+
export async function addTransformedColumn(
|
|
279
|
+
sequencesCol: DG.Column<string>, addHelm: boolean, ruleFiles: string[], chiralityEngine?: boolean
|
|
280
|
+
): Promise<void> {
|
|
281
|
+
const df = sequencesCol.dataFrame;
|
|
282
|
+
|
|
283
|
+
const rules = await getRules(ruleFiles);
|
|
284
|
+
const targetList = getHelms(sequencesCol.toList(), rules);
|
|
285
|
+
const helmColName = df.columns.getUnusedName('transformed(' + sequencesCol.name + ')');
|
|
286
|
+
const targetHelmCol = DG.Column.fromList('string', helmColName, targetList);
|
|
287
|
+
|
|
288
|
+
addCommonTags(targetHelmCol);
|
|
289
|
+
targetHelmCol.setTag('units', NOTATION.HELM);
|
|
290
|
+
|
|
291
|
+
const molCol = await grok.functions.call('Bio:getMolFromHelm', {
|
|
292
|
+
'df': df,
|
|
293
|
+
'helmCol': targetHelmCol,
|
|
294
|
+
'chiralityEngine': chiralityEngine
|
|
295
|
+
});
|
|
296
|
+
|
|
297
|
+
|
|
298
|
+
molCol.name = df.columns.getUnusedName('molfile(' + sequencesCol.name + ')');
|
|
299
|
+
molCol.semType = DG.SEMTYPE.MOLECULE;
|
|
300
|
+
|
|
301
|
+
if (addHelm) {
|
|
302
|
+
targetHelmCol.setTag('cell.renderer', 'helm');
|
|
303
|
+
df.columns.add(targetHelmCol);
|
|
304
|
+
}
|
|
305
|
+
df.columns.add(molCol, true);
|
|
306
|
+
await grok.data.detectSemanticTypes(df);
|
|
307
|
+
}
|
|
@@ -0,0 +1,115 @@
|
|
|
1
|
+
/* Do not change these import lines to match external modules in webpack configuration */
|
|
2
|
+
import * as grok from 'datagrok-api/grok';
|
|
3
|
+
import * as ui from 'datagrok-api/ui';
|
|
4
|
+
import * as DG from 'datagrok-api/dg';
|
|
5
|
+
|
|
6
|
+
import {RuleInputs, RULES_PATH, RULES_STORAGE_NAME} from './pt-rules';
|
|
7
|
+
import {addTransformedColumn} from './pt-conversion';
|
|
8
|
+
|
|
9
|
+
import {handleError} from './utils';
|
|
10
|
+
import {getLibrariesList, HelmInput, getEnumeration} from './pt-enumeration';
|
|
11
|
+
|
|
12
|
+
const PT_ERROR_DATAFRAME = 'No dataframe with macromolecule columns open';
|
|
13
|
+
const PT_WARNING_COLUMN = 'No marcomolecule column chosen!';
|
|
14
|
+
|
|
15
|
+
const PT_UI_GET_HELM = 'Get HELM';
|
|
16
|
+
const PT_UI_ADD_HELM = 'Add HELM column';
|
|
17
|
+
const PT_UI_USE_CHIRALITY = 'Chirality engine';
|
|
18
|
+
const PT_UI_DIALOG_CONVERSION = 'Poly Tool Conversion';
|
|
19
|
+
const PT_UI_DIALOG_ENUMERATION = 'Poly Tool Enumeration';
|
|
20
|
+
const PT_UI_RULES_USED = 'Rules used';
|
|
21
|
+
|
|
22
|
+
export async function getPolyToolConversionDialog(): Promise<DG.Dialog> {
|
|
23
|
+
const targetColumns = grok.shell.t.columns.bySemTypeAll(DG.SEMTYPE.MACROMOLECULE);
|
|
24
|
+
if (!targetColumns)
|
|
25
|
+
throw new Error(PT_ERROR_DATAFRAME);
|
|
26
|
+
|
|
27
|
+
const targetColumnInput = ui.columnInput(
|
|
28
|
+
'Column', grok.shell.t, targetColumns[0], null,
|
|
29
|
+
{filter: (col: DG.Column) => col.semType === DG.SEMTYPE.MACROMOLECULE}
|
|
30
|
+
);
|
|
31
|
+
|
|
32
|
+
const generateHelmChoiceInput = ui.boolInput(PT_UI_GET_HELM, true);
|
|
33
|
+
ui.tooltip.bind(generateHelmChoiceInput.root, PT_UI_ADD_HELM);
|
|
34
|
+
|
|
35
|
+
const chiralityEngineInput = ui.boolInput(PT_UI_USE_CHIRALITY, false);
|
|
36
|
+
const ruleInputs = new RuleInputs(RULES_PATH, RULES_STORAGE_NAME, '.json');
|
|
37
|
+
const rulesHeader = ui.inlineText([PT_UI_RULES_USED]);
|
|
38
|
+
ui.tooltip.bind(rulesHeader, 'Add or specify rules to use');
|
|
39
|
+
const rulesForm = await ruleInputs.getForm();
|
|
40
|
+
|
|
41
|
+
const div = ui.div([
|
|
42
|
+
targetColumnInput,
|
|
43
|
+
generateHelmChoiceInput,
|
|
44
|
+
chiralityEngineInput,
|
|
45
|
+
rulesHeader,
|
|
46
|
+
rulesForm
|
|
47
|
+
]);
|
|
48
|
+
|
|
49
|
+
const dialog = ui.dialog(PT_UI_DIALOG_CONVERSION)
|
|
50
|
+
.add(div)
|
|
51
|
+
.onOK(async () => {
|
|
52
|
+
const pi = DG.TaskBarProgressIndicator.create('PolyTool converting');
|
|
53
|
+
try {
|
|
54
|
+
const sequencesCol = targetColumnInput.value;
|
|
55
|
+
if (!sequencesCol) {
|
|
56
|
+
grok.shell.warning(PT_WARNING_COLUMN);
|
|
57
|
+
return;
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
const files = await ruleInputs.getActive();
|
|
61
|
+
|
|
62
|
+
addTransformedColumn(sequencesCol!,
|
|
63
|
+
generateHelmChoiceInput.value!,
|
|
64
|
+
files,
|
|
65
|
+
chiralityEngineInput.value!);
|
|
66
|
+
} catch (err: any) {
|
|
67
|
+
handleError(err);
|
|
68
|
+
} finally {
|
|
69
|
+
pi.close();
|
|
70
|
+
}
|
|
71
|
+
});
|
|
72
|
+
|
|
73
|
+
return dialog;
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
export async function getPolyToolEnumerationDialog(): Promise<DG.Dialog> {
|
|
77
|
+
const helmInput = await HelmInput.init();
|
|
78
|
+
|
|
79
|
+
const libList = await getLibrariesList();
|
|
80
|
+
const screenLibrary = ui.choiceInput('Library to use', null, libList);
|
|
81
|
+
|
|
82
|
+
screenLibrary.input.setAttribute('style', `min-width:250px!important;`);
|
|
83
|
+
|
|
84
|
+
const div = ui.div([
|
|
85
|
+
helmInput.getDiv(),
|
|
86
|
+
screenLibrary.root
|
|
87
|
+
]);
|
|
88
|
+
|
|
89
|
+
const dialog = ui.dialog(PT_UI_DIALOG_ENUMERATION)
|
|
90
|
+
.add(div)
|
|
91
|
+
.onOK(async () => {
|
|
92
|
+
try {
|
|
93
|
+
const helmString = helmInput.getHelmString();
|
|
94
|
+
const helmSelections = helmInput.getHelmSelections();
|
|
95
|
+
if (helmString === undefined || helmString === '') {
|
|
96
|
+
grok.shell.warning('PolyTool: no molecule was provided');
|
|
97
|
+
} else if (helmSelections === undefined || helmSelections.length < 1) {
|
|
98
|
+
grok.shell.warning('PolyTool: no selection was provided');
|
|
99
|
+
} else {
|
|
100
|
+
const molecules = await getEnumeration(helmString, helmSelections, screenLibrary.value!);
|
|
101
|
+
const molCol = DG.Column.fromStrings('Enumerated', molecules);
|
|
102
|
+
const df = DG.DataFrame.fromColumns([molCol]);
|
|
103
|
+
grok.shell.addTableView(df);
|
|
104
|
+
}
|
|
105
|
+
} catch (err: any) {
|
|
106
|
+
|
|
107
|
+
} finally {
|
|
108
|
+
|
|
109
|
+
}
|
|
110
|
+
}).onCancel(() => {
|
|
111
|
+
|
|
112
|
+
});
|
|
113
|
+
|
|
114
|
+
return dialog;
|
|
115
|
+
}
|
|
@@ -0,0 +1,127 @@
|
|
|
1
|
+
import * as ui from 'datagrok-api/ui';
|
|
2
|
+
import * as grok from 'datagrok-api/grok';
|
|
3
|
+
|
|
4
|
+
import {IHelmHelper, getHelmHelper} from '@datagrok-libraries/bio/src/helm/helm-helper';
|
|
5
|
+
import {IHelmWebEditor} from '@datagrok-libraries/bio/src/helm/types';
|
|
6
|
+
import '@datagrok-libraries/bio/src/types/helm';
|
|
7
|
+
import '@datagrok-libraries/bio/src/types/jsdraw2';
|
|
8
|
+
import * as org from 'org';
|
|
9
|
+
import $ from 'cash-dom';
|
|
10
|
+
import {Unsubscribable, fromEvent} from 'rxjs';
|
|
11
|
+
import {IMonomerLibFileManager, IMonomerLibHelper} from '@datagrok-libraries/bio/src/monomer-works/monomer-utils';
|
|
12
|
+
|
|
13
|
+
import {Chain} from './pt-conversion';
|
|
14
|
+
|
|
15
|
+
const LIB_PATH = 'System:AppData/Bio/monomer-libraries/';
|
|
16
|
+
const PT_HELM_EXAMPLE = 'PEPTIDE1{[R].[F].[T].[G].[H].[F].[G].[A].[A].[Y].[P].[E].[NH2]}$$$$';
|
|
17
|
+
|
|
18
|
+
export async function getLibrariesList(): Promise<string[]> {
|
|
19
|
+
const monomerLibHelper: IMonomerLibHelper = await grok.functions.call('Bio:getMonomerLibHelper', {});
|
|
20
|
+
const monomerFileManager: IMonomerLibFileManager = await monomerLibHelper.getFileManager();
|
|
21
|
+
return monomerFileManager.getValidLibraryPaths();
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
export async function getEnumeration(helmString: string, helmSelections: number[], screenLibrary: string):
|
|
25
|
+
Promise<string[]> {
|
|
26
|
+
const variableMonomers = await getAvaialableMonomers(screenLibrary);
|
|
27
|
+
const chain: Chain = Chain.fromHelm(helmString);
|
|
28
|
+
const size = helmSelections.length*variableMonomers.length;
|
|
29
|
+
const enumerations = new Array<string>(size);
|
|
30
|
+
|
|
31
|
+
for (let i = 0; i < helmSelections.length; i++) {
|
|
32
|
+
for (let j = 0; j < variableMonomers.length; j++)
|
|
33
|
+
enumerations[i*variableMonomers.length + j] = chain.getHelmChanged(helmSelections[i], variableMonomers[j]);
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
return enumerations;
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
export class HelmInput {
|
|
40
|
+
webEditorHost: HTMLDivElement | null;
|
|
41
|
+
webEditorApp: org.helm.IWebEditorApp | null;
|
|
42
|
+
helmHelper: IHelmHelper;
|
|
43
|
+
editor: IHelmWebEditor;
|
|
44
|
+
subs: Unsubscribable[];
|
|
45
|
+
|
|
46
|
+
helmString: string = PT_HELM_EXAMPLE;
|
|
47
|
+
helmSelection: number[];
|
|
48
|
+
|
|
49
|
+
constructor(
|
|
50
|
+
helmHelper: IHelmHelper, editor: IHelmWebEditor) {
|
|
51
|
+
this.helmHelper = helmHelper;
|
|
52
|
+
this.editor = editor;
|
|
53
|
+
|
|
54
|
+
this.webEditorHost = null;
|
|
55
|
+
this.webEditorApp = null;
|
|
56
|
+
const subs: Unsubscribable[] = [];
|
|
57
|
+
|
|
58
|
+
subs.push(fromEvent<MouseEvent>(editor.host, 'click').subscribe(() => {
|
|
59
|
+
this.webEditorHost = ui.div();
|
|
60
|
+
//TODO: use not hh, but anything from editor.getHelm(true)
|
|
61
|
+
this.webEditorApp = helmHelper.createWebEditorApp(this.webEditorHost, this.getHelmString());
|
|
62
|
+
const dlg = ui.dialog({showHeader: false, showFooter: true})
|
|
63
|
+
.add(this.webEditorHost!)
|
|
64
|
+
.onOK(() => {
|
|
65
|
+
try {
|
|
66
|
+
const webEditorValue = this.webEditorApp!.canvas.getHelm(true)
|
|
67
|
+
.replace(/<\/span>/g, '').replace(/<span style='background:#bbf;'>/g, '');
|
|
68
|
+
editor.editor.setHelm(webEditorValue);
|
|
69
|
+
this.helmString = webEditorValue;
|
|
70
|
+
this.helmSelection = [];
|
|
71
|
+
|
|
72
|
+
//@ts-ignore
|
|
73
|
+
const selection = this.webEditorApp?.canvas.helm.jsd.m.atoms;
|
|
74
|
+
for (let i = 0; i < selection.length; i++) {
|
|
75
|
+
if (selection[i].selected)
|
|
76
|
+
this.helmSelection.push(i);
|
|
77
|
+
}
|
|
78
|
+
} catch (err: any) {
|
|
79
|
+
//this.logger.error(err);
|
|
80
|
+
} finally {
|
|
81
|
+
$(this.webEditorHost).empty();
|
|
82
|
+
this.webEditorHost = null;
|
|
83
|
+
this.webEditorApp = null;
|
|
84
|
+
}
|
|
85
|
+
})
|
|
86
|
+
.onCancel(() => {
|
|
87
|
+
$(this.webEditorHost).empty();
|
|
88
|
+
this.webEditorHost = null;
|
|
89
|
+
this.webEditorApp = null;
|
|
90
|
+
})
|
|
91
|
+
.show({modal: true, fullScreen: true});
|
|
92
|
+
}));
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
static async init() {
|
|
96
|
+
const helmHelper = await getHelmHelper();
|
|
97
|
+
|
|
98
|
+
const editor = helmHelper.createHelmWebEditor();
|
|
99
|
+
editor.host.style.width = '200px';
|
|
100
|
+
editor.host.style.height = '100px';
|
|
101
|
+
editor.host.style.paddingLeft = '40px';
|
|
102
|
+
editor.editor.setHelm(PT_HELM_EXAMPLE);
|
|
103
|
+
|
|
104
|
+
return new HelmInput(helmHelper, editor);
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
getHelmString(): string {
|
|
108
|
+
return this.helmString;
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
getHelmSelections() {
|
|
112
|
+
return this.helmSelection;
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
getDiv(): HTMLDivElement {
|
|
116
|
+
const title = ui.divText('Macromolecule', {style: {paddingTop: '43px', color: 'var(--grey-4)'}});
|
|
117
|
+
|
|
118
|
+
return ui.divH([title, this.editor.host], {style: {paddingLeft: '48px'}});
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
async function getAvaialableMonomers(screenLibrary: string): Promise<string[]> {
|
|
123
|
+
const monomerLibHelper: IMonomerLibHelper = await grok.functions.call('Bio:getMonomerLibHelper', {});
|
|
124
|
+
const monomerLib = await monomerLibHelper.readLibrary(LIB_PATH, screenLibrary);
|
|
125
|
+
//NOTICE: works with Peptides only
|
|
126
|
+
return monomerLib.getMonomerSymbolsByType('PEPTIDE');
|
|
127
|
+
}
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
import * as DG from 'datagrok-api/dg';
|
|
2
|
+
import * as grok from 'datagrok-api/grok';
|
|
3
|
+
import {ActiveFiles} from '@datagrok-libraries/utils/src/settings/active-files-base';
|
|
4
|
+
|
|
5
|
+
export const RULES_PATH = 'System:AppData/Bio/polytool-rules/';
|
|
6
|
+
export const RULES_STORAGE_NAME = 'Polytool';
|
|
7
|
+
export const RULES_TYPE_LINK = 'link';
|
|
8
|
+
export const RULES_TYPE_HOMODIMER = 'fragmentDuplication';
|
|
9
|
+
export const RULES_TYPE_HETERODIMER = 'differentFragments';
|
|
10
|
+
|
|
11
|
+
export class RuleInputs extends ActiveFiles {
|
|
12
|
+
constructor(path: string, userStorageName: string, ext: string ) {
|
|
13
|
+
super(path, userStorageName, ext);
|
|
14
|
+
}
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
export type Rules = {
|
|
18
|
+
homodimerCode: string | null,
|
|
19
|
+
heterodimerCode: string | null,
|
|
20
|
+
linkRules: RuleLink[]
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
export type RuleLink = {
|
|
24
|
+
code: number,
|
|
25
|
+
firstMonomer: string,
|
|
26
|
+
secondMonomer: string,
|
|
27
|
+
firstSubstitution: string,
|
|
28
|
+
secondSubstitution: string,
|
|
29
|
+
firstLinkingGroup: number,
|
|
30
|
+
secondLinkingGroup: number
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
export async function getRules(ruleFiles: string[]): Promise<Rules> {
|
|
34
|
+
const fileSource = new DG.FileSource(RULES_PATH);
|
|
35
|
+
const linkRules: RuleLink[] = [];
|
|
36
|
+
const rules: Rules = {homodimerCode: null, heterodimerCode: null, linkRules: linkRules};
|
|
37
|
+
|
|
38
|
+
for (let i = 0; i < ruleFiles.length; i++) {
|
|
39
|
+
const rulesRaw = await fileSource.readAsText(ruleFiles[i].replace(RULES_PATH, ''));
|
|
40
|
+
const ruleSingle = JSON.parse(rulesRaw);
|
|
41
|
+
for (let j = 0; j < ruleSingle.length; j++) {
|
|
42
|
+
if (ruleSingle[j].type !== undefined && ruleSingle[j].code !== undefined) {
|
|
43
|
+
switch (ruleSingle[j].type) {
|
|
44
|
+
case RULES_TYPE_LINK: {
|
|
45
|
+
const rule = ruleSingle[j].monomericSubstitution;
|
|
46
|
+
rule['code'] = ruleSingle[j].code;
|
|
47
|
+
linkRules.push(rule);
|
|
48
|
+
break;
|
|
49
|
+
}
|
|
50
|
+
case RULES_TYPE_HOMODIMER: {
|
|
51
|
+
if (rules.homodimerCode)
|
|
52
|
+
grok.shell.warning(`PolyTool: homodimer code is duplicated in rules.`);
|
|
53
|
+
rules.homodimerCode = ruleSingle[j].code;
|
|
54
|
+
break;
|
|
55
|
+
}
|
|
56
|
+
case RULES_TYPE_HETERODIMER: {
|
|
57
|
+
if (rules.heterodimerCode)
|
|
58
|
+
grok.shell.warning(`PolyTool: heterodimer code is duplicated in rules.`);
|
|
59
|
+
rules.heterodimerCode = ruleSingle[j].code;
|
|
60
|
+
break;
|
|
61
|
+
}
|
|
62
|
+
default:
|
|
63
|
+
grok.shell.warning(`PolyTool: Unexpected type - '${ruleSingle[j]}'.`);
|
|
64
|
+
break;
|
|
65
|
+
}
|
|
66
|
+
} else {
|
|
67
|
+
grok.shell.warning('Polytool: rules contain invalid rule');
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
return rules;
|
|
73
|
+
}
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
/* Do not change these import lines to match external modules in webpack configuration */
|
|
2
|
+
import * as grok from 'datagrok-api/grok';
|
|
3
|
+
import * as ui from 'datagrok-api/ui';
|
|
4
|
+
import * as DG from 'datagrok-api/dg';
|
|
5
|
+
import {_package} from '../package';
|
|
6
|
+
|
|
7
|
+
import {ALPHABET, ALIGNMENT, NOTATION} from '@datagrok-libraries/bio/src/utils/macromolecule';
|
|
8
|
+
|
|
9
|
+
export function _setPeptideColumn(col: DG.Column): void {
|
|
10
|
+
addCommonTags(col);
|
|
11
|
+
col.setTag('units', NOTATION.SEPARATOR);
|
|
12
|
+
col.setTag('separator', '-');
|
|
13
|
+
// col.setTag('cell.renderer', 'sequence');
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
function addCommonTags(col: DG.Column<any>) {
|
|
17
|
+
col.setTag('quality', DG.SEMTYPE.MACROMOLECULE);
|
|
18
|
+
col.setTag('aligned', ALIGNMENT.SEQ);
|
|
19
|
+
col.setTag('alphabet', ALPHABET.PT);
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
export function handleError(err: any): void {
|
|
23
|
+
const errMsg: string = err instanceof Error ? err.message : err.toString();
|
|
24
|
+
const stack: string | undefined = err instanceof Error ? err.stack : undefined;
|
|
25
|
+
grok.shell.error(errMsg);
|
|
26
|
+
_package.logger.error(err.message, undefined, stack);
|
|
27
|
+
}
|
package/src/tests/const.ts
CHANGED
|
@@ -5,20 +5,20 @@ export const formatsToHelm: {[key: string]: Dict} = {
|
|
|
5
5
|
'UfAfsCfsGfuacg': 'RNA1{[fR](U)p.[fR](A)[sp].[fR](C)[sp].[fR](G)p.[25r](U)p.[25r](A)p.[25r](C)p.[25r](G)}$$$$'
|
|
6
6
|
},
|
|
7
7
|
'BioSpring': {
|
|
8
|
-
'
|
|
8
|
+
'A*GC*123456789': 'RNA1{r(A)[sp].r(G)p.r(C)[sp].[fR](U)p.[fR](A)p.[fR](C)p.[fR](G)p.[25r](U)p.[25r](A)p.[25r](C)p.[25r](G)p.d([m5C])}$$$$'
|
|
9
9
|
},
|
|
10
10
|
'Mermade12': {
|
|
11
11
|
'hefglijkLIJKHEFG': 'RNA1{[25r](U)[sp].[25r](A)[sp].[25r](C)[sp].[25r](G)[sp].[fR](U)[sp].[fR](A)[sp].[fR](C)[sp].[fR](G)[sp].[fR](U)p.[fR](A)p.[fR](C)p.[fR](G)p.[25r](U)p.[25r](A)p.[25r](C)p.[25r](G)}$$$$'
|
|
12
12
|
}
|
|
13
|
-
}
|
|
13
|
+
};
|
|
14
14
|
|
|
15
15
|
export const helmToNucleotides: Dict = {
|
|
16
16
|
'RNA1{[fR](U)p.[fR](A)[sp].[fR](C)[sp].[fR](G)p.[25r](U)p.[25r](A)p.[25r](C)p.[25r](G)}$$$$': 'UACGUACG',
|
|
17
17
|
|
|
18
|
-
'RNA1{r(A)
|
|
18
|
+
'RNA1{r(A)[sp].r(G)p.r(C)[sp].[fR](U)p.[fR](A)p.[fR](C)p.[fR](G)p.[25r](U)p.[25r](A)p.[25r](C)p.[25r](G)p.d([m5C])}$$$$': 'AGCUACGUACGC',
|
|
19
19
|
|
|
20
20
|
'RNA1{[25r](U)[sp].[25r](A)[sp].[25r](C)[sp].[25r](G)[sp].[fR](U)[sp].[fR](A)[sp].[fR](C)[sp].[fR](G)[sp].[fR](U)p.[fR](A)p.[fR](C)p.[fR](G)p.[25r](U)p.[25r](A)p.[25r](C)p.[25r](G)}$$$$': 'UACGUACGUACGUACG'
|
|
21
|
-
}
|
|
21
|
+
};
|
|
22
22
|
|
|
23
23
|
export const helmToMolfile: Dict = {
|
|
24
|
-
}
|
|
24
|
+
};
|