@datagrok/sequence-translator 1.0.3 → 1.0.6

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/src/package.ts CHANGED
@@ -1,27 +1,13 @@
1
- /* Do not change these import lines. Datagrok will import API library in exactly the same manner */
2
1
  import * as grok from 'datagrok-api/grok';
3
2
  import * as ui from 'datagrok-api/ui';
4
3
  import * as DG from 'datagrok-api/dg';
5
- import * as OCL from 'openchemlib/full.js';
6
- import $ from 'cash-dom';
7
- import {defineAxolabsPattern} from './defineAxolabsPattern';
4
+ import {autostartOligoSdFileSubscription} from './autostart/registration';
5
+ import {defineAxolabsPattern} from './axolabs/define-pattern';
8
6
  import {saveSenseAntiSense} from './structures-works/save-sense-antisense';
9
- import {sequenceToSmiles, sequenceToMolV3000} from './structures-works/from-monomers';
10
- import {convertSequence, undefinedInputSequence, isValidSequence, getFormat} from
11
- './structures-works/sequence-codes-tools';
12
- import {map, COL_NAMES, MODIFICATIONS} from './structures-works/map';
13
- import {siRnaAxolabsToGcrs} from './structures-works/converters';
14
- import {SALTS_CSV} from './salts';
15
- import {USERS_CSV} from './users';
16
- import {ICDS} from './ICDs';
17
- import {SOURCES} from './sources';
18
- import {IDPS} from './IDPs';
7
+ import {mainView} from './main/main-view';
19
8
 
20
9
  export const _package = new DG.Package();
21
10
 
22
- const defaultInput = 'fAmCmGmAmCpsmU';
23
- const sequenceWasCopied = 'Copied';
24
- const tooltipSequence = 'Copy sequence';
25
11
 
26
12
  //name: Sequence Translator
27
13
  //tags: app
