@datagrok/sequence-translator 1.0.16 → 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 (92) hide show
  1. package/.eslintrc.json +4 -3
  2. package/CHANGELOG.md +3 -0
  3. package/detectors.js +8 -28
  4. package/dist/package-test.js +2 -72987
  5. package/dist/package-test.js.map +1 -0
  6. package/dist/package.js +2 -72192
  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 -26
  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 → model/axolabs}/draw-svg.ts +1 -1
  21. package/src/{axolabs → 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/{structures-works → model/sequence-to-structure-utils}/mol-transformations.ts +61 -87
  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 +106 -77
  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.cmd +0 -14
  66. package/setup.sh +0 -37
  67. package/src/__jest__/remote.test.ts +0 -77
  68. package/src/__jest__/test-node.ts +0 -97
  69. package/src/apps/oligo-sd-file-app.ts +0 -58
  70. package/src/autostart/ICDs.ts +0 -3
  71. package/src/autostart/IDPs.ts +0 -3
  72. package/src/autostart/calculations.ts +0 -40
  73. package/src/autostart/constants.ts +0 -37
  74. package/src/autostart/registration.ts +0 -241
  75. package/src/autostart/salts.ts +0 -2
  76. package/src/autostart/sources.ts +0 -3
  77. package/src/autostart/users.ts +0 -3
  78. package/src/axolabs/constants.ts +0 -101
  79. package/src/axolabs/define-pattern.ts +0 -873
  80. package/src/helpers.ts +0 -28
  81. package/src/main/main-view.ts +0 -262
  82. package/src/structures-works/const.ts +0 -5
  83. package/src/structures-works/converters.ts +0 -323
  84. package/src/structures-works/from-monomers.ts +0 -267
  85. package/src/structures-works/map.ts +0 -720
  86. package/src/structures-works/save-sense-antisense.ts +0 -91
  87. package/src/structures-works/sequence-codes-tools.ts +0 -344
  88. package/src/utils/parse.ts +0 -27
  89. package/src/utils/sdf-add-columns.ts +0 -118
  90. package/src/utils/sdf-save-table.ts +0 -56
  91. package/test-SequenceTranslator-6288c2fbe346-cce4ac1d.html +0 -259
  92. 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
@@ -1,95 +1,124 @@
1
1
  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
- import {autostartOligoSdFileSubscription} from './autostart/registration';
5
- import {defineAxolabsPattern} from './axolabs/define-pattern';
6
- import {saveSenseAntiSense} from './structures-works/save-sense-antisense';
7
- import {mainView} from './main/main-view';
8
- import {IMonomerLib, MonomerWorks, readLibrary} from '@datagrok-libraries/bio';
9
- import {OligoSdFileApp} from './apps/oligo-sd-file-app';
10
4
 
11
- export const _package = new DG.Package();
12
-
13
- const LIB_PATH = 'System:AppData/SequenceTranslator';
14
-
15
- let monomerLib: IMonomerLib | null = null;
16
- export let monomerWorks: MonomerWorks | null = null;
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
+ }
17
25
 
18
- export function getMonomerWorks() {
19
- return monomerWorks;
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
+ }
20
42
  }
21
43
 
22
- export function getMonomerLib() {
23
- return monomerLib;
24
- }
44
+ export const _package: StPackage = new StPackage();
25
45
 
26
46
  //name: Sequence Translator
27
47
  //tags: app
28
- export async function sequenceTranslator(): Promise<void> {
29
- monomerLib = await readLibrary(LIB_PATH, 'helmLib.json');
30
-
31
- if (monomerWorks == null)
32
- monomerWorks = new MonomerWorks(monomerLib);
33
-
34
- const windows = grok.shell.windows;
35
- windows.showProperties = false;
36
- windows.showToolbox = false;
37
- windows.showHelp = false;
38
-
39
- let urlParams: URLSearchParams = new URLSearchParams(window.location.search);
40
- let mainSeq: string = 'fAmCmGmAmCpsmU';
41
- const v = grok.shell.newView('Sequence Translator', []);
42
- v.box = true;
43
- const tc = ui.tabControl({
44
- 'MAIN': await mainView((seq) => {
45
- mainSeq = seq;
46
- urlParams = new URLSearchParams();
47
- urlParams.set('seq', mainSeq);
48
- updatePath();
49
- }),
50
- 'AXOLABS': defineAxolabsPattern(),
51
- 'SDF': saveSenseAntiSense(),
52
- });
53
- tc.onTabChanged.subscribe((value) => {
54
- if (tc.currentPane.name != 'MAIN')
55
- urlParams.delete('seq');
56
- else
57
- urlParams.set('seq', mainSeq);
58
- updatePath();
59
- });
60
-
61
- function updatePath() {
62
- const urlParamsTxt: string = Object.entries(urlParams)
63
- .map(([key, value]) => `${key}=${encodeURIComponent(value)}`).join('&');
64
- v.path = '/apps/SequenceTranslator/SequenceTranslator' + `/${tc.currentPane.name}/?${urlParamsTxt}`;
65
- }
48
+ export async function sequenceTranslatorApp(): Promise<void> {
49
+ const pi: DG.TaskBarProgressIndicator = DG.TaskBarProgressIndicator.create('Loading Sequence Translator app ...');
66
50
 
67
- const pathParts: string[] = window.location.pathname.split('/');
68
- if (pathParts.length >= 5) {
69
- const tabName: string = pathParts[5];
70
- tc.currentPane = tc.getPane(tabName);
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();
71
61
  }
62
+ }
63
+
64
+ //name: initSequenceTranslatorLibData
65
+ export async function initSequenceTranslatorLibData(): Promise<void> {
66
+ await getJsonData();
67
+ await _package.initMonomerLib();
68
+ }
72
69
 
73
- v.append(tc);
74
- console.debug('SequenceTranslator: app sequenceTranslator() ' + `v.path='${v.path}', v.basePath='${v.basePath}'.`);
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
75
  }
76
76
 
77
- //tags: autostart
78
- autostartOligoSdFileSubscription();
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
+ }
79
85
 
80
- //name: oligoSdFileApp
81
- //description: Test/demo app for oligoSdFile
82
- export async function oligoSdFileApp() {
83
- console.debug('SequenceTranslator: package.ts oligoSdFileApp()');
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();
92
+ }
84
93
 
85
- const pi = DG.TaskBarProgressIndicator.create('open oligoSdFile app');
86
- try {
87
- grok.shell.windows.showProperties = false;
88
- grok.shell.windows.showHelp = false;
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
+ }
89
101
 
90
- const app = new OligoSdFileApp();
91
- await app.init();
92
- } finally {
93
- pi.close();
94
- }
95
- }
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
+ }
109
+
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
+ }
117
+
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();
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
+ }