@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,409 @@
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 {MonomerSequenceParser} from './monomer-code-parser';
7
+ import {MonomerLibWrapper} from '../monomer-lib/lib-wrapper';
8
+
9
+ export class SequenceToMolfileConverter {
10
+ constructor(
11
+ sequence: string, invert: boolean = false, format: string
12
+ ) {
13
+ this.lib = MonomerLibWrapper.getInstance();
14
+ const codeToSymbolMap = this.lib.getCodeToSymbolMap(format);
15
+ this.parser = new MonomerSequenceParser(sequence, invert, codeToSymbolMap);
16
+ }
17
+
18
+ private parser: MonomerSequenceParser;
19
+ private lib: MonomerLibWrapper;
20
+
21
+ convert(): string {
22
+ const parsedSequence = this.parser.parseSequence();
23
+ const monomerMolfiles: string[] = [];
24
+ parsedSequence.forEach((monomerSymbol, idx) => {
25
+ const monomerMolfile = this.getMonomerMolfile(monomerSymbol, idx);
26
+ monomerMolfiles.push(monomerMolfile);
27
+ })
28
+ return this.getPolymerMolfile(monomerMolfiles);
29
+ }
30
+
31
+ private getMonomerMolfile(monomerSymbol: string, idx: number): string {
32
+ const molBlock = this.lib.getMolfileBySymbol(monomerSymbol);
33
+ if (this.lib.isModification(monomerSymbol))
34
+ return (idx === 0) ? this.reflect(molBlock) : molBlock;
35
+ else
36
+ return this.rotateNucleotidesV3000(molBlock);
37
+ }
38
+
39
+ private getPolymerMolfile(monomerMolfiles: string[]) {
40
+ return this.linkV3000(monomerMolfiles);
41
+ }
42
+
43
+ private reflect(molBlock: string): string {
44
+ const coordinates = this.extractAtomDataV3000(molBlock);
45
+ const natom = coordinates.atomIndex.length;
46
+
47
+ const indexFivePrime = coordinates.atomIndex.indexOf(1);
48
+ const indexThreePrime = coordinates.atomIndex.indexOf(natom);
49
+
50
+ const xCenter = (coordinates.x[indexThreePrime] + coordinates.x[indexFivePrime]) / 2;
51
+ const yCenter = (coordinates.y[indexThreePrime] + coordinates.y[indexFivePrime]) / 2;
52
+
53
+ //place to center
54
+ for (let i = 0; i < natom; i++) {
55
+ coordinates.x[i] -= xCenter;
56
+ coordinates.y[i] -= yCenter;
57
+ }
58
+
59
+ //place to center
60
+ for (let i = 0; i < natom; i++)
61
+ coordinates.x[i] = -coordinates.x[i];
62
+
63
+ //place to right
64
+ const xShift = coordinates.x[indexFivePrime];
65
+ for (let i = 0; i < natom; i++)
66
+ coordinates.x[i] -= xShift;
67
+
68
+ //rewrite molBlock
69
+ let index = molBlock.indexOf('M V30 BEGIN ATOM'); // V3000 index for atoms coordinates
70
+ index = molBlock.indexOf('\n', index);
71
+ let indexEnd = index;
72
+ for (let i = 0; i < natom; i++) {
73
+ index = molBlock.indexOf('V30', index) + 4;
74
+ index = molBlock.indexOf(' ', index) + 1;
75
+ index = molBlock.indexOf(' ', index) + 1;
76
+ indexEnd = molBlock.indexOf(' ', index) + 1;
77
+ indexEnd = molBlock.indexOf(' ', indexEnd);
78
+
79
+ molBlock = molBlock.slice(0, index) +
80
+ coordinates.x[i] + ' ' + coordinates.y[i] +
81
+ molBlock.slice(indexEnd);
82
+
83
+ index = molBlock.indexOf('\n', index) + 1;
84
+ }
85
+
86
+ return molBlock;
87
+ }
88
+
89
+ private extractAtomDataV3000(molBlock: string) {
90
+ const numbers = this.extractAtomsBondsNumbersV3000(molBlock);
91
+ let index = molBlock.indexOf('M V30 BEGIN ATOM'); // V3000 index for atoms coordinates
92
+ index = molBlock.indexOf('\n', index);
93
+ let indexEnd = index;
94
+
95
+ const indexes: number[] = Array(numbers.natom);
96
+ const types: string[] = Array(numbers.natom);
97
+ const x: number[] = Array(numbers.natom);
98
+ const y: number[] = Array(numbers.natom);
99
+
100
+ for (let i = 0; i < numbers.natom; i++) {
101
+ index = molBlock.indexOf('V30', index) + 4;
102
+ indexEnd = molBlock.indexOf(' ', index);
103
+ indexes[i] = parseInt(molBlock.substring(index, indexEnd));
104
+
105
+ index = indexEnd + 1;
106
+ indexEnd = molBlock.indexOf(' ', index);
107
+ types[i] = molBlock.substring(index, indexEnd);
108
+
109
+ index = indexEnd + 1;
110
+ indexEnd = molBlock.indexOf(' ', index);
111
+ x[i] = parseFloat(molBlock.substring(index, indexEnd));
112
+
113
+ index = indexEnd + 1;
114
+ indexEnd = molBlock.indexOf(' ', index);
115
+ y[i] = parseFloat(molBlock.substring(index, indexEnd));
116
+
117
+ index = molBlock.indexOf('\n', index) + 1;
118
+ }
119
+
120
+ return {atomIndex: indexes, atomType: types, x: x, y: y};
121
+ }
122
+
123
+ private extractAtomsBondsNumbersV3000(molBlock: string): { natom: number, nbond: number } {
124
+ molBlock = molBlock.replaceAll('\r', ''); //equalize old and new sdf standards
125
+ let index = molBlock.indexOf('COUNTS') + 7; // V3000 index for atoms and bonds number
126
+ let indexEnd = molBlock.indexOf(' ', index);
127
+
128
+ const atomsNumber = parseInt(molBlock.substring(index, indexEnd));
129
+ index = indexEnd + 1;
130
+ indexEnd = molBlock.indexOf(' ', index);
131
+ const bondsNumber = parseInt(molBlock.substring(index, indexEnd));
132
+
133
+ return {natom: atomsNumber, nbond: bondsNumber};
134
+ }
135
+
136
+ private rotateNucleotidesV3000(molBlock: string): string {
137
+ const coordinates = this.extractAtomDataV3000(molBlock);
138
+ const natom = coordinates.atomIndex.length;
139
+
140
+ const indexFivePrime = coordinates.atomIndex.indexOf(1);
141
+ const indexThreePrime = coordinates.atomIndex.indexOf(natom);
142
+
143
+ //fix 5 prime if inadequate
144
+ if (natom > 8)
145
+ this.fix5Prime(coordinates, indexFivePrime, indexThreePrime);
146
+
147
+ const xCenter = (coordinates.x[indexThreePrime] + coordinates.x[indexFivePrime]) / 2;
148
+ const yCenter = (coordinates.y[indexThreePrime] + coordinates.y[indexFivePrime]) / 2;
149
+
150
+ //place to center
151
+ for (let i = 0; i < natom; i++) {
152
+ coordinates.x[i] -= xCenter;
153
+ coordinates.y[i] -= yCenter;
154
+ }
155
+
156
+ let angle = 0;
157
+ if (coordinates.x[indexFivePrime] === 0) {
158
+ angle = coordinates.y[indexFivePrime] > coordinates.y[indexThreePrime] ? Math.PI / 2 : 3 * Math.PI / 2;
159
+ } else if (coordinates.y[indexFivePrime] === 0) {
160
+ angle = coordinates.x[indexFivePrime] > coordinates.x[indexThreePrime] ? Math.PI : 0;
161
+ } else {
162
+ const derivative = coordinates.y[indexFivePrime] / coordinates.x[indexFivePrime];
163
+ angle = derivative > 0 ?
164
+ (coordinates.x[indexFivePrime] > 0 ? Math.PI - Math.atan(derivative) : Math.PI * 2 - Math.atan(derivative)) :
165
+ (coordinates.x[indexFivePrime] > 0 ? -Math.PI - Math.atan(derivative) : Math.atan(derivative));
166
+ }
167
+
168
+ const cos = Math.cos(angle);
169
+ const sin = Math.sin(angle);
170
+
171
+ for (let i = 0; i < natom; i++) {
172
+ const xAdd = coordinates.x[i];
173
+ coordinates.x[i] = xAdd * cos - coordinates.y[i] * sin;
174
+ coordinates.y[i] = xAdd * sin + coordinates.y[i] * cos;
175
+ }
176
+
177
+ //place to right
178
+ const xShift = coordinates.x[indexFivePrime];
179
+ for (let i = 0; i < natom; i++)
180
+ coordinates.x[i] -= xShift;
181
+
182
+ //rewrite molBlock
183
+ let index = molBlock.indexOf('M V30 BEGIN ATOM'); // V3000 index for atoms coordinates
184
+ index = molBlock.indexOf('\n', index);
185
+ let indexEnd = index;
186
+ for (let i = 0; i < natom; i++) {
187
+ index = molBlock.indexOf('V30', index) + 4;
188
+ index = molBlock.indexOf(' ', index) + 1;
189
+ index = molBlock.indexOf(' ', index) + 1;
190
+ indexEnd = molBlock.indexOf(' ', index) + 1;
191
+ indexEnd = molBlock.indexOf(' ', indexEnd);
192
+
193
+ molBlock = molBlock.slice(0, index) +
194
+ coordinates.x[i] + ' ' + coordinates.y[i] +
195
+ molBlock.slice(indexEnd);
196
+
197
+ index = molBlock.indexOf('\n', index) + 1;
198
+ }
199
+
200
+ return molBlock;
201
+ }
202
+
203
+
204
+ private linkV3000(molBlocks: string[], useChirality: boolean = true): string {
205
+ let macroMolBlock = '\nDatagrok macromolecule handler\n\n';
206
+ macroMolBlock += ' 0 0 0 0 0 0 999 V3000\n';
207
+ macroMolBlock += 'M V30 BEGIN CTAB\n';
208
+ let atomBlock = '';
209
+ let bondBlock = '';
210
+ let collectionBlock = '';
211
+ const collection: number [] = [];
212
+ let natom = 0;
213
+ let nbond = 0;
214
+ let xShift = 0;
215
+
216
+ for (let i = 0; i < molBlocks.length; i++) {
217
+ const isBoundary = molBlocks[i].includes('MODIFICATION') && i === 0;
218
+ let specLength = 0;
219
+ if (isBoundary) {
220
+ const coordinates = this.extractAtomDataV3000(molBlocks[i]);
221
+ specLength = coordinates.atomIndex.length;
222
+ }
223
+
224
+
225
+ molBlocks[i] = molBlocks[i].replaceAll('(-\nM V30 ', '(')
226
+ .replaceAll('-\nM V30 ', '').replaceAll(' )', ')');
227
+ const numbers = this.extractAtomsBondsNumbersV3000(molBlocks[i]);
228
+ const coordinates = this.extractAtomDataV3000(molBlocks[i]);
229
+
230
+ let indexAtoms = molBlocks[i].indexOf('M V30 BEGIN ATOM'); // V3000 index for atoms coordinates
231
+ indexAtoms = molBlocks[i].indexOf('\n', indexAtoms);
232
+ let index = indexAtoms;
233
+ let indexEnd = indexAtoms;
234
+
235
+ for (let j = 0; j < numbers.natom; j++) {
236
+ if (coordinates.atomIndex[j] !== 1 || i === 0) {
237
+ //rewrite atom number
238
+ index = molBlocks[i].indexOf('V30', index) + 4;
239
+ indexEnd = molBlocks[i].indexOf(' ', index);
240
+ let atomNumber = 0;
241
+ if (isBoundary) {
242
+ atomNumber = parseInt(molBlocks[i].substring(index, indexEnd));
243
+ if (atomNumber === 1)
244
+ atomNumber = specLength;
245
+ else if (atomNumber === specLength)
246
+ atomNumber = 1;
247
+ atomNumber += natom;
248
+ } else {
249
+ atomNumber = parseInt(molBlocks[i].substring(index, indexEnd)) + natom;
250
+ }
251
+ molBlocks[i] = molBlocks[i].slice(0, index) + atomNumber + molBlocks[i].slice(indexEnd);
252
+
253
+ //rewrite coordinates
254
+ index = molBlocks[i].indexOf(' ', index) + 1;
255
+ index = molBlocks[i].indexOf(' ', index) + 1;
256
+ indexEnd = molBlocks[i].indexOf(' ', index);
257
+
258
+ const totalShift = xShift - coordinates.x[0];
259
+ let coordinate =
260
+ Math.round(10000 * (parseFloat(molBlocks[i].substring(index, indexEnd)) + totalShift)) / 10000;
261
+ molBlocks[i] = molBlocks[i].slice(0, index) + coordinate + molBlocks[i].slice(indexEnd);
262
+
263
+ index = molBlocks[i].indexOf(' ', index) + 1;
264
+ indexEnd = molBlocks[i].indexOf(' ', index);
265
+ coordinate =
266
+ Math.round(10000 * (parseFloat(molBlocks[i].substring(index, indexEnd)))) / 10000;
267
+ molBlocks[i] = molBlocks[i].slice(0, index) + coordinate + molBlocks[i].slice(indexEnd);
268
+
269
+ index = molBlocks[i].indexOf('\n', index) + 1;
270
+ } else {
271
+ index = molBlocks[i].indexOf('M V30', index) - 1;
272
+ indexEnd = molBlocks[i].indexOf('\n', index + 1);
273
+ molBlocks[i] = molBlocks[i].slice(0, index) + molBlocks[i].slice(indexEnd);
274
+ }
275
+ }
276
+
277
+ const indexAtomsEnd = molBlocks[i].indexOf('M V30 END ATOM');
278
+ atomBlock += molBlocks[i].substring(indexAtoms + 1, indexAtomsEnd);
279
+
280
+ let indexBonds = molBlocks[i].indexOf('M V30 BEGIN BOND'); // V3000 index for bonds
281
+ indexBonds = molBlocks[i].indexOf('\n', indexBonds);
282
+ index = indexBonds;
283
+ indexEnd = indexBonds;
284
+
285
+ for (let j = 0; j < numbers.nbond; j++) {
286
+ //rewrite bond number
287
+ index = molBlocks[i].indexOf('V30', index) + 4;
288
+ indexEnd = molBlocks[i].indexOf(' ', index);
289
+ const bondNumber = parseInt(molBlocks[i].substring(index, indexEnd)) + nbond;
290
+ molBlocks[i] = molBlocks[i].slice(0, index) + bondNumber + molBlocks[i].slice(indexEnd);
291
+
292
+ //rewrite atom pair in bond
293
+ index = molBlocks[i].indexOf(' ', index) + 1;
294
+ index = molBlocks[i].indexOf(' ', index) + 1;
295
+ indexEnd = molBlocks[i].indexOf(' ', index);
296
+ let atomNumber = 0;
297
+ if (isBoundary) {
298
+ atomNumber = parseInt(molBlocks[i].substring(index, indexEnd));
299
+ if (atomNumber === 1)
300
+ atomNumber = specLength;
301
+ else if (atomNumber === specLength)
302
+ atomNumber = 1;
303
+ atomNumber += natom;
304
+ } else {
305
+ atomNumber = parseInt(molBlocks[i].substring(index, indexEnd)) + natom;
306
+ }
307
+
308
+ molBlocks[i] = molBlocks[i].slice(0, index) + atomNumber + molBlocks[i].slice(indexEnd);
309
+ index = molBlocks[i].indexOf(' ', index) + 1;
310
+ indexEnd = Math.min(molBlocks[i].indexOf('\n', index), molBlocks[i].indexOf(' ', index));
311
+ atomNumber = 0;
312
+ if (isBoundary) {
313
+ atomNumber = parseInt(molBlocks[i].substring(index, indexEnd));
314
+ if (atomNumber === 1)
315
+ atomNumber = specLength;
316
+ else if (atomNumber === specLength)
317
+ atomNumber = 1;
318
+ atomNumber += natom;
319
+ } else {
320
+ atomNumber = parseInt(molBlocks[i].substring(index, indexEnd)) + natom;
321
+ }
322
+ molBlocks[i] = molBlocks[i].slice(0, index) + atomNumber + molBlocks[i].slice(indexEnd);
323
+
324
+ index = molBlocks[i].indexOf('\n', index) + 1;
325
+ }
326
+
327
+ const indexBondEnd = molBlocks[i].indexOf('M V30 END BOND');
328
+ bondBlock += molBlocks[i].substring(indexBonds + 1, indexBondEnd);
329
+
330
+ let indexCollection = molBlocks[i].indexOf('M V30 MDLV30/STEABS ATOMS=('); // V3000 index for collections
331
+
332
+ while (indexCollection !== -1) {
333
+ indexCollection += 28;
334
+ const collectionEnd = molBlocks[i].indexOf(')', indexCollection);
335
+ const collectionEntries = molBlocks[i].substring(indexCollection, collectionEnd).split(' ').slice(1);
336
+ collectionEntries.forEach((e) => {
337
+ collection.push(parseInt(e) + natom);
338
+ });
339
+ indexCollection = collectionEnd;
340
+ indexCollection = molBlocks[i].indexOf('M V30 MDLV30/STEABS ATOMS=(', indexCollection);
341
+ }
342
+
343
+ natom += numbers.natom - 1;
344
+ nbond += numbers.nbond;
345
+ if (isBoundary)
346
+ xShift += Math.max(...coordinates.x);
347
+ else
348
+ xShift += coordinates.x[numbers.natom - 1] - coordinates.x[0];
349
+ }
350
+
351
+ const entries = 4;
352
+ const collNumber = Math.ceil(collection.length / entries);
353
+
354
+ collectionBlock += 'M V30 MDLV30/STEABS ATOMS=(' + collection.length + ' -\n';
355
+ for (let i = 0; i < collNumber; i++) {
356
+ collectionBlock += 'M V30 ';
357
+ const entriesCurrent = i + 1 === collNumber ? collection.length - (collNumber - 1) * entries : entries;
358
+ for (let j = 0; j < entriesCurrent; j++) {
359
+ collectionBlock += (j + 1 === entriesCurrent) ?
360
+ (i === collNumber - 1 ? collection[entries * i + j] + ')\n' : collection[entries * i + j] + ' -\n') :
361
+ collection[entries * i + j] + ' ';
362
+ }
363
+ }
364
+
365
+ //generate file
366
+ natom++;
367
+ macroMolBlock += 'M V30 COUNTS ' + natom + ' ' + nbond + ' 0 0 0\n';
368
+ macroMolBlock += 'M V30 BEGIN ATOM\n';
369
+ macroMolBlock += atomBlock;
370
+ macroMolBlock += 'M V30 END ATOM\n';
371
+ macroMolBlock += 'M V30 BEGIN BOND\n';
372
+ macroMolBlock += bondBlock;
373
+ macroMolBlock += 'M V30 END BOND\n';
374
+ if (useChirality && collection.length > 0) {
375
+ macroMolBlock += 'M V30 BEGIN COLLECTION\n';
376
+ macroMolBlock += collectionBlock;
377
+ macroMolBlock += 'M V30 END COLLECTION\n';
378
+ } else { macroMolBlock = macroMolBlock.replace(/ CFG=\d/g, ' '); }
379
+
380
+ macroMolBlock += 'M V30 END CTAB\n';
381
+ macroMolBlock += 'M END';
382
+
383
+ return macroMolBlock;
384
+ }
385
+
386
+ private fix5Prime(coordinates: { atomIndex: number[], atomType: string[], x: number[], y: number[] },
387
+ indexFivePrime: number, indexThreePrime: number) {
388
+ const indexFivePrimeNeighbour = indexFivePrime + 1;
389
+ const xShift = coordinates.x[indexFivePrimeNeighbour];
390
+ const yShift = coordinates.y[indexFivePrimeNeighbour];
391
+ const base3PrimeX = coordinates.x[indexThreePrime] - xShift;
392
+ const base3PrimeY = coordinates.y[indexThreePrime] - yShift;
393
+ const base5PrimeX = coordinates.x[indexFivePrime] - xShift;
394
+ const base5PrimeY = coordinates.y[indexFivePrime] - yShift;
395
+
396
+ const rotated5PrimeX = base5PrimeX * Math.cos(Math.PI * 2 / 3) - base5PrimeY * Math.sin(Math.PI * 2 / 3);
397
+ const rotated5PrimeY = base5PrimeX * Math.sin(Math.PI * 2 / 3) + base5PrimeY * Math.cos(Math.PI * 2 / 3);
398
+
399
+ const dx = base5PrimeX - base3PrimeX;
400
+ const dy = base5PrimeY - base3PrimeY;
401
+ const dxRotated = rotated5PrimeX - base3PrimeX;
402
+ const dyRotated = rotated5PrimeY - base3PrimeY;
403
+
404
+ if (Math.sqrt(dyRotated * dyRotated + dxRotated * dxRotated) >= Math.sqrt(dy * dy + dx * dx)) {
405
+ coordinates.x[indexFivePrime] = rotated5PrimeX + xShift;
406
+ coordinates.y[indexFivePrime] = rotated5PrimeY + yShift;
407
+ }
408
+ }
409
+ }
package/src/package.ts CHANGED
@@ -2,111 +2,123 @@ import * as grok from 'datagrok-api/grok';
2
2
  import * as ui from 'datagrok-api/ui';
