@datagrok/helm 2.1.28 → 2.1.30
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/.eslintrc.json +2 -2
- package/CHANGELOG.md +14 -0
- package/dist/package-test.js +1 -1
- package/dist/package-test.js.map +1 -1
- package/dist/package.js +1 -1
- package/dist/package.js.map +1 -1
- package/files/tests/sample_HELM-missedInLib.csv +1 -1
- package/package.json +6 -5
- package/src/cell-renderer.ts +66 -70
- package/src/helm-monomer-placer.ts +1 -0
- package/src/tests/findMonomers-tests.ts +3 -2
- package/src/tests/get-molfiles.ts +3 -2
- package/src/utils/index.ts +18 -8
- package/src/widgets/properties-widget.ts +3 -2
|
@@ -2,7 +2,7 @@ HELM,Activity
|
|
|
2
2
|
PEPTIDE1{meI.missed2.Aca.N.T.dE.Thr_PO3H2.Aca.D-Tyr_Et.Tyr_ab-dehydroMe.dV.E.N.D-Orn.D-aThr.Phe_4Me}$$$$,5.307510973968128
|
|
3
3
|
PEPTIDE1{meI.hHis.Aca.Cys_SEt.T.dK.Thr_PO3H2.Aca.Tyr_PO3H2.D-Chg.dV.Phe_ab-dehydro.N.D-Orn.D-aThr.Phe_4Me}$$$$,5.723876853431544
|
|
4
4
|
PEPTIDE1{missed1.hHis.Aca.Cys_SEt.T.dK.Thr_PO3H2.Aca.Tyr_PO3H2.D-Chg.dV.Thr_PO3H2.N.D-Orn.D-aThr.Phe_4Me}$$$$,5.185811246022437
|
|
5
|
-
PEPTIDE1{meI.
|
|
5
|
+
PEPTIDE1{meI.m0.Aca.m1.T.m2.m3.Aca.Tyr_PO3H2.D-Chg.dV.Thr_PO3H2.N.D-Orn.D-aThr.Phe_4Me}$$$$,6.223502390804369
|
|
6
6
|
,3.8459123763832412
|
|
7
7
|
PEPTIDE1{meI.hHis.Aca.N.T.dK.Thr_PO3H2.Aca.D-Tyr_Et.Tyr_Bn.dV.E.N.dV.Phe_4Me}$$$$,3.27920438824657
|
|
8
8
|
PEPTIDE1{meI.hHis.Aca.N.T.dK.Thr_PO3H2.Aca.D-Tyr_Et.Aze.dV.E.N.dV.Phe_4Me}$$$$,2.105852152992568
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@datagrok/helm",
|
|
3
3
|
"friendlyName": "Helm",
|
|
4
|
-
"version": "2.1.
|
|
4
|
+
"version": "2.1.30",
|
|
5
5
|
"author": {
|
|
6
6
|
"name": "Oleksandra Serhiienko",
|
|
7
7
|
"email": "oserhiienko@datagrok.ai"
|
|
@@ -15,9 +15,9 @@
|
|
|
15
15
|
"helm/JSDraw/Pistoia.HELM-uncompressed.js"
|
|
16
16
|
],
|
|
17
17
|
"dependencies": {
|
|
18
|
-
"@datagrok-libraries/bio": "^5.39.
|
|
19
|
-
"@datagrok-libraries/chem-meta": "^1.2.
|
|
20
|
-
"@datagrok-libraries/utils": "^4.1.
|
|
18
|
+
"@datagrok-libraries/bio": "^5.39.29",
|
|
19
|
+
"@datagrok-libraries/chem-meta": "^1.2.2",
|
|
20
|
+
"@datagrok-libraries/utils": "^4.1.45",
|
|
21
21
|
"cash-dom": "^8.1.1",
|
|
22
22
|
"datagrok-api": "^1.17.0",
|
|
23
23
|
"dayjs": "^1.10.6",
|
|
@@ -37,7 +37,8 @@
|
|
|
37
37
|
"@typescript-eslint/parser": "^5.11.0",
|
|
38
38
|
"eslint": "^8.18.0",
|
|
39
39
|
"eslint-config-google": "^0.14.0",
|
|
40
|
-
"@datagrok/bio": "^2.11.
|
|
40
|
+
"@datagrok/bio": "^2.11.38",
|
|
41
|
+
"@datagrok/chem": "^1.8.13"
|
|
41
42
|
},
|
|
42
43
|
"scripts": {
|
|
43
44
|
"link-all": "npm link @datagrok-libraries/chem-meta datagrok-api @datagrok-libraries/utils @datagrok-libraries/bio",
|
package/src/cell-renderer.ts
CHANGED
|
@@ -7,7 +7,7 @@ import wu from 'wu';
|
|
|
7
7
|
import {printLeftOrCentered} from '@datagrok-libraries/bio/src/utils/cell-renderer';
|
|
8
8
|
import {errorToConsole} from '@datagrok-libraries/utils/src/to-console';
|
|
9
9
|
|
|
10
|
-
import {findMonomers, parseHelm} from './utils';
|
|
10
|
+
import {findMonomers, parseHelm, removeGapsFromHelm} from './utils';
|
|
11
11
|
import {IEditor, HelmMonomerPlacer, IEditorMolAtom, ISeqMonomer} from './helm-monomer-placer';
|
|
12
12
|
import {IMonomerLib} from '@datagrok-libraries/bio/src/types/index';
|
|
13
13
|
|
|
@@ -20,11 +20,6 @@ const enum tempTAGS {
|
|
|
20
20
|
helmPlacer = 'bio-helmPlacer',
|
|
21
21
|
}
|
|
22
22
|
|
|
23
|
-
// Global flag is for replaceAll
|
|
24
|
-
const helmGapStartRe = /\{(\*\.)+/g;
|
|
25
|
-
const helmGapIntRe = /\.(\*\.)+/g;
|
|
26
|
-
const helmGapEndRe = /(\.\*)+\}/g;
|
|
27
|
-
|
|
28
23
|
function getSeqMonomerFromHelm(
|
|
29
24
|
helmPrefix: string, symbol: string, monomerLib: IMonomerLib
|
|
30
25
|
): ISeqMonomer {
|
|
@@ -42,16 +37,16 @@ function getSeqMonomerFromHelmAtom(atom: IEditorMolAtom): ISeqMonomer {
|
|
|
42
37
|
let polymerType: string | undefined = undefined;
|
|
43
38
|
// @ts-ignore
|
|
44
39
|
switch (atom.bio.type) {
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
40
|
+
case 'HELM_BASE':
|
|
41
|
+
case 'HELM_SUGAR': // r - ribose, d - deoxyribose
|
|
42
|
+
case 'HELM_LINKER': // p - phosphate
|
|
43
|
+
polymerType = 'RNA';
|
|
44
|
+
break;
|
|
45
|
+
case 'HELM_AA':
|
|
46
|
+
polymerType = 'PEPTIDE';
|
|
47
|
+
break;
|
|
48
|
+
default:
|
|
49
|
+
polymerType = 'PEPTIDE';
|
|
55
50
|
}
|
|
56
51
|
return {symbol: atom.elem, polymerType: polymerType};
|
|
57
52
|
}
|
|
@@ -163,63 +158,63 @@ export class HelmCellRenderer extends DG.GridCellRenderer {
|
|
|
163
158
|
try { tableCol = gridCell.tableColumn; } catch { }
|
|
164
159
|
if (!tableCol) return;
|
|
165
160
|
|
|
166
|
-
const
|
|
167
|
-
const
|
|
161
|
+
/** {@link gridCell}.bounds */ const gcb = gridCell.bounds;
|
|
162
|
+
const argsX = e.offsetX - gcb.x;
|
|
163
|
+
const argsY = e.offsetY - gcb.y;
|
|
168
164
|
|
|
169
165
|
const helmPlacer = HelmMonomerPlacer.getOrCreate(tableCol);
|
|
170
166
|
const editor: IEditor | null = helmPlacer.getEditor(gridCell.tableRowIndex!);
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
if (
|
|
174
|
-
|
|
175
|
-
|
|
167
|
+
let seqMonomer: ISeqMonomer | null;
|
|
168
|
+
let missedMonomers: Set<string> = new Set<string>(); // of .size = 0
|
|
169
|
+
if (editor)
|
|
170
|
+
seqMonomer = getHoveredMonomerFromEditor(argsX, argsY, gridCell, editor);
|
|
171
|
+
else {
|
|
172
|
+
const seq: string = !gridCell.cell.value ? '' : removeGapsFromHelm(gridCell.cell.value as string);
|
|
173
|
+
const monomerList = parseHelm(seq);
|
|
174
|
+
missedMonomers = findMonomers(monomerList);
|
|
175
|
+
const parsedMonomers = new Set<string>(monomerList);
|
|
176
|
+
seqMonomer = getHoveredMonomerFallback(argsX, argsY, gridCell, helmPlacer);
|
|
177
|
+
if (seqMonomer && !parsedMonomers.has(seqMonomer.symbol)) seqMonomer = null;
|
|
178
|
+
if (seqMonomer) {
|
|
179
|
+
const textSize = helmPlacer.monomerTextSizeMap[seqMonomer.symbol];
|
|
180
|
+
if (textSize) {
|
|
181
|
+
const textBaseLine = gcb.height / 2 -
|
|
182
|
+
(textSize.fontBoundingBoxAscent + textSize.fontBoundingBoxDescent) / 2 + 1;
|
|
183
|
+
const textTop = textBaseLine - textSize.fontBoundingBoxAscent;
|
|
184
|
+
const textBottom = textBaseLine + textSize.fontBoundingBoxDescent;
|
|
185
|
+
if (argsY < textTop || textBottom < argsY) seqMonomer = null;
|
|
186
|
+
}
|
|
187
|
+
}
|
|
176
188
|
}
|
|
177
189
|
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
const monomer = helmPlacer.getMonomer(seqMonomer);
|
|
191
|
-
if (monomer) {
|
|
192
|
-
const options = {autoCrop: true, autoCropMargin: 0, suppressChiralText: true};
|
|
193
|
-
const monomerSvg = grok.chem.svgMol(monomer.smiles, undefined, undefined, options);
|
|
190
|
+
if (seqMonomer) {
|
|
191
|
+
if (!missedMonomers.has(seqMonomer.symbol)) {
|
|
192
|
+
const monomer = helmPlacer.getMonomer(seqMonomer);
|
|
193
|
+
if (monomer) {
|
|
194
|
+
const options = {autoCrop: true, autoCropMargin: 0, suppressChiralText: true};
|
|
195
|
+
const monomerSvg = grok.chem.svgMol(monomer.smiles, undefined, undefined, options);
|
|
196
|
+
ui.tooltip.show(ui.divV([
|
|
197
|
+
ui.divText(seqMonomer.symbol),
|
|
198
|
+
monomerSvg,
|
|
199
|
+
]), e.x + 16, e.y + 16);
|
|
200
|
+
}
|
|
201
|
+
} else {
|
|
194
202
|
ui.tooltip.show(ui.divV([
|
|
195
|
-
ui.divText(seqMonomer.symbol),
|
|
196
|
-
|
|
203
|
+
ui.divText(`Monomer '${seqMonomer.symbol}' not found.`),
|
|
204
|
+
ui.divText('Open the Context Panel, then expand Manage Libraries'),
|
|
197
205
|
]), e.x + 16, e.y + 16);
|
|
198
206
|
}
|
|
207
|
+
} else if (missedMonomers.size == 0) {
|
|
208
|
+
ui.tooltip.hide();
|
|
209
|
+
return;
|
|
210
|
+
} else { // seqMonomer == null && missedMonomers.size > 0
|
|
211
|
+
const mmStrList = wu(missedMonomers.keys()).toArray().sort()
|
|
212
|
+
.filter((_, i) => i < 3);
|
|
213
|
+
const missedMonomersStr = mmStrList.join(', ') + (missedMonomers.size > 3 ? ', ...' : '');
|
|
214
|
+
ui.tooltip.show(ui.divV([ui.divText('Monomers missed in monomer libraries:'),
|
|
215
|
+
ui.divText(missedMonomersStr)
|
|
216
|
+
]), e.x + 16, e.y + 16);
|
|
199
217
|
}
|
|
200
|
-
|
|
201
|
-
// const tooltipMessage: HTMLElement[] = [];
|
|
202
|
-
// for (const [part, partI] of wu.enumerate(allParts)) {
|
|
203
|
-
// if (missedMonomers.has(part)) {
|
|
204
|
-
// tooltipMessage[partI] = ui.divV([
|
|
205
|
-
// ui.divText(`Monomer ${allParts[partI]} not found.`),
|
|
206
|
-
// ui.divText('Open the Context Panel, then expand Manage Libraries')
|
|
207
|
-
// ]);
|
|
208
|
-
// } else if (monomers.has(part)) {
|
|
209
|
-
// const elList = [ui.div(part)];
|
|
210
|
-
// const monomer = helmPlacer.getMonomer(part);
|
|
211
|
-
// if (monomer) {
|
|
212
|
-
// const options = {autoCrop: true, autoCropMargin: 0, suppressChiralText: true};
|
|
213
|
-
// const monomerSvg = grok.chem.svgMol(monomer.smiles, undefined, undefined, options);
|
|
214
|
-
// elList.push(monomerSvg);
|
|
215
|
-
// }
|
|
216
|
-
// tooltipMessage[partI] = ui.divV(elList);
|
|
217
|
-
// }
|
|
218
|
-
// }
|
|
219
|
-
//
|
|
220
|
-
// (((tooltipMessage[left]?.childNodes.length ?? 0) > 0)) ?
|
|
221
|
-
// ui.tooltip.show(tooltipMessage[left], e.x + 16, e.y + 16) :
|
|
222
|
-
// ui.tooltip.hide();
|
|
223
218
|
} catch (err: any) {
|
|
224
219
|
const errMsg: string = errorToConsole(err);
|
|
225
220
|
console.error('Helm: HelmCellRenderer.onMouseMove() error:\n' + errMsg);
|
|
@@ -229,6 +224,7 @@ export class HelmCellRenderer extends DG.GridCellRenderer {
|
|
|
229
224
|
}
|
|
230
225
|
}
|
|
231
226
|
|
|
227
|
+
|
|
232
228
|
render(g: CanvasRenderingContext2D, x: number, y: number, w: number, h: number,
|
|
233
229
|
gridCell: DG.GridCell, cellStyle: DG.GridCellStyle
|
|
234
230
|
) {
|
|
@@ -244,9 +240,7 @@ export class HelmCellRenderer extends DG.GridCellRenderer {
|
|
|
244
240
|
const monomerColor: string = '#404040';
|
|
245
241
|
const frameColor: string = '#C0C0C0';
|
|
246
242
|
|
|
247
|
-
const seq: string = !gridCell.cell.value ? '' : gridCell.cell.value
|
|
248
|
-
.replaceAll(helmGapStartRe, '{').replaceAll(helmGapIntRe, '.').replaceAll(helmGapEndRe, '}')
|
|
249
|
-
.replace('{*}', '{}');
|
|
243
|
+
const seq: string = !gridCell.cell.value ? '' : removeGapsFromHelm(gridCell.cell.value);
|
|
250
244
|
const monomerList = parseHelm(seq);
|
|
251
245
|
const monomers: Set<string> = new Set<string>(monomerList);
|
|
252
246
|
const missedMonomers: Set<string> = findMonomers(monomerList);
|
|
@@ -281,14 +275,14 @@ export class HelmCellRenderer extends DG.GridCellRenderer {
|
|
|
281
275
|
}
|
|
282
276
|
|
|
283
277
|
w = grid ? Math.min(grid.canvas.width - x, w) : g.canvas.width - x;
|
|
284
|
-
g.save();
|
|
278
|
+
//g.save();
|
|
285
279
|
g.beginPath();
|
|
286
280
|
g.rect(x, y, w, h);
|
|
287
281
|
g.clip();
|
|
288
282
|
g.transform(1, 0, 0, 1, x, y);
|
|
289
283
|
g.font = '12px monospace';
|
|
290
284
|
g.textBaseline = 'top';
|
|
291
|
-
const [allParts,
|
|
285
|
+
const [allParts, _lengths, sumLengths] = helmPlacer.getCellAllPartsLengths(gridCell.tableRowIndex!);
|
|
292
286
|
|
|
293
287
|
for (let i = 0; i < allParts.length; ++i) {
|
|
294
288
|
const part: string = allParts[i];
|
|
@@ -298,7 +292,9 @@ export class HelmCellRenderer extends DG.GridCellRenderer {
|
|
|
298
292
|
monomers.has(part) ? monomerColor :
|
|
299
293
|
frameColor;
|
|
300
294
|
g.fillStyle = color;
|
|
301
|
-
printLeftOrCentered(sumLengths[i], 0, w, h, g, allParts[i], color, 0, true, 1.0
|
|
295
|
+
printLeftOrCentered(sumLengths[i], 0, w, h, g, allParts[i], color, 0, true, 1.0,
|
|
296
|
+
undefined, undefined, undefined, undefined, undefined,
|
|
297
|
+
undefined, undefined, undefined, helmPlacer.monomerTextSizeMap);
|
|
302
298
|
}
|
|
303
299
|
}
|
|
304
300
|
} finally {
|
|
@@ -49,6 +49,7 @@ export class HelmMonomerPlacer {
|
|
|
49
49
|
|
|
50
50
|
public monomerCharWidth: number = 7;
|
|
51
51
|
public leftPadding: number = 5;
|
|
52
|
+
public monomerTextSizeMap: { [p: string]: TextMetrics } = {};
|
|
52
53
|
|
|
53
54
|
constructor(public readonly col: DG.Column<string>) {
|
|
54
55
|
this.col.dataFrame.onDataChanged.subscribe();
|
|
@@ -3,9 +3,10 @@ import * as ui from 'datagrok-api/ui';
|
|
|
3
3
|
import * as DG from 'datagrok-api/dg';
|
|
4
4
|
|
|
5
5
|
import {after, before, category, delay, expect, expectObject, test} from '@datagrok-libraries/utils/src/test';
|
|
6
|
+
import {UserLibSettings} from '@datagrok-libraries/bio/src/monomer-works/types';
|
|
6
7
|
import {getMonomerLibHelper, IMonomerLibHelper} from '@datagrok-libraries/bio/src/monomer-works/monomer-utils';
|
|
7
8
|
import {
|
|
8
|
-
getUserLibSettings,
|
|
9
|
+
getUserLibSettings, setUserLibSettings, setUserLibSettingsForTests
|
|
9
10
|
} from '@datagrok-libraries/bio/src/monomer-works/lib-settings';
|
|
10
11
|
|
|
11
12
|
import {findMonomers, parseHelm} from '../utils';
|
|
@@ -15,7 +16,7 @@ import {findMonomers, parseHelm} from '../utils';
|
|
|
15
16
|
category('findMonomers', () => {
|
|
16
17
|
let monomerLibHelper: IMonomerLibHelper;
|
|
17
18
|
/** Backup actual user's monomer libraries settings */
|
|
18
|
-
let userLibSettings:
|
|
19
|
+
let userLibSettings: UserLibSettings;
|
|
19
20
|
|
|
20
21
|
before(async () => {
|
|
21
22
|
monomerLibHelper = await getMonomerLibHelper();
|
|
@@ -4,8 +4,9 @@ import * as DG from 'datagrok-api/dg';
|
|
|
4
4
|
|
|
5
5
|
import {after, before, category, delay, expect, test, expectArray, testEvent} from '@datagrok-libraries/utils/src/test';
|
|
6
6
|
import {
|
|
7
|
-
getUserLibSettings,
|
|
7
|
+
getUserLibSettings, setUserLibSettings, setUserLibSettingsForTests
|
|
8
8
|
} from '@datagrok-libraries/bio/src/monomer-works/lib-settings';
|
|
9
|
+
import {UserLibSettings} from '@datagrok-libraries/bio/src/monomer-works/types';
|
|
9
10
|
import {getMonomerLibHelper, IMonomerLibHelper} from '@datagrok-libraries/bio/src/monomer-works/monomer-utils';
|
|
10
11
|
import {MolfileHandler} from '@datagrok-libraries/chem-meta/src/parsing-utils/molfile-handler';
|
|
11
12
|
import {MolfileHandlerBase} from '@datagrok-libraries/chem-meta/src/parsing-utils/molfile-handler-base';
|
|
@@ -16,7 +17,7 @@ import {_package} from '../package-test';
|
|
|
16
17
|
|
|
17
18
|
category('getMolfiles', () => {
|
|
18
19
|
let monomerLibHelper: IMonomerLibHelper;
|
|
19
|
-
let userLibSettings:
|
|
20
|
+
let userLibSettings: UserLibSettings;
|
|
20
21
|
|
|
21
22
|
before(async () => {
|
|
22
23
|
monomerLibHelper = await getMonomerLibHelper();
|
package/src/utils/index.ts
CHANGED
|
@@ -11,6 +11,16 @@ import {
|
|
|
11
11
|
SDF_MONOMER_NAME
|
|
12
12
|
} from '../constants';
|
|
13
13
|
|
|
14
|
+
// Global flag is for replaceAll
|
|
15
|
+
const helmGapStartRe = /\{(\*\.)+/g;
|
|
16
|
+
const helmGapIntRe = /\.(\*\.)+/g;
|
|
17
|
+
const helmGapEndRe = /(\.\*)+\}/g;
|
|
18
|
+
|
|
19
|
+
export function removeGapsFromHelm(srcHelm: string): string {
|
|
20
|
+
return srcHelm.replaceAll(helmGapStartRe, '{').replaceAll(helmGapIntRe, '.')
|
|
21
|
+
.replaceAll(helmGapEndRe, '}').replace('{*}', '{}');
|
|
22
|
+
}
|
|
23
|
+
|
|
14
24
|
export function getParts(subParts: string[], s: string): string[] {
|
|
15
25
|
const j = 0;
|
|
16
26
|
const allParts: string[] = [];
|
|
@@ -226,14 +236,14 @@ function unescape(s: string) {
|
|
|
226
236
|
|
|
227
237
|
return s.replace(/[\\]./g, function(m) {
|
|
228
238
|
switch (m) {
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
239
|
+
case '\\r':
|
|
240
|
+
return '\r';
|
|
241
|
+
case '\\n':
|
|
242
|
+
return '\n';
|
|
243
|
+
case '\\t':
|
|
244
|
+
return '\t';
|
|
245
|
+
default:
|
|
246
|
+
return m.substring(1);
|
|
237
247
|
}
|
|
238
248
|
});
|
|
239
249
|
}
|
|
@@ -7,11 +7,12 @@ import $ from 'cash-dom';
|
|
|
7
7
|
import {UnitsHandler} from '@datagrok-libraries/bio/src/utils/units-handler';
|
|
8
8
|
import {NOTATION} from '@datagrok-libraries/bio/src/utils/macromolecule';
|
|
9
9
|
|
|
10
|
+
import {removeGapsFromHelm} from '../utils';
|
|
11
|
+
|
|
10
12
|
export function getPropertiesDict(seq: string, uh: UnitsHandler): {} {
|
|
11
13
|
const helmString = uh.isHelm() ? seq : uh.getConverter(NOTATION.HELM)(seq);
|
|
12
14
|
// Remove gap symbols for compatibility with WebEditor
|
|
13
|
-
const helmString2 = helmString
|
|
14
|
-
.replaceAll(/\{(\*\.)+/g, '{').replaceAll(/(\.\*)+}/g, '}');
|
|
15
|
+
const helmString2 = removeGapsFromHelm(helmString);
|
|
15
16
|
|
|
16
17
|
const host = ui.div([], {style: {width: '0', height: '0'}});
|
|
17
18
|
document.documentElement.appendChild(host);
|