@datagrok/helm 2.0.3 → 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/dist/package-test.js +1296 -676
- package/dist/package.js +1159 -605
- package/package.json +2 -3
- package/src/__jest__/remote.test.ts +7 -6
- package/src/__jest__/test-node.ts +1 -1
- package/src/package.ts +264 -197
- package/src/tests/helm-tests.ts +24 -6
- package/{test-Helm-4f0c8bae6479-d7a9fa45.html → test-Helm-916a90d7d48b-481b4bd5.html} +7 -10
package/src/package.ts
CHANGED
|
@@ -2,34 +2,34 @@
|
|
|
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
|
-
import
|
|
5
|
+
import * as bio from '@datagrok-libraries/bio';
|
|
6
|
+
|
|
6
7
|
import {createJsonMonomerLibFromSdf} from './utils';
|
|
7
8
|
import {MONOMER_MANAGER_MAP, RGROUPS, RGROUP_CAP_GROUP_NAME, RGROUP_LABEL, SMILES} from './constants';
|
|
8
|
-
import {
|
|
9
|
+
import {delay} from '@datagrok-libraries/utils/src/test';
|
|
9
10
|
|
|
10
11
|
export const _package = new DG.Package();
|
|
11
12
|
|
|
12
|
-
const lru = new DG.LruCache<any, any>();
|
|
13
|
-
const molfiles = new DG.LruCache<any, any>();
|
|
14
13
|
const STORAGE_NAME = 'Libraries';
|
|
15
|
-
let i = 0;
|
|
16
14
|
const LIB_PATH = 'libraries/';
|
|
17
15
|
|
|
18
16
|
|
|
19
17
|
//tags: init
|
|
20
18
|
export async function initHelm(): Promise<void> {
|
|
21
|
-
|
|
22
|
-
for (let j = 0; j <= i; j++) {
|
|
23
|
-
await monomerManager(await grok.dapi.userDataStorage.getValue(STORAGE_NAME, j.toString(), true));
|
|
24
|
-
}
|
|
25
|
-
grid.invalidate();
|
|
26
|
-
|
|
27
|
-
return new Promise((resolve, reject) => {
|
|
19
|
+
return new Promise(async (resolve, reject) => {
|
|
28
20
|
// @ts-ignore
|
|
29
21
|
dojo.ready(function() { resolve(null); });
|
|
22
|
+
await loadLibraries();
|
|
30
23
|
});
|
|
31
24
|
}
|
|
32
25
|
|
|
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
|
+
}
|
|
31
|
+
}
|
|
32
|
+
|
|
33
33
|
//name: helmCellRenderer
|
|
34
34
|
//tags: cellRenderer
|
|
35
35
|
//meta.cellType: helm
|
|
@@ -40,7 +40,7 @@ export function helmCellRenderer(): HelmCellRenderer {
|
|
|
40
40
|
}
|
|
41
41
|
|
|
42
42
|
|
|
43
|
-
|
|
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, {
|
|
@@ -66,15 +66,20 @@ export function webEditor(value: string) {
|
|
|
66
66
|
app.structureview.resize(sizes.rightwidth, sizes.bottomheight + app.toolbarheight);
|
|
67
67
|
app.mex.resize(sizes.topheight - 80);
|
|
68
68
|
setTimeout(function() {
|
|
69
|
-
|
|
69
|
+
if (typeof cell !== 'undefined') {
|
|
70
|
+
app.canvas.helm.setSequence(cell.cell.value, 'HELM');
|
|
71
|
+
} else {
|
|
72
|
+
app.canvas.helm.setSequence(value, 'HELM');
|
|
73
|
+
}
|
|
70
74
|
}, 200);
|
|
71
75
|
//@ts-ignore
|
|
72
76
|
ui.dialog({showHeader: false, showFooter: true})
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
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});
|
|
78
83
|
}
|
|
79
84
|
|
|
80
85
|
|
|
@@ -83,9 +88,7 @@ export function webEditor(value: string) {
|
|
|
83
88
|
//input: grid_cell cell
|
|
84
89
|
export function editMoleculeCell(cell: DG.GridCell): void {
|
|
85
90
|
if (cell.gridColumn.column.tags[DG.TAGS.UNITS] === 'helm') {
|
|
86
|
-
|
|
87
|
-
webEditor(cell.cell.value);
|
|
88
|
-
grid.invalidate();
|
|
91
|
+
webEditor(cell);
|
|
89
92
|
}
|
|
90
93
|
}
|
|
91
94
|
|
|
@@ -95,9 +98,9 @@ export function editMoleculeCell(cell: DG.GridCell): void {
|
|
|
95
98
|
//input: string mol { semType: Macromolecule }
|
|
96
99
|
export function openEditor(mol: string): void {
|
|
97
100
|
let df = grok.shell.tv.grid.dataFrame;
|
|
98
|
-
let converter = new NotationConverter(df.columns.bySemType('Macromolecule'));
|
|
101
|
+
let converter = new bio.NotationConverter(df.columns.bySemType('Macromolecule'));
|
|
99
102
|
const resStr = converter.convertStringToHelm(mol, '/');
|
|
100
|
-
webEditor(resStr);
|
|
103
|
+
webEditor(undefined, resStr);
|
|
101
104
|
}
|
|
102
105
|
|
|
103
106
|
//name: Properties
|
|
@@ -105,73 +108,58 @@ export function openEditor(mol: string): void {
|
|
|
105
108
|
//input: string helmString {semType: Macromolecule}
|
|
106
109
|
//output: widget result
|
|
107
110
|
export async function propertiesPanel(helmString: string) {
|
|
108
|
-
|
|
109
|
-
|
|
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();
|
|
110
123
|
return new DG.Widget(
|
|
111
124
|
ui.tableFromMap({
|
|
112
|
-
'formula':
|
|
113
|
-
'molecular weight':
|
|
114
|
-
'extinction coefficient':
|
|
115
|
-
/*'fasta': ui.wait(async () => ui.divText(await helmToFasta(helmString))),
|
|
116
|
-
'rna analogue sequence': ui.wait(async () => ui.divText(await helmToRNA(helmString))),
|
|
117
|
-
'smiles': ui.wait(async () => ui.divText(await helmToSmiles(helmString))),
|
|
118
|
-
//'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,
|
|
119
128
|
})
|
|
120
129
|
);
|
|
121
130
|
}
|
|
122
131
|
|
|
123
|
-
//name: Molfile
|
|
124
|
-
//tags: panel, widgets
|
|
125
|
-
//input: string helmString {semType: Macromolecule}
|
|
126
|
-
//output: widget result
|
|
127
|
-
export async function molfilePanel(helmString: string) {
|
|
128
|
-
return ui.textInput('', await getMolfile(helmString));
|
|
129
|
-
}
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
//name: loadDialog
|
|
133
|
-
export async function loadDialog() {
|
|
134
|
-
let res = (await _package.files.list(`${LIB_PATH}`, false, '')).map(it => it.fileName);
|
|
135
|
-
let FilesList = await ui.choiceInput('Monomer Libraries', ' ', res);
|
|
136
|
-
let grid = grok.shell.tv.grid;
|
|
137
|
-
ui.dialog('Load library from file')
|
|
138
|
-
.add(FilesList)
|
|
139
|
-
.onOK(async () => {
|
|
140
|
-
await monomerManager(FilesList.value);
|
|
141
|
-
grok.dapi.userDataStorage.postValue(STORAGE_NAME, i.toString(), FilesList.value, true);
|
|
142
|
-
i += 1;
|
|
143
|
-
grid.invalidate();
|
|
144
|
-
}).show();
|
|
145
|
-
};
|
|
146
132
|
|
|
147
133
|
//name: Manage Libraries
|
|
148
134
|
//tags: panel, widgets
|
|
149
135
|
//input: column helmColumn {semType: Macromolecule}
|
|
150
136
|
//output: widget result
|
|
151
|
-
export async function libraryPanel(helmColumn: DG.Column) {
|
|
152
|
-
//@ts-ignore
|
|
153
|
-
let loadButton = ui.button('Load Library');
|
|
154
|
-
loadButton.addEventListener('click', loadDialog);
|
|
137
|
+
export async function libraryPanel(helmColumn: DG.Column): Promise<DG.Widget> {
|
|
155
138
|
//@ts-ignore
|
|
156
|
-
let filesButton = ui.button('Manage');
|
|
157
|
-
|
|
158
|
-
let
|
|
159
|
-
let
|
|
160
|
-
for (let
|
|
161
|
-
|
|
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);
|
|
162
159
|
}
|
|
163
|
-
let unusedLibraries = list.filter(x => !usedLibraries.includes(x));
|
|
164
160
|
return new DG.Widget(ui.splitV([
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
'Uploaded libraries': ui.divText(usedLibraries.toString()),
|
|
168
|
-
'Not used libraries': ui.divText(unusedLibraries.toString()),
|
|
169
|
-
})
|
|
170
|
-
]),
|
|
171
|
-
ui.splitH([
|
|
172
|
-
ui.divH([loadButton]),
|
|
173
|
-
ui.divH([filesButton]),
|
|
174
|
-
])
|
|
161
|
+
divInputs,
|
|
162
|
+
ui.divV([filesButton])
|
|
175
163
|
]));
|
|
176
164
|
}
|
|
177
165
|
|
|
@@ -185,7 +173,6 @@ export async function manageFiles() {
|
|
|
185
173
|
}
|
|
186
174
|
|
|
187
175
|
|
|
188
|
-
|
|
189
176
|
async function accessServer(url: string, key: string) {
|
|
190
177
|
const params: RequestInit = {
|
|
191
178
|
method: 'GET',
|
|
@@ -271,7 +258,7 @@ export async function monomerManager(value: string) {
|
|
|
271
258
|
dfSdf = await grok.functions.call('Chem:importSdf', {bytes: file});
|
|
272
259
|
df = createJsonMonomerLibFromSdf(dfSdf[0]);
|
|
273
260
|
} else {
|
|
274
|
-
grok.shell.warning(
|
|
261
|
+
grok.shell.warning('Chem package is not installed');
|
|
275
262
|
}
|
|
276
263
|
} else {
|
|
277
264
|
const file = await _package.files.readAsText(`${LIB_PATH}${value}`);
|
|
@@ -296,6 +283,8 @@ export async function monomerManager(value: string) {
|
|
|
296
283
|
}
|
|
297
284
|
org.helm.webeditor.Monomers.addOneMonomer(m);
|
|
298
285
|
});
|
|
286
|
+
let grid: DG.Grid = grok.shell.tv.grid;
|
|
287
|
+
grid.invalidate();
|
|
299
288
|
}
|
|
300
289
|
|
|
301
290
|
//name: helmColumnToSmiles
|
|
@@ -304,52 +293,45 @@ export function helmColumnToSmiles(helmColumn: DG.Column) {
|
|
|
304
293
|
//todo: add column with smiles to col.dataFrame.
|
|
305
294
|
}
|
|
306
295
|
|
|
307
|
-
//name: getMolfile
|
|
308
|
-
//input: string helmString {semType: Macromolecule}
|
|
309
|
-
export async function getMolfile(helmString) {
|
|
310
|
-
return molfiles.get(helmString);
|
|
311
|
-
}
|
|
312
296
|
|
|
313
297
|
function split(s: string, sep: string) {
|
|
314
298
|
var ret = [];
|
|
315
|
-
var frag =
|
|
299
|
+
var frag = '';
|
|
316
300
|
var parentheses = 0;
|
|
317
301
|
var bracket = 0;
|
|
318
302
|
var braces = 0;
|
|
319
303
|
var quote = 0;
|
|
320
304
|
for (var i = 0; i < s.length; ++i) {
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
}
|
|
335
|
-
}
|
|
336
|
-
if (c == '\"') {
|
|
337
|
-
if (!(i > 0 && s.substring(i - 1, i) == '\\'))
|
|
338
|
-
quote = quote == 0 ? 1 : 0;
|
|
339
|
-
}
|
|
340
|
-
else if (c == '[')
|
|
341
|
-
++bracket;
|
|
342
|
-
else if (c == ']')
|
|
343
|
-
--bracket;
|
|
344
|
-
else if (c == '(')
|
|
345
|
-
++parentheses;
|
|
346
|
-
else if (c == ')')
|
|
347
|
-
--parentheses;
|
|
348
|
-
else if (c == '{')
|
|
349
|
-
++braces;
|
|
350
|
-
else if (c == '}')
|
|
351
|
-
--braces;
|
|
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
|
+
}
|
|
352
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
|
+
}
|
|
353
335
|
}
|
|
354
336
|
ret.push(frag);
|
|
355
337
|
return ret;
|
|
@@ -358,81 +340,86 @@ function split(s: string, sep: string) {
|
|
|
358
340
|
function detachAnnotation(s: string) {
|
|
359
341
|
var ret = _detachAppendix(s, '\"');
|
|
360
342
|
if (ret.tag != null)
|
|
361
|
-
|
|
343
|
+
return ret;
|
|
362
344
|
|
|
363
345
|
var r = _detachAppendix(s, '\'');
|
|
364
|
-
return {
|
|
346
|
+
return {tag: ret.tag, repeat: r.tag, str: r.str};
|
|
365
347
|
}
|
|
366
348
|
|
|
367
349
|
function _detachAppendix(s: string, c: string) {
|
|
368
350
|
var tag = null;
|
|
369
351
|
//@ts-ignore
|
|
370
352
|
if (scil.Utils.endswith(s, c)) {
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
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
|
+
}
|
|
377
359
|
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
360
|
+
if (p > 0 && p < s.length - 1) {
|
|
361
|
+
tag = s.substring(p + 1, s.length - 1);
|
|
362
|
+
s = s.substring(0, p);
|
|
363
|
+
}
|
|
382
364
|
}
|
|
383
365
|
if (tag != null)
|
|
384
|
-
|
|
385
|
-
return {
|
|
366
|
+
tag = tag.replace(new RegExp('\\' + c, 'g'), c);
|
|
367
|
+
return {tag: unescape(tag), str: s};
|
|
386
368
|
}
|
|
387
369
|
|
|
388
370
|
function unescape(s: string) {
|
|
389
371
|
//@ts-ignore
|
|
390
372
|
if (scil.Utils.isNullOrEmpty(s))
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
return s.replace(/[\\]./g, function
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
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
|
+
}
|
|
404
386
|
});
|
|
405
387
|
}
|
|
406
388
|
|
|
407
|
-
|
|
389
|
+
//name: parseHelm
|
|
390
|
+
//input: string s
|
|
391
|
+
export function parseHelm(s: string) {
|
|
408
392
|
var sections = split(s, '$');
|
|
409
393
|
s = sections[0];
|
|
410
394
|
var monomers = [];
|
|
411
395
|
//@ts-ignore
|
|
412
396
|
if (!scil.Utils.isNullOrEmpty(s)) {
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
}
|
|
431
|
-
} else {
|
|
432
|
-
monomers.push(monomer);
|
|
433
|
-
}
|
|
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);
|
|
434
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
|
+
}
|
|
435
421
|
}
|
|
422
|
+
}
|
|
436
423
|
}
|
|
437
424
|
return monomers;
|
|
438
425
|
}
|
|
@@ -455,6 +442,41 @@ export function findMonomers(helmString: string) {
|
|
|
455
442
|
return new Set(split_string.filter(val => !monomer_names.includes(val)));
|
|
456
443
|
}
|
|
457
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;
|
|
478
|
+
}
|
|
479
|
+
|
|
458
480
|
class HelmCellRenderer extends DG.GridCellRenderer {
|
|
459
481
|
get name() { return 'helm'; }
|
|
460
482
|
|
|
@@ -464,6 +486,49 @@ class HelmCellRenderer extends DG.GridCellRenderer {
|
|
|
464
486
|
|
|
465
487
|
get defaultHeight(): number | null { return 100; }
|
|
466
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
|
+
|
|
467
532
|
render(g: CanvasRenderingContext2D, x: number, y: number, w: number, h: number,
|
|
468
533
|
gridCell: DG.GridCell, cellStyle: DG.GridCellStyle
|
|
469
534
|
) {
|
|
@@ -471,45 +536,47 @@ class HelmCellRenderer extends DG.GridCellRenderer {
|
|
|
471
536
|
const undefinedColor = 'rgb(100,100,100)';
|
|
472
537
|
const grayColor = '#808080';
|
|
473
538
|
const monomers = findMonomers(gridCell.cell.value);
|
|
539
|
+
let s: string = gridCell.cell.value ?? '';
|
|
540
|
+
let subParts: string[] = parseHelm(s);
|
|
474
541
|
if (monomers.size == 0) {
|
|
475
542
|
const host = ui.div([], {style: {width: `${w}px`, height: `${h}px`}});
|
|
476
543
|
host.setAttribute('dataformat', 'helm');
|
|
477
544
|
host.setAttribute('data', gridCell.cell.value);
|
|
478
545
|
gridCell.element = host;
|
|
479
|
-
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
|
|
483
|
-
|
|
484
|
-
|
|
485
|
-
|
|
486
|
-
|
|
487
|
-
|
|
488
|
-
|
|
489
|
-
|
|
490
|
-
|
|
491
|
-
|
|
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);
|
|
492
567
|
}
|
|
493
|
-
|
|
494
|
-
|
|
495
|
-
|
|
496
|
-
|
|
497
|
-
g.rect(x, y, w, h);
|
|
498
|
-
g.clip();
|
|
499
|
-
g.font = '12px monospace';
|
|
500
|
-
g.textBaseline = 'top';
|
|
501
|
-
let x1 = x;
|
|
502
|
-
const s: string = gridCell.cell.value ?? '';
|
|
503
|
-
let subParts: string[] = parseHelm(s);
|
|
504
|
-
subParts.forEach((amino, index) => {
|
|
505
|
-
let color = monomers.has(amino) ? 'red' : grayColor;
|
|
506
|
-
g.fillStyle = undefinedColor;
|
|
507
|
-
let last = index === subParts.length - 1;
|
|
508
|
-
x1 = printLeftOrCentered(x1, y, w, h, g, amino, color, 0, true, 1.0, '/', last);
|
|
509
|
-
});
|
|
510
|
-
g.restore();
|
|
511
|
-
return;
|
|
568
|
+
|
|
569
|
+
maxLengthWordSum[0] = maxLengthWords[0];
|
|
570
|
+
for (let i = 1; i < allParts.length; i++) {
|
|
571
|
+
maxLengthWordSum[i] = maxLengthWordSum[i - 1] + maxLengthWords[i];
|
|
512
572
|
}
|
|
573
|
+
gridCell.cell.column.temp = {
|
|
574
|
+
'helm-sum-maxLengthWords': maxLengthWordSum,
|
|
575
|
+
'helm-maxLengthWords': maxLengthWords
|
|
576
|
+
};
|
|
577
|
+
g.restore();
|
|
578
|
+
return;
|
|
579
|
+
}
|
|
513
580
|
}
|
|
514
581
|
}
|
|
515
582
|
|
package/src/tests/helm-tests.ts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import {after, before, category, delay, expect, test} from '@datagrok-libraries/utils/src/test';
|
|
2
2
|
//import {findMonomers, helmToFasta, helmToPeptide, helmToRNA, initHelm} from '../package';
|
|
3
3
|
import {_package} from '../package-test';
|
|
4
|
-
import {
|
|
4
|
+
import {manageFiles, parseHelm} from '../package';
|
|
5
5
|
import * as DG from 'datagrok-api/dg';
|
|
6
6
|
import * as grok from 'datagrok-api/grok';
|
|
7
7
|
|
|
@@ -46,16 +46,14 @@ category('Helm', () => {
|
|
|
46
46
|
// expect(col.tags[DG.TAGS.UNITS], 'HELM');
|
|
47
47
|
// });
|
|
48
48
|
|
|
49
|
-
|
|
50
49
|
test('manageFiles', async () => {
|
|
51
50
|
return await manageFiles();
|
|
52
51
|
});
|
|
53
52
|
|
|
54
|
-
|
|
55
|
-
return await grok.functions.call('Helm:loadDialog');
|
|
56
|
-
});
|
|
57
|
-
|
|
53
|
+
|
|
58
54
|
test('monomerManager', async() => {
|
|
55
|
+
const df: DG.DataFrame = DG.DataFrame.fromCsv(await _package.files.readAsText('tests/test.csv'));
|
|
56
|
+
grok.shell.addTableView(df);
|
|
59
57
|
await grok.functions.call('Helm:monomerManager', {value: 'HELMCoreLibrary.json'});
|
|
60
58
|
const checkName = '2-Chloroadenine';
|
|
61
59
|
let flag = false;
|
|
@@ -73,4 +71,24 @@ category('Helm', () => {
|
|
|
73
71
|
expect(flag, true);
|
|
74
72
|
});
|
|
75
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
|
+
|
|
76
94
|
});
|