@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/detectors.js +26 -0
- package/dist/package-test.js +79 -67
- package/dist/package.js +768 -631
- package/package.json +28 -19
- package/src/__jest__/remote.test.ts +35 -16
- package/src/__jest__/test-node.ts +3 -2
- package/src/autostart/registration.ts +206 -0
- package/src/{axolabsMap.ts → axolabs/constants.ts} +1 -1
- package/src/{defineAxolabsPattern.ts → axolabs/define-pattern.ts} +87 -62
- package/src/{drawAxolabsPattern.ts → axolabs/draw-svg.ts} +1 -1
- package/src/main/main-view.ts +225 -0
- package/src/package.ts +11 -390
- package/src/structures-works/converters.ts +4 -1
- package/src/structures-works/from-monomers.ts +5 -4
- package/src/structures-works/map.ts +7 -0
- package/src/structures-works/mol-transformations.ts +19 -15
- package/src/structures-works/save-sense-antisense.ts +24 -11
- package/src/structures-works/sequence-codes-tools.ts +35 -27
- package/test-SequenceTranslator-d3cbf13cf137-2cb8277f.html +276 -0
- package/vendors/openchemlib-full.js +293 -0
- package/webpack.config.js +1 -1
- package/test-SequenceTranslator-46784acc64a5-c67dd897.html +0 -245
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
|
|
6
|
-
import
|
|
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 {
|
|
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
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
|
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[]
|
|
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
|
|
91
|
+
return linkV3000(molBlocks, false);
|
|
94
92
|
}
|
|
95
93
|
|
|
96
|
-
export function linkV3000(molBlocks: string[], twoChains: 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
|
-
|
|
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
|
-
|
|
248
|
-
|
|
249
|
-
|
|
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,
|
|
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,
|
|
10
|
-
const molAS = sequenceToMolV3000(as,
|
|
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,
|
|
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
|
|
31
|
-
const asInput = ui.textInput('Anti Sense
|
|
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
|
|
43
|
+
const chirality = ui.switchInput('Use chiral', true);
|
|
34
44
|
const saveBtn = ui.button('Save SDF', () =>
|
|
35
|
-
saveSdf(asInput.value, ssInput.value, saveOption.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
|
-
|
|
43
|
-
|
|
52
|
+
ssInput,
|
|
53
|
+
asInput,
|
|
54
|
+
ui.div([changeSense], {style: {width: '40'}}),
|
|
55
|
+
changeSense,
|
|
56
|
+
changeAntiSense,
|
|
44
57
|
saveOption,
|
|
45
|
-
|
|
58
|
+
chirality,
|
|
46
59
|
ui.buttonsInput([saveBtn]),
|
|
47
60
|
], 'ui-form'),
|
|
48
61
|
], 'ui-form'),
|