@datagrok/helm 2.0.2 → 2.0.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/README.md +1 -1
- package/dist/package-test.js +3582 -205
- package/dist/package.js +1461 -614
- package/helm/JSDraw/test.html +3 -0
- package/package.json +24 -20
- package/src/__jest__/remote.test.ts +9 -8
- package/src/__jest__/test-node.ts +3 -2
- package/src/package-test.ts +0 -1
- package/src/package.ts +382 -122
- package/src/tests/helm-tests.ts +50 -4
- package/{test-Helm-4f0c8bae6479-a2fd943e.html → test-Helm-916a90d7d48b-481b4bd5.html} +10 -7
- /package/{images → vendor}/helm-rendering.png +0 -0
package/src/package.ts
CHANGED
|
@@ -2,45 +2,45 @@
|
|
|
2
2
|
import * as grok from 'datagrok-api/grok';
|
|
3
3
|
import * as ui from 'datagrok-api/ui';
|
|
4
4
|
import * as DG from 'datagrok-api/dg';
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
import {NOTATION} from '@datagrok-libraries/bio/src/utils/units-handler';
|
|
8
|
-
import {WebLogo} from '@datagrok-libraries/bio/src/viewers/web-logo';
|
|
5
|
+
import * as bio from '@datagrok-libraries/bio';
|
|
6
|
+
|
|
9
7
|
import {createJsonMonomerLibFromSdf} from './utils';
|
|
10
8
|
import {MONOMER_MANAGER_MAP, RGROUPS, RGROUP_CAP_GROUP_NAME, RGROUP_LABEL, SMILES} from './constants';
|
|
11
|
-
|
|
9
|
+
import {delay} from '@datagrok-libraries/utils/src/test';
|
|
12
10
|
|
|
13
11
|
export const _package = new DG.Package();
|
|
14
12
|
|
|
15
|
-
const lru = new DG.LruCache<any, any>();
|
|
16
13
|
const STORAGE_NAME = 'Libraries';
|
|
17
|
-
let i = 0;
|
|
18
14
|
const LIB_PATH = 'libraries/';
|
|
19
15
|
|
|
20
16
|
|
|
21
17
|
//tags: init
|
|
22
18
|
export async function initHelm(): Promise<void> {
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
19
|
+
return new Promise(async (resolve, reject) => {
|
|
20
|
+
// @ts-ignore
|
|
21
|
+
dojo.ready(function() { resolve(null); });
|
|
22
|
+
await loadLibraries();
|
|
23
|
+
});
|
|
28
24
|
}
|
|
29
25
|
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
26
|
+
async function loadLibraries() {
|
|
27
|
+
let uploadedLibraries: string[] = Object.values(await grok.dapi.userDataStorage.get(STORAGE_NAME, true));
|
|
28
|
+
for (let i = 0; i < uploadedLibraries.length; ++i) {
|
|
29
|
+
await monomerManager(await grok.dapi.userDataStorage.getValue(STORAGE_NAME, uploadedLibraries[i], true));
|
|
30
|
+
}
|
|
33
31
|
}
|
|
34
32
|
|
|
35
|
-
//name:
|
|
36
|
-
|
|
37
|
-
|
|
33
|
+
//name: helmCellRenderer
|
|
34
|
+
//tags: cellRenderer
|
|
35
|
+
//meta.cellType: helm
|
|
36
|
+
//meta.columnTags: units=helm
|
|
37
|
+
//output: grid_cell_renderer result
|
|
38
|
+
export function helmCellRenderer(): HelmCellRenderer {
|
|
39
|
+
return new HelmCellRenderer();
|
|
38
40
|
}
|
|
39
41
|
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
//input: grid_cell cell
|
|
43
|
-
export function editMoleculeCell(cell: DG.GridCell): void {
|
|
42
|
+
|
|
43
|
+
function webEditor(cell?: DG.GridCell, value?: string) {
|
|
44
44
|
let view = ui.div();
|
|
45
45
|
org.helm.webeditor.MolViewer.molscale = 0.8;
|
|
46
46
|
let app = new scil.helm.App(view, {
|
|
@@ -65,101 +65,106 @@ export function editMoleculeCell(cell: DG.GridCell): void {
|
|
|
65
65
|
scil.apply(app.properties.parent.style, s);
|
|
66
66
|
app.structureview.resize(sizes.rightwidth, sizes.bottomheight + app.toolbarheight);
|
|
67
67
|
app.mex.resize(sizes.topheight - 80);
|
|
68
|
-
|
|
69
|
-
|
|
68
|
+
setTimeout(function() {
|
|
69
|
+
if (typeof cell !== 'undefined') {
|
|
70
70
|
app.canvas.helm.setSequence(cell.cell.value, 'HELM');
|
|
71
|
-
}
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
71
|
+
} else {
|
|
72
|
+
app.canvas.helm.setSequence(value, 'HELM');
|
|
73
|
+
}
|
|
74
|
+
}, 200);
|
|
75
|
+
//@ts-ignore
|
|
76
|
+
ui.dialog({showHeader: false, showFooter: true})
|
|
77
|
+
.add(view)
|
|
78
|
+
.onOK(() => {
|
|
79
|
+
if (typeof cell !== 'undefined') {
|
|
80
|
+
cell.cell.value = app.canvas.getHelm(true).replace(/<\/span>/g, '').replace(/<span style='background:#bbf;'>/g, '');
|
|
81
|
+
}
|
|
82
|
+
}).show({modal: true, fullScreen: true});
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
|
|
86
|
+
//tags: cellEditor
|
|
87
|
+
//description: Macromolecule
|
|
88
|
+
//input: grid_cell cell
|
|
89
|
+
export function editMoleculeCell(cell: DG.GridCell): void {
|
|
90
|
+
if (cell.gridColumn.column.tags[DG.TAGS.UNITS] === 'helm') {
|
|
91
|
+
webEditor(cell);
|
|
91
92
|
}
|
|
92
93
|
}
|
|
93
94
|
|
|
94
|
-
//name:
|
|
95
|
+
//name: Open Helm Web Editor
|
|
96
|
+
//description: Adds editor
|
|
97
|
+
//meta.action: Open Helm Web Editor
|
|
98
|
+
//input: string mol { semType: Macromolecule }
|
|
99
|
+
export function openEditor(mol: string): void {
|
|
100
|
+
let df = grok.shell.tv.grid.dataFrame;
|
|
101
|
+
let converter = new bio.NotationConverter(df.columns.bySemType('Macromolecule'));
|
|
102
|
+
const resStr = converter.convertStringToHelm(mol, '/');
|
|
103
|
+
webEditor(undefined, resStr);
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
//name: Properties
|
|
95
107
|
//tags: panel, widgets
|
|
96
108
|
//input: string helmString {semType: Macromolecule}
|
|
97
109
|
//output: widget result
|
|
98
|
-
export async function
|
|
99
|
-
|
|
100
|
-
|
|
110
|
+
export async function propertiesPanel(helmString: string) {
|
|
111
|
+
let grid = grok.shell.tv.grid;
|
|
112
|
+
let parent = grid.root.parentElement;
|
|
113
|
+
const host = ui.div([]);
|
|
114
|
+
parent.appendChild(host);
|
|
115
|
+
let editor = new JSDraw2.Editor(host, {viewonly: true});
|
|
116
|
+
host.style.width = '0px';
|
|
117
|
+
host.style.height = '0px';
|
|
118
|
+
editor.setHelm(helmString);
|
|
119
|
+
let formula = editor.getFormula(true);
|
|
120
|
+
let molWeight = Math.round(editor.getMolWeight() * 100) / 100;
|
|
121
|
+
let coef = Math.round(editor.getExtinctionCoefficient(true) * 100) / 100;
|
|
122
|
+
parent.lastChild.remove();
|
|
101
123
|
return new DG.Widget(
|
|
102
124
|
ui.tableFromMap({
|
|
103
|
-
'formula':
|
|
104
|
-
'molecular weight':
|
|
105
|
-
'extinction coefficient':
|
|
106
|
-
'molfile': result[3],
|
|
107
|
-
'fasta': ui.wait(async () => ui.divText(await helmToFasta(helmString))),
|
|
108
|
-
'rna analogue sequence': ui.wait(async () => ui.divText(await helmToRNA(helmString))),
|
|
109
|
-
'smiles': ui.wait(async () => ui.divText(await helmToSmiles(helmString))),
|
|
110
|
-
//'peptide analogue sequence': ui.wait(async () => ui.divText(await helmToPeptide(helmString))),
|
|
125
|
+
'formula': formula.replace(/<sub>/g, '').replace(/<\/sub>/g, ''),
|
|
126
|
+
'molecular weight': molWeight,
|
|
127
|
+
'extinction coefficient': coef,
|
|
111
128
|
})
|
|
112
129
|
);
|
|
113
130
|
}
|
|
114
131
|
|
|
115
132
|
|
|
116
|
-
async function loadDialog() {
|
|
117
|
-
let res = (await _package.files.list(`${LIB_PATH}`, false, '')).map(it => it.fileName);
|
|
118
|
-
let FilesList = await ui.choiceInput('Monomer Libraries', ' ', res);
|
|
119
|
-
let grid = grok.shell.tv.grid;
|
|
120
|
-
ui.dialog('Load library from file')
|
|
121
|
-
.add(FilesList)
|
|
122
|
-
.onOK(async () => {
|
|
123
|
-
await monomerManager(FilesList.value);
|
|
124
|
-
grok.dapi.userDataStorage.postValue(STORAGE_NAME, i.toString(), FilesList.value, true);
|
|
125
|
-
i += 1;
|
|
126
|
-
grid.invalidate();
|
|
127
|
-
}).show();
|
|
128
|
-
};
|
|
129
|
-
|
|
130
133
|
//name: Manage Libraries
|
|
131
134
|
//tags: panel, widgets
|
|
132
135
|
//input: column helmColumn {semType: Macromolecule}
|
|
133
136
|
//output: widget result
|
|
134
|
-
export async function libraryPanel(helmColumn: DG.Column) {
|
|
137
|
+
export async function libraryPanel(helmColumn: DG.Column): Promise<DG.Widget> {
|
|
135
138
|
//@ts-ignore
|
|
136
|
-
let
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
let
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
139
|
+
let filesButton: HTMLButtonElement = ui.button('Manage', manageFiles);
|
|
140
|
+
let divInputs: HTMLDivElement = ui.div();
|
|
141
|
+
let librariesList: string[] = (await _package.files.list(`${LIB_PATH}`, false, '')).map(it => it.fileName);
|
|
142
|
+
let uploadedLibraries: string[] = Object.values(await grok.dapi.userDataStorage.get(STORAGE_NAME, true));
|
|
143
|
+
for (let i = 0; i < uploadedLibraries.length; ++i) {
|
|
144
|
+
let libraryName: string = uploadedLibraries[i];
|
|
145
|
+
divInputs.append(ui.boolInput(libraryName, true, async v => {
|
|
146
|
+
org.helm.webeditor.Monomers.clear();
|
|
147
|
+
grok.dapi.userDataStorage.remove(STORAGE_NAME, libraryName, true);
|
|
148
|
+
await loadLibraries();
|
|
149
|
+
grok.shell.tv.grid.invalidate();
|
|
150
|
+
}).root);
|
|
151
|
+
}
|
|
152
|
+
let unusedLibraries: string[] = librariesList.filter(x => !uploadedLibraries.includes(x));
|
|
153
|
+
for (let i = 0; i < unusedLibraries.length; ++i) {
|
|
154
|
+
let libraryName: string = unusedLibraries[i];
|
|
155
|
+
divInputs.append(ui.boolInput(libraryName, false, v => {
|
|
156
|
+
monomerManager(libraryName);
|
|
157
|
+
grok.dapi.userDataStorage.postValue(STORAGE_NAME, libraryName, libraryName, true);
|
|
158
|
+
}).root);
|
|
145
159
|
}
|
|
146
|
-
let unusedLibraries = list.filter(x => !usedLibraries.includes(x));
|
|
147
160
|
return new DG.Widget(ui.splitV([
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
'Uploaded libraries': ui.divText(usedLibraries.toString()),
|
|
151
|
-
'Not used libraries': ui.divText(unusedLibraries.toString()),
|
|
152
|
-
})
|
|
153
|
-
]),
|
|
154
|
-
ui.splitH([
|
|
155
|
-
ui.divH([loadButton]),
|
|
156
|
-
ui.divH([filesButton]),
|
|
157
|
-
])
|
|
161
|
+
divInputs,
|
|
162
|
+
ui.divV([filesButton])
|
|
158
163
|
]));
|
|
159
164
|
}
|
|
160
165
|
|
|
161
166
|
//name: manageFiles
|
|
162
|
-
function manageFiles() {
|
|
167
|
+
export async function manageFiles() {
|
|
163
168
|
const a = ui.dialog({title: 'Manage files'})
|
|
164
169
|
//@ts-ignore
|
|
165
170
|
.add(ui.fileBrowser({path: 'System:AppData/Helm/libraries'}).root)
|
|
@@ -167,6 +172,7 @@ function manageFiles() {
|
|
|
167
172
|
.show();
|
|
168
173
|
}
|
|
169
174
|
|
|
175
|
+
|
|
170
176
|
async function accessServer(url: string, key: string) {
|
|
171
177
|
const params: RequestInit = {
|
|
172
178
|
method: 'GET',
|
|
@@ -238,12 +244,22 @@ function getRS(smiles: string) {
|
|
|
238
244
|
return res;
|
|
239
245
|
}
|
|
240
246
|
|
|
241
|
-
|
|
247
|
+
//name: monomerManager
|
|
248
|
+
//input: string value
|
|
249
|
+
export async function monomerManager(value: string) {
|
|
242
250
|
let df: any[];
|
|
251
|
+
let file;
|
|
252
|
+
let dfSdf;
|
|
243
253
|
if (value.endsWith('.sdf')) {
|
|
244
|
-
const
|
|
245
|
-
|
|
246
|
-
|
|
254
|
+
const funcList: DG.Func[] = DG.Func.find({package: 'Chem', name: 'importSdf'});
|
|
255
|
+
console.debug(`Helm: initHelm() funcList.length = ${funcList.length}`);
|
|
256
|
+
if (funcList.length === 1) {
|
|
257
|
+
file = await _package.files.readAsBytes(`${LIB_PATH}${value}`);
|
|
258
|
+
dfSdf = await grok.functions.call('Chem:importSdf', {bytes: file});
|
|
259
|
+
df = createJsonMonomerLibFromSdf(dfSdf[0]);
|
|
260
|
+
} else {
|
|
261
|
+
grok.shell.warning('Chem package is not installed');
|
|
262
|
+
}
|
|
247
263
|
} else {
|
|
248
264
|
const file = await _package.files.readAsText(`${LIB_PATH}${value}`);
|
|
249
265
|
df = JSON.parse(file);
|
|
@@ -267,6 +283,8 @@ async function monomerManager(value: string) {
|
|
|
267
283
|
}
|
|
268
284
|
org.helm.webeditor.Monomers.addOneMonomer(m);
|
|
269
285
|
});
|
|
286
|
+
let grid: DG.Grid = grok.shell.tv.grid;
|
|
287
|
+
grid.invalidate();
|
|
270
288
|
}
|
|
271
289
|
|
|
272
290
|
//name: helmColumnToSmiles
|
|
@@ -275,48 +293,290 @@ export function helmColumnToSmiles(helmColumn: DG.Column) {
|
|
|
275
293
|
//todo: add column with smiles to col.dataFrame.
|
|
276
294
|
}
|
|
277
295
|
|
|
296
|
+
|
|
297
|
+
function split(s: string, sep: string) {
|
|
298
|
+
var ret = [];
|
|
299
|
+
var frag = '';
|
|
300
|
+
var parentheses = 0;
|
|
301
|
+
var bracket = 0;
|
|
302
|
+
var braces = 0;
|
|
303
|
+
var quote = 0;
|
|
304
|
+
for (var i = 0; i < s.length; ++i) {
|
|
305
|
+
var c = s.substring(i, i + 1);
|
|
306
|
+
if (c == sep && bracket == 0 && parentheses == 0 && braces == 0 && quote == 0) {
|
|
307
|
+
ret.push(frag);
|
|
308
|
+
frag = '';
|
|
309
|
+
} else {
|
|
310
|
+
frag += c;
|
|
311
|
+
if (quote > 0) {
|
|
312
|
+
if (c == '\\' && i + 1 < s.length) {
|
|
313
|
+
++i;
|
|
314
|
+
var c2 = s.substring(i, i + 1);
|
|
315
|
+
frag += c2;
|
|
316
|
+
c += c2;
|
|
317
|
+
}
|
|
318
|
+
}
|
|
319
|
+
if (c == '\"') {
|
|
320
|
+
if (!(i > 0 && s.substring(i - 1, i) == '\\'))
|
|
321
|
+
quote = quote == 0 ? 1 : 0;
|
|
322
|
+
} else if (c == '[')
|
|
323
|
+
++bracket;
|
|
324
|
+
else if (c == ']')
|
|
325
|
+
--bracket;
|
|
326
|
+
else if (c == '(')
|
|
327
|
+
++parentheses;
|
|
328
|
+
else if (c == ')')
|
|
329
|
+
--parentheses;
|
|
330
|
+
else if (c == '{')
|
|
331
|
+
++braces;
|
|
332
|
+
else if (c == '}')
|
|
333
|
+
--braces;
|
|
334
|
+
}
|
|
335
|
+
}
|
|
336
|
+
ret.push(frag);
|
|
337
|
+
return ret;
|
|
338
|
+
}
|
|
339
|
+
|
|
340
|
+
function detachAnnotation(s: string) {
|
|
341
|
+
var ret = _detachAppendix(s, '\"');
|
|
342
|
+
if (ret.tag != null)
|
|
343
|
+
return ret;
|
|
344
|
+
|
|
345
|
+
var r = _detachAppendix(s, '\'');
|
|
346
|
+
return {tag: ret.tag, repeat: r.tag, str: r.str};
|
|
347
|
+
}
|
|
348
|
+
|
|
349
|
+
function _detachAppendix(s: string, c: string) {
|
|
350
|
+
var tag = null;
|
|
351
|
+
//@ts-ignore
|
|
352
|
+
if (scil.Utils.endswith(s, c)) {
|
|
353
|
+
var p = s.length - 1;
|
|
354
|
+
while (p > 0) {
|
|
355
|
+
p = s.lastIndexOf(c, p - 1);
|
|
356
|
+
if (p <= 0 || s.substring(p - 1, p) != '\\')
|
|
357
|
+
break;
|
|
358
|
+
}
|
|
359
|
+
|
|
360
|
+
if (p > 0 && p < s.length - 1) {
|
|
361
|
+
tag = s.substring(p + 1, s.length - 1);
|
|
362
|
+
s = s.substring(0, p);
|
|
363
|
+
}
|
|
364
|
+
}
|
|
365
|
+
if (tag != null)
|
|
366
|
+
tag = tag.replace(new RegExp('\\' + c, 'g'), c);
|
|
367
|
+
return {tag: unescape(tag), str: s};
|
|
368
|
+
}
|
|
369
|
+
|
|
370
|
+
function unescape(s: string) {
|
|
371
|
+
//@ts-ignore
|
|
372
|
+
if (scil.Utils.isNullOrEmpty(s))
|
|
373
|
+
return s;
|
|
374
|
+
|
|
375
|
+
return s.replace(/[\\]./g, function(m) {
|
|
376
|
+
switch (m) {
|
|
377
|
+
case '\\r':
|
|
378
|
+
return '\r';
|
|
379
|
+
case '\\n':
|
|
380
|
+
return '\n';
|
|
381
|
+
case '\\t':
|
|
382
|
+
return '\t';
|
|
383
|
+
default:
|
|
384
|
+
return m.substring(1);
|
|
385
|
+
}
|
|
386
|
+
});
|
|
387
|
+
}
|
|
388
|
+
|
|
389
|
+
//name: parseHelm
|
|
390
|
+
//input: string s
|
|
391
|
+
export function parseHelm(s: string) {
|
|
392
|
+
var sections = split(s, '$');
|
|
393
|
+
s = sections[0];
|
|
394
|
+
var monomers = [];
|
|
395
|
+
//@ts-ignore
|
|
396
|
+
if (!scil.Utils.isNullOrEmpty(s)) {
|
|
397
|
+
var seqs = split(s, '|');
|
|
398
|
+
for (var i = 0; i < seqs.length; ++i) {
|
|
399
|
+
var e = detachAnnotation(seqs[i]);
|
|
400
|
+
s = e.str;
|
|
401
|
+
|
|
402
|
+
var p = s.indexOf('{');
|
|
403
|
+
|
|
404
|
+
s = s.substring(p + 1);
|
|
405
|
+
p = s.indexOf('}');
|
|
406
|
+
s = s.substring(0, p);
|
|
407
|
+
|
|
408
|
+
var ss = split(s, '.');
|
|
409
|
+
for (var monomer of ss) {
|
|
410
|
+
if (monomer.includes('(') && monomer.includes(')')) {
|
|
411
|
+
var elements = monomer.replace(/[()]/g, '').split('');
|
|
412
|
+
for (var el of elements) {
|
|
413
|
+
monomers.push(el);
|
|
414
|
+
}
|
|
415
|
+
} else if (monomer.includes('[') && monomer.includes(']')) {
|
|
416
|
+
var element = monomer.match(/(?<=\[).*?(?=\])/g, '');
|
|
417
|
+
monomers.push(element[0]);
|
|
418
|
+
} else {
|
|
419
|
+
monomers.push(monomer);
|
|
420
|
+
}
|
|
421
|
+
}
|
|
422
|
+
}
|
|
423
|
+
}
|
|
424
|
+
return monomers;
|
|
425
|
+
}
|
|
426
|
+
|
|
278
427
|
//name: findMonomers
|
|
279
|
-
//input: string helmString
|
|
280
|
-
export
|
|
428
|
+
//input: string helmString
|
|
429
|
+
export function findMonomers(helmString: string) {
|
|
430
|
+
//@ts-ignore
|
|
281
431
|
const types = Object.keys(org.helm.webeditor.monomerTypeList());
|
|
282
|
-
const monomers = [];
|
|
283
|
-
const
|
|
284
|
-
for (
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
monomerNames.push(monomers[i][k].id);
|
|
432
|
+
const monomers: any = [];
|
|
433
|
+
const monomer_names: any = [];
|
|
434
|
+
for (var i = 0; i < types.length; i++) {
|
|
435
|
+
//@ts-ignore
|
|
436
|
+
monomers.push(new scil.helm.Monomers.getMonomerSet(types[i]));
|
|
437
|
+
Object.keys(monomers[i]).forEach(k => {
|
|
438
|
+
monomer_names.push(monomers[i][k].id);
|
|
290
439
|
});
|
|
291
440
|
}
|
|
292
|
-
const
|
|
293
|
-
return new Set(
|
|
441
|
+
const split_string = parseHelm(helmString);
|
|
442
|
+
return new Set(split_string.filter(val => !monomer_names.includes(val)));
|
|
443
|
+
}
|
|
444
|
+
|
|
445
|
+
//name: getMolfiles
|
|
446
|
+
//input: column col {semType: Macromolecule}
|
|
447
|
+
//output: column res
|
|
448
|
+
export function getMolfiles(col: DG.Column): DG.Column {
|
|
449
|
+
let grid = grok.shell.tv.grid;
|
|
450
|
+
let parent = grid.root.parentElement;
|
|
451
|
+
let res = DG.Column.string('mols', col.length);
|
|
452
|
+
const host = ui.div([]);
|
|
453
|
+
parent.appendChild(host);
|
|
454
|
+
let editor = new JSDraw2.Editor(host, {viewonly: true});
|
|
455
|
+
host.style.width = '0px';
|
|
456
|
+
host.style.height = '0px';
|
|
457
|
+
res.init((i) => {
|
|
458
|
+
editor.setHelm(col.get(i));
|
|
459
|
+
let mol = editor.getMolfile();
|
|
460
|
+
return mol;
|
|
461
|
+
});
|
|
462
|
+
parent.lastChild.remove();
|
|
463
|
+
return res;
|
|
464
|
+
}
|
|
465
|
+
|
|
466
|
+
function getParts(subParts: string[], s: string): string[] {
|
|
467
|
+
let j = 0;
|
|
468
|
+
let allParts: string[] = [];
|
|
469
|
+
for (let k = 0; k < subParts.length; ++k) {
|
|
470
|
+
let indexOfMonomer = s.indexOf(subParts[k]);
|
|
471
|
+
let helmBeforeMonomer = s.slice(j, indexOfMonomer);
|
|
472
|
+
allParts.push(helmBeforeMonomer);
|
|
473
|
+
allParts.push(subParts[k]);
|
|
474
|
+
s = s.substring(indexOfMonomer + subParts[k].length);
|
|
475
|
+
}
|
|
476
|
+
allParts.push(s);
|
|
477
|
+
return allParts;
|
|
294
478
|
}
|
|
295
479
|
|
|
296
480
|
class HelmCellRenderer extends DG.GridCellRenderer {
|
|
297
|
-
get name() { return '
|
|
481
|
+
get name() { return 'helm'; }
|
|
298
482
|
|
|
299
|
-
get cellType() { return '
|
|
483
|
+
get cellType() { return 'helm'; }
|
|
300
484
|
|
|
301
485
|
get defaultWidth(): number | null { return 400; }
|
|
302
486
|
|
|
303
487
|
get defaultHeight(): number | null { return 100; }
|
|
304
488
|
|
|
489
|
+
onMouseMove(gridCell: DG.GridCell, e: MouseEvent): void {
|
|
490
|
+
const maxLengthWordsSum = gridCell.cell.column.temp['helm-sum-maxLengthWords'];
|
|
491
|
+
const maxIndex = Object.values(gridCell.cell.column.temp['helm-maxLengthWords']).length - 1;
|
|
492
|
+
const argsX = e.offsetX - gridCell.gridColumn.left + (gridCell.gridColumn.left - gridCell.bounds.x);
|
|
493
|
+
let left = 0;
|
|
494
|
+
let right = maxIndex;
|
|
495
|
+
let found = false;
|
|
496
|
+
maxLengthWordsSum[maxIndex + 1] = argsX + 1;
|
|
497
|
+
let mid = 0;
|
|
498
|
+
if (argsX > maxLengthWordsSum[0]) {
|
|
499
|
+
while (!found) {
|
|
500
|
+
mid = Math.floor((right + left) / 2);
|
|
501
|
+
if (argsX >= maxLengthWordsSum[mid] && argsX <= maxLengthWordsSum[mid + 1]) {
|
|
502
|
+
left = mid;
|
|
503
|
+
found = true;
|
|
504
|
+
} else if (argsX < maxLengthWordsSum[mid]) {
|
|
505
|
+
right = mid - 1;
|
|
506
|
+
} else if (argsX > maxLengthWordsSum[mid + 1]) {
|
|
507
|
+
left = mid + 1;
|
|
508
|
+
}
|
|
509
|
+
if (left == right) {
|
|
510
|
+
found = true;
|
|
511
|
+
}
|
|
512
|
+
}
|
|
513
|
+
}
|
|
514
|
+
left = (argsX >= maxLengthWordsSum[left]) ? left + 1 : left;
|
|
515
|
+
const monomers = findMonomers(gridCell.cell.value);
|
|
516
|
+
let s: string = gridCell.cell.value ?? '';
|
|
517
|
+
let subParts: string[] = parseHelm(s);
|
|
518
|
+
let allParts: string[] = getParts(subParts, s);
|
|
519
|
+
let tooltipMessage: HTMLElement[] = [];
|
|
520
|
+
for (let i = 0; i < allParts.length; ++i) {
|
|
521
|
+
if (monomers.has(allParts[i]))
|
|
522
|
+
tooltipMessage[i] = ui.divV([
|
|
523
|
+
ui.divText(`Monomer ${allParts[i]} not found.`),
|
|
524
|
+
ui.divText('Open the Property Panel, then expand Manage Libraries')
|
|
525
|
+
]);
|
|
526
|
+
}
|
|
527
|
+
(((tooltipMessage[left]?.childNodes.length ?? 0) > 0))
|
|
528
|
+
? ui.tooltip.show(ui.div(tooltipMessage[left]), e.x + 16, e.y + 16)
|
|
529
|
+
: ui.tooltip.hide();
|
|
530
|
+
}
|
|
531
|
+
|
|
305
532
|
render(g: CanvasRenderingContext2D, x: number, y: number, w: number, h: number,
|
|
306
533
|
gridCell: DG.GridCell, cellStyle: DG.GridCellStyle
|
|
307
534
|
) {
|
|
308
|
-
const
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
gridCell.
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
535
|
+
const grid = gridCell.gridRow !== -1 ? gridCell.grid : undefined;
|
|
536
|
+
const undefinedColor = 'rgb(100,100,100)';
|
|
537
|
+
const grayColor = '#808080';
|
|
538
|
+
const monomers = findMonomers(gridCell.cell.value);
|
|
539
|
+
let s: string = gridCell.cell.value ?? '';
|
|
540
|
+
let subParts: string[] = parseHelm(s);
|
|
541
|
+
if (monomers.size == 0) {
|
|
542
|
+
const host = ui.div([], {style: {width: `${w}px`, height: `${h}px`}});
|
|
543
|
+
host.setAttribute('dataformat', 'helm');
|
|
544
|
+
host.setAttribute('data', gridCell.cell.value);
|
|
545
|
+
gridCell.element = host;
|
|
546
|
+
//@ts-ignore
|
|
547
|
+
const canvas = new JSDraw2.Editor(host, {width: w, height: h, skin: 'w8', viewonly: true});
|
|
548
|
+
return;
|
|
549
|
+
}
|
|
550
|
+
if (monomers.size > 0) {
|
|
551
|
+
w = grid ? Math.min(grid.canvas.width - x, w) : g.canvas.width - x;
|
|
552
|
+
g.save();
|
|
553
|
+
g.beginPath();
|
|
554
|
+
g.rect(x, y, w, h);
|
|
555
|
+
g.clip();
|
|
556
|
+
g.font = '12px monospace';
|
|
557
|
+
g.textBaseline = 'top';
|
|
558
|
+
let x1 = x;
|
|
559
|
+
let maxLengthWords: any = {};
|
|
560
|
+
let maxLengthWordSum: any = {};
|
|
561
|
+
let allParts: string[] = getParts(subParts, s);
|
|
562
|
+
for (let i = 0; i < allParts.length; ++i) {
|
|
563
|
+
maxLengthWords[i] = allParts[i].length * 7;
|
|
564
|
+
let color = monomers.has(allParts[i]) ? 'red' : grayColor;
|
|
565
|
+
g.fillStyle = undefinedColor;
|
|
566
|
+
x1 = bio.printLeftOrCentered(x1, y, w, h, g, allParts[i], color, 0, true, 1.0);
|
|
567
|
+
}
|
|
568
|
+
|
|
569
|
+
maxLengthWordSum[0] = maxLengthWords[0];
|
|
570
|
+
for (let i = 1; i < allParts.length; i++) {
|
|
571
|
+
maxLengthWordSum[i] = maxLengthWordSum[i - 1] + maxLengthWords[i];
|
|
572
|
+
}
|
|
573
|
+
gridCell.cell.column.temp = {
|
|
574
|
+
'helm-sum-maxLengthWords': maxLengthWordSum,
|
|
575
|
+
'helm-maxLengthWords': maxLengthWords
|
|
576
|
+
};
|
|
577
|
+
g.restore();
|
|
578
|
+
return;
|
|
579
|
+
}
|
|
320
580
|
}
|
|
321
581
|
}
|
|
322
582
|
|
package/src/tests/helm-tests.ts
CHANGED
|
@@ -1,13 +1,13 @@
|
|
|
1
|
-
import {before, category, expect, test} from '@datagrok-libraries/utils/src/test';
|
|
2
|
-
import {helmToFasta, helmToPeptide, helmToRNA} from '../package';
|
|
1
|
+
import {after, before, category, delay, expect, test} from '@datagrok-libraries/utils/src/test';
|
|
2
|
+
//import {findMonomers, helmToFasta, helmToPeptide, helmToRNA, initHelm} from '../package';
|
|
3
3
|
import {_package} from '../package-test';
|
|
4
|
+
import {manageFiles, parseHelm} from '../package';
|
|
4
5
|
import * as DG from 'datagrok-api/dg';
|
|
5
6
|
import * as grok from 'datagrok-api/grok';
|
|
6
7
|
|
|
7
8
|
|
|
8
9
|
category('Helm', () => {
|
|
9
|
-
|
|
10
|
-
// These tests require webservice that is not present on test stand
|
|
10
|
+
//These tests require webservice that is not present on test stand
|
|
11
11
|
// test('helmToFasta', async () => {
|
|
12
12
|
// expect(await helmToFasta('RNA1{R(U)P.R(T)P.R(G)P.R(C)P.R(A)}$$$$'), '>RNA1UTGCA');
|
|
13
13
|
// expect(await helmToFasta('RNA1{P.R(U).P.R(T)}$$$$'), '>RNA1UT');
|
|
@@ -45,4 +45,50 @@ category('Helm', () => {
|
|
|
45
45
|
// await grok.data.detectSemanticTypes(df);
|
|
46
46
|
// expect(col.tags[DG.TAGS.UNITS], 'HELM');
|
|
47
47
|
// });
|
|
48
|
+
|
|
49
|
+
test('manageFiles', async () => {
|
|
50
|
+
return await manageFiles();
|
|
51
|
+
});
|
|
52
|
+
|
|
53
|
+
|
|
54
|
+
test('monomerManager', async() => {
|
|
55
|
+
const df: DG.DataFrame = DG.DataFrame.fromCsv(await _package.files.readAsText('tests/test.csv'));
|
|
56
|
+
grok.shell.addTableView(df);
|
|
57
|
+
await grok.functions.call('Helm:monomerManager', {value: 'HELMCoreLibrary.json'});
|
|
58
|
+
const checkName = '2-Chloroadenine';
|
|
59
|
+
let flag = false;
|
|
60
|
+
const types = Object.keys(org.helm.webeditor.monomerTypeList());
|
|
61
|
+
const monomers: any = [];
|
|
62
|
+
for (var i = 0; i < types.length; i++) {
|
|
63
|
+
//@ts-ignore
|
|
64
|
+
monomers.push(new scil.helm.Monomers.getMonomerSet(types[i]));
|
|
65
|
+
Object.keys(monomers[i]).forEach(k => {
|
|
66
|
+
if (monomers[i][k].n == checkName){
|
|
67
|
+
flag = true;
|
|
68
|
+
}
|
|
69
|
+
});
|
|
70
|
+
}
|
|
71
|
+
expect(flag, true);
|
|
72
|
+
});
|
|
73
|
+
|
|
74
|
+
test('parseHelm', async() => {
|
|
75
|
+
const expectedResults = [
|
|
76
|
+
['meI', 'hHis', 'Aca', 'N', 'T', 'dK', 'Thr_PO3H2', 'D-Tyr_Et', 'Aze', 'dV', 'E', 'Phe_4Me'],
|
|
77
|
+
['A', 'C', 'T', 'G', 'W', 'E', 'Q'],
|
|
78
|
+
['R', 'U', 'P', 'T', 'A'],
|
|
79
|
+
['A', '*', 'G', 'C']
|
|
80
|
+
];
|
|
81
|
+
const helmStrings = [
|
|
82
|
+
'PEPTIDE1{meI.hHis.Aca.N.T.dK.Thr_PO3H2.Aca.D-Tyr_Et.Aze.dV.E.N.dV.Phe_4Me}$$$',
|
|
83
|
+
'PEPTIDE1{A.C.T.G.C.T.W.G.T.W.E.C.W.C.Q.W}|PEPTIDE2{A.C.T.G.C.T.W.G.T.W.E.Q}$PEPTIDE1,PEPTIDE1,5:R3-14:R3|PEPTIDE2,PEPTIDE1,2:R3-12:R3$$$',
|
|
84
|
+
'RNA1{R(U)P.R(T)P}|RNA2{P.R(A)P.R(A)}$RNA1,RNA2,2:pair-6:pair|RNA1,RNA2,5:pair-3:pair$$$',
|
|
85
|
+
'PEPTIDE1{A.*.G.C}$$$$V2.0'
|
|
86
|
+
];
|
|
87
|
+
for (let idx = 0; idx < helmStrings.length; ++idx) {
|
|
88
|
+
let monomerSet = new Set(parseHelm(helmStrings[idx]));
|
|
89
|
+
let monomerArray = Array.from(monomerSet);
|
|
90
|
+
expect(JSON.stringify(monomerArray), JSON.stringify(expectedResults[idx]));
|
|
91
|
+
}
|
|
92
|
+
});
|
|
93
|
+
|
|
48
94
|
});
|