@@ -31,380 +17,15 @@ export function sequenceTranslator(): void {
31
17
  windows.showToolbox = false;
32
18
  windows.showHelp = false;
33
19
 
34
- function updateTableAndMolecule(sequence: string, inputFormat: string, isSet: boolean): void {
35
- moleculeSvgDiv.innerHTML = '';
36
- outputTableDiv.innerHTML = '';
37
- const pi = DG.TaskBarProgressIndicator.create('Rendering table and molecule...');
38
- let errorsExist = false;
39
- try {
40
- sequence = sequence.replace(/\s/g, '');
41
- const output = isValidSequence(sequence, null);
42
- if (isSet)
43
- output.synthesizer = [inputFormat];
44
- inputFormatChoiceInput.value = output.synthesizer![0];
45
- const outputSequenceObj = convertSequence(sequence, output);
46
- const tableRows = [];
47
-
48
- for (const key of Object.keys(outputSequenceObj).slice(1)) {
49
- const indexOfFirstNotValidChar = ('indexOfFirstNotValidChar' in outputSequenceObj) ?
50
- JSON.parse(outputSequenceObj.indexOfFirstNotValidChar!).indexOfFirstNotValidChar :
51
- -1;
52
- if ('indexOfFirstNotValidChar' in outputSequenceObj) {
53
- const indexOfFirstNotValidChar = ('indexOfFirstNotValidChar' in outputSequenceObj) ?
54
- JSON.parse(outputSequenceObj.indexOfFirstNotValidChar!).indexOfFirstNotValidChar :
55
- -1;
56
- if (indexOfFirstNotValidChar != -1)
57
- errorsExist = true;
58
- }
59
-
60
- tableRows.push({
61
- 'key': key,
62
- 'value': ('indexOfFirstNotValidChar' in outputSequenceObj) ?
63
- ui.divH([
64
- ui.divText(sequence.slice(0, indexOfFirstNotValidChar), {style: {color: 'grey'}}),
65
- ui.tooltip.bind(
66
- ui.divText(sequence.slice(indexOfFirstNotValidChar), {style: {color: 'red'}}),
67
- 'Expected format: ' + JSON.parse(outputSequenceObj.indexOfFirstNotValidChar!).synthesizer +
68
- '. See tables with valid codes on the right',
69
- ),
70
- ]) : //@ts-ignore
71
- ui.link(outputSequenceObj[key], () => navigator.clipboard.writeText(outputSequenceObj[key])
72
- .then(() => grok.shell.info(sequenceWasCopied)), tooltipSequence, ''),
73
- });
74
- }
75
-
76
- if (errorsExist) {
77
- const synthesizer = JSON.parse(outputSequenceObj.indexOfFirstNotValidChar!).synthesizer.slice(0, -6);
78
- asoGapmersGrid.onCellPrepare(function(gc) {
79
- gc.style.backColor = (gc.gridColumn.name == synthesizer) ? 0xFFF00000 : 0xFFFFFFFF;
80
- });
81
- omeAndFluoroGrid.onCellPrepare(function(gc) {
82
- gc.style.backColor = (gc.gridColumn.name == synthesizer) ? 0xFFF00000 : 0xFFFFFFFF;
83
- });
84
- switchInput.enabled = true;
85
- } else {
86
- asoGapmersGrid.onCellPrepare(function(gc) {gc.style.backColor = 0xFFFFFFFF;});
87
- omeAndFluoroGrid.onCellPrepare(function(gc) {gc.style.backColor = 0xFFFFFFFF;});
88
- }
89
-
90
- outputTableDiv.append(
91
- ui.div([
92
- DG.HtmlTable.create(tableRows, (item: { key: string; value: string; }) =>
93
- [item.key, item.value], ['Code', 'Sequence']).root,
94
- ]),
95
- );
96
-
97
- if (outputSequenceObj.type != undefinedInputSequence && outputSequenceObj.Error != undefinedInputSequence) {
98
- const canvas = ui.canvas(300, 170);
99
- canvas.addEventListener('click', () => {
100
- const canv = ui.canvas($(window).width(), $(window).height());
101
- const mol = sequenceToMolV3000(inputSequenceField.value.replace(/\s/g, ''), false, true,
102
- output.synthesizer![0]);
103
- // @ts-ignore
104
- OCL.StructureView.drawMolecule(canv, OCL.Molecule.fromMolfile(mol), {suppressChiralText: true});
105
- ui.dialog('Molecule: ' + inputSequenceField.value)
106
- .add(canv)
107
- .showModal(true);
108
- });
109
- $(canvas).on('mouseover', () => $(canvas).css('cursor', 'zoom-in'));
110
- $(canvas).on('mouseout', () => $(canvas).css('cursor', 'default'));
111
- const mol = sequenceToMolV3000(inputSequenceField.value.replace(/\s/g, ''), false, true,
112
- output.synthesizer![0]);
113
- // @ts-ignore
114
- OCL.StructureView.drawMolecule(canvas, OCL.Molecule.fromMolfile(mol), {suppressChiralText: true});
115
- moleculeSvgDiv.append(canvas);
116
- } else
117
- moleculeSvgDiv.innerHTML = '';
118
- } finally {
119
- pi.close();
120
- }
121
- }
122
-
123
- const inputFormatChoiceInput = ui.choiceInput(
124
- 'Input format: ', 'Janssen GCRS Codes', Object.keys(map), (format: string) => {
125
- updateTableAndMolecule(inputSequenceField.value.replace(/\s/g, ''), format, true);
126
- });
127
- const moleculeSvgDiv = ui.block([]);
128
- const outputTableDiv = ui.div([]);
129
- const inputSequenceField = ui.textInput('', defaultInput, (sequence: string) => updateTableAndMolecule(sequence,
130
- inputFormatChoiceInput.value!, false));
131
-
132
- const asoDf = DG.DataFrame.fromObjects([
133
- {'Name': '2\'MOE-5Me-rU', 'BioSpring': '5', 'Janssen GCRS': 'moeT'},
134
- {'Name': '2\'MOE-rA', 'BioSpring': '6', 'Janssen GCRS': 'moeA'},
135
- {'Name': '2\'MOE-5Me-rC', 'BioSpring': '7', 'Janssen GCRS': 'moe5mC'},
136
- {'Name': '2\'MOE-rG', 'BioSpring': '8', 'Janssen GCRS': 'moeG'},
137
- {'Name': '5-Methyl-dC', 'BioSpring': '9', 'Janssen GCRS': '5mC'},
138
- {'Name': 'ps linkage', 'BioSpring': '*', 'Janssen GCRS': 'ps'},
139
- {'Name': 'dA', 'BioSpring': 'A', 'Janssen GCRS': 'A, dA'},
140
- {'Name': 'dC', 'BioSpring': 'C', 'Janssen GCRS': 'C, dC'},
141
- {'Name': 'dG', 'BioSpring': 'G', 'Janssen GCRS': 'G, dG'},
142
- {'Name': 'dT', 'BioSpring': 'T', 'Janssen GCRS': 'T, dT'},
143
- {'Name': 'rA', 'BioSpring': '', 'Janssen GCRS': 'rA'},
144
- {'Name': 'rC', 'BioSpring': '', 'Janssen GCRS': 'rC'},
145
- {'Name': 'rG', 'BioSpring': '', 'Janssen GCRS': 'rG'},
146
- {'Name': 'rU', 'BioSpring': '', 'Janssen GCRS': 'rU'},
147
- ])!;
148
- const asoGapmersGrid = DG.Viewer.grid(asoDf, {showRowHeader: false, showCellTooltip: false});
149
-
150
- asoDf.onCurrentCellChanged.subscribe((_) => {
151
- navigator.clipboard.writeText(asoDf.currentCell.value).then(() => grok.shell.info('Copied'));
152
- });
153
-
154
- const omeAndFluoroGrid = DG.Viewer.grid(
155
- DG.DataFrame.fromObjects([
156
- {'Name': '2\'-fluoro-U', 'BioSpring': '1', 'Axolabs': 'Uf', 'Janssen GCRS': 'fU'},
157
- {'Name': '2\'-fluoro-A', 'BioSpring': '2', 'Axolabs': 'Af', 'Janssen GCRS': 'fA'},
158
- {'Name': '2\'-fluoro-C', 'BioSpring': '3', 'Axolabs': 'Cf', 'Janssen GCRS': 'fC'},
159
- {'Name': '2\'-fluoro-G', 'BioSpring': '4', 'Axolabs': 'Gf', 'Janssen GCRS': 'fG'},
160
- {'Name': '2\'OMe-rU', 'BioSpring': '5', 'Axolabs': 'u', 'Janssen GCRS': 'mU'},
161
- {'Name': '2\'OMe-rA', 'BioSpring': '6', 'Axolabs': 'a', 'Janssen GCRS': 'mA'},
162
- {'Name': '2\'OMe-rC', 'BioSpring': '7', 'Axolabs': 'c', 'Janssen GCRS': 'mC'},
163
- {'Name': '2\'OMe-rG', 'BioSpring': '8', 'Axolabs': 'g', 'Janssen GCRS': 'mG'},
164
- {'Name': 'ps linkage', 'BioSpring': '*', 'Axolabs': 's', 'Janssen GCRS': 'ps'},
165
- ])!, {showRowHeader: false, showCellTooltip: false},
166
- );
167
-
168
- const overhangModificationsGrid = DG.Viewer.grid(
169
- DG.DataFrame.fromColumns([
170
- DG.Column.fromStrings('Name', Object.keys(MODIFICATIONS)),
171
- ])!, {showRowHeader: false, showCellTooltip: false},
172
- );
173
- updateTableAndMolecule(defaultInput, inputFormatChoiceInput.value!, true);
174
-
175
- const appMainDescription = ui.info([
176
- ui.divText('How to convert one sequence:', {style: {'font-weight': 'bolder'}}),
177
- ui.divText('Paste sequence into the text field below'),
178
- ui.divText('\n How to convert many sequences:', {style: {'font-weight': 'bolder'}}),
179
- ui.divText('1. Drag & drop an Excel or CSV file with sequences into Datagrok'),
180
- ui.divText('2. Right-click on the column header, then see the \'Convert\' menu'),
181
- ui.divText('This will add the result column to the right of the table'),
182
- ], 'Convert oligonucleotide sequences between Nucleotides, BioSpring, Axolabs, Mermade 12 and GCRS representations.');
183
-
184
- const codesTablesDiv = ui.splitV([
185
- ui.box(ui.h2('ASO Gapmers'), {style: {maxHeight: '40px'}}),
186
- asoGapmersGrid.root,
187
- ui.box(ui.h2('2\'-OMe and 2\'-F siRNA'), {style: {maxHeight: '40px'}}),
188
- omeAndFluoroGrid.root,
189
- ui.box(ui.h2('Overhang modifications'), {style: {maxHeight: '40px'}}),
190
- overhangModificationsGrid.root,
191
- ], {style: {maxWidth: '350px'}});
192
-
193
- const tabControl = ui.tabControl({
194
- 'MAIN': ui.box(
195
- ui.splitH([
196
- ui.splitV([
197
- ui.panel([
198
- appMainDescription,
199
- ui.div([
200
- ui.h1('Input sequence'),
201
- ui.div([
202
- inputSequenceField.root,
203
- ], 'input-base'),
204
- ], 'inputSequence'),
205
- ui.div([inputFormatChoiceInput], {style: {padding: '5px 0'}}),
206
- ui.block([
207
- ui.h1('Output'),
208
- outputTableDiv,
209
- ]),
210
- moleculeSvgDiv,
211
- ], 'sequence'),
212
- ]),
213
- codesTablesDiv,
214
- ], {style: {height: '100%', width: '100%'}}),
215
- ),
216
- 'AXOLABS': defineAxolabsPattern(),
217
- 'SDF': saveSenseAntiSense(),
218
- });
219
-
220
- $(codesTablesDiv).hide();
221
-
222
- const v = grok.shell.newView('Sequence Translator', [tabControl]);
20
+ const v = grok.shell.newView('Sequence Translator', [
21
+ ui.tabControl({
22
+ 'MAIN': mainView(),
23
+ 'AXOLABS': defineAxolabsPattern(),
24
+ 'SDF': saveSenseAntiSense(),
25
+ }),
26
+ ]);
223
27
  v.box = true;
224
-
225
- const switchInput = ui.switchInput('Codes', false, (v: boolean) => (v) ?
226
- $(codesTablesDiv).show() :
227
- $(codesTablesDiv).hide(),
228
- );
229
-
230
- const topPanel = [
231
- ui.iconFA('download', () => {
232
- const result = sequenceToMolV3000(inputSequenceField.value.replace(/\s/g, ''), false, false,
233
- inputFormatChoiceInput.value!);
234
- const element = document.createElement('a');
235
- element.setAttribute('href', 'data:text/plain;charset=utf-8,' + encodeURIComponent(result));
236
- element.setAttribute('download', inputSequenceField.value.replace(/\s/g, '') + '.mol');
237
- element.click();
238
- }, 'Save .mol file'),
239
- ui.iconFA('copy', () => {
240
- navigator.clipboard.writeText(
241
- sequenceToSmiles(inputSequenceField.value.replace(/\s/g, ''), false, inputFormatChoiceInput.value!))
242
- .then(() => grok.shell.info(sequenceWasCopied));
243
- }, 'Copy SMILES'),
244
- switchInput.root,
245
- ];
246
-
247
- tabControl.onTabChanged.subscribe((_) =>
248
- v.setRibbonPanels([(tabControl.currentPane.name == 'MAIN') ? topPanel : []]));
249
- v.setRibbonPanels([topPanel]);
250
- }
251
-
252
- async function saveTableAsSdFile(table: DG.DataFrame) {
253
- if (!table.columns.contains('Compound Name')) {
254
- grok.shell.warning(
255
- 'File saved without columns \'' +
256
- [COL_NAMES.COMPOUND_NAME, COL_NAMES.COMPOUND_COMMENTS, COL_NAMES.CPD_MW,
257
- COL_NAMES.SALT_MASS, COL_NAMES.BATCH_MW].join('\', \''),
258
- );
259
- }
260
- const structureColumn = table.col(COL_NAMES.SEQUENCE)!;
261
- const typeColumn = table.col(COL_NAMES.TYPE)!;
262
- let result = '';
263
- for (let i = 0; i < table.rowCount; i++) {
264
- const format = getFormat(structureColumn.get(i));
265
- result += (typeColumn.get(i) == 'SS') ?
266
- sequenceToMolV3000(structureColumn.get(i), false, true, format!) + '\n' + `> <Sequence>\nSense Strand\n\n` :
267
- sequenceToMolV3000(structureColumn.get(i), true, true, format!) + '\n' + `> <Sequence>\nAnti Sense\n\n`;
268
- for (const col of table.columns) {
269
- if (col.name != COL_NAMES.SEQUENCE)
270
- result += `> <${col.name}>\n${col.get(i)}\n\n`;
271
- }
272
- result += '$$$$\n\n';
273
- }
274
- const element = document.createElement('a');
275
- element.setAttribute('href', 'data:text/plain;charset=utf-8,' + encodeURIComponent(result));
276
- element.setAttribute('download', table.name + '.sdf');
277
- element.click();
278
- }
279
-
280
- const weightsObj: {[code: string]: number} = {};
281
- for (const synthesizer of Object.keys(map)) {
282
- for (const technology of Object.keys(map[synthesizer])) {
283
- for (const code of Object.keys(map[synthesizer][technology]))
284
- weightsObj[code] ?? map[synthesizer][technology][code].weight;
285
- }
286
- }
287
- for (const [key, value] of Object.entries(MODIFICATIONS))
288
- weightsObj[key] = value.molecularWeight;
289
-
290
- function sortByStringLengthInDescendingOrder(array: string[]): string[] {
291
- return array.sort(function(a, b) {return b.length - a.length;});
292
- }
293
-
294
- function stringifyItems(items: string[]): string {
295
- return '["' + items.join('", "') + '"]';
296
- }
297
-
298
- function molecularWeight(sequence: string, weightsObj: {[index: string]: number}): number {
299
- const codes = sortByStringLengthInDescendingOrder(Object.keys(weightsObj)).concat(Object.keys(MODIFICATIONS));
300
- let weight = 0;
301
- let i = 0;
302
- while (i < sequence.length) {
303
- const matchedCode = codes.find((s) => s == sequence.slice(i, i + s.length))!;
304
- weight += weightsObj[sequence.slice(i, i + matchedCode.length)];
305
- i += matchedCode!.length;
306
- }
307
- return weight - 61.97;
308
28
  }
