@datagrok/sequence-translator 1.1.0 → 1.1.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/CHANGELOG.md +34 -1
- package/dist/package-test.js +1 -1
- package/dist/package-test.js.map +1 -1
- package/dist/package.js +1 -1
- package/dist/package.js.map +1 -1
- package/files/codes-to-symbols.json +1 -0
- package/files/formats-to-helm.json +4 -0
- package/files/monomer-lib.json +48 -0
- package/package.json +9 -12
- package/src/model/axolabs/const.ts +2 -2
- package/src/model/const.ts +0 -1
- package/src/model/format-translation/conversion-utils.ts +3 -2
- package/src/model/format-translation/format-converter.ts +3 -1
- package/src/model/monomer-lib/lib-wrapper.ts +13 -0
- package/src/model/sequence-to-structure-utils/sdf-tab.ts +4 -1
- package/src/package-test.ts +3 -1
- package/src/package.ts +12 -2
- package/src/tests/const.ts +18 -11
- package/src/tests/formats-support.ts +40 -0
- package/src/tests/formats-to-helm.ts +53 -0
- package/src/tests/helm-to-nucleotides.ts +28 -0
- package/src/view/const/view.ts +1 -1
- package/src/view/tabs/axolabs.ts +33 -34
- package/src/view/tabs/main.ts +4 -4
- package/src/view/tabs/sdf.ts +30 -10
- package/src/view/view.ts +0 -2
- package/src/tests/smiles-tests.ts +0 -33
package/src/view/tabs/axolabs.ts
CHANGED
|
@@ -282,14 +282,14 @@ export class AxolabsTabUI {
|
|
|
282
282
|
lstMy.push(ent);//getShortName(ent));
|
|
283
283
|
}
|
|
284
284
|
|
|
285
|
-
let loadPattern = ui.choiceInput('Load
|
|
285
|
+
let loadPattern = ui.choiceInput('Load pattern', '', lstMy, (v: string) => parsePatternAndUpdateUi(v));
|
|
286
286
|
|
|
287
287
|
const currentUserName = (await grok.dapi.users.current()).friendlyName;
|
|
288
288
|
const otherUsers = 'Other users';
|
|
289
289
|
|
|
290
290
|
const patternListChoiceInput = ui.choiceInput('', currentUserName, [currentUserName, otherUsers], (v: string) => {
|
|
291
291
|
const currentList = v === currentUserName ? lstMy : lstOthers;
|
|
292
|
-
loadPattern = ui.choiceInput('Load
|
|
292
|
+
loadPattern = ui.choiceInput('Load pattern', '', currentList, (v: string) => parsePatternAndUpdateUi(v));
|
|
293
293
|
|
|
294
294
|
loadPattern.root.append(patternListChoiceInput.input);
|
|
295
295
|
loadPattern.root.append(loadPattern.input);
|
|
@@ -391,7 +391,7 @@ export class AxolabsTabUI {
|
|
|
391
391
|
const baseChoices: string[] = Object.keys(axolabsStyleMap);
|
|
392
392
|
const defaultBase: string = baseChoices[0];
|
|
393
393
|
const enumerateModifications = [defaultBase];
|
|
394
|
-
const sequenceBase = ui.choiceInput('Sequence
|
|
394
|
+
const sequenceBase = ui.choiceInput('Sequence basis', defaultBase, baseChoices, (v: string) => {
|
|
395
395
|
updateBases(v);
|
|
396
396
|
updateOutputExamples();
|
|
397
397
|
});
|
|
@@ -421,7 +421,7 @@ export class AxolabsTabUI {
|
|
|
421
421
|
));
|
|
422
422
|
const strandLengthInput = Object.fromEntries(STRANDS.map(
|
|
423
423
|
(strand) => {
|
|
424
|
-
const input = ui.intInput(`${strand}
|
|
424
|
+
const input = ui.intInput(`${STRAND_NAME[strand]} length`, DEFAULT_SEQUENCE_LENGTH, () => updateUiForNewSequenceLength());
|
|
425
425
|
input.setTooltip(`Length of ${STRAND_NAME[strand].toLowerCase()}, including overhangs`);
|
|
426
426
|
return [strand, input];
|
|
427
427
|
}));
|
|
@@ -437,7 +437,7 @@ export class AxolabsTabUI {
|
|
|
437
437
|
|
|
438
438
|
// todo: rename to strandColumnInput
|
|
439
439
|
const inputStrandColumn = Object.fromEntries(STRANDS.map((strand) => {
|
|
440
|
-
const input: StringInput = ui.choiceInput(`${STRAND_NAME[strand]}
|
|
440
|
+
const input: StringInput = ui.choiceInput(`${STRAND_NAME[strand]} column`, '', [], (colName: string) => {
|
|
441
441
|
validateStrandColumn(colName, strand);
|
|
442
442
|
strandVar[strand] = colName;
|
|
443
443
|
});
|
|
@@ -491,19 +491,21 @@ export class AxolabsTabUI {
|
|
|
491
491
|
// todo: remove ts-ignore
|
|
492
492
|
// @ts-ignore
|
|
493
493
|
outputExample[s].input.disabled = 'true';
|
|
494
|
+
let options = ui.div([
|
|
495
|
+
ui.button(ui.iconFA('copy', () => {}), () => {
|
|
496
|
+
navigator.clipboard.writeText(outputExample[s].value).then(() =>
|
|
497
|
+
grok.shell.info('Sequence was copied to clipboard'));
|
|
498
|
+
}),
|
|
499
|
+
], 'ui-input-options');
|
|
500
|
+
options.style.height = 'inherit';
|
|
494
501
|
outputExample[s].root.append(
|
|
495
|
-
|
|
496
|
-
ui.button(ui.iconFA('copy', () => {}), () => {
|
|
497
|
-
navigator.clipboard.writeText(outputExample[s].value).then(() =>
|
|
498
|
-
grok.shell.info('Sequence was copied to clipboard'));
|
|
499
|
-
}),
|
|
500
|
-
], 'ui-input-options'),
|
|
502
|
+
options
|
|
501
503
|
);
|
|
502
504
|
})
|
|
503
505
|
|
|
504
506
|
const inputIdColumnDiv = ui.div([]);
|
|
505
507
|
const svgDiv = ui.div([]);
|
|
506
|
-
const asExampleDiv = ui.div([]);
|
|
508
|
+
const asExampleDiv = ui.div([], 'ui-form ui-form-wide');
|
|
507
509
|
const appAxolabsDescription = ui.div([]);
|
|
508
510
|
const loadPatternDiv = ui.div([]);
|
|
509
511
|
const asModificationDiv = ui.div([]);
|
|
@@ -526,7 +528,7 @@ export class AxolabsTabUI {
|
|
|
526
528
|
|
|
527
529
|
const tables = ui.tableInput('Tables', grok.shell.tables[0], grok.shell.tables, (t: DG.DataFrame) => {
|
|
528
530
|
STRANDS.forEach((strand) => {
|
|
529
|
-
inputStrandColumn[strand] = ui.choiceInput(`${strand}
|
|
531
|
+
inputStrandColumn[strand] = ui.choiceInput(`${strand} column`, '', t.columns.names(), (colName: string) => {
|
|
530
532
|
validateStrandColumn(colName, strand);
|
|
531
533
|
strandVar[strand] = colName;
|
|
532
534
|
});
|
|
@@ -535,7 +537,7 @@ export class AxolabsTabUI {
|
|
|
535
537
|
})
|
|
536
538
|
|
|
537
539
|
// todo: unify with inputStrandColumn
|
|
538
|
-
const inputIdColumn = ui.choiceInput('ID
|
|
540
|
+
const inputIdColumn = ui.choiceInput('ID column', '', t.columns.names(), (colName: string) => {
|
|
539
541
|
validateIdsColumn(colName);
|
|
540
542
|
idVar = colName;
|
|
541
543
|
});
|
|
@@ -546,7 +548,7 @@ export class AxolabsTabUI {
|
|
|
546
548
|
|
|
547
549
|
// todo: unify with strandVar
|
|
548
550
|
let idVar = '';
|
|
549
|
-
const inputIdColumn = ui.choiceInput('ID
|
|
551
|
+
const inputIdColumn = ui.choiceInput('ID column', '', [], (colName: string) => {
|
|
550
552
|
validateIdsColumn(colName);
|
|
551
553
|
idVar = colName;
|
|
552
554
|
});
|
|
@@ -554,7 +556,7 @@ export class AxolabsTabUI {
|
|
|
554
556
|
|
|
555
557
|
updatePatternsList();
|
|
556
558
|
|
|
557
|
-
const createAsStrand = ui.boolInput('
|
|
559
|
+
const createAsStrand = ui.boolInput('Anti sense strand', true, (v: boolean) => {
|
|
558
560
|
modificationSection[AS].hidden = !v;
|
|
559
561
|
inputStrandColumnDiv[AS].hidden = !v;
|
|
560
562
|
asLengthDiv.hidden = !v;
|
|
@@ -565,7 +567,7 @@ export class AxolabsTabUI {
|
|
|
565
567
|
});
|
|
566
568
|
createAsStrand.setTooltip('Create antisense strand sections on SVG and table to the right');
|
|
567
569
|
|
|
568
|
-
const saveAs = ui.textInput('Save
|
|
570
|
+
const saveAs = ui.textInput('Save as', 'Pattern name', () => updateSvgScheme());
|
|
569
571
|
saveAs.setTooltip('Name Of New Pattern');
|
|
570
572
|
|
|
571
573
|
|
|
@@ -575,11 +577,11 @@ export class AxolabsTabUI {
|
|
|
575
577
|
|
|
576
578
|
const comment = ui.textInput('Comment', '', () => updateSvgScheme());
|
|
577
579
|
|
|
578
|
-
const savePatternButton = ui.
|
|
580
|
+
const savePatternButton = ui.bigButton('Save', () => {
|
|
579
581
|
if (saveAs.value !== '')
|
|
580
582
|
savePattern().then(() => grok.shell.info('Pattern saved'));
|
|
581
583
|
else {
|
|
582
|
-
const name = ui.stringInput('Enter
|
|
584
|
+
const name = ui.stringInput('Enter name', '');
|
|
583
585
|
ui.dialog('Pattern Name')
|
|
584
586
|
.add(name.root)
|
|
585
587
|
.onOK(() => {
|
|
@@ -590,7 +592,7 @@ export class AxolabsTabUI {
|
|
|
590
592
|
}
|
|
591
593
|
});
|
|
592
594
|
|
|
593
|
-
const convertSequenceButton = ui.
|
|
595
|
+
const convertSequenceButton = ui.bigButton('Convert', () => {
|
|
594
596
|
const condition = [true, createAsStrand.value];
|
|
595
597
|
if (STRANDS.some((s, i) => condition[i] && strandVar[s] === ''))
|
|
596
598
|
grok.shell.info('Please select table and columns on which to apply pattern');
|
|
@@ -633,22 +635,19 @@ export class AxolabsTabUI {
|
|
|
633
635
|
inputExample[SS].root,
|
|
634
636
|
outputExample[SS].root,
|
|
635
637
|
asExampleDiv,
|
|
636
|
-
], 'ui-form');
|
|
638
|
+
], 'ui-form ui-form-wide');
|
|
637
639
|
|
|
638
|
-
const inputsSection = ui.
|
|
640
|
+
const inputsSection = ui.block50([
|
|
639
641
|
ui.h1('Convert options'),
|
|
640
|
-
|
|
641
|
-
|
|
642
|
-
|
|
643
|
-
|
|
644
|
-
ui.divH([
|
|
645
|
-
inputStrandColumnDiv[AS],
|
|
646
|
-
inputIdColumnDiv,
|
|
647
|
-
]),
|
|
642
|
+
tables.root,
|
|
643
|
+
inputStrandColumnDiv[SS],
|
|
644
|
+
inputStrandColumnDiv[AS],
|
|
645
|
+
inputIdColumnDiv,
|
|
648
646
|
ui.buttonsInput([
|
|
649
647
|
convertSequenceButton,
|
|
650
648
|
]),
|
|
651
|
-
]
|
|
649
|
+
]);
|
|
650
|
+
inputsSection.classList.add('ui-form');
|
|
652
651
|
|
|
653
652
|
const downloadButton = ui.button('Download', () => svg.saveSvgAsPng(document.getElementById('mySvg'), saveAs.value,
|
|
654
653
|
{backgroundColor: 'white'}));
|
|
@@ -665,7 +664,7 @@ export class AxolabsTabUI {
|
|
|
665
664
|
ui.h1('Pattern options'),
|
|
666
665
|
]),
|
|
667
666
|
ui.divH([
|
|
668
|
-
ui.
|
|
667
|
+
ui.block([
|
|
669
668
|
strandLengthInput[SS].root,
|
|
670
669
|
asLengthDiv,
|
|
671
670
|
sequenceBase.root,
|
|
@@ -676,7 +675,7 @@ export class AxolabsTabUI {
|
|
|
676
675
|
savePatternButton,
|
|
677
676
|
]),
|
|
678
677
|
], 'ui-form'),
|
|
679
|
-
ui.
|
|
678
|
+
ui.block([
|
|
680
679
|
createAsStrand.root,
|
|
681
680
|
fullyPto.root,
|
|
682
681
|
firstPto[SS].root,
|
|
@@ -685,7 +684,7 @@ export class AxolabsTabUI {
|
|
|
685
684
|
terminalModification[SS][THREE_PRIME].root,
|
|
686
685
|
asModificationDiv,
|
|
687
686
|
], 'ui-form'),
|
|
688
|
-
]
|
|
687
|
+
]),
|
|
689
688
|
], 'ui-form'),
|
|
690
689
|
inputsSection,
|
|
691
690
|
exampleSection,
|
package/src/view/tabs/main.ts
CHANGED
|
@@ -58,9 +58,9 @@ export class MainTabUI {
|
|
|
58
58
|
const sequenceColoredInput = new ColoredTextInput(this.sequenceInputBase, highlightInvalidSubsequence);
|
|
59
59
|
|
|
60
60
|
const downloadMolfileButton = ui.button(
|
|
61
|
-
'Get
|
|
61
|
+
'Get SDF',
|
|
62
62
|
() => { this.saveMolfile(); },
|
|
63
|
-
'Save
|
|
63
|
+
'Save structure as SDF');
|
|
64
64
|
|
|
65
65
|
const copySmilesButton = ui.button(
|
|
66
66
|
'Copy SMILES',
|
|
@@ -100,8 +100,8 @@ export class MainTabUI {
|
|
|
100
100
|
|
|
101
101
|
private saveMolfile(): void {
|
|
102
102
|
const result = (new SequenceToMolfileConverter(this.sequence, false,
|
|
103
|
-
this.formatChoiceInput.value!)).convert();
|
|
104
|
-
download(this.sequence + '.
|
|
103
|
+
this.formatChoiceInput.value!)).convert() + '\n$$$$';
|
|
104
|
+
download(this.sequence + '.sdf', encodeURIComponent(result));
|
|
105
105
|
}
|
|
106
106
|
|
|
107
107
|
private copySmiles(): void {
|
package/src/view/tabs/sdf.ts
CHANGED
|
@@ -26,9 +26,10 @@ const STRANDS = ['ss', 'as', 'as2'] as const;
|
|
|
26
26
|
export class SdfTabUI {
|
|
27
27
|
constructor() {
|
|
28
28
|
this.onInput = new rxjs.Subject<string>();
|
|
29
|
+
this.onInvalidInput = new rxjs.Subject<string>();
|
|
29
30
|
this.inputBase = Object.fromEntries(
|
|
30
31
|
STRANDS.map(
|
|
31
|
-
(key) => [key, ui.textInput('',
|
|
32
|
+
(key) => [key, ui.textInput('', '', () => { this.onInput.next(); })]
|
|
32
33
|
)
|
|
33
34
|
);
|
|
34
35
|
this.useChiralInput = ui.boolInput('Use chiral', true);
|
|
@@ -43,9 +44,14 @@ export class SdfTabUI {
|
|
|
43
44
|
DG.debounce<string>(this.onInput, 300).subscribe(async () => {
|
|
44
45
|
await this.updateMoleculeImg();
|
|
45
46
|
});
|
|
47
|
+
|
|
48
|
+
DG.debounce<string>(this.onInvalidInput, 1000).subscribe(async () => {
|
|
49
|
+
grok.shell.warning('Insert Sense strand');
|
|
50
|
+
});
|
|
46
51
|
}
|
|
47
52
|
|
|
48
53
|
private onInput: rxjs.Subject<string>;
|
|
54
|
+
private onInvalidInput: rxjs.Subject<string>;
|
|
49
55
|
private useChiralInput: DG.InputBase<boolean | null>;
|
|
50
56
|
private saveAllStrandsInput: DG.InputBase<boolean | null>;
|
|
51
57
|
private inputBase: {[key: string]: DG.InputBase<string>};
|
|
@@ -90,15 +96,21 @@ export class SdfTabUI {
|
|
|
90
96
|
|
|
91
97
|
const directionChoiceInput = Object.fromEntries(
|
|
92
98
|
STRANDS.map(
|
|
93
|
-
(key) =>
|
|
94
|
-
|
|
95
|
-
|
|
99
|
+
(key, idx) => {
|
|
100
|
+
const selected = (idx === 0) ? DIRECTION.STRAIGHT : DIRECTION.INVERSE;
|
|
101
|
+
return [key, ui.choiceInput(
|
|
102
|
+
`${key.toUpperCase()} direction`, selected, [DIRECTION.STRAIGHT, DIRECTION.INVERSE]
|
|
103
|
+
)]
|
|
104
|
+
}
|
|
96
105
|
)
|
|
97
106
|
);
|
|
98
107
|
|
|
99
|
-
STRANDS.forEach((strand) => {
|
|
108
|
+
STRANDS.forEach((strand, idx) => {
|
|
100
109
|
directionChoiceInput[strand].onChanged(() => {
|
|
101
|
-
|
|
110
|
+
let value = directionChoiceInput[strand].value === DIRECTION.INVERSE;
|
|
111
|
+
// warning: the next line is necessary until the legacy notion of direction used in the molfile generation gets fixed
|
|
112
|
+
if (idx > 0) { value = !value; }
|
|
113
|
+
this.directionInversion[strand] = value;
|
|
102
114
|
this.onInput.next();
|
|
103
115
|
});
|
|
104
116
|
});
|
|
@@ -142,14 +154,22 @@ export class SdfTabUI {
|
|
|
142
154
|
|
|
143
155
|
private getStrandData() {
|
|
144
156
|
return Object.fromEntries(
|
|
145
|
-
STRANDS.map((strand) =>
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
157
|
+
STRANDS.map((strand, idx) => {
|
|
158
|
+
let invert = this.directionInversion[strand];
|
|
159
|
+
return [strand, {
|
|
160
|
+
strand: this.inputBase[strand].value.replace(/\s*/g, ''),
|
|
161
|
+
invert: invert
|
|
162
|
+
}]
|
|
163
|
+
})
|
|
149
164
|
);
|
|
150
165
|
}
|
|
151
166
|
|
|
152
167
|
private getMolfile(ss: StrandData, as: StrandData, as2: StrandData): string {
|
|
168
|
+
if (ss.strand === '' && (as.strand !== '' || as2.strand !== '')) {
|
|
169
|
+
this.onInvalidInput.next();
|
|
170
|
+
return '';
|
|
171
|
+
}
|
|
172
|
+
|
|
153
173
|
return getLinkedMolfile(ss, as, as2, this.useChiralInput.value!);
|
|
154
174
|
}
|
|
155
175
|
|
package/src/view/view.ts
CHANGED
|
@@ -85,9 +85,7 @@ class TabLayout {
|
|
|
85
85
|
if (control.currentPane.name !== MAIN_TAB)
|
|
86
86
|
this.urlRouter.searchParams.delete('seq');
|
|
87
87
|
else {
|
|
88
|
-
console.log('sequence:', this.mainTab.sequence);
|
|
89
88
|
this.urlRouter.searchParams.set('seq', this.mainTab.sequence);
|
|
90
|
-
console.log('searchParams:', Object.entries(this.urlRouter.searchParams));
|
|
91
89
|
}
|
|
92
90
|
this.urlRouter.updatePath(control);
|
|
93
91
|
});
|
|
@@ -1,33 +0,0 @@
|
|
|
1
|
-
/* Do not change these import lines to match external modules in webpack configuration */
|
|
2
|
-
import * as grok from 'datagrok-api/grok';
|
|
3
|
-
import * as ui from 'datagrok-api/ui';
|
|
4
|
-
import * as DG from 'datagrok-api/dg';
|
|
5
|
-
|
|
6
|
-
import {before, category, expect, test} from '@datagrok-libraries/utils/src/test';
|
|
7
|
-
import {DEFAULT_FORMATS} from '../model/const';
|
|
8
|
-
import {getJsonData} from '../model/data-loading-utils/json-loader';
|
|
9
|
-
import {axolabsToSmiles} from './const';
|
|
10
|
-
import {_package} from '../package';
|
|
11
|
-
import {SequenceToMolfileConverter} from '../model/sequence-to-structure-utils/sequence-to-molfile';
|
|
12
|
-
|
|
13
|
-
function getSmiles(strand: string, format: string): string {
|
|
14
|
-
const molfile = (new SequenceToMolfileConverter(strand, false, format)).convert();
|
|
15
|
-
return DG.chem.convert(molfile, DG.chem.Notation.MolBlock, DG.chem.Notation.Smiles);
|
|
16
|
-
}
|
|
17
|
-
|
|
18
|
-
const AXOLABS = DEFAULT_FORMATS.AXOLABS;
|
|
19
|
-
|
|
20
|
-
category('Axolabs to smiles', () => {
|
|
21
|
-
before(async () => {
|
|
22
|
-
await getJsonData();
|
|
23
|
-
await _package.initMonomerLib();
|
|
24
|
-
});
|
|
25
|
-
|
|
26
|
-
for (const strand of Object.keys(axolabsToSmiles)) {
|
|
27
|
-
test(`${strand} to SMILES`, async () => {
|
|
28
|
-
const expected = axolabsToSmiles[strand];
|
|
29
|
-
const result = getSmiles(strand, AXOLABS);
|
|
30
|
-
expect(result, expected);
|
|
31
|
-
});
|
|
32
|
-
}
|
|
33
|
-
});
|