@datagrok/sequence-translator 1.0.14 → 1.0.15
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 +189 -113
- package/dist/package.js +158 -98
- package/package.json +24 -21
- package/scripts/build-monomer-lib.py +52 -14
- package/src/axolabs/constants.ts +4 -4
- package/src/main/main-view.ts +88 -47
- package/src/package.ts +2 -2
- package/src/structures-works/const.ts +3 -16
- package/src/structures-works/converters.ts +28 -26
- package/src/structures-works/mol-transformations.ts +73 -75
- package/{test-SequenceTranslator-e8c06047b7e7-eb4db608.html → test-SequenceTranslator-91c83d8913ff-f94596bc.html} +6 -6
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@datagrok/sequence-translator",
|
|
3
3
|
"friendlyName": "Sequence Translator",
|
|
4
|
-
"version": "1.0.
|
|
4
|
+
"version": "1.0.15",
|
|
5
5
|
"author": {
|
|
6
6
|
"name": "Alexey Choposky",
|
|
7
7
|
"email": "achopovsky@datagrok.ai"
|
|
@@ -13,7 +13,7 @@
|
|
|
13
13
|
"directory": "packages/SequenceTranslator"
|
|
14
14
|
},
|
|
15
15
|
"dependencies": {
|
|
16
|
-
"@datagrok-libraries/utils": "^1.
|
|
16
|
+
"@datagrok-libraries/utils": "^1.17.2",
|
|
17
17
|
"@types/react": "^18.0.15",
|
|
18
18
|
"@datagrok-libraries/bio": "^5.11.1",
|
|
19
19
|
"datagrok-api": "^1.7.2",
|
|
@@ -24,25 +24,6 @@
|
|
|
24
24
|
"ts-loader": "^9.3.1",
|
|
25
25
|
"typescript": "^4.7.4"
|
|
26
26
|
},
|
|
27
|
-
"scripts": {
|
|
28
|
-
"link-api": "npm link datagrok-api",
|
|
29
|
-
"link-all": "npm link datagrok-api @datagrok-libraries/utils @datagrok-libraries/bio",
|
|
30
|
-
"debug-sequencetranslator": "grok publish",
|
|
31
|
-
"release-sequencetranslator": "grok publish localhost --release",
|
|
32
|
-
"build-sequencetranslator": "webpack",
|
|
33
|
-
"build": "webpack",
|
|
34
|
-
"debug-sequencetranslator-public": "grok publish public",
|
|
35
|
-
"release-sequencetranslator-public": "grok publish public --release",
|
|
36
|
-
"debug-sequencetranslator-local": "grok publish local",
|
|
37
|
-
"release-sequencetranslator-local": "grok publish local --release",
|
|
38
|
-
"test": "jest",
|
|
39
|
-
"test-dev": "set HOST=dev && jest",
|
|
40
|
-
"test-local": "set HOST=localhost && jest"
|
|
41
|
-
},
|
|
42
|
-
"sources": [
|
|
43
|
-
"css/style.css",
|
|
44
|
-
"vendors/openchemlib-full.js"
|
|
45
|
-
],
|
|
46
27
|
"devDependencies": {
|
|
47
28
|
"@types/jest": "^27.0.0",
|
|
48
29
|
"@types/jquery": "^3.5.14",
|
|
@@ -62,5 +43,27 @@
|
|
|
62
43
|
"@types/node-fetch": "^2.6.2",
|
|
63
44
|
"node-fetch": "^2.6.7"
|
|
64
45
|
},
|
|
46
|
+
"grokDependencies": {
|
|
47
|
+
"@datagrok/chem": "1.3.32"
|
|
48
|
+
},
|
|
49
|
+
"scripts": {
|
|
50
|
+
"link-api": "npm link datagrok-api",
|
|
51
|
+
"link-all": "npm link datagrok-api @datagrok-libraries/utils @datagrok-libraries/bio",
|
|
52
|
+
"debug-sequencetranslator": "grok publish",
|
|
53
|
+
"release-sequencetranslator": "grok publish localhost --release",
|
|
54
|
+
"build-sequencetranslator": "webpack",
|
|
55
|
+
"build": "webpack",
|
|
56
|
+
"debug-sequencetranslator-public": "grok publish public",
|
|
57
|
+
"release-sequencetranslator-public": "grok publish public --release",
|
|
58
|
+
"debug-sequencetranslator-local": "grok publish local",
|
|
59
|
+
"release-sequencetranslator-local": "grok publish local --release",
|
|
60
|
+
"test": "jest",
|
|
61
|
+
"test-dev": "set HOST=dev && jest",
|
|
62
|
+
"test-local": "set HOST=localhost && jest"
|
|
63
|
+
},
|
|
64
|
+
"sources": [
|
|
65
|
+
"css/style.css",
|
|
66
|
+
"vendors/openchemlib-full.js"
|
|
67
|
+
],
|
|
65
68
|
"category": "Bioinformatics"
|
|
66
69
|
}
|
|
@@ -1,10 +1,8 @@
|
|
|
1
1
|
from io import TextIOWrapper
|
|
2
2
|
|
|
3
|
-
from rdkit.Chem import AllChem
|
|
4
3
|
from rdkit import Chem
|
|
5
4
|
|
|
6
5
|
import orjson
|
|
7
|
-
import json
|
|
8
6
|
|
|
9
7
|
import click
|
|
10
8
|
|
|
@@ -12,16 +10,55 @@ from click_default_group import DefaultGroup
|
|
|
12
10
|
from rdkit.Chem.rdchem import Mol
|
|
13
11
|
|
|
14
12
|
|
|
15
|
-
def
|
|
16
|
-
|
|
13
|
+
def molAddCollection(mol: Mol, name: str, title: str = None) -> str:
|
|
14
|
+
"""
|
|
15
|
+
Get and postprocess (atom's CFG, title, e.t.c.) molblock
|
|
16
|
+
:param mol: Mol molecule structure / object
|
|
17
|
+
:param name: Monomer name to add to molblock title
|
|
18
|
+
:param title: title to replace in Chem.MolToMolBlock() string output
|
|
19
|
+
:return: molblock string
|
|
20
|
+
"""
|
|
17
21
|
res: str = Chem.MolToMolBlock(mol, forceV3000=True) # MolToMolFile
|
|
18
|
-
return res
|
|
19
22
|
|
|
23
|
+
mb_line_list: list[str] = res.split('\n')
|
|
24
|
+
if title:
|
|
25
|
+
mb_line_list[1] = title
|
|
26
|
+
|
|
27
|
+
if name and name not in mb_line_list[1]:
|
|
28
|
+
mb_line_list[1] += '|' + name
|
|
29
|
+
|
|
30
|
+
end_bond_idx: int = mb_line_list.index('M V30 END BOND')
|
|
31
|
+
chirality = [atom.GetChiralTag() for atom in mol.GetAtoms()]
|
|
32
|
+
begin_atom_idx = mb_line_list.index('M V30 BEGIN ATOM')
|
|
33
|
+
end_atom_idx = mb_line_list.index('M V30 END ATOM')
|
|
34
|
+
for atom_idx in range(1, end_atom_idx - begin_atom_idx):
|
|
35
|
+
line_idx = begin_atom_idx + atom_idx
|
|
36
|
+
atom_ch = chirality[atom_idx - 1]
|
|
37
|
+
if atom_ch != Chem.rdchem.CHI_UNSPECIFIED:
|
|
38
|
+
mb_line_list[line_idx] += " CFG={0}".format(int(atom_ch))
|
|
39
|
+
|
|
40
|
+
steabs: list[int] = [i + 1 for (i, ch) in enumerate(chirality) if ch != Chem.rdchem.CHI_UNSPECIFIED]
|
|
41
|
+
if len(steabs) > 0:
|
|
42
|
+
steabs_str: str = "M V30 MDLV30/STEABS ATOMS=({count} {list})" \
|
|
43
|
+
.format(count=len(steabs), list=' '.join([str(idx) for idx in steabs]))
|
|
44
|
+
|
|
45
|
+
mb_line_list = mb_line_list[:(end_bond_idx + 1)] + \
|
|
46
|
+
["M V30 BEGIN COLLECTION", steabs_str, "M V30 END COLLECTION"] + \
|
|
47
|
+
mb_line_list[(end_bond_idx + 1):]
|
|
20
48
|
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
49
|
+
return '\n'.join(mb_line_list)
|
|
50
|
+
|
|
51
|
+
|
|
52
|
+
def molfile2molfile(src_mol: str, name: str) -> str:
|
|
53
|
+
mol: Mol = Chem.MolFromMolBlock(src_mol)
|
|
54
|
+
src_mf_lines = src_mol.split('\n')
|
|
55
|
+
title = src_mf_lines[1]
|
|
56
|
+
return molAddCollection(mol, name, title=title)
|
|
57
|
+
|
|
58
|
+
|
|
59
|
+
def smiles2molfile(smiles: str, name: str) -> str:
|
|
60
|
+
mol: Mol = Chem.MolFromSmiles(smiles)
|
|
61
|
+
return molAddCollection(mol, name)
|
|
25
62
|
|
|
26
63
|
|
|
27
64
|
CodesType = dict[str, dict[str, list[str]]]
|
|
@@ -29,13 +66,13 @@ CodesType = dict[str, dict[str, list[str]]]
|
|
|
29
66
|
|
|
30
67
|
class Monomer:
|
|
31
68
|
def __init__(self,
|
|
32
|
-
symbol: str, name: str, smiles: str,
|
|
69
|
+
symbol: str, name: str, molfile: str, smiles: str,
|
|
33
70
|
codes: CodesType):
|
|
34
71
|
self.monomerType = 'Backbone'
|
|
35
72
|
self.smiles = smiles
|
|
36
73
|
self.name = name
|
|
37
74
|
self.author = 'SequenceTranslator'
|
|
38
|
-
self.molfile = smiles2molfile(smiles)
|
|
75
|
+
self.molfile = molfile2molfile(molfile, name) if molfile else smiles2molfile(smiles, name)
|
|
39
76
|
self.naturalAnalog = ''
|
|
40
77
|
self.rgroups = [
|
|
41
78
|
{
|
|
@@ -58,8 +95,9 @@ class Monomer:
|
|
|
58
95
|
|
|
59
96
|
@staticmethod
|
|
60
97
|
def from_json(src_json: {}):
|
|
61
|
-
obj = Monomer(src_json['symbol'], src_json['name'],
|
|
62
|
-
|
|
98
|
+
obj = Monomer(src_json['symbol'], src_json['name'],
|
|
99
|
+
src_json['molfile'], src_json['smiles'],
|
|
100
|
+
src_json['codes'])
|
|
63
101
|
return obj
|
|
64
102
|
|
|
65
103
|
def to_json(self):
|
|
@@ -89,7 +127,7 @@ def codes2monomers(codes_json: {}) -> dict[str, Monomer]:
|
|
|
89
127
|
symbol = monomer_json['name']
|
|
90
128
|
name = monomer_json['name']
|
|
91
129
|
smiles = monomer_json['SMILES']
|
|
92
|
-
monomers_res[monomer_name] = Monomer(symbol, name, smiles, {})
|
|
130
|
+
monomers_res[monomer_name] = Monomer(symbol, name, None, smiles, {})
|
|
93
131
|
codes = monomers_res[monomer_name].codes
|
|
94
132
|
if codes_src not in codes:
|
|
95
133
|
codes[codes_src] = {}
|
package/src/axolabs/constants.ts
CHANGED
|
@@ -44,22 +44,22 @@ export const AXOLABS_MAP:
|
|
|
44
44
|
color: 'rgb(255,192,0)',
|
|
45
45
|
},
|
|
46
46
|
'A': {
|
|
47
|
-
fullName: '
|
|
47
|
+
fullName: 'Adenosine',
|
|
48
48
|
symbols: ['a', 'a', 'a', 'a'],
|
|
49
49
|
color: RNA_COLOR,
|
|
50
50
|
},
|
|
51
51
|
'C': {
|
|
52
|
-
fullName: '
|
|
52
|
+
fullName: 'Cytidine',
|
|
53
53
|
symbols: ['c', 'c', 'c', 'c'],
|
|
54
54
|
color: RNA_COLOR,
|
|
55
55
|
},
|
|
56
56
|
'G': {
|
|
57
|
-
fullName: '
|
|
57
|
+
fullName: 'Guanosine',
|
|
58
58
|
symbols: ['g', 'g', 'g', 'g'],
|
|
59
59
|
color: RNA_COLOR,
|
|
60
60
|
},
|
|
61
61
|
'U': {
|
|
62
|
-
fullName: '
|
|
62
|
+
fullName: 'Uridine',
|
|
63
63
|
symbols: ['u', 'u', 'u', 'u'],
|
|
64
64
|
color: RNA_COLOR,
|
|
65
65
|
},
|
package/src/main/main-view.ts
CHANGED
|
@@ -1,36 +1,30 @@
|
|
|
1
1
|
import * as grok from 'datagrok-api/grok';
|
|
2
2
|
import * as ui from 'datagrok-api/ui';
|
|
3
3
|
import * as DG from 'datagrok-api/dg';
|
|
4
|
+
|
|
5
|
+
import * as rxjs from 'rxjs';
|
|
4
6
|
import {convertSequence, undefinedInputSequence, isValidSequence} from '../structures-works/sequence-codes-tools';
|
|
5
7
|
import {map} from '../structures-works/map';
|
|
6
8
|
import {MODIFICATIONS} from '../structures-works/const';
|
|
7
9
|
import {sequenceToSmiles, sequenceToMolV3000} from '../structures-works/from-monomers';
|
|
8
10
|
import $ from 'cash-dom';
|
|
9
11
|
import {download} from '../helpers';
|
|
12
|
+
import {extractAtomDataV3000} from '../structures-works/mol-transformations';
|
|
13
|
+
import {errorToConsole} from '@datagrok-libraries/utils/src/to-console';
|
|
10
14
|
|
|
11
15
|
const defaultInput = 'fAmCmGmAmCpsmU'; // todo: capitalize constants
|
|
12
16
|
const sequenceWasCopied = 'Copied'; // todo: wrap hardcoded literals into constants
|
|
13
17
|
const tooltipSequence = 'Copy sequence';
|
|
14
18
|
|
|
15
19
|
export async function mainView(): Promise<HTMLDivElement> {
|
|
16
|
-
const
|
|
20
|
+
const onInput: rxjs.Subject<string> = new rxjs.Subject<string>();
|
|
21
|
+
|
|
17
22
|
async function updateTableAndMolecule(sequence: string, inputFormat: string): Promise<void> {
|
|
18
23
|
moleculeSvgDiv.innerHTML = '';
|
|
19
24
|
outputTableDiv.innerHTML = '';
|
|
20
25
|
const pi = DG.TaskBarProgressIndicator.create('Rendering table and molecule...');
|
|
21
26
|
let errorsExist = false;
|
|
22
27
|
|
|
23
|
-
// external helm-like monomers library
|
|
24
|
-
const fileExists = await grok.dapi.files.exists(monomersLibAddress);
|
|
25
|
-
if (!fileExists) {
|
|
26
|
-
// todo: improve behaviour in this case
|
|
27
|
-
grok.shell.warning('Please, provide the file with monomers library as System:AppData/SequenceTranslator/helmLib.json');
|
|
28
|
-
pi.close();
|
|
29
|
-
return;
|
|
30
|
-
}
|
|
31
|
-
|
|
32
|
-
const monomersLib = await grok.dapi.files.readAsText(monomersLibAddress);
|
|
33
|
-
|
|
34
28
|
try {
|
|
35
29
|
sequence = sequence.replace(/\s/g, '');
|
|
36
30
|
const output = isValidSequence(sequence, null);
|
|
@@ -46,16 +40,16 @@ export async function mainView(): Promise<HTMLDivElement> {
|
|
|
46
40
|
tableRows.push({
|
|
47
41
|
'key': key,
|
|
48
42
|
'value': ('indexOfFirstNotValidChar' in outputSequenceObj) ?
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
43
|
+
ui.divH([
|
|
44
|
+
ui.divText(sequence.slice(0, indexOfFirstNotValidChar), {style: {color: 'grey'}}),
|
|
45
|
+
ui.tooltip.bind(
|
|
46
|
+
ui.divText(sequence.slice(indexOfFirstNotValidChar), {style: {color: 'red'}}),
|
|
47
|
+
'Expected format: ' + JSON.parse(outputSequenceObj.indexOfFirstNotValidChar!).synthesizer +
|
|
48
|
+
'. See tables with valid codes on the right',
|
|
49
|
+
),
|
|
50
|
+
]) : //@ts-ignore
|
|
51
|
+
ui.link(outputSequenceObj[key], () => navigator.clipboard.writeText(outputSequenceObj[key])
|
|
52
|
+
.then(() => grok.shell.info(sequenceWasCopied)), tooltipSequence, ''),
|
|
59
53
|
});
|
|
60
54
|
}
|
|
61
55
|
|
|
@@ -67,27 +61,71 @@ export async function mainView(): Promise<HTMLDivElement> {
|
|
|
67
61
|
);
|
|
68
62
|
|
|
69
63
|
if (outputSequenceObj.type != undefinedInputSequence && outputSequenceObj.Error != undefinedInputSequence) {
|
|
70
|
-
const
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
64
|
+
const formCanvasWidth = 500;
|
|
65
|
+
const formCanvasHeight = 170;
|
|
66
|
+
const formCanvas = ui.canvas(
|
|
67
|
+
formCanvasWidth * window.devicePixelRatio, formCanvasHeight * window.devicePixelRatio);
|
|
68
|
+
formCanvas.style.width = `${formCanvasWidth}px`;
|
|
69
|
+
formCanvas.style.height = `${formCanvasHeight}px`;
|
|
70
|
+
|
|
71
|
+
formCanvas.addEventListener('click', async () => {
|
|
72
|
+
try {
|
|
73
|
+
const mol = sequenceToMolV3000(
|
|
74
|
+
inputSequenceField.value.replace(/\s/g, ''), false, true,
|
|
75
|
+
output.synthesizer![0],
|
|
76
|
+
);
|
|
77
|
+
console.log(mol);
|
|
78
|
+
|
|
79
|
+
const addDiv = ui.div([], {style: {overflowX: 'scroll'}});
|
|
80
|
+
|
|
81
|
+
// addDiv size required, but now available before dialog show()
|
|
82
|
+
const coordinates = extractAtomDataV3000(mol);
|
|
83
|
+
const cw: number = $(window).width() * 0.80; // addDiv.clientWidth
|
|
84
|
+
const ch: number = $(window).height() * 0.70; // addDiv.clientHeight
|
|
85
|
+
const molWidth: number = Math.max(...coordinates.x) - Math.min(...coordinates.x);
|
|
86
|
+
const molHeight: number = Math.max(...coordinates.y) - Math.min(...coordinates.y);
|
|
87
|
+
|
|
88
|
+
const wR: number = cw / molWidth;
|
|
89
|
+
const hR: number = ch / molHeight;
|
|
90
|
+
const r: number = hR; // Math.max(wR, hR);
|
|
91
|
+
const dlgCanvasWidth = r * molWidth;
|
|
92
|
+
const dlgCanvasHeight = r * molHeight;
|
|
93
|
+
|
|
94
|
+
const dlgCanvas = ui.canvas(dlgCanvasWidth * window.devicePixelRatio, dlgCanvasHeight * window.devicePixelRatio);
|
|
95
|
+
dlgCanvas.style.width = `${dlgCanvasWidth}px`;
|
|
96
|
+
dlgCanvas.style.height = `${dlgCanvasHeight}px`;
|
|
97
|
+
|
|
98
|
+
// // @ts-ignore
|
|
99
|
+
// OCL.StructureView.drawMolecule(dlgCanvas, OCL.Molecule.fromMolfile(mol), {suppressChiralText: true});
|
|
100
|
+
// await grok.chem.canvasMol(0, 0, dlgCanvas.width, dlgCanvas.height, dlgCanvas, mol, null,
|
|
101
|
+
// {setNewCoords: false, normalizeDepiction: false, straightenDepiction: false});
|
|
102
|
+
await grok.functions.call('Chem:canvasMol', {
|
|
103
|
+
x: 0, y: 0, w: dlgCanvas.width, h: dlgCanvas.height, canvas: dlgCanvas,
|
|
104
|
+
molString: mol, scaffoldMolString: '',
|
|
105
|
+
options: {setNewCoords: false, normalizeDepiction: false, straightenDepiction: false}
|
|
106
|
+
});
|
|
107
|
+
|
|
108
|
+
addDiv.appendChild(dlgCanvas);
|
|
109
|
+
ui.dialog('Molecule: ' + inputSequenceField.value)
|
|
110
|
+
.add(addDiv)
|
|
111
|
+
.showModal(true);
|
|
112
|
+
} catch (err) {
|
|
113
|
+
const errStr = errorToConsole(err);
|
|
114
|
+
console.error(errStr);
|
|
115
|
+
}
|
|
83
116
|
});
|
|
84
|
-
$(
|
|
85
|
-
$(
|
|
117
|
+
$(formCanvas).on('mouseover', () => $(formCanvas).css('cursor', 'zoom-in'));
|
|
118
|
+
$(formCanvas).on('mouseout', () => $(formCanvas).css('cursor', 'default'));
|
|
86
119
|
const mol = sequenceToMolV3000(inputSequenceField.value.replace(/\s/g, ''), false, true,
|
|
87
120
|
output.synthesizer![0]);
|
|
88
|
-
// @ts-ignore
|
|
89
|
-
OCL.StructureView.drawMolecule(
|
|
90
|
-
|
|
121
|
+
// // @ts-ignore
|
|
122
|
+
// OCL.StructureView.drawMolecule(formCanvas, OCL.Molecule.fromMolfile(mol), {suppressChiralText: true});
|
|
123
|
+
await grok.functions.call('Chem:canvasMol', {
|
|
124
|
+
x: 0, y: 0, w: formCanvas.width, h: formCanvas.height, canvas: formCanvas,
|
|
125
|
+
molString: mol, scaffoldMolString: '',
|
|
126
|
+
options: {setNewCoords: false, normalizeDepiction: false, straightenDepiction: false}
|
|
127
|
+
});
|
|
128
|
+
moleculeSvgDiv.append(formCanvas);
|
|
91
129
|
} else
|
|
92
130
|
moleculeSvgDiv.innerHTML = '';
|
|
93
131
|
} finally {
|
|
@@ -102,6 +140,11 @@ export async function mainView(): Promise<HTMLDivElement> {
|
|
|
102
140
|
const moleculeSvgDiv = ui.block([]);
|
|
103
141
|
const outputTableDiv = ui.div([]);
|
|
104
142
|
const inputSequenceField = ui.textInput('', defaultInput, (sequence: string) => {
|
|
143
|
+
// Send event to DG.debounce()
|
|
144
|
+
onInput.next(sequence);
|
|
145
|
+
});
|
|
146
|
+
|
|
147
|
+
DG.debounce<string>(onInput, 300).subscribe((sequence) => {
|
|
105
148
|
updateTableAndMolecule(sequence, inputFormatChoiceInput.value!);
|
|
106
149
|
});
|
|
107
150
|
|
|
@@ -139,9 +182,9 @@ export async function mainView(): Promise<HTMLDivElement> {
|
|
|
139
182
|
);
|
|
140
183
|
|
|
141
184
|
const overhangModificationsGrid = DG.Viewer.grid(
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
185
|
+
DG.DataFrame.fromColumns([
|
|
186
|
+
DG.Column.fromStrings('Name', Object.keys(MODIFICATIONS)),
|
|
187
|
+
])!, {showRowHeader: false, showCellTooltip: false, allowEdit: false},
|
|
145
188
|
);
|
|
146
189
|
updateTableAndMolecule(defaultInput, inputFormatChoiceInput.value!);
|
|
147
190
|
|
|
@@ -171,7 +214,6 @@ export async function mainView(): Promise<HTMLDivElement> {
|
|
|
171
214
|
|
|
172
215
|
const downloadMolFileIcon = ui.iconFA('download', async () => {
|
|
173
216
|
const clearSequence = inputSequenceField.value.replace(/\s/g, '');
|
|
174
|
-
const monomersLib = await grok.dapi.files.readAsText(monomersLibAddress);
|
|
175
217
|
const result = sequenceToMolV3000(inputSequenceField.value.replace(/\s/g, ''), false, false,
|
|
176
218
|
inputFormatChoiceInput.value!);
|
|
177
219
|
download(clearSequence + '.mol', encodeURIComponent(result));
|
|
@@ -203,9 +245,8 @@ export async function mainView(): Promise<HTMLDivElement> {
|
|
|
203
245
|
appMainDescription,
|
|
204
246
|
ui.div([
|
|
205
247
|
ui.h1('Input sequence'),
|
|
206
|
-
ui.div([
|
|
207
|
-
|
|
208
|
-
], 'input-base'),
|
|
248
|
+
ui.div([], 'input-base'),
|
|
249
|
+
inputSequenceField.root,
|
|
209
250
|
], 'inputSequence'),
|
|
210
251
|
ui.div([inputFormatChoiceInput], {style: {padding: '5px 0'}}),
|
|
211
252
|
ui.block([
|
package/src/package.ts
CHANGED
|
@@ -16,11 +16,11 @@ export let monomerWorks: MonomerWorks | null = null;
|
|
|
16
16
|
|
|
17
17
|
export function getMonomerWorks() {
|
|
18
18
|
return monomerWorks;
|
|
19
|
-
}
|
|
19
|
+
}
|
|
20
20
|
|
|
21
21
|
export function getMonomerLib() {
|
|
22
22
|
return monomerLib;
|
|
23
|
-
}
|
|
23
|
+
}
|
|
24
24
|
|
|
25
25
|
//name: Sequence Translator
|
|
26
26
|
//tags: app
|
|
@@ -1,18 +1,5 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
left: 'O[C@@H]1C[C@@H]O[C@H]1CO',
|
|
5
|
-
right: 'O[C@@H]1C[C@@H]O[C@H]1CO',
|
|
6
|
-
},
|
|
7
|
-
'(GalNAc-2-JNJ)': {
|
|
8
|
-
molecularWeight: 1273.3,
|
|
9
|
-
left: 'C(COCCC(=O)NCCCNC(=O)CCCCOC2OC(CO)C(O)C(O)C2NC(=O)C)' +
|
|
10
|
-
'(COCCC(=O)NCCCNC(=O)CCCCOC2OC(CO)C(O)C(O)C2NC(=O)C)' +
|
|
11
|
-
'(COCCC(=O)NCCCNC(=O)CCCCOC2OC(CO)C(O)C(O)C2NC(=O)C)NC(=O)CCCC(=O)NCC(O)CO',
|
|
12
|
-
right: 'OCC(O)CNC(=O)CCCC(=O)NC(COCCC(=O)NCCCNC(=O)CCCCOC2OC(CO)C(O)C(O)C2NC(=O)C)' +
|
|
13
|
-
'(COCCC(=O)NCCCNC(=O)CCCCOC2OC(CO)C(O)C(O)C2NC(=O)C)'+
|
|
14
|
-
'(COCCC(=O)NCCCNC(=O)CCCCOC2OC(CO)C(O)C(O)C2NC(=O)C)',
|
|
15
|
-
},
|
|
16
|
-
};
|
|
1
|
+
import * as map from './map';
|
|
2
|
+
|
|
3
|
+
export const MODIFICATIONS = map.MODIFICATIONS;
|
|
17
4
|
|
|
18
5
|
export const standardPhosphateLinkSmiles = 'OP(=O)(O)O';
|
|
@@ -1,33 +1,35 @@
|
|
|
1
|
-
import {lcmsToGcrs} from './map';
|
|
1
|
+
import {lcmsToGcrs, MODIFICATIONS} from './map';
|
|
2
2
|
import * as DG from 'datagrok-api/dg';
|
|
3
3
|
import {DELIMITER} from './map';
|
|
4
|
+
import {sortByStringLengthInDescendingOrder} from '../helpers';
|
|
4
5
|
//name: gcrsToLcms
|
|
5
6
|
//input: string nucleotides {semType: GCRS}
|
|
6
7
|
//output: string result {semType: LCMS}
|
|
7
8
|
export function gcrsToLcms(sequence: string): string {
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
9
|
+
try {
|
|
10
|
+
const df = DG.DataFrame.fromCsv(lcmsToGcrs);
|
|
11
|
+
const arr1: string[] = df.getCol('GCRS').toList();
|
|
12
|
+
const arr2: string[] = df.getCol('LCMS').toList();
|
|
13
|
+
const obj: { [i: string]: string } = {};
|
|
14
|
+
arr1.forEach((element, index) => obj[element] = arr2[index]);
|
|
15
|
+
obj[DELIMITER] = DELIMITER;
|
|
16
|
+
const codes = arr1
|
|
17
|
+
.concat(DELIMITER)
|
|
18
|
+
.concat(Object.keys(MODIFICATIONS));
|
|
19
|
+
const sortedCodes = sortByStringLengthInDescendingOrder(codes);
|
|
20
|
+
let i = 0;
|
|
21
|
+
let r1 = '';
|
|
22
|
+
while (i < sequence.length) {
|
|
23
|
+
const matchedCode = sortedCodes.find((c) => c == sequence.slice(i, i + c.length))!;
|
|
24
|
+
r1 += obj[sequence.slice(i, i + matchedCode.length)];
|
|
25
|
+
i += matchedCode.length;
|
|
26
|
+
}
|
|
27
|
+
while (r1.indexOf('//') != -1)
|
|
28
|
+
r1 = r1.replace('//', '/');
|
|
29
|
+
return r1;
|
|
30
|
+
} catch {
|
|
31
|
+
return '<error>';
|
|
27
32
|
}
|
|
28
|
-
while (r1.indexOf('//') != -1)
|
|
29
|
-
r1 = r1.replace('//', '/');
|
|
30
|
-
return r1;
|
|
31
33
|
}
|
|
32
34
|
|
|
33
35
|
//name: asoGapmersNucleotidesToBioSpring
|
|
@@ -35,9 +37,9 @@ export function gcrsToLcms(sequence: string): string {
|
|
|
35
37
|
//output: string result {semType: BioSpring / Gapmers}
|
|
36
38
|
export function asoGapmersNucleotidesToBioSpring(nucleotides: string): string {
|
|
37
39
|
let count: number = -1;
|
|
38
|
-
const objForEdges: {[index: string]: string} = {
|
|
40
|
+
const objForEdges: { [index: string]: string } = {
|
|
39
41
|
'(invabasic)': '(invabasic)', '(GalNAc-2-JNJ)': '(GalNAc-2-JNJ)', 'T': '5*', 'A': '6*', 'C': '7*', 'G': '8*'};
|
|
40
|
-
const objForCenter: {[index: string]: string} = {
|
|
42
|
+
const objForCenter: { [index: string]: string } = {
|
|
41
43
|
'(invabasic)': '(invabasic)', '(GalNAc-2-JNJ)': '(GalNAc-2-JNJ)', 'T': 'T*', 'A': 'A*', 'C': '9*', 'G': 'G*'};
|
|
42
44
|
return nucleotides.replace(/(\(invabasic\)|\(GalNAc-2-JNJ\)|A|T|C|G)/g, function(x: string) {
|
|
43
45
|
count++;
|
|
@@ -51,7 +53,7 @@ export function asoGapmersNucleotidesToBioSpring(nucleotides: string): string {
|
|
|
51
53
|
//output: string result {semType: GCRS / Gapmers}
|
|
52
54
|
export function asoGapmersNucleotidesToGcrs(nucleotides: string): string {
|
|
53
55
|
let count: number = -1;
|
|
54
|
-
const objForEdges: {[index: string]: string} = {
|
|
56
|
+
const objForEdges: { [index: string]: string } = {
|
|
55
57
|
'(invabasic)': '(invabasic)', '(GalNAc-2-JNJ)': '(GalNAc-2-JNJ)', 'T': 'moeUnps',
|
|
56
58
|
'A': 'moeAnps', 'C': 'moe5mCnps', 'G': 'moeGnps'};
|
|
57
59
|
const objForCenter: {[index: string]: string} = {'(invabasic)': '(invabasic)', '(GalNAc-2-JNJ)': '(GalNAc-2-JNJ)',
|