309
29
 
310
30
  //tags: autostart
311
- export function autostartOligoSdFileSubscription() {
312
- let alreadyAdded = false;
313
- grok.events.onViewAdded.subscribe((v: any) => {
314
- if (v.type == 'TableView') {
315
- if (v.dataFrame.columns.contains(COL_NAMES.TYPE))
316
- oligoSdFile(v.dataFrame);
317
- grok.events.onContextMenu.subscribe((args) => {
318
- for (const col of v.dataFrame.columns) {
319
- if (!alreadyAdded && DG.Detector.sampleCategories(col, (s) => /^[fsACGUacgu]{6,}$/.test(s))) {
320
- alreadyAdded = true;
321
- args.args.menu.item('Convert to GCRS', () => {
322
- const seqCol = args.args.context.table.currentCol;
323
- args.args.context.table.columns.addNewString(seqCol.name + ' to GCRS').init((i: number) => {
324
- return siRnaAxolabsToGcrs(seqCol.get(i));
325
- });
326
- });
327
- break;
328
- };
329
- }
330
- });
331
- }
332
- });
333
- }
334
-
335
- export function oligoSdFile(table: DG.DataFrame) {
336
- const saltsDf = DG.DataFrame.fromCsv(SALTS_CSV);
337
- const usersDf = DG.DataFrame.fromCsv(USERS_CSV);
338
- const sourcesDf = DG.DataFrame.fromCsv(SOURCES);
339
- const icdsDf = DG.DataFrame.fromCsv(ICDS);
340
- const idpsDf = DG.DataFrame.fromCsv(IDPS);
341
-
342
- function addColumns(t: DG.DataFrame, saltsDf: DG.DataFrame) {
343
- if (t.columns.contains(COL_NAMES.COMPOUND_NAME))
344
- return grok.shell.error('Columns already exist!');
345
-
346
- const sequence = t.col(COL_NAMES.SEQUENCE)!;
347
- const salt = t.col(COL_NAMES.SALT)!;
348
- const equivalents = t.col(COL_NAMES.EQUIVALENTS)!;
349
-
350
- t.columns.addNewString(COL_NAMES.COMPOUND_NAME).init((i: number) => sequence.get(i));
351
- t.columns.addNewString(COL_NAMES.COMPOUND_COMMENTS).init((i: number) => (i > 0 && i % 2 == 0) ?
352
- sequence.getString(i) + '; duplex of SS: ' + sequence.getString(i - 2) + ' and AS: ' + sequence.getString(i - 1) :
353
- sequence.getString(i),
354
- );
355
- const molWeightCol = saltsDf.col('MOLWEIGHT')!;
356
- const saltNamesList = saltsDf.col('DISPLAY')!.toList();
357
- t.columns.addNewFloat(COL_NAMES.CPD_MW)
358
- .init((i: number) => molecularWeight(sequence.get(i), weightsObj));
359
- t.columns.addNewFloat(COL_NAMES.SALT_MASS).init((i: number) => {
360
- const saltRowIndex = saltNamesList.indexOf(salt.get(i));
361
- const mw = molWeightCol.get(saltRowIndex);
362
- return mw * equivalents.get(i);
363
- });
364
- t.columns.addNewCalculated(COL_NAMES.BATCH_MW,
365
- '${' + COL_NAMES.CPD_MW + '} + ${' + COL_NAMES.SALT_MASS + '}', DG.COLUMN_TYPE.FLOAT, false,
366
- );
367
-
368
- addColumnsPressed = true;
369
- return newDf = t;
370
- }
371
-
372
- let newDf: DG.DataFrame;
373
- let addColumnsPressed = false;
374
-
375
- const d = ui.div([
376
- ui.icons.edit(() => {
377
- d.innerHTML = '';
378
- if (table.col(COL_NAMES.IDP)!.type != DG.COLUMN_TYPE.STRING)
379
- table.changeColumnType(COL_NAMES.IDP, DG.COLUMN_TYPE.STRING);
380
- d.append(
381
- ui.link('Add Columns', () => {
382
- addColumns(table, saltsDf);
383
- grok.shell.tableView(table.name).grid.columns.setOrder(Object.values(COL_NAMES));
384
- }, 'Add columns: \'' + [COL_NAMES.COMPOUND_NAME, COL_NAMES.COMPOUND_COMMENTS, COL_NAMES.CPD_MW,
385
- COL_NAMES.SALT_MASS, COL_NAMES.BATCH_MW].join('\', \''), ''),
386
- ui.button('Save SD file', () => saveTableAsSdFile(addColumnsPressed ? newDf : table)),
387
- );
388
-
389
- const view = grok.shell.getTableView(table.name);
390
-
391
- view.table!.col(COL_NAMES.TYPE)!.setTag(DG.TAGS.CHOICES, '["AS", "SS", "Duplex"]');
392
- view.table!.col(COL_NAMES.OWNER)!.setTag(DG.TAGS.CHOICES, stringifyItems(usersDf.columns.byIndex(0).toList()));
393
- view.table!.col(COL_NAMES.SALT)!.setTag(DG.TAGS.CHOICES, stringifyItems(saltsDf.columns.byIndex(0).toList()));
394
- view.table!.col(COL_NAMES.SOURCE)!.setTag(DG.TAGS.CHOICES, stringifyItems(sourcesDf.columns.byIndex(0).toList()));
395
- view.table!.col(COL_NAMES.ICD)!.setTag(DG.TAGS.CHOICES, stringifyItems(icdsDf.columns.byIndex(0).toList()));
396
- view.table!.col(COL_NAMES.IDP)!.setTag(DG.TAGS.CHOICES, stringifyItems(idpsDf.columns.byIndex(0).toList()));
397
-
398
- grok.events.onContextMenu.subscribe((args) => {
399
- if ([COL_NAMES.TYPE, COL_NAMES.OWNER, COL_NAMES.SALT, COL_NAMES.SOURCE, COL_NAMES.ICD, COL_NAMES.IDP]
400
- .includes(args.args.context.table.currentCol.name)) {
401
- args.args.menu.item('Fill Column With Value', () => {
402
- const v = args.args.context.table.currentCell.value;
403
- args.args.context.table.currentCell.column.init(v);
404
- });
405
- }
406
- });
407
- }),
408
- ]);
409
- grok.shell.v.setRibbonPanels([[d]]);
410
- }
31
+ autostartOligoSdFileSubscription();
@@ -15,7 +15,10 @@ export function gcrsToLcms(sequence: string): string {
15
15
  arr1[i] = arr1[i].replace(')', '\\)');
16
16
  }
