@datagrok/sequence-translator 1.0.17 → 1.1.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.eslintrc.json +4 -3
- package/CHANGELOG.md +36 -0
- package/detectors.js +8 -0
- package/dist/package-test.js +2 -73079
- package/dist/package-test.js.map +1 -0
- package/dist/package.js +2 -72284
- package/dist/package.js.map +1 -0
- package/files/axolabs-style.json +97 -0
- package/files/codes-to-symbols.json +67 -0
- package/files/formats-to-helm.json +63 -0
- package/files/linkers.json +22 -0
- package/files/monomer-lib.json +1142 -0
- package/link-bio +7 -0
- package/package.json +30 -31
- package/scripts/build-monomer-lib.py +391 -122
- package/src/demo/demo-st-ui.ts +71 -0
- package/src/demo/handle-error.ts +12 -0
- package/src/model/axolabs/axolabs-tab.ts +111 -0
- package/src/model/axolabs/const.ts +33 -0
- package/src/{axolabs-tab → model/axolabs}/draw-svg.ts +1 -1
- package/src/{axolabs-tab → model/axolabs}/helpers.ts +7 -5
- package/src/model/const.ts +18 -0
- package/src/model/data-loading-utils/const.ts +8 -0
- package/src/model/data-loading-utils/json-loader.ts +38 -0
- package/src/model/data-loading-utils/types.ts +30 -0
- package/src/model/format-translation/const.ts +8 -0
- package/src/model/format-translation/conversion-utils.ts +49 -0
- package/src/model/format-translation/format-converter.ts +109 -0
- package/src/model/helpers.ts +12 -0
- package/src/model/monomer-lib/const.ts +3 -0
- package/src/model/monomer-lib/lib-wrapper.ts +119 -0
- package/src/model/parsing-validation/format-detector.ts +57 -0
- package/src/model/parsing-validation/sequence-validator.ts +52 -0
- package/src/model/sequence-to-structure-utils/const.ts +1 -0
- package/src/{utils/structures-works → model/sequence-to-structure-utils}/mol-transformations.ts +33 -41
- package/src/model/sequence-to-structure-utils/monomer-code-parser.ts +92 -0
- package/src/model/sequence-to-structure-utils/sdf-tab.ts +97 -0
- package/src/model/sequence-to-structure-utils/sequence-to-molfile.ts +409 -0
- package/src/package-test.ts +3 -1
- package/src/package.ts +113 -91
- package/src/tests/const.ts +24 -0
- package/src/tests/formats-support.ts +40 -0
- package/src/tests/formats-to-helm.ts +53 -0
- package/src/tests/helm-to-nucleotides.ts +28 -0
- package/src/view/const/main-tab.ts +3 -0
- package/src/view/const/view.ts +10 -0
- package/src/view/css/axolabs-tab.css +1 -0
- package/src/view/css/colored-text-input.css +27 -0
- package/src/view/css/main-tab.css +46 -0
- package/src/view/css/sdf-tab.css +39 -0
- package/src/view/monomer-lib-viewer/viewer.ts +22 -0
- package/src/view/tabs/axolabs.ts +719 -0
- package/src/view/tabs/main.ts +174 -0
- package/src/view/tabs/sdf.ts +193 -0
- package/src/view/utils/app-info-dialog.ts +18 -0
- package/src/view/utils/colored-input/colored-text-input.ts +56 -0
- package/src/view/utils/colored-input/input-painters.ts +44 -0
- package/src/view/utils/draw-molecule.ts +86 -0
- package/src/view/utils/molecule-img.ts +106 -0
- package/src/view/view.ts +127 -0
- package/tsconfig.json +12 -18
- package/webpack.config.js +17 -4
- package/README.md +0 -84
- package/css/style.css +0 -18
- package/img/Sequence Translator Axolabs.png +0 -0
- package/jest.config.js +0 -33
- package/setup-unlink-clean.cmd +0 -14
- package/setup-unlink-clean.sh +0 -21
- package/setup.cmd +0 -14
- package/setup.sh +0 -37
- package/src/__jest__/remote.test.ts +0 -77
- package/src/__jest__/test-node.ts +0 -97
- package/src/apps/oligo-sd-file-app.ts +0 -58
- package/src/autostart/calculations.ts +0 -40
- package/src/autostart/constants.ts +0 -37
- package/src/autostart/registration.ts +0 -306
- package/src/axolabs-tab/axolabs-tab.ts +0 -873
- package/src/axolabs-tab/define-pattern.ts +0 -874
- package/src/hardcode-to-be-eliminated/ICDs.ts +0 -3
- package/src/hardcode-to-be-eliminated/IDPs.ts +0 -3
- package/src/hardcode-to-be-eliminated/const.ts +0 -5
- package/src/hardcode-to-be-eliminated/constants.ts +0 -101
- package/src/hardcode-to-be-eliminated/converters.ts +0 -323
- package/src/hardcode-to-be-eliminated/map.ts +0 -720
- package/src/hardcode-to-be-eliminated/salts.ts +0 -2
- package/src/hardcode-to-be-eliminated/sources.ts +0 -3
- package/src/hardcode-to-be-eliminated/users.ts +0 -3
- package/src/main-tab/main-tab.ts +0 -210
- package/src/sdf-tab/sdf-tab.ts +0 -163
- package/src/sdf-tab/sequence-codes-tools.ts +0 -347
- package/src/tests/smiles-tests.ts +0 -458
- package/src/utils/const.ts +0 -0
- package/src/utils/helpers.ts +0 -28
- package/src/utils/parse.ts +0 -27
- package/src/utils/sdf-add-columns.ts +0 -118
- package/src/utils/sdf-save-table.ts +0 -56
- package/src/utils/structures-works/draw-molecule.ts +0 -84
- package/src/utils/structures-works/from-monomers.ts +0 -266
- package/test-SequenceTranslator-6288c2fbe346-695b7b55.html +0 -259
- package/vendors/openchemlib-full.js +0 -293
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
import {NUCLEOTIDES} from '../const';
|
|
2
|
+
import {MonomerLibWrapper} from '../monomer-lib/lib-wrapper';
|
|
3
|
+
import {sortByReverseLength} from '../helpers';
|
|
4
|
+
import {DEFAULT_FORMATS} from '../const';
|
|
5
|
+
|
|
6
|
+
export class SequenceValidator {
|
|
7
|
+
constructor(private sequence: string) {
|
|
8
|
+
this.libWrapper = MonomerLibWrapper.getInstance();
|
|
9
|
+
};
|
|
10
|
+
private libWrapper: MonomerLibWrapper;
|
|
11
|
+
|
|
12
|
+
getInvalidCodeIndex(format: string): number {
|
|
13
|
+
if (format === DEFAULT_FORMATS.HELM)
|
|
14
|
+
return this.sequence.length;
|
|
15
|
+
const firstUniqueCharacters = ['r', 'd']; // what for?
|
|
16
|
+
const codes = sortByReverseLength(
|
|
17
|
+
this.libWrapper.getCodesByFormat(format)
|
|
18
|
+
);
|
|
19
|
+
let indexOfFirstInvalidChar = 0;
|
|
20
|
+
while (indexOfFirstInvalidChar < this.sequence.length) {
|
|
21
|
+
const matchedCode = codes.find((code) => {
|
|
22
|
+
const subSequence = this.sequence.substring(indexOfFirstInvalidChar, indexOfFirstInvalidChar + code.length);
|
|
23
|
+
return code === subSequence;
|
|
24
|
+
});
|
|
25
|
+
|
|
26
|
+
if (!matchedCode) break;
|
|
27
|
+
|
|
28
|
+
// todo: refactor the vague condition
|
|
29
|
+
if ( // for mistake pattern 'rAA'
|
|
30
|
+
indexOfFirstInvalidChar > 1 &&
|
|
31
|
+
NUCLEOTIDES.includes(this.sequence[indexOfFirstInvalidChar]) &&
|
|
32
|
+
firstUniqueCharacters.includes(this.sequence[indexOfFirstInvalidChar - 2])
|
|
33
|
+
) break;
|
|
34
|
+
|
|
35
|
+
if ( // for mistake pattern 'ArA'
|
|
36
|
+
firstUniqueCharacters.includes(this.sequence[indexOfFirstInvalidChar + 1]) &&
|
|
37
|
+
NUCLEOTIDES.includes(this.sequence[indexOfFirstInvalidChar])
|
|
38
|
+
) {
|
|
39
|
+
indexOfFirstInvalidChar++;
|
|
40
|
+
break;
|
|
41
|
+
}
|
|
42
|
+
indexOfFirstInvalidChar += matchedCode.length;
|
|
43
|
+
}
|
|
44
|
+
if (indexOfFirstInvalidChar === this.sequence.length)
|
|
45
|
+
indexOfFirstInvalidChar = -1
|
|
46
|
+
return indexOfFirstInvalidChar;
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
isValidSequence(format: string): boolean {
|
|
50
|
+
return this.getInvalidCodeIndex(format) === -1;
|
|
51
|
+
}
|
|
52
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export const PHOSPHATE_SYMBOL = 'p';
|
package/src/{utils/structures-works → model/sequence-to-structure-utils}/mol-transformations.ts
RENAMED
|
@@ -29,9 +29,6 @@ export function linkStrandsV3000(
|
|
|
29
29
|
let nbond = 0;
|
|
30
30
|
let xShift = 0;
|
|
31
31
|
|
|
32
|
-
// if (twoChains && molBlocks.length > 1)
|
|
33
|
-
// molBlocks[1] = invertNucleotidesV3000(molBlocks[1]);
|
|
34
|
-
|
|
35
32
|
if (strands.antiStrands.length > 0) {
|
|
36
33
|
for (let i = 0; i < strands.antiStrands.length; i++)
|
|
37
34
|
strands.antiStrands[i] = invertNucleotidesV3000(strands.antiStrands[i]);
|
|
@@ -39,25 +36,35 @@ export function linkStrandsV3000(
|
|
|
39
36
|
|
|
40
37
|
let inverted = false;
|
|
41
38
|
const molBlocks = strands.senseStrands.concat(strands.antiStrands);
|
|
39
|
+
/** Minimal value of AS and AS2 shift */
|
|
40
|
+
let ssYShift = 0;
|
|
42
41
|
|
|
43
42
|
for (let i = 0; i < molBlocks.length; i++) {
|
|
44
|
-
if (i >= strands.senseStrands.length && inverted === false) {
|
|
45
|
-
inverted = true;
|
|
46
|
-
xShift = 0;
|
|
47
|
-
}
|
|
48
|
-
|
|
49
43
|
molBlocks[i] = molBlocks[i].replaceAll('(-\nM V30 ', '(')
|
|
50
44
|
.replaceAll('-\nM V30 ', '').replaceAll(' )', ')');
|
|
51
45
|
const numbers = extractAtomsBondsNumbersV3000(molBlocks[i]);
|
|
52
46
|
const coordinates = extractAtomDataV3000(molBlocks[i]);
|
|
53
47
|
|
|
48
|
+
if (i >= strands.senseStrands.length) {
|
|
49
|
+
if (inverted === false) {
|
|
50
|
+
// AS strand
|
|
51
|
+
inverted = true;
|
|
52
|
+
xShift = 0;
|
|
53
|
+
}
|
|
54
|
+
} else {
|
|
55
|
+
// SS strands
|
|
56
|
+
ssYShift = Math.min(ssYShift, Math.min(
|
|
57
|
+
...coordinates.y.filter((item) => item < 0)
|
|
58
|
+
));
|
|
59
|
+
}
|
|
60
|
+
|
|
54
61
|
if (inverted) {
|
|
55
62
|
const xShiftRight = Math.min(...coordinates.x) - xShift;
|
|
56
|
-
const yShift =
|
|
63
|
+
const yShift = Math.max(...coordinates.y) + 5;
|
|
57
64
|
for (let j = 0; j < coordinates.x.length; j++)
|
|
58
65
|
coordinates.x[j] -= xShiftRight;
|
|
59
66
|
for (let j = 0; j < coordinates.y.length; j++)
|
|
60
|
-
coordinates.y[j] -= yShift;
|
|
67
|
+
coordinates.y[j] -= yShift - ssYShift;
|
|
61
68
|
}
|
|
62
69
|
|
|
63
70
|
let indexAtoms = molBlocks[i].indexOf('M V30 BEGIN ATOM'); // V3000 index for atoms coordinates
|
|
@@ -141,7 +148,7 @@ export function linkStrandsV3000(
|
|
|
141
148
|
|
|
142
149
|
natom += true ? numbers.natom : numbers.natom - 1;
|
|
143
150
|
nbond += numbers.nbond;
|
|
144
|
-
xShift += Math.max(...coordinates.x) +
|
|
151
|
+
xShift += Math.max(...coordinates.x) + 5;//twoChains ? 0 : coordinates.x[numbers.natom - 1] - coordinates.x[0];
|
|
145
152
|
}
|
|
146
153
|
|
|
147
154
|
const entries = 4;
|
|
@@ -167,7 +174,7 @@ export function linkStrandsV3000(
|
|
|
167
174
|
macroMolBlock += 'M V30 BEGIN BOND\n';
|
|
168
175
|
macroMolBlock += bondBlock;
|
|
169
176
|
macroMolBlock += 'M V30 END BOND\n';
|
|
170
|
-
if (useChirality) {
|
|
177
|
+
if (useChirality && collection.length > 0) {
|
|
171
178
|
macroMolBlock += 'M V30 BEGIN COLLECTION\n';
|
|
172
179
|
macroMolBlock += collectionBlock;
|
|
173
180
|
macroMolBlock += 'M V30 END COLLECTION\n';
|
|
@@ -219,7 +226,7 @@ export function linkV3000(molBlocks: string[], useChirality: boolean = true): st
|
|
|
219
226
|
indexEnd = molBlocks[i].indexOf(' ', index);
|
|
220
227
|
let atomNumber = 0;
|
|
221
228
|
if (isBoundary) {
|
|
222
|
-
atomNumber = parseInt(molBlocks[i].substring(index, indexEnd))
|
|
229
|
+
atomNumber = parseInt(molBlocks[i].substring(index, indexEnd));
|
|
223
230
|
if (atomNumber === 1)
|
|
224
231
|
atomNumber = specLength;
|
|
225
232
|
else if (atomNumber === specLength)
|
|
@@ -275,7 +282,7 @@ export function linkV3000(molBlocks: string[], useChirality: boolean = true): st
|
|
|
275
282
|
indexEnd = molBlocks[i].indexOf(' ', index);
|
|
276
283
|
let atomNumber = 0;
|
|
277
284
|
if (isBoundary) {
|
|
278
|
-
atomNumber = parseInt(molBlocks[i].substring(index, indexEnd))
|
|
285
|
+
atomNumber = parseInt(molBlocks[i].substring(index, indexEnd));
|
|
279
286
|
if (atomNumber === 1)
|
|
280
287
|
atomNumber = specLength;
|
|
281
288
|
else if (atomNumber === specLength)
|
|
@@ -290,7 +297,7 @@ export function linkV3000(molBlocks: string[], useChirality: boolean = true): st
|
|
|
290
297
|
indexEnd = Math.min(molBlocks[i].indexOf('\n', index), molBlocks[i].indexOf(' ', index));
|
|
291
298
|
atomNumber = 0;
|
|
292
299
|
if (isBoundary) {
|
|
293
|
-
atomNumber = parseInt(molBlocks[i].substring(index, indexEnd))
|
|
300
|
+
atomNumber = parseInt(molBlocks[i].substring(index, indexEnd));
|
|
294
301
|
if (atomNumber === 1)
|
|
295
302
|
atomNumber = specLength;
|
|
296
303
|
else if (atomNumber === specLength)
|
|
@@ -331,14 +338,6 @@ export function linkV3000(molBlocks: string[], useChirality: boolean = true): st
|
|
|
331
338
|
const entries = 4;
|
|
332
339
|
const collNumber = Math.ceil(collection.length / entries);
|
|
333
340
|
|
|
334
|
-
//if (oclRender) {
|
|
335
|
-
// collectionBlock += 'M V30 MDLV30/STEABS ATOMS=(' + collection.length;
|
|
336
|
-
|
|
337
|
-
// for (let j = 0; j < collection.length; j++)
|
|
338
|
-
// collectionBlock += ' ' + collection[j];
|
|
339
|
-
|
|
340
|
-
// collectionBlock += ')\n';
|
|
341
|
-
//} else {
|
|
342
341
|
collectionBlock += 'M V30 MDLV30/STEABS ATOMS=(' + collection.length + ' -\n';
|
|
343
342
|
for (let i = 0; i < collNumber; i++) {
|
|
344
343
|
collectionBlock += 'M V30 ';
|
|
@@ -360,12 +359,11 @@ export function linkV3000(molBlocks: string[], useChirality: boolean = true): st
|
|
|
360
359
|
macroMolBlock += 'M V30 BEGIN BOND\n';
|
|
361
360
|
macroMolBlock += bondBlock;
|
|
362
361
|
macroMolBlock += 'M V30 END BOND\n';
|
|
363
|
-
if (useChirality) {
|
|
362
|
+
if (useChirality && collection.length > 0) {
|
|
364
363
|
macroMolBlock += 'M V30 BEGIN COLLECTION\n';
|
|
365
364
|
macroMolBlock += collectionBlock;
|
|
366
365
|
macroMolBlock += 'M V30 END COLLECTION\n';
|
|
367
|
-
} else
|
|
368
|
-
macroMolBlock = macroMolBlock.replace(/ CFG=\d/g, ' ');
|
|
366
|
+
} else { macroMolBlock = macroMolBlock.replace(/ CFG=\d/g, ' '); }
|
|
369
367
|
|
|
370
368
|
macroMolBlock += 'M V30 END CTAB\n';
|
|
371
369
|
macroMolBlock += 'M END';
|
|
@@ -373,9 +371,7 @@ export function linkV3000(molBlocks: string[], useChirality: boolean = true): st
|
|
|
373
371
|
return macroMolBlock;
|
|
374
372
|
}
|
|
375
373
|
|
|
376
|
-
function rotateNucleotidesV3000(
|
|
377
|
-
// @ts-ignore
|
|
378
|
-
let molBlock = molecule.includes('M END') ? molecule : OCL.Molecule.fromSmiles(molecule).toMolfileV3();
|
|
374
|
+
function rotateNucleotidesV3000(molBlock: string): string {
|
|
379
375
|
const coordinates = extractAtomDataV3000(molBlock);
|
|
380
376
|
const natom = coordinates.atomIndex.length;
|
|
381
377
|
|
|
@@ -396,15 +392,15 @@ function rotateNucleotidesV3000(molecule: string): string {
|
|
|
396
392
|
}
|
|
397
393
|
|
|
398
394
|
let angle = 0;
|
|
399
|
-
if (coordinates.x[indexFivePrime] === 0)
|
|
395
|
+
if (coordinates.x[indexFivePrime] === 0) {
|
|
400
396
|
angle = coordinates.y[indexFivePrime] > coordinates.y[indexThreePrime] ? Math.PI / 2 : 3 * Math.PI / 2;
|
|
401
|
-
else if (coordinates.y[indexFivePrime] === 0)
|
|
397
|
+
} else if (coordinates.y[indexFivePrime] === 0) {
|
|
402
398
|
angle = coordinates.x[indexFivePrime] > coordinates.x[indexThreePrime] ? Math.PI : 0;
|
|
403
|
-
else {
|
|
399
|
+
} else {
|
|
404
400
|
const derivative = coordinates.y[indexFivePrime] / coordinates.x[indexFivePrime];
|
|
405
|
-
angle = derivative > 0
|
|
406
|
-
|
|
407
|
-
|
|
401
|
+
angle = derivative > 0 ?
|
|
402
|
+
(coordinates.x[indexFivePrime] > 0 ? Math.PI - Math.atan(derivative) : Math.PI * 2 - Math.atan(derivative)) :
|
|
403
|
+
(coordinates.x[indexFivePrime] > 0 ? -Math.PI - Math.atan(derivative) : Math.atan(derivative));
|
|
408
404
|
}
|
|
409
405
|
|
|
410
406
|
const cos = Math.cos(angle);
|
|
@@ -442,9 +438,7 @@ function rotateNucleotidesV3000(molecule: string): string {
|
|
|
442
438
|
return molBlock;
|
|
443
439
|
}
|
|
444
440
|
|
|
445
|
-
function reflect(
|
|
446
|
-
// @ts-ignore
|
|
447
|
-
let molBlock = molecule.includes('M END') ? molecule : OCL.Molecule.fromSmiles(molecule).toMolfileV3();
|
|
441
|
+
function reflect(molBlock: string): string {
|
|
448
442
|
const coordinates = extractAtomDataV3000(molBlock);
|
|
449
443
|
const natom = coordinates.atomIndex.length;
|
|
450
444
|
|
|
@@ -491,9 +485,7 @@ function reflect(molecule: string): string {
|
|
|
491
485
|
}
|
|
492
486
|
|
|
493
487
|
|
|
494
|
-
function invertNucleotidesV3000(
|
|
495
|
-
// @ts-ignore
|
|
496
|
-
let molBlock = molecule.includes('M END') ? molecule : OCL.Molecule.fromSmiles(molecule).toMolfileV3();
|
|
488
|
+
function invertNucleotidesV3000(molBlock: string) {
|
|
497
489
|
const coordinates = extractAtomDataV3000(molBlock);
|
|
498
490
|
const natom = coordinates.atomIndex.length;
|
|
499
491
|
|
|
@@ -0,0 +1,92 @@
|
|
|
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 {PHOSPHATE_SYMBOL} from './const';
|
|
7
|
+
import {sortByReverseLength} from '../helpers';
|
|
8
|
+
import {MonomerLibWrapper} from '../monomer-lib/lib-wrapper';
|
|
9
|
+
import {monomersWithPhosphateLinkers} from '../data-loading-utils/json-loader';
|
|
10
|
+
|
|
11
|
+
/** Wrapper for parsing a strand and getting a sequence of monomer IDs (with
|
|
12
|
+
* omitted linkers, if needed) */
|
|
13
|
+
export class MonomerSequenceParser {
|
|
14
|
+
constructor(
|
|
15
|
+
private sequence: string, private invert: boolean = false,
|
|
16
|
+
// todo: remove from the list of parameters
|
|
17
|
+
private codeMap: Map<string, string>
|
|
18
|
+
) {
|
|
19
|
+
this.lib = MonomerLibWrapper.getInstance();
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
private lib: MonomerLibWrapper;
|
|
23
|
+
|
|
24
|
+
/** Get sequence of parsed monomer symbols, which are unique short names for
|
|
25
|
+
* the monomers within the Monomer Library */
|
|
26
|
+
parseSequence(): string[] {
|
|
27
|
+
const parsedRawCodes = this.parseRawSequence();
|
|
28
|
+
return this.addLinkers(parsedRawCodes);
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
private addLinkers(parsedRawCodes: string[]) {
|
|
32
|
+
const monomerSymbolSequence: string[] = [];
|
|
33
|
+
parsedRawCodes.forEach((code, i) => {
|
|
34
|
+
const monomerSymbol = this.getSymbolForCode(code);
|
|
35
|
+
if (i > 0 && monomerHasLeftPhosphateLinker(monomerSymbol))
|
|
36
|
+
monomerSymbolSequence.pop();
|
|
37
|
+
|
|
38
|
+
monomerSymbolSequence.push(monomerSymbol);
|
|
39
|
+
|
|
40
|
+
const isPhosphate = monomerIsPhosphateLinker(monomerSymbol);
|
|
41
|
+
const lastMonomer = i === parsedRawCodes.length - 1;
|
|
42
|
+
const nextMonomerIsPhosphate = (i + 1 < parsedRawCodes.length && monomerIsPhosphateLinker(this.getSymbolForCode(parsedRawCodes[i + 1])));
|
|
43
|
+
|
|
44
|
+
// todo: refactor as molfile-specific
|
|
45
|
+
if (!isPhosphate && !monomerHasRightPhosphateLinker(monomerSymbol) && !nextMonomerIsPhosphate && !lastMonomer) {
|
|
46
|
+
monomerSymbolSequence.push(PHOSPHATE_SYMBOL);
|
|
47
|
+
}
|
|
48
|
+
});
|
|
49
|
+
return monomerSymbolSequence;
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
private getSymbolForCode(code: string): string {
|
|
53
|
+
let monomerSymbol = this.codeMap.get(code);
|
|
54
|
+
// todo: remove as a legacy workaround, codeMap must contain all the
|
|
55
|
+
// symbols, and symbols are not codes
|
|
56
|
+
monomerSymbol ??= code;
|
|
57
|
+
return monomerSymbol;
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
private parseRawSequence(): string[] {
|
|
61
|
+
const allCodesOfFormat = this.getAllCodesOfFormat();
|
|
62
|
+
const parsedCodes = [];
|
|
63
|
+
let i = 0;
|
|
64
|
+
while (i < this.sequence.length) {
|
|
65
|
+
const code = allCodesOfFormat.find(
|
|
66
|
+
(s: string) => s === this.sequence.substring(i, i + s.length)
|
|
67
|
+
)!;
|
|
68
|
+
this.invert ? parsedCodes.unshift(code) : parsedCodes.push(code);
|
|
69
|
+
i += code.length;
|
|
70
|
+
}
|
|
71
|
+
return parsedCodes;
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
// todo: port to monomer handler
|
|
75
|
+
private getAllCodesOfFormat(): string[] {
|
|
76
|
+
let allCodesInTheFormat = Array.from(this.codeMap.keys());
|
|
77
|
+
return sortByReverseLength(allCodesInTheFormat);
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
// todo: to be eliminated after full helm support
|
|
82
|
+
function monomerHasLeftPhosphateLinker(monomerSymbol: string): boolean {
|
|
83
|
+
return monomersWithPhosphateLinkers['left'].includes(monomerSymbol);
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
function monomerHasRightPhosphateLinker(monomerSymbol: string): boolean {
|
|
87
|
+
return monomersWithPhosphateLinkers['right'].includes(monomerSymbol);
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
function monomerIsPhosphateLinker(monomerSymbol: string): boolean {
|
|
91
|
+
return monomersWithPhosphateLinkers['phosphate'].includes(monomerSymbol);
|
|
92
|
+
}
|
|
@@ -0,0 +1,97 @@
|
|
|
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
|
+
import {errorToConsole} from '@datagrok-libraries/utils/src/to-console';
|
|
6
|
+
|
|
7
|
+
import {download} from '../helpers';
|
|
8
|
+
import {SequenceToMolfileConverter} from './sequence-to-molfile';
|
|
9
|
+
import {linkStrandsV3000} from './mol-transformations';
|
|
10
|
+
import {DEFAULT_FORMATS} from '../const';
|
|
11
|
+
import {FormatDetector} from '../parsing-validation/format-detector';
|
|
12
|
+
|
|
13
|
+
export type StrandData = {
|
|
14
|
+
strand: string,
|
|
15
|
+
invert: boolean
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
/** Get a molfile for a single strand */
|
|
19
|
+
export function getMolfileForStrand(strand: string, invert: boolean): string {
|
|
20
|
+
if (strand === '')
|
|
21
|
+
return '';
|
|
22
|
+
const format = (new FormatDetector(strand)).getFormat();
|
|
23
|
+
if (!format)
|
|
24
|
+
return '';
|
|
25
|
+
let molfile = '';
|
|
26
|
+
try {
|
|
27
|
+
molfile = (new SequenceToMolfileConverter(strand, invert, format)).convert();
|
|
28
|
+
} catch (err) {
|
|
29
|
+
const errStr = errorToConsole(err);
|
|
30
|
+
console.error(errStr);
|
|
31
|
+
}
|
|
32
|
+
return molfile;
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
/** Get molfile for single strand or linked strands */
|
|
36
|
+
export function getLinkedMolfile(
|
|
37
|
+
ss: StrandData, as: StrandData, as2: StrandData, useChiral: boolean
|
|
38
|
+
): string {
|
|
39
|
+
const nonEmptyStrands = [ss, as, as2].filter((item) => item.strand !== '');
|
|
40
|
+
if (nonEmptyStrands.length === 1) {
|
|
41
|
+
return getMolfileForStrand(nonEmptyStrands[0].strand, nonEmptyStrands[0].invert);
|
|
42
|
+
} else {
|
|
43
|
+
const ssMol = getMolfileForStrand(ss.strand, ss.invert);
|
|
44
|
+
const asMol = getMolfileForStrand(as.strand, as.invert);
|
|
45
|
+
const as2Mol = getMolfileForStrand(as2.strand, as2.invert);
|
|
46
|
+
|
|
47
|
+
// select only the non-empty anti-strands
|
|
48
|
+
const antiStrands = [asMol, as2Mol].filter((item) => item !== '');
|
|
49
|
+
const resultingMolfile = linkStrandsV3000({senseStrands: [ssMol], antiStrands: antiStrands}, useChiral);
|
|
50
|
+
|
|
51
|
+
return resultingMolfile;
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
/** Save sdf in case ss and as (and optionally as2) strands entered */
|
|
56
|
+
export function saveSdf(
|
|
57
|
+
ss: StrandData, as: StrandData, as2: StrandData, useChiral: boolean,
|
|
58
|
+
oneEntity: boolean
|
|
59
|
+
): void {
|
|
60
|
+
const nonEmptyStrands = [ss.strand, as.strand, as2.strand].filter((item) => item !== '');
|
|
61
|
+
if (
|
|
62
|
+
nonEmptyStrands.length === 0 ||
|
|
63
|
+
nonEmptyStrands.length === 1 && ss.strand === ''
|
|
64
|
+
) {
|
|
65
|
+
grok.shell.warning('Enter SS and AS/AS2 to save SDF');
|
|
66
|
+
} else {
|
|
67
|
+
let result: string;
|
|
68
|
+
if (oneEntity) {
|
|
69
|
+
result = getLinkedMolfile(ss, as, as2, useChiral) + '\n$$$$\n';
|
|
70
|
+
} else {
|
|
71
|
+
const ssMol = getMolfileForStrand(ss.strand, ss.invert);
|
|
72
|
+
const asMol = getMolfileForStrand(as.strand, as.invert);
|
|
73
|
+
const as2Mol = getMolfileForStrand(as2.strand, as2.invert);
|
|
74
|
+
result = ssMol + '\n' +
|
|
75
|
+
`> <Sequence>\nSense Strand\n$$$$\n`;
|
|
76
|
+
if (asMol) {
|
|
77
|
+
result += asMol + '\n' +
|
|
78
|
+
`> <Sequence>\nAnti Sense\n$$$$\n`;
|
|
79
|
+
}
|
|
80
|
+
if (as2Mol) {
|
|
81
|
+
result += as2Mol + '\n' +
|
|
82
|
+
`> <Sequence>\nAnti Sense 2\n$$$$\n`;
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
// construct date-time in the form yyyy-mm-dd_hh-mm-ss
|
|
87
|
+
const date = new Date();
|
|
88
|
+
function pad(x: number): string {
|
|
89
|
+
return (x >= 10) ? x.toString() : '0' + x.toString();
|
|
90
|
+
}
|
|
91
|
+
const dateString: string = date.getFullYear() + '-' + pad(date.getMonth() + 1) +
|
|
92
|
+
'-' + pad(date.getDate()) + '_' + pad(date.getHours()) + '-' +
|
|
93
|
+
pad(date.getMinutes()) + '-' + pad(date.getSeconds());
|
|
94
|
+
|
|
95
|
+
download(`SequenceTranslator-${dateString}.sdf`, encodeURIComponent(result));
|
|
96
|
+
}
|
|
97
|
+
}
|