@datagrok/sequence-translator 1.2.2 → 1.2.4
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 +1 -0
- 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/package.json +1 -1
- package/src/model/parsing-validation/format-handler.ts +147 -0
- package/src/model/translator-app/format-converter.ts +68 -0
- package/src/package.ts +3 -3
- package/src/tests/formats-support.ts +1 -1
- package/src/tests/formats-to-helm.ts +1 -1
- package/src/tests/helm-to-nucleotides.ts +1 -1
- package/src/view/apps/oligo-pattern.ts +6 -57
- package/src/view/apps/oligo-structure.ts +17 -6
- package/src/view/apps/oligo-translator.ts +11 -5
- package/src/view/style/translator-app.css +2 -2
- package/src/view/utils/colored-input/colored-text-input.ts +2 -2
- package/src/view/utils/draw-molecule.ts +1 -1
- package/src/model/format-translation/format-converter.ts +0 -109
- /package/src/model/{axolabs → pattern-app}/const.ts +0 -0
- /package/src/model/{axolabs → pattern-app}/draw-svg.ts +0 -0
- /package/src/model/{axolabs → pattern-app}/helpers.ts +0 -0
- /package/src/model/{axolabs/axolabs-tab.ts → pattern-app/oligo-pattern.ts} +0 -0
- /package/src/model/{sequence-to-structure-utils → structure-app}/const.ts +0 -0
- /package/src/model/{sequence-to-structure-utils → structure-app}/mol-transformations.ts +0 -0
- /package/src/model/{sequence-to-structure-utils → structure-app}/monomer-code-parser.ts +0 -0
- /package/src/model/{sequence-to-structure-utils/sdf-tab.ts → structure-app/oligo-structure.ts} +0 -0
- /package/src/model/{sequence-to-structure-utils → structure-app}/sequence-to-molfile.ts +0 -0
- /package/src/model/{format-translation → translator-app}/const.ts +0 -0
- /package/src/model/{format-translation → translator-app}/conversion-utils.ts +0 -0
package/package.json
CHANGED
|
@@ -0,0 +1,147 @@
|
|
|
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 {codesToHelmDictionary} from '../data-loading-utils/json-loader';
|
|
7
|
+
import {CodesInfo} from '../data-loading-utils/types';
|
|
8
|
+
import {DEFAULT_FORMATS} from '../const';
|
|
9
|
+
import {GROUP_TYPE, PHOSPHATE_SYMBOL} from '../translator-app/const';
|
|
10
|
+
|
|
11
|
+
const inverseLengthComparator = (a: string, b: string) => b.length - a.length;
|
|
12
|
+
|
|
13
|
+
export class FormatHandler {
|
|
14
|
+
constructor() {
|
|
15
|
+
this.formats = this.getFormats();
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
/** Includes all formats except HELM (the "default" one) */
|
|
19
|
+
private formats: string[];
|
|
20
|
+
|
|
21
|
+
/** All format names except HELM (the "default" one) */
|
|
22
|
+
getFormatNames(): string[] {
|
|
23
|
+
return this.formats.sort();
|
|
24
|
+
};
|
|
25
|
+
|
|
26
|
+
getCodesByFormat(format: string): string[] {
|
|
27
|
+
this.validateFormat(format);
|
|
28
|
+
|
|
29
|
+
if (this.isHelm(format))
|
|
30
|
+
throw new Error(`Codes cannot be obtained for HELM`);
|
|
31
|
+
return this.getFormatCodes(format);
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
getHelmToFormatDict(format: string): {[key: string]: string} {
|
|
35
|
+
this.validateFormat(format);
|
|
36
|
+
|
|
37
|
+
const codesInfoObject = codesToHelmDictionary[format] as CodesInfo;
|
|
38
|
+
const dict = getHelmToCodeDict(codesInfoObject);
|
|
39
|
+
return dict;
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
getFormatToHelmDict(format: string): {[key: string]: string} {
|
|
43
|
+
this.validateFormat(format);
|
|
44
|
+
|
|
45
|
+
const codesInfoObject = codesToHelmDictionary[format] as CodesInfo;
|
|
46
|
+
const dict = Object.assign({}, ...Object.values(codesInfoObject)) as {[code: string]: string};
|
|
47
|
+
return dict;
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
/** Get helm codes for the specified format */
|
|
51
|
+
getTargetFormatHelmCodes(format: string): string[] {
|
|
52
|
+
this.validateFormat(format);
|
|
53
|
+
|
|
54
|
+
const dict = this.getHelmToFormatDict(format);
|
|
55
|
+
const helmCodes = Object.keys(dict).sort(inverseLengthComparator);
|
|
56
|
+
return helmCodes;
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
getTargetFormatHelmCodesRegExp(format: string): RegExp {
|
|
60
|
+
this.validateFormat(format);
|
|
61
|
+
|
|
62
|
+
const helmCodes = this.getTargetFormatHelmCodes(format);
|
|
63
|
+
const helmRegExp = new RegExp(getRegExpPattern(helmCodes) + '|.', 'g');
|
|
64
|
+
return helmRegExp;
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
getFormatRegExp(format: string): RegExp {
|
|
68
|
+
this.validateFormat(format);
|
|
69
|
+
|
|
70
|
+
if (this.isHelm(format))
|
|
71
|
+
throw new Error(`Helm RegExp can be built for non-HELM target formats`);
|
|
72
|
+
return this.getNonHelmFormatRegExp(format);
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
getPhosphateHelmCodesRegExp(format: string): RegExp {
|
|
76
|
+
this.validateFormat(format);
|
|
77
|
+
|
|
78
|
+
const codesInfoObject = codesToHelmDictionary[format] as CodesInfo;
|
|
79
|
+
const phosphateHELMCodes = Array.from(
|
|
80
|
+
new Set(Object.values(codesInfoObject[GROUP_TYPE.LINKAGE]))
|
|
81
|
+
).sort(inverseLengthComparator);
|
|
82
|
+
const phosphateHELMPattern = getRegExpPattern(phosphateHELMCodes);
|
|
83
|
+
const phosphateRegExp = new RegExp(`${PHOSPHATE_SYMBOL}\.(${phosphateHELMPattern})`, 'g');
|
|
84
|
+
return phosphateRegExp;
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
isValidFormat(format: string): boolean {
|
|
88
|
+
return this.formats.includes(format);
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
private getFormats(): string[] {
|
|
92
|
+
return Object.keys(codesToHelmDictionary);
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
private validateFormat(format: string) {
|
|
96
|
+
if (!this.isValidFormat(format))
|
|
97
|
+
throw new Error(`Invalid format: ${format}`);
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
private isHelm(format: string): boolean {
|
|
101
|
+
return format === DEFAULT_FORMATS.HELM;
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
private getFormatCodes(format: string): string[] {
|
|
105
|
+
const dict = this.getFormatToHelmDict(format);
|
|
106
|
+
const formatCodes = Object.keys(dict).sort(inverseLengthComparator);
|
|
107
|
+
return formatCodes;
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
private getNonHelmFormatRegExp(format: string): RegExp {
|
|
111
|
+
const formatCodes = this.getCodesByFormat(format);
|
|
112
|
+
const formatRegExp = new RegExp(getRegExpPattern(formatCodes) + '|\\([^()]*\\)|.', 'g'); // the added group before '|.' is to avoid mismatch inside parenths
|
|
113
|
+
return formatRegExp;
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
export function getRegExpPattern(arr: string[]): string {
|
|
118
|
+
const negativeLookBehind = '(?<!\\([^()]*)'; // not '(' followed by non-parenths
|
|
119
|
+
const negativeLookAhead = '(?![^()]*\\))'; // not ')' preceded by non-parenths
|
|
120
|
+
const escaped = arr.map((key) => key.replace(/[.*+?^${}()|[\]\\]/g, '\\$&'))
|
|
121
|
+
.map((key) => {
|
|
122
|
+
if (!key.includes('(') && !key.includes(')'))
|
|
123
|
+
return `${negativeLookBehind}${key}${negativeLookAhead}`;
|
|
124
|
+
return key;
|
|
125
|
+
});
|
|
126
|
+
const result = escaped.join('|');
|
|
127
|
+
return result;
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
function getHelmToCodeDict(infoObj: CodesInfo) {
|
|
131
|
+
const result: {[key: string]: string | string[]} = {};
|
|
132
|
+
Object.values(infoObj).forEach((obj: {[code: string]: string}) => {
|
|
133
|
+
Object.entries(obj).forEach(([code, helm]) => {
|
|
134
|
+
const key = helm.replace(/\)p/g, ')').replace(/\]p/g, ']');
|
|
135
|
+
if (result[key] === undefined) {
|
|
136
|
+
result[key] = [code];
|
|
137
|
+
} else {
|
|
138
|
+
(result[key] as string[]).push(code);
|
|
139
|
+
}
|
|
140
|
+
})
|
|
141
|
+
});
|
|
142
|
+
Object.entries(result).forEach(([key, value]) => {
|
|
143
|
+
const sorted = (value as string[]).sort(inverseLengthComparator);
|
|
144
|
+
result[key] = sorted[0] as string;
|
|
145
|
+
})
|
|
146
|
+
return result as {[key: string]: string};
|
|
147
|
+
}
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
import * as DG from 'datagrok-api/dg';
|
|
2
|
+
import {DEFAULT_FORMATS} from '../const';
|
|
3
|
+
import {PHOSPHATE_SYMBOL, UNKNOWN_SYMBOL} from './const';
|
|
4
|
+
import {FormatHandler, getRegExpPattern} from '../parsing-validation/format-handler';
|
|
5
|
+
|
|
6
|
+
const HELM_WRAPPER = {
|
|
7
|
+
LEFT: 'RNA1{',
|
|
8
|
+
RIGHT: '}$$$$',
|
|
9
|
+
};
|
|
10
|
+
|
|
11
|
+
export class FormatConverter {
|
|
12
|
+
constructor(private readonly sequence: string, private readonly sourceFormat: string) { };
|
|
13
|
+
|
|
14
|
+
private formats = new FormatHandler();
|
|
15
|
+
|
|
16
|
+
convertTo(targetFormat: string): string {
|
|
17
|
+
const formats = this.formats.getFormatNames();
|
|
18
|
+
|
|
19
|
+
if (this.sourceFormat === DEFAULT_FORMATS.HELM && formats.includes(targetFormat))
|
|
20
|
+
return this.helmToFormat(this.sequence, targetFormat);
|
|
21
|
+
else if (formats.includes(this.sourceFormat) && targetFormat === DEFAULT_FORMATS.HELM)
|
|
22
|
+
return this.formatToHelm(this.sequence, this.sourceFormat);
|
|
23
|
+
else if ([this.sourceFormat, targetFormat].every((el) => formats.includes(el))) {
|
|
24
|
+
const helm = this.formatToHelm(this.sequence, this.sourceFormat);
|
|
25
|
+
return this.helmToFormat(helm, targetFormat);
|
|
26
|
+
}
|
|
27
|
+
else {
|
|
28
|
+
throw new Error (`ST: unsupported translation direction ${this.sourceFormat} -> ${targetFormat}`);
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
private helmToFormat(helmSequence: string, targetFormat: string): string {
|
|
33
|
+
const wrapperRegExp = new RegExp(getRegExpPattern(Object.values(HELM_WRAPPER)), 'g')
|
|
34
|
+
let result = helmSequence.replace(wrapperRegExp, '');
|
|
35
|
+
|
|
36
|
+
const dict = this.formats.getHelmToFormatDict(targetFormat);
|
|
37
|
+
const helmCodes = this.formats.getTargetFormatHelmCodes(targetFormat);
|
|
38
|
+
const helmRegExp = this.formats.getTargetFormatHelmCodesRegExp(targetFormat);
|
|
39
|
+
|
|
40
|
+
result = result.replace(helmRegExp, (match) => {
|
|
41
|
+
return helmCodes.includes(match) ? dict[match] :
|
|
42
|
+
(match === 'p' || match === '.') ? match : '?';
|
|
43
|
+
}).replace(/\?+/g, UNKNOWN_SYMBOL).replace(/p\.|\./g, '');
|
|
44
|
+
result = result.replace(/<empty>/g, '');
|
|
45
|
+
// remove double slash in LCMS codes
|
|
46
|
+
result = result.replace(/\/\//g, '/');
|
|
47
|
+
return result;
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
private formatToHelm(sequence: string, sourceFormat: string): string {
|
|
51
|
+
const dict = this.formats.getFormatToHelmDict(sourceFormat);
|
|
52
|
+
const formatCodes = this.formats.getCodesByFormat(sourceFormat);
|
|
53
|
+
const formatRegExp = this.formats.getFormatRegExp(sourceFormat);
|
|
54
|
+
const phosphateRegExp = this.formats.getPhosphateHelmCodesRegExp(sourceFormat);
|
|
55
|
+
|
|
56
|
+
let helm = sequence.replace(formatRegExp, (match) => {
|
|
57
|
+
const result = formatCodes.includes(match) ? dict[match] + '.' : '?';
|
|
58
|
+
return result;
|
|
59
|
+
});
|
|
60
|
+
helm = helm.replace(/\?+/g, `${UNKNOWN_SYMBOL}.`);
|
|
61
|
+
helm = helm.slice(0, -1); // strip last dot
|
|
62
|
+
if (helm[helm.length - 1] === PHOSPHATE_SYMBOL)
|
|
63
|
+
helm = helm.slice(0, -1);
|
|
64
|
+
helm = helm.replace(phosphateRegExp, (match, group) => group);
|
|
65
|
+
helm = helm.replace(/<empty>/g, '');
|
|
66
|
+
return `${HELM_WRAPPER.LEFT + helm + HELM_WRAPPER.RIGHT}`;
|
|
67
|
+
}
|
|
68
|
+
}
|
package/src/package.ts
CHANGED
|
@@ -8,13 +8,13 @@ import {LIB_PATH, DEFAULT_LIB_FILENAME} from './model/data-loading-utils/const';
|
|
|
8
8
|
import {IMonomerLib} from '@datagrok-libraries/bio/src/types';
|
|
9
9
|
import {getMonomerLibHelper, IMonomerLibHelper} from '@datagrok-libraries/bio/src/monomer-works/monomer-utils';
|
|
10
10
|
import {getJsonData} from './model/data-loading-utils/json-loader';
|
|
11
|
-
import {SequenceToMolfileConverter} from './model/
|
|
12
|
-
import {linkStrandsV3000} from './model/
|
|
11
|
+
import {SequenceToMolfileConverter} from './model/structure-app/sequence-to-molfile';
|
|
12
|
+
import {linkStrandsV3000} from './model/structure-app/mol-transformations';
|
|
13
13
|
import {MonomerLibWrapper} from './model/monomer-lib/lib-wrapper';
|
|
14
14
|
import {FormatDetector} from './model/parsing-validation/format-detector';
|
|
15
15
|
import {SequenceValidator} from './model/parsing-validation/sequence-validator';
|
|
16
16
|
import {demoOligoTranslatorUI, demoOligoPatternUI, demoOligoStructureUI} from './demo/demo-st-ui';
|
|
17
|
-
import {FormatConverter} from './model/
|
|
17
|
+
import {FormatConverter} from './model/translator-app/format-converter';
|
|
18
18
|
import {APP} from './view/const/ui';
|
|
19
19
|
import {getExternalAppViewFactories} from './plugins/mermade';
|
|
20
20
|
|
|
@@ -8,7 +8,7 @@ import {DEFAULT_FORMATS} from '../model/const';
|
|
|
8
8
|
import {getJsonData} from '../model/data-loading-utils/json-loader';
|
|
9
9
|
import {formatsToHelm} from './const';
|
|
10
10
|
import {SequenceValidator} from '../model/parsing-validation/sequence-validator';
|
|
11
|
-
import {getTranslatedSequences} from '../model/
|
|
11
|
+
import {getTranslatedSequences} from '../model/translator-app/conversion-utils';
|
|
12
12
|
import {_package} from '../package';
|
|
13
13
|
|
|
14
14
|
function getTranslationObject(sequence: string, format: string): {[format: string]: string} {
|
|
@@ -5,7 +5,7 @@ import * as DG from 'datagrok-api/dg';
|
|
|
5
5
|
|
|
6
6
|
import {before, category, expect, test} from '@datagrok-libraries/utils/src/test';
|
|
7
7
|
import {DEFAULT_FORMATS} from '../model/const';
|
|
8
|
-
import {FormatConverter} from '../model/
|
|
8
|
+
import {FormatConverter} from '../model/translator-app/format-converter';
|
|
9
9
|
import {getJsonData} from '../model/data-loading-utils/json-loader';
|
|
10
10
|
import {formatsToHelm} from './const';
|
|
11
11
|
import {_package} from '../package';
|
|
@@ -4,7 +4,7 @@ import * as ui from 'datagrok-api/ui';
|
|
|
4
4
|
import * as DG from 'datagrok-api/dg';
|
|
5
5
|
|
|
6
6
|
import {before, category, expect, test} from '@datagrok-libraries/utils/src/test';
|
|
7
|
-
import {getNucleotidesSequence} from '../model/
|
|
7
|
+
import {getNucleotidesSequence} from '../model/translator-app/conversion-utils';
|
|
8
8
|
import {getJsonData} from '../model/data-loading-utils/json-loader';
|
|
9
9
|
import {helmToNucleotides} from './const';
|
|
10
10
|
import {_package} from '../package';
|
|
@@ -5,11 +5,11 @@ import * as DG from 'datagrok-api/dg';
|
|
|
5
5
|
|
|
6
6
|
import {axolabsStyleMap} from '../../model/data-loading-utils/json-loader';
|
|
7
7
|
import {
|
|
8
|
-
DEFAULT_PTO, DEFAULT_SEQUENCE_LENGTH, MAX_SEQUENCE_LENGTH, USER_STORAGE_KEY,
|
|
9
|
-
} from '../../model/
|
|
10
|
-
import {isOverhang} from '../../model/
|
|
11
|
-
import {generateExample, translateSequence, getShortName, isCurrentUserCreatedThisPattern, findDuplicates, addColumnWithIds, addColumnWithTranslatedSequences} from '../../model/
|
|
12
|
-
import {drawAxolabsPattern} from '../../model/
|
|
8
|
+
DEFAULT_PTO, DEFAULT_SEQUENCE_LENGTH, MAX_SEQUENCE_LENGTH, USER_STORAGE_KEY, SS, AS, STRAND_NAME, STRANDS, TERMINAL, TERMINAL_KEYS, THREE_PRIME, FIVE_PRIME, JSON_FIELD as FIELD
|
|
9
|
+
} from '../../model/pattern-app/const';
|
|
10
|
+
import {isOverhang} from '../../model/pattern-app/helpers';
|
|
11
|
+
import {generateExample, translateSequence, getShortName, isCurrentUserCreatedThisPattern, findDuplicates, addColumnWithIds, addColumnWithTranslatedSequences} from '../../model//pattern-app/oligo-pattern';
|
|
12
|
+
import {drawAxolabsPattern} from '../../model/pattern-app/draw-svg';
|
|
13
13
|
// todo: remove ts-ignore
|
|
14
14
|
//@ts-ignore
|
|
15
15
|
import * as svg from 'save-svg-as-png';
|
|
@@ -530,7 +530,6 @@ export class PatternLayoutHandler {
|
|
|
530
530
|
const inputIdColumnDiv = ui.div([]);
|
|
531
531
|
const svgDiv = ui.div([]);
|
|
532
532
|
const asExampleDiv = ui.div([], 'ui-form ui-form-wide');
|
|
533
|
-
const appAxolabsDescription = ui.div([]);
|
|
534
533
|
const loadPatternDiv = ui.div([]);
|
|
535
534
|
const asModificationDiv = ui.form([]);
|
|
536
535
|
const isEnumerateModificationsDiv = ui.divH([
|
|
@@ -678,7 +677,7 @@ export class PatternLayoutHandler {
|
|
|
678
677
|
{backgroundColor: 'white'}), 'Download pattern as PNG image', '');
|
|
679
678
|
|
|
680
679
|
const editPattern = ui.link('Edit pattern', ()=>{
|
|
681
|
-
ui.dialog('Edit
|
|
680
|
+
ui.dialog('Edit pattern')
|
|
682
681
|
.add(ui.divV([
|
|
683
682
|
ui.h1('PTO'),
|
|
684
683
|
ui.divH([
|
|
@@ -695,56 +694,6 @@ export class PatternLayoutHandler {
|
|
|
695
694
|
.show()
|
|
696
695
|
}, 'Edit pattern', '');
|
|
697
696
|
|
|
698
|
-
const mainSection = ui.panel([
|
|
699
|
-
ui.block([
|
|
700
|
-
svgDiv,
|
|
701
|
-
], {style: {overflowX: 'scroll'}}),
|
|
702
|
-
downloadButton,
|
|
703
|
-
isEnumerateModificationsDiv,
|
|
704
|
-
ui.div([
|
|
705
|
-
ui.div([
|
|
706
|
-
ui.divH([
|
|
707
|
-
ui.h1('Pattern options'),
|
|
708
|
-
]),
|
|
709
|
-
ui.divH([
|
|
710
|
-
ui.block([
|
|
711
|
-
strandLengthInput[SS].root,
|
|
712
|
-
asLengthDiv,
|
|
713
|
-
sequenceBase.root,
|
|
714
|
-
comment.root,
|
|
715
|
-
loadPatternDiv,
|
|
716
|
-
saveAs.root,
|
|
717
|
-
ui.buttonsInput([
|
|
718
|
-
//savePatternButton,
|
|
719
|
-
]),
|
|
720
|
-
], 'ui-form'),
|
|
721
|
-
ui.block([
|
|
722
|
-
createAsStrand.root,
|
|
723
|
-
fullyPto.root,
|
|
724
|
-
firstPto[SS].root,
|
|
725
|
-
firstPto[AS].root,
|
|
726
|
-
terminalModification[SS][FIVE_PRIME].root,
|
|
727
|
-
terminalModification[SS][THREE_PRIME].root,
|
|
728
|
-
asModificationDiv,
|
|
729
|
-
], 'ui-form'),
|
|
730
|
-
]),
|
|
731
|
-
], 'ui-form'),
|
|
732
|
-
inputsSection,
|
|
733
|
-
exampleSection,
|
|
734
|
-
], {style: {flexWrap: 'wrap'}}),
|
|
735
|
-
]);
|
|
736
|
-
|
|
737
|
-
const info = ui.info(
|
|
738
|
-
[
|
|
739
|
-
ui.divText('\n How to define new pattern:', {style: {'font-weight': 'bolder'}}),
|
|
740
|
-
ui.divText('1. Choose table and columns with sense and antisense strands'),
|
|
741
|
-
ui.divText('2. Choose lengths of both strands by editing checkboxes below'),
|
|
742
|
-
ui.divText('3. Choose basis and PTO status for each nucleotide'),
|
|
743
|
-
ui.divText('4. Set additional modifications for sequence edges'),
|
|
744
|
-
ui.divText('5. Press \'Convert Sequences\' button'),
|
|
745
|
-
ui.divText('This will add the result column(s) to the right of the table'),
|
|
746
|
-
], 'Create and apply Axolabs translation patterns.',
|
|
747
|
-
);
|
|
748
697
|
strandLengthInput[SS].addCaption('Length');
|
|
749
698
|
|
|
750
699
|
return ui.splitH([
|
|
@@ -10,10 +10,10 @@ import $ from 'cash-dom';
|
|
|
10
10
|
import {errorToConsole} from '@datagrok-libraries/utils/src/to-console';
|
|
11
11
|
|
|
12
12
|
import {highlightInvalidSubsequence} from '../utils/colored-input/input-painters';
|
|
13
|
-
import {getLinkedMolfile, saveSdf} from '../../model/
|
|
13
|
+
import {getLinkedMolfile, saveSdf} from '../../model/structure-app/oligo-structure';
|
|
14
14
|
import {ColoredTextInput} from '../utils/colored-input/colored-text-input';
|
|
15
15
|
import {MoleculeImage} from '../utils/molecule-img';
|
|
16
|
-
import {StrandData} from '../../model/
|
|
16
|
+
import {StrandData} from '../../model/structure-app/oligo-structure';
|
|
17
17
|
|
|
18
18
|
const enum DIRECTION {
|
|
19
19
|
STRAIGHT = '5′ → 3′',
|
|
@@ -63,10 +63,10 @@ export class StructureLayoutHandler {
|
|
|
63
63
|
const bottomDiv = ui.divH([boolInputsAndButton, this.moleculeImgDiv]);
|
|
64
64
|
$(bottomDiv).addClass('st-structure-bottom');
|
|
65
65
|
|
|
66
|
-
const
|
|
67
|
-
$(
|
|
66
|
+
const layout = ui.divV([tableLayout, bottomDiv]);
|
|
67
|
+
$(layout).addClass('st-structure-body');
|
|
68
68
|
|
|
69
|
-
return
|
|
69
|
+
return layout;
|
|
70
70
|
}
|
|
71
71
|
|
|
72
72
|
private getBoolInputsAndButton(): HTMLDivElement {
|
|
@@ -123,15 +123,26 @@ export class StructureLayoutHandler {
|
|
|
123
123
|
)
|
|
124
124
|
);
|
|
125
125
|
|
|
126
|
+
const clearBlock = Object.fromEntries(
|
|
127
|
+
STRANDS.map(
|
|
128
|
+
(key) => {
|
|
129
|
+
const clearIcon = ui.icons.delete(() => { coloredInput[key].inputBase.value = '' });
|
|
130
|
+
const clearButton = ui.button(clearIcon, () => {});
|
|
131
|
+
ui.tooltip.bind(clearButton, `Clear ${key.toUpperCase()}`);
|
|
132
|
+
return [key, clearIcon];
|
|
133
|
+
}
|
|
134
|
+
));
|
|
135
|
+
|
|
126
136
|
const tableRows = STRANDS.map((strand) => {
|
|
127
137
|
return {
|
|
128
138
|
label: label[strand],
|
|
129
139
|
textInput: coloredInput[strand].root,
|
|
140
|
+
clear: clearBlock[strand],
|
|
130
141
|
choiceInput: directionChoiceInput[strand].root,
|
|
131
142
|
};
|
|
132
143
|
});
|
|
133
144
|
const tableLayout = ui.table(
|
|
134
|
-
tableRows, (item) => [item.label, item.textInput, item.choiceInput]);
|
|
145
|
+
tableRows, (item) => [item.label, item.textInput, item.clear, item.choiceInput]);
|
|
135
146
|
$(tableLayout).css('margin-top', '10px');
|
|
136
147
|
|
|
137
148
|
for (const strand of STRANDS) {
|
|
@@ -5,19 +5,18 @@ import * as DG from 'datagrok-api/dg';
|
|
|
5
5
|
|
|
6
6
|
import * as rxjs from 'rxjs';
|
|
7
7
|
import '../style/translator-app.css';
|
|
8
|
-
import $ from 'cash-dom';
|
|
9
8
|
|
|
10
9
|
import {highlightInvalidSubsequence} from '../utils/colored-input/input-painters';
|
|
11
10
|
import {ColoredTextInput} from '../utils/colored-input/colored-text-input';
|
|
12
|
-
import {SequenceToMolfileConverter} from '../../model/
|
|
13
|
-
import {getTranslatedSequences} from '../../model/
|
|
11
|
+
import {SequenceToMolfileConverter} from '../../model/structure-app/sequence-to-molfile';
|
|
12
|
+
import {getTranslatedSequences} from '../../model/translator-app/conversion-utils';
|
|
14
13
|
import {MoleculeImage} from '../utils/molecule-img';
|
|
15
14
|
import {download} from '../../model/helpers';
|
|
16
15
|
import {SEQUENCE_COPIED_MSG, SEQ_TOOLTIP_MSG} from '../const/oligo-translator';
|
|
17
16
|
import {DEFAULT_AXOLABS_INPUT} from '../const/ui';
|
|
18
17
|
import {FormatDetector} from '../../model/parsing-validation/format-detector';
|
|
19
18
|
import {SequenceValidator} from '../../model/parsing-validation/sequence-validator';
|
|
20
|
-
import {FormatConverter} from '../../model/
|
|
19
|
+
import {FormatConverter} from '../../model/translator-app/format-converter';
|
|
21
20
|
import {codesToHelmDictionary} from '../../model/data-loading-utils/json-loader';
|
|
22
21
|
import {DEFAULT_FORMATS} from '../../model/const';
|
|
23
22
|
|
|
@@ -74,12 +73,19 @@ export class TranslatorLayoutHandler {
|
|
|
74
73
|
|
|
75
74
|
const formatChoiceInput = ui.div([this.formatChoiceInput]);
|
|
76
75
|
|
|
76
|
+
const clearButton = ui.button(
|
|
77
|
+
ui.icons.delete(() => { sequenceColoredInput.inputBase.value = '' }),
|
|
78
|
+
() => {}
|
|
79
|
+
);
|
|
80
|
+
ui.tooltip.bind(clearButton, 'Clear input');
|
|
81
|
+
|
|
77
82
|
const inputTableRow = {
|
|
78
83
|
format: formatChoiceInput,
|
|
79
84
|
textInput: sequenceColoredInput.root,
|
|
85
|
+
clearBtn: clearButton
|
|
80
86
|
};
|
|
81
87
|
const upperBlock = ui.table(
|
|
82
|
-
[inputTableRow], (item) => [item.format, item.textInput]
|
|
88
|
+
[inputTableRow], (item) => [item.format, item.textInput, item.clearBtn]
|
|
83
89
|
);
|
|
84
90
|
upperBlock.classList.add('st-translator-input-table');
|
|
85
91
|
|
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
/* Naming convention: class names should begin with st and app name to avoid naming collitions */
|
|
2
2
|
.st-translator-input-table {
|
|
3
|
-
width: 100%;
|
|
3
|
+
/* width: 100%; */
|
|
4
|
+
margin-right: 20px;
|
|
4
5
|
}
|
|
5
6
|
|
|
6
7
|
.st-translator-input-table td:has(textarea) {
|
|
7
8
|
width: 100%;
|
|
8
|
-
padding-right: 20px;
|
|
9
9
|
}
|
|
10
10
|
|
|
11
11
|
.st-translator-input-table td:has(select) {
|
|
@@ -22,7 +22,7 @@ export class ColoredTextInput {
|
|
|
22
22
|
$(this.root).addClass('colored-text-input');
|
|
23
23
|
if (resizeable) {
|
|
24
24
|
// make input field automatically resizeable
|
|
25
|
-
this.textInputBase.
|
|
25
|
+
this.textInputBase.onChanged(
|
|
26
26
|
() => {
|
|
27
27
|
// necessary for the field to be squeezable, not only expandable
|
|
28
28
|
$(this.textArea).css('height', 0);
|
|
@@ -34,7 +34,7 @@ export class ColoredTextInput {
|
|
|
34
34
|
this.root.appendChild(this.highlights);
|
|
35
35
|
this.colorize();
|
|
36
36
|
|
|
37
|
-
this.textInputBase.
|
|
37
|
+
this.textInputBase.onChanged(() => this.colorize());
|
|
38
38
|
}
|
|
39
39
|
|
|
40
40
|
private highlights: HTMLDivElement;
|
|
@@ -6,7 +6,7 @@ import {errorToConsole} from '@datagrok-libraries/utils/src/to-console';
|
|
|
6
6
|
|
|
7
7
|
import $ from 'cash-dom';
|
|
8
8
|
|
|
9
|
-
import {extractAtomDataV3000} from '../../model/
|
|
9
|
+
import {extractAtomDataV3000} from '../../model/structure-app/mol-transformations';
|
|
10
10
|
|
|
11
11
|
/** Draw molecule on the canvas and append it to the specified div, with the
|
|
12
12
|
* option of zoom-in */
|
|
@@ -1,109 +0,0 @@
|
|
|
1
|
-
import * as DG from 'datagrok-api/dg';
|
|
2
|
-
import {DEFAULT_FORMATS} from '../const';
|
|
3
|
-
import {GROUP_TYPE, PHOSPHATE_SYMBOL, UNKNOWN_SYMBOL} from './const';
|
|
4
|
-
import {CodesInfo} from '../data-loading-utils/types';
|
|
5
|
-
import {codesToHelmDictionary} from '../data-loading-utils/json-loader';
|
|
6
|
-
|
|
7
|
-
const HELM_WRAPPER = {
|
|
8
|
-
LEFT: 'RNA1{',
|
|
9
|
-
RIGHT: '}$$$$',
|
|
10
|
-
};
|
|
11
|
-
|
|
12
|
-
export class FormatConverter {
|
|
13
|
-
constructor(private readonly sequence: string, private readonly sourceFormat: string) { };
|
|
14
|
-
|
|
15
|
-
convertTo(targetFormat: string): string {
|
|
16
|
-
const formats = Object.keys(codesToHelmDictionary);
|
|
17
|
-
|
|
18
|
-
if (this.sourceFormat === DEFAULT_FORMATS.HELM && formats.includes(targetFormat))
|
|
19
|
-
return helmToFormat(this.sequence, targetFormat);
|
|
20
|
-
else if (formats.includes(this.sourceFormat) && targetFormat === DEFAULT_FORMATS.HELM)
|
|
21
|
-
return formatToHelm(this.sequence, this.sourceFormat);
|
|
22
|
-
else if ([this.sourceFormat, targetFormat].every((el) => formats.includes(el))) {
|
|
23
|
-
const helm = formatToHelm(this.sequence, this.sourceFormat);
|
|
24
|
-
return helmToFormat(helm, targetFormat);
|
|
25
|
-
}
|
|
26
|
-
else {
|
|
27
|
-
throw new Error (`ST: unsupported translation direction ${this.sourceFormat} -> ${targetFormat}`);
|
|
28
|
-
}
|
|
29
|
-
}
|
|
30
|
-
}
|
|
31
|
-
|
|
32
|
-
function getRegExpPattern(arr: string[]): string {
|
|
33
|
-
const negativeLookBehind = '(?<!\\([^()]*)'; // not '(' followed by non-parenths
|
|
34
|
-
const negativeLookAhead = '(?![^()]*\\))'; // not ')' preceded by non-parenths
|
|
35
|
-
const escaped = arr.map((key) => key.replace(/[.*+?^${}()|[\]\\]/g, '\\$&'))
|
|
36
|
-
.map((key) => {
|
|
37
|
-
if (!key.includes('(') && !key.includes(')'))
|
|
38
|
-
return `${negativeLookBehind}${key}${negativeLookAhead}`;
|
|
39
|
-
return key;
|
|
40
|
-
});
|
|
41
|
-
const result = escaped.join('|');
|
|
42
|
-
return result;
|
|
43
|
-
}
|
|
44
|
-
|
|
45
|
-
function sortCallback(a: string, b: string) {return b.length - a.length};
|
|
46
|
-
|
|
47
|
-
function getHelmToCodeDict(infoObj: CodesInfo) {
|
|
48
|
-
const result: {[key: string]: string | string[]} = {};
|
|
49
|
-
Object.values(infoObj).forEach((obj: {[code: string]: string}) => {
|
|
50
|
-
Object.entries(obj).forEach(([code, helm]) => {
|
|
51
|
-
const key = helm.replace(/\)p/g, ')').replace(/\]p/g, ']');
|
|
52
|
-
if (result[key] === undefined) {
|
|
53
|
-
result[key] = [code];
|
|
54
|
-
} else {
|
|
55
|
-
(result[key] as string[]).push(code);
|
|
56
|
-
}
|
|
57
|
-
})
|
|
58
|
-
});
|
|
59
|
-
Object.entries(result).forEach(([key, value]) => {
|
|
60
|
-
const sorted = (value as string[]).sort(sortCallback);
|
|
61
|
-
result[key] = sorted[0] as string;
|
|
62
|
-
})
|
|
63
|
-
return result as {[key: string]: string};
|
|
64
|
-
}
|
|
65
|
-
|
|
66
|
-
function helmToFormat(helmSequence: string, targetFormat: string): string {
|
|
67
|
-
const codesInfoObject = codesToHelmDictionary[targetFormat] as CodesInfo;
|
|
68
|
-
const dict = getHelmToCodeDict(codesInfoObject);
|
|
69
|
-
const wrapperRegExp = new RegExp(getRegExpPattern(Object.values(HELM_WRAPPER)), 'g')
|
|
70
|
-
let result = helmSequence.replace(wrapperRegExp, '');
|
|
71
|
-
|
|
72
|
-
const helmCodes = Object.keys(dict)
|
|
73
|
-
.sort(sortCallback);
|
|
74
|
-
const helmRegExp = new RegExp(getRegExpPattern(helmCodes) + '|.', 'g');
|
|
75
|
-
result = result.replace(helmRegExp, (match) => {
|
|
76
|
-
return helmCodes.includes(match) ? dict[match] :
|
|
77
|
-
(match === 'p' || match === '.') ? match : '?';
|
|
78
|
-
}).replace(/\?+/g, UNKNOWN_SYMBOL).replace(/p\.|\./g, '');
|
|
79
|
-
result = result.replace(/<empty>/g, '');
|
|
80
|
-
// remove double slash in LCMS codes
|
|
81
|
-
result = result.replace(/\/\//g, '/');
|
|
82
|
-
return result;
|
|
83
|
-
}
|
|
84
|
-
|
|
85
|
-
function formatToHelm(sequence: string, sourceFormat: string): string {
|
|
86
|
-
const codesInfoObject = codesToHelmDictionary[sourceFormat] as CodesInfo;
|
|
87
|
-
const dict = Object.assign({}, ...Object.values(codesInfoObject)) as {[code: string]: string};
|
|
88
|
-
|
|
89
|
-
const formatCodes = Object.keys(dict).sort(sortCallback);
|
|
90
|
-
const formatRegExp = new RegExp(getRegExpPattern(formatCodes) + '|\\([^()]*\\)|.', 'g'); // the added group before '|.' is to avoid mismatch inside parenths
|
|
91
|
-
|
|
92
|
-
const phosphateHELMCodes = Array.from(
|
|
93
|
-
new Set(Object.values(codesInfoObject[GROUP_TYPE.LINKAGE]))
|
|
94
|
-
).sort(sortCallback);
|
|
95
|
-
const phosphateHELMPattern = getRegExpPattern(phosphateHELMCodes);
|
|
96
|
-
const phosphateRegExp = new RegExp(`${PHOSPHATE_SYMBOL}\.(${phosphateHELMPattern})`, 'g');
|
|
97
|
-
|
|
98
|
-
let helm = sequence.replace(formatRegExp, (match) => {
|
|
99
|
-
const result = formatCodes.includes(match) ? dict[match] + '.' : '?';
|
|
100
|
-
return result;
|
|
101
|
-
});
|
|
102
|
-
helm = helm.replace(/\?+/g, `${UNKNOWN_SYMBOL}.`);
|
|
103
|
-
helm = helm.slice(0, -1); // strip last dot
|
|
104
|
-
if (helm[helm.length - 1] === PHOSPHATE_SYMBOL)
|
|
105
|
-
helm = helm.slice(0, -1);
|
|
106
|
-
helm = helm.replace(phosphateRegExp, (match, group) => group);
|
|
107
|
-
helm = helm.replace(/<empty>/g, '');
|
|
108
|
-
return `${HELM_WRAPPER.LEFT + helm + HELM_WRAPPER.RIGHT}`;
|
|
109
|
-
}
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
/package/src/model/{sequence-to-structure-utils/sdf-tab.ts → structure-app/oligo-structure.ts}
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|