17
17
  const regExp = new RegExp('(' + arr1.join('|') + ')', 'g');
18
- return sequence.replace(regExp, function(code) {return obj[code];});
18
+ let r1 = sequence.replace(regExp, function(code) {return obj[code];});
19
+ while (r1.indexOf('//') != -1)
20
+ r1 = r1.replace('//', '/');
21
+ return r1;
19
22
  }
20
23
 
21
24
  //name: asoGapmersNucleotidesToBioSpring
@@ -1,4 +1,4 @@
1
- import {map, stadardPhosphateLinkSmiles, SYNTHESIZERS, TECHNOLOGIES, MODIFICATIONS} from './map';
1
+ import {map, stadardPhosphateLinkSmiles, SYNTHESIZERS, TECHNOLOGIES, MODIFICATIONS, delimiter} from './map';
2
2
  import {isValidSequence} from './sequence-codes-tools';
3
3
  import {getNucleotidesMol} from './mol-transformations';
4
4
 
@@ -12,7 +12,7 @@ export function sequenceToMolV3000(sequence: string, inverted: boolean = false,
12
12
  const links = ['s', 'ps', '*'];
13
13
  const includesStandardLinkAlready = ['e', 'h', /*'g',*/ 'f', 'i', 'l', 'k', 'j'];
14
14
  const dropdowns = Object.keys(MODIFICATIONS);
15
- codes = codes.concat(dropdowns);
15
+ codes = codes.concat(dropdowns).concat(delimiter);
16
16
  while (i < sequence.length) {
17
17
  const code = codes.find((s: string) => s == sequence.slice(i, i + s.length))!;
18
18
  i += code.length;
@@ -36,7 +36,7 @@ export function sequenceToMolV3000(sequence: string, inverted: boolean = false,
36
36
  }
37
37
  }
38
38
 
39
- return getNucleotidesMol(smilesCodes, oclRender);
39
+ return getNucleotidesMol(smilesCodes);
40
40
  }
