@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.
Files changed (30) hide show
  1. package/CHANGELOG.md +1 -0
  2. package/dist/package-test.js +1 -1
  3. package/dist/package-test.js.map +1 -1
  4. package/dist/package.js +1 -1
  5. package/dist/package.js.map +1 -1
  6. package/package.json +1 -1
  7. package/src/model/parsing-validation/format-handler.ts +147 -0
  8. package/src/model/translator-app/format-converter.ts +68 -0
  9. package/src/package.ts +3 -3
  10. package/src/tests/formats-support.ts +1 -1
  11. package/src/tests/formats-to-helm.ts +1 -1
  12. package/src/tests/helm-to-nucleotides.ts +1 -1
  13. package/src/view/apps/oligo-pattern.ts +6 -57
  14. package/src/view/apps/oligo-structure.ts +17 -6
  15. package/src/view/apps/oligo-translator.ts +11 -5
  16. package/src/view/style/translator-app.css +2 -2
  17. package/src/view/utils/colored-input/colored-text-input.ts +2 -2
  18. package/src/view/utils/draw-molecule.ts +1 -1
  19. package/src/model/format-translation/format-converter.ts +0 -109
  20. /package/src/model/{axolabs → pattern-app}/const.ts +0 -0
  21. /package/src/model/{axolabs → pattern-app}/draw-svg.ts +0 -0
  22. /package/src/model/{axolabs → pattern-app}/helpers.ts +0 -0
  23. /package/src/model/{axolabs/axolabs-tab.ts → pattern-app/oligo-pattern.ts} +0 -0
  24. /package/src/model/{sequence-to-structure-utils → structure-app}/const.ts +0 -0
  25. /package/src/model/{sequence-to-structure-utils → structure-app}/mol-transformations.ts +0 -0
  26. /package/src/model/{sequence-to-structure-utils → structure-app}/monomer-code-parser.ts +0 -0
  27. /package/src/model/{sequence-to-structure-utils/sdf-tab.ts → structure-app/oligo-structure.ts} +0 -0
  28. /package/src/model/{sequence-to-structure-utils → structure-app}/sequence-to-molfile.ts +0 -0
  29. /package/src/model/{format-translation → translator-app}/const.ts +0 -0
  30. /package/src/model/{format-translation → translator-app}/conversion-utils.ts +0 -0
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@datagrok/sequence-translator",
3
3
  "friendlyName": "Sequence Translator",
4
- "version": "1.2.2",
4
+ "version": "1.2.4",
5
5
  "author": {
6
6
  "name": "Alexey Choposky",
7
7
  "email": "achopovsky@datagrok.ai"
@@ -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/sequence-to-structure-utils/sequence-to-molfile';
12
- import {linkStrandsV3000} from './model/sequence-to-structure-utils/mol-transformations';
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/format-translation/format-converter';
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/format-translation/conversion-utils';
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/format-translation/format-converter';
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/format-translation/conversion-utils';
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, EXAMPLE_MIN_WIDTH, SS, AS, STRAND_NAME, STRANDS, TERMINAL, TERMINAL_KEYS, THREE_PRIME, FIVE_PRIME, JSON_FIELD as FIELD
9
- } from '../../model/axolabs/const';
10
- import {isOverhang} from '../../model/axolabs/helpers';
11
- import {generateExample, translateSequence, getShortName, isCurrentUserCreatedThisPattern, findDuplicates, addColumnWithIds, addColumnWithTranslatedSequences} from '../../model/axolabs/axolabs-tab';
12
- import {drawAxolabsPattern} from '../../model/axolabs/draw-svg';
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 patter')
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/sequence-to-structure-utils/sdf-tab';
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/sequence-to-structure-utils/sdf-tab';
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 sdfTabBody = ui.divV([tableLayout, bottomDiv]);
67
- $(sdfTabBody).addClass('st-structure-body');
66
+ const layout = ui.divV([tableLayout, bottomDiv]);
67
+ $(layout).addClass('st-structure-body');
68
68
 
69
- return sdfTabBody;
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/sequence-to-structure-utils/sequence-to-molfile';
13
- import {getTranslatedSequences} from '../../model/format-translation/conversion-utils';
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/format-translation/format-converter';
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.onInput(
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.onInput(() => this.colorize());
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/sequence-to-structure-utils/mol-transformations';
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