3
3
  import * as DG from 'datagrok-api/dg';
4
4
 
5
- import {autostartOligoSdFileSubscription} from './autostart/registration';
6
- import {OligoSdFileApp} from './apps/oligo-sd-file-app';
7
-
8
- // three tabs of the app's view
9
- import {getMainTab} from './main-tab/main-tab';
10
- import {getAxolabsTab} from './axolabs-tab/axolabs-tab';
11
- import {getSdfTab} from './sdf-tab/sdf-tab';
12
-
13
- const MAIN = 'MAIN';
14
- const AXOLABS = 'AXOLABS';
15
- const SDF = 'SDF';
16
-
17
- const SEQUENCE_TRANSLATOR = 'Sequence Translator';
18
- const DEFAULT_SEQUENCE = 'fAmCmGmAmCpsmU';
19
- const DEFAULT_LIB_FILENAME = 'helmLib.json';
20
-
21
- import {IMonomerLib, MonomerWorks, readLibrary} from '@datagrok-libraries/bio';
5
+ import {SequenceTranslatorUI} from './view/view';
6
+ import {LIB_PATH, DEFAULT_LIB_FILENAME} from './model/data-loading-utils/const';
7
+ import {IMonomerLib} from '@datagrok-libraries/bio/src/types';
8
+ import {getMonomerLibHelper, IMonomerLibHelper} from '@datagrok-libraries/bio/src/monomer-works/monomer-utils';
9
+ import {getJsonData} from './model/data-loading-utils/json-loader';
10
+ import {SequenceToMolfileConverter} from './model/sequence-to-structure-utils/sequence-to-molfile';
11
+ import {linkStrandsV3000} from './model/sequence-to-structure-utils/mol-transformations';
12
+ import {MonomerLibWrapper} from './model/monomer-lib/lib-wrapper';
13
+ import {FormatDetector} from './model/parsing-validation/format-detector';
14
+ import {SequenceValidator} from './model/parsing-validation/sequence-validator';
15
+ import {demoDesignPatternUI, demoVisualizeDuplexUI, demoTranslateSequenceUI} from './demo/demo-st-ui';
16
+
17
+ class StPackage extends DG.Package {
18
+ private _monomerLib?: IMonomerLib;
19
+
20
+ get monomerLib(): IMonomerLib {
21
+ if (!this._monomerLib)
22
+ throw new Error ('ST: monomer lib not loaded')
23
+ return this._monomerLib!;
24
+ }
22
25
 
23
- export const _package = new DG.Package();
26
+ public async initMonomerLib(): Promise<void> {
27
+ if (this._monomerLib !== undefined)
28
+ return;
29
+
30
+ const pi: DG.TaskBarProgressIndicator = DG.TaskBarProgressIndicator.create(
31
+ 'Initializing Sequence Translator monomer library ...');
32
+ try {
33
+ const libHelper: IMonomerLibHelper = await getMonomerLibHelper();
34
+ this._monomerLib = await libHelper.readLibrary(LIB_PATH, DEFAULT_LIB_FILENAME);
35
+ } catch (err: any) {
36
+ const errMsg: string = err.hasOwnProperty('message') ? err.message : err.toString();
37
+ throw new Error('Sequence Translator: Loading monomer library error: ' + errMsg);
38
+ } finally {
39
+ pi.close();
40
+ }
41
+ }
42
+ }
24
43
 
