@datagrok/sequence-translator 1.0.17 → 1.1.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.
Files changed (96) hide show
  1. package/.eslintrc.json +4 -3
  2. package/CHANGELOG.md +3 -0
  3. package/detectors.js +8 -0
  4. package/dist/package-test.js +2 -73079
  5. package/dist/package-test.js.map +1 -0
  6. package/dist/package.js +2 -72284
  7. package/dist/package.js.map +1 -0
  8. package/files/axolabs-style.json +97 -0
  9. package/files/codes-to-symbols.json +66 -0
  10. package/files/formats-to-helm.json +59 -0
  11. package/files/linkers.json +22 -0
  12. package/files/monomer-lib.json +1094 -0
  13. package/link-bio +7 -0
  14. package/package.json +30 -28
  15. package/scripts/build-monomer-lib.py +391 -122
  16. package/src/demo/demo-st-ui.ts +71 -0
  17. package/src/demo/handle-error.ts +12 -0
  18. package/src/model/axolabs/axolabs-tab.ts +111 -0
  19. package/src/model/axolabs/const.ts +33 -0
  20. package/src/{axolabs-tab → model/axolabs}/draw-svg.ts +1 -1
  21. package/src/{axolabs-tab → model/axolabs}/helpers.ts +7 -5
  22. package/src/model/const.ts +19 -0
  23. package/src/model/data-loading-utils/const.ts +8 -0
  24. package/src/model/data-loading-utils/json-loader.ts +38 -0
  25. package/src/model/data-loading-utils/types.ts +30 -0
  26. package/src/model/format-translation/const.ts +8 -0
  27. package/src/model/format-translation/conversion-utils.ts +48 -0
  28. package/src/model/format-translation/format-converter.ts +107 -0
  29. package/src/model/helpers.ts +12 -0
  30. package/src/model/monomer-lib/const.ts +3 -0
  31. package/src/model/monomer-lib/lib-wrapper.ts +106 -0
  32. package/src/model/parsing-validation/format-detector.ts +57 -0
  33. package/src/model/parsing-validation/sequence-validator.ts +52 -0
  34. package/src/model/sequence-to-structure-utils/const.ts +1 -0
  35. package/src/{utils/structures-works → model/sequence-to-structure-utils}/mol-transformations.ts +33 -41
  36. package/src/model/sequence-to-structure-utils/monomer-code-parser.ts +92 -0
  37. package/src/model/sequence-to-structure-utils/sdf-tab.ts +94 -0
  38. package/src/model/sequence-to-structure-utils/sequence-to-molfile.ts +409 -0
  39. package/src/package.ts +104 -92
  40. package/src/tests/const.ts +17 -0
  41. package/src/tests/smiles-tests.ts +32 -457
  42. package/src/view/const/main-tab.ts +3 -0
  43. package/src/view/const/view.ts +10 -0
  44. package/src/view/css/axolabs-tab.css +1 -0
  45. package/src/view/css/colored-text-input.css +27 -0
  46. package/src/view/css/main-tab.css +46 -0
  47. package/src/view/css/sdf-tab.css +39 -0
  48. package/src/view/monomer-lib-viewer/viewer.ts +22 -0
  49. package/src/view/tabs/axolabs.ts +720 -0
  50. package/src/view/tabs/main.ts +174 -0
  51. package/src/view/tabs/sdf.ts +173 -0
  52. package/src/view/utils/app-info-dialog.ts +18 -0
  53. package/src/view/utils/colored-input/colored-text-input.ts +56 -0
  54. package/src/view/utils/colored-input/input-painters.ts +44 -0
  55. package/src/view/utils/draw-molecule.ts +86 -0
  56. package/src/view/utils/molecule-img.ts +106 -0
  57. package/src/view/view.ts +129 -0
  58. package/tsconfig.json +12 -18
  59. package/webpack.config.js +17 -4
  60. package/README.md +0 -84
  61. package/css/style.css +0 -18
  62. package/img/Sequence Translator Axolabs.png +0 -0
  63. package/jest.config.js +0 -33
  64. package/setup-unlink-clean.cmd +0 -14
  65. package/setup-unlink-clean.sh +0 -21
  66. package/setup.cmd +0 -14
  67. package/setup.sh +0 -37
  68. package/src/__jest__/remote.test.ts +0 -77
  69. package/src/__jest__/test-node.ts +0 -97
  70. package/src/apps/oligo-sd-file-app.ts +0 -58
  71. package/src/autostart/calculations.ts +0 -40
  72. package/src/autostart/constants.ts +0 -37
  73. package/src/autostart/registration.ts +0 -306
  74. package/src/axolabs-tab/axolabs-tab.ts +0 -873
  75. package/src/axolabs-tab/define-pattern.ts +0 -874
  76. package/src/hardcode-to-be-eliminated/ICDs.ts +0 -3
  77. package/src/hardcode-to-be-eliminated/IDPs.ts +0 -3
  78. package/src/hardcode-to-be-eliminated/const.ts +0 -5
  79. package/src/hardcode-to-be-eliminated/constants.ts +0 -101
  80. package/src/hardcode-to-be-eliminated/converters.ts +0 -323
  81. package/src/hardcode-to-be-eliminated/map.ts +0 -720
  82. package/src/hardcode-to-be-eliminated/salts.ts +0 -2
  83. package/src/hardcode-to-be-eliminated/sources.ts +0 -3
  84. package/src/hardcode-to-be-eliminated/users.ts +0 -3
  85. package/src/main-tab/main-tab.ts +0 -210
  86. package/src/sdf-tab/sdf-tab.ts +0 -163
  87. package/src/sdf-tab/sequence-codes-tools.ts +0 -347
  88. package/src/utils/const.ts +0 -0
  89. package/src/utils/helpers.ts +0 -28
  90. package/src/utils/parse.ts +0 -27
  91. package/src/utils/sdf-add-columns.ts +0 -118
  92. package/src/utils/sdf-save-table.ts +0 -56
  93. package/src/utils/structures-works/draw-molecule.ts +0 -84
  94. package/src/utils/structures-works/from-monomers.ts +0 -266
  95. package/test-SequenceTranslator-6288c2fbe346-695b7b55.html +0 -259
  96. package/vendors/openchemlib-full.js +0 -293
