@datagrok/bio 2.8.4 → 2.8.6
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/CHANGELOG.md +12 -2
- 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/dockerfiles/Dockerfile +5 -4
- package/package.json +2 -2
- package/src/analysis/sequence-activity-cliffs.ts +8 -7
- package/src/analysis/sequence-similarity-viewer.ts +8 -8
- package/src/apps/web-logo-app.ts +26 -6
- package/src/calculations/monomerLevelMols.ts +6 -3
- package/src/package.ts +2 -1
- package/src/tests/converters-test.ts +1 -1
- package/src/tests/msa-tests.ts +2 -3
- package/src/tests/renderers-test.ts +37 -3
- package/src/tests/splitters-test.ts +27 -1
- package/src/tests/units-handler-splitted-tests.ts +19 -12
- package/src/tests/units-handler-tests.ts +15 -15
- package/src/utils/cell-renderer.ts +31 -22
- package/src/utils/monomer-cell-renderer.ts +14 -14
- package/src/utils/save-as-fasta.ts +1 -1
- package/src/utils/split-to-monomers.ts +40 -6
- package/src/viewers/vd-regions-viewer.ts +88 -51
- package/src/viewers/web-logo-viewer.ts +307 -310
- package/src/widgets/composition-analysis-widget.ts +6 -2
|
@@ -16,16 +16,18 @@ const Tags = new class {
|
|
|
16
16
|
const svgMolOptions = {autoCrop: true, autoCropMargin: 0, suppressChiralText: true};
|
|
17
17
|
|
|
18
18
|
export class MonomerTooltipHandler {
|
|
19
|
-
private readonly
|
|
19
|
+
private readonly gridCol: DG.GridColumn;
|
|
20
20
|
|
|
21
|
-
constructor(
|
|
22
|
-
this.
|
|
23
|
-
this.grid.onCellTooltip(this.onCellTooltip.bind(this));
|
|
21
|
+
constructor(gridCol: DG.GridColumn) {
|
|
22
|
+
this.gridCol = gridCol;
|
|
23
|
+
this.gridCol.grid.onCellTooltip(this.onCellTooltip.bind(this));
|
|
24
24
|
}
|
|
25
25
|
|
|
26
26
|
private onCellTooltip(gridCell: DG.GridCell, x: number, y: number): any {
|
|
27
|
-
if (
|
|
28
|
-
gridCell.
|
|
27
|
+
if (
|
|
28
|
+
gridCell.grid.dart != this.gridCol.grid.dart || gridCell.gridColumn.dart != this.gridCol.dart ||
|
|
29
|
+
!gridCell.tableColumn || !gridCell.isTableCell
|
|
30
|
+
) return false;
|
|
29
31
|
|
|
30
32
|
const alphabet = gridCell.tableColumn.getTag(bioTAGS.alphabet);
|
|
31
33
|
const monomerName = gridCell.cell.value;
|
|
@@ -46,13 +48,11 @@ export class MonomerTooltipHandler {
|
|
|
46
48
|
return true; // To prevent default tooltip behaviour
|
|
47
49
|
}
|
|
48
50
|
|
|
49
|
-
public static getOrCreate(
|
|
50
|
-
|
|
51
|
-
if (!
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
}
|
|
55
|
-
return gridTemp[Tags.tooltipHandlerTemp];
|
|
51
|
+
public static getOrCreate(gridCol: DG.GridColumn): MonomerTooltipHandler {
|
|
52
|
+
let res = gridCol.temp[Tags.tooltipHandlerTemp];
|
|
53
|
+
if (!res)
|
|
54
|
+
res = gridCol.temp[Tags.tooltipHandlerTemp] = new MonomerTooltipHandler(gridCol);
|
|
55
|
+
return res;
|
|
56
56
|
}
|
|
57
57
|
}
|
|
58
58
|
|
|
@@ -81,7 +81,7 @@ export class MonomerCellRenderer extends DG.GridCellRenderer {
|
|
|
81
81
|
_cellStyle: DG.GridCellStyle
|
|
82
82
|
): void {
|
|
83
83
|
if (gridCell.gridRow < 0) return;
|
|
84
|
-
MonomerTooltipHandler.getOrCreate(gridCell.
|
|
84
|
+
MonomerTooltipHandler.getOrCreate(gridCell.gridColumn);
|
|
85
85
|
|
|
86
86
|
|
|
87
87
|
g.font = `12px monospace`;
|
|
@@ -107,7 +107,7 @@ export function wrapSequence(seq: string, splitter: SplitterFunc, lineWidth: num
|
|
|
107
107
|
const seqLineList: string[] = [];
|
|
108
108
|
while (seqPos < seqLength) {
|
|
109
109
|
/* join sliced monomer into line */
|
|
110
|
-
const seqLine: string[] = seqMonomerList.slice(seqPos, seqPos + lineWidth);
|
|
110
|
+
const seqLine: string[] = wu(seqMonomerList).slice(seqPos, seqPos + lineWidth).toArray();
|
|
111
111
|
const seqLineTxt: string = seqLine.map((m) => m.length > 1 ? `[${m}]` : m).join('');
|
|
112
112
|
seqLineList.push(seqLineTxt);
|
|
113
113
|
seqPos += seqLine.length;
|
|
@@ -1,10 +1,13 @@
|
|
|
1
|
+
import * as grok from 'datagrok-api/grok';
|
|
2
|
+
import * as ui from 'datagrok-api/ui';
|
|
3
|
+
import * as DG from 'datagrok-api/dg';
|
|
4
|
+
|
|
1
5
|
import {delay} from '@datagrok-libraries/utils/src/test';
|
|
2
6
|
import {checkInputColumnUI} from './check-input-column';
|
|
3
7
|
import {splitAlignedSequences} from '@datagrok-libraries/bio/src/utils/splitter';
|
|
4
8
|
import * as C from './constants';
|
|
5
9
|
import {TAGS as bioTAGS} from '@datagrok-libraries/bio/src/utils/macromolecule/consts';
|
|
6
|
-
import
|
|
7
|
-
import * as DG from 'datagrok-api/dg';
|
|
10
|
+
import {SEM_TYPES} from './constants';
|
|
8
11
|
|
|
9
12
|
|
|
10
13
|
export async function splitToMonomersUI(table: DG.DataFrame, seqCol: DG.Column<string>): Promise<DG.DataFrame> {
|
|
@@ -14,6 +17,7 @@ export async function splitToMonomersUI(table: DG.DataFrame, seqCol: DG.Column<s
|
|
|
14
17
|
if (!checkInputColumnUI(seqCol, 'Sequence space')) return table;
|
|
15
18
|
|
|
16
19
|
const tempDf = splitAlignedSequences(seqCol);
|
|
20
|
+
tempDf.name = 'splitToMonomers';
|
|
17
21
|
const originalDf = seqCol.dataFrame;
|
|
18
22
|
for (const tempCol of tempDf.columns) {
|
|
19
23
|
// TODO: GROK-11212
|
|
@@ -21,9 +25,39 @@ export async function splitToMonomersUI(table: DG.DataFrame, seqCol: DG.Column<s
|
|
|
21
25
|
tempCol.semType = C.SEM_TYPES.MONOMER;
|
|
22
26
|
tempCol.setTag(bioTAGS.alphabet, seqCol.getTag(bioTAGS.alphabet));
|
|
23
27
|
}
|
|
24
|
-
// Create the new data frame to enable platform to setup cell renderers
|
|
25
|
-
const newDf = originalDf.join(tempDf, [], [], undefined, undefined, DG.JOIN_TYPE.LEFT, false);
|
|
26
|
-
grok.shell.addTableView(newDf);
|
|
27
28
|
|
|
28
|
-
|
|
29
|
+
const colNameRe = /(\d+)(?: \((\d+)\))?/;
|
|
30
|
+
const generateNewColName = (srcName: string): string => {
|
|
31
|
+
colNameRe.lastIndex = 0;
|
|
32
|
+
const ma = srcName.match(colNameRe);
|
|
33
|
+
if (!ma) return srcName;
|
|
34
|
+
return `${ma[1]} (${parseInt(ma[2] ?? 0) + 1})`;
|
|
35
|
+
};
|
|
36
|
+
|
|
37
|
+
// if (tempDf.columns.length === 0) return;
|
|
38
|
+
|
|
39
|
+
for (let tempColI = 0; tempColI < tempDf.columns.length; tempColI++) {
|
|
40
|
+
const tempCol = tempDf.columns.byIndex(tempColI);
|
|
41
|
+
tempCol.semType = SEM_TYPES.MONOMER;
|
|
42
|
+
tempCol.setTag(bioTAGS.alphabet, seqCol.getTag(bioTAGS.alphabet));
|
|
43
|
+
|
|
44
|
+
const wdMax = 100;
|
|
45
|
+
let wdCount = 0;
|
|
46
|
+
while (originalDf.columns.byName(tempCol.name) && wdCount < wdMax) {
|
|
47
|
+
tempCol.name = generateNewColName(tempCol.name);
|
|
48
|
+
wdCount++;
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
originalDf.columns.add(tempCol);
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
// originalDf.join(tempDf, [], [], undefined, undefined, DG.JOIN_TYPE.LEFT, true);
|
|
55
|
+
await grok.data.detectSemanticTypes(originalDf);
|
|
56
|
+
|
|
57
|
+
for (let tempColI = 0; tempColI < tempDf.columns.length; tempColI++) {
|
|
58
|
+
const tempCol = tempDf.columns.byIndex(tempColI);
|
|
59
|
+
tempCol.setTag(DG.TAGS.CELL_RENDERER, 'Monomer');
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
return originalDf;
|
|
29
63
|
}
|
|
@@ -42,6 +42,24 @@ const vrt = VdRegionType;
|
|
|
42
42
|
// new VdRegion(vrt.FR, 'FR4', 'Heavy', 7, '118', null/*128*/),
|
|
43
43
|
// ];
|
|
44
44
|
|
|
45
|
+
export enum PROPS_CATS {
|
|
46
|
+
STYLE = 'Style',
|
|
47
|
+
BEHAVIOR = 'Behavior',
|
|
48
|
+
LAYOUT = 'Layout',
|
|
49
|
+
DATA = 'Data',
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
export enum PROPS {
|
|
53
|
+
// -- Data --
|
|
54
|
+
skipEmptyPositions = 'skipEmptyPositions',
|
|
55
|
+
regionTypes = 'regionTypes',
|
|
56
|
+
chains = 'chains',
|
|
57
|
+
|
|
58
|
+
// -- Style --
|
|
59
|
+
positionWidth = 'positionWidth',
|
|
60
|
+
positionHeight = 'positionHeight',
|
|
61
|
+
}
|
|
62
|
+
|
|
45
63
|
/** Viewer with tabs based on description of chain regions.
|
|
46
64
|
* Used to define regions of an immunoglobulin LC.
|
|
47
65
|
*/
|
|
@@ -69,20 +87,24 @@ export class VdRegionsViewer extends DG.JsViewer implements IVdRegionsViewer {
|
|
|
69
87
|
constructor() {
|
|
70
88
|
super();
|
|
71
89
|
|
|
90
|
+
// -- Data --
|
|
91
|
+
this.skipEmptyPositions = this.bool(PROPS.skipEmptyPositions, false,
|
|
92
|
+
{category: PROPS_CATS.DATA});
|
|
93
|
+
|
|
72
94
|
// To prevent ambiguous numbering scheme in MLB
|
|
73
|
-
this.regionTypes = this.stringList(
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
this.positionWidth = this.float(
|
|
81
|
-
editor: 'slider', min: 0, max: 64,
|
|
95
|
+
this.regionTypes = this.stringList(PROPS.regionTypes, [vrt.CDR], {
|
|
96
|
+
category: PROPS_CATS.DATA, choices: Object.values(vrt).filter((t) => t != vrt.Unknown)
|
|
97
|
+
}) as VdRegionType[];
|
|
98
|
+
this.chains = this.stringList(PROPS.chains, ['Heavy', 'Light'],
|
|
99
|
+
{category: PROPS_CATS.DATA, choices: ['Heavy', 'Light']});
|
|
100
|
+
|
|
101
|
+
// -- Layout --
|
|
102
|
+
this.positionWidth = this.float(PROPS.positionWidth, 16, {
|
|
103
|
+
category: PROPS_CATS.LAYOUT, editor: 'slider', min: 0, max: 64,
|
|
82
104
|
description: 'Internal WebLogo viewers property width of position. A value of zero means autofit to the width.'
|
|
83
105
|
});
|
|
84
|
-
this.positionHeight = this.string(
|
|
85
|
-
{choices: Object.keys(PositionHeight)}) as PositionHeight;
|
|
106
|
+
this.positionHeight = this.string(PROPS.positionHeight, PositionHeight.Entropy,
|
|
107
|
+
{category: PROPS_CATS.LAYOUT, choices: Object.keys(PositionHeight)}) as PositionHeight;
|
|
86
108
|
}
|
|
87
109
|
|
|
88
110
|
public async init() {
|
|
@@ -140,28 +162,35 @@ export class VdRegionsViewer extends DG.JsViewer implements IVdRegionsViewer {
|
|
|
140
162
|
return;
|
|
141
163
|
}
|
|
142
164
|
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
break;
|
|
149
|
-
case 'sequenceColumnNamePostfix':
|
|
150
|
-
break;
|
|
151
|
-
// for (let orderI = 0; orderI < this.logos.length; orderI++) {
|
|
152
|
-
// for (let chainI = 0; chainI < this.chains.length; chainI++) {
|
|
153
|
-
// const chain: string = this.chains[chainI];
|
|
154
|
-
// this.logos[orderI][chain].setOptions({skipEmptyPositions: this.skipEmptyPositions});
|
|
155
|
-
// }
|
|
156
|
-
// }
|
|
157
|
-
// this.calcSize();
|
|
158
|
-
}
|
|
165
|
+
switch (property.name) {
|
|
166
|
+
case PROPS.regionTypes:
|
|
167
|
+
case PROPS.chains:
|
|
168
|
+
this.setData(this.dataFrame, this.regions);
|
|
169
|
+
break;
|
|
159
170
|
}
|
|
160
171
|
|
|
161
172
|
switch (property.name) {
|
|
162
|
-
case
|
|
163
|
-
|
|
164
|
-
|
|
173
|
+
case PROPS.skipEmptyPositions:
|
|
174
|
+
for (let orderI = 0; orderI < this.logos.length; ++orderI) {
|
|
175
|
+
for (const chain of this.chains)
|
|
176
|
+
this.logos[orderI][chain].setOptions({[wlPROPS.skipEmptyPositions]: this.skipEmptyPositions});
|
|
177
|
+
}
|
|
178
|
+
this.calcSize();
|
|
179
|
+
break;
|
|
180
|
+
|
|
181
|
+
case PROPS.positionWidth:
|
|
182
|
+
this.calcSize();
|
|
183
|
+
break;
|
|
184
|
+
|
|
185
|
+
case PROPS.positionHeight:
|
|
186
|
+
for (let orderI = 0; orderI < this.logos.length; ++orderI) {
|
|
187
|
+
for (const chain of this.chains)
|
|
188
|
+
this.logos[orderI][chain].setOptions({[wlPROPS.positionWidth]: this.positionWidth});
|
|
189
|
+
}
|
|
190
|
+
this.calcSize();
|
|
191
|
+
break;
|
|
192
|
+
|
|
193
|
+
default:
|
|
165
194
|
this.setData(this.dataFrame, this.regions); // onPropertyChanged
|
|
166
195
|
break;
|
|
167
196
|
}
|
|
@@ -265,8 +294,10 @@ export class VdRegionsViewer extends DG.JsViewer implements IVdRegionsViewer {
|
|
|
265
294
|
this.logos = new Array(orderList.length);
|
|
266
295
|
for (let orderI = 0; orderI < orderList.length; ++orderI)
|
|
267
296
|
this.logos[orderI] = {};
|
|
268
|
-
for (const [orderI, chain, wl] of logoList)
|
|
297
|
+
for (const [orderI, chain, wl] of logoList) {
|
|
269
298
|
this.logos[orderI][chain] = wl;
|
|
299
|
+
this.viewSubs.push(wl.onFreqsCalculated.subscribe(() => { this.calcSize(); }));
|
|
300
|
+
}
|
|
270
301
|
|
|
271
302
|
// ui.tableFromMap()
|
|
272
303
|
// DG.HtmlTable.create()
|
|
@@ -336,35 +367,41 @@ export class VdRegionsViewer extends DG.JsViewer implements IVdRegionsViewer {
|
|
|
336
367
|
private calcSize() {
|
|
337
368
|
_package.logger.debug(`Bio: VdRegionsViewer.calcSize(), start`);
|
|
338
369
|
const calcSizeInt = (): void => {
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
const maxHeight: number = Math.min(logoHeight,
|
|
343
|
-
Math.max(...this.logos.map((wlDict) =>
|
|
344
|
-
Math.max(...Object.values(wlDict).map((wl) => wl.maxHeight)))),
|
|
345
|
-
);
|
|
370
|
+
// Postponed calcSizeInt can result call after the viewer has been closed (on tests)
|
|
371
|
+
if (!this.host) return;
|
|
346
372
|
|
|
373
|
+
const logoHeight = (this.root.clientHeight - 54) / this.chains.length;
|
|
347
374
|
let totalPos: number = 0;
|
|
348
375
|
for (let orderI = 0; orderI < this.logos.length; orderI++) {
|
|
349
|
-
for (const chain of this.chains)
|
|
350
|
-
this.logos[orderI][chain]
|
|
376
|
+
for (const chain of this.chains) {
|
|
377
|
+
const wl = this.logos[orderI][chain];
|
|
378
|
+
wl.root.style.height = `${logoHeight}px`;
|
|
379
|
+
}
|
|
351
380
|
|
|
352
381
|
totalPos += Math.max(...this.chains.map((chain) => this.logos[orderI][chain].Length));
|
|
353
382
|
}
|
|
354
383
|
|
|
355
|
-
if (this.positionWidth === 0
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
for (let
|
|
364
|
-
const chain
|
|
365
|
-
|
|
384
|
+
if (this.positionWidth === 0) {
|
|
385
|
+
if (this.logos.length > 0 && totalPos > 0) {
|
|
386
|
+
const leftPad = 22/* Chain label */;
|
|
387
|
+
const rightPad = 6 + 6 + 1;
|
|
388
|
+
const logoMargin = 8 + 1;
|
|
389
|
+
const fitPositionWidth =
|
|
390
|
+
(this.root.clientWidth - leftPad - (this.logos.length - 1) * logoMargin - rightPad) / totalPos;
|
|
391
|
+
|
|
392
|
+
for (let orderI = 0; orderI < this.logos.length; orderI++) {
|
|
393
|
+
for (const chain of this.chains) {
|
|
394
|
+
const wl = this.logos[orderI][chain];
|
|
395
|
+
wl.setOptions({[wlPROPS.positionWidth]: fitPositionWidth});
|
|
396
|
+
wl.root.style.width = `${fitPositionWidth * wl.Length}px`;
|
|
397
|
+
}
|
|
366
398
|
}
|
|
367
399
|
}
|
|
400
|
+
} else {
|
|
401
|
+
for (let orderI = 0; orderI < this.logos.length; orderI++) {
|
|
402
|
+
for (const chain of this.chains)
|
|
403
|
+
this.logos[orderI][chain].setOptions({[wlPROPS.positionWidth]: this.positionWidth});
|
|
404
|
+
}
|
|
368
405
|
}
|
|
369
406
|
|
|
370
407
|
if (this.positionWidth === 0)
|