25
- const LIB_PATH = 'System:AppData/SequenceTranslator';
44
+ export const _package: StPackage = new StPackage();
26
45
 
27
- let monomerLib: IMonomerLib | null = null;
28
- export let monomerWorks: MonomerWorks | null = null;
46
+ //name: Sequence Translator
47
+ //tags: app
48
+ export async function sequenceTranslatorApp(): Promise<void> {
49
+ const pi: DG.TaskBarProgressIndicator = DG.TaskBarProgressIndicator.create('Loading Sequence Translator app ...');
29
50
 
30
- export function getMonomerWorks() {
31
- return monomerWorks;
51
+ try {
52
+ await initSequenceTranslatorLibData();
53
+ const v = new SequenceTranslatorUI();
54
+ await v.createLayout();
55
+ } catch (err: any) {
56
+ const errMsg: string = err.hasOwnProperty('message') ? err.message : err.toString();
57
+ grok.shell.error(`Loading Sequence Translator application error: ` + errMsg);
58
+ throw err;
59
+ } finally {
60
+ pi.close();
61
+ }
32
62
  }
33
63
 
34
- export function getMonomerLib() {
35
- return monomerLib;
64
+ //name: initSequenceTranslatorLibData
65
+ export async function initSequenceTranslatorLibData(): Promise<void> {
66
+ await getJsonData();
67
+ await _package.initMonomerLib();
36
68
  }
37
69
 
38
- //name: Sequence Translator
39
- //tags: app
40
- export async function sequenceTranslator(): Promise<void> {
41
- monomerLib = await readLibrary(LIB_PATH, DEFAULT_LIB_FILENAME);
42
-
43
- if (monomerWorks === null)
44
- monomerWorks = new MonomerWorks(monomerLib);
45
-
46
- const windows = grok.shell.windows;
47
- windows.showProperties = false;
48
- windows.showToolbox = false;
49
- windows.showHelp = false;
50
-
51
- let urlParams = new URLSearchParams(window.location.search);
52
-
53
- let mainSeq: string = DEFAULT_SEQUENCE;
54
- const view = grok.shell.newView(SEQUENCE_TRANSLATOR, []);
55
- view.box = true;
56
-
57
- const tabControl = ui.tabControl({
58
- [MAIN]: await getMainTab((seq) => {
59
- mainSeq = seq;
60
- urlParams = new URLSearchParams();
61
- urlParams.set('seq', mainSeq);
62
- updatePath();
63
- }),
64
- [AXOLABS]: getAxolabsTab(),
65
- [SDF]: getSdfTab(),
66
- });
67
-
68
- tabControl.onTabChanged.subscribe(() => {
69
- if (tabControl.currentPane.name !== MAIN)
70
- urlParams.delete('seq');
71
- else
72
- urlParams.set('seq', mainSeq);
73
- updatePath();
74
- });
75
-
76
- function updatePath() {
77
- const urlParamsTxt: string = Object.entries(urlParams)
78
- .map(([key, value]) => `${key}=${encodeURIComponent(value)}`).join('&');
79
- view.path = '/apps/SequenceTranslator/SequenceTranslator' + `/${tabControl.currentPane.name}/?${urlParamsTxt}`;
80
- }
70
+ //name: getCodeToWeightsMap
71
+ //output: object result
72
+ export function getCodeToWeightsMap(): {[key: string]: number} {
73
+ const map = MonomerLibWrapper.getInstance().getCodesToWeightsMap();
74
+ return Object.fromEntries(map);
75
+ }
81
76
 
82
- const pathParts: string[] = window.location.pathname.split('/');
83
- if (pathParts.length >= 5) {
84
- const tabName: string = pathParts[5];
85
- tabControl.currentPane = tabControl.getPane(tabName);
86
- }
77
+ //name: validateSequence
78
+ //input: string sequence
79
+ //output: bool result
80
+ export function validateSequence(sequence: string): boolean {
81
+ const validator = new SequenceValidator(sequence);
82
+ const format = (new FormatDetector(sequence).getFormat());
83
+ return (format === null) ? false : validator.isValidSequence(format!);
84
+ }
87
85
 
88
- view.append(tabControl);
89
- // console.debug('SequenceTranslator: app sequenceTranslator() ' + `view.path='${view.path}', view.basePath='${view.basePath}'.`);
86
+ //name: validateSequence
87
+ //input: string sequence
88
+ //input: bool invert
89
+ //output: string result
90
+ export function getMolfileFromGcrsSequence(sequence: string, invert: boolean): string {
91
+ return (new SequenceToMolfileConverter(sequence, invert, 'GCRS')).convert();
90
92
  }
91
93
 
92
- //tags: autostart
93
- export async function autostartST() {
94
- autostartOligoSdFileSubscription();
95
- };
94
+ //name: linkStrands
95
+ //input: object strands
96
+ //input: bool invert
97
+ //output: string result
98
+ export function linkStrands(strands: { senseStrands: string[], antiStrands: string[] }): string {
99
+ return linkStrandsV3000(strands, true);
100
+ }
96
101
 
97
- //name: oligoSdFileApp
98
- //description: Test/demo app for oligoSdFile
99
- export async function oligoSdFileApp() {
100
- // console.debug('SequenceTranslator: package.ts oligoSdFileApp()');
102
+ //name: demoTranslateSequence
103
+ //meta.demoPath: Bioinformatics | Oligonucleotide Sequence: Translate
104
+ //description: Translate oligonucleotide sequences across various formats accepted by different synthesizers
105
+ //meta.path: /apps/Tutorials/Demo/Bioinformatics/Oligonucleotide%20Sequence:%20Translate
106
+ export async function demoTranslateSequence(): Promise<void> {
107
+ await demoTranslateSequenceUI();
108
+ }
101
109
 
102
- const pi = DG.TaskBarProgressIndicator.create('open oligoSdFile app');
103
- try {
104
- grok.shell.windows.showProperties = false;
105
- grok.shell.windows.showHelp = false;
110
+ //name: demoDesignPattern
111
+ //meta.demoPath: Bioinformatics | Oligonucleotide Sequence: Design
112
+ //description: Design a modification pattern for an oligonucleotide sequence
113
+ //meta.path:%20/apps/Tutorials/Demo/Bioinformatics/Oligonucleotide%20Sequence:%20Visualize%20duplex
114
+ export async function demoDesignPattern(): Promise<void> {
115
+ await demoDesignPatternUI();
116
+ }
106
117
 
107
- const app = new OligoSdFileApp();
108
- await app.init();
109
- } finally {
110
- pi.close();
111
- }
118
+ //name: demoVisualizeDuplex
119
+ //meta.demoPath: Bioinformatics | Oligonucleotide Sequence: Visualize duplex
120
+ //description: Visualize duplex and save SDF
121
+ //meta.path:%20/apps/Tutorials/Demo/Bioinformatics/Oligonucleotide%20Sequence:%20Visualize%20duplex
122
+ export async function demoVisualizeDuplex(): Promise<void> {
123
+ await demoVisualizeDuplexUI();
112
124
  }
@@ -0,0 +1,17 @@
1
+ export const axolabsToSmiles: {[key: string]: string} = {
2
+ 'usCfCfUfGfAf': 'CO[C@@H]1[C@H](OP(=O)(S)OC[C@H]2O[C@@H](n3ccc(N)nc3=O)[C@H](F)[C@@H]2OP(=O)(O)OC[C@H]2O[C@@H](n3ccc(N)nc3=O)[C@H](F)[C@@H]2OP(=O)(O)OC[C@H]2O[C@@H](n3ccc(=O)[nH]c3=O)[C@H](F)[C@@H]2OP(=O)(O)OC[C@H]2O[C@@H](n3cnc4c(=O)[nH]c(N)nc43)[C@H](F)[C@@H]2OP(=O)(O)OC[C@H]2O[C@@H](n3cnc4c(N)ncnc43)[C@H](F)[C@@H]2O)[C@@H](CO)O[C@H]1n1ccc(=O)[nH]c1=O',
3
+
4
+ 'usAfsusgsgsg': 'CO[C@@H]1[C@H](OP(=O)(S)OC[C@H]2O[C@@H](n3cnc4c(N)ncnc43)[C@H](F)[C@@H]2OP(=O)(S)OC[C@H]2O[C@@H](n3ccc(=O)[nH]c3=O)[C@H](OC)[C@@H]2OP(=O)(S)OC[C@H]2O[C@@H](n3cnc4c(=O)[nH]c(N)nc43)[C@H](OC)[C@@H]2OP(=O)(S)OC[C@H]2O[C@@H](n3cnc4c(=O)[nH]c(N)nc43)[C@H](OC)[C@@H]2OP(=O)(S)OC[C@H]2O[C@@H](n3cnc4c(=O)[nH]c(N)nc43)[C@H](OC)[C@@H]2O)[C@@H](CO)O[C@H]1n1ccc(=O)[nH]c1=O',
5
+
6
+ 'UfUfUfsCfsuacg': 'CO[C@@H]1[C@H](OP(=O)(O)OC[C@H]2O[C@@H](n3cnc4c(=O)[nH]c(N)nc43)[C@H](OC)[C@@H]2O)[C@@H](COP(=O)(O)O[C@@H]2[C@@H](COP(=O)(O)O[C@@H]3[C@@H](COP(=O)(S)O[C@@H]4[C@@H](COP(=O)(S)O[C@@H]5[C@@H](COP(=O)(O)O[C@@H]6[C@@H](COP(=O)(O)O[C@@H]7[C@@H](CO)O[C@@H](n8ccc(=O)[nH]c8=O)[C@@H]7F)O[C@@H](n7ccc(=O)[nH]c7=O)[C@@H]6F)O[C@@H](n6ccc(=O)[nH]c6=O)[C@@H]5F)O[C@@H](n5ccc(N)nc5=O)[C@@H]4F)O[C@@H](n4ccc(=O)[nH]c4=O)[C@@H]3OC)O[C@@H](n3cnc4c(N)ncnc43)[C@@H]2OC)O[C@H]1n1ccc(N)nc1=O',
7
+
8
+ 'susususauasu': 'CO[C@@H]1[C@H](O)[C@@H](COP(=O)(S)O[C@@H]2[C@@H](COP(=O)(O)O[C@@H]3[C@@H](COP(=O)(O)O[C@@H]4[C@@H](COP(=O)(S)O[C@@H]5[C@@H](COP(=O)(S)O[C@@H]6[C@@H](COP(=O)(S)O[C@@H]7[C@@H](COP(=O)(O)S)O[C@@H](n8ccc(=O)[nH]c8=O)[C@@H]7OC)O[C@@H](n7ccc(=O)[nH]c7=O)[C@@H]6OC)O[C@@H](n6ccc(=O)[nH]c6=O)[C@@H]5OC)O[C@@H](n5cnc6c(N)ncnc65)[C@@H]4OC)O[C@@H](n4ccc(=O)[nH]c4=O)[C@@H]3OC)O[C@@H](n3cnc4c(N)ncnc43)[C@@H]2OC)O[C@H]1n1ccc(=O)[nH]c1=O',
9
+
10
+ 'CfGfCfsGfsCf': 'Nc1ccn([C@@H]2O[C@H](COP(=O)(S)O[C@@H]3[C@@H](COP(=O)(S)O[C@@H]4[C@@H](COP(=O)(O)O[C@@H]5[C@@H](COP(=O)(O)O[C@@H]6[C@@H](CO)O[C@@H](n7ccc(N)nc7=O)[C@@H]6F)O[C@@H](n6cnc7c(=O)[nH]c(N)nc76)[C@@H]5F)O[C@@H](n5ccc(N)nc5=O)[C@@H]4F)O[C@@H](n4cnc5c(=O)[nH]c(N)nc54)[C@@H]3F)[C@@H](O)[C@H]2F)c(=O)n1',
11
+
12
+ 'acacacsacsac': 'CO[C@@H]1[C@H](O)[C@@H](COP(=O)(O)O[C@@H]2[C@@H](COP(=O)(S)O[C@@H]3[C@@H](COP(=O)(O)O[C@@H]4[C@@H](COP(=O)(S)O[C@@H]5[C@@H](COP(=O)(O)O[C@@H]6[C@@H](COP(=O)(O)O[C@@H]7[C@@H](COP(=O)(O)O[C@@H]8[C@@H](COP(=O)(O)O[C@@H]9[C@@H](COP(=O)(O)O[C@@H]%10[C@@H](CO)O[C@@H](n%11cnc%12c(N)ncnc%12%11)[C@@H]%10OC)O[C@@H](n%10ccc(N)nc%10=O)[C@@H]9OC)O[C@@H](n9cnc%10c(N)ncnc%109)[C@@H]8OC)O[C@@H](n8ccc(N)nc8=O)[C@@H]7OC)O[C@@H](n7cnc8c(N)ncnc87)[C@@H]6OC)O[C@@H](n6ccc(N)nc6=O)[C@@H]5OC)O[C@@H](n5cnc6c(N)ncnc65)[C@@H]4OC)O[C@@H](n4ccc(N)nc4=O)[C@@H]3OC)O[C@@H](n3cnc4c(N)ncnc43)[C@@H]2OC)O[C@H]1n1ccc(N)nc1=O',
13
+
14
+ 'cccgggusug': 'CO[C@@H]1[C@H](OP(=O)(O)OC[C@H]2O[C@@H](n3ccc(N)nc3=O)[C@H](OC)[C@@H]2OP(=O)(O)OC[C@H]2O[C@@H](n3ccc(N)nc3=O)[C@H](OC)[C@@H]2OP(=O)(O)OC[C@H]2O[C@@H](n3cnc4c(=O)[nH]c(N)nc43)[C@H](OC)[C@@H]2OP(=O)(O)OC[C@H]2O[C@@H](n3cnc4c(=O)[nH]c(N)nc43)[C@H](OC)[C@@H]2OP(=O)(O)OC[C@H]2O[C@@H](n3cnc4c(=O)[nH]c(N)nc43)[C@H](OC)[C@@H]2OP(=O)(O)OC[C@H]2O[C@@H](n3ccc(=O)[nH]c3=O)[C@H](OC)[C@@H]2OP(=O)(S)OC[C@H]2O[C@@H](n3ccc(=O)[nH]c3=O)[C@H](OC)[C@@H]2OP(=O)(O)OC[C@H]2O[C@@H](n3cnc4c(=O)[nH]c(N)nc43)[C@H](OC)[C@@H]2O)[C@@H](CO)O[C@H]1n1ccc(N)nc1=O',
15
+
16
+ 'UfAfCfGfGfCfAfUf': 'Nc1ccn([C@@H]2O[C@H](COP(=O)(O)O[C@@H]3[C@@H](COP(=O)(O)O[C@@H]4[C@@H](CO)O[C@@H](n5ccc(=O)[nH]c5=O)[C@@H]4F)O[C@@H](n4cnc5c(N)ncnc54)[C@@H]3F)[C@@H](OP(=O)(O)OC[C@H]3O[C@@H](n4cnc5c(=O)[nH]c(N)nc54)[C@H](F)[C@@H]3OP(=O)(O)OC[C@H]3O[C@@H](n4cnc5c(=O)[nH]c(N)nc54)[C@H](F)[C@@H]3OP(=O)(O)OC[C@H]3O[C@@H](n4ccc(N)nc4=O)[C@H](F)[C@@H]3OP(=O)(O)OC[C@H]3O[C@@H](n4cnc5c(N)ncnc54)[C@H](F)[C@@H]3OP(=O)(O)OC[C@H]3O[C@@H](n4ccc(=O)[nH]c4=O)[C@H](F)[C@@H]3O)[C@H]2F)c(=O)n1'
17
+ }