@datagrok/bio 2.4.49 → 2.4.51
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 +1 -1
- package/dist/package-test.js.map +1 -1
- package/dist/package.js +1 -1
- package/dist/package.js.map +1 -1
- package/package.json +2 -2
- package/scripts/sequence_generator.py +0 -0
- package/src/package.ts +10 -0
- package/src/tests/renderers-monomer-placer.ts +20 -19
- package/src/utils/cell-renderer-consts.ts +14 -1
- package/src/utils/cell-renderer.ts +22 -29
- package/src/viewers/vd-regions-viewer.ts +67 -46
- package/src/viewers/web-logo-viewer.ts +40 -26
- package/src/widgets/representations.ts +17 -7
package/package.json
CHANGED
|
@@ -5,7 +5,7 @@
|
|
|
5
5
|
"name": "Leonid Stolbov",
|
|
6
6
|
"email": "lstolbov@datagrok.ai"
|
|
7
7
|
},
|
|
8
|
-
"version": "2.4.
|
|
8
|
+
"version": "2.4.51",
|
|
9
9
|
"description": "Bioinformatics support (import/export of sequences, conversion, visualization, analysis). [See more](https://github.com/datagrok-ai/public/blob/master/packages/Bio/README.md) for details.",
|
|
10
10
|
"repository": {
|
|
11
11
|
"type": "git",
|
|
@@ -22,7 +22,7 @@
|
|
|
22
22
|
],
|
|
23
23
|
"dependencies": {
|
|
24
24
|
"@biowasm/aioli": "^3.1.0",
|
|
25
|
-
"@datagrok-libraries/bio": "^5.32.
|
|
25
|
+
"@datagrok-libraries/bio": "^5.32.8",
|
|
26
26
|
"@datagrok-libraries/chem-meta": "^1.0.1",
|
|
27
27
|
"@datagrok-libraries/ml": "^6.3.39",
|
|
28
28
|
"@datagrok-libraries/tutorials": "^1.3.2",
|
|
File without changes
|
package/src/package.ts
CHANGED
|
@@ -614,6 +614,16 @@ export function importFasta(fileContent: string): DG.DataFrame [] {
|
|
|
614
614
|
const ffh = new FastaFileHandler(fileContent);
|
|
615
615
|
return ffh.importFasta();
|
|
616
616
|
}
|
|
617
|
+
//name: importBam
|
|
618
|
+
//description: Opens Bam file
|
|
619
|
+
//tags: file-handler
|
|
620
|
+
//meta.ext: bam, bai
|
|
621
|
+
//input: string fileContent
|
|
622
|
+
//output: list tables
|
|
623
|
+
export function importBam(fileContent: string): DG.DataFrame [] {
|
|
624
|
+
console.log(fileContent);
|
|
625
|
+
return [];
|
|
626
|
+
}
|
|
617
627
|
|
|
618
628
|
//top-menu: Bio | Convert...
|
|
619
629
|
//name: convertDialog
|
|
@@ -3,7 +3,7 @@ import * as DG from 'datagrok-api/dg';
|
|
|
3
3
|
import * as ui from 'datagrok-api/ui';
|
|
4
4
|
|
|
5
5
|
import {_package} from '../package-test';
|
|
6
|
-
import {
|
|
6
|
+
import {category, test} from '@datagrok-libraries/utils/src/test';
|
|
7
7
|
import {MonomerPlacer} from '@datagrok-libraries/bio/src/utils/cell-renderer-monomer-placer';
|
|
8
8
|
import {monomerToShort} from '@datagrok-libraries/bio/src/utils/macromolecule';
|
|
9
9
|
import {UnitsHandler} from '@datagrok-libraries/bio/src/utils/units-handler';
|
|
@@ -22,13 +22,14 @@ category('renderers: monomerPlacer', () => {
|
|
|
22
22
|
'id3,mon1-M-mon3-mon4-mon5-MON8-N9\n', //
|
|
23
23
|
testList: [
|
|
24
24
|
{src: {row: 0, x: -1}, tgt: {pos: null}},
|
|
25
|
-
{src: {row: 1, x: 0}, tgt: {pos:
|
|
26
|
-
{src: {row: 1, x:
|
|
25
|
+
{src: {row: 1, x: 0}, tgt: {pos: null}},
|
|
26
|
+
{src: {row: 1, x: 5}, tgt: {pos: 0}},
|
|
27
|
+
{src: {row: 1, x: 6}, tgt: {pos: 0}},
|
|
27
28
|
{src: {row: 1, x: 26}, tgt: {pos: 1}},
|
|
28
29
|
{src: {row: 1, x: 170}, tgt: {pos: 6}},
|
|
29
|
-
{src: {row: 1, x: 208}, tgt: {pos:
|
|
30
|
-
{src: {row: 2, x: 170}, tgt: {pos:
|
|
31
|
-
{src: {row: 2, x: 175}, tgt: {pos:
|
|
30
|
+
{src: {row: 1, x: 208}, tgt: {pos: 7}},
|
|
31
|
+
{src: {row: 2, x: 170}, tgt: {pos: 5}},
|
|
32
|
+
{src: {row: 2, x: 175}, tgt: {pos: 5}},
|
|
32
33
|
]
|
|
33
34
|
},
|
|
34
35
|
splitterMsa: {
|
|
@@ -41,13 +42,13 @@ category('renderers: monomerPlacer', () => {
|
|
|
41
42
|
'id3,mon1-M-mon3-mon4-mon5---MON8-N9\n', //
|
|
42
43
|
testList: [
|
|
43
44
|
{src: {row: 0, x: -1}, tgt: {pos: null}},
|
|
44
|
-
{src: {row: 1, x: 0}, tgt: {pos:
|
|
45
|
-
{src: {row: 1, x: 1}, tgt: {pos:
|
|
46
|
-
{src: {row: 1, x: 26}, tgt: {pos:
|
|
47
|
-
{src: {row: 1, x: 170}, tgt: {pos:
|
|
48
|
-
{src: {row: 1, x: 227}, tgt: {pos:
|
|
49
|
-
{src: {row: 2, x: 220}, tgt: {pos:
|
|
50
|
-
{src: {row: 2, x: 227}, tgt: {pos:
|
|
45
|
+
{src: {row: 1, x: 0}, tgt: {pos: null}},
|
|
46
|
+
{src: {row: 1, x: 1}, tgt: {pos: null}},
|
|
47
|
+
{src: {row: 1, x: 26}, tgt: {pos: 0}},
|
|
48
|
+
{src: {row: 1, x: 170}, tgt: {pos: 4}},
|
|
49
|
+
{src: {row: 1, x: 227}, tgt: {pos: 5}},
|
|
50
|
+
{src: {row: 2, x: 220}, tgt: {pos: 5}},
|
|
51
|
+
{src: {row: 2, x: 227}, tgt: {pos: 5}},
|
|
51
52
|
]
|
|
52
53
|
},
|
|
53
54
|
fastaMsa: {
|
|
@@ -61,13 +62,13 @@ id3,QHIRE--LT
|
|
|
61
62
|
`,
|
|
62
63
|
testList: [
|
|
63
64
|
{src: {row: 1, x: -1}, tgt: {pos: null}},
|
|
64
|
-
{src: {row: 1, x: 0}, tgt: {pos:
|
|
65
|
-
{src: {row: 1, x: 1}, tgt: {pos:
|
|
66
|
-
{src: {row: 1, x: 19}, tgt: {pos:
|
|
65
|
+
{src: {row: 1, x: 0}, tgt: {pos: null}},
|
|
66
|
+
{src: {row: 1, x: 1}, tgt: {pos: null}},
|
|
67
|
+
{src: {row: 1, x: 19}, tgt: {pos: 0}},
|
|
67
68
|
{src: {row: 1, x: 170}, tgt: {pos: 8}},
|
|
68
|
-
{src: {row: 1, x: 171}, tgt: {pos:
|
|
69
|
+
{src: {row: 1, x: 171}, tgt: {pos: 8}},
|
|
69
70
|
{src: {row: 2, x: 170}, tgt: {pos: 8}},
|
|
70
|
-
{src: {row: 2, x:
|
|
71
|
+
{src: {row: 2, x: 181}, tgt: {pos: null}},
|
|
71
72
|
]
|
|
72
73
|
},
|
|
73
74
|
};
|
|
@@ -78,7 +79,7 @@ id3,QHIRE--LT
|
|
|
78
79
|
await grok.data.detectSemanticTypes(df);
|
|
79
80
|
const seqCol: DG.Column = df.getCol('seq');
|
|
80
81
|
|
|
81
|
-
const monLength: number =
|
|
82
|
+
const monLength: number = 3;
|
|
82
83
|
const charWidth: number = 7;
|
|
83
84
|
const sepWidth: number = 12;
|
|
84
85
|
const colTemp: MonomerPlacer = new MonomerPlacer(null, seqCol, () => {
|
|
@@ -8,15 +8,28 @@ export enum MonomerWidthMode {
|
|
|
8
8
|
}
|
|
9
9
|
|
|
10
10
|
export const enum Tags {
|
|
11
|
-
|
|
11
|
+
RendererSettingsChanged = '.mm.cellRenderer.settingsChanged',
|
|
12
12
|
}
|
|
13
13
|
|
|
14
|
+
export const rendererSettingsChangedState = {
|
|
15
|
+
true: '1',
|
|
16
|
+
false: '0',
|
|
17
|
+
};
|
|
18
|
+
|
|
14
19
|
export const enum Temps {
|
|
15
20
|
monomerWidth = '.mm.cellRenderer.monomerWidth',
|
|
16
21
|
maxMonomerLength = '.mm.cellRenderer.maxMonomerLength',
|
|
17
22
|
colorCode = '.mm.cellRenderer.colorCode',
|
|
18
23
|
compareWithCurrent = '.mm.cellRenderer.compareWithCurrent',
|
|
19
24
|
highlightDifference = '.mm.cellRenderer.highlightDifference',
|
|
25
|
+
gapLength = '.mm.cellRenderer.gapLength',
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
export const enum tempTAGS {
|
|
29
|
+
referenceSequence = 'reference-sequence',
|
|
30
|
+
currentWord = 'current-word',
|
|
31
|
+
monomerWidth = 'monomer-width',
|
|
32
|
+
bioSeqCol = 'bio-seqCol',
|
|
20
33
|
}
|
|
21
34
|
|
|
22
35
|
// export const MacromoleculeCellRendererDefaults = new class {
|
|
@@ -7,10 +7,8 @@ import {printLeftOrCentered, DrawStyle} from '@datagrok-libraries/bio/src/utils/
|
|
|
7
7
|
import * as C from './constants';
|
|
8
8
|
import {MonomerPlacer} from '@datagrok-libraries/bio/src/utils/cell-renderer-monomer-placer';
|
|
9
9
|
import {
|
|
10
|
-
ALIGNMENT, ALPHABET,
|
|
11
10
|
getPaletteByType,
|
|
12
11
|
getSplitter,
|
|
13
|
-
getSplitterForColumn,
|
|
14
12
|
monomerToShort,
|
|
15
13
|
MonomerToShortFunc,
|
|
16
14
|
NOTATION,
|
|
@@ -20,22 +18,8 @@ import {
|
|
|
20
18
|
import {SeqPalette} from '@datagrok-libraries/bio/src/seq-palettes';
|
|
21
19
|
import {UnknownSeqPalettes} from '@datagrok-libraries/bio/src/unknown';
|
|
22
20
|
import {UnitsHandler} from '@datagrok-libraries/bio/src/utils/units-handler';
|
|
23
|
-
import {
|
|
24
|
-
|
|
25
|
-
import { HELM_POLYMER_TYPE } from '@datagrok-libraries/bio/src/utils/const';
|
|
26
|
-
import { MonomerLib } from './monomer-lib';
|
|
27
|
-
import { IMonomerLib } from '@datagrok-libraries/bio/src/types';
|
|
28
|
-
|
|
29
|
-
const enum tempTAGS {
|
|
30
|
-
referenceSequence = 'reference-sequence',
|
|
31
|
-
currentWord = 'current-word',
|
|
32
|
-
monomerWidth = 'monomer-width',
|
|
33
|
-
bioSeqCol = 'bio-seqCol',
|
|
34
|
-
}
|
|
35
|
-
|
|
36
|
-
const enum rndrTAGS {
|
|
37
|
-
calculatedCellRender = '.calculatedCellRender',
|
|
38
|
-
}
|
|
21
|
+
import {Temps as mmcrTemps, Tags as mmcrTags,
|
|
22
|
+
tempTAGS, rendererSettingsChangedState} from '../utils/cell-renderer-consts';
|
|
39
23
|
|
|
40
24
|
type TempType = { [tagName: string]: any };
|
|
41
25
|
|
|
@@ -85,7 +69,7 @@ export class MacromoleculeSequenceCellRenderer extends DG.GridCellRenderer {
|
|
|
85
69
|
// return;
|
|
86
70
|
|
|
87
71
|
const tableCol: DG.Column = gridCell.cell.column;
|
|
88
|
-
const tableColTemp: TempType = tableCol.temp;
|
|
72
|
+
//const tableColTemp: TempType = tableCol.temp;
|
|
89
73
|
const seqColTemp: MonomerPlacer = tableCol.temp[tempTAGS.bioSeqCol];
|
|
90
74
|
if (!seqColTemp) return; // Can do nothing without precalculated data
|
|
91
75
|
|
|
@@ -105,10 +89,10 @@ export class MacromoleculeSequenceCellRenderer extends DG.GridCellRenderer {
|
|
|
105
89
|
const monomerSymbol: string = seqMonList[left];
|
|
106
90
|
const tooltipElements: HTMLElement[] = [ui.div(monomerSymbol)];
|
|
107
91
|
const monomer = seqColTemp.getMonomer(monomerSymbol);
|
|
108
|
-
if(monomer) {
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
92
|
+
if (monomer) {
|
|
93
|
+
const options = {autoCrop: true, autoCropMargin: 0, suppressChiralText: true};
|
|
94
|
+
const monomerSVG = grok.chem.svgMol(monomer.smiles, undefined, undefined, options);
|
|
95
|
+
tooltipElements.push(monomerSVG);
|
|
112
96
|
}
|
|
113
97
|
ui.tooltip.show(ui.divV(tooltipElements), e.x + 16, e.y + 16);
|
|
114
98
|
} else {
|
|
@@ -130,9 +114,10 @@ export class MacromoleculeSequenceCellRenderer extends DG.GridCellRenderer {
|
|
|
130
114
|
*/
|
|
131
115
|
render(
|
|
132
116
|
g: CanvasRenderingContext2D, x: number, y: number, w: number, h: number, gridCell: DG.GridCell,
|
|
133
|
-
|
|
117
|
+
_cellStyle: DG.GridCellStyle
|
|
134
118
|
): void {
|
|
135
|
-
let
|
|
119
|
+
let gapLength = 0;
|
|
120
|
+
const msaGapLength = 8;
|
|
136
121
|
let maxLengthOfMonomer = 8;
|
|
137
122
|
|
|
138
123
|
// TODO: Store temp data to GridColumn
|
|
@@ -144,9 +129,9 @@ export class MacromoleculeSequenceCellRenderer extends DG.GridCellRenderer {
|
|
|
144
129
|
// Cell renderer settings
|
|
145
130
|
const tempMonomerWidth: string | null = tableColTemp[tempTAGS.monomerWidth];
|
|
146
131
|
const monomerWidth: string = (tempMonomerWidth != null) ? tempMonomerWidth : 'short';
|
|
147
|
-
if (monomerWidth === 'short')
|
|
132
|
+
if (monomerWidth === 'short')
|
|
148
133
|
maxLengthOfMonomer = tableColTemp[mmcrTemps.maxMonomerLength] ?? _package.properties.maxMonomerLength;
|
|
149
|
-
|
|
134
|
+
|
|
150
135
|
|
|
151
136
|
let seqColTemp: MonomerPlacer = tableCol.temp[tempTAGS.bioSeqCol];
|
|
152
137
|
if (!seqColTemp) {
|
|
@@ -155,16 +140,24 @@ export class MacromoleculeSequenceCellRenderer extends DG.GridCellRenderer {
|
|
|
155
140
|
const uh = UnitsHandler.getOrCreate(tableCol);
|
|
156
141
|
return {
|
|
157
142
|
unitsHandler: uh,
|
|
158
|
-
monomerCharWidth: 7, separatorWidth: !uh.isMsa() ?
|
|
143
|
+
monomerCharWidth: 7, separatorWidth: !uh.isMsa() ? gapLength : msaGapLength,
|
|
159
144
|
monomerToShort: monomerToShortFunction, monomerLengthLimit: maxLengthOfMonomer,
|
|
160
145
|
monomerLib: getBioLib()
|
|
161
146
|
};
|
|
162
147
|
});
|
|
163
148
|
}
|
|
164
149
|
|
|
150
|
+
if (tableCol.tags[mmcrTags.RendererSettingsChanged] === rendererSettingsChangedState.true) {
|
|
151
|
+
gapLength = tableColTemp[mmcrTemps.gapLength] as number ?? gapLength;
|
|
152
|
+
// this event means that the mm renderer settings have changed, particularly monomer representation and max width.
|
|
153
|
+
seqColTemp.setMonomerLengthLimit(maxLengthOfMonomer);
|
|
154
|
+
seqColTemp.setSeparatorWidth(seqColTemp.isMsa() ? msaGapLength : gapLength);
|
|
155
|
+
tableCol.setTag(mmcrTags.RendererSettingsChanged, rendererSettingsChangedState.false);
|
|
156
|
+
}
|
|
157
|
+
|
|
165
158
|
const [maxLengthWords, maxLengthWordsSum]: [number[], number[]] =
|
|
166
159
|
seqColTemp.getCellMonomerLengths(gridCell.tableRowIndex!);
|
|
167
|
-
const
|
|
160
|
+
const _maxIndex = maxLengthWords.length;
|
|
168
161
|
|
|
169
162
|
// Store updated seqColTemp to the col temp
|
|
170
163
|
if (seqColTemp.updated) tableColTemp[tempTAGS.bioSeqCol] = seqColTemp;
|
|
@@ -1,13 +1,18 @@
|
|
|
1
1
|
import * as ui from 'datagrok-api/ui';
|
|
2
|
+
import * as grok from 'datagrok-api/grok';
|
|
2
3
|
import * as DG from 'datagrok-api/dg';
|
|
3
4
|
|
|
4
|
-
import
|
|
5
|
-
|
|
6
|
-
import {
|
|
5
|
+
import {fromEvent, Unsubscribable} from 'rxjs';
|
|
6
|
+
|
|
7
|
+
import {
|
|
8
|
+
IVdRegionsViewer,
|
|
7
9
|
VdRegion, VdRegionType,
|
|
8
10
|
} from '@datagrok-libraries/bio/src/viewers/vd-regions';
|
|
9
|
-
import {FilterSources, PositionHeight} from '@datagrok-libraries/bio/src/viewers/web-logo';
|
|
10
|
-
|
|
11
|
+
import {FilterSources, IWebLogoViewer, PositionHeight} from '@datagrok-libraries/bio/src/viewers/web-logo';
|
|
12
|
+
|
|
13
|
+
import {WebLogoViewer, PROPS as wlPROPS} from '../viewers/web-logo-viewer';
|
|
14
|
+
|
|
15
|
+
import {_package} from '../package';
|
|
11
16
|
|
|
12
17
|
const vrt = VdRegionType;
|
|
13
18
|
|
|
@@ -101,22 +106,29 @@ export class VdRegionsViewer extends DG.JsViewer implements IVdRegionsViewer {
|
|
|
101
106
|
// this.mlbView.dockManager.dock(this.regionsFg.root, DG.DOCK_TYPE.LEFT, rootNode, 'Filter regions', 0.2);
|
|
102
107
|
|
|
103
108
|
this.subs.push(ui.onSizeChanged(this.root).subscribe(this.rootOnSizeChanged.bind(this)));
|
|
104
|
-
this.subs.push(
|
|
109
|
+
this.subs.push(fromEvent<MouseEvent>(this.root, 'mousemove').subscribe(this.rootOnMouseMove.bind(this)));
|
|
105
110
|
|
|
106
111
|
// await this.buildView('init'); // init
|
|
107
112
|
}
|
|
108
113
|
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
this.
|
|
114
|
+
override detach() {
|
|
115
|
+
if (this.setDataInProgress) return;
|
|
116
|
+
const superDetach = super.detach.bind(this);
|
|
117
|
+
this.detachPromise = this.detachPromise.then(async () => { // detach
|
|
118
|
+
await this.viewPromise;
|
|
119
|
+
if (this.viewed) {
|
|
120
|
+
await this.destroyView('detach');
|
|
121
|
+
this.viewed = false;
|
|
116
122
|
}
|
|
123
|
+
superDetach();
|
|
117
124
|
});
|
|
118
125
|
}
|
|
119
126
|
|
|
127
|
+
override onTableAttached() {
|
|
128
|
+
super.onTableAttached();
|
|
129
|
+
this.setData(this.dataFrame, this.regions);
|
|
130
|
+
}
|
|
131
|
+
|
|
120
132
|
public override onPropertyChanged(property: DG.Property | null): void {
|
|
121
133
|
super.onPropertyChanged(property);
|
|
122
134
|
|
|
@@ -156,39 +168,41 @@ export class VdRegionsViewer extends DG.JsViewer implements IVdRegionsViewer {
|
|
|
156
168
|
|
|
157
169
|
// TODO: .onTableAttached is not calling on dataFrame set, onPropertyChanged also not calling
|
|
158
170
|
public setData(mlbDf: DG.DataFrame, regions: VdRegion[]) {
|
|
159
|
-
|
|
171
|
+
if (!this.setDataInProgress) this.setDataInProgress = true; else return;
|
|
172
|
+
_package.logger.debug('Bio: VdRegionsViewer.setData()');
|
|
173
|
+
|
|
160
174
|
this.viewPromise = this.viewPromise.then(async () => { // setData
|
|
161
175
|
if (this.viewed) {
|
|
162
|
-
await this.destroyView('setData');
|
|
176
|
+
await this.destroyView('setData');
|
|
163
177
|
this.viewed = false;
|
|
164
178
|
}
|
|
165
|
-
})
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
179
|
+
}).then(async () => {
|
|
180
|
+
await this.detachPromise;
|
|
181
|
+
// Wait whether this.dataFrame assigning has called detach() before continue set data and build view
|
|
182
|
+
|
|
183
|
+
// -- Data --
|
|
184
|
+
this.regions = regions;
|
|
185
|
+
if (this.dataFrame.dart !== mlbDf.dart) this.dataFrame = mlbDf; // causes detach and onTableAttached
|
|
186
|
+
}).then(async () => {
|
|
171
187
|
if (!this.viewed) {
|
|
172
|
-
await this.buildView('setData');
|
|
188
|
+
await this.buildView('setData');
|
|
173
189
|
this.viewed = true;
|
|
174
190
|
}
|
|
175
|
-
})
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
await this.destroyView('detach'); // detach
|
|
183
|
-
this.viewed = false;
|
|
184
|
-
}
|
|
185
|
-
superDetach();
|
|
191
|
+
}).catch((err: any) => {
|
|
192
|
+
const errMsg = err instanceof Error ? err.message : err.toString();
|
|
193
|
+
const stack = err instanceof Error ? err.stack : undefined;
|
|
194
|
+
grok.shell.error(errMsg);
|
|
195
|
+
_package.logger.error(errMsg, undefined, stack);
|
|
196
|
+
}).finally(() => {
|
|
197
|
+
this.setDataInProgress = false;
|
|
186
198
|
});
|
|
187
199
|
}
|
|
188
200
|
|
|
189
201
|
// -- View --
|
|
190
202
|
|
|
191
203
|
private viewPromise: Promise<void> = Promise.resolve();
|
|
204
|
+
private detachPromise: Promise<void> = Promise.resolve();
|
|
205
|
+
private setDataInProgress: boolean = false;
|
|
192
206
|
|
|
193
207
|
private host: HTMLElement | null = null;
|
|
194
208
|
private filterSourceInput: DG.InputBase<boolean | null> | null = null;
|
|
@@ -223,25 +237,32 @@ export class VdRegionsViewer extends DG.JsViewer implements IVdRegionsViewer {
|
|
|
223
237
|
const regionsFiltered: VdRegion[] = this.regions.filter((r: VdRegion) => this.regionTypes.includes(r.type));
|
|
224
238
|
const orderList: number[] = Array.from(new Set(regionsFiltered.map((r) => r.order))).sort();
|
|
225
239
|
|
|
226
|
-
|
|
240
|
+
const logoPromiseList: Promise<[number, string, WebLogoViewer]>[] = [];
|
|
227
241
|
for (let orderI = 0; orderI < orderList.length; orderI++) {
|
|
228
|
-
const regionChains: { [chain: string]: WebLogoViewer } = {};
|
|
229
242
|
for (const chain of this.chains) {
|
|
230
243
|
const region: VdRegion | undefined = regionsFiltered
|
|
231
244
|
.find((r) => r.order == orderList[orderI] && r.chain == chain);
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
245
|
+
logoPromiseList.push((async () => {
|
|
246
|
+
const wl: WebLogoViewer = await this.dataFrame.plot.fromType('WebLogo', {
|
|
247
|
+
sequenceColumnName: region!.sequenceColumnName,
|
|
248
|
+
startPositionName: region!.positionStartName,
|
|
249
|
+
endPositionName: region!.positionEndName,
|
|
250
|
+
fixWidth: true,
|
|
251
|
+
skipEmptyPositions: this.skipEmptyPositions,
|
|
252
|
+
positionWidth: this.positionWidth,
|
|
253
|
+
positionHeight: this.positionHeight,
|
|
254
|
+
}) as WebLogoViewer;
|
|
255
|
+
return [orderI, chain, wl];
|
|
256
|
+
})());
|
|
241
257
|
}
|
|
242
|
-
// WebLogo creation fires onRootSizeChanged event even before control being added to this.logos
|
|
243
|
-
this.logos[orderI] = regionChains;
|
|
244
258
|
}
|
|
259
|
+
const logoList: [number, string, WebLogoViewer][] = await Promise.all(logoPromiseList);
|
|
260
|
+
// Fill in this.logos with created viewers
|
|
261
|
+
this.logos = new Array(orderList.length);
|
|
262
|
+
for (let orderI = 0; orderI < orderList.length; ++orderI)
|
|
263
|
+
this.logos[orderI] = {};
|
|
264
|
+
for (const [orderI, chain, wl] of logoList)
|
|
265
|
+
this.logos[orderI][chain] = wl;
|
|
245
266
|
|
|
246
267
|
// ui.tableFromMap()
|
|
247
268
|
// DG.HtmlTable.create()
|
|
@@ -5,24 +5,27 @@ import * as DG from 'datagrok-api/dg';
|
|
|
5
5
|
import wu from 'wu';
|
|
6
6
|
import {fromEvent, Unsubscribable} from 'rxjs';
|
|
7
7
|
|
|
8
|
-
import {SliderOptions} from 'datagrok-api/dg';
|
|
9
8
|
import {UnitsHandler} from '@datagrok-libraries/bio/src/utils/units-handler';
|
|
10
9
|
import {SeqPalette} from '@datagrok-libraries/bio/src/seq-palettes';
|
|
11
10
|
import {
|
|
12
|
-
|
|
11
|
+
monomerToShort,
|
|
12
|
+
pickUpPalette,
|
|
13
|
+
pickUpSeqCol,
|
|
13
14
|
TAGS as bioTAGS,
|
|
14
15
|
} from '@datagrok-libraries/bio/src/utils/macromolecule';
|
|
15
16
|
import {
|
|
16
|
-
|
|
17
|
+
FilterSources,
|
|
18
|
+
HorizontalAlignments,
|
|
17
19
|
PositionHeight,
|
|
20
|
+
PositionMarginStates,
|
|
18
21
|
positionSeparator,
|
|
22
|
+
TAGS as wlTAGS,
|
|
19
23
|
VerticalAlignments,
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
FilterSources,
|
|
24
|
+
WebLogoProps,
|
|
25
|
+
WebLogoPropsDefault,
|
|
23
26
|
} from '@datagrok-libraries/bio/src/viewers/web-logo';
|
|
24
27
|
import {errorToConsole} from '@datagrok-libraries/utils/src/to-console';
|
|
25
|
-
|
|
28
|
+
|
|
26
29
|
import {_package} from '../package';
|
|
27
30
|
|
|
28
31
|
declare global {
|
|
@@ -450,7 +453,7 @@ export class WebLogoViewer extends DG.JsViewer {
|
|
|
450
453
|
this.filterSource = this.string(PROPS.filterSource, defaults.filterSource,
|
|
451
454
|
{category: PROPS_CATS.BEHAVIOR, choices: Object.values(FilterSources)}) as FilterSources;
|
|
452
455
|
|
|
453
|
-
const style: SliderOptions = {style: 'barbell'};
|
|
456
|
+
const style: DG.SliderOptions = {style: 'barbell'};
|
|
454
457
|
this.slider = ui.rangeSlider(0, 100, 0, 20, false, style);
|
|
455
458
|
this.canvas = ui.canvas();
|
|
456
459
|
this.canvas.style.width = '100%';
|
|
@@ -576,7 +579,7 @@ export class WebLogoViewer extends DG.JsViewer {
|
|
|
576
579
|
} catch (err: any) {
|
|
577
580
|
this.seqCol = null;
|
|
578
581
|
this.error = err instanceof Error ? err : new Error(err.toString());
|
|
579
|
-
|
|
582
|
+
throw err;
|
|
580
583
|
}
|
|
581
584
|
if (!this.seqCol) {
|
|
582
585
|
this.unitsHandler = null;
|
|
@@ -700,7 +703,18 @@ export class WebLogoViewer extends DG.JsViewer {
|
|
|
700
703
|
break;
|
|
701
704
|
}
|
|
702
705
|
|
|
703
|
-
|
|
706
|
+
switch (property.name) {
|
|
707
|
+
case PROPS.fixWidth:
|
|
708
|
+
case PROPS.fitArea:
|
|
709
|
+
case PROPS.positionWidth:
|
|
710
|
+
case PROPS.positionMargin:
|
|
711
|
+
this.render(RecalcLevel.Layout, 'onPropertyChanged');
|
|
712
|
+
break;
|
|
713
|
+
|
|
714
|
+
default:
|
|
715
|
+
this.render(RecalcLevel.Freqs, 'onPropertyChanged');
|
|
716
|
+
break;
|
|
717
|
+
}
|
|
704
718
|
}
|
|
705
719
|
|
|
706
720
|
/** Add filter handlers when table is a attached */
|
|
@@ -751,7 +765,7 @@ export class WebLogoViewer extends DG.JsViewer {
|
|
|
751
765
|
|
|
752
766
|
/** Helper function for remove empty positions */
|
|
753
767
|
// TODO: use this function in from core
|
|
754
|
-
protected removeWhere(array: Array<
|
|
768
|
+
protected removeWhere<T>(array: Array<T>, predicate: (item: T) => boolean): Array<any> {
|
|
755
769
|
const length = array.length;
|
|
756
770
|
let updateIterator = 0;
|
|
757
771
|
for (let deleteIterator = 0; deleteIterator < length; deleteIterator++) {
|
|
@@ -767,7 +781,7 @@ export class WebLogoViewer extends DG.JsViewer {
|
|
|
767
781
|
/** Function for removing empty positions */
|
|
768
782
|
protected _removeEmptyPositions() {
|
|
769
783
|
if (this.skipEmptyPositions)
|
|
770
|
-
this.removeWhere(this.positions, (item) => item?.
|
|
784
|
+
this.removeWhere(this.positions, (item) => item?.getFreq('-')?.count === item?.rowCount);
|
|
771
785
|
}
|
|
772
786
|
|
|
773
787
|
private renderRequested: boolean = false;
|
|
@@ -780,11 +794,11 @@ export class WebLogoViewer extends DG.JsViewer {
|
|
|
780
794
|
`.render( recalcLevel=${recalcLevel}, reason='${reason}' )`);
|
|
781
795
|
|
|
782
796
|
/** Calculate freqs of monomers */
|
|
783
|
-
const calculateFreqsInt = (
|
|
797
|
+
const calculateFreqsInt = (): void => {
|
|
784
798
|
_package.logger.debug(`Bio: WebLogoViewer<${this.viewerId}>.render.calculateFreqsInt(), start `);
|
|
785
|
-
|
|
786
799
|
if (!this.host || !this.seqCol || !this.dataFrame) return;
|
|
787
800
|
|
|
801
|
+
const length: number = this.startPosition <= this.endPosition ? this.endPosition - this.startPosition + 1 : 0;
|
|
788
802
|
this.unitsHandler = UnitsHandler.getOrCreate(this.seqCol);
|
|
789
803
|
const posCount: number = this.startPosition <= this.endPosition ? this.endPosition - this.startPosition + 1 : 0;
|
|
790
804
|
this.positions = new Array(posCount);
|
|
@@ -815,9 +829,10 @@ export class WebLogoViewer extends DG.JsViewer {
|
|
|
815
829
|
};
|
|
816
830
|
|
|
817
831
|
/** Calculate layout of monomers on screen (canvas) based on freqs, required to handle mouse events */
|
|
818
|
-
const calculateLayoutInt = (
|
|
832
|
+
const calculateLayoutInt = (dpr: number): void => {
|
|
819
833
|
_package.logger.debug(`Bio: WebLogoViewer<${this.viewerId}>.render.calculateLayoutInt(), start `);
|
|
820
834
|
|
|
835
|
+
const length = this.positions.length;
|
|
821
836
|
this.calcSize();
|
|
822
837
|
const absoluteMaxHeight = this.canvas.height - this.axisHeight * dpr;
|
|
823
838
|
const alphabetSize = this.getAlphabetSize();
|
|
@@ -835,7 +850,8 @@ export class WebLogoViewer extends DG.JsViewer {
|
|
|
835
850
|
*@param {boolean} recalcFreqs - indicates that need to recalculate data for rendering
|
|
836
851
|
*/
|
|
837
852
|
const renderInt = (recalcLevel: RecalcLevel) => {
|
|
838
|
-
_package.logger.debug(`Bio: WebLogoViewer<${this.viewerId}>.render.renderInt( recalcLevel=${recalcLevel} ),
|
|
853
|
+
_package.logger.debug(`Bio: WebLogoViewer<${this.viewerId}>.render.renderInt( recalcLevel=${recalcLevel} ), ` +
|
|
854
|
+
`start `);
|
|
839
855
|
if (this.msgHost) {
|
|
840
856
|
if (this.seqCol && !this.cp) {
|
|
841
857
|
this.msgHost!.innerText = `Unknown palette (column semType: '${this.seqCol.semType}').`;
|
|
@@ -854,11 +870,11 @@ export class WebLogoViewer extends DG.JsViewer {
|
|
|
854
870
|
|
|
855
871
|
this.slider.root.style.width = `${this.host.clientWidth}px`;
|
|
856
872
|
|
|
857
|
-
const length: number = this.Length;
|
|
858
873
|
const dpr: number = window.devicePixelRatio;
|
|
859
|
-
if (recalcLevel >= RecalcLevel.Freqs) calculateFreqsInt(
|
|
860
|
-
if (recalcLevel >= RecalcLevel.Layout) calculateLayoutInt(
|
|
874
|
+
if (recalcLevel >= RecalcLevel.Freqs) calculateFreqsInt();
|
|
875
|
+
if (recalcLevel >= RecalcLevel.Layout) calculateLayoutInt(window.devicePixelRatio);
|
|
861
876
|
|
|
877
|
+
const length: number = this.Length;
|
|
862
878
|
g.resetTransform();
|
|
863
879
|
g.fillStyle = DG.Color.toHtml(this.backgroundColor);
|
|
864
880
|
g.fillRect(0, 0, this.canvas.width, this.canvas.height);
|
|
@@ -896,7 +912,7 @@ export class WebLogoViewer extends DG.JsViewer {
|
|
|
896
912
|
this.positionWidthWithMargin, firstVisibleIndex, this.cp);
|
|
897
913
|
}
|
|
898
914
|
|
|
899
|
-
_package.logger.debug(`Bio: WebLogoViewer<${this.viewerId}>.render.renderInt( recalcLevel=${recalcLevel} ), end
|
|
915
|
+
_package.logger.debug(`Bio: WebLogoViewer<${this.viewerId}>.render.renderInt( recalcLevel=${recalcLevel} ), end`);
|
|
900
916
|
};
|
|
901
917
|
|
|
902
918
|
this.recalcLevelRequested = Math.max(this.recalcLevelRequested, recalcLevel);
|
|
@@ -1039,7 +1055,8 @@ export class WebLogoViewer extends DG.JsViewer {
|
|
|
1039
1055
|
_package.logger.debug('Bio: WebLogoViewer<${this.viewerId}>.dataFrameFilterChanged()');
|
|
1040
1056
|
try {
|
|
1041
1057
|
this.updatePositions();
|
|
1042
|
-
this.
|
|
1058
|
+
if (this.filterSource === FilterSources.Filtered)
|
|
1059
|
+
this.render(RecalcLevel.Freqs, 'dataFrameFilterOnChanged');
|
|
1043
1060
|
} catch (err: any) {
|
|
1044
1061
|
const errMsg = errorToConsole(err);
|
|
1045
1062
|
_package.logger.error('Bio: WebLogoViewer<${this.viewerId}>.dataFrameFilterOnChanged() error:\n' + errMsg);
|
|
@@ -1050,7 +1067,8 @@ export class WebLogoViewer extends DG.JsViewer {
|
|
|
1050
1067
|
private dataFrameSelectionOnChanged(_value: any): void {
|
|
1051
1068
|
_package.logger.debug('Bio: WebLogoViewer<${this.viewerId}>.dataFrameSelectionOnChanged()');
|
|
1052
1069
|
try {
|
|
1053
|
-
this.
|
|
1070
|
+
if (this.filterSource === FilterSources.Selected)
|
|
1071
|
+
this.render(RecalcLevel.Freqs, 'dataFrameSelectionOnChanged');
|
|
1054
1072
|
} catch (err: any) {
|
|
1055
1073
|
const errMsg = errorToConsole(err);
|
|
1056
1074
|
_package.logger.error('Bio: WebLogoViewer<${this.viewerId}>.dataFrameSelectionOnChanged() error:\n' + errMsg);
|
|
@@ -1132,10 +1150,6 @@ export class WebLogoViewer extends DG.JsViewer {
|
|
|
1132
1150
|
export function checkSeqForMonomerAtPos(
|
|
1133
1151
|
df: DG.DataFrame, unitsHandler: UnitsHandler, filter: DG.BitSet, rowI: number, monomer: string, at: PositionInfo,
|
|
1134
1152
|
): boolean {
|
|
1135
|
-
// if (!filter.get(rowI)) return false;
|
|
1136
|
-
// TODO: Use BitSet.get(idx)
|
|
1137
|
-
if (!filter.getSelectedIndexes().includes(rowI)) return false;
|
|
1138
|
-
|
|
1139
1153
|
const seqMList: string[] = unitsHandler.splitted[rowI];
|
|
1140
1154
|
const seqM = at.pos < seqMList.length ? seqMList[at.pos] : null;
|
|
1141
1155
|
return ((seqM === monomer) || (seqM === '' && monomer === '-'));
|