@datagrok/sequence-translator 0.0.4 → 0.0.5
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/.eslintrc.json +39 -0
- package/detectors.js +2 -12
- package/package.json +7 -3
- package/src/axolabsMap.ts +101 -99
- package/src/defineAxolabsPattern.ts +242 -207
- package/src/drawAxolabsPattern.ts +127 -92
- package/src/map.ts +530 -524
- package/src/package-test.ts +6 -7
- package/src/package.ts +430 -193
- package/src/save-sense-antisense.ts +36 -0
- package/src/tests/smiles-tests.ts +448 -7
|
@@ -4,10 +4,10 @@ import * as ui from 'datagrok-api/ui';
|
|
|
4
4
|
import * as DG from 'datagrok-api/dg';
|
|
5
5
|
// @ts-ignore
|
|
6
6
|
import * as svg from 'save-svg-as-png';
|
|
7
|
-
import $ from
|
|
7
|
+
import $ from 'cash-dom';
|
|
8
8
|
|
|
9
|
-
import {drawAxolabsPattern} from
|
|
10
|
-
import {axolabsMap} from
|
|
9
|
+
import {drawAxolabsPattern} from './drawAxolabsPattern';
|
|
10
|
+
import {axolabsMap} from './axolabsMap';
|
|
11
11
|
|
|
12
12
|
const baseChoices: string[] = Object.keys(axolabsMap);
|
|
13
13
|
const defaultBase: string = baseChoices[0];
|
|
@@ -28,37 +28,45 @@ function findDuplicates(data: Int32Array | Float32Array | Float64Array | Uint32A
|
|
|
28
28
|
|
|
29
29
|
async function isCurrentUserCreatedThisPattern(patternName: string): Promise<boolean> {
|
|
30
30
|
return await grok.dapi.users.current().then((user) => {
|
|
31
|
-
|
|
31
|
+
const [firstName, lastName] = getUserName(patternName);
|
|
32
32
|
return (user.firstName != firstName || user.lastName != lastName);
|
|
33
33
|
});
|
|
34
34
|
}
|
|
35
35
|
|
|
36
36
|
function getShortName(patternName: string): string {
|
|
37
37
|
let first = patternName.length + 1;
|
|
38
|
-
for (let i = 0; i < patternName.length; i++)
|
|
38
|
+
for (let i = 0; i < patternName.length; i++) {
|
|
39
39
|
if (patternName[i] == '(') {
|
|
40
40
|
first = i;
|
|
41
41
|
break;
|
|
42
42
|
}
|
|
43
|
+
}
|
|
43
44
|
return patternName.slice(0, first - 1);
|
|
44
45
|
}
|
|
45
46
|
|
|
46
47
|
function getUserName(patternName: string): string[] {
|
|
47
48
|
let first = -1;
|
|
48
|
-
for (let i = 0; i < patternName.length; i++)
|
|
49
|
+
for (let i = 0; i < patternName.length; i++) {
|
|
49
50
|
if (patternName[i] == '(') {
|
|
50
51
|
first = i;
|
|
51
52
|
break;
|
|
52
53
|
}
|
|
54
|
+
}
|
|
53
55
|
return (first == -1) ? ['', ''] : patternName.slice(first + 9, patternName.length - 1).split(' ').slice(1);
|
|
54
56
|
}
|
|
55
57
|
|
|
56
|
-
function translateSequence(
|
|
58
|
+
function translateSequence(
|
|
59
|
+
sequence: string,
|
|
60
|
+
bases: any,
|
|
61
|
+
ptoLinkages: any,
|
|
62
|
+
startModification: any,
|
|
63
|
+
endModification: any,
|
|
64
|
+
firstPtoExist: boolean): string {
|
|
57
65
|
let counter: number = -1;
|
|
58
|
-
let mainSequence = sequence.replace(/[AUGC]/g, function
|
|
66
|
+
let mainSequence = sequence.replace(/[AUGC]/g, function(x: string) {
|
|
59
67
|
counter++;
|
|
60
|
-
|
|
61
|
-
|
|
68
|
+
const indexOfSymbol = axolabsMap['RNA']['symbols'].indexOf(x);
|
|
69
|
+
const symbol = axolabsMap[bases[counter].value]['symbols'][indexOfSymbol];
|
|
62
70
|
return (ptoLinkages[counter].value) ? symbol + 's' : symbol;
|
|
63
71
|
});
|
|
64
72
|
if (mainSequence.slice(0, 5).split('mU').length == 3)
|
|
@@ -70,27 +78,34 @@ function translateSequence(sequence: string, bases: any, ptoLinkages: any, start
|
|
|
70
78
|
|
|
71
79
|
function addColumnWithIds(tableName: string, columnName: string, patternName: string) {
|
|
72
80
|
const nameOfNewColumn = 'ID ' + patternName;
|
|
73
|
-
|
|
81
|
+
const columns = grok.shell.table(tableName).columns;
|
|
74
82
|
if (columns.contains(nameOfNewColumn))
|
|
75
83
|
columns.remove(nameOfNewColumn);
|
|
76
84
|
const columnWithIds = columns.byName(columnName);
|
|
77
85
|
return columns.addNewString(nameOfNewColumn).init((i: number) => columnWithIds.get(i) + '_' + patternName);
|
|
78
86
|
}
|
|
79
87
|
|
|
80
|
-
function addColumnWithTranslatedSequences(
|
|
88
|
+
function addColumnWithTranslatedSequences(
|
|
89
|
+
tableName: string,
|
|
90
|
+
columnName: string,
|
|
91
|
+
bases: any,
|
|
92
|
+
ptoLinkages: any,
|
|
93
|
+
startModification: any,
|
|
94
|
+
endModification: any,
|
|
95
|
+
firstPtoExist: boolean) {
|
|
81
96
|
const nameOfNewColumn = 'Axolabs ' + columnName;
|
|
82
|
-
|
|
97
|
+
const columns = grok.shell.table(tableName).columns;
|
|
83
98
|
if (columns.contains(nameOfNewColumn))
|
|
84
99
|
columns.remove(nameOfNewColumn);
|
|
85
100
|
const columnWithInputSequences = columns.byName(columnName);
|
|
86
101
|
return columns.addNewString(nameOfNewColumn).init((i: number) => {
|
|
87
|
-
return translateSequence(columnWithInputSequences.getString(i),
|
|
102
|
+
return translateSequence(columnWithInputSequences.getString(i),
|
|
103
|
+
bases, ptoLinkages, startModification, endModification, firstPtoExist);
|
|
88
104
|
});
|
|
89
105
|
}
|
|
90
106
|
|
|
91
107
|
export function defineAxolabsPattern() {
|
|
92
|
-
|
|
93
|
-
let enumerateModifications = [defaultBase];
|
|
108
|
+
const enumerateModifications = [defaultBase];
|
|
94
109
|
let maximalSsLength = defaultSequenceLength;
|
|
95
110
|
let maximalAsLength = defaultSequenceLength;
|
|
96
111
|
|
|
@@ -124,17 +139,18 @@ export function defineAxolabsPattern() {
|
|
|
124
139
|
updateOutputExamples();
|
|
125
140
|
});
|
|
126
141
|
let isOverhang = false;
|
|
127
|
-
if (asBases[i].value.slice(-3) == '(o)')
|
|
142
|
+
if (asBases[i].value.slice(-3) == '(o)')
|
|
128
143
|
isOverhang = true;
|
|
129
|
-
|
|
144
|
+
else
|
|
130
145
|
nucleotideCounter++;
|
|
131
|
-
|
|
146
|
+
|
|
132
147
|
asModificationItems.append(
|
|
133
148
|
ui.divH([
|
|
134
|
-
ui.div([ui.label(asBases[i].value.slice(-3) == '(o)' ? '' : String(nucleotideCounter))],
|
|
149
|
+
ui.div([ui.label(asBases[i].value.slice(-3) == '(o)' ? '' : String(nucleotideCounter))],
|
|
150
|
+
{style: {width: '20px'}})!,
|
|
135
151
|
ui.block75([asBases[i]])!,
|
|
136
|
-
ui.div([asPtoLinkages[i]])
|
|
137
|
-
], {style: {alignItems:
|
|
152
|
+
ui.div([asPtoLinkages[i]])!,
|
|
153
|
+
], {style: {alignItems: 'center'}}),
|
|
138
154
|
);
|
|
139
155
|
}
|
|
140
156
|
}
|
|
@@ -169,17 +185,18 @@ export function defineAxolabsPattern() {
|
|
|
169
185
|
updateOutputExamples();
|
|
170
186
|
});
|
|
171
187
|
let isOverhang = false;
|
|
172
|
-
if (ssBases[i].value.slice(-3) == '(o)')
|
|
188
|
+
if (ssBases[i].value.slice(-3) == '(o)')
|
|
173
189
|
isOverhang = true;
|
|
174
|
-
|
|
190
|
+
else
|
|
175
191
|
nucleotideCounter++;
|
|
176
|
-
|
|
192
|
+
|
|
177
193
|
ssModificationItems.append(
|
|
178
194
|
ui.divH([
|
|
179
|
-
ui.div([ui.label(ssBases[i].value.slice(-3) == '(o)' ? '' : String(nucleotideCounter))],
|
|
195
|
+
ui.div([ui.label(ssBases[i].value.slice(-3) == '(o)' ? '' : String(nucleotideCounter))],
|
|
196
|
+
{style: {width: '20px'}})!,
|
|
180
197
|
ui.block75([ssBases[i]])!,
|
|
181
|
-
ui.div([ssPtoLinkages[i]])
|
|
182
|
-
], {style: {alignItems:
|
|
198
|
+
ui.div([ssPtoLinkages[i]])!,
|
|
199
|
+
], {style: {alignItems: 'center'}}),
|
|
183
200
|
);
|
|
184
201
|
}
|
|
185
202
|
}
|
|
@@ -197,29 +214,30 @@ export function defineAxolabsPattern() {
|
|
|
197
214
|
updateOutputExamples();
|
|
198
215
|
} else {
|
|
199
216
|
ui.dialog('Sequence length is out of range')
|
|
200
|
-
.add(ui.divText('Sequence length should be less than ' +
|
|
217
|
+
.add(ui.divText('Sequence length should be less than ' +
|
|
218
|
+
maximalValidSequenceLength.toString() + ' due to UI constrains.'))
|
|
201
219
|
.add(ui.divText('Please change sequence length in order to define new pattern.'))
|
|
202
220
|
.show();
|
|
203
221
|
}
|
|
204
222
|
}
|
|
205
223
|
|
|
206
|
-
function updatePto(newPtoValue: boolean) {
|
|
207
|
-
for (let i = 0; i < ssPtoLinkages.length; i++)
|
|
224
|
+
function updatePto(newPtoValue: boolean): void {
|
|
225
|
+
for (let i = 0; i < ssPtoLinkages.length; i++)
|
|
208
226
|
ssPtoLinkages[i].value = newPtoValue;
|
|
209
|
-
|
|
210
|
-
for (let i = 0; i < asPtoLinkages.length; i++)
|
|
227
|
+
|
|
228
|
+
for (let i = 0; i < asPtoLinkages.length; i++)
|
|
211
229
|
asPtoLinkages[i].value = newPtoValue;
|
|
212
|
-
|
|
230
|
+
|
|
213
231
|
updateSvgScheme();
|
|
214
232
|
}
|
|
215
233
|
|
|
216
|
-
function updateBases(newBasisValue: string) {
|
|
217
|
-
for (let i = 0; i < ssBases.length; i++)
|
|
234
|
+
function updateBases(newBasisValue: string): void {
|
|
235
|
+
for (let i = 0; i < ssBases.length; i++)
|
|
218
236
|
ssBases[i].value = newBasisValue;
|
|
219
|
-
|
|
220
|
-
for (let i = 0; i < asBases.length; i++)
|
|
237
|
+
|
|
238
|
+
for (let i = 0; i < asBases.length; i++)
|
|
221
239
|
asBases[i].value = newBasisValue;
|
|
222
|
-
|
|
240
|
+
|
|
223
241
|
updateSvgScheme();
|
|
224
242
|
}
|
|
225
243
|
|
|
@@ -230,9 +248,12 @@ export function defineAxolabsPattern() {
|
|
|
230
248
|
}
|
|
231
249
|
|
|
232
250
|
function updateOutputExamples() {
|
|
233
|
-
ssOutputExample.value = translateSequence(
|
|
234
|
-
|
|
235
|
-
|
|
251
|
+
ssOutputExample.value = translateSequence(
|
|
252
|
+
ssInputExample.value, ssBases, ssPtoLinkages, ssFiveModification, ssThreeModification, firstSsPto.value);
|
|
253
|
+
if (createAsStrand.value) {
|
|
254
|
+
asOutputExample.value = translateSequence(
|
|
255
|
+
asInputExample.value, asBases, asPtoLinkages, asFiveModification, asThreeModification, firstAsPto.value);
|
|
256
|
+
}
|
|
236
257
|
}
|
|
237
258
|
|
|
238
259
|
function updateSvgScheme() {
|
|
@@ -251,17 +272,18 @@ export function defineAxolabsPattern() {
|
|
|
251
272
|
asThreeModification.value,
|
|
252
273
|
asFiveModification.value,
|
|
253
274
|
comment.value,
|
|
254
|
-
enumerateModifications
|
|
255
|
-
)
|
|
256
|
-
])
|
|
275
|
+
enumerateModifications,
|
|
276
|
+
),
|
|
277
|
+
]),
|
|
257
278
|
);
|
|
258
279
|
}
|
|
259
280
|
|
|
260
281
|
function detectDefaultBasis(array: string[]) {
|
|
261
|
-
|
|
262
|
-
let maxEl = array[0]
|
|
263
|
-
|
|
264
|
-
|
|
282
|
+
const modeMap: {[index: string]: number} = {};
|
|
283
|
+
let maxEl = array[0];
|
|
284
|
+
let maxCount = 1;
|
|
285
|
+
for (let i = 0; i < array.length; i++) {
|
|
286
|
+
const el = array[i];
|
|
265
287
|
if (modeMap[el] == null)
|
|
266
288
|
modeMap[el] = 1;
|
|
267
289
|
else
|
|
@@ -275,9 +297,9 @@ export function defineAxolabsPattern() {
|
|
|
275
297
|
}
|
|
276
298
|
|
|
277
299
|
async function parsePatternAndUpdateUi(newName: string) {
|
|
278
|
-
|
|
300
|
+
const pi = DG.TaskBarProgressIndicator.create('Loading pattern...');
|
|
279
301
|
await grok.dapi.userDataStorage.get(userStorageKey, false).then((entities) => {
|
|
280
|
-
|
|
302
|
+
const obj = JSON.parse(entities[newName]);
|
|
281
303
|
sequenceBase.value = detectDefaultBasis(obj['asBases'].concat(obj['ssBases']));
|
|
282
304
|
createAsStrand.value = (obj['asBases'].length > 0);
|
|
283
305
|
saveAs.value = newName;
|
|
@@ -313,7 +335,7 @@ export function defineAxolabsPattern() {
|
|
|
313
335
|
}
|
|
314
336
|
|
|
315
337
|
function checkWhetherAllValuesInColumnHaveTheSameLength(colName: string): boolean {
|
|
316
|
-
|
|
338
|
+
const col = tables.value.columns.byName(colName);
|
|
317
339
|
let allLengthsAreTheSame = true;
|
|
318
340
|
for (let i = 1; i < col.length; i++) {
|
|
319
341
|
if (col.get(i - 1).length != col.get(i).length) {
|
|
@@ -322,14 +344,14 @@ export function defineAxolabsPattern() {
|
|
|
322
344
|
}
|
|
323
345
|
}
|
|
324
346
|
if (!allLengthsAreTheSame) {
|
|
325
|
-
|
|
347
|
+
const dialog = ui.dialog('Sequences lengths mismatch');
|
|
326
348
|
$(dialog.getButton('OK')).hide();
|
|
327
349
|
dialog
|
|
328
350
|
.add(ui.divText('The sequence length should match the number of Raw sequences in the input file'))
|
|
329
|
-
.add(ui.divText(
|
|
351
|
+
.add(ui.divText('\'ADD COLUMN\' to see sequences lengths'))
|
|
330
352
|
.addButton('ADD COLUMN', () => {
|
|
331
353
|
tables.value.columns.addNewInt('Sequences lengths in ' + colName).init((j: number) => col.get(j).length);
|
|
332
|
-
grok.shell.info(
|
|
354
|
+
grok.shell.info('Column with lengths added to \'' + tables.value.name + '\'');
|
|
333
355
|
dialog.close();
|
|
334
356
|
grok.shell.v = grok.shell.getTableView(tables.value.name);
|
|
335
357
|
})
|
|
@@ -339,47 +361,50 @@ export function defineAxolabsPattern() {
|
|
|
339
361
|
}
|
|
340
362
|
|
|
341
363
|
async function getCurrentUserName(): Promise<string> {
|
|
342
|
-
return await grok.dapi.users.current().then((user) => {
|
|
364
|
+
return await grok.dapi.users.current().then((user) => {
|
|
365
|
+
return ' (created by ' + user.firstName + ' ' + user.lastName + ')';
|
|
366
|
+
});
|
|
343
367
|
}
|
|
344
368
|
|
|
345
369
|
async function postPatternToUserStorage() {
|
|
346
|
-
|
|
370
|
+
const author = await getCurrentUserName();
|
|
347
371
|
if (!saveAs.stringValue.includes('(created by '))
|
|
348
372
|
saveAs.value = saveAs.stringValue + author;
|
|
349
373
|
return grok.dapi.userDataStorage.postValue(
|
|
350
374
|
userStorageKey,
|
|
351
375
|
saveAs.stringValue,
|
|
352
376
|
JSON.stringify({
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
377
|
+
'ssBases': ssBases.slice(0, ssLength.value).map((e) => e.value),
|
|
378
|
+
'asBases': asBases.slice(0, asLength.value).map((e) => e.value),
|
|
379
|
+
'ssPtoLinkages': [firstSsPto.value].concat(ssPtoLinkages.slice(0, ssLength.value).map((e) => e.value)),
|
|
380
|
+
'asPtoLinkages': [firstAsPto.value].concat(asPtoLinkages.slice(0, asLength.value).map((e) => e.value)),
|
|
381
|
+
'ssThreeModification': ssThreeModification.value,
|
|
382
|
+
'ssFiveModification': ssFiveModification.value,
|
|
383
|
+
'asThreeModification': asThreeModification.value,
|
|
384
|
+
'asFiveModification': asFiveModification.value,
|
|
385
|
+
'comment': comment.value,
|
|
362
386
|
}),
|
|
363
|
-
false
|
|
364
|
-
).then(() => grok.shell.info(
|
|
387
|
+
false,
|
|
388
|
+
).then(() => grok.shell.info('Pattern \'' + saveAs.value + '\' was successfully uploaded!'));
|
|
365
389
|
}
|
|
366
390
|
|
|
367
391
|
async function updatePatternsList() {
|
|
368
392
|
grok.dapi.userDataStorage.get(userStorageKey, false).then(async (entities) => {
|
|
369
|
-
|
|
370
|
-
|
|
393
|
+
const lstMy: string[] = [];
|
|
394
|
+
const lstOthers: string[] = [];
|
|
371
395
|
|
|
372
|
-
for (
|
|
396
|
+
for (const ent of Object.keys(entities)) {
|
|
373
397
|
if (await isCurrentUserCreatedThisPattern(ent))
|
|
374
398
|
lstOthers.push(ent);
|
|
375
399
|
else
|
|
376
400
|
lstMy.push(getShortName(ent));
|
|
401
|
+
}
|
|
377
402
|
|
|
378
403
|
let loadPattern = ui.choiceInput('Load Pattern', '', lstMy, (v: string) => parsePatternAndUpdateUi(v));
|
|
379
404
|
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
loadPattern = ui.choiceInput('Load Pattern', '', currentList, (v: string) => parsePatternAndUpdateUi(v))
|
|
405
|
+
const myOrOthersPatternList = ui.choiceInput('', 'Mine', ['Mine', 'Others'], (v: string) => {
|
|
406
|
+
const currentList = v == 'Mine' ? lstMy : lstOthers;
|
|
407
|
+
loadPattern = ui.choiceInput('Load Pattern', '', currentList, (v: string) => parsePatternAndUpdateUi(v));
|
|
383
408
|
|
|
384
409
|
loadPattern.root.append(myOrOthersPatternList.input);
|
|
385
410
|
loadPattern.root.append(loadPattern.input);
|
|
@@ -392,17 +417,17 @@ export function defineAxolabsPattern() {
|
|
|
392
417
|
loadPattern.root.append(
|
|
393
418
|
ui.div([
|
|
394
419
|
ui.button(ui.iconFA('trash-alt', () => {}), async () => {
|
|
395
|
-
if (loadPattern.value == null)
|
|
420
|
+
if (loadPattern.value == null)
|
|
396
421
|
grok.shell.warning('Choose pattern to delete');
|
|
397
|
-
|
|
422
|
+
else if (await isCurrentUserCreatedThisPattern(saveAs.value))
|
|
398
423
|
grok.shell.warning('Cannot delete pattern, created by other user');
|
|
399
|
-
|
|
424
|
+
else {
|
|
400
425
|
await grok.dapi.userDataStorage.remove(userStorageKey, loadPattern.value, false)
|
|
401
|
-
.then(() => grok.shell.info(
|
|
426
|
+
.then(() => grok.shell.info('Pattern \'' + loadPattern.value + '\' deleted'));
|
|
402
427
|
}
|
|
403
428
|
await updatePatternsList();
|
|
404
|
-
})
|
|
405
|
-
], 'ui-input-options')
|
|
429
|
+
}),
|
|
430
|
+
], 'ui-input-options'),
|
|
406
431
|
);
|
|
407
432
|
});
|
|
408
433
|
loadPattern.root.append(myOrOthersPatternList.input);
|
|
@@ -416,17 +441,17 @@ export function defineAxolabsPattern() {
|
|
|
416
441
|
loadPattern.root.append(
|
|
417
442
|
ui.div([
|
|
418
443
|
ui.button(ui.iconFA('trash-alt', () => {}), async () => {
|
|
419
|
-
if (loadPattern.value == null)
|
|
444
|
+
if (loadPattern.value == null)
|
|
420
445
|
grok.shell.warning('Choose pattern to delete');
|
|
421
|
-
|
|
446
|
+
else if (await isCurrentUserCreatedThisPattern(saveAs.value))
|
|
422
447
|
grok.shell.warning('Cannot delete pattern, created by other user');
|
|
423
|
-
|
|
448
|
+
else {
|
|
424
449
|
await grok.dapi.userDataStorage.remove(userStorageKey, loadPattern.value, false)
|
|
425
|
-
.then(() => grok.shell.info(
|
|
450
|
+
.then(() => grok.shell.info('Pattern \'' + loadPattern.value + '\' deleted'));
|
|
426
451
|
}
|
|
427
452
|
await updatePatternsList();
|
|
428
|
-
})
|
|
429
|
-
], 'ui-input-options')
|
|
453
|
+
}),
|
|
454
|
+
], 'ui-input-options'),
|
|
430
455
|
);
|
|
431
456
|
});
|
|
432
457
|
}
|
|
@@ -435,10 +460,10 @@ export function defineAxolabsPattern() {
|
|
|
435
460
|
await grok.dapi.userDataStorage.get(userStorageKey, false)
|
|
436
461
|
.then((entities) => {
|
|
437
462
|
if (Object.keys(entities).includes(saveAs.value)) {
|
|
438
|
-
|
|
463
|
+
const dialog = ui.dialog('Pattern already exists');
|
|
439
464
|
$(dialog.getButton('OK')).hide();
|
|
440
465
|
dialog
|
|
441
|
-
.add(ui.divText(
|
|
466
|
+
.add(ui.divText('Pattern name \'' + saveAs.value + '\' already exists.'))
|
|
442
467
|
.add(ui.divText('Replace pattern?'))
|
|
443
468
|
.addButton('YES', async () => {
|
|
444
469
|
await grok.dapi.userDataStorage.remove(userStorageKey, saveAs.value, false)
|
|
@@ -446,48 +471,47 @@ export function defineAxolabsPattern() {
|
|
|
446
471
|
dialog.close();
|
|
447
472
|
})
|
|
448
473
|
.show();
|
|
449
|
-
} else
|
|
474
|
+
} else
|
|
450
475
|
postPatternToUserStorage();
|
|
451
|
-
}
|
|
452
476
|
});
|
|
453
477
|
await updatePatternsList();
|
|
454
478
|
}
|
|
455
479
|
|
|
456
|
-
|
|
457
|
-
|
|
458
|
-
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
|
|
465
|
-
|
|
466
|
-
|
|
467
|
-
|
|
468
|
-
|
|
469
|
-
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
|
|
474
|
-
|
|
475
|
-
|
|
476
|
-
|
|
477
|
-
|
|
478
|
-
|
|
480
|
+
const inputSsColumnDiv = ui.div([]);
|
|
481
|
+
const inputAsColumnDiv = ui.div([]);
|
|
482
|
+
const inputIdColumnDiv = ui.div([]);
|
|
483
|
+
const ssModificationItems = ui.div([]);
|
|
484
|
+
const asModificationItems = ui.div([]);
|
|
485
|
+
const svgDiv = ui.div([]);
|
|
486
|
+
const asExampleDiv = ui.div([]);
|
|
487
|
+
const appAxolabsDescription = ui.div([]);
|
|
488
|
+
const loadPatternDiv = ui.div([]);
|
|
489
|
+
const asModificationDiv = ui.div([]);
|
|
490
|
+
const firstAsPtoDiv = ui.div([]);
|
|
491
|
+
const isEnumerateModificationsDiv = ui.divH([ui.boolInput(defaultBase, true, (v: boolean) => {
|
|
492
|
+
if (v) {
|
|
493
|
+
if (!enumerateModifications.includes(defaultBase))
|
|
494
|
+
enumerateModifications.push(defaultBase);
|
|
495
|
+
} else {
|
|
496
|
+
const index = enumerateModifications.indexOf(defaultBase, 0);
|
|
497
|
+
if (index > -1)
|
|
498
|
+
enumerateModifications.splice(index, 1);
|
|
499
|
+
}
|
|
500
|
+
updateSvgScheme();
|
|
501
|
+
updateOutputExamples();
|
|
502
|
+
}).root]);
|
|
479
503
|
|
|
480
|
-
let ssBases = Array(defaultSequenceLength).fill(ui.choiceInput('', defaultBase, baseChoices))
|
|
481
|
-
|
|
482
|
-
|
|
483
|
-
|
|
504
|
+
let ssBases = Array(defaultSequenceLength).fill(ui.choiceInput('', defaultBase, baseChoices));
|
|
505
|
+
let asBases = Array(defaultSequenceLength).fill(ui.choiceInput('', defaultBase, baseChoices));
|
|
506
|
+
let ssPtoLinkages = Array(defaultSequenceLength).fill(ui.boolInput('', defaultPto));
|
|
507
|
+
let asPtoLinkages = Array(defaultSequenceLength).fill(ui.boolInput('', defaultPto));
|
|
484
508
|
|
|
485
|
-
|
|
486
|
-
|
|
487
|
-
|
|
509
|
+
const ssLength = ui.intInput('SS Length', defaultSequenceLength, () => updateUiForNewSequenceLength());
|
|
510
|
+
const asLength = ui.intInput('AS Length', defaultSequenceLength, () => updateUiForNewSequenceLength());
|
|
511
|
+
const asLengthDiv = ui.div([asLength.root]);
|
|
488
512
|
|
|
489
513
|
function validateSsColumn(colName: string) {
|
|
490
|
-
|
|
514
|
+
const allLengthsAreTheSame: boolean = checkWhetherAllValuesInColumnHaveTheSameLength(colName);
|
|
491
515
|
const firstSequence = tables.value.columns.byName(colName).get(0);
|
|
492
516
|
if (allLengthsAreTheSame && firstSequence.length != ssLength.value)
|
|
493
517
|
ssLength.value = tables.value.columns.byName(colName).get(0).length;
|
|
@@ -495,7 +519,7 @@ export function defineAxolabsPattern() {
|
|
|
495
519
|
}
|
|
496
520
|
|
|
497
521
|
function validateAsColumn(colName: string) {
|
|
498
|
-
|
|
522
|
+
const allLengthsAreTheSame: boolean = checkWhetherAllValuesInColumnHaveTheSameLength(colName);
|
|
499
523
|
const firstSequence = tables.value.columns.byName(colName).get(0);
|
|
500
524
|
if (allLengthsAreTheSame && firstSequence.length != asLength.value)
|
|
501
525
|
asLength.value = tables.value.columns.byName(colName).get(0).length;
|
|
@@ -511,18 +535,18 @@ export function defineAxolabsPattern() {
|
|
|
511
535
|
else if (col.categories.length < col.length) {
|
|
512
536
|
const duplicates = findDuplicates(col.getRawData());
|
|
513
537
|
ui.dialog('Non-unique IDs')
|
|
514
|
-
.add(ui.divText(
|
|
538
|
+
.add(ui.divText('Press \'OK\' to select rows with non-unique values'))
|
|
515
539
|
.onOK(() => {
|
|
516
|
-
|
|
540
|
+
const selection = tables.value.selection;
|
|
517
541
|
selection.init((i: number) => duplicates.indexOf(col.get(i)) > -1);
|
|
518
542
|
grok.shell.v = grok.shell.getTableView(tables.value.name);
|
|
519
|
-
grok.shell.info(
|
|
543
|
+
grok.shell.info('Rows are selected in table \'' + tables.value.name + '\'');
|
|
520
544
|
})
|
|
521
545
|
.show();
|
|
522
546
|
}
|
|
523
547
|
}
|
|
524
548
|
|
|
525
|
-
|
|
549
|
+
const tables = ui.tableInput('Tables', grok.shell.tables[0], grok.shell.tables, (t: DG.DataFrame) => {
|
|
526
550
|
inputSsColumn = ui.choiceInput('SS Column', '', t.columns.names(), (colName: string) => validateSsColumn(colName));
|
|
527
551
|
inputSsColumnDiv.innerHTML = '';
|
|
528
552
|
inputSsColumnDiv.append(inputSsColumn.root);
|
|
@@ -543,23 +567,23 @@ export function defineAxolabsPattern() {
|
|
|
543
567
|
|
|
544
568
|
updatePatternsList();
|
|
545
569
|
|
|
546
|
-
|
|
570
|
+
const sequenceBase = ui.choiceInput('Sequence Basis', defaultBase, baseChoices, (v: string) => {
|
|
547
571
|
updateBases(v);
|
|
548
572
|
updateOutputExamples();
|
|
549
573
|
});
|
|
550
574
|
|
|
551
|
-
|
|
575
|
+
const fullyPto = ui.boolInput('Fully PTO', defaultPto, (v: boolean) => {
|
|
552
576
|
firstSsPto.value = v;
|
|
553
577
|
firstAsPto.value = v;
|
|
554
578
|
updatePto(v);
|
|
555
579
|
updateOutputExamples();
|
|
556
580
|
});
|
|
557
581
|
|
|
558
|
-
|
|
559
|
-
|
|
582
|
+
const firstSsPto = ui.boolInput('First SS PTO', fullyPto.value, () => updateSvgScheme());
|
|
583
|
+
const firstAsPto = ui.boolInput('First AS PTO', fullyPto.value, () => updateSvgScheme());
|
|
560
584
|
firstAsPtoDiv.append(firstAsPto.root);
|
|
561
585
|
|
|
562
|
-
|
|
586
|
+
const createAsStrand = ui.boolInput('Create AS Strand', true, (v: boolean) => {
|
|
563
587
|
asModificationSection.hidden = (!v);
|
|
564
588
|
inputAsColumnDiv.hidden = (!v);
|
|
565
589
|
asLengthDiv.hidden = (!v);
|
|
@@ -569,62 +593,61 @@ export function defineAxolabsPattern() {
|
|
|
569
593
|
updateSvgScheme();
|
|
570
594
|
});
|
|
571
595
|
|
|
572
|
-
|
|
596
|
+
const saveAs = ui.textInput('Save As', 'Pattern Name', () => updateSvgScheme());
|
|
573
597
|
saveAs.setTooltip('Name Of New Pattern');
|
|
574
598
|
|
|
575
|
-
|
|
599
|
+
const ssThreeModification = ui.stringInput('SS 3\' Modification', '', () => {
|
|
576
600
|
updateSvgScheme();
|
|
577
601
|
updateOutputExamples();
|
|
578
602
|
});
|
|
579
|
-
ssThreeModification.setTooltip(
|
|
603
|
+
ssThreeModification.setTooltip('Additional SS 3\' Modification');
|
|
580
604
|
|
|
581
|
-
|
|
605
|
+
const ssFiveModification = ui.stringInput('SS 5\' Modification', '', () => {
|
|
582
606
|
updateSvgScheme();
|
|
583
607
|
updateOutputExamples();
|
|
584
608
|
});
|
|
585
|
-
ssFiveModification.setTooltip(
|
|
609
|
+
ssFiveModification.setTooltip('Additional SS 5\' Modification');
|
|
586
610
|
|
|
587
|
-
|
|
611
|
+
const asThreeModification = ui.stringInput('AS 3\' Modification', '', () => {
|
|
588
612
|
updateSvgScheme();
|
|
589
613
|
updateOutputExamples();
|
|
590
614
|
});
|
|
591
|
-
asThreeModification.setTooltip(
|
|
615
|
+
asThreeModification.setTooltip('Additional AS 3\' Modification');
|
|
592
616
|
|
|
593
|
-
|
|
617
|
+
const asFiveModification = ui.stringInput('AS 5\' Modification', '', () => {
|
|
594
618
|
updateSvgScheme();
|
|
595
619
|
updateOutputExamples();
|
|
596
620
|
});
|
|
597
|
-
asFiveModification.setTooltip(
|
|
621
|
+
asFiveModification.setTooltip('Additional AS 5\' Modification');
|
|
598
622
|
|
|
599
623
|
asModificationDiv.append(asThreeModification.root);
|
|
600
624
|
asModificationDiv.append(asFiveModification.root);
|
|
601
625
|
|
|
602
|
-
|
|
626
|
+
const comment = ui.textInput('Comment', '', () => updateSvgScheme());
|
|
603
627
|
|
|
604
|
-
|
|
605
|
-
if (saveAs.value != '')
|
|
606
|
-
savePattern().then(r => grok.shell.info('Pattern saved'));
|
|
607
|
-
|
|
608
|
-
|
|
628
|
+
const savePatternButton = ui.button('Save', () => {
|
|
629
|
+
if (saveAs.value != '')
|
|
630
|
+
savePattern().then((r) => grok.shell.info('Pattern saved'));
|
|
631
|
+
else {
|
|
632
|
+
const name = ui.stringInput('Enter Name', '');
|
|
609
633
|
ui.dialog('Pattern Name')
|
|
610
634
|
.add(name.root)
|
|
611
635
|
.onOK(() => {
|
|
612
636
|
saveAs.value = name.value;
|
|
613
|
-
savePattern().then(r => grok.shell.info('Pattern saved'));
|
|
637
|
+
savePattern().then((r) => grok.shell.info('Pattern saved'));
|
|
614
638
|
})
|
|
615
639
|
.show();
|
|
616
640
|
}
|
|
617
641
|
});
|
|
618
642
|
|
|
619
|
-
|
|
620
|
-
if (inputSsColumn.value == null || (createAsStrand.value && inputAsColumn.value == null))
|
|
621
|
-
grok.shell.info(
|
|
622
|
-
}
|
|
643
|
+
const convertSequenceButton = ui.button('Convert Sequences', () => {
|
|
644
|
+
if (inputSsColumn.value == null || (createAsStrand.value && inputAsColumn.value == null))
|
|
645
|
+
grok.shell.info('Please select table and columns on which to apply pattern');
|
|
623
646
|
else if (ssLength.value != ssInputExample.value.length || asLength.value != asInputExample.value.length) {
|
|
624
|
-
|
|
647
|
+
const dialog = ui.dialog('Length Mismatch');
|
|
625
648
|
$(dialog.getButton('OK')).hide();
|
|
626
649
|
dialog
|
|
627
|
-
.add(ui.divText(
|
|
650
|
+
.add(ui.divText('Length of sequences in columns doesn\'t match entered length. Update length value?'))
|
|
628
651
|
.addButton('YES', () => {
|
|
629
652
|
ssLength.value = tables.value.columns.byName(inputSsColumn.value).getString(0).length;
|
|
630
653
|
asLength.value = tables.value.columns.byName(inputAsColumn.value).getString(0).length;
|
|
@@ -634,18 +657,26 @@ export function defineAxolabsPattern() {
|
|
|
634
657
|
} else {
|
|
635
658
|
if (inputIdColumn.value != null)
|
|
636
659
|
addColumnWithIds(tables.value.name, inputIdColumn.value, getShortName(saveAs.value));
|
|
637
|
-
addColumnWithTranslatedSequences(
|
|
638
|
-
|
|
639
|
-
|
|
660
|
+
addColumnWithTranslatedSequences(
|
|
661
|
+
tables.value.name, inputSsColumn.value, ssBases, ssPtoLinkages,
|
|
662
|
+
ssFiveModification, ssThreeModification, firstSsPto.value);
|
|
663
|
+
if (createAsStrand.value) {
|
|
664
|
+
addColumnWithTranslatedSequences(
|
|
665
|
+
tables.value.name, inputAsColumn.value, asBases, asPtoLinkages,
|
|
666
|
+
asFiveModification, asThreeModification, firstAsPto.value);
|
|
667
|
+
}
|
|
640
668
|
grok.shell.v = grok.shell.getTableView(tables.value.name);
|
|
641
|
-
grok.shell.info(((createAsStrand.value) ?
|
|
669
|
+
grok.shell.info(((createAsStrand.value) ? 'Columns were' : 'Column was') +
|
|
670
|
+
' added to table \'' + tables.value.name + '\'');
|
|
642
671
|
}
|
|
643
672
|
});
|
|
644
673
|
|
|
645
|
-
|
|
646
|
-
ssOutputExample.value = translateSequence(ssInputExample.value, ssBases, ssPtoLinkages,
|
|
674
|
+
const ssInputExample = ui.textInput('Sense Strand', generateExample(ssLength.value, sequenceBase.value), () => {
|
|
675
|
+
ssOutputExample.value = translateSequence(ssInputExample.value, ssBases, ssPtoLinkages,
|
|
676
|
+
ssFiveModification, ssThreeModification, firstSsPto.value);
|
|
647
677
|
});
|
|
648
|
-
|
|
678
|
+
const ssOutputExample = ui.textInput(' ', translateSequence(
|
|
679
|
+
ssInputExample.value, ssBases, ssPtoLinkages, ssThreeModification, ssFiveModification, firstSsPto.value));
|
|
649
680
|
(ssInputExample.input as HTMLElement).style.resize = 'none';
|
|
650
681
|
(ssInputExample.input as HTMLElement).style.minWidth = exampleMinWidth;
|
|
651
682
|
(ssOutputExample.input as HTMLElement).style.resize = 'none';
|
|
@@ -655,15 +686,18 @@ export function defineAxolabsPattern() {
|
|
|
655
686
|
ssOutputExample.root.append(
|
|
656
687
|
ui.div([
|
|
657
688
|
ui.button(ui.iconFA('copy', () => {}), () => {
|
|
658
|
-
navigator.clipboard.writeText(ssOutputExample.value).then(() =>
|
|
659
|
-
|
|
660
|
-
|
|
689
|
+
navigator.clipboard.writeText(ssOutputExample.value).then(() =>
|
|
690
|
+
grok.shell.info('Sequence was copied to clipboard'));
|
|
691
|
+
}),
|
|
692
|
+
], 'ui-input-options'),
|
|
661
693
|
);
|
|
662
694
|
|
|
663
|
-
|
|
664
|
-
asOutputExample.value = translateSequence(
|
|
695
|
+
const asInputExample = ui.textInput('Antisense Strand', generateExample(asLength.value, sequenceBase.value), () => {
|
|
696
|
+
asOutputExample.value = translateSequence(
|
|
697
|
+
asInputExample.value, asBases, asPtoLinkages, asFiveModification, asThreeModification, firstSsPto.value);
|
|
665
698
|
});
|
|
666
|
-
|
|
699
|
+
const asOutputExample = ui.textInput(' ', translateSequence(
|
|
700
|
+
asInputExample.value, asBases, asPtoLinkages, asFiveModification, asThreeModification, firstSsPto.value));
|
|
667
701
|
(asInputExample.input as HTMLElement).style.resize = 'none';
|
|
668
702
|
(asInputExample.input as HTMLElement).style.minWidth = exampleMinWidth;
|
|
669
703
|
(asOutputExample.input as HTMLElement).style.resize = 'none';
|
|
@@ -673,40 +707,41 @@ export function defineAxolabsPattern() {
|
|
|
673
707
|
asOutputExample.root.append(
|
|
674
708
|
ui.div([
|
|
675
709
|
ui.button(ui.iconFA('copy', () => {}), () => {
|
|
676
|
-
navigator.clipboard.writeText(asOutputExample.value).then(() =>
|
|
677
|
-
|
|
678
|
-
|
|
710
|
+
navigator.clipboard.writeText(asOutputExample.value).then(() =>
|
|
711
|
+
grok.shell.info('Sequence was copied to clipboard'));
|
|
712
|
+
}),
|
|
713
|
+
], 'ui-input-options'),
|
|
679
714
|
);
|
|
680
715
|
asExampleDiv.append(asInputExample.root);
|
|
681
716
|
asExampleDiv.append(asOutputExample.root);
|
|
682
717
|
|
|
683
718
|
updateUiForNewSequenceLength();
|
|
684
719
|
|
|
685
|
-
|
|
720
|
+
const exampleSection = ui.div([
|
|
686
721
|
ui.h1('Example'),
|
|
687
722
|
ssInputExample.root,
|
|
688
723
|
ssOutputExample.root,
|
|
689
|
-
asExampleDiv
|
|
724
|
+
asExampleDiv,
|
|
690
725
|
], 'ui-form');
|
|
691
726
|
|
|
692
|
-
|
|
727
|
+
const inputsSection = ui.div([
|
|
693
728
|
ui.h1('Inputs'),
|
|
694
729
|
ui.divH([
|
|
695
730
|
tables.root,
|
|
696
|
-
inputSsColumnDiv
|
|
731
|
+
inputSsColumnDiv,
|
|
697
732
|
]),
|
|
698
733
|
ui.divH([
|
|
699
734
|
inputAsColumnDiv,
|
|
700
|
-
inputIdColumnDiv
|
|
735
|
+
inputIdColumnDiv,
|
|
701
736
|
]),
|
|
702
737
|
ui.buttonsInput([
|
|
703
|
-
convertSequenceButton
|
|
704
|
-
])
|
|
738
|
+
convertSequenceButton,
|
|
739
|
+
]),
|
|
705
740
|
], 'ui-form');
|
|
706
741
|
|
|
707
|
-
|
|
742
|
+
const mainSection = ui.panel([
|
|
708
743
|
ui.block([
|
|
709
|
-
svgDiv
|
|
744
|
+
svgDiv,
|
|
710
745
|
], {style: {overflowX: 'scroll'}}),
|
|
711
746
|
ui.button('Download', () => svg.saveSvgAsPng(document.getElementById('mySvg'), saveAs.value)),
|
|
712
747
|
isEnumerateModificationsDiv,
|
|
@@ -715,11 +750,11 @@ export function defineAxolabsPattern() {
|
|
|
715
750
|
ui.divH([
|
|
716
751
|
ui.h1('Pattern'),
|
|
717
752
|
ui.div([
|
|
718
|
-
ui.iconFA('question-circle',() => {
|
|
753
|
+
ui.iconFA('question-circle', () => {
|
|
719
754
|
appAxolabsDescription.innerHTML = '';
|
|
720
755
|
appAxolabsDescription.append(info);
|
|
721
|
-
})
|
|
722
|
-
], {style: {padding: '2px'}})
|
|
756
|
+
}),
|
|
757
|
+
], {style: {padding: '2px'}}),
|
|
723
758
|
]),
|
|
724
759
|
ui.divH([
|
|
725
760
|
ui.div([
|
|
@@ -730,8 +765,8 @@ export function defineAxolabsPattern() {
|
|
|
730
765
|
loadPatternDiv,
|
|
731
766
|
saveAs.root,
|
|
732
767
|
ui.buttonsInput([
|
|
733
|
-
savePatternButton
|
|
734
|
-
])
|
|
768
|
+
savePatternButton,
|
|
769
|
+
]),
|
|
735
770
|
], 'ui-form'),
|
|
736
771
|
ui.div([
|
|
737
772
|
createAsStrand.root,
|
|
@@ -741,56 +776,56 @@ export function defineAxolabsPattern() {
|
|
|
741
776
|
ssFiveModification.root,
|
|
742
777
|
ssThreeModification.root,
|
|
743
778
|
asModificationDiv,
|
|
744
|
-
], 'ui-form')
|
|
745
|
-
], 'ui-form')
|
|
779
|
+
], 'ui-form'),
|
|
780
|
+
], 'ui-form'),
|
|
746
781
|
], 'ui-form'),
|
|
747
782
|
inputsSection,
|
|
748
|
-
exampleSection
|
|
749
|
-
], {style: {flexWrap: 'wrap'}})
|
|
783
|
+
exampleSection,
|
|
784
|
+
], {style: {flexWrap: 'wrap'}}),
|
|
750
785
|
]);
|
|
751
786
|
|
|
752
|
-
|
|
787
|
+
const ssModificationSection = ui.panel([
|
|
753
788
|
ui.h1('Sense Strand'),
|
|
754
789
|
ui.divH([
|
|
755
790
|
ui.div([ui.divText('#')], {style: {width: '20px'}})!,
|
|
756
791
|
ui.block75([ui.divText('Modification')])!,
|
|
757
|
-
ui.div([ui.divText('PTO')], {style: {paddingRight: '8px'}})
|
|
792
|
+
ui.div([ui.divText('PTO')], {style: {paddingRight: '8px'}})!,
|
|
758
793
|
]),
|
|
759
|
-
ssModificationItems
|
|
794
|
+
ssModificationItems,
|
|
760
795
|
])!;
|
|
761
796
|
|
|
762
|
-
|
|
797
|
+
const asModificationSection = ui.panel([
|
|
763
798
|
ui.h1('Antisense Strand'),
|
|
764
799
|
ui.divH([
|
|
765
800
|
ui.div([ui.divText('#')], {style: {width: '20px'}})!,
|
|
766
801
|
ui.block75([ui.divText('Modification')])!,
|
|
767
|
-
ui.div([ui.divText('PTO')], {style: {paddingRight: '8px'}})
|
|
802
|
+
ui.div([ui.divText('PTO')], {style: {paddingRight: '8px'}})!,
|
|
768
803
|
]),
|
|
769
|
-
asModificationItems
|
|
804
|
+
asModificationItems,
|
|
770
805
|
])!;
|
|
771
806
|
|
|
772
|
-
|
|
807
|
+
const info = ui.info(
|
|
773
808
|
[
|
|
774
|
-
ui.divText(
|
|
775
|
-
ui.divText(
|
|
776
|
-
ui.divText(
|
|
777
|
-
ui.divText(
|
|
778
|
-
ui.divText(
|
|
779
|
-
ui.divText(
|
|
780
|
-
ui.divText(
|
|
781
|
-
], 'Create and apply Axolabs translation patterns.'
|
|
809
|
+
ui.divText('\n How to define new pattern:', {style: {'font-weight': 'bolder'}}),
|
|
810
|
+
ui.divText('1. Choose table and columns with sense and antisense strands'),
|
|
811
|
+
ui.divText('2. Choose lengths of both strands by editing checkboxes below'),
|
|
812
|
+
ui.divText('3. Choose basis and PTO status for each nucleotide'),
|
|
813
|
+
ui.divText('4. Set additional modifications for sequence edges'),
|
|
814
|
+
ui.divText('5. Press \'Convert Sequences\' button'),
|
|
815
|
+
ui.divText('This will add the result column(s) to the right of the table'),
|
|
816
|
+
], 'Create and apply Axolabs translation patterns.',
|
|
782
817
|
);
|
|
783
818
|
|
|
784
819
|
return ui.splitH([
|
|
785
820
|
ui.div([
|
|
786
821
|
appAxolabsDescription,
|
|
787
|
-
mainSection
|
|
822
|
+
mainSection!,
|
|
788
823
|
])!,
|
|
789
824
|
ui.box(
|
|
790
825
|
ui.divH([
|
|
791
826
|
ssModificationSection,
|
|
792
|
-
asModificationSection
|
|
793
|
-
]), {style: {maxWidth: '360px'}}
|
|
794
|
-
)
|
|
827
|
+
asModificationSection,
|
|
828
|
+
]), {style: {maxWidth: '360px'}},
|
|
829
|
+
),
|
|
795
830
|
]);
|
|
796
|
-
}
|
|
831
|
+
}
|