@datagrok/sequence-translator 1.0.11 → 1.0.13
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/dist/package-test.js +254 -94
- package/dist/package.js +602 -364
- package/package.json +2 -2
- package/src/__jest__/remote.test.ts +11 -3
- package/src/{ICDs.ts → autostart/ICDs.ts} +0 -0
- package/src/{IDPs.ts → autostart/IDPs.ts} +0 -0
- package/src/autostart/calculations.ts +37 -0
- package/src/autostart/constants.ts +37 -0
- package/src/autostart/registration.ts +94 -129
- package/src/{salts.ts → autostart/salts.ts} +0 -0
- package/src/{sources.ts → autostart/sources.ts} +0 -0
- package/src/{users.ts → autostart/users.ts} +0 -0
- package/src/axolabs/define-pattern.ts +42 -34
- package/src/axolabs/draw-svg.ts +19 -19
- package/src/helpers.ts +28 -0
- package/src/main/main-view.ts +48 -70
- package/src/package.ts +6 -7
- package/src/structures-works/from-monomers.ts +1 -4
- package/src/structures-works/map.ts +14 -28
- package/src/structures-works/mol-transformations.ts +177 -16
- package/src/structures-works/save-sense-antisense.ts +32 -11
- package/src/structures-works/sequence-codes-tools.ts +1 -3
- package/{test-SequenceTranslator-916a90d7d48b-7afd2a61.html → test-SequenceTranslator-62cc009524f3-4a9916b0.html} +11 -8
package/src/helpers.ts
ADDED
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import * as DG from 'datagrok-api/dg';
|
|
2
|
+
|
|
3
|
+
export function sortByStringLengthInDescendingOrder(array: string[]): string[] {
|
|
4
|
+
return array.sort(function(a, b) {return b.length - a.length;});
|
|
5
|
+
}
|
|
6
|
+
|
|
7
|
+
export function stringify(items: string[]): string {
|
|
8
|
+
return '["' + items.join('", "') + '"]';
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
export function download(name: string, href: string): void {
|
|
12
|
+
const element = document.createElement('a');
|
|
13
|
+
element.setAttribute('href', 'data:text/plain;charset=utf-8,' + href);
|
|
14
|
+
element.setAttribute('download', name);
|
|
15
|
+
element.click();
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
export function removeEmptyRows(t: DG.DataFrame, colToCheck: DG.Column): DG.DataFrame {
|
|
19
|
+
for (let i = t.rowCount - 1; i > -1; i--) {
|
|
20
|
+
if (colToCheck.getString(i) == '')
|
|
21
|
+
t.rows.removeAt(i, 1, false);
|
|
22
|
+
}
|
|
23
|
+
return t;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
export function differenceOfTwoArrays(a: string[], b: string[]): string[] {
|
|
27
|
+
return a.filter((x) => !b.includes(x));
|
|
28
|
+
}
|
package/src/main/main-view.ts
CHANGED
|
@@ -5,6 +5,7 @@ import {convertSequence, undefinedInputSequence, isValidSequence} from '../struc
|
|
|
5
5
|
import {map, MODIFICATIONS} from '../structures-works/map';
|
|
6
6
|
import {sequenceToSmiles, sequenceToMolV3000} from '../structures-works/from-monomers';
|
|
7
7
|
import $ from 'cash-dom';
|
|
8
|
+
import {download} from '../helpers';
|
|
8
9
|
|
|
9
10
|
const defaultInput = 'fAmCmGmAmCpsmU';
|
|
10
11
|
const sequenceWasCopied = 'Copied';
|
|
@@ -15,7 +16,6 @@ export function mainView() {
|
|
|
15
16
|
moleculeSvgDiv.innerHTML = '';
|
|
16
17
|
outputTableDiv.innerHTML = '';
|
|
17
18
|
const pi = DG.TaskBarProgressIndicator.create('Rendering table and molecule...');
|
|
18
|
-
let errorsExist = false;
|
|
19
19
|
try {
|
|
20
20
|
sequence = sequence.replace(/\s/g, '');
|
|
21
21
|
const output = isValidSequence(sequence, null);
|
|
@@ -27,13 +27,6 @@ export function mainView() {
|
|
|
27
27
|
const indexOfFirstNotValidChar = ('indexOfFirstNotValidChar' in outputSequenceObj) ?
|
|
28
28
|
JSON.parse(outputSequenceObj.indexOfFirstNotValidChar!).indexOfFirstNotValidChar :
|
|
29
29
|
-1;
|
|
30
|
-
if ('indexOfFirstNotValidChar' in outputSequenceObj) {
|
|
31
|
-
const indexOfFirstNotValidChar = ('indexOfFirstNotValidChar' in outputSequenceObj) ?
|
|
32
|
-
JSON.parse(outputSequenceObj.indexOfFirstNotValidChar!).indexOfFirstNotValidChar :
|
|
33
|
-
-1;
|
|
34
|
-
if (indexOfFirstNotValidChar != -1)
|
|
35
|
-
errorsExist = true;
|
|
36
|
-
}
|
|
37
30
|
|
|
38
31
|
tableRows.push({
|
|
39
32
|
'key': key,
|
|
@@ -51,20 +44,6 @@ export function mainView() {
|
|
|
51
44
|
});
|
|
52
45
|
}
|
|
53
46
|
|
|
54
|
-
if (errorsExist) {
|
|
55
|
-
const synthesizer = JSON.parse(outputSequenceObj.indexOfFirstNotValidChar!).synthesizer.slice(0, -6);
|
|
56
|
-
asoGapmersGrid.onCellPrepare(function(gc) {
|
|
57
|
-
gc.style.backColor = (gc.gridColumn.name == synthesizer) ? 0xFFF00000 : 0xFFFFFFFF;
|
|
58
|
-
});
|
|
59
|
-
omeAndFluoroGrid.onCellPrepare(function(gc) {
|
|
60
|
-
gc.style.backColor = (gc.gridColumn.name == synthesizer) ? 0xFFF00000 : 0xFFFFFFFF;
|
|
61
|
-
});
|
|
62
|
-
switchInput.enabled = true;
|
|
63
|
-
} else {
|
|
64
|
-
asoGapmersGrid.onCellPrepare(function(gc) {gc.style.backColor = 0xFFFFFFFF;});
|
|
65
|
-
omeAndFluoroGrid.onCellPrepare(function(gc) {gc.style.backColor = 0xFFFFFFFF;});
|
|
66
|
-
}
|
|
67
|
-
|
|
68
47
|
outputTableDiv.append(
|
|
69
48
|
ui.div([
|
|
70
49
|
DG.HtmlTable.create(tableRows, (item: { key: string; value: string; }) =>
|
|
@@ -108,46 +87,43 @@ export function mainView() {
|
|
|
108
87
|
updateTableAndMolecule(sequence, inputFormatChoiceInput.value!);
|
|
109
88
|
});
|
|
110
89
|
|
|
111
|
-
const
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
asoDf.onCurrentCellChanged.subscribe((_) => {
|
|
130
|
-
navigator.clipboard.writeText(asoDf.currentCell.value).then(() => grok.shell.info('Copied'));
|
|
131
|
-
});
|
|
90
|
+
const asoGapmersGrid = DG.Viewer.grid(
|
|
91
|
+
DG.DataFrame.fromObjects([
|
|
92
|
+
{'Name': '2\'MOE-5Me-rU', 'BioSpring': '5', 'Janssen GCRS': 'moeT'},
|
|
93
|
+
{'Name': '2\'MOE-rA', 'BioSpring': '6', 'Janssen GCRS': 'moeA'},
|
|
94
|
+
{'Name': '2\'MOE-5Me-rC', 'BioSpring': '7', 'Janssen GCRS': 'moe5mC'},
|
|
95
|
+
{'Name': '2\'MOE-rG', 'BioSpring': '8', 'Janssen GCRS': 'moeG'},
|
|
96
|
+
{'Name': '5-Methyl-dC', 'BioSpring': '9', 'Janssen GCRS': '5mC'},
|
|
97
|
+
{'Name': 'ps linkage', 'BioSpring': '*', 'Janssen GCRS': 'ps'},
|
|
98
|
+
{'Name': 'dA', 'BioSpring': 'A', 'Janssen GCRS': 'A, dA'},
|
|
99
|
+
{'Name': 'dC', 'BioSpring': 'C', 'Janssen GCRS': 'C, dC'},
|
|
100
|
+
{'Name': 'dG', 'BioSpring': 'G', 'Janssen GCRS': 'G, dG'},
|
|
101
|
+
{'Name': 'dT', 'BioSpring': 'T', 'Janssen GCRS': 'T, dT'},
|
|
102
|
+
{'Name': 'rA', 'BioSpring': '', 'Janssen GCRS': 'rA'},
|
|
103
|
+
{'Name': 'rC', 'BioSpring': '', 'Janssen GCRS': 'rC'},
|
|
104
|
+
{'Name': 'rG', 'BioSpring': '', 'Janssen GCRS': 'rG'},
|
|
105
|
+
{'Name': 'rU', 'BioSpring': '', 'Janssen GCRS': 'rU'},
|
|
106
|
+
])!, {showRowHeader: false, showCellTooltip: false, allowEdit: false},
|
|
107
|
+
);
|
|
132
108
|
|
|
133
109
|
const omeAndFluoroGrid = DG.Viewer.grid(
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
110
|
+
DG.DataFrame.fromObjects([
|
|
111
|
+
{'Name': '2\'-fluoro-U', 'BioSpring': '1', 'Axolabs': 'Uf', 'Janssen GCRS': 'fU'},
|
|
112
|
+
{'Name': '2\'-fluoro-A', 'BioSpring': '2', 'Axolabs': 'Af', 'Janssen GCRS': 'fA'},
|
|
113
|
+
{'Name': '2\'-fluoro-C', 'BioSpring': '3', 'Axolabs': 'Cf', 'Janssen GCRS': 'fC'},
|
|
114
|
+
{'Name': '2\'-fluoro-G', 'BioSpring': '4', 'Axolabs': 'Gf', 'Janssen GCRS': 'fG'},
|
|
115
|
+
{'Name': '2\'OMe-rU', 'BioSpring': '5', 'Axolabs': 'u', 'Janssen GCRS': 'mU'},
|
|
116
|
+
{'Name': '2\'OMe-rA', 'BioSpring': '6', 'Axolabs': 'a', 'Janssen GCRS': 'mA'},
|
|
117
|
+
{'Name': '2\'OMe-rC', 'BioSpring': '7', 'Axolabs': 'c', 'Janssen GCRS': 'mC'},
|
|
118
|
+
{'Name': '2\'OMe-rG', 'BioSpring': '8', 'Axolabs': 'g', 'Janssen GCRS': 'mG'},
|
|
119
|
+
{'Name': 'ps linkage', 'BioSpring': '*', 'Axolabs': 's', 'Janssen GCRS': 'ps'},
|
|
120
|
+
])!, {showRowHeader: false, showCellTooltip: false, allowEdit: false},
|
|
145
121
|
);
|
|
146
122
|
|
|
147
123
|
const overhangModificationsGrid = DG.Viewer.grid(
|
|
148
124
|
DG.DataFrame.fromColumns([
|
|
149
125
|
DG.Column.fromStrings('Name', Object.keys(MODIFICATIONS)),
|
|
150
|
-
])!, {showRowHeader: false, showCellTooltip: false},
|
|
126
|
+
])!, {showRowHeader: false, showCellTooltip: false, allowEdit: false},
|
|
151
127
|
);
|
|
152
128
|
updateTableAndMolecule(defaultInput, inputFormatChoiceInput.value!);
|
|
153
129
|
|
|
@@ -175,27 +151,29 @@ export function mainView() {
|
|
|
175
151
|
$(codesTablesDiv).hide(),
|
|
176
152
|
);
|
|
177
153
|
|
|
154
|
+
const downloadMolFileIcon = ui.iconFA('download', () => {
|
|
155
|
+
const clearSequence = inputSequenceField.value.replace(/\s/g, '');
|
|
156
|
+
const result = sequenceToMolV3000(clearSequence, false, false, inputFormatChoiceInput.value!);
|
|
157
|
+
download(clearSequence + '.mol', encodeURIComponent(result));
|
|
158
|
+
}, 'Save .mol file');
|
|
159
|
+
|
|
160
|
+
const copySmilesIcon = ui.iconFA('copy', () => {
|
|
161
|
+
navigator.clipboard.writeText(
|
|
162
|
+
sequenceToSmiles(inputSequenceField.value.replace(/\s/g, ''), false, inputFormatChoiceInput.value!),
|
|
163
|
+
).then(() => grok.shell.info(sequenceWasCopied));
|
|
164
|
+
}, 'Copy SMILES');
|
|
165
|
+
|
|
178
166
|
const topPanel = [
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
inputFormatChoiceInput.value!);
|
|
182
|
-
const element = document.createElement('a');
|
|
183
|
-
element.setAttribute('href', 'data:text/plain;charset=utf-8,' + encodeURIComponent(result));
|
|
184
|
-
element.setAttribute('download', inputSequenceField.value.replace(/\s/g, '') + '.mol');
|
|
185
|
-
element.click();
|
|
186
|
-
}, 'Save .mol file'),
|
|
187
|
-
ui.iconFA('copy', () => {
|
|
188
|
-
navigator.clipboard.writeText(
|
|
189
|
-
sequenceToSmiles(inputSequenceField.value.replace(/\s/g, ''), false, inputFormatChoiceInput.value!))
|
|
190
|
-
.then(() => grok.shell.info(sequenceWasCopied));
|
|
191
|
-
}, 'Copy SMILES'),
|
|
167
|
+
downloadMolFileIcon,
|
|
168
|
+
copySmilesIcon,
|
|
192
169
|
switchInput.root,
|
|
193
170
|
];
|
|
194
171
|
|
|
195
172
|
const v = grok.shell.v;
|
|
196
173
|
const tabControl = grok.shell.sidebar;
|
|
197
|
-
tabControl.onTabChanged.subscribe((_) =>
|
|
198
|
-
v.setRibbonPanels([(tabControl.currentPane.name == 'MAIN') ? topPanel : []])
|
|
174
|
+
tabControl.onTabChanged.subscribe((_) => {
|
|
175
|
+
v.setRibbonPanels([(tabControl.currentPane.name == 'MAIN') ? topPanel : []]);
|
|
176
|
+
});
|
|
199
177
|
v.setRibbonPanels([topPanel]);
|
|
200
178
|
|
|
201
179
|
return ui.box(
|
package/src/package.ts
CHANGED
|
@@ -17,14 +17,13 @@ export function sequenceTranslator(): void {
|
|
|
17
17
|
windows.showToolbox = false;
|
|
18
18
|
windows.showHelp = false;
|
|
19
19
|
|
|
20
|
-
const v = grok.shell.newView('Sequence Translator', [
|
|
21
|
-
ui.tabControl({
|
|
22
|
-
'MAIN': mainView(),
|
|
23
|
-
'AXOLABS': defineAxolabsPattern(),
|
|
24
|
-
'SDF': saveSenseAntiSense(),
|
|
25
|
-
}),
|
|
26
|
-
]);
|
|
20
|
+
const v = grok.shell.newView('Sequence Translator', []);
|
|
27
21
|
v.box = true;
|
|
22
|
+
v.append(ui.tabControl({
|
|
23
|
+
'MAIN': mainView(),
|
|
24
|
+
'AXOLABS': defineAxolabsPattern(),
|
|
25
|
+
'SDF': saveSenseAntiSense(),
|
|
26
|
+
}));
|
|
28
27
|
}
|
|
29
28
|
|
|
30
29
|
//tags: autostart
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import {map, stadardPhosphateLinkSmiles, SYNTHESIZERS, TECHNOLOGIES, MODIFICATIONS, delimiter} from './map';
|
|
2
2
|
import {isValidSequence} from './sequence-codes-tools';
|
|
3
3
|
import {getNucleotidesMol} from './mol-transformations';
|
|
4
|
+
import {sortByStringLengthInDescendingOrder} from '../helpers';
|
|
4
5
|
|
|
5
6
|
export function sequenceToMolV3000(sequence: string, inverted: boolean = false, oclRender: boolean = false,
|
|
6
7
|
format: string): string {
|
|
@@ -108,7 +109,3 @@ function getObjectWithCodesAndSmiles(sequence: string, format: string) {
|
|
|
108
109
|
obj['g'] = map[SYNTHESIZERS.AXOLABS][TECHNOLOGIES.SI_RNA]['g'].SMILES;
|
|
109
110
|
return obj;
|
|
110
111
|
}
|
|
111
|
-
|
|
112
|
-
function sortByStringLengthInDescendingOrder(array: string[]): string[] {
|
|
113
|
-
return array.sort(function(a: string, b: string) {return b.length - a.length;});
|
|
114
|
-
}
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import * as DG from 'datagrok-api/dg';
|
|
2
2
|
import {getAllCodesOfSynthesizer} from './sequence-codes-tools';
|
|
3
|
+
import {differenceOfTwoArrays} from '../helpers';
|
|
3
4
|
|
|
4
5
|
export const delimiter = ';';
|
|
5
6
|
export const SYNTHESIZERS = {
|
|
@@ -16,29 +17,6 @@ export const TECHNOLOGIES = {
|
|
|
16
17
|
ASO_GAPMERS: 'For ASO Gapmers',
|
|
17
18
|
SI_RNA: 'For 2\'-OMe and 2\'-F modified siRNA',
|
|
18
19
|
};
|
|
19
|
-
export const COL_NAMES = {
|
|
20
|
-
CHEMISTRY: 'Chemistry',
|
|
21
|
-
NUMBER: 'Number',
|
|
22
|
-
TYPE: 'Type',
|
|
23
|
-
CHEMISTRY_NAME: 'Chemistry Name',
|
|
24
|
-
INTERNAL_COMPOUND_ID: 'Internal compound ID',
|
|
25
|
-
IDP: 'IDP',
|
|
26
|
-
SEQUENCE: 'Sequence',
|
|
27
|
-
COMPOUND_NAME: 'Compound Name',
|
|
28
|
-
COMPOUND_COMMENTS: 'Compound Comments',
|
|
29
|
-
SALT: 'Salt',
|
|
30
|
-
EQUIVALENTS: 'Equivalents',
|
|
31
|
-
PURITY: 'Purity',
|
|
32
|
-
CPD_MW: 'Cpd MW',
|
|
33
|
-
SALT_MOL_WEIGHT: 'Salt MW',
|
|
34
|
-
SALT_MASS: 'Salt mass',
|
|
35
|
-
BATCH_MW: 'Batch MW',
|
|
36
|
-
SOURCE: 'Source',
|
|
37
|
-
ICD: 'ICD',
|
|
38
|
-
OWNER: 'Owner',
|
|
39
|
-
};
|
|
40
|
-
// interface CODES {
|
|
41
|
-
// }
|
|
42
20
|
export const MODIFICATIONS: {[index: string]: {molecularWeight: number, left: string, right: string}} = {
|
|
43
21
|
'(invabasic)': {
|
|
44
22
|
molecularWeight: 118.13,
|
|
@@ -58,7 +36,7 @@ export const MODIFICATIONS: {[index: string]: {molecularWeight: number, left: st
|
|
|
58
36
|
export const stadardPhosphateLinkSmiles = 'OP(=O)(O)O';
|
|
59
37
|
export const map: {[synthesizer: string]:
|
|
60
38
|
{[technology: string]: {[code: string]:
|
|
61
|
-
{'name'
|
|
39
|
+
{'name': string, 'weight': number, 'normalized': string, 'SMILES': string}}}} = {
|
|
62
40
|
'Raw Nucleotides': {
|
|
63
41
|
'DNA': {
|
|
64
42
|
'A': {
|
|
@@ -724,12 +702,20 @@ fU, fU
|
|
|
724
702
|
/J-CbCS/, J-CbCS
|
|
725
703
|
/J-MtCD/, J-MtCD`;
|
|
726
704
|
|
|
727
|
-
function differenceOfTwoArrays(a: string[], b: string[]): string[] {
|
|
728
|
-
return a.filter((x) => !b.includes(x));
|
|
729
|
-
}
|
|
730
705
|
|
|
731
706
|
const codesWithSmiles = getAllCodesOfSynthesizer(SYNTHESIZERS.GCRS);
|
|
732
707
|
const allGcrsCodes = DG.DataFrame.fromCsv(lcmsToGcrs).getCol('GCRS').toList();
|
|
733
708
|
export const gcrsCodesWithoutSmiles = differenceOfTwoArrays(allGcrsCodes, codesWithSmiles);
|
|
734
709
|
for (const e of gcrsCodesWithoutSmiles)
|
|
735
|
-
map[SYNTHESIZERS.GCRS]['Others'][e] = {'
|
|
710
|
+
map[SYNTHESIZERS.GCRS]['Others'][e] = {name: '', weight: 0, normalized: '', SMILES: ''};
|
|
711
|
+
|
|
712
|
+
|
|
713
|
+
export const weightsObj: {[code: string]: number} = {};
|
|
714
|
+
for (const synthesizer of Object.keys(map)) {
|
|
715
|
+
for (const technology of Object.keys(map[synthesizer])) {
|
|
716
|
+
for (const code of Object.keys(map[synthesizer][technology]))
|
|
717
|
+
weightsObj[code] = map[synthesizer][technology][code].weight;
|
|
718
|
+
}
|
|
719
|
+
}
|
|
720
|
+
for (const [key, value] of Object.entries(MODIFICATIONS))
|
|
721
|
+
weightsObj[key] = value.molecularWeight;
|
|
@@ -564,10 +564,10 @@ export function getNucleotidesMol(smilesCodes: string[]) {
|
|
|
564
564
|
molBlocks.push(rotateNucleotidesV3000(smilesCodes[i]));
|
|
565
565
|
}
|
|
566
566
|
|
|
567
|
-
return linkV3000(molBlocks
|
|
567
|
+
return linkV3000(molBlocks);
|
|
568
568
|
}
|
|
569
569
|
|
|
570
|
-
export function
|
|
570
|
+
export function linkStrandsV3000(strands:{senseStrands: string[], antiStrands: string[]}, useChirality: boolean = true) {
|
|
571
571
|
let macroMolBlock = '\nDatagrok macromolecule handler\n\n';
|
|
572
572
|
macroMolBlock += ' 0 0 0 0 0 0 999 V3000\n';
|
|
573
573
|
macroMolBlock += 'M V30 BEGIN CTAB\n';
|
|
@@ -579,18 +579,34 @@ export function linkV3000(molBlocks: string[], twoChains: boolean = false, useCh
|
|
|
579
579
|
let nbond = 0;
|
|
580
580
|
let xShift = 0;
|
|
581
581
|
|
|
582
|
-
if (twoChains && molBlocks.length > 1)
|
|
583
|
-
|
|
582
|
+
// if (twoChains && molBlocks.length > 1)
|
|
583
|
+
// molBlocks[1] = invertNucleotidesV3000(molBlocks[1]);
|
|
584
|
+
|
|
585
|
+
if (strands.antiStrands.length > 0) {
|
|
586
|
+
for(let i = 0; i < strands.antiStrands.length; i++) {
|
|
587
|
+
strands.antiStrands[i] = invertNucleotidesV3000(strands.antiStrands[i]);
|
|
588
|
+
}
|
|
589
|
+
}
|
|
590
|
+
|
|
591
|
+
let inverted = false;
|
|
592
|
+
let molBlocks = strands.senseStrands.concat(strands.antiStrands);
|
|
584
593
|
|
|
585
594
|
for (let i = 0; i < molBlocks.length; i++) {
|
|
595
|
+
|
|
596
|
+
if (i >= strands.senseStrands.length && inverted == false) {
|
|
597
|
+
inverted = true;
|
|
598
|
+
xShift = 0;
|
|
599
|
+
}
|
|
600
|
+
|
|
601
|
+
|
|
586
602
|
molBlocks[i] = molBlocks[i].replaceAll('(-\nM V30 ', '(')
|
|
587
603
|
.replaceAll('-\nM V30 ', '').replaceAll(' )', ')');
|
|
588
604
|
const numbers = extractAtomsBondsNumbersV3000(molBlocks[i]);
|
|
589
605
|
const coordinates = extractAtomDataV3000(molBlocks[i]);
|
|
590
606
|
|
|
591
|
-
if (
|
|
592
|
-
const xShiftRight = Math.min(...coordinates.x);
|
|
593
|
-
const yShift =
|
|
607
|
+
if (inverted) {
|
|
608
|
+
const xShiftRight = Math.min(...coordinates.x) - xShift;
|
|
609
|
+
const yShift = !inverted ? Math.min(...coordinates.y) - 1 : Math.max(...coordinates.y) + 10;
|
|
594
610
|
for (let j = 0; j < coordinates.x.length; j++)
|
|
595
611
|
coordinates.x[j] -= xShiftRight;
|
|
596
612
|
for (let j = 0; j < coordinates.y.length; j++)
|
|
@@ -603,7 +619,154 @@ export function linkV3000(molBlocks: string[], twoChains: boolean = false, useCh
|
|
|
603
619
|
let indexEnd = indexAtoms;
|
|
604
620
|
|
|
605
621
|
for (let j = 0; j < numbers.natom; j++) {
|
|
606
|
-
if (coordinates.atomIndex[j] != 1 || i == 0 || twoChains) {
|
|
622
|
+
// if (coordinates.atomIndex[j] != 1 || i == 0 || twoChains) {
|
|
623
|
+
//rewrite atom number
|
|
624
|
+
index = molBlocks[i].indexOf('V30', index) + 4;
|
|
625
|
+
indexEnd = molBlocks[i].indexOf(' ', index);
|
|
626
|
+
const atomNumber = parseInt(molBlocks[i].substring(index, indexEnd)) + natom;
|
|
627
|
+
molBlocks[i] = molBlocks[i].slice(0, index) + atomNumber + molBlocks[i].slice(indexEnd);
|
|
628
|
+
|
|
629
|
+
//rewrite coordinates
|
|
630
|
+
index = molBlocks[i].indexOf(' ', index) + 1;
|
|
631
|
+
index = molBlocks[i].indexOf(' ', index) + 1;
|
|
632
|
+
indexEnd = molBlocks[i].indexOf(' ', index);
|
|
633
|
+
|
|
634
|
+
const totalShift = true ? 0 : xShift - coordinates.x[0];
|
|
635
|
+
let coordinate = true ?
|
|
636
|
+
Math.round(10000*coordinates.x[j])/10000 :
|
|
637
|
+
Math.round(10000*(parseFloat(molBlocks[i].substring(index, indexEnd)) + totalShift))/10000;
|
|
638
|
+
molBlocks[i] = molBlocks[i].slice(0, index) + coordinate + molBlocks[i].slice(indexEnd);
|
|
639
|
+
|
|
640
|
+
index = molBlocks[i].indexOf(' ', index) + 1;
|
|
641
|
+
indexEnd = molBlocks[i].indexOf(' ', index);
|
|
642
|
+
coordinate = true ?
|
|
643
|
+
Math.round(10000*coordinates.y[j])/10000 :
|
|
644
|
+
Math.round(10000*(parseFloat(molBlocks[i].substring(index, indexEnd))))/10000;
|
|
645
|
+
molBlocks[i] = molBlocks[i].slice(0, index) + coordinate + molBlocks[i].slice(indexEnd);
|
|
646
|
+
|
|
647
|
+
index = molBlocks[i].indexOf('\n', index) + 1;
|
|
648
|
+
}
|
|
649
|
+
|
|
650
|
+
const indexAtomsEnd = molBlocks[i].indexOf('M V30 END ATOM');
|
|
651
|
+
atomBlock += molBlocks[i].substring(indexAtoms + 1, indexAtomsEnd);
|
|
652
|
+
|
|
653
|
+
let indexBonds = molBlocks[i].indexOf('M V30 BEGIN BOND'); // V3000 index for bonds
|
|
654
|
+
indexBonds = molBlocks[i].indexOf('\n', indexBonds);
|
|
655
|
+
index = indexBonds;
|
|
656
|
+
indexEnd = indexBonds;
|
|
657
|
+
|
|
658
|
+
for (let j = 0; j < numbers.nbond; j++) {
|
|
659
|
+
//rewrite bond number
|
|
660
|
+
index = molBlocks[i].indexOf('V30', index) + 4;
|
|
661
|
+
indexEnd = molBlocks[i].indexOf(' ', index);
|
|
662
|
+
const bondNumber = parseInt(molBlocks[i].substring(index, indexEnd)) + nbond;
|
|
663
|
+
molBlocks[i] = molBlocks[i].slice(0, index) + bondNumber + molBlocks[i].slice(indexEnd);
|
|
664
|
+
|
|
665
|
+
//rewrite atom pair in bond
|
|
666
|
+
index = molBlocks[i].indexOf(' ', index) + 1;
|
|
667
|
+
index = molBlocks[i].indexOf(' ', index) + 1;
|
|
668
|
+
indexEnd = molBlocks[i].indexOf(' ', index);
|
|
669
|
+
let atomNumber = parseInt(molBlocks[i].substring(index, indexEnd)) + natom;
|
|
670
|
+
molBlocks[i] = molBlocks[i].slice(0, index) + atomNumber + molBlocks[i].slice(indexEnd);
|
|
671
|
+
index = molBlocks[i].indexOf(' ', index) + 1;
|
|
672
|
+
indexEnd = Math.min(molBlocks[i].indexOf('\n', index), molBlocks[i].indexOf(' ', index));
|
|
673
|
+
atomNumber = parseInt(molBlocks[i].substring(index, indexEnd)) + natom;
|
|
674
|
+
molBlocks[i] = molBlocks[i].slice(0, index) + atomNumber + molBlocks[i].slice(indexEnd);
|
|
675
|
+
|
|
676
|
+
index = molBlocks[i].indexOf('\n', index) + 1;
|
|
677
|
+
}
|
|
678
|
+
|
|
679
|
+
const indexBondEnd = molBlocks[i].indexOf('M V30 END BOND');
|
|
680
|
+
bondBlock += molBlocks[i].substring(indexBonds + 1, indexBondEnd);
|
|
681
|
+
|
|
682
|
+
let indexCollection = molBlocks[i].indexOf('M V30 MDLV30/STEABS ATOMS=('); // V3000 index for collections
|
|
683
|
+
|
|
684
|
+
while (indexCollection != -1) {
|
|
685
|
+
indexCollection += 28;
|
|
686
|
+
const collectionEnd = molBlocks[i].indexOf(')', indexCollection);
|
|
687
|
+
const collectionEntries = molBlocks[i].substring(indexCollection, collectionEnd).split(' ').slice(1);
|
|
688
|
+
collectionEntries.forEach((e) => {
|
|
689
|
+
collection.push(parseInt(e) + natom);
|
|
690
|
+
});
|
|
691
|
+
indexCollection = collectionEnd;
|
|
692
|
+
indexCollection = molBlocks[i].indexOf('M V30 MDLV30/STEABS ATOMS=(', indexCollection);
|
|
693
|
+
}
|
|
694
|
+
|
|
695
|
+
natom += true ? numbers.natom : numbers.natom - 1;
|
|
696
|
+
nbond += numbers.nbond;
|
|
697
|
+
xShift += Math.max(...coordinates.x) + 1;//twoChains ? 0 : coordinates.x[numbers.natom - 1] - coordinates.x[0];
|
|
698
|
+
}
|
|
699
|
+
|
|
700
|
+
const entries = 4;
|
|
701
|
+
const collNumber = Math.ceil(collection.length / entries);
|
|
702
|
+
|
|
703
|
+
//if (oclRender) {
|
|
704
|
+
// collectionBlock += 'M V30 MDLV30/STEABS ATOMS=(' + collection.length;
|
|
705
|
+
|
|
706
|
+
// for (let j = 0; j < collection.length; j++)
|
|
707
|
+
// collectionBlock += ' ' + collection[j];
|
|
708
|
+
|
|
709
|
+
// collectionBlock += ')\n';
|
|
710
|
+
//} else {
|
|
711
|
+
collectionBlock += 'M V30 MDLV30/STEABS ATOMS=(' + collection.length + ' -\n';
|
|
712
|
+
for (let i = 0; i < collNumber; i++) {
|
|
713
|
+
collectionBlock += 'M V30 ';
|
|
714
|
+
const entriesCurrent = i + 1 == collNumber ? collection.length - (collNumber - 1)*entries : entries;
|
|
715
|
+
for (let j = 0; j < entriesCurrent; j++) {
|
|
716
|
+
collectionBlock += (j + 1 == entriesCurrent) ?
|
|
717
|
+
(i == collNumber - 1 ? collection[entries*i + j] + ')\n' : collection[entries*i + j] + ' -\n') :
|
|
718
|
+
collection[entries*i + j] + ' ';
|
|
719
|
+
}
|
|
720
|
+
}
|
|
721
|
+
//}
|
|
722
|
+
|
|
723
|
+
//generate file
|
|
724
|
+
true? natom : natom++;
|
|
725
|
+
macroMolBlock += 'M V30 COUNTS ' + natom + ' ' + nbond + ' 0 0 0\n';
|
|
726
|
+
macroMolBlock += 'M V30 BEGIN ATOM\n';
|
|
727
|
+
macroMolBlock += atomBlock;
|
|
728
|
+
macroMolBlock += 'M V30 END ATOM\n';
|
|
729
|
+
macroMolBlock += 'M V30 BEGIN BOND\n';
|
|
730
|
+
macroMolBlock += bondBlock;
|
|
731
|
+
macroMolBlock += 'M V30 END BOND\n';
|
|
732
|
+
if(useChirality){
|
|
733
|
+
macroMolBlock += 'M V30 BEGIN COLLECTION\n';
|
|
734
|
+
macroMolBlock += collectionBlock;
|
|
735
|
+
macroMolBlock += 'M V30 END COLLECTION\n';
|
|
736
|
+
} else
|
|
737
|
+
macroMolBlock = macroMolBlock.replace(/ CFG=\d/g, ' ');
|
|
738
|
+
|
|
739
|
+
macroMolBlock += 'M V30 END CTAB\n';
|
|
740
|
+
macroMolBlock += 'M END';
|
|
741
|
+
|
|
742
|
+
return macroMolBlock;
|
|
743
|
+
}
|
|
744
|
+
|
|
745
|
+
export function linkV3000(molBlocks: string[], useChirality: boolean = true) {
|
|
746
|
+
let macroMolBlock = '\nDatagrok macromolecule handler\n\n';
|
|
747
|
+
macroMolBlock += ' 0 0 0 0 0 0 999 V3000\n';
|
|
748
|
+
macroMolBlock += 'M V30 BEGIN CTAB\n';
|
|
749
|
+
let atomBlock = '';
|
|
750
|
+
let bondBlock = '';
|
|
751
|
+
let collectionBlock = '';
|
|
752
|
+
const collection: number [] = [];
|
|
753
|
+
let natom = 0;
|
|
754
|
+
let nbond = 0;
|
|
755
|
+
let xShift = 0;
|
|
756
|
+
|
|
757
|
+
for (let i = 0; i < molBlocks.length; i++) {
|
|
758
|
+
molBlocks[i] = molBlocks[i].replaceAll('(-\nM V30 ', '(')
|
|
759
|
+
.replaceAll('-\nM V30 ', '').replaceAll(' )', ')');
|
|
760
|
+
const numbers = extractAtomsBondsNumbersV3000(molBlocks[i]);
|
|
761
|
+
const coordinates = extractAtomDataV3000(molBlocks[i]);
|
|
762
|
+
|
|
763
|
+
let indexAtoms = molBlocks[i].indexOf('M V30 BEGIN ATOM'); // V3000 index for atoms coordinates
|
|
764
|
+
indexAtoms = molBlocks[i].indexOf('\n', indexAtoms);
|
|
765
|
+
let index = indexAtoms;
|
|
766
|
+
let indexEnd = indexAtoms;
|
|
767
|
+
|
|
768
|
+
for (let j = 0; j < numbers.natom; j++) {
|
|
769
|
+
if (coordinates.atomIndex[j] != 1 || i == 0) {
|
|
607
770
|
//rewrite atom number
|
|
608
771
|
index = molBlocks[i].indexOf('V30', index) + 4;
|
|
609
772
|
indexEnd = molBlocks[i].indexOf(' ', index);
|
|
@@ -615,16 +778,14 @@ export function linkV3000(molBlocks: string[], twoChains: boolean = false, useCh
|
|
|
615
778
|
index = molBlocks[i].indexOf(' ', index) + 1;
|
|
616
779
|
indexEnd = molBlocks[i].indexOf(' ', index);
|
|
617
780
|
|
|
618
|
-
const totalShift =
|
|
619
|
-
let coordinate =
|
|
620
|
-
Math.round(10000*coordinates.x[j])/10000 :
|
|
781
|
+
const totalShift = xShift - coordinates.x[0];
|
|
782
|
+
let coordinate =
|
|
621
783
|
Math.round(10000*(parseFloat(molBlocks[i].substring(index, indexEnd)) + totalShift))/10000;
|
|
622
784
|
molBlocks[i] = molBlocks[i].slice(0, index) + coordinate + molBlocks[i].slice(indexEnd);
|
|
623
785
|
|
|
624
786
|
index = molBlocks[i].indexOf(' ', index) + 1;
|
|
625
787
|
indexEnd = molBlocks[i].indexOf(' ', index);
|
|
626
|
-
coordinate =
|
|
627
|
-
Math.round(10000*coordinates.y[j])/10000 :
|
|
788
|
+
coordinate =
|
|
628
789
|
Math.round(10000*(parseFloat(molBlocks[i].substring(index, indexEnd))))/10000;
|
|
629
790
|
molBlocks[i] = molBlocks[i].slice(0, index) + coordinate + molBlocks[i].slice(indexEnd);
|
|
630
791
|
|
|
@@ -681,9 +842,9 @@ export function linkV3000(molBlocks: string[], twoChains: boolean = false, useCh
|
|
|
681
842
|
indexCollection = molBlocks[i].indexOf('M V30 MDLV30/STEABS ATOMS=(', indexCollection);
|
|
682
843
|
}
|
|
683
844
|
|
|
684
|
-
natom +=
|
|
845
|
+
natom += numbers.natom - 1;
|
|
685
846
|
nbond += numbers.nbond;
|
|
686
|
-
xShift +=
|
|
847
|
+
xShift += coordinates.x[numbers.natom - 1] - coordinates.x[0];
|
|
687
848
|
}
|
|
688
849
|
|
|
689
850
|
const entries = 4;
|
|
@@ -710,7 +871,7 @@ export function linkV3000(molBlocks: string[], twoChains: boolean = false, useCh
|
|
|
710
871
|
//}
|
|
711
872
|
|
|
712
873
|
//generate file
|
|
713
|
-
|
|
874
|
+
natom++;
|
|
714
875
|
macroMolBlock += 'M V30 COUNTS ' + natom + ' ' + nbond + ' 0 0 0\n';
|
|
715
876
|
macroMolBlock += 'M V30 BEGIN ATOM\n';
|
|
716
877
|
macroMolBlock += atomBlock;
|
|
@@ -1,48 +1,67 @@
|
|
|
1
1
|
import * as ui from 'datagrok-api/ui';
|
|
2
|
+
import {download} from '../helpers';
|
|
2
3
|
import {sequenceToMolV3000} from '../structures-works/from-monomers';
|
|
3
|
-
import {
|
|
4
|
+
import {linkStrandsV3000} from '../structures-works/mol-transformations';
|
|
4
5
|
import {getFormat} from '../structures-works/sequence-codes-tools';
|
|
5
6
|
|
|
6
|
-
export function saveSdf(as: string, ss: string,
|
|
7
|
+
export function saveSdf(as: string, ss: string,
|
|
8
|
+
oneEntity: boolean, useChirality: boolean,
|
|
9
|
+
invertSS: boolean, invertAS: boolean,
|
|
10
|
+
as2: string | null = null, invertAS2: boolean | null) {
|
|
7
11
|
const formatAs = getFormat(as);
|
|
8
12
|
const formatSs = getFormat(ss);
|
|
13
|
+
let formatAs2: string | null = null;
|
|
14
|
+
let molAS2: string | null = null;
|
|
15
|
+
|
|
9
16
|
const molSS = sequenceToMolV3000(ss, invertSS, false, formatSs!);
|
|
10
17
|
const molAS = sequenceToMolV3000(as, invertAS, false, formatAs!);
|
|
18
|
+
|
|
19
|
+
if (as2 != null && as2 != '') {
|
|
20
|
+
formatAs2 = getFormat(as2!);
|
|
21
|
+
molAS2 = sequenceToMolV3000(as2, invertAS2!, false, formatAs2!);
|
|
22
|
+
}
|
|
23
|
+
|
|
11
24
|
let result: string;
|
|
12
|
-
if (oneEntity)
|
|
13
|
-
|
|
14
|
-
|
|
25
|
+
if (oneEntity) {
|
|
26
|
+
const antiStrands = molAS2 == null ? [molAS] : [molAS, molAS2];
|
|
27
|
+
result = linkStrandsV3000({senseStrands: [molSS], antiStrands: antiStrands}, useChirality) + '\n$$$$\n';
|
|
28
|
+
|
|
29
|
+
} else {
|
|
15
30
|
result =
|
|
16
31
|
molSS + '\n' +
|
|
17
32
|
`> <Sequence>\nSense Strand\n$$$$\n` +
|
|
18
33
|
molAS + '\n' +
|
|
19
34
|
`> <Sequence>\nAnti Sense\n$$$$\n`;
|
|
20
|
-
}
|
|
21
35
|
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
36
|
+
if (molAS2)
|
|
37
|
+
result += molAS2+ '\n' +
|
|
38
|
+
`> <Sequence>\nAnti Sense 2\n$$$$\n`;
|
|
39
|
+
}
|
|
40
|
+
download(ss.replace(/\s/g, '') + '.sdf', encodeURIComponent(result));
|
|
26
41
|
}
|
|
27
42
|
|
|
28
43
|
export function saveSenseAntiSense() {
|
|
29
44
|
const moleculeSvgDiv = ui.block([]);
|
|
30
45
|
const ssInput = ui.textInput('Sense Strand', '');
|
|
31
46
|
const asInput = ui.textInput('Anti Sense', '');
|
|
47
|
+
const asInput2 = ui.textInput('Anti Sense 2', '');
|
|
32
48
|
const straight = "5 prime -> 3 prime";
|
|
33
49
|
const inverse = "3 prime -> 5 prime";
|
|
34
50
|
let ssInverse = false;
|
|
35
51
|
let asInverse = false;
|
|
52
|
+
let as2Inverse = false;
|
|
36
53
|
|
|
37
54
|
const changeSense = ui.choiceInput('SS direction', straight, [straight, inverse]);
|
|
38
55
|
changeSense.onChanged(() => {ssInverse = changeSense.value == inverse;});
|
|
39
56
|
const changeAntiSense = ui.choiceInput('AS direction', straight, [straight, inverse]);
|
|
40
57
|
changeAntiSense.onChanged(() => {asInverse = changeAntiSense.value == inverse;});
|
|
58
|
+
const changeAntiSense2 = ui.choiceInput('AS 2 direction', straight, [straight, inverse]);
|
|
59
|
+
changeAntiSense2.onChanged(() => {asInverse = changeAntiSense.value == inverse;});
|
|
41
60
|
|
|
42
61
|
const saveOption = ui.switchInput('Save as one entity', true);
|
|
43
62
|
const chirality = ui.switchInput('Use chiral', true);
|
|
44
63
|
const saveBtn = ui.button('Save SDF', () =>
|
|
45
|
-
saveSdf(asInput.value, ssInput.value, saveOption.value, chirality.value, ssInverse, asInverse));
|
|
64
|
+
saveSdf(asInput.value, ssInput.value, saveOption.value, chirality.value, ssInverse, asInverse, asInput2.value, as2Inverse));
|
|
46
65
|
|
|
47
66
|
const saveSection = ui.panel([
|
|
48
67
|
ui.div([
|
|
@@ -51,9 +70,11 @@ export function saveSenseAntiSense() {
|
|
|
51
70
|
ui.divV([
|
|
52
71
|
ssInput,
|
|
53
72
|
asInput,
|
|
73
|
+
asInput2,
|
|
54
74
|
ui.div([changeSense], {style: {width: '40'}}),
|
|
55
75
|
changeSense,
|
|
56
76
|
changeAntiSense,
|
|
77
|
+
changeAntiSense2,
|
|
57
78
|
saveOption,
|
|
58
79
|
chirality,
|
|
59
80
|
ui.buttonsInput([saveBtn]),
|