@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
|
@@ -1,4 +1,3 @@
|
|
|
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';
|
|
@@ -6,8 +5,8 @@ import * as DG from 'datagrok-api/dg';
|
|
|
6
5
|
import * as svg from 'save-svg-as-png';
|
|
7
6
|
import $ from 'cash-dom';
|
|
8
7
|
|
|
9
|
-
import {drawAxolabsPattern} from './
|
|
10
|
-
import {axolabsMap} from './
|
|
8
|
+
import {drawAxolabsPattern} from './draw-svg';
|
|
9
|
+
import {axolabsMap} from './constants';
|
|
11
10
|
|
|
12
11
|
const baseChoices: string[] = Object.keys(axolabsMap);
|
|
13
12
|
const defaultBase: string = baseChoices[0];
|
|
@@ -57,17 +56,23 @@ function getUserName(patternName: string): string[] {
|
|
|
57
56
|
|
|
58
57
|
function translateSequence(
|
|
59
58
|
sequence: string,
|
|
60
|
-
bases:
|
|
61
|
-
ptoLinkages:
|
|
62
|
-
startModification:
|
|
63
|
-
endModification:
|
|
59
|
+
bases: DG.InputBase[],
|
|
60
|
+
ptoLinkages: DG.InputBase[],
|
|
61
|
+
startModification: DG.InputBase,
|
|
62
|
+
endModification: DG.InputBase,
|
|
64
63
|
firstPtoExist: boolean): string {
|
|
65
|
-
let
|
|
64
|
+
let i: number = -1;
|
|
66
65
|
let mainSequence = sequence.replace(/[AUGC]/g, function(x: string) {
|
|
67
|
-
|
|
66
|
+
i++;
|
|
68
67
|
const indexOfSymbol = axolabsMap['RNA']['symbols'].indexOf(x);
|
|
69
|
-
|
|
70
|
-
|
|
68
|
+
let symbol = axolabsMap[bases[i].value]['symbols'][indexOfSymbol];
|
|
69
|
+
if (bases[i].value.slice(-3) == '(o)') {
|
|
70
|
+
if (i < sequence.length / 2 && bases[i + 1].value.slice(-3) != '(o)')
|
|
71
|
+
symbol = symbol + x + 'f';
|
|
72
|
+
else if (i > sequence.length / 2 && bases[i - 1].value.slice(-3) != '(o)')
|
|
73
|
+
symbol = x + 'f' + symbol;
|
|
74
|
+
}
|
|
75
|
+
return (ptoLinkages[i].value) ? symbol + 's' : symbol;
|
|
71
76
|
});
|
|
72
77
|
if (mainSequence.slice(0, 5).split('mU').length == 3)
|
|
73
78
|
mainSequence = '(uu)' + mainSequence.slice(4);
|
|
@@ -82,16 +87,18 @@ function addColumnWithIds(tableName: string, columnName: string, patternName: st
|
|
|
82
87
|
if (columns.contains(nameOfNewColumn))
|
|
83
88
|
columns.remove(nameOfNewColumn);
|
|
84
89
|
const columnWithIds = columns.byName(columnName);
|
|
85
|
-
return columns.addNewString(nameOfNewColumn).init((i: number) =>
|
|
90
|
+
return columns.addNewString(nameOfNewColumn).init((i: number) => {
|
|
91
|
+
return (columnWithIds.getString(i) == '') ? '' : columnWithIds.get(i) + '_' + patternName;
|
|
92
|
+
});
|
|
86
93
|
}
|
|
87
94
|
|
|
88
95
|
function addColumnWithTranslatedSequences(
|
|
89
96
|
tableName: string,
|
|
90
97
|
columnName: string,
|
|
91
|
-
bases:
|
|
92
|
-
ptoLinkages:
|
|
93
|
-
startModification:
|
|
94
|
-
endModification:
|
|
98
|
+
bases: DG.InputBase[],
|
|
99
|
+
ptoLinkages: DG.InputBase[],
|
|
100
|
+
startModification: DG.InputBase,
|
|
101
|
+
endModification: DG.InputBase,
|
|
95
102
|
firstPtoExist: boolean) {
|
|
96
103
|
const nameOfNewColumn = 'Axolabs ' + columnName;
|
|
97
104
|
const columns = grok.shell.table(tableName).columns;
|
|
@@ -99,8 +106,10 @@ function addColumnWithTranslatedSequences(
|
|
|
99
106
|
columns.remove(nameOfNewColumn);
|
|
100
107
|
const columnWithInputSequences = columns.byName(columnName);
|
|
101
108
|
return columns.addNewString(nameOfNewColumn).init((i: number) => {
|
|
102
|
-
return
|
|
103
|
-
|
|
109
|
+
return columnWithInputSequences.getString(i) == '' ?
|
|
110
|
+
'' :
|
|
111
|
+
translateSequence(columnWithInputSequences.getString(i), bases, ptoLinkages, startModification, endModification,
|
|
112
|
+
firstPtoExist);
|
|
104
113
|
});
|
|
105
114
|
}
|
|
106
115
|
|
|
@@ -236,8 +245,9 @@ export function defineAxolabsPattern() {
|
|
|
236
245
|
}
|
|
237
246
|
|
|
238
247
|
function updateInputExamples() {
|
|
239
|
-
|
|
240
|
-
|
|
248
|
+
if (inputSsColumn.value == '')
|
|
249
|
+
ssInputExample.value = generateExample(ssLength.value!, sequenceBase.value!);
|
|
250
|
+
if (createAsStrand.value && inputAsColumn.value == '')
|
|
241
251
|
asInputExample.value = generateExample(asLength.value!, sequenceBase.value!);
|
|
242
252
|
}
|
|
243
253
|
|
|
@@ -329,10 +339,10 @@ export function defineAxolabsPattern() {
|
|
|
329
339
|
}
|
|
330
340
|
|
|
331
341
|
function checkWhetherAllValuesInColumnHaveTheSameLength(colName: string): boolean {
|
|
332
|
-
const col = tables.value!.
|
|
342
|
+
const col = tables.value!.getCol(colName);
|
|
333
343
|
let allLengthsAreTheSame = true;
|
|
334
344
|
for (let i = 1; i < col.length; i++) {
|
|
335
|
-
if (col.get(i - 1).length != col.get(i).length) {
|
|
345
|
+
if (col.get(i - 1).length != col.get(i).length && col.get(i).length != 0) {
|
|
336
346
|
allLengthsAreTheSame = false;
|
|
337
347
|
break;
|
|
338
348
|
}
|
|
@@ -361,12 +371,13 @@ export function defineAxolabsPattern() {
|
|
|
361
371
|
}
|
|
362
372
|
|
|
363
373
|
async function postPatternToUserStorage() {
|
|
364
|
-
const
|
|
365
|
-
|
|
366
|
-
saveAs.value
|
|
374
|
+
const currUserName = await getCurrentUserName();
|
|
375
|
+
saveAs.value = (saveAs.stringValue.includes('(created by ')) ?
|
|
376
|
+
getShortName(saveAs.value) + currUserName :
|
|
377
|
+
saveAs.stringValue + currUserName;
|
|
367
378
|
return grok.dapi.userDataStorage.postValue(
|
|
368
379
|
userStorageKey,
|
|
369
|
-
saveAs.
|
|
380
|
+
saveAs.value,
|
|
370
381
|
JSON.stringify({
|
|
371
382
|
'ssBases': ssBases.slice(0, ssLength.value!).map((e) => e.value),
|
|
372
383
|
'asBases': asBases.slice(0, asLength.value!).map((e) => e.value),
|
|
@@ -387,11 +398,12 @@ export function defineAxolabsPattern() {
|
|
|
387
398
|
const lstMy: string[] = [];
|
|
388
399
|
const lstOthers: string[] = [];
|
|
389
400
|
|
|
401
|
+
// TODO: display short name, but use long for querying userdataStorage
|
|
390
402
|
for (const ent of Object.keys(entities)) {
|
|
391
403
|
if (await isCurrentUserCreatedThisPattern(ent))
|
|
392
404
|
lstOthers.push(ent);
|
|
393
405
|
else
|
|
394
|
-
lstMy.push(getShortName(ent));
|
|
406
|
+
lstMy.push(ent);//getShortName(ent));
|
|
395
407
|
}
|
|
396
408
|
|
|
397
409
|
let loadPattern = ui.choiceInput('Load Pattern', '', lstMy, (v: string) => parsePatternAndUpdateUi(v));
|
|
@@ -504,29 +516,27 @@ export function defineAxolabsPattern() {
|
|
|
504
516
|
const asLength = ui.intInput('AS Length', defaultSequenceLength, () => updateUiForNewSequenceLength());
|
|
505
517
|
const asLengthDiv = ui.div([asLength.root]);
|
|
506
518
|
|
|
507
|
-
function validateSsColumn(colName: string) {
|
|
519
|
+
function validateSsColumn(colName: string): void {
|
|
508
520
|
const allLengthsAreTheSame: boolean = checkWhetherAllValuesInColumnHaveTheSameLength(colName);
|
|
509
|
-
const firstSequence = tables.value!.
|
|
521
|
+
const firstSequence = tables.value!.getCol(colName).get(0);
|
|
510
522
|
if (allLengthsAreTheSame && firstSequence.length != ssLength.value)
|
|
511
|
-
ssLength.value = tables.value!.
|
|
523
|
+
ssLength.value = tables.value!.getCol(colName).get(0).length;
|
|
512
524
|
ssInputExample.value = firstSequence;
|
|
513
525
|
}
|
|
514
526
|
|
|
515
|
-
function validateAsColumn(colName: string) {
|
|
527
|
+
function validateAsColumn(colName: string): void {
|
|
516
528
|
const allLengthsAreTheSame: boolean = checkWhetherAllValuesInColumnHaveTheSameLength(colName);
|
|
517
|
-
const firstSequence = tables.value!.
|
|
529
|
+
const firstSequence = tables.value!.getCol(colName).get(0);
|
|
518
530
|
if (allLengthsAreTheSame && firstSequence.length != asLength.value)
|
|
519
|
-
asLength.value = tables.value!.
|
|
520
|
-
asLengthDiv.innerHTML = '';
|
|
521
|
-
asLengthDiv.append(asLength.root);
|
|
531
|
+
asLength.value = tables.value!.getCol(colName).get(0).length;
|
|
522
532
|
asInputExample.value = firstSequence;
|
|
523
533
|
}
|
|
524
534
|
|
|
525
535
|
function validateIdsColumn(colName: string) {
|
|
526
|
-
const col = tables.value!.
|
|
536
|
+
const col = tables.value!.getCol(colName);
|
|
527
537
|
if (col.type != DG.TYPE.INT)
|
|
528
538
|
grok.shell.error('Column should contain integers only');
|
|
529
|
-
else if (col.categories.length < col.length) {
|
|
539
|
+
else if (col.categories.filter((e) => e != '').length < col.toList().filter((e) => e != '').length) {
|
|
530
540
|
const duplicates = findDuplicates(col.getRawData());
|
|
531
541
|
ui.dialog('Non-unique IDs')
|
|
532
542
|
.add(ui.divText('Press \'OK\' to select rows with non-unique values'))
|
|
@@ -541,25 +551,43 @@ export function defineAxolabsPattern() {
|
|
|
541
551
|
}
|
|
542
552
|
|
|
543
553
|
const tables = ui.tableInput('Tables', grok.shell.tables[0], grok.shell.tables, (t: DG.DataFrame) => {
|
|
544
|
-
const inputSsColumn =
|
|
545
|
-
|
|
554
|
+
const inputSsColumn = ui.choiceInput('SS Column', '', t.columns.names(), (colName: string) => {
|
|
555
|
+
validateSsColumn(colName);
|
|
556
|
+
ssVar = colName;
|
|
557
|
+
});
|
|
546
558
|
inputSsColumnDiv.innerHTML = '';
|
|
547
559
|
inputSsColumnDiv.append(inputSsColumn.root);
|
|
548
|
-
const inputAsColumn =
|
|
549
|
-
|
|
560
|
+
const inputAsColumn = ui.choiceInput('AS Column', '', t.columns.names(), (colName: string) => {
|
|
561
|
+
validateAsColumn(colName);
|
|
562
|
+
asVar = colName;
|
|
563
|
+
});
|
|
550
564
|
inputAsColumnDiv.innerHTML = '';
|
|
551
565
|
inputAsColumnDiv.append(inputAsColumn.root);
|
|
552
|
-
const inputIdColumn =
|
|
553
|
-
|
|
566
|
+
const inputIdColumn = ui.choiceInput('ID Column', '', t.columns.names(), (colName: string) => {
|
|
567
|
+
validateIdsColumn(colName);
|
|
568
|
+
idVar = colName;
|
|
569
|
+
});
|
|
554
570
|
inputIdColumnDiv.innerHTML = '';
|
|
555
571
|
inputIdColumnDiv.append(inputIdColumn.root);
|
|
556
572
|
});
|
|
557
573
|
|
|
558
|
-
|
|
574
|
+
let ssVar = '';
|
|
575
|
+
const inputSsColumn = ui.choiceInput('SS Column', '', [], (colName: string) => {
|
|
576
|
+
validateSsColumn(colName);
|
|
577
|
+
ssVar = colName;
|
|
578
|
+
});
|
|
559
579
|
inputSsColumnDiv.append(inputSsColumn.root);
|
|
560
|
-
|
|
580
|
+
let asVar = '';
|
|
581
|
+
const inputAsColumn = ui.choiceInput('AS Column', '', [], (colName: string) => {
|
|
582
|
+
validateAsColumn(colName);
|
|
583
|
+
asVar = colName;
|
|
584
|
+
});
|
|
561
585
|
inputAsColumnDiv.append(inputAsColumn.root);
|
|
562
|
-
|
|
586
|
+
let idVar = '';
|
|
587
|
+
const inputIdColumn = ui.choiceInput('ID Column', '', [], (colName: string) => {
|
|
588
|
+
validateIdsColumn(colName);
|
|
589
|
+
idVar = colName;
|
|
590
|
+
});
|
|
563
591
|
inputIdColumnDiv.append(inputIdColumn.root);
|
|
564
592
|
|
|
565
593
|
updatePatternsList();
|
|
@@ -638,7 +666,7 @@ export function defineAxolabsPattern() {
|
|
|
638
666
|
});
|
|
639
667
|
|
|
640
668
|
const convertSequenceButton = ui.button('Convert Sequences', () => {
|
|
641
|
-
if (
|
|
669
|
+
if (ssVar == '' || (createAsStrand.value && asVar == ''))
|
|
642
670
|
grok.shell.info('Please select table and columns on which to apply pattern');
|
|
643
671
|
else if (ssLength.value != ssInputExample.value.length || asLength.value != asInputExample.value.length) {
|
|
644
672
|
const dialog = ui.dialog('Length Mismatch');
|
|
@@ -646,20 +674,20 @@ export function defineAxolabsPattern() {
|
|
|
646
674
|
dialog
|
|
647
675
|
.add(ui.divText('Length of sequences in columns doesn\'t match entered length. Update length value?'))
|
|
648
676
|
.addButton('YES', () => {
|
|
649
|
-
ssLength.value = tables.value!.
|
|
650
|
-
asLength.value = tables.value!.
|
|
677
|
+
ssLength.value = tables.value!.getCol(inputSsColumn.value!).getString(0).length;
|
|
678
|
+
asLength.value = tables.value!.getCol(inputAsColumn.value!).getString(0).length;
|
|
651
679
|
dialog.close();
|
|
652
680
|
})
|
|
653
681
|
.show();
|
|
654
682
|
} else {
|
|
655
|
-
if (
|
|
656
|
-
addColumnWithIds(tables.value!.name,
|
|
683
|
+
if (idVar != '')
|
|
684
|
+
addColumnWithIds(tables.value!.name, idVar, getShortName(saveAs.value));
|
|
657
685
|
addColumnWithTranslatedSequences(
|
|
658
|
-
tables.value!.name,
|
|
686
|
+
tables.value!.name, ssVar, ssBases, ssPtoLinkages,
|
|
659
687
|
ssFiveModification, ssThreeModification, firstSsPto.value!);
|
|
660
688
|
if (createAsStrand.value) {
|
|
661
689
|
addColumnWithTranslatedSequences(
|
|
662
|
-
tables.value!.name,
|
|
690
|
+
tables.value!.name, asVar, asBases, asPtoLinkages,
|
|
663
691
|
asFiveModification, asThreeModification, firstAsPto.value!);
|
|
664
692
|
}
|
|
665
693
|
grok.shell.v = grok.shell.getTableView(tables.value!.name);
|
|
@@ -668,10 +696,7 @@ export function defineAxolabsPattern() {
|
|
|
668
696
|
}
|
|
669
697
|
});
|
|
670
698
|
|
|
671
|
-
const ssInputExample = ui.textInput('Sense Strand', generateExample(ssLength.value!, sequenceBase.value!)
|
|
672
|
-
ssOutputExample.value = translateSequence(ssInputExample.value, ssBases, ssPtoLinkages,
|
|
673
|
-
ssFiveModification, ssThreeModification, firstSsPto.value!);
|
|
674
|
-
});
|
|
699
|
+
const ssInputExample = ui.textInput('Sense Strand', generateExample(ssLength.value!, sequenceBase.value!));
|
|
675
700
|
const ssOutputExample = ui.textInput(' ', translateSequence(
|
|
676
701
|
ssInputExample.value, ssBases, ssPtoLinkages, ssThreeModification, ssFiveModification, firstSsPto.value!));
|
|
677
702
|
(ssInputExample.input as HTMLElement).style.resize = 'none';
|
|
@@ -689,10 +714,7 @@ export function defineAxolabsPattern() {
|
|
|
689
714
|
], 'ui-input-options'),
|
|
690
715
|
);
|
|
691
716
|
|
|
692
|
-
const asInputExample = ui.textInput('Antisense Strand', generateExample(asLength.value!, sequenceBase.value!)
|
|
693
|
-
asOutputExample.value = translateSequence(
|
|
694
|
-
asInputExample.value, asBases, asPtoLinkages, asFiveModification, asThreeModification, firstSsPto.value!);
|
|
695
|
-
});
|
|
717
|
+
const asInputExample = ui.textInput('Antisense Strand', generateExample(asLength.value!, sequenceBase.value!));
|
|
696
718
|
const asOutputExample = ui.textInput(' ', translateSequence(
|
|
697
719
|
asInputExample.value, asBases, asPtoLinkages, asFiveModification, asThreeModification, firstSsPto.value!));
|
|
698
720
|
(asInputExample.input as HTMLElement).style.resize = 'none';
|
|
@@ -736,11 +758,14 @@ export function defineAxolabsPattern() {
|
|
|
736
758
|
]),
|
|
737
759
|
], 'ui-form');
|
|
738
760
|
|
|
761
|
+
const downloadButton = ui.button('Download', () => svg.saveSvgAsPng(document.getElementById('mySvg'), saveAs.value,
|
|
762
|
+
{backgroundColor: 'white'}));
|
|
763
|
+
|
|
739
764
|
const mainSection = ui.panel([
|
|
740
765
|
ui.block([
|
|
741
766
|
svgDiv,
|
|
742
767
|
], {style: {overflowX: 'scroll'}}),
|
|
743
|
-
|
|
768
|
+
downloadButton,
|
|
744
769
|
isEnumerateModificationsDiv,
|
|
745
770
|
ui.div([
|
|
746
771
|
ui.div([
|
|
@@ -0,0 +1,225 @@
|
|
|
1
|
+
import * as grok from 'datagrok-api/grok';
|
|
2
|
+
import * as ui from 'datagrok-api/ui';
|
|
3
|
+
import * as DG from 'datagrok-api/dg';
|
|
4
|
+
import {convertSequence, undefinedInputSequence, isValidSequence} from '../structures-works/sequence-codes-tools';
|
|
5
|
+
import {map, MODIFICATIONS} from '../structures-works/map';
|
|
6
|
+
import {sequenceToSmiles, sequenceToMolV3000} from '../structures-works/from-monomers';
|
|
7
|
+
|
|
8
|
+
import $ from 'cash-dom';
|
|
9
|
+
|
|
10
|
+
const defaultInput = 'fAmCmGmAmCpsmU';
|
|
11
|
+
const sequenceWasCopied = 'Copied';
|
|
12
|
+
const tooltipSequence = 'Copy sequence';
|
|
13
|
+
|
|
14
|
+
export function mainView() {
|
|
15
|
+
function updateTableAndMolecule(sequence: string, inputFormat: string, isSet: boolean): void {
|
|
16
|
+
moleculeSvgDiv.innerHTML = '';
|
|
17
|
+
outputTableDiv.innerHTML = '';
|
|
18
|
+
const pi = DG.TaskBarProgressIndicator.create('Rendering table and molecule...');
|
|
19
|
+
let errorsExist = false;
|
|
20
|
+
try {
|
|
21
|
+
sequence = sequence.replace(/\s/g, '');
|
|
22
|
+
const output = isValidSequence(sequence, null);
|
|
23
|
+
if (isSet)
|
|
24
|
+
output.synthesizer = [inputFormat];
|
|
25
|
+
inputFormatChoiceInput.value = output.synthesizer![0];
|
|
26
|
+
const outputSequenceObj = convertSequence(sequence, output);
|
|
27
|
+
const tableRows = [];
|
|
28
|
+
|
|
29
|
+
for (const key of Object.keys(outputSequenceObj).slice(1)) {
|
|
30
|
+
const indexOfFirstNotValidChar = ('indexOfFirstNotValidChar' in outputSequenceObj) ?
|
|
31
|
+
JSON.parse(outputSequenceObj.indexOfFirstNotValidChar!).indexOfFirstNotValidChar :
|
|
32
|
+
-1;
|
|
33
|
+
if ('indexOfFirstNotValidChar' in outputSequenceObj) {
|
|
34
|
+
const indexOfFirstNotValidChar = ('indexOfFirstNotValidChar' in outputSequenceObj) ?
|
|
35
|
+
JSON.parse(outputSequenceObj.indexOfFirstNotValidChar!).indexOfFirstNotValidChar :
|
|
36
|
+
-1;
|
|
37
|
+
if (indexOfFirstNotValidChar != -1)
|
|
38
|
+
errorsExist = true;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
tableRows.push({
|
|
42
|
+
'key': key,
|
|
43
|
+
'value': ('indexOfFirstNotValidChar' in outputSequenceObj) ?
|
|
44
|
+
ui.divH([
|
|
45
|
+
ui.divText(sequence.slice(0, indexOfFirstNotValidChar), {style: {color: 'grey'}}),
|
|
46
|
+
ui.tooltip.bind(
|
|
47
|
+
ui.divText(sequence.slice(indexOfFirstNotValidChar), {style: {color: 'red'}}),
|
|
48
|
+
'Expected format: ' + JSON.parse(outputSequenceObj.indexOfFirstNotValidChar!).synthesizer +
|
|
49
|
+
'. See tables with valid codes on the right',
|
|
50
|
+
),
|
|
51
|
+
]) : //@ts-ignore
|
|
52
|
+
ui.link(outputSequenceObj[key], () => navigator.clipboard.writeText(outputSequenceObj[key])
|
|
53
|
+
.then(() => grok.shell.info(sequenceWasCopied)), tooltipSequence, ''),
|
|
54
|
+
});
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
if (errorsExist) {
|
|
58
|
+
const synthesizer = JSON.parse(outputSequenceObj.indexOfFirstNotValidChar!).synthesizer.slice(0, -6);
|
|
59
|
+
asoGapmersGrid.onCellPrepare(function(gc) {
|
|
60
|
+
gc.style.backColor = (gc.gridColumn.name == synthesizer) ? 0xFFF00000 : 0xFFFFFFFF;
|
|
61
|
+
});
|
|
62
|
+
omeAndFluoroGrid.onCellPrepare(function(gc) {
|
|
63
|
+
gc.style.backColor = (gc.gridColumn.name == synthesizer) ? 0xFFF00000 : 0xFFFFFFFF;
|
|
64
|
+
});
|
|
65
|
+
switchInput.enabled = true;
|
|
66
|
+
} else {
|
|
67
|
+
asoGapmersGrid.onCellPrepare(function(gc) {gc.style.backColor = 0xFFFFFFFF;});
|
|
68
|
+
omeAndFluoroGrid.onCellPrepare(function(gc) {gc.style.backColor = 0xFFFFFFFF;});
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
outputTableDiv.append(
|
|
72
|
+
ui.div([
|
|
73
|
+
DG.HtmlTable.create(tableRows, (item: { key: string; value: string; }) =>
|
|
74
|
+
[item.key, item.value], ['Code', 'Sequence']).root,
|
|
75
|
+
]),
|
|
76
|
+
);
|
|
77
|
+
|
|
78
|
+
if (outputSequenceObj.type != undefinedInputSequence && outputSequenceObj.Error != undefinedInputSequence) {
|
|
79
|
+
const canvas = ui.canvas(300, 170);
|
|
80
|
+
canvas.addEventListener('click', () => {
|
|
81
|
+
const canv = ui.canvas($(window).width(), $(window).height());
|
|
82
|
+
const mol = sequenceToMolV3000(inputSequenceField.value.replace(/\s/g, ''), false, true,
|
|
83
|
+
output.synthesizer![0]);
|
|
84
|
+
// @ts-ignore
|
|
85
|
+
OCL.StructureView.drawMolecule(canv, OCL.Molecule.fromMolfile(mol), {suppressChiralText: true});
|
|
86
|
+
ui.dialog('Molecule: ' + inputSequenceField.value)
|
|
87
|
+
.add(canv)
|
|
88
|
+
.showModal(true);
|
|
89
|
+
});
|
|
90
|
+
$(canvas).on('mouseover', () => $(canvas).css('cursor', 'zoom-in'));
|
|
91
|
+
$(canvas).on('mouseout', () => $(canvas).css('cursor', 'default'));
|
|
92
|
+
const mol = sequenceToMolV3000(inputSequenceField.value.replace(/\s/g, ''), false, true,
|
|
93
|
+
output.synthesizer![0]);
|
|
94
|
+
// @ts-ignore
|
|
95
|
+
OCL.StructureView.drawMolecule(canvas, OCL.Molecule.fromMolfile(mol), {suppressChiralText: true});
|
|
96
|
+
moleculeSvgDiv.append(canvas);
|
|
97
|
+
} else
|
|
98
|
+
moleculeSvgDiv.innerHTML = '';
|
|
99
|
+
} finally {
|
|
100
|
+
pi.close();
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
const inputFormatChoiceInput = ui.choiceInput(
|
|
105
|
+
'Input format: ', 'Janssen GCRS Codes', Object.keys(map), (format: string) => {
|
|
106
|
+
updateTableAndMolecule(inputSequenceField.value.replace(/\s/g, ''), format, true);
|
|
107
|
+
});
|
|
108
|
+
const moleculeSvgDiv = ui.block([]);
|
|
109
|
+
const outputTableDiv = ui.div([]);
|
|
110
|
+
const inputSequenceField = ui.textInput('', defaultInput, (sequence: string) => updateTableAndMolecule(sequence,
|
|
111
|
+
inputFormatChoiceInput.value!, false));
|
|
112
|
+
|
|
113
|
+
const asoDf = DG.DataFrame.fromObjects([
|
|
114
|
+
{'Name': '2\'MOE-5Me-rU', 'BioSpring': '5', 'Janssen GCRS': 'moeT'},
|
|
115
|
+
{'Name': '2\'MOE-rA', 'BioSpring': '6', 'Janssen GCRS': 'moeA'},
|
|
116
|
+
{'Name': '2\'MOE-5Me-rC', 'BioSpring': '7', 'Janssen GCRS': 'moe5mC'},
|
|
117
|
+
{'Name': '2\'MOE-rG', 'BioSpring': '8', 'Janssen GCRS': 'moeG'},
|
|
118
|
+
{'Name': '5-Methyl-dC', 'BioSpring': '9', 'Janssen GCRS': '5mC'},
|
|
119
|
+
{'Name': 'ps linkage', 'BioSpring': '*', 'Janssen GCRS': 'ps'},
|
|
120
|
+
{'Name': 'dA', 'BioSpring': 'A', 'Janssen GCRS': 'A, dA'},
|
|
121
|
+
{'Name': 'dC', 'BioSpring': 'C', 'Janssen GCRS': 'C, dC'},
|
|
122
|
+
{'Name': 'dG', 'BioSpring': 'G', 'Janssen GCRS': 'G, dG'},
|
|
123
|
+
{'Name': 'dT', 'BioSpring': 'T', 'Janssen GCRS': 'T, dT'},
|
|
124
|
+
{'Name': 'rA', 'BioSpring': '', 'Janssen GCRS': 'rA'},
|
|
125
|
+
{'Name': 'rC', 'BioSpring': '', 'Janssen GCRS': 'rC'},
|
|
126
|
+
{'Name': 'rG', 'BioSpring': '', 'Janssen GCRS': 'rG'},
|
|
127
|
+
{'Name': 'rU', 'BioSpring': '', 'Janssen GCRS': 'rU'},
|
|
128
|
+
])!;
|
|
129
|
+
const asoGapmersGrid = DG.Viewer.grid(asoDf, {showRowHeader: false, showCellTooltip: false});
|
|
130
|
+
|
|
131
|
+
asoDf.onCurrentCellChanged.subscribe((_) => {
|
|
132
|
+
navigator.clipboard.writeText(asoDf.currentCell.value).then(() => grok.shell.info('Copied'));
|
|
133
|
+
});
|
|
134
|
+
|
|
135
|
+
const omeAndFluoroGrid = DG.Viewer.grid(
|
|
136
|
+
DG.DataFrame.fromObjects([
|
|
137
|
+
{'Name': '2\'-fluoro-U', 'BioSpring': '1', 'Axolabs': 'Uf', 'Janssen GCRS': 'fU'},
|
|
138
|
+
{'Name': '2\'-fluoro-A', 'BioSpring': '2', 'Axolabs': 'Af', 'Janssen GCRS': 'fA'},
|
|
139
|
+
{'Name': '2\'-fluoro-C', 'BioSpring': '3', 'Axolabs': 'Cf', 'Janssen GCRS': 'fC'},
|
|
140
|
+
{'Name': '2\'-fluoro-G', 'BioSpring': '4', 'Axolabs': 'Gf', 'Janssen GCRS': 'fG'},
|
|
141
|
+
{'Name': '2\'OMe-rU', 'BioSpring': '5', 'Axolabs': 'u', 'Janssen GCRS': 'mU'},
|
|
142
|
+
{'Name': '2\'OMe-rA', 'BioSpring': '6', 'Axolabs': 'a', 'Janssen GCRS': 'mA'},
|
|
143
|
+
{'Name': '2\'OMe-rC', 'BioSpring': '7', 'Axolabs': 'c', 'Janssen GCRS': 'mC'},
|
|
144
|
+
{'Name': '2\'OMe-rG', 'BioSpring': '8', 'Axolabs': 'g', 'Janssen GCRS': 'mG'},
|
|
145
|
+
{'Name': 'ps linkage', 'BioSpring': '*', 'Axolabs': 's', 'Janssen GCRS': 'ps'},
|
|
146
|
+
])!, {showRowHeader: false, showCellTooltip: false},
|
|
147
|
+
);
|
|
148
|
+
|
|
149
|
+
const overhangModificationsGrid = DG.Viewer.grid(
|
|
150
|
+
DG.DataFrame.fromColumns([
|
|
151
|
+
DG.Column.fromStrings('Name', Object.keys(MODIFICATIONS)),
|
|
152
|
+
])!, {showRowHeader: false, showCellTooltip: false},
|
|
153
|
+
);
|
|
154
|
+
updateTableAndMolecule(defaultInput, inputFormatChoiceInput.value!, true);
|
|
155
|
+
|
|
156
|
+
const codesTablesDiv = ui.splitV([
|
|
157
|
+
ui.box(ui.h2('ASO Gapmers'), {style: {maxHeight: '40px'}}),
|
|
158
|
+
asoGapmersGrid.root,
|
|
159
|
+
ui.box(ui.h2('2\'-OMe and 2\'-F siRNA'), {style: {maxHeight: '40px'}}),
|
|
160
|
+
omeAndFluoroGrid.root,
|
|
161
|
+
ui.box(ui.h2('Overhang modifications'), {style: {maxHeight: '40px'}}),
|
|
162
|
+
overhangModificationsGrid.root,
|
|
163
|
+
], {style: {maxWidth: '350px'}});
|
|
164
|
+
|
|
165
|
+
const appMainDescription = ui.info([
|
|
166
|
+
ui.divText('How to convert one sequence:', {style: {'font-weight': 'bolder'}}),
|
|
167
|
+
ui.divText('Paste sequence into the text field below'),
|
|
168
|
+
ui.divText('\n How to convert many sequences:', {style: {'font-weight': 'bolder'}}),
|
|
169
|
+
ui.divText('1. Drag & drop an Excel or CSV file with sequences into Datagrok'),
|
|
170
|
+
ui.divText('2. Right-click on the column header, then see the \'Convert\' menu'),
|
|
171
|
+
ui.divText('This will add the result column to the right of the table'),
|
|
172
|
+
], 'Convert oligonucleotide sequences between Nucleotides, BioSpring, Axolabs, Mermade 12 and GCRS representations.');
|
|
173
|
+
|
|
174
|
+
$(codesTablesDiv).hide();
|
|
175
|
+
const switchInput = ui.switchInput('Codes', false, (v: boolean) => (v) ?
|
|
176
|
+
$(codesTablesDiv).show() :
|
|
177
|
+
$(codesTablesDiv).hide(),
|
|
178
|
+
);
|
|
179
|
+
|
|
180
|
+
const topPanel = [
|
|
181
|
+
ui.iconFA('download', () => {
|
|
182
|
+
const result = sequenceToMolV3000(inputSequenceField.value.replace(/\s/g, ''), false, false,
|
|
183
|
+
inputFormatChoiceInput.value!);
|
|
184
|
+
const element = document.createElement('a');
|
|
185
|
+
element.setAttribute('href', 'data:text/plain;charset=utf-8,' + encodeURIComponent(result));
|
|
186
|
+
element.setAttribute('download', inputSequenceField.value.replace(/\s/g, '') + '.mol');
|
|
187
|
+
element.click();
|
|
188
|
+
}, 'Save .mol file'),
|
|
189
|
+
ui.iconFA('copy', () => {
|
|
190
|
+
navigator.clipboard.writeText(
|
|
191
|
+
sequenceToSmiles(inputSequenceField.value.replace(/\s/g, ''), false, inputFormatChoiceInput.value!))
|
|
192
|
+
.then(() => grok.shell.info(sequenceWasCopied));
|
|
193
|
+
}, 'Copy SMILES'),
|
|
194
|
+
switchInput.root,
|
|
195
|
+
];
|
|
196
|
+
|
|
197
|
+
const v = grok.shell.v;
|
|
198
|
+
const tabControl = grok.shell.sidebar;
|
|
199
|
+
tabControl.onTabChanged.subscribe((_) =>
|
|
200
|
+
v.setRibbonPanels([(tabControl.currentPane.name == 'MAIN') ? topPanel : []]));
|
|
201
|
+
v.setRibbonPanels([topPanel]);
|
|
202
|
+
|
|
203
|
+
return ui.box(
|
|
204
|
+
ui.splitH([
|
|
205
|
+
ui.splitV([
|
|
206
|
+
ui.panel([
|
|
207
|
+
appMainDescription,
|
|
208
|
+
ui.div([
|
|
209
|
+
ui.h1('Input sequence'),
|
|
210
|
+
ui.div([
|
|
211
|
+
inputSequenceField.root,
|
|
212
|
+
], 'input-base'),
|
|
213
|
+
], 'inputSequence'),
|
|
214
|
+
ui.div([inputFormatChoiceInput], {style: {padding: '5px 0'}}),
|
|
215
|
+
ui.block([
|
|
216
|
+
ui.h1('Output'),
|
|
217
|
+
outputTableDiv,
|
|
218
|
+
]),
|
|
219
|
+
moleculeSvgDiv,
|
|
220
|
+
], 'sequence'),
|
|
221
|
+
]),
|
|
222
|
+
codesTablesDiv,
|
|
223
|
+
], {style: {height: '100%', width: '100%'}}),
|
|
224
|
+
);
|
|
225
|
+
}
|