41
41
 
42
42
  export function sequenceToSmiles(sequence: string, inverted: boolean = false, format: string): string {
@@ -48,7 +48,7 @@ export function sequenceToSmiles(sequence: string, inverted: boolean = false, fo
48
48
  const links = ['s', 'ps', '*'];
49
49
  const includesStandardLinkAlready = ['e', 'h', /*'g',*/ 'f', 'i', 'l', 'k', 'j'];
50
50
  const dropdowns = Object.keys(MODIFICATIONS);
51
- codes = codes.concat(dropdowns);
51
+ codes = codes.concat(dropdowns).concat(delimiter);
52
52
  while (i < sequence.length) {
53
53
  const code = codes.find((s: string) => s == sequence.slice(i, i + s.length))!;
54
54
  i += code.length;
@@ -98,6 +98,7 @@ function getObjectWithCodesAndSmiles(sequence: string, format: string) {
98
98
  obj[code] = map[format][technology][code].SMILES;
99
99
  }
100
100
  }
101
+ obj[delimiter] = '';
101
102
  // TODO: create object based from synthesizer type to avoid key(codes) duplicates
102
103
  const output = isValidSequence(sequence, format);
103
104
  if (output.synthesizer!.includes(SYNTHESIZERS.MERMADE_12))
@@ -1,6 +1,7 @@
1
1
  import * as DG from 'datagrok-api/dg';
2
2
  import {getAllCodesOfSynthesizer} from './sequence-codes-tools';
3
3
 
4
+ export const delimiter = ';';
4
5
  export const SYNTHESIZERS = {
5
6
  RAW_NUCLEOTIDES: 'Raw Nucleotides',
6
7
  BIOSPRING: 'BioSpring Codes',
@@ -339,6 +340,12 @@ export const map: {[synthesizer: string]:
339
340
  'normalized': '',
340
341
  'SMILES': 'OP(=O)(S)O',
341
342
  },
343
+ 's': {
344
+ 'name': 'ps linkage',
345
+ 'weight': 16.07,
346
+ 'normalized': '',
347
+ 'SMILES': 'OP(=O)(S)O',
348
+ },
342
349
  'A': {
343
350
  'name': 'Adenine',
344
351
  'weight': 313.21,
@@ -1,5 +1,3 @@
1
- import * as OCL from 'openchemlib/full.js';
2
-
3
1
  const PHOSHATE = `
4
2
  Datagrok monomer library Nucleotides
5
3
 
@@ -80,7 +78,7 @@ M V30 END COLLECTION
80
78
  M V30 END CTAB
81
79
  M END`;
82
80
 
83
- export function getNucleotidesMol(smilesCodes: string[], oclRender: boolean = false) {
81
+ export function getNucleotidesMol(smilesCodes: string[]) {
84
82
  const molBlocks: string[] = [];
85
83
 
86
84
  for (let i = 0; i < smilesCodes.length - 1; i++) {
@@ -90,10 +88,10 @@ export function getNucleotidesMol(smilesCodes: string[], oclRender: boolean = fa
90
88
  molBlocks.push(rotateNucleotidesV3000(smilesCodes[i]));
91
89
  }
92
90
 
93
- return linkV3000(molBlocks, false, oclRender);
91
+ return linkV3000(molBlocks, false);
94
92
  }
95
93
 
96
- export function linkV3000(molBlocks: string[], twoChains: boolean = false, oclRender: boolean = false) {
94
+ export function linkV3000(molBlocks: string[], twoChains: boolean = false, useChirality: boolean = true) {
97
95
  let macroMolBlock = '\nDatagrok macromolecule handler\n\n';
98
96
  macroMolBlock += ' 0 0 0 0 0 0 999 V3000\n';
99
97
  macroMolBlock += 'M V30 BEGIN CTAB\n';
@@ -215,14 +213,14 @@ export function linkV3000(molBlocks: string[], twoChains: boolean = false, oclRe
215
213
  const entries = 4;
216
214
  const collNumber = Math.ceil(collection.length / entries);
217
215
 
218
- if (oclRender) {
219
- collectionBlock += 'M V30 MDLV30/STEABS ATOMS=(' + collection.length;
216
+ //if (oclRender) {
217
+ // collectionBlock += 'M V30 MDLV30/STEABS ATOMS=(' + collection.length;
220
218
 
221
- for (let j = 0; j < collection.length; j++)
222
- collectionBlock += ' ' + collection[j];
219
+ // for (let j = 0; j < collection.length; j++)
220
+ // collectionBlock += ' ' + collection[j];
223
221
 
224
- collectionBlock += ')\n';
225
- } else {
222
+ // collectionBlock += ')\n';
223
+ //} else {
226
224
  collectionBlock += 'M V30 MDLV30/STEABS ATOMS=(' + collection.length + ' -\n';
227
225
  for (let i = 0; i < collNumber; i++) {
228
226
  collectionBlock += 'M V30 ';
@@ -233,7 +231,7 @@ export function linkV3000(molBlocks: string[], twoChains: boolean = false, oclRe
233
231
  collection[entries*i + j] + ' ';
234
232
  }
235
233
  }
236
- }
234
+ //}
237
235
 
238
236
  //generate file
239
237
  twoChains? natom : natom++;
@@ -244,9 +242,13 @@ export function linkV3000(molBlocks: string[], twoChains: boolean = false, oclRe
244
242
  macroMolBlock += 'M V30 BEGIN BOND\n';
245
243
  macroMolBlock += bondBlock;
246
244
  macroMolBlock += 'M V30 END BOND\n';
247
- macroMolBlock += 'M V30 BEGIN COLLECTION\n';
248
- macroMolBlock += collectionBlock;
249
- macroMolBlock += 'M V30 END COLLECTION\n';
245
+ if(useChirality){
246
+ macroMolBlock += 'M V30 BEGIN COLLECTION\n';
247
+ macroMolBlock += collectionBlock;
248
+ macroMolBlock += 'M V30 END COLLECTION\n';
249
+ } else
250
+ macroMolBlock = macroMolBlock.replace(/ CFG=\d/g, ' ');
251
+
250
252
  macroMolBlock += 'M V30 END CTAB\n';
251
253
  macroMolBlock += 'M END\n';
252
254
 
@@ -254,6 +256,7 @@ export function linkV3000(molBlocks: string[], twoChains: boolean = false, oclRe
254
256
  }
255
257
 
256
258
  function rotateNucleotidesV3000(molecule: string) {
259
+ // @ts-ignore
257
260
  let molBlock = molecule.includes('M END') ? molecule : OCL.Molecule.fromSmiles(molecule).toMolfileV3();
258
261
  const coordinates = extractAtomDataV3000(molBlock);
259
262
  const natom = coordinates.atomIndex.length;
@@ -320,6 +323,7 @@ function rotateNucleotidesV3000(molecule: string) {
320
323
  }
321
324
 
322
325
  function invertNucleotidesV3000(molecule: string) {
326
+ // @ts-ignore
323
327
  let molBlock = molecule.includes('M END') ? molecule : OCL.Molecule.fromSmiles(molecule).toMolfileV3();
324
328
  const coordinates = extractAtomDataV3000(molBlock);
325
329
  const natom = coordinates.atomIndex.length;
@@ -3,14 +3,14 @@ import {sequenceToMolV3000} from '../structures-works/from-monomers';
3
3
  import {linkV3000} from '../structures-works/mol-transformations';
4
4
  import {getFormat} from '../structures-works/sequence-codes-tools';
5
5
 
6
- export function saveSdf(as: string, ss: string, oneEntity: boolean, fit3dx: boolean) {
6
+ export function saveSdf(as: string, ss: string, oneEntity: boolean, useChirality: boolean, invertSS: boolean, invertAS: boolean) {
7
7
  const formatAs = getFormat(as);
8
8
  const formatSs = getFormat(ss);
9
- const molSS = sequenceToMolV3000(ss, false, false, formatSs!);
10
- const molAS = sequenceToMolV3000(as, true, false, formatAs!);
9
+ const molSS = sequenceToMolV3000(ss, invertSS, false, formatSs!);
10
+ const molAS = sequenceToMolV3000(as, invertAS, false, formatAs!);
11
11
  let result: string;
12
12
  if (oneEntity)
13
- result = linkV3000([molSS, molAS], true, !fit3dx) + '\n\n$$$$\n';
13
+ result = linkV3000([molSS, molAS], true, useChirality) + '\n\n$$$$\n';
14
14
  else {
15
15
  result =
16
16
  molSS + '\n' +
@@ -27,22 +27,35 @@ export function saveSdf(as: string, ss: string, oneEntity: boolean, fit3dx: bool
27
27
 
28
28
  export function saveSenseAntiSense() {
29
29
  const moleculeSvgDiv = ui.block([]);
30
- const ssInput = ui.textInput('Sense Strand 5\' ->3\'', '');
31
- const asInput = ui.textInput('Anti Sense 3\' ->5\'', '');
30
+ const ssInput = ui.textInput('Sense Strand', '');
31
+ const asInput = ui.textInput('Anti Sense', '');
32
+ const straight = '5\' ->3\'';
33
+ const inverse = '3\' ->5\'';
34
+ let ssInverse = false;
35
+ let asInverse = false;
36
+
37
+ const changeSense = ui.choiceInput('SS direction', straight, [straight, inverse]);
38
+ changeSense.onChanged(() => {ssInverse = changeSense.value == inverse;});
39
+ const changeAntiSense = ui.choiceInput('AS direction', straight, [straight, inverse]);
40
+ changeAntiSense.onChanged(() => {asInverse = changeAntiSense.value == inverse;});
41
+
32
42
  const saveOption = ui.switchInput('Save as one entity', true);
33
- const save3dx = ui.switchInput('Save 3dx', true);
43
+ const chirality = ui.switchInput('Use chiral', true);
34
44
  const saveBtn = ui.button('Save SDF', () =>
35
- saveSdf(asInput.value, ssInput.value, saveOption.value, save3dx.value));
45
+ saveSdf(asInput.value, ssInput.value, saveOption.value, chirality.value, ssInverse, asInverse));
36
46
 
37
47
  const saveSection = ui.panel([
38
48
  ui.div([
39
49
  ui.div([
40
50
  ui.divH([ui.h1('Inputs')]),
41
51
  ui.divV([
42
- ui.div([ssInput.root]),
43
- ui.div([asInput.root]),
52
+ ssInput,
53
+ asInput,
54
+ ui.div([changeSense], {style: {width: '40'}}),
55
+ changeSense,
56
+ changeAntiSense,
44
57
  saveOption,
45
- save3dx,
58
+ chirality,
46
59
  ui.buttonsInput([saveBtn]),
47
60
  ], 'ui-form'),
48
61
  ], 'ui-form'),