@@ -0,0 +1,57 @@
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 {sortByReverseLength} from '../helpers';
7
+ import {DEFAULT_FORMATS} from '../const';
8
+ import {MonomerLibWrapper} from '../monomer-lib/lib-wrapper';
9
+ import {codesToHelmDictionary} from '../data-loading-utils/json-loader';
10
+ import {SequenceValidator} from './sequence-validator';
11
+
12
+ export class FormatDetector {
13
+ constructor (private sequence: string) {
14
+ this.libWrapper = MonomerLibWrapper.getInstance();
15
+ this.formats = Object.keys(codesToHelmDictionary);
16
+ };
17
+
18
+ private libWrapper: MonomerLibWrapper;
19
+ private formats: string[];
20
+
21
+ getFormat(): string | null {
22
+ // todo: reliable criterion
23
+ if (this.sequence.startsWith('RNA'))
24
+ return DEFAULT_FORMATS.HELM;
25
+ const possibleFormats = this.getListOfPossibleSynthesizersByFirstMatchedCode();
26
+ if (possibleFormats.length === 0)
27
+ return null;
28
+
29
+ const validator = new SequenceValidator(this.sequence);
30
+ const outputIndices = Array(possibleFormats.length).fill(0);
31
+ for (let i = 0; i < possibleFormats.length; ++i) {
32
+ const format = possibleFormats[i];
33
+ outputIndices[i] = validator.getInvalidCodeIndex(format);
34
+ }
35
+ const formatIdx = (outputIndices.some((idx) => idx === -1)) ? -1 : Math.max(...outputIndices);
36
+ return possibleFormats[outputIndices.indexOf(formatIdx)];
37
+ }
38
+
39
+ // todo: rename
40
+ private getListOfPossibleSynthesizersByFirstMatchedCode(): string[] {
41
+ const sequence = this.sequence;
42
+ let synthesizers: string[] = [];
43
+ for (const format of this.formats) {
44
+ let codes = sortByReverseLength(this.libWrapper.getCodesByFormat(format));
45
+ let start = 0;
46
+ for (let i = 0; i < sequence.length; i++) {
47
+ if (sequence[i] === ')' && i !== sequence.length - 1) {
48
+ start = i + 1;
49
+ break;
50
+ }
51
+ }
52
+ if (codes.some((s: string) => s === sequence.slice(start, start + s.length)))
53
+ synthesizers.push(format);
54
+ }
55
+ return synthesizers;
56
+ }
57
+ }
@@ -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';
@@ -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 = !inverted ? Math.min(...coordinates.y) - 1 : Math.max(...coordinates.y) + 15;
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) + 1;//twoChains ? 0 : coordinates.x[numbers.natom - 1] - coordinates.x[0];
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(molecule: string): string {
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
- ? (coordinates.x[indexFivePrime] > 0 ? Math.PI - Math.atan(derivative) : Math.PI * 2 - Math.atan(derivative))
407
- : (coordinates.x[indexFivePrime] > 0 ? -Math.PI - Math.atan(derivative) : Math.atan(derivative));
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(molecule: string): string {
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(molecule: string) {
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,94 @@
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
+
12
+ export type StrandData = {
13
+ strand: string,
14
+ invert: boolean
15
+ }
16
+
17
+ /** Get a molfile for a single strand */
18
+ export function getMolfileForStrand(strand: string, invert: boolean): string {
19
+ if (strand === '')
20
+ return '';
21
+ const format = DEFAULT_FORMATS.AXOLABS;
22
+ let molfile = '';
23
+ try {
24
+ molfile = (new SequenceToMolfileConverter(strand, invert, format)).convert();
25
+ } catch (err) {
26
+ const errStr = errorToConsole(err);
27
+ console.error(errStr);
28
+ }
29
+ return molfile;
30
+ }
31
+
32
+ /** Get molfile for single strand or linked strands */
33
+ export function getLinkedMolfile(
34
+ ss: StrandData, as: StrandData, as2: StrandData, useChiral: boolean
35
+ ): string {
36
+ const nonEmptyStrands = [ss, as, as2].filter((item) => item.strand !== '');
37
+ if (nonEmptyStrands.length === 1) {
38
+ return getMolfileForStrand(nonEmptyStrands[0].strand, nonEmptyStrands[0].invert);
39
+ } else {
40
+ const ssMol = getMolfileForStrand(ss.strand, ss.invert);
41
+ const asMol = getMolfileForStrand(as.strand, as.invert);
42
+ const as2Mol = getMolfileForStrand(as2.strand, as2.invert);
43
+
44
+ // select only the non-empty anti-strands
45
+ const antiStrands = [asMol, as2Mol].filter((item) => item !== '');
46
+ const resultingMolfile = linkStrandsV3000({senseStrands: [ssMol], antiStrands: antiStrands}, useChiral);
47
+
48
+ return resultingMolfile;
49
+ }
50
+ }
51
+
52
+ /** Save sdf in case ss and as (and optionally as2) strands entered */
53
+ export function saveSdf(
54
+ ss: StrandData, as: StrandData, as2: StrandData, useChiral: boolean,
55
+ oneEntity: boolean
56
+ ): void {
57
+ const nonEmptyStrands = [ss.strand, as.strand, as2.strand].filter((item) => item !== '');
58
+ if (
59
+ nonEmptyStrands.length === 0 ||
60
+ nonEmptyStrands.length === 1 && ss.strand === ''
61
+ ) {
62
+ grok.shell.warning('Enter SS and AS/AS2 to save SDF');
63
+ } else {
64
+ let result: string;
65
+ if (oneEntity) {
66
+ result = getLinkedMolfile(ss, as, as2, useChiral) + '\n$$$$\n';
67
+ } else {
68
+ const ssMol = getMolfileForStrand(ss.strand, ss.invert);
69
+ const asMol = getMolfileForStrand(as.strand, as.invert);
70
+ const as2Mol = getMolfileForStrand(as2.strand, as2.invert);
71
+ result = ssMol + '\n' +
72
+ `> <Sequence>\nSense Strand\n$$$$\n`;
73
+ if (asMol) {
74
+ result += asMol + '\n' +
75
+ `> <Sequence>\nAnti Sense\n$$$$\n`;
76
+ }
77
+ if (as2Mol) {
78
+ result += as2Mol + '\n' +
79
+ `> <Sequence>\nAnti Sense 2\n$$$$\n`;
80
+ }
81
+ }
82
+
83
+ // construct date-time in the form yyyy-mm-dd_hh-mm-ss
84
+ const date = new Date();
85
+ function pad(x: number): string {
86
+ return (x >= 10) ? x.toString() : '0' + x.toString();
87
+ }
88
+ const dateString: string = date.getFullYear() + '-' + pad(date.getMonth() + 1) +
89
+ '-' + pad(date.getDate()) + '_' + pad(date.getHours()) + '-' +
90
+ pad(date.getMinutes()) + '-' + pad(date.getSeconds());
91
+
92
+ download(`SequenceTranslator-${dateString}.sdf`, encodeURIComponent(result));
93
+ }
94
+ }